本文已整合到:从0开始实现一个三维绘图系统
导入数据
单纯从作图的角度来说,更多情况是已经有了一组数据,然后需要将其绘制。这组数据可能是txt格式的,也可能是csv格式的,还可能是二进制数据。当然,这些一会儿在想,首先就是要添加一个按钮,将setCtrlButtons函数添加一行:
def setCtrlButtons(self, frm):
ttk.Button(frm, text="绘图",width=5,
command=self.btnDrawImg).pack(side=tk.LEFT)
ttk.Button(frm, text="加载",width=5,
command=self.btnLoadData).pack(side=tk.LEFT)
然后就可以考虑self.btnLoadData
函数了。
简洁起见,以后将不再具体展示setCtrlButtons的具体代码,而只是写出新增的代码。
文件对话框
加载数据,其实就是加载文件,那么文件对话框就很重要。
tkinter.filedialog中的askopenfilename就是文件对话框,预感这个函数可能不止一处出现,故而导入一下,以方便调用
from tkinter.filedialog import askopenfilename
这个函数的好处是,只返回读取到的文件名,而不像askopenfile一样返回一个文件对象。
而self.btnLoadData
函数,如果只是想实现一个最简单的功能,那么
可以写为
def btnLoadData(self):
name = askopenfilename()
data = np.genfromtxt(name)
if data.shape[1] < 2:
return
self.xs = data[:,0]
self.ys = data[:,1]
self.drawPlot()
效果如下
修改绘图逻辑
现在,我们有了两种数据生成模式,一是用语法生成,二是通过加载得到。但目前来说这两种生成方式并不兼容。为了解决这个问题,可以为x和y的输入框添加一个标识,比如当x或者y的输入框中是data的时候,再点击绘图,就可以选中加载后的数据。
由于tkinter中输入Entry内容比较繁琐,所以封装一个全局的函数专门用于更改Entry内容
def setEntry(e, text):
e.delete(0, "end")
e.insert(0, text)
接下来,将加载数据函数和绘图函数分别改写为
def btnLoadData(self):
name = askopenfilename()
data = np.genfromtxt(name)
if data.shape[1] < 2:
return
self.xs = data[:,0]
setEntry(self.xEntry, "data")
self.ys = data[:,1]
setEntry(self.yEntry, "data")
def btnDrawImg(self):
xLab = self.xEntry.get()
if xLab != "data":
x = eval(f"np.linspace({xLab})")
self.xs = x
else:
x = self.xs
yLab = self.yEntry.get()
if yLab != "data":
self.ys = eval(yLab)
self.drawPlot()
在btnLoadData函数中,取消了绘图功能,而是在导入数据后,将xEntry和yEntry的内容设置为"data"。
而绘图函数中,检测xEntry和yEntry的内容,如果是data,那么说明已经读取到了相关数据,就直接调用,而非重新生成。
源代码
最后,把源代码附在下面
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.filedialog import askopenfilename
import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import numpy as np
def setEntry(e, text):
e.delete(0, "end")
e.insert(0, text)
class DarwSystem():
def __init__(self):
self.root = tk.Tk()
self.root.title("数据展示工具")
frmCtrl = ttk.Frame(self.root,width=320)
frmCtrl.pack(side=tk.RIGHT, fill=tk.Y)
self.setFrmCtrl(frmCtrl)
frmFig = ttk.Frame(self.root)
frmFig.pack(side=tk.LEFT,fill=tk.BOTH,expand=tk.YES)
self.setFrmFig(frmFig)
self.root.mainloop()
def setFrmCtrl(self, frmCtrl):
frm = ttk.Frame(frmCtrl, width=320)
frm.pack(side=tk.TOP, fill=tk.X)
self.setCtrlButtons(frm)
frm = ttk.Frame(frmCtrl)
frm.pack(side=tk.TOP, fill=tk.X)
self.setFrmX(frm)
frm = ttk.Frame(frmCtrl)
frm.pack(side=tk.TOP, fill=tk.X)
self.setFrmY(frm)
def setFrmX(self, frm):
tk.Label(frm, text="x").pack(side=tk.LEFT)
self.xEntry = tk.Entry(frm)
self.xEntry.pack(side=tk.LEFT, fill=tk.X)
def setFrmY(self, frm):
tk.Label(frm, text="y").pack(side=tk.LEFT)
self.yEntry = tk.Entry(frm)
self.yEntry.pack(side=tk.LEFT, fill=tk.X)
def setCtrlButtons(self, frm):
ttk.Button(frm, text="绘图",width=5,
command=self.btnDrawImg).pack(side=tk.LEFT)
ttk.Button(frm, text="加载",width=5,
command=self.btnLoadData).pack(side=tk.LEFT)
def btnLoadData(self):
name = askopenfilename()
data = np.genfromtxt(name)
if data.shape[1] < 2:
return
self.xs = data[:,0]
setEntry(self.xEntry, "data")
self.ys = data[:,1]
setEntry(self.yEntry, "data")
def btnDrawImg(self):
xLab = self.xEntry.get()
if xLab != "data":
x = eval(f"np.linspace({xLab})")
self.xs = x
else:
x = self.xs
yLab = self.yEntry.get()
if yLab != "data":
self.ys = eval(yLab)
self.drawPlot()
def drawPlot(self):
self.fig.clf()
ax = self.fig.add_subplot()
ax.plot(self.xs, self.ys)
self.fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
self.canvas.draw()
pass
def setFrmFig(self, frmFig):
self.fig = Figure()
self.canvas = FigureCanvasTkAgg(self.fig,frmFig)
self.canvas.get_tk_widget().pack(
side=tk.TOP,fill=tk.BOTH,expand=tk.YES)
self.toolbar = NavigationToolbar2Tk(self.canvas,frmFig,
pack_toolbar=False)
self.toolbar.update()
self.toolbar.pack(side=tk.RIGHT)
if __name__ == "__main__":
test = DarwSystem()