improve graph viewer UI and rendering

clear data before loading new file in graph viewer

improve font load

fix

format
This commit is contained in:
ArnoChen
2025-02-04 01:38:42 +08:00
parent 0c8a2bface
commit 3ae2719bfb

View File

@@ -172,6 +172,10 @@ class GraphViewer:
np.sin(np.radians(self.pitch)), np.sin(np.radians(self.pitch)),
) )
) )
if not imgui.is_window_hovered():
return
if io.mouse_wheel != 0: if io.mouse_wheel != 0:
self.move_speed += io.mouse_wheel * 0.05 self.move_speed += io.mouse_wheel * 0.05
self.move_speed = np.max([self.move_speed, 0.01]) self.move_speed = np.max([self.move_speed, 0.01])
@@ -502,6 +506,14 @@ class GraphViewer:
def load_file(self, filepath: str): def load_file(self, filepath: str):
"""Load a GraphML file with error handling""" """Load a GraphML file with error handling"""
try: try:
# Clear existing data
self.id_node_map.clear()
self.nodes.clear()
self.selected_node = None
self.highlighted_node = None
self.setup_buffers()
# Load new graph
self.graph = nx.read_graphml(filepath) self.graph = nx.read_graphml(filepath)
self.calculate_layout() self.calculate_layout()
self.update_buffers() self.update_buffers()
@@ -672,10 +684,6 @@ class GraphViewer:
self.position, self.position + self.front, self.up self.position, self.position + self.front, self.up
) )
io = imgui.get_io()
self.window_width = int(io.display_size.x)
self.window_height = int(io.display_size.y)
aspect_ratio = self.window_width / self.window_height aspect_ratio = self.window_width / self.window_height
self.proj_matrix = glm.perspective( self.proj_matrix = glm.perspective(
glm.radians(60.0), # FOV glm.radians(60.0), # FOV
@@ -839,7 +847,7 @@ class GraphViewer:
def render(self): def render(self):
"""Render the graph""" """Render the graph"""
# Clear screen # Clear screen
self.glctx.clear(*self.background_color) self.glctx.clear(*self.background_color, depth=1)
if not self.graph: if not self.graph:
return return
@@ -886,11 +894,15 @@ class GraphViewer:
# Render id map # Render id map
self.render_id_map(mvp) self.render_id_map(mvp)
def render_labels(self):
# Render labels if enabled # Render labels if enabled
if self.show_labels: if self.show_labels and self.nodes:
# Save current font scale # Save current font scale
original_scale = imgui.get_font_size() original_scale = imgui.get_font_size()
self.update_view_proj_matrix()
mvp = self.proj_matrix * self.view_matrix
for node in self.nodes: for node in self.nodes:
# Project node position to screen space # Project node position to screen space
pos = mvp * glm.vec4( pos = mvp * glm.vec4(
@@ -1013,13 +1025,39 @@ def create_sphere(sectors: int = 32, rings: int = 16) -> Tuple:
return (vbo_vertices, vbo_elements) return (vbo_vertices, vbo_elements)
def draw_text_with_bg(
text: str,
text_pos: imgui.ImVec2Like,
text_size: imgui.ImVec2Like,
bg_color: int,
):
imgui.get_window_draw_list().add_rect_filled(
(text_pos[0] - 5, text_pos[1] - 5),
(text_pos[0] + text_size[0] + 5, text_pos[1] + text_size[1] + 5),
bg_color,
3.0,
)
imgui.set_cursor_pos(text_pos)
imgui.text(text)
def main(): def main():
"""Main application entry point""" """Main application entry point"""
viewer = GraphViewer() viewer = GraphViewer()
show_fps = True
text_bg_color = imgui.IM_COL32(0, 0, 0, 100)
def gui(): def gui():
if not viewer.initialized: if not viewer.initialized:
viewer.setup() viewer.setup()
# # Change the theme
# tweaked_theme = hello_imgui.get_runner_params().imgui_window_params.tweaked_theme
# tweaked_theme.theme = hello_imgui.ImGuiTheme_.darcula_darker
# hello_imgui.apply_tweaked_theme(tweaked_theme)
viewer.window_width = int(imgui.get_window_width())
viewer.window_height = int(imgui.get_window_height())
# Handle keyboard and mouse input # Handle keyboard and mouse input
viewer.handle_keyboard_input() viewer.handle_keyboard_input()
@@ -1089,11 +1127,34 @@ def main():
# Render graph settings window # Render graph settings window
viewer.render_settings() viewer.render_settings()
window_bg_color.w = 0.0 # Render FPS
if show_fps:
imgui.set_window_font_scale(1)
fps_text = f"FPS: {hello_imgui.frame_rate():.1f}"
text_size = imgui.calc_text_size(fps_text)
cursor_pos = (10, viewer.window_height - text_size.y - 10)
draw_text_with_bg(fps_text, cursor_pos, text_size, text_bg_color)
# Render highlighted node ID
if viewer.highlighted_node:
imgui.set_window_font_scale(1)
node_text = f"Node ID: {viewer.highlighted_node.label}"
text_size = imgui.calc_text_size(node_text)
cursor_pos = (
viewer.window_width - text_size.x - 10,
viewer.window_height - text_size.y - 10,
)
draw_text_with_bg(node_text, cursor_pos, text_size, text_bg_color)
window_bg_color.w = 0
style.set_color_(imgui.Col_.window_bg.value, window_bg_color) style.set_color_(imgui.Col_.window_bg.value, window_bg_color)
# Render the graph # Render labels
viewer.render() viewer.render_labels()
def custom_background():
if viewer.initialized:
viewer.render()
runner_params = hello_imgui.RunnerParams() runner_params = hello_imgui.RunnerParams()
runner_params.app_window_params.window_geometry.size = ( runner_params.app_window_params.window_geometry.size = (
@@ -1102,29 +1163,32 @@ def main():
) )
runner_params.app_window_params.window_title = "3D GraphML Viewer" runner_params.app_window_params.window_title = "3D GraphML Viewer"
runner_params.callbacks.show_gui = gui runner_params.callbacks.show_gui = gui
addons = immapp.AddOnsParams() runner_params.callbacks.custom_background = custom_background
addons.with_markdown = True
def load_font(): def load_font():
io = imgui.get_io()
io.fonts.add_font_default()
# Load font for Chinese character support
# You will need to provide it yourself, or use another font. # You will need to provide it yourself, or use another font.
font_filename = CUSTOM_FONT font_filename = CUSTOM_FONT
if not os.path.exists("assets/" + font_filename): use_custom_font = False
return if not hello_imgui.asset_exists(font_filename):
asset_dir = os.path.join(os.path.dirname(__file__), "assets")
if os.path.isfile(os.path.join(asset_dir, font_filename)):
hello_imgui.set_assets_folder(asset_dir)
use_custom_font = True
# Get the full Chinese character range for ImGui io = imgui.get_io()
# This includes all Chinese characters supported by ImGui io.fonts.tex_desired_width = 4096 # Larger texture for better CJK font quality
cn_glyph_ranges_imgui = imgui.get_io().fonts.get_glyph_ranges_chinese_full()
if not use_custom_font:
io.fonts.add_font_default()
return
# Set up font loading parameters with Chinese character support # Set up font loading parameters with Chinese character support
font_loading_params = hello_imgui.FontLoadingParams() font_loading_params = hello_imgui.FontLoadingParams()
font_loading_params.glyph_ranges = hello_imgui.translate_common_glyph_ranges( font_loading_params.glyph_ranges = hello_imgui.translate_common_glyph_ranges(
cn_glyph_ranges_imgui imgui.get_io().fonts.get_glyph_ranges_chinese_full()
) )
custom_font = hello_imgui.load_font(font_filename, 16.0, font_loading_params) custom_font = hello_imgui.load_font(font_filename, 16.0, font_loading_params)
# # Merge with default font # # Merge with default font
@@ -1137,12 +1201,11 @@ def main():
# glyph_ranges_as_int_list=cn_glyph_ranges_imgui, # glyph_ranges_as_int_list=cn_glyph_ranges_imgui,
# ) # )
io.fonts.tex_desired_width = 4096 # Larger texture for better CJK font quality
io.font_default = custom_font io.font_default = custom_font
runner_params.callbacks.load_additional_fonts = load_font runner_params.callbacks.load_additional_fonts = load_font
immapp.run(runner_params, addons) immapp.run(runner_params)
if __name__ == "__main__": if __name__ == "__main__":