Chris James
Chris James

Reputation: 161

recreating button Command=Function callback?

I am making a custom button using the tk.Label and my own class so i can import use this in a separate module of a project i don't know how i can replicate the command= function of the button object, is this a implementation of the callback function or if not where should i look for information on how to acomplish this

import tkinter as tk

class Main(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.geometry(self, "800x400")
        tk.Tk.config(self, bg="black")
        container = tk.Frame(self)
        container.config(bg="black")
        container.pack(side="top", fill="both", expand="true", pady=30)
        this = Nav(container, text="Button 1")
        this.bind("<Button>", lambda event: print("HELLO"))
        this.pack()
        Nav(container, text="Button 2")

class Nav(tk.Label):
    def __init__(self, *args, **kwargs):
        btn = tk.Label.__init__(self, *args, **kwargs, bg="green")

    def on_enter(event, ref):
        ref.config(text="enter", bg="#990000")
    def on_leave(event, ref):
        ref.config(text="leave", bg="black")
    def left_click(event, ref):
        ref.config(text="left click")
        return True;
    def release(event, ref):
        ref.config(text="release")


app = Main()
app.mainloop()

Upvotes: 1

Views: 93

Answers (2)

stovfl
stovfl

Reputation: 15523

Question: how i can implement the command= named argument of my own Button object.

To get he same usage as command= and the callback like def callback(), you have to bind to self.on_left_click which calls the callback.


Reference:

  • Events and Bindings

    Use the bind method of the Label widget to bind a callback function to an event called "<Button-1>".


class Nav(tk.Label):
    def __init__(self, parent, **kwargs):
        self.command = kwargs.pop('command', None)

        # Defaults
        kwargs['bg'] = kwargs.get('bg', "green")
        kwargs['fg'] = kwargs.get('fg', "white")

        super().__init__(parent, **kwargs)

        self.bind("<Button-1>", self.on_left_click)
   
    def on_left_click(self, event):
        self.command()

Usage:

nav_button = Nav(container, text="Button 1", command=lambda: print("HELLO"))
nav_button .pack()

Upvotes: 1

Bryan Oakley
Bryan Oakley

Reputation: 386020

You need to do four things:

  • have the caller pass in a command parameter, like you do with a Button,
  • have your new class save this command in a variable
  • implement a binding within Nav to call an internal function
  • have that internal function call the saved command

For example, start by adding a command to Main, and passing that command to Nav just like you would do with a Button:

class Main(tk.Tk):
    def __init__(self, *args, **kwargs):
        ...
        this = Nav(container, text="Button 1", command=self.do_something)
        ...

    def do_something(self):
        print("do_something was called")

Next, define Nav to accept this parameter.

Note: you're mistakingly using both inheritance and composition (inheriting from Label and also creating a second Label) which is unnecessary, so I'm going to remove the second label in the following example.

Since a Label doesn't support the command keyword arg, we need to pull that out of kwargs and save it to a variable before passing the remaining arguments to the __init__ of Label,

class Nav(tk.Label):
    def __init__(self, *args, **kwargs):
        self.command = kwargs.pop("command", None)
        super().__init__(*args, **kwargs, bg="green")

Your original code has a call to bind in Main, but if you're trying to mimic a widget then the bindings need to be inside the widget. With that, this new widget will work just like a button, by specifying a command rather than bindings.

This is how you would define the <Enter>, <Leave>, <ButtonPress> and <ButtonRelease> events. I've renamed the functions from your original code to be more consistent with each other.

class Nav(tk.Label):
    def __init__(self, *args, **kwargs):
        ...

        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)
        self.bind("<ButtonPress>", self.on_press)
        self.bind("<ButtonRelease>", self.on_release)

And finally, you need to define these methods. Here's the one you're specifically asking about which implements the command functionality:

class Nav(tk.label):
    ...
    def on_release(self, event):
        if self.command is not None:
            self.command()

Here are the other methods, replacing ref with self, and putting the arguments in the correct order:

def on_enter(self, event):
    self.config(text="enter", bg="#990000")

def on_leave(self, event):
    self.config(text="leave", bg="black")

def left_click(self, event):
    self.config(text="left click")

Upvotes: 1

Related Questions