Skip to content

Commit

Permalink
gui(nviz): Type hinting to the nviz library wrapper and various relat…
Browse files Browse the repository at this point in the history
…ed improvements (#4899)

* gui.wxpython.nviz: numpy import can safely be below

* gui.wxpython.nviz: Move ctypes and grass.lib imports below normal imports

There is no reason (dependency of imports) apparent here that would prevent it from working.

Works locally in a Ubuntu 22.04 WSL on Windows

* gui.wxpython.nviz: Sort remaining imports

grass.script was already below gui imports in 2023

* gui.wxpython.nviz: Set render flags dicts as a TypedDict

This allows static analysis of self.render dict, that contains 4 keys and all boolean values

* gui.wxpython.nviz.mapwindow: Light typing annotations

* gui.wxpython.nviz.mapwindow: GetContentScaleFactor() typing annotations

* gui.wxpython.nviz: Make GLWindow.ZoomToMap()'s layer argument optional

* gui.wxpython.core.utils: PIL.Image and wx.Image type annotations

* gui.wxpython.gui_core.wrap: wx.Image and wx.Bitmap type annotations

* gui.wxpython.gui_core.wrap: wx.Rect and other type annotations

* gui.wxpython.core.gcmd: Add typing and overloads for EncodeString and DecodeString

* gui: isort mapswipe.mapwindow imports

* gui.wxpython.mapswipe.mapwindow: Add type hints related to wx.Size

* gui.wxpython.nviz.wxnviz: Add type hints for OGSF/Nviz wrappers

Includes using different aliases for different type of ids, to be able to check for usage mismatches

* gui.wxpython.nviz.wxnviz: Add type hints for QueryMap with TypedDict

gui.wxpython.nviz.wxnviz: Add some type hints for Texture and ImageTexture

* gui.wxpython.nviz.wxnviz: Add typing to functions that return empty vectors, considered tuples

* gui.wxpython.nviz.wxnviz: Address type checking warnings in QueryMap

When results of GetPointOnSurface() is None, ensure that we return None, and the other branch is assured to contain floats

* gui.wxpython.nviz.wxnviz: Remove unused import

* gui.wxpython.nviz.mapwindow: Sort imports with an isort split group

* gui.wxpython.nviz.mapwindow: Add some type hints

* gui.wxpython.mapdisp.frame: Sort imports with an isort split group

* gui.wxpython.nviz.mapwindow: Handle some cases where self._display is None or other variables are missing
  • Loading branch information
echoix authored Jan 28, 2025
1 parent 71f1bce commit 552e00b
Show file tree
Hide file tree
Showing 7 changed files with 674 additions and 234 deletions.
26 changes: 23 additions & 3 deletions gui/wxpython/core/gcmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import time
import traceback
from threading import Thread
from typing import TYPE_CHECKING, TextIO
from typing import TYPE_CHECKING, AnyStr, TextIO, overload

import wx
from core.debug import Debug
Expand All @@ -59,7 +59,17 @@
from io import TextIOWrapper


def DecodeString(string):
@overload
def DecodeString(string: AnyStr) -> AnyStr | str:
pass


@overload
def DecodeString(string: None) -> None:
pass


def DecodeString(string: AnyStr | None) -> AnyStr | str | None:
"""Decode string using system encoding
:param string: string to be decoded
Expand All @@ -75,7 +85,17 @@ def DecodeString(string):
return string


def EncodeString(string):
@overload
def EncodeString(string: str) -> bytes | str:
pass


@overload
def EncodeString(string: None) -> None:
pass


def EncodeString(string: str | None) -> bytes | str | None:
"""Return encoded string using system locales
:param string: string to be encoded
Expand Down
13 changes: 11 additions & 2 deletions gui/wxpython/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
@author Jachym Cepicky
"""

from __future__ import annotations

import os
import sys
import platform
Expand All @@ -21,6 +23,8 @@
import inspect
import operator
from string import digits
from typing import TYPE_CHECKING


from grass.script import core as grass
from grass.script import task as gtask
Expand All @@ -31,6 +35,11 @@
from core.globalvar import wxPythonPhoenix


if TYPE_CHECKING:
import wx
import PIL.Image


def cmp(a, b):
"""cmp function"""
return (a > b) - (a < b)
Expand Down Expand Up @@ -1011,7 +1020,7 @@ def GetGEventAttribsForHandler(method, event):
return kwargs, missing_args


def PilImageToWxImage(pilImage, copyAlpha=True):
def PilImageToWxImage(pilImage: PIL.Image.Image, copyAlpha: bool = True) -> wx.Image:
"""Convert PIL image to wx.Image
Based on http://wiki.wxpython.org/WorkingWithImages
Expand Down Expand Up @@ -1040,7 +1049,7 @@ def PilImageToWxImage(pilImage, copyAlpha=True):
return wxImage


def autoCropImageFromFile(filename):
def autoCropImageFromFile(filename) -> wx.Image:
"""Loads image from file and crops it automatically.
If PIL is not installed, it does not crop it.
Expand Down
18 changes: 10 additions & 8 deletions gui/wxpython/gui_core/wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
@author Anna Petrasova <kratochanna gmail.com>
"""

from __future__ import annotations

import sys
import wx
import wx.lib.agw.floatspin as fs
Expand Down Expand Up @@ -80,7 +82,7 @@ def convertToInt(argsOrKwargs, roundVal=False):
return result


def IsDark():
def IsDark() -> bool:
"""Detects if used theme is dark.
Wraps wx method for different versions."""

Expand All @@ -96,25 +98,25 @@ def luminance(c):
return luminance(fg) - luminance(bg) > 0.2


def BitmapFromImage(image, depth=-1):
def BitmapFromImage(image: wx.Image, depth=-1) -> wx.Bitmap:
if wxPythonPhoenix:
return wx.Bitmap(img=image, depth=depth)
return wx.BitmapFromImage(image, depth=depth)


def ImageFromBitmap(bitmap):
def ImageFromBitmap(bitmap: wx.Bitmap) -> wx.Image:
if wxPythonPhoenix:
return bitmap.ConvertToImage()
return wx.ImageFromBitmap(bitmap)


def EmptyBitmap(width, height, depth=-1):
def EmptyBitmap(width, height, depth=-1) -> wx.Bitmap:
if wxPythonPhoenix:
return wx.Bitmap(width=width, height=height, depth=depth)
return wx.EmptyBitmap(width=width, height=height, depth=depth)


def EmptyImage(width, height, clear=True):
def EmptyImage(width, height, clear=True) -> wx.Image:
if wxPythonPhoenix:
return wx.Image(width=width, height=height, clear=clear)
return wx.EmptyImage(width=width, height=height, clear=clear)
Expand Down Expand Up @@ -680,17 +682,17 @@ def __init__(self, *args, **kwargs):
kwargs = convertToInt(argsOrKwargs=kwargs)
wx.Rect.__init__(self, *args, **kwargs)

def ContainsXY(self, x, y):
def ContainsXY(self, x: float, y: float) -> bool:
if wxPythonPhoenix:
return wx.Rect.Contains(self, x=int(x), y=int(y))
return wx.Rect.ContainsXY(self, int(x), int(y))

def ContainsRect(self, rect):
def ContainsRect(self, rect: wx.Rect) -> bool:
if wxPythonPhoenix:
return wx.Rect.Contains(self, rect=rect)
return wx.Rect.ContainsRect(self, rect)

def OffsetXY(self, dx, dy):
def OffsetXY(self, dx: float, dy: float) -> wx.Rect:
if wxPythonPhoenix:
return wx.Rect.Offset(self, int(dx), int(dy))
return wx.Rect.OffsetXY(self, int(dx), int(dy))
Expand Down
39 changes: 20 additions & 19 deletions gui/wxpython/mapdisp/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,30 @@
from typing import TYPE_CHECKING

from core import globalvar

# isort: split
import wx
import wx.aui

from mapdisp.toolbars import MapToolbar, NvizIcons
from mapdisp.gprint import PrintOptions
from core.gcmd import GError, GMessage, RunCommand
from core.utils import ListOfCatsToRange, GetLayerNameFromCmd
from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
from core.debug import Debug
from core.gcmd import GError, GMessage, RunCommand
from core.giface import Notification
from core.settings import UserSettings
from gui_core.mapdisp import SingleMapPanel, FrameMixin
from gui_core.query import QueryDialog, PrepareQueryResults
from core.utils import GetLayerNameFromCmd, ListOfCatsToRange
from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
from gui_core.forms import GUI
from gui_core.mapdisp import FrameMixin, SingleMapPanel
from gui_core.query import PrepareQueryResults, QueryDialog
from gui_core.vselect import VectorSelectBase, VectorSelectHighlighter
from gui_core.wrap import Menu
from main_window.page import MainPageBase
from mapdisp import statusbar as sb
from mapdisp.gprint import PrintOptions
from mapdisp.toolbars import MapToolbar, NvizIcons
from mapwin.analysis import (
MeasureAreaController,
MeasureDistanceController,
ProfileController,
)
from mapwin.buffered import BufferedMapWindow
from mapwin.decorations import (
ArrowController,
Expand All @@ -49,17 +61,6 @@
LegendController,
LegendVectController,
)
from mapwin.analysis import (
MeasureAreaController,
MeasureDistanceController,
ProfileController,
)
from gui_core.forms import GUI
from core.giface import Notification
from gui_core.vselect import VectorSelectBase, VectorSelectHighlighter
from gui_core.wrap import Menu
from mapdisp import statusbar as sb
from main_window.page import MainPageBase

import grass.script as gs
from grass.pydispatch.signal import Signal
Expand Down
22 changes: 12 additions & 10 deletions gui/wxpython/mapswipe/mapwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
@author Anna Kratochvilova <kratochanna gmail.com>
"""

import wx
from __future__ import annotations

from typing import Literal

import wx
from core.debug import Debug
from core.settings import UserSettings
from gui_core.wrap import NewId, Rect
from mapwin.buffered import BufferedMapWindow
from gui_core.wrap import Rect, NewId


EVT_MY_MOUSE_EVENTS = wx.NewEventType()
EVT_MY_MOTION = wx.NewEventType()
Expand All @@ -38,17 +40,17 @@ class SwipeBufferedWindow(BufferedMapWindow):
Special mouse events with changed coordinates are used.
"""

def __init__(self, parent, giface, Map, properties, **kwargs):
def __init__(self, parent, giface, Map, properties, **kwargs) -> None:
BufferedMapWindow.__init__(
self, parent=parent, giface=giface, Map=Map, properties=properties, **kwargs
)
Debug.msg(2, "SwipeBufferedWindow.__init__()")

self.specialSize = super().GetClientSize()
self.specialSize: wx.Size = super().GetClientSize()
self.specialCoords = [0, 0]
self.imageId = 99
self.movingSash = False
self._mode = "swipe"
self._mode: Literal["swipe", "mirror"] = "swipe"
self.lineid = NewId()

def _bindMouseEvents(self):
Expand Down Expand Up @@ -77,18 +79,18 @@ def _mouseActions(self, event):
def _mouseMotion(self, event):
self._RaiseMouseEvent(event, EVT_MY_MOTION)

def GetClientSize(self):
def GetClientSize(self) -> wx.Size:
"""Overridden method which returns simulated window size."""
if self._mode == "swipe":
return self.specialSize
return super().GetClientSize()

def SetClientSize(self, size):
def SetClientSize(self, size: wx.Size) -> None:
"""Overridden method which sets simulated window size."""
Debug.msg(3, "SwipeBufferedWindow.SetClientSize(): size = %s" % size)
self.specialSize = size

def SetMode(self, mode):
def SetMode(self, mode: Literal["swipe", "mirror"]) -> None:
"""Sets mode of the window.
:param mode: mode can be 'swipe' or 'mirror'
Expand All @@ -101,7 +103,7 @@ def GetImageCoords(self):
return self.specialCoords
return (0, 0)

def SetImageCoords(self, coords):
def SetImageCoords(self, coords) -> None:
"""Sets coordinates of rendered image"""
Debug.msg(
3,
Expand Down
Loading

0 comments on commit 552e00b

Please sign in to comment.