将不同大小的matplotlib图形动态添加到tkinter窗口

时间:2018-08-16 10:25:53

标签: python matplotlib tkinter

我正在开发一个在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上尝试过代码。

0 个答案:

没有答案