Howto change progress by worker thread - multithreading

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

Related

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

a serial port thread in pyQT5

The following is my code. I am trying to create a serial port thread in my pyQt5 GUI. I checked many examples. This is very helpful one Background thread with QThread in PyQt
I think I made a successful connection but I am confused about the signature.
SelectedItem is the selected port. The GUI is still freezing because of while loop.
I need some help. Thanks!
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# vim:fileencoding=utf-8
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from TTP_Monitor_GUI import Ui_MainWindow
import serial
import serial.tools.list_ports
import binascii
import glob
class SerialPort(QObject):
def __init__(self, parent = None):
super(SerialPort, self).__init__(parent)
# Explicit signal
newParams = pyqtSignal(str)
#pyqtSlot(str)
def ReadSerialPort(self, port):
#initialization and open the port
with serial.Serial(port, 115200, timeout=1) as ser:
print ("Starting up")
while True:
readOut = 0 #chars waiting from laser range finder
#readOut = ser.readline().decode('ascii')
readOut = ser.read(268) # Reads # Bytes
r = binascii.hexlify(readOut).decode('ascii')
print(r)
self.newParams.emit(r)
#ser.flush() #flush the buffer
if ser.isOpen() == False:
print("Serial Port is Close")
class Main(QMainWindow):
# Explicit Signal
#ser = pyqtSignal(str)
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
#QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btnRefresh.clicked.connect(self.Refresh)
self.ui.btnConnect.clicked.connect(self.Connect)
self.ListFunction(self.SerialPorts())
# Implicit Slot
def Connect(self):
Items = self.ui.listMain.selectedItems()
if len(Items) != 1:
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setWindowTitle("BE CAREFULL!")
msg.setText("SELECT ONLY 1 ITEM!...")
msg.exec_()
else:
SelectedItem = Items[0].text()
SelectedItem = SelectedItem.split(" ")[0]
if sys.platform.startswith('win') and SelectedItem[:3] != 'COM':
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setWindowTitle("BE CAREFULL!")
msg.setText("THERE IS A MISTAKE!...")
msg.exec_()
return
# Read Selected Serial Port
self.serialThread = QThread()
self.ser = SerialPort()
self.ser.moveToThread(self.serialThread)
self.ser.newParams.connect(self.serialThread.quit)
#self.serialThread.started.connect(self.ser.ReadSerialPort(SelectedItem))
self.serialThread.started.connect(lambda port=SelectedItem: self.ser.ReadSerialPort(port))
self.serialThread.start()
def Refresh(self):
""" Refresh List Widget """
self.ui.listMain.clear()
if self.ui.radioSerialPorts.isChecked() == True:
self.ListFunction(self.SerialPorts())
elif self.ui.radioTCP.isChecked() == True:
pass
def ListFunction(self, items):
""" Lists items into List Widget """
if type(items) == list and type(items[0]) == str:
#qApp.processEvents() # See Changes on GUI
self.ui.listMain.addItems(items)
else:
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setWindowTitle("BE CAREFULL!")
msg.setText("ITEMS ARE WRONG!...")
msg.exec_()
def SerialPorts(self):
""" Lists serial port names """
device = []
description = []
result = []
for port in serial.tools.list_ports.comports():
device.append(port.device)
description.append(port.description)
result.append(port.device + ' - ' + port.description)
return result
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
myapp = Main()
myapp.show()
sys.exit(app.exec_())
provide delay function time.sleep(1) at thread function ReadSerialPort(self, port) inside the while loop äfter the function self.newParams.emit(r).
pyqt UI does not gets sufficient time to execute. so provide delay. it will work.

How do I thread a single method on a PyQt GUI so that the rest of the GUI is still accessible?

Basically, when the Switch Button is pressed, which is connected to the reconfigure method, I want everything in the reconfigure method to run as a separate thread/process so the main GUI is still accessible and not being blocked. Below is a watered down version of my code.
import sys, time
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import *
#//Popup Class - Will appear when the Switch Button is pressed
class Popup(QWidget):
def __init__(self):
QWidget.__init__(self)
#//nothing here now, it will have a message telling user to wait while program is run
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
label_header = QtGui.QLabel("TEST RECONFIGURE")
font = label_header.font()
font.setPointSize(24)
label_header.setFont(font)
#//Creating Static Labels that will be placed in the GUI
label_1 = QtGui.QLabel("Menu 1:")
label_2 = QtGui.QLabel("Menu 2:")
label_spacer = QtGui.QLabel("")
label_cfg = QtGui.QLabel("Current Configuration: '/tmp/directory_here' ")
global comboBox1
comboBox1 = QtGui.QComboBox()
comboBox1.addItem("1")
comboBox1.addItem("2")
global comboBox2
comboBox2 = QtGui.QComboBox()
comboBox2.addItem("3")
comboBox2.addItem("4")
#//Switch Button!!!
global switchButton
switchButton = QPushButton("Switch")
switchButton.clicked.connect(self.reconfigure)
quitButton = QtGui.QPushButton('Quit')
quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
#//Configure the grid layout
grid.addWidget(label_spacer, 0,0,9,9)
grid.addWidget(label_header, 0,0,1,6)
grid.addWidget(label_1, 1,0,1,1)
grid.addWidget(comboBox1, 2,0,1,1)
grid.addWidget(label_2, 3,0,1,1)
grid.addWidget(comboBox2, 4,0,1,1)
grid.addWidget(switchButton, 5,0,1,2)
grid.addWidget(label_cfg, 6,0,1,9)
grid.addWidget(quitButton, 9,9,1,1)
self.setLayout(grid)
self.setGeometry(640,300,400,600)
self.show()
#//open up the popup window for switch button, and reconfigure
def reconfigure(self):
print "Opening New Window"
self.w = Popup()
self.w.setGeometry(QRect(self.x()+100,self.y()+100,400,200))
self.w.show()
txt1 = str(comboBox1.currentText())
txt2 = str(comboBox2.currentText())
print " reconfiguring to option %s and option %s" %(txt1, txt2)
#
# This is where most of the work is done, and takes about 1/2 an hour for everything to run
# Want to make this method a separate thread/process so the rest of the main GUI is still accessible
# while the program is running as the whole class will be a separate tab in a larger GUI
#
print "all done!"
#//runner
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I feel like I have to use threading and signals to accomplish this, but I am not having much luck. Any help would be greatly appreciated. Thank you!
import sys, time
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import *
class ConfigureThread(QtCore.QThread):
def run(self):
pass
#
# This is where most of the work is done, and takes about 1/2 an hour for everything to run
# Want to make this method a separate thread/process so the rest of the main GUI is still accessible
# while the program is running as the whole class will be a separate tab in a larger GUI
#
#//Popup Class - Will appear when the Switch Button is pressed
class Popup(QWidget):
def __init__(self):
QWidget.__init__(self)
#//nothing here now, it will have a message telling user to wait while program is run
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
label_header = QtGui.QLabel("TEST RECONFIGURE")
font = label_header.font()
font.setPointSize(24)
label_header.setFont(font)
#//Creating Static Labels that will be placed in the GUI
label_1 = QtGui.QLabel("Menu 1:")
label_2 = QtGui.QLabel("Menu 2:")
label_spacer = QtGui.QLabel("")
label_cfg = QtGui.QLabel("Current Configuration: '/tmp/directory_here' ")
global comboBox1
comboBox1 = QtGui.QComboBox()
comboBox1.addItem("1")
comboBox1.addItem("2")
global comboBox2
comboBox2 = QtGui.QComboBox()
comboBox2.addItem("3")
comboBox2.addItem("4")
#//Switch Button!!!
global switchButton
switchButton = QPushButton("Switch")
switchButton.clicked.connect(self.reconfigure)
quitButton = QtGui.QPushButton('Quit')
quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
#//Configure the grid layout
grid.addWidget(label_spacer, 0,0,9,9)
grid.addWidget(label_header, 0,0,1,6)
grid.addWidget(label_1, 1,0,1,1)
grid.addWidget(comboBox1, 2,0,1,1)
grid.addWidget(label_2, 3,0,1,1)
grid.addWidget(comboBox2, 4,0,1,1)
grid.addWidget(switchButton, 5,0,1,2)
grid.addWidget(label_cfg, 6,0,1,9)
grid.addWidget(quitButton, 9,9,1,1)
self.setLayout(grid)
self.setGeometry(640,300,400,600)
self.show()
#//open up the popup window for switch button, and reconfigure
def reconfigure(self):
print("Opening New Window")
self.w = Popup()
self.w.setGeometry(QRect(self.x()+100,self.y()+100,400,200))
self.w.show()
txt1 = str(comboBox1.currentText())
txt2 = str(comboBox2.currentText())
print(" reconfiguring to option %s and option %s" %(txt1, txt2))
self.th = ConfigureThread()
self.th.finished.connect(self.configuring_done)
self.th.start()
def configuring_done(self):
print("all done!")
#//runner
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

How to write a multi-threaded Pyside application

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.

PySide: Threading causes GUI to crash

I'm trying to learn the basics of threading with PySide, and so put together the below code. What I'm trying to do is launch a thread that will update a QPlainTextEdit widget using a list of string, with a delay between each string. Instead what I'm getting is a crash to desktop, and I can't understand why:
import sys
import time
from PySide import QtCore, QtGui
class Worker(QtCore.QThread):
to_log = QtCore.Signal(str)
def __init__(self, txt, parent=None):
super(Worker, self).__init__(parent)
self.txt = txt
def run(self):
for i in self.txt:
self.to_log.emit(i)
time.sleep(1)
class TestThreadsApp(QtGui.QWidget):
def __init__(self):
super(TestThreadsApp, self).__init__()
self.initUI()
def initUI(self):
self.log = QtGui.QPlainTextEdit()
self.pb = QtGui.QPushButton('Go')
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.log)
hbox.addWidget(self.pb)
self.setLayout(hbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test')
self.show()
self.pb.clicked.connect(self.get_worker)
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
def to_log(self, txt):
self.log.appendPlainText(txt)
def main():
app = QtGui.QApplication(sys.argv)
ex = TestThreadsApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If I update the get_worker() method to the below, it will run, but the QPlainTextEdit widget is updated with all the strings simultaneously, where as the behavior I'm wanting is for the widget to be updated by the threaded processes as each string is emitted - not altogether after both have been emitted:
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
while not worker.isFinished():
pass
You need to keep a reference to the thread, otherwise it will be garbage-collected as soon as get_worker returns.
So do something like this, instead:
def get_worker(self):
self.worker = Worker(['This is a test', 'to understand threading'])
self.worker.to_log.connect(self.to_log)
self.worker.start()

Resources