Python桌面股票工具

Python桌面股票工具_第1张图片
微信图片_20190725142216.png

开发一个桌面显示股票、可转债、ETF行情信息的桌面工具,这样你再也不用一直拿着手机不放了。

需要用到的python库:

  1. tkinter 绘制界面
  2. 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')

你可能感兴趣的:(Python桌面股票工具)