使用QThread在PyQt5中显示QProgressDialog

问题描述

我正在使用PyQt5编写一个管理销售订单的应用程序。创建或删除订单时,我想显示一个marqee样式进度对话框以指示该应用程序正在运行。我访问过很多帖子,其中涉及使用QThread的答案。我试图实现它,但似乎我缺少了一些东西。这是我的线程课程。

class Worker(QThread):
    finished = Signal()

def run(self):
    self.x = QProgressDialog("Please wait..",None,0)
    self.x.show()

def stop(self):
    self.x.close()

在主窗口的 init 中,我创建了self.worker = Worker()

现在,用于删除条目的代码例如:

msg = MsgBox("yn","Delete Order","Are you sure you want to delete this order?") # Wrapper for the QMessageBox
if msg == 16384:
    self.worker.start()   ## start the worker thread,hoping to start the progress dialog
    session.delete(order) ##delete order from db
    session.commit()      ##commit to db
    self.change_view("Active",8) ##func. clean up the table.
    self.worker.finished.emit()   ##emit the finished signal to close the progress dialog

结果是不显示进度对话框。 gui只冻结一两秒钟,然后删除该条目,而不会显示任何进度对话框。

对不起,我的代码很长,因此我无法在此处包括所有内容,我只是想看看我是否遇到了严重错误

解决方法

您的代码有两个主要问题:

    必须创建
  1. GUI元素(所有与QWidget子类继承或相关的元素),并只能从Qt主线程中访问。
  2. 假设删除/提交操作需要花费一些时间,那么这些 操作必须在线程中执行,同时显示来自主线程的进度对话框,而不是相反。 另外,请考虑QThread已经有一个finished()信号,并且您不应该覆盖它。

这是一个基于您的代码的示例:

class Worker(QThread):
    def __init__(self,session,order):
        super.__init__()
        self.session = session
        self.order = order

    def run(self):
        self.session.delete(self.order)
        self.session.commit()


class Whatever(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        self.progressDialog = QProgressDialog("Please wait..",None,self)

    def deleteOrder(self,order):
        msg = MsgBox("yn","Delete Order","Are you sure you want to delete this order?")
        if msg == MsgBox.Yes: # you should prefer QMessageBox flags
            self.worker = Worker(session,order)
            self.worker.started(self.progressDialog.show())
            self.worker.finished(self.deleteCompleted)
            self.worker.start()

    def deleteCompleted(self):
        self.progressDialog.hide()
        self.change_view("Active",8)

由于在处理过程中进度对话框应保持打开状态,因此还应防止用户关闭它。为此,您可以在其上安装事件过滤器,并确保所有关闭事件都被接受;同样,由于QProgressDialog继承自QDialog,因此应过滤掉 Esc 键,否则将关闭对话框,但会拒绝并隐藏它。

class Whatever(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        self.progressDialog = QProgressDialog("Please wait..",self)
        self.progressDialog.installEventFilter(self)

    def eventFilter(self,source,event):
        if source == self.progressDialog:
            # check for both the CloseEvent *and* the escape key press
            if event.type() == QEvent.Close or event == QKeySequence.Cancel:
                event.accept()
                return True
        return super().eventFilter(source,event)