import ipaddress import sys import threading 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 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 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): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.master = master self.router_img = ImageTk.PhotoImage( Image.open(sys.path[0] + "/../datas/images/路由器@2x.png").resize((40, 40))) self.switch_img = ImageTk.PhotoImage( Image.open(sys.path[0] + "/../datas/images/交换机@2x.png").resize((40, 40))) self.hub_img = ImageTk.PhotoImage( Image.open(sys.path[0] + "/../datas/images/集线器@2x.png").resize((40, 40))) self.host_img = ImageTk.PhotoImage( 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 self.line_end_obj = None self.line_start_ifs = None self.line_end_ifs = None 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): """ 当被选中时,绘制选中框 :return: """ self.delete("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): """ 为每个子组件绑定事件 :return: """ def init(): """ 初始化方法,将连接对象赋值为初始化值 :return: """ self.line_start_obj = None self.line_end_obj = None self.line_start_ifs = None self.line_end_ifs = None def show_menu(event, tag: SimBase): """ 右击组件弹出选择接口框 :param event: :param tag: :return: """ menu = Menu(self, tearoff=0) flag = False for conn in tag.connections: i = tag.connections.index(conn) if not isinstance(conn, AllSimConnect): menu.add_command(label=f"接口{i + 1}", command=lambda num=i + 1: interface_selected(num, tag)) flag = True if not flag: menu.add_command(label="暂无可用接口", state="disabled") menu.post(event.x_root, event.y_root) def interface_selected(interface, tag): """ 选择接口回调 :param interface: :return: """ if self.line_start_ifs is None: self.line_start_ifs = interface self.line_start_obj = tag self.drawLine = True self.bind("", lambda event: right_motion(event)) self.bind("", quit) self.focus_set() # 获取焦点 else: self.line_end_ifs = interface self.line_end_obj = tag self.delete("Line") flag = False if self.line_start_obj.ObjID == self.line_end_obj.ObjID: messagebox.showerror("注意!", message="不能连接自己") return conn = AllSimConnect(self, self.line_start_obj, self.line_start_ifs, self.line_end_obj, self.line_end_ifs) conn.draw_line() for conn_obj in self.line_start_obj.connections: # 判断两个连接对象是否已经连接过了 if conn_obj == conn: flag = True 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() self.unbind("") else: del conn self.delete("Line") self.unbind("") init() messagebox.showerror("注意!", message="已经连接过了") def right_motion(event): """ 移动鼠标 :param event: :param tag: :return: """ if self.drawLine: self.delete("Line") x, y = event.x, event.y if not (0 < x < self.canvas_size[2] and 0 < y < self.canvas_size[3]): if x < 0: x = 0 elif x > self.canvas_size[2]: x = self.canvas_size[2] if y < 0: y = 0 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") return self.create_line(self.line_start_obj.ObjX, self.line_start_obj.ObjY, x + 8, y + 8, fill="#5b9bd5", width=1, tags="Line") def quit(event): self.delete("Line") # 删除刚刚产生的连接线 self.drawLine = False # 关闭画线标志 init() for tag_id, tag in self.AllSimObjs.items(): self.tag_bind(tag.ObjID, "", lambda event, tag=tag: show_menu(event, tag)) # 绑定右击事件 def add_sim_obj(self, component, name): # todo: 绑定事件 """ 绑定事件 :param component: 组件对象 :param name: 组件名称 """ def move(event, name): """ 鼠标左键松开事件 :param event: 事件对象 """ 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 == "集线器": tag = SimHub(self, event.x, event.y) elif name == "交换机": tag = SimSwitch(self, event.x, event.y) else: tag = SimHost(self, event.x, event.y) 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") def motion(event, name): """ 鼠标拖动事件 :param event: 事件对象 """ if name == "路由器": self.chose = self.router_img elif name == "集线器": self.chose = self.hub_img elif name == "交换机": self.chose = self.switch_img else: self.chose = self.host_img self.delete("L") 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, tags="L") self.create_line(event.x - 7, event.y - 42, event.x + 8, event.y - 27, fill="red", width=2, tags="L") self.create_image(event.x - 30, event.y - 30, image=self.chose, anchor="nw", tags="L") self.tag_bind(component, "", lambda event: move(event, name)) self.tag_bind(component, "", lambda event: motion(event, name)) def reload_data(self): # todo: 加载上一次程序运行的数据 """ 加载上一次程序运行时的数据 :return: """ self.AllSimObjs = {} self.conns = [] sim_obj_sql = "select * from sim_objs" sim_obj_data = search(sim_obj_sql) for index, row in sim_obj_data.iterrows(): # 初始化组件对象 sim_type = row["ObjType"] ObjX = row["ObjX"] ObjY = row["ObjY"] ConfigCorrect = row["ConfigCorrect"] ObjLable = row["ObjLabel"] ObjID = row["ObjID"] if sim_type == 1: tag = SimHost(self, ObjX, ObjY, ObjID, ConfigCorrect, ObjLable) elif sim_type == 2: tag = SimRouter(self, ObjX, ObjY, ObjID, ConfigCorrect, ObjLable) elif sim_type == 3: tag = SimSwitch(self, ObjX, ObjY, ObjID, ConfigCorrect, ObjLable) else: tag = SimHub(self, ObjX, ObjY, ObjID, ConfigCorrect, ObjLable) tag.create_img() self.AllSimObjs[tag.ObjID] = tag sim_conn_sql = "select s.conn_id, ConfigCorrect, node_id, node_ifs from sim_conn s join conn_config c on s.conn_id=c.conn_id" sim_conn_data = search(sim_conn_sql) conn_datas = {} for index, conn in sim_conn_data.iterrows(): if (conn["conn_id"], conn["ConfigCorrect"]) not in conn_datas: conn_datas[(conn["conn_id"], conn["ConfigCorrect"])] = [(conn["node_id"], conn["node_ifs"])] else: conn_datas[(conn["conn_id"], conn["ConfigCorrect"])].append((conn["node_id"], conn["node_ifs"])) for key, value in conn_datas.items(): conn_obj = AllSimConnect(self, self.AllSimObjs[value[0][0]], value[0][1], self.AllSimObjs[value[1][0]], value[1][1], key[1]) conn_obj.draw_line() self.AllSimObjs[value[0][0]].connections[value[0][1] - 1] = conn_obj # 将连接对象传入组件对象 self.AllSimObjs[value[1][0]].connections[value[1][1] - 1] = conn_obj self.conns.append(conn_obj) conn_obj.ConfigCorrect = key[1] self.tag_bind_event() def show_obj(self, AllSimObj, AllSimConn): self.AllSimObjs = AllSimObj self.conns = AllSimConn for key, sim_obj in self.AllSimObjs.items(): sim_obj.create_img() for conn in self.conns: conn.draw_line() def delete_obj(self): # todo: 删除对象 """ 选中删除对象 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要删除的对象!") return ask = messagebox.askquestion(title='确认操作', message='确认删除该对象吗?') if ask == "no": return self.delete(self.chose_obj.ObjID) # 删除图片 self.delete(self.chose_obj.ObjID + "text") # 删除标签 for conn in self.chose_obj.connections: # 删除该对象的所有连接线 if isinstance(conn, AllSimConnect): conn.delete_line() delete_sql = f"delete from sim_conn where conn_id in (select conn_id from conn_config where node_id='{self.chose_obj.ObjID}')" delete_config_sql = f"delete from conn_config where node_id='{self.chose_obj.ObjID}'" 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() def delete_line(self): # todo: 删除连接线 """ 删除连接线 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要删除连接线的对象!") return conn_sql = f""" select s.conn_id, ConfigCorrect, node_id, node_ifs from sim_conn s join conn_config c on s.conn_id=c.conn_id where node_id='{self.chose_obj.ObjID}' """ conn_data = search(conn_sql) conn_names = {} for index, conn in conn_data.iterrows(): if conn["conn_id"] not in conn_names: conn_names[conn["conn_id"]] = [(conn["node_id"], conn["node_ifs"])] else: conn_names[conn["conn_id"]].append((conn["node_id"], conn["node_ifs"])) child_d = tk.Toplevel() child_d.title(f"{self.chose_obj.ObjLabel}的连接线配置") child_d.geometry('300x200+450+200') child_d.grab_set() cv1 = Canvas(child_d) cv1.pack() value = StringVar() combobox = ttk.Combobox( master=child_d, # 父容器 height=12, # 高度,下拉显示的条目数量 width=15, # 宽度 state='readonly', # readonly(只可选) font=('', 18), # 字体 textvariable=value, # 通过StringVar设置可改变的值 values=list(conn_names.keys()), # 设置下拉框的选项 ) def del_line(): if value.get() == "": messagebox.showerror("注意", message="请选择需要删除的连接线!") return conn_sql = f""" select conn_id, node_id, node_ifs from conn_config where conn_id='{value.get()}' """ conn_data = search(conn_sql) delete_sql = f"delete from sim_conn where conn_id='{value.get()}'" execute_sql(delete_sql) conn_names = {} for index, conn in conn_data.iterrows(): if conn["conn_id"] not in conn_names: conn_names[conn["conn_id"]] = [(conn["node_id"], conn["node_ifs"])] else: conn_names[conn["conn_id"]].append((conn["node_id"], conn["node_ifs"])) for data in conn_names[value.get()]: self.AllSimObjs[data[0]].connections[data[1] - 1].delete_line() 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) combobox.place(x=20, y=20) def update_tag_name(self): # todo: 更新组件名称 """ 更新组件名称 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要更新的对象!") return child1 = tk.Toplevel() child1.title(self.chose_obj.ObjLabel + "的标签信息") child1.geometry('360x150+200+150') child1.grab_set() # 设置组件焦点抓取。使焦点在释放之前永远保持在这个组件上,只能在这个组件上操作 tk.Label(child1, text='原标签:' + self.chose_obj.ObjLabel, font=('黑体', 16)).grid(row=0, column=0, columnspan=2, sticky='w') tk.Label(child1, text='新标签:', font=('黑体', 16)).grid(row=1, column=0, sticky='w') # ,sticky='w'靠左显示 new_name = tk.Entry(child1, font=('黑体', 16), textvariable=tk.StringVar()) new_name.grid(row=1, column=1) def update_tag(): name = new_name.get() if name == "": messagebox.showerror("注意", message="请输入新名称!") return self.chose_obj.ObjLabel = name self.chose_obj.update() for conn in self.chose_obj.connections: if isinstance(conn, AllSimConnect): 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) tk.Button(child1, text='取消', font=('黑体', 16), height=1, command=child1.destroy).grid(row=2, column=1, sticky='e', pady=10) def network_config(self): # todo: 网络配置 """ 网络配置 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要配置的对象!") return if len(self.chose_obj.get_config()) == 0: messagebox.showerror("注意", message="请先给对象添加连接线!") return child_r = tk.Toplevel() child_r.title(self.chose_obj.ObjLabel + "的网络配置信息") child_r.geometry('713x522+350+200') child_r.grab_set() # 设置组件焦点抓取。使焦点在释放之前永远保持在这个组件上,只能在这个组件上操作 ifs_frame = tk.Frame(child_r) ifs_frame.grid(row=1, column=0, columnspan=4) # 装接口的框架 ifs_frame_YN = tk.Frame(child_r) ifs_frame_YN.grid(row=2, column=0, columnspan=4) num = 0 datas = {} for conn in self.chose_obj.connections: if isinstance(conn, AllSimConnect): index = self.chose_obj.connections.index(conn) num_label = tk.LabelFrame(ifs_frame, text=f'接口{index + 1}') num_label.grid(row=num, column=0, padx=18, ipady=5) tk.Label(num_label, text='MAC:', font=('黑体', 16)).grid(row=0, column=0) mac_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar(), state=DISABLED if self.chose_obj.ObjType not in [1, 2, 3] else NORMAL) mac_en.insert('0', str(self.chose_obj.interface[index].get("mac", ""))) mac_en.grid(row=0, column=1, padx=10) tk.Label(num_label, text='IP:', font=('黑体', 16)).grid(row=1, column=0) ip_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar(), state=DISABLED if self.chose_obj.ObjType not in [1, 2] else NORMAL) ip_en.insert('0', str(self.chose_obj.interface[index].get("ip", ""))) ip_en.grid(row=1, column=1, padx=10) tk.Label(num_label, text='端口:', font=('黑体', 16)).grid(row=0, column=2) port_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar(), state=DISABLED if self.chose_obj.ObjType != 1 else NORMAL) port_en.insert('0', str(self.chose_obj.interface[index].get("conn_port", ""))) port_en.grid(row=0, column=3, padx=10) tk.Label(num_label, text='应用层地址:', font=('黑体', 16)).grid(row=1, column=2) addr_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar(), state=DISABLED if self.chose_obj.ObjType != 1 else NORMAL) addr_en.insert('0', str(self.chose_obj.interface[index].get("addr", ""))) addr_en.grid(row=1, column=3, padx=10) num += 1 datas[index + 1] = {"mac": mac_en, "ip": ip_en, "conn_port": port_en, "addr": addr_en} num_label = tk.LabelFrame(ifs_frame, text=f'示例') num_label.grid(row=num, column=0, padx=18, ipady=5) tk.Label(num_label, text='MAC:', font=('黑体', 16)).grid(row=0, column=0) mac_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar()) mac_en.insert('0', "MAC11") mac_en.config(state=READONLY) mac_en.grid(row=0, column=1, padx=10) tk.Label(num_label, text='IP:', font=('黑体', 16)).grid(row=1, column=0) ip_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar()) ip_en.insert(0, "10.1.1.10") ip_en.config(state=READONLY) ip_en.grid(row=1, column=1, padx=10) tk.Label(num_label, text='端口:', font=('黑体', 16)).grid(row=0, column=2) port_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar()) port_en.insert('0', "80") port_en.config(state=READONLY) port_en.grid(row=0, column=3, padx=10) tk.Label(num_label, text='应用层地址:', font=('黑体', 16)).grid(row=1, column=2) addr_en = tk.Entry(num_label, font=('黑体', 16), textvariable=tk.StringVar()) addr_en.insert('0', "10.1.2.10:10810:Name3") addr_en.config(state=READONLY) addr_en.grid(row=1, column=3, padx=10) def commit(): self.chose_obj.config(datas) self.message.show_message(f"组件 {self.chose_obj.ObjLabel} 网络配置成功!") child_r.destroy() tk.Button(ifs_frame_YN, text='确定', font=('黑体', 16), height=1, command=commit).grid(row=0, column=0, padx=20, pady=20) # ,sticky="w" tk.Button(ifs_frame_YN, text='取消', font=('黑体', 16), height=1, command=child_r.destroy).grid(row=0, column=2, padx=20, pady=20) def router_table_config(self): # todo: 路由表配置 """ 路由表配置 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要配置的对象!") return if self.chose_obj.ObjType != 2: messagebox.showerror("注意", message="请选择路由器对象!") return RouterConfigWindow(self, self.chose_obj) def mac_table_config(self): # todo: 交换表配置 """ 交换表配置 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要配置的对象!") return if self.chose_obj.ObjType != 3: messagebox.showerror("注意", message="请选择交换机对象!") return SwitchConfigWindow(self, self.chose_obj) def show_label(self): """ 显示/不显示标签 :return: """ self.show_label_flag = not self.show_label_flag if self.show_label_flag: for tag in self.AllSimObjs.values(): tag.create_img() else: for tag in self.AllSimObjs.values(): self.delete(tag.ObjID + "text") def show_interface(self): # todo: 显示/不显示接口 """ 显示/不显示接口 :return: """ self.show_interface_flag = not self.show_interface_flag if self.show_interface_flag: for conn in self.conns: conn.draw_line() else: for conn in self.conns: self.delete(conn.NobjS.ObjID + conn.NobjE.ObjID) def show_network_config(self): # todo: 显示网络配置信息 """ 显示网络配置信息 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要显示的对象!") return self.delete("netSet") self.create_text(self.width * 0.82 + 120, 80, text="(" + self.chose_obj.ObjLabel + ")", anchor="n", font=('微软雅黑', 14, 'bold'), fill="#7030a0", tags="netSet") self.create_text(self.width * 0.82 + 20, 80 + 25, text=self.chose_obj, anchor="nw", font=('宋体', 14), tags="netSet") def show_router_config(self): # todo: 显示路由表交换表信息 """ 显示路由交换表信息 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要显示的对象!") return if self.chose_obj.ObjType != 2 and self.chose_obj.ObjType != 3: messagebox.showerror("注意", message="请选择路由器/交换机对象!") return self.delete("routerSet") 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.canvas_size[3] / 2 + 50 + 25, text=self.chose_obj.get_table_config(), anchor="nw", font=('宋体', 14), tags="routerSet") def send_packet(self): """ 发送数据包 :return: """ if self.chose_obj is None: messagebox.showerror("注意", message="请先选择要显示的对象!") return if self.chose_obj.ConfigCorrect != 1: messagebox.showerror("注意", message="请先对选择对象进行网络配置!") return if self.chose_obj.ObjType != 1: messagebox.showerror("注意", message="请选择主机对象!") return child2 = tk.Toplevel() child2.title("数据包配置") child2.geometry('330x195+200+110') child2.grab_set() # 设置组件焦点抓取。使焦点在释放之前永远保持在这个组件上,只能在这个组件上操作 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='消息:', 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=1, column=1, pady=10) def send(): """ 发送数据包 :return: """ if packet_ip.get() == "": messagebox.showerror("注意", message="ip地址不能为空") return if not validate_ip_address(packet_ip.get()): messagebox.showerror("注意", message="IP地址不规范!") return if packet_message.get() == "": messagebox.showerror("注意", message="消息不能为空!") return 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) def send_packet_list(self): """ 批量发送数据包 :return: """ hosts = {} for tag in self.AllSimObjs.values(): if tag.ObjType == 1 and tag.ConfigCorrect == 1: hosts[tag.ObjLabel] = tag.ObjID child2 = tk.Toplevel() child2.title("批量数据包配置") child2.geometry('462x452+200+110') 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='消息:', 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=1, column=1, pady=5) host = StringVar() tk.Label(child2, text='发送主机:', font=('黑体', 16)).grid(row=2, column=0, sticky='w', pady=5) # ,sticky='w'靠左显示 combobox = ttk.Combobox( master=child2, # 父容器 height=5, # 高度,下拉显示的条目数量 width=12, # 宽度 state='readonly', # readonly(只可选) font=('', 16), # 字体 textvariable=host, # 通过StringVar设置可改变的值 values=list(hosts.keys()), # 设置下拉框的选项 ) 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") packet_treeview.heading("source_host", text="发送主机") packet_treeview.column("source_host", width=60, anchor=CENTER) packet_treeview.heading("ip", text="IP") packet_treeview.column("ip", width=120, anchor=CENTER) packet_treeview.heading("mac", text="MAC") packet_treeview.column("mac", width=60, anchor=CENTER) packet_treeview.heading("message", text="消息") packet_treeview.column("message", width=120, anchor=CENTER) packet_treeview.pack(side="left", fill="both") scrollbar = ttk.Scrollbar(packet_frame, orient="vertical", command=packet_treeview.yview) scrollbar.pack(side="right", fill="y") packet_treeview.configure(yscrollcommand=scrollbar.set) packets = [] def add(): ip = packet_ip.get() message = packet_message.get() chose_host = host.get() 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, None, message) packets.append((self.AllSimObjs[hosts[chose_host]], packet)) packet_treeview.delete(*packet_treeview.get_children()) for datas in packets: tag: SimBase = datas[0] packet_data: SimPacket = datas[1] packet_treeview.insert("", "end", values=( tag.ObjLabel, packet_data.destination_ip, packet_data.destination_mac, packet_data.message)) def send(): if len(packets) == 0: messagebox.showerror("注意", message="请至少添加一条数据包!") return for data in packets: threading.Thread(target=data[0].send, args=(data[1],)).start() child2.destroy() tk.Button(child2, text="添加数据包", font=('黑体', 14), height=3, command=add).grid(row=0, column=2, rowspan=4, padx=10) button_frame = Frame(child2) tk.Button(button_frame, text="发送", font=('黑体', 16), height=1, command=send).pack(side=RIGHT, padx=20) tk.Button(button_frame, text="取消", font=('黑体', 16), height=1, command=child2.destroy).pack(side=RIGHT) button_frame.grid(row=6, column=0, columnspan=3) def clear_canvas(self): """ 清除画布 :return: """ ask = messagebox.askquestion(title='确认操作', message='确认要清除画布吗?') if ask == "no": return 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() 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): font_size = 16 font_style = ("Arial", font_size) style = ttk.Style() style.configure("TMenubutton", font=font_style) # 创建一个菜单栏,这里我们可以把他理解成一个容器,在窗口的上方 menubar = tk.Menu(root, font=font_style) # 定义一个空菜单单元 setMenu = tk.Menu(menubar, tearoff=0, font=font_style) menubar.add_cascade(label='删除与修改', menu=setMenu, font=font_style) setMenu.add_command(label='删除对象', command=self.delete_obj) setMenu.add_command(label='删除连接线', command=self.delete_line) setMenu.add_command(label='修改标签', command=self.update_tag_name) # # 定义一个空菜单单元 setMenu = tk.Menu(menubar, tearoff=0, font=font_style) menubar.add_cascade(label='基础配置', menu=setMenu, font=font_style) setMenu.add_command(label='网络配置', command=self.network_config) setMenu.add_command(label='路由表配置', command=self.router_table_config) setMenu.add_command(label='交换表配置', command=self.mac_table_config) root.config(menu=menubar) # 定义一个空菜单单元 setMenu = tk.Menu(menubar, tearoff=0, font=font_style) menubar.add_cascade(label='显示设置', menu=setMenu, font=font_style) setMenu.add_command(label='显示/不显示标签', command=self.show_label) setMenu.add_command(label='显示/不显示接口', command=self.show_interface) setMenu.add_command(label='显示网络配置信息', command=self.show_network_config) setMenu.add_command(label='显示路由/交换表信息', command=self.show_router_config) # 定义一个空菜单单元 setMenu = tk.Menu(menubar, tearoff=0, font=font_style) menubar.add_cascade(label='发送数据包', menu=setMenu, font=font_style) setMenu.add_command(label='发送数据包', command=self.send_packet) setMenu.add_command(label='批量发送数据包', command=self.send_packet_list) menubar.add_command(label='清除屏幕', command=self.clear_canvas, font=font_style) menubar.add_command(label='导出数据', command=self.export_data, font=font_style) menubar.add_command(label='导入数据', command=self.import_data, font=font_style) menubar.add_command(label='校验', command=self.check_network, font=font_style) # 设置字体大小 for menu in menubar.winfo_children(): menu.config(font=font_style) for item in menu.winfo_children(): item.config(font=font_style) root.config(menu=menubar) def create_widget(self): """ 创建整体页面布局 :return: """ 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')) 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 = 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.add_sim_obj(router, "路由器") self.add_sim_obj(switch, "交换机") self.add_sim_obj(hub, "集线器") self.add_sim_obj(host, "主机") if __name__ == '__main__': root = Window() root.title('网络拓扑图') screen_width = root.winfo_screenwidth() # winfo 方法来获取当前电脑屏幕大小 screen_height = root.winfo_screenheight() root_attr = { "width": screen_width * 0.83, "height": screen_height * 0.85, } size = '%dx%d+%d+%d' % (root_attr['width'], root_attr['height'], (screen_width - root_attr['width']) / 2, (screen_height - root_attr['height']) / 2 - 30) canvas = NetWorkAnalog(root, width=root_attr['width'], heigh=root_attr['height'], bg="white") canvas.place(x=0, y=0, anchor='nw') canvas.create_config_button() canvas.reload_data() root.geometry(size) root.mainloop()