Forum >> Programmazione Python >> GUI >> [tkinter] drag and drop, ne parliamo?

Pagina: 1 2 Avanti

I miei saluti.

Sto, al momento, studiando come realizzare un "drag and drop" in tkinter, che non fornisce (a quanto ne so) una interfaccia nativa in merito, l'idea di fondo è realizzare dei widget con tale capacità, per scambio di dati tra diverse finestre di una stessa applicazione tramite trascinamento.
Con l'aiuto della documentazione, principalmente di winfo mi è riuscito di realizzare una bozza di base funzionante, sub-classando una tkinter.Entry, scambiando testo tra due oggetti di detto sub-classamento posti in finestre diverse, per trascinamento con il tasto destro del mouse premuto.

... però, anche se funziona, non sono pienamente convinto della metodologia adottata, al momento, al rilascio del pulsante destro del mouse l'oggetto su cui si è avviato il "drag" esamina tutta la gerarchia dei parent sino a trovare la main-window (root per gli amici) e utilizza questa per estrarre tra i widget figli quello su cui è stato rilasciato il pulsante, se quest'ultimo ha uno specifico attributo ("dragable") vi viene aggiunto il testo del widget "draggato".

Come detto, è solo un abbozzo preliminare di studio della funzionalità, prima di integrare il concetto in vari elementi (penso almeno a treeview ed entry che si scambino oggetti) non mi dispiacerebbero consigli circa approcci migliori e/o accorgimenti da tenere.
Se qualcuno vuol provare il codice sin ora implementato (106 righe) è in allegato:
Fatti non foste a viver come bruti...
Allegati
Ciao caro, interessante, non ho mai affrontato una cosa simile, anche perché mi occupo di altro di solito.

Lo provo appena ho un lieve allineamento planetare, voglio vedere cosa hai realizzato.

Cya
Daniele aka Palmux said @ 2024-11-15 23:13:11:
Ciao caro, interessante, non ho mai affrontato una cosa simile, anche perché mi occupo di altro di solito.
Lo provo appena ho un lieve allineamento planetare, voglio vedere cosa hai realizzato.
Grazie per la Tua attenzione.
Anche per me è una "prima volta", il altri frangenti ho avuto disponibili interfacce native per tali operazioni ... a dirla tutta, ci sarebbe anche per tkinter una interfaccia di tal genere ma una istruzione tipo
root = TkinterDnD.Tk()  # notice - use this instead of tk.Tk()
mi è così sgradita da rifiutare la minestra e cucinamela da me.

Allo stato è realizzato solo il prototipo funzionale essenziale, privo anche della visualizzazione del trascinamento (lo fornirò appena implementato), è una base su cui vorrei ragionare al fine di impostare una "tecnica" da adottare nelle mie implementazioni ... ci voglio riflettere su al fine di limare detta tecnica ed in tal senso ogni eventuale feedback è graditissimo.

:)

Edit : cosa, la visualizzazione del trascinamento, molto semplice da implementare, ho perso più tempo a selezionare l'icona che a creare una classe "DragWin" che disegna la finestra da trascinare, allego il file modificato, mostra l'effetto in maniera più soddisfacente


--- Ultima modifica di nuzzopippo in data 2024-11-16 08:38:30 ---
Fatti non foste a viver come bruti...
Allegati
Strano, ho effettuato un "Edit" indicando che inviavo una versione di codice con integrata la visualizzazione del trascinamento, il file è stato allegato ma l'edit non è stato riportato.
Fatti non foste a viver come bruti...
Strano, ho effettuato un "Edit" indicando che inviavo una versione di codice con integrata la visualizzazione del trascinamento, il file è stato allegato ma l'edit non è stato riportato.
Comunque è meglio se metti il codice su Pastebin o similari, molto più "maneggiabile".

Cya
Daniele aka Palmux said @ 2024-11-16 11:13:19:
Comunque è meglio se metti il codice su Pastebin o similari, molto più "maneggiabile".
Ok ci ho provato, ho visto che si può incollare del codice ed ho inserito qui il codice di un ulteriore step di prova, nel quale sto provando ad astrarre un pochino il concetto definendo una classe che riceve un oggetto (ovviamente un widget tkinter), gli aggiunge delle proprietà e metodi e poi usa quell'oggetto per azioni "drag and drop".




L'idea è di usare la classe come base per poi definire ulteriori classi "specializzate" a fini specifici ereditando la parte "drag" così come è, utilizzando il polimorfismo per il "drop" adattando il solo metodo "drop_data" della classe "DragDropElement" ai fini specifici occorrenti per un dato compito ... ecco, qui pareri esperti non mi farebbero male, principalmente sulla maniera utilizzata per passare il metodo "drop_data" all'oggetto riferito :D

Per altro, ho definito delle proprietà di controllo "_dragable" e "_dropable" per il controllo dello stato di attivazione delle capacità implementate oltre che una proprietà "tipe" per la definizione del tipo di dato che viene scambiato.




Qualcuno ha qualcosa da suggerire?

Fatti non foste a viver come bruti...
Allora ... dal punto di vista "operativo" ritengo di aver definito dei processi abbastanza bene funzionali, però c'è certamente qualche concetto che non ho capito.




Per "astrarre" il discorso ho definito, appunto, una classe astratta, ove nel corpo della classe ho implementato staticamente i metodi di visualizzazione della "transizione", lasciando astratti i metodi per lo scambio dei dati tra widget, giusto per spiegarmi meglio, stralcio di codice della classe astratta:

    def __init__(self, obj: callable, tipe: str='text', *args, **kwargs):
        self.obj = obj
        self._dragable = True
        self._dropable = True
        self.obj.dropable = self.dropable
        self.obj.dragable = self.dragable
        self.tipe = tipe
        self.obj.tipe = self.tipe
        self.obj.drop_data = self.drop_data
        self.obj.drag_data = self.drag_data
        self.mwin = None
        self.obj.bind("<Button-3>", self._on_press)
        self.obj.bind("<B3-Motion>", self._on_motion)
        self.obj.bind("<ButtonRelease-3>", self._on_release)

    @property
    def dragable(self) -> bool:
        return self._dragable
    @dragable.setter
    def dragable(self, value: bool) -> None:
        self._dragable = value
    
    @property
    def dropable(self) -> bool:
        return self._dropable
    @dropable.setter
    def dropable(self, value: bool) -> None:
        self._dropable = value

    def _on_press(self, evt: callable) -> None:
        print(self._dragable, self.obj.dragable)
        if not self.obj.dragable: return
        x,y = self.obj.winfo_pointerxy()
        self.mwin = DragWin(self.obj)
        self.mwin.geometry(f'+{x}+{y}')
...
    @abc.abstractmethod
    def drop_data(self, descriptor, data: any) -> None:
        pass

    @abc.abstractmethod
    def drag_data(self) -> any:
        pass
ed implementato i metodi astratti nei sub-classamenti "reali" che sono andato a definire per specifiche problematiche, sotto un esempio di definizione per una ttk.Treeview che nel test mi sposta un record completo di un elenco di comuni :

class DADTreeComuni(DAD.DragDropElement):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def drop_data(self, descriptor, data: any) -> None:
        if not self.obj.dropable: return
        if descriptor != self.obj.tipe: return
        self.obj.set_comune(data)

    def drag_data(self) -> str:
        return self.obj.get_element()

ora, noterete quel "print(self._dragable, self.obj.dragable)", è un test di controllo che ho inserito per controllare cosa avviene per gli stati di abilitazione tanto per il "drag" (variabile di istanza self._dragable) quanto per il drop (variabile di istanza self._dropable) che ritenevo venissero ereditate e modificate dai setter applicati sugli oggetti istanziati (istruzioni self.obj.dropable = self.dropable e self.obj.dragable = self.dragable), così non è :


le variabili di istanza rimangono come impostate nella classe astratta mentre la risposta degli oggetti varia secondo le impostazioni ... suppongo che l'insieme di istruzioni sopra indicata causi la creazione di nuove proprietà "_dragable" e "_dropable" negli oggetti passati alle definizioni "reali" della classe astratta : è così?




Fermo restando che che andrò a rileggermi la docs tanto di ABC quanto delle classi in genere e di python in particolare non mi dispiacerebbe avere conforto di utenti più addentrati.




P.S. : vedo che non c'è stato molto interesse sull'argomento, evito quindi di allegare i files delle definizioni e di test, tre in complesso, se vi è interesse lo si indichi, il prodotto finale di ciò che ho fatto è la possibilità di definire dei widget con capacità "drag and drop" attivabile/disattivabile a piacere, con selezione di copia per tipologia di dato.




Grazie dell'attenzione :)

Fatti non foste a viver come bruti...
Ciao caro, ma fammi capire se ho ben interpretato.

Quindi questo nuovo attributo è indipendente dalle proprietà dropable definite nella classe astratta e quindi, self.obj.dropable e self._dropable non sono collegati?

Daniele aka Palmux said @ 2024-11-20 12:01:02:
Ciao caro, ma fammi capire se ho ben interpretato.

Quindi questo nuovo attributo è indipendente dalle proprietà dropable definite nella classe astratta e quindi, self.obj.dropable e self._dropable non sono collegati?
L'effetto sembra quello, e la cosa mi ha un po' stupito, mi aspettavo, avendo "trasferito" i metodi "dragable" e "dropable" della classe originale ad un widget, che i valori fossero conformi, invece no :

Python 3.12.3 (main, Sep 11 2024, 14:17:37) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.

= RESTART: /home/ngiuseppe/src/prove/varie/tk/dragdrop/step02/drag_and_drop.py =

====== RESTART: /home/ngiuseppe/src/prove/varie/tk/dragdrop/step02/test.py =====
dragable classe True, dragable oggetto True
dropable classe True, dropable oggetto False
dragable classe True, dragable oggetto False
dropable classe True, dropable oggetto True

c'è qualcosa che, certo, non ho compreso adeguatamente.





@Palmux, so che non approvi ma collego tre files che debbono essere posti nella stessa directory, così, nel caso Ti andasse e Ti fosse possibile, puoi vedere bene ciò che ho fatto

Fatti non foste a viver come bruti...
Allegati
Lo farò sicuramente, non avere fretta perché ho un "milionaio" di attività da fare, ma una promessa è un debito.

Cya


Pagina: 1 2 Avanti



Esegui il login per scrivere una risposta.