绘图系统四:定制绘图风格

文章目录

    • 创建控件
    • 绘图风格
    • 可定制绘图风格的绘图系统
    • 代码组织

  • 一 三维绘图系统 二 多图绘制系统
  • 三 坐标轴定制
  • 源码地址 Python打造动态绘图系统

创建控件

尽管从matplotlib的角度来说,绘图风格也算是图像类型的一部分,但诸如点线字体标题等内容太过复杂,为了减轻DrawType的负担,所以新建一个组件。有了DrawType的经验,那么DrawStyle类在参数设置上就比较轻车熟路,整体框架大致如下

class DrawStyle(ttk.Frame):
    def __init__(self, master, 
        varDct, ws=None, func=None, **options):
        super().__init__(master, **options)
        self.pack()
    
    def initVars(self):
        pass

    def initWidgets(self):
        pass

但在丰富细节之前,需要修改一下AxisList的布局。考虑到绘图风格并不是经常会用到的控件,所以平时给隐藏起来。其initWidgets函数修改如下,之前直接依托在主frame中的AxisFrame,如今都要放到self._a中。而各种按钮都放在工具栏self._b中。

def initWidgets(self, title, widths):
    self.btn = ttk.Button(self, text=title, width=sum(widths)+5,
        command=self.Click)
    self.btn.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)
    self._c = ttk.Frame(self)       # 此为主frame
    self._b = ttk.Frame(self._c)    # 此外工具栏控件
    self._a = ttk.Frame(self._c)    # 此为坐标轴
    self._s = ttk.Frame(self._c)    # 此为绘图风格控件
    
    self._b.pack(side=tk.TOP)
    self._a.pack(side=tk.TOP)
    self._s.pack(side=tk.TOP)
    
    self.collapsed = True
    self.Click()

然后添加风格按钮

def initFeature(self, types, typeDct):
    frm = self._b
    # ...中间内容不变
    ttk.Button(frm, text="风格",width=5,
        command=self.btnShowStyle).pack(side=tk.LEFT)
    self.showStyle = False

btnShowStyle的逻辑也是老生常谈了

def btnShowStyle(self):
    self.showStyle = not self.showStyle
    if self.showStyle:
        self.sf.pack(side=tk.TOP, fill=tk.X)
    else:
        self.sf.pack_forget()

风格初始化如下

def initStyleFrame(self):
    self.sf = DrawStyle(self._s)
    ttk.Button(self.sf, text="点我").pack(side=tk.LEFT)

这个按钮存粹为了演示,后期要删掉的,演示结果如下

绘图系统四:定制绘图风格_第1张图片

绘图风格

以plot为例,下面列出常用参数,其中枚举类型表示有有限个可选择的值,说明适用于Combobox控件。

参数 类型 功能 参数 类型 功能
label 字符串 图例标签
linestyle 枚举 线条类型 linewidth 小数 线条宽度
marker 枚举 散点形状 markersize 小数 散点尺寸
alpha 小数 透明度 zorder 整数 所在绘图层
color 字符串 颜色 markeredgecolor 字符串 点的边框色

为了便于调用,将这些参数封装为字典

def initConst(self):
    self.VAR_LABS = {
        "线型" : "linestyle", "线宽" : "linewidth",  "线色" : "color",
        "点型" : "marker"   , "点径" : "markersize", "点色" : "markeredgecolor",
        "标签" : "label"    , "透明度"    : "alpha", "层号" : "zorder" 
    }
    self.STR_KEYS = ["标签"]
    self.COM_KEYS = ["线型", "点型"]
    self.NUM_KEYS = ["线宽", "点径", "透明度"]
    self.INT_KEYS = ["层号"]
    self.CLR_KEYS = ["线色", "点色"]

这样一来,初始化StringVar就方便很多

def initVars(self):
    self.varDct = {key:tk.StringVar() for key in self.VAR_LABS}

但接下来才是重头戏,UI绘制。

matplotlib中有四种线型,点型相对较多,而点和线的设置均包含形状、尺寸以及颜色,基于这种对偶关系,可以将这些参数设成下列形式

def initLineMarker(self):
    enumDct = {
        "点型" : ['.', ',', '1', '2', '3', '4', '+', 'x', '|', '_', 
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
            'o', 'v', '^', '<', '>', '8', 's', 'p', '*', 
            'h', 'H', 'D', 'd', 'P', 'X'],
        "线型" : ['-', '--', '-.', ':']
    }
    frm = self.newFrame()
    for i in range(2):
        key = self.COM_KEYS[i]
        ttk.Label(frm, text=key).grid(row=i, column=0, padx=2)
        tmp = ttk.Combobox(frm, width=10, textvariable=self.varDct[key])
        tmp.grid(row=i, column=1, padx=2, pady=2)
        tmp['value'] = enumDct[key]
        
        key = self.NUM_KEYS[i]
        ttk.Label(frm, text=key).grid(row=i, column=2, padx=2)
        tmp = ttk.Entry(frm, width=10, textvariable=self.varDct[key])
        tmp.grid(row=i, column=3, padx=2, pady=2)

        key = self.CLR_KEYS[i]
        ttk.Label(frm, text=key).grid(row=i, column=4, padx=2)
        tmp = ttk.Entry(frm, width=10, textvariable=self.varDct[key])
        tmp.grid(row=i, column=5, padx=2, pady=2)

绘图系统四:定制绘图风格_第2张图片

这样一来就只剩下标签,层号和透明度这三个参数了,由于标签颇有标题的意味,所以把这三个参数放在线型上面。

最后得到

绘图系统四:定制绘图风格_第3张图片

可定制绘图风格的绘图系统

如果想在DrawSystem中调用绘图风格,那么就需要DrawStyle对象可以输出绘图参数,由于这里面所有的参数都在字典里面,所以这一步非常容易

def getOneVar(self, key):
    v = self.varDct[key].get()
    if v=="": return ""
    if key in NUM_KEYS: return float(v)
    elif key in INT_KEYS: return int(v)
    else: return v

def getVarDct(self):
    dct = {self.VAR_LABS[key] : self.varDct[key].get() 
        for key in self.varDct}
    return {key : dct[key] for key in dct if dct[key]!=""}

第一个函数用于得到某个绘图参数,第二个则用字典的形式,返回所有已经设置的绘图参数。毕竟,在plot绘图过程中,并不是需要设置所有的绘图参数。

由于DrawStyle是在AxisList中被调用的,所以这个getVarDct函数最好在AxisList中重新封装一下

def getStyle(self):
    return self.DrawStyle.getVarDct()

最后,绘图风格实现的临门一脚,自然是DrawSystem中的plot函数

def drawPlot(self, ax, data, keys, style):
    ax.plot(*[data[key] for key in keys], **style)

多了一个style参数,由于绘图函数被重新赋给了func,从而所有绘图函数都要有相同的绘图接口,所以尽管暂时不用,drawScatter和drawBar也要加上style参数。同时更改绘图函数。代码如下,主要添加了一个al.getStyle()的调用。

def btnDrawImg(self):
    self.fig.clf()
    keys = self.drawTypeDim.getDim()
    self.axDct = {}
    for al in self.als:
        ax = self.setDrawAxis(al)
        data = self.readDatas(al)
        draw = self.drawDct[al.getDrawType()]
        style = al.getStyle()
        draw(ax, data, keys, style)
    self.fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
    self.canvas.draw()

最后得到下面的效果。

绘图系统四:定制绘图风格_第4张图片

代码组织

目前,这个绘图系统已经有了400多行代码,5个类被放在同一个文件中,修改起来已经有些不便了。为了开发工作得以继续,有必要把这几个类分发到不同的文件中。

绘图系统四:定制绘图风格_第5张图片

下面新建四个文件,分别放入以下内容

  • base.py:DrawType, DrawStyle
  • aframe.py: AxisFrame
  • alist.py: AxisList
  • ds.py: DrawSystem

你可能感兴趣的:(#,python,matplotlib,tkinter,绘图系统,自定义控件)