• PyQt5 笔记(04):主窗口卡死问题


    本文基于:windows 7 + python 3.4

    知识点:

     1. 将 time.sleep 替换为 QTimer

     2. 将 time.sleep 放入到 QThread

     3. 使用 QThread 自己的 sleep 方法

    我们希望实现一个这样的小程序:

                                       

    当点击开始按钮的时候,下面的文本标签每隔一秒自动加1。

    一、直接用 time.sleep(1)

    import time
    
    class TestWindow(QDialog):
        def __init__(self):
            # ...
    
            btn1.clicked.connect(self.update) # 按钮连接到槽
            # ...
            
        def update(self):
            for i in range(20):
                time.sleep(1) # 每隔一秒
                self.sec += 1
                self.sec_label.setText(str(self.sec))

    看起来没有任何逻辑上的错误。

    那就运行一下看看,点击按钮。。。神马情况?主界面卡死了!如图

    我猜测这可能与python的GIL问题有关:

      1. time库是纯python的,而PyQt的背后是Qt,这是纯C++的。

      2. 换句话说,就是time.sleep(1)时,并没有将CPU控制权交还给Qt,从而造成界面卡死

    解决这个问题,既然不能用 python 的 time 库,那就用 PyQt 自己的 QTimer 类

    二、使用 QTimer 类

    class TestWindow(QDialog):
        def __init__(self):
            
            # ...
    
            timer = QTimer() # 计时器
            timer.timeout.connect(self.update)
    
            btn1.clicked.connect(lambda :timer.start(1000)) # 启动计时器,间隔1秒
            btn2.clicked.connect(lambda :timer.stop())
            
    
        def update(self):
            self.sec += 1
            self.sec_label.setText(str(self.sec))

    再运行一下。。。 OK,搞定!如图:

    完整代码:

    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    import sys
    
    class TestWindow(QDialog):
        def __init__(self):
            
            super().__init__()
    
            self.sec = 0
    
            btn1 = QPushButton("Start", self)
            btn2 = QPushButton("Stop", self)
            self.sec_label = QLabel(self)
    
            layout = QGridLayout(self)
            layout.addWidget(btn1,0,0)
            layout.addWidget(btn2,0,1)
            layout.addWidget(self.sec_label,1,0,1,2)
    
            timer = QTimer()
            timer.timeout.connect(self.update) # 计时器挂接到槽:update
            btn1.clicked.connect(lambda :timer.start(1000))
            btn2.clicked.connect(lambda :timer.stop())
            
    
        def update(self):
            self.sec += 1
            self.sec_label.setText(str(self.sec))
    
    
    app=QApplication(sys.argv)
    form=TestWindow()
    form.show()
    app.exec_()

    三、将 time.sleep 放入到 QThread

    解决这个问题的另外一个思路:开一个线程,专门用于计时(即:专门运行 time.sleep)

    在 QThread 中使用 time.sleep 和 for 循环,无压力!

    当然,线程与主窗口的通信使用了信号/槽。

                                           

    代码如下:

    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    import sys
    import time
    
    class TestWindow(QDialog):
        def __init__(self):
            super().__init__()
    
            btn1 = QPushButton("Start", self)
            btn2 = QPushButton("Stop", self)
            self.sec_label = QLabel(self)
    
            layout = QGridLayout(self)
            layout.addWidget(btn1,0,0)
            layout.addWidget(btn2,0,1)
            layout.addWidget(self.sec_label,1,0,1,2)
    
            
            thread = MyThread() # 创建一个线程 
            thread.sec_changed_signal.connect(self.update) # 线发过来的信号挂接到槽:update
            btn1.clicked.connect(lambda :thread.start())
            btn2.clicked.connect(lambda :thread.terminate()) # 线程中止
    
    
        def update(self, sec):  
            self.sec_label.setText(str(sec))
            
            
      
    class MyThread(QThread):  
      
        sec_changed_signal = pyqtSignal(int) # 信号类型:int
      
        def __init__(self, sec=1000, parent=None):  
            super().__init__(parent)
            self.sec = sec # 默认1000秒
      
        def run(self):  
            for i in range(self.sec):
                self.sec_changed_signal.emit(i)  #发射信号
                time.sleep(1)
                  
      
    app = QApplication(sys.argv)
    form = TestWindow()
    form.show()
    app.exec_()

    4. QThread 自身也有一个 sleep 方法

    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    
    import sys
    
    
    class Test(QDialog):
        def __init__(self,parent=None):
            super().__init__(parent)
            
            self.file_list = QListWidget()
            self.btn = QPushButton('Start')
            layout = QGridLayout(self)
            layout.addWidget(self.file_list,0,0,1,2)
            layout.addWidget(self.btn,1,1)
            
            self.thread = Worker()
            self.thread.file_changed_signal.connect(self.update_file_list)
            self.btn.clicked.connect(self.thread_start)
            
        def update_file_list(self, file_inf):
            self.file_list.addItem(file_inf)
            
        def thread_start(self):
            self.btn.setEnabled(False)
            self.thread.start()
            
            
            
    class Worker(QThread):
        
        file_changed_signal = pyqtSignal(str) # 信号类型:str
        
        def __init__(self, sec=0, parent=None):
            super().__init__(parent)
            self.working = True
            self.sec = sec
            
        def __del__(self):
            self.working = False
            self.wait()
            
        def run(self):
            while self.working == True:
                self.file_changed_signal.emit('当前秒数:{}'.format(self.sec))
                self.sleep(1)
                self.sec += 1
                
    app = QApplication(sys.argv)
    dlg = Test()
    dlg.show()
    sys.exit(app.exec_())

    :QObject -> moveToThread 方式应用 QThread

    from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QGridLayout
    from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
    
    import time
    import sys
    
    class Worker(QObject):
        finished = pyqtSignal()
        intReady = pyqtSignal(int)
    
        @pyqtSlot()
        def work(self): # A slot takes no params
            for i in range(1, 100):
                time.sleep(1)
                self.intReady.emit(i)
    
            self.finished.emit()
            
    
    class Form(QWidget):
        def __init__(self):
           super().__init__()
           self.label = QLabel("0")
    
           # 1 - create Worker and Thread inside the Form
           self.worker = Worker()  # no parent!
           self.thread = QThread()  # no parent!
    
           self.worker.intReady.connect(self.updateLabel)
           self.worker.moveToThread(self.thread)
           self.worker.finished.connect(self.thread.quit)
           self.thread.started.connect(self.worker.work)
           #self.thread.finished.connect(app.exit)
    
           self.thread.start()
    
           self.initUI()
    
        def initUI(self):
            grid = QGridLayout()
            self.setLayout(grid)
            grid.addWidget(self.label,0,0)
    
            self.move(300, 150)
            self.setWindowTitle('thread test')
    
        def updateLabel(self, i):
            self.label.setText("{}".format(i))
            #print(i)
    
            
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec_())
  • 相关阅读:
    IOS性别
    IOS生命周期
    读书的作用
    Core Data
    解析Json数据
    sicily Knight Moves
    sicily 简单魔板2
    Sum of Consecutive Primes
    [OI笔记]杂题整理1(基础篇~)
    [IOI1994]The Castle
  • 原文地址:https://www.cnblogs.com/hhh5460/p/5175322.html
Copyright © 2020-2023  润新知