【问题标题】:Using QThread to display QProgressDialog in PyQt5 [duplicate]使用 QThread 在 PyQt5 中显示 QProgressDialog [重复]
【发布时间】:2020-09-17 11:55:04
【问题描述】:

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

class Worker(QThread):
    finished = Signal()

def run(self):
    self.x = QProgressDialog("Please wait..",None,0,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 只是冻结一两秒钟,然后条目删除,没有显示任何进度对话框。

对不起,我的代码很长,所以我不能在这里全部包含,我只是想看看我是否有什么严重的错误。

【问题讨论】:

  • 有一点需要注意,与您遇到的冻结无关,您永远不会使用self.x.setValue(progress) 之类的内容更新进度条。
  • 那是因为我只想让它在数据库更新完成之前给出繁忙的指示。这是一个选取框样式栏,因此最小值和最大值都设置为零。
  • 哦,好的。然后我相信解决方案在于以下两件事之一:1-您需要保持对进度对话框“活动”的引用并存储在self中,我的意思是您的程序界面中的self,而不是来自你的班。或 2- 进度对话框需要有一个父级,即您的程序界面。在这两种情况下,您都需要找到一种方法将 self 作为参数传递给 QThread(称之为 zelf 或其他名称)。
  • 您应该将删除订单的操作发送给线程,而不是创建 QDialog。对话框应该在主线程上创建并使用 QThread.startedQThread.finished 显示/隐藏信号。

标签: python pyqt5 qthread pyside2 qprogressdialog


【解决方案1】:

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

  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, 0, 0, self)

    def deleteOrder(self, session, 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..", None, 0, 0, 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)

【讨论】:

  • 这对理解线程非常有帮助。然而,遵循这一理念意味着我必须为每个处理数据库的函数创建线程。所以最终主窗口功能将被划分为各种单独的线程。这是一个好习惯吗?
  • 任何可能阻塞的操作通常都被移到单独的线程中。请注意,您应该尝试优化它们的使用,避免不必要的创建(您可以重用一个线程,您只需每次都正确设置它)。另外,看看 QRunnable 和 QThreadPool。
猜你喜欢
  • 2023-01-30
  • 2019-12-03
  • 2021-12-03
  • 1970-01-01
  • 2019-04-02
  • 2017-11-30
  • 2019-06-24
  • 2020-05-14
  • 1970-01-01
相关资源
最近更新 更多