123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- # TODO Lot of hardcoded shizzle
- import logging
- from itertools import groupby
- from typing import Optional, List, Union
- from urllib.error import URLError
- import arklog
- import dearpygui.dearpygui as dpg
- import dearpygui.demo as demo
- from graph_exploring_tool import query
- from graph_exploring_tool.query import QueryTemplate
- arklog.set_config_logging()
- def _config(sender, keyword, user_data):
- widget_type = dpg.get_item_type(sender)
- items = user_data
- if widget_type == "mvAppItemType::mvRadioButton":
- value = True
- else:
- keyword = dpg.get_item_label(sender)
- value = dpg.get_value(sender)
- if isinstance(user_data, list):
- for item in items:
- dpg.configure_item(item, **{keyword: value})
- else:
- dpg.configure_item(items, **{keyword: value})
- def add_main_menu():
- with dpg.menu_bar():
- with dpg.menu(label="Menu"):
- dpg.add_menu_item(label="New")
- dpg.add_menu_item(label="Open")
- with dpg.menu(label="Open Recent"):
- dpg.add_menu_item(label="patty.h")
- dpg.add_menu_item(label="nick.py")
- dpg.add_menu_item(label="Save")
- dpg.add_menu_item(label="Exit")
- # TODO Add tab for prefixes
- def create_query_palette(query_palette: List[QueryTemplate]):
- demo_layout_child = dpg.generate_uuid()
- with dpg.child_window(tag=demo_layout_child, label="Query Palette", width=220, menubar=True):
- with dpg.menu_bar():
- dpg.add_menu(label="Query Palette", enabled=False)
- dpg.add_input_text(label="Filter", callback=lambda s, a: dpg.set_value("__query_filter", a))
- with dpg.filter_set(tag="__query_filter"):
- # TODO Populate from onto/base
- # TODO Bug fix this; actually figure out groups
- # TODO Fix filtering
- grouped_query_palette = groupby(sorted(query_palette, key=lambda palette_query: palette_query.group), key=lambda palette_query: palette_query.group)
- for query_template_group, query_template_queries in grouped_query_palette:
- with dpg.tree_node(label=query_template_group, tag=query_template_group, default_open=True, filter_key=""):
- full_filter = ""
- for query_template in query_template_queries:
- query_button = dpg.add_button(label=query_template.name, filter_key=query_template.name, callback=_query_palette_click, user_data=query_template)
- with dpg.tooltip(dpg.last_item()):
- dpg.add_text(query_template.description)
- full_filter =f"{full_filter},{query_template.name}"
- # TODO Make theme-ing more generic
- with dpg.theme() as item_theme:
- with dpg.theme_component(dpg.mvAll):
- dpg.add_theme_color(dpg.mvThemeCol_Text, (200, 200, 100), category=dpg.mvThemeCat_Core)
- dpg.add_theme_style(dpg.mvStyleVar_FrameRounding, 0, category=dpg.mvThemeCat_Core)
- if query_template.visual_support:
- dpg.bind_item_theme(query_button, item_theme)
- dpg.configure_item(query_template_group, filter_key=full_filter)
- def create_query_options(endpoint: str):
- with dpg.group(horizontal=True):
- dpg.add_combo(("Visual", "Textual"), default_value="Visual", callback=_mode_select, width=100, tag="__editor_selector")
- dpg.add_input_text(default_value=endpoint, width=300, enabled=False, tag="__endpoint_input")
- dpg.add_checkbox(label="debug", callback=_config)
- dpg.add_checkbox(label="annotate", default_value=False, callback=_config) # TODO Actually make this annotate the result data with its type
- # TODO Make the elements color coded etc...
- # TODO Fix context menu and keyboard operations
- def create_query_editor_visual(example_prefix:str, example_query:str, show: bool = False):
- with dpg.child_window(autosize_x=True, height=500, menubar=True, show=show, tag="__query_editor_visual"):
- with dpg.menu_bar():
- dpg.add_menu(label="Visual Query Editor", enabled=False)
- with dpg.tab_bar():
- with dpg.tab(label="Query", tag="__query_tab"):
- dpg.add_input_text(default_value=example_query, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__query_editor_visual_input")
- with dpg.tab(label="Prefix", tag="__prefix_tab"):
- dpg.add_input_text(default_value=example_prefix, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__prefix_editor_visual_input")
- with dpg.group(tag="__visual_editor_fields"):
- # TODO Make more clear which placeholder is getting replaced (Maybe do it interactively, replace as user changes)
- pass
- with dpg.group(horizontal=True):
- dpg.add_button(label="Query", callback=_perform_query)
- dpg.add_button(label="Save", callback=_select_directory) # TODO
- dpg.add_button(label="Load") # TODO
- # TODO Maybe remove/change this option, we made all the queries mostly 'visual'
- # It's practically a duplicate at this point
- def create_query_editor_textual(example_prefix:str, example_query:str, show: bool = False):
- with dpg.child_window(autosize_x=True, height=500, menubar=True, show=show, tag="__query_editor_textual"):
- with dpg.menu_bar():
- dpg.add_menu(label="Textual Query Editor", enabled=False)
- with dpg.tab_bar():
- with dpg.tab(label="Query", tag="__query_textual_tab"):
- dpg.add_input_text(default_value=example_query, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__query_editor_textual_input")
- with dpg.tab(label="Prefix", tag="__prefix_textual_tab"):
- dpg.add_input_text(default_value=example_prefix, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__prefix_editor_textual_input")
- with dpg.group(horizontal=True):
- dpg.add_button(label="Query", callback=_perform_query)
- dpg.add_button(label="Save", callback=_select_directory) # TODO
- dpg.add_button(label="Load") # TODO
- # TODO Not at the bottom because fok getting that layout to fit correctly
- def create_status_console():
- with dpg.child_window(autosize_x=True, height=35):
- with dpg.group(horizontal=True):
- dpg.add_text(default_value="Ready.", tag="__status_console")
- def set_copy(element: Union[int, str], text_data:str):
- dpg.set_value(element, shorten(text_data))
- i = dpg.get_item_label(element)
- dpg.set_value(f"__copy_{i}", shorten(text_data))
- dpg.configure_item(f"__copy_drag_payload_{i}", drag_data=shorten(text_data))
- dpg.set_value(f"__copy_drag_{i}", shorten(text_data))
- def create_query_results():
- with dpg.child_window(autosize_x=True, autosize_y=True, menubar=True, tag="__query_results_window"):
- with dpg.menu_bar():
- dpg.add_menu(label="Results", enabled=False)
- # dpg.add_input_text(label="Filter", tag="__result_filter_input", callback=lambda s, a: dpg.set_value("__result_filter", a))
- with dpg.tab_bar():
- with dpg.tab(label="Query Result", tag="__query_result_tab"):
- with dpg.table(header_row=True, policy=dpg.mvTable_SizingFixedFit, row_background=True, reorderable=True,
- resizable=True, no_host_extendX=False, hideable=True,
- borders_innerV=True, delay_search=True, borders_outerV=True, borders_innerH=True,
- borders_outerH=True, tag="__result_table"):
- pass
- with dpg.tab(label="Saved"):
- with dpg.group(horizontal=True):
- with dpg.group():
- for i in range(0, 10):
- dpg.add_input_text(label=f"{i}", payload_type="string", width=500, drop_callback=lambda s, a: set_copy(s,a))
- with dpg.group():
- for i in range(0, 10):
- dpg.add_text(tag=f"__copy_{i}", default_value="Empty")
- with dpg.drag_payload(parent=dpg.last_item(), drag_data="Empty", payload_type="string", tag=f"__copy_drag_payload_{i}"):
- dpg.add_text(tag=f"__copy_drag_{i}", default_value="Empty")
- with dpg.tab(label="Debug"):
- dpg.add_text("This is the debug tab!")
- def _log(sender: int, app_data: str, user_data: Optional[dict]):
- logging.debug(f"sender: {sender}, \t app_data: {app_data}, \t user_data: {user_data}")
- def _perform_text_query(sender: int, app_data: str, user_data: Optional[dict]):
- logging.debug(f"sender: {sender}, \t app_data: {app_data}, \t user_data: {user_data}")
- def _mode_select(sender: int, mode: str, user_data: Optional[dict]):
- mode = mode.lower().strip()
- visual = mode == "visual"
- # TODO When showing visual we need to fix the custom fields
- dpg.configure_item("__query_editor_visual", show=visual)
- dpg.configure_item("__query_editor_textual", show=not visual)
- def shorten(identifier: str) -> str:
- for key, value in query.reverse_prefix.items():
- identifier = identifier.replace(key, f"{value}:")
- return identifier
- def _query_palette_click(sender: int, mode: str, user_data: Optional[QueryTemplate]):
- mode = dpg.get_value("__editor_selector").lower().strip()
- if mode == "visual" and not user_data.visual_support:
- logging.warning(f"Visual mode for template '{user_data.name}' not implemented yet!")
- dpg.set_value("__status_console", f"Visual mode for template '{user_data.name}' not implemented yet!")
- return
- dpg.set_value("__status_console", f"Using {mode} template '{user_data.name}'.")
- dpg.set_value(f"__prefix_editor_{mode}_input", user_data.prefix)
- dpg.set_value(f"__query_editor_{mode}_input", user_data.query)
- dpg.delete_item("__visual_editor_fields", children_only=True)
- if mode == "visual" and user_data.visual_support:
- for replacement in user_data.replacements:
- dpg.add_input_text(label=f"{replacement.description}",default_value=replacement.suggestion, payload_type="string", width=500, parent="__visual_editor_fields", drop_callback=lambda s, a: dpg.set_value(s, shorten(a)), tag=f"__{replacement.suggestion}", user_data=replacement)
- def _perform_query(sender: int, mode: str, user_data: Optional[dict]):
- # TODO Fix for new query structure
- mode = dpg.get_value("__editor_selector").lower().strip()
- prefix_text = dpg.get_value(f"__prefix_editor_{mode}_input")
- query_text = dpg.get_value(f"__query_editor_{mode}_input")
- endpoint = dpg.get_value("__endpoint_input")
- try:
- if mode=="visual":
- for replacement_field in dpg.get_item_children("__visual_editor_fields", 1):
- value = dpg.get_value(replacement_field)
- placeholder = dpg.get_item_user_data(replacement_field).placeholder
- query_text = query_text.replace(f"{{{{ {placeholder} }}}}", value)
- query_result = query.perform_query(endpoint, prefix_text + "\n" + query_text)
- except URLError as e:
- logging.error(f"Connection to '{endpoint}' failed.")
- dpg.set_value(f"__status_console", f"Connection to '{endpoint}' failed.")
- return
- result_items = query_result["results"]["bindings"]
- if not result_items:
- dpg.delete_item("__result_table", children_only=True) # TODO Clear table https://github.com/hoffstadt/DearPyGui/issues/1350
- logging.debug(f"No results returned.")
- dpg.set_value(f"__status_console", "No results.")
- return
- dpg.delete_item("__result_table", children_only=True)
- dpg.set_value(f"__status_console", "Query successful.")
- columns = result_items[0].keys()
- for column in columns:
- dpg.add_table_column(label=column, width_fixed=True, parent="__result_table")
- # dpg.add_table_column(label="CCC", width_stretch=True, init_width_or_weight=0.0)
- # TODO This is as brittle as my ego, needs a fix asap
- # TODO Fix filter
- # with dpg.filter_set(tag="__result_filter", parent="__query_result_tab"):
- for result in result_items:
- with dpg.table_row(parent=f"__result_table"):
- for key, value in result.items():
- shortened = shorten(f"{value.get('value')}")
- dpg.add_text(shortened, filter_key=shortened)
- with dpg.drag_payload(parent=dpg.last_item(), drag_data=shortened, payload_type="string"):
- dpg.add_text(shortened, filter_key=shortened)
- def _select_directory(sender: int, mode: str, user_data: Optional[dict]):
- with dpg.file_dialog(directory_selector=True, show=True, callback=_process_directory):
- dpg.add_file_extension(".*")
- def _process_directory(sender: int, app_data: str, user_data: Optional[dict]):
- directory = app_data["file_path_name"]
- # files = listdir(directory)
- # dpg.configure_item("files_listbox", items=files)
- # dpg.set_value("file_text", directory)
- def _select_file(sender: int, app_data: str, user_data: Optional[dict]):
- selected_file = app_data
- cwd = dpg.get_value("file_text")
- selected_file = cwd + "/" + selected_file
- dpg.set_value("file_info_n", "file :" + selected_file)
- def interface(example_prefix: str, example_query: str, palette: List[QueryTemplate], endpoint: str, width=1200, height=900):
- dpg.create_context()
- dpg.create_viewport(title="Graph Exploring Tool", width=width, height=height)
- with dpg.window(tag="primary", label="Graph Exploring Tool", menubar=True):
- add_main_menu()
- with dpg.group(horizontal=True, label="Main"):
- create_query_palette(palette)
- with dpg.group(horizontal=False):
- create_query_options(endpoint)
- create_query_editor_visual(example_prefix, example_query, show=True)
- create_query_editor_textual(example_prefix, example_query, show=False)
- create_status_console()
- create_query_results()
- # demo.show_demo()
- dpg.setup_dearpygui()
- dpg.show_viewport()
- dpg.set_primary_window("primary", True)
- dpg.start_dearpygui()
- dpg.destroy_context()
|