Sto cercando di implementare un widget in Python/tkinter

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Per curiosità (e con un po' di tempo libero a disposizione) un po' di tempo fa mi avvicinai alla programmazione. Poichè non è il mio settore, scelsi tra i linguaggi di scripting Python. Ora mi sto interessando a tkinter, che permette di creare le interfacce grafiche usando appunto python. Tra i widget disponibili ne manca uno, chiamato iconview, completo di rubberband. Da qualche giorno mi sto interessando al problema, e questo è il risultato che ho ottenuto finora:
(Versione senza Drag and Drop, praticamente definitiva)
Python:
import tkinter as tk
import tkinter.ttk as ttk
import math
import os

class Application(ttk.Frame):
    def __init__(self, master=None, k=None, dd=None):
        super().__init__(master)
        self.master = master
        self.k = k
        self.dd = dd
        # cartella di lavoro
        self.working_dir = self.dd[0]
        # crea elenco di file e cartelle
        self.d = self.create_list_of_items()
        self.pack(fill="both", expand=True)
      
        # applica font e dimensione a tutti i widget tranne popup
        self.s = ttk.Style()
        self.s.configure('.', font=('', self.k[10]))
      
        ########### ESEMPIO
        # premendo il pulsante scambio l'icona del primo elemento:
        # se cartella icona file e se file icona folder
        btn = ttk.Button(self.master, text="pulsante", command=self.btn_click)
        btn.pack(side="bottom")
  
        self.master.update_idletasks()
        #
        self.create_widgets()

     # modifico icona del primo rettangolo
    def btn_click(self):
        if self.rectangle_list:
            # nome associato al rettangolo
            rect_name = self.w.gettags(self.rectangle_list[0])[1]
            # percorso completo
            item_path = os.path.join(self.working_dir, rect_name)
            #
            if os.path.isdir(item_path):
                self.master.after_idle(lambda :self.w.itemconfigure(self.image_list[0], image=self.img_list[1]))
                self.master.update_idletasks()
            elif os.path.isfile(item_path):
                self.master.after_idle(lambda :self.w.itemconfigure(self.image_list[0], image=self.img_list[0]))
                self.master.update_idletasks()

    # creo lista con percorso assoluto dei file e cartelle esistenti nella directory di lavoro
    def create_list_of_items(self):
        item_list = []
        list_file = os.listdir(self.working_dir)
        for ffile in list_file:
            item_list.append(os.path.join(self.working_dir, ffile))

        return item_list

    def create_widgets(self):
        ########### canvas
        self.w = tk.Canvas(self)
        self.w.configure(bg=self.k[4])

        ########### scrollbar
        self.sbar = ttk.Scrollbar(self)
        self.sbar.config(command=self.w.yview)           
        self.w.config(yscrollcommand=self.sbar.set)
        self.sbar.pack(side="right", fill="y")
        self.w.pack(side="left", fill="both", expand=True)
        self.master.update_idletasks()
        ###########

        ########### rubberband
        # il rettangolo di selezione
        self.rubberbandBox = None

        # operazioni col mouse
        # LMB
        self.w.bind("<Button-1>", self.mouseDown)
        self.w.bind("<Button1-Motion>", self.mouseMotion)
        self.w.bind("<Button1-ButtonRelease>", self.mouseUp)
        self.w.bind("<Double-Button-1>", self.mouseDoubleDown)
        # RMB
        self.w.bind("<Button-3>", self.mouseRDown)
        # Ctrl+LMB - premuto e rilasciato
        self.w.bind("<Control-Button-1>", self.CmouseLDown)
        self.w.bind("<Control-Button1-ButtonRelease>", self.CmouseLUp)
        # selezione multipla sequenziale con shift-button1
        self.w.bind("<Shift-Button-1>", self.SmouseLDown)
        self.w.bind("<Shift-Button1-ButtonRelease>", self.SmouseLUp)
        # MOUSE WHEEL SCROLLING
        platform = self.master.tk.call('tk', 'windowingsystem')
        if platform == "x11":
            # Linux, FreeBSD, etc.
            self.w.bind_all('<Button-4>', self.mouseWheelL)
            self.w.bind_all('<Button-5>', self.mouseWheelL)
        elif platform == "win32":
            # Windows
            self.w.bind_all("<MouseWheel>", self.mouseWheelW)
        elif platform == "aqua":
            pass

        # popolo il canvas
        self.populate_canvas()

    def populate_canvas(self):

        # pulsante 1 del mouse down
        self.button1_mouse = 0

        # se trascino col mouse
        self.drag_mouse = 0

        # CTRL+LMB - selezione singolo elemento
        self.ctrl_lmb = 0

        # variabile per modificare il colore degli elementi selezionati col mouse
        self.dragged_items = []

        # lista degli id dei rettangoli creati
        self.rectangle_list = []

        # lista degli id delle icone di ogni rettangolo
        self.image_list = []

        # dimensione di Frame
        self.dim_w = self.winfo_width()

        # dimensione di ogni rettangolo
        ww = self.k[0]
        wh = self.k[1]
        # spazio tra rettangoli e dai bordi
        wpadx = self.k[2]
        wpady = self.k[3]

        # altezza del testo degli elementi di ogni riga
        self.text_height = []

        # lista degli oggetti immagine
        self.img_list = []

        # creo la lista delle immagini
        self.fcreate_image_list()

        ########
        # numero di rettangoli da inserire
        num_rect = len(self.d)
        # numero di rettangoli per riga
        num_rect_row = int(self.dim_w/(wpadx*2+ww)) or 1
        print("numero di rettangoli per riga: ", num_rect_row)
        # numero di righe da occupare
        num_row = math.ceil(num_rect/num_rect_row)
        print("numero di righe: ", num_row)

        # dimensione minima di canvas a seconda dei rettangoli da inserire
        self.canvasw = num_rect_row*(ww+wpadx)+wpadx

        # elenco provvisorio delle altezze del testo per riga
        lista_hh = []
        # memorizza il massimo valore della lista di sopra - incrementale
        self.hh = 0
        # memorizza il massimo valore della lista di sopra
        self.hh2 = 0

        # usato come tag di ogni elemento
        self.ii = 1
        # per ogni riga
        for rr in range(num_row):
            if lista_hh:
                self.hh += max(lista_hh)
                self.hh2 = max(lista_hh)
            # resetto la lista ad ogni nuovo rigo
            lista_hh = []
            # per ogni colonna
            for r in range(num_rect_row):
                if (self.ii-1) < num_rect:
                    # aggiungo il testo
                    x1 = (wpadx * (r + 1)) + (ww * r)
                    y1 = (wpady * (rr + 1)) + (wh * rr)
                    # il testo
                    rtext = os.path.basename(self.d[self.ii-1])
                    # dimensione da aggiungere alla lunghezza preimpostata del rettangolo
                    rect_height_text = self.add_text(x1, y1, rtext)
                    lista_hh.append(rect_height_text)
                    #
                    # aggiungo rettangolo
                    x1r = x1
                    y1r = y1 + self.hh
                    x2r = x1r + ww
                    y2r = y1r + wh + rect_height_text
                    fill = self.k[5]
                    self.add_rect(x1r, y1r, x2r, y2r, fill, rtext)
                    #
                    # aggiungo immagine
                    item_path = os.path.join(self.working_dir, self.d[self.ii-1])
                    # se cartella
                    if os.path.isdir(item_path):
                        item_image = self.img_list[0]
                    # se file
                    elif os.path.isfile(item_path):
                        item_image = self.img_list[1]
            
                    img_padx = (self.k[0]-self.k[7])/2
                    img_pady = (self.k[1]-self.k[7])/2
                    self.add_image(x1+img_padx, y1+img_pady+self.hh, item_image, self.ii)
                    self.ii += 1
                else:
                    lista_hh = []
                    self.hh = 0
                    self.hh2 = 0
                    break

        # per ottenere dinamicamente la dimensione di Frame
        self.bind("<Configure>", self.wconfigure)

        # aggiorno canvas - Updated the screen before calculating the scrollregion
        self.update()
        self.w.config(scrollregion=self.w.bbox("all"))

    # ritorna la lunghezza massima di text per ogni riga
    def ftext_height(self):
        if self.text_height:
                height_text = max(self.text_height)
                self.text_height = []
        else:
            height_text = 0

        return height_text

    # rimuove tutti gli elementi e ripopola canvas
    def delete_all_items_and_populate(self):
        self.w.delete("all")
        self.update()
        self.w.config(scrollregion=self.w.bbox("all"))
        self.populate_canvas()

    def add_rect(self, x1, y1, x2, y2, fill, item_name):
        # creo il rettangolo
        id = self.w.create_rectangle(x1, y1, x2, y2, fill=fill, tags=(self.ii, item_name))
        # porto indietro il rettangolo
        self.w.tag_lower(id)
        # aggiungo id alla lista degli id
        self.rectangle_list.append(id)

    # solo le icone per cartella e file
    def fcreate_image_list(self):
        self.image = tk.PhotoImage(file="folder.png")
        self.img_list.append(self.image)
        self.image = tk.PhotoImage(file="file.png")
        self.img_list.append(self.image)

    def add_image(self, x, y, image, n):
        id = self.w.create_image(x, y, image=image, anchor="nw")
        # aggiungo id alla lista
        self.image_list.append(id)

    # ritorna id di text
    def add_text(self, x, y, rtext):
        ttext = rtext
        id = self.w.create_text(x+(self.k[0]/2), y+self.k[1]+self.hh, text=ttext, font=(self.k[8], self.k[9]), justify="center", width=self.k[0]-20, anchor="n")
        id_box = self.w.bbox(id)
        id_height = id_box[3]-id_box[1]
        self.text_height.append(id_height)

        return id_height

    # per riordinare gli elementi in canvas al
    # variare delle sue dimensioni - stampa la dimensione di canvas
    def wconfigure(self, event):
        # se restringo o allargo la finestra
        if (event.width < self.canvasw) or (event.width > self.canvasw+250+10):
            # svuoto canvas e lo ripopolo
            self.delete_all_items_and_populate()

    def mouseDown(self, event):
        # deseleziono tutti i rettangoli
        if self.dragged_items:
            for i in self.dragged_items:
                self.w.itemconfigure(i, fill=self.k[5])
        #
        # coordinate iniziali
        self.startx = self.w.canvasx(event.x)
        self.starty = self.w.canvasy(event.y)
        selected_item = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        #
        if selected_item[0] != ():
            self.button1_mouse = 1
            selected_item_id = selected_item[0][0]
            # primo elemento selezionato
            print("Selected item: ", selected_item_id)
            # stampo il tag dell elemento cliccato
            item_tag = self.w.gettags(selected_item_id)
            print("Item tag: ", item_tag)
            # seleziono il rettangolo
            self.w.itemconfigure(selected_item_id, fill=self.k[6])
            # stampo il nome selezionato
            print("Selezionato elemento singolo: ", self.w.gettags(selected_item_id)[1])
            # aggiorno
            self.dragged_items = [selected_item_id]
        # se selezionato sfondo
        else:
            # svuoto la lista dei rettangoli selezionati
            self.dragged_items = []
            # resetto la variabile
            self.button1_mouse = 0

    def mouseMotion(self, event):
        # se ho premuto CTRL+LMB niente trascinamento
        if self.ctrl_lmb:
            return
      
        if self.sbar.get()[1] < 1:
            if int(self.w.winfo_height()) - event.y < 50:
                self.w.yview("scroll", "1", "units")
                self.w.config(yscrollcommand=self.sbar.set)
        if self.sbar.get()[0] > 0:      
            if event.y < 50:
                self.w.yview("scroll", "-1", "units")
                self.w.config(yscrollcommand=self.sbar.set)

        # se inizio dallo sfondo
        if self.button1_mouse == 0:
            # inizia il trascinamento
            self.drag_mouse = 1
            # coordinate finali
            x = self.w.canvasx(event.x)
            y = self.w.canvasy(event.y)

            if (self.startx != event.x)  and (self.starty != event.y) :
                self.w.delete(self.rubberbandBox)
                self.rubberbandBox = self.w.create_rectangle(self.startx, self.starty, x, y, fill="cornsilk2",stipple='gray25')
                self.update_idletasks()
            #########
            motion_data = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
            item_selected = list(motion_data[0])
            # solo se almeno un rettangolo viene interessato
            if len(item_selected) > 1:
                for i in self.rectangle_list:
                    # se selezionato modifico il colore di sfondo
                    if i in item_selected:
                        # se non presente:
                        if i not in self.dragged_items:
                            self.w.itemconfigure(i, fill=self.k[6])
                            # aggiungo alla lista
                            self.dragged_items.append(i)
                    # altrimenti ripristino il colore originario
                    else:
                        self.w.itemconfigure(i, fill=self.k[5])
                        # rimuovo dalla lista se presente
                        if i in self.dragged_items:
                            self.dragged_items.remove(i)
            else:
                # tutti i rettangoli con lo sfondo di default
                for i in self.rectangle_list:
                    self.w.itemconfigure(i, fill=self.k[5])
                # resetto
                self.dragged_items = []

    def mouseUp(self, event):
        if self.ctrl_lmb:
            return

        if self.drag_mouse:
            # resetto
            self.drag_mouse = 0
            self.w.delete(self.rubberbandBox)
            ######
            # tutti i dati degli indici selezionati
            item_selected_rough = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
    
            # resetto
            self.button1_mouse = 0
            #
            # stampo il nome degli elementi selezionati
            for item in self.dragged_items:
                print("Elementi selezionati", self.w.gettags(item)[1])

    def mouseWheelL(self, event):
        if event.num == 4:
            self.w.yview_scroll(-1, "units")
        elif event.num == 5:
            self.w.yview_scroll(1, "units")

    def mouseWheelW(self, event):
        self.w.yview_scroll(-1*(event.delta//120), "units")

    # LMB doppio click
    def mouseDoubleDown(self, event):
        # coordinate iniziali
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # elemento selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se selezionato rettangolo:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            ## primo elemento selezionato
            # stampo il tag dell elemento cliccato
            item_tag = self.w.gettags(selected_item_id)
            print("Doppio click su elemento: ", item_tag[1])

    def mouseRDown(self, event):
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            item_tag = self.w.gettags(selected_item_id)
    
            # seleziono il rettangolo se non ancora selezionato
            if selected_item_id not in self.dragged_items:
                # se il rettangolo risulta creato
                if selected_item_id in self.rectangle_list:
                    print("Selezionato col tasto destro: ", item_tag[1])
                    # deseleziono tutti i rettangoli
                    if self.dragged_items:
                        for i in self.dragged_items:
                            self.w.itemconfigure(i, fill=self.k[5])
                    # ne cambio il colore di sfondo
                    # ... e setto solo questo rettangolo come selezionato
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                    self.dragged_items = [selected_item_id]
                # crea un popup se risulta selezionato un solo elemento
                self.mouseRpopup3(event)
            # se risulta selezionato anche con altri
            else:
                # stampo i nomi di tutti gli elementi selezionati
                for item in self.dragged_items:
                    print("Dal tasto destro risultano selezionati:", self.w.gettags(item)[1])
                # crea un popup - selezione multipla
                self.mouseRpopup3(event)

    def create_window(self):
        window = tk.Toplevel(self)
        # titolo della finestra
        window.title("Info")
        window.minsize(300, 300)
        #
        # se un solo elemento
        dim_selected_items = len(self.dragged_items)
        if dim_selected_items == 1:
            item_name = self.w.gettags(self.dragged_items[0])[1]
        elif dim_selected_items > 1:
            item_name = "selezione multipla"
        #
        label1 = ttk.Label(window, text="Nome del file: "+item_name)
        label1.pack()
        #
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        window.update()
        ww = window.winfo_width()
        wh = window.winfo_height()
        # centro la finestra
        x = int((sw-ww)/2)
        y = int((sh-wh)/2)
        window.geometry('+{}+{}'.format(x, y))

    # popup di ogni elemento col tasto destro
    def mouseRpopup3(self, event):
        self.popup_menu = tk.Menu(self, tearoff=0, font=("", self.k[10]))
        self.popup_menu.add_command(label="Uno", command=self.create_window)
        try:
            self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
        finally:
            self.popup_menu.grab_release()

    # tasto CTRL+LMB
    def CmouseLDown(self, event):
        # setto la variabile
        self.ctrl_lmb = 1
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            # se il rettangolo risulta creato
            if selected_item_id in self.rectangle_list:
                # seleziono il rettangolo se non ancora selezionato
                if selected_item_id not in self.dragged_items:
                    # ne cambio il colore di sfondo
                    # ... e aggiungo il rettangolo come selezionato
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                    self.dragged_items.append(selected_item_id)
                    # stampo il nome degli elementi selezionati
                    for item in self.dragged_items:
                        print("Selezionato con CTRL+LMB:", self.w.gettags(item)[1])
                # se risulta selezionato lo deseleziono
                else:
                    self.w.itemconfigure(selected_item_id, fill=self.k[5])
                    self.dragged_items.remove(selected_item_id)
                    # stampo il nome degli elementi deselezionato
                    print("Deselezionato con CTRL+LMB:", self.w.gettags(selected_item_id)[1])

    # solo per resettare la variabile
    def CmouseLUp(self, event):
        # resetto la variabile
        self.ctrl_lmb = 0
  
    # SHIFT+LMB
    def SmouseLDown(self, event):
        self.ctrl_lmb = 1
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            # se il rettangolo risulta creato
            if selected_item_id in self.rectangle_list:
                # se lista vuota solo selected_item_id
                if self.dragged_items == []:
                    # lo aggiungo alla lista
                    self.dragged_items.append(selected_item_id)
                    # cambio il colore di sfondo
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                # se non vuota
                else:
                    # creo una lista temporanea di elementi ordinati degli elementi gia esistenti
                    temp_list = sorted(self.dragged_items)
                    # il primo elemento di temp_list
                    temp_list_first = temp_list[0]
                    # l ultimo elemento di temp_list
                    temp_list_last = temp_list[-1]
                    # se elemento selezionato maggiore del primo o uguale
                    if selected_item_id >= temp_list_first:
                        # indice di elemento selezionato nella lista dei rettangoli
                        selected_item_id_idx = self.rectangle_list.index(selected_item_id)
                        # indice di temp_list_first in self.rectangle_list
                        temp_list_first_idx = self.rectangle_list.index(temp_list_first)
                        # lista temporanea da temp_list_first a selected_item_id
                        temp_list_b = self.rectangle_list[temp_list_first_idx:(selected_item_id_idx+1)]
                        # deseleziono tutti
                        for item in self.dragged_items:
                            self.w.itemconfigure(item, fill=self.k[5])
                        # azzero la lista
                        self.dragged_items = []
                        # seleziono gli elementi di temp_list_b
                        for item in temp_list_b:
                            self.w.itemconfigure(item, fill=self.k[6])
                        # nuova lista di elementi selezionati
                        self.dragged_items = temp_list_b
                    # se minore
                    elif selected_item_id < temp_list_first:
                        # indice di elemento selezionato nella lista dei rettangoli
                        selected_item_id_idx = self.rectangle_list.index(selected_item_id)
                        # indice di temp_list_first in self.rectangle_list
                        temp_list_last_idx = self.rectangle_list.index(temp_list_last)
                        # lista temporanea da temp_list_first a selected_item_id
                        temp_list_b = self.rectangle_list[selected_item_id_idx:(temp_list_last_idx+1)]
                        # deseleziono tutti
                        for item in self.dragged_items:
                            self.w.itemconfigure(item, fill=self.k[5])
                        # azzero la lista
                        self.dragged_items = []
                        # seleziono gli elementi di temp_list_b
                        for item in temp_list_b:
                            self.w.itemconfigure(item, fill=self.k[6])
                        # nuova lista di elementi selezionati
                        self.dragged_items = temp_list_b
                          
    def SmouseLUp(self, event):
        self.ctrl_lmb = 0

def main():
    root = tk.Tk()

    # titolo della finestra
    root.title("ICONVIEW")

    ## per centrare la finestra
    root.update_idletasks()

    sw = root.winfo_screenwidth()
    sh = root.winfo_screenheight()

    # dimensione e posizione della finestra
    width = 1000
    height = 800

    # centro la finestra
    x = int((sw-width)/2)
    y = int((sh-height)/2)
    root.geometry('{}x{}+{}+{}'.format(width, height, x, y))
    #
    # prima lista configurazione: larghezza rettangolo, altezza rettangolo, wpadx, wpady,
    #... colore di sfondo di canvas, colore di default dei rettangoli,
    #... colore di selezione dei rettangoli, dimensione icona, famiglia del font del widget,
    #... dimensione font del widget, dimensione font di default di altri elementi e widget;
    canvas_config = [250, 250, 4, 4, "gray99", "light gray", "PaleGreen2", 64, "", 12, 12]
    # seconda lista dati: cartella di lavoro
    working_dir = os.path.realpath("DATI")
    canvas_data = [working_dir]
    app = Application(master=root, k=canvas_config, dd=canvas_data)

    app.mainloop()

if __name__ == "__main__":
    main()
(Versione attualmente in fase di completamento, con Drag and Drop)
Python:
import tkinter as tk
import tkinter.ttk as ttk
import math
import os
from TkinterDnD2 import *

class Application(ttk.Frame):
    def __init__(self, master=None, k=None, dd=None):
        super().__init__(master)
        self.master = master
        self.k = k
        self.dd = dd
        # cartella di lavoro
        self.working_dir = self.dd[0]
        # crea elenco di file e cartelle
        self.d = self.create_list_of_items()
        self.pack(fill="both", expand=True)
      
        # applica font e dimensione a tutti i widget tranne popup
        self.s = ttk.Style()
        self.s.configure('.', font=('', self.k[10]))
      
        ########### ESEMPIO
        # premendo il pulsante scambio l'icona del primo elemento:
        # se cartella icona file e se file icona folder
        btn = ttk.Button(self.master, text="pulsante", command=self.btn_click)
        btn.pack(side="bottom")
   
        self.master.update_idletasks()
        #
        self.create_widgets()
   
    # modifico icona del primo rettangolo
    def btn_click(self):
        # se esiste almeno un elemento
        if self.rectangle_list:
            # nome associato al rettangolo
            rect_name = self.w.gettags(self.rectangle_list[0])[1]
            # percorso completo
            item_path = os.path.join(self.working_dir, rect_name)
            # scambio icona
            if os.path.isdir(item_path):
                self.master.after_idle(lambda :self.w.itemconfigure(self.image_list[0], image=self.img_list[1]))
                self.master.update_idletasks()
            elif os.path.isfile(item_path):
                self.master.after_idle(lambda :self.w.itemconfigure(self.image_list[0], image=self.img_list[0]))
                self.master.update_idletasks()

    # creo lista con percorso assoluto dei file e cartelle esistenti nella directory di lavoro
    def create_list_of_items(self):
        item_list = []
        list_file = os.listdir(self.working_dir)
        for ffile in list_file:
            item_list.append(os.path.join(self.working_dir, ffile))
   
        return item_list

    def create_widgets(self):
        ########### canvas
        self.w = tk.Canvas(self)
        self.w.configure(bg=self.k[4])
   
        ####### DnD
        # add a boolean flag to the canvas which can be used to disable
        # files from the canvas being dropped on the canvas again
        self.w.dragging = False
        # DROP
        self.w.drop_target_register(DND_FILES)
        self.w.dnd_bind('<<DropEnter>>', self.drop_enter)
        self.w.dnd_bind('<<DropPosition>>', self.drop_position)
        self.w.dnd_bind('<<DropLeave>>', self.drop_leave)
        self.w.dnd_bind('<<Drop>>', self.drop)
        # DRAG
        self.w.drag_source_register(1, DND_FILES)
        self.w.dnd_bind('<<DragInitCmd>>', self.drag_init)
        self.w.dnd_bind('<<DragEndCmd>>', self.drag_end)
   
        ########### scrollbar
        self.sbar = ttk.Scrollbar(self)
        self.sbar.config(command=self.w.yview)              
        self.w.config(yscrollcommand=self.sbar.set)
        self.sbar.pack(side="right", fill="y")
        self.w.pack(side="left", fill="both", expand=True)
        self.master.update_idletasks()
        ###########
   
        ########### rubberband
        # il rettangolo di selezione
        self.rubberbandBox = None

        # operazioni col mouse
        # LMB
        self.w.bind("<Button-1>", self.mouseDown)
        self.w.bind("<Button1-Motion>", self.mouseMotion)
        self.w.bind("<Button1-ButtonRelease>", self.mouseUp)
        self.w.bind("<Double-Button-1>", self.mouseDoubleDown)
        # RMB
        self.w.bind("<Button-3>", self.mouseRDown)
        # Ctrl+LMB - premuto e rilasciato
        self.w.bind("<Control-Button-1>", self.CmouseLDown)
        self.w.bind("<Control-Button1-ButtonRelease>", self.CmouseLUp)
        # selezione multipla sequenziale con shift-button1
        self.w.bind("<Shift-Button-1>", self.SmouseLDown)
        self.w.bind("<Shift-Button1-ButtonRelease>", self.SmouseLUp)
        # MOUSE WHEEL SCROLLING
        platform = self.master.tk.call('tk', 'windowingsystem')
        if platform == "x11":
            # Linux and FreeBSD
            self.w.bind_all('<Button-4>', self.mouseWheelL)
            self.w.bind_all('<Button-5>', self.mouseWheelL)
        elif platform == "win32":
            # Windows # e OSX (non supportato)
            self.w.bind_all("<MouseWheel>", self.mouseWheelW)
        elif platform == "aqua":
            pass
   
        ###########
        # popolo il canvas
        self.populate_canvas()

    def populate_canvas(self):
   
        # pulsante 1 del mouse down
        self.button1_mouse = 0
   
        # se trascino col mouse
        self.drag_mouse = 0
   
        # CTRL+LMB - selezione singolo elemento
        self.ctrl_lmb = 0
   
        # variabile per modificare il colore degli elementi selezionati col mouse
        self.dragged_items = []
   
        # lista degli id dei rettangoli creati
        self.rectangle_list = []
   
        # lista degli id delle icone di ogni rettangolo
        self.image_list = []
   
        # selezionato rettangolo: pulsante 1 del mouse down
        self.rect_selected = 0
   
        # inizio il DnD
        self.items_dragged_for_dropped = 0
   
        # se il drop lo termino in canvas stesso
        self.drop_in_canvas = 0
   
        # dimensione di Frame
        self.dim_w = self.winfo_width()
   
        # dimensione di ogni rettangolo
        ww = self.k[0]
        wh = self.k[1]
        # spazio tra rettangoli e dai bordi
        wpadx = self.k[2]
        wpady = self.k[3]
   
        # altezza del testo degli elementi di ogni riga
        self.text_height = []
   
        # lista degli oggetti immagine
        self.img_list = []
   
        # creo la lista delle immagini
        self.fcreate_image_list()
   
        ########
        # numero di rettangoli da inserire
        num_rect = len(self.d)
        # numero di rettangoli per riga
        num_rect_row = int(self.dim_w/(wpadx*2+ww)) or 1
        print("numero di rettangoli per linea: ", num_rect_row)
        # numero di linee da occupare
        num_row = math.ceil(num_rect/num_rect_row)
        print("numero di linee: ", num_row)
        #
        # dimensione minime di canvas a seconda dei rettangoli da inserire
        self.canvasw = num_rect_row*(ww+wpadx)+wpadx
   
        # elenco provvisorio delle altezze del testo per riga
        lista_hh = []
        # memorizza il massimo valore della lista di sopra - incrementale
        self.hh = 0
        # memorizza il massimo valore della lista di sopra
        self.hh2 = 0
   
        # usato come tag di ogni elemento
        self.ii = 1
        # per ogni riga
        for rr in range(num_row):
            if lista_hh:
                self.hh += max(lista_hh)
                self.hh2 = max(lista_hh)
            # resetto la lista ad ogni nuovo rigo
            lista_hh = []
            # per ogni colonna
            for r in range(num_rect_row):
                if (self.ii-1) < num_rect:
                    # aggiungo il testo
                    x1 = (wpadx * (r + 1)) + (ww * r)
                    y1 = (wpady * (rr + 1)) + (wh * rr)
                    # il testo
                    rtext = os.path.basename(self.d[self.ii-1])
                    # dimensione da aggiungere alla lunghezza preimpostata del rettangolo
                    rect_height_text = self.add_text(x1, y1, rtext)
                    lista_hh.append(rect_height_text)
                    #
                    # aggiungo rettangolo
                    x1r = x1
                    y1r = y1 + self.hh
                    x2r = x1r + ww
                    y2r = y1r + wh + rect_height_text
                    fill = self.k[5]
                    self.add_rect(x1r, y1r, x2r, y2r, fill, rtext)
                    #
                    # aggiungo immagine
                    item_path = os.path.join(self.working_dir, self.d[self.ii-1])
                    # se cartella
                    if os.path.isdir(item_path):
                        item_image = self.img_list[0]
                    # se file
                    elif os.path.isfile(item_path):
                        item_image = self.img_list[1]
                    #
                    img_padx = (self.k[0]-self.k[7])/2
                    img_pady = (self.k[1]-self.k[7])/2
                    self.add_image(x1+img_padx, y1+img_pady+self.hh, item_image, self.ii)
                    self.ii += 1
                else:
                    lista_hh = []
                    self.hh = 0
                    self.hh2 = 0
                    break
   
        # per ottenere dinamicamente la dimensione di Frame
        self.bind("<Configure>", self.wconfigure)

        # aggiorno canvas
        self.update()
        self.w.config(scrollregion=self.w.bbox("all"))

    # drop methods
    def drop_enter(self, event):
        event.widget.focus_force()
        print('Entering %s' % event.widget)
        return event.action

    def drop_position(self, event):
        return event.action

    def drop_leave(self, event):
        print('Leaving %s' % event.widget)
        return event.action

    def drop(self, event):
      
        if self.w.dragging:
            # setto la variabile
            self.drop_in_canvas = 1
            # the canvas itself is the drag source
            return REFUSE_DROP
        if event.data:
            files = self.w.tk.splitlist(event.data)
            print("Ricevuti elementi: ", files)
   
        return event.action

    # drag methods
    def drag_init(self, event):
   
        # setto la variabile
        self.items_dragged_for_dropped = 1
   
        # se sono sul fondo e trascino esco
        if not self.rect_selected:
            print("Nessun DnD")
            return 'break'
   
        sel = self.dragged_items
        # se ci sono elementi selezionati
        if self.dragged_items:
            # creo la lista degli elementi
            data = ()
            for item in self.dragged_items:
                file_name = self.w.gettags(item)[1]
                item_data = (os.path.join(self.working_dir, file_name), )
                data += item_data
           
            print("Selezionati per il drag and drop: ", data)
            # in a decent application we should check here if the mouse
            # actually hit an item, but for now we will stick with this
            self.w.dragging = True
            return ((ASK, COPY), (DND_FILES, DND_TEXT), data)
        else:
            # don't start a dnd-operation when nothing is selected; the
            # return "break" here is only cosmetical, return "foobar" would
            # probably do the same
            return 'break'

    def drag_end(self, event):
        # reset the "dragging" flag to enable drops again
        self.w.dragging = False
    ##########

    # ritorna la lunghezza massima di text per ogni riga
    def ftext_height(self):
        if self.text_height:
                height_text = max(self.text_height)
                self.text_height = []
        else:
            height_text = 0
   
        return height_text

    # rimuove tutti gli elementi e ripopola canvas
    def delete_all_items_and_populate(self):
        self.w.delete("all")
        self.update()
        self.w.config(scrollregion=self.w.bbox("all"))
        self.populate_canvas()

    def add_rect(self, x1, y1, x2, y2, ifill, item_name):
        # creo il rettangolo
        id = self.w.create_rectangle(x1, y1, x2, y2, fill=ifill, outline="black", tags=(self.ii, item_name))
        self.w.tag_lower(id)
        self.rectangle_list.append(id)

    # solo le icone per cartella e file
    def fcreate_image_list(self):
        self.image = tk.PhotoImage(file="folder.png")
        self.img_list.append(self.image)
        self.image = tk.PhotoImage(file="file.png")
        self.img_list.append(self.image)

    def add_image(self, x, y, image, n):
        id = self.w.create_image(x, y, image=image, anchor="nw")
        # aggiungo id alla lista
        self.image_list.append(id)

    # ritorna id di text
    def add_text(self, x, y, rtext):
        ttext = rtext
        id = self.w.create_text(x+(self.k[0]/2), y+self.k[1]+self.hh, text=ttext, font=(self.k[8], self.k[9]), justify="center", width=self.k[0]-20, anchor="n")
        id_box = self.w.bbox(id)
        id_height = id_box[3]-id_box[1]
        self.text_height.append(id_height)
   
        return id_height

    # per riordinare gli elementi in canvas al
    # variare delle sue dimensioni - stampa la dimensione di canvas
    def wconfigure(self, event):
        # se restringo o allargo la finestra
        if (event.width < self.canvasw) or (event.width > self.canvasw+250+10):
            # svuoto canvas e lo ripopolo
            self.delete_all_items_and_populate()

    def mouseDown(self, event):
        # coordinate iniziali
        self.startx = self.w.canvasx(event.x)
        self.starty = self.w.canvasy(event.y)
        selected_item = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
   
        # se selezionato rettangolo: id
        if selected_item[0] != ():
            # setto la variabile di selezionato rettangolo
            self.rect_selected = 1
            #
            # se clicco su elemento gia selezionato, singolo o in gruppo
            if selected_item[0][0] in self.dragged_items:
                # se almeno un elemento in lista
                if len(self.dragged_items) > 0:
                    return
            # se non in lista
            else:
                # deseleziono tutto
                if self.dragged_items:
                    for i in self.dragged_items:
                        self.w.itemconfigure(i, fill=self.k[5])
                #
                # seleziono elemento
                self.button1_mouse = 1
                # id dell elemento selezionato
                selected_item_id = selected_item[0][0]
                # stampo il tag dell elemento cliccato
                item_tag = self.w.gettags(selected_item_id)
                print("Item tag: ", item_tag)
                # seleziono il rettangolo
                self.w.itemconfigure(selected_item_id, fill=self.k[6])
                # stampo il nome selezionato
                print("Selezionato elemento singolo: ", self.w.gettags(selected_item_id)[1])
                # aggiorno
                self.dragged_items = [selected_item_id]
   
        self.button1_mouse = 0
   
    def mouseMotion(self, event):
        if self.ctrl_lmb:
            return
      
        if self.sbar.get()[1] < 1:
            if int(self.w.winfo_height()) - event.y < 50:
                self.w.yview("scroll", "1", "units")
                self.w.config(yscrollcommand=self.sbar.set)
        if self.sbar.get()[0] > 0:      
            if event.y < 50:
                self.w.yview("scroll", "-1", "units")
                self.w.config(yscrollcommand=self.sbar.set)
      
        if self.rect_selected:
            return
   
        # se inizio dallo sfondo
        if self.button1_mouse == 0:
            self.drag_mouse = 1
            # coordinate finali
            x = self.w.canvasx(event.x)
            y = self.w.canvasy(event.y)

            if (self.startx != event.x)  and (self.starty != event.y) :
                self.w.delete(self.rubberbandBox)
                self.rubberbandBox = self.w.create_rectangle(self.startx, self.starty, x, y, fill="cornsilk2",stipple='gray25')
                self.update_idletasks()
            #########
            motion_data = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
            item_selected = list(motion_data[0])
       
            # solo se almeno un rettangolo viene interessato
            if len(item_selected) > 1:
                # per ogni rettangolo creato
                for i in self.rectangle_list:
                    # se selezionato modifico il colore di sfondo
                    if i in item_selected:
                        # se non presente:
                        if i not in self.dragged_items:
                            self.w.itemconfigure(i, fill=self.k[6])
                            # aggiungo alla lista
                            self.dragged_items.append(i)
                    # altrimenti ripristino il colore originario
                    else:
                        self.w.itemconfigure(i, fill=self.k[5])
                        # rimuovo dalla lista se presente
                        if i in self.dragged_items:
                            self.dragged_items.remove(i)
            else:
                # tutti i rettangoli con lo sfondo di default
                for i in self.rectangle_list:
                    self.w.itemconfigure(i, fill=self.k[5])
                # resetto
                self.dragged_items = []

    def mouseUp(self, event):
        # resetto la variabile di selezione rettangolo singolo
        self.rect_selected = 0
        # se selezione ogni singolo rettangolo con CTRL+LMB esco
        if self.ctrl_lmb:
            return
   
        # se il dropo è stato completato in canvas valuto se file o cartella
        if self.drop_in_canvas:
            # resetto
            self.drop_in_canvas = 0
       
            # su quale rettangolo il puntatore si trova
            startx = self.w.canvasx(event.x)
            starty = self.w.canvasy(event.y)
            selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
            # il suo id se non vuoto
            if selected_item[0]:
                selected_item_id = selected_item[0][0]
                name_file = self.w.gettags(selected_item_id)[1]
                # percorso completo
                item_full_path = os.path.join(self.working_dir, name_file)
                # se cartella stampo un messaggio
                # se la cartella non risulta selezionata
                if selected_item_id not in self.dragged_items:
                    if os.path.isdir(item_full_path):
                        print("Item(s) dropped on folder: {}".format(name_file))
           
            return
   
        # se seleziono lo sfondo deseleziono tutto
        self.startx = self.w.canvasx(event.x)
        self.starty = self.w.canvasy(event.y)
        selected_item = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        #
        # se non selezionato nessun rettangolo, per es. sto sullo sfondo
        if selected_item[0] == ():
            # deseleziono tutti i rettangoli
            if self.dragged_items:
                for item in self.dragged_items:
                    self.w.itemconfigure(item, fill=self.k[5])
            # svuoto la lista dei rettangoli selezionati
            self.dragged_items = []
            # resetto la variabile
            self.button1_mouse = 0
        # se cliccato su un rettangolo
        else:
            # se non sto trascinando col mouse
            if not self.drag_mouse:
                # coordinate del rettangolo selezionato
                startx = self.w.canvasx(event.x)
                starty = self.w.canvasy(event.y)
                selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
                # id del rettangolo
                selected_item_id = selected_item[0][0]
                # deseleziono tutti gli altri
                for item in self.dragged_items:
                    if item != selected_item_id:
                        self.w.itemconfigure(item, fill=self.k[5])
                        # resetto la lista
                        self.dragged_items = []
                        # aggiungo questo rettangolo
                        self.dragged_items.append(selected_item_id)
       
        # se sto trascinando col mouse
        if self.drag_mouse:
            # resetto
            self.drag_mouse = 0
            self.w.delete(self.rubberbandBox)
            ######
            # tutti i dati degli indici selezionati
            item_selected_rough = self.w.find_overlapping(self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), self.startx, self.starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
       
            # resetto
            self.button1_mouse = 0
            #
            # stampo il nome degli elementi selezionati
            for item in self.dragged_items:
                print("Elementi selezionati", self.w.gettags(item)[1])

    def mouseWheelL(self, event):
        if event.num == 4:
            self.w.yview_scroll(-1, "units")
        elif event.num == 5:
            self.w.yview_scroll(1, "units")

    def mouseWheelW(self, event):
        self.w.yview_scroll(-1*(event.delta//120), "units")

    # LMB doppio click
    def mouseDoubleDown(self, event):
        # coordinate iniziali
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # elemento selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se selezionato rettangolo:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            item_tag = self.w.gettags(selected_item_id)
            print("Doppio click su elemento: ", item_tag[1])

    def mouseRDown(self, event):
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            item_tag = self.w.gettags(selected_item_id)
       
            # seleziono il rettangolo se non ancora selezionato
            if selected_item_id not in self.dragged_items:
                # se il rettangolo risulta creato
                if selected_item_id in self.rectangle_list:
                    print("Selezionato col tasto destro: ", item_tag[1])
                    # deseleziono tutti i rettangoli
                    if self.dragged_items:
                        for i in self.dragged_items:
                            self.w.itemconfigure(i, fill=self.k[5])
                    # ne cambio il colore di sfondo
                    # ... e setto solo questo rettangolo come selezionato
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                    self.dragged_items = [selected_item_id]
                # crea un popup se risulta selezionato un solo elemento
                self.mouseRpopup3(event)
            # se risulta selezionato anche con altri
            else:
                # stampo i nomi di tutti gli elementi selezionati
                for item in self.dragged_items:
                    print("Dal tasto destro risultano selezionati:", self.w.gettags(item)[1])
                # crea un popup - selezione multipla
                self.mouseRpopup3(event)

    def create_window(self):
        window = tk.Toplevel(self)
        # titolo della finestra
        window.title("Info")
        window.minsize(300, 300)
        #
        # se un solo elemento
        dim_selected_items = len(self.dragged_items)
        if dim_selected_items == 1:
            item_name = self.w.gettags(self.dragged_items[0])[1]
        elif dim_selected_items > 1:
            item_name = "selezione multipla"
        #
        label1 = ttk.Label(window, text="Nome del file: "+item_name)
        label1.pack()
        #
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        window.update()
        ww = window.winfo_width()
        wh = window.winfo_height()
        # centro la finestra
        x = int((sw-ww)/2)
        y = int((sh-wh)/2)
        window.geometry('+{}+{}'.format(x, y))

    # popup di ogni elemento col tasto destro
    def mouseRpopup3(self, event):
        self.popup_menu = tk.Menu(self, tearoff=0, font=("", self.k[10]))
        self.popup_menu.add_command(label="Uno", command=self.create_window)
        try:
            self.popup_menu.tk_popup(event.x_root+60, event.y_root, entry=0)
        finally:
            self.popup_menu.grab_release()

    # tasto CTRL+LMB
    def CmouseLDown(self, event):
        # setto la variabile
        self.ctrl_lmb = 1
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            # elemento selezionato
            # se il rettangolo risulta creato
            if selected_item_id in self.rectangle_list:
                # seleziono il rettangolo se non ancora selezionato
                if selected_item_id not in self.dragged_items:
                    # ne cambio il colore di sfondo
                    # ... e aggiungo il rettangolo come selezionato
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                    self.dragged_items.append(selected_item_id)
                    # stampo il nome degli elementi selezionati
                    for item in self.dragged_items:
                        print("Selezionato con CTRL+LMB:", self.w.gettags(item)[1])
                # se risulta selezionato lo deseleziono
                else:
                    self.w.itemconfigure(selected_item_id, fill=self.k[5])
                    self.dragged_items.remove(selected_item_id)
                    # stampo il nome degli elementi deselezionato
                    print("Deselezionato con CTRL+LMB:", self.w.gettags(selected_item_id)[1])

    def CmouseLUp(self, event):
        # resetto la variabile
        self.ctrl_lmb = 0
  
    # SHIFT+LMB
    def SmouseLDown(self, event):
        self.ctrl_lmb = 1
        # coordinate
        startx = self.w.canvasx(event.x)
        starty = self.w.canvasy(event.y)
        # rettangolo selezionato
        selected_item = self.w.find_overlapping(startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)), startx, starty, self.w.canvasx(event.x), self.w.canvasy(event.y)
        # se non vuoto:
        if selected_item[0] != ():
            selected_item_id = selected_item[0][0]
            # se il rettangolo risulta creato
            if selected_item_id in self.rectangle_list:
                # se lista vuota solo selected_item_id
                if self.dragged_items == []:
                    # lo aggiungo alla lista
                    self.dragged_items.append(selected_item_id)
                    # cambio il colore di sfondo
                    self.w.itemconfigure(selected_item_id, fill=self.k[6])
                # se non vuota
                else:
                    # creo una lista temporanea di elementi ordinati degli elementi gia esistenti
                    temp_list = sorted(self.dragged_items)
                    # il primo elemento di temp_list
                    temp_list_first = temp_list[0]
                    # l ultimo elemento di temp_list
                    temp_list_last = temp_list[-1]
                    # se elemento selezionato maggiore del primo o uguale
                    if selected_item_id >= temp_list_first:
                        # indice di elemento selezionato nella lista dei rettangoli
                        selected_item_id_idx = self.rectangle_list.index(selected_item_id)
                        # indice di temp_list_first in self.rectangle_list
                        temp_list_first_idx = self.rectangle_list.index(temp_list_first)
                        # lista temporanea da temp_list_first a selected_item_id
                        temp_list_b = self.rectangle_list[temp_list_first_idx:(selected_item_id_idx+1)]
                        # deseleziono tutti
                        for item in self.dragged_items:
                            self.w.itemconfigure(item, fill=self.k[5])
                        # azzero la lista
                        self.dragged_items = []
                        # seleziono gli elementi di temp_list_b
                        for item in temp_list_b:
                            self.w.itemconfigure(item, fill=self.k[6])
                        # nuova lista di elementi selezionati
                        self.dragged_items = temp_list_b
                    # se minore
                    elif selected_item_id < temp_list_first:
                        # indice di elemento selezionato nella lista dei rettangoli
                        selected_item_id_idx = self.rectangle_list.index(selected_item_id)
                        # indice di temp_list_first in self.rectangle_list
                        temp_list_last_idx = self.rectangle_list.index(temp_list_last)
                        # lista temporanea da temp_list_first a selected_item_id
                        temp_list_b = self.rectangle_list[selected_item_id_idx:(temp_list_last_idx+1)]
                        # deseleziono tutti
                        for item in self.dragged_items:
                            self.w.itemconfigure(item, fill=self.k[5])
                        # azzero la lista
                        self.dragged_items = []
                        # seleziono gli elementi di temp_list_b
                        for item in temp_list_b:
                            self.w.itemconfigure(item, fill=self.k[6])
                        # nuova lista di elementi selezionati
                        self.dragged_items = temp_list_b
                          
    def SmouseLUp(self, event):
        self.ctrl_lmb = 0

def main():
    root = TkinterDnD.Tk()

    # titolo della finestra
    root.title("ICONVIEW")

    ## per centrare la finestra
    root.update_idletasks()

    sw = root.winfo_screenwidth()
    sh = root.winfo_screenheight()

    # dimensione e posizione della finestra
    width = 1000
    height = 800
    # centro la finestra
    x = int((sw-width)/2)
    y = int((sh-height)/2)
    root.geometry('{}x{}+{}+{}'.format(width, height, x, y))
    #
    # prima lista configurazione: larghezza rettangolo, altezza rettangolo, wpadx, wpady,
    #... colore di sfondo di canvas, colore di default dei rettangoli,
    #... colore di selezione dei rettangoli, dimensione icona, famiglia del font del widget,
    #... dimensione font del widget, dimensione font di default di altri elementi e widget;
    canvas_config = [250, 250, 4, 4, "gray99", "light gray", "PaleGreen2", 64, "", 12, 12]
    # seconda lista dati: cartella di lavoro
    working_dir = os.path.realpath("DATI")
    canvas_data = [working_dir]
    app = Application(master=root, k=canvas_config, dd=canvas_data)

    app.mainloop()

if __name__ == "__main__":
    main()
Cosa in questo programma, che si trova in uno stato ancora non definitivo, è stato implementato:
- dispone gli elementi su più righe, senza oltrepassare il bordo, a seconda del numero di elementi da disporre
- è stato implementato il rubberband, necessario per selezionare gli elementi trascinando il mouse
- tiene conto della variazione della dimensione della finestra
- scrollbar
- selezione e deselezione singolo elemento (con CTRL)
- selezione multipla sequenziale (con SHIFT)
- menu a comparsa (se usato il tasto destro)
- doppio click
- i dati, di configurazione e non, verranno forniti dall'esterno della classe principale
- scrolling con la rotellina del mouse
- scrolling durante il dragging, sia verso l'alto che verso il basso
- possibilità di scegliere un diverso tipo di font e dimensione per tutti gli elementi di iconview
- possibilità di scegliere la dimensione del font per altri elementi e per il menu
- a titolo di esempio, ho inserito il pulsante che scambia l'icona associata del primo elemento visualizzato.
Il primo listato è la versione senza Drag and Drop, il secondo è stato leggermente modificato per implementare il Drag and Drop. Per il Drag and Drop è stata usata la libreria TkinterDnD2: per usarla procurarsi il pacchetto e poi a scelta o estrarre il contenuto nella cartella in cui si trova il programma o provvedere all'installazione secondo la procedura prevista per i moduli python. Il secondo listato è più indicato per i programmi che hanno a che fare con file e cartelle.
Non sto ponendo un problema ma sto descrivendo un percorso. Ogni suggerimento è comunque ben accetto, se competente.
Per ogni variazione del programma o stato di avanzamento modificherò il codice incluso nel post.
Non cercherò di creare un widget di tipo iconview astratto, poichè porterebbe via molto tempo e mi complicherebbe la vita non poco, ma un widget generico di partenza che possa essere usato in diversi scenari con piccole modifiche, se il caso.

Il programma richiede ulteriori passaggi per poterlo usare. Ora usa dati veri.
Nel mio caso, che ho installato Python 3 dalla versione portatile, ovvero non installabile, ho dovuto creare nella cartella principale di python la cartella 'DATI'; in questa si possono creare tutti i file e cartelle che si vuole; inoltre, poichè il programma usa icone, è necessario copiare nella stessa cartella principale di python due file png di 64x64 pixel chiamati folder.png e file.png, dato che il programma userà il giusto file a seconda del dipo di elemento che troverà nella cartella DATI. Anche questo programma va lanciato partendo dalla cartella principale di python.
Nel caso che Python fosse stato installato, probabilmente è solo necessario seguire questa procedura scegliendo una cartella qualunque(con Linux è così, con Windows non so), ma non ho provato.
 
Ultima modifica:

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Codice aggiornato. Ora il programma aggiorna il contenuto a seconda delle dimensioni della finestra. Aggiunto uno scrollbar verticale.
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Ho ulteriormente sviluppato il programma.
Credo di aver implementato quasi tutto quello che serve nella gestione delle selezioni.
Per ora mi sono divertito, ma so che ora verrà la parte più complicata.
 
Ultima modifica:
  • Mi piace
Reazioni: Mursey

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Programma sviluppato ulteriormente.
Credo che la gestione degli elementi col mouse sia ora completa nei suoi elementi principali. Se premo il tasto destro del mouse compare un menu, che per ora non fa nulla. Col doppio click stampo alcune informazione dell'elemento selezionato. Con CTRL+LMB posso selezionare e deselezionare i singoli elementi.
 
Ultima modifica:

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Il programma è stato sviluppato ulteriormente.
Credo che la gestione tramite mouse sia abbastanza completa, e ogni volta che seleziono o deseleziono qualche elemento il programma me lo comunica a video (stampa qualche messaggio).
Ora usa dati reali. Nel primo post ho descritto la procedura da seguire. Quello che il programma fa è determinare se gli elementi che trova nella cartella DATI sono file o cartelle, facendo apparire l'icona giusta.
Il menù a comparsa (tramite tasto destro) farà apparire una finestra di informazioni se si selezionerà la voce "Uno".
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Ho aggiunto la possibilità di fare scrolling verticale con la rotellina del mouse.
Semplificati alcuni passaggi.
Post unito automaticamente:

Ho modificato il codice affinchè possa essere utilizzato in ambiti più ampi.
Il programma necessita di due file immagini, folder.png e file.png (come preimpostazione devono essere di 64x64 pixels ma accetta tutte le dimensioni).
Di default il programma verifica che tipo di file trova nella cartella di esempio, se cartella o file, e associa la relativa icona. Successivamente l'icona di ogni elemento può essere cambiata. A titolo di esempio, ho inserito in basso al programma un pulsante che se premuto scambia appunto l'icona associata al primo elemento: se il primo elemento è un file verrà assegnata l'icona folder e viceversa. Si tratta di una soluzione molto semplice per quei casi in cui è necessario aggiornare l'icona visualizzata sulla base di eventi interni o esterni al programma.
Credo che mi restano solo un paio di cose per concludere.
 
Ultima modifica:

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Ho ulteriormente sviluppato il programma, implementando il Drag and Drop usando la libreria TkinterDnD2, che va ricercata e installata a parte (la procedura l'ho descritta nel primo post). Ora i listati sono due, il primo è la versione direi definitiva senza DnD, il secondo è una versione leggermente modificato per poter ospitare il supporto, per ora iniziale, al DnD.
Post unito automaticamente:

Non me lo sarei mai aspettato.
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Modificato il codice sulla centratura orizzontale del testo di ogni elemento.
 

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Ho aggiunto ai due listati la seguente funzionalità:
scrolling durante il dragging, sia verso l'alto che verso il basso.
Post unito automaticamente:

Ora la cartella da gestire è un dato che viene passato alla classe principale.
Possibilità di scegliere un font differente da quello predefinito come anche la sua dimensione, per tutti gli elementi del widget.
 
Ultima modifica:

clessidra

Utente Attivo
766
272
CPU
VIA C3
GPU
Ati Rage
OS
linux - Red Hat 1.1
Aggiunti: selezione multipla sequenziale con SHIFT+LMB; possibilità di scegliere la dimensione del font per gli altri elementi e per il menu.
A questo punto credo di aver implementato tutti gli elementi necessari che un widget di tipo iconview dovrebbe avere.
 
Ultima modifica:
  • Mi piace
Reazioni: Mursey

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!