class Query: def __init__(self, scale, font, color, text_size, suggestions_text_size, query_delay): self.scale = scale self.font = font self.color = color self.text_size = text_size self.suggestions_text_size = suggestions_text_size self.query_delay = query_delay self.background = None self.prefix = None self.query = None self.suggestions = None self.owner = None self.current_selection = None self.current_list = [] self.completion_task = None self.max_columns = 4 self.max_lines = 3 self.max_elems = self.max_columns * self.max_lines def do_query(self, text): body = None if self.current_selection is not None: if self.current_selection < len(self.current_list): body = self.current_list[self.current_selection][1] else: text = self.query.get() body = self.owner.get_object(text) self.owner.select_object(body) self.close() def close(self): self.background.destroy() self.background = None self.prefix.destroy() self.prefix = None self.query.destroy() self.query = None self.suggestions.destroy() self.suggestions = None self.current_selection = None self.current_list = [] if self.completion_task is not None: taskMgr.remove(self.completion_task) self.completion_task = None def escape(self, event): self.close() def update_suggestions(self): if self.current_selection is not None: page = self.current_selection // self.max_elems else: page = 0 start = page * self.max_elems end = min(start + self.max_elems - 1, len(self.current_list) - 1) suggestions = "" for i in range(start, end + 1): if i != start and ((i - start) % self.max_columns) == 0: suggestions += '\n' if i == self.current_selection: suggestions += "\1md_bold\1%s\2" % self.current_list[i][0] else: suggestions += self.current_list[i][0] suggestions += '\t' self.suggestions.setText(suggestions) def completion(self, event): text = self.query.get() if text != '': self.current_list = self.owner.list_objects(text) else: self.current_list = [] self.current_selection = None if self.completion_task is not None: taskMgr.remove(self.completion_task) self.completion_task = taskMgr.doMethodLater(self.query_delay, self.update_suggestions, 'completion task', extraArgs=[]) def select(self, event): modifiers = event.getModifierButtons() if modifiers.isDown(KeyboardButton.shift()): incr = -1 else: incr = 1 if self.current_selection is not None: new_selection = self.current_selection + incr else: new_selection = 0 if new_selection < 0: new_selection = len(self.current_list) - 1 if new_selection >= len(self.current_list): new_selection = 0 self.current_selection = new_selection self.update_suggestions() def open_query(self, owner): self.owner = owner bg_color = LColor(*self.color) bg_color[3] = 0.2 scale3 = LVector3(self.scale[0], 1.0, self.scale[1]) self.background = DirectFrame( frameColor=bg_color, frameSize=(-1 / self.scale[0], 1.0 / self.scale[0], 0.15 + self.scale[1] * self.text_size, 0.0), parent=base.a2dBottomLeft) self.prefix = OnscreenText( text=_("Target name:"), font=self.font, fg=self.color, align=TextNode.ALeft, parent=base.a2dBottomLeft, scale=tuple(self.scale * self.text_size), pos=(0, .15), ) bounds = self.prefix.getTightBounds() length = bounds[1][0] - bounds[0][ 0] + self.scale[0] * self.text_size / 2 self.query = DirectEntry(text="", text_fg=self.color, scale=tuple(scale3 * self.text_size), command=self.do_query, parent=base.a2dBottomLeft, frameColor=(0, 0, 0, 0), pos=(length, 0, .15), initialText="", numLines=1, width=200, entryFont=self.font, focus=1, suppressKeys=1) self.query.bind("press-escape-", self.escape) self.query.bind("press-tab-", self.select) self.query.accept(self.query.guiItem.getTypeEvent(), self.completion) self.query.accept(self.query.guiItem.getEraseEvent(), self.completion) pos = self.prefix.getPos() bounds = self.query.getBounds() llz = bounds[2] / self.text_size self.suggestions = OnscreenText( text="", font=self.font, fg=self.color, align=TextNode.ALeft, mayChange=True, parent=base.a2dBottomLeft, scale=tuple(self.scale * self.suggestions_text_size), pos=(pos[0], pos[1] + llz), )
def __init__(self, services: Services, mouse1_press_callbacks: List[Callable[[], None]]): self.__services = services self.__services.ev_manager.register_listener(self) self.__base = self.__services.graphics.window self.__text = " t - find the path between the agent and goal\n\n" \ " mouse click - moves agent to mouse location \n\n mouse right click - moves goal to" \ " mouse location\n\n arrow keys, PgUp, PgDn - move agent / goal (Alt down)\n\n x - toggle trace" \ " animation (animations required)\n\n m - toggle map between Sparse and Dense\n\n o - take a " \ "default screenshot of the map\n\n p - take a custom screenshot of the scene\n\n w, a, s, d " \ "- orbit around the map\n\n q - top view of the map\n\n c, v - toggle Simulator Configuration /" \ " View Editor\n\n i - toggle Debug Overlay" self.__algorithms = self.__services.settings.algorithms self.__maps = self.__services.settings.maps self.__map_keys = list(self.__maps.keys()) self.__algorithm_keys = list(self.__algorithms.keys()) self.__animation_keys = list(self.__animations.keys()) self.__window = Window(self.__base, "simulator_config", mouse1_press_callbacks, borderWidth=(0.0, 0.0), frameColor=WINDOW_BG_COLOUR, pos=(-1, 0.5, 0.5), frameSize=(-1.7, 1.3, -5.68, 0.85)) # spacer # DirectFrame(parent=self.__window.frame, borderWidth=(.0, .0), frameColor=WIDGET_BG_COLOUR, frameSize=(-1.4, 1.4, -0.011, 0.011), pos=(-0.2, 0.0, 0.4)) DirectFrame(parent=self.__window.frame, borderWidth=(.0, .0), frameColor=WIDGET_BG_COLOUR, frameSize=(-1.4, 1.4, -0.01, 0.01), pos=(-0.2, 0.0, -2.96)) self.sim_config = DirectLabel(parent=self.__window.frame, text="Simulator Configuration", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, borderWidth=(.0, .0), pos=(-0.53, 0.0, 0.56), scale=(0.2, 3, 0.2)) # Zoom buttons self.btn_zoom_out = DirectButton(text="-", text_fg=WHITE, pressEffect=1, command=self.__window.zoom_out, pos=(0.71, 0., 0.55), parent=self.__window.frame, scale=(0.3, 4.15, 0.35), frameColor=TRANSPARENT) self.btn_zoom_in = DirectButton(text="+", text_fg=WHITE, pressEffect=1, command=self.__window.zoom_in, pos=(0.92, 0., 0.56), parent=self.__window.frame, scale=(0.3, 4.15, 0.35), frameColor=TRANSPARENT) # Quit button self.btn = DirectButton(text='x', text_fg=WHITE, command=self.__window.toggle_visible, pos=(1.12, 0., 0.576), parent=self.__window.frame, scale=(0.3, 2.9, 0.2), pressEffect=1, frameColor=TRANSPARENT) self.user_information = DirectLabel(parent=self.__window.frame, text=self.__text, text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, borderWidth=(.0, .0), pos=(-1.55, 0.0, -3.2), scale=(0.11, 1.1, 0.11)) self.map_label = DirectLabel(parent=self.__window.frame, text="Map:", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, frameColor=WINDOW_BG_COLOUR, borderWidth=(.0, .0), pos=(-1.52, 0.4, 0.), scale=(0.17, 1.09, 0.13)) self.algo_label = DirectLabel(parent=self.__window.frame, text="Algorithm:", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, borderWidth=(.0, .0), pos=(-1.52, 0.4, -0.5), scale=(0.17, 1.09, 0.13)) self.animation_label = DirectLabel(parent=self.__window.frame, text="Animation:", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, borderWidth=(.0, .0), pos=(-1.52, 0.4, -1), scale=(0.17, 1.09, 0.13)) self.agent_label = DirectLabel(parent=self.__window.frame, text="Agent:", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, borderWidth=(.0, .0), pos=(-1.52, 0.4, -1.5), scale=(0.17, 1.09, 0.13)) self.goal_label = DirectLabel(parent=self.__window.frame, text="Goal:", text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, frameColor=WINDOW_BG_COLOUR, text_align=TextNode.ALeft, borderWidth=(.0, .0), pos=(-1.52, 0.4, -2), scale=(0.17, 1.09, 0.13)) # Creating goal and agent's entry fields self.__entries = [] self.__entry_hovered = False mouse1_press_callbacks.append(self.__entry_mouse_click_callback) for i in range(0, 6): e = DirectEntry(parent=self.__window.frame, scale=0.12, pos=(-0.24 + (i % 3) * 0.57, 0.4, -1.5 - 0.5 * (i // 3)), numLines=1, width=3, suppressKeys=True, text_align=TextNode.ACenter, focusInCommand=self.clear_text, focusInExtraArgs=[i]) self.__entries.append(e) e.bind(DGG.EXIT, self.__entry_exit_callback) e.bind(DGG.ENTER, self.__entry_enter_callback) e.bind(DGG.B1PRESS, self.__entry_mouse_click_callback) self.accept("mouse1", self.__entry_mouse_click_callback) self.__agent_disable_overlay = DirectButton(parent=self.__window.frame, frameColor=TRANSPARENT, borderWidth=(0.0, 0.0), frameSize=(-0.6, 1.4, -0.2, 0.2), pos=(-0.24, 0.4, -1.5), suppressMouse=True) self.__agent_disable_overlay.hide() self.__maps_option = DirectOptionMenu( text="options", scale=0.14, parent=self.__window.frame, initialitem=self.__map_keys.index("Labyrinth") if "Labyrinth" in self.__map_keys else 0, items=self.__map_keys, pos=(-0.65, 0.4, 0.), highlightColor=(0.65, 0.65, 0.65, 1), textMayChange=1, command=self.__use_default_map_positions) self.__algorithms_option = DirectOptionMenu( text="options", scale=0.14, parent=self.__window.frame, initialitem=self.__algorithm_keys.index("A*") if "A*" in self.__algorithm_keys else 0, items=self.__algorithm_keys, pos=(-0.46, 0.4, -0.5), highlightColor=(0.65, 0.65, 0.65, 1), textMayChange=1) self.__animations_option = DirectOptionMenu( text="options", scale=0.14, parent=self.__window.frame, initialitem=self.__animation_keys.index("Fast"), items=self.__animation_keys, pos=(-0.45, 0.4, -1), highlightColor=(0.65, 0.65, 0.65, 1), textMayChange=1) self._update_frame = DirectFrame(parent=self.__window.frame, frameColor=WHITE, pos=(-1, 0.4, -2.6), borderWidth=(0.25, 0.15), frameSize=(-0.5, 0.95, -0.54, 0.54), scale=(0.50, 3.1, 0.25)) self._reset_frame = DirectFrame(parent=self.__window.frame, frameColor=WHITE, pos=(0.412, 0.4, -2.6), borderWidth=(0.25, 0.15), frameSize=(-0.5, 0.92, -0.54, 0.54), scale=(0.50, 3.1, 0.25)) self.btn_update = DirectButton( text="Update", text_fg=(0.3, 0.3, 0.3, 1.0), pressEffect=1, command=self.__update_simulator_callback, pos=(-0.9, 0.4, -2.65), parent=self.__window.frame, scale=(0.20, 2.1, 0.15), frameColor=TRANSPARENT) self.btn_reset = DirectButton(text="Reset", text_fg=(0.4, 0.3, 0.3, 1.0), pressEffect=1, command=self.__reset_simulator_callback, pos=(0.51, 0.4, -2.65), parent=self.__window.frame, scale=(0.20, 2.1, 0.15), frameColor=TRANSPARENT) # setup state & use saved state if possible self.__state = None for so in self.__services.state.objects: if isinstance(so, SimulatorConfigState): self.__state = so cmd = self.__maps_option['command'] try: self.__maps_option.set(self.__map_keys.index(so.mp)) self.__algorithms_option.set( self.__algorithm_keys.index(so.algo)) self.__animations_option.set( self.__animation_keys.index(so.ani)) self.__update_position_entries() except: msg = "Failed to load Simulator Config state:\n{}".format( traceback.format_exc()) self.__services.debug.write(msg, DebugLevel.NONE) break finally: self.__maps_option['command'] = cmd return new_state = self.__state is None if new_state: self.__state = SimulatorConfigState(self.__services.state) self.__state.mp = self.__maps_option.get() self.__state.algo = self.__algorithms_option.get() self.__state.ani = self.__animations_option.get() self.__use_default_map_positions() if new_state: self.__services.state.add(self.__state) else: self.__services.state.save()
class PlacerToolSpinner(DirectFrame): def __init__(self, parent=render2d, pos=(0.0, 0.0, 0.0), scale=1.0, value=0, callback=None, increment=0.01): DirectFrame.__init__(self, parent, pos=pos, scale=1.0) self.increment = increment self.value = Decimal(value) self.callback = callback self.display = DirectEntry(parent=self, relief=None, initialText="%.2f" % value, scale=1, text_scale=0.055, text_align=TextNode.ACenter, pos=(0.0, 0.0, 0.0), frameColor=(0.8, 0.8, 0.5, 1), borderWidth=(0.1, 0.1), numLines=1, width=6, frameSize=(-0.1, 0.1, -0.1, 0.1), cursorKeys=1) self.display.bind(DGG.TYPE, self.typeCallback) # This allows the text box to handle mouse events self.display.guiItem.setActive(True) gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui.bam') image = (gui.find('**/tt_t_gui_mat_shuffleArrowUp'), gui.find('**/tt_t_gui_mat_shuffleArrowDown'), gui.find('**/tt_t_gui_mat_shuffleArrowUp'), gui.find('**/tt_t_gui_mat_shuffleArrowDisabled')) self.upArrow = DirectButton(self, relief=None, image=image, image_scale=(0.6, 0.6, 0.6), image1_scale=(0.7, 0.7, 0.7), image2_scale=(0.7, 0.7, 0.7), pos=(0.0, 0.0, 0.08), command=self.__handleUpClicked) self.upArrow.setR(90) self.downArrow = DirectButton(self, relief=None, image=image, image_scale=(0.6, 0.6, 0.6), image1_scale=(0.7, 0.7, 0.7), image2_scale=(0.7, 0.7, 0.7), pos=(0.0, 0.0, -0.05), command=self.__handleDownClicked) self.downArrow.setR(-90) def typeCallback(self, e): if self.display is None: return value = self.display.get() value = re.sub("[^0-9\.-]", "", value) if value == '': value = '000.00' elif value == '-': return if '.' not in value: try: value = int(value) except: return else: try: value = '%.2f' % float(value) except: return self.setValue(value) def setValue(self, value): getcontext().prec = 2 self.value = Decimal(value) self.display.enterText('%.2f' % float(value)) if self.callback: self.callback(self.value) def __handleUpClicked(self): getcontext().prec = 2 self.setValue(float(self.value) + float(self.increment)) def __handleDownClicked(self): getcontext().prec = 2 self.setValue(float(self.value) - float(self.increment))
class ColourChannel(DirectObject): __slider_edit_callback: Callable[['ColourChannel', float], None] __entry_edit_callback: Callable[['ColourChannel', float], None] __frame: DirectFrame __label: DirectLabel __slider: DirectSlider __entry: DirectEntry __disable_frame_overlay: DirectButton __enabled: bool def __init__(self, parent: DirectFrame, text: str, value: float, slider_edit_callback: Callable[['ColourChannel', float], None], entry_edit_callback: Callable[['ColourChannel', float], None], mouse1_press_callbacks: List[Callable[[], None]]): self.__frame = DirectFrame(parent=parent) self.__slider_edit_callback = slider_edit_callback self.__entry_edit_callback = entry_edit_callback self.__label = DirectLabel(parent=self.__frame, text=text, text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, pos=(-0.5, 0.0, 0.0), scale=(0.1, 1.0, 0.1)) self.__slider = DirectSlider(parent=self.__frame, orientation=DGG.HORIZONTAL, borderWidth=(0.0, 0.0), frameColor=WIDGET_BG_COLOUR, frameSize=(-1.0, 1.0, -0.4, 0.4), thumb_frameSize=(-0.075, 0.075, -0.2, 0.2), value=value, pos=(0.05, 0.0, 0.0255), scale=(0.45, 1.0, 0.5)) self.__entry_hovered = False mouse1_press_callbacks.append(self.__entry_mouse_click_callback) self.__entry = DirectEntry(parent=self.__frame, frameColor=WIDGET_BG_COLOUR, text_fg=WHITE, initialText=str(value), scale=0.1, width=3, suppressKeys=True, pos=(0.55, 0.0, -0.01105)) self.__entry.bind(DGG.EXIT, self.__entry_exit_callback) self.__entry.bind(DGG.ENTER, self.__entry_enter_callback) self.__entry.bind(DGG.B1PRESS, self.__entry_mouse_click_callback) self.accept("mouse1", self.__entry_mouse_click_callback) self.__disable_frame_overlay = DirectButton(parent=self.__frame, frameColor=TRANSPARENT, borderWidth=(0.0, 0.0), frameSize=(-0.6, 0.9, -0.2, 0.2), suppressMouse=True) self.__disable_frame_overlay.hide() self.__enabled = True self.__set_callbacks() def __entry_exit_callback(self, *discard) -> None: self.__entry_hovered = False def __entry_enter_callback(self, *discard) -> None: self.__entry_hovered = True def __entry_mouse_click_callback(self, *discard) -> None: if self.__entry_hovered: s = self.__entry.get() try: f = float(s) except: return self.__entry_edit_callback(self, f) else: self.__entry['focus'] = False def __unset_callbacks(self): self.__slider['command'] = None self.__entry['command'] = None def __set_callbacks(self): def slider_callback() -> None: self.__slider_edit_callback(self, self.__slider['value']) def entry_callback(s) -> None: try: f = float(s) except: return self.__entry_edit_callback(self, f) self.__slider['command'] = slider_callback self.__entry['command'] = entry_callback @property def frame(self) -> DirectFrame: return self.__frame def update_slider(self, value: float): if not self.__enabled: return self.__unset_callbacks() self.__slider['value'] = value self.__set_callbacks() def update_entry(self, value: float): if not self.__enabled: return self.__unset_callbacks() self.__entry.enterText(f'{value:.3f}') self.__set_callbacks() def update(self, value: float): self.update_slider(value) self.update_entry(value) @property def value(self) -> float: return self.__slider['value'] @property def enabled(self) -> str: return 'enabled' @enabled.setter def enabled(self, enabled: bool) -> None: if self.__enabled == enabled: return self.__enabled = enabled if enabled: self.__disable_frame_overlay.hide() else: self.__disable_frame_overlay.show() @enabled.getter def enabled(self) -> bool: return self.__enabled