from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import notify2
import sys
import os
import time
import threading as thd
import tushare as ts
import Txt2Png
class Mythread(QThread):
# 定义信号,定义参数为str类型
_signal = pyqtSignal()
def __init__(self):
super(Mythread, self).__init__()
def run(self):
while True:
# 发出信号通知主线程
self._signal.emit()
# 让程序休眠
time.sleep(2)
class Start:
# 实时行情
df = []
# 托盘集合
tpList = []
# 最新price集合,用于和前后两次比较
priceList = []
# 提示上下限
myPriceList = []
# 是否有弹过提示
notifyList = []
# 行情时间
current_time = ''
def update_ui(self):
# 计算时间,如果开市时间则去获取行情
s = self.current_time.split(':')
if len(s) > 1:
# print(s[0])
h = int(s[0])
if h < 9 or 13 > h >= 12 or h >= 15:
return
# print('running')
# 获取实时行情
self.df = ts.get_realtime_quotes(self.codes)
for i in self.df.index:
# 昨收盘价
pre = self.df.loc[i]['pre_close']
# 当前时间
self.current_time = self.df.loc[i]['time']
# 当前价
p = self.df.loc[i]['price']
# 小数点后有3位,舍弃最后一位
price = p[0:len(p) - 1]
# 如果本次得到的price和上一次不一样,则刷新托盘图标
if price != self.priceList[i]:
# 记录变动后的新price
self.priceList[i] = price
# 转成图标
Txt2Png.Price2Png(i, price, pre)
# 刷新托盘图标
self.tpList[i].setIcon(QIcon(str(i)+'.png'))
# 判断是否需要提示
if self.notifyList[i] is False:
price = float(self.df.loc[i]['price'])
pre = float(self.df.loc[i]['pre_close'])
percent = (price - pre) / pre * 100
# 检查预警上限
if price >= self.myPriceList[i]['max']:
self.show_message(self.df.loc[i]['name'], '恭喜!已达预期值' + str(self.myPriceList[i]['max']))
self.notifyList[i] = True
# 检查预警下限
elif price <= self.myPriceList[i]['min']:
self.show_message(self.df.loc[i]['name'], '小心!已到预警线' + str(self.myPriceList[i]['min']))
self.notifyList[i] = True
# 波动大于5个点需要提示
elif abs(percent) >= 5:
self.show_message(self.df.loc[i]['name'], str(format(percent, '.2f') + '%,注意波动!!!'))
self.notifyList[i] = True
# 弹出提示
def show_message(self, title, content):
notify2.init('notify'+title)
bubble = notify2.Notification(title, content)
bubble.show()
# 打开详情窗
def show_window(self):
# self.tpList[0].showMessage("提示", "你点了消息", icon=0)
# 关闭所有窗口,也不关闭应用程序
QApplication.setQuitOnLastWindowClosed(False)
# QWidget窗口是PyQt5中所有用户界口对象的基本类.我们使用了QWidget默认的构造器.默认的构造器没有父类.一个没有父类的窗口被称为一个window.
self.w = QWidget()
# resize()方法调整了窗口的大小.被调整为250像素宽和250像素高.
self.w.resize(1000, 100)
# move()方法移动了窗口到屏幕坐标x=300, y=300的位置.
self.w.move(300, 300)
# 在这里我们设置了窗口的标题.标题会被显示在标题栏上.
self.w.setWindowTitle('List')
# 创建一个底layout
form_layout = QFormLayout()
# 标题
layout = QHBoxLayout()
layout.addWidget(QLabel('名称(代码)'))
layout.addWidget(QLabel('当前价'))
layout.addWidget(QLabel('百分比'))
layout.addWidget(QLabel('最高价'))
layout.addWidget(QLabel('最低价'))
layout.addWidget(QLabel('开盘价'))
layout.addWidget(QLabel('昨收价'))
layout.addWidget(QLabel('成交量'))
layout.addWidget(QLabel('成交额'))
# 添加一行
form_layout.addRow(layout)
for i in self.df.index:
price = float(self.df.loc[i]['price'])
pre = float(self.df.loc[i]['pre_close'])
percent = format((price - pre) / pre * 100, '.2f')
name = self.df.loc[i]['name']
code = self.df.loc[i]['code']
high = format(float(self.df.loc[i]['high']), '.2f')
low = format(float(self.df.loc[i]['low']), '.2f')
open = format(float(self.df.loc[i]['open']), '.2f')
volume = self.df.loc[i]['volume']
amount = self.df.loc[i]['amount']
layout = QHBoxLayout()
layout.addWidget(QLabel(name+'\n('+code+')'))
layout.addWidget(QLabel(str(price)))
layout.addWidget(QLabel(str(percent)+' %'))
layout.addWidget(QLabel(high))
layout.addWidget(QLabel(low))
layout.addWidget(QLabel(open))
layout.addWidget(QLabel(str(pre)))
layout.addWidget(QLabel(volume))
layout.addWidget(QLabel(amount))
# 间距为0
layout.setSpacing(0)
# 添加一行
form_layout.addRow(layout)
self.w.setLayout(form_layout)
# show()方法将窗口显示在屏幕上.一个窗口是先在内存中被创建,然后显示在屏幕上的.
self.w.show()
def quit_app(self):
# 关闭窗体程序
QCoreApplication.instance().quit()
# 在应用程序全部关闭后,TrayIcon其实还不会自动消失,
# 直到你的鼠标移动到上面去后,才会消失,
# 这是个问题,(如同你terminate一些带TrayIcon的应用程序时出现的状况),
# 这种问题的解决我是通过在程序退出前将其setVisible(False)来完成的。
for i in range(len(self.tpList)):
self.tpList[i].setVisible(False)
# 删除生成托盘图标
os.remove(str(i)+'.png')
def __init__(self, codes, myPriceList):
# 要关注的代码
self.codes = codes
self.myPriceList = myPriceList
# 初始化提示状态
for i in range(len(self.codes)):
self.notifyList.append(False)
# pyqt窗口必须在QApplication方法中使用
# 每一个PyQt5应用都必须创建一个应用对象.sys.argv参数是来自命令行的参数列表.Python脚本可以从shell里运行.这是我们如何控制我们的脚本运行的一种方法.
app = QApplication(sys.argv)
# 设置系统托盘图标的菜单
menu = QMenu()
a1 = QAction('&显示(Show)', triggered=self.show_window) # 直接退出可以用qApp.quit
a2 = QAction('&退出(Exit)', triggered=self.quit_app) # 直接退出可以用qApp.quit
menu.addAction(a1)
menu.addAction(a2)
for i in range(len(self.codes)):
# 在系统托盘处显示图标
tp = QSystemTrayIcon()
tp.setIcon(QIcon('./bg.png'))
# 设置右键弹出菜单
tp.setContextMenu(menu)
# 不调用show不会显示系统托盘
tp.show()
# 添加托盘到list中
self.tpList.append(tp)
# 定义好要保存的最新price
self.priceList.append('')
# 创建线程
self.thread = Mythread()
# 注册信号处理函数
self.thread._signal.connect(self.update_ui)
# 启动线程
self.thread.start()
# sys为了调用sys.exit(0)退出程序
# 最后,我们进入应用的主循环.事件处理从这里开始.主循环从窗口系统接收事件,分派它们到应用窗口.如果我们调用了exit()方法或者主窗口被销毁,则主循环结束.sys.exit()方法确保一个完整的退出.环境变量会被通知应用是如何结束的.
# exec_()方法是有一个下划线的.这是因为exec在Python中是关键字.因此,用exec_()代替.
sys.exit(app.exec_())
# if __name__ == "__main__":
# Start(['000014', '002358'])