pyqt passing signals between classes - python

I have seen similar questions but none of them seems to work in order to solve my problem.
Basically, my intention is very simple. I try to pass the signal from my settingsWindow class to the centerWindow. Therefore, I created a Qbject called brigde to pass the signal.
In this example, I suppose to get a print "fire" when I press the Ok Button in the settingsWindow. But, nothing is happening.
I'm not sure if I defined all my classes properly. Are all the inheritances correct?
import sys
from PyQt5.QtCore import QFileInfo, QSettings, QCoreApplication, QSize, QRect, Qt, QObject, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (qApp, QApplication, QMainWindow, QFormLayout, QPushButton, QTabWidget,QDialog, QWidget, QAction, QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy)
class mainWindow(QMainWindow):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.initUI()
def initUI(self):
exitAction = QAction('Exit', self)
exitAction.triggered.connect(qApp.quit)
gSettingAction = QAction('Settings', self)
gSettingAction.triggered.connect(settingsWindow)
self.toolbar = self.addToolBar('Exit')
self.toolbar.setMovable(False)
self.toolbar.addAction(exitAction)
self.toolbar.addAction(gSettingAction)
self.center_window = centerWindow(self)
self.setCentralWidget(self.center_window)
class centerWindow(QWidget):
def __init__(self, parent):
super(centerWindow, self).__init__(parent)
self.initUI()
wb = bridge()
wb.valueUpdated.connect(self.fire)
def initUI(self):
lytLWin = QVBoxLayout()
self.hbox1 = QHBoxLayout()
self.hbox1.addLayout(lytLWin)
self.setLayout(self.hbox1)
def fire(self):
print 'fire'
class settingsWindow(QDialog):
def __init__(self):
QDialog.__init__(self)
self.tab_widget = QTabWidget()
self.win_vbox = QVBoxLayout(self)
self.win_vbox.addWidget(self.tab_widget)
self.tab1 = QWidget()
self.tab_widget.addTab(self.tab1, "Tab_1")
t1 = self.tab1_UI()
self.tab1.setLayout(t1)
self.win_vbox.addLayout(self.btnbar())
self.setLayout(self.win_vbox)
self.wb = bridge()
self.exec_()
def tab1_UI(self):
lytSettings = QFormLayout()
vbox = QVBoxLayout()
vbox.addLayout(lytSettings)
return vbox
def btnbar(self):
ok_set_PB = QPushButton('OK')
ok_set_PB.setObjectName("ok_set_PB_IH")
ok_set_PB.clicked.connect(self.ok_settings)
cancel_set_PB = QPushButton('Cancel')
cancel_set_PB.setObjectName("cancel_set_PB_IH")
btn_hbox = QHBoxLayout()
btn_hbox.addStretch()
btn_hbox.addWidget(ok_set_PB)
btn_hbox.addWidget(cancel_set_PB)
spacerItem = QSpacerItem(2, 2, QSizePolicy.Minimum, QSizePolicy.Minimum)
btn_hbox.addItem(spacerItem)
return btn_hbox
def ok_settings(self):
self.wb.sendSignal()
class bridge(QObject):
valueUpdated = pyqtSignal()
def __init__(self):
QObject.__init__(self)
def sendSignal(self):
self.valueUpdated.emit()
def main():
app = QApplication(sys.argv)
ex = mainWindow()
ex.setGeometry(100,100,1000,600)
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

The problem is that you are working with different instances, so that it works you should change to the following:
class mainWindow(QMainWindow):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.wb = bridge()
self.initUI()
def initUI(self):
[...]
gSettingAction.triggered.connect(lambda: settingsWindow(self.wb))
[...]
self.center_window = centerWindow(self.wb, self)
[...]
class centerWindow(QWidget):
def __init__(self, bridge, parent=None):
super(centerWindow, self).__init__(parent)
self.initUI()
wb = bridge
wb.valueUpdated.connect(self.fire)
[...]
class settingsWindow(QDialog):
def __init__(self, bridge, parent=None):
QDialog.__init__(self)
[...]
self.wb = bridge
[...]

Related

PyQt5: Update QMaindow from a QWidget

I use PyQt5 to create an application. I want to update the QMainWinddow after a (time-consuming) function embedded in a QThread, which is started from a QWidget, is finished. How do I address the QMainWindow from QWidget.
Here is a minimal example of the code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMainWindow, QVBoxLayout, QPushButton, QLineEdit
from PyQt5.QtCore import QThread, pyqtSignal
class Thread(QThread):
progressFinished = pyqtSignal(int)
def __init__(self, n):
super(Thread, self).__init__()
self.n = n
def run(self):
for i in range(10):
QThread.msleep(100)
function_class = Function()
x = function_class.function(self.n)
self.progressFinished.emit(x)
class Function():
def function(self, n):
return n*5
class Window(QWidget):
def __init__(self):
super().__init__()
layout_window = QVBoxLayout()
self.setLayout(layout_window)
self.button = QPushButton("Open", self)
layout_window.addWidget(self.button)
self.button.clicked.connect(self.open)
self.lineedit = QLineEdit()
layout_window.addWidget(self.lineedit)
def open(self):
self.loading()
def loading(self):
self.thread = Thread(n=int(self.lineedit.text()))
self.thread.progressFinished.connect(self.set_text)
self.thread.finished.connect(self.close)
self.thread.start()
def set_text(self, link):
print(link)
class MyApp(QMainWindow):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 200, 200
self.setMinimumSize(self.window_width, self.window_height)
self.button = QPushButton("Open", self)
self.button.move(0, 100)
self.button.clicked.connect(self.open)
self.label = QLabel(self)
self.label.setText(str(0))
self.label.move(0, 0)
def open(self):
self.window = Window()
self.window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
myapp = MyApp()
myapp.show()
try:
sys.exit(app.exec_())
except SystemExit:
print('Closing Window...')
I want to use the def set_text(self, link) function from the Window class to set the text of the QLabel from the MyApp class.

Share attribute between two classes "QWidget"

I'm trying to "send" the attribute from a QWidget class to another.
In the example below, I'm trying to set the text of the QLineEdit "self.edit" belonging to the class "Widget1" as text of the QLabel "self.label" belonging to the class "Widget2".
This attempt is made in the function "setLabel".
The part that I cannot figure out is "Widget2.label.setText(text)"
Having a class in a class in a function... I'm a little bit confused how to achieve that...
import sys
from PySide2.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QLabel, QLineEdit)
class Main_UI(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
widget1 = Widget1()
widget2 = Widget2()
layout.addWidget(widget1)
layout.addWidget(widget2)
self.setLayout(layout)
self.show()
class Widget1(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.edit = QLineEdit("")
button = QPushButton("Set value")
button.clicked.connect(self.setLabel)
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
def setLabel(self):
text = self.edit.text()
Widget2.label.setText(text)
class Widget2(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.label = QLabel("")
layout.addWidget(self.label)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
ex = Main_UI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Any help would be appreciated, and if my example or explanations are not clear, I'll provide further explanations.
You can do this with a custom signal.
import sys
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QLabel, QLineEdit)
from PyQt5 import QtCore
class Main_UI(QWidget):
def __init__(self, parent=None):
super(Main_UI, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QVBoxLayout()
widget1 = Widget1()
widget2 = Widget2()
layout.addWidget(widget1)
layout.addWidget(widget2)
self.setLayout(layout)
widget1.button_signal.connect(widget2.label.setText) # Connecting the label to the custom signal.
self.show()
class Widget1(QWidget):
button_signal = QtCore.pyqtSignal(str) # Creating a signal.
def __init__(self, parent=None):
super(Widget1, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.edit = QLineEdit("")
button = QPushButton("Set value")
button.clicked.connect(self.setLabel)
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
def setLabel(self):
"""Emit button signal with text.
This could have been solved with a lambda.
"""
self.button_signal.emit(self.edit.text()) # Emitting Signal.
class Widget2(QWidget):
def __init__(self, parent=None):
super(Widget2, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.label = QLabel("")
layout.addWidget(self.label)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
ex = Main_UI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Docs: https://doc.qt.io/qt-5/signalsandslots.html

How can I inherit between classes in pyqt?

In order to track progress, this is the third question about practicing with different classes in PyQt5 .Here are the links to my previous questions:opening a new window, Open a file from main window to a new window in PyQt5 (in different files).
I'm trying to work with two classes, one with one button and when it's pressed it will load a file and show the text in a QTextEdit in other class.
In the first questions I was suggested that as an alternative to work with more classes, they can inherit from QMainWindow so I looked for more info for doing this: PyQt class inheritance
The second question code did worked but it would show both windows at the same time, so this question: PyQt: How to hide QMainWindow guided me to write this code (I attatch this here because it's a little bit different from the one in the link, plus I apply what it says in the answer):
import sys, os
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class Dialog_02(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_02, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_02 = QPushButton ("Show Dialog 01")
myBoxLayout.addWidget(Button_02)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 02')
Button_02.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_01, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_01 = QPushButton ("Show Dialog 02")
myBoxLayout.addWidget(Button_01)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 01')
Button_01.clicked.connect(self.callAnotherQMainWindow)
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
In this code I'm not inheriting, but it works fine.
The issue is that when I try to follow the same syntax in the original question code, it won't run, I'm not sure I'm getting inheritances fine.
import sys
import os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class SecondWindow(QWidget):
def __init__(self, Window):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.text = QTextEdit(self)
self.btn_return= QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setLayout(v_layout)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Window(QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.img = QLabel()
self.load_file= QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.load_file)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.load_file.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.hide()
self.dialog_02 = SecondWindow(self)
self.dialog_02.show()
def main():
app = QApplication(sys.argv)
main = Window()
s = SecondWindow(main)
main.textChanged.connect(s.text.append)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are coupling many classes: What happens if at a moment SecondWindow does not have a parent? Well, your code will have problems and you will have to modify it a lot so that it works correctly. So first it is to design the behavior of each class, for example SecondWindow has to warn the other windows that it was clicked, it has to have a method that updates the text. Similarly, Window must notify that there is new text available.
On the other hand QMainWindow already has a predefined layout so you must create a centralwidget where you place the other widgets.
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class SecondWindow(QtWidgets.QWidget):
closed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.text = QtWidgets.QTextEdit()
self.btn_return= QtWidgets.QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.close)
self.btn_return.clicked.connect(self.closed)
#QtCore.pyqtSlot(str)
def update_text(self, text):
self.text.setText(text)
self.show()
class Window(QtWidgets.QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.img = QtWidgets.QLabel()
self.load_file= QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
self.load_file.clicked.connect(self.loadafile)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
h_layout = QtWidgets.QHBoxLayout(central_widget)
h_layout.addWidget(self.img)
h_layout.addWidget(self.load_file)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
if filename:
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.close()
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.update_text)
s.closed.connect(main.show)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQt4 threading properly

I want to make a progressbar which runs on a thread and I want to be able to move the widget during the process:
import sys
from PyQt4.QtGui import QApplication, QMainWindow, QPushButton, QLineEdit, QLabel, QComboBox, QProgressBar, QFileDialog
from PyQt4.QtCore import QSize, pyqtSlot, QCoreApplication, SIGNAL, QThread
class App(QMainWindow):
def __init__(self):
super(App, self).__init__()
self.setGeometry(500, 300, 820, 350)
self.setWindowTitle("Program")
self.initUI()
def initUI(self):
#Buttons
btnposx = 30
btnposy = 50
self.btn4 = QPushButton('Load', self)
self.btn4.move(btnposx,btnposy+220)
self.connect(self.btn4, SIGNAL("released()"), self.test)
#ProgressBar
self.pb = QProgressBar(self)
self.pb.move(btnposx+150,btnposy+220)
self.pb.resize(470,27)
self.show()
def load(self, val):
self.pb.setValue(val)
def test(self):
self.workThread = WorkThread()
self.connect( self.workThread, SIGNAL('pb_update'), self.load)
self.workThread.start()
class WorkThread(QThread):
def __init__(self):
super(WorkThread, self).__init__()
QThread.__init__(self)
def __del__(self):
self.wait()
#pyqtSlot()
def run(self):
val = 0
l = range(1000000)
for i in l:
if i < len(l):
val += 100/len(l)
self.emit(SIGNAL('pb_update'), val)
return
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
So far this works, but it does very poorly. The Widget does barely run on my machine, when I try to move it during the process. Is there a way to make this work better so that the Widget doesn't lag or stop responding?
The improvements that your code can have are the following:
Use the new connection style between signals and slots
You must leave a little time for the secondary thread to send the information to the primary thread.
You must indicate the type of connection, in your case Qt::QueuedConnection.
Use pyqtSlot decorator.
You only have to emit the signal when it is necessary, in your case when the whole value of the value changes since the QProgressBar does not recognize floating.
import sys
from PyQt4.QtGui import QApplication, QMainWindow, QPushButton, QLineEdit, QLabel, QComboBox, QProgressBar, QFileDialog
from PyQt4.QtCore import QSize, pyqtSlot, pyqtSignal, QThread, Qt
class App(QMainWindow):
def __init__(self):
super(App, self).__init__()
self.setGeometry(500, 300, 820, 350)
self.setWindowTitle("Program")
self.initUI()
def initUI(self):
#Buttons
btnposx = 30
btnposy = 50
self.btn4 = QPushButton('Load', self)
self.btn4.move(btnposx,btnposy+220)
self.btn4.released.connect(self.test)
#ProgressBar
self.pb = QProgressBar(self)
self.pb.move(btnposx+150,btnposy+220)
self.pb.resize(470,27)
self.show()
#pyqtSlot(int)
def load(self, val):
self.pb.setValue(val)
def test(self):
self.workThread = WorkThread()
self.workThread.pb_update.connect(self.load, Qt.QueuedConnection)
#self.workThread.pb_update.connect(self.pb.setValue)
self.workThread.start()
class WorkThread(QThread):
pb_update = pyqtSignal(float)
def __init__(self, *args, **kwargs):
QThread.__init__(self, *args, **kwargs)
self.value = 0
def __del__(self):
self.wait()
#pyqtSlot()
def run(self):
val = 0
l = range(1000000)
for i in l:
if i < len(l):
val += 100/len(l)
int_val = int(val)
if int_val != self.value:
self.value = int_val
self.pb_update.emit(self.value)
QThread.msleep(1)
return
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

How to add a menubar(QMainWindow) alongside QGraphicsView in PyQt?

A picture is displayed with scrollbars on a window. We can draw on it. I want to add a menubar to the same window. I tried the following, it didn't work. Nothing is shown on the window when I run this.
#!/usr/bin/env python
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt)
from PyQt5.QtGui import (QIcon, QBrush, QColor, QPainter, QPixmap)
from PyQt5.QtWidgets import (QAction, QMainWindow, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem,
QGridLayout, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton)
class TicTacToe(QGraphicsItem):
def __init__(self):
super(TicTacToe, self).__init__()
def paint(self, painter, option, widget):
painter.setPen(Qt.black)
painter.drawLine(0,100,300,100)
def boundingRect(self):
return QRectF(0,0,300,300)
def mousePressEvent(self, event):
pos = event.pos()
self.select(int(pos.x()/100), int(pos.y()/100))
self.update()
super(TicTacToe, self).mousePressEvent(event)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
scene = QGraphicsScene(self)
self.tic_tac_toe = TicTacToe()
scene.addItem(self.tic_tac_toe)
scene.addPixmap(QPixmap("exit.png"))
self.setScene(scene)
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_R:
self.tic_tac_toe.reset()
super(MyGraphicsView, self).keyPressEvent(event)
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.initUI()
def initUI(self):
menubar = self.menuBar()
menu = menubar.addMenu('File')
db_action = menu.addAction("Open file")
self.setGeometry(30, 30, 30, 20)
self.setWindowTitle('Menubar')
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = Example()
mainWindow.showFullScreen()
sys.exit(app.exec_())
A widget is shown in a window if it is a child of some component of the window, in your case self.y is not the child of Example, but only an attribute, a possible solution is to set it as centralWidget:
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.setCentralWidget(self.y)
self.initUI()
def initUI(self):
menubar = self.menuBar()
menu = menubar.addMenu('File')
db_action = menu.addAction("Open file")
self.setGeometry(30, 30, 30, 20)
self.setWindowTitle('Menubar')
self.show()

Categories

Resources