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

mac2022-06-30  82

本文基于: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_())

 

转载于:https://www.cnblogs.com/hhh5460/p/5175322.html

相关资源:PyQt5多线程防卡死和多窗口用法的实现
最新回复(0)