1

I've been looking around for way to bind trackpad swipe event to horizontal scrollbar.

I've been able to bind <mousewheel> to vertical scrollbar (vsb) and <shift-mousewheel> to horizontal scrollbar (hsb), but still want to implement trackpad swipe (both work fine though).

note: I've tried to use <Button-6> and <Button-7> like suggested in What are the Tkinter events for horizontal edge scrolling (in Linux)? and also tried to check for the event.num like in python tkinter: detecting horizontal scrolling on touchpad, but somehow it only react to button(1,2,3..) but not 6 and 7 which 'supposed' to be trackpad swipe

Here's some sample code to try out:

import tkinter as tk
from tkinter import ttk


def frame_conf(event):
    """
    Reset the scroll region to encompass the inner frame
    """
    canvas.configure(scrollregion=canvas.bbox("all"))


def scroll_vertical(event):
    """
    Enable vertical scrolling by mouse scroll
    """
    if vsb.get() != (0.0, 1.0):
        canvas.yview_scroll(-1 * int(event.delta / 60), "units")


def scroll_horizontal(event):
    """
    Enable horizontal scrolling by shift + mouse scroll
    """
    if hsb.get() != (0.0, 1.0):
        canvas.xview_scroll(-1 * int(event.delta / 60), "units")


def bound_to_mousewheel(event):
    """
    Bound scrollbar to mouse wheel
    """

    canvas.bind_all('<MouseWheel>', scroll_vertical)
    canvas.bind_all('<Shift-MouseWheel>', scroll_horizontal)


def unbound_to_mousewheel(event):
    """
    Unbound scrollbar to mouse wheel
    """

    canvas.unbind_all('<MouseWheel>')
    canvas.unbind_all('<Shift-MouseWheel>')


root = tk.Tk()
frame = ttk.Frame(root)
frame.pack()

canvas = tk.Canvas(frame)
canvas.pack(side="left", expand=True, fill="both")

# Setup horizontal scrollbar 
hsb = ttk.Scrollbar(frame, command=canvas.xview, orient="horizontal")
canvas.configure(xscrollcommand=hsb.set)
hsb.pack(side="bottom", fill="x", before=canvas)

# Setup vertical scrollbar 
vsb = ttk.Scrollbar(frame, command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")

# Create main frame for other widget
widget_frame = tk.Frame(canvas, bg="blue")
canvas_frame = canvas.create_window((0, 0), window=widget_frame, anchor="nw")

# Dynamic frame setup
widget_frame.bind("<Configure>", frame_conf)

# Populate frame
for row in range(50):
    tk.Label(widget_frame, text="%s" % row, width=3, borderwidth="1",
             relief="solid").pack(side="left", anchor="n")

for row in range(50):
    tk.Label(widget_frame, text="%s" % row, width=3, borderwidth="1",
             relief="solid").pack(anchor="sw")

# Bind canvas to mousewheel
canvas.bind("<Enter>", bound_to_mousewheel)
canvas.bind("<Leave>", unbound_to_mousewheel)


root.mainloop()

Thanks in advance :)

Community
  • 1
  • 1
Jessin Ra
  • 63
  • 7

1 Answers1

0

On my system, Debian GNU/Linux 11 (bullseye), the sequences corresponding to horizontally scrolling using the touchpad are '<Shift-Button-4>' and '<Shift-Button-5>'. You can make the following change to get started.

def bound_to_mousewheel(event):
    """
    Bound scrollbar to mouse wheel
    """

    canvas.bind_all('<MouseWheel>', scroll_vertical)
    canvas.bind_all('<Shift-MouseWheel>', scroll_horizontal)
    canvas.bind_all('<Shift-Button-4>', lambda *args: canvas.xview(tk.SCROLL, -1, tk.UNITS))
    canvas.bind_all('<Shift-Button-5>', lambda *args: canvas.xview(tk.SCROLL, 1, tk.UNITS))

Alternatively, you can have a single scroll handler which checks the LSB of the state before performing any scrolling action.

def scroll(event):
    if event.state & 1:
        # Either Shift was held down while scrolling the mouse wheel
        # or a horizontal two-finger swipe was performed on the touchpad.
        # Scroll horizontally.
    else:
        # Either Shift was not held down while scrolling the mouse wheel
        # or a vertical two-finger swipe was performed on the touchpad.
        # Scroll vertically.

Are you on Linux? If yes, you should use '<Button-4>' and '<Button-5>' for vertical scrolling, not '<MouseWheel>'.

tfpf
  • 612
  • 1
  • 9
  • 19