Python threading - Cannot set parent, new parent is in a different thread - python

I'm trying to make a GUI that, once you press a button a scrollArea would dynamically get updated. I tried using QThread to not lock the GUI and to show the user the scrollArea as it gets populated with elements.
I made a thread worker class where inside a loop I'm creating the elements for the scrollArea.The elements are put on a QVBoxLayout and I'm trying to pass that layout to my GUI scrollArea using a signal of type QObject.
I'm receiving this error: QObject::setParent: Cannot set parent, new parent is in a different thread
I don't know what to do to solve this
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class WorkerThread(QThread):
my_signal = pyqtSignal(QObject)
def run(self):
top_layout = QtWidgets.QVBoxLayout()
for i in range(2500):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('box {0}'.format(i))
label = QtWidgets.QLabel()
label.setText('label {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
layout.addWidget(label)
top_layout.addWidget(group_box)
self.my_signal.emit(top_layout)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(520, 30, 71, 51))
self.pushButton.setObjectName("pushButton")
self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
self.scrollArea.setGeometry(QtCore.QRect(0, 90, 511, 461))
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 505, 455))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton.clicked.connect(self.click)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "add"))
def click(self):
self.worker = WorkerThread()
self.worker.start()
self.worker.my_signal.connect(self.update_scrollArea)
def update_scrollArea(self, top_layout):
top_widget = QtWidgets.QWidget()
top_widget.setLayout(top_layout)
self.scrollArea.setWidget(top_widget)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

I've had problems with creating PyQt5 objects in threads in the past, and one option for you would be to use a timer instead.
class Ui_MainWindow(object):
def __init__(self):
self.timer = QtCore.QBasicTimer()
self.timer.start(250, self)
self.i = 0
def timerEvent(self, event):
if self.i < 2500:
self.i+=1
top_layout = QtWidgets.QVBoxLayout()
group_box = QtWidgets.QGroupBox()
group_box.setTitle('box {0}'.format(i))
label = QtWidgets.QLabel()
label.setText('label {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
layout.addWidget(label)
top_layout.addWidget(group_box)
self.my_signal.emit(top_layout)
def setupUi(self, MainWindow):
...
This way you will interrupt the main loop once every 250msec to update one loop iteration at a time and you will not freeze up the GUI.

Related

How to work with multiple windows in PyQt5? Passing Values through multiple windows and Calling Modules in Other Modules

I have created a GUI with PyQt5. I have total three windows. first window has one push button (**btn_OpenSecondWIndow**) which opens a second window. second window has one push button (**btn_OpenCalendar**) which opens third (Calendar) window. user picks a date from there and select a button (**btn_selecteddate**) on third window, I want to display that selection in the label (label_Date) in the second window.
Snippet to get the flow
Now I'm at this point where the first window works just fine and the second window opens BUT pushbuttons doesn't work.
The button in the second window does nothing when the window is opened from the first one but when the class "SecondWindow" is called by its own and not from the first window, it works.
here is the code:
from PyQt5 import QtWidgets
from FirstWindow import Ui_FirstWindow
from SecondWindow import Ui_SecondWindow
from Calendar import Ui_CalendarWindow
class Calendar(QtWidgets.QMainWindow, Ui_CalendarWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
class FirstWindow(QtWidgets.QMainWindow, Ui_FirstWindow):
def __init__(self):
super(FirstWindow,self).__init__()
self.setupUi(self)
self.btn_OpenSecondWIndow.clicked.connect(self.open_SecondWindow)
def open_SecondWindow(self):
self.window = QtWidgets.QMainWindow()
self.ui = SecondWindow()
self.ui.setupUi(self.window)
self.window.show()
self.setEnabled(False)
self.window.setEnabled(True)
class SecondWindow(QtWidgets.QMainWindow, Ui_SecondWindow):
def __init__(self):
super(SecondWindow, self).__init__()
self.setupUi(self)
self.btn_OpenCalendar.clicked.connect(self.Open_Calendar)
def Open_Calendar(self):
self.window = Calendar()
self.window.setupUi(self.window)
self.window.show()
self.window.btn_Selecteddate.clicked.connect(self.PickedDate)
def PickedDate(self):
self.selecteddate = self.window.CalendarBox.selectedDate()
self.label_Date.setText(self.selecteddate.toString('MMM')+'-'+self.selecteddate.toString('yyyy'))
self.window.hide()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
window = FirstWindow()
window.show()
sys.exit(app.exec_())
Reference Ui Class codes are as below:
First Window:
from PyQt5 import QtCore, QtWidgets
class Ui_FirstWindow(object):
def setupUi(self, FirstWindow):
FirstWindow.setObjectName("FirstWindow")
FirstWindow.resize(380, 195)
self.centralwidget = QtWidgets.QWidget(FirstWindow)
self.centralwidget.setObjectName("centralwidget")
self.btn_OpenSecondWIndow = QtWidgets.QPushButton(self.centralwidget)
self.btn_OpenSecondWIndow.setGeometry(QtCore.QRect(80, 60, 221, 61))
self.btn_OpenSecondWIndow.setObjectName("btn_OpenSecondWIndow")
FirstWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(FirstWindow)
self.statusbar.setObjectName("statusbar")
FirstWindow.setStatusBar(self.statusbar)
self.retranslateUi(FirstWindow)
QtCore.QMetaObject.connectSlotsByName(FirstWindow)
def retranslateUi(self, FirstWindow):
_translate = QtCore.QCoreApplication.translate
FirstWindow.setWindowTitle(_translate("FirstWindow", "MainWindow"))
self.btn_OpenSecondWIndow.setText(_translate("FirstWindow", "Open Second Window"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
FirstWindow = QtWidgets.QMainWindow()
ui = Ui_FirstWindow()
ui.setupUi(FirstWindow)
FirstWindow.show()
sys.exit(app.exec_())
Second WIndow:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_SecondWindow(object):
def setupUi(self, SecondWindow):
SecondWindow.setObjectName("SecondWindow")
SecondWindow.resize(654, 242)
self.centralwidget = QtWidgets.QWidget(SecondWindow)
self.centralwidget.setObjectName("centralwidget")
self.label_Date = QtWidgets.QLabel(self.centralwidget)
self.label_Date.setGeometry(QtCore.QRect(330, 60, 281, 131))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label_Date.setFont(font)
self.label_Date.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_Date.setObjectName("label_Date")
self.btn_OpenCalendar = QtWidgets.QPushButton(self.centralwidget)
self.btn_OpenCalendar.setGeometry(QtCore.QRect(80, 90, 191, 61))
self.btn_OpenCalendar.setObjectName("btn_OpenCalendar")
SecondWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(SecondWindow)
self.statusbar.setObjectName("statusbar")
SecondWindow.setStatusBar(self.statusbar)
self.retranslateUi(SecondWindow)
QtCore.QMetaObject.connectSlotsByName(SecondWindow)
def retranslateUi(self, SecondWindow):
_translate = QtCore.QCoreApplication.translate
SecondWindow.setWindowTitle(_translate("SecondWindow", "MainWindow"))
self.label_Date.setText(_translate("SecondWindow", "Date"))
self.btn_OpenCalendar.setText(_translate("SecondWindow", "Open Calendar"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
SecondWindow = QtWidgets.QMainWindow()
ui = Ui_SecondWindow()
ui.setupUi(SecondWindow)
SecondWindow.show()
sys.exit(app.exec_())
Third (Calendar) Window:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_CalendarWindow(object):
def setupUi(self, CalendarWindow):
CalendarWindow.setObjectName("CalendarWindow")
CalendarWindow.resize(512, 458)
self.centralwidget = QtWidgets.QWidget(CalendarWindow)
self.centralwidget.setObjectName("centralwidget")
self.CalendarBox = QtWidgets.QCalendarWidget(self.centralwidget)
self.CalendarBox.setGeometry(QtCore.QRect(20, 20, 464, 289))
self.CalendarBox.setObjectName("CalendarBox")
self.btn_Selecteddate = QtWidgets.QPushButton(self.centralwidget)
self.btn_Selecteddate.setGeometry(QtCore.QRect(160, 330, 181, 60))
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.btn_Selecteddate.setFont(font)
self.btn_Selecteddate.setObjectName("btn_Selecteddate")
CalendarWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(CalendarWindow)
self.statusbar.setObjectName("statusbar")
CalendarWindow.setStatusBar(self.statusbar)
self.retranslateUi(CalendarWindow)
QtCore.QMetaObject.connectSlotsByName(CalendarWindow)
def retranslateUi(self, CalendarWindow):
_translate = QtCore.QCoreApplication.translate
CalendarWindow.setWindowTitle(_translate("CalendarWindow", "MainWindow"))
self.btn_Selecteddate.setText(_translate("CalendarWindow", "Select"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
CalendarWindow = QtWidgets.QMainWindow()
ui = Ui_CalendarWindow()
ui.setupUi(CalendarWindow)
CalendarWindow.show()
sys.exit(app.exec_())
Thanks in Advance. :)
In FirstWindow.open_SecondWindow you don't need to create a separate QMainWindow object for the second window. SecondWindow() already returns a QMainWindow object with the proper UI set up and a slot connected to btn_OpenCalendar for opening the calendar window. Therefore it is sufficient to do something like:
def open_SecondWindow(self):
self.window = SecondWindow()
self.window.show()
self.setEnabled(False)
self.window.setEnabled(True)

How to animate the splitter when setSizes() method is called?

I have the QSplitter widget in my Qt5 GUI, and there are two child widgets added to that splitter. and there is a QPushButton widget, when that button is pushed second child disappears and when again that button is pushed that child widget appears. but I want like expanding and collapsing of that child widget. but I have no idea how to animate the event when 'setSizes()' is called on the splitter widget.
can anybody provide me the code for that in pyqt5?
Here is my code
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(QtWidgets.QMainWindow):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(400, 300)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralWidget)
self.verticalLayout.setContentsMargins(11, 11, 11, 11)
self.verticalLayout.setSpacing(6)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralWidget)
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.splitter_resize)
self.pushButton.setCheckable(True)
self.verticalLayout.addWidget(self.pushButton)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSpacing(6)
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton_2 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_3.setObjectName("pushButton_3")
self.splitter = QtWidgets.QSplitter()
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.addWidget(self.pushButton_2)
self.splitter.addWidget(self.pushButton_3)
self.horizontalLayout.addWidget(self.splitter)
self.verticalLayout.addLayout(self.horizontalLayout)
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def splitter_resize(self):
if self.pushButton.isChecked():
self.splitter.setSizes([16777215, 0])
self.pushButton.setChecked = False
else:
self.splitter.setSizes([16777215, 16777215])
self.pushButton.setChecked = True
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton_2.setText(_translate("MainWindow", "PushButton"))
self.pushButton_3.setText(_translate("MainWindow", "PushButton"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You can use QVariantAnimation:
from PyQt5 import QtCore, QtGui, QtWidgets
MIN_SIZE, MAX_SIZE = 0, 16777215
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
button = QtWidgets.QPushButton(
"Press me", checkable=True, toggled=self.onToggled
)
left_button = QtWidgets.QPushButton("Left")
right_button = QtWidgets.QPushButton("Right")
sp = right_button.sizePolicy()
sp.setHorizontalPolicy(QtWidgets.QSizePolicy.Ignored)
right_button.setSizePolicy(sp)
self.m_splitter = QtWidgets.QSplitter(orientation=QtCore.Qt.Horizontal)
self.m_splitter.addWidget(left_button)
self.m_splitter.addWidget(right_button)
widget = QtWidgets.QWidget()
self.setCentralWidget(widget)
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(button)
lay.addWidget(self.m_splitter)
self.m_animation = QtCore.QVariantAnimation(
self,
startValue=MAX_SIZE,
endValue=MIN_SIZE,
valueChanged=self.onValueChanged,
duration=1000,
easingCurve=QtCore.QEasingCurve.InOutCubic,
)
#QtCore.pyqtSlot(bool)
def onToggled(self, checked):
self.m_animation.setDirection(
QtCore.QAbstractAnimation.Forward
if checked
else QtCore.QAbstractAnimation.Backward
)
self.m_animation.start()
#QtCore.pyqtSlot(QtCore.QVariant)
def onValueChanged(self, value):
s = [MAX_SIZE, value]
self.m_splitter.setSizes(s)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

In PyQt5, new parent is in a different Qthread after pop up QMessageBox

This is PyQt5 code. I want to count down 5 seconds and update every 1 second. Then application will pop up QMessageBox. But it will closed after click button on QMessageBox due to QObject::setParent: Cannot set parent, new parent is in a different thread, detail code below:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys, time
from _thread import *
class ThreadClass(QtCore.QThread):
# Create the signal
sig = QtCore.pyqtSignal(int)
def __init__(self, mw, parent=None):
self.mw = mw
self.mbox = QtWidgets.QMessageBox()
super().__init__(parent)
self.sig.connect(self.showtime)
def showtime(self, t):
self.mw.label.setText(str(t))
def run(self):
for t in range(5):
self.sig.emit(t)
time.sleep(1)
self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(253, 181)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(90, 100, 75, 23))
self.pushButton.setObjectName("pushButton")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 20, 211, 16))
self.label.setObjectName("label")
self.tc = ThreadClass(self)
self.pushButton.clicked.connect(lambda: self.tc.start())
#self.pushButton.clicked.connect(lambda: start_new_thread(showtime, (self.label, )))
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.setText("Show")
self.label.setText("Time")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Ui_MainWindow()
w = QtWidgets.QMainWindow()
ex.setupUi(w)
w.show()
sys.exit(app.exec_())
App will close after clicked button on QMessageBox and error message below.
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
Try it:
import sys #, time
from PyQt5 import QtCore, QtGui, QtWidgets
from _thread import *
class ThreadClass(QtCore.QThread):
# Create the signal
sig = QtCore.pyqtSignal(int)
finish = QtCore.pyqtSignal() # +++
def __init__(self, mw, parent=None):
# self.mw = mw
# self.mbox = QtWidgets.QMessageBox()
super().__init__(parent)
# self.sig.connect(self.showtime)
# def showtime(self, t):
# self.mw.label.setText(str(t))
def run(self):
for t in range(5):
self.sig.emit(t)
#time.sleep(1)
QtCore.QThread.msleep(1000)
self.finish.emit() # +++
# self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(253, 181)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(90, 100, 75, 23))
self.pushButton.setObjectName("pushButton")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 20, 211, 16))
self.label.setObjectName("label")
self.tc = ThreadClass(self)
self.pushButton.clicked.connect(lambda: self.tc.start())
#self.pushButton.clicked.connect(lambda: start_new_thread(showtime, (self.label, )))
self.tc.sig.connect(self.showtime) # +++
self.tc.finish.connect(self.finishTime) # +++
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.setText("Show")
self.label.setText("Time")
# +++
def showtime(self, t):
self.label.setText(str(t))
# +++
def finishTime(self):
self.mbox = QtWidgets.QMessageBox()
self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
ex = Ui_MainWindow()
ex.setupUi(w)
w.show()
sys.exit(app.exec_())

AttributeError: 'MyMainWindow' object has no attribute 'pushButton'

Trying to proceed click button event in my code but got some issue.
AttributeError: 'MyMainWindow' object has no attribute 'pushButton'
Seems, like clicked event can`t find pushbutton from my subclass. Probably i did some mistakes in syntax, so really waiting for your help guys. Sorry if the question very banal, pyQt5 is simply new for me, trying to figure out in all of this.
Here is my files.
ui_main.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '1.ui'
#
# Created by: PyQt5 UI code generator 5.7.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(349, 131)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
self.lineEdit.setObjectName("lineEdit")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.lineEdit)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.label)
self.gridLayout.addLayout(self.formLayout, 0, 0, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 349, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "TextLabel"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton_2.setText(_translate("MainWindow", "PushButton"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
py_main.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, qApp
from PyQt5.QtCore import Qt, QEvent, QObject
from ui_main import Ui_MainWindow
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
qApp.installEventFilter(self)
self.pushButton.clicked.connect(self.clickButton()) #here is where the issue is occurs
self.show()
self.setupUi(self)
def eventFilter(self, obj, event):
if event.type() == QEvent.KeyPress:
if event.key() == Qt.Key_Escape:
self.close()
return super(MyMainWindow, self).eventFilter(obj, event)
def clickButton(self):
sender = self.sender()
self.statusbar().showMessage(sender.text() + ' was pressed')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyMainWindow()
test = TestClass()
sys.exit(app.exec_())
Waiting forward for your help!
In your code there are 3 errors:
You must call the setupUI function first since the button is created here and then connect that button to the slot.
When the button is connected to the slot you just have to pass the slot name without parenthesis.
Change statusbar() to statusBar().
Code:
# ...
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
qApp.installEventFilter(self)
self.setupUi(self)
self.pushButton.clicked.connect(self.clickButton)
self.show()
# ...
def clickButton(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
# ...
it work for me
call the setupUI function first
self.setupUi(self)
self.pushButton.clicked.connect(self.click_button)
tag QDialog and Ui_Dialog
main.py
MyMainWindow(QDialog, Ui_Dialog):
mainGui.py
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
OUTPUT:
SOLVED: AttributeError Main object has no attribute pushButton

Using custom widget in PyQt?

I followed this example (PyQT4) to create a "custom widget" in PyQT5 and ended up with following code:
progress.py
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QApplication,QMainWindow)
class Ui_Form(QMainWindow):
def __init__(self, name, parent=None):
super(Ui_Form,self).__init__(parent)
#Form.setObjectName("Form")
self.resize(619, 202)
self.formLayoutWidget = QtWidgets.QWidget()
self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 621, 201))
self.formLayoutWidget.setObjectName("formLayoutWidget")
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.formLayout.setObjectName("formLayout")
self.progressBar = QtWidgets.QProgressBar(self.formLayoutWidget)
self.progressBar.setProperty("value", 24)
self.progressBar.setObjectName("progressBar")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.progressBar)
self.graphicsView = QtWidgets.QGraphicsView(self.formLayoutWidget)
self.graphicsView.setObjectName("graphicsView")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.graphicsView)
self.label_Title = QtWidgets.QLabel(self.formLayoutWidget)
font = QtGui.QFont()
font.setFamily("Verdana")
font.setPointSize(22)
font.setBold(True)
font.setWeight(75)
self.label_Title.setFont(font)
self.label_Title.setObjectName("label_Title")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.label_Title)
self.timer = QtCore.QBasicTimer()
self.step = 0
self.timer.start(100,self)
self.retranslateUi()
QtCore.QMetaObject.connectSlotsByName(self)
def timerEvent(self, e):
if self.step>= 100:
self.timer.stop()
return
self.step = self.step + 1
self.progressBar.setValue(self.step)
def retranslateUi(self):
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("Form", "Form"))
self.label_Title.setText(_translate("Form", "TextLabel"))
new_ui.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(742, 538)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton_Start = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_Start.setGeometry(QtCore.QRect(630, 20, 91, 31))
self.pushButton_Start.setObjectName("pushButton_Start")
self.lineEdit_URL = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_URL.setGeometry(QtCore.QRect(20, 20, 601, 31))
self.lineEdit_URL.setObjectName("lineEdit_URL")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 460, 46, 13))
self.label.setObjectName("label")
self.label_status = QtWidgets.QLabel(self.centralwidget)
self.label_status.setGeometry(QtCore.QRect(73, 460, 651, 16))
self.label_status.setObjectName("label_status")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(20, 60, 701, 391))
self.listWidget.setObjectName("listWidget")
#MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 742, 21))
self.menubar.setObjectName("menubar")
#MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
#MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "My Downloader"))
self.pushButton_Start.setText(_translate("MainWindow", "Start"))
self.label.setText(_translate("MainWindow", "Status :"))
self.label_status.setText(_translate("MainWindow", "Ideal..."))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
main.py
import sys
import threading
import logging
from PyQt5 import QtCore, QtGui, QtWidgets
import json
import urllib.request
from new_ui import Ui_MainWindow
import progress
import random
class MyForm(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.pushButton_Start.clicked.connect(self.thread_start)
def thread_start(self):
p = threading.Thread(name='worker', target=self.start_download)
#Do UI updates
self.ui.label_status.setText('Fetching information...')
#self.listWidget.addItem(prgstr)
p.start()
def start_download(self):
............
code to fetch information from web
............
#itm = self.ui.listWidget.addItem(json_data['entries'][0]['title'])
self.ui.label_status.setText(json_data['entries'][0]['title'])
item = QtWidgets.QListWidgetItem(self.ui.listWidget)
item_widget = progress.Ui_Form("It works")
item.setSizeHint(item_widget.sizeHint())
self.ui.listWidget.addItem(item)
self.ui.listWidget.setItemWidget(item,item_widget)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
The above runs without any errors, but the following code fails to add any list item (custom widget), the label_status gets correctly populated with retrieved data - what am I missing here?
self.ui.label_status.setText(json_data['entries'][0]['title'])
item = QtWidgets.QListWidgetItem(self.ui.listWidget)
item_widget = progress.Ui_Form("It works")
item.setSizeHint(item_widget.sizeHint())
self.ui.listWidget.addItem(item)
self.ui.listWidget.setItemWidget(item,item_widget)
You should only ever update GUI elements from within the main GUI thread.
The worker thread should emit a signal when it's done, or perhaps do so periodically so that pending GUI events can be processed (i.e. by calling qApp.processEvents()).
If the worker thread generates data, use a Queue so that it can be accessed safely from the main thread.
UPDATE:
Here is a basic example (python3 only) of how to use QThread with a worker object. All communication between the GUI thread and the worker thread is done using signals and slots (see "Signals and Slots Across Threads" in the Qt docs for more on this).
import urllib.request
from PyQt4 import QtCore, QtGui
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
error = QtCore.pyqtSignal(object)
dataReady = QtCore.pyqtSignal(object)
#QtCore.pyqtSlot(str)
def process(self, url):
try:
response = urllib.request.urlopen(url, timeout=20)
try:
self.dataReady.emit(response.read())
finally:
response.close()
except urllib.error.URLError as exception:
self.error.emit(exception.reason)
except BaseException as exception:
self.error.emit(str(exception))
finally:
self.finished.emit()
class Window(QtGui.QWidget):
downloadRequest = QtCore.pyqtSignal(str)
def __init__(self):
QtGui.QWidget.__init__(self)
self.viewer = QtGui.QPlainTextEdit(self)
self.edit = QtGui.QLineEdit(self)
self.edit.setFocus()
self.button = QtGui.QPushButton('Download', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.viewer)
layout.addWidget(self.edit)
layout.addWidget(self.button)
# worker must not have a parent
self._worker = Worker()
self._thread = QtCore.QThread(self)
self._worker.moveToThread(self._thread)
self._worker.finished.connect(self._thread.quit)
self._worker.error.connect(self.handleError)
self._worker.dataReady.connect(self.handleDataReady)
self.downloadRequest.connect(self._worker.process)
def handleButton(self):
url = self.edit.text().strip()
if url and not self._thread.isRunning():
self.viewer.clear()
self._thread.start()
# safely communicate with worker via signal
self.downloadRequest.emit(url)
def handleError(self, reason):
self.viewer.clear()
print('ERROR:', reason)
def handleDataReady(self, data):
self.viewer.setPlainText(data.decode('utf-8'))
def closeEvent(self, event):
if self._thread.isRunning():
# Qt will complain if thread is still
# running, so quit it before closing
self._thread.quit()
self._thread.wait()
QtGui.QWidget.closeEvent(self, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.edit.setText('http://stackoverflow.com')
window.setGeometry(500, 300, 300, 300)
window.show()
sys.exit(app.exec_())

Categories