I have a tk.Button(..., command=_on_button_click).
Once it is clicked, I want to know whether the Shift key is currently held down.
But the _on_button_click is called without any event object or similar for me to check it
Assuming a button named button and a handler named shift_click, you can use the bind method:
button.bind("<Shift-Button-1>", shift_click)
Of course, you will probably also want one without shift click:
button.bind("<Button-1>", not_shift_click)
Related
As far as I understand, the "normal" binding events order of a Tk/Tkinter Listbox widget is (simplifying): <ButtonPress>, <<ListboxSelect>>, <ButtonRelease>
Is it possible to "change" the order so to have <<ListboxSelect>> event triggering after the <ButtonRelease> one?
I was trying using bindtags together with a custom "fake" bind_class for this but without getting the desired result so far...
Here's the code sample:
import tkinter as tk
root = tk.Tk()
l = tk.Listbox(root, name='custlist')
for e in range(55): l.insert(tk.END, 'L_item'+str(e))
l.pack()
l.bind('<ButtonPress>', lambda e: print("L: Click"))
l.bind('<ButtonRelease>', lambda e: print("L: ButtonRelease"))
l.bind_class("post-class-bindings", "<<ListboxSelect>>", lambda e: print("L: post-ListboxSelect"))
l.bindtags((l.winfo_pathname(l.winfo_id()),'Listbox','post-class-bindings', '.', 'all'))
Can you tell me if is it possible to obtain such a thing in this way or another?
Thank you
EDIT:
Thinking of it I realize I can't get it that way 'cause I guess
<ButtonPress>+<<ListboxSelect>> events are somewhat "chained" in their flow while <ButtonRelease> is something "untied", so that I should invoke/generate the <<ListboxSelect>> event at the end of the <ButtonRelease> callback to trigger it... That might work most of the times but... That's not what I was trying to get here... (Indeed, that would be just like "moving" the <<ListboxSelect>> event code to the <ButtonRelease> callbak...)
So, in the end, the question would be if is it possible to "chain" ButtonRelease together with ListboxSelect (in the same way as for ButtonPress)? ... And I guess something like that isn't possible
Is it possible to "change" the order so to have <<ListboxSelect>> event triggering after the one?
The only way to do that is to prevent the selection from changing on a button click. <<ListboxSelect>> isn't a direct result of a button click or a button release, it's a direct result of the selection having been changed. You can't have the selection change on a click and not have <<ListboxSelect>> be generated.
I don't quite understand what your real need is, but if you want to be notified of a selection change on the release of the button you can always emit your own custom virtual event in a handler for <ButtonRelease-1>, for instance, <<ListboxSelectAfterClick>>.
Thanks to Bryan Oakley's clarifications I came to a possible solution for this:
The main intent for me was to have the element
selection in the list to activate/trigger only after the mouse button being released.
import tkinter as tk
root = tk.Tk()
l = tk.Listbox(temp, name='custlist', selectmode='single')
for e in range(55): l.insert(tk.END, 'L_item'+str(e))
l.pack()
l.bind('<ButtonPress>', lambda e: "break")
def AfterReleaseSelect(event):
event.widget.selection_clear(0,tk.END)
event.widget.selection_set(event.widget.nearest(event.y))
#more stuffs here if needed...
l.bind('<ButtonRelease>', AfterReleaseSelect)
What I'm doing here, in practice, is preventing the button click to do its job, so that the listbox selection won't be triggering, and then wait for the mouse button being released to do the stuff.
Some final thoughts on this:
First of all, please notice: I added selectmode='single' parameter to the Listbox widget here as I think it wouldn't be worth to apply such approach when dealing with default ("'browse'-select") Listboxes. This is because, differently than "'browse'-select" Listboxes, with "'single'-select" Listboxes elements selection doesn't "follow" the mouse 'till the end, it gets "stuck" on the first-clicked-element, even if you end releasing the mouse button on a different item.
I must say, anyhow, this probably won't be worth most of the times 'cause you might simply:
avoid using selectmode='single' parameter, staying with the default listbox 'browse'-select-behavior;
"move" the code you would execute with a bind <<ListboxSelect>> to a bind on <ButtonRelease> (as far as this doesn't involve too much "twisting" on the rest of the code, of course...)
I am trying to make a simplest calculator and I want to operate it with the keyboard. This is the file.
I want that whenever I press any number button or a sign button it insert to Entry widget and it does nothing if any button other than number button or sign button is pressed. I want to put all the code in the keyboardbutton function.
Sorry for less comments in the code. And thanks in advance for any help.
First, I made a list in __init__ that matched your self.lst but where all characters are strings:
self.STR_LIST = list(map(str, self.lst))
Next, I bound keypresses to your tkinter window, so that the keyboardbutton mehtod will be called whenever a key is pressed (also in __init__):
self.window.bind("<Key>", self.keyboardbutton)
Then this should work for your keyboardbutton method:
def keyboardbutton(self, event):
if event.char in self.STR_LIST:
self.insert(event.char)
When a key is pressed, an event is sent to this method and we just simply check if it is a valid character for entry, then call your insert method.
Let me know if this works for you.
I have an entry field, and as I type into the data field, I want a method, that updates a treeview widget to be ran. Currently, I can type in a search parameter, then press a 'search' button and it will run the method to search through the treeview to find specified clients, but I want to the treeview to be updating whilst typing into the entry, not by a button press
I am unsure as to weather this is possible, if it should be doing by binding keys or if there is a way using the event loop to achieve this?
See this SO post:
TkInter keypress, keyrelease events
Essentially:
from Tkinter import *
def keyup(e):
pass;
# e.char contains the pressed key if you need that info
# use your search function here
Edit (Sorry I forgot this):
You'll need to bind the keyup function to your widget with something like:
frame.bind("<KeyRelease>", keyup) # you can also bind to a search widget
I have the following problem when using tkinter to create a very simple window containing a matrix of buttons: When one of the buttons is clicked, the event handler changes the text of that button using the configure method on the button widget. This works. But I also want to change the text in one of the other buttons, which does not work. The method I use is that on creating the button, I store the object returned by the Button method before I use the grid geometry manager to place it. This object looks like ".123456789L" when printed and seems to be a pointer to the widget. I also use configure on this to change the button text. But somehow it seems to be wrong, because it works sometimes, and most of the times not. There's unfortunately no error message, just nothing happens when calling configure. I checked and it seems to be the correct pointer to the widget. Do I have to use a special way to affect a widget other that the one that called the event handler? These are the relevant parts of the code:
# CREATING THE BUTTONS:
buttons={} # global
for i in range(3):
for j in range(3):
button = Tkinter.Button(self,text='foo')
buttons[button]=(i,j)
button.grid(column=j,row=i)
button.bind( "<Button-1>", self.OnButtonClick )
# CHANGING BUTTONS:
def find_button(i,j):
"""Return the pointer to the other button to be changed when a button has been clicked."""
for button,key in buttons.items():
if key==(i,j): return button
def OnButtonClick(self,event):
print "You clicked the button",buttons[event.widget]
i,j=buttons[event.widget]
old_button=find_button(i,j) # This is simplified, I don't actually pass i,j, but other values. But I checked this and it returns the reference to the correct button. But this simplified version works the same way, just assume a different button that the one pressed would be returned.
old_button.configure(text = 'blabla') # THIS DOES NOT WORK
event.widget.configure(text = 'something') # THIS WORKS
I have the same problem and i solve it with:
buttons[button]=(i,j,button)
and in the function OnButtonClicK:
i,j,old_button=buttons[event.widget]
old_button.configure(text = 'blabla') # THIS DOES NOT WORK
I have a Dialog Window with a TreeView and two Buttons (Gtk.ResponseType.CANCEL and Gtk.ResponseType.ACCEPT). I can't find how to activates the ResponseType.ACCEPT button when I press Enter inside the Gtk.TreeView. I set cant_datault on desired button and set_default on GtkDialog but Gtk.TreeView doesn't have an activates default method.
Is there any way I can do this?
The quickest way I could find would be to use the "row-activated" signal within the TreeView. This is activated whenever you press Enter, double-click an item or press the Spacebar.
So for example:
treeview.connect("row-activated", lambda a, b, c: dialog.response(Gtk.ResponseType.ACCEPT))
I've used lambda to create the anonymous function, but if you have anything else that needs to run at the same time, you can swap it for a proper function.
Just be aware that if you also change your default response in the future, you'll need to update this function as well.