import sys
import pickle
import subprocess
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import tkinter.scrolledtext as st
from PIL import Image, ImageTk
from tkinter import Canvas, Frame, BOTH, PhotoImage, filedialog
from X3 import load_data
from X2 import ModelObj,connect_class

# 网络模型正确的图元和连接关系的数据结构，用一个数组表示
RIGHT_MODEL = [
    [0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 1],
    [0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0, 0]
]

# 创建三个空的卷积层图元实例和三个空的全连接层图元实例
Conv1 = None
Conv2 = None
Conv3 = None
Pool1 = None
Pool2 = None
Pool3 = None
Fullconn1 = None
Fullconn2 = None
Fullconn3 = None

global Viewcanvas  # 定义画布
global Text  # 带滚动条
global Root  # 主窗口
global AllModelObj  # 网络对象

List_image = []  # 存储图元的列表
Prob_images = []  # 存储图元的列表
Train_images = []  # 存储训练集的列表
Test_images = []  # 存储测试集的列表
Items1 = []  # 存储新图元的列表
Items2 = []  # 存储新数据线的列表
Items3 = []  # 存储被拖拽的图元的列表
Items4 = []  # 存储新图元文字标签的列表
Counts = {}  # 用来存储Items3每个数字出现过的次数
Conv_xy_list = []  # 存储卷积图元的坐标
Fullconn_xy_list = []  # 存储全连接图元的坐标
Train_list = []  # 存储训练设置参数
Listinstance = []  # 图元对象实例
Dragged_item = ()  # 记录被拖拽的图元id
Original_x = 0  # 记录图元原来的x坐标
Original_y = 0  # 记录图元原来的y坐标
Offset_x = 0  # 记录鼠标相对于图元的x偏移量
Offset_y = 0  # 记录鼠标相对于图元的y偏移量
Yt = 0  # 新图元的y坐标
Batch = 0  # 批次

def create_instance():
    # 创建图元对象实例
    DataSet = ModelObj("DataSet", 1, "数据集", "LoadData", "SetDataPara", ".", 90, 40).output()
    Conv = ModelObj("Conv", 2, "卷积", "ConvProc", "SetConvPara", ".", 200, 40).output()
    Pool = ModelObj("Pool", 3, "最大池化", "MaxPoolProc", "SetPollPara", ".", 90, 105).output()
    FullConn = ModelObj("FullConn", 4, "全连接", "FullConnProc", "SetFullConnPara", ".", 200, 105).output()
    Nonline = ModelObj("Nonline", 5, "非线性函数", "NonlinearProc", "SetNonLPara", ".", 90, 170).output()
    Classifier = ModelObj("Classifier", 6, "分类", "ClassifierProc", "SetClassifyPara", ".", 200, 170).output()
    Error = ModelObj("Error", 7, "误差计算", "ErrorProc", "SetErrorPara", ".", 90, 235).output()
    AjConv = ModelObj("AjConv", 8, "卷积调整", "AjConvProc", "SetAjConvPara", ".", 200, 235).output()
    AjFullconn = ModelObj("AjFullconn", 9, "全连接调整", "AjFullconnProc", "SetAjFCPara", ".", 90, 300).output()
    AjNonline = ModelObj("AjNonline", 10, "非线性调整", "AjNonlineProc", "SetAjNLPara", ".", 200, 300).output()
    listinstance = [DataSet, Conv, Pool, FullConn, Nonline, Classifier, Error, AjConv, AjFullconn, AjNonline]
    return listinstance  # 还回图元对象实例列表

def picture_frame(window_width,window_height):
    gap = 20 # 设置矩形框之间的间隔为10像素
    # 计算矩形框的坐标和尺寸
    rect1_x = gap                                        # 第一个矩形框的左上角x坐标
    rect1_y = gap                                        # 第一个矩形框的左上角y坐标
    rect1_width = window_width * 1 / 3 - gap-30          # 第一个矩形框的宽度
    rect1_height = window_height * 3 / 4 - gap * 2       # 第一个矩形框的高度
    rect2_x = window_width * 1 / 3 + gap - 30            # 第二个矩形框的左上角x坐标
    rect2_y = gap                                        # 第二个矩形框的左上角y坐标
    rect2_width = window_width * 2 / 3 - gap + 30        # 第二个矩形框的宽度
    rect2_height = window_height * 3 / 4 - gap * 2       # 第二个矩形框的高度
    rect3_x = gap                                        # 第三个矩形框的左上角x坐标
    rect3_y = window_height * 3 / 4 + gap                # 第三个矩形框的左上角y坐标
    rect3_width = window_width - gap * 2                 # 第三个矩形框的宽度
    rect3_height = window_height * 1 / 4 - gap * 2       # 第三个矩形框的高度
    global Viewcanvas
    # 创建一个画布，用于绘制矩形框，设置画布的大小和背景色
    Viewcanvas = tk.Canvas(Root, width=window_width, height=window_height, bg="white")
    Viewcanvas.pack()    # 将画布添加到主窗口中
    # 绘制矩形框，使用不同的颜色和线宽，指定矩形框的左上角和右下角坐标，填充色，边框色和边框宽度
    Viewcanvas.create_rectangle(rect1_x, rect1_y, rect1_x + rect1_width, rect1_y + rect1_height,
                            fill=None, outline="lightblue", width=2)
    Viewcanvas.create_rectangle(rect2_x, rect2_y + 20, rect2_x + rect2_width - 20, rect2_y + rect2_height,
                            fill=None, outline="lightblue", width=2)
    Viewcanvas.create_rectangle(rect3_x, rect3_y, rect3_x + rect3_width, rect3_y + rect3_height,
                            fill=None, outline="lightblue", width=2)
    global Text
    Text = st.ScrolledText(Root) # 创建一个带滚动条的文本框，用于展示计算过程
    Text.place(x=rect3_x, y=rect3_y, width=760, height=rect3_height - 1)
# if __name__ == '__main__':
#     Root = tk.Tk()
#     # 设置窗口的大小为1200*750
#     window_width = 1200  # 窗口的宽度
#     window_height = 750  # 窗口的高度
#     Root.title("神经网络可视化")
#     Root.geometry("1200x750")  # 设置窗口的大小和位置
#     picture_frame(window_width, window_height)
#     Root.mainloop()

def element(path):
    # 加载图元对应的图片文件
    img=Image.open(path)
    # 使用resize方法调整图片
    img = img.resize((40, 30))
    # 把Image对象转换成PhotoImage对象
    img = ImageTk.PhotoImage(img)
    # 保存图片的引用，防止被垃圾回收
    Root.img = img
    return img

def push_button():
    button = {}
    style = ttk.Style(Root)
    # 设置blue.TButton样式的背景色为亮蓝灰，前景色为白色
    style.configure("blue.TButton", background="LightSlateGray", foreground="black", font=("黑体", 10))
    style.map("blue.TButton",
              background=[('active', 'PowderBlue'), ('pressed', 'red')],  # 设置激活状态下的背景色为粉蓝色，按下状态下的背景色为红色
              foreground=[('active', 'black'), ('pressed', 'white')])     # 设置激活状态下的前景色为黑色，按下状态下的前景色为白色
    # 设置green.TButton样式的背景色为中海洋绿，前景色为白色
    style.configure("green.TButton", background="MediumSeaGreen", foreground="black", font=("黑体", 8))
    style.map("green.TButton",
              background=[('active', 'PaleGreen'), ('pressed', 'red')],  # 设置激活状态下的背景色为弱绿色，按下状态下的背景色为红色
              foreground=[('active', 'black'), ('pressed', 'white')])     # 设置激活状态下的前景色为黑色，按下状态下的前景色为白色

    # 创建三个按钮，用于触发训练参数的设置、训练、输出模型程序的操作
    button1 = ttk.Button(Root, text=" 训练参数设置 ", style="blue.TButton")
    button1.place(x=20, y=330)
    button['button1'] = button1
    button2 = ttk.Button(Root, text=" 训练 ", style="blue.TButton")
    button2.place(x=140, y=330)
    button['button2'] = button2
    button3 = ttk.Button(Root, text=" 输出模型程序 ", style="blue.TButton")
    button3.place(x=240, y=330)
    button['button3'] = button3
    # 创建五个按钮，用于触发操作
    button4 = ttk.Button(Root, text=" 加载已有模型 ", style="green.TButton")
    button4.place(x=255, y=10)
    button['button4'] = button4
    button5 = ttk.Button(Root, text=" 新建模型 ", style="green.TButton")
    button5.place(x=355, y=10)
    button['button5'] = button5
    button6 = ttk.Button(Root, text=" 图元参数设置 ", style="green.TButton")
    button6.place(x=435, y=10)
    button['button6'] = button6
    button7 = ttk.Button(Root, text=" 保存模型 ", style="green.TButton")
    button7.place(x=535, y=10)
    button['button7'] = button7
    button8 = ttk.Button(Root, text=" 模型正确性检查 ", style="green.TButton")
    button8.place(x=615, y=10)
    button['button8'] = button8
    button9 = ttk.Button(Root, text="退出", style="green.TButton")
    button9.place(x=725, y=10)
    button['button9'] = button9
    return button

def setload_data():
    # 设置数据集路径信息
    train_imgPath = filedialog.askdirectory(title="请选择训练集") + "/"  # 训练集文件夹的位置
    test_imgPath = filedialog.askdirectory(title="请选择测试集") + "/"  # 测试集文件夹的位置
    # 设置图片大小
    img_width = 48  # 图片宽度
    img_height = 48  # 图片高度
    # 设置每批次读入图片的数量
    batch_size = 32  # 批次大小
    # 返回DataPara参数，这里用一个字典来存储
    DataPara = {"train_imgPath": train_imgPath,
                "test_imgPath": test_imgPath,
                "img_width": img_width,
                "img_height": img_height,
                "batch_size": batch_size}
    return DataPara

# 定义图元拖拽开始的函数
def on_drag_start(event):
    global Dragged_item  # 记录被拖拽的图元id
    global Original_x  # 记录图元原来的x坐标
    global Original_y  # 记录图元原来的y坐标
    global Offset_x  # 记录鼠标相对于图元的x偏移量
    global Offset_y  # 记录鼠标相对于图元的y偏移量
    # 获取当前图元的id
    # 这个物体的id是由tkinter自动分配的。当在画布上创建一个物体时，它会返回一个唯一的整数id，可以用它来引用这个物体
    Dragged_item = event.widget.find_closest(event.x, event.y)

    Original_x = event.widget.coords(Dragged_item)[0]    # 获取当前图元的x坐标
    Original_y = event.widget.coords(Dragged_item)[1]    # 获取当前图元的y坐标
    Offset_x = Original_x - event.x    # 计算鼠标相对于图元的x偏移量
    Offset_y = Original_y - event.y    # 计算鼠标相对于图元的y偏移量

# 定义图元拖拽移动的函数
def on_drag_motion(event):
    x = event.x + Offset_x    # 计算图元新的x坐标
    y = event.y + Offset_y    # 计算图元新的y坐标
    event.widget.coords(Dragged_item, x, y)   # 更新图元的位置

def entity_loading_dataset(event):
    global Viewcanvas
    x = event.x + Offset_x
    y = event.y + Offset_y

    Items3.append(0) # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    new_item1 = Viewcanvas.create_image(x, y, image=List_image[0]) # 复制一个图片，表示数据集1
    Viewcanvas.create_text(x, y + 45, text='数据集')# 复制图元对象的标签
    event.widget.coords(Dragged_item, Original_x, Original_y)# 恢复图元的原位置
    Items1.append((new_item1, x, y))# 将新图元的id和坐标添加到列表中
    global Yt
    Items_y = Items1[0] # 将新图元的y坐标取出，方便后续图元水平排列
    Yt = Items_y[2]
    # 创建一个tkinter根窗口，并隐藏它
    root1 = tk.Tk()
    root1.withdraw()

    DataPara = setload_data() # 调用X3中setload_data()函数，获取加载数据集的参数
    global Train_images
    global Test_images
    Train_images, Test_images = load_data(DataPara) # 调用X3中load_data()函数，根据参数加载数据集
    # 打印一些信息，检查是否正确加载
    print("训练集图片的形状：", Train_images.shape)
    print("测试集图片的形状：", Test_images.shape)

# 创建弹出窗口
def creat_window(title):
    top = tk.Toplevel(Root)
    top.geometry("300x350")
    top.title(title)
    return top
# 输入框
def create_input_box(top, text,value):
    box_label = tk.Label(top, text=text)
    box_label.pack(padx=10, pady=10)
    box_size = tk.IntVar(top, value=value)  # 创建一个IntVar对象，并设置默认值为3
    box_size_entry = tk.Entry(top, textvariable=box_size)  # 关联IntVar对象
    box_size_entry.pack(padx=20, pady=20)
    return box_size_entry
# 下拉框
def create_dropdown_box(top, text, listvalues):
    # 创建一个下拉框，用于选择
    box_mode_label = tk.Label(top, text=text)
    box_mode_label.pack(padx=10, pady=10)
    box_mode_combobox = ttk.Combobox(top)
    box_mode_combobox["values"] = listvalues
    box_mode_combobox.pack(padx=20, pady=15)
    return box_mode_combobox

def convolution(event):
    global Items3
    global Viewcanvas
    x = event.x + Offset_x
    Items3.append(1) # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[1]) # 复制一个图片，表示卷积层
    event.widget.coords(Dragged_item, Original_x, Original_y) # 恢复图元的原位置
    Items1.append((new_item1, x, Yt))    # 将新图元的id和坐标添加到列表中
    top = creat_window('卷积参数配置')     # 创建弹出窗口
    kernel_size_entry = create_input_box(top,"卷积核大小：",3) # 创建一个输入框，获取卷积核大小
    stride_entry = create_input_box(top,"卷积核大小：",1) # 创建一个输入框，获取卷积步长
    def get_input(): # 创建
        global Listinstance
        result1 = int(kernel_size_entry.get())# 获取输入框的内容
        result2 = int(stride_entry.get())
        Viewcanvas.create_text(x, Yt + 40, text=str(result1) + "*" + str(result1) + "卷积") # 创建图元对象的标签
        if Listinstance[1][5] == ".":
            Listinstance[1][5] = str(result1) + "," + str(result2)
        else:
            Listinstance[1][5] = Listinstance[1][5] + "  " + str(result1) + "," + str(result2)
        top.destroy()# 关闭窗口
    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def pooling(event):
    global Items3
    global Viewcanvas
    x = event.x + Offset_x
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(2)
    # 复制一个图片，表示池化层
    new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[2])
    # 恢复图元的原位置
    event.widget.coords(Dragged_item, Original_x, Original_y)
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, Yt))
    # 创建弹出窗口
    top=creat_window("池化参数配置")
    pool_type_combobox=create_dropdown_box(top, "池化池类型：", ["max", "avg", "min"])# 创建一个下拉框，用于选择池化类型
    pool_size_entry = create_input_box(top, "池化池大小：", 2)# 创建一个输入框，用于输入池大小
    # 创建
    def get_input():
        # 从下拉框中获取池化类型
        pool_type = pool_type_combobox.get()
        # 从输入框中获取池大小
        pool_size = int(pool_size_entry.get())
        # 复制对象的标签
        Viewcanvas.create_text(x, Yt + 40, text=str(pool_size) + "*" + str(pool_size) + str(pool_type) + "池化")
        if Listinstance[2][5] == ".":
            Listinstance[2][5] = str(pool_type) + "," + str(pool_size)
        else:
            Listinstance[2][5] = Listinstance[2][5] + "  " + str(pool_type) + "," + str(pool_size)
        # 关闭窗口
        top.destroy()

    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def fully_connected(event):
    global Items3
    global Viewcanvas
    x = event.x + Offset_x
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(3)
    # 复制一个图片，表示全连接层
    new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[3])
    # 恢复图元的原位置
    event.widget.coords(Dragged_item, Original_x, Original_y)
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, Yt))
    top = tk.Toplevel(Root)  # 创建弹出窗口
    top.geometry("300x250")
    top.title("全连接参数配置")
    num_outputs_entry = create_input_box(top, "全连接输出维度：", 10)
    # 创建
    def get_input():
        global Listinstance
        # 从下拉框中获取输出维度
        num = int(num_outputs_entry.get())
        # 复制对象的标签
        Viewcanvas.create_text(x, Yt + 40, text=str(num) + "--全连接")
        if Listinstance[3][5] == ".":
            Listinstance[3][5] = str(num)
        else:
            Listinstance[3][5] = Listinstance[3][5] + "  " + str(num)
        # 关闭窗口
        top.destroy()

    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack(padx=20, pady=50)

def nonlinear(event):
    global Items3
    global Viewcanvas
    x = event.x + Offset_x
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(4)
    # 复制一个图片，表示非线性函数图元
    new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[4])
    # 恢复图元的原位置
    event.widget.coords(Dragged_item, Original_x, Original_y)
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, Yt))
    top = creat_window('非线性参数配置')  # 创建弹出窗口
    # 创建一个下拉框，用于选择非线性类型
    nonlinear_mode_combobox = create_dropdown_box(top, "非线性类型", ["Sigmoid", "ReLU", "Tanh"])# 创建一个下拉框，用于选择池化类型
    # 创建
    def get_input():
        global Listinstance
        # 从下拉框中获取池化类型
        nonlinear_mode = nonlinear_mode_combobox.get()
        # 复制对象的标签
        Viewcanvas.create_text(x, Yt + 40, text=str(nonlinear_mode) + "处理")
        if Listinstance[4][5] == ".":
            Listinstance[4][5] = str(nonlinear_mode)
        else:
            Listinstance[4][5] = Listinstance[4][5] + "  " + str(nonlinear_mode)
        # 关闭窗口
        top.destroy()
    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def class_ification(event):
    global Listinstance
    global Viewcanvas
    global Items3
    x = event.x + Offset_x
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(5)
    # 复制一个图片，表示分类器
    new_item1 = Viewcanvas.create_image(x, Yt, image=List_image[5])
    # 恢复图元的原位置
    event.widget.coords(Dragged_item, Original_x, Original_y)
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, Yt))
    top = tk.Toplevel(Root)  # 创建弹出窗口
    top.geometry("300x250")
    top.title("分类参数配置")
    # 创建一个输入框，用于输入阈值
    threshold_label = tk.Label(top, text="阈值：")
    threshold_label.pack(padx=10, pady=10)
    threshold = tk.DoubleVar(top, value=0.001)  # 创建一个DoubleVar对象，并设置默认值为0.001
    threshold_entry = tk.Entry(top, textvariable=threshold)  # 关联DoubleVar对象
    threshold_entry.pack(padx=20, pady=20)

    # 创建
    def get_input():
        # 从输入框中获取阈值
        threshold = float(threshold_entry.get())
        # 复制对象的标签
        Viewcanvas.create_text(x, Yt + 40, text="输出分类结果")
        if Listinstance[5][5] == ".":
            Listinstance[5][5] = str(threshold)
        else:
            Listinstance[5][5] = Listinstance[5][5] + "  " + str(threshold)
        # 关闭窗口
        top.destroy()

    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def error_calculation(event):
    global Items3
    global Viewcanvas
    x = event.x + Offset_x
    y = event.y + Offset_y
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(6)
    # 复制一个图片，表示误差计算
    new_item1 = Viewcanvas.create_image(x, y, image=List_image[6])
    # 恢复图元的原位置
    event.widget.coords(Dragged_item, Original_x, Original_y)
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, y))
    global x_loss
    global y_loss
    x_loss = x
    y_loss = y
    # 创建弹出窗口
    top = creat_window('误差参数配置')
    # 创建一个下拉框，用于选择误差类型
    loss_type_combobox=create_dropdown_box(top, '', ["CEE", "MSE", "MAE"])
    # 创建
    def get_input():
        # 从下拉框中获取池化类型
        loss_type = loss_type_combobox.get()
        # 复制对象的标签
        Viewcanvas.create_text(x, y_loss + 40,
                           text=str(loss_type) + "误差计算")
        if Listinstance[6][5] == ".":
            Listinstance[6][5] = str(loss_type)
        else:
            Listinstance[6][5] = Listinstance[6][5] + "  " + str(loss_type)
        # 关闭窗口
        top.destroy()

    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def adjust_one(event):
    global Viewcanvas
    global Items1
    global Items3
    x = event.x + Offset_x
    y = event.y + Offset_y
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(7)
    # 复制一个图片，表示调整操作
    new_item1 = Viewcanvas.create_image(x, y_loss - 20, image=List_image[7])

    Viewcanvas.create_text(x, y_loss + 20, text='调整1')# 复制对象的标签
    event.widget.coords(Dragged_item, Original_x, Original_y)  # 恢复图元的原位置

    Items1.append((new_item1, x, y))# 将新图元的id和坐标添加到列表中


def adjust_two(event):
    global Viewcanvas
    global Items1
    global Items3
    x = event.x + Offset_x
    y = event.y + Offset_y
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(8)
    # 复制一个图片，表示调整操作
    new_item1 = Viewcanvas.create_image(x, y_loss + 20, image=List_image[7])
    # 复制对象的标签
    Viewcanvas.create_text(x, y_loss + 60, text='调整2')
    event.widget.coords(Dragged_item, Original_x, Original_y)  # 恢复图元的原位置
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, y))


def adjust_three(event):
    global Viewcanvas
    global Items1
    global Items3
    x = event.x + Offset_x
    y = event.y + Offset_y
    # 记录被拖拽的原始图元的id，方便后续进行模型的正确性检查
    Items3.append(9)
    # 复制一个图片，表示调整操作
    new_item1 = Viewcanvas.create_image(x, y, image=List_image[7])
    # 复制对象的标签
    Viewcanvas.create_text(x, y + 40, text='调整3')
    event.widget.coords(Dragged_item, Original_x, Original_y)  # 恢复图元的原位置
    # 将新图元的id和坐标添加到列表中
    Items1.append((new_item1, x, y))

def train_setting():
    global Train_list
    Train_list = []
    top = creat_window("训练参数配置")# 创建弹出的参数配置窗口
    epoch_entry = create_input_box(top,"训练轮数：", 4)# 创建一个输入框，获取训练轮数
    rate_entry=create_input_box(top, '学习率：', 0.1)# 创建一个输入框，获取学习率
    batch_size_entry=create_input_box(top, "批次：", 60)# 创建一个输入框，获取批次
    # 创建
    def get_input():
        # 获取输入框的内容
        result1 = int(epoch_entry.get())
        result2 = float(rate_entry.get())
        result3 = int(batch_size_entry.get())
        Train_list.append(result1)
        Train_list.append(result2)
        Train_list.append(result3)
        # 关闭窗口
        top.destroy()
    button = tk.Button(top, text="获取信息", command=get_input)
    button.pack()

def connecting_line():
    global Viewcanvas
    global Items2
    last_item = Items1[-1]  # 获取最后一个图元的id和坐标
    prev_item = Items1[-2]  # 获取倒数第二个图元的id和坐标
    # 获取最后一个图元的边界框坐标，形式为(x1,y1,x2,y2)，其中(x1,y1)是左上角，(x2,y2)是右下角
    last_bbox = Viewcanvas.bbox(last_item[0])
    # 获取倒数第二个图元的边界框坐标，形式同上
    prev_bbox = Viewcanvas.bbox(prev_item[0])
    # 计算线条的起点和终点，使其与图元的边界对齐，并留出一定距离以避免重叠
    start_x = prev_bbox[2] + 5  # 起点x坐标为倒数第二个图元右边界加10像素
    start_y = prev_bbox[3] - (prev_bbox[3] - prev_bbox[1]) / 2  # 起点y坐标为倒数第二个图元垂直中心
    end_x = last_bbox[0]  # 终点x坐标为最后一个图元左边界
    end_y = last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2  # 终点y坐标为最后一个图元垂直中心
    # 创建一条线，带有箭头，并设置箭头形状、大小、颜色和平滑度等参数
    if Items3[-1] < 6:
        new_item = Viewcanvas.create_line(start_x, start_y, end_x, end_y, arrow=tk.LAST,
                                      arrowshape=(16, 20, 4), fill='lightblue', smooth=True)
        if Items3[-1] == 1:
            x = last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2
            y = last_bbox[1]
            Conv_xy_list.append((x, y))
        if Items3[-1] == 3:
            x = last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2
            y = last_bbox[1]
            Fullconn_xy_list.append((x, y))

    elif Items3[-1] == 6:
        # 计算折线的中间点，使其与图元的边界对齐，并留出一定距离以避免重叠
        mid_x = prev_bbox[2] - (prev_bbox[2] - prev_bbox[0]) / 2  # 中间点x坐标为起点和终点的平均值
        mid_y = prev_bbox[1] - 80  # 中间点y坐标为起点y坐标减20像素
        # 创建一条折线，带有箭头，并设置箭头形状、大小、颜色和平滑度等参数
        x1 = prev_bbox[2] - (prev_bbox[2] - prev_bbox[0]) / 2
        y1 = prev_bbox[1]
        x2 = last_bbox[2]
        y2 = last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2
        new_item = Viewcanvas.create_line(x1, y1, mid_x, mid_y, x2, y2, arrow=tk.LAST,
                                      arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
    elif Items3[-1] == 7:
        x1 = x_loss
        y1 = y_loss
        x2, y2 = Conv_xy_list[0]
        new_item = Viewcanvas.create_line(x1, y1, last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2,
                                      last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2, x2, y2, arrow=tk.LAST,
                                      arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
        Conv_xy_list.remove(Conv_xy_list[0])
    elif Items3[-1] == 8:
        x1 = x_loss
        y1 = y_loss
        x2, y2 = Fullconn_xy_list[0]
        new_item = Viewcanvas.create_line(x1, y1, last_bbox[2] - (last_bbox[2] - last_bbox[0]) / 2,
                                      last_bbox[3] - (last_bbox[3] - last_bbox[1]) / 2, x2, y2, arrow=tk.LAST,
                                      arrowshape=(16, 20, 4), fill='lightblue', smooth=False)
        Fullconn_xy_list.remove(Fullconn_xy_list[0])
    Items2.append(new_item)

def on_drag(obj_id1,event):
    while obj_id1 == 4:  # 如果拖拽的是图元加载数据集
        entity_loading_dataset(event)
        break
    while obj_id1 == 6:  # 卷积
        convolution(event)
        break
    while obj_id1 == 8:  # 池化
        pooling(event)
        break
    while obj_id1 == 10:  # 全连接
        fully_connected(event)
        break
    while obj_id1 == 12:  # 非线性
        nonlinear(event)
        break
    while obj_id1 == 14:  # 分类
        class_ification(event)
        break
    while obj_id1 == 16:  # 误差计算
        error_calculation(event)
        break
    while obj_id1 == 18:  # 调整1
        adjust_one(event)
        break
    while obj_id1 == 20:  # 调整2
        adjust_two(event)
        break
    while obj_id1 == 22:  # 调整3
        adjust_three(event)
        break

# 定义图元拖拽结束的函数
def on_drag_end(event):
    obj_id1 = Dragged_item[0]  # 获取被拖拽的图元的id
    #  使用 print(obj_id1) 从而查看各个图元的id,此步省略
    # 根据图元的类型，复制相应的图片到右侧画布
    on_drag(obj_id1,event)
    # 如果列表中有至少两个图元，就创建一条线，连接最后两个图元的中心点
    while len(Items1) >= 2:
        connecting_line()
        break
def element_binding(obj_x, obj_y, image, y, text):
    global Viewcanvas
    l1 = Viewcanvas.create_image(obj_x, obj_y, image=image) # 创建图元对象
    # 创建图元对象的标签
    Viewcanvas.create_text(obj_x - 40, obj_y + y, text=text, font=("黑体", 10))
    return l1

def create_left_element(AllModelObj,List_image):
    global Viewcanvas
    # 遍历AllModelObj列表，在窗口左侧创建图元菜单
    for obj in AllModelObj:
        # 获取图元对象的类型、标签等信息
        obj_type = obj[1]
        # 并且要根据需求调整每个对象的位置
        obj_x = obj[6]      # 根据对象的id计算x坐标
        obj_y = obj[7]   # 根据对象的id计算y坐标
        # 根据对象的类型，绘制相应的图形
        if obj_type == 1:  # 加载数据集
            l1 = element_binding(obj_x, obj_y, List_image[0], 3, " 加载" + "\n" + "数据集")
        elif obj_type == 2:  # 卷积
            l1 = element_binding(obj_x, obj_y, List_image[1], 0, "卷积")
        elif obj_type == 3:  # 池化
            l1 = element_binding(obj_x, obj_y, List_image[2], 0, "池化")
        elif obj_type == 4:  # 全连接
            l1 = element_binding(obj_x, obj_y, List_image[3], 0, "全连接" + "\n" + " 函数")
        elif obj_type == 5:  # 非线性
            l1 = element_binding(obj_x, obj_y, List_image[4], 0, "非线性" + "\n" + " 函数")
        elif obj_type == 6:  # 分类
            l1 = element_binding(obj_x, obj_y, List_image[5], 0, "类别")
        elif obj_type == 7:  # 误差计算
            l1 = element_binding(obj_x, obj_y, List_image[6], 0, "误差")
        elif obj_type == 8:  # 调整
            l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整1")
        elif obj_type == 9:  # 调整
            l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整2")
        elif obj_type == 10:  # 调整
            l1 = element_binding(obj_x, obj_y, List_image[7], 0, "调整3")
        elif obj_type == 11:  # 调整
            l1 = element_binding(obj_x, obj_y, List_image[8], 3, "主数据连接线")
        elif obj_type == 12:  # 调整
            l1 = element_binding(obj_x, obj_y, List_image[9], 3, "参数连接线")
        # 为左边菜单的每个图元绑定鼠标按下、移动和松开的事件
        Viewcanvas.tag_bind(l1, "<Button-1>", on_drag_start)
        Viewcanvas.tag_bind(l1, "<B1-Motion>", on_drag_motion)
        Viewcanvas.tag_bind(l1, "<ButtonRelease-1>", on_drag_end)

# if __name__ == '__main__':
#     Root = tk.Tk()
#     # 设置窗口的大小为1200*750
#     window_width = 1200  # 窗口的宽度
#     window_height = 750  # 窗口的高度
#     Root.title("神经网络可视化")
#     Root.geometry("1200x750")  # 设置窗口的大小和位置
#     picture_frame(window_width,window_height)
#     Listinstance = create_instance()
#     listclass = connect_class(Listinstance)
#         # 创建网络对象总表和网络连接对象总表
#     AllModelObj = [Listinstance[0], Listinstance[1], Listinstance[2], Listinstance[3], Listinstance[4], Listinstance[5],
#                        Listinstance[6], Listinstance[7], Listinstance[8], Listinstance[9]]
#     AllModelConn = [listclass[0], listclass[1], listclass[2], listclass[3], listclass[4], listclass[5], listclass[6],
#                         listclass[7], listclass[8], listclass[9]]
#     img_path = ["img/data.png", "img/conv.png", "img/pool.png", "img/full_connect.png", "img/nonlinear.png",
#                     "img/classifier.png", "img/error.png", "img/adjust.png", "img/arrow1.png", "img/arrow2.png"]
#     List_image = []
#     for path in img_path:
#         List_image.append(element(path))
#     create_left_element(AllModelObj, List_image)
#     button = push_button()
#     Root.mainloop()
# if __name__ == '__main__':
#     Root = tk.Tk()
#     train_setting()
#     Root.mainloop()
def check_connection():
    connection = [x for x in Items3 if x < 6]
    length = len(connection)
    if length == 0:
        messagebox.showerror("错误", "请添加图元！")
        return
    for i in range(length-1):
        right_model_x = connection[i]
        right_model_y = connection[i+1]
        if RIGHT_MODEL[right_model_x][right_model_y] == 0:
            messagebox.showerror("错误", "连接不正确，请检查！")
            return
    messagebox.showinfo("成功", "连接正确，恭喜！")



def number_judgments(lsts):
    global Items3
    count = []
    sequence = []
    for index,lll in enumerate(lsts):
        if type(lll) == list:
            count.append(lll[1])
            sequence.append(index)
            if lll[1]==7:
                break
    count_2,count_3,count_4=0,0,0
    list_count=[]
    for i in count:
        if i == 2:
            count_2 += 1
            list_count.append(i*10+count_2)
        elif i == 3:
            count_3 += 1
            list_count.append(i * 10 + count_3)
        elif i == 4:
            count_4 += 1
            list_count.append(i * 10 + count_4)
        else:
            list_count.append(i * 10)
    Items3 = [0]
    for i in count:
        Items3.append(i - 1)
    return count,list_count,sequence

def load_file():
    filename = filedialog.askopenfilename(filetypes=[("Pickle files", "*.pkl")])
    # 从本地文件中读取两个列表，用rb模式打开文件
    with open(filename, "rb") as f:
        # 创建一个空列表，用来存储读取的列表
        lsts = []
        # 用一个循环来读取所有的列表，直到文件结束
        while True:
            try:
                # 读取一个列表，并追加到大列表中
                lst = pickle.load(f)
                lsts.append(lst)
            except EOFError:
                # 如果遇到文件结束的错误，就跳出循环
                break
    return lsts

def connecting_lines(obj_x, obj_y, index, x, y, x1, x2, y1, y2, y3 , image, text, smooth, width):
    # 创建图元对象
    Viewcanvas.create_image(obj_x+(index + x) * 80, obj_y, image=image)
    # 创建图元对象的标签
    Viewcanvas.create_text(obj_x+(index + x) * 80, obj_y + y1, text=text, font=("黑体", 8))
    # 创建数据线箭头
    Viewcanvas.create_line(obj_x+(index + x) * 80 + x1, obj_y + y2, obj_x+(index + y) * 80 + x2, obj_y + y3, arrow=tk.LAST,
                       arrowshape=(16, 20, 4), fill='lightblue', smooth=smooth, width=1)

def connecting(obj_x, obj_y, index, x, y1, image, text):
    # 创建图元对象
    Viewcanvas.create_image(obj_x+(index + x) * 80, obj_y, image=image)
    # 创建图元对象的标签
    Viewcanvas.create_text(obj_x+(index + x) * 80, obj_y + y1, text=text, font=("黑体", 8))
# 加载已有模型
def load_model():
    import copy
    global Train_images
    global Test_images
    global Viewcanvas
    global Listinstance
    global Viewcanvas
    lsts = load_file()
    DataPara = setload_data()
    Viewcanvas.delete('all')
    create_left_element(AllModelObj, List_image)
    Train_images, Test_images = load_data(DataPara)  # load_data()函数，根据参数加载数据集
    # 打印一些信息，检查是否正确加载
    print("训练集图片的形状：", Train_images.shape)
    print("测试集图片的形状：", Test_images.shape)
    # 根据pkl文件的前向传播顺序列表，依次识别卷积层、池化层、全连接层、非线性层和分类层的操作
    count,counts,sequence = number_judgments(lsts)
    x,y = 290,200
    connecting_lines(x, y, 0, 0, 0, 25, 60, 45, 0, 0, List_image[0], '数据集', True, 3)
    for layer in range(len(count)):
        if count[layer] == 2: # 如果是卷积层，将读取的卷积层的信息存储到Conv实例
            if counts[layer] % 10 == 1:
                global Conv1
                Listinstance[1] = copy.deepcopy(lsts[sequence[layer]])
                Conv1 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层1', True, 3)
            if counts[layer] % 10 == 2:
                global Conv2
                Conv2 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层2', True, 3)
            if counts[layer] % 10 == 3:
                global Conv3
                Conv3 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[1], '卷积层3', True, 3)
        if count[layer] == 3: # 如果是池化层，将读取的池化层的信息存储到Pool实例
            if counts[layer] % 10 == 1:
                global Pool1
                Listinstance[2] =copy.deepcopy(lsts[sequence[layer]])
                Pool1 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化1', True, 3)
            if counts[layer]%10 == 2:
                global Pool2
                Pool2 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化2', True, 3)
            if counts[layer]%10 == 3:
                global Pool3
                Pool3 = lsts[sequence[layer]]
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[2], '池化3', True, 3)
        if count[layer] == 4: # 如果是全连接层，将读取的全连接层的信息存储到Pool实例
            if counts[layer]%10 == 1:
                global Fullconn1
                Listinstance[3] = lsts[sequence[layer]]
                Fullconn1 = copy.deepcopy(lsts[sequence[layer]])# 深拷贝创建了一个全新的对象,要使用深拷贝，不然报错
                Listinstance[3][5] = str(Fullconn1[5][1]["num_outputs"])
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接1', True, 3)
            if counts[layer]%10 == 2:
                global Fullconn2
                Fullconn2 = lsts[sequence[layer]]
                # FullConn[5] = FullConn[5] + "  " + str(Fullconn2[5][2]["num_outputs"])
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接3', True, 3)
            if counts[layer]%10 == 3:
                global Fullconn3
                Fullconn3 = lsts[sequence[layer]]
                # FullConn[5] = FullConn[5] + "  " + str(Fullconn3[5][3]["num_outputs"])
                connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[3], '全连接3', True, 3)
        if count[layer] == 5: # 如果是非线性层，将读取的非线性层的信息存储到Nonline实例
            Listinstance[4] = lsts[sequence[layer]]
            connecting_lines(x, y, layer, 1, 2, 25, -25, 45, 0, 0, List_image[4], '非线性', True, 3)
        if count[layer] == 6: # 如果是分类输出层，将读取的信息存储到Nonline实例
            Listinstance[5] = lsts[sequence[layer]]
            connecting(x, y, layer, 1, 45,List_image[5], '分类')
        if count[layer] == 7:  # 如果是计算误差层，将读取的信息存储到Error实例
            Listinstance[6] = lsts[sequence[layer]]
            connecting(x-30, y-120, layer, 0, 45, List_image[6], '计算误差')
            Viewcanvas.create_line(x + 5 * 80 + 25, y, x + 5 * 80 + 20, y - 80,
                                   x + 5 * 80 -20 , y-120, arrow=tk.LAST, arrowshape=(16, 20, 4),
                                   fill='lightblue', smooth=True, width=1)

def create_window(number,count,list1,list2,list3,list4,list5,list6):
    global Listinstance
    if number == 1:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取第一层卷积层的参数
            para_list = list1[count[number] - 1].split(",")
            para_list = [int(x) for x in para_list]
            window = creat_window('卷积1参数调整')  # 创建一个新的窗口
            kernel_size_entry = create_input_box(window, "卷积核大小：", para_list[0])  # 创建一个输入框，获取卷积核大小
            stride_entry = create_input_box(window, "卷积步长：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 获取输入框的内容
                result1 = int(kernel_size_entry.get())
                result2 = int(stride_entry.get())
                new_para = str(result1)+ ','+str(result2)
                Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="卷积参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 2:
            # 获取第一层卷积层的参数
            para_list = list1[count[number] - 1].split(",")
            para_list = [int(x) for x in para_list]
            window = creat_window('卷积2参数调整')  # 创建一个新的窗口
            kernel_size_entry = create_input_box(window, "卷积核大小：", para_list[0])  # 创建一个输入框，获取卷积核大小
            stride_entry = create_input_box(window, "卷积步长：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 获取输入框的内容
                result1 = int(kernel_size_entry.get())
                result2 = int(stride_entry.get())
                new_para = str(result1)+ ','+str(result2)
                Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="卷积参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 3:
            # 获取第一层卷积层的参数
            para_list = list1[count[number] - 1].split(",")
            para_list = [int(x) for x in para_list]
            # 创建一个输入框，获取卷积核大小
            window = creat_window('卷积3参数调整')  # 创建一个新的窗口
            kernel_size_entry = create_input_box(window, "卷积核大小：", para_list[0])  # 创建一个输入框，获取卷积核大小
            stride_entry = create_input_box(window, "卷积步长：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 获取输入框的内容
                result1 = int(kernel_size_entry.get())
                result2 = int(stride_entry.get())
                new_para = str(result1)+ ','+str(result2)
                Listinstance[1][5] = Listinstance[1][5].replace(list1[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()

            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="卷积参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        # 返回窗口对象
        return window
    # 如果是池化层
    if number == 2:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取第一层卷积层的参数
            para_list = list2[count[number] - 1].split(",")
            para_list[1] = int(para_list[1])
            window = creat_window('池化1参数调整')  # 创建一个新的窗口
            pool_type_combobox = create_dropdown_box(window, "池化池类型：", ["max", "avg", "min"])  # 创建一个下拉框，用于选择池化类型
            pool_size_entry = create_input_box(window, "池化池大小：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 从下拉框中获取池化类型
                pool_type = pool_type_combobox.get()
                # 从输入框中获取池大小
                pool_size = int(pool_size_entry.get())
                # 关闭窗口
                new_para = str(pool_type)+ ','+str(pool_size)
                Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="池化参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 2:
            # 获取第一层卷积层的参数
            para_list = list2[count[number] - 1].split(",")
            para_list[1] = int(para_list[1])
            window = creat_window('池化2参数调整')  # 创建一个新的窗口
            pool_type_combobox = create_dropdown_box(window, "池化池类型：", ["max", "avg", "min"])  # 创建一个下拉框，用于选择池化类型
            pool_size_entry = create_input_box(window, "池化池大小：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 从下拉框中获取池化类型
                pool_type = pool_type_combobox.get()
                # 从输入框中获取池大小
                pool_size = int(pool_size_entry.get())
                # 关闭窗口
                new_para = str(pool_type)+ ','+str(pool_size)
                Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="池化参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 3:
            # 获取第一层卷积层的参数
            para_list = list2[count[number] - 1].split(",")
            para_list[1] = int(para_list[1])
            window = creat_window('池化3参数调整')  # 创建一个新的窗口
            pool_type_combobox = create_dropdown_box(window, "池化池类型：", ["max", "avg", "min"])  # 创建一个下拉框，用于选择池化类型
            pool_size_entry = create_input_box(window, "池化池大小：", para_list[1])  # 创建一个输入框，获取卷积步
            def modify_para():
                # 从下拉框中获取池化类型
                pool_type = pool_type_combobox.get()
                # 从输入框中获取池大小
                pool_size = int(pool_size_entry.get())
                # 关闭窗口
                new_para = str(pool_type)+ ','+str(pool_size)
                Listinstance[2][5] = Listinstance[2][5].replace(list2[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="池化参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        # 返回窗口对象
        return window
    if number == 3:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取第一层全连接层的参数
            para = list3[count[number] - 1]
            para = int(para)
            window = creat_window('全连接1参数调整')  # 创建一个新的窗口
            num_outputs_entry = create_input_box(window, "输出维度：", para)  # 创建一个输入框，获取卷积步

            def modify_para():
                # 获取输入框的内容
                num = int(num_outputs_entry.get())
                new_para = str(num)
                Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="全连接参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 2:
            # 获取第一层全连接层的参数
            para = list3[count[number] - 1]
            para = int(para)
            window = creat_window('全连接2参数调整')  # 创建一个新的窗口
            num_outputs_entry = create_input_box(window, "输出维度：", para)  # 创建一个输入框，获取全连接

            def modify_para():
                # 获取输入框的内容
                num = int(num_outputs_entry.get())
                new_para = str(num)
                Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="全连接参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        if count[number] == 3:
            # 获取第一层全连接层的参数
            para = list3[count[number] - 1]
            para = int(para)
            window = creat_window('全连接3参数调整')  # 创建一个新的窗口
            num_outputs_entry = create_input_box(window, "输出维度：", para)  # 创建一个输入框，获取全连接

            def modify_para():
                # 获取输入框的内容
                num = int(num_outputs_entry.get())
                new_para = str(num)
                Listinstance[3][5] = Listinstance[3][5].replace(list3[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()
            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="全连接参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        # 返回窗口对象
        return window
    if number == 4:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取第一层非线性层的参数
            para = list3[count[number] - 1]
            window = creat_window('非线性1参数调整')  # 创建一个新的窗口
            nonlinear_mode_combobox = create_dropdown_box(window, "非线性类型：",
                                                          ["Sigmoid", "ReLU", "Tanh"])  # 创建一个下拉框，用于选择非线性类型
            def modify_para():
                # 获取输入框的内容
                nonlinear_mode = nonlinear_mode_combobox.get()
                new_para = f"{nonlinear_mode}"
                Listinstance[4][5] = Listinstance[4][5].replace(list4[count[number] - 1], new_para)
                # 关闭窗口
                window.destroy()

            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="非线性参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        # 返回窗口对象
        return window
    if number == 5:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取分类的参数
            para = list5
            para = float(para)
            window = creat_window('分类参数调整')  # 创建一个新的窗口
            threshold_entry = create_input_box(window, "阈值：", para)  # 创建一个输入框，用于输入阈值

            def modify_para():
                # 获取输入框的内容
                threshold = threshold_entry.get()
                new_para = str(threshold)
                Listinstance[5][5] = Listinstance[5][5].replace(list5, new_para)
                # 关闭窗口
                window.destroy()

            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="分类参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        # 返回窗口对象
        return window
    if number == 6:
        # 把数字的出现次数加一
        count[number] = count.get(number, 0) + 1
        if count[number] == 1:
            # 获取分类的参数
            para = list6
            window = creat_window('误差参数调整')  # 创建一个新的窗口
            loss_type_combobox = create_dropdown_box(window, "误差类型：",
                                                     ["CEE", "MSE", "MAE"])  # 创建一个下拉框，用于选择误差类型
            def modify_para():
                # 获取输入框的内容
                loss_type = loss_type_combobox.get()
                new_para = str(loss_type)
                Listinstance[6][5] = Listinstance[6][5].replace(list6[count[number] - 1], new_para)
                print(Listinstance[6])
                # 关闭窗口
                window.destroy()

            # 创建一个按钮，文本为数字
            button = tk.Button(window, text="误差参数修改")
            # 绑定按钮的点击事件，使得点击后销毁窗口
            button.config(command=modify_para)
            # 把按钮放在窗口中
            button.pack()
        return window  # 返回窗口对象

# 清空当前模型展示区
def restart_new_model():
    # 这将在子进程中调用当前Python解释器，并传递当前文件的路径作为参数
    subprocess.call([sys.executable, __file__])

def setpara_func(): # 图元参数配置与修改
    global Viewcanvas
    # 一个字典，用来记录每个数字出现的次数
    count = {}
    list1 = Listinstance[1][5].split()
    list2 = Listinstance[2][5].split()
    list3 = Listinstance[3][5].split()
    list4 = Listinstance[4][5].split()
    list5 = Listinstance[5][5]
    list6 = Listinstance[6][5].split()
    for i in Items4: # 清除所有文字标签
        Viewcanvas.delete(i[0])
    numbers = [x for x in Items3 if x <= 6] # 一个函数，用来创建一个带有一个数字标签的按钮的窗口
    numbers = numbers[1:]
    def iterate_numbers(): # 一个函数，用来遍历数字列表，依次创建窗口
        if numbers: # 如果数字列表不为空
            number = numbers.pop(0) # 从列表中弹出第一个数字
            window = create_window(number, count, list1, list2, list3, list4, list5, list6)
            window.wait_window() # 等待窗口被关闭
            iterate_numbers() # 再次调用这个函数，创建下一个窗口

    iterate_numbers() # 调用遍历数字的函数

def redirector(inputStr):
    global Text
    Text.configure(state="normal") # 设置文本框为可编辑状态
    Text.insert("end", inputStr) # 在文本框末尾插入内容
    Text.see("end") # 滚动到末尾
    Text.configure(state="disabled") # 设置文本框为只读状态
def close_program():
    sys.exit()

if __name__ == '__main__':
    # 创建一个主窗口
    Root = tk.Tk()
    # 设置窗口的大小为1200*750
    window_width = 800  # 窗口的宽度
    window_height = 450  # 窗口的高度
    Root.title("神经网络可视化")
    Root.geometry("800x450")  # 设置窗口的大小和位置
    picture_frame(window_width,window_height)
    Listinstance = create_instance()
    listclass = connect_class(Listinstance)
    # 创建网络对象总表和网络连接对象总表
    AllModelObj = [Listinstance[0], Listinstance[1], Listinstance[2], Listinstance[3], Listinstance[4], Listinstance[5],
                   Listinstance[6], Listinstance[7], Listinstance[8], Listinstance[9]]
    AllModelConn = [listclass[0], listclass[1], listclass[2], listclass[3], listclass[4], listclass[5], listclass[6],
                    listclass[7], listclass[8], listclass[9]]
    img_path = ["img/data.png", "img/conv.png", "img/pool.png", "img/full_connect.png", "img/nonlinear.png",
                "img/classifier.png", "img/error.png", "img/adjust.png", "img/arrow1.png", "img/arrow2.png"]
    List_image = []
    for path in img_path:
        List_image.append(element(path))
    create_left_element(AllModelObj, List_image)
    button = push_button()
    button['button1'].config(command=train_setting)
    button['button4'].config(command=load_model)
    button['button5'].config(command=restart_new_model)
    button['button6'].config(command=setpara_func)
    button['button8'].config(command=check_connection)
    button['button9'].config(command=close_program)
    sys.stdout.write = redirector
    Root.mainloop()