1

I'm trying to insert a scrollbar into a canvas filled with buttons (where, buttons can be switched to clickable text as well).

With the code below, the window seems OK. screencapture1

However, when I uncomment the lines with the scrollbar, the window geometry scrambles. screencapture2

Can anybody give a hand to figure out what's wrong with the code?

System: Windows 10, python 3.9.6.

import tkinter as tk

class tWindow(tk.Tk):
    frame = None
    canvas = None
    scrollbar = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.geometry("640x480+50+50")
        self.setup_frame()
        self.setup_canvas()

    def setup_frame(self):
        self.frame = tk.Frame(master=self, width=640, height=480)
        self.frame.configure(bg="#003163")
        self.frame.place(x=0, y=0)

    def setup_canvas(self):
        self.update()
        self.canvas = tk.Canvas(master=self.frame, bg="#006331",
            width=int(self.frame.winfo_width() / 4), height=self.frame.winfo_height())
        self.canvas.place(x=0, y=0)
        self.update()

        # self.scrollbar = tk.Scrollbar(master=self.canvas, orient=tk.VERTICAL)
        # self.scrollbar.pack(side=tk.RIGHT, fill=tk.BOTH, expand=tk.TRUE)
        # self.canvas.configure(yscrollcommand=self.scrollbar.set)
        # self.scrollbar.configure(command=self.canvas.yview)
        # self.update()

        tmp_pos = 0

        for i in range(20):
            btn_tmp = tk.Button(master=self.canvas, text=f"testing testing testing testing testing {i:02} ...",
                justify=tk.LEFT, wraplength=self.canvas.winfo_width(), bg="#00c0c0", fg="#000000")
            btn_tmp.place(x=0, y=tmp_pos)
            self.update()
            tmp_pos = btn_tmp.winfo_y() + btn_tmp.winfo_height()

def main():
    app = tWindow()
    app.mainloop()

if __name__ == "__main__":
    main()
ssd
  • 2,340
  • 5
  • 19
  • 37
  • @Thingamabobs: It didn't help or I failed to figure it. My intend is to insert scrollbar for the "canvas" only (the **cyan** region). Using the answer in your link, I was able to insert scrollbar into the whole "window" (the **navy** region). – ssd Nov 28 '21 at 10:07
  • Why would you intend to have the scrollbar in the canvas? Thats a problem, not unsolvable but your scrollbar would be scrolled away if you dont do anything agains it. – Thingamabobs Nov 28 '21 at 10:10
  • @Thingamabobs: That **navy** region is to print some text (via SQL) depending on the button pressed (which is placed in the **cyan** region). – ssd Nov 28 '21 at 10:15

1 Answers1

1

See comments in code as additional to the link I have provided. Also see why update is considered harmfull and how do I organize my tkinter gui for more details.

import tkinter as tk

class tWindow(tk.Tk):
    frame = None
    canvas = None
    scrollbar = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.geometry("640x480+50+50")
        self.setup_frame()
        self.setup_canvas()

    def setup_frame(self):
        self.frame = tk.Frame(master=self, width=640, height=480,bg="#003163")
        self.frame.pack(side='left',fill='both',expand=True)##pack left
##        self.frame.place(x=0, y=0) ##place requiers too think to much

    def setup_canvas(self):
##        self.update() ##update is harmfull
        self.canvas = tk.Canvas(master=self.frame, bg='orange',#"#006331",
            width=int(self.frame.winfo_reqwidth() / 4), height=self.frame.winfo_reqheight())
        ##use reqwidth/hight to avoid the use of update. 
        ##It takes the requested size instead the actual
        self.canvas.pack(side='left')

        self.canvas_frame = tk.Frame(self.canvas,bg="#006331")
        ##frame to pack buttons in canvas
        self.canvas.create_window((0,0), window=self.canvas_frame, anchor="nw")
        ##show frame in canvas

        self.scrollbar = tk.Scrollbar(master=self.frame, orient=tk.VERTICAL)
        self.scrollbar.pack(side='left',fill='y')
        
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.scrollbar.configure(command=self.canvas.yview)
        self.canvas_frame.bind('<Configure>',self.oncanvasconfigure)
        ##bind event configure to update the scrollregion

        for i in range(20):
            btn_tmp = tk.Button(master=self.canvas_frame, text=f"testing testing testing testing testing {i:02} ...",
                justify=tk.LEFT, wraplength=self.canvas.winfo_reqwidth(), bg="#00c0c0", fg="#000000")
            btn_tmp.pack()
    def oncanvasconfigure(self,event):
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

def main():
    app = tWindow()
    app.mainloop()

if __name__ == "__main__":
    main()
Thingamabobs
  • 7,274
  • 5
  • 21
  • 54
  • First of all, thanks a lot! I think my problem was using `place` (for the frame & canvas) and `pack` (for the scrollbar) methods at the same time, right? – ssd Nov 28 '21 at 11:42
  • 1
    @ssd You had a few issues. You have placed the buttons on a canvas, the proper way of adding widgets to canvas is to us `create_window`, therefore mostly a frame is used. Another thing is, as you said was mixin place with pack. There are only a few cases where place actually makes sense to use. One additional thing I would mention is that you have never set the scrollregion and did not have an event to update it, so the scrollbar never was usefull. – Thingamabobs Nov 28 '21 at 11:54