【问题标题】:PyQt Threading: GUI interaction Crashes ProgramPyQt 线程:GUI 交互崩溃程序
【发布时间】:2017-06-07 19:12:42
【问题描述】:

我正在设计一个程序来编辑 DICOM。具体来说,我在与我的 PyQt UI 进行适当交互时遇到问题。

我希望能够单击“暂停”和“停止”按钮来暂停或停止我的编辑功能。我的编辑功能需要大量时间来处理/循环。根据它正在编辑的文件数量,它可能需要 30 秒到一个多小时不等。正因为如此,我决定使用 Qt 的本机线程功能将我的编辑功能投入到它自己的线程中。我能够让线程工作,即:从我的 MainWindow 类中,我可以单击一个初始化我的编辑类的按钮(类编辑(QThread),但是与 GUI 交互仍然会使程序崩溃,我不知道为什么!下面我添加了我正在使用的一般代码结构/设置的示例。

class anonymizeThread(QThread):
    def __init__(self):
        QThread.__init__(self)


    def __del__(self):
        self.wait()

    #def sendAnon(self, progress_val):
     #   self.completed = 0
      #  return self.completed


    def run(self):
            # while self.completed < 100:
            #     self.completed += 0.00001
            #     self.emit(QtCore.SIGNAL('PROGRESS'), self.completed)
            # ANONYMIZE FUNCTION!
            i = 0
            #flag = self.stop_flag
            while i < 10000000: # and self.stop_flag is not 1:
                print(i)
                i+=1
            print('i didnt enter the loop')


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)

        # connect the buttons
        self.worker = anonymizeThread()
        self.anonbtn.clicked.connect(self.anonymize)
        self.open_directory.clicked.connect(self.open_dir)
        self.pause.clicked.connect(self.paused)
        self.stopbtn.clicked.connect(self.stopped)

        # block button signals to start
        self.pause.blockSignals(True)
        self.stopbtn.blockSignals(True)

        self.dir_name = None
        self.pause_flag = None
        self.stop_flag = None
        self.anon_flag = None

        # This is how we quit from the main menu "File" option
        extractAction = self.actionQuit_Ctrl_Q
        extractAction.setShortcut("Ctrl+Q")
        extractAction.setStatusTip('Leave The App')
        extractAction.triggered.connect(self.close_application)



    def updateProgressBar(self,val):
        self.progressBar.setValue(val)
    def close_application(self):
        choice = QMessageBox.question(self, 'Just had to check...', "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            sys.exit()
        else:
            pass

    def anonymize(self):
        self.pause.blockSignals(False)
        self.stopbtn.blockSignals(False)
        self.worker.start()
        # check if directory chosen
        # self.progressBar.setMaximum(len(dcm)
        # start our anon thread!


    def paused(self):
        #only if running
        if self.pause_flag is 0:
            self.pause_flag = 1
            self.pause.setText('Start')
        elif self.pause_flag is 1:
            self.pause_flag = 0
            self.pause.setText('Pause')
        else:
            pass

    def stopped(self): # need a self.stop() for anonThread

        choice = QMessageBox.question(self,'Stop', "Are you sure you want to stop? You will not be able to pick up exactly where you left off.",
                              QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            self.stop_flag = 1
            #self.stopbtn.blockSignals(True)
            #self.paused.blockSignals(True)
        else:
            pass


    def open_dir(self):
        self.dir_name = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
        if len(self.dir_name) is not 0:
            self.anon_flag = 0




def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

【问题讨论】:

  • 你为什么使用blockSignals?,也请分享一个可重现的代码,因为在editThread类中不存在stop_flag属性
  • 当然应该是:self.worker.stop_flag = 1?显然,您需要将self.stop_flag = 0 放入editThread.__init__。
  • 堆栈跟踪在哪里?
  • 我想阻止在编辑线程运行之前点击停止按钮和暂停按钮的能力。我的想法是在 MainWindow 类和 def stop(self) 函数下调用 self.stop_flag = 1 就足以将 stop_flag 的值更改为 1 我在复制和粘贴时不小心遗漏了一行代码:self.stop_flag = 0 在 MainWindow 的 init 中。我在上面的问题中改变了它
  • @eyllanesc 总的来说,我是 pyqt/qt 的新手。我以为类线程会从 MainWindow 继承“self”?

标签: python multithreading qt pyqt pyqt5


【解决方案1】:

建议不要直接访问标志,最好通过函数来​​透明地使用它,因为同一个类应该验证任务。

另外最好给一点延迟,以便应用程序可以处理图形部分,另一个可能的改进是避免使用 sys.exit,您可以调用关闭窗口的 close 方法。

在下面的代码中,我实现了 stop 和 pause 方法。

class anonymizeThread(QThread):
    def __init__(self):
        QThread.__init__(self)
        self.onRunning = True
        self.onStop = False


    def __del__(self):
        self.wait()

    def stop(self):
        self.onStop = True

    def pause(self):
        if self.isRunning():
            self.onRunning = not self.onRunning


    def run(self):
            i = 0
            #flag = self.stop_flag
            while i < 10000000:
                if self.onRunning: # and self.stop_flag is not 1:
                    print(i)
                    i+=1

                if self.onStop:
                    break
                QThread.msleep(10)
            print('i didnt enter the loop')


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)


        # connect the buttons
        self.worker = anonymizeThread()
        self.anonbtn.clicked.connect(self.anonymize)
        self.pause.clicked.connect(self.paused)
        self.stopbtn.clicked.connect(self.stopped)

        # block button signals to start
        self.pause.blockSignals(True)
        self.stopbtn.blockSignals(True)

    def close_application(self):
        choice = QMessageBox.question(self, 'Just had to check...', "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            self.close()

    def anonymize(self):
        self.pause.blockSignals(False)
        self.stopbtn.blockSignals(False)
        self.worker.start()


    def paused(self):
        self.worker.pause()

    def stopped(self): # need a self.stop() for anonThread

        choice = QMessageBox.question(self,'Stop', "Are you sure you want to stop? You will not be able to pick up exactly where you left off.",
                              QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            self.worker.stop()

【讨论】:

  • QThread.msleep() 有必要吗?从技术上讲,它会减慢该线程的操作。没有它,是否有可能单击按钮仍然会使程序崩溃?感谢您实施暂停,它工作得很好。我会投票,但显然我需要 15 声望才能这样做:)
【解决方案2】:

感谢@eyllansec 和@ekhumoro..

在上面的代码中,self.stop_flag = ... 的所有实例都应该是self.worker.stop_flag = ...,因为它正在更改要在工作类/线程中使用的变量。我的错误是假设两个类都继承了相同的“自我”。

如果还有其他错误或对我做错的更好的解释,请发布答案,我会接受!

【讨论】:

    猜你喜欢
    • 2016-07-01
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多