簡體   English   中英

如何在其他進程在后台運行時顯示 PyQt5/PySide2 對話框

[英]How to display a PyQt5/PySide2 dialog box while other processes run in the background

概述:我有一個相當大的 GUI 程序,內置於 PyQt5/PySide2 中,可以執行多種操作。 有些過程非常快,有些過程可能需要大約一分鍾才能完成。 我將我的程序設置為顯示一種“請稍候...”對話框,以顯示任何花費超過一秒左右的進程。 我將threading模塊與 PyQt5/PySide2 中內置的signals結合使用。 雖然它工作得很好,但我發現我也不能使用concurrent.futures運行線程,因為這些線程模塊沒有 function 很好地結合在一起。 concurrent.futures不處理信號,並且在背靠背處理大量函數時threading通常要慢得多。 所以我會在合適的時候選擇其中任何一個。

問題:然而,我開始遇到這樣的情況,雖然對話框會出現,但它不會在框中顯示文本,這通常是一條類似於“請等待,正在處理請求”的消息。 本質上,顯示文本的過程被/正在被擱置,直到底層過程完成。 該過程完成后,只要 window 沒有關閉,它就會顯示文本。

障礙:除了上述情況之外,我還重寫了部分信號和對話顯示類,從表面上看,它們如預期的那樣出現在 function 中,並且我已經包含了一個基本示例的完整代碼。 但是,當我將這些確切的方法應用於我的大型程序時,它會在第一次開始顯示對話框時自行關閉。

問題:我想知道我是否在下面的示例中遺漏了一個基本元素或概念,當應用於更大的程序時,可能會給我帶來一些問題。 我正在尋找此示例中的潛在危險信號。

編輯:運行此示例時,單擊“確定”按鈕以測試對話框和線程。 “主”框將消失,只應顯示對話框。 3 秒后,對話框消失,出現另一個框。 這與我的大型程序的實際功能非常相似。 本質上,當您啟動時,您“登錄”到程序,因此開始菜單消失,然后實際程序初始化並加載。 正如您在此示例中看到的那樣,該框將短暫顯示然后消失,這就是我的程序中發生的情況。 用戶登錄,但在登錄后的一秒鍾內,程序關閉。 我已經嘗試過如何讓 window 加載的變體。 下面列出的至少實際上顯示了它,但我使用的其他方法只會導致QApplication::exec: Must be called from the main thread錯誤。 我嘗試了其他一些方法並在下面列出了它們,但顯然它們都不起作用。

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

def startThread(functionName, *args, **kwargs):
    startThread.t = threading.Thread(target=functionName)
    startThread.t.daemon = True
    startThread.t.start()

class UserInput(object):
    def setupUi(self, get_user_input=None):
        # Basic shape
        self.width = 175
        get_user_input.setObjectName("get_user_input")
        get_user_input.resize(175, self.width)

        # Grid layout for the buttons
        self.buttonLayoutGrid = QtWidgets.QWidget(get_user_input)
        self.buttonLayoutGrid.setGeometry(QtCore.QRect(10, 115, 155, 50))
        self.buttonLayoutGrid.setObjectName("buttonLayoutGrid")
        self.buttonLayout = QtWidgets.QGridLayout(self.buttonLayoutGrid)
        self.buttonLayout.setContentsMargins(0, 0, 0, 0)
        self.buttonLayout.setObjectName("buttonLayout")
        self.buttonLayout.setAlignment(PySide2.QtCore.Qt.AlignLeft|PySide2.QtCore.Qt.AlignVCenter)
        # Buttons
        self.buttonOK = QtWidgets.QPushButton(self.buttonLayoutGrid)
        self.buttonOK.setObjectName("buttonOK")
        self.buttonOK.setText("OK")

class FakeBox(PySide2.QtWidgets.QDialog):

    def __init__(self):
        super(FakeBox, self).__init__()
        self.setupUi(self)
        self.buttonProcessCompleted.clicked.connect(self.close)

    def setupUi(self, box_details):
        box_details.setObjectName("box_details")
        box_details.resize(500, 89)
        self.labelProcessStatus = QtWidgets.QLabel(box_details)
        self.labelProcessStatus.setGeometry(QtCore.QRect(10, 0, 221, 51))
        self.labelProcessStatus.setAlignment(QtCore.Qt.AlignCenter)
        self.labelProcessStatus.setWordWrap(True)
        self.labelProcessStatus.setObjectName("labelProcessStatus")
        self.buttonProcessCompleted = QtWidgets.QPushButton(box_details)
        self.buttonProcessCompleted.setEnabled(False)
        self.buttonProcessCompleted.setGeometry(QtCore.QRect(60, 60, 111, 23))
        self.buttonProcessCompleted.setObjectName("buttonProcessCompleted")
        QtCore.QMetaObject.connectSlotsByName(box_details)

class FUNCTION_RUN(PySide2.QtWidgets.QDialog):
    display_dialog_window = PySide2.QtCore.Signal(str)
    display_process_complete = PySide2.QtCore.Signal(str)
    process_complete_no_msg = PySide2.QtCore.Signal()

    def __init__(self):
        super(FUNCTION_RUN, self).__init__()
        self.setupUi(self)
        self.buttonProcessCompleted.clicked.connect(self.close)

    def setupUi(self, functionRunning):
        functionRunning.setObjectName("functionRunning")
        functionRunning.resize(234, 89)
        self.labelProcessStatus = QtWidgets.QLabel(functionRunning)
        self.labelProcessStatus.setGeometry(QtCore.QRect(10, 0, 221, 51))
        self.labelProcessStatus.setAlignment(QtCore.Qt.AlignCenter)
        self.labelProcessStatus.setWordWrap(True)
        self.labelProcessStatus.setObjectName("labelProcessStatus")
        self.buttonProcessCompleted = QtWidgets.QPushButton(functionRunning)
        self.buttonProcessCompleted.setEnabled(False)
        self.buttonProcessCompleted.setGeometry(QtCore.QRect(60, 60, 111, 23))
        self.buttonProcessCompleted.setObjectName("buttonProcessCompleted")
        QtCore.QMetaObject.connectSlotsByName(functionRunning)

    def show_msg(self, msg_text=None):
        self.setWindowTitle("RUNNING")
        self.labelProcessStatus.setText(msg_text)
        self.setModal(False)
        self.show()

    def process_complete(self, msg_text=None):
        self.setWindowTitle("FINISHED")
        self.labelProcessStatus.setText(msg_text)
        self.buttonProcessCompleted.setText('OK')
        self.buttonProcessCompleted.setEnabled(True)
        self.setModal(False)
        self.show()

    def process_finished(self):
        self.hide()

class UserInputPrompt(PySide2.QtWidgets.QDialog, UserInput):

    def __init__(self):
        super(UserInputPrompt, self).__init__()
        self.setupUi(self)
        self.buttonOK.clicked.connect(self.scoreMass)

    def some_more_text(self):
        print('Some more...')

    def scoreMass(self):
        startThread(MASTER.UI.display_msg)

    def display_msg(self):
        def dialog():
            MASTER.UI.hide()
            m = ' Processing things...'
            MASTER.processing_window.display_dialog_window.emit(m)
            MASTER.UI.some_more_text()
            time.sleep(3)
            MASTER.Second_UI.show()
            MASTER.processing_window.process_complete_no_msg.emit()    

        dialog()    

class MASTER(object):
    def __init__(self):
        super(MASTER, self).__init__()
        MASTER.UI = UserInputPrompt()
        MASTER.Second_UI = FakeBox()
        MASTER.processing_window = FUNCTION_RUN()
        MASTER.processing_window.display_dialog_window.connect(MASTER.processing_window.show_msg)
        MASTER.processing_window.display_process_complete.connect(MASTER.processing_window.process_complete)
        MASTER.processing_window.process_complete_no_msg.connect(MASTER.processing_window.process_finished)
        MASTER.UI.show()
        app.exec_()

def main(): 
    MASTER()

if __name__ == '__main__':
    global app
    app = PySide2.QtWidgets.QApplication(sys.argv)
    main()

您擁有MASTER.Second_UI.show()的那一行可能是您遇到問題的地方。 您在主線程中創建了一個實例,這很好,但您需要在 class 中創建一個信號,您可以發出show()方法。 使FakeBox class 看起來像這樣:

class FakeBox(PySide2.QtWidgets.QDialog):
    show_new_prompt = PySide2.QtCore.Signal()

    def __init__(self):
        super(FakeBox, self).__init__()
        self.setupUi(self)
        self.buttonProcessCompleted.clicked.connect(self.close)

然后在您的MASTER class 中看起來像這樣:

class MASTER(object):
    def __init__(self):
        super(MASTER, self).__init__()
        MASTER.UI = UserInputPrompt()
        MASTER.Second_UI = FakeBox()
        MASTER.Second_UI.show_new_prompt.connect(MASTER.Second_UI.show)
        # Keeping everything after this line

最后,在您的display_msg() function 中,將其更改為:

def display_msg(self):
    def dialog():
        MASTER.UI.hide()
        m = ' Processing things...'
        MASTER.processing_window.display_dialog_window.emit(m)
        MASTER.UI.some_more_text()
        time.sleep(3)
        MASTER.processing_window.process_complete_no_msg.emit()    
        MASTER.Second_UI.show_new_prompt.emit()

    dialog()     

這應該按照您描述的進度進行,並將最后一個 window 顯示在最后。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM