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

PyQt5 笔记(04):主窗口卡死问题的更多相关文章

  1. Python pyQt4/pyQt5 学习笔记1(空白窗口,按钮,控件事件,控件提示,窗体显示到屏幕中间,messagebox)

    PyQt4是用来编写有图形界面程序(GUI applications)的一个工具包.PyQt4作为一个Python模块来使用,它有440个类和超过6000种函数和方法.同时它也是一个可以在几乎所有主流 ...

  2. PyQt5学习笔记-从主窗体打开一个子窗体

    PyQt5学习笔记-从主窗体打开一个子窗体 软件环境: Eric6+Python3.5+PyQt5 试验目标: 1.点击菜单项Open,打开一个子窗体 2.点击按钮Open,打开一个子窗体 主窗体设计 ...

  3. Redis:学习笔记-04

    Redis:学习笔记-04 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 10. Redis主从复制 1 ...

  4. 主窗口QMainWindow和启动画面

    在较为大型复杂,功能较多的应用程序中,我们通常继承QMainWindow类来进行开发.该主窗口为搭建应用用户界面提供了非常好的框架,请看下图: 可以看出该主窗口类为我们提供了菜单栏(Menu Bar) ...

  5. pyqt5设计无边框窗口(一)

    import sys from PyQt5 import QtGui,QtCore from PyQt5 import QtCore, QtGui, QtWidgets ############### ...

  6. pyQt5设计无边框窗口(二)

    无边框,自定义窗口背景 from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * impor ...

  7. 【2016-11-6】【坚持学习】【Day21】【子窗口关闭时,同步关闭它的主窗口(方法二)】

    根据上文,在子窗口设置一个委托.然后在子窗口关闭事件,执行委托实例,然后在主窗口增加监听委托的方法.... 想想,本事关闭事件就是一个特殊的委托.那么干嘛还要特意去声明一个新的呢?多此一举. 于是有下 ...

  8. Qt5 主窗口组成

    1. 菜单栏 菜单是一系列命令的列表.为了实现菜单.工具栏按钮.键盘快捷键等命令的一致性,Qt使用动作(Action)来表示这些命令.Qt的菜单就是由一系列的QAction动作对象构成的列表,而菜单栏 ...

  9. eclipse中的Console控制台视图脱离主窗口解决办法

    问题:Console控制台视图由于操作不当,跑出来了,脱离了主窗口 解决:在eclipse主窗口最上面的工具条选项中,找到Window,点击里面的Reset Perspective,即可,这样视图就重 ...

随机推荐

  1. Shell: extract more from listener.log (分析oracle监听日志)

    最近遇到了两起数据库连接数不足的问题, 通常都会预留一些会话增加的情况, 但在一些特殊情况下如连接风暴(logon storm), 如果在监听中没有做rate限流,对数据库来说巨大的冲击可能会导致数据 ...

  2. android-eclips中logcat不显示信息的问题解决

    time:2015/11/20 1. logcat窗口不显示问题 解决: 参考[1] 2. logcat中不显示信息 (1)红米手机 (2)解决问题 * 有些文章提到重启eclipse,或者重启手机. ...

  3. 批处理文件(Batch Files )

    后缀是bat的文件就是批处理文件,是一种文本文件.简单的说,它的作用就是自动的连续执行多条命令,批处理文件的内容就是一条一条的命令. 新建一个批处理abc.bat,里面内容如下:@echo offec ...

  4. (转)透明光照模型与环境贴图之基础理论篇(折射率、色散、fresnel定律) .

     摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人” 材质和光的交互除了反射现象,对于透明物 ...

  5. October 05th 2017 Week 40th Thursday

    Happiness is to find someone who can give you warm and share your life together. 幸福,就是找一个温暖的人过一辈子. Y ...

  6. CSS控制边界、边框与外轮廓

    一.CSS控制边界 1.内边距 padding(内边距也叫内填充) padding-bottom 长度/百分比 元件下端边线的空隙 padding-left 长度/百分比 元件左端边线的空隙 padd ...

  7. Django在admin模块中显示auto_now_add=True或auto_now=True的时间类型列

    转载自: http://www.tuicool.com/articles/ZryE7f 在Django如果model中的列定义了auto_now_add或auto_now属性,那么这种列不会在admi ...

  8. 【转】Android随笔之——PackageManager详解

    参考:http://www.cnblogs.com/xingfuzzhd/p/3374504.html 今天要讲的是PackageManager.Android系统为我们提供了很多服务管理的类,包括A ...

  9. Linux MySql5.6.38安装过程

    1.下载mysql安装包mysql-5.6.38-linux-glibc2.12-x86_64.tar.gz 2.用xftp工具将其上传到Linux服务器上的soft文件夹,没有的话先创建 [root ...

  10. github与git基本操作(一)

    一.git上传本地项目到github 前提:github创建一个空仓库(得到“https://自己的仓库url地址”)1.第一步:就是要进入这个目录下,cmd2.第二步:输入git init3.第三步 ...