【问题标题】:PyQt: execution order not correct when setting and clearing layoutPyQt:设置和清除布局时执行顺序不正确
【发布时间】:2017-11-18 16:16:56
【问题描述】:
self.dialogBox.close() 
self.state = "processing"
self.__clearLayout(self.layout)
print(1)
self.controller.process()

我正在运行以下代码。对话框是一个 PyQt 对话框,self.state 只是一个当前状态,self.__clearLayout 清除 PyQt 小部件的当前布局,print(1) 用于调试目的,self.controller.process() 是处理事物并需要一会儿。我的目标是清除 UI,然后有我的程序进程,但它是乱序的。它打印 1 并开始处理,这告诉我它是有序的,但 UI 直到处理完成后才设置。有关如何解决此问题的任何提示?

def processUi(self):
    self.dialogBox.close()
    self.state = "processing"
    self.__clearLayout(self.layout)

    #label
    processingLabel = QtWidgets.QLabel()
    processingLabel.setMaximumSize(500, 500)
    processingLabel.setFont(QtGui.QFont("Mono", 16))
    processingLabel.setText("Processing...may take a few minutes.")

    #set the current layout
    currentLayout = QtWidgets.QVBoxLayout()
    currentLayout.setContentsMargins(0,0,0,0)
    currentLayout.addStretch()
    currentLayout.addWidget(processingLabel)
    currentLayout.addStretch()

    self.layout.addStretch()
    self.layout.addLayout(currentLayout)
    self.layout.addStretch()

    #thread for processing
    processing = multiprocessing.Process(target=self.controller.process, args=())
    processing.start()
    processing.join()
    self.finishedUi()

这是完整的代码。目标是设置您在处理工作时看到的 ui 代码,一旦处理完成,调用finishedUi。

【问题讨论】:

    标签: python events layout pyqt


    【解决方案1】:

    我假设您的 clear-layout 方法与我在 this answer 中给出的方法相同。

    如果是这样,问题出在deleteLater,它会延迟删除,直到控制返回到事件循环。无法强制处理延迟删除事件(例如,QApplication.processEvents() 将无效)。相反,有必要使用sip module 直接删除小部件:

    import sip
    
    def clearLayout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    sip.delete(widget)
                else:
                    self.clearLayout(item.layout())
    

    如果使用多线程,则无需理会sip.delete,因为任何待处理的删除事件都会在线程启动后立即处理。下面是一个简单的演示,展示了如何使用线程进行非阻塞处理:

    from PyQt5 import QtCore, QtWidgets
    
    class Worker(QtCore.QThread):
        progressChanged = QtCore.pyqtSignal(int)
    
        def run(self):
            for tick in range(10):
                self.msleep(500)
                self.progressChanged.emit(tick + 1)
    
    class Window(QtWidgets.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.button = QtWidgets.QPushButton('Start', self)
            self.button.clicked.connect(self.handleButton)
            self.frame = QtWidgets.QFrame(self)
            self.frame.setMinimumHeight(100)
            self.frame.setLayout(QtWidgets.QVBoxLayout())
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.frame)
            layout.addWidget(self.button)
    
        def handleButton(self):
            self.button.setEnabled(False)
            self.clearLayout(self.frame.layout())
            label = QtWidgets.QLabel('Starting')
            self.frame.layout().addWidget(label)
            def progress(tick):
                label.setText('Processing... Count = %d' % tick)
            def finish():
                label.setText('Finished')
                self.button.setEnabled(True)
                thread.deleteLater()
            thread = Worker(self)
            thread.finished.connect(finish)
            thread.progressChanged.connect(progress)
            thread.start()
    
        def clearLayout(self, layout):
            if layout is not None:
                while layout.count():
                    item = layout.takeAt(0)
                    widget = item.widget()
                    if widget is not None:
                        widget.deleteLater()
                    else:
                        self.clearLayout(item.layout())
    
    if __name__ == '__main__':
    
        app = QtWidgets.QApplication(['test'])
        window = Window()
        window.setGeometry(600, 100, 300, 200)
        window.show()
        app.exec_()
    

    【讨论】:

    • 你是对的,我使用的是你发布的上一个版本,我将其更改为这个版本,但是行为没有改变。用户界面仍然冻结。
    • @Aleks。行为确实改变了。如果您在清除布局后输入QApplication.processEvents(),则ui 立即更新。但是,您的处理功能会阻塞,因此之后仍然无法与 ui 交互。为此,您需要在单独的线程中进行处理。不过,这是一个完全不同的问题 - 您当前的问题仅询问如何在清除布局时修复执行顺序。
    • 谢谢,我知道使用不同的进程,我正在使用它们,我只是简化了这个问题的代码。添加我不知道的 QApplication.processEvents() 后,行为发生了变化。但是,现在它在过程完成期间和之后阻止了我的整个用户界面。当我点击 ui 上的任意位置时,计算机只会发出阻塞噪音。
    • @Aleks。我已经解决了您原始问题中所述的具体问题。当然你的用户界面会阻塞:正如我所说,如果你想避免这种情况,你需要在单独的线程中进行处理。
    • 正如我之前所说,我显示的代码只是我所做工作的简化版本。 self.controller.process() 实际上被称为一个单独的进程。在我添加 QApplication.processEvents() 行后,我的用户界面在进程完成后保持阻塞。如果没有这一行,我的 ui 至少在进程完成后会被解除阻塞。再次感谢所有帮助。
    猜你喜欢
    • 2018-07-10
    • 1970-01-01
    • 2023-03-24
    • 2015-12-13
    • 2019-06-23
    • 1970-01-01
    • 2011-05-30
    • 2013-05-12
    • 1970-01-01
    相关资源
    最近更新 更多