208 lines
5.5 KiB
Python
208 lines
5.5 KiB
Python
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("<Button-1>", canvas_save_pos)
|
|
canvas.bind("<B1-Motion>", 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("<KeyRelease>", on_area_input)
|
|
area.bind("<B3-Motion>", 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()
|