From 81efbcacf2fb6a5c6cb553dfda5d8bcdaa152950 Mon Sep 17 00:00:00 2001 From: Rnhmjoj Date: Fri, 15 Feb 2013 19:28:32 +0100 Subject: [PATCH] Commit iniziale --- .gitignore | 2 + Dropchat.py | 271 ++++++++++++++++++++++++++++++++++++++++++++++++ License.txt | 10 ++ README.md | 31 ++++++ preferenze.json | 1 + setup.py | 12 +++ 6 files changed, 327 insertions(+) create mode 100644 .gitignore create mode 100644 Dropchat.py create mode 100644 License.txt create mode 100644 README.md create mode 100644 preferenze.json create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f678d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +__pycache__/ \ No newline at end of file diff --git a/Dropchat.py b/Dropchat.py new file mode 100644 index 0000000..a1f22f1 --- /dev/null +++ b/Dropchat.py @@ -0,0 +1,271 @@ +import tkinter, tkinter.filedialog, tkinter.messagebox, tkinter.simpledialog +import tempfile, json, datetime, re + +class Text(tkinter.Text): + + def __init__(self, *args, **kwargs): + tkinter.Text.__init__(self, *args, **kwargs) + + def evidenzia(self, pattern, tag, inizio="1.0", fine="end", regex=True): + inizio = self.index(inizio) + fine = self.index(fine) + self.mark_set("matchStart", inizio) + self.mark_set("matchEnd", inizio) + self.mark_set("searchLimit", fine) + occorrenze = tkinter.IntVar() + while True: + indice = self.search(pattern, "matchEnd", "searchLimit", count = occorrenze, regexp = regex) + if indice == "": + break + self.mark_set("matchStart", indice) + self.mark_set("matchEnd", "%s+%sc" % (indice, occorrenze.get())) + self.tag_add(tag, "matchStart", "matchEnd") + +class applicazione(tkinter.Frame): + + def __init__(self, finestra): + tkinter.Frame.__init__(self, finestra) + finestra.iconify() + + #Variabili + self.temp = tempfile.NamedTemporaryFile(mode = "w+t") + self.testo = tkinter.StringVar() + self.opzionifile = { + "parent": finestra, + "filetypes": [("text files", ".txt")], + "defaultextension": ".txt", + "initialfile": "file.txt" + } + self.opzionichat = { + "font": ("Monaco", 13), + "borderwidth": 2, + "highlightthickness": 0 + } + + #Gestione delle preferenze ed eventuali errori + try: + self.preferenze = json.load(open("preferenze.json")) + except FileNotFoundError: + tkinter.messagebox.showwarning("Dropchat", "File delle preferenze non trovato!", detail = "Preferenze di default ricostruite.") + self.creapreferenze() + try: + self.cancellanontrovati() + self.file = self.preferenze["Chat"][-1] + except IndexError: + if tkinter.messagebox.askquestion("Dropchat","Nessuna conversazione trovata.", detail = "Crearne una nuova ?") == "yes": + self.nuovofile() + else: + self.file = "" + + #Gestione della finestra + finestra.geometry(self.preferenze["Geometria"]) + finestra.configure(background = "#a8a8a8") + finestra.columnconfigure(1, weight=1) + finestra.rowconfigure(0, weight=1) + finestra.protocol('WM_DELETE_WINDOW', self.chiudi) + finestra.deiconify() + self.grid() + + #Inizzializzazione e loop + self.cambiatitolo() + self.widgets() + self.leggi() + self.loop() + + #Widget della finestra + def widgets(self): + self.menubar = tkinter.Menu(finestra) + self.chat = Text(finestra, width = 100, height = 30, relief = "sunken", insertbackground = "#fff", **self.opzionichat) + self.sidebar = tkinter.Listbox(finestra, width = 20, borderwidth = 0, background ="#dce0e8") + self.casella = tkinter.Entry(finestra, textvariable = self.testo, **self.opzionichat) + self.chat.grid(column = 1, row = 0, sticky = "nswe", pady = (6, 3), padx = 6) + self.casella.grid(column = 1, row = 1, sticky = "nswe", pady = (3, 6), padx = 6) + self.sidebar.grid(column = 0, row = 0, sticky = "nswe", rowspan = 2) + + #Bindings + self.chat.bind("", "break") + self.sidebar.bind("", self.cambiafile) + self.sidebar.bind("", self.mostramenu) + self.casella.bind("", self.scrivi) + + #Barra dei menu + self.menubar = tkinter.Menu(self) + menu = tkinter.Menu(self.menubar) + #Primo menu + self.menubar.add_cascade(label = "Conversazione", menu = menu) + menu.add_command(label = "Nuovo file", command = self.nuovofile) + menu.add_command(label = "Apri file...", command = self.aprifile) + #Secondo menu + menu = tkinter.Menu(self.menubar) + self.menubar.add_cascade(label="Profilo", menu = menu) + menu.add_command(label = "Nome utente", command = lambda: self.modificapreferenza("Utente", tkinter.simpledialog.askstring("Nome Utente", "Scrivi il nome:", initialvalue = self.preferenze["Utente"], parent = finestra))) + menu.add_command(label = "Chiave", command = lambda: self.modificapreferenza("Chiave", tkinter.filedialog.askopenfilename(title = "Scegli un file...", message = "Apri una chiave di decodifica.", **self.opzionifile))) + #Terzo menu + menu = tkinter.Menu(self.menubar) + self.menubar.add_cascade(label="Modifica", menu = menu) + menu.add_command(label = "Taglia", command = lambda: finestra.focus_get().event_generate("<>")) + menu.add_command(label = "Copia", command = lambda: finestra.focus_get().event_generate("<>")) + menu.add_command(label = "Incolla", command = lambda: finestra.focus_get().event_generate("<>")) + finestra.config(menu=self.menubar) + + #Menu contestuale 1 + self.menu1 = tkinter.Menu(self) + self.menu1.add_command(label = "Apri", command = self.cambiafile) + self.menu1.add_command(label = "Cancella", command = self.cancellafile) + self.menu1.add_separator() + self.menu1.add_command(label = "Nuovo file", command = self.nuovofile) + self.menu1.add_command(label = "Ricarica", command = lambda: self.sidebar.delete(0,"end")) + + #Menu contestuale 2 + self.menu2 = tkinter.Menu(self) + self.menu2.add_command(label = "Nuovo file", command = self.nuovofile) + self.menu2.add_command(label = "Ricarica", command = lambda: self.sidebar.delete(0,"end")) + + + #Legge il file dal buffer e lo scrive nella chat + def leggi(self): + self.aggiorna() + self.temp.seek(0) + self.chat.delete(0.0,"end") + self.chat.insert("end", self.temp.read()) + self.colora() + + #Scrive un messaggio nel file + def scrivi(self, event): + if self.file != "": + with open(self.file, "a") as file: + if self.testo.get() != "": + riga = "[" + datetime.datetime.now().strftime("%d-%m-%y %H:%M") + "] " + self.preferenze["Utente"] + ": " + self.testo.get() + "\n" + self.testo.set("") + file.write(riga) + + #copia il file nel buffer + def aggiorna(self): + open(self.temp.name,"w").close() + if self.file != "": + self.temp.seek(0) + with open(self.file) as testo: + self.temp.write(testo.read()) + + def cambiatitolo(self): + file = re.search("([^/]*)$", self.file) + if self.file == "": + finestra.title("Dropchat") + else: + finestra.title("Dropchat - " + file.group(0)) + + #Colora il testo della chat + def colora(self): + self.chat.tag_configure("data", foreground = "#005d8f") + self.chat.tag_configure("nome", foreground = "#648f00") + self.chat.tag_configure("altronome", foreground = "#de7a31") + self.chat.evidenzia("\[\d+-\d+-\d+ \d+:\d+\]", "data") + for nome in re.findall(" ([a-zA-Z]+): ", self.chat.get(0.0, "end")): + if nome == self.preferenze["Utente"]: + self.chat.evidenzia(nome, "nome") + else: + self.chat.evidenzia(nome, "altronome") + + #Gestisce i menu contestuali + def mostramenu(self, event): + self.sidebar.indice = self.sidebar.nearest(event.y) + if self.sidebar.indice < 0: + self.menu2.post(event.x_root, event.y_root) + return + self.sidebar.activate(self.sidebar.indice) + _, offset, _, altezza = self.sidebar.bbox(self.sidebar.indice) + if event.y > altezza + offset + self.sidebar.size(): + self.menu2.post(event.x_root, event.y_root) + else: + self.menu1.post(event.x_root, event.y_root) + + #Scrive i file aperti nella sidebar + def filerecenti(self): + for x, file in enumerate(self.preferenze["Chat"]): + file = " - " + re.search("([^/]*)$", file).group(0) + if file not in self.sidebar.get(x): + self.sidebar.insert(x, file) + + #Cancella il file selezionato nella sidebar + def cancellafile(self): + if self.preferenze["Chat"][self.sidebar.indice] == self.file: + self.file = "" + del self.preferenze["Chat"][self.sidebar.indice] + json.dump(self.preferenze, open("preferenze.json", "w")) + self.leggi() + self.cambiatitolo() + + #Apre il file selezionato nella sidebar + def cambiafile(self, _ = None): + file = self.preferenze["Chat"][int(self.sidebar.curselection()[0])] + if self.file != file: + self.file = file + self.leggi() + self.cambiatitolo() + + #Apre un file + def aprifile(self): + file = tkinter.filedialog.askopenfilename(title = "Scegli un file...", message = "Apri una conversazione esistente.", **self.opzionifile) + if file not in self.preferenze["Chat"] and file != "": + self.preferenze["Chat"] += file, + json.dump(self.preferenze, open("preferenze.json", "w")) + self.file = file + self.leggi() + self.cambiatitolo() + + #Crea un nuovo file e lo apre + def nuovofile(self): + file = tkinter.filedialog.asksaveasfilename(title = "Crea un file...", message = "Scegli il nome della nuova conversazione.", **self.opzionifile) + if file != "": + self.file = file + file = open(self.file,"w") + file.close() + if self.file not in self.preferenze["Chat"]: + self.preferenze["Chat"] += self.file, + json.dump(self.preferenze, open("preferenze.json", "w")) + self.leggi() + self.cambiatitolo() + + #Cancella i file non trovati dalle preferenze + def cancellanontrovati(self): + for x, file in enumerate(self.preferenze["Chat"]): + try: + open(file) + self.file = file + except FileNotFoundError: + del self.preferenze["Chat"][x] + continue + json.dump(self.preferenze, open("preferenze.json", "w")) + + #Ricrea le preferenze se vengono cancellate + def creapreferenze(self): + default = { + "Utente": "Nomeutente", + "Chat": ["chat.txt"], + "Chiave": "chiave.key", + "Geometria": "800x500+500+250" + } + json.dump(default,open("preferenze.json", "w")) + self.preferenze = json.load(open("preferenze.json")) + + #Cambia una preferenze e la salva + def modificapreferenza(self, chiave, preferenza): + if preferenza != "": + self.preferenze[chiave] = preferenza + json.dump(self.preferenze, open("preferenze.json", "w")) + + #Salva la geometria della finestra e la chiude + def chiudi(self): + self.modificapreferenza("Geometria", finestra.geometry()) + self.temp.close() + finestra.destroy() + + #Loop principale + def loop(self): + self.filerecenti() + self.leggi() + self.after(200, self.loop) + +finestra = tkinter.Tk() +app = applicazione(finestra) +app.mainloop() \ No newline at end of file diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..16f372a --- /dev/null +++ b/License.txt @@ -0,0 +1,10 @@ + * Dropchat + * + * Chat tramite Dropbox in python 3. + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * @author Michele Guerini Rocco aka Rnhmjoj + * @since 2013 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..67ae738 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Dropchat +======== + +Chat tramite Dropbox in python 3. +--------------------------------- + +### Informazioni +Un chat client per chattare tramite le cartelle condivise di Dropbox. +Realizzato usando python 3 e l'interfaccia Tk. + +### Istruzioni +Primo utilizzo: + +Dalla barra dei menu selezionare: +* Profilo -> "Nome Utente" ed immettere il proprio nome. +* Conversazione -> "Nuovo file" o "Apri file.." e scegliere un file di testo +Scrivere un messaggio nella casella di testo e dare invio per immetterlo nel file. +Una volta scritto il file viene sincronizzato con Dropbox in modo che altri possano rispondere. + +I file recenti vengono elencati nella barra laterale e l'ultimo utilizzato viene riaperto all'avvio. +Per cancellare definitivamente un file basta spostarlo nel cestino mentre per rimuovere il riferimento dall'applicazione fare click destro sul file nella barra laterale e selezionare "Cancella". + +### Conversione +È incluso un setup.py per convertire Dropchat.py in un binario tramite py2app o py2exe. +Utilizzo: + + python setup.py py2app + +o + + python setup.py py2exe \ No newline at end of file diff --git a/preferenze.json b/preferenze.json new file mode 100644 index 0000000..bb0e537 --- /dev/null +++ b/preferenze.json @@ -0,0 +1 @@ +{"Chat": [], "Utente": "Nomeutente", "Chiave": "chiave.key", "Geometria": "800x500+500+250"} \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..878fc85 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +from setuptools import setup + +APP = ['dropchat.py'] +DATA_FILES = ['preferenze.json'] +OPTIONS = {'argv_emulation': False} + +setup( + app=APP, + data_files=DATA_FILES, + options={'py2app': OPTIONS}, + setup_requires=['py2app'], +)