【记录】python3 使用tkinter制作tkinterUI编辑器 《十八》

使用tkinter制作tkinterUI编辑器



前言

最近更新了几个小功能,这篇统一记录一下,包括复制粘贴控件,将xml读取到当前编辑的控件中,鼠标右键点击树结点弹出右键菜单,还有一些报错就不记录了,下一个目标是实现多重选择功能。

完整代码已上传到github,可从第一篇记录下载


一、快捷键的添加

  1. 发现忘了记录快捷键的处理逻辑,这里记录一下,代码如下,tkinterEditor.py

        ############################################### hot key ###################################################
    
        def init_hot_key(self):
            """
            初始化快捷键
            :return: None
            """
            self.master.bind("<Control-s>", lambda event: self.save_gui())
            self.master.bind("<Control-o>", lambda event: self.open_gui())
            self.master.bind("<Control-n>", lambda event: self.new_gui())
            self.master.bind("<Control-p>", lambda event: self.new_project())
            self.master.bind("<Control-Delete>", lambda event: self.delete_control())
            for k in ("Up", "Down", "Left", "Right"):
                self.master.bind("<Control-{0}>".format(k), partial(self.move_control, k))
    
  2. 修改EditorTree.py,因为我暂时不想处理树的上下左右逻辑(treeview有默认的上下左右事件),所以先break掉,不处理

        def key_press(self, event):
            if event.keysym in ("Up", "Down", "Left", "Right"):
                return "break"
    
  3. 修改EditorPropertyList.py,因为comboBox也有默认的上下事件,所以也先不处理

        def key_click(self, event):
            if event.keysym in ("Up", "Down"):
                return "break"
    

二、复制粘贴控件

  1. 修改default.ini,添加复制粘贴的菜单

    [menu]
    edit = delete_control#delete,move_up#ctrl+up,move_down#ctrl+down,move_left#ctrl+left,move_right#ctrl+right,copy#ctrl+comma,paste#ctrl+period
    
  2. 修改tkinterEditor.py

    from copy import deepcopy
    
    class tkinterEditor(componentMgr):
    
        def __init__(self, master, gui_path):
            componentMgr.__init__(self, master)
            self.config_parser = ToolConfigParser()
            self.config_parser.read("default.ini", encoding="utf-8-sig")
            self.load_from_xml(master, gui_path, True)
            self.theme = EDITOR_THEME_DEFAULT                                       # 主题
            self.right_edit_menu = None                                             # 鼠标右键edit菜单
            self.edit_components = {}                                               # 存储可编辑的控件
            self.selected_component = None                                          # 当前被选中的控件
            self.created_time = 0                                                   # 创建控件时的时间
            self.created_pos_x = 0                                                  # 创建控件时的坐标x
            self.created_pos_y = 0                                                  # 创建控件时的坐标y
            self.is_new_project_show = True                                         # 创建新project界面是否显示
            self.copied_component = None                                            # 复制的控件信息
            self.init_frame()
    
        def create_control(self, quick_name, gui_name, property_dict=None):
            """
            创建控件
            :param quick_name: 快捷按钮名字
            :param gui_name: 控件名字
            :return: None
            """
            if self.get_selected_component() is None:
                return
    
            child_master = self.get_selected_component().component.get_child_master()
            if time.time() - self.created_time > 2:
                self.created_pos_x = 0
                self.created_pos_y = 0
    
            control_name = self.create_random_name(gui_name)
            prop = {
                "background": "white", "x": self.created_pos_x, "y": self.created_pos_y,
                "component_name": control_name, "gui_type": gui_name,
            }
    
            if child_master["background"]== "white":
                prop["background"] = "grey"
    
            # 创建一个frame套在真正的控件外面
            frame_prop = {
                "background": child_master["background"], "highlightcolor": "red",
            }
            frame, info = create_default_component(child_master, "Frame", "None", frame_prop, False)
    
            # 创建控件
            if property_dict is not None:
                property_dict["component_name"] = control_name
                property_dict["is_main"] = 0
                property_dict["x"] = prop["x"]
                property_dict["y"] = prop["y"]
                component = create_component_from_dict(frame, property_dict)
            else:
                component, property_dict = create_default_component(frame, gui_name, control_name, prop)
    
            frame.configure(width=component.winfo_reqwidth() + 4, height=component.winfo_reqheight() + 4)
    
            # 以下控件需要重新修改宽和高
            if gui_name in ("Progressbar", "Scrollbar", "Separator"):
                frame.configure(width=int(property_dict["width"]) + 4, height=int(property_dict["height"]) + 4)
    
            frame.place_configure(x=property_dict["x"], y=property_dict["y"], anchor=property_dict["anchor"])
            component.place_configure(x=0, y=0, anchor="nw")
    
            self.on_component_create(self.file_tab_window.get_cur_tab(), True, component, property_dict, child_master)
    
            self.created_time = time.time()
            self.created_pos_x += 10
            self.created_pos_y += 10
    
        def copy(self):
            """
            复制控件
            :return: None
            """
            edit_component = self.get_selected_component()
            if edit_component is None:
                return
    
            self.copied_component = deepcopy(edit_component.get_component_info())
    
        def paste(self):
            """
            粘贴控件
            :return: None
            """
            if self.copied_component is None:
                return
    
            gui_type = self.copied_component["gui_type"]
            self.create_control(gui_type, gui_type, deepcopy(self.copied_component))
    
        def init_hot_key(self):
            """
            初始化快捷键
            :return: None
            """
            self.master.bind("<Control-s>", lambda event: self.save_gui())
            self.master.bind("<Control-o>", lambda event: self.open_gui())
            self.master.bind("<Control-n>", lambda event: self.new_gui())
            self.master.bind("<Control-p>", lambda event: self.new_project())
            self.master.bind("<Control-Delete>", lambda event: self.delete_control())
            self.master.bind("<Control-comma>", lambda event: self.copy())
            self.master.bind("<Control-period>", lambda event: self.paste())
            for k in ("Up", "Down", "Left", "Right"):
                self.master.bind("<Control-{0}>".format(k), partial(self.move_control, k))
    
  3. 快捷键使用逗号和句号是因为编辑属性的时候会用到ctrl c和v,为了不冲突而使用的

  4. create_control函数里添加了粘贴的逻辑,处理了下控件位置和名字啥的

  5. 复制粘贴的时候一定要用深拷贝,一开始忘了导致出现了严重的问题

三、将xml读取到当前编辑的控件中

  1. 有了这个功能就能重复利用以前已经创建好的ui,不用再新创建了

  2. 修改default.ini,添加快捷功能按钮

    [quick_btn]
    LoadXml = LoadXml
    
  3. 修改tkinterEditor.py

        def init_quick_btn(self):
            """
            初始化功能快捷按键
            :return: None
            """
            prop = {"y": 5, "activebackground": "red", "borderwidth": 3, "width": 0,}
            for quick_name in self.config_parser.options("quick_btn"):
                gui_name = self.config_parser.get("quick_btn", quick_name)
                prop.update({"text": quick_name})
                btn = self.quick_list.add_col(prop, False)
                btn.bind("<ButtonRelease-1>", partial(self.on_quick_btn_click, quick_name, gui_name))
            self.quick_list.do_layout_col()
    
        def on_quick_btn_click(self, quick_name, gui_name, event):
            """
            点击快捷键
            :param gui_name:模块名
            :return:None
            """
            if quick_name == "LoadXml":
                self.load_xml_to_edit_component()
                return
    
            self.create_control(quick_name, gui_name)
    
        def load_xml_to_edit_component(self):
            """
            读取xml后创建到当前编辑的component中
            :return: None
            """
            edit_component = self.get_selected_component()
            if edit_component is None:
                return
    
            file_path = askopenfilename(title=u"选择文件", filetypes=[("xml files", "xml"), ("all files", "*")])
            if not file_path:
                return
    
            cur_tab = self.file_tab_window.get_cur_tab()
            components = self.load_from_xml(edit_component.component.get_child_master(), file_path, False, partial(self.on_component_create, cur_tab, False))
            for parent_name, component_info in components.items():
                if component_info.get("is_main", "0") == "1":
                    component_info["is_main"] = 0
                edit_component.component_info.setdefault("children", []).append(component_info)
    
            self.refresh_tree()
            self.treeview.tree.selection_set(edit_component.name)
    
  4. 读取ui后我重新刷新了一下树,因为我又懒的处理添加逻辑了

四、treeview鼠标右键选中结点功能

  1. 修改EditorTree.py,添加鼠标右键事件

    class EditorTree(ScrollCanvas):
    
        def __init__(self, master=None, cnf={}, **kw):
            ScrollCanvas.__init__(self, master, cnf, **kw)
            self.root_index = 0                                 # 根节点索引
            self.node_num = 0                                   # 所有节点个数
            self.data = {}                                      # 额外存储的数据
            self.editor = None                                  # 编辑器
            self.on_select_tree = None                          # 选中树的回调
            self.on_select_tree_3 = None                        # 右键树
    
        def set_on_select_tree_3(self, on_select_tree_3):
            self.on_select_tree_3 = on_select_tree_3
    
        def create_tree(self):
            """
            创建树
            :return: None
            """
            property_dict = {
                "height": 14,
                "show": "tree",
                "selectmode": "browse",
            }
    
            tree_view, info = create_default_component(self.slide_window, "Treeview", "tree", property_dict)
    
            tree_view.bind('<ButtonRelease-1>', self.select_tree)
            tree_view.bind('<ButtonRelease-3>', self.select_tree_3)
            tree_view.bind('<Key>', self.key_press)
    
        def select_tree_3(self, event):
            if self.on_select_tree_3 != None:
                self.on_select_tree_3(event)
    
  2. 修改tkinterEditor.py

        def init_treeview(self):
            """
            初始化treeview
            :return: None
            """
            # 修改第一列的宽度
            self.treeview.tree.column("#0", width=int(self.treeview["width"]) - 20)
    
            self.treeview.set_on_select_tree(self.on_select_tree)
            self.treeview.set_on_select_tree_3(self.on_select_tree_3)
            self.treeview.set_editor(self)
    
        def on_select_tree(self, event):
            """
            选中树节点
            :param event: event
            :return: None
            """
            index = self.treeview.tree.focus()
            component = self.treeview.get_data_by_index(index)
            if not component:
                return
    
            if index == "root":
                return
    
            self.on_edit_component_selected(component, True, None)
    
        def on_select_tree_3(self, event):
            """
            右键树
            :param event: event
            :return: None
            """
            row = event.widget.identify_row(event.y)
            if not row:
                return
    
            if row == "root":
                return
    
            event.widget.selection_set(row)
    
            component = self.treeview.get_data_by_index(row)
            if not component:
                return
    
            self.on_edit_component_selected(component, True, None)
            self.right_edit_menu.post(event.x_root, event.y_root)
    
  3. 添加这个功能的时候发现点击根节点有报错,在这里处理一下

  4. 使用identify_row函数找到鼠标右键点击的节点,然后选中这个节点,之后弹出鼠标右键菜单


上一篇记录

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

archmage199

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值