Python绘图系统:
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('<>' , 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)
最后效果如下
另一方面,需要更改getArray函数
def getArray(self):
vs = [float(t.get()) for t in self.arrText]
self.data = np.arange(*vs)
return self.data
绘图结果如下
本文仅改动了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('<>' , 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