def draw(self): imgui.begin(self.title) #imgui.text(str(imgui.is_mouse_down(0))) imgui.label_text(str(imgui.get_mouse_pos()), "position") imgui.label_text(str(imgui.is_mouse_down(0)), "left button") imgui.label_text(str(imgui.is_mouse_down(1)), "right button") imgui.label_text(str(imgui.is_mouse_down(2)), "middle button") imgui.end()
def on_gui(self): # --------------------------------------------------- # Scene Hierarchy # --------------------------------------------------- imgui.begin("Scene Hierarchy") # draw all entities for entity in self.scene: self._draw_entity_node(entity) # clear selection if imgui.is_mouse_down(0) and imgui.is_window_hovered(): self.selected_entity = None # create new entity if imgui.begin_popup_context_window(): if menu_item_clicked("Create Empty Entity"): self.scene.create_entity("Empty Entity") imgui.end_popup() imgui.end() # --------------------------------------------------- # Properties # --------------------------------------------------- imgui.begin("Properties") if self.selected_entity: self._draw_entity_components(self.selected_entity) imgui.end()
def mainloop(self): if self.use_imgui: self.canva.init_shader() self.canva.create_objects() self.rqq.rq_set_size(self.canva.view_size[0], self.canva.view_size[1]) print("Waiting set size reply") self.rqq.process(self.canva) self.parse_reply() self.rqq.rq_load_image() while (not self.canva.should_close() and (not self.please_stop)): self.canva.proc() if self.canva.get_need_resize(): self.canva.set_image_black() self.rqq.rq_set_size(self.canva.view_size[0], self.canva.view_size[1]) self.canva.start_frame() self.canva.swap_buffers() self.canva.poll_events() continue self.rqq.process(self.canva) if self.canva.pipe.poll() == True: self.parse_reply() self.impl.process_inputs() imgui.new_frame() if imgui.begin_main_menu_bar(): if imgui.begin_menu("File", True): clicked_quit, selected_quit = imgui.menu_item( "Quit", 'Ctrl+Q', False, True) if clicked_quit: self.please_stop = True imgui.end_menu() if imgui.begin_menu("Render", True): imgui.menu_item("Save", None, False, True) imgui.end_menu() imgui.end_main_menu_bar() # right button rotation if imgui.is_mouse_down(1): if self.canva.drag_start == None: pos = imgui.get_io().mouse_pos self.canva.drag_start = [pos[0], pos[1]] self.rqq.rq_start_rotation(self.canva.drag_start[0], self.canva.drag_start[1]) else: pos = imgui.get_io().mouse_pos self.pt = [pos[0], pos[1]] self.rqq.rq_rotate(self.pt[0], self.pt[1]) # left button panning elif imgui.is_mouse_down(0): if self.canva.drag_start == None: pos = imgui.get_io().mouse_pos self.canva.drag_start = [pos[0], pos[1]] self.prev_pos = pos else: pos = imgui.get_io().mouse_pos if not self.prev_pos == pos: self.rqq.rq_pan(pos[0] - self.prev_pos[0], pos[1] - self.prev_pos[1]) self.prev_pos = pos # wheel button scrolling else: self.canva.drag_start = None mw = imgui.get_io().mouse_wheel pos = imgui.get_io().mouse_pos if mw != 0: self.rqq.rq_scroll(mw) else: if not self.prev_pos == pos: self.rqq.rq_move(pos[0], pos[1]) self.prev_pos = pos self.canva.start_frame() imgui.render() draw_data = imgui.get_draw_data() self.impl.render(draw_data) self.canva.swap_buffers() self.canva.poll_events() self.canva.rq_stop() self.canva.pipe.close() print("GUI stopped") else: while True: rq = self.canva.pipe.recv() jdata = json.loads(rq) #print("rq:", rq, "jdata:", type(jdata)) if jdata['rq'] == requests[RQ_LOAD_IMAGE]: tex = self.canva.call_load_image() elif jdata['rq'] == requests[RQ_START_ROTATION]: self.canva.call_start_rotation(jdata['args'][0], jdata['args'][1]) elif jdata['rq'] == requests[RQ_ROTATE]: self.canva.call_rotate(jdata['args'][0], jdata['args'][1]) elif jdata['rq'] == requests[RQ_SET_SIZE]: self.canva.call_set_size(jdata['args'][0], jdata['args'][1]) elif jdata['rq'] == requests[RQ_SCROLL]: self.canva.call_scroll(jdata['args']) elif jdata['rq'] == requests[RQ_MOVE]: self.canva.call_move(jdata['args'][0], jdata['args'][1]) elif jdata['rq'] == requests[RQ_PAN]: self.canva.call_pan(jdata['args'][0], jdata['args'][1]) elif jdata['rq'] == requests[RQ_STOP]: break self.canva.img.unlink() self.canva.pipe.close() print("OCCT stopped")
def main(): global width_library global width_shematic global width_context global height_window global previous_key_callback global selected_link global selected_node global iggraph global node_library global debug_is_mouse_dragging global show_debug_window # states ------------------------- scrolling = imgui.Vec2(0, 0) iggraph = IGGraph(node_library) # iggraph.reset() node_hovered_in_scene = -1 parameter_link_start = None selected_parameter = None io_hovered = None io_anchors_width_not_hovered = 10 io_anchors_width_hovered = 15 right_splitter_is_active = False left_splitter_is_active = False image_width = 0 image_height = 0 image_texture = None # states ------------------------- imgui.create_context() window = impl_glfw_init() impl = GlfwRenderer(window) io = imgui.get_io() previous_key_callback = glfw.set_key_callback(window,key_event) init_textures() assign_parameter_colors(node_library) while not glfw.window_should_close(window): glfw.poll_events() impl.process_inputs() imgui.new_frame() if imgui.begin_main_menu_bar(): if imgui.begin_menu("File", True): clicked_new, selected_new = imgui.menu_item( "New", 'Cmd+N', False, True ) if clicked_new: iggraph = IGGraph(node_library) clicked_load, selected_load = imgui.menu_item( "Load", 'Cmd+L', False, True ) if clicked_load: root = tk.Tk() root.withdraw() filename = filedialog.askopenfilename() if filename : f=open(filename) iggraph.from_json(json.load(f)) f.close() clicked_save, selected_save = imgui.menu_item( "Save", 'Cmd+S', False, True ) if clicked_save: iggraph.reset() graph_json = iggraph.to_json() text2save = json.dumps(graph_json, indent=4, sort_keys=True) root = tk.Tk() root.withdraw() f = filedialog.asksaveasfile(mode='w', defaultextension=".json") if f is None: # asksaveasfile return `None` if dialog closed with "cancel". return f.write(text2save) f.close() clicked_quit, selected_quit = imgui.menu_item( "Quit", 'Cmd+Q', False, True ) if clicked_quit: exit(0) imgui.end_menu() if imgui.begin_menu("Debug", True): show_debug_window_clicked, show_debug_window_selected = imgui.menu_item( "Show Debug window", 'Cmd+D', show_debug_window, True ) if show_debug_window_clicked: show_debug_window = not show_debug_window catch_exceptions_clicked, catch_exceptions_selected = imgui.menu_item( "Catch Exceptions", '', iggraph.catch_exceptions, True ) if catch_exceptions_clicked: iggraph.catch_exceptions = not iggraph.catch_exceptions imgui.separator() imgui.menu_item( "Examples", '', False, False ) show_example_mosaic_clicked, show_example_mosaic_selected = imgui.menu_item( "Mosaic", '', False, True ) if show_example_mosaic_clicked: example_mosaic(iggraph) imgui.end_menu() imgui.end_main_menu_bar() height_window = io.display_size.y - 18 # imgui.get_cursor_pos_y() imgui.push_style_var(imgui.STYLE_ITEM_SPACING, imgui.Vec2(0,0)) imgui.set_next_window_size(io.display_size.x, height_window) imgui.set_next_window_position(0, 18) imgui.push_style_var(imgui.STYLE_WINDOW_ROUNDING, 0) imgui.begin("Splitter test", False, imgui.WINDOW_NO_TITLE_BAR | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS) imgui.pop_style_var() imgui.pop_style_var() width_shematic = io.display_size.x - separator_width - width_context - separator_width - width_library # ============================================================================== # Library # ============================================================================== imgui.push_style_var(imgui.STYLE_CHILD_BORDERSIZE, 0) imgui.begin_child("Library", width_library, 0, True) for node_name in node_library.nodes: if imgui.button(node_name, width_library): iggraph.create_node(node_name) imgui.end_child() imgui.pop_style_var() imgui.same_line() imgui.button("left_splitter", separator_width, height_window - 20) left_splitter_is_active = imgui.is_item_active() if (left_splitter_is_active): width_library += io.mouse_delta.x scrolling = imgui.Vec2(scrolling.x - io.mouse_delta.x, scrolling.y) if (imgui.is_item_hovered()): imgui.set_mouse_cursor(imgui.MOUSE_CURSOR_RESIZE_EW) imgui.same_line() # ============================================================================== # Shematic # ============================================================================== imgui.push_style_var(imgui.STYLE_CHILD_BORDERSIZE, 0) imgui.begin_child("shematic", width_shematic, 0, True) if show_inputs_popup(iggraph): imgui.open_popup("Outputs") show_outputs_popup(iggraph) # create our child canvas if iggraph.get_state() == iggraph.STATE_IDLE: imgui.text("status: edit | ") elif iggraph.get_state() == iggraph.STATE_RUNNING: imgui.text("status: run | ") imgui.same_line() if iggraph.get_state() == iggraph.STATE_IDLE: if imgui.button("run"): #TODO if not running iggraph.set_state(iggraph.STATE_RUNNING) iggraph.prepare_to_run() imgui.open_popup("User Input") imgui.same_line() if imgui.button("run one step"): iggraph.set_state(iggraph.STATE_RUNNING) iggraph.prepare_to_run() iggraph.run_one_step() elif iggraph.get_state() == iggraph.STATE_RUNNING: if imgui.button("stop running"): iggraph.reset() iggraph.set_state(iggraph.STATE_IDLE) imgui.same_line() if imgui.button("run one step"): iggraph.set_state(iggraph.STATE_RUNNING) iggraph.run_one_step() # imgui.same_line(imgui.get_window_width() - 100) imgui.push_style_var(imgui.STYLE_FRAME_PADDING, imgui.Vec2(1, 1)) imgui.push_style_var(imgui.STYLE_WINDOW_PADDING, imgui.Vec2(0, 0)) imgui.begin_child("scrolling_region", 0, 0, True, imgui.WINDOW_NO_SCROLLBAR | imgui.WINDOW_NO_MOVE) imgui.pop_style_var() imgui.pop_style_var() imgui.push_item_width(120.0) offset = add(imgui.get_cursor_screen_pos(), scrolling) draw_list = imgui.get_window_draw_list() # Display links draw_list.channels_split(2) draw_list.channels_set_current(0) for link in iggraph.links: draw_link_param_to_param(draw_list, offset, link.output_parameter, link.input_parameter, selected_link == link) # Display nodes parameter_link_end = None one_parameter_hovered = False one_node_moving_active = False for node in iggraph.nodes: imgui.push_id(str(node.id)) node_rect_min = add(offset, node.pos) draw_list.channels_set_current(1) # foreground old_any_active = imgui.is_any_item_active() #display node content first # todo test = add(node_rect_min, NODE_WINDOW_PADDING) imgui.set_cursor_screen_position(add(node_rect_min, NODE_WINDOW_PADDING)) imgui.begin_group() imgui.text("") imgui.text(node.name) imgui.text("") imgui.end_group() # save size node_widgets_active = False # (not old_any_active and imgui.is_any_item_active()) node.size = add( add( imgui.get_item_rect_size(), NODE_WINDOW_PADDING) , NODE_WINDOW_PADDING) node_rect_max = add(node.size, node_rect_min) #display node box draw_list.channels_set_current(0) # background imgui.set_cursor_screen_position(node_rect_min) imgui.invisible_button(str(node.id), node.size.x, node.size.y) if imgui.is_item_hovered(): node_hovered_in_scene = node.id else: node_hovered_in_scene = None node_moving_active = imgui.is_item_active() use_hovered_color = node_hovered_in_scene or selected_node == node draw_list.add_rect_filled(node_rect_min.x, node_rect_min.y, node_rect_max.x, node_rect_max.y, get_node_color(node, iggraph, use_hovered_color), 5) if node_hovered_in_scene and iggraph.is_error(node): imgui.begin_tooltip() imgui.text(iggraph.error_nodes[node]) imgui.end_tooltip() # input parameters for parameter_name in node.inputs: parameter = node.inputs[parameter_name] center = node.get_intput_slot_pos(parameter) center_with_offset = add(offset, center) if io_hovered == parameter: io_anchors_width = io_anchors_width_hovered else: io_anchors_width = io_anchors_width_not_hovered imgui.set_cursor_pos(imgui.Vec2(center.x-io_anchors_width/2, center.y-io_anchors_width/2)) imgui.push_id(str(str(node.id) + "input" + parameter.id)) if (imgui.invisible_button("input", io_anchors_width, io_anchors_width)): selected_parameter = parameter # imgui.is_item_hovered() does not work when dragging is_hovering = ((io.mouse_pos.x-offset.x>center.x-io_anchors_width/2) and (io.mouse_pos.x-offset.x<center.x+io_anchors_width/2) and (io.mouse_pos.y-offset.y>center.y-io_anchors_width/2) and (io.mouse_pos.y-offset.y<center.y+io_anchors_width/2)) if is_hovering: io_hovered = parameter one_parameter_hovered = True imgui.begin_tooltip() imgui.text(parameter_name) imgui.end_tooltip() if is_hovering and imgui.is_mouse_released(0): parameter_link_end = parameter imgui.pop_id() draw_list.add_circle_filled(center_with_offset.x, center_with_offset.y, io_anchors_width/2, get_parameter_color(parameter)) # output parameters for parameter_name in node.outputs: parameter = node.outputs[parameter_name] center = node.get_output_slot_pos(parameter) center_with_offset = add(offset, center) if io_hovered == parameter: io_anchors_width = io_anchors_width_hovered else: io_anchors_width = io_anchors_width_not_hovered imgui.set_cursor_pos(imgui.Vec2(center.x-io_anchors_width/2, center.y-io_anchors_width/2)) imgui.push_id(str(str(node.id) + "output" + parameter.id)) if (imgui.invisible_button("output", io_anchors_width, io_anchors_width)): selected_parameter = parameter is_hovering = ((io.mouse_pos.x-offset.x>center.x-io_anchors_width/2) and (io.mouse_pos.x-offset.x<center.x+io_anchors_width/2) and (io.mouse_pos.y-offset.y>center.y-io_anchors_width/2) and (io.mouse_pos.y-offset.y<center.y+io_anchors_width/2)) if is_hovering: io_hovered = parameter one_parameter_hovered = True imgui.begin_tooltip() imgui.text(parameter_name) imgui.end_tooltip() draw_list.add_circle_filled(center_with_offset.x, center_with_offset.y, io_anchors_width/2, get_parameter_color(parameter)) imgui.pop_id() # cannot use imgui.is_item_active, seems buggy with the scroll if is_hovering and imgui.is_mouse_down(0): parameter_link_start = parameter if node_widgets_active or node_moving_active: selected_node = node one_node_moving_active = True if node_moving_active and imgui.is_mouse_dragging(0) and node.id==selected_node.id: node.pos = add(node.pos, io.mouse_delta) debug_is_mouse_dragging = imgui.is_mouse_dragging(0) imgui.pop_id() draw_list.channels_merge() if not one_parameter_hovered: io_hovered = None # scrolling mouse_is_in_schematic = (io.mouse_pos.x > width_library) and\ (io.mouse_pos.x < (width_library + width_shematic)) and\ (io.mouse_pos.y < height_window) and\ (io.mouse_pos.y > 18) if not one_node_moving_active and\ not parameter_link_start and\ imgui.is_mouse_dragging(0) and\ mouse_is_in_schematic and\ not left_splitter_is_active and\ not right_splitter_is_active and\ imgui.is_window_focused(): scroll_offset = imgui.Vec2(io.mouse_delta.x, io.mouse_delta.y) scrolling = add(scrolling, scroll_offset) # link creation if parameter_link_start and parameter_link_end: iggraph.add_link(parameter_link_start, parameter_link_end) # creating link elif parameter_link_start and imgui.is_mouse_dragging(0): draw_link_param_to_point(draw_list, offset, parameter_link_start, io.mouse_pos.x, io.mouse_pos.y, True) # mouse release if imgui.is_mouse_released(0): parameter_link_start = None imgui.pop_item_width() imgui.end_child() # mouse click on the scene if imgui.is_mouse_clicked(0): mouse_pos = imgui.get_mouse_pos() local_mouse_pos = imgui.Vec2(mouse_pos.x - offset.x, mouse_pos.y - offset.y) selected_link = None for link in iggraph.links: start_node = link.output_parameter.owner start_pos = start_node.get_output_slot_pos(link.output_parameter) end_node = link.input_parameter.owner end_pos = end_node.get_intput_slot_pos(link.input_parameter) distance_mouse_start = math.sqrt(((local_mouse_pos.x-start_pos.x)**2) + ((local_mouse_pos.y-start_pos.y)**2)) distance_mouse_end = math.sqrt(((local_mouse_pos.x-end_pos.x)**2) + ((local_mouse_pos.y-end_pos.y)**2)) distance_start_end = math.sqrt(((start_pos.x-end_pos.x)**2) + ((start_pos.y-end_pos.y)**2)) if ((distance_mouse_start + distance_mouse_end) - distance_start_end) < 0.1: selected_link = link imgui.end_child() imgui.pop_style_var() imgui.same_line() imgui.button("right_splitter", separator_width, height_window - 20) right_splitter_is_active = imgui.is_item_active() if (right_splitter_is_active): width_context -= io.mouse_delta.x if (imgui.is_item_hovered()): imgui.set_mouse_cursor(imgui.MOUSE_CURSOR_RESIZE_EW) # ============================================================================== # Context # ============================================================================== imgui.same_line() imgui.push_style_var(imgui.STYLE_CHILD_BORDERSIZE, 0) imgui.begin_child("child3", width_context, 0, True); if selected_node: if selected_node.handle_dynamic_parameters(): if imgui.button("add parameter"): selected_node.add_dynamic_parameter() if imgui.tree_node("Inputs"): for parameter_name in selected_node.inputs: parameter = selected_node.inputs[parameter_name] if imgui.tree_node(parameter.id): display_parameter(parameter, True) imgui.tree_pop() imgui.tree_pop() if imgui.tree_node("Output"): for parameter_name in selected_node.outputs: parameter = selected_node.outputs[parameter_name] if imgui.tree_node(parameter.id): display_parameter(parameter, False) imgui.tree_pop() imgui.tree_pop() imgui.end_child() imgui.pop_style_var() # imgui.end() # ============================================================================== # Debug Window # ============================================================================== if show_debug_window: debug_window_expanded, show_debug_window = imgui.begin("Debug", True) if parameter_link_start: imgui.text("parameter_link_start: " + parameter_link_start.id) else: imgui.text("parameter_link_start: " + "None") if selected_parameter: imgui.text("selected_parameter: " + selected_parameter.id) else: imgui.text("selected_parameter: " + "None") imgui.text("is_mouse_dragging: " + str(debug_is_mouse_dragging)) imgui.text("mouse: (" + str(io.mouse_pos.x) + ", " + str(io.mouse_pos.y) + ")") imgui.end() gl.glClearColor(1., 1., 1., 1) gl.glClear(gl.GL_COLOR_BUFFER_BIT) imgui.render() impl.render(imgui.get_draw_data()) glfw.swap_buffers(window) impl.shutdown() glfw.terminate()
def global_keyboard_capture() -> bool: # TOOD: Mouse thing is weird behavior but probably fine? if _frames_without_modal < 2: return False return not ig.get_io().want_capture_keyboard or ig.is_mouse_down()
def on_frame(): global cur_state, last_frame_x, last_frame_y, got_hold_last_frame imgui.set_next_window_position(0, 0) io = imgui.get_io() imgui.set_next_window_size(io.display_size.x, io.display_size.y) imgui.begin("Map", flags=imgui.WINDOW_NO_TITLE_BAR | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE | imgui.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS) map_hovered = imgui.is_window_hovered() draw_map() imgui.end() if imgui.begin_main_menu_bar(): if imgui.begin_menu("File", True): clicked_quit, selected_quit = imgui.menu_item( "Quit", 'Cmd+Q', False, True) if clicked_quit: exit(1) clicked_load, selected_load = imgui.menu_item( "Load", "", False, True) if selected_load: load_map() clicked_save, selected_save = imgui.menu_item( "Save", "", False, True) if selected_save: save_map() clicked_export, selected_export = imgui.menu_item( "Export", "", False, True) if selected_export: export_map() clicked_reset, selected_reset = imgui.menu_item( "Reset", "", False, True) if selected_reset: cur_state = State() imgui.end_menu() imgui.end_main_menu_bar() imgui.begin("Tools", False) tools_hovered = imgui.is_window_hovered() draw_mode() left_button_clicked = imgui.is_mouse_clicked(button=0) right_button_clicked = imgui.is_mouse_clicked(button=1) mouse_button_clicked = (left_button_clicked or right_button_clicked) left_button_down = imgui.is_mouse_down(button=0) if mouse_button_clicked: print("clicked left {} right {}".format(left_button_clicked, right_button_clicked)) #else: # print("clearing down_x and down_y") # start_down_x = None # start_down_y = None if got_hold_last_frame: cur_x, cur_y = imgui.get_mouse_pos() moved_cam_x = last_frame_x - cur_x moved_cam_y = last_frame_y - cur_y cur_state.camera_x += moved_cam_x cur_state.camera_y += moved_cam_y if map_hovered and not tools_hovered and mouse_button_clicked: x, y = imgui.get_mouse_pos() print("interpreting click at {},{}".format(x, y)) interpret_click(x + cur_state.camera_x, y + cur_state.camera_y, LEFT_BUTTON if left_button_clicked else RIGHT_BUTTON) elif map_hovered and left_button_down: got_hold_last_frame = True last_frame_x, last_frame_y = imgui.get_mouse_pos() elif not left_button_down: got_hold_last_frame = False imgui.end()
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))
def pan_zoom(name, state, width=None, height=None, content_gen=None, drag_tag=None, down_tag=None, hover_tag=None): """ Create the Pan & Zoom widget, serving as a container for a thing given by `content_gen`. This widget is mostly not used directly. Instead, a special-purpose wrapper can be used, such as `concur.extra_widgets.image.image`, or `concur.extra_widgets.frame.frame`. Args: name: widget name. Also serves as an event identifier state: `PanZoom` object representing state. width: widget width in pixels. If `None`, it fills the available space. height: widget height in pixels. If `None`, it fills the available space. content_gen: Widget generator to display inside the pan-zoom widget. All events are passed through as a return tuple member. This is a function that returns a Concur widget, and takes two keyword arguments: * **tf**: `concur.extra_widgets.pan_zoom.TF` object with transformation information * **event_gen**: This is an event generator which returns the drag, down, and hover events when they occur. It is up to the widget to do the necessary transformations using the `TF` object, and optionally react to the events created by `event_gen`. drag_tag: The event tag for LMB drag events. If `None`, no drag events are fired. down_tag: The event tag for LMB down events. If `None`, no down events are fired. Useful in combination with `drag_tag` to indicate when the dragging started. hover_tag: The event tag for mouse hover when no mouse buttons are down. If `None`, no hover events are fired. Events are fired only when the mouse is moving. Returns: To ease integration with other widgets, this widget returns events in a special format, `(name, state, event)`. `state` or `event` may be `None` in case `state` didn't change, or there is no `event`. If `drag_tag`, or `down_tag` arguments are used, these are fired in the `event` member of the return tuple. """ assert content_gen is not None tf, content = None, None event_queue = queue.Queue() while True: assert not state.keep_aspect or not state.fix_axis, \ "Can't fix axis and keep_aspect at the same time." # Dynamically rescale width and height if they weren't specified if width is None: w = imgui.get_content_region_available()[0] else: w = width if height is None: h = imgui.get_content_region_available()[1] else: h = height frame_w = max(1, w - state.margins[0] + state.margins[2]) frame_h = max(1, h - state.margins[1] + state.margins[3]) w = max(1, w) h = max(1, h) zoom_x = frame_w / (state.right - state.left) zoom_y = frame_h / (state.bottom - state.top) left, right = state.left, state.right top, bottom = state.top, state.bottom if state.keep_aspect: aspect = float(state.keep_aspect) assert state.keep_aspect > 0, "Negative aspect ratio is not supported." if abs(zoom_x) > abs(zoom_y) * aspect: zoom_x = np.sign(zoom_x) * abs(zoom_y) * aspect center_x = (left + right) / 2 left = center_x - frame_w / zoom_x / 2 right = center_x + frame_w / zoom_x / 2 if abs(zoom_y) > abs(zoom_x) / aspect: zoom_y = np.sign(zoom_y) * abs(zoom_x) / aspect center_y = (top + bottom) / 2 top = center_y - frame_h / zoom_y / 2 bottom = center_y + frame_h / zoom_y / 2 origin = imgui.get_cursor_screen_pos() # needs to be before is_window_hovered imgui.begin_child("Pan-zoom", w, h, False, flags=imgui.WINDOW_NO_SCROLLBAR | imgui.WINDOW_NO_SCROLL_WITH_MOUSE) # Interaction st = copy.deepcopy(state) io = imgui.get_io() is_window_hovered = imgui.is_window_hovered() if (imgui.is_mouse_clicked(1) or imgui.is_mouse_clicked(2)) and is_window_hovered: st.is_rmb_dragged = True if not (io.mouse_down[1] or io.mouse_down[2]): st.is_rmb_dragged = False delta = io.mouse_delta # Pan if st.is_rmb_dragged and (delta[0] or delta[1]): if st.fix_axis != 'x': st.left -= delta[0] / zoom_x st.right -= delta[0] / zoom_x if st.fix_axis != 'y': st.top -= delta[1] / zoom_y st.bottom -= delta[1] / zoom_y # Zoom if (imgui.is_window_hovered() or st.is_rmb_dragged) and io.mouse_wheel: factor = 1.3**io.mouse_wheel if st.fix_axis != 'x': mx_rel = (io.mouse_pos[0] - origin[0] - state.margins[0]) / frame_w * 2 - 1 mx = mx_rel * (right - left) / (st.right - st.left) / 2 + 0.5 wi = st.right - st.left st.left = st.left + wi * mx - wi / factor * mx st.right = st.right - wi * (1 - mx) + wi / factor * (1 - mx) if st.fix_axis != 'y': my_rel = (io.mouse_pos[1] - origin[1] - state.margins[1]) / frame_h * 2 - 1 my = my_rel * (bottom - top) / (st.bottom - st.top) / 2 + 0.5 hi = st.bottom - st.top st.top = st.top + hi * my - hi / factor * my st.bottom = st.bottom - hi * (1 - my) + hi / factor * (1 - my) # Get screen-space bounds disregarding the margins left_s = left - st.margins[0] / zoom_x top_s = top - st.margins[1] / zoom_y right_s = right - st.margins[2] / zoom_x bottom_s = bottom - st.margins[3] / zoom_y s2c = np.array([ # Screen to Content [1 / zoom_x, 0, left_s - origin[0] / zoom_x], [0, 1 / zoom_y, top_s - origin[1] / zoom_y] ]) c2s = np.array([ # Content to Screen [zoom_x, 0, origin[0] - left_s * zoom_x], [0, zoom_y, origin[1] - top_s * zoom_y] ]) view_s = [origin[0], origin[1], origin[0] + w, origin[1] + h] view_c = [left_s, top_s, right_s, bottom_s] content_value = None content_returned = False new_tf = TF(c2s, s2c, view_c, view_s) if down_tag \ and not st.is_rmb_dragged \ and imgui.is_mouse_clicked() \ and is_window_hovered: event_queue.put( (down_tag, new_tf.inv_transform(np.array([[*io.mouse_pos] ]))[0])) if hover_tag \ and not st.is_rmb_dragged \ and not any(imgui.is_mouse_down(i) for i in [0,1,2]) \ and is_window_hovered: new_tf.hovered = True if (delta[0] or delta[1]): event_queue.put( (hover_tag, new_tf.inv_transform(np.array([[*io.mouse_pos]]))[0])) if drag_tag \ and not st.is_rmb_dragged: imgui.invisible_button("", w, h) dx, dy = io.mouse_delta if imgui.is_item_active() and (dx or dy): event_queue.put( (drag_tag, np.array([new_tf.s2c[0, 0] * dx, new_tf.s2c[1, 1] * dy]))) imgui.set_item_allow_overlap() try: from concur.core import lift if tf is None or tf != new_tf: tf = new_tf w, h = tf.view_s[2] - tf.view_s[0], tf.view_s[3] - tf.view_s[1] content = content_gen(tf=tf, event_gen=lambda: listen(event_queue)) next(content) except StopIteration as e: content_value = e.value content_returned = True finally: imgui.end_child() changed = st != state if changed or content_returned: return name, (st if changed else None, content_value) yield