为什么我无法关闭QThreadPool?

问题描述 投票:0回答:1

我正在尝试使用 PyQt6 为我的函数创建一个加载屏幕(有时需要很长时间),并且我正在使用 QThreadPool 将这些函数移动到不同的线程中,这是迄今为止我的代码:

from src.gui.loading import Ui_Form
from PyQt6.QtWidgets import QWidget
from PyQt6.QtCore import QThreadPool, QRunnable, QTimer, QThread
from typing import Callable


class Loading(Ui_Form, QWidget):
    def __init__(self,
                 parent: QWidget | None,
                 next_widget: QWidget | None,
                 action: str,
                 time: int,
                 task: Callable,
                 task_len: int,
                 initial_task: str):
        super().__init__()
        self.setupUi(self)
        self.setParent(parent)
        self.parent = parent
        self.next_widget = next_widget
        self.time = time
        self.task = TaskRunner(self, task)
        self.current_time = 0
        self.tasks_done = 0
        self.all_tasks = task_len

        self.Task.setText(action)
        self.Estimation.setText(f"estimated time: {self.int_to_time(time)}")
        self.progressBar.setValue(0)
        self.TimeLeft.setText("")
        self.Current.setText("")
        self.Task.setText("")

        self.thread_pool = QThreadPool(self)
        self.thread_pool.destroyed.connect(self.quit)
        self.run_tasks()
        self.task_done(initial_task)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

    @staticmethod
    def int_to_time(time: int) -> str:
        if time >= 3600:
            return f"{time / 3600} hours"
        elif time >= 60:
            return f"{time / 60} minutes"
        else:
            return f"{time} seconds"

    def update_time(self):
        self.current_time += 1
        self.TimeLeft.setText(self.int_to_time(self.current_time))

    def task_done(self, next_task: str = None):
        self.tasks_done += 1
        if not next_task:
            self.Current.setText("finished all tasks, closing window")
            self.Tasks.setText(f"{self.tasks_done} out of {self.all_tasks}")
        elif self.tasks_done != self.all_tasks:
            self.Current.setText(f"currently: {next_task}")
            self.Tasks.setText(f"{self.tasks_done} out of {self.all_tasks}")

    def quit(self):
        self.close()

    def closeEvent(self, a0):
        self.timer.stop()
        self.thread_pool.waitForDone(-1)
        self.close()

    def run_tasks(self): self.thread_pool.start(self.task)


class TaskRunner(QRunnable):
    def __init__(self, parent: Loading | None, task: Callable):
        super().__init__()
        self.parent = parent
        self.task = task

    def run(self):
        self.task(self.parent)
        if self.parent:
            self.parent.task_done(None)

虽然这段代码大部分有效,但无论如何我似乎都无法关闭窗口(通过关闭窗口,我的意思是在代码内部,使用函数 self.close() 不起作用)。我尝试调试了一下,发现即使所有任务都完成了, self.thread_pool 似乎也没有关闭。我来自 Rust,在 Rust 中,如果没有函数正在运行,线程池就可以关闭,是否可以在 PyQt6 中做同样的事情?如果没有,那为什么?

顺便说一句,对于任何想要尝试此代码的人,这里是 Ui_Form:

# Form implementation generated from reading ui file 'loading.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(836, 302)
        Form.setStyleSheet("#centralwidget {background-color:rgba(20, 20, 20, 250);}\n"
"#Form {background-color:rgba(20, 20, 20, 250);}\n"
"QMainWindow{background-color:rgba(20, 20, 20, 250);}\n"
"\n"
"QWidget{\n"
"    color:rgba(249, 249, 249, 240);\n"
"}\n"
"\n"
"QMenuBar{background-color:rgba(20, 20, 20, 250);}\n"
"QMenu{background-color:rgba(20, 20, 20, 250);}\n"
"QMenu::item:selected{background-color:rgba(229, 229, 229, 100);}\n"
"QMenuBar::item:selected{background-color:rgba(229, 229, 229, 100);}\n"
"QListWidget{background-color:rgba(20, 20, 20, 250);}\n"
"QSpinBox{background-color:rgba(20, 20, 20, 250);}\n"
"\n"
"QMenu::separator{height:5px; background-color:rgba(191, 191, 191, 100);}\n"
"\n"
"QComboBox {\n"
"    color:rgba(193, 193, 193, 250);\n"
"    background-color:rgba(29, 29, 29, 250);\n"
"    border:none;\n"
"}\n"
"QComboBox QAbstractItemView{\n"
"    color:rgba(193, 193, 193, 250);\n"
"    background-color:rgba(29, 29, 29, 250);\n"
"    border:none;\n"
"}\n"
"QLineEdit {\n"
"    color:rgba(193, 193, 193, 250);\n"
"    background-color:rgba(29, 29, 29, 250);\n"
"    border:none;\n"
"}\n"
"QTextEdit {\n"
"    color:rgba(193, 193, 193, 250);\n"
"    background-color:rgba(29, 29, 29, 250);\n"
"    border:none;\n"
"}\n"
"QProgressBar {\n"
"     border: 0px solid grey;\n"
"     border-radius: 0px;\n"
"     background-color:rgba(255, 255, 255, 0);\n"
" }\n"
"QTableWidget {\n"
"    background-color: rgba(29, 29, 29, 250);\n"
"    color: rgba(193, 193, 193, 250);\n"
"}\n"
"\n"
"QTableWidget::item {\n"
"    padding: 4px;\n"
"}\n"
"\n"
"QTableWidget::item:selected {\n"
"    background-color: rgba(50, 50, 50, 250);\n"
"}\n"
"\n"
"QTableWidget::item:focus {\n"
"    background-color: rgba(70, 70, 70, 250);\n"
"    outline: none;\n"
"}\n"
"\n"
"QHeaderView::section {\n"
"    background-color: rgba(50, 50, 50, 250);\n"
"    color: rgba(193, 193, 193, 250);\n"
"    padding: 4px;\n"
"    border: none;\n"
"}\n"
"\n"
"QHeaderView {\n"
"    background-color: rgba(50, 50, 50, 250);\n"
"    color: rgba(193, 193, 193, 250);\n"
"    padding: 4px;\n"
"    border: none;\n"
"}\n"
"\n"
"QHeaderView::section:checked {\n"
"    background-color: rgba(70, 70, 70, 250);\n"
"}\n"
" QTableView QTableCornerButton::section {\n"
"    background-color: rgba(50, 50, 50, 250);\n"
" }\n"
"\n"
"QPushButton{background-color:rgba(59, 59, 59, 250);}\n"
"QPushButton:hover{background-color:rgba(107, 107, 107, 250);}")
        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout.setObjectName("verticalLayout")
        self.Task = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.Task.setFont(font)
        self.Task.setText("")
        self.Task.setObjectName("Task")
        self.verticalLayout.addWidget(self.Task, 0, QtCore.Qt.AlignmentFlag.AlignHCenter)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.Estimation = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.Estimation.setFont(font)
        self.Estimation.setObjectName("Estimation")
        self.horizontalLayout.addWidget(self.Estimation)
        self.TimeLeft = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.TimeLeft.setFont(font)
        self.TimeLeft.setText("")
        self.TimeLeft.setObjectName("TimeLeft")
        self.horizontalLayout.addWidget(self.TimeLeft, 0, QtCore.Qt.AlignmentFlag.AlignRight)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.Current = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.Current.setFont(font)
        self.Current.setObjectName("Current")
        self.horizontalLayout_2.addWidget(self.Current)
        self.Tasks = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.Tasks.setFont(font)
        self.Tasks.setObjectName("Tasks")
        self.horizontalLayout_2.addWidget(self.Tasks)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.progressBar = QtWidgets.QProgressBar(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.progressBar.setFont(font)
        self.progressBar.setMinimum(0)
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        self.verticalLayout.addWidget(self.progressBar)
        self.Error = QtWidgets.QLabel(parent=Form)
        font = QtGui.QFont()
        font.setFamily("Secular One")
        font.setPointSize(20)
        self.Error.setFont(font)
        self.Error.setStyleSheet("color: red;")
        self.Error.setText("")
        self.Error.setObjectName("Error")
        self.verticalLayout.addWidget(self.Error)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.Estimation.setText(_translate("Form", "estimated time:"))
        self.Current.setText(_translate("Form", "current task:"))
        self.Tasks.setText(_translate("Form", "task x out of y"))

python window qthread pyqt6 qrunnable
1个回答
0
投票

在我的例子中使用 QRunnable 是一个错误。 QRunnable 的想法是它比 QThread 更轻、更简单,使其更容易在需要运行大量线程的项目中使用。因为我只想运行一个单边线程,所以使用 QThread 更简单(并解决问题)。我刚刚删除了 self.thread_pool 并将

class TaskRunner(QRunnable)
变成了
class TaskRunner(QThread)
,而不是运行
self.thread_pool.start(self.task)
我可以只运行
self.task.start
。现在我可以随时删除线程,但代码保持(几乎)相同。

© www.soinside.com 2019 - 2024. All rights reserved.