wolfhece.PyVertex._gui ====================== .. py:module:: wolfhece.PyVertex._gui .. autoapi-nested-parse:: Author: HECE - University of Liege, Pierre Archambeau Date: 2024 Copyright (c) 2024 University of Liege. All rights reserved. This script and its content are protected by copyright law. Unauthorized copying or distribution of this file, via any medium, is strictly prohibited. Module Contents --------------- .. py:class:: Cloud_Styles(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.Cloud_Styles :parts: 1 :private-bases: Rendering styles for a cloud of vertices in OpenGL. Each member maps to an integer used by :class:`cloudproperties` ``style`` attribute and the :meth:`cloud_vertices.plot` method. .. py:attribute:: POINT :value: 0 .. py:attribute:: CIRCLE :value: 1 .. py:attribute:: CROSS :value: 2 .. py:attribute:: QUAD :value: 3 .. py:attribute:: SYMBOL :value: 4 .. py:class:: Cloud_OGLRenderer(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.Cloud_OGLRenderer :parts: 1 :private-bases: Rendering backend for cloud vertices. .. py:attribute:: LIST :value: 0 .. py:attribute:: SHADER :value: 1 .. py:class:: Cloud_AnimationMode(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.Cloud_AnimationMode :parts: 1 :private-bases: Animation modes for cloud rendering. .. py:attribute:: NONE :value: 0 .. py:attribute:: BLINK :value: 1 .. py:attribute:: FADE :value: 2 .. py:attribute:: GROW :value: 3 .. py:attribute:: SEASONS :value: 4 .. py:attribute:: PULSE :value: 5 .. py:function:: circle(x: float, y: float, r: float, numseg: int = 20) Draw a filled circle using an OpenGL polygon. :param x: X coordinate of the center. :param y: Y coordinate of the center. :param r: radius of the circle (in map units). :param numseg: number of segments used to approximate the circle. .. py:function:: circle_outline(x: float, y: float, r: float, numseg: int = 20) Draw a hollow circle using an OpenGL line loop. .. py:function:: cross(x: float, y: float, r: float) Draw a cross (two perpendicular lines) using OpenGL. :param x: X coordinate of the center. :param y: Y coordinate of the center. :param r: half-length of each arm (in map units). .. py:function:: quad(x: float, y: float, r: float) Draw a filled square (quad) using OpenGL. :param x: X coordinate of the center. :param y: Y coordinate of the center. :param r: half-side length of the square (in map units). .. py:class:: cloudproperties(lines=[], parent: cloud_vertices = None) Bases: :py:obj:`wolfhece.PyVertex._model.cloudproperties` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.cloudproperties :parts: 1 :private-bases: Cloud properties with wx GUI support. Extends :class:`cloudproperties_model` with a ``Wolf_Param`` dialog for interactive editing of drawing and legend parameters. :ivar myprops: ``Wolf_Param`` dialog instance, ``None`` until :meth:`defaultprop` or :meth:`show` is called. .. py:attribute:: myprops :type: wolfhece.PyParams.Wolf_Param :value: None .. py:attribute:: SYMBOLS_DIR .. py:attribute:: CUSTOM_SYMBOL_CHOICE :value: '' .. py:attribute:: PACKAGE_SYMBOL_PROP_NAME :value: 'DrawPackage symbol' .. py:attribute:: SYMBOL_SOURCE_PROP_NAME :value: 'DrawSymbol source' .. py:attribute:: RESOLVED_SYMBOL_PROP_NAME :value: 'DrawResolved symbol' .. py:method:: _package_symbol_map() -> dict[str, str] :classmethod: Return ``{display_label: relative_path}`` for bundled symbols. .. py:method:: _list_package_symbols() -> list[str] :classmethod: Return bundled symbol choices formatted for GUI selection. .. py:method:: _resolve_symbolpreset_to_relpath(preset: str | None) -> str :classmethod: Resolve a GUI preset label or legacy relative path to a relative path. .. py:method:: _infer_symbolpreset_from_source(symbolsource: str | None) -> str :classmethod: Infer the bundled symbol preset from a source path when possible. .. py:method:: get_effective_symbol_source() -> str Return the actual symbol path used for rendering. Package presets take priority. When no preset is selected, the custom file path stored in ``symbolsource`` is used. .. py:method:: _effective_symbol_source_from_values(preset: str | None, symbolsource: str | None) -> str Resolve effective symbol path from explicit preset/source values. .. py:method:: _make_placeholder_bitmap(message: str, size: int = 96) -> wx.Bitmap Build a neutral placeholder bitmap for missing/invalid previews. .. py:method:: _load_symbol_preview_bitmap(symbol_path: str, size: int = 96) -> wx.Bitmap Load a symbol preview bitmap from raster image or SVG. .. py:method:: _format_preview_info(symbol_path: str | None) -> str Return a compact preview label to avoid stretching the dialog width. .. py:method:: _ensure_symbol_preview_widgets() -> None Create the simple symbol preview widgets below the property grid. .. py:method:: _update_symbol_preview(symbol_path: str | None = None) -> None Refresh the preview panel with the currently selected symbol. .. py:method:: _on_symbol_property_changed(event) -> None Update symbol preview when package/custom symbol selection changes. .. py:method:: defaultprop() Create the ``Wolf_Param`` dialog and populate it with default values. If the dialog already exists, it is reused. Parameters for drawing style (color, width, style, transparency…) and legend (text, font, position…) are added. .. py:method:: destroyprop() Release the ``Wolf_Param`` dialog (set ``myprops`` to ``None``). .. py:method:: fill_property() Read all current values from the ``Wolf_Param`` dialog into attributes. Called automatically by ``Wolf_Param`` when the user validates a parameter change. Also triggers an OpenGL refresh on the parent cloud if a mapviewer is available. .. py:method:: show() Populate the ``Wolf_Param`` dialog with current attribute values and display it. Creates the dialog via :meth:`defaultprop` if it does not exist yet, then synchronises all fields and calls ``Populate()`` / ``Show()``. .. py:class:: cloud_vertices(fname='', fromxls='', header=False, toload=True, idx='', plotted=True, mapviewer=None, need_for_wx=False, bbox=None, dxf_imported_elts=['MTEXT', 'INSERT']) Bases: :py:obj:`wolfhece.PyVertex._model.cloud_vertices`, :py:obj:`wolfhece.drawing_obj.Element_To_Draw` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.cloud_vertices :parts: 1 :private-bases: Cloud of vertices with OpenGL rendering and wx GUI support. Inherits data handling from :class:`cloud_vertices_model` and drawable behaviour from :class:`Element_To_Draw`. :ivar gllist: OpenGL display list identifier (0 = not compiled yet). :ivar forceupdategl: if ``True``, the display list will be recompiled on the next :meth:`plot` call. :ivar ongoing: guard flag to prevent recursive display-list compilation. .. py:attribute:: ANIM_NONE :value: 0 .. py:attribute:: ANIM_BLINK :value: 1 .. py:attribute:: ANIM_FADE :value: 2 .. py:attribute:: ANIM_GROW :value: 3 .. py:attribute:: ANIM_SEASONS :value: 4 .. py:attribute:: ANIM_PULSE :value: 5 .. py:attribute:: gllist :value: 0 .. py:attribute:: forceupdategl :value: False .. py:attribute:: ongoing :value: False .. py:method:: check_plot() Mark the cloud as plotted and lazy-load data if needed. Sets ``self.plotted = True``. If the cloud has not been loaded yet, the source file is read before the first draw. .. py:method:: on_changed_vertices() Invalidate rendering caches after vertex edits and request redraw. If this cloud belongs to a :class:`cloud_of_clouds` whose editor frame is open and this cloud is the active one, the CpGrid is refreshed automatically. .. py:method:: _make_cloud_vertices(**kwargs) -> cloud_vertices Create a sibling GUI-aware cloud_vertices. .. py:method:: _make_cloudproperties(**kwargs) -> cloudproperties Create a GUI-aware cloudproperties. .. py:method:: _make_cloudproperties_from_dict(d: dict, **kwargs) -> cloudproperties Create a GUI-aware cloudproperties from a dictionary. .. py:method:: _effective_animation_mode() -> int Return the active cloud animation mode. .. py:method:: _resolve_animation_phase(xmin=None, ymin=None, xmax=None, ymax=None) -> tuple[float, int] Return animation phase/mode and manage animation-clock subscription. .. py:method:: _apply_animation_effect(base_rgb: tuple[float, float, float], alpha: float, point_size_px: float, phase: float, mode: int, amplitude: float = 1.0) -> tuple[bool, tuple[float, float, float], float, float] :staticmethod: Apply animation effect and return (visible, rgb, alpha, size_px). .. py:method:: uncheck_plot(unload=True) Mark the cloud as not plotted. :param unload: reserved for future use (currently unused). .. py:method:: _resolve_shapefile_column(gdf, targetcolumn: str) -> str Present a wx dialog to let the user choose a column interactively. Overrides :meth:`cloud_vertices_model._resolve_shapefile_column`. Falls back to logging an error if wx is not available. :param gdf: ``GeoDataFrame`` already loaded from the Shapefile. :param targetcolumn: the originally requested column name (not found). :return: column name chosen by the user, or ``None`` to abort. .. py:method:: _legend_alignment_from_relpos(relpos: int) -> str :staticmethod: Map keypad-like relative position to text horizontal alignment. .. py:method:: _is_shader_mode(rendering_machine) -> bool :staticmethod: Return True when rendering mode requests the shader pipeline. Accepted inputs are: - Cloud_OGLRenderer enum value - integer backend id (0=list, 1=shader) - string name ('shader') .. py:method:: _resolve_mvp_viewport(xmin=None, ymin=None, xmax=None, ymax=None, mvp=None, viewport=None) -> tuple[numpy.ndarray | None, tuple[int, int] | None] Resolve MVP/viewport with the following priority: 1) explicit function arguments, 2) mapviewer projection/viewport, 3) orthographic MVP from bounds + canvas viewport fallback. .. py:method:: _compute_shader_point_size_px(style: int, width: float, mvp: numpy.ndarray, viewport: tuple[int, int]) -> float :staticmethod: Convert cloud width semantics to point-size pixels for shader draw. .. py:method:: _width_world_for_pixel_size(width_px: float) -> float Convert a marker width expressed in pixels to world units. .. py:method:: _build_points_xy() -> numpy.ndarray Build an ``(N, 2)`` float32 array from model coordinates. This relies on the model-level ``xyz`` accessor so both storage backends (dict or numpy) are handled transparently. .. py:method:: set_per_point_transform(pts_transform: numpy.ndarray | None) -> None Attach per-point rotation/scale data for the next draw call (Option B). :param pts_transform: ``(N, 2)`` float32 array whose columns are ``[rotation_rad, scale]``, one row per cloud point. Pass ``None`` to revert to the per-cloud uniform values (Option A: *Symbol rotation* and *Symbol scale* properties). .. py:method:: _plot_shader(xmin=None, ymin=None, xmax=None, ymax=None, mvp=None, viewport=None, anim_phase: float = 0.0, anim_mode: int = ANIM_NONE) Render cloud points with the shader pipeline. .. py:method:: _build_ortho_mvp(xmin: float, ymin: float, xmax: float, ymax: float) -> numpy.ndarray | None :staticmethod: Build a 2D orthographic MVP matching the current world view bounds. .. py:method:: _get_viewport_size() -> tuple[int, int] | None Return viewport size from mapviewer/canvas if available. .. py:method:: _legend_anchor_for_vertex(vx: float, vy: float, text_height_world: float = 0.0) -> tuple[float, float] Compute legend anchor around a vertex from ``legendrelpos``. Horizontal placement is handled by text alignment (left/center/right), so X stays anchored on the vertex. Vertical placement is approximated with a half-height shift to match historical relative-position behavior. .. py:method:: _legend_text_height_world(text: str, atlas: wolfhece.opengl.text_renderer2d.GlyphAtlas, mvp: numpy.ndarray, viewport: tuple[int, int], *, font_size: float, size_in_pixels: bool, world_height: float | None, world_width: float | None, line_spacing: float = 1.2) -> float :staticmethod: Estimate rendered text height in world units for relpos offsets. .. py:method:: plot_legend(sx=None, sy=None, xmin=None, ymin=None, xmax=None, ymax=None, size=None, rendering_machine=None, mvp=None, viewport=None, anim_phase=0.0) Render the legend for each visible vertex using the SDF text renderer. For every vertex inside the current view bounds, text is rendered directly in OpenGL using :class:`TextRenderer2D`. :param sx: unused (reserved). :param sy: unused (reserved). :param xmin: left boundary of the current view (map units). :param ymin: bottom boundary of the current view (map units). :param xmax: right boundary of the current view (map units). :param ymax: top boundary of the current view (map units). :param size: unused (reserved). :param rendering_machine: optional renderer hint (unused). :param mvp: optional 4x4 MVP matrix. :param viewport: optional ``(width_px, height_px)``. :param anim_phase: animation phase in ``[0, 1]``. .. py:method:: reset_listogl() Delete the compiled OpenGL display list and schedule recompilation. Sets ``forceupdategl = True`` so the next :meth:`plot` call rebuilds the list. .. py:method:: _plot_list(update: bool = False, color_override: int | None = None, width_override: float | None = None) Render the cloud with the legacy display-list / immediate-mode path. .. py:method:: plot_highlight_vertex(row_id, size_factor: float | None = None, color_rgb: tuple[int, int, int] | None = None) Draw ring/cross marker around one cloud vertex identified by row id. .. py:method:: plot(update: bool = False, *args, rendering_machine=None, mvp=None, viewport=None, **kwargs) Render the cloud of vertices using OpenGL (dispatcher). :param update: if ``True``, force recompilation of the display list even if one already exists. :param args: additional positional arguments (unused). :param rendering_machine: backend selector (list/shader). :param mvp: optional 4x4 MVP matrix for shader mode. :param viewport: optional ``(width_px, height_px)`` for shader mode. :param kwargs: keyword arguments forwarded to :meth:`plot_legend` (typically ``xmin``, ``ymin``, ``xmax``, ``ymax``). .. py:method:: show_properties() Open the interactive properties dialog for this cloud. Delegates to :meth:`cloudproperties.show`. .. py:method:: plot_matplotlib(ax=None, **kwargs) Display the cloud using Matplotlib (2D scatter plot). :param ax: existing Matplotlib axes. If ``None``, a new figure and axes are created. :param kwargs: extra keyword arguments forwarded to ``ax.scatter(x, y, **kwargs)`` (e.g. ``c=``, ``s=``, ``marker=``, ``cmap=``…). :return: tuple ``(fig, ax)``. .. py:method:: set_mapviewer(newmapviewer=None) Attach a (new) mapviewer to the object .. py:class:: cloud_of_clouds(idx: str = '', clouds: list[cloud_vertices] | None = None, plotted: bool = True, mapviewer=None, need_for_wx: bool = False, parent=None) Bases: :py:obj:`wolfhece.PyVertex._model.cloud_of_clouds`, :py:obj:`wx.Frame`, :py:obj:`wolfhece.drawing_obj.Element_To_Draw` .. autoapi-inheritance-diagram:: wolfhece.PyVertex._gui.cloud_of_clouds :parts: 1 :private-bases: GUI-enabled collection of :class:`cloud_vertices` instances. Inherits data handling from :class:`cloud_of_clouds_model` and drawable behaviour from :class:`Element_To_Draw`. Also inherits from ``wx.Frame`` to provide an interactive editor with a tree list of clouds and a CpGrid for vertex coordinates, following the same pattern as ``Zones``. Each child cloud is a GUI-aware :class:`cloud_vertices` (with OpenGL rendering). The collection delegates ``plot``, ``check_plot``, ``uncheck_plot`` and ``set_mapviewer`` to every child cloud so that toggling visibility on the collection toggles all children at once. .. py:attribute:: treelist :type: wx.dataview.TreeListCtrl .. py:attribute:: xls :type: wolfhece.CpGrid.CpGrid .. py:attribute:: parent :value: None .. py:attribute:: labelactcloud :value: None .. py:attribute:: _wx_frame_initialized :value: False .. py:attribute:: init_struct :value: True .. py:attribute:: active_cloud :type: cloud_vertices | None :value: None .. py:attribute:: last_active :value: None .. py:method:: _make_cloud_vertices(**kwargs) -> cloud_vertices Create a GUI-enabled cloud_vertices. .. py:method:: _make_cloud_vertices_from_dict(d: dict, **kwargs) -> cloud_vertices Create a GUI-enabled cloud_vertices from a dictionary. .. py:method:: create_cloud(idx: str = '', **kwargs) -> cloud_vertices Create a new GUI-enabled cloud and add it to the collection. :param idx: identifier for the new cloud. :param kwargs: forwarded to :class:`cloud_vertices` constructor. :return: the newly created cloud. .. py:method:: set_mapviewer(newmapviewer=None) Attach a mapviewer to the collection and propagate to all children. .. py:method:: check_plot() Mark the collection and all children as plotted. .. py:method:: uncheck_plot(unload=True) Mark the collection and all children as not plotted. .. py:method:: find_minmax(force: bool = True) Recompute bounds for all clouds and update Element_To_Draw extent. .. py:method:: reset_listogl() Reset OpenGL display lists for all child clouds. .. py:method:: plot(sx=None, sy=None, xmin=None, ymin=None, xmax=None, ymax=None, size=None, rendering_machine=None, mvp=None, viewport=None, **kwargs) Render all child clouds. Delegates to each cloud's :meth:`plot` method. :param rendering_machine: backend selector (list/shader). :param mvp: optional 4×4 MVP matrix for shader mode. :param viewport: optional viewport tuple for shader mode. .. py:method:: show_properties(parent=None, forceupdate=False) Open the interactive editor frame. If the editor has not been built yet, create the wx.Frame and its widget tree (tree list, CpGrid, buttons). :param parent: parent object (e.g. mapviewer). :param forceupdate: if ``True``, rebuild the whole UI. .. py:method:: add_cloud(cloud: cloud_vertices) -> None Append a cloud and propagate current mapviewer. .. py:method:: plot_matplotlib(ax=None, **kwargs) Display all clouds using Matplotlib. :param ax: existing Matplotlib axes. If ``None``, a new figure and axes are created. :param kwargs: forwarded to each cloud's ``plot_matplotlib``. :return: tuple ``(fig, ax)``. .. py:method:: _on_close(event) Hide the editor frame instead of destroying it. .. py:method:: _init_ui() Build the editor widgets: tree list, CpGrid, and button panels. .. py:method:: _fill_structure() Populate the tree list with current clouds. .. py:method:: _activate_cloud(cloud: cloud_vertices | None) Set the active cloud and update the grid. .. py:method:: _fill_grid() Fill the CpGrid with the active cloud's vertices. .. py:method:: _on_check_item(event) Handle check/uncheck of a tree item. .. py:method:: _on_activate_item(event) Handle activation (double-click) of a tree item. .. py:method:: _on_rdown(event) Handle right-click on a tree item: show properties. .. py:method:: _on_edit_label(event) Handle F2 key to rename a cloud. .. py:method:: _on_add_cloud(event) Add a new empty cloud to the collection. .. py:method:: _on_delete_cloud(event) Remove the active cloud from the collection. .. py:method:: _on_up_cloud(event) Move the active cloud one position up. .. py:method:: _on_down_cloud(event) Move the active cloud one position down. .. py:method:: _on_save_json(event) Save the collection to a JSON file. .. py:method:: _on_load_json(event) Load a collection from a JSON file. .. py:method:: _on_merge(event) Merge all clouds into a single new cloud and add it to the mapviewer. .. py:method:: _on_bulk_properties(event) Edit display properties for all clouds at once. Only properties whose value is common across every cloud are placed as *Active* parameters. Non-common properties appear only in the *Default* page. The user can promote a Default value by editing it; only Active values are propagated to the individual clouds upon *Apply*. .. py:method:: _on_addrows(event) Add rows to the vertex grid. .. py:method:: _on_update_vertices(event) Transfer coordinates from the grid back to the active cloud. .. py:method:: _on_add_vertex(event) Add vertices to the active cloud. .. py:method:: _on_modify_vertex(event) Remove the selected vertex from the active cloud. .. py:method:: _on_plot_mpl(event) Plot the active cloud in a Matplotlib window. .. py:method:: _on_zoom(event) Zoom the mapviewer to the active cloud's extent. .. py:method:: _on_cloud_properties(event) Open the properties dialog for the active cloud.