Fixes, IP list

This commit is contained in:
Pascal Engélibert 2024-10-30 17:07:15 +01:00
commit cd0bf3113e
3 changed files with 119 additions and 43 deletions

124
editor.py
View file

@ -2,26 +2,33 @@ import network
import tkinter as tk import tkinter as tk
from tkinter import ttk, filedialog, colorchooser from tkinter import ttk, filedialog, colorchooser
import sys 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])) listen_addr = ("0.0.0.0", int(sys.argv[1]))
send_addr = ("192.168.1.255", int(sys.argv[2])) send_addr = ("192.168.0.255", int(sys.argv[2]))
sock = network.Sock() sock = network.Sock()
waiting_for_text = True waiting_for_text = True
color = "#000000"
root = tk.Tk() root = tk.Tk()
root.title("Éditeur en réseau")
def foo():
print("foo")
root.option_add('*tearOff', False) root.option_add('*tearOff', False)
menubar = tk.Menu(root) menubar = tk.Menu(root)
root['menu'] = menubar root['menu'] = menubar
menu_file = tk.Menu(menubar) menu_file = tk.Menu(menubar)
menu_edit = tk.Menu(menubar) menu_edit = tk.Menu(menubar)
menubar.add_cascade(menu=menu_file, label='File') menubar.add_cascade(menu=menu_file, label='Fichier')
menubar.add_cascade(menu=menu_edit, label='Edit') menubar.add_cascade(menu=menu_edit, label='Édition')
menu_file.add_command(label='New', command=foo)
area = tk.Text(root) area = tk.Text(root)
area.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) area.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W))
@ -30,47 +37,79 @@ 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_scrolly.grid(column=1, row=1, sticky=(tk.N, tk.S, tk.E, tk.W))
area.configure(yscrollcommand=area_scrolly.set) 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 = tk.Canvas(root, background='white')
canvas.grid(column=2, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) canvas.grid(column=2, row=1, sticky=(tk.N, tk.S, tk.E, tk.W))
canvas_last_x = 0 canvas_last_x = 0
canvas_last_y = 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): def canvas_save_pos(event):
global canvas_last_x, canvas_last_y global canvas_last_x, canvas_last_y
canvas_last_x = event.x canvas_last_x = event.x
canvas_last_y = event.y canvas_last_y = event.y
def canvas_move(event): def canvas_move(event):
canvas.create_line(canvas_last_x, canvas_last_y, event.x, event.y) global color
sock.send({"type":"create_line", "x1":canvas_last_x, "y1":canvas_last_y, "x2":event.x, "y2":event.y}, send_addr) 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) canvas_save_pos(event)
def canvas_click(event): menu_file.add_command(label='Ouvrir', command=on_open_bt)
print(colorchooser.askcolor(initialcolor='black')) menu_file.add_command(label='Enregistrer', command=on_save_bt)
canvas.bind("<Button-1>", canvas_save_pos) canvas.bind("<Button-1>", canvas_save_pos)
canvas.bind("<B1-Motion>", canvas_move) canvas.bind("<B1-Motion>", canvas_move)
canvas.bind("<Button-3>", canvas_click)
root.rowconfigure(1, weight=1) root.rowconfigure(1, weight=1)
root.columnconfigure(0, weight=1) root.columnconfigure(0, weight=1)
root.columnconfigure(2, weight=1) root.columnconfigure(2, weight=1)
def on_save_bt():
path = tk.filedialog.asksaveasfilename(filetypes=[("Fichier texte", ".txt")])
file = open(path, "w")
file.write(area.get("1.0", "end"))
toolbar = ttk.Frame(root) toolbar = ttk.Frame(root)
toolbar.grid(column=0, row=0, columnspan=2, sticky=(tk.N, tk.S, tk.E, tk.W)) 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 = ttk.Button(toolbar, text="Enregistrer", command=on_save_bt)
save_bt.grid(column=0, row=0, sticky=(tk.W)) save_bt.grid(column=0, row=0, sticky=(tk.W))
open_bt = ttk.Button(toolbar, text="Ouvrir") open_bt = ttk.Button(toolbar, text="Ouvrir", command=on_open_bt)
open_bt.grid(column=1, row=0, sticky=(tk.W)) 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 = "" old_text = ""
def diff(old, new): def diff(old, new):
if old == new: if old == new:
@ -101,46 +140,69 @@ def diff(old, new):
def on_area_input(event): def on_area_input(event):
global old_text global old_text
#print(area.get("1.0", "end"))
print(event)
#sock.send({"type":"insert", "start":"1.0", "text":area.get("1.0", "end")}, send_addr)
#sock.send({"type":"replace", "start":"1.0", "end":"end", "text":area.get("1.0", "end")}, send_addr)
d = diff(old_text, area.get("1.0","end")) d = diff(old_text, area.get("1.0","end"))
if d == None: if d == None:
return return
sock.send(d, send_addr) sock.send(d, send_addr)
old_text = area.get("1.0","end") old_text = area.get("1.0","end")
#if event.keycode == 25:
# area.mark_set("insert", "1.0")
area.bind("<KeyRelease>", on_area_input) area.bind("<KeyRelease>", on_area_input)
area.bind("<B3-Motion>", on_area_input) area.bind("<B3-Motion>", on_area_input)
area.tag_configure("plop", background="#ffaaaa") 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): def on_request(r):
global waiting_for_text, old_text global waiting_for_text, old_text
print(r) print(r)
c, a = 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 c["type"] == "set":
if not "text" in c:
return
if waiting_for_text: if waiting_for_text:
area.replace("1.0", "end", c["text"]) if c["text"] != "\n":
area.replace("1.0", "end", c["text"])
waiting_for_text = False waiting_for_text = False
old_text = area.get("1.0","end") old_text = area.get("1.0","end")
elif c["type"] == "insert": elif c["type"] == "insert":
area.insert(c["start"], c["text"], "plop") 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") old_text = area.get("1.0","end")
elif c["type"] == "replace": elif c["type"] == "replace":
area.replace(c["start"], c["end"], c["text"], "plop") 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") old_text = area.get("1.0","end")
elif c["type"] == "get": elif c["type"] == "get":
sock.send({"type":"set", "text":area.get("1.0", "end")}, send_addr) sock.send({"type":"set", "text":area.get("1.0", "end")}, send_addr)
elif c["type"] == "create_line": elif c["type"] == "create_line":
canvas.create_line(c["x1"], c["y1"], c["x2"], c["y2"]) 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.listen(listen_addr, on_request)
sock.send({"type":"get"}, send_addr) #sock.send({"type":"get"}, send_addr)
root.mainloop() root.mainloop()

View file

@ -11,8 +11,10 @@ class Sock:
self.queue = [] self.queue = []
self.queue_lock = Lock() self.queue_lock = Lock()
self.sock_thread = None self.sock_thread = None
self.has_sent = False
def send_raw(self, message, address): def send_raw(self, message, address):
self.has_sent = True
if type(message) == str: if type(message) == str:
message = message.encode() message = message.encode()
if NETTRACE: if NETTRACE:
@ -21,6 +23,7 @@ class Sock:
# Envoie une requete # Envoie une requete
def send(self, message, address): def send(self, message, address):
self.has_sent = True
message = json.dumps(message) message = json.dumps(message)
if NETTRACE: if NETTRACE:
print("Send to", tuple(address), ":", message, file=sys.stderr) print("Send to", tuple(address), ":", message, file=sys.stderr)
@ -30,6 +33,8 @@ class Sock:
# Si la fonction callback est donnee, elle sera appelee avec les requetes recues. # Si la fonction callback est donnee, elle sera appelee avec les requetes recues.
# La methode get ne fonctionnera pas si un callback est defini. # La methode get ne fonctionnera pas si un callback est defini.
def listen(self, address, callback=None, length=65535): def listen(self, address, callback=None, length=65535):
if self.has_sent:
print("Attention, listen ne fonctionne pas si send a ete appele avant.")
self.sock_thread = SockThread(self, tuple(address), length, callback) self.sock_thread = SockThread(self, tuple(address), length, callback)
self.sock_thread.setDaemon(True) self.sock_thread.setDaemon(True)
self.sock_thread.start() self.sock_thread.start()

View file

@ -1,6 +1,14 @@
# Tutoriel Tkinter # Tutoriel Tkinter
Pour aller plus loin : [TkDocs Tutorial](https://tkdocs.com/tutorial/index.html) **Prérequis** :
Il faut être un peu à l'aise avec le Python, au moins les variables, fonctions, manipulation des chaînes de caractères.
Si ce n'est pas le cas, [France-IOI](https://www.france-ioi.org/algo/chapters.php) propose une introduction ludique au Python.
Tkinter est une bibliothèque d'interface graphique, qui permet de créer des fenêtres sous Linux, Windows et Mac.
C'est une des plus simples à utiliser en Python.
Seulement elle est un peu vieille et trop peu intuitive, d'où ce tutoriel qui peut servir de mini-référence.
Pour aller plus loin : [TkDocs Tutorial](https://tkdocs.com/tutorial/index.html) (en anglais)
## Code de base ## Code de base
@ -64,7 +72,8 @@ On peut appeler plusieurs fois ces fonctions pour choisir plusieurs lignes et co
## Widgets ## Widgets
Tous les widgets peuvent être positionnés avec `grid`, de la même manière que ci-dessus. Tous les widgets doivent être positionnés avec `grid`, de la même manière que ci-dessus.
Sans ça, ils ne seront pas affichés !
### Label ### Label
@ -128,21 +137,21 @@ text = tk.Text(root)
Il y a plusieurs méthodes pour interagir avec le texte : Il y a plusieurs méthodes pour interagir avec le texte :
```python ```python
print(area.get("1.0", "end")) # obtenir tout le texte print(text.get("1.0", "end")) # obtenir tout le texte
print(area.get("2.0", "8.0")) # le texte de la ligne 2 à la ligne 8 print(text.get("2.0", "8.0")) # le texte de la ligne 2 à la ligne 8
area.replace("1.0", "end", "le nouveau texte") # remplacer du texte text.replace("1.0", "end", "le nouveau texte") # remplacer du texte
area.insert("1.0", "le nouveau texte") # insérer du texte text.insert("1.0", "le nouveau texte") # insérer du texte
area.insert("1.0 +42 chars", "le nouveau texte") text.insert("1.0 +42 chars", "le nouveau texte")
area.insert("end -3 lines", "le nouveau texte") text.insert("end -3 lines", "le nouveau texte")
``` ```
Pour mettre en forme certaines parties du texte, on peut utiliser des tags : Pour mettre en forme certaines parties du texte, on peut utiliser des tags :
```python ```python
area.tag_configure("fond_rouge", background="#ffaaaa") text.tag_configure("fond_rouge", background="#ffaaaa")
area.tag_configure("insistance", foreground="#008800", underline=True) text.tag_configure("insistance", foreground="#008800", underline=True)
area.insert("1.0", "ce texte sera sur fond rouge", "fond_rouge") text.insert("1.0", "ce texte sera sur fond rouge", "fond_rouge")
text.tag_add("insistance", "1.3", "1.8") text.tag_add("insistance", "1.3", "1.8")
``` ```
@ -220,7 +229,7 @@ def onrelease(event):
print("On a frappé le clavier !") print("On a frappé le clavier !")
print(event) print(event)
area.bind("<KeyRelease>", onrelease) text.bind("<KeyRelease>", onrelease)
``` ```
On peut aussi utiliser les infos contenues dans l'événement, par exemple `event.keycode`. On peut aussi utiliser les infos contenues dans l'événement, par exemple `event.keycode`.