1

When Tkinter detects a modified click event such as <Shift-Button-1>, <Control-Button-1>, etc. is there a way to "stub out" the modifier key and have it handled as merely <Button-1> instead?

The only approach I can think of -- which isn't working for my use case -- is to create event bindings for the various modifiers, and then point them all to a callback that triggers the unmodified event. E.g.,

import Tkinter as tk
root = tk.Tk()
lst = tk.Listbox(root,selectmode=tk.EXTENDED)
[...geometry setup...]

def clickOnly(*args):
    lst.event_generate('<Button-1>')

lst.bind('<Shift-Button-1>',clickOnly)
lst.bind('<Control-Button-1>',clickOnly)
[...other related bindings...]

tk.mainloop()

When I run this code, the shift-click etc. are intercepted by the callback but the behavioral results deviate from what the plain click would do. E.g. the shift-click gets anchored to the item at index 0 and still selects multiple items instead of just the clicked one.

Guidance on how to correct my approach would be fine, but I'm really hoping someone can point me to a different, cleaner approach -- some more direct way to have the shift, control, and other modifiers treated as if they were always false.

(Environment is Python 2.7.x, running on Windows 7/10.)

EDIT: What I'm trying to make happen is this:

  1. User points and clicks: Default behavior
  2. User points and shift-clicks: Shift ignored, default behavior for a click
  3. User points and control-clicks: Control ignored, default behavior for a click
  4. User points and alt-clicks: Alt ignored, default behavior for a click
  5. (you get the idea)

I.e. there any way to make Tkinter ignore the modifiers?

JDM
  • 1,709
  • 3
  • 25
  • 48
  • Your question is a bit unclear. Are you wanting to have the event not do the default behavior? Or are you saying that you want a shift-click to do the default behavior of a normal click? – Bryan Oakley Jun 29 '18 at 18:58
  • What's the purpose of setting `selectmode` to external if you want to turn off the bindings that work in that mode? Why not set `selectmode` to single? – Bryan Oakley Jun 29 '18 at 19:04
  • @BryanOakley I'm trying to get any modified click (shift, control, alt, whatever) to yield the same result as a simple click without the modifier key being held. Regarding the choice of `selectmode` there's a long-ish background story, but the tl;dr version is that I'm just being anal about the fine details of the GUI behavior. `tk.SINGLE` isn't doing what I want, the way I want it to. `tk.EXTENDED` is closer so I'm trying to tweak it. – JDM Jun 29 '18 at 19:50
  • maybe the better solution is to describe what you're really trying to accomplish. There may be better ways than to start with the extended mode and then start removing part of the implementation. – Bryan Oakley Jun 29 '18 at 20:01
  • @BryanOakley I went with this question because it deals with a more generalized capability that could have applications beyond my specific current use case -- which in turn would make the question more useful to others in the future. If the answer is, "Sorry, Tkinter just doesn't support what you're describing," then I'll punt and have another try. – JDM Jun 29 '18 at 20:20
  • You can almost certainly do what you want, it's unfortunately not really clear what you want. It would really help to know what you want to happen when the user shift-clicks, rather than trying to explain what you don't want. Tkinter gives you absolute control over what happens for any event, since you can easily override all of the built-in bindings. – Bryan Oakley Jun 29 '18 at 20:23
  • Editing the question to spell it out. – JDM Jun 29 '18 at 21:07

1 Answers1

3

The simplest solution is to create explicit bindings to do exactly what you want, and then to prevent the default binding from occurring. You can do the latter by having your bound function return the string "break".

Example:

import Tkinter as tk
root = tk.Tk()
lst = tk.Listbox(root,selectmode=tk.EXTENDED)
lst.pack()
lst.insert("end", "one", "two", "three", "four", "five")

def clickOnly(event):
    index = '@%s,%s' % (event.x, event.y)
    event.widget.focus_set()
    event.widget.select_clear(0, "end")
    event.widget.select_set(index)
    event.widget.activate(index)
    event.widget.select_anchor(index)
    return "break"

lst.bind('<Any-ButtonPress-1>', clickOnly)

tk.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 1
    Thanks, not only does that cover what I needed but serendipity paid off in two ways: (1) I understand now that a binding callback only *amends* the normal behavior rather than replacing it, and (2) the way to defeat this behavior is to return the string "break" from the callback. – JDM Jul 02 '18 at 10:50
  • 2
    @JDM: that's not quite right. A custom binding doesn't _amend_ anything. A custom binding will run first, and has the option whether to inhibit the default binding or not. The original default binding is left unchanged. For a description of what happens, see https://stackoverflow.com/questions/11541262/basic-query-regarding-bindtags-in-tkinter/11542200#11542200 – Bryan Oakley Jul 02 '18 at 11:45