股票盯盘小工具代码梳理

   在笔者上一篇文章GUI版本的股票盯盘小工具中,笔者提到了这个小工具如何使用,但没有梳理里面的代码,这次就梳理总结一下它的源码,并根据粉丝的反馈讲一下这个小工具新增的功能。Python源码分为两大部分:即窗口渲染代码和后台控制运算部分。

一、窗口渲染

   这部分代码由tkinter.py模块完成,主要是实现Windows窗口的创建、窗口组件布局等功能,并根据人机交互调用后台程序实现某一功能。用到的组件主要有:
窗口

1. 窗口
   这里的窗口就是我们常说的Windows窗口,这里只有一个主窗口(也叫根窗口)和若干个顶层子窗口(top-level windows)。窗口的创建在这里非常好实现,几行代码就能配置好其初始大小、位置、底层色等属性。小工具中的顶层子窗口有多个,它和主窗口的创建非常相似,只加一行代码说明它是子窗口就行了,窗口代码如下:

root = Tk() # 创建主窗口
root.title('股票盯盘工具') #窗口名称
root.geometry('800x600+200+50') #窗口默认大小和在屏幕中的位置
root.configure(bg = 'lightyellow') #配置底色,这个类方法的参数有很多,但这里我们只配置它的底色,其他属性选择默认就行

#root.after(200,time_date) #每隔200ms定时,后面会解释
root.mainloop() #显示主窗口
subroot1 = Toplevel() #指明是位于root窗口上的子窗口
subroot1.title('设置预警阈值') #子窗口名称
subroot1.geometry('600x50') #设置几何属性

2. 按钮
   按钮组件如上图中绿色标注的地方,它是人际交互的一种接口,点击按钮会调用后台的函数,然后完成特定的功能。按钮创建的过程中最重要的一步就是指明它的command属性,该属性用于绑定按钮与某一函数。创建代码如下:

#设置“添加”、“删除”按钮
addBtn = Button(root,text = '增加',
                width = 10,height = 3,
                font = '宋体 12 bold',
                command = addItem).place(relx = 0.3,rely = 0.75) #Button类的place方法就是把前面属性设置好的按钮放在窗口的指定位置
rmBtn = Button(root,text = '删除', 
               width = 10,height = 3,
               font = '宋体 12 bold',
               command = removeItem).place(relx = 0.6,rely = 0.75)
# additem和removeItem是两个自定义的无形参函数

3. 标签
   标签组件应用的场景很多,它只起到一个提示的作用,比如说上图中灰色部分的显示时间、日期,提示输入阈值上下限等。创建的时候只需要说明它依附于哪一个窗口、显示位置等属性就行了。以时间、日期标签为例,代码如下:

# 配置显示时间、日期的标签组件
TimeLabel = Label(root,text = date_time, #依附于主窗口
                  bg = 'lightblue',fg = 'black', #背景色、前景色
                  relief = 'ridge', #显示方式
                  height = 3, 
                  font = '宋体 24 normal') # 显示字符串的字形、字号、是否加粗
TimeLabel.pack(fill = X) #填充方式,布满横轴

4. 菜单栏
   创建菜单栏,如上图蓝色标注的地方。在此工具中菜单栏的作用很简单,但是笔者加上菜单栏的代码主要是为了搭建一个常用GUI项目框架。代码如下:

menubar = Menu(root) #设置菜单栏,依附于主窗口
setmenu = Menu(menubar,tearoff = False) #主菜单栏

menubar.add_cascade(label = '设置周期',menu = setmenu)
#创建子菜单栏,并且绑定获取刷新周期的函数get_period(),形参为周期
setmenu.add_command(label = '每隔5秒',command = lambda:get_period(5))
setmenu.add_command(label = '每隔10秒',command = lambda:get_period(10))
setmenu.add_command(label = '每隔30秒',command = lambda:get_period(30))
setmenu.add_command(label = '每隔60秒',command = lambda:get_period(60))

menubar.add_command(label = '帮助',command = Help)
menubar.add_command(label = '退出',command = Exit)
root.config(menu = menubar)

5. Tree类(二维表格)
   如上图紫色标注的地方,在窗口中它是一个表格,它的数据存储格式是一个Tree,所以称为Tree类。下面的代码在整个项目中的位置不是连续在一起的,但都涉及到了Tree类操作,这里先写出来,到后面讲后台程序实现时再解释。代码如下:


#设置表格
tree = ttk.Treeview(root,columns = ("code",'name',
                                    'pre_close','open',
                                    'price','fluctuation'),
                    selectmode = EXTENDED)
tree.heading("#0",text = "股票代码") #设置列名称
tree.heading("#1",text = "股票名称")
tree.heading("#2",text = "今日开盘价")
tree.heading("#3",text = "昨日收盘价")
tree.heading("#4",text = "当前价格")
tree.heading("#5",text = "涨跌幅(%)")
tree.heading("#6",text = "监控状态")


tree.column("#0",anchor = CENTER,width = 80) #设置各列宽度
tree.column("#1",anchor = CENTER,width = 160)
tree.column("#2",anchor = CENTER,width = 100)
tree.column("#3",anchor = CENTER,width = 100)
tree.column("#4",anchor = CENTER,width = 100)
tree.column("#5",anchor = CENTER,width = 100)
tree.column("#6",anchor = CENTER,width = 80)
tree.tag_configure("evenColor",background = 'lightblue') 
#为表格设置Y轴滚动栏
yscrollbar = Scrollbar(root)
yscrollbar.pack(side = RIGHT,fill = Y)
tree.bind("<Double-1>",doubleClick) #绑定双击表格某行的事件
tree.place(relx = 0.1,rely = 0.2,relwidth = 0.75,relheight = 0.5)
yscrollbar.config(command = tree.yview)
tree.config(yscrollcommand = yscrollbar.set)
#下面这一行代码是往表格后面插入一行数据,很重要,后面还会讲
tree.insert("",index = END,text = code[i],values = value[i])
#下面的代码获取选中的某一行的ID,然后删除选中的这一行。
item_text = tree.item(iid,"text")
share_codes.remove(item_text)
tree.delete(iid)

6. Entry(输入框)
   如上图中橙色标注的,输入框用于获取键盘输入的字符串,然后根据后台逻辑把获取到的字符串再处理成其他信息(比如一个浮点型的数)。特别注意,不能把pack方法直接加在Entry类后面,否则创捷的up1、down1不再是Entry对象,后面执行get方法时就会报错。​代码如下:

up1 = Entry(subroot1) #获取上限
up1.pack(side = LEFT)

down1 = Entry(subroot1) #获取下限
down1.pack(side = LEFT)

down1.get() #获取输入的数据,如果无输入,返回None,up1类似。

7. 消息盒子
   消息盒子用于展示提示、警告、报错等信息。使用方法见代码中的注释代码如下:

#创建错误消息盒子,第一个参数表示盒子的名称,第二个参数表示显示的内容
err = messagebox.showerror('错误','找不到文件!')
err = messagebox.showerror('错误','股票代码错误!')
#创建提示消息盒子,询问“是-否-取消”,点击“是”,a值为True,点击“否”,a值为False,点击“取消”按钮,a值为None。
a = messagebox.askyesnocancel('退出','是否保存修改后的记录?')

二、后台程序

   后台程序比较繁杂,我们以每个功能如何实现的方式来梳理,这样好理解一点。

1. 时序问题
   此问题牵涉到全局的时序逻辑如何安排,它的实现方式是一个类似定时器中断的方式,每隔一定的时间执行一次某一部分代码。下面给出的代码第一行和最后一行最重要,第一行位于整个项目的最后,相当于启动定时器,做过嵌入式软件开发的都知道,配置好定时器后还需要启动它,然后在定时器中断服务函数里面执行代码,但是这里没有定时器中断,如何实现定时、重复地执行某一部分代码呢?用递归!下面代码的最后一行给出了具体的实现方法,after方法的第一个参数表示延时的时长,单位是ms,它延时的时基来源于操作系统;第二个参数表示延时后要执行的代码,我们把它封装在一个函数中,这个函数就相当于了上面提到的定时器中断服务函数。也就是说,我们用递归的方式,每间隔200ms执行一次time_date函数中的代码,这就相当于给整个后台程序提供了一个5Hz的定时中断功能!

root.after(200,time_date) #每隔200ms定时,此行代码跟下面的子函数不在一个地方

def time_date(): #5Hz的定时程序,提供整个后台的时间基准
    global TimeLabel 
    global share_codes
    global thresh_warning
    a = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') #获取时间
    TimeLabel.config(text = a)
    h,m,s = map(int,a[11:].split(':')) #把字符串处理成整型数据
    if s % period == 0: #刷新的周期
        iids = tree.get_children()  #删掉旧的数据
        for iid in iids:
            tree.delete(iid)
        try:
            res = ts.get_realtime_quotes(share_codes) #从网络获取新的股票信息
        except Exception:
            err = messagebox.showerror('错误','网络连接状态不佳!')
            root.destroy()
        show(share_codes,get_values(res,thresh_warning)) #显示出新的信息
    root.after(200,time_date) #200ms后重复一次

2. 获取实时的股票信息
    获取实时股票信息这一功能,我们用到了tushare库中的get_realtime_quotes([‘share code’])函数,该函数的形参是字符串的时候,表示只爬取某单只股票的实时数据;如果要爬取多只股票的信息,可以将要爬取的股票代码放到一个列表中,然后直接把列表作为实参就行了(如下面第一段代码所示)。另外,笔者特地把get_realtime_quotes()函数支持获取数据的股票代码整合到了压缩文件中的A_shares.xlsx文件中,实际应用中,大家可以参考输入股票代码。
另外,需要指出的是上面用到的函数的返回值不能直接用来显示到二维表格中,我们需要对它做进一步的处理,将获取到的数据提炼出我们感兴趣的,然后经过一个逻辑判断的过程再额外添加一栏状态信息(也就是判断是否处于“警告”状态),然后封装成一个列表,以便后续显示的需要。具体实现如下面的第二段代码所示:

#第一段代码
share_codes = []
'''  ''' #读取文件,获取share_codes列表的元素
res = ts.get_realtime_quotes(share_codes)
def get_values(a,thresh): #数据格式转换,用于计算股票的值,以及判断状态
    res = []
    global share_codes
    for i in range(len(a)):
        if a[i:i+1] is None:
            err = messagebox.showerror('错误','股票代码错误!')
            res.append(['-','-','-','-','-','-']) 
        else:
            b = list(a[i:i+1].values[0][0:4])
            c = (float(b[3])-float(b[2]))/float(b[2])*100
            c = round(c,3)
            b.append(c)
            if thresh[share_codes[i]][0]<c<thresh[share_codes[i]][1]:
                b.append('正常')
            else:
                b.append('警告')
            res.append(b)
    return res

3. “记忆”功能
    所谓“记忆”功能,就是可以加载上一次的数据,并支持保存这一次数据的操作。小工具的一开始执行就会读取上一次保存的股票代码以及相应的阈值(记忆阈值的功能是新增的,后面还会讲),然后进入正常的周期性工作状态。退出小工具时,如果对股票有新增或删减或阈值变动的操作,就会提示“是否保存当前的状态”,如果是,便会写入文件以备下一次加载读取。相关源码如下:

#读取文件,加载上一次保存的结果
try:
    f = open('D:\\Projects\\Python_project\\gui\\share_codes.txt','r')
    while True:
        s = f.readline().replace('\n','')
        if not s:
            break;
        cd,lwr,upr = s.split()
        share_codes.append(cd)
        lwr = float(lwr)
        upr = float(upr)
        thresh_warning[cd] = [lwr,upr]
    f.close()
except FileNotFoundError:
    err = messagebox.showerror('错误','找不到文件!')
    root.destroy()
#写入文件,保存此次操作变动的结果
def Exit(): #点击退出后执行此函数
    global changeFlag
    global share_codes
    global thresh_warning
    if changeFlag:
        a = messagebox.askyesnocancel('退出','是否保存修改后的记录?')
        if a :
            with open('share_codes.txt','w') as f:
                for i in share_codes:
                    f.write(i+' ') #写入文件
                    f.write(str(thresh_warning[i][0])+' ')
                    f.write(str(thresh_warning[i][1]))
                    f.write('\r')
        root.destroy()
    else:
        root.destroy()

4. 显示刷新后的信息
    当股票信息从网络刷新后,还需要显示在表格中的信息刷新,以便我们可以观察到变化。显示刷新分为两步:第一步删掉旧的信息;第二步,显示新的内容。删除旧的内容就需要获取Tree类的中二叉树的所有孩子的ID,然后调用delete方法,删掉ID就会清掉相应的数据。具体实现见第一部分代码。第二步显示新内容,需要上面讲的 “2. 获取实时的股票信息”中传回的数据,在末尾插入数据即可,代码如下:

#第一段代码
iids = tree.get_children()
for iid in iids:
    tree.delete(iid)
#第二段代码
def show(code,value): #显示出表格
    rowCounter = 1
    for i in range(len(code)): 
        if rowCounter % 2 == 1:
            tree.insert("",index = END,text = code[i],values = value[i])
        else:
            tree.insert("",index = END,text = code[i],
                        values = value[i],tags = ('evenColor'))
        rowCounter += 1    

5. 获取交互数据
    获取人机交互的数据这一功能实现的方法有多种,我们一一列举:增加/删除股票、设置阈值、设置周期。这一部分需要给上面讲的渲染GUI界面那一部分代码提供接口,比如说增删按钮,设置它的GUI属性时要额外加上它的一个参数"command",其值是一个函数名称,该参数意为:当点击按钮时,会执行相应的函数,进而实现某种功能。设置阈值功能的实现是通过绑定事件来完成的,双击表格某一行,会触发该行的一个doubleEvent事件,在该事件里面我们添加相关的处理代码,然后获取结果。设置周期这一功能也是通过配置子菜单时设置command参数然后调用相关函数来实现的。另外需要再说一点,在交互的过程中用到到tkinter库的Entry输入框组件、复选框组件,二者获取输入数据通过调用对应组件的get()方法实现。具体代码如下:

def get_period(a=60): # 获取刷新周期的函数
    global period
    period = a

def removeItem(): #删除表格中的项目
    iids = tree.selection()
    global changeFlag
    global share_codes
    changeFlag = True
    for iid in iids:
        item_text = tree.item(iid,"text")
        share_codes.remove(item_text)
        tree.delete(iid)
def addItem(): #添加要监控的股票
    subroot = Toplevel()
    subroot.geometry('300x100')
    subroot.title('增加股票/指数')
    tip = Label(subroot,text = '输入股票代码:').place(relx = 0.01,rely = 0.01)
    tip1 = Label(subroot,text = '或者挑选基金:').place(relx = 0.01,rely = 0.36)
    code = Entry(subroot)
    code.place(relx = 0.3,rely = 0.01)
    def get_entry_str(): #内联函数,获取股票/基金代码
        global changeFlag
        global share_codes
        global indexs
        global thresh_warning
        if code.get() is not '':
            share_codes.append(code.get())
            thresh_warning[share_codes[-1]] = [-1.0,1.0]
            changeFlag = True
        if var.get() is not '':
            share_codes.append(indexs[var.get()])
            thresh_warning[share_codes[-1]] = [-1.0,1.0]
            changeFlag = True
        share_codes = list(set(share_codes))
        subroot.destroy()
    cfmBtn = Button(subroot,text = '确定',
                    width = 10,
                    command = get_entry_str).place(relx = 0.4,rely = 0.68)
    var = StringVar()
    indice = ttk.Combobox(subroot,textvariable = var)
    indice['value'] = ('上证指数','上证50','沪深300')
    indice.place(relx = 0.3,rely = 0.36)
    subroot.mainloop()


def doubleClick(event):  #绑定双击事件,用于设定预警阈值
    global changeFlag
    changeFlag = True
    e = event.widget
    iid = e.identify("item",event.x,event.y)
    code = e.item(iid,"text")
    subroot1 = Toplevel()
    subroot1.title('设置预警阈值')
    subroot1.geometry('600x50')
    tip_up = Label(subroot1,text = '输入阈值上限:').pack(side = LEFT)
    up1 = Entry(subroot1)
    up1.pack(side = LEFT)
    tip_dowm = Label(subroot1,text = '输入阈值下限:').pack(side = LEFT)
    down1 = Entry(subroot1)
    down1.pack(side = LEFT)
    def get_thresh(): #内联函数,获取阈值上下限
        global thresh_warning
        if (up1.get() is '') or (down1.get() is ''):
            err = messagebox.showerror('错误','输入不可为空!')
        else:
            thresh_warning[code] = [float(down1.get()),float(up1.get())]
            subroot1.destroy()

    cfm = Button(subroot1,text = '确定',width = 10,
                 command = get_thresh).pack(side = LEFT,padx = 10)
    subroot1.mainloop()
    

6. 小工具新增的功能
   相对于第一个版本,笔者又稍微调整并新增了部分功能:
   新增当日开盘状态显示,如果处于收盘状态,会自动关掉访问网络,以增加稳定性,见下面的代码;
   新增记忆阈值数据,退出时将会把阈值上下限写入文件,源码见第3小节“记忆”功能部分代码;
   新增异常情况的处理,如果出现网络异常,将会提示以避免程序崩溃,源码见第3小节“记忆”部分代码;

#定义判断交易当日是否为开盘状态的函数
def is_open(h,m,s):
    t = h*10000+m*100
    if (93000<=t<=113000)or(130000<=t<=150000):
        return True
    else:
        return False

#显示大盘状态
    h,m,s = map(int,a[11:].split(':'))
    if station:
        TimeLabel.config(text = a+" 开盘中")
    else:
        TimeLabel.config(text = a+" 已收盘")
        
#每分钟都更新是否开盘的状态       
    if s == 0:
        station = is_open(h,m,s)

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 有很多工具可以快速梳理程序代码,具体可以根据您使用的编程语言和编辑器以及您的需求来选择。常见的工具包括: 1. 代码格式化工具,如 `clang-format`,可以将代码自动格式化为指定的风格。 2. 代码重构工具,如 `Refactor! Pro`,可以自动重构代码,比如自动提取出函数、重命名变量等。 3. 代码规范检查工具,如 `ESLint` 和 `Pylint`,可以帮助您找出代码中的不规范之处,并提出建议修改。 4. 代码浏览工具,如 `CTags` 和 `Cscope`,可以帮助您快速浏览代码中的函数、变量和类定义等。 5. 代码图形化工具,如 `Doxygen` 和 `Graphviz`,可以将代码生成 UML 图或其他图形,从而帮助您更好地理解代码结构。 ### 回答2: 在软件开发过程中,有一些工具可以帮助程序员快速梳理程序代码。以下是一些常用的工具: 1. IDE(集成开发环境):常见的IDE如Eclipse、IntelliJ IDEA等提供了各种功能,包括代码编辑、编译、调试等。IDE通常会自动格式化代码,并提供代码补齐和重构工具,帮助程序员快速修改和梳理代码。 2. 代码静态分析工具:例如FindBugs、Checkstyle等。这些工具通过检查代码中的潜在错误、不合规范的编码风格以及代码质量问题等,帮助程序员梳理代码,并提供修复建议。 3. UML工具:Unified Modeling Language(统一建模语言)工具如Enterprise Architect、Visual Paradigm等可以将代码转换为UML图,帮助程序员理清代码结构和逻辑关系,快速梳理和分析程序。 4. 代码审查工具:例如SonarQube、Crucible等。这些工具可以对代码进行全面、系统的审查,帮助程序员发现潜在的问题和改进代码质量。 5. 版本控制工具:例如Git、SVN等。版本控制工具可以记录和管理代码的修改历史,帮助程序员追踪代码的演变和变更,方便梳理代码以及回退修改。 6. 搜索和替换工具:例如grep、sed等。这些工具可以通过正则表达式或其他搜索条件来快速搜索和替换代码中的特定模式,帮助程序员快速修改和梳理代码。 通过使用这些工具,程序员可以更高效地梳理程序代码,提高代码质量和开发效率。 ### 回答3: 在程序开发的过程中,有一些工具能够帮助程序员快速梳理程序代码,提高开发效率和代码质量。以下是几个常用的工具: 1. 编辑器与集成开发环境(IDE):像Visual Studio Code、Atom、Sublime Text和Eclipse等编辑器和IDE都提供了代码编辑、代码提示、代码格式化、代码折叠等功能,方便程序员对代码进行梳理和重构。 2. 代码版本控制工具:如Git和SVN等,它们能够对代码进行版本管理,并提供分支管理、合并等功能,可以方便地对代码进行梳理、重构和修改。 3. 静态代码分析工具:例如Pylint、Checkstyle和SonarLint等工具,能够对代码进行静态分析,检测代码中的潜在错误、代码规范性问题和性能问题等,帮助程序员梳理代码,提高代码质量。 4. 代码重构工具:例如Visual Studio、IntelliJ IDEA和Eclipse等IDE都提供了代码重构功能,可以通过自动重构或批量重构的方式对代码进行梳理和优化,例如提取方法、提取变量、重命名等。 5. 代码测试工具:例如JUnit和Selenium等测试框架,可以帮助程序员编写和运行单元测试、UI测试等,及时发现代码中的问题,帮助梳理代码,提高代码质量。 总之,以上提到的工具不仅能够帮助程序员快速梳理代码,还能提高代码质量、减少错误和提高开发效率。在实际开发中,根据具体需求选择适合自己的工具,可以更好地进行代码梳理和维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值