无法重新运行线程(但调试时一切正常)
Can't rerun thread (but when debugging everything is OK)
我正在尝试为我的程序创建基础,Main Window 带有按钮,运行 进度条的新线程带有计算按钮,计算也在新线程中。第一次 运行 计算正常,但当我第二次按 "Open calculation bar" 时程序崩溃了。我试图使用调试来捕获错误的位置,但是在调试时一切正常。我假设我错误地使用了 QThread...如何改进我的代码?
# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
self.open_progress_bar_button.move(100, 50)
self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)
self.setGeometry(500, 300, 300, 100)
self.setWindowTitle('MainWindow')
self.show()
def run_calculation_thread(self):
self.open_progress_bar_button.setEnabled(False)
self.progress_bar_thread = QThread()
self.calculation_progress_bar = CalculationProgressBar()
self.calculation_progress_bar.moveToThread(self.progress_bar_thread)
QtWidgets.qApp.aboutToQuit.connect(self.progress_bar_thread.quit)
self.progress_bar_thread.start()
self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)
def on_close_create_progress_bar_thread(self):
self.progress_bar_thread.terminate() # maybe error is here
self.open_progress_bar_button.setEnabled(True)
class CalculationProgressBar(QtWidgets.QWidget):
request_calculation = pyqtSignal()
close_progress_bar = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.calculation_thread = QThread()
self.calculation = Calculation()
self.calculation.notify_progress.connect(self.on_progress)
self.calculation.calculation_done.connect(self.on_finish)
self.request_calculation.connect(self.calculation.calculate)
self.calculation_thread.started.connect(self.calculation.start)
self.calculation.moveToThread(self.calculation_thread)
QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)
self.calculation_thread.start()
self.setup_gui()
self.show()
def setup_gui(self):
l = QtWidgets.QVBoxLayout(self)
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setTextVisible(False)
self.progress_bar.setRange(0, 100)
l.addWidget(self.progress_bar)
self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
l.addWidget(self.open_progress_bar_button)
self.setFixedWidth(300)
def on_progress(self, i):
self.progress_bar.setValue(i)
def on_finish(self):
self.close()
self.close_progress_bar.emit()
@pyqtSlot()
def tables_creation_requested(self):
self.request_calculation.emit()
self.open_progress_bar_button.setEnabled(False)
class Calculation(QObject):
notify_progress = pyqtSignal(int)
calculation_done = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
@pyqtSlot()
def start(self):
print("Ready to calculate")
@pyqtSlot()
def calculate(self):
for i in range(2):
self.notify_progress.emit(i*50)
sleep(1)
self.calculation_done.emit()
if __name__ == "__main__":
application = QtWidgets.QApplication(argv)
main_window = MainWindow()
main_window.show()
exit(application.exec_())
为什么要创建 2 个线程?
CalculationProgressBar 的任务并不重,因为它的任务只是显示它从另一个线程接收到的数据,除此之外,GUI 不能移动到另一个线程。您只需要创建一个 一个线程 ,计算 class 的对象将在其中运行。
启动任务不需要连接started信号,如图所示用信号调用信号即可。另一方面,没有必要在每次按下按钮时都创建一个 CalculationProgressBar,最好重复使用它。
考虑到上面的解决方案是:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
self.open_progress_bar_button.move(100, 50)
self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)
self.calculation_progress_bar = CalculationProgressBar()
self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)
self.setGeometry(500, 300, 300, 100)
self.setWindowTitle('MainWindow')
self.show()
def run_calculation_thread(self):
self.open_progress_bar_button.setEnabled(False)
self.calculation_progress_bar.progress_bar.reset()
self.calculation_progress_bar.show()
def on_close_create_progress_bar_thread(self):
self.open_progress_bar_button.setEnabled(True)
class CalculationProgressBar(QtWidgets.QWidget):
request_calculation = pyqtSignal()
close_progress_bar = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.calculation_thread = QThread(self)
self.calculation_thread.start()
self.calculation = Calculation()
self.calculation.moveToThread(self.calculation_thread)
self.calculation.notify_progress.connect(self.on_progress)
self.calculation.calculation_done.connect(self.on_finish)
self.request_calculation.connect(self.calculation.calculate)
QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)
self.setup_gui()
def setup_gui(self):
l = QtWidgets.QVBoxLayout(self)
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setTextVisible(False)
self.progress_bar.setRange(0, 100)
l.addWidget(self.progress_bar)
self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
l.addWidget(self.open_progress_bar_button)
self.setFixedWidth(300)
def reset(self):
self.progress_bar.setValue(0)
@pyqtSlot(int)
def on_progress(self, i):
self.progress_bar.setValue(i)
@pyqtSlot()
def on_finish(self):
self.hide()
self.close_progress_bar.emit()
self.open_progress_bar_button.setEnabled(True)
@pyqtSlot()
def tables_creation_requested(self):
self.request_calculation.emit()
self.open_progress_bar_button.setEnabled(False)
class Calculation(QObject):
notify_progress = pyqtSignal(int)
calculation_done = pyqtSignal()
@pyqtSlot()
def calculate(self):
for i in range(3):
self.notify_progress.emit(i*50)
sleep(1)
self.calculation_done.emit()
if __name__ == "__main__":
application = QtWidgets.QApplication(argv)
main_window = MainWindow()
main_window.show()
exit(application.exec_())
你应该阅读 QThread documentation 你会明白它是如何工作的。
MyWin
class - 是你的起点。
创建一个 class MyTask
,其中方法 "run" 进行 "heavy" 计算并使用简单的 QtCore.pyqtSignal
- self.updatebar.emit
发出新值。
如果您理解下面的代码,那么您可以创建 QDialog,其中可以插入下面的逻辑。
代码:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *;
import sys, time
# this code was generated in designer
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(217, 138)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.bar_btn = QtWidgets.QPushButton(self.centralwidget)
self.bar_btn.setGeometry(QtCore.QRect(20, 20, 101, 23))
self.bar_btn.setObjectName("bar_btn")
self.bar = QtWidgets.QProgressBar(self.centralwidget)
self.bar.setGeometry(QtCore.QRect(30, 60, 118, 23))
self.bar.setProperty("value", 24)
self.bar.setObjectName("bar")
MainWindow.setCentralWidget(self.centralwidget)
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.bar_btn.setText(_translate("MainWindow", "open bar"))
# Your 'calc' thread
class MyTask(QtCore.QThread):
updatebar = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def run(self):
for i in range(101):
time.sleep(0.01)
self.updatebar.emit(i)
class MyWin(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent); self.ui = Ui_MainWindow(); self.ui.setupUi(self)
# make a task
self.my_task = MyTask()
self.my_task.updatebar.connect(lambda val: self.ui.bar.setValue(val))
self.ui.bar_btn.clicked.connect(self.my_task.start)
# optional - disable button
self.my_task.started.connect(lambda : self.ui.bar_btn.setEnabled(False))
self.my_task.finished.connect(lambda : self.ui.bar_btn.setEnabled(True))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
myapp = MyWin()
myapp.show()
sys.exit(app.exec_())
我正在尝试为我的程序创建基础,Main Window 带有按钮,运行 进度条的新线程带有计算按钮,计算也在新线程中。第一次 运行 计算正常,但当我第二次按 "Open calculation bar" 时程序崩溃了。我试图使用调试来捕获错误的位置,但是在调试时一切正常。我假设我错误地使用了 QThread...如何改进我的代码?
# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
self.open_progress_bar_button.move(100, 50)
self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)
self.setGeometry(500, 300, 300, 100)
self.setWindowTitle('MainWindow')
self.show()
def run_calculation_thread(self):
self.open_progress_bar_button.setEnabled(False)
self.progress_bar_thread = QThread()
self.calculation_progress_bar = CalculationProgressBar()
self.calculation_progress_bar.moveToThread(self.progress_bar_thread)
QtWidgets.qApp.aboutToQuit.connect(self.progress_bar_thread.quit)
self.progress_bar_thread.start()
self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)
def on_close_create_progress_bar_thread(self):
self.progress_bar_thread.terminate() # maybe error is here
self.open_progress_bar_button.setEnabled(True)
class CalculationProgressBar(QtWidgets.QWidget):
request_calculation = pyqtSignal()
close_progress_bar = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.calculation_thread = QThread()
self.calculation = Calculation()
self.calculation.notify_progress.connect(self.on_progress)
self.calculation.calculation_done.connect(self.on_finish)
self.request_calculation.connect(self.calculation.calculate)
self.calculation_thread.started.connect(self.calculation.start)
self.calculation.moveToThread(self.calculation_thread)
QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)
self.calculation_thread.start()
self.setup_gui()
self.show()
def setup_gui(self):
l = QtWidgets.QVBoxLayout(self)
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setTextVisible(False)
self.progress_bar.setRange(0, 100)
l.addWidget(self.progress_bar)
self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
l.addWidget(self.open_progress_bar_button)
self.setFixedWidth(300)
def on_progress(self, i):
self.progress_bar.setValue(i)
def on_finish(self):
self.close()
self.close_progress_bar.emit()
@pyqtSlot()
def tables_creation_requested(self):
self.request_calculation.emit()
self.open_progress_bar_button.setEnabled(False)
class Calculation(QObject):
notify_progress = pyqtSignal(int)
calculation_done = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
@pyqtSlot()
def start(self):
print("Ready to calculate")
@pyqtSlot()
def calculate(self):
for i in range(2):
self.notify_progress.emit(i*50)
sleep(1)
self.calculation_done.emit()
if __name__ == "__main__":
application = QtWidgets.QApplication(argv)
main_window = MainWindow()
main_window.show()
exit(application.exec_())
为什么要创建 2 个线程?
CalculationProgressBar 的任务并不重,因为它的任务只是显示它从另一个线程接收到的数据,除此之外,GUI 不能移动到另一个线程。您只需要创建一个 一个线程 ,计算 class 的对象将在其中运行。
启动任务不需要连接started信号,如图所示用信号调用信号即可。另一方面,没有必要在每次按下按钮时都创建一个 CalculationProgressBar,最好重复使用它。
考虑到上面的解决方案是:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
self.open_progress_bar_button.move(100, 50)
self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)
self.calculation_progress_bar = CalculationProgressBar()
self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)
self.setGeometry(500, 300, 300, 100)
self.setWindowTitle('MainWindow')
self.show()
def run_calculation_thread(self):
self.open_progress_bar_button.setEnabled(False)
self.calculation_progress_bar.progress_bar.reset()
self.calculation_progress_bar.show()
def on_close_create_progress_bar_thread(self):
self.open_progress_bar_button.setEnabled(True)
class CalculationProgressBar(QtWidgets.QWidget):
request_calculation = pyqtSignal()
close_progress_bar = pyqtSignal()
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.calculation_thread = QThread(self)
self.calculation_thread.start()
self.calculation = Calculation()
self.calculation.moveToThread(self.calculation_thread)
self.calculation.notify_progress.connect(self.on_progress)
self.calculation.calculation_done.connect(self.on_finish)
self.request_calculation.connect(self.calculation.calculate)
QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)
self.setup_gui()
def setup_gui(self):
l = QtWidgets.QVBoxLayout(self)
self.progress_bar = QtWidgets.QProgressBar(self)
self.progress_bar.setTextVisible(False)
self.progress_bar.setRange(0, 100)
l.addWidget(self.progress_bar)
self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
l.addWidget(self.open_progress_bar_button)
self.setFixedWidth(300)
def reset(self):
self.progress_bar.setValue(0)
@pyqtSlot(int)
def on_progress(self, i):
self.progress_bar.setValue(i)
@pyqtSlot()
def on_finish(self):
self.hide()
self.close_progress_bar.emit()
self.open_progress_bar_button.setEnabled(True)
@pyqtSlot()
def tables_creation_requested(self):
self.request_calculation.emit()
self.open_progress_bar_button.setEnabled(False)
class Calculation(QObject):
notify_progress = pyqtSignal(int)
calculation_done = pyqtSignal()
@pyqtSlot()
def calculate(self):
for i in range(3):
self.notify_progress.emit(i*50)
sleep(1)
self.calculation_done.emit()
if __name__ == "__main__":
application = QtWidgets.QApplication(argv)
main_window = MainWindow()
main_window.show()
exit(application.exec_())
你应该阅读 QThread documentation 你会明白它是如何工作的。
MyWin
class - 是你的起点。
创建一个 class MyTask
,其中方法 "run" 进行 "heavy" 计算并使用简单的 QtCore.pyqtSignal
- self.updatebar.emit
发出新值。
如果您理解下面的代码,那么您可以创建 QDialog,其中可以插入下面的逻辑。
代码:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *;
import sys, time
# this code was generated in designer
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(217, 138)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.bar_btn = QtWidgets.QPushButton(self.centralwidget)
self.bar_btn.setGeometry(QtCore.QRect(20, 20, 101, 23))
self.bar_btn.setObjectName("bar_btn")
self.bar = QtWidgets.QProgressBar(self.centralwidget)
self.bar.setGeometry(QtCore.QRect(30, 60, 118, 23))
self.bar.setProperty("value", 24)
self.bar.setObjectName("bar")
MainWindow.setCentralWidget(self.centralwidget)
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.bar_btn.setText(_translate("MainWindow", "open bar"))
# Your 'calc' thread
class MyTask(QtCore.QThread):
updatebar = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def run(self):
for i in range(101):
time.sleep(0.01)
self.updatebar.emit(i)
class MyWin(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent); self.ui = Ui_MainWindow(); self.ui.setupUi(self)
# make a task
self.my_task = MyTask()
self.my_task.updatebar.connect(lambda val: self.ui.bar.setValue(val))
self.ui.bar_btn.clicked.connect(self.my_task.start)
# optional - disable button
self.my_task.started.connect(lambda : self.ui.bar_btn.setEnabled(False))
self.my_task.finished.connect(lambda : self.ui.bar_btn.setEnabled(True))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
myapp = MyWin()
myapp.show()
sys.exit(app.exec_())