文章目录

  • 前情提要
  • 源代码模式
  • 输入序列
  • 源码


Python绘图系统:

  • 📈从0开始的3D绘图系统📉一套3D坐标,多个函数📊散点图、极坐标和子图
  • 自定义控件:极坐标📉绘图风格📉风格控件
  • 图表类型和风格:定制绘图风格
  • 坐标设置进阶:动态更新组件

前情提要

AxisFrame是存放某一维坐标的组件,目前由一个标签,一个下拉选框和一个输入框构成。下拉选框主要目的是判断输入方式,目前有4个选择,分别是源代码、序列化、外部导入和无数据。

其中源代码就是通过调用eval执行的代码,这个没什么好说的。但序列化目前的实现方式,用的仍旧是类似Pyton的语法,然后在外面套一层壳,这还是得程序员才能懂。而外部导入目前只起到一个说明的作用,即外部数据导入了,然后这个地方一变,换言之,AxisFrame自身并没有导入数据的功能。

所以,这两个功能还是需要优化的,当ComboBox的选中项发生变化时,需要调整一下布局,故而initWidgets函数改成下面的形式,其中initRes则根据传入的数据获取模式来初始化其他控件。

def initWidgets(self, widths):
    tk.Label(self, text=self.label, width=widths[0]).pack(side=tk.LEFT)
    slct = ttk.Combobox(self, width=widths[1], 
        textvariable=self.mode)
    slct['value'] = self.MODES
    slct.bind('<<ComboboxSelected>>', self.slctChanged)
    slct.pack(side=tk.LEFT)
    self.initRes(widths[3])

源代码模式

源码模式最简单,只要有一个Entry就可以,从外观上来看,不需要做任何修改,但代码需要写到另一个函数中。

代码如下,其逻辑顺序是,先实现一个srcEntry,然后调用showSrcEntry将其展示出来。

def initRes(self, width):
    self.errWidth = width
    mode = self.mode.get()
    self.srcEntry = tk.Entry(self, width=width, textvariable=self.srcText)
    if mode=="源代码":
        self.showSrcEntry()
    
def showSrcEntry(self):
    self.srcEntry.pack(padx=5, pady=2, side=tk.LEFT, fill=tk.X)

其中self.srcText是一个StringVar,是在initVar中定义的

self.srcText = tk.StringVar()

然后把readPython函数改为

def readPython(self, t=None, x=None, y=None, z=None):
    self.data = eval(self.srcText.get())
    return self.data

输入序列

序列化的含义是生成一个等差数列,要有起点,有终点,还得有步长。所以最直接的创建方法,就是三个Label和三个Entry,而且这些部件需要放进一个Frame中。结合已有的源代码输入控件,initRes函数改为下列形式。

def initRes(self, width):
    self.errWidth = width
    self.srcEntry = tk.Entry(self, width=width, textvariable=self.srcText)
    
    self.arrFrame = ttk.Frame(self, width=width-5)
    for i, key in enumerate(["起点", "终点", "步长"]):
        tk.Label(self.arrFrame, text=key).grid(row=0, column=i*3)
        tk.Entry(self.arrFrame, width=int(width/6), 
            textvariable=self.arrText[i]).grid(row=0, column=i*3+1)                

    self.showRes(self.mode.get())

其中self.arrText是一个StringVar列表,定义在initVar中

self.arrText = [tk.StringVar() for _ in range(3)]

self.showRes则是显示某组部件的方法

def showRes(self, mode):
    resDct = {"源代码":self.srcEntry, "序列化":self.arrFrame}
    resDct[mode].pack(padx=5, pady=2, side=tk.LEFT, fill=tk.X)

最后效果如下

python agent 如何在线更新代码_动态更新组件

另一方面,需要更改getArray函数

def getArray(self):
    vs = [float(t.get()) for t in self.arrText]
    self.data = np.arange(*vs)
    return self.data

绘图结果如下

python agent 如何在线更新代码_tkinter_02

源码

本文仅改动了aframe.py中的源代码,其他代码在这篇博客后面:定制风格的绘图系统

aframe.py改动之后的代码如下

import tkinter as tk
import tkinter.ttk as ttk

import numpy as np

class AxisFrame(ttk.Frame):
    # widths 是每个控件的宽度
    def __init__(self, master, label, mode, widths, **options):
        super().__init__(master, **options)
        self.pack()
        self.label = label
        self.initVar(mode)
        self.initWidgets(widths)
    
    def initVar(self, mode):
        self.MODES = ("序列化", "源代码", "外部导入", "无数据")
        self.mode = tk.StringVar()
        self.srcText = tk.StringVar()
        self.arrText = [tk.StringVar() for _ in range(3)]
        self.setMode(mode)
    
    def initWidgets(self, widths):
        tk.Label(self, text=self.label, width=widths[0]).pack(side=tk.LEFT)
        slct = ttk.Combobox(self, width=widths[1], 
            textvariable=self.mode)
        slct['value'] = self.MODES
        slct.bind('<<ComboboxSelected>>', self.slctChanged)
        slct.pack(side=tk.LEFT)
        self.initRes(widths[2])

    def initRes(self, width):
        self.errWidth = width
        self.srcEntry = tk.Entry(self, width=width, textvariable=self.srcText)
        
        self.arrFrame = ttk.Frame(self, width=width-5)
        for i, key in enumerate(["起点", "终点", "步长"]):
            tk.Label(self.arrFrame, text=key).grid(row=0, column=i*3)
            tk.Entry(self.arrFrame, width=int(width/6), 
                textvariable=self.arrText[i]).grid(row=0, column=i*3+1)                

        self.showRes(self.mode.get())
        
    def showRes(self, mode):
        resDct = {"源代码":self.srcEntry, "序列化":self.arrFrame}
        resDct[mode].pack(padx=5, pady=2, side=tk.LEFT, fill=tk.X)
        
    def showSrcEntry(self):
        self.srcEntry.pack(padx=5, pady=2, side=tk.LEFT, fill=tk.X)
    
    def showArrFrame(self):
        self.arrFrame.pack(padx=5, pady=2, side=tk.LEFT, fill=tk.X)


    def slctChanged(self, evt):
        self.srcEntry.pack_forget()
        self.arrFrame.pack_forget()
        mode = self.mode.get()
        self.showRes(self.mode.get())

    def setText(self, text):
        self.entry.delete(0, "end")
        self.entry.insert(0, text)

    def get(self):
        return self.entry.get()

    def setMode(self, mode):
        if type(mode) != str:
            mode = self.MODES[mode]
        self.mode.set(mode)

    def setData(self, data=None, **txyz):
        if self.mode.get() == "序列化":
            return self.getArray()
        elif self.mode.get() == "外部导入":
            return self.loadData(data)
        else:
            return self.readPython(**txyz)
    
    def readPython(self, t=None, x=None, y=None, z=None):
        self.data = eval(self.srcText.get())
        return self.data
    
    def loadData(self, data):
        if type(data) != type(None):
            self.data = data
        return self.data

    def getArray(self):
        vs = [float(t.get()) for t in self.arrText]
        self.data = np.arange(*vs)
        return self.data