def bindHelpEvent(helpId: str, window: wx.Window): window.Unbind(wx.EVT_HELP) window.Bind( wx.EVT_HELP, lambda evt: _onEvtHelp(helpId, evt), ) log.debug(f"Did context help binding for {window.__class__.__qualname__}")
def ScrollChildIntoView(self, child: wx.Window) -> None: """ Overrides child.GetRect with `GetChildRectRelativeToSelf` before calling `super().ScrollChildIntoView`. `super().ScrollChildIntoView` incorrectly uses child.GetRect to navigate scrolling, which is relative to the parent, where it should instead be relative to this ScrolledPanel. """ oldChildGetRectFunction = child.GetRect child.GetRect = lambda: self.GetChildRectRelativeToSelf(child) try: super().ScrollChildIntoView(child) finally: # ensure child.GetRect is reset properly even if super().ScrollChildIntoView throws an exception child.GetRect = oldChildGetRectFunction
def _set_dark_mode(window: wx.Window, enabled: bool) -> None: if enabled: bg = wx.Colour(40, 40, 40) fg = wx.Colour(255, 255, 255) else: bg = wx.NullColour fg = wx.NullColour window.SetBackgroundColour(bg) window.SetForegroundColour(fg) for child in window.Children: if isinstance(child, wx.stc.StyledTextCtrl): set_stc_dark_mode(child, enabled) else: _set_dark_mode(child, enabled) window.Refresh()
def __init__(self, parent: Window, displayText: str, valueChangedCallback: Callable, minValue: int = DEFAULT_MIN_VALUE, maxValue: int = DEFAULT_MAX_VALUE): """ Args: parent The parent window displayText: The text to display as the static box title valueChangedCallback: The method to call when the value changes; The method should expect the first parameter to be an object of type SpinnerValues minValue: The minimum value for the width/height maxValue: The maximum value for the width/height """ self.logger: Logger = getLogger(__name__) self.__callback: Callable = valueChangedCallback box: StaticBox = StaticBox(parent, ID_ANY, displayText) super().__init__(box, HORIZONTAL) self._wxValue0SpinnerId: int = wxNewIdRef() self._wxValue1SpinnerId: int = wxNewIdRef() self._scValue0: SpinCtrl = SpinCtrl(parent, self._wxValue0SpinnerId, "", (SPINNER_WIDTH, SPINNER_HEIGHT)) self._scValue1: SpinCtrl = SpinCtrl(parent, self._wxValue1SpinnerId, "", (SPINNER_WIDTH, SPINNER_HEIGHT)) self.Add(self._scValue0, 0, ALL, DualSpinnerContainer.HORIZONTAL_GAP) self.Add(self._scValue1, 0, ALL, DualSpinnerContainer.HORIZONTAL_GAP) self._scValue0.SetRange(minValue, maxValue) self._scValue1.SetRange(minValue, maxValue) parent.Bind(EVT_SPINCTRL, self.__onSpinnerValueChanged, id=self._wxValue0SpinnerId) parent.Bind(EVT_SPINCTRL, self.__onSpinnerValueChanged, id=self._wxValue1SpinnerId) self._spinnerValues: SpinnerValues = SpinnerValues(minValue, minValue)
def __init__(self, parent: Window, labelText: str, valueChangedCallback: Callable): """ Args: parent: The parent window labelText: How to label the text input valueChangedCallback: The method to call when the value changes; The method should expect the first parameter to be a string argument that is the new value """ super().__init__(HORIZONTAL) self._textId: int = wxNewIdRef() self._callback: Callable = valueChangedCallback textLabel: StaticText = StaticText(parent, ID_ANY, labelText) textControl: TextCtrl = TextCtrl(parent, self._textId) self.Add(textLabel, WX_SIZER_CHANGEABLE, ALL | ALIGN_CENTER_VERTICAL, TextContainer.HORIZONTAL_GAP) self.Add(textControl, WX_SIZER_CHANGEABLE, ALL, TextContainer.HORIZONTAL_GAP) self._textControl: TextCtrl = textControl self._textValue: str = '' parent.Bind(EVT_TEXT, self._onTextValueChanged, id=self._textId)
def create_menu_item( parent: wx.Window, menu: wx.Menu, text: str, tooltip: str = '', icon_image: Optional[Image.Image] = None, id: int = wx.ID_ANY, callback: Optional[Callable] = None, ): """Create menu item The tooltip is actually not visible, menu items with tooltips are not supported by WxWidgets. """ menu_item = wx.MenuItem(menu, id, text, helpString=tooltip) if icon_image: bitmap = image_to_bitmap(icon_image) menu_item.SetBitmap(bitmap) menu.Append(menu_item) if callback: parent.Bind(wx.EVT_MENU, partial(on_menu_item, callback=callback), id=id) else: menu_item.Enable(False) return menu_item
def __init__(self, parent: wx.Window): display_attributes = wx.glcanvas.GLAttributes() display_attributes.PlatformDefaults().MinRGBA( 8, 8, 8, 8).DoubleBuffer().Depth(24).EndList() super().__init__( parent, display_attributes, size=parent.GetClientSize(), style=wx.WANTS_CHARS, ) if sys.platform == "linux": # setup the OpenGL context. This apparently fixes #84 self._context = glcanvas.GLContext(self) else: context_attributes = wx.glcanvas.GLContextAttrs() context_attributes.CoreProfile().OGLVersion( 3, 3).Robust().ResetIsolation().EndList() self._context = glcanvas.GLContext( self, ctxAttrs=context_attributes) # setup the OpenGL context self.SetCurrent(self._context) self._context_identifier = str( uuid.uuid4()) # create a UUID for the context. Used to get shaders self._gl_texture_atlas = glGenTextures( 1) # Create the atlas texture location self._setup_opengl() # set some OpenGL states
def get_parameters_helper(self): """ Helper method to launch get_parameters_from_script on a thread so that it isn't run on the GUI thread, since it may be slow (when initializing pyimagej). """ # Reset previously parsed parameters self.clear_script_parameters() global stop_progress_thread stop_progress_thread = False progress_gauge = Gauge(Window.FindFocus(), -1, size=(100, -1)) progress_gauge.Show(True) parse_param_thread = Thread(target=self.get_parameters_from_script, name="Parse Parameters Thread", daemon=True) parse_param_thread.start() while True: # Wait for get_parameters_from_script to finish progress_gauge.Pulse() time.sleep(0.025) if stop_progress_thread: progress_gauge.Show(False) break if not self.initialization_failed: self.parsed_params = True
def _enable_operation_ui(self, ui: wx.Window): self._hide_all() self._shown_ui = ui self.Show() ui.Show() self._canvas().select_mode = 1 self.Fit()
def __init__(self, window: wx.Window, size: int) -> None: super().__init__() self._face = freetype.Face(_FONT_PATH) self._face.set_char_size(size * 64) self._glyphs = {} # type: typing.Dict[str, _Glyph] window.Bind(wx.EVT_WINDOW_DESTROY, self._OnWindowDestroy)
def get_children(window: wx.Window): my_children = window.GetChildren() if my_children is not None: their_children = [] for my_child in my_children: their_children += get_children(my_child) return list(my_children) + their_children else: return []
def __init__(self, canvas: wx.Window, text: str, header: str): self.text = text self.canvas = canvas self.font = canvas.GetFont() if header == "h1": self.font.SetPointSize(16) elif header == "h2": self.font.SetPointSize(12) else: raise KeyError("Unknown header type. Either h1 or h2") self.rect = wx.Rect(0, 0, 0, 0)
def GetChildRectRelativeToSelf(self, child: wx.Window) -> wx.Rect: """ window.GetRect returns the size of a window, and its position relative to its parent. When calculating ScrollChildIntoView, the position relative to its parent is not relevant unless the parent is the ScrolledPanel itself. Instead, calculate the position relative to scrolledPanel """ childRectRelativeToScreen = child.GetScreenRect() scrolledPanelScreenPosition = self.GetScreenPosition() return wx.Rect( childRectRelativeToScreen.x - scrolledPanelScreenPosition.x, childRectRelativeToScreen.y - scrolledPanelScreenPosition.y, childRectRelativeToScreen.width, childRectRelativeToScreen.height)
def populate_output_fields(parent: wx.Window, fields: dict) -> None: '''Find text fields by name and set their text to str(value). Fields must be derived from wx.TextEntry Arguments: parent: The parent window fields: { name: value }''' for name, value in fields.items(): window = parent.FindWindowByName(name) if window and isinstance(window, wx.TextEntry): window.SetValue(str(value)) else: raise Exception(f'Could not populate output field: {name}, {value}')
def __init__(self, parent: wx.Window, open_world_callback: Callable[[str], None]): super().__init__( parent, title="World Select", pos=wx.Point(50, 50), size=wx.Size(*[int(s * 0.95) for s in parent.GetSize()]), style=wx.CAPTION | wx.CLOSE_BOX | wx.MAXIMIZE_BOX # | wx.MAXIMIZE | wx.SYSTEM_MENU | wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.RESIZE_BORDER, ) self.Bind(wx.EVT_CLOSE, self._hide_event) self._open_world_callback = open_world_callback self.world_select = WorldSelectAndRecentUI(self, self._run_callback)
def __init__(self, parent: wx.Window): attribs = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) super().__init__(parent, -1, size=parent.GetClientSize(), attribList=attribs) self._context = glcanvas.GLContext(self) # setup the OpenGL context self.SetCurrent(self._context) self.context_identifier = str( uuid.uuid4()) # create a UUID for the context. Used to get shaders self._gl_texture_atlas = glGenTextures( 1) # Create the atlas texture location self._setup_opengl() # set some OpenGL states self._transformation_matrix: Optional[numpy.ndarray] = None
def __init__(self, parent: wx.Window, installed: List[Plugin], plugin_dir: str): super().__init__(parent) self.plugin_dir = plugin_dir html = '<h2>{title}</h2>'.format(title="Browse Plugins") self.SetPage(html) installed_names = dict() for p in installed: installed_names.update({p.metadata.name: p}) try: # for just displaying info r = requests.get( 'https://raw.githubusercontent.com/samuels342/PyRKViewer-Plugins/main/metadata.json' ) metadata = r.json() for m in metadata: item = ''' <div> <h3>{name}</h3> <br>{author}|v{version} <p>{long_desc}</p> '''.format(name=metadata[m]['name'], author=metadata[m]['author'], version=metadata[m]['version'], long_desc=metadata[m]['long_desc']) if metadata[m]['name'] in installed_names: prev_install = installed_names[metadata[m]['name']] if not prev_install.metadata.version == metadata[m][ 'version']: item += ''' <div align="right">v{v} Installed<br> <a href={file}>Update</a></div> '''.format(v=prev_install.metadata.version, file=m) else: item += ''' <div align="right">v{v} Installed</div> '''.format(v=prev_install.metadata.version) else: item += '<div align="right"><a href={file}>Install</a></div>'.format( file=m) item += '</div><hr>' self.AppendToPage(item) except: # TODO define error, checking if there is internet connection html = '<h2>Browse Plugins</h2><p>Could not connect to server, please try again later!</p>' self.SetPage(html) self.SetBackgroundColour(parent.GetBackgroundColour())
def parse_input_fields(parent: wx.Window, fields: list, convert: type = None) -> dict: '''Find text input fields by name and collect their values. Fields must be derived from wx.TextEntry Arguments: parent: The parent window fields: [{ name: window name}] type: convert values to the provided type Return: result: { name: value (str) }''' result = {} for field in fields: window = parent.FindWindowByName(field['name']) if window and isinstance(window, wx.TextEntry): if convert: result[field['name']] = convert(window.GetValue()) else: result[field['name']] = window.GetValue() else: raise Exception(f'Could not parse input field: {field["name"]}') return result
def __init__(self, parent: wx.Window, plugin: Plugin): super().__init__(parent) html = ''' <h3>{name}</h3> <div>{author}|v{version}</div> <hr/> <div> {description} </div> '''.format( name=plugin.metadata.name, version=plugin.metadata.version, author=plugin.metadata.author, description=plugin.metadata.long_desc, ) self.SetPage(html) # inherit parent background color for better look self.SetBackgroundColour(parent.GetBackgroundColour())
def __init__(self, parent: wx.Window): display_attributes = wx.glcanvas.GLAttributes() display_attributes.PlatformDefaults().MinRGBA( 8, 8, 8, 8).DoubleBuffer().Depth(24).EndList() super().__init__(parent, display_attributes, size=parent.GetClientSize()) self._projection = [70.0, 4 / 3, 0.1, 10000.0] context_attributes = wx.glcanvas.GLContextAttrs() context_attributes.CoreProfile().OGLVersion( 3, 3).Robust().ResetIsolation().EndList() self._context = glcanvas.GLContext( self, ctxAttrs=context_attributes) # setup the OpenGL context self.SetCurrent(self._context) self._context_identifier = str( uuid.uuid4()) # create a UUID for the context. Used to get shaders self._gl_texture_atlas = glGenTextures( 1) # Create the atlas texture location self._setup_opengl() # set some OpenGL states self._transformation_matrix: Optional[numpy.ndarray] = None
def __init__(self, master: wx.Window): super().__init__(parent=master, size=master.GetSize()) Browser.instance = self self.loadPackages()
def autoThaw(control: wx.Window): control.Freeze() yield control.Thaw()
def listenForEnableChanged(self, _ctrl: wx.Window): self.Bind(wx.EVT_WINDOW_DESTROY, self._onDestroy) _ctrl.Bind(LabeledControlHelper.EVT_ENABLE_CHANGED, self._onEnableChanged) self.isListening = True
def patch(dom: wx.Window, vdom): parent = dom.GetParent() try: # if parent: # parent.Freeze() if not isclass(vdom['type']): # because stateless functions are just opaque wrappers # they have no relevant diffing logic -- there is no # associated top-level WX element produced from a SFC, only # their inner contents matter. As such, we evaluate it and # push the result back into `patch` return patch(dom, vdom['type'](vdom['props'])) if isclass(vdom['type']) and issubclass(vdom['type'], Component): return Component.patch_component(dom, vdom) elif not isinstance(dom, vdom['type']): for child in dom.GetChildren(): dom.RemoveChild(child) child.Destroy() dom.Destroy() newdom = render(vdom, parent) elif isinstance(dom, vdom['type']): update(vdom, dom) pool = { f'__index_{index}': child for index, child in enumerate(dom.GetChildren()) } for index, child in enumerate(vdom['props'].get('children', [])): key = f'__index_{index}' if key in pool: patch(pool[key], child) del pool[key] else: # TODO: this IS the addition case, right? # why would I need this removeChild line..? if key in pool: # if we're adding something new to the # tree, it won't be present in the pool parent.RemoveChild(pool[key]) # TODO: need to understand this case more # if we're not updating, we're adding # in which case.. why doesn't this fall to the # `dom` instance..? inst = render(child, dom) if dom.GetSizer(): dom.GetSizer().Add(inst, child['props'].get('proportion', 0), child['props'].get('flag', 0), child['props'].get('border', 0)) # any keys which haven't been removed in the # above loop represent wx.Objects which are no longer # part of the virtualdom and should thus be removed. for key, orphan in pool.items(): dom.RemoveChild(orphan) orphan.Destroy() newdom = dom else: raise Exception("unexpected case!") p = parent while p: p.Layout() p = p.GetParent() return newdom finally: # TODO: we sometimes call parent.Thaw() when # parent isn't frozen. I think this has something # to do with the child removal case. Not sure tho # if parent and parent.IsFrozen(): # parent.Thaw() pass
def patch(dom: wx.Window, vdom): parent = dom.GetParent() try: # if parent: # parent.Freeze() if not isclass(vdom['type']): # because stateless functions are just opaque wrappers # they have no relevant diffing logic -- there is no # associated top-level WX element produced from a SFC, only # their inner contents matter. As such, we evaluate it and # push the result back into `patch` return patch(dom, vdom['type'](vdom['props'])) if isclass(vdom['type']) and issubclass(vdom['type'], Component): return Component.patch_component(dom, vdom) elif not isinstance(dom, vdom['type']): for child in dom.GetChildren(): dom.RemoveChild(child) child.Destroy() dom.Destroy() newdom = render(vdom, parent) elif isinstance(dom, vdom['type']) and getattr(dom, 'self_managed', False): # self-managed components manage their children by hand rather than # automatically via the rewx dom. As such, we don't perform any child # diffing or reconciliation operations for them. The virtualdom will NOT # match the actual dom for these widgets. # # Background: These components are legacy/vanilla wx components the user created # which have been introduced into rewx land through custom mount/update handlers. # These are commonly used while porting over existing code or for wx components # which are sufficiently cranky about their child management. update(vdom, dom) newdom = dom elif isinstance(dom, vdom['type']): update(vdom, dom) pool = {f'__index_{index}': child for index, child in enumerate(dom.GetChildren())} for index, child in enumerate(vdom['props'].get('children', [])): key = f'__index_{index}' if key in pool: patch(pool[key], child) del pool[key] else: # TODO: this IS the addition case, right? # why would I need this removeChild line..? if key in pool: # if we're adding something new to the # tree, it won't be present in the pool parent.RemoveChild(pool[key]) # TODO: need to understand this case more # if we're not updating, we're adding # in which case.. why doesn't this fall to the # `dom` instance..? inst = render(child, dom) if dom.GetSizer(): dom.GetSizer().Add( inst, child['props'].get('proportion', 0), child['props'].get('flag', 0), child['props'].get('border', 0) ) # any keys which haven't been removed in the # above loop represent wx.Objects which are no longer # part of the virtualdom and should thus be removed. for key, orphan in pool.items(): # Debugging InspectionFrame gets lumped in with the # top-level hierarchy. We want to leave this alone as # it's there for debugging and not part of the actual # declared component tree if not isinstance(orphan, wx.lib.inspection.InspectionFrame): dom.RemoveChild(orphan) orphan.Destroy() newdom = dom else: raise Exception("unexpected case!") p = parent while p: p.Layout() p = p.GetParent() return newdom finally: # TODO: we sometimes call parent.Thaw() when # parent isn't frozen. I think this has something # to do with the child removal case. Not sure tho # if parent and parent.IsFrozen(): # parent.Thaw() pass