async def show_pleple_window(name, plot_info, multi_resolution_signals, plot_gl_resources, plot_gl_canvas) -> IMGui[None]: with window(name): window_width, window_height = im.get_content_region_available() window_width, window_height = int(window_width), int(window_height) if (window_width, window_height) != (plot_gl_canvas.width_px, plot_gl_canvas.height_px): print("\t### resizing canvas {} -> {}".format( (plot_gl_canvas.width_px, plot_gl_canvas.height_px), (window_width, window_height)), flush=True) del plot_gl_canvas plot_gl_canvas = aplt.init_plot_canvas(window_width, window_height) await emit(___new_canvas_(plot_gl_canvas)) canvas_width = plot_gl_canvas.width_px canvas_height = plot_gl_canvas.height_px should_redraw = True if should_redraw: aplt.draw_plot_to_canvas_multires( plot_gl_canvas, plot_info, multi_resolution_signals, plot_gl_resources, line_color=aplt.WHITE, background_color=aplt.TRANSPARENT_BLACK) im.image(plot_gl_canvas.texture_id, canvas_width, canvas_height, uv0=(0, 1), uv1=(1, 0))
async def signal_plot_window( plot_box_state: PlotBoxState, m_inputs: List[Maybe[Signal]], ui_settings: Dict[str, Any]) -> Eff[[ACTIONS], IMGui[None]]: m_signal = m_inputs[0] assert ((type(m_signal.val) == Signal) if m_signal.is_Just() else True), repr(m_signal) PLOT_WINDOW_FLAGS = 0 if ui_settings[ 'plot_window_movable'] else im.WINDOW_NO_MOVE plot_name = "Plot (id={id})###{id}".format(id=plot_box_state.id_) with window(name=plot_name, flags=PLOT_WINDOW_FLAGS): plot_width = -1. # auto plot_height = im.get_content_region_available().y - 20 await signal_plot(plot_box_state, m_signal, width=plot_width, height=plot_height, ui_settings=ui_settings) if m_signal.is_Nothing(): im.text(" ") elif m_signal.is_Just(): signal = m_signal.val im.text_colored(str(signal), 0.8, 0.8, 0.8) return get_window_rect()
async def signal_source_window( source_state: SourceState, signal_data: PMap_[SignalId, Signal], signal_names: PMap_[SignalId, str]) -> Eff[[ACTIONS], IMGui[None]]: source_name = "Source (id={id})###{id}".format(id=source_state.id_) with window(name=source_name): # signal selector if len(signal_data) > 0: signal_ids = signal_data.keys() visible_signal_names = { sig_id: signal_names[sig_id] for sig_id in signal_ids } # disambiguate signals with duplicate names duplicated_signal_names = { sig_id: sig_name for (sig_id, sig_name) in visible_signal_names.items() if list(visible_signal_names.values()).count(sig_name) > 1 } disambiguated_signal_names = { sig_id: "{name} ({id})".format(name=sig_name, id=sig_id) for (sig_id, sig_name) in duplicated_signal_names.items() } visible_signal_names.update(disambiguated_signal_names) # now `visible_signal_names` is an invertible mapping labels = sorted(visible_signal_names.values()) prev_o_selected_signal_name = None if source_state.is_Empty( ) else visible_signal_names[source_state.signal_id] changed, o_selected_signal_name = str_combo_with_none( "signal", prev_o_selected_signal_name, labels) if changed: if o_selected_signal_name != None: # invert `visible_signal_names` to find the signal id signal_name_to_id = { sig_name: sig_id for (sig_id, sig_name) in visible_signal_names.items() } selected_signal_id = signal_name_to_id[ o_selected_signal_name] await emit( SourceAction.SelectSignal(id_=source_state.id_, signal_id=selected_signal_id) ) else: await emit(SourceAction.SetEmpty(id_=source_state.id_)) else: im.text("No signals available") return util.get_window_rect()
async def filter_box_window(filter_box_state: FilterBoxState, ui_settings) -> Eff[[ACTIONS], IMGui[None]]: name = None if filter_box_state.filter_state.is_Filter(): filter_id = filter_box_state.filter_state.filter_id filter = available_filters[filter_id] name = "{f_name} (id={id})###{id}".format(f_name=filter.name, id=filter_box_state.id_) else: name = "Filter###{id}".format(id=filter_box_state.id_) with window(name=name): # # filter type combo # filter_ids = sorted(available_filters.keys()) # o_filter_id = None if filter_box_state.filter_state.is_NoFilter() else filter_box_state.filter_state.filter_id # changed, o_filter_id = str_combo_with_none("filter", o_filter_id, filter_ids) # if changed: # if o_filter_id != None: # await emit( SetFilter(id_=filter_box_state.id_, filter_id=o_filter_id) ) # else: # await emit( UnsetFilter(id_=filter_box_state.id_) ) # param inputs slider_power = ui_settings['filter_slider_power'] if filter_box_state.filter_state.is_Filter(): filter_id = filter_box_state.filter_state.filter_id filter_params = filter_box_state.filter_state.params filter = available_filters[filter_id] for param_name in sorted(filter.func_sig): changed, new_param_val = im.slider_float( param_name, filter_params[param_name], min_value=0.001, max_value=95., power=slider_power) if changed: await emit( FilterAction.SetParam(id_=filter_box_state.id_, name=param_name, value=new_param_val)) else: im.text("No filter selected") return util.get_window_rect()
def debug_window() -> IMGui[None]: with window(name="debug"): debug_window_draw_start_t_s = glfw.get_time() if debug_dict['crash'] is not None: origin = debug_dict['crash']['origin'] cause = debug_dict['crash']['cause'] exception = debug_dict['crash']['exception'] with window(name="Crash report"): im.text( 'Exception raised during `{}`. State rolled back'.format( origin)) im.text('Caused by:') im.text('\t' + repr(cause)) im.text('\n' + str(exception.__cause__) if exception. __cause__ is not None else '') for line in traceback.format_tb(exception.__traceback__): im.text(line) im.text("{}: {}".format( type(exception).__qualname__, str.join(', ', map(repr, exception.args)))) if im.button('close'): debug_dict['crash'] = None # print some general app state # im.text("actions: "+str(frame_actions)) im.text("mouse: " + str(get_mouse_position())) im.text("click: " + str(im.is_mouse_clicked(button=0))) im.text("down: " + str(im.is_mouse_down(button=0))) im.text( "cl+down: " + str(im.is_mouse_clicked(button=0) and im.is_mouse_down(button=0))) im.text("drag: " + str(im.is_mouse_dragging(button=0))) im.text("drag-d: " + str(im.get_mouse_drag_delta())) im.new_line() # print the avg durations of ranges set with `debug_log_time` show_avg_durations_of_recorded_ranges() # print the values in dicts logged with `debug_log_dict` for name, sequence in debug_dict['sequences'].items(): im.new_line() show_sequence(sequence, name=name) # print the values in dicts logged with `debug_log_dict` for name, dictionary in debug_dict['dicts'].items(): im.new_line() # d = order_dict_by_key(stringify_keys(flatten_dict(dictionary))) show_varied_dict(dictionary, name=name) # print all other values in debug (set with `debug_log`) im.new_line() show_varied_dict(debug_dict['values']) # print how long it took to draw the debug window (hehe) debug_window_draw_end_t_s = glfw.get_time() debug_window_draw_dur_ms = (debug_window_draw_end_t_s - debug_window_draw_start_t_s) * 1000 im.new_line() im.text( "drawing debug took {:4.1f} ms".format(debug_window_draw_dur_ms))
async def draw() -> Eff[[ACTIONS], None]: global state im.show_metrics_window() im.show_test_window() # ------------------------ # t_flags = 0 # t_flags = ( # im.WINDOW_NO_TITLE_BAR # | im.WINDOW_NO_MOVE # | im.WINDOW_NO_RESIZE # | im.WINDOW_NO_COLLAPSE # | im.WINDOW_NO_FOCUS_ON_APPEARING # | im.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS # ) # with window(name="test", flags=t_flags): # im.button("bloop") # pos = util.point_offset(im.get_window_position(), im.Vec2(40, 80)) # im.set_next_window_position(pos.x, pos.y) # with window(name="a window"): # im.text("I'm a window") # top_left = im.get_item_rect_min() # size = im.get_item_rect_size() # bottom_right = util.point_offset(top_left, size) # im.text('TL: '+str(top_left)) # im.text('BR: '+str(bottom_right)) # util.add_rect(im.get_window_draw_list(), util.Rect(top_left, bottom_right), (1.,1.,1.,1.)) debug_log("test_drew", False) with window("test") as (expanded, _): only_draw_if(expanded) debug_log("test_drew", True) # only_draw_if didn't abort, overwrite US = ui['settings'] opts = ['first', 'second', 'third'] default_state = ('no_selection', None) US.setdefault('selectable_state', default_state) US.setdefault('selectable_added', []) if not im.is_window_focused(): US['selectable_state'] = default_state changed, selection_changed, selectable_state = double_click_listbox( US['selectable_state'], opts) if changed: US['selectable_state'] = selectable_state o_ix = selectable_state[1] im.text_colored( "[ {!s:<10} ] {}".format(opts[o_ix] if o_ix is not None else "---", "(!)" if selection_changed else ""), *(0.3, 0.8, 0.5)) im.text("") im.text("{!r:<5} {!r:<5} {}".format(changed, selection_changed, selectable_state)) if selectable_state[0] == 'double_clicked': US['selectable_added'].append(opts[selectable_state[1]]) im.text(str(US['selectable_added'])) c = im.is_mouse_clicked() dc = im.is_mouse_double_clicked() im.text("{!r:<5} {!r:<5} {!r:<5}".format(c, dc, c and dc)) im.text("focused: " + repr(im.is_window_focused())) with window(name="signals"): if im.button("load example"): await emit(FileAction.Load(filename=example_file_path)) if len(state.data.signals) > 0: def right_pad(s: str, limit: int) -> str: n_spaces = max(0, limit - len(s)) return s + (' ' * n_spaces) for sig_id, sig_name in sorted(state.data.signal_names.items(), key=lambda pair: pair[1]): im.text_colored(right_pad(sig_name, 5), 0.2, 0.8, 1) im.same_line() signal = state.data.signals[sig_id] im.text(str(signal)) else: im.text("No signals loaded") # ------------------------- # with window(name="modules"): # modules = sorted( # rlu.all_modules(dir=pathlib.Path.cwd()), # key=lambda mod: (getattr(mod, '__reload_incarnation__', -1), mod.__name__) # ) # for mod in modules: # incarnation_text = str(getattr(mod, '__reload_incarnation__', '-')) # im.text("{mod}[{inc}]".format(mod=mod.__name__, inc=incarnation_text)) with window(name="settings"): ui_settings = ui['settings'] changed, move = im.checkbox("move plots", ui_settings['plot_window_movable']) if changed: ui_settings['plot_window_movable'] = move # im.same_line() changed, option = str_combo( "resample", ui_settings['plot_resample_function'], ['numpy_interp', 'scipy_zoom', 'crude_downsample']) if changed: ui_settings['plot_resample_function'] = option # im.same_line() changed, option = str_combo("plots", ui_settings['plot_draw_function'], ['imgui', 'manual']) if changed: ui_settings['plot_draw_function'] = option changed, val = im.slider_float('filter_slider_power', ui_settings['filter_slider_power'], min_value=1., max_value=5., power=1.0) if changed: ui_settings['filter_slider_power'] = val if im.button("reload"): await emit(AppRunnerAction.Reload()) # im.same_line() # im.button("bla bla bla") # im.same_line() # im.button("ple ple ple") im.text("state | ") im.same_line() if im.button("dump##state"): await emit(AppStateAction.SaveState) im.same_line() if im.button("load##state"): await emit(AppStateAction.LoadState) im.same_line() if im.button("reset##state"): await emit(AppStateAction.ResetState) im.text("history | ") im.same_line() if im.button("dump##history"): await emit(AppStateAction.SaveUserActionHistory) im.same_line() if im.button("load##history"): await emit(AppStateAction.LoadUserActionHistory) im.same_line() if im.button("reset##history"): await emit(AppStateAction.ResetUserActionHistory) im.same_line() if im.button("run##history"): await emit(AppStateAction.RunHistory) # TODO: Window positions can theoretically be accessed # after being drawn using internal APIs. # See: # imgui_internal.h > ImGuiWindow (search "struct IMGUI_API ImGuiWindow") # imgui.cpp > ImGui::GetCurrentContext() # # (sort-of pseudocode example) # # foo_id = None # # with window("foo"): # foo_id = GetCurrentWindow().ID # ... # # ... # # with window("accessing other windows"): # windows = imgui.GetCurrentContext().Windows # foo_win = windows[ windows.find(lambda win: win.ID = foo_id) ] # im.text( "pos:{}, size:{}".format(foo_win.Pos, foo_win.Size) ) # ---------------------------- # await ng.graph_window(state.graph) prev_color_window_background = im.get_style().color( im.COLOR_WINDOW_BACKGROUND) im.set_next_window_position(0, 100) with color({im.COLOR_WINDOW_BACKGROUND: (0., 0., 0., 0.05)}), \ window(name="nodes", flags = ( im.WINDOW_NO_TITLE_BAR # | im.WINDOW_NO_MOVE # | im.WINDOW_NO_RESIZE | im.WINDOW_NO_COLLAPSE | im.WINDOW_NO_FOCUS_ON_APPEARING | im.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS ) ): box_positions = {} inputs = ng.get_inputs(state.graph, state.data.box_outputs) with color({im.COLOR_WINDOW_BACKGROUND: prev_color_window_background}): # source boxes for (id_, box_state) in state.source_boxes.items(): pos = await signal_source_window(box_state, state.data.signals, state.data.signal_names) box_positions[id_] = pos # filter boxes for (id_, box_state) in state.filter_boxes.items(): pos = await filter_box_window(box_state, ui_settings=ui_settings) box_positions[id_] = pos # signal plot 1 for (id_, box_state) in state.plots.items(): pos = await signal_plot_window(box_state, inputs[id_], ui_settings=ui_settings) box_positions[id_] = pos # connections between boxes link_selection = state.link_selection prev_cursor_screen_pos = im.get_cursor_screen_position() # get slot coords and draw slots with color({im.COLOR_CHECK_MARK: (0, 0, 0, 100 / 255)}): draw_list = im.get_window_draw_list() SPACING = 20. slot_center_positions = {} for (id_, position) in box_positions.items(): node = state.graph.nodes[id_] left_x = position.top_left.x right_x = position.bottom_right.x top_y = position.top_left.y for slot_ix in range(node.n_inputs): pos = im.Vec2(left_x - 20 - 3, top_y + 30 + slot_ix * SPACING) im.set_cursor_screen_position(pos) slot = ng.InputSlotId(id_, slot_ix) was_selected = (slot == link_selection.dst_slot) changed, selected = im.checkbox( "##in{}{}".format(id_, slot_ix), was_selected) if changed: await emit(LinkSelectionAction.ClickInput(slot)) center_pos = util.rect_center( util.get_item_rect()) # bounding rect of prev widget slot_center_positions[('in', id_, slot_ix)] = center_pos for slot_ix in range(node.n_outputs): pos = im.Vec2(right_x + 3, top_y + 30 + slot_ix * SPACING) im.set_cursor_screen_position(pos) slot = ng.OutputSlotId(id_, slot_ix) was_selected = (slot == link_selection.src_slot) changed, selected = im.checkbox( "##out{}{}".format(id_, slot_ix), was_selected) if changed: await emit(LinkSelectionAction.ClickOutput(slot)) center_pos = util.rect_center( util.get_item_rect()) # bounding rect of prev widget slot_center_positions[('out', id_, slot_ix)] = center_pos # end drawing slots # draw links for (src, dst) in state.graph.links: src_pos = slot_center_positions[('out', src.node_id, src.ix)] dst_pos = slot_center_positions[('in', dst.node_id, dst.ix)] draw_list.add_line(*src_pos, *dst_pos, col=im.get_color_u32_rgba(0.5, 0.5, 0.5, 1.), thickness=2.) im.set_cursor_screen_position(prev_cursor_screen_pos) # end nodes window im.show_style_editor()
async def graph_window(graph: Graph) -> Eff[[ACTIONS], IMGui[None]]: with window(name="Graph"): # maybe use im.selectable? # sources labels = sorted( [str((slot.node_id, slot.ix)) for slot in output_slots(graph)]) prev_o_source = SELECTED_SLOTS_CONNECT['source'] prev_o_source_txt = str( (prev_o_source.node_id, prev_o_source.ix)) if prev_o_source is not None else None changed, o_source_txt = str_combo_with_none("src##connect", prev_o_source_txt, labels) if changed: o_source = OutputSlotId( *eval(o_source_txt)) if o_source_txt is not None else None SELECTED_SLOTS_CONNECT['source'] = o_source # dests labels = sorted( [str((slot.node_id, slot.ix)) for slot in free_input_slots(graph)]) prev_o_dest = SELECTED_SLOTS_CONNECT['dest'] prev_o_dest_txt = str( (prev_o_dest.node_id, prev_o_dest.ix)) if prev_o_dest is not None else None changed, o_dest_txt = str_combo_with_none("dst##connect", prev_o_dest_txt, labels) if changed: o_dest = InputSlotId( *eval(o_dest_txt)) if o_dest_txt is not None else None SELECTED_SLOTS_CONNECT['dest'] = o_dest # buttons conn = im.button("Connect") if (conn and SELECTED_SLOTS_CONNECT['source'] is not None and SELECTED_SLOTS_CONNECT['dest'] is not None): await emit( GraphAction.Connect(SELECTED_SLOTS_CONNECT['source'], SELECTED_SLOTS_CONNECT['dest'])) SELECTED_SLOTS_CONNECT['source'] = None SELECTED_SLOTS_CONNECT['dest'] = None # ----------------------------- # sources labels = sorted([ str((slot.node_id, slot.ix)) for slot in used_output_slots(graph) ]) prev_o_source = SELECTED_SLOTS_DISCONNECT['source'] prev_o_source_txt = str( (prev_o_source.node_id, prev_o_source.ix)) if prev_o_source is not None else None changed, o_source_txt = str_combo_with_none("src##disconnect", prev_o_source_txt, labels) if changed: o_source = OutputSlotId( *eval(o_source_txt)) if o_source_txt is not None else None SELECTED_SLOTS_DISCONNECT['source'] = o_source # dests labels = sorted([ str((slot.node_id, slot.ix)) for slot in filled_input_slots(graph) ]) prev_o_dest = SELECTED_SLOTS_DISCONNECT['dest'] prev_o_dest_txt = str( (prev_o_dest.node_id, prev_o_dest.ix)) if prev_o_dest is not None else None changed, o_dest_txt = str_combo_with_none("dst##disconnect", prev_o_dest_txt, labels) if changed: o_dest = InputSlotId( *eval(o_dest_txt)) if o_dest_txt is not None else None SELECTED_SLOTS_DISCONNECT['dest'] = o_dest conn = im.button("Disconnect") if (conn and SELECTED_SLOTS_DISCONNECT['source'] is not None and SELECTED_SLOTS_DISCONNECT['dest'] is not None): await emit( GraphAction.Disconnect(SELECTED_SLOTS_DISCONNECT['source'], SELECTED_SLOTS_DISCONNECT['dest'])) SELECTED_SLOTS_DISCONNECT['source'] = None SELECTED_SLOTS_DISCONNECT['dest'] = None # ---------------------------------- im.text(graph_repr(graph))