My app is freezing because i need to create a new QThread, but am a little confused how to create it when i call widgets that exists on the main class.
here is my code so far ...
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUi
import time
##Main Class
class HWindow(QtWidgets.QWidget):
def __init__(self):
super(HWindow, self).__init__()
loadUi("Hw.ui",self) ##load ui
#Create a QThread object
self.thread = QtCore.QThread()
#Create a QThread object
self.workerMain = WorkerMain()
#Move worker to the thread
self.workerMain.moveToThread(self.thread)
#Start the thread
self.thread.start()
self.runningMain = False
def activateMain(self):
if self.chkb_main.isChecked():
self.lbl_disena_main.setText("Activated")
self.lbl_disena_main.setStyleSheet('color: green;')
#Change variable and call the function
runningMain = True
self.myFunction()
else:
self.lbl_disena_main.setText("Disabled")
self.lbl_disena_main.setStyleSheet('color: red;')
#Change variable and call the function
runningMain = False
def myFunction(self):
while self.runningMain == True:
if self.gpb_main.isChecked() and self.chkb_main.isChecked():
print("running ...")
time.sleep(3)
##Worker Class
class WorkerMain(QtCore.QObject):
threadRunning = QtCore.pyqtSignal()
def __init__(self):
super(WorkerMain, self).__init__()
def run(self):
print("Thread Running ...")
#i cant call my widgets from my main class from here.
'''
while self.runningMain == True:
if self.gpb_main.isChecked() and self.chkb_main.isChecked():
print("running ...")
time.sleep(3)
'''
def stop(self):
print("Thread Stopped ...")
self.terminate()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
hWindow = QtWidgets.QWidget()
hWindow = HWindow()
hWindow.show()
sys.exit(app.exec_())
Based on pyqt5 documentation and examples, you create a loop on the run method, but it is complicated when i have to create that loop based on what the user select (GUI widgets).
i just figure it out, here is my code ...
class HWindow(QtWidgets.QWidget):
def __init__(self):
super(HWindow, self).__init__()
loadUi("Hw.ui",self) ##load ui
#Create a QThread object
self.thread = QtCore.QThread()
#Create a QThread object
self.workerMain = WorkerMain(self)
#Move worker to the thread
self.workerMain.moveToThread(self.thread)
#Connect the start signal to the run slot
self.workerMain.threadRunning.connect(self.workerMain.run)
#Connect the activateMain signal to the start slot
self.chkb_main.toggled.connect(self.activateMain)
#Start the thread
self.thread.start()
def activateMain(self):
if self.chkb_main.isChecked():
self.lbl_disena_main.setText("Activated")
self.lbl_disena_main.setStyleSheet('color: green;')
self.workerMain.runningMain = True
self.workerMain.threadRunning.emit()
if not self.thread.isRunning():
self.thread.start()
else:
self.lbl_disena_main.setText("Disabled")
self.lbl_disena_main.setStyleSheet('color: red;')
self.workerMain.runningMain = False
self.workerMain.stop()
##Worker Class
class WorkerMain(QtCore.QObject):
threadRunning = QtCore.pyqtSignal()
def __init__(self, parent):
super(WorkerMain, self).__init__()
self.parent = parent
self.runningMain = False
def run(self):
print("Thread Running ...")
while self.runningMain:
if self.parent.gpb_main.isChecked() and self.parent.chkb_main.isChecked():
print("running ...")
time.sleep(3)
def stop(self):
print("Thread Stopped ...")
if self.parent.thread.isRunning():
self.parent.thread.quit()
self.parent.thread.wait()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
hWindow = QtWidgets.QWidget()
hWindow = HWindow()
hWindow.show()
sys.exit(app.exec_())
Related
I use a pyqt_signal to transmit a sub window, which has a button whose function is to print. I use a thread to transmit this sub window to the main window to show, however the button loses its function. I know that I should put the statement self.sub_window = SubWindow() into the __init__ function in the second class, but how can I achieve the same effect if I still put this statement here.
# -*- coding: utf-8 -*-
from threading import currentThread
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QObject, QThread
class SubWindow(QWidget):
def __init__(self):
super(SubWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
def print_(self):
print('***')
class SignalStore(QThread):
window_signal = pyqtSignal(object)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
# if i put this statement here, how can i acquire window's print button function
self.sub_window = SubWindow()
self.window_signal.emit(self.sub_window)
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.window_signal.connect(self.get_sub_window)
def send_signal(self):
self.med_signal.start()
def get_sub_window(self, para):
self.sub_window = para
self.sub_window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
Don't create or access gui objects inside threads. Read Qt guide.
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
This is probably what you are looking for:
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QThread, pyqtSlot
class SubWindow(QWidget):
def __init__(self, parent=None):
super(SubWindow, self).__init__(parent)
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
#pyqtSlot()
def print_(self):
print('hello from subwindow')
class SignalStore(QThread):
print_func = pyqtSignal(str)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
time.sleep(1) # fake working...
self.print_func.emit("hello from thread")
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.subwin = SubWindow()
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.print_func.connect(self.print_from_main)
def send_signal(self):
self.subwin.show()
self.med_signal.start()
#pyqtSlot(str)
def print_from_main(self, string: str):
print(string)
self.subwin.print_()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
In pyqt5 when i try to close a tread for a my program Ui freezes and stop responding.I'm new to pyqt5 couldn't find a answer for this.
These are the methods in main window(GUI) file
def clickButton_on(self):
print("clicked on/off")
ControlPanel.start()
def clickButton_Off(self):
print("clicked on/off")
ControlPanel.stop()
This is the controlPanel class methods
#classmethod
def start(cls):
print('start method fired')
cls.th = threading.Thread(target=cls.test,args=(cls.attr,))
cls.th.start()
#classmethod
def stop(cls):
print('stop method fired')
cls.th.join()
Full code to recreate the issue
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUi
import sys
import threading
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("on")
self.b2 = QtWidgets.QPushButton(self)
self.b2.setText("off")
self.b2.move(50,50)
self.b1.clicked.connect(self.clickButton_on)
self.b2.clicked.connect(self.clickButton_Off)
def clickButton_on(self):
print("clicked on/off")
ControlPanel.start()
def clickButton_Off(self):
print("clicked on/off")
ControlPanel.stop()
class ControlPanel:
n=2
#classmethod
def start(cls):
print('start method fired')
cls.th = threading.Thread(target=cls.test,args=(cls.n,))
cls.th.start()
#classmethod
def stop(cls):
print('stop method fired')
cls.th.join()
#classmethod
def test(cls,n):
print('test method fired')
n= 0
while True:
n += 0.1
print(f'thread running n = {n}')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
windows completely freezes. It doesnt close the thread with .join() it thread keeps running.
I'm trying to create a play/pause/resume/ button with a progress bar. User presses 'Start' and the progress bar initializes and a pause/resume button populates. Once the progress completes, a message is printed and the pause/resume buttons are hidden while the 'Start' button is shown again. However, when I try to run 'Start' again, it prints the same completion message an additional time.
If I run it twice, it prints out two completion messages. Run it three times, prints out three completion messages and etc.
from PyQt5.QtWidgets import (
QWidget, QApplication, QProgressBar, QMainWindow,
QHBoxLayout, QPushButton
)
from PyQt5.QtCore import (
Qt, QObject, pyqtSignal, pyqtSlot, QRunnable, QThreadPool
)
import time
class WorkerSignals(QObject):
progress = pyqtSignal(int)
finished = pyqtSignal()
class JobRunner(QRunnable):
signals = WorkerSignals()
def __init__(self):
super().__init__()
self.is_paused = False
self.is_killed = False
#pyqtSlot()
def run(self):
for n in range(100):
self.signals.progress.emit(n + 1)
time.sleep(0.001)
while self.is_paused:
time.sleep(0)
if self.is_killed:
break
self.signals.finished.emit()
def finished(self):
print('Finished')
def pause(self):
self.is_paused = True
def resume(self):
self.is_paused = False
# def kill(self):
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# Some buttons
self.w = QWidget()
self.l = QHBoxLayout()
self.w.setLayout(self.l)
self.btn_start = QPushButton("Start")
self.l.addWidget(self.btn_start)
self.setCentralWidget(self.w)
# Create a statusbar.
self.status = self.statusBar()
self.progress = QProgressBar()
self.status.addPermanentWidget(self.progress)
# Thread runner
# self.threadpool = QThreadPool()
# # Create a runner
# self.runner = JobRunner()
# self.runner.signals.progress.connect(self.update_progress)
# self.threadpool.start(self.runner)
self.btn_start.pressed.connect(self.start_runner)
# btn_pause.pressed.connect(self.runner.pause)
# btn_resume.pressed.connect(self.runner.resume)
# self.startTestSignal.connect(self.update_progress)
self.show()
def update_progress(self, n):
self.progress.setValue(n)
def start_runner(self):
# Create ThreadPool
self.threadpool = QThreadPool()
self.threadpool.clear()
# Create a runner
self.runner = JobRunner()
self.runner.signals.progress.connect(self.update_progress)
self.threadpool.start(self.runner)
self.progress.setValue(0)
# Change Start Button to Pause
self.btn_start.hide()
# self.btn_start.setEnabled(False)
self.btn_pause = QPushButton("Pause")
self.btn_resume = QPushButton("Resume")
self.l.addWidget(self.btn_pause)
self.l.addWidget(self.btn_resume)
self.btn_pause.pressed.connect(self.runner.pause)
self.btn_resume.pressed.connect(self.runner.resume)
self.runner.signals.finished.connect(self.check)
def check(self):
print('Thread Done')
self.btn_start.show()
self.progress.setValue(0)
# self.runner.terminate()
self.btn_pause.hide()
self.btn_resume.hide()
app = QApplication([])
w = MainWindow()
app.exec_()
You are creating a WorkerSignals as a class attribute, making it a persistent for the class; so when you do this:
self.runner.signals.finished.connect(self.check)
you are actually connecting again to signals.finished, and when you emit the signal the function self.check is called each time the signal has been connected.
The solution is to create the WorkerSignals as an instance attribute:
class JobRunner(QRunnable):
def __init__(self):
super().__init__()
self.signals = WorkerSignals()
self.is_paused = False
self.is_killed = False
My python script need to change one object lcd_p1 every time the function wait_thread_v1 is call every second by a thread t1, but how do this? I don't know how to access this object inside the function? Anyone can help?
vazao1 = 12
global pulses_v1
pulses_v1 = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(vazao1, GPIO.IN)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60,320)
def count_pulses_v1(channel):
global pulses_v1
pulses_v1 += 1
def wait_thread_v1():
global pulses_v1
while True:
time.sleep(1)
print("Pulsos total de vazão 1: "+str(pulses_v1))
#Window.initUI.self.lcd_p1.display(100)
pulses_v1 = 0
GPIO.add_event_detect(vazao1, GPIO.FALLING, callback=count_pulses_v1)
t1 = Thread(target=wait_thread_v1, name='thread_01', args=())
t1.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
You cannot and should not modify the GUI from a secondary thread as indicated by the docs since Qt does not guarantee that it works besides that it is unnecessary to create a new thread. In this case it is better to create a QObject that emits the signal when the callback is invoked since it is thread-safe, and then connect that signal to a slot that increases the pulses, then use a QTimer to implement the logic of the thread.
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer
from PyQt5.QtWidgets import QApplication, QLCDNumber, QWidget
import RPi.GPIO as GPIO
class Window(QWidget):
def __init__(self):
super().__init__()
self.pulses = 0
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
timer = QTimer(self, timeout=self.on_timeout, interval=1000)
timer.start()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60, 320)
#pyqtSlot()
def falling_slot(self):
self.pulses += 1
#pyqtSlot()
def on_timeout(self):
self.lcd_v1.display(self.pulses)
self.pulses = 0
class GPIOManager(QObject):
fallingSignal = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
pin = 12
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=self.falling_callback)
def falling_callback(self, channel):
# This method is called in the thread where GPIOs are monitored.
self.fallingSignal.emit()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
manager = GPIOManager()
manager.fallingSignal.connect(window.falling_slot)
sys.exit(app.exec())
I'm having an issue when I try to subclass my worker class the main UI thread locks up and becomes unresponsive until the Worker Thread has finished. I'm at a loss here and suspect something with how the GIL interacts with inheritance?
In MainWindow.clicked() these two lines:
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
Here is my sample code that reproduces this:
from PySide import QtGui, QtCore
import sys
import time
class Worker(QtCore.QObject):
started = QtCore.Signal()
progress = QtCore.Signal(int)
finished = QtCore.Signal(bool)
def __init__(self, parent=None):
QtCore.QObject.__init__(self, parent)
self.lock = QtCore.QMutex()
#QtCore.Slot()
def start(self):
self.flag_to_quit = False
try:
self.started.emit()
time.sleep(5)
time.sleep(0.5)
for i in range(10):
self.progress.emit(i)
time.sleep(1)
with QtCore.QMutexLocker(self.lock):
print(self.flag_to_quit)
if self.flag_to_quit:
raise RuntimeError("Requested to quit.")
self.finished.emit(True)
except Exception as ex:
print(ex)
self.finished.emit(False)
finally:
print("start() finished!")
##QtCore.Slot()
def stop(self):
print("Stopping...")
with QtCore.QMutexLocker(self.lock):
self.flag_to_quit = True
#QtCore.Slot()
def __quit_handler(self):
self.flag_to_quit = True
class SubWorker(Worker):
def __init__(self):
Worker.__init__(self)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.label = QtGui.QLabel("")
self.layout().addWidget(self.label)
self.button = QtGui.QPushButton("Start Thread")
self.layout().addWidget(self.button)
self.button.clicked.connect(self.clicked)
def closeEvent(self, event):
if hasattr(self, "_thread") and self._thread:
self.worker.flag_to_quit = True
self._thread.quit()
print("waiting for stop...")
self._thread.wait()
self._thread.deleteLater()
#QtCore.Slot()
def worker_started(self):
self.label.setText("Started!")
#QtCore.Slot(int)
def worker_progress(self, interval):
self.label.setText(str(interval))
#QtCore.Slot()
def worker_finished(self, success):
msg = "Finished "
msg += "successfully!" if success else "with errors"
self.label.setText(msg)
#QtCore.Slot()
def clicked(self):
if hasattr(self, "_thread") and self._thread:
self.worker.stop()
self._thread.quit()
print("waiting for stop...")
self._thread.wait()
self._thread.quit()
self._thread.deleteLater()
self._thread = QtCore.QThread()
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
self.worker.started.connect (self.worker_started, QtCore.Qt.QueuedConnection)
self.worker.progress.connect(self.worker_progress, QtCore.Qt.QueuedConnection)
self.worker.finished.connect(self.worker_finished, QtCore.Qt.QueuedConnection)
self.worker.moveToThread(self._thread)
self._thread.started.connect(self.worker.start)
self._thread.finished.connect(self.worker.stop)
self._thread.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
sys.exit()
Version information:
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
edit:
Looks like QThread::moveToThread() is failing with Subclasses. this is output of QtCore.QThread.currentThread() in various places:
#self.worker = Worker():
Main:<PySide.QtCore.QThread object at 0x7ff89df384d0>
Real thread: <PySide.QtCore.QThread object at 0x7ff89df383f8>
Worker:<PySide.QtCore.QThread object at 0x7ff89df384d0>
#self.worker = SubWorker()
Main:<PySide.QtCore.QThread object at 0x7f968602c3b0>
Real thread: <PySide.QtCore.QThread object at 0x7f968602c488>
Worker:<PySide.QtCore.QThread object at 0x7f968602c488>
Okay I managed to fix this by doing it the "Wrong" way by inheriting QThread and overriding the run method. I'm not sure why or how but it works.
from PySide import QtGui, QtCore
import sys
import time
class Worker(QtCore.QThread):
_started = QtCore.Signal()
progress = QtCore.Signal(int)
_finished = QtCore.Signal(bool)
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
self.lock = QtCore.QMutex()
#QtCore.Slot()
def run(self):
self.flag_to_quit = False
print("Worker:" + str(QtCore.QThread.currentThread()))
try:
self._started.emit()
time.sleep(2)
for i in range(10):
self.progress.emit(i)
time.sleep(1)
with QtCore.QMutexLocker(self.lock):
if self.flag_to_quit:
raise RuntimeError("Requested to quit.")
self._finished.emit(True)
except Exception as ex:
print(ex)
self._finished.emit(False)
finally:
print("start() finished!")
#QtCore.Slot()
def stop(self):
with QtCore.QMutexLocker(self.lock):
self.flag_to_quit = True
#QtCore.Slot()
def __quit_handler(self):
self.flag_to_quit = True
class SubWorker(Worker):
def __init__(self, parent=None):
Worker.__init__(self, parent)
#QtCore.Slot()
def start(self):
Worker.start(self)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.label = QtGui.QLabel("")
self.layout().addWidget(self.label)
self.button = QtGui.QPushButton("Start Thread")
self.layout().addWidget(self.button)
self.button.clicked.connect(self.clicked)
def closeEvent(self, event):
self.hide()
if hasattr(self, "worker") and self.worker:
self.worker.stop()
self.worker.quit()
self.worker.wait()
self.worker.deleteLater()
#QtCore.Slot()
def worker_started(self):
self.label.setText("Started!")
#QtCore.Slot(int)
def worker_progress(self, interval):
self.label.setText(str(interval))
#QtCore.Slot()
def worker_finished(self, success):
msg = "Finished "
msg += "successfully!" if success else "with errors!"
self.label.setText(msg)
def clicked(self):
if hasattr(self, "worker") and self.worker:
self.worker.stop()
self.worker.quit()
self.worker.wait()
self.worker.deleteLater()
print("Main:" + str(QtCore.QThread.currentThread()))
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
self.worker._started.connect (self.worker_started)
self.worker.progress.connect(self.worker_progress)
self.worker._finished.connect(self.worker_finished)
self.worker.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
sys.exit()