The <MouseWheel> event is documented on the bind manual page.
It is emitted by a mouse wheel on a mouse or by a mousepad gesture. It indicated vertical scrolling (positive delta - upwards) or horizontal scrolling with the shift modifier.
The unit of scrolling is different on the platforms:
There are a couple of changes in TK which developped the MouseWheel binding. Below are many recipies, which address older TK versions and don't apply any more as the support is now quite native.
Here is an overview of the changes:
The package scrollutil by Csaba Nemethi gives currently a state of the art mousewheel support.
Windows example binding to scroll a canvas (in a little XML browser) up or down:
bind .t.c <MouseWheel> {%W yview scroll [expr {-%D/120}] units}
Dividing by 120 leads to scrolling one line per wheel click (minimum effective wheel movement) - use a smaller divisor if you want to scroll faster. (RS)
By "cubing" the number of units, you have the effect that slow turning of the wheel moves slowly, while fast turning lets you jump up or down:
bind .t.c <MouseWheel> {%W yview scroll [expr {int(pow(%D/-120,3))}] units}
Kevin Walzer On OS X/Aqua the correct mousewheel binding would be:
bind .t.c <MouseWheel> {%W yview scroll [expr {- (%D)}] units}
This will also work on Unix with the Button-4/Button-5 to MouseWheel translation given below.
KBK Note that on Windows, the <MouseWheel> events don't go to the window that contains the mouse pointer, but rather the window that has the keyboard focus. For various arcane reasons, this behavior is The Right Thing, but it surprises most programmers the first time they see it. Also, note that directing the <MouseWheel> events to the window with the focus means that the window that is to be scrolled must be able to take the focus.
PAK If you insist on doing The Wrong Thing, you can do so by binding the toplevel window to the <MouseWheel> event then trigger a virtual event <<Wheel>> with the correct window. Because you can't set -delta for virtual events, you will need to save the delta value in a global variable which you can recover in the wheel binding.
bind [winfo toplevel .t.graph] <MouseWheel> { trigger %W %X %Y %D } proc trigger {W X Y D} { set w [winfo containing -displayof $W $X $Y] if { $w ne "" } { set x [expr {$X-[winfo rootx $w]}] set y [expr {$Y-[winfo rooty $w]}] global delta set delta $D event generate $w <<Wheel>> -rootx $X -rooty $Y -x $x -y $y } } bind .t.graph <<Wheel>> { %W zoom %x %y $delta }
KPV Tip 171 [L3 ] proposes to change <MouseWheel> events from being sent to the focus window to the window containing the mouse. This is actually how BWidgets has been doing it all along. I find that this is more intuitive and is The Right Thing.
RS 2007-09-24: I'm not sure how the state of discussion is about mousewheel enabling by just mousing-over, but I wanted this for a UI with a listbox and a text widget, both scrollable. Simple but useful:
foreach i {.listbox .text} {bind $i <Enter> {focus %W}}
MG has added this binding to one of his apps, which has a lot of textwidgets, for handling mousewheel events (requires Tcl 8.5):
bind Text <MouseWheel> {} bind all <MouseWheel> [list mouseWheel %W %D] proc mouseWheel {widget delta} { if { $delta >= 0 } { set cmd [list yview scroll [expr {-$delta/3}] pixels] } else { set cmd [list yview scroll [expr {(2-$delta)/3}] pixels] } set over [winfo containing -displayof $widget {*}[winfo pointerxy $widget]] if { $over == "" || [catch {$over {*}$cmd}] } { catch {$widget {*}$cmd} } return; };# mouseWheel
It attempts to scroll the widget the mouse is over first, and then tries the one with the focus (where the event was actually generated) if it fails.
RWC Some links to other MouseWheel code:
Of the above, only the ASPN example will vertically scroll a canvas. Later in the ASPN article is another implementation that attempts to include horizontal scrolling, but doesn't appear to work for a canvas.
Font resizing via mousewheel: The following binding reconfigures a widget with -font attribute (e.g. text, message, entry ...) according to mousewheel rotation:
pack [text .t -font {Helvetica 10} -wrap word -width 30 -height 10] .t insert end "This is a little demo text to test the font sizing via mousewheel" catch {bind .t <MouseWheel> { set font [%W cget -font] set fs [expr {[lindex $font 1]+%D/120}] %W config -font [lreplace $font 1 1 $fs] }}
I've added the catch because on older Windows versions this event is not supported. Also, the default font does not scale in fine steps - but by specifying one that is implemented as TrueType, the effect is really nice ;-) RS
MGS Here's a quick little hack to get MouseWheel events on X (Linux):
bind all <Button-4> \ {event generate [focus -displayof %W] <MouseWheel> -delta 120} bind all <Button-5> \ {event generate [focus -displayof %W] <MouseWheel> -delta -120}
Then you could do (for instance):
bind Scrollbar <MouseWheel> {eval [%W cget -command] scroll [expr {%D/-120}] units}
but this won't work (as it is) for scrollbars with no -command set. RS: Then again, a scrollbar without -command is a pretty useless creature...
chrstphrchvz 2020-06-24: TIP #474 [L4 ] hopes to make <MouseWheel> available in Tk 8.7 on X11, eliminating the need for this. In the meantime, please remember to use if {[tk windowingsystem] eq "x11"} {…} when binding to buttons 4/5 for scrolling (as buttons with those numbers have different meaning on Windows and macOS Aqua, corresponding to buttons 8/9 on X11).
Here you are another implementation for the mousewheel: making a scale scrolled by mousewheel
scale .s -orient horizontal -from 0 -to 100 pack .s bind Scale <Enter> {focus %W} ; # focus on the scale when mouse comes over it bind Scale <Leave> {focus .} ; # focus out the scale bind Scale <MouseWheel> {set increment [expr (%D/120)]; if {$increment == 1} {event generate %W <Left>} else {event generate %W <Right>} }