Home > Tkinter > Tkinter callbacks

Tkinter callbacks

5 Novembre 2013

Partiamo dalla situazione classica dove abbiamo
un widget collegato ad una callback.
Quindi creiamo una semplicissima window con frame,
un bottone generico ed un bottone di chiusura.

Di solito tengo completamente separata la logica
del programma dalla “grafica” e quindi anche i vari bindings ai
widgets, che lascio gestire ad una classe estranea, eccezion fatta
per il bottone di chiusura che lego chiaramente al metodo destroy.

Perchè questo?
Perchè questa GUI e questo bottone, potrei volerli usare per altri
scopi, senza dover quindi mettere mano al codice della grafica stessa.
In questo caso dovrei modificare solo le callback dei vari handlers.

import Tkinter as tk

CENTER = tk.N + tk.S + tk.E + tk.W

class MyApp:
    def __init__(self):
        self.root = tk.Tk()
        self.frame = tk.Frame(master=self.root)
        self.frame.pack(fill=tk.BOTH, expand=1)
        self.btn_a = tk.Button(master=self.frame, text='Button 1', width=10)
        self.btn_a.grid(sticky=CENTER, row=0, column=0)
        btn_quit = tk.Button(master=self.frame, text='Quit', width=10,
                             command=self.root.destroy)
        btn_quit.grid(sticky=CENTER, row=0, column=1)

class GUIHandler:
    def __init__(self):
        self.app = MyApp()
        self.bind_widgets()

    def bind_widgets(self):
        self.app.btn_a.configure(command=self.on_button)

    def on_button(self):
        print 'You have clicked on Button-1'

    def start(self):
        self.app.root.mainloop()


if __name__ == '__main__':
    h = GUIHandler()
    h.start()

Come dicevo, il btn_quit, l’ho lasciato interno alla GUI e non collegato
dall’esterno, poichè il suo scopo è di chiudere la app, quindi anche gestendo
la gui da un altro handler, questa funzione rimarrebbe la stessa.
Il btn_a invece (con self e accessibile dall’esterno!), lo bindo direttamente dall’handler.
Questa cosa la si può fare sia con il metodo configure(command=func), o con il
metodo bind(event, func, **kwargs):

...
class GUIHandler:
    def __init__(self):
        self.app = MyApp()
        self.bind_widgets()

    def bind_widgets(self):
        self.app.btn_a.bind('<Button-1>', func=self.on_button)

    def on_button(self, event):
        print 'You have clicked on Button-1'

    def start(self):
        self.app.root.mainloop()


if __name__ == '__main__':
    h = GUIHandler()
    h.start()

in questo caso al metodo bind, bisogna passare come argomento, il tipo di evento
(button-1 sta per il bottone 1 del mouse cliccato) ed ovviamente la callback.
La stessa callback, deve avere anche l’argomento event.

Se volessi passare degli argomenti alla callback, dovrei utilizzare una lambda:

import Tkinter as tk

CENTER = tk.N + tk.S + tk.E + tk.W

class MyApp:
    def __init__(self):
        self.root = tk.Tk()
        self.frame = tk.Frame(master=self.root)
        self.frame.pack(fill=tk.BOTH, expand=1)
        self.btn_a = tk.Button(master=self.frame, text='Button 1', width=10)
        self.btn_a.grid(sticky=CENTER, row=0, column=0)
        btn_quit = tk.Button(master=self.frame, text='Quit', width=10,
                             command=self.root.destroy)
        btn_quit.grid(sticky=CENTER, row=0, column=1)

class GUIHandler:
    def __init__(self):
        self.app = MyApp()
        self.bind_widgets()

    def bind_widgets(self):
        self.app.btn_a.configure(command=lambda: self.on_button(100))

    def on_button(self, num):
        print 'You have clicked Button-%s' % num

    def start(self):
        self.app.root.mainloop()


if __name__ == '__main__':
    h = GUIHandler()
    h.start()
Categorie:Tkinter Tag:
I commenti sono chiusi.