0

I have implemented a canvas with scrollable content using the mouse or trackpad. This works well if there is only one page using the mousewheel event.

The problem is that if I add a second page, the mousewheel event does not work on the first page. It will only work on the last page added.

Currently the mousewheel function is written inside each page (class). It should not have conflict of cross-referencing with the following page. It must be an issue with the event handler.

I feel that to make it right, the mousewheel function should be a class that can be accessed by the different pages, but I have no clue how to pass the event to a class and bind it with the scrollabar of each page.

I attach an example of the issue. Can anybody help please.

Thanks in advance

EDIT: Perhaps a solution could be to bind and unbind the mousewheel after leaving each page? How this would be accomplished?

import tkinter as tk
from tkinter import ttk
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import platform


class scrolltest (tk.Tk):
    
    def __init__(self,*args,**kwargs):
        tk.Tk.__init__(self,*args,**kwargs)
      
                       
        container=tk.Frame(self)
        container.pack(side="top",fill="both",expand=True)
        container.grid_rowconfigure(0,weight=1)
        container.grid_columnconfigure(0, weight=1)
        
        
        self.frames={}
        
        for F in (StartPage,Example,Example2):
            
            frame=F(container,self)
            self.frames[F]=frame
            frame.grid(row=0,column=0,sticky="nsew")
            
        self.show_frame(StartPage)
  
    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()
        
        
class StartPage(tk.Frame):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        
        
        button1=tk.Button(self,text="Example",command=lambda:controller.show_frame(Example))
        button1.pack()
        
        button2=tk.Button(self,text="Example2",command=lambda:controller.show_frame(Example2))
        button2.pack()
        
                
        

class Example(tk.Frame):            
    def __init__(self,parent,controller):         
        
        
        tk.Frame.__init__(self, parent)
      
        self.columnconfigure(0, weight=1)
        self.rowconfigure(3, weight=3)
        
        # Button
        tk.Button(self,bg='red',text="Back",command=lambda:controller.show_frame(StartPage)).grid(row=0,column=0,sticky='we', padx=5, pady=10)
        
        # Label
        tk.Label(self,bg='green',text="label").grid(row=2,column=0,sticky='we',padx=5, pady=5)
        
        
        # Create frame and pack
        first_frame= tk.Frame(self,height=480,width=400, pady=5, padx=5)
        first_frame.grid(row=3,column=0)
          
        # Create canvas inside frame and pack
        canvas=tk.Canvas(first_frame)
        canvas.pack(side="left", fill="both", expand=True)
        
        # Create a second frame inside the canvas
        second_frame=tk.Canvas(canvas)  
        
        # Create canvas inside second frame
        canvas.create_window((0,0), window=second_frame, anchor="nw")
            
        # Add scrollbar to first frame
        scrollbar = ttk.Scrollbar(first_frame, orient="vertical", command=canvas.yview)
        
        # Bind scrollbar to canvas region
        canvas.configure(yscrollcommand=scrollbar.set) 
        scrollbar.bind("<Configure>",lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        
        # A fucntion to scroll canvas
        def _on_mouse_wheel(event):
            canvas.yview_scroll(-1 * int((event.delta / 120)), "units")
        
        # Bond scroll fucntion to canvas
        canvas.bind_all("<MouseWheel>", _on_mouse_wheel)
        
        # Pack
        scrollbar.pack(side="right", fill="y")
        
    
        # Random data
        np.random.seed(3)
        x = 0.5 + np.arange(8)
        y = np.random.uniform(2, 7, len(x))
        
        # Plot 2 stacked figures
        
        # Figure 1
        fig, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig1 = FigureCanvasTkAgg(fig,second_frame)
        canvas_fig1.get_tk_widget().pack(side="bottom", fill="y") 
        
        
        #Figure 2
        fig2, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig2 = FigureCanvasTkAgg(fig2,second_frame)
        canvas_fig2.get_tk_widget().pack(side="bottom", fill="y")   
        
        
class Example2(tk.Frame):
                
    def __init__(self,parent,controller):
   
        tk.Frame.__init__(self, parent)    
      
        self.columnconfigure(0, weight=1)
        self.rowconfigure(3, weight=3)
        
        # Button
        tk.Button(self,bg='red',text="Back",command=lambda:controller.show_frame(StartPage)).grid(row=0,column=0,sticky='we', padx=5, pady=10)
        
        # Label
        tk.Label(self,bg='green',text="label").grid(row=2,column=0,sticky='we',padx=5, pady=5)
        
        
        # Create frame and pack
        first_frame2= tk.Frame(self,height=480,width=400, pady=5, padx=5)
        first_frame2.grid(row=3,column=0)
          
        # Create canvas inside frame and pack
        canvas2=tk.Canvas(first_frame2)
        canvas2.pack(side="left", fill="both", expand=True)
        
        # Create a second frame inside the canvas
        second_frame2=tk.Canvas(canvas2)  
        
        # Create canvas inside second frame
        canvas2.create_window((0,0), window=second_frame2, anchor="nw")
            
        # Add scrollbar to first frame
        scrollbar2 = ttk.Scrollbar(first_frame2, orient="vertical", command=canvas2.yview)
        
        # Bind scrollbar to canvas region
        canvas2.configure(yscrollcommand=scrollbar2.set) 
        scrollbar2.bind("<Configure>",lambda e: canvas2.configure(scrollregion=canvas2.bbox("all")))
        
        # A fucntion to scroll canvas
        def _on_mouse_wheel1(event):
            canvas2.yview_scroll(-1 * int((event.delta / 120)), "units")
        
        # Bond scroll fucntion to canvas
        canvas2.bind_all("<MouseWheel>", _on_mouse_wheel1)
        
        # Pack
        scrollbar2.pack(side="right", fill="y")
        
    
        # Random data
        np.random.seed(3)
        x = 0.5 + np.arange(8)
        y = np.random.uniform(2, 7, len(x))
        
        # Plot 2 stacked figures
        
        # Figure 1
        fig, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig1 = FigureCanvasTkAgg(fig,second_frame2)
        canvas_fig1.get_tk_widget().pack(side="bottom", fill="y") 
        
        
        #Figure 2
        fig2, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig2 = FigureCanvasTkAgg(fig2,second_frame2)
        canvas_fig2.get_tk_widget().pack(side="bottom", fill="y")   
                
        

app=scrolltest()
app.title("Scrollfigure")
app.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Andres
  • 13
  • 4

1 Answers1

0

Yes! The issue was that I did not include the unbinding of the mousewheel, so it got locked on the last page and therefore not working on the previous page.

I found the answer in a related post https://stackoverflow.com/a/37858368/18437604

Below is the corrected code

Cheers

import tkinter as tk
from tkinter import ttk
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import platform



class scrolltest (tk.Tk):
    
    def __init__(self,*args,**kwargs):
        tk.Tk.__init__(self,*args,**kwargs)
      
                       
        container=tk.Frame(self)
        container.pack(side="top",fill="both",expand=True)
        container.grid_rowconfigure(0,weight=1)
        container.grid_columnconfigure(0, weight=1)
        
        
        self.frames={}
        
        for F in (StartPage,Example,Example2):
            
            frame=F(container,self)
            self.frames[F]=frame
            frame.grid(row=0,column=0,sticky="nsew")
            
        self.show_frame(StartPage)
  
    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()
        
        
class StartPage(tk.Frame):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        
        
        button1=tk.Button(self,text="Example",command=lambda:controller.show_frame(Example))
        button1.pack()
        
        button2=tk.Button(self,text="Example2",command=lambda:controller.show_frame(Example2))
        button2.pack()
        
                
        

class Example(tk.Frame):            
    def __init__(self,parent,controller):         
        
        
        tk.Frame.__init__(self, parent)
      
        
        
        self.columnconfigure(0, weight=1)
        self.rowconfigure(3, weight=3)
        
        # Button
        tk.Button(self,bg='red',text="Back",command=lambda:controller.show_frame(StartPage)).grid(row=0,column=0,sticky='we', padx=5, pady=10)
        
        # Label
        tk.Label(self,bg='green',text="label").grid(row=2,column=0,sticky='we',padx=5, pady=5)
        
        
        # Create frame and pack
        first_frame= tk.Frame(self,height=480,width=400, pady=5, padx=5)
        first_frame.grid(row=3,column=0)
          
        # Create canvas inside frame and pack
        self.canvas=tk.Canvas(first_frame)
        self.canvas.pack(side="left", fill="both", expand=True)
        
        # Create a second frame inside the canvas
        second_frame=tk.Canvas(self.canvas)  
        
        # Create canvas inside second frame
        self.canvas.create_window((0,0), window=second_frame, anchor="nw")
            
        # Add scrollbar to first frame
        scrollbar = ttk.Scrollbar(first_frame, orient="vertical", command=self.canvas.yview)
        
        # Bind scrollbar to canvas region
        self.canvas.configure(yscrollcommand=scrollbar.set) 
        scrollbar.bind("<Configure>",lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
        
        # A fucntion to scroll canvas
            
        # Bond scroll fucntion to canvas
        self.canvas.bind('<Enter>', self._bound_to_mousewheel)
        self.canvas.bind('<Leave>', self._unbound_to_mousewheel)
        # Pack
        scrollbar.pack(side="right", fill="y")
        
    
        # Random data
        np.random.seed(3)
        x = 0.5 + np.arange(8)
        y = np.random.uniform(2, 7, len(x))
        
        # Plot 2 stacked figures
        
        # Figure 1
        fig, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig1 = FigureCanvasTkAgg(fig,second_frame)
        canvas_fig1.get_tk_widget().pack(side="bottom", fill="y") 
        
        
        #Figure 2
        fig2, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig2 = FigureCanvasTkAgg(fig2,second_frame)
        canvas_fig2.get_tk_widget().pack(side="bottom", fill="y") 
        
        
    def _bound_to_mousewheel(self, event):
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)

    def _unbound_to_mousewheel(self, event):
        self.canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(self, event):
        self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")    
       
        
class Example2(tk.Frame):
                
    def __init__(self,parent,controller):
   
        tk.Frame.__init__(self, parent)    
      
        self.columnconfigure(0, weight=1)
        self.rowconfigure(3, weight=3)
        
        # Button
        tk.Button(self,bg='red',text="Back",command=lambda:controller.show_frame(StartPage)).grid(row=0,column=0,sticky='we', padx=5, pady=10)
        
        # Label
        tk.Label(self,bg='green',text="label").grid(row=2,column=0,sticky='we',padx=5, pady=5)
        
        
        # Create frame and pack
        first_frame= tk.Frame(self,height=480,width=400, pady=5, padx=5)
        first_frame.grid(row=3,column=0)
          
        # Create canvas inside frame and pack
        self.canvas=tk.Canvas(first_frame)
        self.canvas.pack(side="left", fill="both", expand=True)
        
        # Create a second frame inside the canvas
        second_frame=tk.Canvas(self.canvas)  
        
        # Create canvas inside second frame
        self.canvas.create_window((0,0), window=second_frame, anchor="nw")
            
        # Add scrollbar to first frame
        scrollbar = ttk.Scrollbar(first_frame, orient="vertical", command=self.canvas.yview)
        
        # Bind scrollbar to canvas region
        self.canvas.configure(yscrollcommand=scrollbar.set) 
        scrollbar.bind("<Configure>",lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
        
        # A fucntion to scroll canvas
            
        # Bond scroll fucntion to canvas
        self.canvas.bind('<Enter>', self._bound_to_mousewheel)
        self.canvas.bind('<Leave>', self._unbound_to_mousewheel)
        # Pack
        scrollbar.pack(side="right", fill="y")
        
    
        # Random data
        np.random.seed(3)
        x = 0.5 + np.arange(8)
        y = np.random.uniform(2, 7, len(x))
        
        # Plot 2 stacked figures
        
        # Figure 1
        fig, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig1 = FigureCanvasTkAgg(fig,second_frame)
        canvas_fig1.get_tk_widget().pack(side="bottom", fill="y") 
        
        
        #Figure 2
        fig2, ax1= plt.subplots(2,figsize=(5.5,10))
        ax1[0].step(x, y, linewidth=2.5)
        ax1[0].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        ax1[1].step(x, y, linewidth=2.5)
        ax1[1].set(xlim=(0, 8), xticks=np.arange(1, 8),
                ylim=(0, 8), yticks=np.arange(1, 8))
        
        # Draw into canvas and pack
        canvas_fig2 = FigureCanvasTkAgg(fig2,second_frame)
        canvas_fig2.get_tk_widget().pack(side="bottom", fill="y") 
        
        
    def _bound_to_mousewheel(self, event):
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)

    def _unbound_to_mousewheel(self, event):
        self.canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(self, event):
        self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")        

app=scrolltest()
app.title("Scrollfigure")
app.mainloop()
Andres
  • 13
  • 4