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)
(Versione attualmente in fase di completamento, con Drag and Drop)
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.
(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()
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()
- 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: