Кадр, содержащий прокручиваемый холст, не занимает оставшуюся часть выделенного окна Tk. - PullRequest
0 голосов
/ 31 марта 2020

Я написал приложение, которое позволяет пользователям выбирать каталог и загружать информацию в каталог. Затем пользователь может выбрать, какие аспекты файлов отображать на рисунке. Рисунок помещается в холст Tkinter-matplotlib, который находится внутри окна холста, которое пользователь может прокручивать. У меня проблема в том, что фрейм (canvas_frame в StartPage), содержащий прокручиваемый фрейм, не занимает выделенное пространство в окне Tkinter.

Приведенный ниже код повторяет проблему, а изображение - это то, что приложение похоже. Большая часть кода для прокручиваемого фрейма была взята из ex. 1 и отл. 2 . Изображение заявки здесь:

https://imgur.com/ELXmehG

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()

1 Ответ

0 голосов
/ 02 мая 2020

Основной причиной проблемы было использование исходного объекта Frame из класса MainContainer для создания экземпляра Frame, в котором содержится класс Scrollable. Объект MainContainer - это то, что должно быть передано, потому что он наследуется от класса Tk.

Во-вторых, смешение оконных менеджеров вызывало проблемы. Из-за этого я перешел на упаковку исключительно. Решение ниже.

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
        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()
...