pyqt5 formatting using multiple layouts - pyqt5

I have 4 labels and need first 1st 2 table on the first row and next 2 on the second row.
The below code works fine but the row spans the whole screen.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QDialog, QGridLayout, QLabel, QLineEdit
from PyQt5.Qt import QHBoxLayout, QWindow, QMainWindow, QVBoxLayout
class Example(QMainWindow):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
vlayout = QVBoxLayout()
hlayout1 = QHBoxLayout()
hlayout2 = QHBoxLayout()
widget = QWidget()
widget.setLayout(vlayout)
a1 = QLabel('label1')
a2 = QLabel('label2')
hlayout1.addWidget(a1)
hlayout1.addWidget(a2)
hlayout1.addStretch(2)
vlayout.addLayout(hlayout1)
vlayout.addStretch(1)
a3 = QLabel('label3')
a4 = QLabel('label3')
hlayout2.addWidget(a3)
hlayout2.addWidget(a4)
hlayout2.addStretch(1)
vlayout.addLayout(hlayout2)
vlayout.addStretch(1)
self.setCentralWidget(widget)
self.setGeometry(500, 500, 500, 500)
self.setWindowTitle('Lines')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
# ex.show()
sys.exit(app.exec_())

Delete the line vlayout.addStretch(1)
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QDialog, QGridLayout, QLabel, QLineEdit
from PyQt5.Qt import QHBoxLayout, QWindow, QMainWindow, QVBoxLayout
class Example(QMainWindow):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
vlayout = QVBoxLayout()
hlayout1 = QHBoxLayout()
hlayout2 = QHBoxLayout()
widget = QWidget()
widget.setLayout(vlayout)
a1 = QLabel('label1')
a2 = QLabel('label2')
hlayout1.addWidget(a1)
hlayout1.addWidget(a2)
hlayout1.addStretch(2)
vlayout.addLayout(hlayout1)
#vlayout.addStretch(1) # < ----
a3 = QLabel('label3')
a4 = QLabel('label3')
hlayout2.addWidget(a3)
hlayout2.addWidget(a4)
hlayout2.addStretch(1)
vlayout.addLayout(hlayout2)
vlayout.addStretch(1)
self.setCentralWidget(widget)
self.setGeometry(500, 500, 500, 500)
self.setWindowTitle('Lines')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
# ex.show()
sys.exit(app.exec_())

Related

How to get log and show it on GUI from multiprocessing work?

I try to get logs from multiprocessing work and show them on GUI.
Based on this document
gui.py:
from PyQt5 import QtCore, QtWidgets
import logging
from log_test import main
Signal = QtCore.pyqtSignal
Slot = QtCore.pyqtSlot
class Signaller(QtCore.QObject):
signal = Signal(str, logging.LogRecord)
class QtHandler(logging.Handler):
def __init__(self, slotfunc, *args, **kwargs):
super().__init__(*args, **kwargs)
self.signaller = Signaller()
self.signaller.signal.connect(slotfunc)
def emit(self, record):
s = self.format(record)
self.signaller.signal.emit(s, record)
class Worker(QtCore.QObject):
finished = Signal()
#Slot()
def start(self):
main()
self.finished.emit()
class Ui_Dialog(QtCore.QObject):
def __init__(self):
super().__init__()
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.setEnabled(True)
Dialog.resize(530, 440)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.button = QtWidgets.QPushButton(Dialog)
self.button.setText("start working")
self.verticalLayout.addWidget(self.button)
self.logWidget = QtWidgets.QPlainTextEdit(Dialog)
self.logWidget.setReadOnly(True)
self.verticalLayout.addWidget(self.logWidget)
self.handler = QtHandler(self.update_log_gui)
logging.getLogger('log').addHandler(self.handler)
self.button.clicked.connect(self.start_work)
#Slot(str, logging.LogRecord)
def update_log_gui(self, status, record):
self.logWidget.appendPlainText(status)
def config_thread(self):
self.worker_thread = QtCore.QThread()
self.worker_thread.setObjectName('WorkerThread')
self.worker = Worker()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.started.connect(self.worker.start)
self.worker.finished.connect(self.worker_thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker_thread.finished.connect(self.worker_thread.deleteLater)
self.worker_thread.finished.connect(lambda: self.button.setEnabled(True))
pass
def start_work(self):
self.config_thread()
self.worker_thread.start()
self.button.setEnabled(False)
if __name__ == "__main__":
import sys
QtCore.QThread.currentThread().setObjectName('MainThread')
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
log_test.py (where multiprocessing work happens)
import logging
import time
from multiprocessing import Pool
def f(name):
logger = logging.getLogger('log.' + name)
logger.error('hello there 1')
time.sleep(0.5)
logger.error('hello there 2')
time.sleep(0.5)
logger.error('hello there 3')
time.sleep(0.5)
def main():
with Pool(5) as p:
p.map(f, ['aaa', 'bbb', 'ccc'])
At first time, I thought working in single thread causing the problem. So I added QThread to this.
Later I discovered in debug, it seems to QtHandler.emit() works fine at receiving log messages. But the connected slot function, update_log_gui() does not work somehow.
I solved it myself.
#Alexander was right. Indeed my QtHandler has a problem when multiprocessing but I don't know exactly why. Rather, you wanna implement QueueHandler. An example in this article (Written in Korean) helped me.
from PyQt5 import QtCore, QtWidgets
import logging
import multiprocessing
from log_test import main
Signal = QtCore.pyqtSignal
Slot = QtCore.pyqtSlot
QThread = QtCore.QThread
class Signaller(QtCore.QObject):
signal = Signal(logging.LogRecord)
class Worker(QtCore.QObject):
finished = Signal()
def __init__(self, q):
super().__init__()
self.q = q
#Slot()
def start(self):
main(self.q)
self.finished.emit()
class Consumer(QThread):
popped = Signaller()
def __init__(self, q):
super().__init__()
self.q = q
self.setObjectName('ConsumerThread')
def run(self):
while True:
if not self.q.empty():
record = self.q.get()
self.popped.signal.emit(record)
class Ui_Dialog(QtCore.QObject):
def __init__(self, app):
super().__init__()
self.app = app
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.setEnabled(True)
Dialog.resize(530, 440)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.button = QtWidgets.QPushButton(Dialog)
self.button.setText("start working")
self.verticalLayout.addWidget(self.button)
self.logWidget = QtWidgets.QPlainTextEdit(Dialog)
self.logWidget.setReadOnly(True)
self.verticalLayout.addWidget(self.logWidget)
self.button.clicked.connect(self.start_work)
self.q = multiprocessing.Manager().Queue()
self.consumer = Consumer(self.q)
self.consumer.popped.signal.connect(self.update_log_gui)
self.consumer.start()
app.aboutToQuit.connect(self.shutdown_consumer)
#Slot(logging.LogRecord)
def update_log_gui(self, record):
self.logWidget.appendPlainText(str(record.msg))
def config_thread(self):
self.worker_thread = QtCore.QThread()
self.worker_thread.setObjectName('WorkerThread')
self.worker = Worker(self.q)
self.worker.moveToThread(self.worker_thread)
self.worker_thread.started.connect(self.worker.start)
self.worker.finished.connect(self.worker_thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker_thread.finished.connect(self.worker_thread.deleteLater)
self.worker_thread.finished.connect(lambda: self.button.setEnabled(True))
def start_work(self):
self.config_thread()
self.worker_thread.start()
self.button.setEnabled(False)
def shutdown_consumer(self):
if self.consumer.isRunning():
self.consumer.requestInterruption()
self.consumer.quit()
self.consumer.wait()
if __name__ == "__main__":
import sys
QtCore.QThread.currentThread().setObjectName('MainThread')
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog(app)
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())

cannot be associated with QComboBox

button of class Main don't connect with class Qcombobox of Signals
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import *
import sys
from PyQt5 import QtGui
class Signals(QWidget):
asignal = pyqtSignal(str)
def __init__(self):
super(Signals, self).__init__()
self.setGeometry(300, 250, 400, 300)
self.ii()
self.show()
def ii(self):
vbox = QVBoxLayout()
self.combo = QComboBox()
self.combo.addItem("Python")
self.combo.addItem("Java")
self.combo.addItem("C++")
self.combo.addItem("C#")
self.combo.addItem("Ruby")
self.buttom = QPushButton("Click")
self.buttom.clicked.connect(self.windown2)
vbox.addWidget(self.combo)
vbox.addWidget(self.buttom)
self.setLayout(vbox)
def do_something(self):
self.asignal.emit(self.combo.currentText())
def windown2(self):
self.ggpp = Main()
self.ggpp.show()
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.coso1)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self):
s = Signals()
s.asignal.connect(lambda sig: print("self.combo.currentText()>>>>>" + sig))
s.do_something()
if __name__ == '__main__':
app = QApplication(sys.argv)
nals = Signals()
nals.show()
sys.exit(app.exec())
What you see happens because you're not using the existing instance of Signals, but you're creating a new one each time the button is clicked.
In your case, you could add a reference to the instance as an argument when you create the new window, so that you can correctly connect to its signal.
class Signals(QWidget):
# ...
def windown2(self):
self.ggpp = Main(self)
self.ggpp.show()
class Main(QWidget):
def __init__(self, signals):
super(Main, self).__init__()
self.signals = signals
self.signals.asignal.connect(self.coso1)
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.signals.do_something)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self, sig):
print("self.combo.currentText()>>>>>" + sig)

PyQt5: drawing multiple rectangles using mouseEvents by implementing QGraphicsScene, QGraphicsView and QGraphicsItem [duplicate]

I have a scene like this
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
def mousePressEvent(self, event):
print('scene pressed')
self.wid = MyRect(event.pos(), event.pos())
self.addItem(self.wid)
self.wid.show()
I would like class MyRect(QtWidgets.QGraphicsRectItem) with painter, mouse event and so on to be a draggable rectangle.
all stuff in MyRect
So then I could have many Rectangle to the scene and even after draw line between them and so on (kind of diagram app), but keeping objects related editable options in MyRect, MyLine , ....
I thought :
class MyRect(QtWidgets.QGraphicsRectItem):
def __init__(self, begin, end, parent=None):
super().__init__(parent)
self.begin = begin
self.end = end
def paintEvent(self, event):
print('painting')
qp = QtGui.QPainter(self)
qp.drawRect(QtCore.QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
But I does not work (paint event not initiated whereas mousepressed event in scene is intiated)
I did not find what I wanted through the web so started totry do it by myself. I'm pretty sure it is a must known starting point but I cannot find it
First of all a QGraphicsItem is not a QWidget, so it has those events and does not handle them directly, that's what QGraphicsView and QGraphicsScene do. For example you say that you want to have a moveable rectangle because that task is simple is QGraphicsView, it is not necessary to overwrite:
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
If you want to change the way you paint the rectangle you must overwrite the paint() method as shown below:
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def paint(self, painter, option, widget=None):
super(RectItem, self).paint(painter, option, widget)
painter.save()
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(QtCore.Qt.red)
painter.drawEllipse(option.rect)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = RectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Update:
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(QtCore.QRectF(-500, -500, 1000, 1000), parent)
self._start = QtCore.QPointF()
self._current_rect_item = None
def mousePressEvent(self, event):
if self.itemAt(event.scenePos(), QtGui.QTransform()) is None:
self._current_rect_item = QtWidgets.QGraphicsRectItem()
self._current_rect_item.setBrush(QtCore.Qt.red)
self._current_rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.addItem(self._current_rect_item)
self._start = event.scenePos()
r = QtCore.QRectF(self._start, self._start)
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._current_rect_item is not None:
r = QtCore.QRectF(self._start, event.scenePos()).normalized()
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self._current_rect_item = None
super(GraphicsScene, self).mouseReleaseEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene =GraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

how to transmit true value to formal parameter in decorator function(pyqtSlot())?

first, see code below:
import sys
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def printLabel(self, str):
print(str)
#pyqtSlot(int)
def on_sld_valueChanged(self, value):
self.lcd.display(value)
self.printLabel(value)
def initUI(self):
self.lcd = QLCDNumber(self)
self.sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(self.lcd)
vbox.addWidget(self.sld)
self.setLayout(vbox)
self.sld.valueChanged.connect(self.on_sld_valueChanged)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I'm a little puzzled about how the true value in sld is transmitted to the formal parameter 'value' in the slot function : def sld_valChanged(self, value).
Because i can't see something like this: self.sld.valueChanged.connect(partial(self.sld_valChanged, self.sld.value))
Could someone explain that?

pyqt5 videowidget not showing in layout

I am writing a program with pyqt5 where pressing a button first cycles through some pictures then cycles through some videos.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import *
import glob
import argparse
import sys
class MainWindow(QMainWindow):
def __init__(self,args):
super(MainWindow, self).__init__()
self.setWindowTitle('Navon test')
self.setWindowFlags(Qt.FramelessWindowHint)
# exit option for the menu bar File menu
self.exit = QAction('Exit', self)
self.exit.setShortcut('Ctrl+q')
# message for the status bar if mouse is over Exit
self.exit.setStatusTip('Exit program')
# newer connect style (PySide/PyQT 4.5 and higher)
self.exit.triggered.connect(app.quit)
self.setWindowIcon(QIcon('icon.ico'))
self.centralwidget = CentralWidget(args)
self.setCentralWidget(self.centralwidget)
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Escape:
QCoreApplication.instance().quit()
self.centralwidget.startvid()
class CentralWidget(QWidget):
def __init__(self,args):
super(CentralWidget, self).__init__()
self.layout = QVBoxLayout()
self.layout.setAlignment(Qt.AlignCenter)
self.setLayout(self.layout)
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.vw = QVideoWidget()
self.player.setVideoOutput(self.vw)
def startvid(self):
self.layout.addWidget(self.vw)
url= QUrl.fromLocalFile(glob.glob("videos/*")[0])
content= QMediaContent(url)
self.player.setMedia(content)
self.player.setVideoOutput(self.vw)
self.player.play()
if __name__== "__main__":
parser = argparse.ArgumentParser()
#~ parser.add_argument("-nb","--nobox",action="store_true", help="do not wait for the box connection")
args = parser.parse_args()
app = QApplication(sys.argv)
mainwindow = MainWindow(args)
#~ mainwindow.showFullScreen()
mainwindow.show()
sys.exit(app.exec_())
I tried to paste the minimal code. The thing is, I press the button nothing shows, although I used examples like this one PyQt5 - Can't play video using QVideoWidget to test if playing the video is ok, and these work. It's as if it is not adding the widget to the layout or something. Any idea what might be wrong?
I had to use QGraphicsView to achieve what I wanted, here is a fix:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import *
import glob
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle('Navon test')
self.setWindowFlags(Qt.FramelessWindowHint)
# exit option for the menu bar File menu
self.exit = QAction('Exit', self)
self.exit.setShortcut('Ctrl+q')
# message for the status bar if mouse is over Exit
self.exit.setStatusTip('Exit program')
# newer connect style (PySide/PyQT 4.5 and higher)
self.exit.triggered.connect(app.quit)
self.setWindowIcon(QIcon('icon.ico'))
self.centralwidget = VideoPlayer()
self.setCentralWidget(self.centralwidget)
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Escape:
self.centralwidget.phaseQuit(2)
self.centralwidget.play()
class VideoPlayer(QWidget):
def __init__(self, parent=None):
super(VideoPlayer, self).__init__(parent)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.videoItem = QGraphicsVideoItem()
self.videoItem.setSize(QSizeF(640, 480))
scene = QGraphicsScene(self)
graphicsView = QGraphicsView(scene)
scene.addItem(self.videoItem)
layout = QVBoxLayout()
layout.addWidget(graphicsView)
self.setLayout(layout)
self.mediaPlayer.setVideoOutput(self.videoItem)
self.counter = 0
def play(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
pass
else:
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(glob.glob("videos/*")[self.counter])))
self.mediaPlayer.play()
self.counter += 1
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
player = MainWindow()
player.show()
sys.exit(app.exec_())