My compile environment is PyQt4,Qt4,Python2.In my code,there is a Signal:
class ReadThread(QtCore.QThread):
#always read UART RX pin
def __init__(self,parent=None):
QtCore.QThread.__init__(self,parent)
self.trigger=QtCore.pyqtSignal()#creat a signal
def run(self):
#thread stop when the "run" function is over
for i in range (25536):
pass
self.trigger.emit()
And there is a Slot() in Class ChatDialog.
class ChatDialog(QtGui.QDialog):
#dialog contain two widget-"recived"and"send"
def __init__(self,parent=None):
QtGui.QDialog.__init__(self,parent)
self.ui=Ui_Dialog()
self.ui.setupUi(self)
#QtCore.pyqtSlot()
def print_slot():
print "reciece str"
I write the __main__like this:
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
myqq=ChatDialog()
myqq.show()
read=ReadThread()
read.trigger.connect(myqq.print_slot,QueuedConnection)
read.start()
sys.exit(app.exec_())
But my "read.trigger.connect(myqq.print_slot,QueuedConnection)" is wrong.How can I connect the Signal and the Slot?Thanks
Signals have to be defined as a class attributes. You cannot create signals on instances.
class ReadThread(QtCore.QThread):
trigger = QtCore.pyqtSignal()
def __init__(self,parent=None):
QtCore.QThread.__init__(self,parent)
Related
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()
I have an app that starts the GUI and performs "the heavy part" of the code in another thread using QThread. In this thread I emit a SIGNAL that is connected to the GUI Class and that performs addItem on QListWidget.
There are a massive "signaling" from this thread to the GUI and it "freeze".
Is there a way to avoid this? Have I to use another mini GUI in different thread only for QListWidget?
Thanks
EDIT:
This is the thread that execute the heavy logic
class YourThreadName(QThread):
def __init__(self, some variables):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# Here there is a for cycle that emits a SIGNAL
for ... :
...
self.emit(SIGNAL("needed_variable"), needed_variable)
...
In the GUI Class there are some methods, particularly:
class GUI(QtGui.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(GUI, self).__init__(parent)
self.setupUi(self)
def ... (self):
...
def start_main_code(self):
self.new_thread = YourThreadName(some variables)
self.connect(self.new_thread, SIGNAL("finished()"), self.done)
self.connect(self.new_thread, SIGNAL("needed_variable"), self.show_variable)
self.new_thread.start()
def show_variable(self, data):
self.QListWidget_object.addItem(data)
def ... (self):
...
The script below is a Minimal, Complete, and Verifiable Example based on the information currently given in your question and comments. It emits data from a worker thread every 10ms and updates a list-widget in the GUI. On my Linux system (using Python-3.6.3, Qt-4.8.7 and PyQt-4.12.1) it does not block or freeze the GUI. There is obviously some flickering whilst the list-widget is being updated, but I am able to select items, scroll up and down, click the button, etc. And if I increase the sleep to 25ms, I don't even get any flickering.
UPDATE:
The performance can be improved by using setUniformItemSizes and sending the messages in batches. On my system, after a slight initial delay, the list populates with fifty thousand items almost instantly.
import sys
from PyQt4 import QtCore, QtGui
class Worker(QtCore.QThread):
message = QtCore.pyqtSignal(object)
def run(self):
batch = []
for index in range(50000):
if len(batch) < 200:
batch.append(index)
continue
self.message.emit(batch)
batch = []
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.listWidget = QtGui.QListWidget()
self.listWidget.setUniformItemSizes(True)
self.button = QtGui.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.listWidget)
layout.addWidget(self.button)
self.worker = Worker()
self.worker.message.connect(self.handleMessages)
def handleMessages(self, batch):
for message in batch:
self.listWidget.addItem('Item (%s)' % message)
def handleButton(self):
if not self.worker.isRunning():
self.worker.start()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 200, 400)
window.show()
sys.exit(app.exec_())
I have a pyqt GUI and a method[BigramClassification()]which causes the GUI to hang for several seconds. Therefore, i figured out threading need to be used. So after reading several tutorials i came up with the following code.
import sys,os
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QThread
import time
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.lblHistory.setPixmap(QtGui.QPixmap(os.getcwd() + "/historygraph.png"))
self.workerThread=WorkingThread()
self.ui.pushButton.clicked.connect(self.generateDetails)
self.ui.btnsubmitsettings.clicked.connect(self.addDetails)
def generateDetails(self):
self.workerThread.start()
self.ui.lblHistory.setPixmap(QtGui.QPixmap(os.getcwd() + "/historygraph.png"))
self.addPiechart()
self.addWordCloud()
self.summaryText()
def addPiechart(self):
print ("Added")
def addWordCloud(self):
print ("Added")
def addDetails(self):
def summaryText(self):
print("Added")
class WorkingThread(QThread):
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
def run(self):
BigramsClassifier()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
The problem i have is when i run this and click on pushButton the thread starts but also executes the methods after the start() as seen in def generateDetails(self): I need to prepare this code so that the methods in def generateDetails(self): executes after the thread is done executing with the heavy method BigramClassification()execution.
Summary How can i stop the auto execution of the methods in def generateDetails(self): but only after the method BigramClassification() is completed.
EDIT This error is thrown up when i try to close the GUI.
Connect a slot to the thread's finished signal which can performs other actions once the long-running task is completed:
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.workerThread = WorkingThread()
self.workerThread.finished.connect(self.doOtherStuff)
...
def generateDetails(self):
if not self.workerThread.isRunning():
self.workerThread.start()
def doOtherStuff(self):
self.ui.lblHistory.setPixmap(QtGui.QPixmap(os.getcwd() + "/historygraph.png"))
self.addPiechart()
self.addWordCloud()
self.summaryText()
In the following code I try to deal with QThread. In this executable example there are three buttons: first for start, second for stop and third for close. Well, when I start the task its runs like a charm. BUT when I want the while-loop to stop I click on the stop-button. And now, there is a problem: the while-loop doesn't stop.
You see, the stop-button emits a signal to call the stop() method on TestTask().
What is wrong?
from sys import argv
from PyQt4.QtCore import QObject, pyqtSignal, QThread, Qt, QMutex
from PyQt4.QtGui import QDialog, QApplication, QPushButton, \
QLineEdit, QFormLayout, QTextEdit
class TestTask(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
self._mutex = QMutex()
self._end_loop = True
def init_object(self):
while self._end_loop:
print "Sratus", self._end_loop
def stop(self):
self._mutex.lock()
self._end_loop = False
self._mutex.unlock()
class Form(QDialog):
stop_loop = pyqtSignal()
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.init_ui()
def init_ui(self):
self.pushButton_start_loop = QPushButton()
self.pushButton_start_loop.setText("Start Loop")
self.pushButton_stop_loop = QPushButton()
self.pushButton_stop_loop.setText("Stop Loop")
self.pushButton_close = QPushButton()
self.pushButton_close.setText("Close")
layout = QFormLayout()
layout.addWidget(self.pushButton_start_loop)
layout.addWidget(self.pushButton_stop_loop)
layout.addWidget(self.pushButton_close)
self.setLayout(layout)
self.setWindowTitle("Tes Window")
self.init_signal_slot_pushButton()
def start_task(self):
self.task_thread = QThread(self)
self.task_thread.work = TestTask()
self.task_thread.work.moveToThread(self.task_thread)
self.task_thread.started.connect(self.task_thread.work.init_object)
self.stop_loop.connect(self.task_thread.work.stop)
self.task_thread.start()
def stop_looping(self):
self.stop_loop.emit()
def init_signal_slot_pushButton(self):
self.pushButton_start_loop.clicked.connect(self.start_task)
self.pushButton_stop_loop.clicked.connect(self.stop_looping)
self.pushButton_close.clicked.connect(self.close)
app = QApplication(argv)
form = Form()
form.show()
app.exec_()
The stop_loop signal is converted to an event and sent to the thread of the signal receiver. But your worker object is running a blocking while-loop, and this prevents the thread processing any pending events in its event-queue. So the slot connected to the stop_loop signal will never be called.
To work around this, you can call processEvents in the while-loop to allow the thread to process its pending events:
def init_object(self):
while self._end_loop:
QThread.sleep(1)
QApplication.processEvents()
print "Status", self._end_loop
Alternatively, you could call the worker's stop() method directly instead of emitting a signal. Strictly speaking, this is not thread-safe, but it would only be a problem if multiple threads could call stop() at the same time. So a more correct way to do that is to use a mutex to protect the value being changed:
class TestTask(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
self._mutex = QtCore.QMutex()
self._end_loop = True
def stop(self):
self._mutex.lock()
self._end_loop = False
self._mutex.unlock()
And now calling stop() directly from the main thread is thread-safe.
I have the following piece of example code of my problem. Running this, I would expect that (if you type something in the lineedit) the A.updateValue slot would be called twice and thus show 'a.updatevalue called' and 'a2.updatevalue called'
However, it is only called once, namely for the self.a2 object and not for the self.a object, the latter which is sent from a worker thread to the GUI thread. How can I fix this so that this piece of code also triggers the slot for the self.a object?
Thank you,
David
import os, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class A(QObject):
def __init__(self, name):
QObject.__init__(self)
self.name = name
def updateValue(self, value):
print(self.name + ".updatevalue called")
class workerthread(QThread):
def __init__(self, parent=None):
QThread.__init__(self, parent)
def run(self):
a = A('a')
QObject.emit(self, SIGNAL("mySignal"), a)
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.centralwidget = QWidget(self)
self.hbox = QHBoxLayout()
self.centralwidget.setLayout(self.hbox)
def update(self, a):
self.a = a
edit = QLineEdit("", self)
self.hbox.addWidget(edit)
edit.textChanged.connect(self.a.updateValue)
self.a2 = A('a2')
edit.textChanged.connect(self.a2.updateValue)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = Main()
worker = workerthread()
worker.connect(worker, SIGNAL('mySignal'), gui.update)
worker.start()
gui.show()
sys.exit(app.exec_())
Define a when you initialize workerThread
class workerthread(QThread):
def __init__(self, parent=None):
QThread.__init__(self, parent)
self.a = A('a')
def run(self):
QObject.emit(self, SIGNAL("mySignal"), self.a)