包含可滚动画布的帧没有占据分配给Tk窗口的剩余部分。

8 浏览
0 Comments

包含可滚动画布的帧没有占据分配给Tk窗口的剩余部分。

我编写了一个应用程序,允许用户选择一个目录并加载目录中的信息。然后用户可以选择在图表中显示文件的哪些方面。图表放置在一个Tkinter-matplotlib画布中,该画布位于用户可以滚动的画布窗口中。我遇到的问题是包含可滚动框架的帧(在StartPage中的canvas_frame)不会占用Tkinter窗口中的分配空间。

以下代码复制了问题,并且图片显示了应用程序的样子。可滚动框架的大部分代码来自ex. 1ex. 2。应用程序的图片在这里:

https://i.stack.imgur.com/jle7H.jpg

from tkinter import Tk, Frame, Canvas
from tkinter.ttk import Scrollbar
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigCanvas
from matplotlib.figure import Figure
class Scrollable(Frame):    
    def __init__(self, frame, fig, width=16):
        Frame.__init__(self, frame)
        self.tk_cnv = Canvas(frame, highlightthickness=0)
        self.tk_cnv.pack(side='left', fill='both', expand=True)
        
        v_scroll = Scrollbar(frame, width=width)
        v_scroll.pack(side="right", fill="y", expand=False)
        v_scroll.config(command=self.tk_cnv.yview)
        v_scroll.activate(" ")
        
        self.mpl_cnv = FigCanvas(fig, frame)
        self.cnv_widget = self.mpl_cnv.get_tk_widget()
        self.cnv_widget.config(yscrollcommand=v_scroll.set)
        self.cnv_widget.bind("", self.__fill_canvas)
        
        self.windows_item = \
            self.tk_cnv.create_window((0, 900), window=self.cnv_widget, anchor='e',
                                      tag='self.canvas')
        self.tk_cnv.config(scrollregion=self.tk_cnv.bbox("all"))
        
    def __fill_canvas(self, event):
        canvas_width = event.width
        canvas_height = event.height
        self.tk_cnv.itemconfig(self.windows_item, width=canvas_width,
                               height=canvas_height)
class StartPage(Frame):
    LARGE_FONT = ("Veranda", 12)
    
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.window = parent
        self.canvas_frame = Frame(self.window, relief="sunken")
        self.canvas_frame.grid(row=0, column=0, pady=5, sticky="news")
        
        self.plot_fig = Figure(figsize=[14.0, 18.0])
        
        self.canvas_body = Scrollable(self.canvas_frame, self.plot_fig)
        self.canvas = self.canvas_body.mpl_cnv
        self.canvas_setup()
        
    def canvas_setup(self):
        self.canvas_frame.grid_rowconfigure(2, weight=1)
        self.canvas_frame.grid_columnconfigure(0, weight=1)
class MainContainer(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)
        Tk.wm_title(self, "Sequence Viewer")
        Tk.wm_resizable(self, width=True, height=True)
        
        container = Frame(self)
        container.grid_configure(row=0, column=0, sticky="nsew")
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        
        self.frames = {}
        frame = StartPage(container, self)
        self.frames[StartPage] = frame
        self.show_frame(StartPage)
        self.center_window()
        
    def show_frame(self, frame_to_add):
        frame = self.frames[frame_to_add]
        frame.tkraise()
        
    def center_window(self):
        w = 1100
        h = 900
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        x = (sw - w) / 2
        y = (sh - h) / 2
        self.geometry('%dx%d+%d+%d' % (w, h, x, y))
if __name__ == "__main__":
    app = MainContainer()
    app.mainloop()

0
0 Comments

问题的原因是在实例化包含Scrollable类的Frame时,使用了MainContainer类的初始Frame对象。应该传递MainContainer对象,因为它继承自Tk类。

解决方法是改用pack作为唯一的窗口管理器。以下是解决方案的代码示例:

from tkinter import Tk, Frame, Canvas
from tkinter.ttk import Scrollbar
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigCanvas
from matplotlib.figure import Figure
class Scrollable(Frame):
    def __init__(self, frame, fig, width=16):
        # Base class initialization
        Frame.__init__(self, frame)
        # Instance variable for tkinter canvas
        self.tk_cnv = Canvas(frame, highlightthickness=0)
        self.tk_cnv.pack(side="left", anchor="nw", fill="both", expand=True)
        # Instance variable for the scroll-bar
        v_scroll = Scrollbar(frame)
        v_scroll.pack(side="right", fill="y", expand=False)
        v_scroll.config(command=self.tk_cnv.yview, width=width)
        v_scroll.activate("slider")
        # Instance variable for the matplotlib canvas
        self.mpl_cnv = FigCanvas(fig, frame)
        self.cnv_widget = self.mpl_cnv.get_tk_widget()
        self.tk_cnv.config(yscrollcommand=v_scroll.set)
        self.tk_cnv.bind("", self.__fill_canvas)
        # Assign frame generated by the class to the canvas
        # and create a scrollable window for it.
        self.windows_item = self.tk_cnv.create_window((0, 900), window=self.cnv_widget, anchor='e', tag='self.canvas')
        self.tk_cnv.config(scrollregion=self.tk_cnv.bbox("all"))
    def __fill_canvas(self, event):
        # Enlarge the windows item to the canvas width
        canvas_width = event.width
        canvas_height = event.height * 2.825
        self.tk_cnv.itemconfig(self.windows_item, width=canvas_width, height=canvas_height)
class StartPage(Frame):
    """ Tkinter based class for single frame upon which widgets
    such as buttons, check-buttons, and entry are used as a
    simple graphical user interface.
    """
    LARGE_FONT = ("Veranda", 12)
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller
        # Instance variable for third row of widgets
        self.canvas_frame = Frame(self.controller, relief="sunken")
        self.canvas_frame.pack(side="top", anchor="nw", fill="both", expand=True)
        # Instance variables for the figure
        self.plot_fig = Figure(figsize=[14.0, 18.0])
        # Instance variable for the frame with scrolling functionality
        self.canvas_body = Scrollable(self.canvas_frame, self.plot_fig)
        self.canvas = self.canvas_body.mpl_cnv
        # Instance variable for third row of widgets
        self.control_frame = Frame(self.controller, relief="sunken")
        self.control_frame.pack(side="right", anchor="ne", fill="y", expand=True)
class MainContainer(Tk):
    """ Tkinter based class used to generate a single window
    and contain a single frame. The frame contains multiple
    widgets for user choice and visualization.
    """
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)
        Tk.wm_title(self, "Sequence Viewer")
        Tk.wm_resizable(self, width=True, height=True)
        container = Frame(self)
        container.pack_configure(side="top", anchor="nw", fill="both")
        self.frames = {}
        frame = StartPage(container, self)
        self.frames[StartPage] = frame
        self.show_frame(StartPage)
        self.center_window()
    def show_frame(self, frame_to_add):
        frame = self.frames[frame_to_add]
        frame.tkraise()
    def center_window(self):
        w = 1100
        h = 900
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        x = (sw - w) / 2
        y = (sh - h) / 2
        self.geometry('%dx%d+%d+%d' % (w, h, x, y))
if __name__ == "__main__":
    app = MainContainer()
    app.mainloop()

0