Topic: visualizzare dati intabellati  (Letto 55 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline Giuse

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
visualizzare dati intabellati
« il: Novembre 28, 2019, 15:18 »
Vorrei creare una tabella dove visualizzo dei dati provenienti da un db MySql.
Il collegamento e la lettura dei dati da db è andata a buonfine, ma adesso vorrei intabellarli e visualizzarli dentro una finestra di tkinter.

Quindi sull'asse x i nomi dei campi o le label, mentre sull'asse y campo per campo il contenuto di questo.
Premesso che ho cercato anche in inglese, ma non sono riuscito a trovare nessun articolo o esempio che parlasse di ciò.
Forse non è possibile farlo in tkinter?
Cortesemente mi date una direzione, un link o qualche spunto/consiglio dove potere studiare.

Grazie

Offline nuzzopippo

  • python habilis
  • **
  • Post: 90
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #1 il: Novembre 29, 2019, 13:00 »
Vorrei creare una tabella dove visualizzo dei dati provenienti da un db MySql.
Il collegamento e la lettura dei dati da db è andata a buonfine, ma adesso vorrei intabellarli e visualizzarli dentro una finestra di tkinter...

Ciao @Giuse

La rappresentazione "tabellare" dei dati è un "qualcosa" che ho cercato a mia volta in tkinter : mi sembra proprio che non ci sia.

In ogni caso, non è poi troppo difficile realizzarne una, più o meno complessa, sfruttando il gestore di geometria "grid", giusto per giocarci un po' ieri sera mi son divertito a realizzare un esempio minimale che Ti propongo come spunto

# -*- coding: utf-8 -*-

import tkinter as tk

class TableModel():
    '''
Modello base per contenimento dati, intestazioni e dimensionamento per
un oggetto "Table".
'''
   
    def __init__(self, geometry=None, headers=None, data=None):
        '''
"Costruttore" del modello di tabella.

Parametri :
    geometry - dizionario contenente le chiavi :
                minsizes : lista di interi, dimensioni minime delle colonne
                weights : lista di interi definente il proporzionamento al
                           resize, 0 = colonna fissa
    headers  - lista intestazioni delle colonne
    data     . lista di liste : valori nelle colonne

Note : mi piacerebbe aggiungere un "maxsize" alle colonne ma non mi sembra supportato

'''
        self.geometry = geometry
        self.headers = headers
        self.data = data

       
class Table(tk.Frame):
    '''
Elementare "Tabella" dati, crea una singola riga di etichette di intestazione
ed n righe di entry per i dati.
'''
    def __init__(self, master=None, model=None):
        '''
"Costruttore" della tabella

parametri :
    master : widget/finestra proprietaria
    model  : modello della tabella
'''
        super().__init__(master)
        # verifica il modello di tabella, se incoerente si auto-distrugge
        if not model.geometry or not model.headers:
            self.destroy()
            return
        self.model = model
        self.__populate()

    def __populate(self):
        ''' Popola la tabella con i dati memorizzati nel modello. '''
        # etichette
        self.labels = []
        h_bg = tk.Frame(self)
        h_bg.grid(row=0, column=0, sticky='ew', padx=0, pady=0)
        for i in range(len(self.model.headers)):
            lbl = tk.Label(h_bg)
            lbl.configure(text=self.model.headers[i], relief='raised')
            lbl.grid(row=0, column=i, sticky='ew')
            self.labels.append(lbl)
        # definizione sfondo delle celle dati
        cells_bg = tk.Frame(self)
        cells_bg.grid(row=1, column=0, sticky='nsew', padx=0, pady=0)
        # definizione delle celle dati
        self.cells = []
        for i in range(len(self.model.data)):
            row_cells = []
            for j in range(len(self.model.headers)):
                c = tk.Entry(cells_bg, relief='ridge', bg='white')
                c.grid(row=i, column=j, sticky='ew')
                c.insert(tk.END, self.model.data[i][j])
                c.bind('<FocusIn>', self.__cell_focus)
                row_cells.append(c)
            self.cells.append(row_cells)
        # scroolbar ... poi

        # proporzionamento colonne
        for i in range(len(self.model.geometry['minsizes'])):
            if self.model.geometry['minsizes'][i]:
                h_bg.grid_columnconfigure(i, minsize=self.model.geometry['minsizes'][i])
                cells_bg.grid_columnconfigure(i, minsize=self.model.geometry['minsizes'][i])
            if self.model.geometry['weights'][i]:
                h_bg.grid_columnconfigure(i, weight=self.model.geometry['weights'][i])
                cells_bg.grid_columnconfigure(i, weight=self.model.geometry['weights'][i])
        # definizione del "peso" di riga/colonna
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        # definizione riga corrente
        self.__curr_row = 0

    def __cell_focus(self, evt):
        row = evt.widget.grid_info()['row']
        if row != self.__curr_row:
            # "decolora" la vecchia riga selezionata
            for c in self.cells[self.__curr_row]:
                c.configure(bg='white')
            self.__curr_row = row
            # "colora" la nuova riga selezionata
            for c in self.cells[self.__curr_row]:
                c.configure(bg='#ffffc0')
           

# un primo test "al volo"
if __name__ == '__main__':
    # preparo le impostazioni per il "modello"
    g = {'minsizes' : [0, 0, 0],
         'weights'  : [1, 1, 1]
         }
    h = ['Cognome', 'Nome', 'data nascita']
    data = [['De Paperoni', 'Paperon', '1901-01-01'],
            ['Paperino', 'Paolino', '1950-11-01'],
            ['Paperone', 'Gastone', '1950-01-06'],
            ['Mouse', 'Miky', '1900-02-29'],
            ['Mouse', 'Minnie', '1910-01-01'],
            ['Mucca', 'Clarabella', '1923-12-13']
            ]
    model = TableModel(g, h, data)
    app = tk.Tk()
    f = tk.Frame(app)
    f.grid(row=0, column=0, sticky='nsew')
    t = Table(f, model)
    t.grid(row=0, column=0, sticky='nsew')
    b = tk.Button(f, text='Chiudi', command=app.destroy)
    b.grid(row=1, column=0, sticky='ew')
    f.grid_columnconfigure(0, weight=1)
    f.grid_rowconfigure(0, weight=1)
    app.grid_columnconfigure(0, weight=1)
    app.grid_rowconfigure(0, weight=1)
    app.mainloop()

Non è molto curato, ma testato poco fa sembra andare benino, per lo meno non da errori ed "evidenzia" la riga selezionata.

Ho ipotizzato un "modello di tabella" per una ipotesi di sviluppo futuro di caratteristiche del giocattolo. Rendere qualcosa di migliore motrebbe essere un interessante esercizio di studio per apprendisti tipo me e, suppongo, altri in circolazione eventualmente interessati.

Ciao :)

Offline Giuse

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #2 il: Novembre 29, 2019, 13:45 »
Ti ringrazio per il codice, in effetti la mia richiesta è solo per studio, sono interessato a python e a quelle applicazioni che servono a registrare dati, editarli o cancellarli, quelli che ingergo vengono chiamati CRUD.
  • Create (creare i dati)
  • Read o Retrieve (leggere i dati)
  • Update (aggiornare i dati)
  • Delete o Destroy (eliminare i dati)
Per adesso sto procedendo per step, quindi mi sono concentrato sul collegamento ad un database che risiede on line e ci sono riuscito, quindi andando avanti volevo capire se potevo avere una view complessiva dei dati. L'intento è quello di scrivere un applicativo dove inserire i titoli della mia libreria, potendoli poi ricercare per autore genere editore etc.. ma per quest'ultima parte penso di essere ancora molto lontano.


Il mio database di riferimento è MySql che è quello che conosco meglio, mentre la scelta del db on line anzichè in un file sul proprio PC, magari con MySqlLite, era semplicemente per studio...ho visto tante guide su come lavorare con un file db in locale e quasi niente su come lavorare su un db in remoto.

Nei prossimi giorni o meglio nel fine settimana ci giocherò un pò e ti faccio sapere.

Offline nuzzopippo

  • python habilis
  • **
  • Post: 90
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #3 il: Novembre 29, 2019, 15:48 »
Ti ringrazio per il codice, in effetti la mia richiesta è solo per studio, sono interessato a python e a quelle applicazioni che servono a registrare dati, editarli o cancellarli, quelli che ingergo vengono chiamati CRUD. ...

Nei prossimi giorni o meglio nel fine settimana ci giocherò un pò e ti faccio sapere.

Capisco, personalmente preferisco operare in LAN locale e su PostgreSQL, ma anche a me piace adoperare la visualizzazione tabellare, prevalentemente per la consultazzione/selezione tra i records da manipolare.

In merito al mio precedente post, ho riscontrato che viene meglio non mischiare label ed entry, una resa migliore si ottiene con le entry anche per le intestazioni di colonna, ovviamente poste in sola lettura. Considera questa modifica nrò popolare le etichette, imho la resa visiva è migliore.


    def __populate(self):
        ''' Popola la tabella con i dati memorizzati nel modello. '''
        # etichette
        self.labels = []
        h_bg = tk.Frame(self)
        h_bg.grid(row=0, column=0, sticky='ew', padx=0, pady=0)
        for i in range(len(self.model.headers)):
            lbl = tk.Entry(h_bg, justify='center', bg='gray91')
            lbl.configure(relief='raised')
            lbl.insert(tk.END, self.model.headers[i])
            lbl.configure(state='readonly')
            lbl.grid(row=0, column=i, sticky='ew')
            self.labels.append(lbl)


Ciao

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.886
  • Punti reputazione: 9
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #4 il: Novembre 29, 2019, 16:07 »
Purtroppo non è così semplice.
Se stai facendo questo per motivi di studio, ciò che devi studiare è MCV, e non c'è davvero altra soluzione che capire e digerire MCV.

Ora, l'esempio che ti è stato fatto è abbastanza istruttivo, nel senso che è una pessima applicazione di MCV, e quindi si può imparare studiando i suoi problemi.
(E no, giusto per prevenire la solita obiezione: non è che "è solo uno schizzo", è proprio una questione delle fondamenta. Io posso fare "solo uno schizzo" del disegno di un corpo umano, ma se ci metto cinque braccia e sei occhi, non è "solo uno schizzo", è che che non sto seguendo l'anatomia umana).

Non c'è nessuna separazione tra "Model" e "View". In particolare nota che il "Model" contiene anche delle caratteristiche che logicamente appartengono alla "View"... ma curiosamente solo alcune di queste caratteristiche sono nel Model, altre restano di compentenza del codice della View... in base a quale criterio?
Ma una cosa più istruttiva da notare è che il Model si esprime "convenientemente" nello stesso linguaggio della View... se guardi alla riga 85-85, vedrai che la View si limita a ricevere i valori del Model tali e quali. Ma questo vuol dire che Model e View sono interdipendenti ("accoppiati", "coupled" come si dice in gergo OOP). In realtà il Model dovrebbe esprimersi in un modo più astratto, e lasciare alla View il compito di decidere come presentare concretamente le indicazioni che dà il Model. Per esempio, il Model potrebbe avere il compito di segnalare che uno dei campi è "il più importante" (è un po' discutibile... ma insomma poniamo). Potrebbe farlo con un flag, immaginiamo. Poi però la View avrebbe il diritto di decidere *come* presentare questa informazione. Una View potrebbe decidere di fare più larga la colonna corrispondente, un'altra View potrebbe colorare quella colonna di rosso, un'altra ancora potrebbe ignorare del tutto questa informazione. Nell'esempio che vedi, d'altra parte, il Model è concepito solo per "servire" quella particolare View, e nessun altra possibile. Metti, per esempio, che adesso ti viene in mente di cambiare la View: non vuoi più vedere i dati in formato tabulare, ma come "schede" con dei pulsanti di navigazione ("successiva", "precedente"... hai presente, no?). Che te ne fai, a questo punto delle informazioni del Model specifiche sulla geometria delle colonne? Ovviamente le scarteresti, ma capisci che a questo punto il Model contiene "un po' più informazioni" se decidi di usare un certo tipo di View, e "un po' meno informazioni" se decidi di usare un altro tipo di View. La verità è che il Model non dovrebbe contenere istruzioni specifiche per nessun tipo di View. Il Model dovrebbe solo essere una rappresentazione astratta della "situazione" (dei dati, ma non solo e non necessariamente).

Un'altra cosa affascinante da vedere, è come il Model "sorvola con eleganza" (!) il problema dei tipi dei dati, semplicemente falsificando i tipi dei dati in modo (di nuovo) da parlare lo stesso linguaggio della View. Quella specifica View ha bisogno di stringhe? Benissimo, allora già nel Model memorizziamo tutto quanto come stringhe, così poi passiamo stringhe "alla cieca" e va tutto bene. Ma vedi che lì per esempio c'è anche una data... non importa! tutto dev'essere una stringa.
Immagina di creare un altro campo di tipo "Si/No", per esempio "Online in questo momento" (o qualcosa del genere, va a sapere). Ora, avrebbe senso che nel Model questo fosse un tipo Bool, ma se vuoi puoi anche utilizzare qualcos'altro... diciamo che usi una stringa che può essere "Y" o "N" (questo è molto più fragile che usare un vero Bool, ma pazienza). Ora, in quella specifica View tu potresti anche cavartela... il Model ha una stringa, la View ti farà vedere una colonna con "Y" o "N". Ma un'altra View potrebbe decidere di farti vedere una casella con la spunta... Un'altra View potrebbe colorare di rosso o di verde la cella della tabella... E così via.
Se ti poni il problema in modo generale, capirai ben presto che occorre ancora una volta "disaccoppiare" il Model dalla View. Il Model deve avere i dati con il tipo "giusto" (Bool per i vero/falso, datetime.date per le date, quello che vuoi). La View deve rappresentare visivamente questi tipi nel modo che preferisce. In mezzo deve esserci un componente che "traduce" i tipi del Model, e li passa alla View nella forma che lei preferisce. Questo è uno dei compiti possibili per un Controller, nel gergo MCV.

E si potrebbe andare avanti... Ma torniamo alla tua idea. Le applicazioni CRUD sono l'abc di un certo tipo di "informatica pratica"... e sono anche il terreno di applicazione più concreto per studiare MCV. Non puoi fare un CRUD senza MCV, sarebbe una faticaccia tremenda.
Il problema che ti è subito chiaro nel momento in cui devi metterti a fare un CRUD, è che non ti basta semplicemente un meccanismo che "travasa" i dati di un Model dentro una View, come fa (non benissimo, come visto) l'esempio che ti è stato proposto. Quello che ti serve in un CRUD è un meccanismo per cui, quando modifichi un valore nella View, il Model si aggiorna di conseguenza. E viceversa: quando modifichi un valore in un Model, la View si aggiorna di conseguenza. View e Model devono "parlarsi". A questo soprattutto serve MCV: a mantenere separate le competenze tra le parti, in modo che la comunicazione possa avvenire secondo protocolli precisi e nessuno pesti i piedi a nessun altro.

La prima cosa che devi fare, è non lasciarti confondere le idee dagli oggetti concreti con cui lavori. Non importa quale database usi. Puoi anche non usare nessun database, se è per questo. Quello che conta è il principio. Quando hai capito quello, puoi aggiungere tutti i dettagli che vuoi.
La seconda cosa che devi capire, è che devi imparare a non strafare: parti dagli esempi più semplici, e vai avanti per gradi. Una tabella è già un esempio troppo complicato da programmare. Capisco che tu pensi a un database, a qualche esempio concreto, e ti viene l'acquolina in bocca e vorresti subito fare un bel gestionale di magazzino (mannaggia, ma come fanno in Delphi a fare tutte 'ste cose senza starci troppo a pensare? E... infatti... e poi si vede come li fanno certi programmi... Ma vabbè, lasciamo stare la polemica).

Invece ti propongo un esercizio più semplice. Hai due cose: un Model e una View.
Il Model è un semplice valore numerico. Davvero, tutto qui. Un numero e niente di più. Diciamo che è una variabile (chiamiamola "model"... che fantasia) che all'inizio vale "42". Quindi hai:
model = 42 # questo è il model!!!
Dall'altra parte ha la View. Questa è una finestra di testo con esattamente 3 elementi (non importa dove li metti... fa un po' come vuoi):
- una casella di testo
- un pulsante "salva"
- un pulsante "resetta model"

Nella casella di testo ci deve essere visualizzato il contenuto del Model (quindi, all'inizio, "42"... ricordati che è una casella di testo, quindi dovrà contenere una stringa, mentre il Model maneggia dei numeri... ma vabbè, vedi tu come fare).
Azione A: quando cambi il valore nella casella di testo e poi fai clic su "salva", il valore nel Model deve aggiornarsi di conseguenza.
Azione B: quando fai clic su "resetta model", il valore del Model torna a 42. Diciamo che questo simula qualunque intervento che potrebbe succedere sul Model, indipendente dalla tua View... qualcosa che accade. Quando questo succede, la casella di testo deve aggiornarsi di conseguenza.

Attenzione! Non sto dicendo che "Azione B" vuol dire "cambia il model a 42, e cambia il valore della casella di testo a 42". Così sarebbe banale. Invece dico che alla pressione del pulsante il Model deve cambiare, e quando cambia deve notificare alla View che il suo stato è cambiato.
Attenzione! Non sto dicendo che "Azione A" vuol dire "leggi il valore della casella di testo e sbattilo nel Model". Così sarebbe banale. Invece dico che alla pressione del pulsante la View deve notificare al Model che il suo stato è cambiato, e ovviamente il Model deve recepire questo messaggio.

Se provi a fare questo esercizio, ti accorgerai che in realtà è molto complicato...

(PS: in realtà questo esercizio andrebbe fatto prima come puro esercizio di OOP, senza nessuna GUI...)

Offline nuzzopippo

  • python habilis
  • **
  • Post: 90
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #5 il: Novembre 29, 2019, 16:33 »
Ciao @RicPol

Purtroppo non è così semplice. ...

Intervento molto interessante, sei da leggere :)

comunque,  riguardo
(E no, giusto per prevenire la solita obiezione: non è che "è solo uno schizzo", è proprio una questione delle fondamenta ...

Non avevo intenzione di toccare alcuna fondamenta (MCV? Quale arcano esotismo per me!), solo dare un esempio di "assemblaggio" buttato li. Per il resto, nessuna pretesa.

Spero continuamente in interventi di qualità come il Tuo in questo post, a me danno almeno idea su ciò che ignoro, se il discorso continuerà a svilupparsi leggerò con interesse.

Ciao :)

Offline Giuse

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #6 il: Novembre 29, 2019, 16:43 »
@nuzzopippo grazie ancora

@ricpol Capisco e ti ringrazio, procedo per passi, se riesco il gestionale lo finirò sine die, per adesso volevo solo visualizzare i dati intabellati provenienti da un db. Si certo che ho l'acquolina in bocca, ma sono consapevole delle mie limitazioni, che forse con impegno e studio,passo dopo passo, arriverò a capire. Non ho fretta, questa cosa la faccio solo per soddisfazione personale, non ho scadenze e ne impegni. Di quello che hai scritto non ho capito nulla, perdona la franchezza, ma a 53 anni suonati potrebbe essere normale. MVC? certo so cosa è separazione dei dati modello-vista-controllo. Ne sento parlare da anni, mi occupo di grafica per il web, ma non saprei come procedere. Nel voler imparare python, scelto perchè non ho altre esperienze di programmazione, e a quanto si dice è un linguaggio molto più semplice di C C++ e C#.......Quindi sono all'ABC, e per usare il tuo stesso esempio, se per adesso disegno un omino un pò sbilenco mi perdonerai, farò meglio con l'esperienza.

Offline nuzzopippo

  • python habilis
  • **
  • Post: 90
  • Punti reputazione: 0
    • Mostra profilo
Re:visualizzare dati intabellati
« Risposta #7 il: Novembre 29, 2019, 18:22 »
Riletto, e cercato di meditare, sul post di @RicPol ... mi rendo conto di aver fatto un "qualcosa" di quelle che fanno andare il latte alle ginocchia agli esperti, me ne dolgo

Ma una cosa più istruttiva da notare è che il Model si esprime "convenientemente" nello stesso linguaggio della View... se guardi alla riga 85-85, vedrai che la View si limita a ricevere i valori del Model tali e quali. Ma questo vuol dire che Model e View sono interdipendenti ("accoppiati", "coupled" come si dice in gergo OOP). In realtà il Model dovrebbe esprimersi in un modo più astratto, e lasciare alla View il compito ...

In effetti la classe TableModel da me definita è pensata proprio quale ambito di definizione di alcune caratteristiche della visualizzazione di cui deve tenere la "tabella", i dati, nel caso, sono solo un elemento di comodo esemplificativo. La immaginavo destinataria anche di ulteriori caratteristiche, tipo colrazione alternata delle righe, colori di background e foreground, fonts, etc.

Che la sua "denominazione" potesse individuarla quale elemento di un contesto MCV non mi aveva sfiorato neppure vagamente.

Ciò nulla toglie al valore delle critiche ed indicazioni espresse, mi riservo, ammesso riesca a comprendere l'argomento, di tentare una futura implementazione secondo il paradigma MCV.

Grazie delle osservazioni.