Python绘图系统:
在作图过程中必然会产生大量数据,所以AxisList这个类预留了导出按钮。在具体实现之前,不妨分析一下数据导出所需要的参数。
首先,AxisList类中往往不止有一个坐标轴,所以在导出数据之前,有必要想清楚要导出哪些坐标轴的文件,或者将所有坐标轴的数据都导出。如果都导出,那么是将所有轴的数据合在一处,还是每个坐标轴一个文件。
正因为选择太多,所以一个按钮可能不太够用了,所以采用Menubutton来实现。
首先,将initFeature中有关导出按钮的部分改为如下形式
btn = ttk.Menubutton(frm, text="导出",width=4)
btn.pack(side=tk.LEFT)
m = self.initExportMenu(btn)
btn.config(menu=m)
然后设置菜单
def initExportMenu(self, btn):
m = tk.Menu(btn)
m.add_command(label="合并导出", command = self.mExportMerge)
m.add_command(label="全部导出", command = self.mExportAll)
m.add_command(label="导出导出", command = self.mExportOne)
return m
def mExportMerge(self):
pass
def mExportAll(self):
pass
def mExportOne(self):
pass
效果如下

单轴导出和全部导出其实是一码事,区别只是后者需要跑一个循环而已,这个功能做在AxisFrame类中显然更合适,考虑到本文只对Axis进行修改,所以这里只做出一个接口,等到日后再去编辑AxisFrame时再行实现。
其单轴另存的代码如下,self.als[flag].save()是尚未实现的功能。
def mExportOne(self):
flag = askstring("请选择输出的轴")
if flag not in self.getDim():
showwarning("您输入的坐标轴并不存在")
#self.afs[flag].save()
相应地,全部导出可以做成下面的形式:把所有轴的数据导入到同一个文件夹中。
def mExportAll(self):
path = askdirectory()
for flag in self.getDim():
#self.afs[flag].save(path)
pass
合并导出却并不是应该在AxisFrame中完成的工作,而且考虑到数据合并后将使得坐标轴的信息丢失,所以不适合用纯粹的二进制格式,而是采用numpy的.npy或者是csv格式作为输出方案。
def mExportMerge(self):
FILES = [('numpy数组', 'npy'), ('文本文件', 'csv')]
path = asksaveasfilename(filetypes=FILES)
arr = np.array([self.afs[flag].data for flag in self.getDim()])
if path.endswith('.npy'):
arr.tofile(path)
else:
np.savetxt(path, arr, delimiter=', ')
最后,附上AxisList的源代码。
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.filedialog import *
from tkinter.messagebox import showwarning
import numpy as np
from aframe import AxisFrame, AskDct
from base import DrawType, DrawStyle
class AxisList(ttk.Frame):
def __init__(self, master,
title, mode, widths,
types, typeDct, # 绘图类型Combobox的参数
**options):
super().__init__(master, **options)
self.pack()
self.afs = {}
self.data = {}
self.initWidgets(title, widths)
self.initFeature(types, typeDct)
self.initAxis(mode, widths)
self.initStyleFrame()
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) # 此为主控件
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
frm.pack(pady=2, side=tk.TOP, fill=tk.X)
ttk.Button(frm, text="导入",width=4,
command=self.btnLoadData).pack(side=tk.LEFT)
btn = ttk.Menubutton(frm, text="导出",width=4)
btn.pack(side=tk.LEFT)
m = self.initExportMenu(btn)
btn.config(menu=m)
self.drawType = DrawType(frm, typeDct,
func=self.dimChanged)
self.drawType.pack(side=tk.LEFT, padx=2)
self.vis = {L : True for L in 'txyz'}
ttk.Button(frm, text="风格",width=5,
command=self.btnShowStyle).pack(side=tk.LEFT)
self.showStyle = False
# 设置导出按钮的菜单
def initExportMenu(self, btn):
m = tk.Menu(btn)
m.add_command(label="合并导出", command = self.mExportMerge)
m.add_command(label="全部导出", command = self.mExportAll)
m.add_command(label="单轴导出", command = self.mExportOne)
return m
def mExportMerge(self):
FILES = [('numpy数组', 'npy'), ('文本文件', 'csv')]
path = asksaveasfilename(filetypes=FILES, initialfile=True)
arr = np.array([self.afs[flag].data for flag in self.getDim()])
if path.endswith('.npy'):
arr.tofile(path)
else:
np.savetxt(path, arr, delimiter=', ')
def mExportAll(self):
path = askdirectory()
for flag in self.getDim():
#self.afs[flag].save(path)
pass
def mExportOne(self):
flag = askstring("请选择输出的轴")
if flag not in self.getDim():
showwarning("您输入的坐标轴并不存在")
#self.afs[flag].save()
# 初始化坐标轴
def initAxis(self, mode, widths):
for flag in 'txyz':
self.afs[flag] = AxisFrame(self._a, flag, mode, widths)
self.afs[flag].pack(side=tk.TOP, fill=tk.X)
self.vis = {L : L in self.getDim() for L in 'txyz'}
self.updateVisible()
# 初始化风格控件
def initStyleFrame(self):
self.sf = ttk.LabelFrame(self._s, text="绘图风格")
self.drawStyle = DrawStyle(self.sf)
self.drawStyle.pack(side=tk.TOP, fill=tk.X)
# 维度改变时的回调函数
def dimChanged(self, evt):
txyz = self.getDim()
for flag in 'txyz':
self.vis[flag] = flag in txyz
self.updateVisible()
# 更新隐藏和显示
def updateVisible(self):
for flag in 'txyz':
self.afs[flag].pack_forget()
for flag in 'txyz':
if self.vis[flag]:
self.afs[flag].pack(side=tk.TOP, fill=tk.X)
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 getSub(self):
return self.drawType.getSub()
def getProj(self):
return self.drawType.getProj()
def getType(self):
return self.drawType.getType()
def getDim(self):
return self.drawType.getDim()
def getXYZ(self):
return self.getDim().replace("t", "")
def hasTimeAxis(self):
return "t" in self.getDim()
def getStyle(self):
return self.drawStyle.getVarDct()
# 加载数据
def btnLoadData(self):
name = askopenfilename()
data = np.genfromtxt(name)
for i, flag in enumerate('xyz'):
if i >= data.shape[1]:
return
self.setOneMode(flag, "外部导入")
self.data[flag] = self.setData(flag, data[:,i])
def Click(self):
if self.collapsed:
self._c.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES)
else:
self._c.pack_forget()
self.collapsed = not self.collapsed
# 设置数据
def setData(self, flag, data=None, **options):
return self.afs[flag].setData(data, **options)
# 设置模式
def setOneMode(self, flag, mode):
self.afs[flag].setMode(mode)