From c9836bbea574b7513c6447f16475479ed38a4ed8 Mon Sep 17 00:00:00 2001 From: Maryshca <kuzmichova.maria@ya.ru> Date: Sat, 3 Aug 2024 23:30:48 +0300 Subject: [PATCH] v2 --- app.py | 173 ++++++++++++++++++++++++++++++++++-------------------- styles.py | 3 +- 2 files changed, 112 insertions(+), 64 deletions(-) diff --git a/app.py b/app.py index 604dbaf..d5a523d 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ import re import ctypes +import sys import dearpygui.dearpygui as dpg @@ -8,69 +9,77 @@ from config import load_config, dump_config ctypes.windll.shcore.SetProcessDpiAwareness(1) +def get_absolute_y(item): + y = dpg.get_item_pos(item)[1] + parent = dpg.get_item_parent(item) + while parent: + if dpg.get_item_type(parent) == 'mvAppItemType::mvChildWindow': + y += dpg.get_item_pos(parent)[1] + parent = dpg.get_item_parent(parent) + return y + +def get_item_level(item): + level = 0 + parent = dpg.get_item_parent(item) + while parent: + if dpg.get_item_type(parent) == 'mvAppItemType::mvTabBar': + level += 1 + parent = dpg.get_item_parent(parent) + return level + +def config_iter(config, prefix=""): + for path in config: + namespaces = path.split('.') + for i in range(len(namespaces)): + yield prefix + '.' + '.'.join(namespaces[:i+1]) class ConfigEditor: def __init__(self): dpg.create_context() - self.styles = GuiStyle() self.config = dict() - self.config_file_path = "" - - self.construct_main_window() + self.config_file_path = str() def delete_config_window_and_clear_aliases(self, file_path): if dpg.does_item_exist(file_path): - dpg.delete_item(file_path) - if dpg.does_item_exist(f"{file_path}_tab_bar"): - dpg.delete_item(f"{file_path}_tab_bar") - if dpg.does_item_exist(f"{file_path}_tab"): - dpg.delete_item(f"{file_path}_tab") + + if dpg.get_item_type(file_path) == "mvAppItemType::mvChildWindow": + uuid = dpg.get_item_parent(dpg.get_item_parent(file_path)) + dpg.delete_item(uuid) + else: + dpg.delete_item(file_path) if dpg.does_alias_exist(file_path): dpg.remove_alias(file_path) - if dpg.does_alias_exist(f"{file_path}_tab_bar"): - dpg.remove_alias(f"{file_path}_tab_bar") - if dpg.does_alias_exist(f"{file_path}_tab"): - dpg.remove_alias(f"{file_path}_tab") - for path in self.config: - namespaces = path.split('.') - for i in range(len(namespaces)): - path = self.config_file_path + '.' + '.'.join(namespaces[:i+1]) - if dpg.does_alias_exist(path): - dpg.remove_alias(path) - if dpg.does_alias_exist(f"{path}_tab_bar"): - dpg.remove_alias(f"{path}_tab_bar") - if dpg.does_alias_exist(f"{path}_tab"): - dpg.remove_alias(f"{path}_tab") - if dpg.does_alias_exist(f"{path}_text"): - dpg.remove_alias(f"{path}_text") + for path in config_iter(self.config, prefix=self.config_file_path): + if dpg.does_alias_exist(path): + dpg.remove_alias(path) def update_config(self): for path in self.config: - self.config[path].value = dpg.get_value(f"{self.config_file_path}.{path}") + if dpg.does_item_exist(f"{self.config_file_path}.{path}"): + self.config[path].value = dpg.get_value(f"{self.config_file_path}.{path}") - def manage_file(self, sender, sender_data, user_data): - if user_data == "save": - if not self.config_file_path: - return - self.update_config() - dump_config(self.config, self.config_file_path) - dpg.show_item("file_save_popup") - return + def check_tabview(self, root): + for uuid in dpg.get_item_children(root, 1): + if dpg.get_item_type(uuid) == "mvAppItemType::mvTabBar": + return True, uuid + return False, None + def manage_file(self, sender, sender_data, user_data): file_path = sender_data['file_path_name'] + if not file_path: + return + if user_data == "open": self.delete_config_window_and_clear_aliases(self.config_file_path) self.config_file_path = file_path self.config = load_config(file_path) self.construct_config_ui() - elif user_data == "save_as": - if not self.config_file_path: - return + elif user_data == "save_as" or user_data == "save": self.update_config() - dump_config(config, file_path) + dump_config(self.config, file_path) dpg.show_item("file_save_popup") def set_file_path(self, sender, sender_data, user_data): @@ -86,6 +95,7 @@ class ConfigEditor: dpg.set_item_user_data("folder_dialog", user_data) def construct_config_ui(self): + self.update_config() self.delete_config_window_and_clear_aliases(self.config_file_path) if dpg.get_value("use_tabs_ui"): self.construct_config_ui_tabs() @@ -96,21 +106,23 @@ class ConfigEditor: dpg.bind_font(self.styles.montserrat_font) dpg.bind_theme(self.styles.main_theme) - with dpg.window(label="config file editor", pos=(10, 10), width=800, height=600, tag="main", menubar=True): - with dpg.menu_bar(): + with dpg.window(label="config file editor", pos=(10, 10), width=dpg.get_viewport_client_width(), height=dpg.get_viewport_client_height(), tag="main", menubar=True): + with dpg.menu_bar(tag="main_menu_bar"): with dpg.menu(label="file"): dpg.add_menu_item(label="open", callback=self.open_file_dialog, tag="open_btn", user_data="open") - dpg.add_menu_item(label="save", callback=self.manage_file, tag="save_btn", user_data="save") + dpg.add_menu_item(label="save", callback=lambda: self.manage_file("", {'file_path_name': self.config_file_path}, "save")) dpg.add_menu_item(label="save as", callback=self.open_file_dialog, tag="save_as_btn", user_data="save_as") with dpg.menu(label="view"): dpg.add_menu_item(label="use default theme", check=True, callback=lambda _, v: dpg.bind_theme(None if v else self.styles.main_theme), default_value=False) dpg.add_menu_item(label="use default font", check=True, callback=lambda _, v: dpg.bind_font(None if v else self.styles.montserrat_font), default_value=False) - dpg.add_menu_item(label="use tabs [all entered data will be lost]", check=True, callback=lambda: self.construct_config_ui(), default_value=False, tag="use_tabs_ui") + dpg.add_menu_item(label="use tabs", check=True, callback=lambda: self.construct_config_ui(), default_value=False, tag="use_tabs_ui") dpg.add_menu_item(label="show theme editor", callback=dpg.show_style_editor) with dpg.menu(label="settings"): - dpg.add_menu_item(label="auto save [not impl]", check=True, callback=lambda: print("TODO"), default_value=False) + dpg.add_menu_item(label="show mouse coords", check=True, callback=lambda _, v: dpg.show_item("mouse") if v else dpg.hide_item("mouse"), default_value=False) + + dpg.add_text("", tag="mouse", show=False) with dpg.file_dialog(directory_selector=False, show=False, callback=self.manage_file, tag="file_dialog", width=600, height=400): dpg.add_file_extension(".txt", color=(0, 255, 0, 255)) @@ -122,6 +134,17 @@ class ConfigEditor: with dpg.window(label="", autosize=True, no_resize=True, modal=True, tag="file_save_popup", show=False, pos=(40, 40)): dpg.add_text("file saved successfully:)") + with dpg.item_handler_registry(tag="main_handler") as handler: + dpg.add_item_resize_handler(callback=lambda: self.change_height(self.config_file_path)) + + dpg.bind_item_handler_registry("main", "main_handler") + dpg.set_viewport_resize_callback(lambda: self.change_height(self.config_file_path)) + + uuid = dpg.add_handler_registry() + def clb(s, a, u): + dpg.set_value("mouse", str(a)) + dpg.add_mouse_move_handler(parent=uuid, callback=clb) + def construct_config_ui_collapsing_headers(self): dpg.add_collapsing_header(label=self.config_file_path, parent="main", tag=self.config_file_path, indent=0, default_open=True) @@ -138,18 +161,13 @@ class ConfigEditor: self.create_widget(full_path) - def check_tabview(self, root): - for uuid in dpg.get_item_children(root, 1): - if dpg.get_item_type(uuid) == "mvAppItemType::mvTabBar": - return True, uuid - return False, None - def construct_config_ui_tabs(self): tab_bar_exists, tab_bar_uuid = self.check_tabview("main") if not tab_bar_exists: - tab_bar_uuid = dpg.add_tab_bar(parent="main", tag=f"{self.config_file_path}_tab_bar") - dpg.add_tab(label=self.config_file_path, parent=tab_bar_uuid, tag=f"{self.config_file_path}_tab") - dpg.add_child_window(parent=f"{self.config_file_path}_tab", tag=self.config_file_path, border=True, autosize_y=True, autosize_x=True) + tab_bar_uuid = dpg.add_tab_bar(parent="main") + tab_uuid = dpg.add_tab(label=self.config_file_path, parent=tab_bar_uuid) + dpg.add_child_window(parent=tab_uuid, tag=self.config_file_path, border=True, autosize_y=False, autosize_x=True) + dpg.set_item_user_data(self.config_file_path, 2 * SPACE) for full_path in self.config: namespaces = full_path.split('.') @@ -160,31 +178,48 @@ class ConfigEditor: tab_bar_exists, tab_bar_uuid = self.check_tabview(parent) if not tab_bar_exists: - tab_bar_uuid = dpg.add_tab_bar(parent=parent, tag=f"{path}_tab_bar") - dpg.set_item_height(parent, dpg.get_item_height(parent) + 4 * SPACE + FONT_SIZE) + tab_bar_uuid = dpg.add_tab_bar(parent=parent, callback=lambda s, a: self.change_height(a)) + dpg.set_item_user_data(parent, dpg.get_item_user_data(parent) + 4 * SPACE + FONT_SIZE) if not dpg.does_item_exist(path): - dpg.add_tab(label=namespaces[level], parent=tab_bar_uuid, tag=f"{path}_tab") - dpg.add_child_window(parent=f"{path}_tab", tag=path, border=True, autosize_y=False, autosize_x=True, height=2 * SPACE) + tab_uuid = dpg.add_tab(label=namespaces[level], parent=tab_bar_uuid) + dpg.add_child_window(parent=tab_uuid, tag=path, border=True, autosize_y=False, autosize_x=True) + dpg.set_item_user_data(path, 2 * SPACE) parent = path self.create_widget(full_path) + def change_height(self, item): + if not dpg.does_item_exist(item): + return + if dpg.get_item_type(item) == "mvAppItemType::mvChildWindow": + uuid = dpg.get_item_parent(item) + new_height = dpg.get_viewport_client_height() - get_absolute_y(uuid) - SPACE * get_item_level(item) + + dpg.set_item_height(item, max(new_height, dpg.get_item_user_data(item))) + + for child in dpg.get_item_children(item, 1): + self.change_height(child) + def create_widget(self, full_path): tag = self.config_file_path + '.' + full_path parent = '.'.join([self.config_file_path] + full_path.split('.')[:-1]) - if dpg.get_value("use_tabs_ui") and parent != self.config_file_path: - dpg.set_item_height(parent, dpg.get_item_height(parent) + 2 * SPACE + FONT_SIZE + SPACE) - value = self.config[full_path].value value_type = self.config[full_path].value_type key = self.config[full_path].key comment = self.config[full_path].comment - with dpg.group(horizontal=True, parent=parent, indent=INDENT): - dpg.add_text(key, tag=f"{tag}_text") + if dpg.get_value("use_tabs_ui") and parent != self.config_file_path and full_path != key: + dpg.set_item_user_data(parent, dpg.get_item_user_data(parent) + 2 * SPACE + FONT_SIZE + SPACE) + + indent = INDENT + if dpg.get_value("use_tabs_ui"): + indent = 0 + + with dpg.group(horizontal=True, parent=parent, indent=indent): + text_uuid = dpg.add_text(key) if value_type == 'BOOLEAN': dpg.add_checkbox(default_value=value, tag=tag) elif value_type == 'DOUBLE': @@ -197,16 +232,28 @@ class ConfigEditor: else: dpg.add_input_text(default_value=value, tag=tag) if comment: - dpg.add_text(comment, parent=dpg.add_tooltip(parent=f"{tag}_text")) + dpg.add_text(comment, parent=dpg.add_tooltip(parent=text_uuid)) def run(self): - dpg.create_viewport(title='config file editor', width=800, height=600, small_icon=ICON_PATH) + dpg.create_viewport( + title='config file editor', + small_icon=ICON_PATH, + large_icon=ICON_PATH + ) dpg.setup_dearpygui() dpg.show_viewport() + self.construct_main_window() + if self.config_file_path: + self.construct_config_ui() dpg.set_primary_window("main", True) dpg.start_dearpygui() dpg.destroy_context() if __name__ == "__main__": app = ConfigEditor() + + if len(sys.argv) > 1: + app.config_file_path = sys.argv[1] + app.config = load_config(sys.argv[1]) + app.run() diff --git a/styles.py b/styles.py index adbc8bd..eb4d17c 100644 --- a/styles.py +++ b/styles.py @@ -7,6 +7,7 @@ ROUNDING = 10 SPACE = 10 INDENT = 40 ICON_PATH = "icons\\icon.ico" +BORDER = 3 class GuiStyle: def __init__(self): @@ -59,7 +60,7 @@ class GuiStyle: dpg.add_theme_style(item, SPACE, SPACE, category=dpg.mvThemeCat_Core, parent=theme_all) dpg.add_theme_style(dpg.mvStyleVar_IndentSpacing, 30, category=dpg.mvThemeCat_Core, parent=theme_all) - dpg.add_theme_style(dpg.mvStyleVar_ChildBorderSize, 3, category=dpg.mvThemeCat_Core, parent=theme_all) + dpg.add_theme_style(dpg.mvStyleVar_ChildBorderSize, BORDER, category=dpg.mvThemeCat_Core, parent=theme_all) dpg.add_theme_style(dpg.mvStyleVar_WindowBorderSize, 0, category=dpg.mvThemeCat_Core, parent=theme_all) dpg.add_theme_style(dpg.mvStyleVar_FrameBorderSize, 0, category=dpg.mvThemeCat_Core, parent=theme_all) dpg.add_theme_style(dpg.mvStyleVar_WindowMinSize, 200, 200, category=dpg.mvThemeCat_Core, parent=theme_all) \ No newline at end of file -- GitLab