Python TKinter : Manually Trigger a Bind Event (Specifically Configure) - python

I'm busy working with a TKinter GUI and am trying to append additional Items into a Scrollbar Canvas. The scrollbar bits seem to be working just fine
def canvas_configure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
canvas.bind("<Configure>", lambda event: self.canvas_configure(event))
The configure function triggers on resizes just fine but does not trigger when appending Items, I was wondering if it's possible to manually trigger the <Configure> (or other events for that matter)
def append_to_canvas(self, parent):
label = tk.Label(parent, text="Yes")
label.pack();
# canvas execute <Configure>
NOTE
The way I handle situations like this at the moment is to just execute the same function that the event would be executing, for example
def append_to_canvas(self, parent):
label = tk.Label(parent, text="Yes")
label.pack()
event = # create imposter event here
self.canvas_configure(event)
But this isn't always practical
CLARITY
The example I'm using above is just an example. I'm more concerned with the Manual Triggering of a Bind Event part of the question.
This applies to other bind events such as
button.bind("<Button-1>", lambda event: exec_button_click());

I think I've found out how to do this, it looks like there's a function for this feature called event_generate
self.canvas.event_generate("<Configure>");

Related

Does customtkinter CTkButton hover has event option?

I want to perform an action when a mouse hover event occurred on customtkinter ctkbutton hover. is it yet implemented?
Per the CTkButton source code, the on_enter method is bound to the <Enter> event. This method is predominantly focused on updating the button's appearance on hover. If you want to trigger an additional callback on hover, you'll have to add another binding to the button
def callback(event):
# put whatever you want to do 'on hover' into this function
print('Button hovered!')
button = CTkButton(parent, text='Button!')
# use '+' to avoid overwriting the existing binding
button.bind('<Enter>', callback, add='+')
button.pack()

Tkinter gets slow when resizing the window. Can I have the resize command activate when the user settles instead of activating it continuously? [duplicate]

I am trying to write a function to dynamically resize an image displayed in a tkinter window.
Therefore I bound this function to the Configure event:
connroot.bind( "<Configure>", connresiz)
My problems are:
That the connresiz() function gets called 3 times (why 3?) at program start, and
More troublesome, that dynamically resizing the window calls the function continuously as I drag the mouse! How can avoid this?
I thought about checking at the simultaneous presence of a <Configure> and <ButtonRelease-1> events, but I don't know how to code it.
1) We don't know that, since we can't see your code...
2) Short answer is: you can't, because that's exactly what <Configure> event does! Long answer, you can, with a little trick/hack. Since anytime the window is changing, it will call all the binded functions to <Configure>, and the same happens anytime as the mouse button released (right after the last <Configure> call) we can create a flag/switch which will tell us, if the window was "configured" then we can check that switch anytime the mouse button is released, and switch it back to the default value after we ran some actions.
So if you want the image to resized only, when the mouse was released and the window was changed this is the code you need:
from tkinter import *
class Run:
def __init__(self):
self.root = Tk()
self.clicked = False
self.root.bind('<ButtonRelease-1>', self.image_resize)
self.root.bind('<Configure>', lambda e: self.click(True))
def image_resize(self, event):
if self.clicked:
print("I'm printed after <Configure>.") # the action goes here!
self.click(False)
def click(self, value):
self.clicked = value
app = Run()
app.root.mainloop()
According to the official tk documentation, <Configure> events fire "whenever its size, position, or border width changes, and sometimes when it has changed position in the stacking order." This can happen several times during startup.
It is called continuously while you resize the window because the size of the widget is changing. That's what it's defined to do. You can't prevent it from being called, though you can certainly modify what you do in the callback. For example, you could delay resizing the image until you've not received another <Configure> event for a second or two -- which likely means the user has stopped interactive resizing.

How can I unbind every single binding from a Tkinter root window

So I have an app in Tkinter that has a lot of buttons in the first screen and when you press one you pass into a new "Window" (basically destroying all widgets and drawing the ones that are needed for the 'window'). There is a standard function that uses some commands to destroy every child on the root. I would like to add some code that can unbind all of the bindings that are made in the root. Bindings that are on specific widgets get destroyed but those that are bind on the root stay there and cause error.
Here's the code for destroying the widgets.
#staticmethod
def clear():
for widget in guihandle.root.winfo_children():
widget.destroy()
#staticmethod
def set(db,table):
guihandle.clear()
curW = Window(db,table)
guihandle.current_Window = curW
curW.initialize()
guihandle.windows.push(curW)
(Yes, I make the base GUI from a sqlite3 database :P)
There is nothing in Tkinter to do what you want. Your app will need to keep track of the bindings it wants to remove.
That being said, depending on just how complex your real problem is, there may be other solutions. For example, instead of binding to the root window, bind to a custom binding tag (also called a bind tag or bindtag). You will then need to add that tag to every widget to enable the bindings, and remove the tag from any existing widgets to effectively disable the bindings.
I know I am VERY late but, if you go through your code and replace every widget.bind with the function below, which was taken and modified from here
def bindWidget(widget: Widget, event, all:bool=False, func=None):
# https://stackoverflow.com/a/226141/19581763
'''Set or retrieve the binding for an event on a widget'''
try:
_ = widget.__dict__['bindings']
except KeyError:
has_binding_key = False
else:
has_binding_key = True
if not has_binding_key:
setattr(widget, 'bindings', dict())
if func:
if not all:
widget.bind(event, func)
else:
widget.bind_all(event, func)
widget.bindings[event] = func
else:
return(widget.bindings.setdefault(event, None))
Than the function will keep track of every binding for you by setting a attribute called bindings which has both, the event and the function inside of it.
For example:
label.bind('<Button-1>', label.destroy)
Will become:
bindWidget(label, '<Button-1>', func=label.destroy)
After doing that, you can write a simple function which deletes all of the bindings and widgets:
def clear(self): # Self will be your Tk() instance
"""Clears everything on the window, including bindings"""
for child in self.winfo_children():
child.destroy()
if not hasattr(self, 'bindings'):
self.bindings = {}
for event, func in self.bindings.items():
self.unbind_all(event)
self.bindings = {}
There are only 2 caveats with this approach
Time
You will have to go through your code to replace every widget.bind and if you have lots of bindings, than it will take a lot of time
Readability
bindWidget(label, '<Button-1>', func=label.destroy) is less readable and less clear than label.bind('<Button-1>', label.destroy)
Edit 8/11/2022
Alternatively, you can destroy the entire window and recreate it like this:
window.destroy()
window = Tk()
However, I am not sure if creating a whole Tcl interpreter is good.

How to derive a Tkinter widget?

I am trying to enhance the Tkinter Text widget and provide additional capabilities.
One of the key features of my new widget is to hack the arrows/keyboard key strokes (syntax highlighting, auto-complete context menus).
I tried a direct approach of binding the Key and Up/Down keystrokes to methods, but this approach failed as my method handlers were executed before the Text event handlers therefore my methods were executed before the last keystroke handled by the text itself.
class Editor(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent) # initialize the base class
# The Main Text Widget
self.text = scrolledtext.ScrolledText(self, *args, **kwargs)
self.text.pack(side='left', fill='both', expand=1)
self.text.bind("<Key>", lambda event: self.editor_key_handler())
self.text.bind("<Up>", lambda event:self.editor_arrow_key_handler('Up'))
self.text.bind("<Down>", lambda event: self.editor_arrow_key_handler('Down'))
I then tried to switch the bindtag order, and make the Class handler run first, then my instance handlers - this indeed fixed the original issue:
bindtag = list()
bindtag.append(self.text.bindtags()[1])
bindtag.append(self.text.bindtags()[0])
bindtag.append(self.text.bindtags()[2])
bindtag.append(self.text.bindtags()[3])
self.text.bindtags(bindtag)
But now as my handlers were running after the Text's... my Up / Down handlers were running after the insert cursor has already moved inside the Text losing the original position where the user clicked the Up/Down arrows.
Being an experienced Perl Tk programmer, I moved along to try and derive the Tkinter Text widget to allow me to hijack the Up/Down default handler and provide my own customised methods..
I didn't find a way to simply derive and override the Text widget (something seems trivial to an OOP system.)
How this can be done?
The best way to add custom event handling to a widget is to leave the bind tags alone, and simply add your own bindings that return the literal string "break". Returning "break" will prevent the default bindings from firing.
In the following example I've added a custom handler for the up arrow, inserting "" rather than doing the default behavior. Notice that the handler returns the string "break":
import tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.text = tk.Text(root)
self.text.pack(fill="both", expand=True)
self.text.bind("<Up>", self.handle_up)
def handle_up(self, event):
self.text.insert("insert", "<up>")
return "break"
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
For a lengthy description of what happens when a key is handled, see this answer: https://stackoverflow.com/a/11542200/7432. The answer is to a question about the Entry widget, but event processing is the same for all widgets.

WASD input in python

In python, how can I receive keyboard input. I'm well aware of console input with input("...") but I'm more concerned with receiving keyboard input while the console window is not active. For example if I created an instance of a Tkinter screen how could I check to see if let's say "w" was pressed. Then if the statement returned true i could move an object accordingly.
The way you do this with a GUI toolkit like tkinter is to create a binding. Bindings say "when this widget has focus and the user presses key X, call this function".
There are many ways to accomplish this. You can, for example, create a distinct binding for each character. Or, you could create a single binding that fires for any character. With these bindings, you can have them each call a unique function, or you can have all the bindings call a single function. And finally, you can put the binding on a single widget, or you can put the binding on all widgets. It all depends on exactly what you are trying to accomplish.
In a simple case where you only want to detect four keys, four bindings (one for each key) calling a single function makes perhaps the most sense. For example, in python 2.x it would look something like this:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent, width=400, height=400)
self.label = tk.Label(self, text="last key pressed: ", width=20)
self.label.pack(fill="both", padx=100, pady=100)
self.label.bind("<w>", self.on_wasd)
self.label.bind("<a>", self.on_wasd)
self.label.bind("<s>", self.on_wasd)
self.label.bind("<d>", self.on_wasd)
# give keyboard focus to the label by default, and whenever
# the user clicks on it
self.label.focus_set()
self.label.bind("<1>", lambda event: self.label.focus_set())
def on_wasd(self, event):
self.label.configure(text="last key pressed: " + event.keysym);
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

Categories

Resources