pythontkintercanvasscrollframe

How to set an individual mouse scroll on two different canvases that are connected to separate frames but the frames are one on top of eachother?


I know the title is a bit difficult to understand so let me explain it better here:

I have a class that is inheriting a selection that I've made in a previous window, now that class has a couple of methods(functions) inside it, one of which is a method that creates three frames on a certain part of my root frame. Within that method I'm also creating two instances of two different classes which contain all of the necessary information which I want to place in 2 of the 3 frames described above.

What I'm attempting to achieve is to have 2 frames one of top of another. Those frames will be accordingly called to show on top of eachother per 2 buttons that I have added in my code. Each of those frames contains a canvas that also contains a bunch of tables and I am attempting to scroll through it all in order to be able to better present the tables and the data within those tables.

My problem is that whenever I set the scroll with a "bind_all" it will scroll only on the frame set to show first, even if I click the button that swaps the frame (and it does successfully swap it), it will still scroll the other frame in the background (as per my print statements). However, if I set it to "bind" only I get the results which I'm looking for but the problem is that I cannot scroll on top of all the widgets here, I can only scroll on a thin vertical area on the left side of each of the frames.

Code in question:

import tkinter as tk
from tkinter import Listbox, messagebox, StringVar, ttk
from PIL import Image, ImageTk

class Coupler15(tk.Toplevel):
    def __init__(self, selected_items):
        super().__init__()
        self.configure_window()
        self.create_basics_frame(selected_items)
        self.create_connection_frame()
        self.create_measurement_frame()

    def configure_window(self):
        self.configure(background='light grey')
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        self.geometry(f"{screen_width}x{screen_height}+0+0")
        self.protocol("WM_DELETE_WINDOW", self.on_closing)

    def create_basics_frame(self, selected_items):
        # Basics frame, not necessary info

    def create_connection_frame(self):
        # Connection frame, not necessary info

    def create_measurement_frame(self):
        
        options_frame=tk.Frame(self, relief="raised", bg="light grey", bd=2, height=35, width=208)
        
        button_30kV=tk.Button(options_frame, text='30kV range', font=("Helvetica", 10), command=self.show_30kV_page)
        button_30kV.place(x=0, y=0, width=100)
        button_30kV.configure(background="light grey")
        button_30kV.configure(foreground="black")
        button_30kV.configure(activebackground="dark grey")
        button_30kV.configure(bd=3)
        
        button_6kV=tk.Button(options_frame, text='6kV range', font=("Helvetica", 10), command=self.show_6kV_page)
        button_6kV.place(x=102, y=0, width=100)
        button_6kV.configure(background="light grey")
        button_6kV.configure(foreground="black")
        button_6kV.configure(activebackground="dark grey")
        button_6kV.configure(bd=3)
        
        options_frame.pack(pady=5)
        options_frame.place(x=310, y=5)

        self._6kV_frame = tk.Frame(self, relief="raised", bg="light grey", bd=2, height=750, width=1360)
        self._6kV_frame.pack()
        self._6kV_frame.place(x=310, y=40)

        self._30kV_frame = tk.Frame(self, relief="raised", bg="light grey", bd=2, height=750, width=1360)
        self._30kV_frame.pack()
        self._30kV_frame.place(x=310, y=40)

        for i in [self, self._30kV_frame, self._6kV_frame, options_frame]:
            i.configure(background="light grey")

        self._30kV_page=_30kV_page(parent=self, _30kV_frame=self._30kV_frame)
        self._6kV_page=_6kV_page(parent=self, _6kV_frame=self._6kV_frame)

    def show_30kV_page(self):
        self._30kV_frame.tkraise()
    
    def show_6kV_page(self):
        self._6kV_frame.tkraise()

class _30kV_page(tk.Frame):
    def __init__(self, parent, _30kV_frame):
        super().__init__(parent)
        
        self._30kV_canvas=tk.Canvas(_30kV_frame, bg="light grey")
        _30kV_scrollbar=ttk.Scrollbar(_30kV_frame, orient="vertical", command=self._30kV_canvas.yview)
        _30kV_scrollable_frame=tk.Frame(self._30kV_canvas, bg="light grey")
        _30kV_scrollable_frame.bind("<Configure>", lambda e: self._30kV_canvas.configure(scrollregion=self._30kV_canvas.bbox("all")))
        self._30kV_canvas.create_window((50,50), window=_30kV_scrollable_frame, anchor="nw")
        self._30kV_canvas.configure(yscrollcommand=_30kV_scrollbar.set)
        self._30kV_canvas.config(height=750, width=1345)
        self._30kV_canvas.bind("<MouseWheel>", self._30kV_mouse_wheel_movement)
        self._30kV_canvas.pack(side="left", fill="both", expand=True)
        _30kV_scrollbar.pack(side="right", fill="y")

        _30kV_frame_label=tk.Label(_30kV_scrollable_frame, text="3. Measurements", bg="light grey", font=("Helvetica", 9))
        _30kV_frame_label.grid(row=0, column=0, sticky="nw", pady=10)

        table_labels=["Index", "Upeak [kV]", "+Upeak/1,41 [kV]", "-Upeak/1,41 [kV]", "f [Hz]", "Temp [°C]", "Uist CC100ref", "Umess Prüfung", "Diff. [%]", "crest factor", "Divider"]

        _15kV=tk.Label(_30kV_scrollable_frame, text="15kV", bg="light grey", font=("Helvetica", 10))
        _15kV.grid(row=1, column=0, sticky="nw", pady=20)
        table_frame_15=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_15.grid(row=1, column=0, pady=50)
        table_15 = self.create_table(table_frame_15, 11, 11, table_labels)

        _13kV=tk.Label(_30kV_scrollable_frame, text="13kV", bg="light grey", font=("Helvetica", 10))
        _13kV.grid(row=2, column=0, sticky="nw", pady=10)
        table_frame_13=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_13.grid(row=2, column=0, pady= 40)
        table_13 = self.create_table(table_frame_13, 11, 11, table_labels)

        _11kV=tk.Label(_30kV_scrollable_frame, text="11kV", bg="light grey", font=("Helvetica", 10))
        _11kV.grid(row=3, column=0, sticky="nw", pady=10)
        table_frame_11=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_11.grid(row=3, column=0, pady=40)
        table_11 = self.create_table(table_frame_11, 11, 11, table_labels)

        _9kV=tk.Label(_30kV_scrollable_frame, text="9kV", bg="light grey", font=("Helvetica", 10))
        _9kV.grid(row=4, column=0, sticky="nw", pady=10)
        table_frame_9=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_9.grid(row=4, column=0, pady=40)
        table_9 = self.create_table(table_frame_9, 11, 11, table_labels)

        _7kV=tk.Label(_30kV_scrollable_frame, text="7kV", bg="light grey", font=("Helvetica", 10))
        _7kV.grid(row=5, column=0, sticky="nw", pady=10)
        table_frame_7=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_7.grid(row=5, column=0, pady=40)
        table_7 = self.create_table(table_frame_7, 11, 11, table_labels)

        _5kV=tk.Label(_30kV_scrollable_frame, text="5kV", bg="light grey", font=("Helvetica", 10))
        _5kV.grid(row=6, column=0, sticky="nw", pady=10)
        table_frame_5=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_5.grid(row=6, column=0, pady=40)
        table_5 = self.create_table(table_frame_5, 11, 11, table_labels)

        _3kV=tk.Label(_30kV_scrollable_frame, text="3kV", bg="light grey", font=("Helvetica", 10))
        _3kV.grid(row=7, column=0, sticky="nw", pady=10)
        table_frame_3=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_3.grid(row=7, column=0, pady=40)
        table_3 = self.create_table(table_frame_3, 11, 11, table_labels)

        _1kV=tk.Label(_30kV_scrollable_frame, text="1kV", bg="light grey", font=("Helvetica", 10))
        _1kV.grid(row=8, column=0, sticky="nw", pady=10)
        table_frame_1=tk.Frame(_30kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_1.grid(row=8, column=0, pady=40)
        table_1 = self.create_table(table_frame_1, 11, 11, table_labels)

        for i in [self, self._30kV_canvas, _30kV_scrollable_frame, _30kV_frame, _30kV_frame_label, _15kV, _13kV, _11kV, _9kV, _7kV, _5kV, _3kV, _1kV]:
            i.configure(background="light grey")
    
    def show_30kV_page(self):
        self._30kV_frame.tkraise()

    def _30kV_mouse_wheel_movement(self, event):
        print("Scrolling on 30kV page")
        self._30kV_canvas.yview_scroll(int(-1*(event.delta/120)), "units")
    
    def create_table(self, root, rows, columns, column_labels=None):
        table = []
        if column_labels:
            label_row = []
            for j, label_text in enumerate(column_labels):
                label = tk.Label(root, text=label_text, bg="light grey", font=("Helvetica", 10))
                label.grid(row=0, column=j, sticky="nsew")
                label_row.append(label)
            table.append(label_row)

        for i in range(1, rows):  # Start from 1 to skip the first row reserved for labels
            row = []
            for j in range(columns):
                cell = tk.Entry(root, width=19, justify="center")
                cell.grid(row=i, column=j)
                cell.insert(tk.END, "0")
                row.append(cell)
            table.append(row)
        return table

class _6kV_page(tk.Frame):
    def __init__(self, parent, _6kV_frame):
        super().__init__(parent)

        self._6kV_canvas=tk.Canvas(_6kV_frame, bg="light grey")
        _6kV_scrollbar=ttk.Scrollbar(_6kV_frame, orient="vertical", command=self._6kV_canvas.yview)
        _6kV_scrollable_frame=tk.Frame(self._6kV_canvas, bg="light grey")
        _6kV_scrollable_frame.bind("<Configure>", lambda e: self._6kV_canvas.configure(scrollregion=self._6kV_canvas.bbox("all")))
        self._6kV_canvas.create_window((50,50), window=_6kV_scrollable_frame, anchor="nw")
        self._6kV_canvas.configure(yscrollcommand=_6kV_scrollbar.set)
        self._6kV_canvas.config(height=750, width=1345)
        self._6kV_canvas.bind("<MouseWheel>", self._6kV_mouse_wheel_movement)
        self._6kV_canvas.pack(side="left", fill="both", expand=True)
        _6kV_scrollbar.pack(side="right", fill="y")

        _6kV_frame_label=tk.Label(_6kV_scrollable_frame, text="3. Measurements", bg="light grey", font=("Helvetica", 9))
        _6kV_frame_label.grid(row=0, column=0, sticky="nw", pady=10)

        # THIS NEEDS TO BE REWORKED
        table_labels=["Index", "Upeak [kV]", "+Upeak/1,41 [kV]", "-Upeak/1,41 [kV]", "f [Hz]", "Temp [°C]", "Uist CC100ref", "Umess Prüfung", "Diff. [%]", "crest factor", "Divider"]

        _6kV=tk.Label(_6kV_scrollable_frame, text="6kV", bg="light grey", font=("Helvetica", 10))
        _6kV.grid(row=1, column=0, sticky="nw", pady=20)
        table_frame_6=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_6.grid(row=1, column=0, pady=50)
        table_6 = self.create_table(table_frame_6, 11, 11, table_labels)

        _5kV=tk.Label(_6kV_scrollable_frame, text="5kV", bg="light grey", font=("Helvetica", 10))
        _5kV.grid(row=2, column=0, sticky="nw", pady=10)
        table_frame_5=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_5.grid(row=2, column=0, pady= 40)
        table_5 = self.create_table(table_frame_5, 11, 11, table_labels)

        _4kV=tk.Label(_6kV_scrollable_frame, text="4kV", bg="light grey", font=("Helvetica", 10))
        _4kV.grid(row=3, column=0, sticky="nw", pady=10)
        table_frame_4=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_4.grid(row=3, column=0, pady=40)
        table_4 = self.create_table(table_frame_4, 11, 11, table_labels)

        _3kV=tk.Label(_6kV_scrollable_frame, text="3kV", bg="light grey", font=("Helvetica", 10))
        _3kV.grid(row=4, column=0, sticky="nw", pady=10)
        table_frame_3=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_3.grid(row=4, column=0, pady=40)
        table_3 = self.create_table(table_frame_3, 11, 11, table_labels)

        _2kV=tk.Label(_6kV_scrollable_frame, text="2kV", bg="light grey", font=("Helvetica", 10))
        _2kV.grid(row=5, column=0, sticky="nw", pady=10)
        table_frame_2=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_2.grid(row=5, column=0, pady=40)
        table_2 = self.create_table(table_frame_2, 11, 11, table_labels)

        _1kV=tk.Label(_6kV_scrollable_frame, text="1kV", bg="light grey", font=("Helvetica", 10))
        _1kV.grid(row=6, column=0, sticky="nw", pady=10)
        table_frame_1=tk.Frame(_6kV_scrollable_frame, relief="sunken", bg="light grey", borderwidth=2, height=730, width=1345)
        table_frame_1.grid(row=6, column=0, pady=40)
        table_1 = self.create_table(table_frame_1, 11, 11, table_labels)

        for i in [self, _6kV_frame, _6kV_frame_label, _6kV, _5kV, _4kV, _3kV, _2kV, _1kV, _6kV_scrollable_frame, self._6kV_canvas]:
            i.configure(background="light grey")


    def _6kV_mouse_wheel_movement(self, event):
        print("Scrolling on 6kV page")
        self._6kV_canvas.yview_scroll(int(-1*(event.delta/120)), "units")
    
    def create_table(self, root, rows, columns, column_labels=None):
        table = []
        if column_labels:
            label_row = []
            for j, label_text in enumerate(column_labels):
                label = tk.Label(root, text=label_text, bg="light grey", font=("Helvetica", 10))
                label.grid(row=0, column=j, sticky="nsew")
                label_row.append(label)
            table.append(label_row)

        for i in range(1, rows):  # Start from 1 to skip the first row reserved for labels
            row = []
            for j in range(columns):
                cell = tk.Entry(root, width=19, justify="center")
                cell.grid(row=i, column=j)
                cell.insert(tk.END, "0")
                row.append(cell)
            table.append(row)
        return table

enter image description here

Any help is much appreciated.


Solution

  • Since bind_all() will override previous binding, so only the last binding is effective.

    You need to call bind_all() on the raised frame:

    class Coupler15(tk.Toplevel):
        ...
    
        def create_measurement_frame(self):
            ...
    
            # need to raise one of the frame to activate the binding
            self.show_30kV_page()
    
        def show_30kV_page(self):
            self._30kV_frame.tkraise()
            self._30kV_page.setup_binding() # activate the binding
    
        def show_6kV_page(self):
            self._6kV_frame.tkraise()
            self._6kV_page.setup_binding() # activate the binding
    
    class _30kV_page(tk.Frame):
        def __init__(self, parent, _30kV_frame):
            ...
            # don't call bind_all() here
            #self._30kV_canvas.bind_all("<MouseWheel>", self._30kV_mouse_wheel_movement)
            ...
    
        # new function to activate the binding
        def setup_binding(self):
            self._30kV_canvas.bind_all("<MouseWheel>", self._30kV_mouse_wheel_movement)
    
        ...
    
    class _6kV_page(tk.Frame):
        def __init__(self, parent, _6kV_frame):
            ...
            # don't call bind_all() here
            #self._6kV_canvas.bind_all("<MouseWheel>", self._6kV_mouse_wheel_movement)
            ...
    
        # new function to activate the binding
        def setup_binding(self):
            self._6kV_canvas.bind_all("<MouseWheel>", self._6kV_mouse_wheel_movement)
    
        ...