我已经被困在这个问题上一段时间了,无法解决这个问题,所以希望这里有人能提供帮助。
我正在尝试创建一个程序来绘制.csv文件中存储的一些数据。我的程序从一个空图形开始,要求用户首先选择一个文件,然后提示用户指定应该绘制csv文件中的哪些列。为了获得帮助,我创建了一个较小的程序来概述我正在经历的问题。
作为绘图过程的一部分,我希望在x轴上始终显示5条网格线,这些网格线之间的间隔由当前可见的x轴限制决定。出于这个原因,我创建了一个"CustomFormatter"类,它继承了matplotlib.ticker的Formatter类,并重写了">调用"方法,试图获得所需的行为。我认为我需要这样做,以便跟踪图形的当前可见xlimits,从而使我能够计算所需的间隔。
当我运行代码时,会生成以下GUI,它似乎按预期工作:在此处输入图像描述
但是,一旦我单击"加载数据"按钮,我指定的数据就会加载到图形中,但图形的x轴标签和网格线会消失,如下图所示。在此处输入图像描述
直到我使用内置的平移工具平移图形,这些网格线和标签才重新出现,如下图所示。在此处输入图像描述
当我放大图形时也会发生同样的事情。缩放后,直到我再次平移,x轴标签和网格线才将自己重新校准到所需的位置。
任何人愿意提供任何帮助来帮助我解决这个问题,我们将不胜感激。希望这只是一些基本的或愚蠢的东西,我忽略了。
干杯。
我用来生成嵌入在tkinter框架中的matplotlib图的代码如下所示:
import tkinter as tk
import matplotlib
import matplotlib.dates as mdates
from matplotlib.ticker import Formatter
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class CustomFormatter(Formatter):
def __init__(self, ax, graph):
super().__init__()
self.set_axis(ax)
self.ax = ax
self.graph = graph
def __call__(self, x, pos=None):
xlims = self.ax.get_xlim()
ylims = self.ax.get_ylim()
xmin, xmax = xlims[0], xlims[1]
xmin = mdates.num2date(xmin)
xmax = mdates.num2date(xmax)
x = mdates.num2date(x)
interval = (xmax - xmin) / 6
self.ax.set_xticks([xmin, xmin + 1 * interval, xmin + 2 * interval, xmin + 3 * interval, xmin + 4 * interval, xmin + 5 * interval, xmax])
self.ax.grid(b=True, which='major', axis='both')
return f"{x.hour}:{x.minute}:{x.second}n{x.day}/{x.month}/{x.year}"
class App(object):
def __init__(self, master):
initialFrame = tk.Frame(master)
initialFrame.pack(fill=tk.BOTH, expand=1)
master.title('Tkinter Graph Example')
self.master = master
self._load_data_button = tk.Button(initialFrame, takefocus=0, text='Load data')
self._load_data_button.bind('<Button-1>', self.load_data)
self._load_data_button.pack(side=tk.TOP, fill=tk.BOTH, padx=2, pady=2, ipady=5)
self.graphFrame = tk.Frame(initialFrame, borderwidth=5, relief=tk.SUNKEN)
self.graphFrame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.f=Figure(figsize=(5,5), dpi=100)
self.a=self.f.add_subplot(111)
self.graph = FigureCanvasTkAgg(self.f, self.graphFrame)
self.graph.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
self.toolbar = NavigationToolbar2Tk(self.graph, self.graphFrame)
self.toolbar.update()
self.graph._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.a.set_xlabel('Time')
self.a.set_ylabel('Y-Axis')
self.a.set_title('Test')
self.a.grid(b=True)
self.a.xaxis.set_major_formatter(CustomFormatter(self.a, self.graph))#, self.df, self.toPlot, self.index_dict, self.name_dict))
self.graph.draw()
def load_data(self, event):
data = [1, 4, 2, 7, 4, 6]
time = [18820.410517624932, 18820.414807940015, 18820.41623804504, 18820.41766815007, 18820.41862155342, 18820.420528360122]
self.a.plot(time, data)
self.a.set_xlim(xmin=time[0], xmax=time[-1])
self.graph.draw()
def main():
root = tk.Tk()
app = App(root)
root.geometry('%dx%d+0+0'%(800,600))
root.mainloop()
if __name__ == '__main__':
main()
最后,我设法找到了解决问题的方法。我在应用程序的_init方法中添加了以下两行代码,以及两个新的函数定义。
self.a.callbacks.connect('xlim_changed', self.on_xlims_change)
self.a.callbacks.connect('ylim_changed', self.on_ylims_change)
def on_xlims_change(self, event_ax):
self.graph.draw()
def on_ylims_change(self, event_ax):
self.graph.draw()
这些事件处理程序使我能够在x或y限制更改时重新绘制图形(就像在缩放或平移操作中一样(。我现在正在观察我想要的行为。
完整代码如下所示。
import tkinter as tk
import matplotlib
import matplotlib.dates as mdates
from matplotlib.ticker import Formatter
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class CustomFormatter(Formatter):
def __init__(self, ax, graph):
super().__init__()
self.set_axis(ax)
self.ax = ax
self.graph = graph
def __call__(self, x, pos=None):
xlims = self.ax.get_xlim()
ylims = self.ax.get_ylim()
xmin, xmax = xlims[0], xlims[1]
xmin = mdates.num2date(xmin)
xmax = mdates.num2date(xmax)
x = mdates.num2date(x)
interval = (xmax - xmin) / 6
self.ax.set_xticks([xmin, xmin + 1 * interval, xmin + 2 * interval, xmin + 3 * interval, xmin + 4 * interval, xmin + 5 * interval, xmax])
self.ax.grid(b=True, which='major', axis='both')
return f"{x.hour}:{x.minute}:{x.second}n{x.day}/{x.month}/{x.year}"
class App(object):
def __init__(self, master):
initialFrame = tk.Frame(master)
initialFrame.pack(fill=tk.BOTH, expand=1)
master.title('Tkinter Graph Example')
self.master = master
self._load_data_button = tk.Button(initialFrame, takefocus=0, text='Load data')
self._load_data_button.bind('<Button-1>', self.load_data)
self._load_data_button.pack(side=tk.TOP, fill=tk.BOTH, padx=2, pady=2, ipady=5)
self.graphFrame = tk.Frame(initialFrame, borderwidth=5, relief=tk.SUNKEN)
self.graphFrame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.f=Figure(figsize=(5,5), dpi=100)
self.a=self.f.add_subplot(111)
self.graph = FigureCanvasTkAgg(self.f, self.graphFrame)
self.graph.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
self.toolbar = NavigationToolbar2Tk(self.graph, self.graphFrame)
self.toolbar.update()
self.graph._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.a.set_xlabel('Time')
self.a.set_ylabel('Y-Axis')
self.a.set_title('Test')
self.a.grid(b=True)
self.a.callbacks.connect('xlim_changed', self.on_xlims_change)
self.a.callbacks.connect('ylim_changed', self.on_ylims_change)
self.a.xaxis.set_major_formatter(CustomFormatter(self.a, self.graph))#, self.df, self.toPlot, self.index_dict, self.name_dict))
self.graph.draw()
def on_xlims_change(self, event_ax):
self.graph.draw()
def on_ylims_change(self, event_ax):
self.graph.draw()
def load_data(self, event):
data = [1, 4, 2, 7, 4, 6]
time = [18820.410517624932, 18820.414807940015, 18820.41623804504, 18820.41766815007, 18820.41862155342, 18820.420528360122]
self.a.plot(time, data)
self.a.set_xlim(xmin=time[0], xmax=time[-1])
self.graph.draw()
def main():
root = tk.Tk()
app = App(root)
root.geometry('%dx%d+0+0'%(800,600))
root.mainloop()
if __name__ == '__main__':
main()