# encoding: utf-8 from tkinter import * from tkinter import messagebox from PIL import Image, ImageTk import math from time import sleep from dbUtil import DbUtil class Message(Canvas): # todo: 日志消息类 def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.message = [] self.master = master self.scrollbar = 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="white"): # todo: 添加日志消息 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 AddFractal(Toplevel): def __init__(self, master, _type, fractal_id=None, *args, **kwargs): super().__init__(*args, **kwargs) self.transient(root) self.master = master self.type = _type self.fractal_id = fractal_id self.cv = Canvas(self, width=screen_width * 0.65, height=screen_height * 0.8) self.width, self.height = int(screen_width * 0.65), int(screen_height * 0.8) self.cv.place(x=0, y=0, anchor=NW) self.label_width, self.label_height = 140, 40 self.top_window_back_img = ImageTk.PhotoImage( Image.open("./images/大背景@3x.jpg").resize((int(screen_width * 0.65), int(screen_height * 0.8)))) self.save_img = ImageTk.PhotoImage(Image.open("./images/保存@3x.png").resize((100, self.label_height))) self.update_img = ImageTk.PhotoImage(Image.open("./images/修改@3x.png").resize((100, self.label_height))) self.cv.create_image(0, 0, image=self.top_window_back_img, anchor=NW) self.var_data_dict = {} if self.type == "update": fractal = DbUtil.query_fractal_by_id(self.fractal_id).iloc[0] self.is_default = fractal["is_default"] self.init_window() if self.type == "update": self.load_fractal_data() def load_fractal_data(self): fractal = DbUtil.query_fractal_by_id(self.fractal_id).iloc[0] self.fractal_name.insert(0, str(fractal["name"])) self.fractal_e_name.insert(0, str(fractal["e_name"])) self.basic_pattern.insert(0.0, "\n".join(str(fractal["basic_pattern"]).split("\n")[:-1])) self.function_code.insert(0.0, "\n".join(str(fractal["function_code"]).split("\n")[:-1])) self.basic_execute_function.insert(0, str(fractal["basic_pattern"]).split("\n")[-1]) self.function_code_execute_function.insert(0, str(fractal["function_code"]).split("\n")[-1]) self.var_tree.insert(0.0, "\n".join(str(fractal["vars"]).split("\n")[:-1])) if self.is_default == 0: self.fractal_name.config(state="readonly") self.fractal_e_name.config(state="readonly") self.basic_pattern.config(state="disabled") self.basic_execute_function.config(state="readonly") self.function_code_execute_function.config(state="readonly") self.var_tree.config(state="disabled") self.function_code.config(state="disabled") def create_label(self, x, y, img, label_text, entry_width=200): self.cv.create_image(x, y, image=img, anchor=NW) self.cv.create_text(x + self.label_width / 2, y + self.label_height / 2, text=label_text, font=("黑体", 12), fill="black") entry = Entry(self.cv, font=("Arial", 18, "bold")) entry.place(x=x + self.label_width, y=y, width=entry_width, height=40, anchor=NW) return entry def save(self): fractal_name = self.fractal_name.get() fractal_e_name = self.fractal_e_name.get() basic_execute_function = self.basic_execute_function.get() function_code_execute_function = self.function_code_execute_function.get() function_code = self.function_code.get(0.0, "end") basic_pattern = self.basic_pattern.get(0.0, "end") vars = self.var_tree.get(0.0, "end") if fractal_name.strip() == "" \ and fractal_e_name == "" \ and basic_execute_function.strip() == ""\ and basic_pattern.strip() == ""\ and function_code.strip() == ""\ and function_code_execute_function.strip() == "": messagebox.showwarning("提示", "值不能为空!") return function_code = function_code + "\n" + function_code_execute_function basic_pattern = basic_pattern + "\n" + basic_execute_function if self.type == "update": DbUtil.update_to_fractal(self.fractal_id, fractal_name, fractal_e_name, basic_pattern, function_code, vars) self.master.master.message.show_message("更新成功!") else: id = DbUtil.insert_to_fractal(fractal_name, fractal_e_name, basic_pattern, function_code, vars) self.master.master.message.show_message("添加成功!") self.destroy() self.master.master.flush() def init_window(self): self.label_img = ImageTk.PhotoImage( Image.open("./images/文字背景@3x.png").resize((self.label_width, self.label_height))) self.fractal_name = self.create_label(35, 30, self.label_img, "分形名称", int((self.width - 100) / 2 - 140)) self.fractal_e_name = self.create_label(35 + int((self.width - 100) / 2) + 30, 30, self.label_img, "分形英文名称", int((self.width - 100) / 2 - 140)) self.basic_execute_function = self.create_label(35, 30 + (self.label_height + 15) * 1, self.label_img, "基本图案调用函数", int((self.width - 100) / 2 - 140)) self.function_code_execute_function = self.create_label(35 + int((self.width - 100) / 2) + 30, 30 + (self.label_height + 15) * 1, self.label_img, "分形树调用函数", entry_width=int((self.width - 100) / 2 - 140)) if self.is_default != 0: Button(self.cv, image=self.save_img if self.type == "add" else self.update_img, command=self.save, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, ).place(x=(self.width - 100) / 2, y=self.height - 60, width=100, height=self.label_height, anchor=NW) self.basic_label_image = ImageTk.PhotoImage(Image.open("./images/文字背景@3x.png").resize((int((self.width - 100) / 2), 30))) # 分形树绘制表单 self.cv.create_image(35 + int((self.width - 100) / 2) + 30, 30 + (self.label_height + 15) * 2, image=self.basic_label_image, anchor=NW) self.cv.create_text(35 + int((self.width - 100) / 2) + 30 + int((self.width - 100) / 4), 30 + (self.label_height + 15) * 2 + 15, text="分形图案绘制函数", font=("黑体", 14)) self.function_code = Text(self.cv, font=("Arial", 14), bg="#bce7f7", highlightthickness=0, highlightcolor="#bce7f7", spacing1=5, spacing2=3, spacing3=3, wrap="none") self.function_code.place(x=35 + int((self.width - 100) / 2) + 30, y=30 + (self.label_height + 15) * 2 + 30, height=self.height - 250, width=int((self.width - 100) / 2)) # 基本图形绘制表单 self.cv.create_image(35, 30 + (self.label_height + 15) * 2, image=self.basic_label_image, anchor=NW) self.cv.create_text(35 + int((self.width - 100) / 4), 30 + (self.label_height + 15) * 2 + 15, text="基本图案绘制函数", font=("黑体", 14)) self.basic_pattern = Text(self.cv, font=("Arial", 14), bg="#bce7f7", highlightthickness=0, spacing1=5, spacing2=3, spacing3=3, wrap="none", undo=True) self.basic_pattern.place(x=35, y=30 + (self.label_height + 15) * 2 + 30, width=int((self.width - 100) / 2), height=285) # 初始变量表单 self.cv.create_image(35, 30 + (self.label_height + 15) * 2 + 30 + 285 + 15, image=self.basic_label_image, anchor=NW) self.cv.create_text(35 + int((self.width - 100) / 4), 30 + (self.label_height + 15) * 2 + 30 + 285 + 30, text="初始变量", font=("黑体", 14)) self.var_tree = Text(self.cv, font=("Arial", 14), bg="#bce7f7", highlightthickness=0, highlightcolor="#bce7f7", spacing1=10, spacing2=5, spacing3=5, wrap="none") self.var_tree.place(x=35, y=30 + (self.label_height + 15) * 2 + 30 + 280 + 45, height=290, width=int((self.width - 100) / 2)) class SelectFractalFrame(Frame): # todo: 右上方分形图案单选框类 def __init__(self, master, *args, ** kwargs): super().__init__(master, *args, **kwargs) self.db = DbUtil() self.width = int(self.cget("width")) self.height = int(self.cget("height")) self.img = ImageTk.PhotoImage(Image.open("./images/上部背景@3x.png").resize( (int(self.width), self.height))) Label(self, width=self.width, height=self.height, image=self.img).place(x=0, y=0, anchor=NW) Label(self, text="分形图案类型", font=("黑体", 14, "bold"), bg="#f7f7f7", ).place(x=10, y=10) self.button_img = ImageTk.PhotoImage(Image.open("./images/新增分形图案类型@3x.png").resize((120, 36))) Button(self, image=self.button_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=self.add_fractal_tree).place(x=280, y=8) self.update_button_img = ImageTk.PhotoImage(Image.open("./images/修改分形图案@3x.png").resize((120, 36))) Button(self, image=self.update_button_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=self.update_fractal_tree).place(x=150, y=8) self.select_frame = Frame(self, width=self.width, height=self.height * 0.8, bg="#f7f7f7") self.select_frame.place(x=0, y=self.height * 0.2) self.create_select_menu() def add_fractal_tree(self): screen_width = root.winfo_screenwidth() # winfo方法来获取当前电脑屏幕大小 screen_height = root.winfo_screenheight() x, y = int(screen_width - screen_width * 0.65) // 2, int(screen_height - screen_height * 0.8) // 2 top_window = AddFractal(self, "add", width=screen_width * 0.65, height=screen_height * 0.8) top_window.geometry(f'{int(screen_width * 0.65)}x{int(screen_height * 0.8)}+{x}+{y}') top_window.mainloop() def update_fractal_tree(self): screen_width = root.winfo_screenwidth() # winfo方法来获取当前电脑屏幕大小 screen_height = root.winfo_screenheight() x, y = int(screen_width - screen_width * 0.65) // 2, int(screen_height - screen_height * 0.8) // 2 top_window = AddFractal(self, "update", width=screen_width * 0.65, height=screen_height * 0.8, fractal_id=self.master.chose_fractal.get()) top_window.geometry(f'{int(screen_width * 0.65)}x{int(screen_height * 0.8)}+{x}+{y}') top_window.mainloop() def create_select_menu(self): # todo: 创建分形图案单选框 data_frame = self.db.execute("select * from fractal") yscro = Scrollbar(self.select_frame, orient=VERTICAL) yscro.place(x=self.width - 20, y=0, height=self.height * 0.8, width=20, anchor='nw') canvas = Canvas(self.select_frame, width=self.width - 20, height=self.height * 0.8, bg="#f7f7f7") canvas.place(x=0, y=0, anchor='nw') f2 = Frame(canvas, bg="#f7f7f7", width=self.width * 0.8, height=self.height * 0.8) canvas.create_window((0, 0), window=f2, anchor=NW) self.master.chose_fractal = IntVar() self.master.chose_fractal.set(data_frame.loc[0:]["id"].tolist()[0]) for data in data_frame.itertuples(index=False): f_id, name, e_name, basic_pattern, function_code = data[0], data[1], data[2], data[3], data[4] Radiobutton(f2, text=f"{name} {e_name}", value=f_id, command=lambda message=f"{name} {e_name}": self.master.update_select(message), variable=self.master.chose_fractal, font=("", 18), bg="#f7f7f7").pack(ipadx=10, anchor="w") canvas.update() canvas.config(scrollregion=canvas.bbox("all"), yscrollcommand=yscro.set) # 设置画布的滚动区域 yscro.config(command=canvas.yview) class FormFractalFrame(Frame): # todo: 右下方变量填写类 def __init__(self, master, *args, **kwargs): super().__init__(master, *args, **kwargs) self.db = DbUtil() self.width = int(self.cget("width")) self.height = int(self.cget("height")) self.var_dict = {} self.img = ImageTk.PhotoImage(Image.open("./images/上部背景@3x.png").resize( (int(self.width), self.height))) Label(self, width=self.width, height=self.height, image=self.img).place(x=0, y=0, anchor=NW) Label(self, text="分形参数设置", font=("黑体", 14, "bold"), bg="#f7f7f7", ).place(x=10, y=10) self.create_form() self.button_img = ImageTk.PhotoImage(Image.open("./images/基本图案绘制@3x.png").resize((90, 36))) Button(self, image=self.button_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=self.master.basic_tree_canvas.draw_basic_tree).place(x=10, y=self.height - 50) self.button2_img = ImageTk.PhotoImage(Image.open("./images/绘制参数更新@3x.png").resize((90, 36))) Button(self, image=self.button2_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=self.update_fractal_var).place(x=100 + 5, y=self.height - 50) self.button3_img = ImageTk.PhotoImage(Image.open("./images/绘制@3x.png").resize((70, 36))) Button(self, image=self.button3_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=lambda params=self.var_dict: self.master.fractal_tree_canvas.draw_fractal_tree(params)).place(x=100 * 2 + 5, y=self.height - 50) self.button4_img = ImageTk.PhotoImage(Image.open("./images/删除图案@3x.png").resize((90, 36))) Button(self, image=self.button4_img, bd=0, relief="solid", bg="#f7f7f7", highlightthickness=0, command=self.delete_fractal).place(x=95 * 3, y=self.height - 50) def delete_fractal(self): """ 删除分形树 :return: """ result = messagebox.askquestion("确认框", "您确定要执行此操作吗?") if not result == "yes": return fractal_id = self.master.chose_fractal.get() DbUtil.delete_fractal(fractal_id) self.master.flush() self.master.message.show_message("删除成功!") def update_fractal_var(self): # todo: 绘制参数更新 text = self.var_text.get(0.0, "end").strip() try: for i in text.split("\n"): i = i.replace(" ", "") var = i.split(":")[1].split("=") self.var_dict[var[0]] = var[1] except Exception as E: messagebox.showerror("注意", message=str(f"{E}\n变量格式错误,格式如下:\n分形层数:finishLevel=10")) def create_form(self): # todo: 分形图案变量生成 data_frame = self.db.query_fractal_by_id(self.master.chose_fractal.get()).iloc[0] self.var_text = Text(self, font=("Arial", 14), bg="#f7f7f7", highlightthickness=0, highlightcolor="#f7f7f7", spacing1=10, spacing2=5, spacing3=5, wrap="none") self.var_text.place(x=0, y=self.height * 0.2, width=self.width, height=self.height * 0.6) self.var_text.insert(0.0, data_frame["vars"]) text = self.var_text.get(0.0, "end").strip() try: for i in text.split("\n"): self.master.message.show_message("\t" + i) i = i.replace(" ", "") var = i.split(":")[1].split("=") self.var_dict[var[0]] = var[1] except Exception as E: messagebox.showerror("注意", message=str(f"{E}\n变量格式错误,格式如下:\n分形层数:finishLevel=10")) class BasicTree(Canvas): # 基本图案绘制画布类 def __init__(self, master, *args, **kwargs): super().__init__(master, *args, **kwargs) self.db = DbUtil() self.width = int(self.cget("width")) self.height = int(self.cget("height")) self.background_img = ImageTk.PhotoImage(Image.open("./images/全览图@3x.png").resize((self.width + 20, self.height + 20))) self.create_image(-5, -5, image=self.background_img, anchor=NW) def draw_basic_tree(self): # todo: 绘制基本图案 self.delete("all") data_frame = self.db.query_fractal_by_id(self.master.chose_fractal.get()) params = { "canvas": self } try: exec(data_frame["basic_pattern"].tolist()[0], params) self.master.message.show_message(f"基本图案绘制成功!") except Exception as E: self.master.message.show_message(f"{E}", color="red") class FractalTreeCanvas(Canvas): # todo: 绘制分形树画布 def __init__(self, master, *args, **kwargs): super().__init__(master, *args, **kwargs) self.db = DbUtil() self.width = int(self.cget("width")) self.height = int(self.cget("height")) def draw_fractal_tree(self, params): try: self.delete("all") data_frame = self.db.query_fractal_by_id(self.master.chose_fractal.get()) params = {key: eval(value) for key, value in params.items()} params["canvas"] = self params["math"] = math params["sleep"] = sleep exec(data_frame["function_code"].tolist()[0], params) self.master.message.show_message("分形树绘制成功!") except Exception as E: self.master.message.show_message(f"{E}", "red") class FractalTree(Canvas): def __init__(self, master, *args, **kwargs): super().__init__(master, *args, **kwargs) self.db = DbUtil() self.width = int(self.cget("width")) self.height = int(self.cget("height")) self.background_image = ImageTk.PhotoImage(Image.open("./images/大背景@3x.jpg").resize((self.width, self.height))) self.create_image(0, 0, image=self.background_image, anchor=NW) self.chose_fractal = 1 self.function_code = "" self.fractal_var = {} self.init_window() def update_select(self, message): # todo: 切换分形图案 self.message.show_message(f"选择了{message},初始参数如下:") self.form_fractal_frame.create_form() def flush(self): self.select_fractal_frame.create_select_menu() self.form_fractal_frame.create_form() def init_window(self): """ 初始化窗口,创建窗口并布局 :return: """ # 创建下方消息显示框 message_frame = Frame(self, width=self.width - 60, height=self.height * 0.2) message_frame.place(x=15, y=self.height * 0.8 + 15, anchor=NW) self.message = Message(message_frame, width=self.width - 60, height=self.height * 0.2 - 30) self.message.pack(anchor=NW) self.message.configure(bg="#85bbd1") # 分形树画布 self.fractal_tree_canvas = FractalTreeCanvas(self, width=self.width * 0.73 - 10, height=self.height * 0.8 + 12, highlightthickness=0, bg="#b5e6f5") self.fractal_tree_canvas.place(x=0, y=0, anchor=NW) # 基本图形绘制框 self.basic_tree_canvas = BasicTree(self, width=150, height=150, bg="#e0eaf5", bd=0) self.basic_tree_canvas.place(x=15, y=self.height * 0.8 - 150, anchor=NW) # 创建左上方分形图案选择框 self.select_fractal_frame = SelectFractalFrame(self, width=self.width * 0.27 - 10, height=int((self.height * 0.8) / 2) - 10) self.select_fractal_frame.place(x=self.width * 0.73, y=10, anchor=NW) # 创建左下方输入框 self.form_fractal_frame = FormFractalFrame(self, width=self.width * 0.27 - 10, height=int((self.height * 0.8) / 2)) self.form_fractal_frame.place(x=self.width * 0.73, y=self.height * 0.8 / 2 + 10, anchor=NW) if __name__ == '__main__': # 创建主窗口 root = Tk() root.title('分形树') screen_width = root.winfo_screenwidth() # winfo方法来获取当前电脑屏幕大小 screen_height = root.winfo_screenheight() root_attr = { "width": screen_width * 0.8, "height": screen_height * 0.8, } size = '%dx%d+%d+%d' % (root_attr['width'], root_attr['height'], (screen_width - root_attr['width']) / 2, (screen_height - root_attr['height']) / 2 - 30) root.geometry(size) tree = FractalTree(root, width=root_attr["width"], height=root_attr["height"]) tree.place(x=0, y=0, anchor=NW) root.mainloop()