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

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

Related

Stop a Qthread PyQt5

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

Why does the label show only the last set image? (PyQt5) [duplicate]

I have got this problem. I´m trying to set text on a lineEdit object on pyqt4, then wait for a few seconds and changing the text of the same lineEdit. For this I´m using the time.sleep() function given on the python Time module. But my problem is that instead of setting the text, then waiting and finally rewrite the text on the lineEdit, it just waits the time it´s supposed to sleep and only shows the final text. My code is as follows:
from PyQt4 import QtGui
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
time.sleep(2)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.
You should probably use a QTimer and use it's timeout signal to schedule a signal for deferred delivery, or it's singleShot method.
For example (adapted your code to make it run without dependencies):
from PyQt4 import QtGui, QtCore
class Ventana(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.lineEdit = QtGui.QLineEdit(self)
self.button = QtGui.QPushButton('clickme', self)
self.layout().addWidget(self.lineEdit)
self.layout().addWidget(self.button)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
self.lineEdit.setText('Start')
QtCore.QTimer.singleShot(2000, lambda: self.lineEdit.setText('End'))
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
Also, take a look at the QThread sleep() function, it puts the current thread to sleep and allows other threads to run. https://doc.qt.io/qt-5/qthread.html#sleep
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.You can use QtTest module rather than time.sleep().
from PyQt4 import QtTest
QtTest.QTest.qWait(msecs)
So your code should look like:
from PyQt4 import QtGui,QtTest
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
QtTest.QTest.qWait(2000)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())

QObject: Cannot create children for a parent that is in a different thread. (Multithreading pyqt5)

I am trying to edit the text in 'QTextEdit' but I got the error which is in the title above. I know that I shouldn't edit the widgets in the main app from another thread and I searched for some answers, But I didn't get the full answer that I want
This is a sample that produce the same error that I have
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
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.text_edit = QTextEdit(self)
self.text_edit.resize(200, 200)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(lambda x : self.thread_creator(self.test))
self.btn2 = QPushButton('print', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(lambda x : print('dad'))
def test(self):
for i in range(99):
self.text_edit.setText(f'dad {i}')
time.sleep(0.1)
def thread_creator(self, function):
self.thread = threading.Thread(target=function)
self.thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())

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

Pyqt lunch separate Qtapplications in separate process

So what i would like to do is from the main Qt application start a bunch of separate Qt applications each on separate processes ... do their job then report to the main application when finished.
Could you guys point me in the right direction . I tried with multiprocessing but i get stuck initializing the separate Qt applications
Thank you
Something like this ... which is wrong
import sys
import multiprocessing
from multiprocessing import Pool
from PyQt4 import QtCore, QtGui ,QtNetwork
from PyQt4 import QtWebKit
from PyQt4.QtCore import *
from PyQt4.QtWebKit import *
from PyQt4.QtGui import QApplication
class SimpleWin(QtGui.QWidget):
def __init__(self, parent=None):
super(SimpleWin, self).__init__(parent)
self.buton = QtGui.QPushButton("START")
self.buton.clicked.connect(self.RunJob)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.buton)
self.setLayout(hbox)
def RunJob(self):
print "RUUN"
jobs = []
for i in range(5):
p = multiprocessing.Process(target=Render,)
print i
jobs.append(p)
p.start()
class Render(QWebView,multiprocessing.Process):
def __init__(self, prox ):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
# self.crawl()
self.load(QUrl("http://www.ip-score.com/"))
self.show()
self.app.exec_()
def main():
app = QtGui.QApplication(sys.argv)
browse = SimpleWin()
browse.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Resources