I have an app with two buttons start and end. The start button will start a thread, which runs the audio recording function. This function is written using sounddevice and soundfile libraries. The audio recording can take place for an arbitary duration and the user can stop anytime by pressing ctrl+c.
So, now I want to implement a function for the end button to stop the thread which started by pressing start button or the function can send ctrl+c signal to the thread. So, that the current recording will be stopped. I am not sure how to achieve this. Any help is appreciated.
The code consisting in two .py is as it follows:
audio_record.py
import os
import signal
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import queue
from PyQt5 import QtCore, QtGui, QtWidgets
import soundfile as sf
import sounddevice as sd
import mythreading
class Ui_MainWindow(object):
def __init__(self):
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(280, 190, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.start_button_func)
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setGeometry(QtCore.QRect(380, 190, 75, 23))
self.pushButton_1.setObjectName("pushButton")
self.pushButton_1.clicked.connect(self.end_button_func)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Start"))
self.pushButton_1.setText(_translate("MainWindow", "End"))
def record(self):
self.pid = os.getpid()
self.q = queue.Queue()
self.s = sd.InputStream(samplerate=48000, channels=2, callback=self.callback)
try:
# Make sure the file is open before recording begins
with sf.SoundFile('check.wav', mode='x', samplerate=48000, channels=2, subtype="PCM_16") as file:
with self.s:
# 1 second silence before the recording begins
time.sleep(1)
print('START')
print('#' * 80)
print('press Ctrl+C to stop the recording')
while True:
file.write(self.q.get())
except OSError:
print('The file to be recorded already exists.')
sys.exit(1)
def callback(self, indata, frames, time, status):
"""
This function is called for each audio block from the record function.
"""
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
def start_button_func(self):
self.worker = mythreading.Worker(self.record)
self.threadpool.start(self.worker)
def end_button_func(self):
print('how to stop?')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
mythreading.py is as follows:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
self.fn = fn
#pyqtSlot()
def run(self):
self.fn()
You have to use a flag, in this case threading.Event() to indicate that the thread should no longer be executed. For the case of Ctrl + C you must use QShortcut
import os
import queue
from PyQt5 import QtCore, QtGui, QtWidgets
import soundfile as sf
import sounddevice as sd
import mythreading
import threading
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(280, 190, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setGeometry(QtCore.QRect(380, 190, 75, 23))
self.pushButton_1.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Start"))
self.pushButton_1.setText(_translate("MainWindow", "End"))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.threadpool = QtCore.QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
self.pushButton.clicked.connect(self.start_button_func)
self.pushButton_1.clicked.connect(self.end_button_func)
self.event_stop = threading.Event()
QtWidgets.QShortcut("Ctrl+C", self, activated=self.end_button_func)
def record(self):
self.pid = os.getpid()
self.q = queue.Queue()
self.s = sd.InputStream(samplerate=48000, channels=2, callback=self.callback)
try:
# Make sure the file is open before recording begins
with sf.SoundFile('check.wav', mode='x', samplerate=48000, channels=2, subtype="PCM_16") as file:
with self.s:
# 1 second silence before the recording begins
QtCore.QThread.sleep(1)
print('START')
print('#' * 80)
print('press Ctrl+C to stop the recording')
while not self.event_stop.is_set():
file.write(self.q.get())
print("STOP")
except OSError:
print('The file to be recorded already exists.')
sys.exit(1)
def callback(self, indata, frames, time, status):
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
#QtCore.pyqtSlot()
def start_button_func(self):
print("start")
self.worker = mythreading.Worker(self.record)
self.threadpool.start(self.worker)
#QtCore.pyqtSlot()
def end_button_func(self):
print('how to stop?')
self.event_stop.set()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Related
I'm new to GUI-programming and need help with a QThread application. I have a gui program that makes a query and lists the results of the query on the screen. I want an information dialog to open when the listing on the screen is finished. I wrote a function for this and when the operation is successful, it works without any problems. But I couldn't figure out how to show an error dialog if an invalid link entry occurs. Thank you for helping.
-I have commented out some of the methods I tried and failed.
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.finished.connect(self.uyariBox) #---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText("Tüm Sonuçlar Getirildi")
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(ui.uyariBox) #---> When you activate this place, the warning appears instantly and disappears immediately.
#---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except:
print("link error")
self.finished.emit()
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())
You can pass the operation result as string to the uyariBox method.
First you can create another signal in Bs4Worker:
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
Change the previous worker.finished connection to worker.result:
def startBs4Worker(self):
else:
self.worker.result.connect(self.uyariBox)
#Previous was self.worker.finished.connect(self.uyariBox)
Add variable to function:
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
Then use signal to pass on the result of the operation:
def run(self):
try:
self.result.emit("Tüm Sonuçlar Getirildi")
except Exception as e:
self.result.emit(f"Error: {e}")
Full code:
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.result.connect(
self.uyariBox) # ---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(
ui.uyariBox) # ---> When you activate this place, the warning appears instantly and disappears immediately.
# ---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
self.result.emit("Tüm Sonuçlar Getirildi")
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except Exception as e:
print("link error")
self.finished.emit()
self.result.emit(f"Error: {e}")
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())
We are creating an object detection project using Object detection API. We are train the program making pyqt5 GUI application. Trying to run the training part using thread. We want to stop the running thread using a push button. Here the code sample
class stopClass(QtCore.QThread):
def __init__(self, parent=None):
super(stopClass, self).__init__(parent)
def startTrain(self):
#changing directory
os.chdir(r"c://tensorflow_1//models//research//object_detection")
args3 = shlex.split('python train.py --logtostderr --train_dir=training/ --
pipeline_config_path=training/faster_rcnn_inception_v2_pets.config')
subprocess.run(args3, shell = True)
return
def run(self):
self.startTrain()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(701, 495)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Annotation = QtWidgets.QPushButton(self.centralwidget)
self.Annotation.setGeometry(QtCore.QRect(480, 10, 181, 41))
self.Annotation.setToolTip("")
self.Annotation.setObjectName("Annotation")
self.Start_train = QtWidgets.QPushButton(self.centralwidget)
self.Start_train.setGeometry(QtCore.QRect(480, 110, 181, 41))
self.Start_train.setObjectName("Start_train")
self.Stop_train = QtWidgets.QPushButton(self.centralwidget)
self.Stop_train.setEnabled(False)
self.Stop_train.setGeometry(QtCore.QRect(480, 160, 181, 41))
self.Stop_train.setObjectName("Stop_train")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 701, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.Start_train.clicked.connect(self.starting)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.Start_train.setText(_translate("MainWindow", "start train"))
self.Stop_train.setText(_translate("MainWindow", "stop train"))
def starting(self):
self.stopThread = stopClass()
self.stopThread.start()
self.Stop_train.setEnabled(True)
self.Stop_train.clicked.connect(self.stopThread.exit)
self.Start_train.setEnabled(False)
In this case I do not see the need to use threads since it is enough to use QProcess that allows a simple handling of the execution of the script. On the other hand, do not modify the script generated by pyuic so my solution assumes that you must recreate that script that must be called gui.py:
import shlex
import sys
from PyQt5.QtCore import QObject, QProcess
from PyQt5.QtWidgets import QApplication, QMainWindow
from gui import Ui_MainWindow
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._process = QProcess()
working_directory = r"c://tensorflow_1//models//research//object_detection"
self.process.setWorkingDirectory(working_directory)
self.process.setProgram(sys.executable)
args = shlex.split(
"train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2_pets.config"
)
self.process.setArguments(args)
#property
def process(self):
return self._process
def start(self):
if self.process.state() == QProcess.NotRunning:
self.process.start()
def stop(self):
if self.process.state() != QProcess.NotRunning:
self.process.kill()
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.manager = Manager()
self.Start_train.clicked.connect(self.manager.start)
self.Stop_train.clicked.connect(self.manager.stop)
self.manager.process.stateChanged.connect(self._handle_stateChanged)
def _handle_stateChanged(self, state):
self.Start_train.setEnabled(state != QProcess.Running)
self.Stop_train.setEnabled(state == QProcess.Running)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I'm facing the problem with Threads. I'm displaying current CPU usage with progress bar and it seems to be working well but the performance of whole window is terrible. Can't even click the button without laggy behavior. Is there any simple solution to fix it?
Here is my main code
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
import progressBarUI
import sys
import sysnfo
class MainWindow(QtWidgets.QMainWindow, progressBarUI.Ui_MainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
self.threadclass.signal.connect(self.updateProgressBar)
def updateProgressBar(self):
current_percentage = sysnfo.getCpuPercentage()
self.progressBar.setValue(current_percentage)
class ThreadClass(QThread):
signal = pyqtSignal(int)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def run(self):
while True:
current_percentage = sysnfo.getCpuPercentage()
self.signal.emit(current_percentage)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainAppWin = MainWindow()
mainAppWin.show()
app.exec_()
Here is sysnfo module:
import psutil as ps
def getCpuPercentage():
return ps.cpu_percent(interval=1)
And UI file (converted to .py file):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(787, 203)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(370, 20, 381, 111))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 50, 151, 41))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 787, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
When you use the "interval" parameter you are indicating that it measures the information during that time and calculates the average causing the execution of that function to take "interval" seconds, and the correct thing is to execute it in another thread, but you make the mistake of executing it too in the updateProgressBar method that lives in the main thread blocking it, instead use the information that sends you the signal:
#QtCore.pyqtSlot(int)
def updateProgressBar(self, percentage):
self.progressBar.setValue(percentage)
so basically i have programs that are meant to show the progress of reading an excel file line by line in the background. So far i have the following code:
excelresult.py:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtCore import Qt, QBasicTimer
import os, sys, xlrd, threading, time, pythoncom
import win32com.client
from time import sleep
from test import MyGlobals
class ExcelCheck(threading.Thread):
progPercent = 0
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
def run(self):
pythoncom.CoInitialize()
try:
while not self.event.is_set():
excel = win32com.client.Dispatch("Excel.Application")
wb = excel.ActiveWorkbook
ws = wb.Worksheets("TC")
va_title = ws.Range(ws.Range('I7'), ws.Range('I700'))
i = 0
for r in va_title.Cells:
if r.Text != '':
i = i + 1
# print(r.Text)
# print(i)
# print(round(i / 178.0 * 100,0))
# rounding off
progPercent = round(i / 178.0 * 100.0)
MyGlobals.x=progPercent
print(progPercent)
return progPercent
except:
print('Excel is not executed')
# sleep(1)
# self.event.wait()
def stop(self):
self.event.set()
scm.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import Qt, QBasicTimer
import os, sys, xlrd, win32com.client, xlwt, threading, time
from time import sleep
from ExcelResult import *
from test import MyGlobals
import threading
class Ui_MainWindow(object):
def __init__(self):
super().__init__()
self.btn_active = False
print('init_false')
def startBtnClicked(self):
self.btnStart.setText('start!')
self.btn_active = True
print(self.btn_active)
tmr = ExcelCheck()
tmr.start()
while(MyGlobals.x<=100):
#print (MyGlobals.x)
self.progressBar.setValue(MyGlobals.x)
# self.progressBar.minimum = 1
# self.progressBar.maximum = 100
# for progPercent in range(1, 101):
# self.progressBar.setValue(progPercent)
# time.sleep(1)
def exitBtnClicked(self):
# self.ExcelCheck()
self.btn_active = False
print(self.btn_active)
# os.system("taskkill /f /im Scm21.Client.exe")
# self.close()
# Stop the progress of python
self.sys.exit()
tmr = ExcelCheck()
tmr.stop()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(446, 207)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(40, 70, 381, 23))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.btnStart = QtWidgets.QPushButton(self.centralwidget)
self.btnStart.setGeometry(QtCore.QRect(110, 110, 75, 23))
self.btnStart.setObjectName("btnStart")
self.btnStart.clicked.connect(self.startBtnClicked)
self.btnExit = QtWidgets.QPushButton(self.centralwidget)
self.btnExit.setGeometry(QtCore.QRect(260, 110, 75, 23))
self.btnExit.setObjectName("btnExit")
self.btnExit.clicked.connect(self.exitBtnClicked)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 446, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "SCM21"))
self.btnStart.setText(_translate("MainWindow", "Start"))
self.btnExit.setText(_translate("MainWindow", "Exit"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
and, test.py
class MyGlobals(object):
x = 0
So, i am able to get the value of the ProgPercent from the ExcelResult.py into the scm.py using the test.py but completely not aware how would i update the progressbar value.
I tried using a loop but it hangs the GUI.
Thanks.
Use Qthread:
import time
from PyQt5 import QtCore
from PyQt5 import QtWidgets
class ExcelCheck(QtCore.QThread):
updated = QtCore.pyqtSignal(int)
running = False
def __init__(self, parent=None):
super(ExcelCheck, self).__init__(parent)
self.progPercent = 0
self.running = True
def run(self):
while self.running:
self.progPercent += 1
self.progPercent %= 100
self.updated.emit(int(self.progPercent))
time.sleep(0.1)
def stop(self):
self.running = False
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
self.btn_active = False
def startBtnClicked(self):
self.btnStart.setText('start!')
self.btn_active = True
self.tmr = ExcelCheck(self)
self.tmr.updated.connect(self.updateValue)
self.tmr.start()
def updateValue(self, data):
self.progressBar.setValue(data)
def exitBtnClicked(self):
# self.ExcelCheck()
self.btn_active = False
self.tmr.stop()
self.sys.exit()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(446, 207)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(40, 70, 381, 23))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.btnStart = QtWidgets.QPushButton(self.centralwidget)
self.btnStart.setGeometry(QtCore.QRect(110, 110, 75, 23))
self.btnStart.setObjectName("btnStart")
self.btnStart.clicked.connect(self.startBtnClicked)
self.btnExit = QtWidgets.QPushButton(self.centralwidget)
self.btnExit.setGeometry(QtCore.QRect(260, 110, 75, 23))
self.btnExit.setObjectName("btnExit")
self.btnExit.clicked.connect(self.exitBtnClicked)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 446, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "SCM21"))
self.btnStart.setText(_translate("MainWindow", "Start"))
self.btnExit.setText(_translate("MainWindow", "Exit"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I'm making a light GUI program with PyQT5.
But now I'm facing some problem about thread.
I just made simple test program like bottom:
the program simply trying to append numbers to textbox, but it crashes.
I don't know why but somehow I can prevent it by removing a comment(time.sleep)
import sys
import threading
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Some(QWidget):
e = threading.Event()
def btnfunc(self):
self.e.set()
def __init__(self):
super().__init__()
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
t = threading.Thread(target=self.myfunc, args=( ))
t.start()
self.myButton.clicked.connect(self.btnfunc)
def myfunc(self):
for i in range(300):
# time.sleep(0.4)
self.logs.append(str(i))
if i == 20:
self.e.wait()
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
It would be better if sets time higher.
I thought it is because of resource accessing, since it is pyQT5 GUI.
So I've find QThread. and I tried like bottom,
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Some(QWidget):
qw = QWaitCondition()
qm = QMutex()
def btnfunc(self):
self.qw.wakeAll()
def __init__(self):
super().__init__()
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
self.myButton.clicked.connect(self.btnfunc)
self.thread = QThread()
self.thread.started.connect(self.myfunc)
self.thread.start()
def myfunc(self):
for i in range(300):
self.logs.append(str(i))
if i == 20:
self.qw.wait(self.qm)
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
But crashes, doesn't work. and tried QThread+threading.Event(). It freezes GUI.
Now I don't know how to proceed it...
Edit:
I just realized about thread. Should not be accessed from other thread except QThread.
Then I will keep find about QWaitCondition
You should not control GUI directly via multithreading. Since two different threads are trying to control the GUI this results to freeze or crash.
I have learnt about this concept from here http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html
Here is your code that will work perfectly.
import sys
import threading
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
# Added new
class Communicate(QObject):
signal = pyqtSignal(str)
class Some(QWidget):
e = threading.Event()
def btnfunc(self):
self.e.set()
def __init__(self):
super().__init__()
#communicate object
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
t = threading.Thread(target=self.myfunc, args=( ))
t.start()
self.myButton.clicked.connect(self.btnfunc)
def myfunc(self):
for i in range(300):
# time.sleep(0.4)
#self.logs.append(str(i))
self.comm.signal.emit(str(i))
if i == 20:
self.e.wait()
def append_data(self, data):
self.logs.append(data)
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
You can pause the loop by using while = True and you can stop the loop with break statement
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import (
Qt, QObject, pyqtSignal, pyqtSlot, QRunnable, QThreadPool
)
import time
from time import sleep
import threading
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(655, 589)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.run = QtWidgets.QPushButton(self.centralwidget)
self.run.setGeometry(QtCore.QRect(260, 50, 93, 28))
self.run.setObjectName("run")
self.result = QtWidgets.QTextEdit(self.centralwidget)
self.result.setGeometry(QtCore.QRect(110, 120, 491, 201))
self.result.setObjectName("result")
self.stop = QtWidgets.QPushButton(self.centralwidget)
self.stop.setGeometry(QtCore.QRect(110, 390, 93, 28))
self.stop.setObjectName("stop")
self.pause = QtWidgets.QPushButton(self.centralwidget)
self.pause.setGeometry(QtCore.QRect(300, 390, 93, 28))
self.pause.setObjectName("pause")
self.resume = QtWidgets.QPushButton(self.centralwidget)
self.resume.setGeometry(QtCore.QRect(480, 390, 93, 28))
self.resume.setObjectName("resume")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 655, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.run.setText(_translate("MainWindow", "Run"))
self.stop.setText(_translate("MainWindow", "stop"))
self.pause.setText(_translate("MainWindow", "Pause"))
self.resume.setText(_translate("MainWindow", "Resume"))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.is_paused = False
self.is_killed = False
self.run.clicked.connect(self.send_wala)
self.stop.clicked.connect(self.kill_g)
self.pause.clicked.connect(self.pause_g)
self.resume.clicked.connect(self.resume_g)
#QtCore.pyqtSlot()
def send_wala(self):
threading.Thread(target=self.working, daemon=True).start()
def working(self):
for i in range(10):
sleep(3)
self.result.append(str(i))
while self.is_paused:
time.sleep(0)
if self.is_killed:
break
def pause_g(self):
self.is_paused = True
def resume_g(self):
self.is_paused = False
def kill_g(self):
self.is_killed = True
import sys
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())