开发一个桌面显示股票、可转债、ETF行情信息的桌面工具,这样你再也不用一直拿着手机不放了。
需要用到的python库:
- tkinter 绘制界面
- requests 网络请求(如果没有这个库,就pip install requests)
思路:
主要是通过http://hq.sinajs.cn/list= 地址请求行情信息,用UI界面展示出来。
程序不知道怎么上传,就贴一下代码吧。
app.py
import tkinter as tk
from controls import *
from fileutil import *
# from tray import *
import json
lass Application(tk.Frame):
def __init__(self, master=None, file=''):
super().__init__(master)
self.file = file
self.rendered = False
self.items = []
self.config = {}
self.width = 500
self.height = 500
self.master = master
self.childrenFrame = []
self.master.geometry( str(self.width) + "x" + str(self.height))
self.init()
self.bind('', self.destoryEvent)
# self.bind('', self.createEvent)
self.pack(ipadx=0, ipady=0, padx=0, pady=0, fill='both')
def create_widgets(self):
for i in range(len(self.items)):
item = self.items[i]
frame = ItemFrame(self, text= item['name'], labelanchor='n')
frame.name = item['name']
frame.setItems(item['items'])
frame.pack(fill='both')
self.childrenFrame.append(frame)
def init(self):
print("------------create------------------")
if self.rendered:
return
if self.file != '':
txt = readFile(self.file)
self.config = json.loads(txt)
self.items = self.config['items']
self.master.geometry(self.config['geometry'])
self.create_widgets()
self.rendered = True
#托盘
# menu_options = (
# # ('Say Hello', icons.next(), hello),
# # ('Switch Icon', None, switch_icon),
# ('A sub-menu', None, None),
# )
# self.tray = SysTrayIcon('./icon.ico', 'Yinc', menu_options, on_quit= self.quit, default_menu_index=1)
def quit(self, event):
pass
def destoryEvent(self, event):
config = {}
items = []
for i in range(len(self.childrenFrame)):
_frame = self.childrenFrame[i]
item = {}
item['name'] = _frame.name
item['items'] = _frame.items
items.append(item)
config['geometry'] = self.master.winfo_geometry()
config['title'] = self.master.winfo_name()
config['items'] = items
txt = json.dumps(config)
print(txt)
writeToFile(self.file, txt)
print("------------destory------------------")
root = tk.Tk()
_file = 'config.txt'
app = Application(master=root, file=_file)
app.mainloop()
controls.py
########################################
# TODO: 1、增加成本显示
# 2、增加提醒功能
#
########################################
import tkinter as tk
import net as httpNet
import threading
import time
from item import *
from log import *
log = Logger(level='debug')
class ItemFrame(tk.LabelFrame):
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
self.name = ""
self.items = []
self.itemEls = []
self._initMenu()
self.bind("", self.showMenu)
self.run()
def showMenu(self, event):
self.menubar.post(event.x_root, event.y_root)
def _initMenu(self):
self.menubar = tk.Menu(self)
# self.menubar.add_separator()
self.menubar.add_command(label='增加', command= self.addItem)
def addItem(self, item = []):
if len(item) == 0:
item = ['', '', '', '', '', '']
self.items.append(item)
_itemElement = Item(self)
_itemElement.setItem(item)
_itemElement.destroyListener = self.itemDestory
self.itemEls.append(_itemElement)
def setItems(self, items):
_len = len(items)
self.items = items
self.itemEls = []
if _len == 0:
self.addItem()
return
for i in range(_len):
_item = items[i]
_itemElement = Item(self)
_itemElement.setItem(_item)
_itemElement.destroyListener = self.itemDestory
self.itemEls.append(_itemElement)
def run(self):
th = threading.Thread(target=self._updateItems ,args=(1,))
# th.setDaemon(True)#守护线程
th.daemon = True
th.start()
def _updateItems(self, code):
while(True):
# print("=========update============")
_codes = []
#拼装code
for i in range(len(self.itemEls)):
_ele = self.itemEls[i]
if len(_ele.item) > 1 and _ele.item[0] != '':
_code = _ele.item[0]
_codes.append(_code)
log.logger.info( "=========update============ %s", (_codes))
if len(_codes) > 0:
try:
_codeStr = ",".join(_codes)
items = httpNet.getStockInfo(_codeStr)
if len(items) == 0:
return
log.logger.info( "=========req============ %s", (items))
for i in range(len(items)):
_item = items[i]
for j in range(len(self.itemEls)):
_ele = self.itemEls[j]
_eleItem = _ele.item
if _item[0] == _eleItem[0]:
_eleitem =_ele.item
_item[4] = _eleitem[4]
_ele.setItem(_item)
self.items[j] = _item
break
except Exception as e:
log.logger.error(e)
# self.setItem(items[0])
time.sleep(2)
pass
def itemDestory(self, item):
for i in range(len(self.items)):
_item = self.items[i]
if item[0] == _item[0]:
self.items.remove(item)
break
# print(self.items)
log.logger.info( 'destory:', self.items )
pass
net.py
import requests as rq
"""
http://hq.sinajs.cn/list=sz002732
0:”大秦铁路”,股票名字;
1:”27.55″,今日开盘价;
2:”27.25″,昨日收盘价;
3:”26.91″,当前价格; """
def getStockInfo(code):
_url = 'http://hq.sinajs.cn/list=%s' % code
resp = rq.get(_url, timeout= 3)
text = resp.text.strip()
lines = text.split(';\n')
items = []
for i in range(len(lines)):
line = lines[i]
line = line.replace('var hq_str_','').replace('"', '')
itemArr = line.split("=")
itemArr2 = itemArr[1].split(",")
code = itemArr[0]
caption = itemArr2[0]
op = float(itemArr2[1])
yp = itemArr2[2]
np = float(itemArr2[3])
yp = float(yp)
rise = format( ((( np - yp) / yp) * 100), '.3f' ) + '%'
item = [code, caption, rise, np, yp, op]
items.append(item)
# print(items)
return items
if __name__ == '__main__':
getStockInfo('sz002732,sz002304,sh110051,sh512170')
item.py
import tkinter as tk
import net as httpNet
import threading
import time
from log import *
log = Logger(level='debug')
class Item(tk.Frame):
# {caption, val, }
# 001 0.5%
def __init__(self, master=None):
super().__init__(master=master)
self.codeVal = tk.StringVar()
self.costVal = tk.DoubleVar()
self.render = False
self.item = []
self.grid()
self.create_widgets()
self.destroyListener = None
# self.run()
def create_widgets(self):
# ,background='gray' ,foreground='red'
self.code = tk.Label(self, textvariable= self.codeVal, justify='center',width=10)
self.code.grid(row=0, column=0)
self.code.bind('', self.codeClick)
self.caption = tk.Label(self, text="Item1", justify='center',width=10)
self.caption.grid(row=0, column=1)
self.rise = tk.Label(self, text="Item1", justify='center',width=8)
self.rise.grid(row=0, column=2)
self.nowprice = tk.Label(self, text="Item1", justify='center',width=8)
self.nowprice.grid(row=0, column=3)
self.costPrice = tk.Label(self, textvariable= self.costVal, justify='center',width=8)
self.costPrice.grid(row=0, column=4)
self.costPrice.bind('', self.costClick)
self.costEdit = tk.Entry(self, textvariable=self.costVal)
self.grow = tk.Label(self, text="Item1", justify='center',width=8)
self.grow.grid(row=0, column=5)
self.deleteBtn = tk.Button(self, text="删除", justify='center',width=8)
self.deleteBtn.grid(row=0, column=10)
self.deleteBtn.bind('', self.deleteClick)
self.codeEdit = tk.Entry(self, textvariable=self.codeVal)
def setItem(self, item):
self.item = item
# code
self.codeVal.set(item[0])
# self.code.config(text= item[0])
#caption
self.caption.config(text= item[1])
#rise
self.rise.config(text= item[2], foreground=self.getTextColor(item[2]))
#price
self.nowprice.config(text= item[3])
#成本
if self.render == False:
self.costVal.set(item[4])
#盈利
v = self.getGrow()
self.grow.config(text= v, foreground=self.getTextColor(v))
self.render = True
# self.costPrice.config(text= item[4])
# self.oprice.config(text= item[5])
def getGrow(self):
n = self.item[3]
cost = self.item[4]
if cost == '' or cost is None: cost = 0
if float(cost) == 0:
return '0%'
v = round( (n - cost) * 100 / cost , 2)
return str(v) + '%'
def getTextColor(self, v):
if type(v) == str:
# log.logger.info( "=========color============ %s", (v))
if v == '': v = '0'
v = float(v.replace('%','').replace('--','0'))
if v > 0:
return 'red'
elif v < 0:
return 'blue'
else:
return 'black'
def deleteClick(self,event):
if self.destroyListener != None:
self.destroyListener(self.item)
self.destroy()
pass
def costClick(self, event):
self.costPrice.grid_forget()
self.costEdit.grid(row=0, column=4)
self.costEdit.bind('', self.costOutEdit)
def costOutEdit(self, evet):
self.costEdit.grid_forget()
_val = self.costVal.get()
self.item[4] = _val
self.costPrice.grid(row=0, column=4)
self.costPrice.bind('', self.costClick)
def codeClick(self, event):
self.code.grid_forget()
self.codeEdit.grid(row=0, column=0, padx=5)
self.codeEdit.bind('', self.outEdit)
def outEdit(self, event):
self.codeEdit.grid_forget()
self.item[0] = self.codeVal.get()
self.code.grid(row=0, column=0, padx=5)
self.code.bind('', self.codeClick)
def run(self):
th = threading.Thread(target=self._updateItem ,args=(1,))
th.setDaemon(True)#守护线程
th.start()
def _updateItem(self, code):
while(True):
if len(self.item) > 1 and self.item[0] != '':
_code = self.item[0]
items = httpNet.getStockInfo(_code)
if len(items) == 1:
print(items)
self.setItem(items[0])
print("=========update============")
time.sleep(2)
pass
fileutil
def writeToFile(file, txt):
f = open(file, "w", encoding='utf-8')
f.writelines(txt)
f.close()
def readFile(file):
f = open(file, "r", encoding='utf-8')
lines = f.readlines()
return "".join(lines)
# while line != "":
# f.readlines()
log.py
import logging
from logging import handlers
class Logger(object):
#日志级别关系映射
level_relations = {
'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'crit':logging.CRITICAL
}
def __init__(self,filename='log/log.log',level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
self.logger = logging.getLogger(filename)
format_str = logging.Formatter(fmt)#设置日志格式
self.logger.setLevel(self.level_relations.get(level))#设置日志级别
sh = logging.StreamHandler()#往屏幕上输出
sh.setFormatter(format_str) #设置屏幕上显示的格式
th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')#往文件里写入#指定间隔时间自动生成文件的处理器
#实例化TimedRotatingFileHandler
#interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种:
# S 秒
# M 分
# H 小时、
# D 天、
# W 每星期(interval==0时代表星期一)
# midnight 每天凌晨
th.setFormatter(format_str)#设置文件里写入的格式
self.logger.addHandler(sh) #把对象加到logger里
self.logger.addHandler(th)
if __name__ == '__main__':
# log = Logger('all.log',level='debug')
log = Logger(level='debug')
log.logger.debug('debug')
log.logger.info('info')
log.logger.info([1,2,3])
log.logger.warning('警告')
log.logger.error('报错')
log.logger.critical('严重')
Logger('error.log', level='error').logger.error('error')