多功能调用QTimer主窗口挂起

2022-02-23 00:00:00 python python-requests pyqt pyqt5

问题描述

现在我尝试向计时器函数添加另一个函数,当我尝试运行它时,输出窗口挂起。用于同时运行两个函数的QTimer与timer.start(1000)不同吗?下面是我正在使用的代码。

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import requests
import json
import xmltodict
import time 

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.widget_2 = QtWidgets.QWidget(self.centralwidget)
        self.widget_2.setObjectName("widget_2")
        self.label = QtWidgets.QLabel(self.widget_2)
        self.label.setGeometry(QtCore.QRect(40, 30, 181, 41))
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.widget_2)
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setObjectName("widget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label_2 = QtWidgets.QLabel(self.widget)
        self.label_2.setObjectName("label_2")
        self.verticalLayout_2.addWidget(self.label_2, 0, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
        self.line = QtWidgets.QFrame(self.widget)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout_2.addWidget(self.line)
        self.label_3 = QtWidgets.QLabel(self.widget)
        self.label_3.setObjectName("label_3")
        self.verticalLayout_2.addWidget(self.label_3, 0, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout_2.addItem(spacerItem)
        self.horizontalLayout.addWidget(self.widget)
        self.widget_3 = QtWidgets.QWidget(self.centralwidget)
        self.widget_3.setObjectName("widget_3")
        self.horizontalLayout.addWidget(self.widget_3)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(
            _translate(
                "MainWindow",
                '<html><head/><body><p><span style=" font-size:28pt;"></span></p></body></html>',
            )
        )
        self.label_2.setText(_translate("MainWindow", "<html><head/><body><p><span style=" font-size:14pt;">Header</span></p></body></html>"))
        self.label_3.setText(_translate("MainWindow", "<html><head/><body><p><span style=" font-size:12pt;">News</span></p></body></html>"))


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.data)
        timer.timeout.connect(self.liveNews)
        timer.start(1000)
        self.data()
        self.liveNews()

    def data(self):
        time_str = QTime.currentTime().toString()
        self.label.setText(
            '<html><head/><body><p><span style=" font-size:28pt;">{}</span></p></body></html>'.format(
                time_str
            )
        )

    def liveNews(self):
        _translate = QtCore.QCoreApplication.translate
        liveNews_url = 'https://timesofindia.indiatimes.com/rssfeedstopstories.cms'
        response = requests.get(liveNews_url)
        result = response.content
        xml = xmltodict.parse(result)
        resp_json = json.loads(json.dumps(xml))
        for news in resp_json['rss']['channel']['item']:
            self.label_2.setText(_translate("MainWindow", "<html><head/><body><p><span style=" font-size:14pt;">" + resp_json['rss']['channel']['title'] +"</span></p></body></html>"))
            self.label_3.setText(_translate("MainWindow", "<html><head/><body><p><span style=" font-size:12pt;word-wrap: break-word">" + news['title'] + "</span></p></body></html>"))
            time.sleep(5)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

请建议我解决此问题。

我还希望每隔5秒运行一次我在live news方法中定义的foreach循环。如何存档此文件?


解决方案

问题不在于多个QTimer,而在于在主线程中使用requests.get()和time.睡眠(),因为它们阻塞了图形用户界面所在的事件循环,因此您必须在辅助线程上运行它。

在下面的部分中,我展示了一个示例:

import json

import requests
import xmltodict

from PyQt5 import QtCore, QtGui, QtWidgets


class Timer(QtCore.QObject):
    datetimeChanged = QtCore.pyqtSignal(QtCore.QTime)

    def __init__(self, parent=None):
        super(Timer, self).__init__(parent)
        self.m_timer = QtCore.QTimer(self, timeout=self.onTimeout)

    @property
    def interval(self):
        return self.m_timer.interval()

    @interval.setter
    def interval(self, v):
        self.m_timer.setInterval(v)

    @QtCore.pyqtSlot()
    def start(self):
        self.m_timer.start()
        self.onTimeout()

    @QtCore.pyqtSlot()
    def stop(self):
        self.m_timer.stop()

    @QtCore.pyqtSlot()
    def onTimeout(self):
        dt = QtCore.QTime.currentTime()
        self.datetimeChanged.emit(dt)


class LiveNewsManager(QtCore.QObject):
    titleChanged = QtCore.pyqtSignal(str)
    contentChanged = QtCore.pyqtSignal(dict)

    def __init__(self, parent=None):
        super(LiveNewsManager, self).__init__(parent)
        self.m_timer = QtCore.QTimer(self, timeout=self.onTimeout)

    @property
    def interval(self):
        return self.m_timer.interval()

    @interval.setter
    def interval(self, v):
        self.m_timer.setInterval(v)

    @QtCore.pyqtSlot()
    def start_request(self):
        url = "https://timesofindia.indiatimes.com/rssfeedstopstories.cms"
        response = requests.get(url)
        xml = xmltodict.parse(response.content)
        resp_json = json.loads(json.dumps(xml))
        channel = resp_json["rss"]["channel"]
        self.titleChanged.emit(channel["title"])
        self.m_iterator = iter(channel["item"])
        self.m_timer.start()
        self.onTimeout()

    @QtCore.pyqtSlot()
    def onTimeout(self):
        try:
            item = next(self.m_iterator)
            self.contentChanged.emit(item)
        except StopIteration:
            self.m_timer.stop()
            self.start_request()
        except Exception as e:
            pass


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.m_time_label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
        self.m_header_label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
        self.m_content_label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)

        line = QtWidgets.QFrame(
            frameShape=QtWidgets.QFrame.HLine,
            frameShadow=QtWidgets.QFrame.Sunken,
        )

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        vlay = QtWidgets.QVBoxLayout(central_widget)

        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.m_time_label)

        vlay2 = QtWidgets.QVBoxLayout()
        vlay2.addWidget(self.m_header_label)
        vlay2.addWidget(line)
        vlay2.addWidget(self.m_content_label)
        hlay.addLayout(vlay2, stretch=1)

        vlay.addLayout(hlay)
        vlay.addStretch()

        self.resize(640, 480)

        timer = Timer(self)
        timer.interval = 1000
        timer.datetimeChanged.connect(self.update_time)
        timer.start()

        thread = QtCore.QThread(self)
        thread.start()

        self.m_worker = LiveNewsManager()
        self.m_worker.interval = 5 * 1000
        self.m_worker.moveToThread(thread)
        self.m_worker.titleChanged.connect(self.update_header)
        self.m_worker.contentChanged.connect(self.update_content)
        QtCore.QTimer.singleShot(0, self.m_worker.start_request)

    @QtCore.pyqtSlot(QtCore.QTime)
    def update_time(self, dt):
        self.m_time_label.setText(
            """<html><head/><body><p><span style=" font-size:28pt;">{}</span></p></body></html>""".format(
                dt.toString()
            )
        )

    @QtCore.pyqtSlot(str)
    def update_header(self, header):
        self.m_header_label.setText(
            """<html><head/><body><p><span style=" font-size:14pt;">{}</span></p></body></html>""".format(
                header
            )
        )

    @QtCore.pyqtSlot(dict)
    def update_content(self, content):
        self.m_content_label.setText(
            """<html><head/><body><p><span style=" font-size:12pt;word-wrap: break-word">{}</span></p></body></html>""".format(
                content["title"]
            )
        )


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

相关文章