包含可滚动 canvas 的框架未占用剩余的分配 Tk window
Frame containing a scrollable canvas is not taking up the rest of the allotted Tk window
我编写了一个应用程序,允许用户select 一个目录并加载目录中的信息。然后,用户可以 select 将文件的哪些方面显示在图中。该图放置在用户可以滚动的 canvas window 内的 Tkinter-matplotlib canvas 中。我遇到的问题是包含可滚动框架的框架(StartPage 中的 canvas_frame)没有采用 Tkinter window.
中分配的 space
下面的代码重现了这个问题,图像就是应用程序的样子。可滚动框架的大部分代码取自 ex. 1 and ex. 2。应用程序的图像在这里:
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', fill='both', expand=True)
# Instance variable for the scroll-bar
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(" ")
# Instance variable for the matplotlib canvas
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("<Configure>", 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
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)
# Instance variables with page/window info of current frame
self.window = parent
# Instance variable for third row of widgets
self.canvas_frame = Frame(self.window, relief="sunken")
self.canvas_frame.grid(row=0, column=0, pady=5, sticky="news")
# 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
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):
""" 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.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()
问题的主要原因是使用了 MainContainer class 中的初始 Frame 对象来实例化包含 Scrollable class 的 Frame。 MainContainer的对象应该是传过来的,这是因为它继承自Tk class.
其次,window 经理的混合导致了问题。因此,我转而专门打包。解决方案如下。
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("<Configure>", 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()
我编写了一个应用程序,允许用户select 一个目录并加载目录中的信息。然后,用户可以 select 将文件的哪些方面显示在图中。该图放置在用户可以滚动的 canvas window 内的 Tkinter-matplotlib canvas 中。我遇到的问题是包含可滚动框架的框架(StartPage 中的 canvas_frame)没有采用 Tkinter window.
中分配的 space下面的代码重现了这个问题,图像就是应用程序的样子。可滚动框架的大部分代码取自 ex. 1 and ex. 2。应用程序的图像在这里:
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', fill='both', expand=True)
# Instance variable for the scroll-bar
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(" ")
# Instance variable for the matplotlib canvas
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("<Configure>", 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
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)
# Instance variables with page/window info of current frame
self.window = parent
# Instance variable for third row of widgets
self.canvas_frame = Frame(self.window, relief="sunken")
self.canvas_frame.grid(row=0, column=0, pady=5, sticky="news")
# 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
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):
""" 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.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()
问题的主要原因是使用了 MainContainer class 中的初始 Frame 对象来实例化包含 Scrollable class 的 Frame。 MainContainer的对象应该是传过来的,这是因为它继承自Tk class.
其次,window 经理的混合导致了问题。因此,我转而专门打包。解决方案如下。
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("<Configure>", 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()