首页 > 解决方案 > 如何每 60 秒更新一次 PyQt5 图标?

问题描述

我是 python 新手,我正在做一个简单的应用程序,其中比特币的价值显示在系统托盘中。我正在使用 PyQt5。

我的问题是:如何每分钟刷新一次值和图标。当我尝试它时,我的菜单将不再起作用。有简单的解决方法吗?

这是我的代码:

import sys
import time
import math
import json

from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu
from PyQt5.QtGui import QIcon
from urllib.request import urlopen
from time import sleep

app = QApplication(sys.argv)

while True:
    # Connecting the api
    with urlopen("https://api.alternative.me/v2/ticker/1/") as response:
        source = response.read()

data = json.loads(source)

price = (json.dumps(data['data']['1']['quotes']['USD']['price']))
change = (json.dumps(data['data']['1']['quotes']['USD']['percentage_change_1h']))

intPrice = float(price)
intChange = float(change)

roundPrice = round(intPrice,2)
roundStringPrice = str(roundPrice)

# Icon change
if intChange <=0.00:
    trayIcon = QSystemTrayIcon(QIcon('icons/down.png'), parent=app)
else:
    trayIcon = QSystemTrayIcon(QIcon('icons/up.png'), parent=app)
    
trayIcon.setToolTip(roundStringPrice + ' USD')
trayIcon.show()
time.sleep(5)
trayIcon.update()

menu = QMenu()
exitAction = menu.addAction('Quit app')
exitAction.triggered.connect(app.quit)

trayIcon.setContextMenu(menu)
sys.exit(app.exec_())

标签: pythonapipyqt5

解决方案


不要使用 while True、urlopen 或 time.sleep,因为它们会阻塞事件循环,而是使用带有 QNetworkAccessManager 的 QTimer:

import os.path
import json

from functools import cached_property

from PyQt5.QtCore import pyqtSignal, QObject, QTimer, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMenu, QMessageBox, QSystemTrayIcon

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


class ApiManager(QObject):
    infoChanged = pyqtSignal(float, float)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.manager.finished.connect(self.handle_finished)

    @cached_property
    def manager(self):
        return QNetworkAccessManager()

    def start_request(self):
        url = QUrl("https://api.alternative.me/v2/ticker/1/")
        qrequest = QNetworkRequest(url)
        self.manager.get(qrequest)

    def handle_finished(self, reply):
        if reply.error() != QNetworkReply.NoError:
            print(f"code: {reply.error()} message: {reply.errorString()}")
        else:
            print("successful")
            source = reply.readAll().data()
            data = json.loads(source)
            r = data["data"]["1"]["quotes"]["USD"]
            price = r["price"]
            change = r["percentage_change_1h"]
            self.infoChanged.emit(price, change)


class SystemTrayIcon(QSystemTrayIcon):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIcon(QIcon(self.get_resource("icons/up.png")))

        self.menu = QMenu()
        exitAction = self.menu.addAction("Quit app")
        exitAction.triggered.connect(QApplication.quit)
        self.setContextMenu(self.menu)

        self.manager = ApiManager()
        self.manager.infoChanged.connect(self.handle_info_changed)
        timer = QTimer(self, timeout=self.manager.start_request, interval=60 * 1000)
        timer.start()
        self.manager.start_request()

    def get_resource(self, path):
        return os.path.join(CURRENT_DIR, path)

    def handle_info_changed(self, price, change):
        icon = (
            QIcon(self.get_resource("icons/down.png"))
            if change < 0
            else QIcon(self.get_resource("icons/up.png"))
        )
        self.setIcon(icon)
        self.setToolTip(f"{price:.2f} USD")


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)

    if not QSystemTrayIcon.isSystemTrayAvailable():
        QMessageBox.critical(
            None, "Systray", "I couldn't detect any system tray on this system."
        )
        sys.exit(1)
    QApplication.setQuitOnLastWindowClosed(False)

    trayIcon = SystemTrayIcon(parent=app)
    trayIcon.show()
    sys.exit(app.exec_())

推荐阅读