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("") # 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):主窗口卡死问题的更多相关文章
- Python pyQt4/pyQt5 学习笔记1(空白窗口,按钮,控件事件,控件提示,窗体显示到屏幕中间,messagebox)
PyQt4是用来编写有图形界面程序(GUI applications)的一个工具包.PyQt4作为一个Python模块来使用,它有440个类和超过6000种函数和方法.同时它也是一个可以在几乎所有主流 ...
- PyQt5学习笔记-从主窗体打开一个子窗体
PyQt5学习笔记-从主窗体打开一个子窗体 软件环境: Eric6+Python3.5+PyQt5 试验目标: 1.点击菜单项Open,打开一个子窗体 2.点击按钮Open,打开一个子窗体 主窗体设计 ...
- Redis:学习笔记-04
Redis:学习笔记-04 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 10. Redis主从复制 1 ...
- 主窗口QMainWindow和启动画面
在较为大型复杂,功能较多的应用程序中,我们通常继承QMainWindow类来进行开发.该主窗口为搭建应用用户界面提供了非常好的框架,请看下图: 可以看出该主窗口类为我们提供了菜单栏(Menu Bar) ...
- pyqt5设计无边框窗口(一)
import sys from PyQt5 import QtGui,QtCore from PyQt5 import QtCore, QtGui, QtWidgets ############### ...
- pyQt5设计无边框窗口(二)
无边框,自定义窗口背景 from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * impor ...
- 【2016-11-6】【坚持学习】【Day21】【子窗口关闭时,同步关闭它的主窗口(方法二)】
根据上文,在子窗口设置一个委托.然后在子窗口关闭事件,执行委托实例,然后在主窗口增加监听委托的方法.... 想想,本事关闭事件就是一个特殊的委托.那么干嘛还要特意去声明一个新的呢?多此一举. 于是有下 ...
- Qt5 主窗口组成
1. 菜单栏 菜单是一系列命令的列表.为了实现菜单.工具栏按钮.键盘快捷键等命令的一致性,Qt使用动作(Action)来表示这些命令.Qt的菜单就是由一系列的QAction动作对象构成的列表,而菜单栏 ...
- eclipse中的Console控制台视图脱离主窗口解决办法
问题:Console控制台视图由于操作不当,跑出来了,脱离了主窗口 解决:在eclipse主窗口最上面的工具条选项中,找到Window,点击里面的Reset Perspective,即可,这样视图就重 ...
随机推荐
- Shell: extract more from listener.log (分析oracle监听日志)
最近遇到了两起数据库连接数不足的问题, 通常都会预留一些会话增加的情况, 但在一些特殊情况下如连接风暴(logon storm), 如果在监听中没有做rate限流,对数据库来说巨大的冲击可能会导致数据 ...
- android-eclips中logcat不显示信息的问题解决
time:2015/11/20 1. logcat窗口不显示问题 解决: 参考[1] 2. logcat中不显示信息 (1)红米手机 (2)解决问题 * 有些文章提到重启eclipse,或者重启手机. ...
- 批处理文件(Batch Files )
后缀是bat的文件就是批处理文件,是一种文本文件.简单的说,它的作用就是自动的连续执行多条命令,批处理文件的内容就是一条一条的命令. 新建一个批处理abc.bat,里面内容如下:@echo offec ...
- (转)透明光照模型与环境贴图之基础理论篇(折射率、色散、fresnel定律) .
摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人” 材质和光的交互除了反射现象,对于透明物 ...
- October 05th 2017 Week 40th Thursday
Happiness is to find someone who can give you warm and share your life together. 幸福,就是找一个温暖的人过一辈子. Y ...
- CSS控制边界、边框与外轮廓
一.CSS控制边界 1.内边距 padding(内边距也叫内填充) padding-bottom 长度/百分比 元件下端边线的空隙 padding-left 长度/百分比 元件左端边线的空隙 padd ...
- Django在admin模块中显示auto_now_add=True或auto_now=True的时间类型列
转载自: http://www.tuicool.com/articles/ZryE7f 在Django如果model中的列定义了auto_now_add或auto_now属性,那么这种列不会在admi ...
- 【转】Android随笔之——PackageManager详解
参考:http://www.cnblogs.com/xingfuzzhd/p/3374504.html 今天要讲的是PackageManager.Android系统为我们提供了很多服务管理的类,包括A ...
- Linux MySql5.6.38安装过程
1.下载mysql安装包mysql-5.6.38-linux-glibc2.12-x86_64.tar.gz 2.用xftp工具将其上传到Linux服务器上的soft文件夹,没有的话先创建 [root ...
- github与git基本操作(一)
一.git上传本地项目到github 前提:github创建一个空仓库(得到“https://自己的仓库url地址”)1.第一步:就是要进入这个目录下,cmd2.第二步:输入git init3.第三步 ...