文章目录
- 前情提要
- 源代码模式
- 输入序列
- 源码
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.MODESslct.bind('<<ComboboxSelected>>', self.slctChanged)slct.pack(side=tk.LEFT)self.initRes(widths[3])
源代码模式
源码模式最简单,只要有一个Entry就可以,从外观上来看,不需要做任何修改,但代码需要写到另一个函数中。
代码如下,其逻辑顺序是,先实现一个srcEntry,然后调用showSrcEntry将其展示出来。
def initRes(self, width):self.errWidth = widthmode = 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 = widthself.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 ttkimport numpy as npclass AxisFrame(ttk.Frame):# widths 是每个控件的宽度def __init__(self, master, label, mode, widths, **options):super().__init__(master, **options)self.pack()self.label = labelself.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.MODESslct.bind('<<ComboboxSelected>>', self.slctChanged)slct.pack(side=tk.LEFT)self.initRes(widths[2])def initRes(self, width):self.errWidth = widthself.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.datadef loadData(self, data):if type(data) != type(None):self.data = datareturn self.datadef getArray(self):vs = [float(t.get()) for t in self.arrText]self.data = np.arange(*vs)return self.data