我正在开发一个在matplotlib
窗口中动态组织多个tkinter
图形的应用程序。我希望这些图可以具有不同的大小,但始终“抢占”可用空间。基于this answer,我想出了一个适合我需要的解决方案,但是我遇到的问题是,当我向窗口添加新图形时,图形可能无法正确显示。调整窗口大小后,所有图形将再次正确显示。
在我的应用程序中,我使用pack
命令组织图形,限制图形在一帧内垂直或水平排列。然后,如果我想在另一个方向上添加更多图形,则将一个图形替换为一个空框架,将该图形移至新框架,然后添加以新方向打包的新图形。
我一直在努力想出一个好的MCVE,但是示例代码反而变得很长-对此感到抱歉:
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
try:
import ttk
except ImportError:
import tkinter.ttk as ttk
class FigFrame(FigureCanvasTkAgg):
def __init__(self, parent):
self.fig,self.ax = plt.subplots(figsize=(2,2))
FigureCanvasTkAgg.__init__(self, self.fig, master=parent)
self.cid = self.fig.canvas.mpl_connect('resize_event', self.on_resize)
def on_resize(self, event):
self.fig.tight_layout()
self.fig.canvas.draw()
def pack(self, *args, **kwargs):
self.get_tk_widget().pack(*args, **kwargs)
def pack_forget(self, *args, **kwargs):
self.get_tk_widget().pack_forget(*args, **kwargs)
def add_figure():
"""
hard-coding the interactive addition of another figure:
replacing figure2 with a tk.Frame, placing figure2 inside that
frame, and adding figure 5 right of figure 2.
"""
global figure5
global frame3
global frame3_widgets
##creating frame
frame3 = tk.Frame(frame2)
frame3_widgets = []
##replacing figure2 with frame3:
idx = frame2_widgets.index(figure2)
frame2_widgets[idx]=frame3
##placing figure2 in frame3
frame3_widgets.append(figure2)
##creating figure5:
figure5 = FigFrame(root)
figure5.ax.set_title('figure 5')
figure5.pack(side='left', expand=True, fill='both', in_=frame3)
frame3_widgets.append(figure5)
##repacking everything:
for widget in frame1_widgets:
widget.pack_forget()
for widget in frame1_widgets:
widget.pack(side='left', expand=True, fill='both', in_=frame1)
for widget in frame2_widgets:
widget.pack_forget()
for widget in frame2_widgets:
widget.pack(side='top', expand=True, fill='both', in_=frame2)
for widget in frame3_widgets:
widget.pack_forget()
for widget in frame3_widgets:
widget.pack(side='left', expand=True, fill='both', in_=frame3)
if __name__ == '__main__':
root=tk.Tk()
root.protocol("WM_DELETE_WINDOW", exit)
frame1 = tk.Frame(root)
frame1.pack(side='left', expand=True, fill='both')
frame1_widgets=[]
figure1 = FigFrame(root)
figure1.ax.set_title('figure 1')
figure1.pack(side='left', expand=True, fill='both', in_=frame1)
frame1_widgets.append(figure1)
frame2 = tk.Frame(frame1)
frame2.pack(side='left', expand=True, fill='both')
frame2_widgets=[]
frame1_widgets.append(frame2)
figure2 = FigFrame(root)
figure2.ax.set_title('figure 2')
figure2.pack(side='top', expand=True, fill='both', in_=frame2)
frame2_widgets.append(figure2)
figure3 = FigFrame(root)
figure3.ax.set_title('figure 3')
figure3.pack(side='top', expand=True, fill='both', in_=frame2)
frame2_widgets.append(figure3)
figure4 = FigFrame(root)
figure4.ax.set_title('figure 4')
figure4.pack(side='left', expand=True, fill='both', in_=frame1)
frame1_widgets.append(figure4)
##here I simulate the interactive part that adds a new figure:
root.after(1000,add_figure)
##add_figure() ##<-- this line instead of the above displays the figures correctly
root.mainloop()
如您所见,我首先创建四个图形,这些图形在调用root.mainloop()
时显示。为了模拟另一个图形的动态添加,我向函数add_figure()
添加了一个延迟调用,然后仅在显示原始四个图形后才对其进行处理。
一旦调用add_figure()
,figure 2
的大小将被调整为原始宽度的一半,而figure 5
将被放置在剩余的空间中。但是,在过程figure 2
中“消失”并且仅在调整窗口大小(使用鼠标)后才重新出现。我还会收到一个matplotlib
错误:
Traceback (most recent call last):
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 388, in process
proxy(*args, **kwargs)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 228, in __call__
return mtd(*args, **kwargs)
File "subframe_problem.py", line 30, in on_resize
self.fig.tight_layout()
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/figure.py", line 2276, in tight_layout
self.subplots_adjust(**kwargs)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/figure.py", line 2088, in subplots_adjust
self.subplotpars.update(*args, **kwargs)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/figure.py", line 241, in update
raise ValueError('left cannot be >= right')
ValueError: left cannot be >= right
错误是由于我在调整图形大小时使用tight_layout()
,使我认为重新分配空间时发生的太早或太晚。
如果我不延迟向add_figure
的呼叫,即如果我将root.after(1000,add_figure)
替换为add_figure
(这不是我想要的,因为用户应该可以添加数字动态地),所有数字都可以正确显示。
Python 3.6和2.7都会出现此问题。我只在macOS上尝试过代码。