[Tkinter] Passing information with a function - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Passing information with a function (/thread-34143.html) |
Passing information with a function - Krisve94 - Jun-30-2021 I am unsure if this is the right forum, but since it concerns a wigdet it seemed appropriate. In line 26 the line "strength_entry.bind('<Return>', update_score(ab=2))" uses braces behind the calling the function "update_score". If I remove the braces and change the argumetn "ab" to "0" in line 19 I will be able to change the label with the use of the enter button in the GUI. But since I want to be able to expand the number of wigdets with more entry and have them all use the same function I cant let it be written in line 19. I want to be able to expand the library in line 18 later. (The "40" is just a test to see if I could be able to change it.) To clearify, the function will run one time when I specify which element in the library I want to use, and not run everytime I press enter. Is there a way to write this? I am learning Python by creating this program, but sometimes i get stuck at stuff like this. from tkinter import * import math parent_w = Tk() ability_w = Frame(parent_w) ability_w.grid(row=0, column=0) class AbilityScore: def __init__(self, score): self.score = score def my_score(self): return math.floor(int(self.score - 10) / 2) def update_score(*args, ab): abilities_list = [strength_entry.get(), 40] strength_label.config(text=math.floor((int(abilities_list[ab]) - 10) / 2)) strength_entry = Entry(ability_w, widt=3, font=("Arial", 20), justify=CENTER) strength_entry.insert(0, 0) strength_label = Label(ability_w, font=("Arial", 15), text=AbilityScore(int(strength_entry.get())).my_score()) strength_text = Label(ability_w, text="Strength", font=("Arial", 20)) strength_entry.bind('<Return>', update_score(ab=0)) strength_entry.grid(row=0, column=1) strength_text.grid(row=0, rowspan=2, column=0) strength_label.grid(row=1, column=1) parent_w.geometry("400x250") parent_w.mainloop() RE: Passing information with a function - Yoriz - Jun-30-2021 Your code is a bit chaotic for me to understand what you are trying to do. The following example shows that event has a widget attribute that is the widget that called the event handler,it also shows the use of partial to pass extra information.import tkinter as tk from functools import partial class TkApp(tk.Tk): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.entry1 = tk.Entry(self) self.entry1.pack(padx=5, pady=5) self.entry1.bind('<Return>', self.on_entries) self.entry2 = tk.Entry(self) self.entry2.pack(padx=5, pady=5) self.entry2.bind('<Return>', partial( self.on_entries, identity='entry2')) def on_entries(self, event, identity=None): if event.widget == self.entry1: print('This was called from entry1\n' f'Entry value was {event.widget.get()}') if identity == 'entry2': print('This was called from entry2\n' f'Entry value was {event.widget.get()}\n' f'identity value was {identity}') if __name__ == '__main__': app = TkApp() app.mainloop() Note: see Namespace flooding with * imports Using classes with GUI's makes the code more organised, grouping related parts together that are self-contained. RE: Passing information with a function - deanhystad - Jun-30-2021 This may be a good place to use a partial function (functools library) or a lambda expression. Either give you control of what gets passed to the function. I will use a lambda expression to bind each entry to a different label. import tkinter as tk def update_label(event, label): label['text'] = event.widget.get() root = tk.Tk() frame = tk.Frame(root) frame.pack() label = tk.Label(frame, text='Label 1') label.pack() entry = tk.Entry(frame) entry.pack() entry.bind('<Return>', lambda event, arg=label: update_label(event, arg)) label = tk.Label(frame, text='Label 2') label.pack() entry = tk.Entry(frame) entry.pack() entry.bind('<Return>', lambda event, arg=label: update_label(event, arg)) root.mainloop()This is the lambda expression. lambda event, arg=label: update_label(event, arg)event is the event argument passed when bind calls the function when <Return> is pressed. arg is an additional argument passed to the function. I assign the value to label, so the label widget is passed as the second argument when update_label is called. Below is the same example using partial. import tkinter as tk from functools import partial def update_label(event, label): label['text'] = event.widget.get() root = tk.Tk() frame = tk.Frame(root) frame.pack() label = tk.Label(frame, text='Label 1') label.pack() entry = tk.Entry(frame) entry.pack() entry.bind('<Return>', partial(update_label, label=label)) label = tk.Label(frame, text='Label 2') label.pack() entry = tk.Entry(frame) entry.pack() entry.bind('<Return>', partial(update_label, label=label)) root.mainloop() RE: Passing information with a function - deanhystad - Jun-30-2021 What you really want is an Entry widget that calls a command when Return is pressed. The code below makes an Entry widget for numbers that calls a command when Return is pressed or the widget loses focus. Losing focus is a common way to force a widget to "accept" input. import tkinter as tk from functools import partial class NumberEntry(tk.Entry): """I am like an Entry widget, but for numbers""" def __init__(self, *args, command=None, justify=tk.RIGHT, **kvargs): self.var = tk.StringVar() super().__init__(*args, textvariable=self.var, justify=justify, **kvargs) self.bind('<FocusOut>', self._validate) self.bind('<Return>', self._validate) self.var.trace_add('write', lambda a, b, c: self._changed()) self.text_changed = False self.command = command self.set(0) def text(self): """Return text in entry""" return self.var.get() def get(self): """Return my numeric value""" return self.value def set(self, value): """Set my numeric value""" self.value = value self.var.set(str(value)) self.changed = False def _changed(self): """Called when something changes text""" self.text_changed = True def _validate(self, event): """Called wnen lose focus or return key is pressed""" if self._changed: try: self.set(float(self.var.get())) except ValueError: self.set(self.value) if self.command is not None: self.command(self.value) def update_label(value, label): label['text'] = str(value) root = tk.Tk() frame = tk.Frame(root) frame.pack() label = tk.Label(frame, text='Label 1') label.pack() NumberEntry(frame, command=partial(update_label, label=label)).pack() label = tk.Label(frame, text='Label 2') label.pack() NumberEntry(frame, command=partial(update_label, label=label)).pack() root.mainloop()This is more code, but you pay the price of a new class only once and reap the reward each time it is used. |