Python Forum
[Tkinter] How to create scrollable frame
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] How to create scrollable frame
#1
I've been using this framework Multipage Framework for developing an app... I'm kinda new to tkinter and I just finished developing a beta version and I just noticed that frames are not native scrollable. I checked some tutorials online on how to make them scrollable creating canvas inside or using ScrolledFrame package...). So, I tried with the scrolled frame package but it doesn't work right (like a normal app should): basically the content doesn't resize with the window. So is here anyone kind to help me and improve the basic structure of the framework in the link to make frames scrollables? Here's how I managed to create it scrollable but loosing "content-auto-fitting" capabilities:

import tkinter as tk                # python 3
from tkinter import font as tkfont  # python 3

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
        
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        scrollable_frame = ScrollableFrame(container, bg="red")
        scrollable_frame.pack(fill="both", expand=True)
  
        scrollable_frame.grid_rowconfigure(0, weight=1)
        scrollable_frame.grid_columnconfigure(0, weight=1)
        

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):
            page_name = F.__name__
            frame = F(parent=scrollable_frame, controller=self)
            self.frames[page_name] = frame

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")
            frame.grid_rowconfigure(0, weight=1)
            frame.grid_columnconfigure(0, weight=1)
            frame.config(highlightbackground="green", highlightcolor="green", highlightthickness=1)

      
        
        
        self.show_frame("StartPage")

    def show_frame(self, page_name):
        #Show a frame for the given page name
        frame = self.frames[page_name]
        frame.tkraise()


    FIT_WIDTH = "fit_width"
    FIT_HEIGHT = "fit_height"


class ScrollableFrame(tk.Frame):
    """
    There is no way to scroll <tkinter.Frame> so we are
    going to create a canvas and place the frame there.
    Scrolling the canvas will give the illution of scrolling
    the frame
    Partly taken from:
        https://blog.tecladocode.com/tkinter-scrollable-frames/
        https://stackoverflow.com/a/17457843/11106801

    master_frame---------------------------------------------------------
    | dummy_canvas-----------------------------------------  y_scroll--  |
    | | self---------------------------------------------  | |         | |
    | | |                                                | | |         | |
    | | |                                                | | |         | |
    | | |                                                | | |         | |
    | |  ------------------------------------------------  | |         | |
    |  ----------------------------------------------------  |         | |
    |                                                        |         | |
    | x_scroll---------------------------------------------  |         | |
    | |                                                    | |         | |
    |  ----------------------------------------------------   ---------  |
     --------------------------------------------------------------------
    """
    def __init__(self, master=None, scroll_speed=2,
                 hscroll=False, vscroll=True, **kwargs):
        assert isinstance(scroll_speed, int), "`scroll_speed` must be an int"
        self.scroll_speed = scroll_speed

        self.master_frame = tk.Frame(master)
        self.dummy_canvas = tk.Canvas(self.master_frame, **kwargs)
        super().__init__(self.dummy_canvas)

        # Create the 2 scrollbars
        if vscroll:
            self.v_scrollbar = tk.Scrollbar(self.master_frame,
                                            orient="vertical",
                                            command=self.dummy_canvas.yview)
            self.v_scrollbar.pack(side="right", fill="y")
            self.dummy_canvas.configure(yscrollcommand=self.v_scrollbar.set)
        if hscroll:
            self.h_scrollbar = tk.Scrollbar(self.master_frame,
                                            orient="horizontal",
                                            command=self.dummy_canvas.xview)
            self.h_scrollbar.pack(side="bottom", fill="x")
            self.dummy_canvas.configure(xscrollcommand=self.h_scrollbar.set)

        # Bind to the mousewheel scrolling
        self.dummy_canvas.bind_all("<MouseWheel>", self.scrolling_windows,
                                   add=True)
        self.dummy_canvas.bind_all("<Button-4>", self.scrolling_linux, add=True)
        self.dummy_canvas.bind_all("<Button-5>", self.scrolling_linux, add=True)
        self.bind("<Configure>", self.scrollbar_scrolling, add=True)

        # Place `self` inside `dummy_canvas`
        self.dummy_canvas.create_window((0, 0), window=self, anchor="nw")
        # Place `dummy_canvas` inside `master_frame`
        self.dummy_canvas.pack(side="top", expand=True, fill="both")

        self.pack = self.master_frame.pack
        self.grid = self.master_frame.grid
        self.place = self.master_frame.place
        self.pack_forget = self.master_frame.pack_forget
        self.grid_forget = self.master_frame.grid_forget
        self.place_forget = self.master_frame.place_forget

    def scrolling_windows(self, event):
        assert event.delta != 0, "On Windows, `event.delta` should never be 0"
        y_steps = int(-event.delta/abs(event.delta)*self.scroll_speed)
        self.dummy_canvas.yview_scroll(y_steps, "units")

    def scrolling_linux(self, event):
        y_steps = self.scroll_speed
        if event.num == 4:
            y_steps *= -1
        self.dummy_canvas.yview_scroll(y_steps, "units")

    def scrollbar_scrolling(self, event):
        region = list(self.dummy_canvas.bbox("all"))
        region[2] = max(self.dummy_canvas.winfo_width(), region[2])
        region[3] = max(self.dummy_canvas.winfo_height(), region[3])
        self.dummy_canvas.configure(scrollregion=region)

    def resize(self, fit=None, height=None, width=None):
        if fit == FIT_WIDTH:
            super().update()
            self.dummy_canvas.config(width=super().winfo_width())
        if fit == FIT_HEIGHT:
            super().update()
            self.dummy_canvas.config(height=super().winfo_height())
        if height is not None:
            self.dummy_canvas.config(height=height)
        if width is not None:
            self.dummy_canvas.config(width=width)
    fit = resize

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is the start page", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One",
                            command=lambda: controller.show_frame("PageOne"))
        button2 = tk.Button(self, text="Go to Page Two",
                            command=lambda: controller.show_frame("PageTwo"))
        button1.pack()
        button2.pack()
       

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 1", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        for i in range(0,99):
            button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
            button.pack()

class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()

if __name__ == "__main__":
    
    app = SampleApp()
    app.mainloop()
Reply


Messages In This Thread
How to create scrollable frame - by mandiatutti - Aug-06-2021, 04:09 PM
RE: How to create scrollable frame - by deanhystad - Aug-06-2021, 06:38 PM
RE: How to create scrollable frame - by mandiatutti - Aug-06-2021, 07:15 PM
RE: How to create scrollable frame - by deanhystad - Aug-06-2021, 07:28 PM
RE: How to create scrollable frame - by mandiatutti - Aug-06-2021, 07:48 PM
RE: How to create scrollable frame - by deanhystad - Aug-07-2021, 05:39 AM
RE: How to create scrollable frame - by mandiatutti - Aug-07-2021, 06:58 AM
RE: How to create scrollable frame - by deanhystad - Aug-07-2021, 03:34 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] PyQt5 drawing on scrollable area HeinKurz 3 1,385 Mar-28-2023, 12:58 PM
Last Post: Axel_Erfurt
  [Tkinter] Scrollable buttons with an add/delete button Clich3 5 3,543 Jun-16-2022, 07:19 PM
Last Post: rob101
Question [Tkinter] Scrollable Treeview: change behavior of Prior/Next keys? RockDoctor 2 3,264 Apr-10-2021, 05:40 PM
Last Post: RockDoctor
  Scrollable big image in a window (TKinter) Prospekteur 3 4,554 Sep-14-2020, 03:06 AM
Last Post: Larz60+
  [Tkinter] Scrollbar, Frame and size of Frame Maksim 2 9,132 Sep-30-2019, 07:30 AM
Last Post: Maksim
  [Tkinter] create and insert a new frame on top of another frame atlass218 4 11,287 Apr-18-2019, 05:36 PM
Last Post: atlass218
  [Tkinter] Fixate graphs on scrollable canvas janema 6 5,474 Apr-12-2019, 03:57 PM
Last Post: Larz60+
  [Tkinter] ListBox not contained or scrollable kellykimble 6 5,244 Apr-05-2018, 11:59 AM
Last Post: Larz60+
  [Tkinter] Frame size only works if frame is empty(Solved) Tuck12173 7 6,553 Jan-29-2018, 10:44 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020