'''
ARGOMENTI: 
---------------------------------------------------------------------

    - Canvas, gestione oggetti mediante ID, event_generate


    - Variabili globali
        in questo esempio si e' utilizzato (volutamente) una serie di 
        variabili globali visibili da tutte le funzioni utilizzate.
        Quando una funzione ha bisogno di modificare una o piu' variabili
        globali e' necessario dichiararle al suo interno come global

            Es. global xSTA, ySTA, TIPO, newIdOGG, oldIdOGG

'''
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
from math import fabs
import random
import os

# =================================================================
# Funzioni
# =================================================================
def f_Stato_Iniziale():
    CNV.create_rectangle(1,1,xCAN,yCAN, fill=cnvBK)
    CNV.create_line(1,1,yCAN,yCAN, fill="red", dash=(4, 4))
    CNV.create_line(1,yCAN,xCAN,1, fill="red", dash=(4, 4))
    lstITEM.delete(0, END)
 
# -----------------------------------------------------------------
def f_Press(event):
    '''
        Modifica le seguenti variabili globali:
            - xSTA e ySTA: assegnando le coordinate di "partenza" dell'oggetto, 
            - TIPO: assegnando il tipo dell'oggetto [Linea (default), Rettangolo o Ovale] ricavato dai radioBUTTON
            - newIdOGG, oldIdOGG: assegnando identificativo "None" agli oggetti (verranno creati successivamente durante il "motion") 
            - COLORATO: assegnando valore "0" (non colorato) 
    '''
    global xSTA, ySTA, TIPO, newIdOGG, oldIdOGG, COLORATO
    newIdOGG= None
    oldIdOGG= None
    TIPO=varRADIO.get()
    xSTA=event.x
    ySTA=event.y
    COLORATO=0

# -----------------------------------------------------------------
def f_Motion(event):
    '''
        La funzione crea un oggetto e lo elimina immediatamente ad ogni modifica
        delle coordinate xEND,yEND assegnate durante il "motion".
        Quando termina la fase "motion" (ricordare che si verifica con il 
        tasto SX del mouse premuto) resta visibile l'ultimo oggetto creato.

        Le variabili globali che vengono modificate sono:
            - xEND, yEND: coordinate "finali" dell'oggetto
            - gli ID degli oggetti (quello che serve effettivamentea a livello globale è 
              "newIdOGG", "oldIdOGG" serve solo all'interno di questa funzione per 
              tenere traccia dell'oggetto da eliminare dopo la modifica delle coordinate "finali")

    '''
    global xEND, yEND, newIdOGG, oldIdOGG
    xEND=event.x
    yEND=event.y


    # Restringe l'area di puntamento del mouse all'interno del canvas
    if event.x < 0: 
        CNV.event_generate('<Motion>', warp=True, x=0, y=event.y)
    elif event.y < 0: 
        CNV.event_generate('<Motion>', warp=True, x=event.x, y=0)
    elif event.x > xCAN: 
        CNV.event_generate('<Motion>', warp=True, x=xCAN, y=event.y)
    elif event.y > yCAN: 
        CNV.event_generate('<Motion>', warp=True, x=event.x, y=yCAN)


    COORDINATE=xSTA,ySTA,xEND,yEND
    if TIPO=="1":
        newIdOGG = CNV.create_line(COORDINATE, width=4, fill="black")
    elif TIPO=="2":
        newIdOGG = CNV.create_rectangle(COORDINATE, outline="black")
    else: # TIPO=3
        newIdOGG = CNV.create_oval(COORDINATE, outline="black")
    
    # Se esiste un oggetto creato precedentemente (oldIdOGG) -> viene eliminato
    if oldIdOGG != None:
        CNV.delete(oldIdOGG)

    # il nuovo oggetto viene salvato in oldIdOGG
    oldIdOGG=newIdOGG

# -----------------------------------------------------------------
def f_Release(event):
    '''
        Al rilascio del tasto SX del mouse viene richiamata questa funzione
        che completa la procedura di creazione dell'oggetto inserendo
        in "lstITEM" tutte le caratteristiche dell'oggetto stesso.
        NOTA: i dati da inserire sono memorizzati nelle variabili globali
    '''
    if newIdOGG==None: return
    lstITEM.insert(END, f_setItemList())

# -----------------------------------------------------------------
def f_SwapColor(event):

    '''
        Scambia il colore dell'oggetto, o meglio, per i tipi OVAL e RECTANGLE
        abilita o meno il "fill"

        Una volta individuato un oggetto dalla "lstITEM"
            - viene inserito uno nuovo oggetto (sia sul CANVAS che nella "lstITEM") 
              con le stesse caratteristiche e con la sola modifica del FILL
            - viene eliminato il vecchio oggetto (sia sul CANVAS che nella "lstITEM")
    '''

    global newIdOGG, COLORATO

    # recupera la stringa dall'elemento attivo della "lstITEM"
    str=lstITEM.get(ACTIVE)

    # se la "lstITEM" è vuota, viene restituita una stringa vuota
    if str=="": 
        messagebox.showinfo("Info", "Nessun oggetto selezionato")
        return

    # vengono assegnate tutte le variabili globali a partire dalla stringa recuperata nella "lstITEM"
    f_setVarGlobalList(str)

    COORDINATE=xSTA,ySTA,xEND,yEND

    # eliminazione vecchio oggetto ed inserimento del nuovo sul CANVAS
    CNV.delete(newIdOGG)
    f=""
    if TIPO=="1":
        if COLORATO==0:
            f="red"
        else:
            f="black"
        newIdOGG=CNV.create_line(COORDINATE, width=4, fill=f)
    elif TIPO=="2":
        if COLORATO==0: f="orange"
        newIdOGG=CNV.create_rectangle(COORDINATE, fill=f, outline="black")
    else: # TIPO=3
        if COLORATO==0: f="yellow"
        newIdOGG=CNV.create_oval(COORDINATE, fill=f, outline="black")

    # scambia 0->1, 1->0
    COLORATO=int(fabs(COLORATO-1))

    # inserimento nuovo oggetto ed eliminazione del vecchio nella "lstITEM"
    # NOTA: per gli elementi della lista, mantenere questa sequenza delle istruzioni
    lstITEM.insert(ACTIVE, f_setItemList())
    lstITEM.delete(ACTIVE)

 # -----------------------------------------------------------------  
def f_Delete(event):
    '''
        Elimina un oggetto sia dal "canvas" che da "lstITEM"
    '''

    str=lstITEM.get(ACTIVE)

    # doppioClick su "lstITEM" vuota
    if str=="": 
        messagebox.showinfo("Info", "Nessun oggetto selezionato")
        return

    ris=messagebox.askyesno("Elimina", "Conferma eliminazione\n Item: <"+str+">")

    if ris:  
        dati=str.split("*")

        # l'unico dato che serve è l'id dell'oggetto
        idOGG= dati[1]

        lstITEM.delete(ACTIVE)
        CNV.delete(idOGG)

# -----------------------------------------------------------------
def f_setItemList():
    '''
        Funzione utility: costruisce una stringa (da inserire nella "lstITEM")
        a partire da tutte le variabili globali
    '''
    sINS=str(TIPO) + "*" + str(newIdOGG) + "*"
    sINS=sINS + str(xSTA) + "*" + str(ySTA) + "*"
    sINS=sINS + str(xEND) + "*" + str(yEND) + "*"
    sINS=sINS + str(COLORATO)
    return sINS

# -----------------------------------------------------------------
def f_setVarGlobalList(str):
    '''
        Funzione utility: recupera dalla stringa (ricavata dalla "lstITEM")
        tutti i dati da assegnare alle variabili globali
    '''
    global xSTA, ySTA, xEND, yEND, TIPO, newIdOGG, oldIdOGG, COLORATO
    dati     = str.split("*")
    TIPO     = dati[0]
    newIdOGG = int(dati[1])
    xSTA     = int(dati[2])
    ySTA     = int(dati[3])
    xEND     = int(dati[4])
    yEND     = int(dati[5])
    COLORATO = int(dati[6])

    # NOTA: non vi è alcun bisogno di memorizzare nella "lstITEM" le coordinate
    #       dell'oggetto perche' sono "reperibili" conoscendo l'ID. 
    #       Si puo' verificare nella console la corrispondenza tra i valori
    #       della lista e quelli stampati dall'istruzione seguente

    print (CNV.coords(newIdOGG))

# -----------------------------------------------------------------
def f_Salva():
    '''
        Salva il "canvas" in formato "PostScript"
    '''
    ris = filedialog.asksaveasfilename( initialdir=os.getcwd(),
                                        title="Salva file:",
                                        filetypes=[("PostScript",".ps")])
    if ris:
        CNV.postscript(file=ris, colormode="color")
        messagebox.showinfo("Salva", "File salvato correttamente")
    else:
        messagebox.showinfo("Salva", "Salvataggio annullato")

# -----------------------------------------------------------------


# =================================================================
# LAYOUT
# =================================================================

cnvBK="white"   # colore di sfondo del canvas

# definizione della finestra
fin = Tk()
fin.title("Applicazione")
fin.geometry("570x500+300+50")
fin.resizable(width=False, height=False)


# -------------------------------------------------------------
# labelframe CANVAS - "lstITEM" ITEM
# -------------------------------------------------------------
lblFRA_1=LabelFrame(fin, text="Canvas", pady=20)
xCAN=yCAN=300
CNV = Canvas(lblFRA_1, bg=cnvBK, cursor="dot", height=yCAN, width=xCAN)
CNV.pack(side=LEFT)
lbl = Label(text="CANVAS")
CNV.create_window(10, 10, window=lbl, anchor=N+W)

# "lstITEM" ITEM  con scrollbar
scrITEM = Scrollbar(lblFRA_1)                       # definisco la scrollbar
lstITEM = Listbox(lblFRA_1,yscrollcommand=scrITEM.set, width=30)   # definisco la "lstITEM" con scrollbar associata
scrITEM.config(command=lstITEM.yview)           # collego la scrollbar alla "lstITEM"
lstITEM.pack(side=LEFT, fill=Y)
scrITEM.pack(side=LEFT, fill=Y)

lblFRA_1.pack(fill="x")     



# -------------------------------------------------------------
# labelframe AZIONI
# -------------------------------------------------------------
lblFRA_2=LabelFrame(fin, text="Azioni", padx=10, pady=10)
lblFRA_2.pack(pady=(20, 0)) # padding verticale 20:nord, 0:sud

# radio button
varRADIO=StringVar()
varRADIO.set("1") # default
rbt1 = Radiobutton(lblFRA_2, text="Linea", variable=varRADIO, value="1")
rbt1.grid(row=0,column=0, sticky="we") 
rbt2 = Radiobutton(lblFRA_2, text="Rettangolo", variable=varRADIO, value="2")
rbt2.grid(row=0,column=1, sticky="we") 
rbt3 = Radiobutton(lblFRA_2, text="Ovale", variable=varRADIO, value="3")
rbt3.grid(row=0,column=2, sticky="we") 

# Altro 
btn_CLE = Button(lblFRA_2, text="Clear", command=f_Stato_Iniziale).grid(row=1,column=0, sticky="we")  
btn_SAV = Button(lblFRA_2, text="Salva PostScript", command=f_Salva).grid(row=1,column=1, columnspan=2,sticky="we")
lbl = Label(lblFRA_2, text="Lista_Doppio_Click: swap fill").grid(row=2,column=0, columnspan=3,sticky="we") 
lbl = Label(lblFRA_2, text="Lista_Click_DX: elimina").grid(row=3,column=0, columnspan=3,sticky="we")   
# -------------------------------------------------------------


# -------------------------------------------------------------
# VARIABILI GLOBALI
# -------------------------------------------------------------
xSTA=xEND=ySTA=yEND=0
newIdOGG = oldIdOGG= None
TIPO=1
COLORATO=0


# =================================================================
# BIND: canvas e lista
# =================================================================
CNV.bind("<ButtonPress-1>",     f_Press)      
CNV.bind("<B1-Motion>",         f_Motion)            
CNV.bind("<ButtonRelease-1>",   f_Release)  
lstITEM.bind("<Double-1>",      f_SwapColor)
lstITEM.bind("<ButtonPress-3>", f_Delete)   


f_Stato_Iniziale()
fin.mainloop()
