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

简介: 本文基于:windows 7 + python 3.4 知识点:  1. 将 time.sleep 替换为 QTimer  2. 将 time.sleep 放入到 QThread  3. 使用 QThread 自己的 sleep 方法   我们希望实现一个这样的小程序:                                         当点击开始按钮的时候,下面的文本标签每隔一秒自动加1。

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

 

目录
相关文章
|
缓存 Android开发
Android Studio中如何清理gradle缓存
Android Studio中如何清理gradle缓存
|
4月前
|
机器学习/深度学习 自然语言处理 API
Qwen-MT:翻得快,译得巧
今天,机器翻译模型Qwen-MT正式上线,支持92种语言互译,具备高度可控性与低延迟、低成本特点,适用于多种场景。开发者可通过Qwen API体验其强大翻译能力。
1023 15
|
7月前
|
存储 算法 安全
MD5加密
MD5(Message-Digest Algorithm 5)是一种单向加密算法,将任意长度的数据转换为128位固定长度的散列值,主要用于数据完整性校验和密码存储。其特点包括不可逆运算、高度离散性和相同输入生成一致结果。然而,MD5存在碰撞风险,直接加密密码不安全,需配合“加盐”处理增强安全性。文中提供了未加盐的MD5工具类`MD5Utils`,核心方法`msgToMD5`实现基本加密功能。尽管MD5理论上不可逆,但通过彩虹表等手段可能存在伪破解风险,建议结合多种加密算法提升安全性。
486 2
|
Web App开发 搜索推荐 Linux
xwiki使用指南
xwiki使用指南
971 0
|
9月前
|
人工智能
MIT 76页深度报告:AI加速创新马太效应,科学家产出分化加剧!缺乏判断力将被淘汰
近日,麻省理工学院(MIT)发布了一份76页的深度研究报告,探讨AI对科学发现和创新的影响。研究对象为1018名美国科学家,结果显示AI使新材料发现增加44%,专利申请增长39%,产品创新提升17%。然而,AI对高能力科学家的产出提升更显著,加剧了科学家间的分化。AI还改变了科学家的工作内容,减少了创意构思时间,增加了评估任务,导致工作满意度下降,但科学家对AI的信心增强。报告全面分析了AI带来的机遇与挑战。论文地址:https://conference.nber.org/conf_papers/f210475.pdf
347 14
WK
|
C++ 开发者
QDialog
QDialog是Qt框架中的对话框基类,支持模态和非模态两种模式。它提供了丰富的函数,如exec()、show()、accept()和reject(),用于控制对话框的行为。QDialog可以包含多种控件,用于收集用户输入或显示信息。此外,Qt还提供了一些常用的标准化对话框,如QColorDialog、QFileDialog、QFontDialog、QInputDialog和QMessageBox,方便开发者快速实现特定功能。
WK
360 2
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
1548 2
|
SQL 安全 关系型数据库
【100天精通python】Day38:GUI界面编程_PyQt 从入门到实战(中)_数据库操作与多线程编程
【100天精通python】Day38:GUI界面编程_PyQt 从入门到实战(中)_数据库操作与多线程编程
552 0
|
存储 安全 Linux
python文件操作open的使用方法详解
python文件操作open的使用方法详解
406 2
|
jenkins 测试技术 持续交付