Topic: Lavorare con variabili o con widgets?  (Letto 269 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline eciap

  • python unicellularis
  • *
  • Post: 13
  • Punti reputazione: 0
    • Mostra profilo
Lavorare con variabili o con widgets?
« il: Maggio 14, 2020, 00:08 »
Sicuramente il titolo fa abbastanza schifo ma non riesco a trovare un modo per esprimere meglio il concetto in modo sintetico.

La domanda e' in realta' molto semplice.
Poniamo che io abbia due listbox e voglia spostare un elemento da una  all'altra, ho due opzioni:
  • Assegnare il contenuto di una listbox ad una lvariabile (lista) e poi lavorare con quella
  • lavorare con il dato prendendolo direttamente dal widget.

In altri termini e' questo il modo migliore di procedere?

    def add(self):
        if self.listbox1.curselection():
            current = self.listbox1.get(self.listbox1.curselection())
            if current not in self.listbox2.get(0, "end"):
                self.listbox2.insert(END, current)
                self.listbox1.delete(self.listbox1.curselection())



Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Lavorare con variabili o con widgets?
« Risposta #1 il: Maggio 14, 2020, 08:10 »
Ciao ... Ti funziona "quel" codice?

Chiedo perché dalla documentazione, curselection() restituisce una lista di indici, e una listbox può avere selezionati più elementi.

Suppongo, ma è solo un pensiero, non posso sperimentare "al volo" in questo momento, che a Te interessi un "item" corrispondente ad un indice non l'indice stesso, giusto? Cosa succede se vi sono più indici nella selezione?
Personalmente, interagirei con una currselection() estraendomi l'item corrispondente ad ogni indice restituito e verificando, singolarmente, se esso sia presente o meno nella seconda lista, agendo di conseguenza.

Comunque non esistono modi "migliori", solo metodi efficaci ad ottenere il risultato voluto.

Offline eciap

  • python unicellularis
  • *
  • Post: 13
  • Punti reputazione: 0
    • Mostra profilo
Re:Lavorare con variabili o con widgets?
« Risposta #2 il: Maggio 14, 2020, 09:22 »
Ciao nuzzopippo,grazie per la risposta!  il codice funziona. Ammetto che non avevo provato a selezionare piu' di un elemento.
Ho appena provato ma non riesco ad evidenziarne piu' di uno.
Giusto per chiarezza, in realta' mi sono sbagliato: sto usando due scrolledlistbox (utilizzo PAGE per generare la GUI) non delle semplici listbox.

In ogni caso, mi chiedevo se fosse questo il sistema piu' efficace perche' ho ad esempio un metodo "aggiungi tutti" scritto in questo modo:

def add_al(self):
    a=(self.listbox1.get(0, "end"))
    if a:
        for i in range(len(a)):
            if a not in self.listbox2.get(0, "end"):
                self.listbox2.insert(END, a[i])
        self.listbox1.delete(0, "end")


Se per assurdo nella listbox1 ci fossero 100K elementi, non diventerebbe inefficiente usare  self.listbox2.get(0, "end") in un for?
Mi chiedo se non sia meglio estrarre il contenuto della listbox2 all'inizio, darlo ad una variabile e poi fare una cosa di questo tipo:

def add_al(self):
    a=(self.listbox1.get(0, "end"))
    content = self.listbox2.get(0, "end")
    if a:
        for i in range(len(a)):
            if a[i] not in content:
                self.listbox2.insert(END, a[i])
        self.listbox1.delete(0, "end")


E' questo un sistema piu' efficiente oppure non cambia nulla?

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 381
  • Punti reputazione: 0
    • Mostra profilo
Re:Lavorare con variabili o con widgets?
« Risposta #3 il: Maggio 14, 2020, 16:49 »
Giusto per chiarezza, in realta' mi sono sbagliato: sto usando due scrolledlistbox (utilizzo PAGE per generare la GUI) non delle semplici listbox.
Purtoppo, non conosco tanto PAGE quanto le "scrolledlistbox", inesistenti in tkinter, è forse un qualche GUI-Designer? Personalmente non ne uso, preferisco di gran lunga codificarmele direttamente le finestre.

Nel caso sia così, suppongo che le scrolledlistbox non siano altro che dei frame con una listbox e delle scrollbar collegate, in linea di massima dovrebbe funzionare come una normale listbox, la modalità di selezione selezione si dovrebbe controllare tramite la proprietà "selectmode"

In ogni caso, mi chiedevo se fosse questo il sistema piu' efficace perche' ho ad esempio un metodo "aggiungi tutti" scritto in questo modo:

def add_al(self):
    a=(self.listbox1.get(0, "end"))
    if a:
        for i in range(len(a)):
            if a not in self.listbox2.get(0, "end"):
                self.listbox2.insert(END, a[i])
        self.listbox1.delete(0, "end")

Suppongo che la funzione "add_al" si applichi all'intero corpo degli item, a questo punto perché non glieli passi tutti di blocco?

Se per assurdo nella listbox1 ci fossero 100K elementi, non diventerebbe inefficiente usare  self.listbox2.get(0, "end") in un for?
Si, è decisamente un "ragionare per assurdo", si parla di interfacce utente, già 100 elementi in una lista sono "troppi", con 100K di elementi, il primo che vede la Tua applicazione ti manda a quel paese, temo, se giungi già ad un centinaio di elementi è bene che ti metti a pensare ad opzioni di selezione dei dati.

Per altro, dato che cancelli gli item dalla tabella di origine, perché effettui una ricerca di "presenza" della aggiunta nella seconda lista? potrai senz'altro controllare i dati prima ancora di caricarli nella prima lista ed escludere le ripetizioni (i "set" dicono niente?).

Comunque, Ti ho preparato un piccolo esempio tkinter fatto a manina con le normali listbox, per dimostrarti un "caricamento massivo", con cancellazione della lista di destinazione, ed il problema di "selezione multipla", guarda la definizione della lista (il "selectmode=tk.MULTIPLE")  e la funzione "on_sel", come impostata attualmente un singolo elemento lo stampa, ma se ne selezioni due ...
Citazione
_tkinter.TclError: bad listbox index "1 2": must be active, anchor, end, @x,y, or a number

Per vederla funzionare in selezione multipla commenta le prime due righe di on_sel e de-commenta le successive tre
L'esempio :
import tkinter as tk

a_list = [['uno', 'due', 'tre'],
          ['quattro', 'cinque', 'sei'],
          ['sette', 'otto', 'nove']
          ]

class TestForm(tk.Tk):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.lst = tk.Listbox(self, selectmode=tk.MULTIPLE)
        self.lst.pack(expand=True, fill='both')
        button_pnl = tk.Frame(self)
        button_pnl.pack(expand=True, fill='x')
        btn1 = tk.Button(button_pnl, text="Inserisci", command=self.on_insert)
        btn1.pack(side='left', expand=True, fill='x')
        btn2 = tk.Button(button_pnl, text="Selezione", command=self.on_sel)
        btn2.pack(side='left', expand=True, fill='x')
        self.index = 0

    def on_insert(self):
        self.lst.delete(0, tk.END)
        self.lst.insert(tk.END, *a_list[self.index])
        self.index += 1
        if self.index >= len(a_list):
            self.index = 0
           

    def on_sel(self):
        curr_item = self.lst.get(self.lst.curselection())
        print(curr_item)
        #indexes = self.lst.curselection()
        #for ind in indexes:
        #    print(self.lst.get(ind))

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

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 3.154
  • Punti reputazione: 9
    • Mostra profilo
Re:Lavorare con variabili o con widgets?
« Risposta #4 il: Maggio 15, 2020, 23:12 »
>> Se per assurdo nella listbox1 ci fossero 100K elementi
> Si, è decisamente un "ragionare per assurdo", si parla di interfacce utente...

Questa è una *ottima* osservazione, e non solo per il caso specifico delle gui.
Ragionando per assurdo, potrei chiedere che il codice dell'OP serva anche a guidare un'astronave per Marte: e quindi?
Il design del codice non si fa ragionando per assurdo. Si capisce quale scenario il codice deve supportare, e si mira a quello. Se poi si sbaglia, ovviamente, sono c***.
In questo caso specifico, nunzio ha ragione: se il tuo scenario fosse di offrire all'utente la scelta tra 100k elementi, allora NON sarebbe più comunque un lavoro per una lista... ti servirebbe qualcosa di più sofisticato, una paginazione di qualche tipo per esempio; meglio ancora, un motore di ricerca. 100k dati si cercano e si filtrano, non si sfogliano. E' proprio uno scenario diverso.


Detto questo, il problema di quell'approccio non è l'efficienza (per quello, non c'è mai problema in questi casi... si può ridisegnare tutta la gui 100 volte e l'utente non se ne accorge nemmeno). Il problema, ho paura, è la *robustezza* di quel codice. Sorry ma non si può andare avanti a martellare il codice della gui pretendendo che faccia anche da model... prima o poi qualcosa si sfascia.

Questo è uno di quei momenti in cui uno dovrebbe mettere da parte Tkinter per una settimana, e mettersi a pensare seriamente di che tipo di oggetto ha bisogno, e scriversi un *model* robusto. Deve esserci una classe che ha due liste interne, "destra" e "sinistra", e offre la possibilità di spostare elementi da una parte all'altra. Diciamo così, per fissare le idee:


class ReArrangeList:
    def __init__(self, right, left):  # due liste
        self.right = right
        self.left = left

    def move(self, index, from='left'):
        # sposta l'elemento con indice "index" da "from" all'altra lista

    # ...

Poi uno comincia a ragionare su questo model e già gli vengono in mente degli ostacoli che bisognerebbe capire. Può servire un metodo che sposta gli elementi a partire non dalla loro posizione (index) ma dal loro nome? Ma allora si può ammettere che le liste abbiano più elementi con lo stesso nome? E l'ordinamento conta? Va preservato? E in che modo? E aggiungere elementi esterni a una delle due liste, si può? E rimuoverli definitivamente? Allora bisogna aggiungere altri metodi che facciano questo.
Poi uno magari si chiede se davvero due liste sono la cosa migliore per modellare questo tipo di scenario... mi viene in mente che una lista di coppie potrebbe essere interessante: [['mela', 'L'], ['pera', 'R'], ['banana', 'R'], ...] dove L e R indicano la posizione dell'elemento... a questo punto per cambiare posizione basterebbe cambiare una L in R e viceversa... questo potrebbe essere interessante per preservare facilmente un ordine alfabetico, per dire... o se si volesse generalizzare al caso di tre o più liste...

Insomma, ci sarebbe parecchio lavoro da fare su questo model. E questo aiuterebbe a chiarirsi anche un po' le idee su quello che davvero si cerca di fare, e sulle possibili trappole. Bisognerebbe arrivare (ripeto, prendendosi una settimana di tempo senza essere ansiosi di arrivare a una soluzione da martellare dentro le Tk...) a un model scritto in modo chiaro e soprattutto *testato*, che si può manovrare senza nessuna gui.

A questo punto aggiungere la gui sarebbe semplice, ma soprattutto non ci sarebbero possibilità di errori. Tutto ciò che bisogna fare dal "lato gui" sono esattamente due operazioni:
- raccogliere gli elementi selezionati in ciascuna lista
- ridisegnare entrambe le liste partendo da zero.
Il model si occuperebbe di fare le operazioni di spostamento necessarie, e alla fine direbbe semplicemente alla gui qual è la nuova lista da disegnare in ciascuno dei due box.