from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui, QtWidgets
# 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:
codes = []
# 实时行情
df = []
# 托盘集合
tpList = []
# 最新price集合,用于和前后两次比较
priceList = []
# 提示上下限
code_list = []
# 行情时间
current_time = ''
# 装载view的集合
codeView = []
maxView = []
minView = []
tipView = []
def update_ui(self):
# 计算时间,如果开市时间则去获取行情
s = self.current_time.split(':')
if len(s) > 1:
h = int(s[0])
if h < 9 or 13 > h >= 12 or h >= 15:
return
# 获取实时行情
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.code_list[i]['tip'] 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.code_list[i]['max'] != 0:
self.show_message(self.df.loc[i]['name'], '恭喜!已达预期值' + str(self.code_list[i]['max']))
self.code_list[i]['tip'] = True
# 检查预警下限
elif price <= self.code_list[i]['min'] != 0:
self.show_message(self.df.loc[i]['name'], '小心!已到预警线' + str(self.code_list[i]['min']))
self.code_list[i]['tip'] = True
# 波动大于5个点需要提示
elif abs(percent) >= 5:
self.show_message(self.df.loc[i]['name'], str(format(percent, '.2f') + '%,注意波动!!!'))
self.code_list[i]['tip'] = True
# 弹出提示
def show_message(self, title, content):
self.tpList[0].showMessage(title, content, icon=0)
# 打开详情窗
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)
# 获取大盘指数
zs = ts.get_index()
for i in zs.index:
code = zs.loc[i]['code']
if code == '000001' or code == '399001' or code == '399006':
name = zs.loc[i]['name']
price = format(float(zs.loc[i]['close']), '.2f')
percent = format(float(zs.loc[i]['change']), '.2f')
high = format(float(zs.loc[i]['high']), '.2f')
low = format(float(zs.loc[i]['low']), '.2f')
open = format(float(zs.loc[i]['open']), '.2f')
pre = format(float(zs.loc[i]['preclose']), '.2f')
volume = zs.loc[i]['volume']
amount = zs.loc[i]['amount']
layout = QHBoxLayout()
layout.addWidget(QLabel("<font color='#888888'>" + name + '<br>' + '(' + code + ')</font>'))
if float(percent) > 0:
layout.addWidget(QLabel("<font color='#C3281C'>" + str(price) + ' </font>'))
layout.addWidget(QLabel("<font color='#C3281C'>" + str(percent) + ' %</font>'))
elif float(percent) < 0:
layout.addWidget(QLabel("<font color='#369B5C'>" + str(price) + ' </font>'))
layout.addWidget(QLabel("<font color='#369B5C'>" + str(percent) + ' %</font>'))
else:
layout.addWidget(QLabel(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(str(volume)))
layout.addWidget(QLabel(str(amount)))
# 间距为0
layout.setSpacing(0)
# 添加一行
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+')'))
if float(percent) > 0:
layout.addWidget(QLabel("<font color='#C3281C'>" + str(price) + ' </font>'))
layout.addWidget(QLabel("<font color='#C3281C'>" + str(percent) + ' %</font>'))
elif float(percent) < 0:
layout.addWidget(QLabel("<font color='#369B5C'>" + str(price) + ' </font>'))
layout.addWidget(QLabel("<font color='#369B5C'>" + str(percent) + ' %</font>'))
else:
layout.addWidget(QLabel(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 show_setting(self):
# self.tpList[0].showMessage("提示", "你点了消息", icon=0)
# 关闭所有窗口,也不关闭应用程序
QApplication.setQuitOnLastWindowClosed(False)
# QWidget窗口是PyQt5中所有用户界口对象的基本类.我们使用了QWidget默认的构造器.默认的构造器没有父类.一个没有父类的窗口被称为一个window.
self.w = QWidget()
# resize()方法调整了窗口的大小.被调整为250像素宽和250像素高.
self.w.resize(666, 100)
# move()方法移动了窗口到屏幕坐标x=300, y=300的位置.
self.w.move(300, 300)
# 在这里我们设置了窗口的标题.标题会被显示在标题栏上.
self.w.setWindowTitle('SETTING')
# 创建一个底layout
form_layout = QFormLayout()
self.codeView = []
self.maxView = []
self.minView = []
self.tipView = []
for i in range(len(self.code_list)):
layout = QHBoxLayout()
layout.addWidget(QLabel('Code'))
code_edit = QLineEdit(self.code_list[i]['code'])
layout.addWidget(code_edit)
self.codeView.append(code_edit)
layout.addWidget(QLabel('Max'))
max_edit = QLineEdit(str(self.code_list[i]['max']))
layout.addWidget(max_edit)
self.maxView.append(max_edit)
layout.addWidget(QLabel('Min'))
min_edit = QLineEdit(str(self.code_list[i]['min']))
layout.addWidget(min_edit)
self.minView.append(min_edit)
tip_btn = QCheckBox('Tip')
tip_btn.setChecked(self.code_list[i]['tip'])
layout.addWidget(tip_btn)
self.tipView.append(tip_btn)
del_btn = QPushButton('Del')
del_btn.setChecked(True)
del_btn.setObjectName(str(i))
del_btn.clicked.connect(lambda: self.del_code())
layout.addWidget(del_btn)
# 添加一行
form_layout.addRow(layout)
# 添加两个按钮
layout = QHBoxLayout()
add_btn = QPushButton('Add')
add_btn.setChecked(True)
add_btn.setObjectName(str(i))
add_btn.clicked.connect(lambda: self.add_code())
layout.addWidget(add_btn)
save_btn = QPushButton('Save + Restart')
save_btn.setChecked(True)
save_btn.setObjectName(str(i))
save_btn.clicked.connect(lambda: self.save_code())
layout.addWidget(save_btn)
# 添加一行
form_layout.addRow(layout)
self.w.setLayout(form_layout)
# show()方法将窗口显示在屏幕上.一个窗口是先在内存中被创建,然后显示在屏幕上的.
self.w.show()
@pyqtSlot()
def add_code(self):
# 先保存界面上全部数据
for i in range(len(self.codeView)):
self.code_list[i]['code'] = self.codeView[i].text()
self.code_list[i]['max'] = float(self.maxView[i].text())
self.code_list[i]['min'] = float(self.minView[i].text())
self.code_list[i]['tip'] = self.tipView[i].isChecked()
f = open('code.txt', '+w')
f.write(str(self.code_list))
f.close()
# 新增一条空记录
n = {'code': '', 'max': 0, 'min': 0, 'tip': False}
self.code_list.append(n)
self.show_setting()
@pyqtSlot()
def del_code(self):
if len(self.code_list) > 1:
source = self.MainWindow.sender()
print(source.text())
print(source.objectName())
i = int(source.objectName())
self.codeView.remove(self.codeView[i])
self.maxView.remove(self.maxView[i])
self.minView.remove(self.minView[i])
self.tipView.remove(self.tipView[i])
self.code_list.remove(self.code_list[i])
f = open('code.txt', '+w')
f.write(str(self.code_list))
f.close()
self.show_setting()
@pyqtSlot()
def save_code(self):
# source = self.MainWindow.sender()
# print(source.text())
# print(source.objectName())
# i = int(source.objectName())
for i in range(len(self.codeView)):
if self.codeView[i].text() != '':
self.code_list[i]['code'] = self.codeView[i].text()
self.code_list[i]['max'] = float(self.maxView[i].text())
self.code_list[i]['min'] = float(self.minView[i].text())
self.code_list[i]['tip'] = self.tipView[i].isChecked()
f = open('code.txt', '+w')
f.write(str(self.code_list))
f.close()
self.restart_program()
# 退出应用程序
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, code_list):
super(Start, self).__init__()
# 要关注的代码
self.code_list = code_list
for i in range(len(self.code_list)):
self.codes.append(self.code_list[i]['code'])
# pyqt窗口必须在QApplication方法中使用
# 每一个PyQt5应用都必须创建一个应用对象.sys.argv参数是来自命令行的参数列表.Python脚本可以从shell里运行.这是我们如何控制我们的脚本运行的一种方法.
app = QApplication(sys.argv)
self.MainWindow = QtWidgets.QMainWindow()
# 设置系统托盘图标的菜单
menu = QMenu()
a1 = QAction('&显示(Show)', triggered=self.show_window) # 直接退出可以用qApp.quit
a2 = QAction('&设置(Setting)', triggered=self.show_setting) # 直接退出可以用qApp.quit
a3 = QAction('&退出(Exit)', triggered=self.quit_app) # 直接退出可以用qApp.quit
menu.addAction(a1)
menu.addAction(a2)
menu.addAction(a3)
for i in range(len(self.code_list)):
# 在系统托盘处显示图标
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_())
# 重启自身
def restart_program(self):
python = sys.executable
os.execl(python, python, * sys.argv)
# if __name__ == "__main__":
# Start(['000014', '002358'])