Skip to content
Advertisement

I can’t scroll grids in frames in canvas (tkinter)

I am using Tkinter for a project, and I found in Stackoverflow a code to create a scrollbar, which I understood well. I wanted to scroll some buttons, which worked, but now when I try to scroll some grids (button and scales), it doesn’t work.

I have tried to manage my code with answers here and here, but I didn’t reach my goal!

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# -------------------------------- Importation ------------------------------- #

import tkinter as tk
from tkinter import messagebox

# ------------------------------ Initialisation ------------------------------ #

root = tk.Tk()

width_screen, height_screen = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (width_screen, height_screen))



# ----------------------- Creation of a list of sounds ----------------------- #

wav_files = ["a.wav","b.wav","c.wav","d.wav","e.wav","f.wav","g.wav","h.wav","i.wav","j.wav","k.wav","l.wav","m.wav","n.wav","o.wav","p.wav","q.wav","r.wav","s.wav","t.wav","u.wav","v.wav","w.wav","x.wav","y.wav","z.wav","aa.wav","bb.wav","cc.wav","dd.wav","ee.wav","ff.wav"]

# -------------------------- Vertical scrolled frame ------------------------- #

class VerticalScrolledFrame(tk.Frame):

    def __init__(self, parent, *args, **kw):
        tk.Frame.__init__(self, parent, *args, **kw)            

        # create a canvas object and a vertical scrollbar for scrolling it
        vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
        vscrollbar.grid(row=0, column=1, sticky='ns')
        canvas = tk.Canvas(self, bd=0, highlightthickness=0,
                        yscrollcommand=vscrollbar.set, width=root.winfo_screenwidth(), height=root.winfo_screenheight())
        canvas.grid(row=0, column=0, sticky="news")
        vscrollbar.config(command=canvas.yview)

        # reset the view
        #canvas.xview_moveto(0)
        #canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = interior = tk.Frame(canvas)
        interior_id = canvas.create_window(0, 0, window=interior,
                                           anchor=tk.NW)

        # track changes to the canvas and frame width and sync them,
        # also updating the scrollbar
        def _configure_interior(event):
            # update the scrollbars to match the size of the inner frame
            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
            canvas.config(scrollregion="0 0 %s %s" % size)
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the canvas's width to fit the inner frame
                canvas.config(width=interior.winfo_reqwidth())

        interior.bind('<Configure>', _configure_interior)

        def _configure_canvas(event):
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the inner frame's width to fill the canvas
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
        canvas.bind('<Configure>', _configure_canvas)


# ------------------------------- Sound buttons ------------------------------ #


class Make_sound:
    def __init__(self, name, parent):
        self.varbutton = tk.StringVar()
        self.varbutton.set("OFF")
        self.name = name
        self.parent = parent

        self.soundbuttoncreator()


    def launchsound(self):
        if self.varbutton.get() == "OFF":
            self.varbutton.set("ON")
        else:
            self.varbutton.set("OFF")

    def soundbuttoncreator(self):

        self.volumescale = tk.Scale(self.parent, orient='vertical', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)', )
        self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew")
        self.faderscale = tk.Scale(self.parent, orient='horizontal', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)')
        self.faderscale.grid(row=1,column=0, sticky="nsew")
        self.button = tk.Checkbutton(self.parent,text=self.name, indicatoron=False, selectcolor="green", background="red", onvalue="ON", offvalue="OFF")
        self.button.grid(row=0, column=0, sticky="nsew")

def sounds_buttons(parent):
    for i in range(len(wav_files)):
        new_name = wav_files[i][:-4]
        globals()["wav_files"][i] = Make_sound(new_name,parent)

# ---------------------------------------------------------------------------- #
#                                   Creation                                   #
# ---------------------------------------------------------------------------- #

# ----------------------------- Buttons of sound ----------------------------- #

scframe = VerticalScrolledFrame(root)
scframe.pack(side=tk.LEFT)

sounds_buttons(scframe.interior)

root.mainloop()

EDIT 1: It seems that the output is just made of the last grid, because the text in the only button shown is ff, which corresponds to the last sound in the list. I didn’t have this problem with just buttons (and not grids): I could see every sounds and I had the scrolling bar active…

EDIT 2: The problem is solved, but now we have a new problem : putting a command to the checkbutton isn’t possible like that. You can see by yourself that the self.varbutton.set(“OFF) isn’t respected, the button is always on “ON”… Here is the code with the problem of command :

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# -------------------------------- Importation ------------------------------- #

import tkinter as tk
from tkinter import messagebox

# ------------------------------ Initialisation ------------------------------ #

root = tk.Tk()

width_screen, height_screen = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (width_screen, height_screen))



# ----------------------- Creation of a list of sounds ----------------------- #

wav_files = ["a.wav","b.wav","c.wav","d.wav","e.wav","f.wav","g.wav","h.wav","i.wav","j.wav","k.wav","l.wav","m.wav","n.wav","o.wav","p.wav","q.wav","r.wav","s.wav","t.wav","u.wav","v.wav","w.wav","x.wav","y.wav","z.wav","aa.wav","bb.wav","cc.wav","dd.wav","ee.wav","ff.wav"]

# -------------------------- Vertical scrolled frame ------------------------- #

class VerticalScrolledFrame(tk.Frame):

    def __init__(self, parent, *args, **kw):
        tk.Frame.__init__(self, parent, *args, **kw)            

        # create a canvas object and a vertical scrollbar for scrolling it
        vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
        vscrollbar.grid(row=0, column=1, sticky='ns')
        canvas = tk.Canvas(self, bd=0, highlightthickness=0,
                        yscrollcommand=vscrollbar.set, width=root.winfo_screenwidth(), height=root.winfo_screenheight())
        canvas.grid(row=0, column=0, sticky="news")
        vscrollbar.config(command=canvas.yview)

        # reset the view
        #canvas.xview_moveto(0)
        #canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = interior = tk.Frame(canvas)
        interior_id = canvas.create_window(0, 0, window=interior,
                                           anchor=tk.NW)

        # track changes to the canvas and frame width and sync them,
        # also updating the scrollbar
        def _configure_interior(event):
            # update the scrollbars to match the size of the inner frame
            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
            canvas.config(scrollregion="0 0 %s %s" % size)
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the canvas's width to fit the inner frame
                canvas.config(width=interior.winfo_reqwidth())

        interior.bind('<Configure>', _configure_interior)

        def _configure_canvas(event):
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the inner frame's width to fill the canvas
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
        canvas.bind('<Configure>', _configure_canvas)


# ------------------------------- Sound buttons ------------------------------ #


class Make_sound:
    def __init__(self, name, parent):
        self.varbutton = tk.StringVar()
        self.name = name
        self.parent = parent

        self.soundbuttoncreator()


    def launchsound(self):
        print(self.varbutton.get())
        if self.varbutton.get() == 1:
            self.list=[]
        else:
            self.list.append("A")

    def soundbuttoncreator(self):
        self.frame = tk.Frame(self.parent) # create a frame to hold the widgets
        
        # use self.frame as parent instead of self.parent
        self.volumescale = tk.Scale(self.frame, orient='vertical', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)', )
        self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew")
        self.faderscale = tk.Scale(self.frame, orient='horizontal', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)')
        self.faderscale.grid(row=1,column=0, sticky="nsew")
        self.button = tk.Checkbutton(self.frame, text=self.name, indicatoron=False, selectcolor="green", background="red", variable=self.varbutton, command=self.launchsound) 
        self.button.grid(row=0, column=0, sticky="nsew")

        self.frame.pack()  # use pack() on the frame so new instance of `Make_sound` will not overlap the old instances


def sounds_buttons(parent):
    for i in range(len(wav_files)):
        new_name = wav_files[i][:-4]
        globals()["wav_files"][i] = Make_sound(new_name,parent)

# ---------------------------------------------------------------------------- #
#                                   Creation                                   #
# ---------------------------------------------------------------------------- #

# ----------------------------- Buttons of sound ----------------------------- #

scframe = VerticalScrolledFrame(root)
scframe.pack(side=tk.LEFT)

sounds_buttons(scframe.interior)

root.mainloop()

EDIT 3: I edited my second code (see EDIT 2).

Advertisement

Answer

It is because you put all the set of Scale and Checkbutton into same location, so the last set will overlap the previous ones:

def soundbuttoncreator(self):

    self.volumescale = tk.Scale(self.parent, orient='vertical', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)', )
    self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew") # same for all instances of Make_sound
    self.faderscale = tk.Scale(self.parent, orient='horizontal', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)')
    self.faderscale.grid(row=1,column=0, sticky="nsew") # same for all instances of Make_sound
    self.button = tk.Checkbutton(self.parent,text=self.name, indicatoron=False, selectcolor="green", background="red", onvalue="ON", offvalue="OFF")
    self.button.grid(row=0, column=0, sticky="nsew") # same for all instances of Make_sound

You should put the set of widgets in a frame and use pack() on the frame:

def soundbuttoncreator(self):
    self.frame = tk.Frame(self.parent) # create a frame to hold the widgets
    
    # use self.frame as parent instead of self.parent
    self.volumescale = tk.Scale(self.frame, orient='vertical', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)', )
    self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew")
    self.faderscale = tk.Scale(self.frame, orient='horizontal', from_=0, to=10, resolution=0.1, tickinterval=2, label='Volume (db)')
    self.faderscale.grid(row=1,column=0, sticky="nsew")
    self.button = tk.Checkbutton(self.frame, text=self.name, indicatoron=False, selectcolor="green", background="red",
                                 onvalue="ON", offvalue="OFF", variable=self.varbutton) 
    self.button.grid(row=0, column=0, sticky="nsew")

    self.frame.pack()  # use pack() on the frame so new instance of `Make_sound` will not overlap the old instances
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement