import network import tkinter as tk from tkinter import ttk, filedialog, colorchooser import sys import colorsys import random def random_color(seed): return "#"+"".join([ hex(int(i*255))[2:].zfill(2) for i in colorsys.hsv_to_rgb(random.Random(seed).uniform(0.0, 1.0), 0.8, 1) ]) listen_addr = ("0.0.0.0", int(sys.argv[1])) send_addr = ("192.168.0.255", int(sys.argv[2])) sock = network.Sock() waiting_for_text = True color = "#000000" root = tk.Tk() root.title("Éditeur en réseau") root.option_add('*tearOff', False) menubar = tk.Menu(root) root['menu'] = menubar menu_file = tk.Menu(menubar) menu_edit = tk.Menu(menubar) menubar.add_cascade(menu=menu_file, label='Fichier') menubar.add_cascade(menu=menu_edit, label='Édition') area = tk.Text(root) area.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) area_scrolly = ttk.Scrollbar(root, orient=tk.VERTICAL, command=area.yview) area_scrolly.grid(column=1, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) area.configure(yscrollcommand=area_scrolly.set) area_net = tk.Text(root, width=21) area_net["state"] = "disabled" area_net.grid(column=3, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) area_net_scrolly = ttk.Scrollbar(root, orient=tk.VERTICAL, command=area_net.yview) area_net_scrolly.grid(column=4, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) area_net.configure(yscrollcommand=area_net_scrolly.set) canvas = tk.Canvas(root, background='white') canvas.grid(column=2, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) canvas_last_x = 0 canvas_last_y = 0 def on_save_bt(): path = tk.filedialog.asksaveasfilename(filetypes=[("Fichier texte", ".txt"), ("Autre", ".*")]) if path != "": f = open(path, "w") f.write(area.get("1.0", "end")) f.close() def on_open_bt(): path = tk.filedialog.askopenfilename(filetypes=[("Fichier texte", ".txt"), ("Autre", ".*")]) if path != (): f = open(path, "r") if f: area.replace("1.0", "end", f.read()) f.close() def on_color_bt(): global color color = tk.colorchooser.askcolor(initialcolor=color)[1] def canvas_save_pos(event): global canvas_last_x, canvas_last_y canvas_last_x = event.x canvas_last_y = event.y def canvas_move(event): global color canvas.create_line(canvas_last_x, canvas_last_y, event.x, event.y, fill=color) sock.send({ "type":"create_line", "x1":canvas_last_x, "y1":canvas_last_y, "x2":event.x, "y2":event.y, "color":color }, send_addr) canvas_save_pos(event) menu_file.add_command(label='Ouvrir', command=on_open_bt) menu_file.add_command(label='Enregistrer', command=on_save_bt) canvas.bind("", canvas_save_pos) canvas.bind("", canvas_move) root.rowconfigure(1, weight=1) root.columnconfigure(0, weight=1) root.columnconfigure(2, weight=1) toolbar = ttk.Frame(root) toolbar.grid(column=0, row=0, columnspan=2, sticky=(tk.N, tk.S, tk.E, tk.W)) save_bt = ttk.Button(toolbar, text="Enregistrer", command=on_save_bt) save_bt.grid(column=0, row=0, sticky=(tk.W)) open_bt = ttk.Button(toolbar, text="Ouvrir", command=on_open_bt) open_bt.grid(column=1, row=0, sticky=(tk.W)) color_bt = ttk.Button(toolbar, text="Couleur", command=on_color_bt) color_bt.grid(column=2, row=0, sticky=(tk.W)) old_text = "" def diff(old, new): if old == new: return None index = 0 for i in range(min(len(old), len(new))): if old[i] != new[i]: return { "type": "replace", "start": "1.0+"+str(i)+"c", "end": "1.0+"+str(i+abs(len(old) - len(new))-1)+"c", "text": new[i:i+abs(len(old)-len(new))] } if len(old) > len(new): return { "type": "replace", "start": "end-"+str(len(old)-len(new))+"c", "end": "end", "text": "" } else: return { "type": "insert", "start": "end", "text": new[len(old):] } def on_area_input(event): global old_text d = diff(old_text, area.get("1.0","end")) if d == None: return sock.send(d, send_addr) old_text = area.get("1.0","end") area.bind("", on_area_input) area.bind("", on_area_input) authors = {} def set_author(author): author = author[0]+"."+str(author[1]) if author in authors: return author author_color = random_color(author.encode()) authors[author] = author_color area.tag_configure(author, background=author_color) area_net.tag_configure(author, background=author_color) area_net["state"] = "normal" area_net.insert("end", author+"\n", author) area_net["state"] = "disabled" return author def on_request(r): global waiting_for_text, old_text print(r) c, a = r if type(c) != dict: return if "message" in c: area.replace("1.0", "end", c["message"], set_author(a)) waiting_for_text = False old_text = area.get("1.0","end") if not "type" in c: return if c["type"] == "set": if not "text" in c: return if waiting_for_text: if c["text"] != "\n": area.replace("1.0", "end", c["text"]) waiting_for_text = False old_text = area.get("1.0","end") elif c["type"] == "insert": if not "text" in c or not "start" in c: return area.insert(c["start"], c["text"], set_author(a)) old_text = area.get("1.0","end") elif c["type"] == "replace": if not "text" in c or not "start" in c or not "end" in c: return area.replace(c["start"], c["end"], c["text"], set_author(a)) old_text = area.get("1.0","end") elif c["type"] == "get": sock.send({"type":"set", "text":area.get("1.0", "end")}, send_addr) elif c["type"] == "create_line": if not "color" in c or not "x1" in c or not "y1" in c or not "x2" in c or not "y2" in c: return canvas.create_line(c["x1"], c["y1"], c["x2"], c["y2"], fill=c["color"]) sock.listen(listen_addr, on_request) #sock.send({"type":"get"}, send_addr) root.mainloop()