zoukankan      html  css  js  c++  java
  • PyQt5执行耗时操作导致主页面暂时性卡死问题

    问题: PyQt5主界面,如果某些操作比较耗时,比如点击按钮执行某个脚本、点击按钮从网络上读取数据等,则点击按钮后,很可能造成整个主窗口卡死,无法执行窗口最大化、最小化、文本输入、按钮点击等其他操作。

    例子如下:

        程序执行后,有个按钮和一个QLabel显示框,点击按钮后,会计算1+2+...+50000000,整个计算需要十几秒,计算过程中,主界面卡死,无法执行窗口最大化等操作。

    # coding=utf-8
    import sys
    from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QHBoxLayout, QPushButton)
    from PyQt5.QtGui import QIcon
    from PyQt5.QtCore import Qt, QThread, pyqtSignal


    class MyGui(QWidget):
    """基础Gui页面"""

    def __init__(self):
    super().__init__()
    self.init_ui() # 主窗口

    def init_ui(self):
    # self.setGeometry(300, 300, 600, 200) # 设置主窗口的位置和大小
    self.setWindowTitle('Simple Little tools') # 设置主窗口的标题
    self.setWindowIcon(QIcon('pics/icon1.gif')) # 设置主窗口的图标(左上角)

    # 通用样式。页面上创建的一些部件,会默认使用设置的通用样式
    self.setStyleSheet(
    'QPushButton{font-weight: bold; background: skyblue; border-radius: 14px;'
    ' 300px; height: 28px; font-size: 20px; text-align: center;}'
    'QPushButton:hover{background: rgb(50, 150, 255);}'
    'QLabel{font-weight: bold; font-size: 20px; color: orange}'
    )
    self.main_layout() # 布局函数
    self.show() # 显示窗口

    def main_layout(self):

    btn = QPushButton(' 点击计算1+2+...+50000000 ')
    show_label = QLabel(' ')
    show_label.setStyleSheet('color: black; background: skyblue; border-radius: 13px;')
    hbox = QHBoxLayout()
    hbox.addWidget(btn)
    hbox.addWidget(show_label)
    hbox.addStretch()
    self.setLayout(hbox)
    btn.clicked.connect(lambda: self.update_text(show_label))

    def update_text(self, w):
    txt = str(self.cal_sum(50000000))
    w.setText(' ' + txt + ' ')

    def cal_sum(self, n=100):
    sum, k = 0, 0
    while k <= n:
    sum += k
    k += 1
    return sum


    if __name__ == '__main__':
    app = QApplication(sys.argv)
    qt = MyGui()
    sys.exit(app.exec_())

    几个截图如下::

              

    截图1是程序执行完的情况,主窗口显示了一个按钮个一个QLabel; 截图2是点击按钮执行累加操作的情况,此时主页面卡死,
    窗口无法最大化、最小化等; 截图3是加法操作执行完的情况,此时界面恢复正常。

    推测原因: 在PyQt中,GUI界面本身就是一个主线程,当点击按钮执行累加操作时,因为这个累加操作直接跑在这个主线程上,GUI需要等待累加操作完成后才会响应,在等待这段时间,整个GUI就处于卡死的状态。如果这个操作是一个死循环(比如按秒更新时间),在windows下,系统会认为这个程序运行出错了,会自动显示未响应,进而关闭程序。

    这里可以考虑另开一个线程来执行这个累加操作。(PyQt5的QThread)

    # coding=utf-8
    import sys
    from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QHBoxLayout, QPushButton)
    from PyQt5.QtGui import QIcon
    from PyQt5.QtCore import Qt, QThread, pyqtSignal


    class MyGui(QWidget):
    """基础Gui页面"""

    def __init__(self):
    super().__init__()
    self.init_ui() # 主窗口

    def init_ui(self):
    # self.setGeometry(300, 300, 600, 200) # 设置主窗口的位置和大小
    self.setWindowTitle('Simple Little tools') # 设置主窗口的标题
    self.setWindowIcon(QIcon('pics/icon1.gif')) # 设置主窗口的图标(左上角)

    # 通用样式。页面上创建的一些部件,会默认使用设置的通用样式
    self.setStyleSheet(
    'QPushButton{font-weight: bold; background: skyblue; border-radius: 14px;'
    ' 300px; height: 28px; font-size: 20px; text-align: center;}'
    'QPushButton:hover{background: rgb(50, 150, 255);}'
    'QLabel{font-weight: bold; font-size: 20px; color: orange}'
    )
    self.main_layout() # 布局函数
    self.show() # 显示窗口

    def main_layout(self):
    self.btn = QPushButton(' 点击计算1+2+...+50000000 ')
    self.show_label = QLabel(' ')
    self.show_label.setStyleSheet('color: black; background: skyblue; border-radius: 13px;')
    hbox = QHBoxLayout()
    hbox.addWidget(self.btn)
    hbox.addWidget(self.show_label)
    hbox.addStretch()
    self.setLayout(hbox)
    self.btn.clicked.connect(self.update) # 按钮点击后创建一个新的线程

    def update(self):
    self.btn.setText(' 计算中... ') # 主页面按钮点击后更新按钮文本
    self.btn.setEnabled(False) # 将按钮设置为不可点击
    self.cal = CalSumTheard() # 创建一个线程
    self.cal._sum.connect(self.update_sum) # 线程发过来的信号挂接到槽函数update_sum
    self.cal.start() # 线程启动

    def update_sum(self, r):
    self.show_label.setText(' ' + r + ' ') # 信号发过来时,更新QLabel内容
    self.btn.setText(' 点击计算1+2+...+50000000 ') # 更新按钮
    self.btn.setEnabled(True) # 让按钮恢复可点击状态


    class CalSumTheard(QThread):
    """该线程用于计算耗时的累加操作"""
    _sum = pyqtSignal(str) # 信号类型 str

    def __init__(self):
    super().__init__()

    def run(self):
    s, k = 0, 0
    while k <= 50000000:
    s += k
    k += 1
    self._sum.emit(str(s)) # 计算结果完成后,发送结果


    if __name__ == '__main__':
    app = QApplication(sys.argv)
    qt = MyGui()
    sys.exit(app.exec_())
    效果如下:

              

    截图1就是程序执行完成后的效果;截图2就是按钮点击后的效果(新加了个点击按钮后更新按钮文字和将按钮设置为不可点击),
    此时主页面可以正常拖动、最大、最小化;截图三累加操作执行完成后的效果(鼠标放在按钮会变色,此时鼠标是放在按钮上的)


    另写了一个QThread,按秒更新时间
    # coding=utf-8
    import sys
    from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QFrame,
    QSplitter, QHBoxLayout, QVBoxLayout)
    from PyQt5.QtGui import QIcon
    from PyQt5.QtCore import Qt, QThread, pyqtSignal
    import time


    class MyGui(QWidget):
    """基础Gui页面"""

    def __init__(self):
    super().__init__()
    self.update() # 因为没有按钮触发新线程,因此需要在初始化时就先执行线程函数
    self.init_ui() # 主窗口

    def init_ui(self):
    self.setGeometry(300, 300, 800, 400) # 设置主窗口的位置和大小
    self.setWindowTitle('Simple Little tools') # 设置主窗口的标题
    self.setWindowIcon(QIcon('pics/icon1.gif')) # 设置主窗口的图标(左上角)

    # 通用样式。页面上创建的一些部件,会默认使用设置的通用样式
    self.setStyleSheet(
    'QPushButton{font-weight: bold; background: skyblue; border-radius: 14px;'
    ' 64px; height: 28px; font-size: 20px; text-align: center;}'
    'QPushButton:hover{background: rgb(50, 150, 255);}'
    'QLabel{font-weight: bold; font-size: 20px; color: orange}'
    )
    self.main_layout() # 布局函数
    self.show() # 显示窗口

    def main_layout(self):
    hbox = QHBoxLayout() # 外层大盒
    top_left = QFrame()
    top_left.setFrameShape(QFrame.StyledPanel)
    splitter_top = QSplitter(Qt.Horizontal) # 横向QSpli
    splitter_top.addWidget(top_left)

    # 当前时间显示
    current_time_text_label = QLabel('当前时间:')
    self.current_time_show_label = QLabel()
    self.current_time_show_label.setStyleSheet('color: black; background: skyblue;'
    'border-radius: 13px; font-size: 26px')
    current_time_hbox = QHBoxLayout()
    current_time_hbox.addWidget(current_time_text_label)
    current_time_hbox.addWidget(self.current_time_show_label)
    current_time_hbox.addStretch()

    current_time_vbox = QVBoxLayout()
    current_time_vbox.addLayout(current_time_hbox)
    current_time_vbox.addStretch()

    top_left.setLayout(current_time_vbox)
    hbox.addWidget(splitter_top)
    self.setLayout(hbox)

    def update(self):
    self.current = CurrentTimeThread() # 创建一个线程
    self.current._update_time.connect(self.update_time) # 连接到槽函数update_time上
    self.current.start()

    def update_time(self, curr_time):
    self.current_time_show_label.setText(' ' + curr_time + ' ') # 更信QLabel的时间


    class CurrentTimeThread(QThread):
    _update_time = pyqtSignal(str) # 信号类型 str

    def __init__(self):
    super().__init__()

    def run(self):
    while 1:
    current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
    self._update_time.emit(current_time)
    time.sleep(1) # 每秒发送一个信号


    if __name__ == '__main__':
    app = QApplication(sys.argv)
    qt = MyGui()
    sys.exit(app.exec_())
    效果图:

      

    截图中看不到效果,实际效果是每秒都会更新时间,这种循环更新的还可以使用QTimer来处理。

    总结:
    1. 比较耗时的任务最好使用新线程去处理(QThread),主线程仅仅用于GUI显示
    2. 循环任务可以使用新线程去处理,也可以使用QTimer去处理(这里没有演示QTimer)
    3. 新线程要创建了才能正常使用。可以按钮触发创建,可以在初始化页面的时候就创建
  • 相关阅读:
    Sublime text 3支持utf-8
    ubuntu17.10 安装firefox的flash
    opencv mat裁剪
    Ubuntu寻找某某库
    Ubuntu的 g++ gcc版本升降级
    Autotools知识点
    Counted(内存管理机制)
    operator new和operator delete
    STL学习笔记:空间配置器allocator
    function call操作符(operator()) 仿函数(functor)
  • 原文地址:https://www.cnblogs.com/lipx9527/p/14026512.html
Copyright © 2011-2022 走看看