RISOLTO Come creare classi in tkinter (python 3)

Stato
Discussione chiusa ad ulteriori risposte.

Hirmia

Nuovo Utente
18
2
Ero titubante sul creare o meno questa discussione. Mi rendo conto che qualcosa mi sfugge sull'argomento classi perché provando a costruire qualcosa di mio..qualcosa non torna. Semplice XD
Vi chiedo quindi se potete gentilmente aiutarmi a far luce sulle classi in tkinter adoperando python 3. Magari conoscete fonti diverse da quelle che ho consultato (e che includono quelle indicate in questo forum).

Per contestualizzare il problema, vi mostro il codice che ho costruito (nella modalità 'meno peggio' e qui ho AttributeError: 'App' object has no attribute 'frame2b'). Si tratta della versione con classi di un altro codice scritto inizialmente come semplice procedura e che mi funzione correttamente.

Se non riuscite a sbrogliarmi la situazione, mi andrebbe bene anche solo chiarirmi meglio l'argomento classi.


Python:
import tkinter as tk

class App:

    def __init__(self, master):

        # frame 1

        frame1=tk.Frame(master, bg='light cyan2')
        frame1.grid(row=0, column=0, sticky="we")

        self.bottone=tk.Button(frame1, command=self.FinestraScorrevole)
        self.bottone.grid(row=0, column=0)
        self.iconaRigata= tk.PhotoImage(file="pupazzo-neve-animato_gif.gif", width=100, height=100)  
        self.bottone.configure(image=self.iconaRigata, width=100, height=100)

        self.label=tk.Label(frame1, text='sono un adesivo', bg='VioletRed2')
        self.label.grid(row=0, column=1)

        # framePub

        framePub=tk.Frame(master, height=300, width=400, bg='thistle1')
        framePub.grid(row=1, column=0, padx=10,pady=10)

        # frame 2

        frame2=tk.Frame(master, bg='aquamarine')
        frame2.grid(row=2, column=0, sticky="we", padx=1,pady=10)

        # frame 2b

        canvas = tk.Canvas(master, borderwidth=0, bg='khaki1')
        frame2b = tk.Frame(canvas, bg='khaki1')
        vsb = tk.Scrollbar(master, orient="vertical", command=canvas.yview)
        canvas.configure(yscrollcommand=vsb.set)

        vsb.grid(row=3, column=1, sticky='ns')
        canvas.grid(row=3, column=0, sticky='nsew')
        canvas.create_window((4,4), window=self.frame2b, anchor="nw")

        self.frame2b.bind("<Configure>", lambda event, canvas=canvas: root.tk.onFrameConfigure(canvas))


        self.label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        self.label.grid(row=3, column=1)

        self.label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        label.grid(row=4, column=1, padx=10,pady=10)

        self.label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        self.label.grid(row=5, column=1, padx=10,pady=10)

        populate(frame2b)

        # frame 3

        frame3=tk.Frame(master, bg='lavender')
        frame3.grid(row=4, column=0, sticky="we", padx=1,pady=1)

        self.bottone=tk.Button(frame3, text='salutami', bg='light cyan2', activeforeground='plum1', command=self.saluto)
        bottone.grid(row=0, column=0)

        self.bottone=tk.Button(frame3, text='fiaba', bg='light cyan2', activeforeground='azure', command=self.fiaba)
        bottone.grid(row=0, column=1)


    ###################

    # def frame 1

    def FinestraScorrevole(self):
        print('prova')

    # def frame 2b

    def populate(self, frame2b):
        self.label=tk.Label(frame2b, text='sono un adesivo1', bg='DarkOrange1', height=100, width=30)
        self.label.grid(row=3, column=1)

        self.label=tk.Label(frame2b, text='sono un adesivo2', bg='DarkOrange1')
        self.label.grid(row=4, column=1, padx=10,pady=10)

        self.label=tk.Label(frame2b, text='sono un adesivo3', bg='DarkOrange1')
        vlabel.grid(row=5, column=1, padx=10,pady=10)

    def onFrameConfigure(self, canvas):
        '''Reset the scroll region to encompass the inner frame'''
        canvas.configure(scrollregion=canvas.bbox("all"))

    # def frame 3
    
    def saluto(self):
        print('ciao ciao')

    def fiaba(self):
        print('Biancaneve!!')

###############

# ROOT

root=tk.Tk()
root.title('comando saluto')
root.configure(bg='white')
#root.geometry('1200x800')

app = App(root)

root.mainloop()
 

pabloski

Utente Èlite
2,868
916
In che senso "classi tkinter"? Intendi classi Python?

La prima cosa che vedo, è che in __init__ non usi self. per assegnare valori agli attributi frame, frame2, ecc... Ovviamente l'interprete Python ti dà un errore.
 

pabloski

Utente Èlite
2,868
916
Anche senza i self. i frame si vedono ma la scrollbar no.

Comunque sì sarebbero le classi Python 3

Beh ovvio, tu comunque chiami tk.Frame e quindi il framework registra internamente dei riferimenti a questi frame. Il problema è che poi il valore di ritorno di queste chiamate non va da nessuna parte, perchè manca il self.

Cioè se scrivi
Python:
frame2b = tk.Frame(canvas, bg='khaki1')

Tu stai dicendo di creare una variabile locale nel metodo __init__ e assegnarle il valore di ritorno della chiamata. Ma non stai definendo un attributo della classe App chiamato frame2b. E infatti quando poi fai

Python:
canvas.create_window((4,4), window=self.frame2b, anchor="nw")

l'interprete risponde picche.

Dunque, l'intenzione iniziale era definito degli attributi degli oggetti di classe App o di definire delle variabili locali, non visibili dall'esterno di __init__?

p.s. che poi non ho capito come fa a funzionarti, se dà errore sulla riga 40????
Post unito automaticamente:

Il codice che sono riuscito a far funzionare, dopo varie modifiche, è questo


Python:
import tkinter as tk

class App:

    def __init__(self, master):

        # frame 1

        frame1=tk.Frame(master)
        frame1.grid(row=0, column=0, sticky="we")

        bottone=tk.Button(frame1, command=self.FinestraScorrevole)
        bottone.grid(row=0, column=0)
        bottone.configure(width=100, height=100)

        label=tk.Label(frame1, text='sono un adesivo', bg='VioletRed2')
        label.grid(row=0, column=1)

        # framePub

        framePub=tk.Frame(master, height=300, width=400, bg='thistle1')
        framePub.grid(row=1, column=0, padx=10,pady=10)

        # frame 2

        frame2=tk.Frame(master, bg='aquamarine')
        frame2.grid(row=2, column=0, sticky="we", padx=1,pady=10)

        # frame 2b

        canvas = tk.Canvas(master, borderwidth=0, bg='khaki1')
        frame2b = tk.Frame(canvas, bg='khaki1')
        vsb = tk.Scrollbar(master, orient="vertical", command=canvas.yview)
        canvas.configure(yscrollcommand=vsb.set)

        vsb.grid(row=3, column=1, sticky='ns')
        canvas.grid(row=3, column=0, sticky='nsew')
        canvas.create_window((4,4), window=frame2b, anchor="nw")

        frame2b.bind("<Configure>", lambda event, canvas=canvas: root.tk.onFrameConfigure(canvas))


        label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        label.grid(row=3, column=1)

        label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        label.grid(row=4, column=1, padx=10,pady=10)

        label=tk.Label(frame2b, text='sono un adesivo', bg='DarkOrange1')
        label.grid(row=5, column=1, padx=10,pady=10)

        self.populate(frame2b)

        # frame 3

        frame3=tk.Frame(master, bg='lavender')
        frame3.grid(row=4, column=0, sticky="we", padx=1,pady=1)

        bottone=tk.Button(frame3, text='salutami', activeforeground='plum1', command=self.saluto)
        bottone.grid(row=0, column=0)

        self.bottone=tk.Button(frame3, text='fiaba', activeforeground='azure', command=self.fiaba)
        bottone.grid(row=0, column=1)


    ###################

    # def frame 1

    def FinestraScorrevole(self):
        print('prova')

    # def frame 2b

    def populate(self, frame2b):
        self.label=tk.Label(frame2b, text='sono un adesivo1', bg='DarkOrange1', height=100, width=30)
        self.label.grid(row=3, column=1)

        self.label=tk.Label(frame2b, text='sono un adesivo2', bg='DarkOrange1')
        self.label.grid(row=4, column=1, padx=10,pady=10)

        self.label=tk.Label(frame2b, text='sono un adesivo3', bg='DarkOrange1')
        self.label.grid(row=5, column=1, padx=10,pady=10)

    def onFrameConfigure(self, canvas):
        '''Reset the scroll region to encompass the inner frame'''
        canvas.configure(scrollregion=canvas.bbox("all"))

    # def frame 3
    
    def saluto(self):
        print('ciao ciao')

    def fiaba(self):
        print('Biancaneve!!')

###############

# ROOT

root=tk.Tk()
root.title('comando saluto')
root.configure(bg='white')
#root.geometry('1200x800')

app = App(root)

root.mainloop()

E non è nemmeno corretto dal punto di vista della canonicità.

In ogni caso, vedo che non ti è chiaro come funzionano le classi in Python, chi è self e a che serve.
 
Ultima modifica:
  • Mi piace
Reazioni: Moffetta88

Hirmia

Nuovo Utente
18
2
Eh sì, hai perfettamente ragione, so che non mi sono chiare le classi in Python e sto leggendo nuovi documenti a riguardo. Suggerimenti?

Ora sulla stregua di un po' di chiarezza che ho raggiunto, ho modificato il codice..ma niente, ho sempre errori.

Inoltre prima intendevo che il codice mi funzionava bene per i primi frame, ma a quello 2b si blocca. Credo che in particolare il problema sia la scrollbar lì contenuta. Anche se ora ho un nuovo errore nella definizione "populate".. :doh:

Posto una delle versioni che ho ottenuto. L'errore generato in questo caso è "NameError: name 'populate' is not defined".
Cosa ho sbagliato questa volta? Come ovviare? Almeno la classe ora è corretta?

Codice:
Python 3:

import tkinter as tk


class App(tk.Frame):
    def __init__(self, master):
        super().__init__(master)   # create a frame (self)
       



        # frame 1

        self.frame1=tk.Frame(self, bg='light cyan2')
        self.frame1.grid(row=0, column=0, sticky="we")

        self.bottone=tk.Button(self.frame1, command=self.FinestraScorrevole)
        self.bottone.grid(row=0, column=0)
        self.iconaRigata= tk.PhotoImage(file="pupazzo-neve-animato_gif.gif", width=100, height=100)
        self.bottone.configure(image=self.iconaRigata, width=100, height=100)

        self.label=tk.Label(self.frame1, text='sono un adesivo', bg='VioletRed2')
        self.label.grid(row=0, column=1)

        # framePub

        self.framePub=tk.Frame(self, height=300, width=400, bg='thistle1')
        self.framePub.grid(row=1, column=0, padx=10,pady=10)

        # frame 2

        self.frame2=tk.Frame(self, bg='aquamarine')
        self.frame2.grid(row=2, column=0, sticky="we", padx=1,pady=10)

        # frame 2b

        self.canvas = tk.Canvas(self, borderwidth=0, bg='khaki1')
        self.frame2b = tk.Frame(self.canvas, bg='khaki1')
        self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.vsb.set)

        self.vsb.grid(row=3, column=1, sticky='ns')
        self.canvas.grid(row=3, column=0, sticky='nsew')
        self.canvas.create_window((4,4), window=self.frame2b, anchor="nw", tags='self.frame2b')

        self.frame2b.bind("<Configure>", lambda event, canvas=self.canvas: onFrameConfigure(canvas))


        self.label=tk.Label(self.frame2b, text='sono un adesivo', bg='DarkOrange1')
        self.label.grid(row=3, column=1)

        self.label=tk.Label(self.frame2b, text='sono un adesivo', bg='DarkOrange1')
        self.label.grid(row=4, column=1, padx=10,pady=10)

        self.label=tk.Label(self.frame2b, text='sono un adesivo', bg='DarkOrange1')
        self.label.grid(row=5, column=1, padx=10,pady=10)

        populate(self.frame2b)

        # frame 3

        self.frame3=tk.Frame(self, bg='lavender')
        self.frame3.grid(row=4, column=0, sticky="we", padx=1,pady=1)

        self.bottone=tk.Button(self.frame3, text='salutami', bg='light cyan2', activeforeground='plum1', command=self.saluto)
        self.bottone.grid(row=0, column=0)

        self.bottone=tk.Button(self.frame3, text='fiaba', bg='light cyan2', activeforeground='azure', command=self.fiaba)
        self.bottone.grid(row=0, column=1)


    ###################

    # def frame 1

    def FinestraScorrevole(self):
        print('prova')

    # def frame 2b

    def populate(self, frame2b):
        self.label=tk.Label(self.frame2b, text='sono un adesivo1', bg='DarkOrange1', height=100, width=30)
        self.label.grid(row=3, column=1)

        self.label=tk.Label(self.frame2b, text='sono un adesivo2', bg='DarkOrange1')
        self.label.grid(row=4, column=1, padx=10,pady=10)

        self.label=tk.Label(frame2b, text='sono un adesivo3', bg='DarkOrange1')
        self.label.grid(row=5, column=1, padx=10,pady=10)

    def onFrameConfigure(self, canvas):
        '''Reset the scroll region to encompass the inner frame'''
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))


    # def frame 3
   
    def saluto(self):
        print('ciao ciao')

    def fiaba(self):
        print('Biancaneve!!')

###############

# ROOT

if __name__ == "__main__":

    root=tk.Tk()
    root.title('comando saluto')
    root.configure(bg='white')


    app = App(root)

    root.mainloop()


Per rendere meglio l'idea, di seguito posto l'interfaccia generata con il codice senza classi. Voglio ottenere lo stesso risultato applicando le classi al codice.

anteprima senza classi.png
 
Ultima modifica:

pabloski

Utente Èlite
2,868
916
  • Mi piace
Reazioni: Moffetta88

Hirmia

Nuovo Utente
18
2
Inizialmente avevo inserito self.populate ma mi appariva una schermata bianca. Oppure 'invalid syntax' se in più aggiungevo self. a 'def populate'.
Comunque grazie per il link. Lo sto leggendo per chiarirmi meglio le idee.
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Come linea guida basilare la sintassi per una classe con chiamata a funzione è:

class nomeClasse():
def __init__(self, arg):
self.funzione(arg)

def funzione(self, arg):
.........

Poi c'è tutto il resto che non è nemmeno spiegato in quell'articolo.
 

Hirmia

Nuovo Utente
18
2
Come linea guida basilare la sintassi per una classe con chiamata a funzione è:

class nomeClasse():
def __init__(self, arg):
self.funzione(arg)

def funzione(self, arg):
.........

Poi c'è tutto il resto che non è nemmeno spiegato in quell'articolo.

Grazie del suggerimento, ho preso nota e inserito nel codice ma mi appare:
''TypeError: object.__init__() takes exactly one argument (the instance to initialize)''.

Da una prima ricerca in rete non mi è chiaro come risolvere questo ennesimo errore.

Continuo ad approfondire la teoria e proverò a riscrivere il codice.
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Ho scritto come regola basilare, ma non necessariamante deve essere usata in quel modo.
Bisogna imparare a usarle.
 

Hirmia

Nuovo Utente
18
2
Sì immaginavo e infatti avevo adattato quella regola al codice. Approfondirò meglio anche questo aspetto.
Comunque a cosa ti riferivi con il resto non presente in quell'articolo? Dove lo trovo?
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Sì immaginavo e infatti avevo adattato quella regola al codice. Approfondirò meglio anche questo aspetto.
Comunque a cosa ti riferivi con il resto non presente in quell'articolo? Dove lo trovo?
Il passo principale è leggere la documentazione ufficiale di Python. Poi, tkinter è integrato in python ma non in maniera perfetta. Come linguaggio a se stante non lo posso giudicare ma l'accoppiata python-tkinter lascia molto a desiderare. Se vuoi solo imparare va bene, altrimenti pensa ad altro.
 

Hirmia

Nuovo Utente
18
2
Con grande gioia e soddisfazione vi comunico che sono riuscita a scrivere correttamente il codice. Finalmente mi è tutto più chiaro. Grazie tante per il supporto che mi avete offerto, mi ha aiutato veramente molto.
Post unito automaticamente:

Il passo principale è leggere la documentazione ufficiale di Python. Poi, tkinter è integrato in python ma non in maniera perfetta. Come linguaggio a se stante non lo posso giudicare ma l'accoppiata python-tkinter lascia molto a desiderare. Se vuoi solo imparare va bene, altrimenti pensa ad altro.

Grazie per il suggerimento. Per il momento approfondirò tkinter per poi magari passare ad altro. In rete ho letto diversi nomi. Giusto per un ulteriore confronto, cosa consiglieresti da accoppiare a python? Inizialmente avevo provato kivy ma ho riscontrato grandi difficoltà. Forse lo riprenderò quando avrò più dimestichezza con la programmazione.
 
Ultima modifica:

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Per me non esiste una accoppiata migliore di python2/gtk2 (sebbene non abbia mai approfondito le gtk2). Ma python2 incomincia a non essere più integrato nelle distribuzioni Linux (se poi usi Windows il discorso potrebbe essere diverso, io uso Linux) e tra poco usciranno le gtk4. Mi sono divertito un po' con le qt5 e in questo caso ti posso dire che non appena cerchi di personalizzare i widget diventa sempre più un'odissea. Forse ti conviene continuare con Tkinter.
(io non sono un programmatore, è solo un hobby)
 
Stato
Discussione chiusa ad ulteriori risposte.

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!