Using custom widget in PyQt? - python

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_())

Related

How to stop from while loop in pyqt?

I tried to design a pyqt gui in when I press "x" will stop run function a counter , what should i do ?
I only know to use the following code
def closeEvent(self,event):
pass
Logic Code
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import *
import sys
import time
from GUI import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.thread = MyThread(self)
self.thread.trigger.connect(self.textBrowser)
self.thread.start()
def textBrowser(self,a):
self.ui.textBrowser_4.clear()
self.ui.textBrowser_4.append(str(a))
def closeEvent(self,event):
pass
class MyThread(QThread):
trigger = pyqtSignal(str)
def run(self):
a = 0
while True:
a = a + 1
self.trigger.emit(str(a))
time.sleep(1)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
MainWindow = MainWindow()
MainWindow.show()
sys.exit(app.exec_())
GUI Code
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(944, 587)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(30, 310, 181, 41))
self.label_2.setObjectName("label_2")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(30, 20, 141, 21))
self.label_4.setObjectName("label_4")
self.textBrowser_4 = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser_4.setGeometry(QtCore.QRect(230, 300, 241, 61))
self.textBrowser_4.setObjectName("textBrowser_4")
MainWindow.setCentralWidget(self.centralwidget)
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_2.setText(_translate("MainWindow", "counter"))
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_())
Add an initiator and an is_running variable in your Thread class like so :
def __init__(self):
self.is_running = True
Then make your loop depend on the truthness of this new variable :
def run(self):
a = 0
while self.is_running:
... your logic here
And finally when you want to exit in closeEvent() :
self.thread.is_running = False
Cheers

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

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.

Threading a class in Python [duplicate]

This question already has answers here:
Start() vs run() for threads in Python?
(3 answers)
Closed 2 years ago.
I'm currently doing a program in python using an UI done in Qt. This UI consists of programming some data and sending them after a click of a button every 200ms. My problem is that if I do this in my main class, the program completely crashes (freeze) so I have to do this in another class or function. After a bit of research, I came across threading, I don't know if it's the right solution for me but it's supposed to run my class in another thread(in the background) until I press the stop button without making my main window crash. The second class needs to have a loop until i press the stop button.
Here is an example of my code:
import Panel
from PyQt5 import QtWidgets, QtCore
import sys
from threading import Thread
import threading
import time
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Panel.Ui_MainWindow()
self.ui.setupUi(self)
self.ui.B_START.clicked.connect(self.StartBtnClicked)
self.ui.B_STOP.clicked.connect(self.StopBtnClicked)
#..
#Other connect and function
#..
def StartBtnClicked(self):
global Startbtn
Startbtn = 1
myClassA()
def StopBtnClicked(self):
global Startbtn
Startbtn = 0
class myClassA(Thread):
def __init__(self):
Thread.__init__(self)
print("myClassA")
self.daemon = True
self.run()
def run(self):
while Startbtn == 1:
print("DONE")
#Doing stuff
time.sleep(0.2)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
And the UI to test it with:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(197, 310)
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
MainWindow.setFont(font)
MainWindow.setStyleSheet("background-color: rgb(252, 252, 252);\n""")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setStyleSheet("")
self.centralwidget.setObjectName("centralwidget")
self.B_START = QtWidgets.QPushButton(self.centralwidget)
self.B_START.setGeometry(QtCore.QRect(50, 50, 71, 61))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_START.setFont(font)
self.B_START.setFocusPolicy(QtCore.Qt.ClickFocus)
self.B_START.setStyleSheet("background-color: rgb(65, 218, 78);\n""\n""\n""\n""")
self.B_START.setObjectName("B_START")
self.B_STOP = QtWidgets.QPushButton(self.centralwidget)
self.B_STOP.setGeometry(QtCore.QRect(50, 190, 71, 61))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_STOP.setFont(font)
self.B_STOP.setStyleSheet("background-color: rgb(255, 0, 0);")
self.B_STOP.setObjectName("B_STOP")
self.B_SET = QtWidgets.QPushButton(self.centralwidget)
self.B_SET.setGeometry(QtCore.QRect(50, 140, 71, 23))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.B_SET.setFont(font)
self.B_SET.setStyleSheet("background-color: rgb(255,255,255);\n""\n""border-width: 1px;\n""\n""")
self.B_SET.setObjectName("B_SET")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 197, 26))
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.menubar.setFont(font)
self.menubar.setObjectName("menubar")
self.menulk = QtWidgets.QMenu(self.menubar)
font = QtGui.QFont()
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
self.menulk.setFont(font)
self.menulk.setObjectName("menulk")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menulk.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Panel"))
self.B_START.setText(_translate("MainWindow", "START"))
self.B_STOP.setText(_translate("MainWindow", "STOP"))
self.B_SET.setText(_translate("MainWindow", "SET"))
self.menulk.setTitle(_translate("MainWindow", "TEST"))
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 just have to put it in the same file and name the ui : "Pannel.py"
Is the solution i went with is the good one? If yes, why is main window is still crashing when I press Start.
Usually you would create a QObject and move it into a QThread, in order to make it run in another thread, preventing the main thread to freeze:
import sys
import random
import string
import datetime
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton, QPlainTextEdit
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QThread, QObject, QTimer
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("My Awesome App")
layout1 = QVBoxLayout()
layout2 = QHBoxLayout()
self.startButton = QPushButton('Start Thread 1')
self.stopButton = QPushButton('Stop Thread 1')
self.startButton.clicked.connect(self._startThread)
self.stopButton.clicked.connect(self._stopThread)
layout2.addWidget(self.startButton)
layout2.addWidget(self.stopButton)
layout1.addLayout( layout2 )
self.textArea = QPlainTextEdit()
self.textArea.setPlaceholderText("")
layout1.addWidget(self.textArea)
widget = QWidget()
widget.setLayout(layout1)
self.setCentralWidget(widget)
# setup threads
self._threads = []
worker = Worker()
self.wThread = QThread()
self.wThread.setObjectName('worker')
self._threads.append((self.wThread, worker)) # you need to keep track of the threads and instances.
worker.moveToThread(self.wThread)
worker.sendData.connect(self._showData) # connect the signal from the thread to a function to to something with the data.
self.wThread.started.connect(worker.start)
def _showData(self, data):
msg = f"[{datetime.datetime.now()}] {data}"
self.textArea.appendPlainText(msg)
def _startThread(self):
self.wThread.start()
def _stopThread(self):
self.wThread.quit()
class Worker(QObject):
sendData = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__()
#pyqtSlot()
def start(self):
self.timer = QTimer()
self.timer.setInterval(200) # 200ms!
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.run)
self.timer.start()
def run(self):
data = ''.join(random.choice(string.ascii_letters) for x in range(24))
self.sendData.emit(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Screenshot:
As you can see +- every 200ms a randomly generated string is sent back to the main window.

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)

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_())

Categories