How to write a multi-threaded Pyside application - multithreading

What is the simplest way to multi-thread a Pyside application, so the GUI can be operational and the thread will still run?
Thread class:
class MyLongThread(QThread):
def __init__(self, parent = None):
QThread.__init__(self, parent)
self.exiting = False
def run(self):
while 1:
self.msleep(100)
print("run")
Complete .pyw
import sys,time
from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtWebKit import *
def thread():
global threade
threade = MyLongThread()
threade.run()
def thread_terminate():
global threade
threade.terminate()
class MyLongThread(QThread):
def __init__(self, parent = None):
QThread.__init__(self, parent)
self.exiting = False
def run(self):
while 1:
self.msleep(100)
print("run")
app = QApplication(sys.argv)
wid = QWidget()
wid.resize(250, 400)
wid.setWindowTitle('Threaded Program')
#wid.setWindowIcon(QIcon('web.png'))
#### BUTTONS
btn = QPushButton('Stop', wid)
btn.setToolTip('Stop the thread.') ## Stop the thread
btn.resize(btn.sizeHint())
btn.move(147, 50)
btn.clicked.connect(thread_terminate)
qbtn = QPushButton('Start', wid)
qbtn.setToolTip('Start the thread.') ## End the Thread
qbtn.resize(btn.sizeHint())
qbtn.move(27, 50)
qbtn.clicked.connect(thread)
####
#### LABEL
label = QLabel('Start The Thread :)',wid)
label.resize(label.sizeHint())
label.move(28, 15)
####
wid.show()
sys.exit(app.exec_())
When I run the code and press the start button, it freezes the gui but prints run.

Don't directly call thread.run() as this executes the method in the main thread. Instead, call thread.start() which will launch a thread and start executing your run() method in the thread.

Related

How to communicate QThread with my Widgets from main class

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

I wonder why pyqtSignal() is used. (pyqt, QThread, signal, slot)

This is the test code about QThread and Signal.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import sys
class Thread1(QThread):
set_signal = pyqtSignal(int) # (1) ##
def __init__(self, parent):
super().__init__(parent)
def run(self):
for i in range(10):
time.sleep(1)
self.set_signal.emit(i) # (3) ##
class MainWidget(QWidget):
def __init__(self):
super().__init__()
thread_start = QPushButton("시 작!")
thread_start.clicked.connect(self.increaseNumber)
vbox = QVBoxLayout()
vbox.addWidget(thread_start)
self.resize(200,200)
self.setLayout(vbox)
def increaseNumber(self):
x = Thread1(self)
x.set_signal.connect(self.print) # (2) ##
x.start()
def print(self, number):
print(number)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = MainWidget()
widget.show()
sys.exit(app.exec_())
In the example of QThread I searched for, a pyqtSignal()(step 1) object was created, the desired slot function was connected(step 2) by connect, and then called by emit()(step 3).
I don't know the difference from calling the desired method immediately without connecting the connect().
So, the goal of codes is triggering a desired function every second. You are right about it.
But if you create multiple objects, you can connect it to different desired functions. Or connect multiple function to one signal.
x = Thread1(self)
x.set_signal.connect(self.print) # (2) ##
x.start()
y = Thread1(self)
y.set_signal.connect(self.print2)
time.sleep(0.5)
y.start()

PyQt Thread Completes and outputs same message twice

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

Show Raspberry Pi GPIO readings with PyQt [duplicate]

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

Howto change progress by worker thread

I'm new to PyQt4 so maybe it is a bagatelle. I try to show a progress in my GUI, which will be updated by an worker thread.The QProgressBar is with other memory's in a QTableWidget.
The worker thread starts in the init function of my GUI.
self.st = ServerThread()
self.st.start()
Here is the thread class
_exportedMethods = {
'changes': signal_when_changes,
}
class ServerThread(QtCore.QThread):
def __init__(self):
super(ServerThread,self).__init__()
st = self
#threading.Thread.__init__(self)
def run(self):
HOST = '' # local host
PORT = 50000
SERVER_ADDRESS = HOST, PORT
# set up server socket
s = socket.socket()
s.bind(SERVER_ADDRESS)
s.listen(1)
while True:
conn, addr = s.accept()
connFile = conn.makefile()
name = cPickle.load(connFile)
args = cPickle.load(connFile)
kwargs = cPickle.load(connFile)
res = _exportedMethods[name](*args,**kwargs)
cPickle.dump(res,connFile) ; connFile.flush()
conn.close()
If my Server changes values in the database he will call the following method which will captured with a remote prozedure call in the thread.
def signal_when_changes():
s = Subject()
s.advise()
The pattern is a simple observer, which updated my GUI. To update the table in my gui is the following method called.
def refresh(self,table):
clients = self.db.get_clients()
if(self.ui.mainTable.rowCount() != len(clients)):
self.search_add_client
allRows = table.rowCount()
for row in xrange(0,allRows):
for c in clients:
if table.item(row,0).text() == c.get_macaddr().text():
self.refresh_line(table,row,c)
This method checks wheter there were changes in a row if the needs a update the following method will do this.
def refresh_line(self,table,rowNumber,client):
table.item(rowNumber, 0).setText(client.get_macaddr().text())
table.item(rowNumber, 1).setText(client.get_product().text())
table.item(rowNumber, 2).setText(client.get_site().text())
table.item(rowNumber, 3).setText(client.get_hostname().text())
table.item(rowNumber, 4).setText(client.get_priv_data().text())
table.cellWidget(rowNumber, 5).setValue(client.get_progress_value())
table.item(rowNumber, 6).setText(client.get_stage().text())
The other memory's can be updated but not the progress, here the line in which i want to update the progress
self.ui.mainTable.setCellWidget(appendRowIndex,5,c.get_progress())
After this line the GUI crashes and i get the following message
QPixmap: It is not safe to use pixmaps outside the GUI thread
My conjecture is that i can't change QPixmaps outside the "Main/Gui" thread. I don't know how i can solve this problem, so I welcome all suggestions for resolution.
Thanks in advance.
Don't try to update the progress bar from within the thread: use a signal instead.
from PyQt4 import QtCore, QtGui
class Thread(QtCore.QThread):
def __init__(self,parent):
QtCore.QThread.__init__(self, parent)
def run (self):
for step in range(5):
self.sleep(1)
self.emit(QtCore.SIGNAL('taskUpdated'))
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Start', self)
self.progress = QtGui.QProgressBar(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.progress)
self.connect(self.button, QtCore.SIGNAL('clicked()'),
self.handleButton)
self.thread = Thread(self)
self.connect(self.thread, QtCore.SIGNAL('taskUpdated'),
self.handleTaskUpdated)
def handleButton(self):
self.progress.setRange(0, 4)
self.progress.setValue(0)
self.thread.quit()
self.thread.start()
def handleTaskUpdated(self):
self.progress.setValue(self.progress.value() + 1)
def closeEvent(self, event):
self.thread.wait()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Resources