Tkinter – Scroll with touchpad / mouse gestures / two fingers scrolling in tkinter?

Question:

I wanted to implement two finger scrolling in tkinter.
Here is the result of my own attempt:

import tkinter as tk

class Scene:
    def __init__(self, canvas):
        self.canvas = canvas
        self.elements = [
            {
                "type": "rect",
                "x": canvas.winfo_width() / 2,
                "y": canvas.winfo_height() / 2,
                "width": 200,
                "height": 200,
                "color": (55 / 255, 55 / 255, 10 / 255),
            },
            {
                "type": "rect",
                "x": 100,
                "y": 300,
                "width": 200,
                "height": 200,
                "color": (155 / 255, 200 / 255, 10 / 255),
            },
        ]

    def update_scene(self, offset):
        for element in self.elements:
            element["x"] -= offset[0]
            element["y"] -= offset[1]
        self.render_scene()

    def render_scene(self):
        self.canvas.delete("all")
        for element in self.elements:
            if element["type"] == "rect":
                self.canvas.create_rectangle(
                    element["x"],
                    element["y"],
                    element["x"] + element["width"],
                    element["y"] + element["height"],
                    fill=f"#{int(element['color'][0] * 255):02x}{int(element['color'][1] * 255):02x}{int(element['color'][2] * 255):02x}",
                )
            else:
                print(f"Error: type {element['type']} is not supported.")


root = tk.Tk()
root.geometry("{}x{}".format(800, 600))

canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)

canvas_scroll = [0, 0]

scene = Scene(canvas)
scene.render_scene()


def on_mouse_scroll(event):
    canvas_scroll[0] = event.delta
    canvas_scroll[1] = event.delta
    scene.update_scene(canvas_scroll)


canvas.bind("<MouseWheel>", on_mouse_scroll)

root.mainloop()

The above only works in one diagonal/direction, instead of any direction (up, down, left, right, and all four diagonals)

The above was inspired by a Javascript snippet I found here: https://jsfiddle.net/qmyho24r/

I know using Shift-MouseWheel works, but then I have to also press the shift key, instead of just using the trackpad and two fingers (like in the Javascript example).

How can I use two fingers scrolling in Tkinter?

Asked By: Nordine Lotfi

||

Answers:

tkinter does only support horizontal scrolling on windows from patchlevel 8.6.10 <=
I’ve created a small example that works for me with tkinter 8.6.12 on Win11.
When using two fingers with a little gap between them I can successfully scroll in both direction and move the View in a circle. I retrieve two different events depending on which direction I swap. This is also mentioned in the documentation.

import tkinter as tk
import sys

#https://stackoverflow.com/a/13874620/13629335
OS = sys.platform

def horizontal_scroll(event):
    if OS in ('win32','darwin'):
        canvas.xview_scroll(int(event.delta/120), 'units')
    elif OS == 'linux':
        if event.num == 5:
            canvas.xview_scroll(-1, 'units')
        elif event.num == 4:
            canvas.xview_scroll(1, 'units')

def vertical_scroll(event):
    if OS in ('win32','darwin'):
        canvas.yview_scroll(int(event.delta/120), 'units')
    elif OS == 'linux':
        if event.num == 5:
            canvas.yview_scroll(-1, 'units')
        elif event.num == 4:
            canvas.yview_scroll(1, 'units')

root = tk.Tk('test')
if int(root.tk.call("info", "patchlevel").split('.')[-1]) >= 10:
    #https://docs.activestate.com/activetcl/8.6/get/relnotes/
    canvas = tk.Canvas(root,highlightthickness=0,bd=0)
    #something to show
    v = viewsize = 150
    cw = canvas.winfo_reqwidth()+v
    ch = canvas.winfo_reqheight()+v/2
    s = square = 50
    canvas.create_rectangle(0,0, s,s, tags=('NW',))
    canvas.create_rectangle(cw-s,0, cw,s, tags=('NE',))
    canvas.create_rectangle(cw-s,ch-s, cw,ch, tags=('SE',))
    canvas.create_rectangle(0,ch-s, s,ch, tags=('SW',))
    canvas.pack(fill='both', expand=True, padx=10)
    #update scrollregion
    canvas.configure(scrollregion=canvas.bbox('all'))
    #bindings
    #https://stackoverflow.com/a/17457843/13629335
    if OS in ('win32','darwin'):
        #https://apple.stackexchange.com/q/392936
        root.bind('<MouseWheel>', vertical_scroll)
        root.bind('<Shift-MouseWheel>', horizontal_scroll)
    if OS == 'linux':
        #https://stackoverflow.com/a/17452217/13629335
        root.bind('<Button-4>', vertical_scroll)
        root.bind('<Button-5>', vertical_scroll)
        root.bind('<Shift-Button-4>', horizontal_scroll)
        root.bind('<Shift-Button-5>', horizontal_scroll)
    root.mainloop()
else:
    print('at least for windows it is supported at patchlevel 8.6.10')
    root.destroy()
Answered By: Thingamabobs