Seite 1 von 1

Scrollbar in ttk Notebook einfügen

Verfasst: Sonntag 6. August 2023, 17:25
von NinoBaumann
Hallo,

ich baue mir gerade eine Anwendung, mit der ich bei uns den Essens- und Einkaufsalltag vereinfachen möchte. Mit einem ttk.Notebook habe ich mir verschiedene Tabs erstellt, in denen dann in Frames Gerichte und ihre Basisdaten stehen. Mittlerweile sind so viele Frames im Schachbrettmuster vorhanden, dass diese am unteren Rand verschwinden. Ich brauche also eine Scrollbar. Ich habe mich schon durch das Internet gewühlt. Aber ich habe nicht richtig verstanden, wie ich bei mir eine Scrollbar intergrieren kann. Kann mir jemand mit einer konkreten Lösung helfen?
Anbei der zum Verständnis wichtige Codeabschnitt.

Code: Alles auswählen

class MealFrame(tk.Frame):
    def __init__(self, master, name, image_path, nutritional_values):
        tk.Frame.__init__(self, master, highlightbackground="grey", highlightthickness=2)
        self.name = name
        self._food_image = ImageTk.PhotoImage(
            Image.open(image_path).resize((300, 200), Image.Resampling.LANCZOS)
        )
        tk.Label(self, image=self._food_image).pack()
        
        self.label_frame_text = tk.LabelFrame(self)
        self.label_frame_text.pack()
        
        self._selected_var = tk.BooleanVar()
        tk.Checkbutton(self.label_frame_text, text=name, variable=self._selected_var).grid(row = 1, column = 0)
        
        self.label_frame_servings = tk.LabelFrame(self.label_frame_text, borderwidth = 0)
        self.label_frame_servings.grid(row = 2, column = 0, padx = 10)
        tk.Label(self.label_frame_servings, text='Portionen: ', bg="light grey").pack(side = tk.LEFT)
        self._number_servings = tk.Entry(self.label_frame_servings, justify='center', width=10)
        self._number_servings.insert(0, 2)
        self._number_servings.pack(side = tk.RIGHT)
        
        self.label_frame_nutritional_values = tk.LabelFrame(self.label_frame_text)
        self.label_frame_nutritional_values.grid(row = 1, column = 1, rowspan = 2)
        tk.Label(self.label_frame_nutritional_values, text = 'Nährwerte:').grid(row = 0, column = 0, padx = 5, columnspan = 2)
        j = 0
        for i in nutritional_values:
            j = j + 1
            tk.Label(self.label_frame_nutritional_values, text = i).grid(row = j, column = 0, padx = 5)
            tk.Label(self.label_frame_nutritional_values, text = nutritional_values.get(i)).grid(row = j, column = 1, padx = 5)        
        
    @property
    def is_selected(self):
        return self._selected_var.get()
    @property
    def is_quantified(self):
        return self._number_servings.get()

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        
        style = ttk.Style()

        style.theme_create( "yummy", parent="alt", settings={
                "TNotebook": {"configure": {"tabmargins": [2, 5, 2, 0] } },
                "TNotebook.Tab": {
                    "configure": {"padding": [62, 5], "background": 'grey',
                                  "font" : ('URW Gothic L', '10', 'bold')},
                    "map":       {"background": [("selected", 'red3')],
                                  "expand": [("selected", [1, 1, 1, 0])] } } } )
        
        style.theme_use("yummy")

        self.notebook = ttk.Notebook(self.master)
        
        frame_chicken = ttk.Frame(self.notebook)
        frame_pig = ttk.Frame(self.notebook)
        frame_beef = ttk.Frame(self.notebook)
        frame_vegetarian = ttk.Frame(self.notebook)
        frame_fast_food = ttk.Frame(self.notebook)
        frame_chicken.grid(row=0, column=0, padx = 5, pady = 5)
        frame_pig.grid(row=0, column=1, padx = 5, pady = 5)
        frame_beef.grid(row=0, column=2, padx = 5, pady = 5)
        frame_vegetarian.grid(row=0, column=3, padx = 5, pady = 5)
        frame_fast_food.grid(row=0, column=4, padx = 5, pady = 5)
        cuisine_frames = [frame_chicken, frame_pig, frame_beef, frame_vegetarian, frame_fast_food]
        
        self.notebook.add(frame_chicken, text='Hähnchen')
        self.notebook.add(frame_pig, text='Schwein')
        self.notebook.add(frame_beef, text='Rind')
        self.notebook.add(frame_vegetarian, text='Vegetarisch')
        self.notebook.add(frame_fast_food, text='Fast-Food')
        .
        .
        .
        .
        def main():
  	root = tk.Tk()
   	root.title('Lisa & Ninos virtuelle Einkaufsliste')
    	window = App(root)
   	window.mainloop()


	if __name__ == "__main__":
    	main()

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Sonntag 6. August 2023, 19:51
von __blackjack__
@NinoBaumann: Ein Notebook sollte einfach nicht so viele Reiter haben, falls ich das Problem richtig verstanden habe.

Ansonsten sind Scrollbalken in Tk nur bei bestimmten Widgets möglich, die spezielle Unterstützung haben. Wenn man also für ein beliebiges Widget einen Scrollbalken braucht, dann muss man das in ein `Canvas` stecken, denn *das* kann man mit Scrollbalken verbinden und ein beliebiges Widget da rein stecken.

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Montag 7. August 2023, 09:33
von NinoBaumann
Hallo blackjack,

nein, Du hast es etwas falsch verstanden. Aber liegt wohl eher an meiner Beschreibung. Deshalb hier ein Link zu einem Bild, was meine Vorstellung verbildlicht.
https://ibb.co/kqMyzSG
Ich habe lediglich eine Hand voll Reiter. Aber innerhalb eines Reiters sind sehr viele kleinere Frames angeordnet. Und das soll dann praktisch über einen Scrollbar durchgescrollt werden können.

VG
Nino

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Montag 7. August 2023, 10:25
von __blackjack__
Ah, okay. Die Antwort bleibt die gleiche: Sowas muss man sich in Tk mit einem `Canvas` selbst basteln. Die meisten anderen/modernen Rahmenwerke haben da bereits etwas fertiges.

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Montag 7. August 2023, 10:43
von Fire Spike
Canvas sollte gut funktionieren. Habe ich selbst so gemacht. Wenn du willst kann ich dir ein Beispiel geben.

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Montag 7. August 2023, 12:56
von NinoBaumann
Hallo blackjack,

danke für die Info.

Hallo Fire Spike,

es würde ja dann im Endeffekt so funktionieren, dass ich meine Frames in das Canvas packe, dieses dann mit eine Scrollbar versehe und das ganze dann in meinen Reiter packe oder?
Gerne kannst Du mir als Hilfe ein Beispiel geben. Meine Frames habe ich halt mit der grid Methode angeordnet.

VG
Nino

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Montag 7. August 2023, 13:06
von __blackjack__
@NinoBaumann: Fast. Man würde eher nicht die Frames in ein `Canvas` packen, sondern in einen Frame und *den* dann in das `Canvas`-Widget.

Re: Scrollbar in ttk Notebook einfügen

Verfasst: Donnerstag 10. August 2023, 16:16
von Fire Spike

Code: Alles auswählen

class ScrollableFrame(ttk.Frame):
    def __init__(self, master, **kwargs) -> None:
        super().__init__(master, **kwargs)
        self.canvas = tk.Canvas(self, borderwidth=0, background="#fafafa", **kwargs)
        self.canvas.grid(row=0, column=0, sticky=NSEW)

        self.scrollbar_horizontal = ttk.Scrollbar(self, orient=HORIZONTAL, command=self.canvas.xview)
        self.scrollbar_horizontal.grid(row=1, column=0, columnspan=1, sticky=EW)

        self.scrollbar_vertical = ttk.Scrollbar(self, orient=VERTICAL, command=self.canvas.yview)
        self.scrollbar_vertical.grid(row=0, column=1, sticky=NS)

        self.canvas.configure(xscrollcommand=self.scrollbar_horizontal.set, yscrollcommand=self.scrollbar_vertical.set)

        self.scrolled_frame = ttk.Frame(self.canvas)

        self.canvas.create_window((0, 0), window=self.scrolled_frame, anchor=NW)
        self.scrolled_frame.bind("<Configure>", self.on_configure)

    def on_configure(self, event) -> None:
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

class ScrollableFrameHorizontal(ttk.Frame):
    def __init__(self, master, **kwargs) -> None:
        super().__init__(master, **kwargs)
        self.canvas = tk.Canvas(self, borderwidth=0, background="#fafafa", **kwargs)
        self.canvas.grid(row=0, column=0, sticky=NSEW)

        self.scrollbar_horizontal = ttk.Scrollbar(self, orient=HORIZONTAL, command=self.canvas.xview)
        self.scrollbar_horizontal.grid(row=1, column=0, columnspan=1, sticky=EW)

        self.canvas.configure(xscrollcommand=self.scrollbar_horizontal.set)

        self.scrolled_frame = ttk.Frame(self.canvas)

        self.canvas.create_window((0, 0), window=self.scrolled_frame, anchor=NW)
        self.scrolled_frame.bind("<Configure>", self.on_configure)

    def on_configure(self, event) -> None:
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))
Zwei Klassen. Die eine scrollt horizontal die andere vertikal. Scrollbars werden direkt miterstellt.

Verwenden kannst du sie so:

Code: Alles auswählen

scrollable_frame = ScrollableFrame(self, width=900, height=750)
scrollable_frame = scrollable_frame.scrolled_frame