How could I use my defined function as an argument to activate another function?

Question:

What I am trying to do is to set the button status to DISABLED whenever the textbox is empty.
I recreated a snippet of my code bellow:

import tkinter
from tkinter import *

root = tkinter.Tk()

textbox = tkinter.Text(root, height=4)
textbox.pack()

button = tkinter.Button(text="Button", height=2, width=20, state=ACTIVE)
button.pack()


def check_if_textbox_is_empty():
    if textbox.get("1.0", tkinter.END) == "":
        return True


def if_textbox_is_empty():
    button["state"] = DISABLED

# Here is the .bind method I tried, but since it doesn't take func as the first arg it doesn't work for me.
# root.bind(check_if_textbox_is_empty, if_textbox_is_empty)


root.mainloop()

How should go about doing this?

What I want to happen is, when my tkinter.Text has no text in it textbox.get("1.0", tkinter.END) == "" I want my button widget to be disabled button["state"] = DISABLED.
What I have tried is the .bind method, but it seems that it only takes tkinter events ex.:"<Keypress>" as an argument.

Asked By: Dronius

||

Answers:

Firstly:
You don’t need both

import tkinter

and

from tkinter import *

Common practice is

import tkinter as tk

after which you can instantiate widgets like so (note how the constant for the state is also prefixed with tk. – these constants are, as you may have guessed, part of tkinter)

button = tk.Button(text="Button", height=2, width=20, state=tk.ACTIVE)

However, just stick with import tkinter for now


With that out of the way…

Why not simply enable/disable the button within the if clause of check_if_textbox_is_empty()? You can bind the '<KeyRelease>' event to your text box and have it call the check function.

You have several options, all of which will work the same way in the end.


Option 1

Add an _event parameter to the check_if_textbox_is_empty function.

The leading underscore _ is convention to let people know that the value is unused by the function, and event is the conventional name for the event argument taken by event-driven functions in tkinter. The default value of None isn’t strictly necessary, but it’s good practice.

def check_if_textbox_is_empty(_event = None):
    if textbox.get("1.0", tkinter.END) == "":
        button["state"] = tkinter.DISABLED
    else:
        button["state"] = tkinter.NORMAL


# bind an event handler to your textbox to check it as the user types
textbox.bind('<KeyRelease>', check_if_textbox_is_empty)

Option 2

Use *_args in your function to allow it to accept any number of arguments.
Again, the _ is convention for unused values, and args is convention for this type of parameter

def check_if_textbox_is_empty(*_args):  # accept any number of arguments
    if textbox.get("1.0", tkinter.END) == "":
        button["state"] = tkinter.DISABLED
    else:
        button["state"] = tkinter.NORMAL


# bind an event handler to your textbox to check it as the user types
textbox.bind('<KeyRelease>', check_if_textbox_is_empty)

Option 3

Use a lambda to absorb the event and call check... as an anonymous function

def check_if_textbox_is_empty():  # no params needed!
    if textbox.get("1.0", tkinter.END) == "":
        button["state"] = tkinter.DISABLED
    else:
        button["state"] = tkinter.NORMAL


# bind an event handler to your textbox to check it as the user types
textbox.bind('<KeyRelease>', lambda _event: check_if_textbox_is_empty())

Bonus Info!

The reason for adding the _event parameter (or absorbing it with *_args or using a lambda) is that tkinter events typically generate an event value when triggered, so your code has to be able to accommodate that value.

When you bind a function to an event and that event is triggered, the event object gets passed to your function whether you want it or not!

There are plenty of reasons you might want your function to use the value of event, but for this particular case you don’t need it. You can use any of the methods described above to basically throw it out safely.

Answered By: JRiggles

Based on correct @JRiggles answer

Only when I added .strip() in if textbox.get("1.0", tkinter.END).strip() == "": button started change state.

import tkinter as tk

root = tk.Tk()

textbox = tk.Text(root, height=4)
textbox.pack()

button = tk.Button(text="Button", height=2, width=20, state=tk.ACTIVE)
button.pack()


def on_textbox_change(_event=None):
    if textbox.get("1.0", tk.END).strip() == "":
        button["state"] = tk.DISABLED
    else:
        button["state"] = tk.NORMAL


textbox.bind('<KeyRelease>', on_textbox_change)

root.mainloop()
Answered By: Ronin
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.