因此,我正在构建一个tkinter GUI程序,其中一个页面上具有matplotlib图。用户可以从各种弹出窗口中添加,删除和修改图形上的轨迹。
到目前为止,我一直在使用draw()
对象上的FigureCanvasTkAgg
方法来更改图形。我在其他几篇文章中也读到过,这会重绘 entire 图,并使渲染非常缓慢。另外,在我的特定情况下,它在轴上绘制了一个 lot 刻度标签,似乎在源中的每个点上都绘制了一个刻度标签,因此,除了花很长时间之外,它还使该图绝对不可读。它还不能正确绘制数据。
我偶然发现一些帖子建议不要使用draw方法,因为它会重新绘制整个图形,但这并不能真正解释为什么绘制了如此多的刻度标签,或者为什么未按预期绘制数据。我已经研究过使用动画,但是我不确定这是我所需要的,因为图形不必仅在用户更改时不断更新。
我正在使用Python 3.7.2和matplotlib 3.1.1。我正在绘制的数据是光谱数据,只有大约4000个点。我还用很小的数据集(10-15点)对其进行了测试,但这似乎没有发生。我也尝试过使用axis.locator_params(nbins=10)
设置刻度标签,但是并不能阻止所有这些标签的绘制。
任何想法都在这里发生了什么,如何预防呢?这是它产生的图形以及代码的相关部分。对不起,如果我什么都没有。我是SO的新手,但是我还是一个相对缺乏经验的程序员。
class App(tk.Tk): ###The controller of all pages, & control of operations
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.dfs = {} #contains dataframes loaded from csv/made by the user
self.spectra = {} #contains spectrum objects
self.plots = {} #contains Figure objects. Each figure can have exactly one axis
for F in (HomePage, SpectraPage, GraphPage, MakeSpectrumPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(HomePage)
def show_frame(self, cont):
self.frames[cont].tkraise()
def load(self, filename):
df = pd.read_csv(filename, header=None)
if any(df.iloc[0].apply(lambda x: isinstance(x, str))):
df = df[1:].reset_index(drop=True).rename(columns=df.iloc[0])
else:
names=[]
for i in range(len(df.columns)):
names.append("w%i" %i)
df.columns = names
self.dfs[filename] = df
self.frames[MakeSpectrumPage].makeFileList()
def get_dfs(self):
return self.dfs
def get_spectra(self):
return self.spectra
def get_plots(self):
return self.plots
def make_spectrum(self, name, df, x, y):
spectrum = Spectrum(name, df, x, y)
self.spectra[name] = spectrum
self.frames[SpectraPage].insertItems()
def make_plot(self, name):
fig = Figure(dpi=100)
fig.suptitle(name)
axis = fig.add_subplot(111, xlim=(4000, 500), ylim=(0,1))
axis.locator_params(nbins=10)
self.plots[name] = fig
def graph(self, axis, spectrum, **kwargs): #plot spectrum obj
axis.plot(spectrum.xdata, spectrum.ydata, label=spectrum.name, **kwargs)
class GraphPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.grid_columnconfigure(0,weight=1)
self.controller.make_plot('Plot 1')
self.showFigure(self.controller.get_plots()['Plot 1'])
self.makeGraphTray()
def showFigure(self, fig):
self.canvas = FigureCanvasTkAgg(fig, self)
self.canvas.draw()
self.canvas._tkcanvas.grid(row=1, column=1, rowspan=2, columnspan=2)
self.makeToolbar()
def makeToolbar(self):
self.toolbar_frame = tk.Frame(self)
self.toolbar_frame.grid(row=3,column=1, columnspan=2)
self.toolbar = NavigationToolbar2Tk(self.canvas, self.toolbar_frame)
self.toolbar.update()
def makeGraphTray(self):
#make container for graph/trace modification buttons
self.graphTray = tk.Frame(self, width=50)
self.graphTray.grid(row=1, column=3, sticky='nsew')
addTraceButton = ttk.Button(self.graphTray, text="Add Trace", command=lambda:self.raisePopup(NewTracePopup))
addTraceButton.grid(row=0, column=0, sticky='nsew')
def raisePopup(self, Popup):
popup = Popup(self)
class Spectrum: #Objects of this class are two-column structures.
def __init__(self, name, sourcedf, x, y):
self.xdata = sourcedf[x]
self.ydata = sourcedf[y]
self.df = pd.concat([self.xdata, self.ydata], axis=1)
self.name = name
class ConditionalPopup(tk.Toplevel):
#parent class of OK/Cancel popups where OK is disabled until all fields are filled
def __init__(self, master, title, **kwargs):#@param **kwargs the Variables traced by the widgets in the popup
if all(isinstance(kwarg, tk.Variable) for kwarg in kwargs.values()):
super().__init__(master)
self.__dict__.update(kwargs)
self.master = master
self.vars = kwargs #dictionary of kwargs
self.wm_title(title)
self.widgetFrame = tk.Frame(self)
self.widgetFrame.grid(row=0, column=0)
self.traceVars()
self.makeWidgets(self.widgetFrame)
self.placeWidgets()
else: raise TypeError
def traceVars(self):
for var in self.vars.values():
var.trace('w', self.activateOK)
def activateOK(self, *args):
if all(self.vars[key].get() for (key, value) in self.vars.items()):
self.okButton.configure(state='normal')
else:
self.okButton.configure(state='disabled')
def makeWidgets(self, frame):
self.okButton = ttk.Button(self, text="OK", state='disabled', command=self.okPressed)
self.cancelButton = ttk.Button(self, text="Cancel", command=self.destroy)
def placeWidgets(self):
self.okButton.grid(row=1, column=0, padx=2.5, pady=10, sticky='e')
self.cancelButton.grid(row=1, column=1, padx=2.5, pady=10, sticky='w')
def okPressed(self, *args):
self.destroy()
class NewTracePopup(ConditionalPopup):
#a popup that enables adding a trace to a chosen plot
def __init__(self, master):
super().__init__(master, "Add trace to plot",
plotVar=tk.StringVar(),
spectrumVar=tk.StringVar(),
colorVar=tk.StringVar(),
linewidthVar=tk.StringVar())
def activateOK(self, *args):
if self.linewidthVar.get().isdecimal():
super().activateOK(*args)
def makeWidgets(self, frame):
self.plotLabel = tk.Label(frame, text="Plot:")
self.plotCombobox = ttk.Combobox(frame, state='readonly', values=list(self.master.controller.get_plots().keys()), textvariable=self.plotVar)
self.spectrumLabel = tk.Label(frame, text="Spectrum:")
self.spectrumCombobox = ttk.Combobox(frame, state='readonly', values=list(self.master.controller.get_spectra().keys()), textvariable=self.spectrumVar)
self.cLabel = tk.Label(frame, text="Colour:")
self.cCombobox = ttk.Combobox(frame, state='readonly', values=list(mcolors.BASE_COLORS)+list(mcolors.TABLEAU_COLORS), textvariable=self.colorVar)
self.mLabel = tk.Label(frame, text="Line width:")
self.mEntry = ttk.Entry(frame, textvariable=self.linewidthVar)
super().makeWidgets(frame)
def placeWidgets(self):
self.plotLabel.grid(row=0, column=0, padx=10, pady=10, sticky='e')
self.plotCombobox.grid(row=0, column=1, padx=10, pady=10, sticky='w')
self.spectrumLabel.grid(row=1, column=0, padx=10, pady=10, sticky='e')
self.spectrumCombobox.grid(row=1, column=1, padx=10, pady=10, sticky='w')
self.cLabel.grid(row=2, column=0, padx=10, pady=10, sticky='e')
self.cCombobox.grid(row=2, column=1, padx=10, pady=10, sticky='w')
self.mLabel.grid(row=3, column=0, padx=10, pady=10, sticky='e')
self.mEntry.grid(row=3, column=1, padx=10, pady=10, sticky='w')
super().placeWidgets()
def okPressed(self, *args):
self.master.controller.graph(self.master.controller.get_plots()[self.plotVar.get()].axes[0],
self.master.controller.get_spectra()[self.spectrumVar.get()],
color=self.colorVar.get(),
linewidth=float(self.linewidthVar.get()))
self.master.canvas.draw()
super().okPressed()