You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
21 KiB
411 lines
21 KiB
# 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: 绘制参数更新
|
|
data_frame = self.db.query_fractal_by_id(self.master.chose_fractal.get()).iloc[0]
|
|
params = {
|
|
"self": self,
|
|
"var_text": self.var_text.get(0.0, END).strip()
|
|
}
|
|
self.var_text.delete(0.0, END)
|
|
exec(data_frame["vars"], params)
|
|
|
|
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)
|
|
params = {
|
|
"self": self,
|
|
"var_text": self.var_text.get(0.0, END)
|
|
}
|
|
exec(data_frame["vars"], params)
|
|
|
|
|
|
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: 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()
|