PySide2 Non-Blocking QMessageBox 不显示文本

Posted

技术标签:

【中文标题】PySide2 Non-Blocking QMessageBox 不显示文本【英文标题】:PySide2 Non-Blocking QMessageBox doesn't show text 【发布时间】:2019-03-05 21:51:34 【问题描述】:

虽然我的应用程序做了一些耗时的工作,但我想在应用程序忙碌时向用户显示一个消息框。我不想要任何按钮(如OKCancel),我不能在消息框上调用exec_(),因为那会阻塞。

我检查了许多qt 网站,我需要的代码似乎可以归结为:

    message_box = QMessageBox()
    message_box.setText(str('Reading Device, Please Wait...'))
    message_box.show()
    # do work here
    message_box.close()

当我运行代码时,我得到消息框,但没有文本。我做错了什么?

我在下面提供了一个工作示例:

#!/usr/bin/env python3

import sys
import time
from PySide2.QtWidgets import (QLineEdit, QPushButton, QApplication,
    QVBoxLayout, QDialog, QMessageBox)

class Form(QDialog):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)
        self.button = QPushButton("Click Me")
        layout = QVBoxLayout()
        layout.addWidget(self.button)
        self.setLayout(layout)
        # Add button signal to dowork slot
        self.button.clicked.connect(self.dowork)

    def dowork(self):
        message_box = QMessageBox()
        message_box.setText(str('Reading Device, Please Wait...'))
        message_box.show()
        delay = 2.5
        while delay:
           sys.stdout.write('Working...\n')
           time.sleep(0.5)  # do some time-consuming stuff...
           delay -= 0.5
        message_box.close()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = Form()
    print('starting app...')
    form.show()
    sys.exit(app.exec_())

如果您单击该按钮,消息框会弹出并在“工作”完成时显示。当“工作”完成时,消息框再次消失 - 应该如此。但是消息框中没有显示任何文字。

这里有一个类似的问题:qmessagebox-not-show-text-when-call-show,但这并不能回答我的问题。

【问题讨论】:

【参考方案1】:

你不能有一个消耗大量时间(超过 30 毫秒)的任务,因为它阻塞了 GUI 事件循环,阻止了 Qt 正常工作,而是使用信号旁边的线程从其他话题:

import sys
import threading
import time
from PySide2 import QtCore, QtWidgets

class Form(QtWidgets.QDialog):
    started = QtCore.Signal()
    finished = QtCore.Signal()

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)
        self.button = QtWidgets.QPushButton("Click Me")
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.button)
        # Add button signal to dowork slot
        self.button.clicked.connect(self.on_clicled)

        self._message_box = QtWidgets.QMessageBox()
        self._message_box.setText(str('Reading Device, Please Wait...'))
        self._message_box.setStandardButtons(QtWidgets.QMessageBox.NoButton)
        self.started.connect(self._message_box.show)
        self.finished.connect(self._message_box.accept)

    @QtCore.Slot()
    def on_clicled(self):
        thread = threading.Thread(target=self.dowork, daemon=True)
        thread.start()

    def dowork(self):
        delay = 2.5
        self.started.emit()
        while delay:
           sys.stdout.write('Working...\n')
           time.sleep(0.5)  # do some time-consuming stuff...
           delay -= 0.5
        self.finished.emit()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = Form()
    print('starting app...')
    form.show()
    sys.exit(app.exec_())

【讨论】:

就我而言,GUI 阻塞不是问题。我尝试了您的代码,它可以工作,但给出了一个我不想要的OK 按钮(我的代码根本没有任何按钮)。如果我将self._message_box.setStandardButtons(0) 添加到您的代码中,您的代码将停止工作;消息框永远不会消失,主窗口也无法关闭。 显示消息框时,GUI 被阻止。这正是我想要的,因为我从设备读取数据,允许用户对其进行操作,然后将其写回。读写操作应该是“原子的”;在我编写数据或使用新数据填充窗口时,用户应该无法修改数据。 @NZD 您不应该直接从另一个线程修改 GUI,因为您必须使用信号,另一方面您应该使用互斥锁,并且可能实现一个小的 FSM 来管理逻辑,如我不知道真正的问题我不能再帮你了。【参考方案2】:

您几乎可以肯定使用 QProgressDialog。将最小值和最大值设置为零以获得“不确定”外观。

https://doc.qt.io/qt-5/qprogressdialog.html

虽然在 setText() 之后强制重绘可能会起作用:

self._message_box.repaint()
QtWidgets.QApplication.processEvents()

【讨论】:

以上是关于PySide2 Non-Blocking QMessageBox 不显示文本的主要内容,如果未能解决你的问题,请参考以下文章

Pyside2.QtCore.QObject 不是 MyTableWidget 的直接基类——PySide2 出错

blocking cache和non-blocking cache

Java NIO (10) Non-blocking Server

Non-blocking Spring Boot with Kotlin Coroutines

PySide2 和支持 addToJavaScriptWindowObject

使用 PySide2 开发 Maya 插件系列 总览