python PyQt5程序运行界面无响应

写在前面

此文档仅记录本人遇到的问题及解决办法,并非涵盖所有

最近写一个股价监控小程序,在改进为界面程序后,每次运行到耗时方法时界面就卡死,无响应,花费了很长事件才把问题解决了,记录一下

原因分析

程序在主线程中执行,当主程序中有一个事件比较耗时时,主程序就会等耗时事件处理完才会进行下一步,此时界面就会卡死出现无响应的状态

代码展示

main.py

import sys
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QApplication

import stock_price_wring

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainW = QMainWindow()
    ui = monitor.Ui_MainWindow()
    ui.setupUi(mainW)
    mainW.show()
    sys.exit(app.exec_())

调用的耗时代码

 def do_monitor(monitor_info, send_mail, authorization, receive_mail):
    """
    预警监控
    :param monitor_info: 监控条件数据
    :param send_mail: 发件邮箱
    :param authorization: 授权码
    :param receive_mail: 收件邮箱
    :return: 回显信息
    """
    now_date = datetime.datetime.now().strftime('%Y-%m-%d')
    for item in monitor_info:
        info = get_stock_info(item[0])
        # 判断监控方式
        if item[2] == 0:
            # 价格下跌至
            if float(info['price'][0]) <= item[1]:
                text = info['time'][0] + '  ' + info['name'][0] + '触发预警条件:股价跌到' + str(item[1]) + '元,提醒您及时关注'
                send_content = info['name'][0] + '触发预警条件:股价跌到' + str(item[1]) + '元,提醒您及时关注' + '\n\n' + '股票代码:' + info['code'][0] + '\n' + '股票名称:' + info['name'][0] + '\n' + '预警周期:当日' + '\n' + '预警指标:股价' + '\n' + '时\t\t间:' + now_date + ' ' + info['time'][0]
                res = do_send_mail(send_mail, authorization, receive_mail, send_content)
                monitor_info.remove(item)
                return text+'\n'+res
        elif item[2] == 1:
            # 价格上涨至
            if float(info['price'][0]) >= item[1]:
                text = info['time'][0] + '  ' + info['name'][0] + '触发预警条件:股价涨到' + str(item[1]) + '元,提醒您及时关注'
                send_content = info['name'][0] + '触发预警条件:股价涨到' + str(item[1]) + '元,提醒您及时关注' + '\n\n' + '股票代码:' + info['code'][0] + '\n' + '股票名称:' + info['name'][0] + '\n' + '预警周期:当日' + '\n' + '预警指标:股价' + '\n' + '时\t\t间:' + now_date + ' ' + info['time'][0]
                res = do_send_mail(send_mail, authorization, receive_mail, send_content)
                monitor_info.remove(item)
                return text+'\n'+res
        else:
            return ''

解决办法

  • 使用多线程
    • 主线程只执行界面显示
    • 子线程中执行耗时任务
  • 构建窗口类
    • 编写事件响应代码
    • 开启线程
    • 接收线程发送的数据
    • 数据回显
  • 构建线程类
    • 执行耗时任务
    • 发送数据给主程序(信号

子线程中定义信号和发送信号

# 实例化一个信号
signal = pyqtSignal(str)

# 发送信号
self.signal.emit(text)

主线程中接收信号

# 实例化线程
self.thread = MonitorThread(self)
# 绑定接收线程信号的方法
self.thread.signal.connect(self.receive_signal)

def receive_signal(self, text):
     """
     接收子线程传送的数据 回显至浏览框
     :param text:
     """
     self.textBrowser.append(text)

部分代码展示

代码结构
python PyQt5程序运行界面无响应_第1张图片
程序入口

if __name__ == '__main__':
    log_dir = os.path.join(os.getcwd(), 'log')
    if not os.path.exists(log_dir):
        os.mkdir(log_dir)
    cgitb.enable(format='text', logdir=log_dir)
    app = QApplication(sys.argv)
    myWin = MyMainForm()
    myWin.show()
    sys.exit(app.exec_())

窗口类

class MyMainForm(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainForm, self).__init__(parent)
        self.setupUi(self)
        # 实例化线程
        self.thread = MonitorThread(self)
        # 窗口启动自动执行获取配置数据
        self.read_base_info()
        # 按钮点击事件
        self.chooseBtn.clicked.connect(self.choose_file)
        self.runBtn.clicked.connect(self.start_monitoring)
        # 接收线程信号,进行数据回显
        self.thread.signal.connect(self.receive_signal)

    def read_base_info(self):
        """
        获取基础数据:发件箱,授权码,收件箱
        """
        base_info = pd.read_table('./baseinfo.txt', header=None)
        self.sendMail.setText(base_info[0][0])
        self.authorization.setText(base_info[0][1])
        self.receiveMail.setText(base_info[0][2])

    def choose_file(self):
        """
        选择预警信息的存储文件 -> Excel
        """
        root = tkinter.Tk()
        root.withdraw()
        filepath = filedialog.askopenfilename()
        self.filePath.setText(filepath)

    def start_monitoring(self):
        """
        开始监控 启动子线程 调用子线程接收数据的方法
        """
        # 数据整理
        file_path = self.filePath.text()
        send_mail = self.sendMail.text()
        authorization = self.authorization.text()
        receive_mail = self.receiveMail.text()
        base_info = {'file_path': file_path,
                     'send_mail': send_mail,
                     'authorization': authorization,
                     'receive_mail': receive_mail}
        # 调用线程接收主线程传递数据的方法
        self.thread.accept_info(base_info)
        # 开启子线程
        self.thread.start()

    def receive_signal(self, text):
        """
        接收子线程传送的数据 回显至浏览框
        :param text:
        """
        self.textBrowser.append(text)

子线程类

class MonitorThread(QThread):
    # 实例化一个信号
    signal = pyqtSignal(str)

    def __init__(self, main_form):
        super(MonitorThread, self).__init__()
        self.file_path = ''
        self.send_mail = ''
        self.authorization = ''
        self.receive_mail = ''
        self.main_form = main_form

    def accept_info(self, base_info):
        """
        接收主线程发送的数据
        :param base_info:
        """
        self.file_path = base_info.get('file_path')
        self.send_mail = base_info.get('send_mail')
        self.authorization = base_info.get('authorization')
        self.receive_mail = base_info.get('receive_mail')

    def run(self):
        """
        子线程运行方法,执行耗时任务
        """
        # 获取监控信息
        monitor_info = get_monitor_info(self.file_path)
        # 循环监控
        while True:
            # 调用方法 返回信息
            text = do_monitor(monitor_info, self.send_mail, self.authorization, self.receive_mail)
            # 发送信号
            self.signal.emit(text)
            time.sleep(1)

你可能感兴趣的:(Python学习历程,python,qt,pyqt)