Stop a Qthread PyQt5 - python-3.x

I've tried several methods to stop a Qthread, but none seem to work. When I look at the Call Stack, the thread has not ended, and while click again, another thread starts, thus getting infinite active threads and consuming more unnecessary memory. I tried to put terminate() and quit() and nothing stops the thread. So I also tried all of them together and no result
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
class MyThread(QThread):
def run(self):
x=0
print("Starting...")
while x<5:
time.sleep(1.5)
print("Running...")
x=x+1
def close(self):
self.isRunning=False
self.terminate()
self.wait()
self.quit()
self.wait()
print("Done")
class Ui_MainWindow(QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
self.worker=MyThread()
self.worker.setTerminationEnabled(True)
self.worker.finished.connect(self.worker.close)
self.worker.start()
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())

I managed to solve my problem using threading native library and with pyqtSignal. When clicking on the button, it loses its connection and starts the thread. After the end of the loop in the thread, a signal is issued and thus returning the connection to the button. This way, it prevents the user from clicking the button several times.
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import pyqtSignal
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
import threading
class Ui_MainWindow(QMainWindow):
signal=pyqtSignal(str)
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.signal.connect(self.signalResponse)
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
print("Starting...")
self.btn.disconnect()
worker=threading.Thread(target=self.worker)
worker.start()
def signalResponse(self,response):
if response=="Finished":
self.btn.clicked.connect(self.doSomething)
print(response)
def worker(self):
x=0
while x<5:
time.sleep(0.5)
print("Running...")
x=x+1
self.signal.emit("Finished")
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())

Related

How to use a pyqtsignal to transmit a window which retains its function?

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

How Can I stop pytts3 connected with thread through a button in pyqt5?

I use pyttsx3 engine to convert text to speech. And when I click 'start' I create a thread with I target set to 'self.start' so the app doesn't freeze while speaking. And I want to STOP the thread and therefore the engine will stop automaticly. And I tried to rasie exception to kill the thread but it was so useless for my case...
The main qustion here is 'I want to kill the thread so I can stop the engine when clicking 'end' button'
my code:
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import sys
import time
import threading
import pyttsx3
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(self.create_a_thread)
self.btn2 = QPushButton('end', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(self.end)
self.engine = pyttsx3.init()
self.rate = self.engine.getProperty('rate')
self.engine.setProperty('rate', 75)
def start(self):
self.engine.say('I want to stop this thread')
self.engine.runAndWait()
def create_a_thread(self):
thread = threading.Thread(target=self.start, daemon=True)
thread.start()
def end(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())

PyQt5 windows freeze when terminating a thread

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.

PyQt5 all button signals/events?

The signal for MOUSE1 on the button is widget.clicked, what are the ones for MOTION and MOUSE2? Also if anyone knows a site with all signals listed it would really help
import sys, pyautogui
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def pressed_mouse2():
pass
def window():
app = QApplication(sys.argv)
root = QMainWindow()
root.setGeometry(200, 200, 500, 500)
root.setWindowTitle('Test')
root.setWindowFlags(QtCore.Qt.FramelessWindowHint)
root.setAttribute(Qt.WA_TranslucentBackground)
button = QtWidgets.QPushButton(root)
#i need here to signal when user has pressed on MOUSE2 on the button
button.clicked.connect(clicked)
button.move(50,50)
root.show()
sys.exit(app.exec_())
window()
You just need to connect the button's clicked signal to your function.
button.clicked.connect(pressed_mouse2)
Now when you click the button you can execute any code here:
def pressed_mouse2():
print('Button clicked')
There are many kinds of widgets, each with different signals. You can find them in the Qt documentation. Here are the signals for QAbstractButton, which is inherited by QPushButton.
There is no predefined signal for a right click on the button, but you can subclass QPushButton and emit your own signal in the mousePressEvent().
class Button(QPushButton):
right_clicked = pyqtSignal()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def mousePressEvent(self, event):
super().mousePressEvent(event)
if event.button() == Qt.RightButton:
self.right_clicked.emit()
And it will respond with:
button = Button(root)
button.right_clicked.connect(pressed_mouse2)

My gui doesn't appear when I use while loop as a startup function in pyqt and python

I want to use startup function which should have while loop.
but I run the code my gui doesn't appear until while loop ends.
I tried with self.show() it can make show gui but it doesn't allow to use sys.exit()
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
import time
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
self.myfunc()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
app.exec_()
If you need to perform an action again, you have several options.
For example, if each iteration takes very little time, without the possibility of blocking the main loop, you can replace the cycle with a timer (QTimer) and call the method each time, which is responsible for obtaining new data and updating the necessary interface elements in accordance with them:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread, QTimer
import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
self.k = 0
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
self.button = QtWidgets.QPushButton('Start', self)
self.button.clicked.connect(self.read_data)
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
layout = QtWidgets.QGridLayout(centralWidget)
layout.addWidget(self.label_data)
layout.addWidget(self.button)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.read_data_from_sensor)
#QtCore.pyqtSlot()
def read_data(self):
''' Start / Stop reading at the touch of a button '''
if not self.timer.isActive():
self.timer.start()
self.button.setText("Stop")
else:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
#QtCore.pyqtSlot()
def read_data_from_sensor(self):
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.label_data.setText(dt)
self.label_data.adjustSize()
self.k += 1
self.statusBar().showMessage('{} item(s)'.format(self.k))
if self.k > 10:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
self.k = 0
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
myWindow.show()
app.exec_()
What you wrote may also work, but this is not very good. You can compare.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread
#import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
# self.setupUi(self)
self.show()
self.myfunc()
def myfunc(self):
k = 0
stat = True
while stat:
k += 1
# time.sleep(1)
# self.statusMessage.append(str(k))
self.statusBar().showMessage('{} item(s)'.format(k))
QThread.msleep(1000)
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
# myWindow.show()
app.exec_()
In your loop you are sleeping for 10 second, since you are creating a while loop on the main thread, the GUI wont show until the loop is done because it would be blocking the main thread. You can test this by removing time.sleep(1).
Without changing your code much, try this:
import sys,threading, time
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
t = threading.Thread(target=self.myfunc)
t.start()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit() - if you are trying to close the window here use self.close()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
sys.exit(app.exec_())

Resources