diff --git a/.idea/NetworkAnalog.iml b/.idea/NetworkAnalog.iml
index 8fb85ed..c444878 100644
--- a/.idea/NetworkAnalog.iml
+++ b/.idea/NetworkAnalog.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ebb0faf..a2e120d 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/NetworkAnalog/NetworkAnalog.py b/NetworkAnalog/NetworkAnalog.py
index 7622f79..3ab7bcc 100644
--- a/NetworkAnalog/NetworkAnalog.py
+++ b/NetworkAnalog/NetworkAnalog.py
@@ -1,226 +1,36 @@
import ipaddress
import sys
import threading
-import ttkbootstrap as tk
-from ttkbootstrap import *
-from ttkbootstrap import ttk
-from tkinter import messagebox
-import re
-from PIL import ImageTk, Image
+from tkinter import filedialog
+
+from SimUtil import *
from SimObjs import SimPacket, SimHost, AllSimConnect, SimRouter, SimSwitch, SimHub, SimBase
from dbUtil import search, execute_sql, delete_obj, truncate_db
+from PIL import Image as imim
-def round_rectangle(cv, x1, y1, x2, y2, radius=30, **kwargs):
- """
- 绘制圆角矩形
- :param cv: canvas对象
- :param radius: 圆角值
- :return:
- """
- points = [x1 + radius, y1,
- x1 + radius, y1,
- x2 - radius, y1,
- x2 - radius, y1,
- x2, y1,
- x2, y1 + radius,
- x2, y1 + radius,
- x2, y2 - radius,
- x2, y2 - radius,
- x2, y2,
- x2 - radius, y2,
- x2 - radius, y2,
- x1 + radius, y2,
- x1 + radius, y2,
- x1, y2,
- x1, y2 - radius,
- x1, y2 - radius,
- x1, y1 + radius,
- x1, y1 + radius,
- x1, y1]
-
- return cv.create_polygon(points, **kwargs, smooth=True)
-
-def validate_ip_address(ip_address):
- """
- 匹配ip地址格式是否规范
- :param ip_address: IP地址
- :return: Boolean
- """
- # 定义IP地址的正则表达式模式
- pattern_with_subnet = r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})$'
- pattern_without_subnet = r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$'
- # 使用re模块进行匹配
- match_with_subnet = re.match(pattern_with_subnet, ip_address)
- match_without_subnet = re.match(pattern_without_subnet, ip_address)
- if match_with_subnet:
- # 带有子网掩码的IP地址
- # 检查每个组件的取值范围是否在 0-255 之间
- for group in match_with_subnet.groups()[:4]:
- if not (0 <= int(group) <= 255):
- return False
- # 检查子网掩码的取值范围是否在 0-32 之间
- subnet_mask = int(match_with_subnet.groups()[4])
- if not (0 <= subnet_mask <= 32):
- return False
- return True
- elif match_without_subnet:
- # 不带子网掩码的IP地址
- # 检查每个组件的取值范围是否在 0-255 之间
- for group in match_without_subnet.groups():
- if not (0 <= int(group) <= 255):
- return False
- return True
- else:
- # IP地址格式不正确
- return False
-
-
-class RouterConfigWindow(tk.Toplevel):
- def __init__(self, parent, router_obj):
- super().__init__(parent)
- self.geometry("435x433+350+200")
- self.title(f"{router_obj.ObjLabel}路由表配置")
- self.router_obj = router_obj
- self.interface_entries = []
- self.router_table = {}
- self.create_interface_inputs()
- self.create_router_table()
-
- def create_interface_inputs(self):
- label_text = ["接口1", "接口2", "接口3", "接口4"]
- for i in range(4):
- label = tk.Label(self, text=label_text[i], font=("黑体", 16))
- label.grid(row=i, column=0, padx=10, pady=5, sticky="w")
- entry = tk.Entry(self, width=20, font=("黑体", 16),)
- entry.grid(row=i, column=1, padx=10, pady=5, sticky="w")
- self.interface_entries.append(entry)
- button = tk.Button(self, text="添加", font=("黑体", 16), command=lambda index=i: self.add_router_entry(index))
- button.grid(row=i, column=2, padx=10, pady=5)
- lab = LabelFrame(self, text="示例")
- lab.grid(row=4, column=0, columnspan=3, sticky=W, padx=20)
- Label(lab, text="10.1.2.0/24 或者 10.1.2.12" if self.router_obj.ObjType == 2 else "MAC11", font=("黑体", 16)).pack()
-
- def create_router_table(self):
- def on_right_click(event):
- row = self.router_treeview.identify_row(event.y) # 获取鼠标位置的行索引
- if row:
- self.router_treeview.selection_set(row) # 选中该行
- delete_menu.post(event.x_root, event.y_root) # 在鼠标位置弹出删除菜单
-
- def delete_row():
- selected_items = self.router_treeview.selection() # 获取选中的行
- for item in selected_items:
- ifs, network = int(self.router_treeview.item(item)["values"][0][-1:]), self.router_treeview.item(item)["values"][1]
- self.router_obj.delete_config(ifs, network)
- self.router_treeview.delete(item)
- self.router_table_frame = tk.Frame(self)
- self.router_table_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
- style = ttk.Style()
- style.configure("Custom.Treeview.Heading", font=("宋体", 15))
- style.configure("Custom.Treeview", rowheight=30, font=("宋体", 15))
- self.router_treeview = ttk.Treeview(self.router_table_frame, style="Custom.Treeview", columns=("Interface", "Route"), show="headings")
- self.router_treeview.heading("Interface", text="接口")
- self.router_treeview.heading("Route", text="网段")
- self.router_treeview.pack(side="left", fill="both")
- scrollbar = ttk.Scrollbar(self.router_table_frame, orient="vertical", command=self.router_treeview.yview)
- scrollbar.pack(side="right", fill="y")
- self.router_treeview.configure(yscrollcommand=scrollbar.set)
- self.router_table = self.router_obj.router_table
- self.router_treeview.bind("", on_right_click)
- # 创建删除菜单
- delete_menu = tk.Menu(root, tearoff=False)
- delete_menu.add_command(label="删除", command=delete_row)
- self.update_router_table()
-
- def add_router_entry(self, index):
- entry_text = self.interface_entries[index].get()
- try:
- ipaddress.ip_network(entry_text)
- if isinstance(self.router_obj, SimRouter):
- if not validate_ip_address(entry_text):
- messagebox.showerror("注意", message="添加的网段信息格式不合格")
- self.interface_entries[index].delete(0, tk.END)
- self.focus_set()
- return
- if entry_text:
- if index + 1 in self.router_table:
- self.router_table[index + 1].append(entry_text)
- else:
- self.router_table[index + 1] = [entry_text]
- self.interface_entries[index].delete(0, tk.END)
- self.router_obj.add_config(entry_text, index + 1)
- self.update_router_table()
- except:
- messagebox.showerror("注意", message="网段格式错误!网段示例如下:\n10.1.2.0/24\n10.1.2.12")
- return
+class Message(Canvas):
+ def __init__(self, master, **kwargs):
+ super().__init__(master, **kwargs)
+ self.message = []
+ self.master = master
+ self.scrollbar = tk.Scrollbar(master, orient="vertical", command=self.yview)
+ self.scrollbar.pack(side="right", fill=BOTH)
+ self.config(yscrollcommand=self.scrollbar.set)
- def update_router_table(self):
- self.router_treeview.delete(*self.router_treeview.get_children())
- for i, entrys in self.router_table.items():
- for entry in entrys:
- self.router_treeview.insert("", "end", values=(f"接口{i}", entry))
-
-
-class SwitchConfigWindow(RouterConfigWindow):
- def __init__(self, parent, router_obj):
- super().__init__(parent, router_obj)
- self.geometry("435x433+350+200")
- self.title(f"{router_obj.ObjLabel}交换表配置")
- self.router_obj = router_obj
- self.interface_entries = []
- self.router_table = {}
- self.create_interface_inputs()
- self.create_router_table()
-
- def create_router_table(self):
- def on_right_click(event):
- row = self.router_treeview.identify_row(event.y) # 获取鼠标位置的行索引
- if row:
- self.router_treeview.selection_set(row) # 选中该行
- delete_menu.post(event.x_root, event.y_root) # 在鼠标位置弹出删除菜单
-
- def delete_row():
- selected_items = self.router_treeview.selection() # 获取选中的行
- for item in selected_items:
- ifs, network = int(self.router_treeview.item(item)["values"][0][-1:]), self.router_treeview.item(item)["values"][1]
- self.router_obj.delete_config(ifs, network)
- self.router_treeview.delete(item)
-
- self.router_table_frame = tk.Frame(self)
- self.router_table_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
- self.router_treeview = ttk.Treeview(self.router_table_frame, columns=("Interface", "Route"), show="headings")
- self.router_treeview.heading("Interface", text="接口")
- self.router_treeview.heading("Route", text="mac")
- self.router_treeview.pack(side="left", fill="both")
- scrollbar = ttk.Scrollbar(self.router_table_frame, orient="vertical", command=self.router_treeview.yview)
- scrollbar.pack(side="right", fill="y")
- self.router_treeview.configure(yscrollcommand=scrollbar.set)
- self.router_table = self.router_obj.mac_table
- self.router_treeview.bind("", on_right_click)
- # 创建删除菜单
- delete_menu = tk.Menu(root, tearoff=False)
- delete_menu.add_command(label="删除", command=delete_row)
- self.update_router_table()
-
-
- def add_router_entry(self, index):
- entry_text = self.interface_entries[index].get()
- if isinstance(self.router_obj, SimRouter):
- if not validate_ip_address(entry_text):
- messagebox.showerror("注意", message="添加的网段信息格式不合格")
- self.interface_entries[index].delete(0, tk.END)
- self.focus_set()
- return
- if entry_text:
- if index + 1 in self.router_table:
- self.router_table[index + 1].append(entry_text)
- else:
- self.router_table[index + 1] = [entry_text]
- self.interface_entries[index].delete(0, tk.END)
- self.router_obj.add_config(entry_text, index + 1)
- self.update_router_table()
+ def show_message(self, message, color="#5fa8fe"):
+ self.message.append({"message": message, "color": color})
+ num = 0
+ self.delete("message")
+ for function in self.message:
+ self.create_text(20, 25 * num + 10, anchor="nw", text=function["message"], font=("", 15),
+ fill=function["color"], tags="message")
+ num += 1
+ self.update()
+ self.configure(scrollregion=self.bbox("all"))
+ self.scrollbar.set(1.0, 1.0)
+ self.yview_moveto(1.0)
class NetWorkAnalog(Canvas):
@@ -228,17 +38,39 @@ class NetWorkAnalog(Canvas):
super().__init__(master, **kwargs)
self.master = master
self.router_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/路由器.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/路由器@2x.png").resize((40, 40)))
self.switch_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/交换机.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/交换机@2x.png").resize((40, 40)))
self.hub_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/集线器.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/集线器@2x.png").resize((40, 40)))
self.host_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/主机.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/主机@2x.png").resize((40, 40)))
+ self.pack_img = ImageTk.PhotoImage(
+ Image.open(sys.path[0] + "/../datas/images/packet.png").resize((30, 30)))
+ self.true_img = ImageTk.PhotoImage(
+ Image.open(sys.path[0] + "/../datas/images/true.png").resize((30, 30)))
+ self.false_img = ImageTk.PhotoImage(
+ Image.open(sys.path[0] + "/../datas/images/false.png").resize((30, 30)))
self.width = int(self.cget("width"))
self.height = int(self.cget("height"))
+ self.canvas_size = [150, 0, int(self.width * 0.8), int(self.height * 0.8)]
+ self.label_top_img = ImageTk.PhotoImage(imim.open("../datas/images/右上框@2x.png").resize((int(self.width - self.width * 0.85), int(self.canvas_size[3] * 0.4))))
+ self.label_bottom_img = ImageTk.PhotoImage(imim.open("../datas/images/右下框@2x.png").resize((int(self.width - self.width * 0.85), int(self.canvas_size[3] * 0.45))))
+ self.message_img = ImageTk.PhotoImage(
+ Image.open(sys.path[0] + "/../datas/images/日志消息展示框@2x.png").resize((self.width - 60, self.height - self.canvas_size[3] - 30))
+ )
+ message_frame = Frame(self, width=self.width - 60, height=self.height - self.canvas_size[3] - 30)
+ message_frame.place(x=15, y=self.canvas_size[3] + 15, anchor=NW)
+ self.message = Message(message_frame, width=self.width - 60, height=self.height - self.canvas_size[3] - 30)
+ self.message.pack()
+ self.message.configure(bg="#edf8ff")
+ self.configure(bg="#ddeafe")
+ self.filename = ImageTk.PhotoImage(imim.open("../datas/images/背景@2x.png").resize((self.width, self.canvas_size[3])))
+ self.check_filename = ImageTk.PhotoImage(Image.open("../datas/images/背景@2x.png").resize((self.width, self.height)))
+ self.create_image(0, 0, image=self.filename, anchor=NW)
self.chose = self.host_img
self.AllSimObjs = {}
+ self.receive = None
self.conns = []
self.drawLine = True # 画线标志
self.line_start_obj = None
@@ -248,6 +80,7 @@ class NetWorkAnalog(Canvas):
self.chose_obj = None
self.show_label_flag = True
self.show_interface_flag = True
+ self.check_error_obj = None
self.create_widget()
def is_chose(self):
@@ -256,8 +89,11 @@ class NetWorkAnalog(Canvas):
:return:
"""
self.delete("rectangle")
- self.create_rectangle(self.chose_obj.ObjX - 25, self.chose_obj.ObjY - 25, self.chose_obj.ObjX + 25,
- self.chose_obj.ObjY + 25, outline="red", tags="rectangle")
+ self.create_rectangle(self.chose_obj.ObjX - 35, self.chose_obj.ObjY - 35, self.chose_obj.ObjX + 15,
+ self.chose_obj.ObjY + 15, outline="red", tags="rectangle")
+ self.show_network_config()
+ if self.chose_obj.ObjType == 2 or self.chose_obj.ObjType == 3:
+ self.show_router_config()
def tag_bind_event(self):
"""
@@ -323,6 +159,7 @@ class NetWorkAnalog(Canvas):
if not flag:
self.line_start_obj.connections[self.line_start_ifs - 1] = conn
self.line_end_obj.connections[self.line_end_ifs - 1] = conn
+ self.message.show_message(f"{self.line_start_obj.ObjLabel}连接{self.line_end_obj.ObjLabel}成功!")
conn.save()
self.conns.append(conn)
init()
@@ -344,15 +181,15 @@ class NetWorkAnalog(Canvas):
if self.drawLine:
self.delete("Line")
x, y = event.x, event.y
- if not (0 < x < self.width * 0.8 and 0 < y < self.height * 0.85):
+ if not (0 < x < self.canvas_size[2] and 0 < y < self.canvas_size[3]):
if x < 0:
x = 0
- elif x > self.width * 0.8:
- x = self.width * 0.8
+ elif x > self.canvas_size[2]:
+ x = self.canvas_size[2]
if y < 0:
y = 0
- elif y > self.height * 0.85:
- y = self.height * 0.85
+ elif y > self.canvas_size[3]:
+ y = self.canvas_size[3]
self.create_line(self.line_start_obj.ObjX, self.line_start_obj.ObjY, x + 8, y + 8, fill="#5b9bd5",
width=1,
tags="Line")
@@ -380,7 +217,7 @@ class NetWorkAnalog(Canvas):
鼠标左键松开事件
:param event: 事件对象
"""
- if 20 < event.x < self.width * 0.8 + 20 and 20 < event.y < self.height * 0.85 - 20: # 在方框内,无变化
+ if 20 + self.canvas_size[0] < event.x < self.canvas_size[2] + 20 and 20 + self.canvas_size[1] < event.y < self.canvas_size[3] - 20: # 在方框内,无变化
if name == "路由器":
tag = SimRouter(self, event.x, event.y)
elif name == "集线器":
@@ -392,6 +229,7 @@ class NetWorkAnalog(Canvas):
tag.create_img()
self.AllSimObjs[tag.ObjID] = tag
tag.save()
+ self.message.show_message(f"组件 {tag.ObjLabel} 创建成功!")
self.tag_bind_event()
else:
self.delete("L")
@@ -410,7 +248,7 @@ class NetWorkAnalog(Canvas):
else:
self.chose = self.host_img
self.delete("L")
- if 20 < event.x < self.width * 0.8 + 20 and 20 < event.y < self.height * 0.85 - 20: # 在方框内,无变化
+ if 20 + self.canvas_size[0] < event.x < self.canvas_size[2] + 20 and 20 + self.canvas_size[1] < event.y < self.canvas_size[3] - 20: # 在方框内,无变化
pass
else: # 在方框外,显示禁止放置标识
self.create_oval(event.x - 10, event.y - 45, event.x + 10, event.y - 25, outline="red", width=2,
@@ -471,7 +309,6 @@ class NetWorkAnalog(Canvas):
self.AllSimObjs = AllSimObj
self.conns = conns
for key, sim_obj in self.AllSimObjs.items():
- print(key)
sim_obj.create_img()
for conn in self.conns:
conn.draw_line()
@@ -498,6 +335,7 @@ class NetWorkAnalog(Canvas):
execute_sql(delete_sql)
execute_sql(delete_config_sql)
delete_obj(self.chose_obj.ObjID)
+ self.message.show_message(f"组件 {self.chose_obj.ObjLabel} 删除成功!")
self.delete("rectangle")
self.reload_data()
@@ -565,6 +403,7 @@ class NetWorkAnalog(Canvas):
self.AllSimObjs[data[0]].connections[data[1] - 1] = None
child_d.destroy()
messagebox.showinfo("提示", f"连接线{value.get()}删除成功!")
+ self.message.show_message(f"连接线{value.get()}删除成功!")
btn_yes = tk.Button(child_d, text='确定', font=('黑体', 12), height=1, command=del_line)
btn_yes.place(x=240, y=20)
@@ -601,6 +440,7 @@ class NetWorkAnalog(Canvas):
conn.update_info(self.chose_obj.ObjID)
child1.destroy()
messagebox.showinfo("提示", message="修改成功!")
+ self.message.show_message(f"{name} 修改名称成功!")
tk.Button(child1, text='确定', font=('黑体', 16), height=1, command=update_tag).grid(row=2, column=0,
sticky='e', pady=10)
@@ -677,6 +517,7 @@ class NetWorkAnalog(Canvas):
addr_en.grid(row=1, column=3, padx=10)
def commit():
self.chose_obj.config(datas)
+ self.message.show_message(f"组件 {self.chose_obj} 网络配置成功!")
child_r.destroy()
tk.Button(ifs_frame_YN, text='确定', font=('黑体', 16), height=1, command=commit).grid(row=0, column=0,
@@ -767,9 +608,9 @@ class NetWorkAnalog(Canvas):
messagebox.showerror("注意", message="请选择路由器/交换机对象!")
return
self.delete("routerSet")
- self.create_text(self.width * 0.82 + 120, self.height * 0.85 / 2 + 50, text="(" + self.chose_obj.ObjLabel + ")", anchor="n", font=('微软雅黑', 14, 'bold'),
+ self.create_text(self.width * 0.82 + 120, self.canvas_size[3] / 2 + 50, text="(" + self.chose_obj.ObjLabel + ")", anchor="n", font=('微软雅黑', 14, 'bold'),
fill="#7030a0", tags="routerSet")
- self.create_text(self.width * 0.82 + 20, self.height * 0.85 / 2 + 50 + 25, text=self.chose_obj.get_table_config(),
+ self.create_text(self.width * 0.82 + 20, self.canvas_size[3] / 2 + 50 + 25, text=self.chose_obj.get_table_config(),
anchor="nw", font=('宋体', 14), tags="routerSet")
def send_packet(self):
@@ -793,12 +634,9 @@ class NetWorkAnalog(Canvas):
tk.Label(child2, text='目的IP:', font=('黑体', 16)).grid(row=0, column=0, columnspan=2, sticky='w', pady=10)
packet_ip = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
packet_ip.grid(row=0, column=1, pady=10)
- tk.Label(child2, text='目的MAC:', font=('黑体', 16)).grid(row=1, column=0, sticky='w', pady=10) # ,sticky='w'靠左显示
- packet_mac = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
- packet_mac.grid(row=1, column=1, pady=10)
- tk.Label(child2, text='消息:', font=('黑体', 16)).grid(row=2, column=0, sticky='w', pady=10) # ,sticky='w'靠左显示
+ tk.Label(child2, text='消息:', font=('黑体', 16)).grid(row=1, column=0, sticky='w', pady=10) # ,sticky='w'靠左显示
packet_message = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
- packet_message.grid(row=2, column=1, pady=10)
+ packet_message.grid(row=1, column=1, pady=10)
def send():
"""
@@ -811,16 +649,16 @@ class NetWorkAnalog(Canvas):
if not validate_ip_address(packet_ip.get()):
messagebox.showerror("注意", message="IP地址不规范!")
return
- if packet_mac.get() == "":
- messagebox.showerror("注意", message="mac地址不能为空!")
- return
if packet_message.get() == "":
messagebox.showerror("注意", message="消息不能为空!")
return
- self.chose_obj.create_packet(packet_ip.get(),
- packet_mac.get(),
- packet_message.get())
+ ip = packet_ip.get()
+ message = packet_message.get()
child2.destroy()
+ self.chose_obj.create_packet(ip,
+ None,
+ message)
+
tk.Button(child2, text='确定', font=('黑体', 16), height=1, command=send).grid(row=3, column=0, sticky='e', pady=10)
tk.Button(child2, text='取消', font=('黑体', 16), height=1, command=child2.destroy).grid(row=3, column=1, sticky='e', pady=10)
@@ -840,15 +678,11 @@ class NetWorkAnalog(Canvas):
tk.Label(child2, text='目的IP:', font=('黑体', 16)).grid(row=0, column=0, columnspan=2, sticky='w', ipady=10)
packet_ip = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
packet_ip.grid(row=0, column=1, pady=5)
- tk.Label(child2, text='目的MAC:', font=('黑体', 16)).grid(row=1, column=0, sticky='w',
- pady=5) # ,sticky='w'靠左显示
- packet_mac = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
- packet_mac.grid(row=1, column=1, pady=5)
- tk.Label(child2, text='消息:', font=('黑体', 16)).grid(row=2, column=0, sticky='w', pady=5) # ,sticky='w'靠左显示
+ tk.Label(child2, text='消息:', font=('黑体', 16)).grid(row=1, column=0, sticky='w', pady=5) # ,sticky='w'靠左显示
packet_message = tk.Entry(child2, font=('黑体', 16), textvariable=tk.StringVar())
- packet_message.grid(row=2, column=1, pady=5)
+ packet_message.grid(row=1, column=1, pady=5)
host = StringVar()
- tk.Label(child2, text='发送主机:', font=('黑体', 16)).grid(row=3, column=0, sticky='w',
+ tk.Label(child2, text='发送主机:', font=('黑体', 16)).grid(row=2, column=0, sticky='w',
pady=5) # ,sticky='w'靠左显示
combobox = ttk.Combobox(
master=child2, # 父容器
@@ -859,7 +693,7 @@ class NetWorkAnalog(Canvas):
textvariable=host, # 通过StringVar设置可改变的值
values=list(hosts.keys()), # 设置下拉框的选项
)
- combobox.grid(row=3, column=1, pady=5)
+ combobox.grid(row=2, column=1, pady=5)
packet_frame = tk.Frame(child2)
packet_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
packet_treeview = ttk.Treeview(packet_frame, columns=("source_host", "ip", "mac", "message"), show="headings")
@@ -879,15 +713,14 @@ class NetWorkAnalog(Canvas):
def add():
ip = packet_ip.get()
- mac = packet_mac.get()
message = packet_message.get()
chose_host = host.get()
- if ip == "" or mac == "" or message == "" or chose_host == "":
+ if ip == "" or message == "" or chose_host == "":
messagebox.showerror("注意", message="输入框不能为空")
return
packet = SimPacket(self.AllSimObjs[hosts[chose_host]].interface[0]["ip"],
self.AllSimObjs[hosts[chose_host]].interface[0]["mac"],
- ip, mac, message)
+ ip, None, message)
packets.append((self.AllSimObjs[hosts[chose_host]], packet))
packet_treeview.delete(*packet_treeview.get_children())
for datas in packets:
@@ -928,6 +761,99 @@ class NetWorkAnalog(Canvas):
self.AllSimObjs.clear()
self.conns.clear()
+ def export_data(self):
+ try:
+ export = ExportUtil(sys.path[0] + "/database.xlsx")
+ export.export()
+ self.message.show_message("文件导出成功!文件位置在{}".format(sys.path[0] + "/database.xlsx"))
+ except Exception as E:
+ self.message.show_message(str(E), color="red")
+
+ def import_data(self):
+ truncate_db()
+ for tag_id, tag in self.AllSimObjs.items():
+ self.delete(tag_id)
+ self.delete(tag_id + "text")
+ for conn in self.conns:
+ conn.delete_line()
+ self.delete("rectangle")
+ self.AllSimObjs.clear()
+ self.conns.clear()
+ file_path = filedialog.askopenfilename()
+ if file_path:
+ export = ExportUtil(file_path)
+ export.import_data()
+ self.message.show_message("文件导入成功!")
+ self.reload_data()
+
+ def check_network(self):
+ """
+ 校验
+ :return:
+ """
+ hosts = {}
+ for key, tag in self.AllSimObjs.items():
+ if tag.ObjType == 1 and tag.ConfigCorrect == 1:
+ hosts[tag.ObjLabel] = tag.ObjID
+ child2 = tk.Toplevel()
+ child2.title("数据包配置")
+ child2.geometry('392x168+200+110')
+ child2.grab_set() # 设置组件焦点抓取。使焦点在释放之前永远保持在这个组件上,只能在这个组件上操作
+ tk.Label(child2, text='发送主机:', font=('黑体', 16)).grid(row=0, column=0, columnspan=2, sticky='w', pady=10)
+ send_host = StringVar()
+ host_combobox = ttk.Combobox(
+ master=child2, # 父容器
+ height=5, # 高度,下拉显示的条目数量
+ width=18, # 宽度
+ state='readonly', # readonly(只可选)
+ font=('', 16), # 字体
+ textvariable=send_host, # 通过StringVar设置可改变的值
+ values=list(hosts.keys()), # 设置下拉框的选项
+ )
+
+ host_combobox.grid(row=0, column=1, pady=10)
+ tk.Label(child2, text='接收主机:', font=('黑体', 16)).grid(row=1, column=0, sticky='w',
+ pady=10) # ,sticky='w'靠左显示
+ receive_host = StringVar()
+ ttk.Combobox(
+ master=child2, # 父容器
+ height=5, # 高度,下拉显示的条目数量
+ width=18, # 宽度
+ state='readonly', # readonly(只可选)
+ font=('', 16), # 字体
+ textvariable=receive_host, # 通过StringVar设置可改变的值
+ values=list(hosts.keys()), # 设置下拉框的选项
+ ).grid(row=1, column=1, pady=10)
+
+ def check():
+ """
+ 发送数据包
+ :return:
+ """
+ if send_host.get() == "" or receive_host.get() == "":
+ messagebox.showerror("注意", message="信息不能为空")
+ return
+ if send_host.get() == receive_host.get():
+ messagebox.showerror("注意", message="发送主机和接收主机不能相同!")
+ send_host_obj: SimHost = self.AllSimObjs[hosts[send_host.get()]]
+ receive_host_obj: SimHost = self.AllSimObjs[hosts[receive_host.get()]]
+ child2.destroy()
+ send_host_obj.create_packet(receive_host_obj.interface[0]["ip"], receive_host_obj.interface[0]["mac"], "ping", True)
+ TransferAnimate(self, self.receive == receive_host_obj)
+ if self.receive == receive_host_obj:
+ self.message.show_message("校验成功:{} 与 {} 能正常连通".format(send_host_obj.ObjLabel, receive_host_obj.ObjLabel))
+ else:
+ self.message.show_message(
+ "校验成功:{} 与 {} 无法正常连通,请检查一下组件 {} 的配置".format(send_host_obj.ObjLabel, receive_host_obj.ObjLabel, self.check_error_obj.ObjLabel), "red")
+ self.check_error_obj = None
+ self.receive = None
+
+
+ tk.Button(child2, text='校验', font=('黑体', 16), height=1, command=check).grid(row=5, column=0, sticky='e',
+ pady=10)
+ tk.Button(child2, text='取消', font=('黑体', 16), height=1, command=child2.destroy).grid(row=5, column=1,
+ sticky='e', pady=10)
+
def create_config_button(self):
# 创建一个菜单栏,这里我们可以把他理解成一个容器,在窗口的上方
menubar = tk.Menu(root)
@@ -958,6 +884,9 @@ class NetWorkAnalog(Canvas):
setMenu.add_command(label='批量发送数据包', command=self.send_packet_list)
menubar.add_command(label='清除屏幕', command=self.clear_canvas)
+ menubar.add_command(label='导出数据', command=self.export_data)
+ menubar.add_command(label='导入数据', command=self.import_data)
+ menubar.add_command(label='校验', command=self.check_network)
root.config(menu=menubar)
def create_widget(self):
@@ -966,21 +895,22 @@ class NetWorkAnalog(Canvas):
创建整体页面布局
:return:
"""
- self.create_rectangle(0, 0, self.width * 0.8, self.height * 0.85, outline="#7f6000", width=3) # 矩形框,左上角坐标,右下角坐标
- round_rectangle(self, self.width * 0.82, 30, self.width * 0.98, self.height * 0.85 / 2 - 30, outline="#ffff00", width=3, fill="#f4b88e") # 矩形框,左上角坐标,右下角坐标
+ self.create_rectangle(self.canvas_size[0], self.canvas_size[1], self.canvas_size[2], self.canvas_size[3], outline="#7f6000", width=0) # 矩形框,左上角坐标,右下角坐标
+ self.create_image(self.width * 0.82, 30, image=self.label_top_img, anchor=NW) # 矩形框,左上角坐标,右下角坐标
self.create_text(self.width * 0.82 + 120, 30 + 15, text="网络配置信息", anchor="n", font=('微软雅黑', 18, 'bold'))
- round_rectangle(self, self.width * 0.82, self.height * 0.85 / 2, self.width * 0.98, self.height * 0.85 - 30, outline="#ffff00", width=2, fill="#f4b88e") # 矩形框,左上角坐标,右下角坐标
- self.create_text(self.width * 0.82 + 120, self.height * 0.85 / 2 + 15, text="路由/交换表信息", anchor="n", font=('微软雅黑', 18, 'bold'))
+ self.create_image(self.width * 0.82, self.canvas_size[3] / 2, image=self.label_bottom_img, anchor=NW) # 矩形框,左上角坐标,右下角坐标
+ # round_rectangle(self, self.width * 0.82, self.canvas_size[3] / 2, self.width * 0.98, self.canvas_size[3] - 30, outline="#ffff00", width=2, fill="#f4b88e") # 矩形框,左上角坐标,右下角坐标
+ self.create_text(self.width * 0.82 + 120, self.canvas_size[3] / 2 + 15, text="路由/交换表信息", anchor="n", font=('微软雅黑', 18, 'bold'))
# 显示左边的固定图片
- img_height, text_height, split_width = self.height - 120, self.height - 60, 120
- self.create_text(split_width, text_height, text="路由器", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
- router = self.create_image(split_width, img_height, image=self.router_img, anchor="nw")
- self.create_text(split_width * 2, text_height, text="交换机", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
- switch = self.create_image(split_width * 2, img_height, image=self.switch_img, anchor="nw")
- self.create_text(split_width * 3, text_height, text="集线器", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
- hub = self.create_image(split_width * 3, img_height, image=self.hub_img, anchor="nw")
- self.create_text(split_width * 4, text_height, text="主机", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
- host = self.create_image(split_width * 4, img_height, image=self.host_img, anchor="nw")
+ img_height, text_height, split_width = 50, 40, 120
+ self.create_text(text_height, split_width + 40, text="路由器", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
+ router = self.create_image(img_height, split_width, image=self.router_img, anchor="nw")
+ self.create_text(text_height, split_width * 2 + 40, text="交换机", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
+ switch = self.create_image(img_height, split_width * 2, image=self.switch_img, anchor="nw")
+ self.create_text(text_height, split_width * 3 + 40, text="集线器", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
+ hub = self.create_image(img_height, split_width * 3, image=self.hub_img, anchor="nw")
+ self.create_text(text_height, split_width * 4 + 40, text="主机", anchor="nw", font=('微软雅黑', 18, 'bold')) # 显示文字
+ host = self.create_image(img_height, split_width * 4, image=self.host_img, anchor="nw")
self.bind_event(router, "路由器")
self.bind_event(switch, "交换机")
self.bind_event(hub, "集线器")
@@ -990,7 +920,7 @@ class NetWorkAnalog(Canvas):
if __name__ == '__main__':
root = Window()
root.title('网络拓扑图')
- screen_width = root.winfo_screenwidth() # winfo方法来获取当前电脑屏幕大小
+ screen_width = root.winfo_screenwidth() # winfo 方法来获取当前电脑屏幕大小
screen_height = root.winfo_screenheight()
root_attr = {
"width": screen_width * 0.83,
diff --git a/NetworkAnalog/SimObjs.py b/NetworkAnalog/SimObjs.py
index 64227d6..4eedab3 100644
--- a/NetworkAnalog/SimObjs.py
+++ b/NetworkAnalog/SimObjs.py
@@ -27,21 +27,21 @@ class SimBase():
self.ObjY = y
self.canvas = canvas
self.host_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/主机.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/主机@2x.png").resize((40, 40)))
self.host_img_tm = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/主机_tm.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/主机--暗色@2x.png").resize((40, 40)))
self.router_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/路由器.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/路由器@2x.png").resize((40, 40)))
self.router_img_tm = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/路由器_tm.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/路由器--暗色@2x.png").resize((40, 40)))
self.switch_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/交换机.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/交换机@2x.png").resize((40, 40)))
self.switch_img_tm = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/交换机_tm.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/交换机--暗色@2x.png").resize((40, 40)))
self.hub_img = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/集线器.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/集线器@2x.png").resize((40, 40)))
self.hub_img_tm = ImageTk.PhotoImage(
- Image.open(sys.path[0] + "/../datas/images/集线器_tm.png").resize((60, 60)))
+ Image.open(sys.path[0] + "/../datas/images/集线器--暗色@2x.png").resize((40, 40)))
self.img = None
self.img_tm = None
self.interface = [{}, {}, {}, {}]
@@ -90,10 +90,8 @@ class SimBase():
self.canvas.chose_obj = None
dx = event.x - self.start_x
dy = event.y - self.start_y
- width = int(self.canvas.cget("width"))
- height = int(self.canvas.cget("height"))
# 移动范围限制, 超出移动范围则直接返回
- if not (0 + 20 <= self.ObjX + dx <= width * 0.8 - 20 and 20 <= self.ObjY + dy <= height * 0.85 - 20):
+ if not (self.canvas.canvas_size[0] + 20 <= self.ObjX + dx <= self.canvas.canvas_size[2] - 20 and 20 + self.canvas.canvas_size[1] <= self.ObjY + dy <= self.canvas.canvas_size[3] - 20):
return
self.ObjX += dx
self.ObjY += dy
@@ -127,8 +125,8 @@ class SimBase():
image=self.img if self.ConfigCorrect == 1 else self.img_tm, anchor="nw",
tags=self.ObjID)
self.canvas.dtag("L", "L")
- self.canvas.create_text(self.ObjX, self.ObjY - 40, text=self.ObjLabel, font=("", 16, "bold"),
- fill="#7030a0", tags=self.ObjID + "text", anchor="nw")
+ self.canvas.create_text(self.ObjX - 20, self.ObjY - 60, text=self.ObjLabel, font=("", 16, "bold"),
+ fill="#9b78eb", tags=self.ObjID + "text", anchor="nw")
self.canvas.tag_raise(id)
self.bind_event()
@@ -177,7 +175,7 @@ class SimBase():
:return:
"""
self.canvas.delete(self.ObjID + "text")
- self.canvas.create_text(self.ObjX, self.ObjY - 40, text=self.ObjLabel, font=("", 16, "bold"),
+ self.canvas.create_text(self.ObjX - 20, self.ObjY - 60, text=self.ObjLabel, font=("", 16, "bold"),
fill="#7030a0", tags=self.ObjID + "text", anchor="nw")
sql = f"update sim_objs set objLabel='{self.ObjLabel}', ObjX={self.ObjX}," \
f"ObjY={self.ObjY}, ConfigCorrect={self.ConfigCorrect} where ObjID='{self.ObjID}'"
@@ -220,7 +218,7 @@ class SimPacket():
"""
数据包类
"""
- def __init__(self, source_ip, source_mac, destination_ip, destination_mac, message):
+ def __init__(self, source_ip, source_mac, destination_ip, destination_mac, message, ping=False):
"""
:param source_mac: 源主机mac地址
:param source_ip: 源主机ip地址
@@ -234,6 +232,7 @@ class SimPacket():
self.destination_ip = destination_ip
self.message = message
self.up_jump = None # 上一跳连接对象
+ self.ping = ping
self.img = ImageTk.PhotoImage(
Image.open(sys.path[0] + "/../datas/images/packet.png").resize((30, 30)))
self.id = str(uuid4())
@@ -260,6 +259,8 @@ class SimPacket():
cv.delete(self.id)
def transfer_packet(self, cv: Canvas, nodex_tag: SimBase, nodey_tag: SimBase):
+ if self.ping:
+ return
cv.create_image(nodex_tag.ObjX - 15, nodex_tag.ObjY - 15, image=self.img, anchor="nw", tags=self.id)
self.move(cv, nodey_tag.ObjX - 15, nodey_tag.ObjY - 15, 500)
@@ -280,7 +281,7 @@ class AllSimConnect():
self.NobjE = nodey
self.IfsS = nodex_ifs
self.IfsE = nodey_ifs
- self.width = 1 if self.ConfigCorrect == 0 else 4 # 线的粗细
+ self.width = False if self.ConfigCorrect == 0 else True # 线的粗细
def is_connected_to(self, other):
"""
@@ -342,16 +343,21 @@ class AllSimConnect():
:return:
"""
if source_node == self.NobjS:
- packet.transfer_packet(self.canvas, self.NobjS, self.NobjE)
+ if not packet.ping:
+ packet.transfer_packet(self.canvas, self.NobjS, self.NobjE)
self.NobjE.receive(packet)
else:
packet.transfer_packet(self.canvas, self.NobjE, self.NobjS)
self.NobjS.receive(packet)
def draw_line(self):
- line = self.canvas.create_line(self.NobjS.ObjX, self.NobjS.ObjY, self.NobjE.ObjX, self.NobjE.ObjY,
- width=self.width, fill="#5b9bd5", tags=self.NobjS.ObjID + self.NobjE.ObjID + "line")
- self.canvas.tag_lower(line)
+ if self.width:
+ line = self.canvas.create_line(self.NobjS.ObjX, self.NobjS.ObjY, self.NobjE.ObjX, self.NobjE.ObjY,
+ width=2, fill="#c372f0", tags=self.NobjS.ObjID + self.NobjE.ObjID + "line")
+ else:
+ line = self.canvas.create_line(self.NobjS.ObjX, self.NobjS.ObjY, self.NobjE.ObjX, self.NobjE.ObjY,
+ width=2, dash=(250, 4), fill="#c372f0", tags=self.NobjS.ObjID + self.NobjE.ObjID + "line")
+ self.canvas.tag_lower(line, 2)
self.analyseIFS(self.NobjS.ObjX, self.NobjS.ObjY, self.NobjS.ObjLabel, self.IfsS,
self.NobjE.ObjX, self.NobjE.ObjY, self.NobjE.ObjLabel, self.IfsE)
@@ -384,6 +390,7 @@ class AllSimConnect():
:param SLabel: S对象的标签
:param IfsS: S对象要显示的接口号
'''
+ offset = 15
if (EX - SX) == 0: # 即垂直的时候,NobjS在NobjE对象的正上方和正下方,x坐标无变化,只需要y变化
R = 18
x = 0 # x无偏移量
@@ -401,10 +408,10 @@ class AllSimConnect():
x_E = EX - x
y_E = EY - y # NobjE的连接点坐标
# 显示S接口号
- self.canvas.create_text((x_S - 5, y_S - 5), text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text((x_S + offset, y_S - offset), text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
# 显示E接口号
- self.canvas.create_text(x_E - 10, y_E - 10, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_E - offset, y_E - offset, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
elif SX < EX and SY > EY: # NobjS在NobjE的左下角,NobjS的右上角连接NobjE的左下角
x_S = SX + x
@@ -412,10 +419,10 @@ class AllSimConnect():
x_E = EX - x
y_E = EY + y # NobjE的连接点坐标
# 显示S接口号
- self.canvas.create_text(x_S + 5, y_S - 10, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_S + offset, y_S - offset, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
# 显示E接口号
- self.canvas.create_text(x_E - 5, y_E, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_E - offset, y_E, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
elif SX > EX and SY < EY: # NobjS在NobjE的右上角,NobjS的左下角连接NobjE的右上角
x_S = SX - x
@@ -423,10 +430,10 @@ class AllSimConnect():
x_E = EX + x
y_E = EY - y # NobjE的连接点坐标
# 显示S接口号
- self.canvas.create_text(x_S - 5, y_S, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_S - offset, y_S, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
# 显示E接口号
- self.canvas.create_text(x_E + 5, y_E, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_E + offset, y_E, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
elif SX >= EX and SY >= EY: # NobjS在NobjE的右下角,NobjS的左上角连接NobjE的右下角
x_S = SX - x
@@ -434,10 +441,10 @@ class AllSimConnect():
x_E = EX + x
y_E = EY + y # NobjE的连接点坐标
# 显示S接口号
- self.canvas.create_text(x_S - 5, y_S - 15, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_S - offset, y_S - offset, text=IfsS, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
# 显示E接口号
- self.canvas.create_text(x_E - 5, y_E - 5, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="red",
+ self.canvas.create_text(x_E + offset, y_E - offset, text=IfsE, anchor="nw", font=("幼圆", 16, "bold"), fill="#5fa6d6",
tag=self.NobjS.ObjID + self.NobjE.ObjID) # 显示文字
def save(self):
@@ -485,7 +492,7 @@ class SimHost(SimBase):
self.set_default_config()
- def create_packet(self, ip, mac, message):
+ def create_packet(self, ip, mac, message, ping=False):
"""
创建数据包
:param ip: 目的主机ip
@@ -493,7 +500,10 @@ class SimHost(SimBase):
:param message: 消息
:return:
"""
- packet = SimPacket(self.interface[0]["ip"], self.interface[0]["mac"], ip, mac, message)
+ if ping:
+ packet = SimPacket(self.interface[0]["ip"], self.interface[0]["mac"], ip, mac, message, True)
+ else:
+ packet = SimPacket(self.interface[0]["ip"], self.interface[0]["mac"], ip, mac, message)
print(f"创建数据包成功,数据包由{packet.source_ip} 发往 {packet.destination_ip}")
self.send(packet)
@@ -516,7 +526,10 @@ class SimHost(SimBase):
"""
print(f"主机{self.ObjLabel}接受到数据{packet.message}")
if packet.destination_ip == self.interface[0]["ip"]:
- self.transfer_animate(True, packet)
+ if not packet.ping:
+ self.transfer_animate(True, packet)
+ else:
+ self.canvas.receive = self
else:
self.transfer_animate(False, packet)
@@ -606,6 +619,9 @@ class SimRouter(SimBase):
conn.transfer(self, packet)
break
error_message = "路由寻址失败"
+ if packet.ping:
+ self.canvas.check_error_obj = self
+ return
self.transfer_animate(False, packet, error_message)
def receive(self, packet):
diff --git a/NetworkAnalog/SimUtil.py b/NetworkAnalog/SimUtil.py
new file mode 100644
index 0000000..ef40d6a
--- /dev/null
+++ b/NetworkAnalog/SimUtil.py
@@ -0,0 +1,332 @@
+import ipaddress
+import re
+import sqlite3
+from time import sleep
+from tkinter import messagebox
+
+import ttkbootstrap as tk
+from ttkbootstrap import *
+from ttkbootstrap import ttk
+import pandas as pd
+import sys
+from PIL import Image, ImageTk
+
+from SimObjs import SimRouter
+
+
+def round_rectangle(cv, x1, y1, x2, y2, radius=30, **kwargs):
+ """
+ 绘制圆角矩形
+ :param cv: canvas对象
+ :param radius: 圆角值
+ :return:
+ """
+ points = [x1 + radius, y1,
+ x1 + radius, y1,
+ x2 - radius, y1,
+ x2 - radius, y1,
+ x2, y1,
+ x2, y1 + radius,
+ x2, y1 + radius,
+ x2, y2 - radius,
+ x2, y2 - radius,
+ x2, y2,
+ x2 - radius, y2,
+ x2 - radius, y2,
+ x1 + radius, y2,
+ x1 + radius, y2,
+ x1, y2,
+ x1, y2 - radius,
+ x1, y2 - radius,
+ x1, y1 + radius,
+ x1, y1 + radius,
+ x1, y1]
+
+ return cv.create_polygon(points, **kwargs, smooth=True)
+
+def validate_ip_address(ip_address):
+ """
+ 匹配ip地址格式是否规范
+ :param ip_address: IP地址
+ :return: Boolean
+ """
+ # 定义IP地址的正则表达式模式
+ pattern_with_subnet = r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})$'
+ pattern_without_subnet = r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$'
+ # 使用re模块进行匹配
+ match_with_subnet = re.match(pattern_with_subnet, ip_address)
+ match_without_subnet = re.match(pattern_without_subnet, ip_address)
+ if match_with_subnet:
+ # 带有子网掩码的IP地址
+ # 检查每个组件的取值范围是否在 0-255 之间
+ for group in match_with_subnet.groups()[:4]:
+ if not (0 <= int(group) <= 255):
+ return False
+ # 检查子网掩码的取值范围是否在 0-32 之间
+ subnet_mask = int(match_with_subnet.groups()[4])
+ if not (0 <= subnet_mask <= 32):
+ return False
+ return True
+ elif match_without_subnet:
+ # 不带子网掩码的IP地址
+ # 检查每个组件的取值范围是否在 0-255 之间
+ for group in match_without_subnet.groups():
+ if not (0 <= int(group) <= 255):
+ return False
+ return True
+ else:
+ # IP地址格式不正确
+ return False
+
+
+class ExportUtil():
+ def __init__(self, path):
+ self.conn = sqlite3.connect('./network.db')
+ self.path = path
+
+ def get_table_names(self):
+ cursor = self.conn.cursor()
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") # 如果你使用SQLite数据库
+ tables = cursor.fetchall()
+ cursor.close()
+ return [table[0] for table in tables]
+
+ def export(self):
+ tables = self.get_table_names()
+ with pd.ExcelWriter(self.path, engine='openpyxl') as writer:
+ for table in tables:
+ table_name = table
+ # a. 从数据库中获取表的数据并存储在DataFrame中
+ query = f"SELECT * FROM {table_name}"
+ df = pd.read_sql(query, self.conn)
+ # b. 使用Pandas将数据写入Excel文件的不同sheet中
+ df.to_excel(writer, sheet_name=table_name, index=False)
+
+ def import_data(self):
+ excel_file = pd.ExcelFile(self.path)
+ sheet_names = excel_file.sheet_names
+ print(sheet_names)
+ cursor = self.conn.cursor()
+ for sheet_name in sheet_names:
+ # 4. 使用 Pandas 读取工作表数据
+ df = pd.read_excel(excel_file, sheet_name=sheet_name)
+ # 5. 获取工作表的列名
+ columns = df.columns.tolist()
+ # 6. 构造插入语句
+ columns_str = ', '.join(columns)
+ placeholders = ', '.join(['?' for _ in range(len(columns))])
+ sql = f"INSERT INTO {sheet_name} ({columns_str}) VALUES ({placeholders})"
+ # 7. 将数据插入数据库
+ for index, row in df.iterrows():
+ # 8. 使用动态生成的 SQL 语句将数据插入数据库
+ cursor.execute(sql, tuple(row))
+ self.conn.commit()
+
+
+class RouterConfigWindow(tk.Toplevel):
+ def __init__(self, parent, router_obj):
+ super().__init__(parent)
+ self.geometry("435x433+350+200")
+ self.title(f"{router_obj.ObjLabel}路由表配置")
+ self.router_obj = router_obj
+ self.interface_entries = []
+ self.router_table = {}
+ self.create_interface_inputs()
+ self.create_router_table()
+
+ def create_interface_inputs(self):
+ label_text = ["接口1", "接口2", "接口3", "接口4"]
+ for i in range(4):
+ label = tk.Label(self, text=label_text[i], font=("黑体", 16))
+ label.grid(row=i, column=0, padx=10, pady=5, sticky="w")
+ entry = tk.Entry(self, width=20, font=("黑体", 16),)
+ entry.grid(row=i, column=1, padx=10, pady=5, sticky="w")
+ self.interface_entries.append(entry)
+ button = tk.Button(self, text="添加", font=("黑体", 16), command=lambda index=i: self.add_router_entry(index))
+ button.grid(row=i, column=2, padx=10, pady=5)
+ lab = LabelFrame(self, text="示例")
+ lab.grid(row=4, column=0, columnspan=3, sticky=W, padx=20)
+ Label(lab, text="10.1.2.0/24 或者 10.1.2.12" if self.router_obj.ObjType == 2 else "MAC11", font=("黑体", 16)).pack()
+
+ def create_router_table(self):
+ def on_right_click(event):
+ row = self.router_treeview.identify_row(event.y) # 获取鼠标位置的行索引
+ if row:
+ self.router_treeview.selection_set(row) # 选中该行
+ delete_menu.post(event.x_root, event.y_root) # 在鼠标位置弹出删除菜单
+
+ def delete_row():
+ selected_items = self.router_treeview.selection() # 获取选中的行
+ for item in selected_items:
+ ifs, network = int(self.router_treeview.item(item)["values"][0][-1:]), self.router_treeview.item(item)["values"][1]
+ self.router_obj.delete_config(ifs, network)
+ self.router_treeview.delete(item)
+ self.router_table_frame = tk.Frame(self)
+ self.router_table_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
+ style = ttk.Style()
+ style.configure("Custom.Treeview.Heading", font=("宋体", 15))
+ style.configure("Custom.Treeview", rowheight=30, font=("宋体", 15))
+ self.router_treeview = ttk.Treeview(self.router_table_frame, style="Custom.Treeview", columns=("Interface", "Route"), show="headings")
+ self.router_treeview.heading("Interface", text="接口")
+ self.router_treeview.heading("Route", text="网段")
+ self.router_treeview.pack(side="left", fill="both")
+ scrollbar = ttk.Scrollbar(self.router_table_frame, orient="vertical", command=self.router_treeview.yview)
+ scrollbar.pack(side="right", fill="y")
+ self.router_treeview.configure(yscrollcommand=scrollbar.set)
+ self.router_table = self.router_obj.router_table
+ self.router_treeview.bind("", on_right_click)
+ # 创建删除菜单
+ delete_menu = tk.Menu(self.master, tearoff=False)
+ delete_menu.add_command(label="删除", command=delete_row)
+ self.update_router_table()
+
+ def add_router_entry(self, index):
+ entry_text = self.interface_entries[index].get()
+ try:
+ ipaddress.ip_network(entry_text)
+ if isinstance(self.router_obj, SimRouter):
+ if not validate_ip_address(entry_text):
+ messagebox.showerror("注意", message="添加的网段信息格式不合格")
+ self.interface_entries[index].delete(0, tk.END)
+ self.focus_set()
+ return
+ if entry_text:
+ if index + 1 in self.router_table:
+ self.router_table[index + 1].append(entry_text)
+ else:
+ self.router_table[index + 1] = [entry_text]
+ self.interface_entries[index].delete(0, tk.END)
+ self.router_obj.add_config(entry_text, index + 1)
+ self.update_router_table()
+ self.master.message.show_message(f"{self.router_obj.ObjLabel}添加配置{entry_text}成功!")
+ except:
+ messagebox.showerror("注意", message="网段格式错误!网段示例如下:\n10.1.2.0/24\n10.1.2.12")
+ return
+
+ def update_router_table(self):
+ self.router_treeview.delete(*self.router_treeview.get_children())
+ for i, entrys in self.router_table.items():
+ for entry in entrys:
+ self.router_treeview.insert("", "end", values=(f"接口{i}", entry))
+
+
+class SwitchConfigWindow(RouterConfigWindow):
+ def __init__(self, parent, router_obj):
+ super().__init__(parent, router_obj)
+ self.geometry("435x433+350+200")
+ self.title(f"{router_obj.ObjLabel}交换表配置")
+ self.router_obj = router_obj
+ self.interface_entries = []
+ self.router_table = {}
+ self.create_interface_inputs()
+ self.create_router_table()
+
+ def create_router_table(self):
+ def on_right_click(event):
+ row = self.router_treeview.identify_row(event.y) # 获取鼠标位置的行索引
+ if row:
+ self.router_treeview.selection_set(row) # 选中该行
+ delete_menu.post(event.x_root, event.y_root) # 在鼠标位置弹出删除菜单
+
+ def delete_row():
+ selected_items = self.router_treeview.selection() # 获取选中的行
+ for item in selected_items:
+ ifs, network = int(self.router_treeview.item(item)["values"][0][-1:]), self.router_treeview.item(item)["values"][1]
+ self.router_obj.delete_config(ifs, network)
+ self.router_treeview.delete(item)
+
+ self.router_table_frame = tk.Frame(self)
+ self.router_table_frame.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
+ self.router_treeview = ttk.Treeview(self.router_table_frame, columns=("Interface", "Route"), show="headings")
+ self.router_treeview.heading("Interface", text="接口")
+ self.router_treeview.heading("Route", text="mac")
+ self.router_treeview.pack(side="left", fill="both")
+ scrollbar = ttk.Scrollbar(self.router_table_frame, orient="vertical", command=self.router_treeview.yview)
+ scrollbar.pack(side="right", fill="y")
+ self.router_treeview.configure(yscrollcommand=scrollbar.set)
+ self.router_table = self.router_obj.mac_table
+ self.router_treeview.bind("", on_right_click)
+ # 创建删除菜单
+ delete_menu = tk.Menu(self.master, tearoff=False)
+ delete_menu.add_command(label="删除", command=delete_row)
+ self.update_router_table()
+
+
+ def add_router_entry(self, index):
+ entry_text = self.interface_entries[index].get()
+ if isinstance(self.router_obj, SimRouter):
+ if not validate_ip_address(entry_text):
+ messagebox.showerror("注意", message="添加的网段信息格式不合格")
+ self.interface_entries[index].delete(0, tk.END)
+ self.focus_set()
+ return
+ if entry_text:
+ if index + 1 in self.router_table:
+ self.router_table[index + 1].append(entry_text)
+ else:
+ self.router_table[index + 1] = [entry_text]
+ self.interface_entries[index].delete(0, tk.END)
+ self.router_obj.add_config(entry_text, index + 1)
+ self.master.message.show_message(f"{self.router_obj.ObjLabel}添加配置{entry_text}成功!")
+ self.update_router_table()
+
+
+class TransferAnimate:
+ """
+ 校验动画
+ """
+ def __init__(self, master, flag=False):
+ self.master = master
+ self.flag = flag
+ self.width, self.height = 320, 140
+ self.host_img = ImageTk.PhotoImage(
+ Image.open(sys.path[0] + "/../datas/images/主机@2x.png").resize((40, 40)))
+ self.filename = ImageTk.PhotoImage(Image.open("../datas/images/背景@2x.png").resize((self.width, self.height)))
+ self.toplevel = Toplevel(alpha=0.9)
+ self.toplevel.geometry("{}x{}+200+150".format(self.width, self.height))
+ self.toplevel.title("校验")
+ self.toplevel.resizable(width=False, height=False)
+ self.canvas = Canvas(self.toplevel, width=self.width, height=self.height, bg="red")
+ self.canvas.place(x=0, y=0, anchor=NW)
+ back = self.canvas.create_image(10, 20, image=master.check_filename)
+ self.send_x, self.receive_x, self.obj_y = 50, self.width - 50, self.height / 2 - 20
+ self.canvas.create_image(self.send_x, self.obj_y, image=master.host_img)
+ self.canvas.create_image(self.receive_x, self.obj_y, image=master.host_img)
+ line = self.canvas.create_line(self.send_x, self.obj_y, self.receive_x, self.obj_y, width=2, fill="#c372f0")
+ self.canvas.tag_lower(line)
+ self.canvas.tag_lower(back)
+ self.animate()
+
+ def animate(self):
+ for i in range(3):
+ self.canvas.create_image(self.send_x, self.obj_y, image=self.master.pack_img, tags="check_img")
+ self.move(self.canvas, self.receive_x, self.obj_y, 500)
+ self.canvas.delete("check_img")
+ self.canvas.update()
+ self.canvas.create_image(self.receive_x - self.send_x - 45, self.obj_y, image=self.master.true_img if self.flag else self.master.false_img)
+ self.canvas.update()
+ sleep(2)
+ if not self.flag:
+ messagebox.showerror("提示", "校验失败,请检查组件{}的相关配置!".format(self.master.check_error_obj.ObjLabel))
+ self.toplevel.destroy()
+ def move(self, cv, target_x, target_y, duration):
+ start_x, start_y = cv.coords("check_img")
+ distance_x = target_x - start_x
+ distance_y = target_y - start_y
+ steps = duration // 10 # 以10毫秒为间隔进行移动
+ step_x = distance_x / steps
+ step_y = distance_y / steps
+ self._move_step(cv, start_x, start_y, target_x, target_y, step_x, step_y, steps)
+
+ def _move_step(self, cv, start_x, start_y, target_x, target_y, step_x, step_y, steps):
+ if steps > 0:
+ new_x = start_x + step_x
+ new_y = start_y + step_y
+ cv.coords("check_img", new_x, new_y)
+ cv.update() # 更新画布显示
+ sleep(0.01) # 添加延迟以控制动画速度
+ self._move_step(cv, new_x, new_y, target_x, target_y, step_x, step_y, steps - 1)
+ else:
+ cv.coords("check_img", target_x, target_y)
+ cv.delete("check_img")
+
diff --git a/NetworkAnalog/basic.png b/NetworkAnalog/basic.png
index 5da2abb..7ad9081 100644
Binary files a/NetworkAnalog/basic.png and b/NetworkAnalog/basic.png differ
diff --git a/NetworkAnalog/network.db b/NetworkAnalog/network.db
index a1239a1..6f4a422 100644
Binary files a/NetworkAnalog/network.db and b/NetworkAnalog/network.db differ
diff --git a/NetworkAnalog/tkTest.py b/NetworkAnalog/tkTest.py
index f9e6f84..f8174ae 100644
--- a/NetworkAnalog/tkTest.py
+++ b/NetworkAnalog/tkTest.py
@@ -1,8 +1,34 @@
-import sys
+import sqlite3
+import pandas as pd
+import os
+import xlwt
from PIL import Image
-path = sys.path[0] + "/../datas/images/packet.png"
-Image.open(path).resize((60, 60)).save(path)
+class ExportUtil():
+ def __init__(self, path):
+ self.conn = sqlite3.connect('./network.db')
+ self.path = path
+
+ def get_table_names(self):
+ cursor = self.conn.cursor()
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") # 如果你使用SQLite数据库
+ tables = cursor.fetchall()
+ cursor.close()
+ return [table[0] for table in tables]
+
+ def export(self):
+ tables = self.get_table_names()
+ with pd.ExcelWriter(self.path, engine='openpyxl') as writer:
+ for table in tables:
+ table_name = table
+ # a. 从数据库中获取表的数据并存储在DataFrame中
+ query = f"SELECT * FROM {table_name}"
+ df = pd.read_sql(query, self.conn)
+ # b. 使用Pandas将数据写入Excel文件的不同sheet中
+ df.to_excel(writer, sheet_name=table_name, index=False)
+
+export = ExportUtil("./data.xlsx")
+export.export()
diff --git a/datas/images/主机--暗色.svg b/datas/images/主机--暗色.svg
new file mode 100644
index 0000000..c5250f3
--- /dev/null
+++ b/datas/images/主机--暗色.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/datas/images/主机--暗色@1x.png b/datas/images/主机--暗色@1x.png
new file mode 100644
index 0000000..9e0b53a
Binary files /dev/null and b/datas/images/主机--暗色@1x.png differ
diff --git a/datas/images/主机--暗色@2x.png b/datas/images/主机--暗色@2x.png
new file mode 100644
index 0000000..198bd06
Binary files /dev/null and b/datas/images/主机--暗色@2x.png differ
diff --git a/datas/images/主机.svg b/datas/images/主机.svg
new file mode 100644
index 0000000..dd17a26
--- /dev/null
+++ b/datas/images/主机.svg
@@ -0,0 +1,55 @@
+
+
\ No newline at end of file
diff --git a/datas/images/主机@1x.png b/datas/images/主机@1x.png
new file mode 100644
index 0000000..2f0d607
Binary files /dev/null and b/datas/images/主机@1x.png differ
diff --git a/datas/images/主机@2x.png b/datas/images/主机@2x.png
new file mode 100644
index 0000000..f7f2600
Binary files /dev/null and b/datas/images/主机@2x.png differ
diff --git a/datas/images/交换机--暗色.svg b/datas/images/交换机--暗色.svg
new file mode 100644
index 0000000..d0b01d1
--- /dev/null
+++ b/datas/images/交换机--暗色.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/datas/images/交换机--暗色@1x.png b/datas/images/交换机--暗色@1x.png
new file mode 100644
index 0000000..664084a
Binary files /dev/null and b/datas/images/交换机--暗色@1x.png differ
diff --git a/datas/images/交换机--暗色@2x.png b/datas/images/交换机--暗色@2x.png
new file mode 100644
index 0000000..b3c782d
Binary files /dev/null and b/datas/images/交换机--暗色@2x.png differ
diff --git a/datas/images/交换机.svg b/datas/images/交换机.svg
new file mode 100644
index 0000000..8c4551e
--- /dev/null
+++ b/datas/images/交换机.svg
@@ -0,0 +1,391 @@
+
+
\ No newline at end of file
diff --git a/datas/images/交换机@1x.png b/datas/images/交换机@1x.png
new file mode 100644
index 0000000..32115d7
Binary files /dev/null and b/datas/images/交换机@1x.png differ
diff --git a/datas/images/交换机@2x.png b/datas/images/交换机@2x.png
new file mode 100644
index 0000000..22799bc
Binary files /dev/null and b/datas/images/交换机@2x.png differ
diff --git a/datas/images/右上框.png b/datas/images/右上框.png
new file mode 100644
index 0000000..15c561e
Binary files /dev/null and b/datas/images/右上框.png differ
diff --git a/datas/images/右上框@2x.png b/datas/images/右上框@2x.png
new file mode 100644
index 0000000..d9e9ede
Binary files /dev/null and b/datas/images/右上框@2x.png differ
diff --git a/datas/images/右下框.png b/datas/images/右下框.png
new file mode 100644
index 0000000..f29a074
Binary files /dev/null and b/datas/images/右下框.png differ
diff --git a/datas/images/右下框@2x.png b/datas/images/右下框@2x.png
new file mode 100644
index 0000000..38fdbf6
Binary files /dev/null and b/datas/images/右下框@2x.png differ
diff --git a/datas/images/日志消息展示框.png b/datas/images/日志消息展示框.png
new file mode 100644
index 0000000..e280753
Binary files /dev/null and b/datas/images/日志消息展示框.png differ
diff --git a/datas/images/日志消息展示框@2x.png b/datas/images/日志消息展示框@2x.png
new file mode 100644
index 0000000..6999741
Binary files /dev/null and b/datas/images/日志消息展示框@2x.png differ
diff --git a/datas/images/背景.png b/datas/images/背景.png
new file mode 100644
index 0000000..827527b
Binary files /dev/null and b/datas/images/背景.png differ
diff --git a/datas/images/背景@2x.png b/datas/images/背景@2x.png
new file mode 100644
index 0000000..615f60e
Binary files /dev/null and b/datas/images/背景@2x.png differ
diff --git a/datas/images/路由器--暗色.svg b/datas/images/路由器--暗色.svg
new file mode 100644
index 0000000..d40a4e1
--- /dev/null
+++ b/datas/images/路由器--暗色.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/datas/images/路由器--暗色@1x.png b/datas/images/路由器--暗色@1x.png
new file mode 100644
index 0000000..f836c2f
Binary files /dev/null and b/datas/images/路由器--暗色@1x.png differ
diff --git a/datas/images/路由器--暗色@2x.png b/datas/images/路由器--暗色@2x.png
new file mode 100644
index 0000000..cbbb492
Binary files /dev/null and b/datas/images/路由器--暗色@2x.png differ
diff --git a/datas/images/路由器.svg b/datas/images/路由器.svg
new file mode 100644
index 0000000..ee28574
--- /dev/null
+++ b/datas/images/路由器.svg
@@ -0,0 +1,68 @@
+
+
\ No newline at end of file
diff --git a/datas/images/路由器@1x.png b/datas/images/路由器@1x.png
new file mode 100644
index 0000000..d6959d7
Binary files /dev/null and b/datas/images/路由器@1x.png differ
diff --git a/datas/images/路由器@2x.png b/datas/images/路由器@2x.png
new file mode 100644
index 0000000..ffb4ee4
Binary files /dev/null and b/datas/images/路由器@2x.png differ
diff --git a/datas/images/集线器--暗色.svg b/datas/images/集线器--暗色.svg
new file mode 100644
index 0000000..fda9fec
--- /dev/null
+++ b/datas/images/集线器--暗色.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/datas/images/集线器--暗色@1x.png b/datas/images/集线器--暗色@1x.png
new file mode 100644
index 0000000..12356bf
Binary files /dev/null and b/datas/images/集线器--暗色@1x.png differ
diff --git a/datas/images/集线器--暗色@2x.png b/datas/images/集线器--暗色@2x.png
new file mode 100644
index 0000000..418f181
Binary files /dev/null and b/datas/images/集线器--暗色@2x.png differ
diff --git a/datas/images/集线器.svg b/datas/images/集线器.svg
new file mode 100644
index 0000000..7ac89c9
--- /dev/null
+++ b/datas/images/集线器.svg
@@ -0,0 +1,1743 @@
+
+
\ No newline at end of file
diff --git a/datas/images/集线器@1x.png b/datas/images/集线器@1x.png
new file mode 100644
index 0000000..3d204f3
Binary files /dev/null and b/datas/images/集线器@1x.png differ
diff --git a/datas/images/集线器@2x.png b/datas/images/集线器@2x.png
new file mode 100644
index 0000000..1e41c6c
Binary files /dev/null and b/datas/images/集线器@2x.png differ
diff --git a/datas/network.db b/datas/network.db
index f4128ed..bde5b24 100644
Binary files a/datas/network.db and b/datas/network.db differ