I'm new to PyQT and QThread. My PyQT program has a button which trigger a continuous writing/reading of the serial port every second.
Problem: When the button is clicked and the looping starts, the GUI freezes up. Am I using QThread wrongly?
import sys
from PyQt4.QtGui import *
from PyQt4 import QtCore
import serial
import time
from TemperatureReader import TemperatureReader # my module to talk to serial hardware
class Screen(QMainWindow):
def __init__(self):
super(Screen, self).__init__()
self.initTemperatureReader()
self.initUI()
def initTemperatureReader(self):
ser = serial.Serial(port='COM9', baudrate=115200, timeout=5)
self.temperatureReader = TemperatureReader(ser)
def initUI(self):
startReadingTCsBtn = QPushButton('Start Reading')
startReadingTCsBtn.clicked.connect(self.startReadingTCsThread)
startReadingTCsBtn.show()
directControlBoxLayout = QVBoxLayout()
directControlBoxLayout.addWidget(startReadingTCsBtn)
self.mainFrame = QWidget()
mainLayout = QVBoxLayout()
mainLayout.addWidget(directControlGroupBox)
self.mainFrame.setLayout(mainLayout)
self.setCentralWidget(self.mainFrame)
self.setGeometry(300,300,400,150)
self.show()
def startReadingTCsThread(self):
self.tcReaderThread = TCReaderThread(self.temperatureReader)
self.tcReaderThread.temperatures.connect(self.onTemperatureDataReady)
self.tcReaderThread.start()
def onTemperatureDataReady(self, data):
print data
class TCReaderThread(QtCore.QThread):
temperatures = QtCore.pyqtSignal(object)
def __init__(self, temperatureReader):
QtCore.QThread.__init__(self)
self.temperatureReader = temperatureReader
def run(self):
while True:
resultString = self.temperatureReader.getTemperature() # returns a strng
self.temperatures.emit(resultString)
time.sleep(1)
def main():
app = QApplication(sys.argv)
screen = Screen()
app.exec_()
if __name__ == '__main__':
main()
Related
I'm trying to change style of button during action.
When button is pressed, I want to make them blue, then read rfid card and then by the ID change color of button to green or red (in code is just red to make it easier).
The problem is that changing style of button to blue do nothing (green and red works fine).
It is waiting to finish 'clicked()' method?
How to tell to PyQt5 "Do it now!" ?
Edit: I modified the pseudocode to reproducible Example.
#!/usr/bin/python3
import sys
import time
from PyQt5.QtWidgets import *
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black") # set default style
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
uid = self.readCard() # reading ID from rfid card
self.changeButtonColor("red")
def readCard(self):
#return rfid.read() # in real case
time.sleep(2)
return "12345678"
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need to bother with a timerEvent or create a local QTimer object.
import sys
import time
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
Update
import sys
import time
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
class Worker(QtCore.QObject): # +++
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
data = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.running = False
self.stop = 5
#QtCore.pyqtSlot()
def read_data_from_sensor(self):
self.started.emit()
time.sleep(1) # We simulate the blocking process
while self.running and self.stop:
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.data.emit(dt)
time.sleep(1) # We simulate the blocking process
self.stop -= 1
self.finished.emit()
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT Start")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
self.grid = QGridLayout(self)
self.grid.addWidget(self.label_data)
self.grid.addWidget(self.button)
self.show()
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self._worker = Worker()
self._worker.started.connect(self.on_started)
self._worker.finished.connect(self.on_finished)
self._worker.data.connect(self.update_label)
self._thread = QtCore.QThread(self)
self._thread.start()
self._worker.moveToThread(self._thread)
#QtCore.pyqtSlot()
def on_started(self):
self.label_data.setText("Start to read")
self.button.setText("TEXT Stop")
self.button.setEnabled(True)
self._worker.stop = 5
#QtCore.pyqtSlot()
def on_finished(self):
self.label_data.setText("Pending")
self.button.setText("TEXT Start")
self.button.setEnabled(True)
self.changeButtonColor("red") # <---
self._worker.running = False
self._worker.stop = 5
#QtCore.pyqtSlot(str)
def update_label(self, data):
self.label_data.setText(data)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
# QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
if self._worker.running:
self._worker.running = False
else:
self._worker.running = True
QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
self.button.setEnabled(False)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
install quamash and qtawesome
#!/usr/bin/python3
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize
import asyncio
from quamash import QEventLoop
import qtawesome as qt
class MainScreen(QWidget):
oop = None
def __init__(self, _loop):
try:
QWidget.__init__(self)
self.loop = _loop
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.synced_click)
self.button.setStyleSheet("color: black;")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
except Exception as e:
print(e)
def synced_click(self):
try:
asyncio.ensure_future(self.clicked(), loop=self.loop)
except Exception as e:
print('error')
print(e)
#asyncio.coroutine
async def clicked(self):
try:
spin_icon = qt.icon('fa5s.spinner', color='red', animation=qt.Spin(self.button))
self.loop.call_soon_threadsafe(self.button.setIconSize, QSize(12, 12))
self.loop.call_soon_threadsafe(self.button.setIcon, spin_icon)
self.loop.call_soon_threadsafe(self.button.setText, "progress")
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color: red;")
await asyncio.sleep(3)
tick_icon = qt.icon('fa5s.check-circle', color='blue')
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color:blue;")
self.loop.call_soon_threadsafe(self.button.setText, "done")
self.loop.call_soon_threadsafe(self.button.setIcon, tick_icon)
await asyncio.sleep(1)
except Exception as e:
print('error')
print(e)
if __name__ == "__main__":
import sys
loop = QEventLoop(QApplication(sys.argv))
asyncio.set_event_loop(loop=loop)
with loop:
window = MainScreen(loop)
loop.run_forever()
I have an application on pyqt5. And if I click on button time lcd display start to countdown with thread I created. After the countdown finished I cant use the same thread for the same action. How can I terminate that thread and start it again with clicking button
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
self.button1=QPushButton(self)
self.lcd=QLCDNumber(self)
self.tbutton1=threading.Thread(target=self.timing)
def initUI(self):
self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1.start()
self.lcd.setVisible(True)
timing=self.spin.value()
for i in range(timing,-1,-1):
time.sleep(1)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
self.lcd.setVisible(False)
if __name__=='__main__':
app=QApplication(sys.argv)
win=window()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.button1 = QPushButton("Start to countdown", self)
self.lcd = QLCDNumber(self)
self.spin = QSpinBox(self)
grid = QGridLayout(centralWidget)
grid.addWidget(self.lcd)
grid.addWidget(self.spin)
grid.addWidget(self.button1)
self.initUI()
def initUI(self):
# self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1 = threading.Thread(target=self.timingThread)
self.tbutton1.start()
# self.lcd.setVisible(True)
def timingThread(self):
timing=self.spin.value()
for i in range(timing, -1, -1):
# time.sleep(1)
QtCore.QThread.msleep(1000)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
# self.lcd.setVisible(False)
if __name__=='__main__':
app = QApplication(sys.argv)
win = window()
win.show()
sys.exit(app.exec_())
I'm trying to write a pyqt5 application with a long running, but not CPU intensive process. I'd like to be able to run it without hanging the UI, so I'm trying to use threading, but since it doesn't seem like I can just run a thread and have it stop after its gone through its code so that it can be run again, I've tried setting up the thread to wait for a variable to change before running.
I know this can't be the correct pattern for running long processes in a pyqt app.
import time
import threading
from PyQt5 import QtWidgets, uic
class MyApp(QtWidgets.QMainWindow):
_run_thread = False
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi('myapp.ui', self)
self.ui.start_thread_button.clicked.connect(self._run_thread_function)
self._thread = threading.Thread(target=self._run_thread_callback)
self._thread.daemon = True
self._thread.start()
self.ui.show()
def _run_thread_callback(self):
while True:
if self._run_thread:
print("running thread code...")
time.sleep(10)
print("thread code finished")
self._run_thread = False
def _run_thread_function(self):
print("starting thread...")
self._run_thread = True
def main():
app = QtWidgets.QApplication(sys.argv)
MyApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a simple demo showing how to start and stop a worker thread, and safely comminucate with the gui thread.
import sys
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QThread):
dataSent = QtCore.pyqtSignal(dict)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self._stopped = True
self._mutex = QtCore.QMutex()
def stop(self):
self._mutex.lock()
self._stopped = True
self._mutex.unlock()
def run(self):
self._stopped = False
for count in range(10):
if self._stopped:
break
self.sleep(1)
data = {
'message':'running %d [%d]' % (
count, QtCore.QThread.currentThreadId()),
'time': QtCore.QTime.currentTime(),
'items': [1, 2, 3],
}
self.dataSent.emit(data)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.edit = QtWidgets.QPlainTextEdit()
self.edit.setReadOnly(True)
self.button = QtWidgets.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.edit)
layout.addWidget(self.button)
self._worker = Worker()
self._worker.started.connect(self.handleThreadStarted)
self._worker.finished.connect(self.handleThreadFinished)
self._worker.dataSent.connect(self.handleDataSent)
def handleThreadStarted(self):
self.edit.clear()
self.button.setText('Stop')
self.edit.appendPlainText('started')
def handleThreadFinished(self):
self.button.setText('Start')
self.edit.appendPlainText('stopped')
def handleDataSent(self, data):
self.edit.appendPlainText('message [%d]' %
QtCore.QThread.currentThreadId())
self.edit.appendPlainText(data['message'])
self.edit.appendPlainText(data['time'].toString())
self.edit.appendPlainText(repr(data['items']))
def handleButton(self):
if self._worker.isRunning():
self._worker.stop()
else:
self._worker.start()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 100, 400, 400)
window.show()
sys.exit(app.exec_())
I'm new to python and pyqt.
I'm learning how to use threading with GUI.
I followed this tutorial
http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import threading
from _ast import While
class Communicate(QObject):
signal = pyqtSignal(int, str)
class My_Gui(QWidget):
def __init__(self):
super().__init__()
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.initUI()
def initUI(self):
btn_count = QPushButton('Count')
btn_count.clicked.connect(self.start_counting)
self.te = QTextEdit()
vbox = QVBoxLayout()
vbox.addWidget(btn_count)
vbox.addWidget(self.te)
self.setLayout(vbox)
self.setWindowTitle('MultiThreading in PyQT5')
self.setGeometry(400, 400, 400, 400)
self.show()
def count(self, comm):
'''
for i in range(10):
data = "Data "+str(i)
comm.signal.emit(i, data)
'''
i = 0
while True:
data = "Data "+str(i)
comm.signal.emit(i, data)
i+=1
def start_counting(self):
my_Thread = threading.Thread(target=self.count, args=(self.comm,))
my_Thread.start()
def append_data(self, num, data):
self.te.append(str(num) + " " + data)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_gui = My_Gui()
sys.exit(app.exec_())
I changed the for loop to infinite while loop(incrementing the 'i').
If I execute the program, the GUI still hangs but if I remove the emit signal inside the loop, it no longer hangs.
Are there some tricks to make it not hangs?
while True makes an endless loop in the background
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import threading
from _ast import While
class Communicate(QObject):
signal = pyqtSignal(int, str)
class My_Gui(QWidget):
def __init__(self):
super().__init__()
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.initUI()
def initUI(self):
btn_count = QPushButton('Count')
btn_count.clicked.connect(self.start_counting)
self.te = QTextEdit()
vbox = QVBoxLayout()
vbox.addWidget(btn_count)
vbox.addWidget(self.te)
self.setLayout(vbox)
self.setWindowTitle('MultiThreading in PyQT5')
self.setGeometry(400, 400, 400, 400)
self.show()
def count(self, comm):
for i in range(10):
data = "Data "+str(i)
comm.signal.emit(i, data)
# While True below will never stop and cause your program to stuck
'''
i = 0
while True:
data = "Data "+str(i)
comm.signal.emit(i, data)
i+=1
'''
def start_counting(self):
my_Thread = threading.Thread(target=self.count, args=(self.comm,))
my_Thread.start()
def append_data(self, num, data):
self.te.append(str(num) + " " + data)
if __name__ == '__main__':
app = QApplication(sys.argv)
my_gui = My_Gui()
sys.exit(app.exec_())
I think you are getting downvoted for two things:
you did only copy and paste the code from the tutorial
you didn't read the tutorial
In the tutorial, the author states:
In the above example, we have created a QPushbutton and QTextEdit. When the button is clicked it creates a new Thread that counts from 0 to 9 and emits the signal in order to append the number and data in the QTextEdit. In class Communicate signal is initialized as pyqtSignal(int, str). This int and str means when a signal will be emitted it will also pass two arguments the first one will be of Integer type and second one will be of String type.
By changing the loop to while true you continuosly emit signals and append the text in the QTextEdit. Probably not what you want.
Also commenting the emit statement internally continues to run the while loop.
I'm developing some programe in python.
Now i can open a second window but now i will go back to my first window.
My problem is when i push the close button my first and second window close.
And i will that only my second window closed.
My code:
import sys
from PyQt4 import QtCore, QtGui
from forms.runtime_form import Ui_runtime_form
import datetime
import time
"""
The main class
First window
"""
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
self.run_mode = 0
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_runtime_form()
self.ui.setupUi(self)
self.main_thread = main_thread(self)
self.main_thread.start()
self.main_thread.update_date_time.connect(self.update_date_time)
if self.run_mode == 0:
Uit = QtGui.QPushButton('Uit', self)
Uit.move(20,650)
Uit.clicked.connect(QtCore.QCoreApplication.instance().quit)
#button to open second window
bla = QtGui.QPushButton('bla', self)
bla.move(150,650)
bla.clicked.connect(self.child)
#open second window
def child(self):
test = Example(self)
test.showFullScreen()
def update_date_time(self):
#some other code
#Second window
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
Uit = QtGui.QPushButton('Uit', self)
Uit.clicked.connect(self.sluit)
Uit.move(150,50)
self.setGeometry(300, 300, 500, 150)
self.setWindowTitle('Modbus Domotica - Touchscreen')
self.show()
#Close second window
def sluit(self):
sys.exit(0)
class main_thread(QtCore.QThread):
update_date_time = QtCore.pyqtSignal()
def __init__(self, layout):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
while True:
self.update_date_time.emit()
time.sleep(10)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
QtGui.QApplication.setPalette(QtGui.QApplication.style().standardPalette())
myapp = StartQT4()
myapp.showFullScreen()
sys.exit(app.exec_())
Of course both windows close, you are exiting Python!
#Close second window
def sluit(self):
sys.exit(0)
Instead, try -
def sluit(self):
self.close()