10天速通Tkinter库——Day8:《植物杂交实验室》杂交实验及历史记录界面

本篇博客我将介绍Tkinter实践项目《植物杂交实验室》中的杂交实验、实验结果和历史记录两个页面的制作。

它们作为主窗口的子页面实例,除了继承主窗口的基础设置(如图标、标题、尺寸等等)、还可以使用主窗口的属性和方法(如数据变量self.hybridizationPlants、页面跳转方法switch_to_screen等等),以及在constants.py中的常量定义和tool.py中定义的通用组件。具体实现见博客10天速通Tkinter库——Day6:《植物杂交实验室》整体框架介绍

目录

1. 杂交实验

1.1 加载背景、返回按钮、关闭按钮 

1.2 初始化杂交实验信息

1.3 杂交实验开始按钮

1.4 选择重置按钮 

1.5 初始化基础植物卡片矩阵

1.6 初始化基础植物卡片选择结果矩阵

 1.7 杂交方式选择

1.8 杂交算法

2. 实验结果

3. 历史记录

3.1 类定义与初始化 

3.2 历史记录数据加载 

3.3 记录显示

one_item 方法

all_record 方法

4. 总结


1. 杂交实验

组件构成如下:

让我们一步一步来实现,定义一个ExperimentScreen类,继承了主界面和tk.Frame。

1.1 加载背景、返回按钮、关闭按钮 

class ExperimentScreen(tk.Frame):
    """植物杂交系统
    
    1. 基础植物卡片矩阵
    2. 基础植物选择框
    3. 杂交方法选择
    4. 杂交实验开始
    """
    def __init__(self, parent):
        super().__init__(parent)
        # 背景和基础组件
        self.breeding_background = create_background(self,experiment_background_path)
        self.close_button = close_button(self,close2_1_path,close2_2_path,clear=2)
        self.back_button = back_button(self,back2_1_path,back2_2_path,clear=2)
  • 初始化父类。
  • 创建了背景 breeding_background 。
  • 创建了关闭按钮 close_button 。
  • 创建了返回按钮 back_button 。

1.2 初始化杂交实验信息

        # 杂交信息
        self.card_choose_photo = [None for _ in range(4)]
        self.card_choose_photo_id = [0,0,0,0]
        self.card_choose_num = 0
        self.method_id = 0
        self.result_plant_id = 0   
        self.result = 0
  • self.card_choose_photo:一个包含 4 个元素的列表,用于存储选择的植物卡片。
  • self.card_choose_photo_id:一个包含 4 个元素的列表,用于存储选择的植物照片的 ID。
  • self.card_choose_num:选择的植物数量。
  • self.method_id:杂交方法的 ID。
  • self.result_plant_id:杂交结果植物的 ID。
  • self.result:杂交结果。

1.3 杂交实验开始按钮

        self.start_button = create_button(self,
                                          dark_image_path=start1_path,
                                          light_image_path=start2_path,
                                          width=351,
                                          height=103,
                                          locate_x=503,
                                          locate_y=455,
                                          command=self.on_start_button_click)

 定义了一个开始按钮,用于在选择植物和杂交方法后开始实验,它的点击事件处理函数如下:

    def on_start_button_click(self):
        """开始杂交实验按钮点击事件"""
        if self.card_choose_photo_id[0]!=0 and self.method_id!=0:
            if (self.method_id==3 or self.method_id==4) and self.card_choose_photo_id[1]!=0:
                worning_music.play()
                messagebox.showinfo("提示", "该杂交方法只能选择单株植物!")
                self.on_clear_button_click()
            else:
                self.on_breeding_method_select()
                self.master.children['result_screen'].result_show(self.result,self.result_plant_id)
                self.on_clear_button_click()
                self.master.switch_to_screen('result_screen')
        elif self.card_choose_photo_id[0]!=0 and self.method_id==0:
            worning_music.play()
            messagebox.showinfo("提示", "请选择杂交方式!")
        else:
            worning_music.play()
            messagebox.showinfo("提示", "请选择基础植物!")

这段代码定义了一个名为 on_start_button_click 的方法,用于处理开始杂交实验按钮的点击事件。

  1. 检查条件

    • 首先检查 self.card_choose_photo_id 是否不为空(即玩家选择了基础植物)且 self.method_id 是否不为零(即玩家选择了杂交方法)。
    • 然后根据 self.method_id 的值进一步检查玩家的选择是否合理。
  2. 错误提示与处理

    • 如果 self.method_id 为 3 或 4(假设这代表特定的杂交方法),并且玩家选择了第二株植物(self.card_choose_photo_id[1] 不为零),则播放警告音乐并显示提示信息,告知只能选择单株植物进行该杂交方法。
    • 如果 self.method_id 为 3 或 4 且玩家未选择第二株植物,则播放警告音乐并显示提示信息,告知玩家需要选择单株植物进行该杂交方法。
    • 如果 self.method_id 为 0(即未选择杂交方法),则播放警告音乐并显示提示信息,告知玩家需要选择杂交方法。
    • 如果 self.card_choose_photo_id[0] 为零(即未选择基础植物),则播放警告音乐并显示提示信息,告知玩家需要选择基础植物。
  3. 执行杂交实验

    • 如果所有条件都满足,调用 self.on_breeding_method_select() 方法执行杂交实验的逻辑。
    • 调用 self.master.children['result_screen'].result_show(self.result,self.result_plant_id) 方法显示结果屏幕,并传递结果和植物ID。
    • 调用 self.on_clear_button_click() 方法清除当前屏幕的任何状态或选择。
    • 使用 self.master.switch_to_screen('result_screen') 方法切换到结果屏幕。

1.4 选择重置按钮 

        self.clear_button = create_button(self,
                                          dark_image_path=clearButton1_path,
                                          light_image_path=clearButton2_path,
                                          width=180,
                                          height=100,
                                          locate_x=590,
                                          locate_y=238,
                                          command=self.on_clear_button_click,
                                          )

定义了一个清空按钮,可以重置我们的选择,它的点击事件处理函数如下:

    def on_clear_button_click(self):
        """清空组件,初始化基础植物选择和杂交方法"""

        self.card_choose_photo_id = [0,0,0,0]
        self.card_choose_num = 0
        self.method_id = 0
        for col in range(4):
            label = tk.Label(self.card_frame,image=self.empty_card,bd=0)
            label.grid(row=0, column=col,sticky="NSEW")      

 这段代码定义了一个名为 on_clear_button_click 的方法,用于处理清除按钮的点击事件。这个方法的主要功能是清空用户界面中与基础植物选择和杂交方法相关的所有组件,并初始化这些选择的状态。

  1. 初始化基础植物选择

    • self.card_choose_photo_id 是一个列表,用于存储用户选择的基础植物图片ID。列表长度为4,意味着它可以存储4个基础植物的选择状态。将列表中的所有元素初始化为0,表示没有选择任何基础植物。
  2. 初始化杂交方法选择

    • self.method_id 是一个变量,用于存储用户选择的杂交方法ID。将其初始化为0,表示没有选择任何杂交方法。
  3. 清空用户界面

    • 使用一个循环遍历 self.card_frame 中的每一列(共4列),这表明用户界面可能包含4个用于选择基础植物的组件。
    • 对于每一列,创建一个标签 (tk.Label),并设置其图像为 self.empty_card(一个空卡牌的图像)。这表示在用户选择基础植物之前,每个选择区域显示为空。
    • bd=0 设置标签的边界为0,使得标签看起来更简洁。
    • 使用 grid 方法将标签放置在界面上,其中 row=0 表示标签位于第一行,column=col 表示标签位于第 col 列,sticky="NSEW" 表示标签将填充其所在的格子,允许在所有方向上伸缩。

1.5 初始化基础植物卡片矩阵

这个和基础植物图鉴的实现方式相同,只是对点击事件进行了修改。

        self.card_button_matrix()
    def card_button_matrix(self):
        """基础植物卡片矩阵"""
        card_frame = tk.Frame(self)
        card_frame.place(x=28,y=88)
        card_frame.config(bg="orange")
        for row in range(6):
            for col in range(8):
                button = tk.Button(card_frame, image=self.master.basal_plant_card[row][col])
                button.config(borderwidth=0,highlightthickness=0)
                button.grid(row=row, column=col,sticky="NSEW")
                def on_click(row, col):
                    card_music.play()
                    if self.card_choose_num ==4 :
                        worning_music.play()
                        messagebox.showinfo("错误", "你最多只能添加四种基础植物!")
                    else:
                        self.card_choose_photo_id[self.card_choose_num] = col+1+row*8
                        self.card_choose_num += 1
                        self.card_choose_frame()
                button.bind('<Button-1>', lambda event, r=row, c=col: on_click(r, c))

这段代码定义了一个名为 card_button_matrix 的方法,用于创建一个基础植物卡片矩阵,并处理卡片点击事件。 

  1. 创建卡片框架

    • card_frame = tk.Frame(self)
    • 创建一个 tk.Frame 实例,作为卡片矩阵的容器,并设置为其父容器 self
    • 使用 place 方法将 card_frame 放置在父容器中的特定位置 (x=28, y=88)
    • card_frame.config(bg="orange")
    • 设置 card_frame 的背景颜色为橙色。
  2. 填充卡片矩阵

    • 使用两个嵌套的 for 循环来遍历行和列,创建卡片按钮。
    • for row in range(6) 和 for col in range(8)
    • 在每个位置创建一个 tk.Button 实例,按钮的图片设置为 self.master.basal_plant_card[row][col],这是从主窗口对象中获取的植物卡片图片。
    • button.config(borderwidth=0,highlightthickness=0)
    • 设置按钮的边框宽度为0,高亮边框宽度也为0,使得按钮看起来没有边框。
    • button.grid(row=row, column=col,sticky="NSEW")
    • 使用 grid 布局管理器将按钮放置在卡片矩阵的相应位置,sticky="NSEW" 表示按钮会粘在单元格的东、南、西、北边缘。
  3. 处理点击事件

    • def on_click(row, col):
    • 定义一个局部函数 on_click,它接受行号 row 和列号 col 作为参数。
    • card_music.play()
    • 当按钮被点击时,播放 card_music
    • 检查 self.card_choose_num 是否等于4:
      • 如果等于4,表示用户已经选择了四种基础植物,此时:
        • worning_music.play()
        • 使用 messagebox.showinfo("错误", "你最多只能添加四种基础植物!")
        • 显示一个警告信息框,告知用户最多只能添加四种基础植物。
      • 如果不等于4,表示用户还可以添加更多基础植物,此时:
        • self.card_choose_photo_id[self.card_choose_num] = col+1+row*8
        • 将选中的植物卡片ID存储在 self.card_choose_photo_id 数组中,其中 col+1+row*8 是根据行和列计算出的卡片ID。
        • self.card_choose_num += 1
        • 增加已选择植物卡片的数量。
        • self.card_choose_frame()
        • 调用 self.card_choose_frame() 方法,可能用于更新界面或处理卡片选择逻辑。
  4. 绑定点击事件

    • button.bind('<Button-1>', lambda event, r=row, c=col: on_click(r, c))
    • 将 <Button-1> 事件(即鼠标左键点击)绑定到 on_click 函数上,其中 r=row 和 c=col 是传递给 on_click 函数的参数。

1.6 初始化基础植物卡片选择结果矩阵

 我们使用一个1*4的矩阵存储选择的基础植物卡片。当然了,还是老样子,空的部分我们用空白卡片填充

        # 基础植物卡片和选择框初始化
        self.empty_card = self.empty_card_load()
    def empty_card_load(self):
        """空白卡片加载"""
        image = Image.open(emptycard_path)
        image = image.resize((78, 100), Image.Resampling.LANCZOS)
        photo = ImageTk.PhotoImage(image)
        return photo    

组件实现如下:

  self.card_choose_frame()
    def card_choose_frame(self):
        """基础植物卡片选择框"""
        self.card_frame = tk.Frame(self,bd=0)
        self.card_frame.place(x=518,y=138)
        for col in range(4):
            if col < self.card_choose_num:
                self.card_choose_photo[col] = image_load(78,100,basal_card_path,self.card_choose_photo_id[col],".jpg")
                label = tk.Label(self.card_frame,image=self.card_choose_photo[col] ,bd=0)
                label.grid(row=0, column=col,sticky="NSEW")
            else:
                label = tk.Label(self.card_frame,image=self.empty_card,bd=0)
                label.grid(row=0, column=col,sticky="NSEW")    

这段代码定义了一个名为 card_choose_frame 的方法,用于创建和显示用户已选择的基础植物卡片选择框。 

  1. 创建卡片选择框架

    • self.card_frame = tk.Frame(self, bd=0)
    • 创建一个 tk.Frame 实例,作为卡片选择框的容器,并设置其父容器为 selfbd=0 表示没有边框。
    • self.card_frame.place(x=518, y=138)
    • 使用 place 方法将 self.card_frame 放置在父容器中的特定位置 (x=518, y=138)
  2. 填充卡片选择框

    • 使用 for 循环遍历列,从0到3(因为 col 范围是0到3,对应于最多4张卡片的选择)。
    • if col < self.card_choose_num:
      • 如果当前列号 col 小于 self.card_choose_num,表示用户已经选择了足够的卡片。
        • self.card_choose_photo[col] = image_load(78,100,basal_card_path,self.card_choose_photo_id[col],".jpg")
          • 调用 image_load 函数加载图片,将图片设置为卡片选择框中相应卡片的图片。参数包括图片的宽度和高度(78x100),卡片路径(basal_card_path),卡片ID(self.card_choose_photo_id[col])和文件扩展名(.jpg)。
        • label = tk.Label(self.card_frame, image=self.card_choose_photo[col], bd=0)
          • 创建一个 tk.Label 实例,用于显示卡片图片,并设置边框宽度为0。
        • label.grid(row=0, column=col, sticky="NSEW")
          • 使用 grid 布局管理器将标签放置在卡片选择框的相应位置,sticky="NSEW" 表示标签会粘在单元格的东、南、西、北边缘。
    • else:
      • 如果当前列号 col 大于或等于 self.card_choose_num,表示用户尚未选择足够的卡片。
        • label = tk.Label(self.card_frame, image=self.empty_card, bd=0)
          • 创建一个 tk.Label 实例,用于显示一个空卡片图片,self.empty_card 应该是一个空卡片的图片对象。
        • label.grid(row=0, column=col, sticky="NSEW")
          • 使用 grid 布局管理器将标签放置在卡片选择框的相应位置。

 1.7 杂交方式选择

self.method_choose_optionmenu()
    def method_choose_optionmenu(self):
        """杂交方法选择"""
        def on_option_change(value):
            self.method_id = options.index(value) + 1
        options = [" 基础杂交方式 ",
                   " 使用强化药剂 ", 
                   " 单株辐射变异 ",
                   " 注射僵尸血清 ",
                   " 使用神奇魔法 "]
        
        selected_value = tk.StringVar()
        selected_value.set(" 杂交方式选择 ")
        option_menu = tk.OptionMenu(self, selected_value, *options, command=on_option_change,)
        option_menu.config(font=("华文新魏", 29, "bold"),
                           bg="darkslategray",activebackground="gray",
                           fg="limegreen", activeforeground="lime",
                           borderwidth=0, highlightthickness=0)
        menu = option_menu['menu']
        menu.config(bg="darkslategray", fg="lime", font=("华文新魏", 25, "bold"))
        option_menu.place(x=530, y=386)

这段代码定义了一个名为 method_choose_optionmenu 的方法,用于创建一个下拉菜单(OptionMenu),让用户从预定义的杂交方法中选择一个选项。

  1. 内部函数定义

    • def on_option_change(value):
      • 定义了一个内部函数 on_option_change,该函数在选项改变时被调用。
      • self.method_id = options.index(value) + 1
      • 当选项改变时,将选择的选项的索引加1后赋值给 self.method_id。这通常用于将用户的选择转换为一个整数标识符,以便程序可以识别。
  2. 定义选项列表

    • options = [" 基础杂交方式 ", " 使用强化药剂 ", " 单株辐射变异 ", " 注射僵尸血清 ", " 使用神奇魔法 "]
    • 定义了一个名为 options 的列表,包含了下拉菜单中的所有选项。
  3. 创建 StringVar 对象

    • selected_value = tk.StringVar()
    • 创建了一个 tk.StringVar 对象,用于跟踪下拉菜单中当前选中的值。
    • selected_value.set(" 杂交方式选择 ")
    • 初始化 selected_value,将其设置为下拉菜单的默认选项。
  4. 创建 OptionMenu 控件

    • option_menu = tk.OptionMenu(self, selected_value, *options, command=on_option_change,)
    • 创建一个 tk.OptionMenu 控件,它允许用户从给定的选项中选择一个。
    • self 作为父容器传递给 OptionMenu,使下拉菜单能够正确地放置在GUI中。
    • *options 将 options 列表中的元素解包,作为选项传递给 OptionMenu
    • command=on_option_change 指定当用户选择一个选项时调用的函数。
  5. 配置 OptionMenu 控件

    • option_menu.config(...)
    • 使用 config 方法对 option_menu 进行配置,包括:
      • 字体样式和大小:font=("华文新魏", 29, "bold")
      • 背景和活动背景颜色:bg="darkslategray", activebackground="gray"
      • 文本颜色和活动文本颜色:fg="limegreen", activeforeground="lime"
      • 边框和突出显示宽度:borderwidth=0, highlightthickness=0
  6. 配置菜单的菜单项

    • menu = option_menu['menu']
    • 获取 option_menu 的菜单对象。
    • menu.config(bg="darkslategray", fg="lime", font=("华文新魏", 25, "bold"))
    • 对菜单项进行配置,设置背景、文本颜色和字体样式。
  7. 放置 OptionMenu 控件

    • option_menu.place(x=530, y=386)
    • 使用 place 方法将 option_menu 控件放置在父容器中的特定位置 (x=530, y=386)

1.8 杂交算法

这也是开始按钮的点击事件处理

    def on_breeding_method_select(self):
        """杂交实验: 数据匹配
        
        1. 匹配杂交方法, 成功则:
            a. 匹配规则:
                规则1-4: 亲本植物是否为玩家选择的子集,是则杂交成功
                规则5: 产生一个随机数, 匹配随机植物
            b. 判断是否为新植物, 是则result=0,否则result=1
        2. 失败则: result=2
        3. 跳转至结果界面
        4. 修改杂交植物数据, 添加杂交记录
        """
        def is_subset(list1, list2):
            # 子集判断
            return all(item in list2 for item in list1)
        def is_new(type):
            # 是否为新植物判断
            if type: return 0
            else: return 1

        sign = False
        random_integer = random.randint(0, 6)
        num = 0
        for plant in self.master.hybridizationPlants:
            if plant.get("hybridization_method")==self.method_id: 
                if self.method_id!=5:
                    sign = is_subset(plant.get("parent_base_plant_ids"),self.card_choose_photo_id)
                    if sign==True: break
                else:
                    if num==random_integer:
                        sign=True
                        break
                    else:
                        num += 1
        if sign:
            success_music.play()
            self.result_plant_id = plant.get("id")
            self.result = is_new(plant.get("new_hybrid"))
            self.master.hybridizationPlants[self.result_plant_id-1]["new_hybrid"] = False
        else:
            fail_music.play()
            self.result = 2
            self.result_plant_id = 0
        self.add_record()

    def add_record(self):
        # 添加杂交记录
        record = {  
                    "id": len(self.master.recorditem)+1,
                    "parent_base_plant_ids": self.card_choose_photo_id,
                    "hybridization_method": self.method_id,
                    "hybridization_plant_id": self.result_plant_id   
                }
        self.master.recorditem.append(record)

定义了一个名为 on_breeding_method_select 的方法,用于处理杂交实验中的数据匹配和结果处理。

  1. 定义内部函数

    • def is_subset(list1, list2):
      • 定义了一个内部函数 is_subset,用于判断一个列表是否是另一个列表的子集。
    • def is_new(type):
      • 定义了一个内部函数 is_new,用于判断一个植物是否为新植物。
  2. 初始化变量

    • sign = False
      • 初始化一个标志变量 sign,用于表示杂交是否成功。
    • random_integer = random.randint(0, 6)
      • 生成一个 0 到 6 之间的随机整数。
    • num = 0
      • 初始化一个计数器变量 num
  3. 遍历杂交植物

    • 通过遍历 self.master.hybridizationPlants 列表中的每个植物。
    • 对于每个植物,检查其杂交方法是否与当前选择的方法 self.method_id 匹配。
    • 如果方法不是 5,则使用 is_subset 函数检查亲本植物是否为玩家选择的子集。如果是,则设置标志变量 sign 为 True,并退出循环。
    • 如果方法是 5,则使用计数器 num 和随机整数 random_integer 来模拟随机匹配。如果计数器等于随机整数,则设置标志变量 sign 为 True,并退出循环。
  4. 处理杂交结果

    • 如果标志变量 sign 为 True,表示杂交成功。
    • 播放成功音乐。
    • 获取杂交成功的植物的 ID,并将其存储在 self.result_plant_id 变量中。
    • 使用 is_new 函数判断植物是否为新植物,并将结果存储在 self.result 变量中。
    • 将杂交成功的植物的 new_hybrid 属性设置为 False
    • 如果标志变量 sign 为 False,表示杂交失败。
    • 播放失败音乐。
    • 将 self.result 设置为 2,表示杂交失败。
    • 将 self.result_plant_id 设置为 0。
  5. 添加杂交记录

    • 调用 add_record 方法添加杂交记录。
    • 创建一个包含杂交记录信息的字典 record
    • 将记录的 ID 设置为当前记录数量加 1。
    • 将亲本植物的 ID 列表设置为玩家选择的照片 ID。
    • 将杂交方法设置为当前选择的方法。
    • 将杂交植物的 ID 设置为杂交成功的植物的 ID。
    • 将记录添加到 self.master.recorditem 列表中。

2. 实验结果

 其实共有三种结果:

  1. 杂交出来新植物
  2. 杂交出了已拥有植物,如上图
  3. 杂交失败

class ResultScreen(tk.Frame):
    """杂交结果界面"""
    def __init__(self, parent):
        super().__init__(parent)
        self.breeding_background = create_background(self,result_background_path)
        self.back_button = create_button(self,
                                         dark_image_path=result_back1_path,
                                         light_image_path=result_back2_path,
                                         width=85,
                                         height=25,
                                         locate_x=24,
                                         locate_y=567,
                                         command=self.on_back_button_click)
        self.result_plant = None
        self.result_text = [None, None, None]
        self.result_text_load()
        self.pack()

    def result_text_load(self):
        """加载结果的三种情况"""
        for col in range(3):
            self.result_text[col] = image_load(300,90,result_path,col+1,".png")

    def on_back_button_click(self):
        """返回植物杂交界面"""
        self.master.children['experiment_screen'].on_clear_button_click()
        self.master.switch_to_screen('experiment_screen')

    def result_show(self,result,result_plants_id):
        """result: 
            0. 新植物
            1. 已经获得
            2. 杂交失败
        """
        self.label1 = tk.Label(self,image=self.result_text[result],bd=0)
        self.label1.place(x=290,y=362)

        if result_plants_id!=0:
            self.result_plant = image_load(114,156,hybrid_card_path,result_plants_id,".jpg")
        else:
            image = Image.open(emptyresult_path)
            image = image.resize((114, 156), Image.Resampling.LANCZOS)
            self.result_plant = ImageTk.PhotoImage(image)
        self.label2 = tk.Label(self,image=self.result_plant,bd=0)
        self.label2.place(x=382,y=123)

这段代码定义了一个名为 ResultScreen 的类,用于创建植物杂交结果的界面。这个界面包含了用于显示杂交结果的图像、返回按钮以及用于加载和显示结果文本的功能。 

初始化方法 __init__

  • 初始化背景:通过调用 create_background 函数创建一个背景图像,并将其设置为 self.breeding_background
  • 创建返回按钮:使用 create_button 函数创建一个按钮,该按钮在用户点击时调用 on_back_button_click 方法返回到植物杂交界面。
  • 初始化结果变量:设置 self.result_plant 为 None,用于存储显示的杂交结果植物图像。同时,初始化 self.result_text 为一个包含三个 None 的列表,用于存储结果文本的图像。
  • 加载结果文本:调用 result_text_load 方法来加载结果的文本图像。
  • 布局:使用 pack 方法将界面组件添加到父窗口中。

result_text_load 方法

  • 加载结果文本:遍历列表的三个元素,为每种结果情况加载相应的文本图像,并存储在 self.result_text 列表中。

on_back_button_click 方法

  • 返回到植物杂交界面:通过调用 master.children['experiment_screen'].on_clear_button_click() 清空实验屏幕的组件,并使用 master.switch_to_screen('experiment_screen') 切换回植物杂交界面。

result_show 方法

  • 显示结果:根据 result 参数的值显示不同的结果图像。如果结果为 0(表示新植物),则显示新植物的图像;如果结果为 1(表示已获得),则显示已获得植物的图像;如果结果为 2(表示杂交失败),则显示失败的图像。
  • 显示杂交结果植物:根据 result_plants_id 参数的值加载并显示杂交结果植物的图像。如果 result_plants_id 为 0,表示没有结果植物,因此使用空图像。

3. 历史记录

历史记录这个模块略微复杂,因为涉及一个切页操作,并且每一条历史记录数据都是单独呈现的

3.1 类定义与初始化 

class RecordScreen(tk.Frame):
    """杂交记录界面
    1. 每页3条记录
    2. 最多3页记录
    """
    def __init__(self, parent):
        super().__init__(parent)
        self.breeding_background = create_background(self,record_background_path)
        self.close_button = close_button(self,close1_1_path,close1_2_path)
        self.back_button = back_button(self,back1_1_path,back1_2_path)
        self.ahead_buttton = create_button(self,
                                           dark_image_path=ahead2_1_path,
                                           light_image_path=ahead2_2_path,
                                           width=110,
                                           height=27,
                                           locate_x=330,
                                           locate_y=564,
                                           command=self.on_ahead_button_click)
        self.ahead_buttton = create_button(self,
                                           dark_image_path=next2_1_path,
                                           light_image_path=next2_2_path,
                                           width=110,
                                           height=27,
                                           locate_x=450,
                                           locate_y=564,
                                           command=self.on_next_button_click)
        
        self.page = 0
        self.record_num = 0
        self.empty_card = self.empty_card_load()
        self.empty_method = self.empty_method_load()
        self.all_record(page=0)
        self.pack()

    def empty_card_load(self):
        # 加载空白卡片
        image = Image.open(emptycard_path)
        image = image.resize((78, 100), Image.Resampling.LANCZOS)
        photo = ImageTk.PhotoImage(image)
        return photo  
    def empty_method_load(self):
        # 加载空白方法
        image = Image.open(emptymethod_path)
        image = image.resize((175, 100), Image.Resampling.LANCZOS)
        photo = ImageTk.PhotoImage(image)
        return photo        
    
    def on_ahead_button_click(self):
        if self.page!=0:
            self.page -= 1
            self.all_record(self.page)
            self.pack()
    def on_next_button_click(self):
        if self.page!=2 and (self.record_num-3*(self.page+1))>0:
            self.page += 1
            self.all_record(self.page)
            self.pack()

 这段代码定义了一个名为 RecordScreen 的类,用于创建植物杂交记录的界面。这个界面允许用户浏览最多3页的记录,每页显示3条记录。

初始化方法 __init__

  • 初始化背景:通过调用 create_background 函数创建一个背景图像,并将其设置为 self.breeding_background
  • 创建按钮:创建关闭按钮、返回按钮以及用于跳转到上一页和下一页的按钮。这些按钮分别调用 close_buttonback_buttonon_ahead_button_click 和 on_next_button_click 方法。
  • 初始化页面和记录数:设置 self.page 为 0,表示当前显示第一页记录;设置 self.record_num 为 0,表示记录总数。
  • 加载空白卡片和空白方法:通过调用 empty_card_load 和 empty_method_load 方法加载空白卡片和空白方法的图像,并存储在 self.empty_card 和 self.empty_method 中。
  • 加载所有记录:调用 all_record 方法初始化显示的记录,并使用 pack 方法将界面组件添加到父窗口中。

empty_card_load 方法

  • 加载空白卡片:从指定路径加载空白卡片的图像,调整尺寸,并转换为 ImageTk.PhotoImage 格式以便在界面上显示。

empty_method_load 方法

  • 加载空白方法:与 empty_card_load 类似,从指定路径加载空白方法的图像,调整尺寸,并转换为 ImageTk.PhotoImage 格式。

on_ahead_button_click 方法

  • 跳转到上一页:如果当前页不是第一页,减小 self.page 的值,调用 all_record 方法重新加载记录,并使用 pack 方法重新布局界面。

on_next_button_click 方法

  • 跳转到下一页:如果当前页不是最后一页,并且还有更多记录可以显示,增加 self.page 的值,调用 all_record 方法重新加载记录,并使用 pack 方法重新布局界面。

3.2 历史记录数据加载 

    def data_load(self):
        """加载杂交记录数据"""
        records = self.master.recorditem
        records = records[::-1]
        self.basal_plants_card = [[None for _ in range(4)] for _ in range(9)]
        self.hybrid_plants_card = [None for _ in range(9)] 
        self.method = [None for _ in range(9)] 
        self.record_num = 0
        for index, item in enumerate(records):
            basal_plant_card = item.get("parent_base_plant_ids")
            for col in range(len(basal_plant_card)):
                if basal_plant_card[col]!=0:
                    self.basal_plants_card[index][col] = image_load(78,100,basal_card_path,basal_plant_card[col],".jpg")
            if item.get("hybridization_plant_id")!=0:
                self.hybrid_plants_card[index] = image_load(78,100,hybrid_card_path,item.get("hybridization_plant_id"),".jpg")
            self.method[index] = image_load(175,100,method_path,item.get("hybridization_method"),".png")
            self.record_num += 1
            if index==8: break

data_load 方法是 RecordScreen 类的一个方法,用于加载杂交记录数据。以下是该方法的详细解释:

方法目的

  • 加载保存在 self.master.recorditem 中的杂交记录数据,并将这些数据用于界面显示。

方法步骤

  1. 反转记录列表:将 self.master.recorditem 中的记录列表反转,这是因为希望最新的记录显示在最前面。

  2. 初始化卡片和方法列表

    • self.basal_plants_card:创建一个9行4列的二维列表,用于存储基础植物卡片的图像,每行对应一条记录,每列对应一个基础植物。
    • self.hybrid_plants_card:创建一个9元素的列表,用于存储杂交植物卡片的图像,每个元素对应一条记录。
    • self.method:创建一个9元素的列表,用于存储杂交方法的图像,每个元素对应一条记录。
  3. 初始化记录数:将 self.record_num 设置为0,用于记录已加载的记录数量。

  4. 遍历记录:遍历反转后的记录列表 records,对每条记录执行以下操作:

    • 加载基础植物卡片:从记录中获取基础植物ID列表 basal_plant_card,然后遍历这个列表,对于每个非零ID,调用 image_load 函数加载对应的图像,并将其存储在 self.basal_plants_card 对应的位置。
    • 加载杂交植物卡片:如果记录中的杂交植物ID不为零,调用 image_load 函数加载对应的图像,并将其存储在 self.hybrid_plants_card 对应的位置。
    • 加载杂交方法:调用 image_load 函数加载对应的杂交方法图像,并将其存储在 self.method 对应的位置。
    • 更新记录数:每次循环结束时,增加 self.record_num 的值。
  5. 终止条件:如果已经遍历了9条记录,则终止循环。

3.3 记录显示

    def one_item(self,id):
        """展示一条数据: 亲本植物+杂交方法+杂交植物"""
        line = id% 3 

        #  亲本植物
        self.card_frame = tk.Frame(self,bd=0)
        self.card_frame.place(x=156,y=108+line*155)
        for col in range(4):
            if self.basal_plants_card[id][col]!=None:
                label = tk.Label(self.card_frame,image=self.basal_plants_card[id][col] ,bd=0)
                label.grid(row=0, column=col,sticky="NSEW")
            else:
                label = tk.Label(self.card_frame,image=self.empty_card,bd=0)
                label.grid(row=0, column=col,sticky="NSEW")         

        # 杂交方法
        if self.method[id]!=None:
            label2 = tk.Label(self,image=self.method[id],bd=0)
        else:
            label2 = tk.Label(self,image=self.empty_method,bd=0)
        label2.place(x=475,y=line*155+109)
        
        # 杂交植物
        if self.hybrid_plants_card[id]!=None:
            label3 = tk.Label(self,image=self.hybrid_plants_card[id],bd=0)
        else:
            label3 = tk.Label(self,image=self.empty_card,bd=0)
        label3.place(x=654,y=line*155+109)

    def all_record(self,page=0):
        self.data_load()
        self.one_item(0+page*3)
        self.one_item(1+page*3)
        self.one_item(2+page*3)
    

one_item 和 all_record 是两个用于展示数据的方法,它们可能是 tkinter GUI 应用程序中的一部分,用于在一个图形用户界面中展示杂交记录的详细信息。

one_item 方法

这个方法用于展示一条具体的记录,它包含以下步骤:

  1. 计算行位置:通过 id % 3 计算出记录应该显示的行位置。

  2. 创建并放置卡片框架:创建一个 tk.Frame 对象 self.card_frame,然后根据行位置将其放置在界面上。

  3. 展示亲本植物卡片

    • 遍历 self.basal_plants_card[id] 列表,对于每个非空的图像,创建一个 tk.Label 并将其放置在框架中。
    • 如果某个位置是空的(即 None),则显示一个空卡片图像。
  4. 展示杂交方法

    • 如果 self.method[id] 不为空,则创建一个 tk.Label 来展示对应的图像。
    • 如果为空,则展示一个空的方法图像。
  5. 展示杂交植物卡片

    • 如果 self.hybrid_plants_card[id] 不为空,则创建一个 tk.Label 来展示对应的图像。
    • 如果为空,则展示一个空卡片图像。

all_record 方法

这个方法用于展示所有记录,它包含以下步骤:

  1. 加载数据:调用 self.data_load() 方法来加载杂交记录数据。

  2. 展示记录:根据给定的页面 page 参数,调用 self.one_item 方法来展示每条记录。每页显示3条记录,通过 page*3 计算出当前页的第一条记录的索引。

4. 总结

到这里整个项目的全部内容就结束了,今天我们实现了杂交实验、实验结果、杂交记录三个界面。杂交实验重点在于植物卡片的选择、杂交方法的选择,以及匹配算法;杂交记录则是在数据加载和展示部分略微复杂一点。

下期预告:我将对博客进行汇总,介绍我使用到的工具,以及将代码和数据文件放在GitHub仓库。

感谢大家支持!

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值