Topic: Listbox variabile 2 variabile 3 problem.  (Letto 665 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline johnwick

  • python unicellularis
  • *
  • Post: 12
  • Punti reputazione: 0
    • Mostra profilo
Listbox variabile 2 variabile 3 problem.
« il: Maggio 17, 2020, 13:08 »
Buonasera. Ho un problema con queste 2 liste. Numeri e squadre non allineati. Come devo fare per fare uscire il numero alla sinistra delle squadre?

import tkinter as tk
 
window = tk.Tk()
window.title('Campionato di calcio serie A - 2019/20')
 
window.geometry('1100x600')
 
var1 = tk.StringVar()
l = tk.Label(window, bg='green', fg='yellow',font=('Arial', 12), width=10, textvariable=var1)
l.pack()
 
def print_selection():
    value = lb.get(lb.curselection())   
    var1.set(value) 
 
b1 = tk.Button(window, text='Seleziona Squad.', width=15, height=2, command=print_selection)
b1.pack()
 
var2 = tk.StringVar()
var2.set(())
lb = tk.Listbox(window, listvariable=var2, width=30, height=30)

var3 = tk.StringVar()
var3.set(())
lb = tk.Listbox(window, listvariable=var3, width=30, height=30)

numeri = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
          "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"]

squadre = ["Atalanta", "Bologna", "Brescia", "Cagliari", "Fiorentina", "Genoa", "Inter",
          "Juventus", "Lazio", "Lecce", "Milan", "Napoli", "Parma", "Roma",
          "Sampdoria", "Sassuolo", "Spal", "Torino", "Udinese", "Verona"]
for item in numeri + squadre:
    lb.insert('end', item)

lb.pack()
 
window.mainloop()
« Ultima modifica: Maggio 17, 2020, 21:23 da johnwick »

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #1 il: Maggio 17, 2020, 22:29 »
Personalmente, per prima cosa "disegnerei" su carta la finestra che vorrei ottenere e dopo aver letto, attentamente, il funzionamento del geometry manager pack e quindi del geometry manager grid deciderei quale sarebbe più opportuno applicare per il mio progetto.

Di per se, a mio parere, il "grid" ha dalla sua una maggiore possibilità di controllo del posizionamento al primo impatto, controllo che si raggiunge anche con "pack" imparando a suddividere la superficie in aree omogenee tramite frame utilizzati quali pannelli elementari.

Per altro, eviterei di mischiare di due geometry manager.

Offline johnwick

  • python unicellularis
  • *
  • Post: 12
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #2 il: Maggio 18, 2020, 19:56 »
Grazie nuzzopippo, ho seguito le 2 guide segnalate. Sono riuscito a creare questa tabella che posto sotto. Adesso nella colonna Squadre devo inserire le squadre. Non ho idea come proseguire. Qualche consiglio è bene accettato. Grazie.

http://www.filedropper.com/tabellapy
« Ultima modifica: Maggio 18, 2020, 19:59 da johnwick »

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #3 il: Maggio 19, 2020, 09:14 »
Visto la tabella, carina ... spero Tu NON l'abbia realizzata utilizzando tecniche imperative o funzionali, avrai grossi problemi in tal caso.

Citazione
Adesso nella colonna Squadre devo inserire le squadre. Non ho idea come proseguire. Qualche consiglio è bene accettato. Grazie.

Tabellina fatta, ed ora?...  :question:

... vedi, la realizzazione di un programma è analoga alla costruzione di un edificio, richiede, necessariamente tutta una serie di passi ben precisi, quelli preliminari sono la conoscenza dello scopo dell'edificio, cosa è destinato a fare in sostanza, e la "consistenza" della costruzione da realizzare, una abitazione monofamiliare è ben diversa da una palazzina condominiale. Ovviamente conditi dalla conoscenza del territorio e delle caratteristiche geologiche e normative esistenti nel lotto da destinarsi.

In base a tali caratteristiche un professionista provvede a redigere un "progetto" idoneo alla realizzazione dell'opera richiesta rispettando tutti i limiti, fisici e normativi, inferenti con l'opera, analizzando e predisponendo le caratteristiche strutturali in modo da ottenere il miglior rapporto costi/benefici e la massima robustezza statica dell'edificio ... Ovviamente, il tecnico progettista sarà molto edotto delle tecniche di lavorazione richieste, così come ne sarà edotto chi realizzerà effettivamente l'opera.

La fase successiva, cantierazione, è la predisposizione ed acquisizione dei mezzi d'opera e dei materiali necessari alla successiva fase di realizzazione che verrà effettuata partendo dalle fondamenta, quindi passando, in successione, agli elementi strutturali, agli impianti tecnologici, opere di finitura, infissi ed opere di completamento, il tutto eseguito con le migliori tecniche costruttive al fine di ottenere la massima ergonomia ed efficienza possibili. Ovviamente ogni significativo elemento o fase subirà un collaudo atto a verificare la rispondenza allo scopo da ottenersi.

... Ora, la Tua tabella è, più o meno, equivalente ad un impianto tecnologico secondario, tipo l'impianto elettrico di una singola stanza, che non ha neanche subito un collaudo (quale è il codice?) e che non è nemmeno corredata da una situazione progettuale, da quale quadro elettrico ottiene l'energia (i dati)?, cosa produce l'energia (fonte dati)?, come viene consumata (manipolazione dati)?, l'energia non consumata che fine fa (destinazione nuovi dati)?

Difficile dare un "consiglio" su di una situazione a se stante e, di per se, molto campata in aria. Comprendo la Tua voglia di fare, però tale voglia necessita di un "saper fare" e di un obiettivo da conseguire, lo "obiettivo" stabilisce il "cosa fare" e quest'ultimo definisce cosa apprendere/utilizzare per conseguire la meta.

Quanto sopra, per dare una idea su come si affronta un "argomento", per Tua stessa affermazione sei carente sul "saper fare", che sarebbe prioritario ... vuoi un consiglio? Ok! Tempo permettendo cercherò di darti un parere, nei miei limiti.
Parliamo un po' di cosa vuoi realizzare e del come vorresti farlo, poi posta il codice realizzato, ti dirò che ne penso e, magari, qualche idea esce fuori su come proseguire.

Offline johnwick

  • python unicellularis
  • *
  • Post: 12
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #4 il: Maggio 19, 2020, 10:58 »
Grazie per il tuo interessamento. Posto il link di quello che voglio realizzare, Vedi alla meta pagina:

https://it.wikipedia.org/wiki/Serie_A_2019-2020

Questo è il mio codice sorgente, versione 0.0.1 (versione, revisione, relese) . Il codice è molto elementare, l'ho tagliato perché è troppo lungo....

import tkinter as tk

master = tk.Tk()
master.title('Campionato di calcio serie A - 2019/20')
 
master.geometry('1200x640')
master.configure(background="#98AFC7")

spazioy = tk.Label(master, text="", width=3).grid(row=0, column=0)
spaziox = tk.Label(master, text="", width=3).grid(row=0, column=1)

squadre = tk.Label(master, text="Squadre", width=22, bg="#FDD017").grid(row=0, column=2)
punti = tk.Label(master, text="PT", width=5, bg="#FDD017").grid(row=0, column=3)
giornate = tk.Label(master, text="GI", width=5, bg="#FDD017").grid(row=0, column=4)
par_vinte = tk.Label(master, text="VI", width=5, bg="#FDD017").grid(row=0, column=5)
par_pareggi = tk.Label(master, text="PA", width=5, bg="#FDD017").grid(row=0, column=6)
par_perse = tk.Label(master, text="PE", width=5, bg="#FDD017").grid(row=0, column=7)
gol_fatti = tk.Label(master, text="GOL F", width=5, bg="#FDD017").grid(row=0, column=8)
gol_subiti = tk.Label(master, text="GOL S", width=5, bg="#FDD017").grid(row=0, column=9)
differ_reti = tk.Label(master, text="D RET", width=5, bg="#FDD017").grid(row=0, column=10)

label = tk.Label(master, text="1", width=3, bg="#82CAFA").grid(row=1, column=1)
label = tk.Label(master, text="2", width=3, bg="#E0FFFF").grid(row=2, column=1)
label = tk.Label(master, text="3", width=3, bg="#E0FFFF").grid(row=3, column=1)
label = tk.Label(master, text="4", width=3, bg="#E0FFFF").grid(row=4, column=1)
label = tk.Label(master, text="5", width=3, bg="#57E964").grid(row=5, column=1)
label = tk.Label(master, text="6", width=3, bg="#C3FDB8").grid(row=6, column=1)
label = tk.Label(master, text="7", width=3, bg="#FDD017").grid(row=7, column=1)
label = tk.Label(master, text="8", width=3, bg="#FDD017").grid(row=8, column=1)
label = tk.Label(master, text="9", width=3, bg="#FDD017").grid(row=9, column=1)
label = tk.Label(master, text="10", width=3, bg="#FDD017").grid(row=10, column=1)
label = tk.Label(master, text="11", width=3, bg="#FDD017").grid(row=11, column=1)
label = tk.Label(master, text="12", width=3, bg="#FDD017").grid(row=12, column=1)
label = tk.Label(master, text="13", width=3, bg="#FDD017").grid(row=13, column=1)
label = tk.Label(master, text="14", width=3, bg="#FDD017").grid(row=14, column=1)
label = tk.Label(master, text="15", width=3, bg="#FDD017").grid(row=15, column=1)
label = tk.Label(master, text="16", width=3, bg="#FDD017").grid(row=16, column=1)
label = tk.Label(master, text="17", width=3, bg="#FDD017").grid(row=17, column=1)
label = tk.Label(master, text="18", width=3, bg="#F9B7FF").grid(row=18, column=1)
label = tk.Label(master, text="19", width=3, bg="#F9B7FF").grid(row=19, column=1)
label = tk.Label(master, text="20", width=3, bg="#F9B7FF").grid(row=20, column=1)

# Creazione box dove inserire squadre
e1 = tk.Entry(master, width=25, bg="#82CAFA")
e2 = tk.Entry(master, width=25, bg="#E0FFFF")
e3 = tk.Entry(master, width=25, bg="#E0FFFF")
e4 = tk.Entry(master, width=25, bg="#E0FFFF")
e5 = tk.Entry(master, width=25, bg="#57E964")
e6 = tk.Entry(master, width=25, bg="#C3FDB8")
e7 = tk.Entry(master, width=25)
e8 = tk.Entry(master, width=25)
e9 = tk.Entry(master, width=25)
e10 = tk.Entry(master, width=25)
e11 = tk.Entry(master, width=25)
e12 = tk.Entry(master, width=25)
e13 = tk.Entry(master, width=25)
e14 = tk.Entry(master, width=25)
e15 = tk.Entry(master, width=25)
e16 = tk.Entry(master, width=25)
e17 = tk.Entry(master, width=25)
e18 = tk.Entry(master, width=25, bg="#F9B7FF")
e19 = tk.Entry(master, width=25, bg="#F9B7FF")
e20 = tk.Entry(master, width=25, bg="#F9B7FF")

# Posizione box, pady=5 spazio tra box
e1.grid(row=1, column=2, pady=5)
e2.grid(row=2, column=2, pady=5)
e3.grid(row=3, column=2, pady=5)
e4.grid(row=4, column=2, pady=5)
e5.grid(row=5, column=2, pady=5)
e6.grid(row=6, column=2, pady=5)
e7.grid(row=7, column=2, pady=5)
e8.grid(row=8, column=2, pady=5)
e9.grid(row=9, column=2, pady=5)
e10.grid(row=10, column=2, pady=5)
e11.grid(row=11, column=2, pady=5)
e12.grid(row=12, column=2, pady=5)
e13.grid(row=13, column=2, pady=5)
e14.grid(row=14, column=2, pady=5)
e15.grid(row=15, column=2, pady=5)
e16.grid(row=16, column=2, pady=5)
e17.grid(row=17, column=2, pady=5)
e18.grid(row=18, column=2, pady=5)
e19.grid(row=19, column=2, pady=5)
e20.grid(row=20, column=2, pady=5)

# Creazione secondo box dove inserire Punti
b1 = tk.Entry(master, width=6, bg="#82CAFA")
b2 = tk.Entry(master, width=6, bg="#E0FFFF")
b3 = tk.Entry(master, width=6, bg="#E0FFFF")
b4 = tk.Entry(master, width=6, bg="#E0FFFF")
b5 = tk.Entry(master, width=6, bg="#57E964")
b6 = tk.Entry(master, width=6, bg="#C3FDB8")
b7 = tk.Entry(master, width=6)
b8 = tk.Entry(master, width=6)
b9 = tk.Entry(master, width=6)
b10 = tk.Entry(master, width=6)
b11 = tk.Entry(master, width=6)
b12 = tk.Entry(master, width=6)
b13 = tk.Entry(master, width=6)
b14 = tk.Entry(master, width=6)
b15 = tk.Entry(master, width=6)
b16 = tk.Entry(master, width=6)
b17 = tk.Entry(master, width=6)
b18 = tk.Entry(master, width=6, bg="#F9B7FF")
b19 = tk.Entry(master, width=6, bg="#F9B7FF")
b20 = tk.Entry(master, width=6, bg="#F9B7FF")

# Posizione secondo box Punti, pady=5 spazio tra box
b1.grid(row=1, column=3, pady=5)
b2.grid(row=2, column=3, pady=5)
b3.grid(row=3, column=3, pady=5)
b4.grid(row=4, column=3, pady=5)
b5.grid(row=5, column=3, pady=5)
b6.grid(row=6, column=3, pady=5)
b7.grid(row=7, column=3, pady=5)
b8.grid(row=8, column=3, pady=5)
b9.grid(row=9, column=3, pady=5)
b10.grid(row=10, column=3, pady=5)
b11.grid(row=11, column=3, pady=5)
b12.grid(row=12, column=3, pady=5)
b13.grid(row=13, column=3, pady=5)
b14.grid(row=14, column=3, pady=5)
b15.grid(row=15, column=3, pady=5)
b16.grid(row=16, column=3, pady=5)
b17.grid(row=17, column=3, pady=5)
b18.grid(row=18, column=3, pady=5)
b19.grid(row=19, column=3, pady=5)
b20.grid(row=20, column=3, pady=5)

# Creazione terzo box dove inserire Giornate
c1 = tk.Entry(master, width=6, bg="#82CAFA")
c2 = tk.Entry(master, width=6, bg="#E0FFFF")
c3 = tk.Entry(master, width=6, bg="#E0FFFF")
c4 = tk.Entry(master, width=6, bg="#E0FFFF")
c5 = tk.Entry(master, width=6, bg="#57E964")
c6 = tk.Entry(master, width=6, bg="#C3FDB8")
c7 = tk.Entry(master, width=6)
c8 = tk.Entry(master, width=6)
c9 = tk.Entry(master, width=6)
c10 = tk.Entry(master, width=6)
c11 = tk.Entry(master, width=6)
c12 = tk.Entry(master, width=6)
c13 = tk.Entry(master, width=6)
c14 = tk.Entry(master, width=6)
c15 = tk.Entry(master, width=6)
c16 = tk.Entry(master, width=6)
c17 = tk.Entry(master, width=6)
c18 = tk.Entry(master, width=6, bg="#F9B7FF")
c19 = tk.Entry(master, width=6, bg="#F9B7FF")
c20 = tk.Entry(master, width=6, bg="#F9B7FF")

# Posizione terzo box Giornate, pady=5 spazio tra box
c1.grid(row=1, column=4, pady=5)
c2.grid(row=2, column=4, pady=5)
c3.grid(row=3, column=4, pady=5)
c4.grid(row=4, column=4, pady=5)
c5.grid(row=5, column=4, pady=5)
c6.grid(row=6, column=4, pady=5)
c7.grid(row=7, column=4, pady=5)
c8.grid(row=8, column=4, pady=5)
c9.grid(row=9, column=4, pady=5)
c10.grid(row=10, column=4, pady=5)
c11.grid(row=11, column=4, pady=5)
c12.grid(row=12, column=4, pady=5)
c13.grid(row=13, column=4, pady=5)
c14.grid(row=14, column=4, pady=5)
c15.grid(row=15, column=4, pady=5)
c16.grid(row=16, column=4, pady=5)
c17.grid(row=17, column=4, pady=5)
c18.grid(row=18, column=4, pady=5)
c19.grid(row=19, column=4, pady=5)
c20.grid(row=20, column=4, pady=5)

def createNewWindow():
    newWindow = tk.Toplevel(master)
    labelris = tk.Label(newWindow, text = "Risultati", font='helvetica 12')
    buttonris = tk.Button(newWindow, text = "Clicca qui")
    buttonris.configure(font='helvetica 12')
   
    labelris.pack()
    buttonris.pack()


buttonris = tk.Button(master,
              text="Risultati",
              command=createNewWindow)

buttonris.grid(row=21, column=13)
buttonris.configure(font='helvetica 12')

btn1 = tk.Button(master, text="Salva", padx=10)
btn1.grid(row=21, column=11)
btn1.configure(font='helvetica 12')

btn2 = tk.Button(master, text="Apri", padx=10)
btn2.grid(row=21, column=12)
btn2.configure(font='helvetica 12')

master.mainloop()



Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #5 il: Maggio 19, 2020, 12:22 »
Grazie per il tuo interessamento. Posto il link di quello che voglio realizzare, Vedi alla meta pagina:

https://it.wikipedia.org/wiki/Serie_A_2019-2020

Questo è il mio codice sorgente, versione 0.0.1 (versione, revisione, relese) . Il codice è molto elementare, l'ho tagliato perché è troppo lungo....

Vuoi solo riprodurre la tabellina delle classifiche, magari con le bandierine ed "ordinamenti" ma senza modifiche dati?

Riguardo il Tuo codice ... in un altro post Ti ho già suggerito di guardarti la programmazione ad oggetti in python prima di interagire con le GUI, Ti confermo il parere, la programmazione imperativa che utilizzi è semplicemente "terribile", assolutamente non idonea a tali implementazioni. Così com'è ...

Fermo restando che non è che io sia un gran che, potrei farti un esempio in merito al Tuo scopo nel fine settimana ma ti servirà a ben poco se prima non acquisisci una certa familiarità di base con la programmazione ad oggetti e sospetto ci sia ben altro da acquisire prima

... non è un discorso facile consigliarti in merito, non è che conosca molto in python, il mio background è principalmente in altri linguaggi ... un pdf che ho trovato interessante (ma non so che ne pensino gli avanzati) è stato "Dive into python 3", trovato su questa pagina di debianizzati.org, è in italiano e tratta classi ed iteratori in unica soluzione, è una lettura "ponderosa" e da seguire passo-passo, non è che si possa saltare da un punto ad un altro e comunque non esaurisce l'argomento, c'è proprio tanto da imparare (anche per me).

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #6 il: Maggio 22, 2020, 19:22 »
Allora, @Johnwick, questo pomeriggio ho dedicato un paio d'ore per fare un mix tra la tabella da Te scritta e quella di esempio nel Tuo link, tale esempio è la sola "grafica", giusto per farTi vedere una maniera un po' più compatta per fare all'incirca quello che hai fatto Tu utilizzando le classi.
Lo scrivo separatamente ora perché penso che l'intero codice necessario per un esempio completo sarebbe eccessivo per un singolo post.

nel codice sono preparati dei punti per rispondere, in successivo post, a
Citazione
Adesso nella colonna Squadre devo inserire le squadre. Non ho idea come proseguire.
utilizzando la tabella, metodo che non trovo "magnifico", ma che comunque può andare per esempio spicciolo, tale predisposizione sono i metodi:

# in Data_Contenitor
    def set_data(self, data): pass
    def get_data(self): pass
# in Tabella_campionato ke funzioni di callback
    def _on_save(self): pass
    def _on_open(self): pass

Domani cercherò di implementarle e postarle

intanto il codice attuale dell'esempio

import tkinter as tk

class Data_Contenitor(tk.Frame):
    ''' Pannello dati. '''
    headers = ';Pos;Squadre;Pt;Gi;Vi;Pa;Pe;Gol F;Gol S;D Reti'.split(';')
    colors = ['#eaecf0', '#99cbff', '#afeeee', '#b0ffb0', '#ccffcc', '#ffcccc']
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sfondo = tk.Frame()
        self.sfondo.configure(border=None, bg='#afeeee')
        self._make_header()
        self.data_rows = []
        for i in range(1, 22):
            self._make_row(i)
        for i in range(12):
            self.grid_columnconfigure(i, minsize=50)
        self.grid_columnconfigure(3, weight=1)
        self.update()
       
   
    def _make_header(self):
        for i in range(len(self.headers)):
            lbl = tk.Label(self, relief='raised', text=self.headers[i],
                           font='Serif 10 bold', bg=self.colors[0], justify='center')
            if i < 2:
                lbl.grid(row=0, column=i, sticky='nsew')
            elif i == 2:
                lbl.grid(row=0, column=i, columnspan=2, sticky='nsew')
            else:
                lbl.grid(row=0, column=i+1, sticky='nsew')
   
    def _make_row(self, i):
        #for i in range(1, 22):
        row = []
        if i == 1:
            color = self.colors[1]
        elif i >= 2 and i <= 4:
            color = self.colors[2]
        elif i == 5:
            color = self.colors[3]
        elif i == 6:
            color = self.colors[4]
        elif i >= 18:
            color = self.colors[5]
        else:
            color = 'white'
        for j in range(12):
            if j < 3:
                ctrl = tk.Label(self, relief='sunken', bg=color, justify='center')
                if j == 1 : ctrl.configure(text='{}.'.format(i))
            elif j == 3:
                ctrl = tk.Entry(self, width=20, bg=color)
            else:
                ctrl = tk.Entry(self, width=5, bg=color)
            ctrl.grid(row=i, column=j, sticky='ew')
            row.append(ctrl)
        self.data_rows.append(row)

    def set_data(self, data): pass
    def get_data(self): pass

class Tabella_campionato(tk.Tk):
    ''' Riproduzione tabelle campionato di esempio. '''
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title('Campionati ')
        self.canvas = tk.Canvas()
        self.canvas.grid(row=0, column=0, sticky='nsew')
        self.d_c = Data_Contenitor(self.canvas, border=4)
        self.d_c.grid(row=0, column=0)
        sy = tk.Scrollbar(self, orient=tk.VERTICAL)
        sy.grid(row=0, column=1, sticky='ns')
        sy.configure(command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=sy.set)
        sx = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        sx.grid(row=1, column=0, sticky='ew')
        sx.configure(command=self.canvas.xview)
        self.canvas.configure(xscrollcommand=sx.set)
        self.canvas.create_window((4,4), window=self.d_c, anchor='nw')
        self._add_buttons()
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.d_c.bind('<Configure>', self._on_d_c_configure)
        self.configure(width=self.d_c.winfo_width())

    def _on_d_c_configure(self, evt):
        self.canvas.configure(scrollregion=self.canvas.bbox('all'))
   
    def _add_buttons(self):
        panel = tk.Frame(self)
        panel.grid(row=2, column=0, columnspan=2, sticky='ew')
        bt1 = tk.Button(panel, text='Salva', command=self._on_save)
        bt1.grid(row=0, column=0, sticky='ew')
        bt2 = tk.Button(panel, text='Apri', command=self._on_open)
        bt2.grid(row=0, column=1, sticky='ew')
        bt3 = tk.Button(panel, text='Risultati', command=self._on_results)
        bt3.grid(row=0, column=2, sticky='ew')
        bt3 = tk.Button(panel, text='Esci', command=self.destroy)
        bt3.grid(row=0, column=3, sticky='ew')
        panel.grid_columnconfigure(0, weight=1)
        panel.grid_columnconfigure(1, weight=1)
        panel.grid_columnconfigure(2, weight=1)
        panel.grid_columnconfigure(3, weight=1)
   
    def _on_save(self): pass
    def _on_open(self): pass
    def _on_results(self): pass


if __name__ == '__main__':
    Tabella_campionato().mainloop()

Non è una scheggia, troppi widget, ma Ti apparirà la tabella in un'area ridimensionabilecon scrollbar ... ciò perché penso che un tal genere di tabella sia più idoneo ad essere inserito in una form più "articolata" e non a se stante.

Ne parliamo poi, ciao :)

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Listbox variabile 2 variabile 3 problem.
« Risposta #7 il: Maggio 25, 2020, 18:34 »
Allora@Johnwick, scusa il ritardo nella risposta ma ho trovato uno strano problema nel prepararTi l'esempio (che si verifica solo in linux) che mi sta facendo perdere tempo.

Ho testato un esempio funzionante della tabellina di campionato, capace di salvare ed aprire i dati, oltre che di permetterne l'inserimento.

Purtroppo, il codice completo è molto grande, circa 400 righe (e mancano le ventilate immagini ed ordinamenti vari) e non è il caso di inserirlo tutto assieme qui, indicherò, per argomenti, le integrazioni al codice precedente (sperando di non dimenticarmene qualcuna) e cercando, per sommi capi di descrivere la logica adottata, ovviamente NON mvc, sarebbe servito molto più codice, va da se che se Tu, od anche altri utenti, siete interessati fatevi sentire che cercherò di rendere disponibile il codice intero con qualche dettaglio in più e, magari, tempo permettendo si potrebbe anche integrare qualcosa, tipo le immagini ed ordinamenti sopra o, meglio, un design più "appropriato", sarebbe un ottimo esercizio, anche per me, penso.

La Tua domanda era sul "come proseguire" a disegno della GUI realizzato ... Tale approccio ha un "peccato originale" dato che una GUI si crea avendo già ben chiaro un contesto della gestione dati e del risultato da raggiungere.

DELLE CONSIDERAZIONI :

Guardando la tabella su wikipedia, od anche la tabella da Te realizzata, considerandola quale elemento di input, salta subito agli occhi che alcuni dei dati sono da inserirsi : Squadra, Vi, Pa, Pe, Gol F e Gol S
Gli altri, Pos, Pt, Gi e D Reti sono elementi derivati, un calcolo in sostanza, e non devono essere inseriti, bensì "elaborati", si "dovrebbe" fare tramite un gestore dei dati (model) normalmente ma data l'esigenza (e possibilità) minimale tali elaborazioni possono anche farsi direttamente nella tabellina, per far questo possono sfruttarsi gli eventi di focus sui widget predisposti all'inserimento, cioè intervenire quando una "casella di immissione" perde il focus per effettuare un calcolo relativo ai dati inseriti per aggiornare la visualizzazione.
Una cosa da tener presente, nel fare ciò, è che ci serve identificare esattamente la "posizione" della casella in questione nel contesto dei dati, dopo vari abbozzi di gestione, ho optato per un sub-classamento delle Entry che permetta di tener traccia delle coordinate del widget nella tabella (fondamentali per le varie operazioni), oltre che del colore di sfondo assegnato.

L'oggetto definito :

class DCEntry(tk.Entry):
    ''' Entry "speciale" per la tabella. '''
    def __init__(self, master, x, y, **kwargs):
        self.x = x
        self.y = y
        super().__init__(master, **kwargs)
        self.def_color = None


tener traccia del "colore di sfondo assegnato" scaturisce dalla considerazione che seppur oggetto "specifico" per un uso ben preciso è irrazionale tenerlo direttamente in una condizione di classifica definita, e quindi con le colorazioni specifiche per la posizione, dato che una classificazione è l'ultima delle operazioni da applicarsi, a campionato finito, mentre la condizione "normale" dovrebbe essere neutra ("white" nell'esempio) salvo evidenziare la casella di input corrente, giusto per evitare confusione.
Inoltre, "dovrebbe" essere reso semplice all'utente spostarsi al controllo successivo, una volta completato l'input in una cella, ciò può essere automatizzato intercettando, p.e., la pressione del tasto "return".

Per realizzare quanto sopra, ho definto tre metodi di callback in Data_Contenitor : _receive_focus, _eval_data e _move_to_other

l'ultimo metodo provvede, altresì, a definire una nuova riga quando è all'ultima delle caselle di input dell'ultima riga e si è in stato di nuova immissione, tale stato è controllato dalla variabile di istanza "self.state" definita nello __init__ di Data_Contenitor quale stringa vuota.

La definizione del colore è controllata da un nuovo parametro "yes_color", booleano di default False, del metodo _make_row che è stato modificato per utilizzare oggetti Entry ridefiniti ed applicare i bind opportuni.

Il codice relativo alle nuove caratteristiche e ri-definizioni :

    def _make_row(self, i, yes_color=False):
        row = []
        if i == 1 and yes_color:
            color = self.colors[1]
        elif i >= 2 and i <= 4 and yes_color:
            color = self.colors[2]
        elif i == 5 and yes_color:
            color = self.colors[3]
        elif i == 6 and yes_color:
            color = self.colors[4]
        elif i >= 18 and yes_color:
            color = self.colors[5]
        else:
            color = 'white'
        label_indexes = [0, 1, 2, 4, 5, 11]
        for j in range(12):
            if j in label_indexes:
                ctrl = tk.Label(self, relief='sunken', bg=color, justify='center')
            elif j == 3:
                ctrl = DCEntry(self, j, i, width=20, bg=color)
                ctrl.def_color = color
                ctrl.bind('<FocusIn>', self._receive_focus)
                ctrl.bind('<FocusOut>', self._eval_data)
                ctrl.bind('<Return>', self._move_to_other)
                ctrl.bind('<KP_Enter>', self._move_to_other)
            else:
                ctrl = DCEntry(self, j, i, width=5, bg=color)
                ctrl.def_color = color
                ctrl.bind('<FocusIn>', self._receive_focus)
                ctrl.bind('<FocusOut>', self._eval_data)
                ctrl.bind('<Return>', self._move_to_other)
                ctrl.bind('<KP_Enter>', self._move_to_other)
            ctrl.grid(row=i, column=j, sticky='ew')
            row.append(ctrl)
        self.data_rows.append(row)
   
    def _receive_focus(self, evt):
        ''' Se oggetto DCEntry memorizza il colore corrente e si evidenzia '''
        w = evt.widget
        w.configure(bg='#fffdcd')

    def _eval_data(self, evt):
        ''' in base al widget che perde il focus valuta i dati da rappresentare '''
        w = evt.widget
        w.configure(bg=w.def_color)
        if w.x == 3:
            self._get_flag(w)
        elif w.x in [6, 7, 8]:
            giocate = int(self.data_rows[w.y][6].get()) if self.data_rows[w.y][6].get() else 0
            punti = int(self.data_rows[w.y][6].get()) * 3 if self.data_rows[w.y][6].get() else 0
            giocate += int(self.data_rows[w.y][7].get()) if self.data_rows[w.y][7].get() else 0
            punti += int(self.data_rows[w.y][7].get()) if self.data_rows[w.y][7].get() else 0
            giocate += int(self.data_rows[w.y][8].get()) if self.data_rows[w.y][8].get() else 0
            self.data_rows[w.y][4].configure(text=str(punti))
            self.data_rows[w.y][5].configure(text=str(giocate))
        elif w.x in [9, 10]:
            if self.data_rows[w.y][9].get() and self.data_rows[w.y][10].get():
                diff_r = int(self.data_rows[w.y][9].get()) - int(self.data_rows[w.y][10].get())
                self.data_rows[w.y][11].configure(text=str(diff_r))
                   
    def _get_flag(self, widget): pass

    def _move_to_other(self, evt):
        '''
        Si sposta alla Successiva entry della riga od alla riga successiva.
        Se è all'ultima riga e si è in stato "new" aggiunge una riga.
        '''
        # rel_entry = [(1, 3), (4, 6), (5, 7), (6, 8), (7, 9), (8, 10)]
        w = evt.widget
        if w.x == 3: self.data_rows[w.y][6].focus_set()
        if w.x == 6: self.data_rows[w.y][7].focus_set()
        if w.x == 7: self.data_rows[w.y][8].focus_set()
        if w.x == 8: self.data_rows[w.y][9].focus_set()
        if w.x == 9: self.data_rows[w.y][10].focus_set()
        if w.x == 10:
            if w.y < len(self.data_rows) - 1:
                self.data_rows[w.y+1][3].focus_set()
            else:
                index = len(self.data_rows)
                self._make_row(index)
                self.update()
                self.data_rows[index][3].focus_set()

da notare che in "_eval_data" vi è una chiamata ad un metodo "self._get_flag(w)" pensato per mostrare la bandierina della quadra, al momento non fa nulla (pass)

Da tener presente che quando si giunge all'ultima casella di immissione dell'ultima squadra, per non creare una ulteriore riga, dopo l'inserimento, bisogna spostare il focus su di un diverso controllo "senza" premere return (usa il mouse)

SALVATAGGIO DEI DATI

Il salvataggio, come il caricamento, dei dati è un aspetto "delicato" e fondamentale nel progettare una applicazione; anche queste fasi andrebbero effettuate da un gestore dati, nell'esempio sono state delegate alla finestra principale dell'applicazione "Tabella_Campionato", la scelta di dove salvare e da dove caricare è libera per l'utente ma, al fine di evitare confusione, viene creata e proposta una sub-directory "data" della direttrice contenente l'applicazione ed aggiunte due variabili di istanza "self.app_dir" e "self.storage_dir" definite e valorizzate dal metodo di classe :

    def __def_dir(self):
        ''' Stabilisce la directory della applicazione.'''
        self.app_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
        self.storage_dir = os.path.join(self.app_dir, 'data')
        if not os.path.exists(self.storage_dir) and not os.path.isdir(self.storage_dir):
            try:
                os.mkdir(self.storage_dir)
            except OSError:
                raise EnvironmentError('Errore definizione direttrice dati.')


I dati da salvare vendono ottenuti da "Data_Contenitor" che implementa il metodo pubblico "get_data" che provvede a creare e restituire una lista di liste dei dati correntemente inseriti ed elaborati

    def get_data(self):
        '''
        Crea una lista contenente le intestiazioni ed i dati inseriti ed, eventualemnte,
        i risultati elaborati, poi la restituisce. Annulla lo stato

        return : una lista di liste.
        '''
        data_coll = []
        # definisce la riga di intestazione
        row = []
        for e in self.headers[1:]:
            row.append(e)
        data_coll.append(row)
        # legge e memorizza il contenuto dei widget di interesse
        for r_w in self.data_rows[1:]:
            row = []
            # rel_entry = [(1, 3), (4, 6), (5, 7), (6, 8), (7, 9), (8, 10)]
            # rel_label = [(0, 1), (2, 4), (3, 5), (9, 11)]
            data = int(r_w[1].cget('text')) if r_w[1].cget('text') else None
            row.append(data)
            data = r_w[3].get() if r_w[3].get() else None
            row.append(data)
            data = int(r_w[4].cget('text')) if r_w[4].cget('text') else None
            row.append(data)
            data = int(r_w[5].cget('text')) if r_w[5].cget('text') else None
            row.append(data)
            data = int(r_w[6].get()) if r_w[6].get() else None
            row.append(data)
            data = int(r_w[7].get()) if r_w[7].get() else None
            row.append(data)
            data = int(r_w[8].get()) if r_w[8].get() else None
            row.append(data)
            data = int(r_w[9].get()) if r_w[9].get() else None
            row.append(data)
            data = int(r_w[10].get()) if r_w[10].get() else None
            row.append(data)
            data = int(r_w[11].cget('text')) if r_w[11].cget('text') else None
            row.append(data)
            data_coll.append(row)
        self.state = ''
        return data_coll


Indipendentemente dall'estensione definita o meno dall'utente Tabella_campionato scrive i dati in un file di testo in formato csv ben preciso, ciò è realizzato implementando il metodo di callback "_on_save" già definito nella stesura originale, oltre un uulteriore funzione di utilità:

    def _on_save(self):
        '''
        Registra i dati inseriti.
        '''
        data = self.d_c.get_data()
        if not data: return
        # richiede in quale file salvare
        f_nome = fdlg.asksaveasfilename(parent=self,
                                      initialdir=self.storage_dir,
                                      title='Salvataggio Torneo')
        if not f_nome: return
        # scrive i dati nel file
        try:
            with open(f_nome, 'w') as f:
                for row in data:
                    self._write_row(f, row)
        except OSError:
            msgb.showerror('Errore', 'Salvataggio torneo non riuscito')
            return
        msgb.showinfo('Salvataggio Torneo', 'Salvataggio effettuato con successo')
   
    def _write_row(self, f, row):
        '''
        Scrive nel file una singola riga dati
        '''
        tmp = row[0] if row[0] else ''
        for elem in row[1:]:
            tmp += '|'
            tmp += elem if elem else ''
        tmp +='\n'
        f.write(tmp)


CARICAMENTO DEI DATI

I dati salvati possono essere ricaricati per successiva ridefinizione, ciò viefe effettuato nella implementazione del metodo di callback "_on_open" già predisposto nel precedente post:

    def _on_open(self):
        ''' Cerca di caricare un file dati salvato. '''
        # selezione del file da aprire
        f_nome = fdlg.askopenfilename(parent=self,
                                      initialdir=self.storage_dir,
                                      title='Seleziona Torneo')
        if not f_nome:
            # Nessuna selezione, passa l'informazione a self.d_c
            self.d_c.set_data(None)
            return
        # cerca di leggere il file
        try:
            with open(f_nome, 'r') as f:
                data_rows = f.readlines()
        except OSError:
            msgb.showerror('Errore', 'Errore lettura file campionato')
            return
        dati = []
        for riga in data_rows:
            dr = riga.replace('\n', '').split('|')
            dati.append(dr)
        self.d_c.set_data(dati)
        self.update_idletasks()
        self.update()

Questo codice provvede ad aprire un file di testo e creare una lista di liste che poi trasmette a Data_Contenitor tramite il metodo "set_data".

Data_Contenitor è molto schifiltoso sul formato dei dati, analizzerà per prima cosa le intestazioni dei dati ricevuti, se tutto quadra provvederà a eliminare i dati presenti, tramite il metodo "_clear", ed esporre i dati ricevuti:

    def set_data(self, data, yes_color=False):
        ''' Popola la tabella dei dati ricevuti. '''
        # se non ci sono dati inizializza una riga vuota ed imposta a 'new'
        # lo stato della classe (modalità "inserimento") ed esce
        if not data and self.state == 'opened':
            self.state = 'new'
            return
        elif not data and self.state == '':
            self._clear()
            self._make_row(1)
            self.state = 'new'
            return
        elif data:
            self._clear()
        # verifica che sia il "tipo" di dati che la tabella si aspetta
        data_headers = data[0]
        if not self._verify_headers(data_headers):
            # dovrebbe scatenare una eccezione, si limita a non fare nulla
            return
        # ricrea la tabella popolandola dei dati ricevuti
        for i in range(1, len(data)):
            self._make_row(i, yes_color)
            row = self.data_rows[-1]
            self._make_data(data[i], row)
        self.state = 'opened'
        self.update()

    def _clear(self):
        # fa "spazio", svuota la tabella e la lista, distrugento i controlli
        while len(self.data_rows) > 1:
            row = self.data_rows.pop()
            for widget in row:
                widget.destroy()

    def _verify_headers(self, h):
        # verifica la lunghezza dei dati
        if len(h) != len(self.headers) - 1: return False
        # verifica i singoli campi
        for i in range(len(h)):
            if h[i] != self.headers[i+1]: return False
        return True

    def _make_data(self, d, row):
        # indicizzazione rapporti per gli oggetti DCEntry
        rel_entry = [(1, 3), (4, 6), (5, 7), (6, 8), (7, 9), (8, 10)]
        # indicizzazione rapporti per gli oggetti Label
        rel_label = [(0, 1), (2, 4), (3, 5), (9, 11)]
        for c in rel_entry:
            if d[c[0]]: row[c[1]].insert(tk.END, d[c[0]])
        for c in rel_label:
            if d[c[0]]: row[c[1]].configure(text=d[c[0]], justify=tk.CENTER)

Dopo il caricamento dei dati, Data_Contenitor si pone in modalità "opened" che permette di modificare i dati presenti nelle squadre presenti ma non di aggiungere nuove squadre, per far ciò è sufficiente invocare il comando i caricamento dati senza scegliere alcun file, Data_contenitor si porrà in modalità di nuovi inserimenti.

ELABORAZIONE DELLA CLASSIFICA

La fa la finestra principale della applicazione, tramite il comando _on_results, che provvederà a richiede l'elenco dei dati ed ordinarli per punteggio e differenza reti, per quindi rinviarli a Data_Contenitor in maniera tale che vengano "Colorati" :

    def _on_results(self):
        '''
        Elabora la classifica in base ai punteggi ed alla differenza reti.
        '''
        # richiede i dati a self.d_c
        data = self.d_c.get_data()
        headers = data[0]   # mette da parte le intestazioni
        data = data[1:]     # riduce ai soli dati
        # ordina in base al punteggio realizzato ed alla differenza reti
        data = sorted(data, key=itemgetter(2, 9), reverse=True)
        # definisce la classifica
        cla = 1
        data[0][0] = 1
        for i in range(1, len(data)):
            if data[i][2] == data[i-1][2]:
                data[i][0] = cla
                cla += 1
            else:
                cla += 1
                data[i][0] = cla
        # compila la classifica finale
        final_data = []
        final_data.append(headers)
        for row in data:
            final_data.append(row)
        self.d_c.set_data(final_data, True)
        self.update_idletasks()
        self.update()


Tieni presente l'istruzione "data = sorted(data, key=itemgetter(2, 9), reverse=True)", è quella che, di fatto, definisce la classifica, cerca e leggi la docs relativa, è importante Tu la comprenda.

Ultimo particolare, questi sono gli import necessari :

import sys
import os
import tkinter as tk
import tkinter.filedialog as fdlg
import tkinter.messagebox as msgb
from operator import itemgetter


Spero di non aver dimenticato niente, nel caso qui troverai il codice completo, che ho inserito in una richiesta di feedback su di un problema che credo sia proprio di linux e non mi riesce di comprendere.

Spero, inoltre, che Ti sia sufficiente il papiro sopra è una miserrima parte di quello che ci sarebbe da dire per il Tuo "come proseguire"

Ciao