How to use Threading in Python pyqt4 GUI design [duplicate] - python

This question already has answers here:
Simplest way for PyQT Threading
(2 answers)
Closed 4 years ago.
I have written a simple code of python GUI for sleeping my code for 10 seconds. At the same time, the GUI should run without freezing. Is not possible by defining primari thready and secondary thread? While I click on "Run" button pythonw.exe has stopped working. Does anyone have any thoughts on how I may be able to troubleshoot this? Thank you.
import sys, threading
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSlot
from PyQt4.QtGui import *
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window,self).__init__()
self.setGeometry(400,150, 550, 500)
self.setWindowTitle("my first program")
self.home()
def home(self):
self.button_1 = QtGui.QPushButton('Run', self)
self.button_1.resize(60,25)
self.button_1.move(230,230)
self.button_1.clicked.connect(self.Thread_test)
self.textbox_1 = QTextEdit(self)
self.textbox_1.move(15,290)
self.textbox_1.resize(510,170)
self.show()
def Thread_test(self):
t = threading.Thread(target=self.calculation)
t.start()
def calculation(self):
msg="program is working"
self.textbox_1.setText(msg)
time.sleep(5)
msg="program was slept for few sec."
self.textbox_1.setText(msg)
def run():
app=QtGui.QApplication(sys.argv)
GUI=Window()
sys.exit(app.exec_())
run()

Qt Widgets are not thread safe. You should access a widget only from the thread that created it. See http://doc.qt.io/qt-5/thread-basics.html:
All widgets and several related classes [...] don't work in secondary threads.
You will have to notify your main (GUI) thread to update the UI. You can do so with signals & slots.

Related

How to disable widgets during handling of a signal in PySide6 [duplicate]

This question already has answers here:
PyQt run time issue
(3 answers)
Disabling QPushButton before a task
(1 answer)
PyQT: PushButton receives commands while disabled
(1 answer)
Closed 21 days ago.
During handling of a clicked signal I would like to show a wait cursor until this is done.
All mouse clicks on other widgets should be ignored during that time.
The cursor part is working fine using setCursor().
But all mouse clicks on other buttons (or the original one) are still detected and cached, so they are handled after the first button
operation is finished.
How can I make sure that no user interface events are generated until my operation is finished?
This code shows the things I have already tried:
pushButton_B.setEnabled()
pushButton_B.clicked.disconnect()
pushButton_B.blockSignals()
None of those have led to the desired behaviour.
I am hoping for some way to disable user interface events for the whole window, so I don't need to do this for every
widget I introduce, but I couldn't find the right methods for this.
I'd appreciate any hints on how to do this properly.
import sys
import time
from PySide6.QtCore import Qt, QRect, QCoreApplication, QMetaObject
from PySide6.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(200, 200)
self.centralwidget = QWidget(self)
self.pushButton_A = QPushButton(self.centralwidget)
self.pushButton_A.setText('A')
self.pushButton_A.setGeometry(QRect(20, 20, 100, 24))
self.pushButton_B = QPushButton(self.centralwidget)
self.pushButton_B.setText('B')
self.pushButton_B.setGeometry(QRect(20, 60, 100, 24))
self.setCentralWidget(self.centralwidget)
self.pushButton_A.clicked.connect(self.pushButton_A_clicked)
self.pushButton_B.clicked.connect(self.pushButton_B_clicked)
def pushButton_A_clicked(self):
print("pushButton_A clicked...")
self.setCursor(Qt.CursorShape.WaitCursor)
self.pushButton_B.setEnabled(False)
self.pushButton_B.clicked.disconnect(self.pushButton_B_clicked)
self.pushButton_B.blockSignals(True)
self.repaint()
time.sleep(3) # Some lengthy operation
self.setCursor(Qt.CursorShape.ArrowCursor)
self.pushButton_B.setEnabled(True)
self.pushButton_B.clicked.connect(self.pushButton_B_clicked)
self.pushButton_B.blockSignals(False)
print("pushButton_A done.")
def pushButton_B_clicked(self):
print("pushButton_B clicked.")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Ui_MainWindow()
w.show()
app.exec()

How to call a function periodically while my MainWindow is active?

I have tried doing it with multiprocessing module to no avail. I get the following error:
TypeError: cannot pickle 'MainWindow' object
import time, multiprocessing
from PyQt5 import QtWidgets, QtGui
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initializeUI()
self.show()
def initializeUI(self):
# UI stuff
self.setLayout(QtWidgets.QGridLayout())
dummy_btn = QtWidgets.QPushButton("Ok")
self.layout().addWidget(dummy_btn)
updater = multiprocessing.Process(target=self.update_function, args=[])
updater.start()
def update_function(self):
time.sleep(2)
print("This text, again!")
self.update_function()
app = QtWidgets.QApplication([])
mw = MainWindow()
app.exec_()
the proper way to do this is to not use any sort of parallel mechanism, instead use QTimer.singleshot, as QT doesn't work well with multiprocessing or threading, and if you want to repeat it then you can just connect the function to a Qtimer.timeout signal and set the timer on repeat using Qtimer.start() as in this tutorial
import time, multiprocessing
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QTimer
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initializeUI()
self.show()
def initializeUI(self):
# UI stuff
self.setLayout(QtWidgets.QGridLayout())
dummy_btn = QtWidgets.QPushButton("Ok")
self.layout().addWidget(dummy_btn)
self.timer = QTimer(self)
# self.timer.singleShot(2000,self.update_function) # for one time call only
self.timer.timeout.connect(self.update_function)
self.timer.start(2000) # time in milliseconds.
def update_function(self):
# time.sleep(2) this is not needed
print("This text, again!")
self.update() # this had a typo
app = QtWidgets.QApplication([])
mw = MainWindow()
app.exec_()
Edit: to clarify on working with threads and multiprocessing, if you use multiprocessing for example there are many precautions, such as putting an if __name__ == "__main__": guard on your code, and not use anything that belong to QT inside the subprocesses, and just use it for running things that don't need QT, like reading files and doing calculations.
as for threading, using any QWidget object in another thread other than your main application thread is going to crash your application, you can emit signals from child threads for signaling, but you cannot update the GUI on another thread, so only use QT objects that don't touch the GUI inside threads. (like networking, reading files, and sharing the CPU for extra calculations)

Window opened from a class is not displaying correctly [duplicate]

This question already has answers here:
Equivalent to time.sleep for a PyQt application
(5 answers)
Closed 1 year ago.
If I open a window from the main() method it is displayed correctly.
If I open the same window out of a class instantiated from the main() method (WorkerClass), the window is only partially displayed, all the content inside (e.g. a QText) edit is missing.
Here's the code I used to reproduce this with a minimal amount of code:
import sys, time
from PyQt5.QtWidgets import QApplication, QTextEdit
from PyQt5.QtCore import QObject
class StatusWindow(QTextEdit):
def __init__(self):
super().__init__()
def appendText(self, text):
self.append(text)
class WorkerClass(QObject):
def __init__(self):
super().__init__()
def openWindow(self):
s = StatusWindow()
s.show()
s.appendText('opened from WorkerClass')
time.sleep(10)
def main():
app = QApplication(sys.argv)
win = StatusWindow()
win.show()
win.appendText('opened from main()')
work = WorkerClass()
work.openWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
What goe's wrong here? How to fix this example to be able to get the window opened from WorkerClass displayed correctly?
Many thanks to musicamante, know I understand what happens.
The StatusWindow would also build correctly if instantiated from WorkerClass. If time.sleep is replaced by the Qtimer you can proove this.
In my example the window is not build correctly as the whole application is too busy, here simulated with time.sleep, and will never find the time to properly finish the window as long WorkerClass is running.

Is my threading proper ? if yes then why code is not working?

I am creating an alarm clock in python using PyQt4 and in that I am using LCD display widget, which display current updating time. For that I am using threading. But I am new to it so the problem is I have no clue how to debug that thing.
This is my code
import sys
from PyQt4 import QtGui, uic
import time
import os
from threading import Thread
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('AlarmClock_UI.ui', self)
self.show()
self.comboBox.setCurrentIndex(0)
self.comboBox.currentIndexChanged.connect(self.getSelection)
self.lineEdit.setText('Please select the reminder type')
timeThread = Thread(target = self.showTime())
timeThread.start()
def getSelection(self):
if self.comboBox.currentIndex() == 1:
self.lineEdit.setText('Select the alarm time of your choice')
elif self.comboBox.currentIndex() == 2:
self.lineEdit.setText('Use those dials to adjust hour and minutes')
else:
self.lineEdit.setText('Please select the reminder type')
def showTime(self):
showTime = time.strftime('%H:%M:%S')
self.lcdNumber.display(showTime)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
I tried while loop in showTime() function then it was not even loading GUI just running in the background.
Thanks :)
As has been said elsewhere, you do not need to use threading for this, as a simple timer will do. Here is a basic demo script:
import sys
from PyQt4 import QtCore, QtGui
class Clock(QtGui.QLCDNumber):
def __init__(self):
super(Clock, self).__init__(8)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.showTime)
self.timer.start(1000)
self.showTime()
def showTime(self):
time = QtCore.QTime.currentTime()
self.display(time.toString('hh:mm:ss'))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Clock()
window.setWindowTitle('Clock')
window.setGeometry(500, 100, 400, 100)
window.show()
sys.exit(app.exec_())
Qt does not support doing GUI operations in threads other than the main thread. So when you call self.lcddisplay.display(showTime) from within the context of your spawned thread, that is an error and Qt will not work correctly.
As tdelaney suggested in his comment, the best way to handle this sort of thing is to use a QTimer to emit a signal at the appropriate intervals, and update your lcddisplay in the slot that signal is connected to.
(if you insist on using threads, however, e.g. as a learning exercise, then your spawned thread would need to send a message to the main thread to tell the main thread to do the display update, rather than trying to do the update itself)

Pyqt Terminal hangs after excuting close window command

I have read lots of threads online, but still I could not find the solution. My question should be very simple: how to close a Pyqt window WITHOUT clicking a button or using a timer.
The code I tried is pasted below
from PyQt4 import QtGui, QtCore
import numpy as np
import progressMeter_simple
import sys
import time
import pdb
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
QtGui.QApplication.processEvents()
def termination(self):
time.sleep(5)
self.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Dialog = ProgressMeter()
Dialog.show()
Dialog.termination()
sys.exit(app.exec_())
My Pyqt GUI is designed using Qt designer, and it is nothing but a progress bar that keeps moving from left to right (busy indication).
However, when I run the code above, the terminal still hangs after the Pyqt window is closed. Ctrl+C also couldn't kill the process.
In short, how can I properly close/terminate a Pyqt window without clicking a button or using a timer?
It's not working because you're calling GUI methods on the dialog (close()) outside of the event loop. The event loop doesn't start until you call app.exec_().
If you really want to close the dialog immediately after it opens without using a QTimer, you can override the showEvent() method and call termination() from there, which gets called when the dialog is first displayed.
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
def showEvent(self, event):
super(ProgressMeter, self).showEvent(event)
self.termination()

Categories

Resources