Jelajahi Sumber

Apply Black to wxGUI (#1527)

This applies Black 20.8b1 to wxGUI and GUI scripts in gui dir.
Uses Black with settings from pyproject.toml file, i.e., enables the gui subdir.
Ignores lmgr/layertree.py because of a bug in Black.
Fixed formatting checks enabled in Flake8.
Black does not add (but preserves) some empty lines required by Flake8. Adding these manually.
Vaclav Petras 4 tahun lalu
induk
melakukan
02bfae03d5
100 mengubah file dengan 21449 tambahan dan 19463 penghapusan
  1. 3 3
      gui/scripts/d.rast3d.py
  2. 19 15
      gui/scripts/d.wms.py
  3. 6 31
      gui/wxpython/.flake8
  4. 10 10
      gui/wxpython/animation/__init__.py
  5. 4 9
      gui/wxpython/animation/anim.py
  6. 155 147
      gui/wxpython/animation/controller.py
  7. 61 59
      gui/wxpython/animation/data.py
  8. 633 720
      gui/wxpython/animation/dialogs.py
  9. 163 127
      gui/wxpython/animation/frame.py
  10. 20 17
      gui/wxpython/animation/g.gui.animation.py
  11. 23 23
      gui/wxpython/animation/mapwindow.py
  12. 174 148
      gui/wxpython/animation/nviztask.py
  13. 233 194
      gui/wxpython/animation/provider.py
  14. 136 135
      gui/wxpython/animation/temporal_manager.py
  15. 73 80
      gui/wxpython/animation/toolbars.py
  16. 87 67
      gui/wxpython/animation/utils.py
  17. 14 14
      gui/wxpython/core/__init__.py
  18. 17 12
      gui/wxpython/core/debug.py
  19. 140 119
      gui/wxpython/core/gcmd.py
  20. 227 184
      gui/wxpython/core/gconsole.py
  21. 41 41
      gui/wxpython/core/giface.py
  22. 45 41
      gui/wxpython/core/globalvar.py
  23. 17 13
      gui/wxpython/core/gthread.py
  24. 46 34
      gui/wxpython/core/layerlist.py
  25. 69 54
      gui/wxpython/core/menutree.py
  26. 430 336
      gui/wxpython/core/render.py
  27. 704 828
      gui/wxpython/core/settings.py
  28. 178 149
      gui/wxpython/core/toolboxes.py
  29. 11 8
      gui/wxpython/core/treemodel.py
  30. 49 44
      gui/wxpython/core/units.py
  31. 349 351
      gui/wxpython/core/utils.py
  32. 1056 983
      gui/wxpython/core/workspace.py
  33. 103 109
      gui/wxpython/core/ws.py
  34. 1 7
      gui/wxpython/datacatalog/__init__.py
  35. 58 26
      gui/wxpython/datacatalog/catalog.py
  36. 142 69
      gui/wxpython/datacatalog/dialogs.py
  37. 3 10
      gui/wxpython/datacatalog/frame.py
  38. 3 1
      gui/wxpython/datacatalog/g.gui.datacatalog.py
  39. 75 72
      gui/wxpython/datacatalog/toolbars.py
  40. 813 547
      gui/wxpython/datacatalog/tree.py
  41. 6 6
      gui/wxpython/dbmgr/__init__.py
  42. 1588 1474
      gui/wxpython/dbmgr/base.py
  43. 241 210
      gui/wxpython/dbmgr/dialogs.py
  44. 5 5
      gui/wxpython/dbmgr/g.gui.dbmgr.py
  45. 68 60
      gui/wxpython/dbmgr/manager.py
  46. 341 302
      gui/wxpython/dbmgr/sqlbuilder.py
  47. 60 60
      gui/wxpython/dbmgr/vinfo.py
  48. 99 106
      gui/wxpython/docs/wxgui_sphinx/conf.py
  49. 3 3
      gui/wxpython/gcp/__init__.py
  50. 7 5
      gui/wxpython/gcp/g.gui.gcp.py
  51. 1229 1097
      gui/wxpython/gcp/manager.py
  52. 196 130
      gui/wxpython/gcp/mapdisplay.py
  53. 13 13
      gui/wxpython/gcp/statusbar.py
  54. 65 88
      gui/wxpython/gcp/toolbars.py
  55. 8 8
      gui/wxpython/gmodeler/__init__.py
  56. 341 277
      gui/wxpython/gmodeler/dialogs.py
  57. 536 525
      gui/wxpython/gmodeler/frame.py
  58. 4 2
      gui/wxpython/gmodeler/g.gui.gmodeler.py
  59. 2 2
      gui/wxpython/gmodeler/giface.py
  60. 1 3
      gui/wxpython/gmodeler/menudata.py
  61. 883 812
      gui/wxpython/gmodeler/model.py
  62. 429 443
      gui/wxpython/gmodeler/preferences.py
  63. 55 74
      gui/wxpython/gmodeler/toolbars.py
  64. 15 15
      gui/wxpython/gui_core/__init__.py
  65. 740 735
      gui/wxpython/gui_core/dialogs.py
  66. 1375 1113
      gui/wxpython/gui_core/forms.py
  67. 308 283
      gui/wxpython/gui_core/ghelp.py
  68. 194 148
      gui/wxpython/gui_core/goutput.py
  69. 960 855
      gui/wxpython/gui_core/gselect.py
  70. 9 4
      gui/wxpython/gui_core/infobar.py
  71. 142 121
      gui/wxpython/gui_core/mapdisp.py
  72. 71 59
      gui/wxpython/gui_core/menu.py
  73. 1028 1096
      gui/wxpython/gui_core/preferences.py
  74. 154 147
      gui/wxpython/gui_core/prompt.py
  75. 152 118
      gui/wxpython/gui_core/pyedit.py
  76. 78 107
      gui/wxpython/gui_core/pystc.py
  77. 72 85
      gui/wxpython/gui_core/query.py
  78. 132 126
      gui/wxpython/gui_core/simplelmgr.py
  79. 98 90
      gui/wxpython/gui_core/toolbars.py
  80. 58 41
      gui/wxpython/gui_core/treeview.py
  81. 103 86
      gui/wxpython/gui_core/vselect.py
  82. 213 236
      gui/wxpython/gui_core/widgets.py
  83. 142 42
      gui/wxpython/gui_core/wrap.py
  84. 7 7
      gui/wxpython/iclass/__init__.py
  85. 191 175
      gui/wxpython/iclass/dialogs.py
  86. 21 20
      gui/wxpython/iclass/digit.py
  87. 485 360
      gui/wxpython/iclass/frame.py
  88. 28 29
      gui/wxpython/iclass/g.gui.iclass.py
  89. 36 52
      gui/wxpython/iclass/plots.py
  90. 6 8
      gui/wxpython/iclass/statistics.py
  91. 98 127
      gui/wxpython/iclass/toolbars.py
  92. 1 1
      gui/wxpython/icons/grass_icons.py
  93. 18 24
      gui/wxpython/icons/icon.py
  94. 3 3
      gui/wxpython/image2target/__init__.py
  95. 50 49
      gui/wxpython/image2target/g.gui.image2target.py
  96. 465 356
      gui/wxpython/image2target/ii2t_gis_set.py
  97. 3 4
      gui/wxpython/image2target/ii2t_gis_set_error.py
  98. 1226 1069
      gui/wxpython/image2target/ii2t_manager.py
  99. 196 130
      gui/wxpython/image2target/ii2t_mapdisplay.py
  100. 0 0
      gui/wxpython/image2target/ii2t_statusbar.py

+ 3 - 3
gui/scripts/d.rast3d.py

@@ -10,9 +10,9 @@
 #
 # COPYRIGHT:	(C) 2008, 2010 by the GRASS Development Team
 #
-#		This program is free software under the GNU General
-#		Public License (>=v2). Read the file COPYING that
-#		comes with GRASS for details.
+# 		This program is free software under the GNU General
+# 		Public License (>=v2). Read the file COPYING that
+# 		comes with GRASS for details.
 #
 #############################################################################
 

+ 19 - 15
gui/scripts/d.wms.py

@@ -165,25 +165,26 @@ sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms"))
 
 
 def GetRegion():
-    """!Parse region from GRASS_REGION env var.
-    """
+    """!Parse region from GRASS_REGION env var."""
     region = os.environ["GRASS_REGION"]
-    conv_reg_vals = {'east': 'e',
-                     'north': 'n',
-                     'west': 'w',
-                     'south': 's',
-                     'rows': 'rows',
-                     'cols': 'cols',
-                     'e-w resol': 'ewres',
-                     'n-s resol': 'nsres'}
+    conv_reg_vals = {
+        "east": "e",
+        "north": "n",
+        "west": "w",
+        "south": "s",
+        "rows": "rows",
+        "cols": "cols",
+        "e-w resol": "ewres",
+        "n-s resol": "nsres",
+    }
 
     keys_to_convert = conv_reg_vals.keys()
 
     conv_region = {}
-    region = region.split(';')
+    region = region.split(";")
 
     for r in region:
-        r = r.split(':')
+        r = r.split(":")
         r[0] = r[0].strip()
 
         if r[0] in keys_to_convert:
@@ -193,15 +194,17 @@ def GetRegion():
 
 
 def main():
-    options['region'] = GetRegion()
+    options["region"] = GetRegion()
 
-    if 'GRASS' in options['driver']:
+    if "GRASS" in options["driver"]:
         grass.debug("Using GRASS driver")
         from wms_drv import WMSDrv
+
         wms = WMSDrv()
-    elif 'GDAL' in options['driver']:
+    elif "GDAL" in options["driver"]:
         grass.debug("Using GDAL WMS driver")
         from wms_gdal_drv import WMSGdalDrv
+
         wms = WMSGdalDrv()
 
     temp_map = wms.GetMap(options, flags)
@@ -209,6 +212,7 @@ def main():
 
     return 0
 
+
 if __name__ == "__main__":
     options, flags = grass.parser()
     sys.exit(main())

+ 6 - 31
gui/wxpython/.flake8

@@ -1,36 +1,9 @@
 [flake8]
 ignore =
-    E265, # block comment should start with '# '
-    E117, # over-indented
-    E122, # continuation line missing indentation or outdented
-    E123, # closing bracket does not match indentation of opening bracket's line
-    E124, # closing bracket does not match visual indentation
-    E125, # continuation line with same indent as next logical line
-    E126, # continuation line over-indented for hanging indent
-    E127, # continuation line over-indented for visual indent
-    E128, # continuation line under-indented for visual indent
-    E131, # continuation line unaligned for hanging indent
-    E202, # whitespace before '}'
-    E203, # whitespace before ':'
-    E211, # whitespace before '('
-    E222, # multiple spaces after operator
-    E225, # missing whitespace around operator
-    E226, # missing whitespace around arithmetic operator
-    E231, # missing whitespace after ','
-    E241, # multiple spaces after ','
-    E261, # at least two spaces before inline comment
-    E271, # multiple spaces after keyword
-    E272, # multiple spaces before keyword
-    E301, # expected 1 blank line, found 0
-    E302, # expected 2 blank lines, found 1
-    E303, # too many blank lines (3)
-    E305, # expected 2 blank lines after class or function definition, found 1
-    E306, # expected 1 blank line before a nested definition, found 0
-    E501, # line too long (96 > 79 characters)
-    W291, # trailing whitespace
-    W293, # blank line contains whitespace
-    W503, # line break before binary operator
-    W504, # line break after binary operator
+    E203,  # whitespace before ':' (Black)
+    W503,  # line break before binary operator (Black)
+    E501,  # line too long (fix needed)
+    E265,  # block comment should start with '# ' (fix needed)
 
 per-file-ignores =
     # Many of these ignores can and should be removed and the problem fixed.
@@ -56,6 +29,8 @@ per-file-ignores =
     image2target/*: F841, E722
     iscatt/*: F841, E722, E741, F405, F403
     lmgr/*: F841, E266, E722, E741, W605
+    # layertree still includes some formatting issues (it is ignored by Black)
+    lmgr/layertree.py: E722, E266, W504, E225
     modules/*: F841, E722, W605
     nviz/*: F841, E266, E722, W605, F403, F405
     photo2image/*: F841, E722

+ 10 - 10
gui/wxpython/animation/__init__.py

@@ -1,12 +1,12 @@
 all = [
-    'nviztask',
-    'temporal_manager',
-    'dialogs',
-    'mapwindow',
-    'g.gui.animation',
-    'controller',
-    'anim',
-    'toolbars',
-    'utils',
-    'frame',
+    "nviztask",
+    "temporal_manager",
+    "dialogs",
+    "mapwindow",
+    "g.gui.animation",
+    "controller",
+    "anim",
+    "toolbars",
+    "utils",
+    "frame",
 ]

+ 4 - 9
gui/wxpython/animation/anim.py

@@ -103,9 +103,7 @@ class Animation(wx.EvtHandler):
         if not self.IsActive():
             return
         self.currentIndex = 0
-        self.callbackEndAnimation(
-            self.currentIndex, self.GetFrame(
-                self.currentIndex))
+        self.callbackEndAnimation(self.currentIndex, self.GetFrame(self.currentIndex))
 
     def _arrivedToEnd(self):
         """Decides which action to do after animation end (stop, repeat)."""
@@ -134,9 +132,7 @@ class Animation(wx.EvtHandler):
         if not self.IsActive():
             return
 
-        self.callbackUpdateFrame(
-            self.currentIndex, self.GetFrame(
-                self.currentIndex))
+        self.callbackUpdateFrame(self.currentIndex, self.GetFrame(self.currentIndex))
         if self.orientation == Orientation.FORWARD:
             self.currentIndex += 1
             if self.currentIndex == self.count:
@@ -151,9 +147,7 @@ class Animation(wx.EvtHandler):
         if not self.IsActive():
             return
         self.currentIndex = index
-        self.callbackUpdateFrame(
-            self.currentIndex, self.GetFrame(
-                self.currentIndex))
+        self.callbackUpdateFrame(self.currentIndex, self.GetFrame(self.currentIndex))
 
     def PreviousFrameIndex(self):
         if not self.IsActive():
@@ -179,6 +173,7 @@ class Animation(wx.EvtHandler):
             if self.currentIndex == -1:
                 self.currentIndex = 0
 
+
 # def test():
 #    import wx
 #    app = wx.PySimpleApp()

+ 155 - 147
gui/wxpython/animation/controller.py

@@ -24,22 +24,30 @@ from gui_core.wrap import EmptyImage, ImageFromBitmap
 
 from animation.temporal_manager import TemporalManager
 from animation.dialogs import InputDialog, EditDialog, ExportDialog
-from animation.utils import TemporalMode, TemporalType, Orientation, RenderText, WxImageToPil, \
-    sampleCmdMatrixAndCreateNames, layerListToCmdsMatrix, HashCmds
+from animation.utils import (
+    TemporalMode,
+    TemporalType,
+    Orientation,
+    RenderText,
+    WxImageToPil,
+    sampleCmdMatrixAndCreateNames,
+    layerListToCmdsMatrix,
+    HashCmds,
+)
 from animation.data import AnimationData
 
 
 class AnimationController(wx.EvtHandler):
-
-    def __init__(self, frame, sliders, animations, mapwindows,
-                 provider, bitmapPool, mapFilesPool):
+    def __init__(
+        self, frame, sliders, animations, mapwindows, provider, bitmapPool, mapFilesPool
+    ):
         wx.EvtHandler.__init__(self)
 
         self.mapwindows = mapwindows
 
         self.frame = frame
         self.sliders = sliders
-        self.slider = self.sliders['temporal']
+        self.slider = self.sliders["temporal"]
         self.animationToolbar = None
 
         self.temporalMode = None
@@ -53,13 +61,12 @@ class AnimationController(wx.EvtHandler):
         self.bitmapProvider = provider
         for anim, win in zip(self.animations, self.mapwindows):
             anim.SetCallbackUpdateFrame(
-                lambda index, dataId, win=win: self.UpdateFrame(
-                    index, win, dataId))
+                lambda index, dataId, win=win: self.UpdateFrame(index, win, dataId)
+            )
             anim.SetCallbackEndAnimation(
-                lambda index, dataId, win=win: self.UpdateFrameEnd(
-                    index, win, dataId))
-            anim.SetCallbackOrientationChanged(
-                self.OrientationChangedInReverseMode)
+                lambda index, dataId, win=win: self.UpdateFrameEnd(index, win, dataId)
+            )
+            anim.SetCallbackOrientationChanged(self.OrientationChangedInReverseMode)
 
         for slider in self.sliders.values():
             slider.SetCallbackSliderChanging(self.SliderChanging)
@@ -138,8 +145,9 @@ class AnimationController(wx.EvtHandler):
 
     def UpdateFrame(self, index, win, dataId):
         bitmap = self.bitmapProvider.GetBitmap(dataId)
-        if not UserSettings.Get(group='animation', key='temporal',
-                                subkey=['nodata', 'enable']):
+        if not UserSettings.Get(
+            group="animation", key="temporal", subkey=["nodata", "enable"]
+        ):
             if dataId is not None:
                 win.DrawBitmap(bitmap)
         else:
@@ -209,8 +217,8 @@ class AnimationController(wx.EvtHandler):
             parent=self.frame,
             evalFunction=self.EvaluateInput,
             animationData=self.animationData,
-            maxAnimations=len(
-                self.animations))
+            maxAnimations=len(self.animations),
+        )
         dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_CANCEL:
             dlg.Destroy()
@@ -232,8 +240,8 @@ class AnimationController(wx.EvtHandler):
         if not found:
             GMessage(
                 parent=self.frame,
-                message=_("Maximum number of animations is %d.") %
-                len(self.animations))
+                message=_("Maximum number of animations is %d.") % len(self.animations),
+            )
             return
 
         # running = False
@@ -244,13 +252,9 @@ class AnimationController(wx.EvtHandler):
 
         animData = AnimationData()
         # number of active animations
-        animationIndex = len(
-            [anim for anim in self.animations if anim.IsActive()])
+        animationIndex = len([anim for anim in self.animations if anim.IsActive()])
         animData.SetDefaultValues(windowIndex, animationIndex)
-        dlg = InputDialog(
-            parent=self.frame,
-            mode='add',
-            animationData=animData)
+        dlg = InputDialog(parent=self.frame, mode="add", animationData=animData)
         dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_CANCEL:
             dlg.UnInit()
@@ -260,13 +264,17 @@ class AnimationController(wx.EvtHandler):
         # check compatibility
         if animData.windowIndex in indices:
             GMessage(
-                parent=self.frame, message=_(
+                parent=self.frame,
+                message=_(
                     "More animations are using one window."
-                    " Please select different window for each animation."))
+                    " Please select different window for each animation."
+                ),
+            )
             return
         try:
             temporalMode, tempManager = self.EvaluateInput(
-                self.animationData + [animData])
+                self.animationData + [animData]
+            )
         except GException as e:
             GError(parent=self.frame, message=e.value, showTraceback=False)
             return
@@ -293,8 +301,12 @@ class AnimationController(wx.EvtHandler):
                     animationData.append(anim)
 
         except (GException, ValueError, IOError) as e:
-            GError(parent=self.frame, message=str(e),
-                   showTraceback=False, caption=_("Invalid input"))
+            GError(
+                parent=self.frame,
+                message=str(e),
+                showTraceback=False,
+                caption=_("Invalid input"),
+            )
             return
         try:
             temporalMode, tempManager = self.EvaluateInput(animationData)
@@ -316,16 +328,12 @@ class AnimationController(wx.EvtHandler):
         else:
             timeLabels, mapNamesDict = None, None
         for anim in self.animationData:
-            if anim.viewMode == '2d':
+            if anim.viewMode == "2d":
                 anim.cmdMatrix = layerListToCmdsMatrix(anim.layerList)
             else:
-                anim.cmdMatrix = [(cmd,)
-                                  for cmd in anim.GetNvizCommands()
-                                  ['commands']]
+                anim.cmdMatrix = [(cmd,) for cmd in anim.GetNvizCommands()["commands"]]
         self._updateSlider(timeLabels=timeLabels)
-        self._updateAnimations(
-            activeIndices=indices,
-            mapNamesDict=mapNamesDict)
+        self._updateAnimations(activeIndices=indices, mapNamesDict=mapNamesDict)
         self._updateBitmapData()
         # if running:
         #     self.PauseAnimation(False)
@@ -335,13 +343,13 @@ class AnimationController(wx.EvtHandler):
 
     def _updateSlider(self, timeLabels=None):
         if self.temporalMode == TemporalMode.NONTEMPORAL:
-            self.frame.SetSlider('nontemporal')
-            self.slider = self.sliders['nontemporal']
+            self.frame.SetSlider("nontemporal")
+            self.slider = self.sliders["nontemporal"]
             frameCount = self.animationData[0].mapCount
             self.slider.SetFrames(frameCount)
         elif self.temporalMode == TemporalMode.TEMPORAL:
-            self.frame.SetSlider('temporal')
-            self.slider = self.sliders['temporal']
+            self.frame.SetSlider("temporal")
+            self.slider = self.sliders["temporal"]
             self.slider.SetTemporalType(self.temporalManager.temporalType)
             self.slider.SetFrames(timeLabels)
         else:
@@ -354,34 +362,40 @@ class AnimationController(wx.EvtHandler):
                 if i not in activeIndices:
                     self.animations[i].SetActive(False)
                     continue
-                anim = [anim for anim in self.animationData
-                        if anim.windowIndex == i][0]
+                anim = [anim for anim in self.animationData if anim.windowIndex == i][0]
                 regions = anim.GetRegions()
                 self.animations[i].SetFrames(
-                    [HashCmds(cmdList, region) for cmdList,
-                     region in zip(anim.cmdMatrix, regions)])
+                    [
+                        HashCmds(cmdList, region)
+                        for cmdList, region in zip(anim.cmdMatrix, regions)
+                    ]
+                )
                 self.animations[i].SetActive(True)
         else:
             for i in range(len(self.animations)):
                 if i not in activeIndices:
                     self.animations[i].SetActive(False)
                     continue
-                anim = [anim for anim in self.animationData
-                        if anim.windowIndex == i][0]
+                anim = [anim for anim in self.animationData if anim.windowIndex == i][0]
                 regions = anim.GetRegions()
                 identifiers = sampleCmdMatrixAndCreateNames(
-                    anim.cmdMatrix, mapNamesDict[
-                        anim.firstStdsNameType[0]], regions)
+                    anim.cmdMatrix, mapNamesDict[anim.firstStdsNameType[0]], regions
+                )
                 self.animations[i].SetFrames(identifiers)
                 self.animations[i].SetActive(True)
 
     def _updateWindows(self, activeIndices):
         # add or remove window
         for windowIndex in range(len(self.animations)):
-            if not self.frame.IsWindowShown(
-                    windowIndex) and windowIndex in activeIndices:
+            if (
+                not self.frame.IsWindowShown(windowIndex)
+                and windowIndex in activeIndices
+            ):
                 self.frame.AddWindow(windowIndex)
-            elif self.frame.IsWindowShown(windowIndex) and windowIndex not in activeIndices:
+            elif (
+                self.frame.IsWindowShown(windowIndex)
+                and windowIndex not in activeIndices
+            ):
                 self.frame.RemoveWindow(windowIndex)
 
     def _updateBitmapData(self):
@@ -390,54 +404,43 @@ class AnimationController(wx.EvtHandler):
 
         # load new data
         for animData in self.animationData:
-            if animData.viewMode == '2d':
+            if animData.viewMode == "2d":
                 self._set2DData(animData)
             else:
                 self._load3DData(animData)
             self._loadLegend(animData)
-        color = UserSettings.Get(
-            group='animation',
-            key='bgcolor',
-            subkey='color')
-        cpus = UserSettings.Get(
-            group='animation',
-            key='nprocs',
-            subkey='value')
+        color = UserSettings.Get(group="animation", key="bgcolor", subkey="color")
+        cpus = UserSettings.Get(group="animation", key="nprocs", subkey="value")
         self.bitmapProvider.Load(nprocs=cpus, bgcolor=color)
         # clear pools
         self.bitmapPool.Clear()
         self.mapFilesPool.Clear()
 
     def _set2DData(self, animationData):
-        opacities = [
-            layer.opacity for layer in animationData.layerList
-            if layer.active]
-        #w, h = self.mapwindows[animationData.GetWindowIndex()].GetClientSize()
+        opacities = [layer.opacity for layer in animationData.layerList if layer.active]
+        # w, h = self.mapwindows[animationData.GetWindowIndex()].GetClientSize()
         regions = animationData.GetRegions()
-        self.bitmapProvider.SetCmds(
-            animationData.cmdMatrix, opacities, regions)
+        self.bitmapProvider.SetCmds(animationData.cmdMatrix, opacities, regions)
 
     def _load3DData(self, animationData):
         nviz = animationData.GetNvizCommands()
-        self.bitmapProvider.SetCmds3D(nviz['commands'], nviz['region'])
+        self.bitmapProvider.SetCmds3D(nviz["commands"], nviz["region"])
 
     def _loadLegend(self, animationData):
         if animationData.legendCmd:
             try:
-                bitmap = self.bitmapProvider.LoadOverlay(
-                    animationData.legendCmd)
+                bitmap = self.bitmapProvider.LoadOverlay(animationData.legendCmd)
                 try:
                     from PIL import Image  # noqa: F401
+
                     for param in animationData.legendCmd:
-                        if param.startswith('at'):
-                            b, t, l, r = param.split('=')[1].split(',')
-                            x, y = float(l) / 100., 1 - float(t) / 100.
+                        if param.startswith("at"):
+                            b, t, l, r = param.split("=")[1].split(",")
+                            x, y = float(l) / 100.0, 1 - float(t) / 100.0
                             break
                 except ImportError:
                     x, y = 0, 0
-                self.mapwindows[
-                    animationData.windowIndex].SetOverlay(
-                    bitmap, x, y)
+                self.mapwindows[animationData.windowIndex].SetOverlay(bitmap, x, y)
             except GException:
                 GError(message=_("Failed to display legend."))
         else:
@@ -451,8 +454,8 @@ class AnimationController(wx.EvtHandler):
         windowIndex = []
         for anim in animationData:
             for layer in anim.layerList:
-                if layer.active and hasattr(layer, 'maps'):
-                    if layer.mapType in ('strds', 'stvds', 'str3ds'):
+                if layer.active and hasattr(layer, "maps"):
+                    if layer.mapType in ("strds", "stvds", "str3ds"):
                         stds += 1
                     else:
                         maps += 1
@@ -471,7 +474,8 @@ class AnimationController(wx.EvtHandler):
         if temporalMode == TemporalMode.NONTEMPORAL:
             if len(mapCount) > 1:
                 raise GException(
-                    _("Inconsistent number of maps, please check input data."))
+                    _("Inconsistent number of maps, please check input data.")
+                )
         elif temporalMode == TemporalMode.TEMPORAL:
             tempManager = TemporalManager()
             # these raise GException:
@@ -487,14 +491,8 @@ class AnimationController(wx.EvtHandler):
     def Reload(self):
         self.EndAnimation()
 
-        color = UserSettings.Get(
-            group='animation',
-            key='bgcolor',
-            subkey='color')
-        cpus = UserSettings.Get(
-            group='animation',
-            key='nprocs',
-            subkey='value')
+        color = UserSettings.Get(group="animation", key="bgcolor", subkey="color")
+        cpus = UserSettings.Get(group="animation", key="nprocs", subkey="value")
         self.bitmapProvider.Load(nprocs=cpus, bgcolor=color, force=True)
 
         self.EndAnimation()
@@ -504,15 +502,16 @@ class AnimationController(wx.EvtHandler):
             GMessage(parent=self.frame, message=_("No animation to export."))
             return
 
-        if 'export' in self._dialogs:
-            self._dialogs['export'].Show()
-            self._dialogs['export'].Raise()
+        if "export" in self._dialogs:
+            self._dialogs["export"].Show()
+            self._dialogs["export"].Raise()
         else:
-            dlg = ExportDialog(self.frame, temporal=self.temporalMode,
-                               timeTick=self.timeTick)
+            dlg = ExportDialog(
+                self.frame, temporal=self.temporalMode, timeTick=self.timeTick
+            )
             dlg.CenterOnParent()
             dlg.doExport.connect(self._export)
-            self._dialogs['export'] = dlg
+            self._dialogs["export"] = dlg
             dlg.Show()
 
     def _export(self, exportInfo, decorations):
@@ -521,8 +520,7 @@ class AnimationController(wx.EvtHandler):
             timeLabels, mapNamesDict = self.temporalManager.GetLabelsAndMaps()
             frameCount = len(timeLabels)
         else:
-            frameCount = self.animationData[
-                0].mapCount  # should be the same for all
+            frameCount = self.animationData[0].mapCount  # should be the same for all
 
         animWinSize = []
         animWinPos = []
@@ -537,27 +535,20 @@ class AnimationController(wx.EvtHandler):
                 animWinIndex.append(i)
 
         images = []
-        busy = wx.BusyInfo(
-            _("Preparing export, please wait..."),
-            parent=self.frame)
+        busy = wx.BusyInfo(_("Preparing export, please wait..."), parent=self.frame)
         wx.GetApp().Yield()
         lastBitmaps = {}
-        fgcolor = UserSettings.Get(
-            group='animation',
-            key='font',
-            subkey='fgcolor')
-        bgcolor = UserSettings.Get(
-            group='animation',
-            key='font',
-            subkey='bgcolor')
+        fgcolor = UserSettings.Get(group="animation", key="font", subkey="fgcolor")
+        bgcolor = UserSettings.Get(group="animation", key="font", subkey="bgcolor")
         for frameIndex in range(frameCount):
             image = EmptyImage(*size)
             image.Replace(0, 0, 0, 255, 255, 255)
             # collect bitmaps of all windows and paste them into the one
             for i in animWinIndex:
                 frameId = self.animations[i].GetFrame(frameIndex)
-                if not UserSettings.Get(group='animation', key='temporal',
-                                        subkey=['nodata', 'enable']):
+                if not UserSettings.Get(
+                    group="animation", key="temporal", subkey=["nodata", "enable"]
+                ):
                     if frameId is not None:
                         bitmap = self.bitmapProvider.GetBitmap(frameId)
                         lastBitmaps[i] = bitmap
@@ -587,31 +578,38 @@ class AnimationController(wx.EvtHandler):
             # paste decorations
             for decoration in decorations:
                 # add image
-                x = decoration['pos'][0] / 100. * size[0]
-                y = decoration['pos'][1] / 100. * size[1]
-                if decoration['name'] == 'image':
-                    decImage = wx.Image(decoration['file'])
-                elif decoration['name'] == 'time':
+                x = decoration["pos"][0] / 100.0 * size[0]
+                y = decoration["pos"][1] / 100.0 * size[1]
+                if decoration["name"] == "image":
+                    decImage = wx.Image(decoration["file"])
+                elif decoration["name"] == "time":
                     timeLabel = timeLabels[frameIndex]
                     if timeLabel[1]:  # interval
                         text = _("%(from)s %(dash)s %(to)s") % {
-                            'from': timeLabel[0],
-                            'dash': u"\u2013", 'to': timeLabel[1]}
+                            "from": timeLabel[0],
+                            "dash": "\u2013",
+                            "to": timeLabel[1],
+                        }
                     else:
-                        if self.temporalManager.GetTemporalType() == TemporalType.ABSOLUTE:
+                        if (
+                            self.temporalManager.GetTemporalType()
+                            == TemporalType.ABSOLUTE
+                        ):
                             text = timeLabel[0]
                         else:
-                            text = _("%(start)s %(unit)s") % \
-                                {'start': timeLabel[0], 'unit': timeLabel[2]}
+                            text = _("%(start)s %(unit)s") % {
+                                "start": timeLabel[0],
+                                "unit": timeLabel[2],
+                            }
 
                     decImage = RenderText(
-                        text, decoration['font'],
-                        bgcolor, fgcolor).ConvertToImage()
-                elif decoration['name'] == 'text':
-                    text = decoration['text']
+                        text, decoration["font"], bgcolor, fgcolor
+                    ).ConvertToImage()
+                elif decoration["name"] == "text":
+                    text = decoration["text"]
                     decImage = RenderText(
-                        text, decoration['font'],
-                        bgcolor, fgcolor).ConvertToImage()
+                        text, decoration["font"], bgcolor, fgcolor
+                    ).ConvertToImage()
 
                 image.Paste(decImage, x, y)
 
@@ -620,10 +618,12 @@ class AnimationController(wx.EvtHandler):
 
         # export
         pilImages = [WxImageToPil(image) for image in images]
-        self.busy = wx.BusyInfo(_("Exporting animation, please wait..."),
-                                parent=self.frame)
+        self.busy = wx.BusyInfo(
+            _("Exporting animation, please wait..."), parent=self.frame
+        )
         wx.GetApp().Yield()
         try:
+
             def export_avi_callback(event):
                 error = event.ret
                 del self.busy
@@ -631,33 +631,41 @@ class AnimationController(wx.EvtHandler):
                     GError(parent=self.frame, message=error)
                     return
 
-            if exportInfo['method'] == 'sequence':
+            if exportInfo["method"] == "sequence":
                 filename = os.path.join(
-                    exportInfo['directory'],
-                    exportInfo['prefix'] +
-                    '.' +
-                    exportInfo['format'].lower())
+                    exportInfo["directory"],
+                    exportInfo["prefix"] + "." + exportInfo["format"].lower(),
+                )
                 writeIms(filename=filename, images=pilImages)
-            elif exportInfo['method'] == 'gif':
-                writeGif(filename=exportInfo['file'], images=pilImages,
-                         duration=self.timeTick / float(1000), repeat=True)
-            elif exportInfo['method'] == 'swf':
-                writeSwf(filename=exportInfo['file'], images=pilImages,
-                         duration=self.timeTick / float(1000), repeat=True)
-            elif exportInfo['method'] == 'avi':
+            elif exportInfo["method"] == "gif":
+                writeGif(
+                    filename=exportInfo["file"],
+                    images=pilImages,
+                    duration=self.timeTick / float(1000),
+                    repeat=True,
+                )
+            elif exportInfo["method"] == "swf":
+                writeSwf(
+                    filename=exportInfo["file"],
+                    images=pilImages,
+                    duration=self.timeTick / float(1000),
+                    repeat=True,
+                )
+            elif exportInfo["method"] == "avi":
                 thread = gThread()
-                thread.Run(callable=writeAvi,
-                           filename=exportInfo['file'],
-                           images=pilImages,
-                           duration=self.timeTick / float(1000),
-                           encoding=exportInfo['encoding'],
-                           inputOptions=exportInfo['options'],
-                           bg_task=True,
-                           ondone=export_avi_callback,
-                           )
+                thread.Run(
+                    callable=writeAvi,
+                    filename=exportInfo["file"],
+                    images=pilImages,
+                    duration=self.timeTick / float(1000),
+                    encoding=exportInfo["encoding"],
+                    inputOptions=exportInfo["options"],
+                    bg_task=True,
+                    ondone=export_avi_callback,
+                )
         except Exception as e:
             del self.busy
             GError(parent=self.frame, message=str(e))
             return
-        if exportInfo['method'] in ('sequence', 'gif', 'swf'):
+        if exportInfo["method"] in ("sequence", "gif", "swf"):
             del self.busy

+ 61 - 59
gui/wxpython/animation/data.py

@@ -23,13 +23,17 @@ from grass.script import core as gcore
 
 from core.gcmd import GException
 from animation.nviztask import NvizTask
-from animation.utils import validateMapNames, getRegisteredMaps, \
-    checkSeriesCompatibility, validateTimeseriesName, interpolate
+from animation.utils import (
+    validateMapNames,
+    getRegisteredMaps,
+    checkSeriesCompatibility,
+    validateTimeseriesName,
+    interpolate,
+)
 from core.layerlist import LayerList, Layer
 
 
 class AnimationData(object):
-
     def __init__(self):
         self._name = None
         self._windowIndex = 0
@@ -39,9 +43,8 @@ class AnimationData(object):
         self._firstStdsNameType = None
         self._mapCount = None
         self._cmdMatrix = None
-        self._viewModes = [('2d', _("2D view")),
-                           ('3d', _("3D view"))]
-        self.viewMode = '2d'
+        self._viewModes = [("2d", _("2D view")), ("3d", _("3D view"))]
+        self.viewMode = "2d"
 
         self.nvizTask = NvizTask()
         self._nvizParameters = self.nvizTask.ListMapParameters()
@@ -59,7 +62,7 @@ class AnimationData(object):
         return self._name
 
     def SetName(self, name):
-        if name == '':
+        if name == "":
             raise ValueError(_("No animation name selected."))
         self._name = name
 
@@ -80,8 +83,8 @@ class AnimationData(object):
         mapSeriesList = []
         timeseriesList = []
         for layer in layerList:
-            if layer.active and hasattr(layer, 'maps'):
-                if layer.mapType in ('strds', 'stvds', 'str3ds'):
+            if layer.active and hasattr(layer, "maps"):
+                if layer.mapType in ("strds", "stvds", "str3ds"):
                     timeseriesList.append((layer.name, layer.mapType))
                     self._firstStdsNameType = layer.name, layer.mapType
                 else:
@@ -89,8 +92,9 @@ class AnimationData(object):
         if not timeseriesList:
             self._firstStdsNameType = None, None
         # this throws GException
-        count = checkSeriesCompatibility(mapSeriesList=mapSeriesList,
-                                         timeseriesList=timeseriesList)
+        count = checkSeriesCompatibility(
+            mapSeriesList=mapSeriesList, timeseriesList=timeseriesList
+        )
         self._mapCount = count
         self._layerList = layerList
 
@@ -125,7 +129,7 @@ class AnimationData(object):
             self._workspaceFile = None
             return
 
-        if fileName == '':
+        if fileName == "":
             raise ValueError(_("No workspace file selected."))
 
         if not os.path.exists(fileName):
@@ -179,11 +183,12 @@ class AnimationData(object):
         if not self.workspaceFile or not self._layerList:
             return []
 
-        cmds = self.nvizTask.GetCommandSeries(layerList=self._layerList,
-                                              paramName=self.nvizParameter)
+        cmds = self.nvizTask.GetCommandSeries(
+            layerList=self._layerList, paramName=self.nvizParameter
+        )
         region = self.nvizTask.GetRegion()
 
-        return {'commands': cmds, 'region': region}
+        return {"commands": cmds, "region": region}
 
     def SetStartRegion(self, region):
         self._startRegion = region
@@ -207,34 +212,33 @@ class AnimationData(object):
     def GetZoomRegionValue(self):
         return self._zoomRegionValue
 
-    zoomRegionValue = property(
-        fset=SetZoomRegionValue,
-        fget=GetZoomRegionValue)
+    zoomRegionValue = property(fset=SetZoomRegionValue, fget=GetZoomRegionValue)
 
     def GetRegions(self):
-        self._computeRegions(self._mapCount, self._startRegion,
-                             self._endRegion, self._zoomRegionValue)
+        self._computeRegions(
+            self._mapCount, self._startRegion, self._endRegion, self._zoomRegionValue
+        )
         return self._regions
 
-    def _computeRegions(
-            self, count, startRegion, endRegion=None,
-            zoomValue=None):
+    def _computeRegions(self, count, startRegion, endRegion=None, zoomValue=None):
         """Computes regions based on start region and end region or zoom value
         for each of the animation frames."""
         region = dict(gcore.region())  # cast to dict, otherwise deepcopy error
         if startRegion:
-            region = dict(parse_key_val(gcore.read_command('g.region',
-                                                           flags='gu',
-                                                           region=startRegion),
-                                        val_type=float))
-
-        del region['cells']
-        del region['cols']
-        del region['rows']
-        if 'projection' in region:
-            del region['projection']
-        if 'zone' in region:
-            del region['zone']
+            region = dict(
+                parse_key_val(
+                    gcore.read_command("g.region", flags="gu", region=startRegion),
+                    val_type=float,
+                )
+            )
+
+        del region["cells"]
+        del region["cols"]
+        del region["rows"]
+        if "projection" in region:
+            del region["projection"]
+        if "zone" in region:
+            del region["zone"]
         regions = []
         for i in range(self._mapCount):
             regions.append(copy.copy(region))
@@ -243,36 +247,33 @@ class AnimationData(object):
             return
 
         startRegionDict = parse_key_val(
-            gcore.read_command(
-                'g.region',
-                flags='gu',
-                region=startRegion),
-            val_type=float)
+            gcore.read_command("g.region", flags="gu", region=startRegion),
+            val_type=float,
+        )
         if endRegion:
             endRegionDict = parse_key_val(
-                gcore.read_command(
-                    'g.region',
-                    flags='gu',
-                    region=endRegion),
-                val_type=float)
-            for key in ('n', 's', 'e', 'w', 'nsres', 'ewres'):
+                gcore.read_command("g.region", flags="gu", region=endRegion),
+                val_type=float,
+            )
+            for key in ("n", "s", "e", "w", "nsres", "ewres"):
                 values = interpolate(
-                    startRegionDict[key],
-                    endRegionDict[key],
-                    self._mapCount)
+                    startRegionDict[key], endRegionDict[key], self._mapCount
+                )
                 for value, region in zip(values, regions):
                     region[key] = value
 
         elif zoomValue:
             for i in range(self._mapCount):
-                regions[i]['n'] -= zoomValue[0] * i
-                regions[i]['e'] -= zoomValue[1] * i
-                regions[i]['s'] += zoomValue[0] * i
-                regions[i]['w'] += zoomValue[1] * i
+                regions[i]["n"] -= zoomValue[0] * i
+                regions[i]["e"] -= zoomValue[1] * i
+                regions[i]["s"] += zoomValue[0] * i
+                regions[i]["w"] += zoomValue[1] * i
 
                 # handle cases when north < south and similarly EW
-                if regions[i]['n'] < regions[i]['s'] or \
-                   regions[i]['e'] < regions[i]['w']:
+                if (
+                    regions[i]["n"] < regions[i]["s"]
+                    or regions[i]["e"] < regions[i]["w"]
+                ):
                     regions[i] = regions[i - 1]
 
         self._regions = regions
@@ -287,22 +288,23 @@ class AnimLayer(Layer):
 
     def __init__(self):
         Layer.__init__(self)
-        self._mapTypes.extend(['strds', 'stvds', 'str3ds'])
+        self._mapTypes.extend(["strds", "stvds", "str3ds"])
         self._maps = []
 
     def SetName(self, name):
         if not self.hidden:
             if self._mapType is None:
                 raise ValueError(
-                    "To set layer name, the type of layer must be specified.")
-            if self._mapType in ('strds', 'stvds', 'str3ds'):
+                    "To set layer name, the type of layer must be specified."
+                )
+            if self._mapType in ("strds", "stvds", "str3ds"):
                 try:
                     name = validateTimeseriesName(name, self._mapType)
                     self._maps = getRegisteredMaps(name, self._mapType)
                 except (GException, gcore.ScriptError) as e:
                     raise ValueError(str(e))
             else:
-                self._maps = validateMapNames(name.split(','), self._mapType)
+                self._maps = validateMapNames(name.split(","), self._mapType)
         self._name = name
         self.label = name
 

File diff ditekan karena terlalu besar
+ 633 - 720
gui/wxpython/animation/dialogs.py


+ 163 - 127
gui/wxpython/animation/frame.py

@@ -31,8 +31,7 @@ from gui_core.wrap import StaticText, TextCtrl
 from core.gcmd import RunCommand, GWarning
 
 from animation.mapwindow import AnimationWindow
-from animation.provider import BitmapProvider, BitmapPool, \
-    MapFilesPool, CleanUp
+from animation.provider import BitmapProvider, BitmapPool, MapFilesPool, CleanUp
 from animation.controller import AnimationController
 from animation.anim import Animation
 from animation.toolbars import MainToolbar, AnimationToolbar, MiscToolbar
@@ -47,21 +46,21 @@ gcore.set_raise_on_error(True)
 
 
 class AnimationFrame(wx.Frame):
-
-    def __init__(self, parent, giface, title=_("Animation Tool"),
-                 rasters=None, timeseries=None):
-        wx.Frame.__init__(self, parent, title=title,
-                          style=wx.DEFAULT_FRAME_STYLE, size=(800, 600))
+    def __init__(
+        self, parent, giface, title=_("Animation Tool"), rasters=None, timeseries=None
+    ):
+        wx.Frame.__init__(
+            self, parent, title=title, style=wx.DEFAULT_FRAME_STYLE, size=(800, 600)
+        )
         self._giface = giface
         self.SetClientSize(self.GetSize())
         self.iconsize = (16, 16)
 
         self.SetIcon(
             wx.Icon(
-                os.path.join(
-                    globalvar.ICONDIR,
-                    'grass_map.ico'),
-                wx.BITMAP_TYPE_ICO))
+                os.path.join(globalvar.ICONDIR, "grass_map.ico"), wx.BITMAP_TYPE_ICO
+            )
+        )
 
         # Make sure the temporal database exists
         try:
@@ -77,7 +76,8 @@ class AnimationFrame(wx.Frame):
         self.animations = [Animation() for i in range(MAX_COUNT)]
         self.windows = []
         self.animationPanel = AnimationsPanel(
-            self, self.windows, initialCount=MAX_COUNT)
+            self, self.windows, initialCount=MAX_COUNT
+        )
         bitmapPool = BitmapPool()
         mapFilesPool = MapFilesPool()
 
@@ -85,27 +85,27 @@ class AnimationFrame(wx.Frame):
         self._progressDlgMax = None
 
         self.provider = BitmapProvider(
+            bitmapPool=bitmapPool, mapFilesPool=mapFilesPool, tempDir=TMP_DIR
+        )
+        self.animationSliders = {}
+        self.animationSliders["nontemporal"] = SimpleAnimationSlider(self)
+        self.animationSliders["temporal"] = TimeAnimationSlider(self)
+        self.controller = AnimationController(
+            frame=self,
+            sliders=self.animationSliders,
+            animations=self.animations,
+            mapwindows=self.windows,
+            provider=self.provider,
             bitmapPool=bitmapPool,
             mapFilesPool=mapFilesPool,
-            tempDir=TMP_DIR)
-        self.animationSliders = {}
-        self.animationSliders['nontemporal'] = SimpleAnimationSlider(self)
-        self.animationSliders['temporal'] = TimeAnimationSlider(self)
-        self.controller = AnimationController(frame=self,
-                                              sliders=self.animationSliders,
-                                              animations=self.animations,
-                                              mapwindows=self.windows,
-                                              provider=self.provider,
-                                              bitmapPool=bitmapPool,
-                                              mapFilesPool=mapFilesPool)
+        )
         for win in self.windows:
             win.Bind(wx.EVT_SIZE, self.FrameSizeChanged)
-            self.provider.mapsLoaded.connect(lambda: self.SetStatusText(''))
+            self.provider.mapsLoaded.connect(lambda: self.SetStatusText(""))
             self.provider.renderingStarted.connect(self._showRenderingProgress)
             self.provider.renderingContinues.connect(self._updateProgress)
             self.provider.renderingFinished.connect(self._closeProgress)
-            self.provider.compositionStarted.connect(
-                self._showRenderingProgress)
+            self.provider.compositionStarted.connect(self._showRenderingProgress)
             self.provider.compositionContinues.connect(self._updateProgress)
             self.provider.compositionFinished.connect(self._closeProgress)
 
@@ -119,8 +119,8 @@ class AnimationFrame(wx.Frame):
         self._mgr.Update()
 
         self.dialogs = dict()
-        self.dialogs['speed'] = None
-        self.dialogs['preferences'] = None
+        self.dialogs["speed"] = None
+        self.dialogs["preferences"] = None
 
         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
 
@@ -129,19 +129,34 @@ class AnimationFrame(wx.Frame):
         self.CreateStatusBar(number=1, style=0)
 
     def _addPanes(self):
-        self._mgr.AddPane(self.animationPanel,
-                          wx.aui.AuiPaneInfo().CentrePane().
-                          Name('animPanel').CentrePane().CaptionVisible(False).PaneBorder(False).
-                          Floatable(False).BestSize((-1, -1)).
-                          CloseButton(False).DestroyOnClose(True).Layer(0))
+        self._mgr.AddPane(
+            self.animationPanel,
+            wx.aui.AuiPaneInfo()
+            .CentrePane()
+            .Name("animPanel")
+            .CentrePane()
+            .CaptionVisible(False)
+            .PaneBorder(False)
+            .Floatable(False)
+            .BestSize((-1, -1))
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Layer(0),
+        )
         for name, slider in six.iteritems(self.animationSliders):
             self._mgr.AddPane(
                 slider,
-                wx.aui.AuiPaneInfo().PaneBorder(False).Name(
-                    'slider_' +
-                    name). Layer(1).CaptionVisible(False).BestSize(
-                    slider.GetBestSize()). DestroyOnClose(True).CloseButton(False).Bottom())
-            self._mgr.GetPane('slider_' + name).Hide()
+                wx.aui.AuiPaneInfo()
+                .PaneBorder(False)
+                .Name("slider_" + name)
+                .Layer(1)
+                .CaptionVisible(False)
+                .BestSize(slider.GetBestSize())
+                .DestroyOnClose(True)
+                .CloseButton(False)
+                .Bottom(),
+            )
+            self._mgr.GetPane("slider_" + name).Hide()
 
     def _addToolbars(self):
         """Add toolbars to the window
@@ -152,36 +167,62 @@ class AnimationFrame(wx.Frame):
          - 'miscToolbar'          - help, close
         """
         self.toolbars["mainToolbar"] = MainToolbar(self)
-        self._mgr.AddPane(self.toolbars["mainToolbar"],
-                          wx.aui.AuiPaneInfo().
-                          Name('mainToolbar').Caption(_("Main Toolbar")).
-                          ToolbarPane().Top().
-                          LeftDockable(False).RightDockable(False).
-                          BottomDockable(True).TopDockable(True).
-                          CloseButton(False).Layer(2).Row(1).Position(0).
-                          BestSize((self.toolbars['mainToolbar'].GetBestSize())))
-
-        self.toolbars['animationToolbar'] = AnimationToolbar(self)
-        self._mgr.AddPane(self.toolbars['animationToolbar'],
-                          wx.aui.AuiPaneInfo().
-                          Name('animationToolbar').Caption(_("Animation Toolbar")).
-                          ToolbarPane().Top().
-                          LeftDockable(False).RightDockable(False).
-                          BottomDockable(True).TopDockable(True).
-                          CloseButton(False).Layer(2).Row(1).Position(1).
-                          BestSize((self.toolbars['animationToolbar'].GetBestSize())))
-        self.controller.SetAnimationToolbar(
-            self.toolbars['animationToolbar'])
-
-        self.toolbars['miscToolbar'] = MiscToolbar(self)
-        self._mgr.AddPane(self.toolbars['miscToolbar'],
-                          wx.aui.AuiPaneInfo().
-                          Name('miscToolbar').Caption(_("Misc Toolbar")).
-                          ToolbarPane().Top().
-                          LeftDockable(False).RightDockable(False).
-                          BottomDockable(True).TopDockable(True).
-                          CloseButton(False).Layer(2).Row(1).Position(2).
-                          BestSize((self.toolbars['miscToolbar'].GetBestSize())))
+        self._mgr.AddPane(
+            self.toolbars["mainToolbar"],
+            wx.aui.AuiPaneInfo()
+            .Name("mainToolbar")
+            .Caption(_("Main Toolbar"))
+            .ToolbarPane()
+            .Top()
+            .LeftDockable(False)
+            .RightDockable(False)
+            .BottomDockable(True)
+            .TopDockable(True)
+            .CloseButton(False)
+            .Layer(2)
+            .Row(1)
+            .Position(0)
+            .BestSize((self.toolbars["mainToolbar"].GetBestSize())),
+        )
+
+        self.toolbars["animationToolbar"] = AnimationToolbar(self)
+        self._mgr.AddPane(
+            self.toolbars["animationToolbar"],
+            wx.aui.AuiPaneInfo()
+            .Name("animationToolbar")
+            .Caption(_("Animation Toolbar"))
+            .ToolbarPane()
+            .Top()
+            .LeftDockable(False)
+            .RightDockable(False)
+            .BottomDockable(True)
+            .TopDockable(True)
+            .CloseButton(False)
+            .Layer(2)
+            .Row(1)
+            .Position(1)
+            .BestSize((self.toolbars["animationToolbar"].GetBestSize())),
+        )
+        self.controller.SetAnimationToolbar(self.toolbars["animationToolbar"])
+
+        self.toolbars["miscToolbar"] = MiscToolbar(self)
+        self._mgr.AddPane(
+            self.toolbars["miscToolbar"],
+            wx.aui.AuiPaneInfo()
+            .Name("miscToolbar")
+            .Caption(_("Misc Toolbar"))
+            .ToolbarPane()
+            .Top()
+            .LeftDockable(False)
+            .RightDockable(False)
+            .BottomDockable(True)
+            .TopDockable(True)
+            .CloseButton(False)
+            .Layer(2)
+            .Row(1)
+            .Position(2)
+            .BestSize((self.toolbars["miscToolbar"].GetBestSize())),
+        )
 
     def SetAnimations(self, layerLists):
         """Set animation data
@@ -206,16 +247,16 @@ class AnimationFrame(wx.Frame):
         self.controller.EditAnimations()
 
     def SetSlider(self, name):
-        if name == 'nontemporal':
-            self._mgr.GetPane('slider_nontemporal').Show()
-            self._mgr.GetPane('slider_temporal').Hide()
+        if name == "nontemporal":
+            self._mgr.GetPane("slider_nontemporal").Show()
+            self._mgr.GetPane("slider_temporal").Hide()
 
-        elif name == 'temporal':
-            self._mgr.GetPane('slider_temporal').Show()
-            self._mgr.GetPane('slider_nontemporal').Hide()
+        elif name == "temporal":
+            self._mgr.GetPane("slider_temporal").Show()
+            self._mgr.GetPane("slider_nontemporal").Hide()
         else:
-            self._mgr.GetPane('slider_temporal').Hide()
-            self._mgr.GetPane('slider_nontemporal').Hide()
+            self._mgr.GetPane("slider_temporal").Hide()
+            self._mgr.GetPane("slider_nontemporal").Hide()
         self._mgr.Update()
 
     def OnPlayForward(self, event):
@@ -247,7 +288,7 @@ class AnimationFrame(wx.Frame):
         self.controller.SetReplayMode(mode)
 
     def OnAdjustSpeed(self, event):
-        win = self.dialogs['speed']
+        win = self.dialogs["speed"]
         if win:
             win.SetTemporalMode(self.controller.GetTemporalMode())
             win.SetTimeGranularity(self.controller.GetTimeGranularity())
@@ -258,11 +299,13 @@ class AnimationFrame(wx.Frame):
                 win.Show()
         else:  # start
             win = SpeedDialog(
-                self, temporalMode=self.controller.GetTemporalMode(),
+                self,
+                temporalMode=self.controller.GetTemporalMode(),
                 timeGranularity=self.controller.GetTimeGranularity(),
-                initialSpeed=self.controller.timeTick)
+                initialSpeed=self.controller.timeTick,
+            )
             win.CenterOnParent()
-            self.dialogs['speed'] = win
+            self.dialogs["speed"] = win
             win.speedChanged.connect(self.ChangeSpeed)
             win.Show()
 
@@ -280,12 +323,12 @@ class AnimationFrame(wx.Frame):
             message="Loading data started, please be patient.",
             maximum=count,
             parent=self,
-            style=wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_SMOOTH)
+            style=wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_SMOOTH,
+        )
         self._progressDlgMax = count
 
     def _updateProgress(self, current, text):
-        text += _(" ({c} out of {p})").format(c=current,
-                                              p=self._progressDlgMax)
+        text += _(" ({c} out of {p})").format(c=current, p=self._progressDlgMax)
         keepGoing, skip = self._progressDlg.Update(current, text)
         if not keepGoing:
             self.provider.RequestStopRendering()
@@ -307,19 +350,16 @@ class AnimationFrame(wx.Frame):
         event.Skip()
 
     def OnPreferences(self, event):
-        if not self.dialogs['preferences']:
+        if not self.dialogs["preferences"]:
             dlg = PreferencesDialog(parent=self, giface=self._giface)
-            self.dialogs['preferences'] = dlg
-            dlg.formatChanged.connect(
-                lambda: self.controller.UpdateAnimations())
+            self.dialogs["preferences"] = dlg
+            dlg.formatChanged.connect(lambda: self.controller.UpdateAnimations())
             dlg.CenterOnParent()
 
-        self.dialogs['preferences'].Show()
+        self.dialogs["preferences"].Show()
 
     def OnHelp(self, event):
-        RunCommand('g.manual',
-                   quiet=True,
-                   entry='wxGUI.animation')
+        RunCommand("g.manual", quiet=True, entry="wxGUI.animation")
 
     def OnCloseWindow(self, event):
         if self.controller.timer.IsRunning():
@@ -330,14 +370,13 @@ class AnimationFrame(wx.Frame):
 
     def __del__(self):
         """It might not be called, therefore we try to clean it all in OnCloseWindow."""
-        if hasattr(self, 'controller') and hasattr(self.controller, 'timer'):
+        if hasattr(self, "controller") and hasattr(self.controller, "timer"):
             if self.controller.timer.IsRunning():
                 self.controller.timer.Stop()
         CleanUp(TMP_DIR)()
 
 
 class AnimationsPanel(wx.Panel):
-
     def __init__(self, parent, windows, initialCount=4):
         wx.Panel.__init__(self, parent, id=wx.ID_ANY, style=wx.NO_BORDER)
         self.shown = []
@@ -378,14 +417,17 @@ class AnimationsPanel(wx.Panel):
 
 
 class AnimationSliderBase(wx.Panel):
-
     def __init__(self, parent):
         wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
         self.label1 = StaticText(self, id=wx.ID_ANY)
         self.slider = wx.Slider(self, id=wx.ID_ANY, style=wx.SL_HORIZONTAL)
-        self.indexField = TextCtrl(self, id=wx.ID_ANY, size=(40, -1),
-                                   style=wx.TE_PROCESS_ENTER | wx.TE_RIGHT,
-                                   validator=IntegerValidator())
+        self.indexField = TextCtrl(
+            self,
+            id=wx.ID_ANY,
+            size=(40, -1),
+            style=wx.TE_PROCESS_ENTER | wx.TE_RIGHT,
+            validator=IntegerValidator(),
+        )
 
         self.callbackSliderChanging = None
         self.callbackSliderChanged = None
@@ -467,7 +509,6 @@ class AnimationSliderBase(wx.Panel):
 
 
 class SimpleAnimationSlider(AnimationSliderBase):
-
     def __init__(self, parent):
         AnimationSliderBase.__init__(self, parent)
 
@@ -476,15 +517,16 @@ class SimpleAnimationSlider(AnimationSliderBase):
 
     def _doLayout(self):
         hbox = wx.BoxSizer(wx.HORIZONTAL)
-        hbox.Add(self.indexField, proportion=0,
-                 flag=wx.ALIGN_CENTER | wx.LEFT, border=5)
-        hbox.Add(self.label1, proportion=0,
-                 flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border=5)
         hbox.Add(
-            self.slider,
-            proportion=1,
-            flag=wx.EXPAND,
-            border=0)
+            self.indexField, proportion=0, flag=wx.ALIGN_CENTER | wx.LEFT, border=5
+        )
+        hbox.Add(
+            self.label1,
+            proportion=0,
+            flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
+            border=5,
+        )
+        hbox.Add(self.slider, proportion=1, flag=wx.EXPAND, border=0)
         self.SetSizerAndFit(hbox)
 
     def _setFrames(self, count):
@@ -497,7 +539,7 @@ class SimpleAnimationSlider(AnimationSliderBase):
         self._setLabel()
 
     def _setLabel(self):
-        label = "/ %(framesCount)s" % {'framesCount': self.framesCount}
+        label = "/ %(framesCount)s" % {"framesCount": self.framesCount}
         self.label1.SetLabel(label)
         self.Layout()
 
@@ -506,7 +548,6 @@ class SimpleAnimationSlider(AnimationSliderBase):
 
 
 class TimeAnimationSlider(AnimationSliderBase):
-
     def __init__(self, parent):
         AnimationSliderBase.__init__(self, parent)
         self.timeLabels = []
@@ -521,24 +562,18 @@ class TimeAnimationSlider(AnimationSliderBase):
     def _doLayout(self):
         vbox = wx.BoxSizer(wx.VERTICAL)
         hbox = wx.BoxSizer(wx.HORIZONTAL)
-        hbox.Add(self.label1, proportion=0,
-                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
+        hbox.Add(self.label1, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL, border=0)
         hbox.AddStretchSpacer()
-        hbox.Add(self.indexField, proportion=0,
-                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
-        hbox.Add(self.label2, proportion=0,
-                 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3)
+        hbox.Add(self.indexField, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL, border=0)
+        hbox.Add(
+            self.label2, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3
+        )
         hbox.AddStretchSpacer()
-        hbox.Add(self.label3, proportion=0,
-                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
+        hbox.Add(self.label3, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL, border=0)
         vbox.Add(hbox, proportion=0, flag=wx.EXPAND, border=0)
 
         hbox = wx.BoxSizer(wx.HORIZONTAL)
-        hbox.Add(
-            self.slider,
-            proportion=1,
-            flag=wx.EXPAND,
-            border=0)
+        hbox.Add(self.slider, proportion=1, flag=wx.EXPAND, border=0)
         vbox.Add(hbox, proportion=0, flag=wx.EXPAND, border=0)
 
         self._setTemporalType()
@@ -584,7 +619,7 @@ class TimeAnimationSlider(AnimationSliderBase):
                     end = self.timeLabels[-1][1]
                 else:
                     end = self.timeLabels[-1][0]
-                end = "%(end)s %(unit)s" % {'end': end, 'unit': unit}
+                end = "%(end)s %(unit)s" % {"end": end, "unit": unit}
                 self.label3.SetLabel(end)
 
             self.label2Length = len(start)
@@ -601,16 +636,17 @@ class TimeAnimationSlider(AnimationSliderBase):
         if self.timeLabels[index][1]:  # interval
             if self.temporalType == TemporalType.ABSOLUTE:
                 label = _("%(from)s %(dash)s %(to)s") % {
-                    'from': start,
-                    'dash': u"\u2013",
-                    'to': self.timeLabels[index][1]}
+                    "from": start,
+                    "dash": "\u2013",
+                    "to": self.timeLabels[index][1],
+                }
             else:
-                label = _("to %(to)s") % {'to': self.timeLabels[index][1]}
+                label = _("to %(to)s") % {"to": self.timeLabels[index][1]}
         else:
             if self.temporalType == TemporalType.ABSOLUTE:
                 label = start
             else:
-                label = ''
+                label = ""
         self.label2.SetLabel(label)
         if self.temporalType == TemporalType.RELATIVE:
             self.indexField.SetValue(start)

+ 20 - 17
gui/wxpython/animation/g.gui.animation.py

@@ -64,6 +64,7 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.giface import StandaloneGrassInterface
@@ -71,10 +72,10 @@ def main():
     from animation.frame import AnimationFrame, MAX_COUNT
     from animation.data import AnimLayer
 
-    rast = options['raster']
-    vect = options['vector']
-    strds = options['strds']
-    stvds = options['stvds']
+    rast = options["raster"]
+    vect = options["vector"]
+    strds = options["strds"]
+    stvds = options["stvds"]
 
     numInputs = 0
 
@@ -88,8 +89,10 @@ def main():
         numInputs += 1
 
     if numInputs > 1:
-        gscript.fatal(_("%s=, %s=, %s= and %s= are mutually exclusive.") %
-                       ("raster", "vector", "strds", "stvds"))
+        gscript.fatal(
+            _("%s=, %s=, %s= and %s= are mutually exclusive.")
+            % ("raster", "vector", "strds", "stvds")
+        )
 
     if numInputs > 0:
         # We need to initialize the temporal framework in case
@@ -103,27 +106,27 @@ def main():
     layerList = LayerList()
     if rast:
         layer = AnimLayer()
-        layer.mapType = 'raster'
+        layer.mapType = "raster"
         layer.name = rast
-        layer.cmd = ['d.rast', 'map={name}'.format(name=rast.split(',')[0])]
+        layer.cmd = ["d.rast", "map={name}".format(name=rast.split(",")[0])]
         layerList.AddLayer(layer)
     if vect:
         layer = AnimLayer()
-        layer.mapType = 'vector'
+        layer.mapType = "vector"
         layer.name = vect
-        layer.cmd = ['d.vect', 'map={name}'.format(name=vect.split(',')[0])]
+        layer.cmd = ["d.vect", "map={name}".format(name=vect.split(",")[0])]
         layerList.AddLayer(layer)
     if strds:
         layer = AnimLayer()
-        layer.mapType = 'strds'
+        layer.mapType = "strds"
         layer.name = strds
-        layer.cmd = ['d.rast', 'map=']
+        layer.cmd = ["d.rast", "map="]
         layerList.AddLayer(layer)
     if stvds:
         layer = AnimLayer()
-        layer.mapType = 'stvds'
+        layer.mapType = "stvds"
         layer.name = stvds
-        layer.cmd = ['d.vect', 'map=']
+        layer.cmd = ["d.vect", "map="]
         layerList.AddLayer(layer)
 
     app = wx.App()
@@ -137,9 +140,9 @@ def main():
     frame.Show()
     if len(layerList) >= 1:
         # CallAfter added since it was crashing with wxPython 3 gtk
-        wx.CallAfter(frame.SetAnimations,
-                     [layerList] + [None] * (MAX_COUNT - 1))
+        wx.CallAfter(frame.SetAnimations, [layerList] + [None] * (MAX_COUNT - 1))
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

+ 23 - 23
gui/wxpython/animation/mapwindow.py

@@ -17,8 +17,7 @@ This program is free software under the GNU General Public License
 
 import wx
 from core.debug import Debug
-from gui_core.wrap import BitmapFromImage, EmptyBitmap, ImageFromBitmap, \
-    PseudoDC, Rect
+from gui_core.wrap import BitmapFromImage, EmptyBitmap, ImageFromBitmap, PseudoDC, Rect
 from .utils import ComputeScaledRect
 
 
@@ -40,8 +39,10 @@ class BufferedWindow(wx.Window):
 
     def __init__(self, *args, **kwargs):
         # make sure the NO_FULL_REPAINT_ON_RESIZE style flag is set.
-        kwargs['style'] = kwargs.setdefault(
-            'style', wx.NO_FULL_REPAINT_ON_RESIZE) | wx.NO_FULL_REPAINT_ON_RESIZE
+        kwargs["style"] = (
+            kwargs.setdefault("style", wx.NO_FULL_REPAINT_ON_RESIZE)
+            | wx.NO_FULL_REPAINT_ON_RESIZE
+        )
         wx.Window.__init__(self, *args, **kwargs)
 
         Debug.msg(2, "BufferedWindow.__init__()")
@@ -66,7 +67,7 @@ class BufferedWindow(wx.Window):
         Debug.msg(5, "BufferedWindow.OnSize()")
         # The Buffer init is done here, to make sure the buffer is always
         # the same size as the Window
-        #Size  = self.GetClientSizeTuple()
+        # Size  = self.GetClientSizeTuple()
         size = self.GetClientSize()
 
         # Make new offscreen bitmap: this bitmap will always have the
@@ -103,10 +104,12 @@ class BufferedWindow(wx.Window):
 
 
 class AnimationWindow(BufferedWindow):
-
-    def __init__(self, parent, id=wx.ID_ANY,
-                 style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE |
-                 wx.BORDER_RAISED):
+    def __init__(
+        self,
+        parent,
+        id=wx.ID_ANY,
+        style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE | wx.BORDER_RAISED,
+    ):
         Debug.msg(2, "AnimationWindow.__init__()")
 
         self.bitmap = EmptyBitmap(1, 1)
@@ -145,21 +148,18 @@ class AnimationWindow(BufferedWindow):
         if abs(bW - wW) > 5 or abs(bH - wH) > 5:
             params = ComputeScaledRect((bW, bH), (wW, wH))
             im = ImageFromBitmap(bitmap)
-            im.Rescale(params['width'], params['height'])
-            self.x = params['x']
-            self.y = params['y']
+            im.Rescale(params["width"], params["height"])
+            self.x = params["x"]
+            self.y = params["y"]
             bitmap = BitmapFromImage(im)
             if self._overlay:
                 im = ImageFromBitmap(self.bitmap_overlay)
                 im.Rescale(
-                    im.GetWidth() *
-                    params['scale'],
-                    im.GetHeight() *
-                    params['scale'])
+                    im.GetWidth() * params["scale"], im.GetHeight() * params["scale"]
+                )
                 self._setOverlay(
-                    BitmapFromImage(im),
-                    xperc=self.perc[0],
-                    yperc=self.perc[1])
+                    BitmapFromImage(im), xperc=self.perc[0], yperc=self.perc[1]
+                )
         else:
             self.x = 0
             self.y = 0
@@ -180,8 +180,9 @@ class AnimationWindow(BufferedWindow):
         self._pdc.BeginDrawing()
         self._pdc.SetId(1)
         self._pdc.DrawBitmap(bmp=self._overlay, x=x, y=y)
-        self._pdc.SetIdBounds(1, Rect(x, y, self._overlay.GetWidth(),
-                                         self._overlay.GetHeight()))
+        self._pdc.SetIdBounds(
+            1, Rect(x, y, self._overlay.GetWidth(), self._overlay.GetHeight())
+        )
         self._pdc.EndDrawing()
 
     def _setOverlay(self, bitmap, xperc, yperc):
@@ -232,8 +233,7 @@ class AnimationWindow(BufferedWindow):
         current = event.GetPosition()
         if event.LeftDown():
             self._dragid = None
-            idlist = self._pdc.FindObjects(current[0], current[1],
-                                           radius=10)
+            idlist = self._pdc.FindObjects(current[0], current[1], radius=10)
             if 1 in idlist:
                 self._dragid = 1
             self._tmpMousePos = current

+ 174 - 148
gui/wxpython/animation/nviztask.py

@@ -29,7 +29,6 @@ from core.settings import UserSettings
 
 
 class NvizTask:
-
     def __init__(self):
         self.task = None
         self.filename = None
@@ -41,18 +40,23 @@ class NvizTask:
         try:
             gxwXml = ProcessWorkspaceFile(etree.parse(self.filename))
         except Exception:
-            raise GException(_("Reading workspace file <%s> failed.\n"
-                               "Invalid file, unable to parse XML document.") % filename)
+            raise GException(
+                _(
+                    "Reading workspace file <%s> failed.\n"
+                    "Invalid file, unable to parse XML document."
+                )
+                % filename
+            )
         # for display in gxwXml.displays:
-            # pprint(display)
+        # pprint(display)
         # for layer in gxwXml.layers:
-            # pprint(layer)
+        # pprint(layer)
         # pprint(gxwXml.nviz_state)
 
         if not gxwXml.nviz_state:
             raise GException(
-                _("No 3d view information in workspace file <%s>.") %
-                self.filename)
+                _("No 3d view information in workspace file <%s>.") % self.filename
+            )
 
         self._getExtent(gxwXml)
         self._processState(gxwXml.nviz_state)
@@ -60,56 +64,62 @@ class NvizTask:
 
     def _getExtent(self, root):
         for display in root.displays:
-            if display['viewMode'] == '3d':
-                self.region['w'], self.region['s'],\
-                    self.region['e'], self.region['n'],\
-                    self.region['b'], self.region['t'] = display['extent']
-                self.region['tbres'] = display['tbres']
+            if display["viewMode"] == "3d":
+                (
+                    self.region["w"],
+                    self.region["s"],
+                    self.region["e"],
+                    self.region["n"],
+                    self.region["b"],
+                    self.region["t"],
+                ) = display["extent"]
+                self.region["tbres"] = display["tbres"]
 
     def _processLayers(self, layers):
         for layer in layers:
-            if not layer['checked']:
+            if not layer["checked"]:
                 continue
 
-            if not layer['nviz']:
+            if not layer["nviz"]:
                 continue
             layerName, found = GetLayerNameFromCmd(
-                layer['cmd'], fullyQualified=False, param='map')
+                layer["cmd"], fullyQualified=False, param="map"
+            )
             if not found:
                 continue
 
-            if 'surface' in layer['nviz']:
-                self._processSurface(
-                    layer['nviz']['surface'],
-                    mapName=layerName)
-            if 'volume' in layer['nviz']:
-                self._processVolume(layer['nviz']['volume'], mapName=layerName)
-            if 'vector' in layer['nviz']:
-                if 'points' in layer['nviz']['vector']:
+            if "surface" in layer["nviz"]:
+                self._processSurface(layer["nviz"]["surface"], mapName=layerName)
+            if "volume" in layer["nviz"]:
+                self._processVolume(layer["nviz"]["volume"], mapName=layerName)
+            if "vector" in layer["nviz"]:
+                if "points" in layer["nviz"]["vector"]:
                     self._processPoints(
-                        layer['nviz']['vector']['points'],
-                        mapName=layerName)
+                        layer["nviz"]["vector"]["points"], mapName=layerName
+                    )
 
     def _processSurface(self, surface, mapName):
-        self._setMultiTaskParam('elevation_map', mapName)
+        self._setMultiTaskParam("elevation_map", mapName)
 
         # attributes like color, shine, transparency
-        attributes = ('color', 'shine', 'transp')  # mask missing
-        parameters = (('color_map', 'color'),
-                      ('shininess_map', 'shininess_value'),
-                      ('transparency_map', 'transparency_value'))
+        attributes = ("color", "shine", "transp")  # mask missing
+        parameters = (
+            ("color_map", "color"),
+            ("shininess_map", "shininess_value"),
+            ("transparency_map", "transparency_value"),
+        )
         for attr, params in zip(attributes, parameters):
             mapname = None
             const = None
-            if attr in surface['attribute']:
-                if surface['attribute'][attr]['map']:
-                    mapname = surface['attribute'][attr]['value']
+            if attr in surface["attribute"]:
+                if surface["attribute"][attr]["map"]:
+                    mapname = surface["attribute"][attr]["value"]
                 else:
-                    const = surface['attribute'][attr]['value']
+                    const = surface["attribute"][attr]["value"]
             else:
-                if attr == 'transp':
+                if attr == "transp":
                     const = 0
-                elif attr == 'color':
+                elif attr == "color":
                     mapname = mapName
 
             if mapname:
@@ -118,155 +128,173 @@ class NvizTask:
                 self._setMultiTaskParam(params[1], const)
 
         # draw mode
-        for mode in ('mode', 'shading', 'style'):
-            value = surface['draw']['mode']['desc'][mode]
+        for mode in ("mode", "shading", "style"):
+            value = surface["draw"]["mode"]["desc"][mode]
             self._setMultiTaskParam(mode, value)
         # wire color
-        value = surface['draw']['wire-color']['value']
-        self._setMultiTaskParam('wire_color', value)
+        value = surface["draw"]["wire-color"]["value"]
+        self._setMultiTaskParam("wire_color", value)
         # resolution
         for mode1, mode2 in zip(
-                ('coarse', 'fine'),
-                ('resolution_coarse', 'resolution_fine')):
-            value = surface['draw']['resolution'][mode1]
+            ("coarse", "fine"), ("resolution_coarse", "resolution_fine")
+        ):
+            value = surface["draw"]["resolution"][mode1]
             self._setMultiTaskParam(mode2, value)
 
         # position
         pos = []
-        for coor in ('x', 'y', 'z'):
-            pos.append(str(surface['position'][coor]))
-        value = ','.join(pos)
-        self._setMultiTaskParam('surface_position', value)
+        for coor in ("x", "y", "z"):
+            pos.append(str(surface["position"][coor]))
+        value = ",".join(pos)
+        self._setMultiTaskParam("surface_position", value)
 
     def _processPoints(self, points, mapName):
-        for attrib in ('color', 'size', 'width'):
+        for attrib in ("color", "size", "width"):
             if attrib in points:
-                val = points[attrib]['value']
-                self._setMultiTaskParam('vpoint_' + attrib, str(val))
-        if 'height' in points:
-            height = points['height']['value']
-            self._setMultiTaskParam(
-                'vpoint_position',
-                '0,0,{h}'.format(
-                    h=height))
-        if 'marker' in points:
+                val = points[attrib]["value"]
+                self._setMultiTaskParam("vpoint_" + attrib, str(val))
+        if "height" in points:
+            height = points["height"]["value"]
+            self._setMultiTaskParam("vpoint_position", "0,0,{h}".format(h=height))
+        if "marker" in points:
             marker = list(
                 UserSettings.Get(
-                    group='nviz', key='vector', subkey=[
-                        'points', 'marker'], settings_type='internal'))[
-                points['marker']['value']]
-            self._setMultiTaskParam('vpoint_marker', marker)
-        if 'mode' in points:
-            if points['mode']['type'] == '3d':
-                self._setMultiTaskParam('vpoint_mode', '3D')
+                    group="nviz",
+                    key="vector",
+                    subkey=["points", "marker"],
+                    settings_type="internal",
+                )
+            )[points["marker"]["value"]]
+            self._setMultiTaskParam("vpoint_marker", marker)
+        if "mode" in points:
+            if points["mode"]["type"] == "3d":
+                self._setMultiTaskParam("vpoint_mode", "3D")
             else:
-                self._setMultiTaskParam('vpoint_mode', 'surface')
+                self._setMultiTaskParam("vpoint_mode", "surface")
 
     def _processVolume(self, volume, mapName):
-        self._setMultiTaskParam('volume', mapName)
+        self._setMultiTaskParam("volume", mapName)
 
-        if volume['draw']['box']['enabled']:
-            self.task.set_flag('b', True)
+        if volume["draw"]["box"]["enabled"]:
+            self.task.set_flag("b", True)
 
         # isosurfaces
-        isosurfaces = volume['isosurface']
+        isosurfaces = volume["isosurface"]
         if isosurfaces:
-            res_value = volume['draw']['resolution']['isosurface']['value']
-            self._setMultiTaskParam('volume_resolution', res_value)
+            res_value = volume["draw"]["resolution"]["isosurface"]["value"]
+            self._setMultiTaskParam("volume_resolution", res_value)
             for isosurface in isosurfaces:
-                attributes = ('topo', 'color', 'shine', 'transp')
+                attributes = ("topo", "color", "shine", "transp")
                 parameters = (
-                    (None, 'isosurf_level'),
-                    ('isosurf_color_map', 'isosurf_color_value'),
-                    ('isosurf_shininess_map', 'isosurf_shininess_value'),
-                    ('isosurf_transparency_map', 'isosurf_transparency_value'))
+                    (None, "isosurf_level"),
+                    ("isosurf_color_map", "isosurf_color_value"),
+                    ("isosurf_shininess_map", "isosurf_shininess_value"),
+                    ("isosurf_transparency_map", "isosurf_transparency_value"),
+                )
                 for attr, params in zip(attributes, parameters):
                     mapname = None
                     const = None
                     if attr in isosurface:
-                        if isosurface[attr]['map']:
-                            mapname = isosurface[attr]['value']
+                        if isosurface[attr]["map"]:
+                            mapname = isosurface[attr]["value"]
                         else:
-                            const = float(isosurface[attr]['value'])
+                            const = float(isosurface[attr]["value"])
                     else:
-                        if attr == 'transp':
+                        if attr == "transp":
                             const = 0
-                        elif attr == 'color':
+                        elif attr == "color":
                             mapname = mapName
 
                     if mapname:
                         self._setMultiTaskParam(params[0], mapname)
                     else:
-                        if attr == 'topo':
+                        if attr == "topo":
                             # TODO: we just assume it's the first volume, what
                             # to do else?
-                            self._setMultiTaskParam(
-                                params[1], '1:' + str(const))
+                            self._setMultiTaskParam(params[1], "1:" + str(const))
                         else:
                             self._setMultiTaskParam(params[1], const)
-                if isosurface['inout']['value']:
-                    self.task.set_flag('n', True)
+                if isosurface["inout"]["value"]:
+                    self.task.set_flag("n", True)
         # slices
-        slices = volume['slice']
+        slices = volume["slice"]
         if slices:
-            res_value = volume['draw']['resolution']['slice']['value']
-            self._setMultiTaskParam('volume_resolution', res_value)
+            res_value = volume["draw"]["resolution"]["slice"]["value"]
+            self._setMultiTaskParam("volume_resolution", res_value)
             for slice_ in slices:
+                self._setMultiTaskParam("slice_transparency", slice_["transp"]["value"])
+                axis = slice_["position"]["axis"]
+                self._setMultiTaskParam("slice", "1:" + "xyz"[axis])
+                pos = slice_["position"]
+                coords = (
+                    pos["x1"],
+                    pos["x2"],
+                    pos["y1"],
+                    pos["y2"],
+                    pos["z1"],
+                    pos["z2"],
+                )
                 self._setMultiTaskParam(
-                    'slice_transparency', slice_['transp']['value'])
-                axis = slice_['position']['axis']
-                self._setMultiTaskParam('slice', '1:' + 'xyz'[axis])
-                pos = slice_['position']
-                coords = pos['x1'], pos['x2'], pos[
-                    'y1'], pos['y2'], pos['z1'], pos['z2']
-                self._setMultiTaskParam('slice_position', ','.join(
-                    [str(c) for c in coords]))
+                    "slice_position", ",".join([str(c) for c in coords])
+                )
 
         # position
         pos = []
-        for coor in ('x', 'y', 'z'):
-            pos.append(str(volume['position'][coor]))
-            value = ','.join(pos)
+        for coor in ("x", "y", "z"):
+            pos.append(str(volume["position"][coor]))
+            value = ",".join(pos)
 
-        self._setMultiTaskParam('volume_position', value)
+        self._setMultiTaskParam("volume_position", value)
 
     def _processState(self, state):
-        color = state['view']['background']['color']
-        self.task.set_param('bgcolor', self._join(color, delim=':'))
+        color = state["view"]["background"]["color"]
+        self.task.set_param("bgcolor", self._join(color, delim=":"))
         self.task.set_param(
-            'position', self._join(
-                (state['view']['position']['x'],
-                 state['view']['position']['y'])))
-        self.task.set_param('height', state['iview']['height']['value'])
-        self.task.set_param('perspective', state['view']['persp']['value'])
-        self.task.set_param('twist', state['view']['twist']['value'])
+            "position",
+            self._join(
+                (state["view"]["position"]["x"], state["view"]["position"]["y"])
+            ),
+        )
+        self.task.set_param("height", state["iview"]["height"]["value"])
+        self.task.set_param("perspective", state["view"]["persp"]["value"])
+        self.task.set_param("twist", state["view"]["twist"]["value"])
         # TODO: fix zexag
-        self.task.set_param('zexag', state['view']['z-exag']['value'])
+        self.task.set_param("zexag", state["view"]["z-exag"]["value"])
         self.task.set_param(
-            'focus', self._join(
-                (state['iview']['focus']['x'],
-                 state['iview']['focus']['y'],
-                 state['iview']['focus']['z'])))
+            "focus",
+            self._join(
+                (
+                    state["iview"]["focus"]["x"],
+                    state["iview"]["focus"]["y"],
+                    state["iview"]["focus"]["z"],
+                )
+            ),
+        )
         self.task.set_param(
-            'light_position', self._join(
-                (state['light']['position']['x'],
-                 state['light']['position']['y'],
-                 state['light']['position']['z'] / 100.)))
-        color = state['light']['color'][:3]
-        self.task.set_param('light_color', self._join(color, delim=':'))
-        self.task.set_param('light_brightness', int(state['light']['bright']))
-        self.task.set_param('light_ambient', state['light']['ambient'])
+            "light_position",
+            self._join(
+                (
+                    state["light"]["position"]["x"],
+                    state["light"]["position"]["y"],
+                    state["light"]["position"]["z"] / 100.0,
+                )
+            ),
+        )
+        color = state["light"]["color"][:3]
+        self.task.set_param("light_color", self._join(color, delim=":"))
+        self.task.set_param("light_brightness", int(state["light"]["bright"]))
+        self.task.set_param("light_ambient", state["light"]["ambient"])
 
     def _setMultiTaskParam(self, param, value):
-        last = self.task.get_param(param)['value']
+        last = self.task.get_param(param)["value"]
         self.task.set_param(param, self._join((last, value)))
 
-    def _join(self, toJoin, delim=','):
+    def _join(self, toJoin, delim=","):
         toJoin = filter(self._ignore, toJoin)
         return delim.join(map(str, toJoin))
 
     def _ignore(self, value):
-        if value == '' or value is None:
+        if value == "" or value is None:
             return False
         else:
             return True
@@ -275,7 +303,7 @@ class NvizTask:
         # params = self.task.get_list_params()
         # parameter with 'map' name
         # params = filter(lambda x: 'map' in x, params)
-        return ('elevation_map', 'color_map', 'vline', 'vpoint', 'volume')
+        return ("elevation_map", "color_map", "vline", "vpoint", "volume")
 
     def GetCommandSeries(self, layerList, paramName):
         commands = []
@@ -286,25 +314,25 @@ class NvizTask:
             raise GException(_("Please add only one layer in the list."))
             return
         layer = layerList[0]
-        if hasattr(layer, 'maps'):
+        if hasattr(layer, "maps"):
             series = layer.maps
         else:
-            raise GException(
-                _("No map series nor space-time dataset is added."))
+            raise GException(_("No map series nor space-time dataset is added."))
 
         for value in series:
             self.task.set_param(paramName, value)
             # FIXME: we assume we want always default color map
-            if paramName == 'elevation_map':
-                self.task.set_param('color_map', self.task.get_param(paramName)['value'])
-            if paramName == 'volume':
-                self.task.set_param('isosurf_color_map', '')
-            self.task.set_flag('overwrite', True)
-            self.task.set_param('output', 'tobechanged')
+            if paramName == "elevation_map":
+                self.task.set_param(
+                    "color_map", self.task.get_param(paramName)["value"]
+                )
+            if paramName == "volume":
+                self.task.set_param("isosurf_color_map", "")
+            self.task.set_flag("overwrite", True)
+            self.task.set_param("output", "tobechanged")
             cmd = self.task.get_cmd(
-                ignoreErrors=False,
-                ignoreRequired=False,
-                ignoreDefault=True)
+                ignoreErrors=False, ignoreRequired=False, ignoreDefault=True
+            )
             commands.append(cmd)
 
         return commands
@@ -312,12 +340,11 @@ class NvizTask:
     def GetCommand(self):
         if not self.task:
             return None
-        self.task.set_flag('overwrite', True)
-        self.task.set_param('output', 'tobechanged')
+        self.task.set_flag("overwrite", True)
+        self.task.set_param("output", "tobechanged")
         cmd = self.task.get_cmd(
-            ignoreErrors=False,
-            ignoreRequired=False,
-            ignoreDefault=True)
+            ignoreErrors=False, ignoreRequired=False, ignoreDefault=True
+        )
         return gtask.cmdlist_to_tuple(cmd)
 
     def GetRegion(self):
@@ -325,18 +352,17 @@ class NvizTask:
 
 
 def test():
-    nviz = NvizTask('/home/anna/testy/nviz/t12.gxw')
+    nviz = NvizTask("/home/anna/testy/nviz/t12.gxw")
     # nviz = NvizState('/home/anna/testy/nviz/t3.gxw')
 
     # cmd = nviz.GetCommand()
-    cmds = nviz.GetCommandSeries(['aspect', 'elevation'], 'color_map')
+    cmds = nviz.GetCommandSeries(["aspect", "elevation"], "color_map")
     for cmd in cmds:
         print(cmd)
-        returncode, message = RunCommand(
-            getErrorMsg=True, prog=cmd[0], **cmd[1])
+        returncode, message = RunCommand(getErrorMsg=True, prog=cmd[0], **cmd[1])
         print(returncode, message)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
 
     test()

+ 233 - 194
gui/wxpython/animation/provider.py

@@ -45,12 +45,15 @@ class BitmapProvider:
     It handles both 2D and 3D animations.
     """
 
-    def __init__(self, bitmapPool, mapFilesPool, tempDir,
-                 imageWidth=640, imageHeight=480):
+    def __init__(
+        self, bitmapPool, mapFilesPool, tempDir, imageWidth=640, imageHeight=480
+    ):
 
         self._bitmapPool = bitmapPool
         self._mapFilesPool = mapFilesPool
-        self.imageWidth = imageWidth  # width of the image to render with d.rast or d.vect
+        self.imageWidth = (
+            imageWidth  # width of the image to render with d.rast or d.vect
+        )
         # height of the image to render with d.rast or d.vect
         self.imageHeight = imageHeight
         self._tempDir = tempDir
@@ -64,19 +67,23 @@ class BitmapProvider:
         self._regions = []
         self._regionsForUniqueCmds = []
 
-        self._renderer = BitmapRenderer(self._mapFilesPool, self._tempDir,
-                                        self.imageWidth, self.imageHeight)
-        self._composer = BitmapComposer(self._tempDir, self._mapFilesPool,
-                                        self._bitmapPool, self.imageWidth,
-                                        self.imageHeight)
-        self.renderingStarted = Signal('BitmapProvider.renderingStarted')
-        self.compositionStarted = Signal('BitmapProvider.compositionStarted')
-        self.renderingContinues = Signal('BitmapProvider.renderingContinues')
-        self.compositionContinues = Signal(
-            'BitmapProvider.compositionContinues')
-        self.renderingFinished = Signal('BitmapProvider.renderingFinished')
-        self.compositionFinished = Signal('BitmapProvider.compositionFinished')
-        self.mapsLoaded = Signal('BitmapProvider.mapsLoaded')
+        self._renderer = BitmapRenderer(
+            self._mapFilesPool, self._tempDir, self.imageWidth, self.imageHeight
+        )
+        self._composer = BitmapComposer(
+            self._tempDir,
+            self._mapFilesPool,
+            self._bitmapPool,
+            self.imageWidth,
+            self.imageHeight,
+        )
+        self.renderingStarted = Signal("BitmapProvider.renderingStarted")
+        self.compositionStarted = Signal("BitmapProvider.compositionStarted")
+        self.renderingContinues = Signal("BitmapProvider.renderingContinues")
+        self.compositionContinues = Signal("BitmapProvider.compositionContinues")
+        self.renderingFinished = Signal("BitmapProvider.renderingFinished")
+        self.compositionFinished = Signal("BitmapProvider.compositionFinished")
+        self.mapsLoaded = Signal("BitmapProvider.mapsLoaded")
 
         self._renderer.renderingContinues.connect(self.renderingContinues)
         self._composer.compositionContinues.connect(self.compositionContinues)
@@ -93,8 +100,8 @@ class BitmapProvider:
         :param regions: list of regions
         """
         Debug.msg(
-            2, "BitmapProvider.SetCmds: {n} lists".format(
-                n=len(cmdsForComposition)))
+            2, "BitmapProvider.SetCmds: {n} lists".format(n=len(cmdsForComposition))
+        )
         self._cmdsForComposition.extend(cmdsForComposition)
         self._opacities.extend(opacities)
         self._regions.extend(regions)
@@ -107,9 +114,7 @@ class BitmapProvider:
         :param cmds: list of commands m.nviz.image (cmd as a list)
         :param region: for 3D rendering
         """
-        Debug.msg(
-            2, "BitmapProvider.SetCmds3D: {c} commands".format(
-                c=len(cmds)))
+        Debug.msg(2, "BitmapProvider.SetCmds3D: {c} commands".format(c=len(cmds)))
         self._cmds3D = cmds
         self._regionFor3D = region
 
@@ -125,8 +130,12 @@ class BitmapProvider:
                     unique.append((tuple(cmd), None))
         unique = list(set(unique))
         self._uniqueCmds = [cmdAndRegion[0] for cmdAndRegion in unique]
-        self._regionsForUniqueCmds.extend([dict(cmdAndRegion[1]) if cmdAndRegion[
-                                          1] else None for cmdAndRegion in unique])
+        self._regionsForUniqueCmds.extend(
+            [
+                dict(cmdAndRegion[1]) if cmdAndRegion[1] else None
+                for cmdAndRegion in unique
+            ]
+        )
 
     def Unload(self):
         """Unloads currently loaded data.
@@ -134,12 +143,10 @@ class BitmapProvider:
         """
         Debug.msg(2, "BitmapProvider.Unload")
         if self._cmdsForComposition:
-            for cmd, region in zip(self._uniqueCmds,
-                                   self._regionsForUniqueCmds):
+            for cmd, region in zip(self._uniqueCmds, self._regionsForUniqueCmds):
                 del self._mapFilesPool[HashCmd(cmd, region)]
 
-            for cmdList, region in zip(
-                    self._cmdsForComposition, self._regions):
+            for cmdList, region in zip(self._cmdsForComposition, self._regions):
                 del self._bitmapPool[HashCmds(cmdList, region)]
             self._uniqueCmds = []
             self._cmdsForComposition = []
@@ -160,15 +167,18 @@ class BitmapProvider:
         count = 0
         for cmd, region in zip(uniqueCmds, regions):
             filename = GetFileFromCmd(self._tempDir, cmd, region)
-            if not force and os.path.exists(filename) and self._mapFilesPool.GetSize(
-                    HashCmd(cmd, region)) == (self.imageWidth, self.imageHeight):
+            if (
+                not force
+                and os.path.exists(filename)
+                and self._mapFilesPool.GetSize(HashCmd(cmd, region))
+                == (self.imageWidth, self.imageHeight)
+            ):
                 continue
             count += 1
 
         Debug.msg(
-            3,
-            "BitmapProvider._dryRender: {c} files to be rendered".format(
-                c=count))
+            3, "BitmapProvider._dryRender: {c} files to be rendered".format(c=count)
+        )
 
         return count
 
@@ -182,17 +192,18 @@ class BitmapProvider:
         """
         count = 0
         for cmdList, region in zip(cmdLists, regions):
-            if not force and HashCmds(
-                    cmdList, region) in self._bitmapPool and self._bitmapPool[
-                    HashCmds(cmdList, region)].GetSize() == (
-                    self.imageWidth, self.imageHeight):
+            if (
+                not force
+                and HashCmds(cmdList, region) in self._bitmapPool
+                and self._bitmapPool[HashCmds(cmdList, region)].GetSize()
+                == (self.imageWidth, self.imageHeight)
+            ):
                 continue
             count += 1
 
         Debug.msg(
-            2,
-            "BitmapProvider._dryCompose: {c} files to be composed".format(
-                c=count))
+            2, "BitmapProvider._dryCompose: {c} files to be composed".format(c=count)
+        )
 
         return count
 
@@ -204,10 +215,11 @@ class BitmapProvider:
         :param bgcolor: background color as a tuple of 3 values 0 to 255
         :param nprocs: number of procs to be used for rendering
         """
-        Debug.msg(2, "BitmapProvider.Load: "
-                     "force={f}, bgcolor={b}, nprocs={n}".format(f=force,
-                                                                 b=bgcolor,
-                                                                 n=nprocs))
+        Debug.msg(
+            2,
+            "BitmapProvider.Load: "
+            "force={f}, bgcolor={b}, nprocs={n}".format(f=force, b=bgcolor, n=nprocs),
+        )
         cmds = []
         regions = []
         if self._uniqueCmds:
@@ -223,7 +235,8 @@ class BitmapProvider:
         # create no data bitmap
         if None not in self._bitmapPool or force:
             self._bitmapPool[None] = createNoDataBitmap(
-                self.imageWidth, self.imageHeight)
+                self.imageWidth, self.imageHeight
+            )
 
         ok = self._renderer.Render(
             cmds,
@@ -231,16 +244,16 @@ class BitmapProvider:
             regionFor3D=self._regionFor3D,
             bgcolor=bgcolor,
             force=force,
-            nprocs=nprocs)
+            nprocs=nprocs,
+        )
         self.renderingFinished.emit()
         if not ok:
             self.mapsLoaded.emit()  # what to do here?
             return
         if self._cmdsForComposition:
             count = self._dryCompose(
-                self._cmdsForComposition,
-                self._regions,
-                force=force)
+                self._cmdsForComposition, self._regions, force=force
+            )
             self.compositionStarted.emit(count=count)
             self._composer.Compose(
                 self._cmdsForComposition,
@@ -248,12 +261,14 @@ class BitmapProvider:
                 self._opacities,
                 bgcolor=bgcolor,
                 force=force,
-                nprocs=nprocs)
+                nprocs=nprocs,
+            )
             self.compositionFinished.emit()
         if self._cmds3D:
             for cmd in self._cmds3D:
-                self._bitmapPool[HashCmds([cmd], None)] = \
-                    wx.Bitmap(GetFileFromCmd(self._tempDir, cmd, None))
+                self._bitmapPool[HashCmds([cmd], None)] = wx.Bitmap(
+                    GetFileFromCmd(self._tempDir, cmd, None)
+                )
 
         self.mapsLoaded.emit()
 
@@ -278,8 +293,9 @@ class BitmapProvider:
     def WindowSizeChanged(self, width, height):
         """Sets size when size of related window changes."""
         Debug.msg(
-            5, "BitmapProvider.WindowSizeChanged: w={w}, h={h}".format(
-                w=width, h=height))
+            5,
+            "BitmapProvider.WindowSizeChanged: w={w}, h={h}".format(w=width, h=height),
+        )
 
         self.imageWidth, self.imageHeight = width, height
 
@@ -298,13 +314,17 @@ class BitmapProvider:
         fileHandler, filename = tempfile.mkstemp(suffix=".png")
         os.close(fileHandler)
         # Set the environment variables for this process
-        _setEnvironment(self.imageWidth, self.imageHeight, filename,
-                        transparent=True, bgcolor=(0, 0, 0))
+        _setEnvironment(
+            self.imageWidth,
+            self.imageHeight,
+            filename,
+            transparent=True,
+            bgcolor=(0, 0, 0),
+        )
 
         Debug.msg(1, "Render raster legend " + str(filename))
         cmdTuple = cmdlist_to_tuple(cmd)
-        returncode, stdout, messages = read2_command(
-            cmdTuple[0], **cmdTuple[1])
+        returncode, stdout, messages = read2_command(cmdTuple[0], **cmdTuple[1])
 
         if returncode == 0:
             return BitmapFromImage(autoCropImageFromFile(filename))
@@ -316,14 +336,13 @@ class BitmapProvider:
 class BitmapRenderer:
     """Class which renders 2D and 3D images to files."""
 
-    def __init__(self, mapFilesPool, tempDir,
-                 imageWidth, imageHeight):
+    def __init__(self, mapFilesPool, tempDir, imageWidth, imageHeight):
         self._mapFilesPool = mapFilesPool
         self._tempDir = tempDir
         self.imageWidth = imageWidth
         self.imageHeight = imageHeight
 
-        self.renderingContinues = Signal('BitmapRenderer.renderingContinues')
+        self.renderingContinues = Signal("BitmapRenderer.renderingContinues")
         self._stopRendering = False
         self._isRendering = False
 
@@ -349,8 +368,12 @@ class BitmapRenderer:
         filteredCmdList = []
         for cmd, region in zip(cmdList, regions):
             filename = GetFileFromCmd(self._tempDir, cmd, region)
-            if not force and os.path.exists(filename) and self._mapFilesPool.GetSize(
-                    HashCmd(cmd, region)) == (self.imageWidth, self.imageHeight):
+            if (
+                not force
+                and os.path.exists(filename)
+                and self._mapFilesPool.GetSize(HashCmd(cmd, region))
+                == (self.imageWidth, self.imageHeight)
+            ):
                 # for reference counting
                 self._mapFilesPool[HashCmd(cmd, region)] = filename
                 continue
@@ -365,7 +388,7 @@ class BitmapRenderer:
             # Queue object for interprocess communication
             q = Queue()
             # The separate render process
-            if cmd[0] == 'm.nviz.image':
+            if cmd[0] == "m.nviz.image":
                 p = Process(
                     target=RenderProcess3D,
                     args=(
@@ -375,7 +398,9 @@ class BitmapRenderer:
                         cmd,
                         regionFor3D,
                         bgcolor,
-                        q))
+                        q,
+                    ),
+                )
             else:
                 p = Process(
                     target=RenderProcess2D,
@@ -386,7 +411,9 @@ class BitmapRenderer:
                         cmd,
                         region,
                         bgcolor,
-                        q))
+                        q,
+                    ),
+                )
             p.start()
 
             queue_list.append(q)
@@ -400,21 +427,19 @@ class BitmapRenderer:
                     proc_list[i].join()
                     filename = queue_list[i].get()
                     self._mapFilesPool[
-                        HashCmd(
-                            cmd_list[i][0],
-                            cmd_list[i][1])] = filename
+                        HashCmd(cmd_list[i][0], cmd_list[i][1])
+                    ] = filename
                     self._mapFilesPool.SetSize(
-                        HashCmd(cmd_list[i][0],
-                                cmd_list[i][1]),
-                        (self.imageWidth, self.imageHeight))
+                        HashCmd(cmd_list[i][0], cmd_list[i][1]),
+                        (self.imageWidth, self.imageHeight),
+                    )
 
                 proc_count = 0
                 proc_list = []
                 queue_list = []
                 cmd_list = []
 
-            self.renderingContinues.emit(
-                current=count, text=_("Rendering map layers"))
+            self.renderingContinues.emit(current=count, text=_("Rendering map layers"))
             if self._stopRendering:
                 self._stopRendering = False
                 stopped = True
@@ -432,15 +457,14 @@ class BitmapRenderer:
 class BitmapComposer:
     """Class which handles the composition of image files with g.pnmcomp."""
 
-    def __init__(self, tempDir, mapFilesPool, bitmapPool,
-                 imageWidth, imageHeight):
+    def __init__(self, tempDir, mapFilesPool, bitmapPool, imageWidth, imageHeight):
         self._mapFilesPool = mapFilesPool
         self._bitmapPool = bitmapPool
         self._tempDir = tempDir
         self.imageWidth = imageWidth
         self.imageHeight = imageHeight
 
-        self.compositionContinues = Signal('BitmapComposer.composingContinues')
+        self.compositionContinues = Signal("BitmapComposer.composingContinues")
         self._stopComposing = False
         self._isComposing = False
 
@@ -466,19 +490,17 @@ class BitmapComposer:
 
         filteredCmdLists = []
         for cmdList, region in zip(cmdLists, regions):
-            if not force and HashCmds(
-                    cmdList, region) in self._bitmapPool and self._bitmapPool[
-                    HashCmds(cmdList, region)].GetSize() == (
-                    self.imageWidth, self.imageHeight):
+            if (
+                not force
+                and HashCmds(cmdList, region) in self._bitmapPool
+                and self._bitmapPool[HashCmds(cmdList, region)].GetSize()
+                == (self.imageWidth, self.imageHeight)
+            ):
                 # TODO: find a better way than to assign the same to increase
                 # the reference
-                self._bitmapPool[
-                    HashCmds(
-                        cmdList,
-                        region)] = self._bitmapPool[
-                    HashCmds(
-                        cmdList,
-                        region)]
+                self._bitmapPool[HashCmds(cmdList, region)] = self._bitmapPool[
+                    HashCmds(cmdList, region)
+                ]
                 continue
             filteredCmdLists.append((cmdList, region))
 
@@ -490,9 +512,19 @@ class BitmapComposer:
             # Queue object for interprocess communication
             q = Queue()
             # The separate render process
-            p = Process(target=CompositeProcess,
-                        args=(self.imageWidth, self.imageHeight, self._tempDir,
-                              cmdList, region, opacityList, bgcolor, q))
+            p = Process(
+                target=CompositeProcess,
+                args=(
+                    self.imageWidth,
+                    self.imageHeight,
+                    self._tempDir,
+                    cmdList,
+                    region,
+                    opacityList,
+                    bgcolor,
+                    q,
+                ),
+            )
             p.start()
 
             queue_list.append(q)
@@ -508,18 +540,14 @@ class BitmapComposer:
                     filename = queue_list[i].get()
                     if filename is None:
                         self._bitmapPool[
-                            HashCmds(
-                                cmd_lists[i][0],
-                                cmd_lists[i][1])] = createNoDataBitmap(
-                            self.imageWidth,
-                            self.imageHeight,
-                            text="Failed to render")
+                            HashCmds(cmd_lists[i][0], cmd_lists[i][1])
+                        ] = createNoDataBitmap(
+                            self.imageWidth, self.imageHeight, text="Failed to render"
+                        )
                     else:
                         self._bitmapPool[
-                            HashCmds(
-                                cmd_lists[i][0],
-                                cmd_lists[i][1])] = BitmapFromImage(
-                            wx.Image(filename))
+                            HashCmds(cmd_lists[i][0], cmd_lists[i][1])
+                        ] = BitmapFromImage(wx.Image(filename))
                         os.remove(filename)
                 proc_count = 0
                 proc_list = []
@@ -527,7 +555,8 @@ class BitmapComposer:
                 cmd_lists = []
 
             self.compositionContinues.emit(
-                current=count, text=_("Overlaying map layers"))
+                current=count, text=_("Overlaying map layers")
+            )
             if self._stopComposing:
                 self._stopComposing = False
                 break
@@ -540,8 +569,7 @@ class BitmapComposer:
             self._stopComposing = True
 
 
-def RenderProcess2D(imageWidth, imageHeight, tempDir,
-                    cmd, region, bgcolor, fileQueue):
+def RenderProcess2D(imageWidth, imageHeight, tempDir, cmd, region, bgcolor, fileQueue):
     """Render raster or vector files as ppm image and write the
        resulting ppm filename in the provided file queue
 
@@ -559,27 +587,27 @@ def RenderProcess2D(imageWidth, imageHeight, tempDir,
     transparency = True
 
     # Set the environment variables for this process
-    _setEnvironment(imageWidth, imageHeight, filename,
-                    transparent=transparency, bgcolor=bgcolor)
+    _setEnvironment(
+        imageWidth, imageHeight, filename, transparent=transparency, bgcolor=bgcolor
+    )
     if region:
-        os.environ['GRASS_REGION'] = gcore.region_env(**region)
+        os.environ["GRASS_REGION"] = gcore.region_env(**region)
     cmdTuple = cmdlist_to_tuple(cmd)
     returncode, stdout, messages = read2_command(cmdTuple[0], **cmdTuple[1])
     if returncode != 0:
         gcore.warning("Rendering failed:\n" + messages)
         fileQueue.put(None)
         if region:
-            os.environ.pop('GRASS_REGION')
+            os.environ.pop("GRASS_REGION")
         os.remove(filename)
         return
 
     if region:
-        os.environ.pop('GRASS_REGION')
+        os.environ.pop("GRASS_REGION")
     fileQueue.put(filename)
 
 
-def RenderProcess3D(imageWidth, imageHeight, tempDir,
-                    cmd, region, bgcolor, fileQueue):
+def RenderProcess3D(imageWidth, imageHeight, tempDir, cmd, region, bgcolor, fileQueue):
     """Renders image with m.nviz.image and writes the
        resulting ppm filename in the provided file queue
 
@@ -594,30 +622,30 @@ def RenderProcess3D(imageWidth, imageHeight, tempDir,
     """
 
     filename = GetFileFromCmd(tempDir, cmd, None)
-    os.environ['GRASS_REGION'] = gcore.region_env(region3d=True, **region)
+    os.environ["GRASS_REGION"] = gcore.region_env(region3d=True, **region)
     Debug.msg(1, "Render image to file " + str(filename))
 
     cmdTuple = cmdlist_to_tuple(cmd)
-    cmdTuple[1]['output'] = os.path.splitext(filename)[0]
+    cmdTuple[1]["output"] = os.path.splitext(filename)[0]
     # set size
-    cmdTuple[1]['size'] = '%d,%d' % (imageWidth, imageHeight)
+    cmdTuple[1]["size"] = "%d,%d" % (imageWidth, imageHeight)
     # set format
-    cmdTuple[1]['format'] = 'ppm'
-    cmdTuple[1]['bgcolor'] = bgcolor = ':'.join(
-        [str(part) for part in bgcolor])
+    cmdTuple[1]["format"] = "ppm"
+    cmdTuple[1]["bgcolor"] = bgcolor = ":".join([str(part) for part in bgcolor])
     returncode, stdout, messages = read2_command(cmdTuple[0], **cmdTuple[1])
     if returncode != 0:
         gcore.warning("Rendering failed:\n" + messages)
         fileQueue.put(None)
-        os.environ.pop('GRASS_REGION')
+        os.environ.pop("GRASS_REGION")
         return
 
-    os.environ.pop('GRASS_REGION')
+    os.environ.pop("GRASS_REGION")
     fileQueue.put(filename)
 
 
-def CompositeProcess(imageWidth, imageHeight, tempDir,
-                     cmdList, region, opacities, bgcolor, fileQueue):
+def CompositeProcess(
+    imageWidth, imageHeight, tempDir, cmdList, region, opacities, bgcolor, fileQueue
+):
     """Performs the composition of image ppm files and writes the
        resulting ppm filename in the provided file queue
 
@@ -636,19 +664,26 @@ def CompositeProcess(imageWidth, imageHeight, tempDir,
     masks = []
     for cmd in cmdList:
         maps.append(GetFileFromCmd(tempDir, cmd, region))
-        masks.append(GetFileFromCmd(tempDir, cmd, region, 'pgm'))
+        masks.append(GetFileFromCmd(tempDir, cmd, region, "pgm"))
     filename = GetFileFromCmds(tempDir, cmdList, region)
     # Set the environment variables for this process
-    _setEnvironment(imageWidth, imageHeight, filename,
-                    transparent=False, bgcolor=bgcolor)
+    _setEnvironment(
+        imageWidth, imageHeight, filename, transparent=False, bgcolor=bgcolor
+    )
 
     opacities = [str(op) for op in opacities]
-    bgcolor = ':'.join([str(part) for part in bgcolor])
+    bgcolor = ":".join([str(part) for part in bgcolor])
     returncode, stdout, messages = read2_command(
-        'g.pnmcomp', overwrite=True, input='%s' % ",".join(reversed(maps)),
-        mask='%s' % ",".join(reversed(masks)),
-        opacity='%s' % ",".join(reversed(opacities)),
-        bgcolor=bgcolor, width=imageWidth, height=imageHeight, output=filename)
+        "g.pnmcomp",
+        overwrite=True,
+        input="%s" % ",".join(reversed(maps)),
+        mask="%s" % ",".join(reversed(masks)),
+        opacity="%s" % ",".join(reversed(opacities)),
+        bgcolor=bgcolor,
+        width=imageWidth,
+        height=imageHeight,
+        output=filename,
+    )
 
     if returncode != 0:
         gcore.warning("Rendering composite failed:\n" + messages)
@@ -677,23 +712,21 @@ class DictRefCounter:
             self.referenceCount[key] = 1
         else:
             self.referenceCount[key] += 1
-        Debug.msg(
-            5, 'DictRefCounter.__setitem__: +1 for key {k}'.format(k=key))
+        Debug.msg(5, "DictRefCounter.__setitem__: +1 for key {k}".format(k=key))
 
     def __contains__(self, key):
         return key in self.dictionary
 
     def __delitem__(self, key):
         self.referenceCount[key] -= 1
-        Debug.msg(
-            5, 'DictRefCounter.__delitem__: -1 for key {k}'.format(k=key))
+        Debug.msg(5, "DictRefCounter.__delitem__: -1 for key {k}".format(k=key))
 
     def keys(self):
         return self.dictionary.keys()
 
     def Clear(self):
         """Clears items which are not needed any more."""
-        Debug.msg(4, 'DictRefCounter.Clear')
+        Debug.msg(4, "DictRefCounter.Clear")
         for key in self.dictionary.copy().keys():
             if key is not None:
                 if self.referenceCount[key] <= 0:
@@ -718,14 +751,14 @@ class MapFilesPool(DictRefCounter):
         """Removes files which are not needed anymore.
         Removes both ppm and pgm.
         """
-        Debug.msg(4, 'MapFilesPool.Clear')
+        Debug.msg(4, "MapFilesPool.Clear")
 
         for key in self.dictionary.keys():
             if self.referenceCount[key] <= 0:
                 name, ext = os.path.splitext(self.dictionary[key])
                 os.remove(self.dictionary[key])
-                if ext == '.ppm':
-                    os.remove(name + '.pgm')
+                if ext == ".ppm":
+                    os.remove(name + ".pgm")
                 del self.dictionary[key]
                 del self.referenceCount[key]
                 del self.size[key]
@@ -746,16 +779,13 @@ class CleanUp:
 
     def __call__(self):
         import shutil
+
         if os.path.exists(self._tempDir):
             try:
                 shutil.rmtree(self._tempDir)
-                Debug.msg(
-                    5, 'CleanUp: removed directory {t}'.format(
-                        t=self._tempDir))
+                Debug.msg(5, "CleanUp: removed directory {t}".format(t=self._tempDir))
             except OSError:
-                gcore.warning(
-                    _("Directory {t} not removed.").format(
-                        t=self._tempDir))
+                gcore.warning(_("Directory {t} not removed.").format(t=self._tempDir))
 
 
 def _setEnvironment(width, height, filename, transparent, bgcolor):
@@ -771,24 +801,23 @@ def _setEnvironment(width, height, filename, transparent, bgcolor):
         5,
         "_setEnvironment: width={w}, height={h}, "
         "filename={f}, transparent={t}, bgcolor={b}".format(
-            w=width,
-            h=height,
-            f=filename,
-            t=transparent,
-            b=bgcolor))
-
-    os.environ['GRASS_RENDER_WIDTH'] = str(width)
-    os.environ['GRASS_RENDER_HEIGHT'] = str(height)
-    driver = UserSettings.Get(group='display', key='driver', subkey='type')
-    os.environ['GRASS_RENDER_IMMEDIATE'] = driver
-    os.environ['GRASS_RENDER_BACKGROUNDCOLOR'] = '{r:02x}{g:02x}{b:02x}'.format(
-        r=bgcolor[0], g=bgcolor[1], b=bgcolor[2])
-    os.environ['GRASS_RENDER_TRUECOLOR'] = "TRUE"
+            w=width, h=height, f=filename, t=transparent, b=bgcolor
+        ),
+    )
+
+    os.environ["GRASS_RENDER_WIDTH"] = str(width)
+    os.environ["GRASS_RENDER_HEIGHT"] = str(height)
+    driver = UserSettings.Get(group="display", key="driver", subkey="type")
+    os.environ["GRASS_RENDER_IMMEDIATE"] = driver
+    os.environ["GRASS_RENDER_BACKGROUNDCOLOR"] = "{r:02x}{g:02x}{b:02x}".format(
+        r=bgcolor[0], g=bgcolor[1], b=bgcolor[2]
+    )
+    os.environ["GRASS_RENDER_TRUECOLOR"] = "TRUE"
     if transparent:
-        os.environ['GRASS_RENDER_TRANSPARENT'] = "TRUE"
+        os.environ["GRASS_RENDER_TRANSPARENT"] = "TRUE"
     else:
-        os.environ['GRASS_RENDER_TRANSPARENT'] = "FALSE"
-    os.environ['GRASS_RENDER_FILE'] = str(filename)
+        os.environ["GRASS_RENDER_TRANSPARENT"] = "FALSE"
+    os.environ["GRASS_RENDER_FILE"] = str(filename)
 
 
 def createNoDataBitmap(imageWidth, imageHeight, text="No data"):
@@ -800,15 +829,25 @@ def createNoDataBitmap(imageWidth, imageHeight, text="No data"):
     :param imageWidth: image width
     :param imageHeight: image height
     """
-    Debug.msg(4, "createNoDataBitmap: w={w}, h={h}, text={t}".format(
-        w=imageWidth, h=imageHeight, t=text))
+    Debug.msg(
+        4,
+        "createNoDataBitmap: w={w}, h={h}, text={t}".format(
+            w=imageWidth, h=imageHeight, t=text
+        ),
+    )
     bitmap = EmptyBitmap(imageWidth, imageHeight)
     dc = wx.MemoryDC()
     dc.SelectObject(bitmap)
     dc.Clear()
     text = _(text)
-    dc.SetFont(wx.Font(pointSize=40, family=wx.FONTFAMILY_SCRIPT,
-                       style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_BOLD))
+    dc.SetFont(
+        wx.Font(
+            pointSize=40,
+            family=wx.FONTFAMILY_SCRIPT,
+            style=wx.FONTSTYLE_NORMAL,
+            weight=wx.FONTWEIGHT_BOLD,
+        )
+    )
     tw, th = dc.GetTextExtent(text)
     dc.DrawText(text, (imageWidth - tw) / 2, (imageHeight - th) / 2)
     dc.SelectObject(wx.NullBitmap)
@@ -816,8 +855,8 @@ def createNoDataBitmap(imageWidth, imageHeight, text="No data"):
 
 
 def read2_command(*args, **kwargs):
-    kwargs['stdout'] = gcore.PIPE
-    kwargs['stderr'] = gcore.PIPE
+    kwargs["stdout"] = gcore.PIPE
+    kwargs["stderr"] = gcore.PIPE
     ps = gcore.start_command(*args, **kwargs)
     stdout, stderr = ps.communicate()
     return ps.returncode, DecodeString(stdout), DecodeString(stderr)
@@ -830,53 +869,55 @@ def test():
     from animation.data import AnimLayer
     from animation.utils import layerListToCmdsMatrix
     import grass.temporal as tgis
+
     tgis.init()
 
     layerList = LayerList()
     layer = AnimLayer()
-    layer.mapType = 'strds'
-    layer.name = 'JR'
-    layer.cmd = ['d.rast', 'map=elev_2007_1m']
+    layer.mapType = "strds"
+    layer.name = "JR"
+    layer.cmd = ["d.rast", "map=elev_2007_1m"]
     layerList.AddLayer(layer)
 
     layer = Layer()
-    layer.mapType = 'vector'
-    layer.name = 'buildings_2009_approx'
-    layer.cmd = ['d.vect', 'map=buildings_2009_approx',
-                 'color=grey']
+    layer.mapType = "vector"
+    layer.name = "buildings_2009_approx"
+    layer.cmd = ["d.vect", "map=buildings_2009_approx", "color=grey"]
     layer.opacity = 50
     layerList.AddLayer(layer)
 
     bPool = BitmapPool()
     mapFilesPool = MapFilesPool()
 
-    tempDir = '/tmp/test'
+    tempDir = "/tmp/test"
     if os.path.exists(tempDir):
         shutil.rmtree(tempDir)
     os.mkdir(tempDir)
     # comment this line to keep the directory after prgm ends
-#    cleanUp = CleanUp(tempDir)
-#    import atexit
-#    atexit.register(cleanUp)
+    #    cleanUp = CleanUp(tempDir)
+    #    import atexit
+    #    atexit.register(cleanUp)
 
-    prov = BitmapProvider(bPool, mapFilesPool, tempDir,
-                          imageWidth=640, imageHeight=480)
+    prov = BitmapProvider(bPool, mapFilesPool, tempDir, imageWidth=640, imageHeight=480)
     prov.renderingStarted.connect(
-        lambda count: sys.stdout.write(
-            "Total number of maps: {c}\n".format(
-                c=count)))
+        lambda count: sys.stdout.write("Total number of maps: {c}\n".format(c=count))
+    )
     prov.renderingContinues.connect(
         lambda current, text: sys.stdout.write(
-            "Current number: {c}\n".format(
-                c=current)))
-    prov.compositionStarted.connect(lambda count: sys.stdout.write(
-        "Composition: total number of maps: {c}\n".format(c=count)))
+            "Current number: {c}\n".format(c=current)
+        )
+    )
+    prov.compositionStarted.connect(
+        lambda count: sys.stdout.write(
+            "Composition: total number of maps: {c}\n".format(c=count)
+        )
+    )
     prov.compositionContinues.connect(
         lambda current, text: sys.stdout.write(
-            "Composition: Current number: {c}\n".format(
-                c=current)))
-    prov.mapsLoaded.connect(
-        lambda: sys.stdout.write("Maps loading finished\n"))
+            "Composition: Current number: {c}\n".format(c=current)
+        )
+    )
+    prov.mapsLoaded.connect(lambda: sys.stdout.write("Maps loading finished\n"))
     cmdMatrix = layerListToCmdsMatrix(layerList)
     prov.SetCmds(cmdMatrix, [layer.opacity for layer in layerList])
     app = wx.App()
@@ -885,11 +926,9 @@ def test():
 
     for key in bPool.keys():
         if key is not None:
-            bPool[key].SaveFile(
-                os.path.join(
-                    tempDir,
-                    key + '.png'),
-                wx.BITMAP_TYPE_PNG)
+            bPool[key].SaveFile(os.path.join(tempDir, key + ".png"), wx.BITMAP_TYPE_PNG)
+
+
 #    prov.Unload()
 #    prov.SetCmds(cmdMatrix, [l.opacity for l in layerList])
 #    prov.Load(bgcolor=(13, 156, 230))
@@ -903,5 +942,5 @@ def test():
 #    print bPool.keys(), mapFilesPool.keys()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()

+ 136 - 135
gui/wxpython/animation/temporal_manager.py

@@ -71,10 +71,8 @@ class TemporalManager(object):
         :param etype: element type (strds, stvds)
         """
         self._gatherInformation(
-            timeseries,
-            etype,
-            self.timeseriesList,
-            self.timeseriesInfo)
+            timeseries, etype, self.timeseriesList, self.timeseriesInfo
+        )
 
     def EvaluateInputData(self):
         """Checks if all timeseries are compatible (raises GException).
@@ -102,13 +100,15 @@ class TemporalManager(object):
         # check for absolute x relative
         absolute, relative = 0, 0
         for infoDict in self.timeseriesInfo.values():
-            if infoDict['temporal_type'] == 'absolute':
+            if infoDict["temporal_type"] == "absolute":
                 absolute += 1
             else:
                 relative += 1
         if bool(absolute) == bool(relative):
-            message = _("It is not allowed to display data with different "
-                        "temporal types (absolute and relative).")
+            message = _(
+                "It is not allowed to display data with different "
+                "temporal types (absolute and relative)."
+            )
             return False, message
         if absolute:
             self.temporalType = TemporalType.ABSOLUTE
@@ -119,16 +119,17 @@ class TemporalManager(object):
         if relative:
             units = set()
             for infoDict in self.timeseriesInfo.values():
-                units.add(infoDict['unit'])
+                units.add(infoDict["unit"])
             if len(units) > 1:
                 message = _(
-                    "It is not allowed to display data with different units (%s).") % ','.join(units)
+                    "It is not allowed to display data with different units (%s)."
+                ) % ",".join(units)
                 return False, message
 
         # check for interval x point
         interval, point = 0, 0
         for infoDict in self.timeseriesInfo.values():
-            if infoDict['map_time'] == 'interval':
+            if infoDict["map_time"] == "interval":
                 interval += 1
             else:
                 point += 1
@@ -136,20 +137,19 @@ class TemporalManager(object):
             message = _(
                 "You are going to display data with different "
                 "temporal types of maps (interval and point)."
-                " It is recommended to use data of one temporal type to avoid confusion.")
+                " It is recommended to use data of one temporal type to avoid confusion."
+            )
             return True, message  # warning
 
         return True, None
 
     def GetGranularity(self):
-        """Returns temporal granularity of currently loaded timeseries.
-        """
+        """Returns temporal granularity of currently loaded timeseries."""
         if self.dataMode == DataMode.SIMPLE:
-            gran = self.timeseriesInfo[self.timeseriesList[0]]['granularity']
-            if 'unit' in self.timeseriesInfo[
-                    self.timeseriesList[0]]:  # relative:
+            gran = self.timeseriesInfo[self.timeseriesList[0]]["granularity"]
+            if "unit" in self.timeseriesInfo[self.timeseriesList[0]]:  # relative:
                 granNum = gran
-                unit = self.timeseriesInfo[self.timeseriesList[0]]['unit']
+                unit = self.timeseriesInfo[self.timeseriesList[0]]["unit"]
                 if self.granularityMode == GranularityMode.ONE_UNIT:
                     granNum = 1
             else:  # absolute
@@ -165,7 +165,7 @@ class TemporalManager(object):
     def _getCommonGranularity(self):
         allMaps = []
         for dataset in self.timeseriesList:
-            maps = self.timeseriesInfo[dataset]['maps']
+            maps = self.timeseriesInfo[dataset]["maps"]
             allMaps.extend(maps)
 
         if self.temporalType == TemporalType.ABSOLUTE:
@@ -175,15 +175,14 @@ class TemporalManager(object):
                 granNum = 1
             return int(granNum), unit
         if self.temporalType == TemporalType.RELATIVE:
-            unit = self.timeseriesInfo[self.timeseriesList[0]]['unit']
+            unit = self.timeseriesInfo[self.timeseriesList[0]]["unit"]
             granNum = tgis.compute_relative_time_granularity(allMaps)
             if self.granularityMode == GranularityMode.ONE_UNIT:
                 granNum = 1
             return (granNum, unit)
 
     def GetLabelsAndMaps(self):
-        """Returns time labels and map names.
-        """
+        """Returns time labels and map names."""
         mapLists = []
         labelLists = []
         labelListSet = set()
@@ -209,7 +208,7 @@ class TemporalManager(object):
             # compare start time
             while timestamps[i][0] != labelList[0][0]:  # compare
                 i += 1
-            newMapList[i:i + len(mapList)] = mapList
+            newMapList[i : i + len(mapList)] = mapList
             newMapLists.append(newMapList)
 
         mapDict = {}
@@ -219,28 +218,33 @@ class TemporalManager(object):
         if self.temporalType == TemporalType.ABSOLUTE:
             # ('1996-01-01 00:00:00', '1997-01-01 00:00:00', 'year'),
             formatString = UserSettings.Get(
-                group='animation', key='temporal', subkey='format')
+                group="animation", key="temporal", subkey="format"
+            )
             timestamps = [
-                (datetime.datetime.strftime(
-                    st, formatString), datetime.datetime.strftime(
-                    end, formatString) if end is not None else None, unit) for (
-                    st, end, unit) in timestamps]
+                (
+                    datetime.datetime.strftime(st, formatString),
+                    datetime.datetime.strftime(end, formatString)
+                    if end is not None
+                    else None,
+                    unit,
+                )
+                for (st, end, unit) in timestamps
+            ]
         else:
             # ('15', '16', u'years'),
-            timestamps = [(str(st), end if end is None else str(end), unit)
-                          for st, end, unit in timestamps]
+            timestamps = [
+                (str(st), end if end is None else str(end), unit)
+                for st, end, unit in timestamps
+            ]
         return timestamps, mapDict
 
     def _getLabelsAndMaps(self, timeseries):
         """Returns time labels and map names (done by sampling)
         for both interval and point data.
         """
-        sp = tgis.dataset_factory(
-            self.timeseriesInfo[timeseries]['etype'], timeseries)
+        sp = tgis.dataset_factory(self.timeseriesInfo[timeseries]["etype"], timeseries)
         if sp.is_in_db() is False:
-            raise GException(
-                _("Space time dataset <%s> not found.") %
-                timeseries)
+            raise GException(_("Space time dataset <%s> not found.") % timeseries)
         sp.select()
 
         listOfMaps = []
@@ -248,12 +252,12 @@ class TemporalManager(object):
         granNum, unit = self.GetGranularity()
         if self.temporalType == TemporalType.ABSOLUTE:
             if self.granularityMode == GranularityMode.ONE_UNIT:
-                gran = '%(one)d %(unit)s' % {'one': 1, 'unit': unit}
+                gran = "%(one)d %(unit)s" % {"one": 1, "unit": unit}
             else:
-                gran = '%(num)d %(unit)s' % {'num': granNum, 'unit': unit}
+                gran = "%(num)d %(unit)s" % {"num": granNum, "unit": unit}
 
         elif self.temporalType == TemporalType.RELATIVE:
-            unit = self.timeseriesInfo[timeseries]['unit']
+            unit = self.timeseriesInfo[timeseries]["unit"]
             if self.granularityMode == GranularityMode.ONE_UNIT:
                 gran = 1
             else:
@@ -277,7 +281,7 @@ class TemporalManager(object):
                 series = map.get_id()
 
                 start, end = map.get_temporal_extent_as_tuple()
-                if self.timeseriesInfo[timeseries]['map_time'] == 'point':
+                if self.timeseriesInfo[timeseries]["map_time"] == "point":
                     # point data
                     listOfMaps.append(series)
                     afterPoint = True
@@ -322,14 +326,13 @@ class TemporalManager(object):
                 end = tgis.string_to_datetime(end)
                 end = tgis.datetime_to_grass_datetime_string(end)
             grassLabels.append((start, end, unit))
-            if '00:00:00' not in start or (
-                    end is not None and '00:00:00' not in end):
+            if "00:00:00" not in start or (end is not None and "00:00:00" not in end):
                 isTime = True
         if not isTime:
             for i, (start, end, unit) in enumerate(grassLabels):
-                start = start.replace('00:00:00', '').strip()
+                start = start.replace("00:00:00", "").strip()
                 if end is not None:
-                    end = end.replace('00:00:00', '').strip()
+                    end = end.replace("00:00:00", "").strip()
                 grassLabels[i] = (start, end, unit)
         return grassLabels
 
@@ -343,33 +346,33 @@ class TemporalManager(object):
         maps = sp.get_registered_maps_as_objects()
 
         if not sp.check_temporal_topology(maps):
-            raise GException(
-                _("Topology of Space time dataset %s is invalid." % id))
+            raise GException(_("Topology of Space time dataset %s is invalid." % id))
 
         timeseriesList.append(id)
         infoDict[id] = {}
-        infoDict[id]['etype'] = etype
-        infoDict[id]['temporal_type'] = sp.get_temporal_type()
+        infoDict[id]["etype"] = etype
+        infoDict[id]["temporal_type"] = sp.get_temporal_type()
         if sp.is_time_relative():
-            infoDict[id]['unit'] = sp.get_relative_time_unit()
-        infoDict[id]['granularity'] = sp.get_granularity()
-        infoDict[id]['map_time'] = sp.get_map_time()
-        infoDict[id]['maps'] = maps
+            infoDict[id]["unit"] = sp.get_relative_time_unit()
+        infoDict[id]["granularity"] = sp.get_granularity()
+        infoDict[id]["map_time"] = sp.get_map_time()
+        infoDict[id]["maps"] = maps
 
 
 def test():
     from pprint import pprint
+
     # Make sure the temporal database exists
     tgis.init()
 
     temp = TemporalManager()
-#    timeseries = createAbsolutePoint()
-#    timeseries = createRelativePoint()
-#    timeseries1, timeseries2 = createAbsoluteInterval()
+    #    timeseries = createAbsolutePoint()
+    #    timeseries = createRelativePoint()
+    #    timeseries1, timeseries2 = createAbsoluteInterval()
     timeseries1, timeseries2 = createRelativeInterval()
 
-    temp.AddTimeSeries(timeseries1, 'strds')
-    temp.AddTimeSeries(timeseries2, 'strds')
+    temp.AddTimeSeries(timeseries1, "strds")
+    temp.AddTimeSeries(timeseries2, "strds")
 
     try:
         warn = temp.EvaluateInputData()
@@ -378,7 +381,7 @@ def test():
         print(e)
         return
 
-    print('///////////////////////////')
+    print("///////////////////////////")
     gran = temp.GetGranularity()
     print("granularity: " + str(gran))
     pprint(temp.GetLabelsAndMaps())
@@ -386,7 +389,7 @@ def test():
 
 def createAbsoluteInterval():
     grass.run_command(
-        'g.region',
+        "g.region",
         s=0,
         n=80,
         w=0,
@@ -395,8 +398,9 @@ def createAbsoluteInterval():
         t=50,
         res=10,
         res3=10,
-        flags='p3',
-        quiet=True)
+        flags="p3",
+        quiet=True,
+    )
 
     grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
     grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
@@ -412,8 +416,8 @@ def createAbsoluteInterval():
     grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
     grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
 
-    n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
-    fd = open(n1, 'w')
+    n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
+    fd = open(n1, "w")
     fd.write(
         "prec_1|2001-01-01|2001-02-01\n"
         "prec_2|2001-04-01|2001-05-01\n"
@@ -424,8 +428,8 @@ def createAbsoluteInterval():
     )
     fd.close()
 
-    n2 = grass.read_command("g.tempfile", pid=2, flags='d').strip()
-    fd = open(n2, 'w')
+    n2 = grass.read_command("g.tempfile", pid=2, flags="d").strip()
+    fd = open(n2, "w")
     fd.write(
         "temp_1|2000-10-01|2001-01-01\n"
         "temp_2|2001-04-01|2001-05-01\n"
@@ -435,33 +439,34 @@ def createAbsoluteInterval():
         "temp_6|2002-05-01|2002-07-01\n"
     )
     fd.close()
-    name1 = 'absinterval1'
-    name2 = 'absinterval2'
-    grass.run_command('t.unregister', type='raster',
-                      maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,'
-                      'temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
+    name1 = "absinterval1"
+    name2 = "absinterval2"
+    grass.run_command(
+        "t.unregister",
+        type="raster",
+        maps="prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,"
+        "temp_1,temp_2,temp_3,temp_4,temp_5,temp_6",
+    )
     for name, fname in zip((name1, name2), (n1, n2)):
         grass.run_command(
-            't.create',
+            "t.create",
             overwrite=True,
-            type='strds',
-            temporaltype='absolute',
+            type="strds",
+            temporaltype="absolute",
             output=name,
             title="A test with input files",
-            descr="A test with input files")
+            descr="A test with input files",
+        )
         grass.run_command(
-            't.register',
-            flags='i',
-            input=name,
-            file=fname,
-            overwrite=True)
+            "t.register", flags="i", input=name, file=fname, overwrite=True
+        )
 
     return name1, name2
 
 
 def createRelativeInterval():
     grass.run_command(
-        'g.region',
+        "g.region",
         s=0,
         n=80,
         w=0,
@@ -470,8 +475,9 @@ def createRelativeInterval():
         t=50,
         res=10,
         res3=10,
-        flags='p3',
-        quiet=True)
+        flags="p3",
+        quiet=True,
+    )
 
     grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
     grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
@@ -487,8 +493,8 @@ def createRelativeInterval():
     grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
     grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
 
-    n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
-    fd = open(n1, 'w')
+    n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
+    fd = open(n1, "w")
     fd.write(
         "prec_1|1|4\n"
         "prec_2|6|7\n"
@@ -499,8 +505,8 @@ def createRelativeInterval():
     )
     fd.close()
 
-    n2 = grass.read_command("g.tempfile", pid=2, flags='d').strip()
-    fd = open(n2, 'w')
+    n2 = grass.read_command("g.tempfile", pid=2, flags="d").strip()
+    fd = open(n2, "w")
     fd.write(
         "temp_1|5|6\n"
         "temp_2|6|7\n"
@@ -510,33 +516,38 @@ def createRelativeInterval():
         "temp_6|19|22\n"
     )
     fd.close()
-    name1 = 'relinterval1'
-    name2 = 'relinterval2'
-    grass.run_command('t.unregister', type='raster',
-                      maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,'
-                      'temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
+    name1 = "relinterval1"
+    name2 = "relinterval2"
+    grass.run_command(
+        "t.unregister",
+        type="raster",
+        maps="prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,"
+        "temp_1,temp_2,temp_3,temp_4,temp_5,temp_6",
+    )
     for name, fname in zip((name1, name2), (n1, n2)):
         grass.run_command(
-            't.create',
+            "t.create",
             overwrite=True,
-            type='strds',
-            temporaltype='relative',
+            type="strds",
+            temporaltype="relative",
             output=name,
             title="A test with input files",
-            descr="A test with input files")
+            descr="A test with input files",
+        )
         grass.run_command(
-            't.register',
-            flags='i',
+            "t.register",
+            flags="i",
             input=name,
             file=fname,
             unit="years",
-            overwrite=True)
+            overwrite=True,
+        )
     return name1, name2
 
 
 def createAbsolutePoint():
     grass.run_command(
-        'g.region',
+        "g.region",
         s=0,
         n=80,
         w=0,
@@ -545,8 +556,9 @@ def createAbsolutePoint():
         t=50,
         res=10,
         res3=10,
-        flags='p3',
-        quiet=True)
+        flags="p3",
+        quiet=True,
+    )
 
     grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
     grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
@@ -555,8 +567,8 @@ def createAbsolutePoint():
     grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
     grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
 
-    n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
-    fd = open(n1, 'w')
+    n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
+    fd = open(n1, "w")
     fd.write(
         "prec_1|2001-01-01\n"
         "prec_2|2001-03-01\n"
@@ -566,28 +578,24 @@ def createAbsolutePoint():
         "prec_6|2001-09-01\n"
     )
     fd.close()
-    name = 'abspoint'
+    name = "abspoint"
     grass.run_command(
-        't.create',
+        "t.create",
         overwrite=True,
-        type='strds',
-        temporaltype='absolute',
+        type="strds",
+        temporaltype="absolute",
         output=name,
         title="A test with input files",
-        descr="A test with input files")
+        descr="A test with input files",
+    )
 
-    grass.run_command(
-        't.register',
-        flags='i',
-        input=name,
-        file=n1,
-        overwrite=True)
+    grass.run_command("t.register", flags="i", input=name, file=n1, overwrite=True)
     return name
 
 
 def createRelativePoint():
     grass.run_command(
-        'g.region',
+        "g.region",
         s=0,
         n=80,
         w=0,
@@ -596,8 +604,9 @@ def createRelativePoint():
         t=50,
         res=10,
         res3=10,
-        flags='p3',
-        quiet=True)
+        flags="p3",
+        quiet=True,
+    )
 
     grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
     grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
@@ -606,35 +615,27 @@ def createRelativePoint():
     grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
     grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
 
-    n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
-    fd = open(n1, 'w')
+    n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
+    fd = open(n1, "w")
     fd.write(
-        "prec_1|1\n"
-        "prec_2|3\n"
-        "prec_3|5\n"
-        "prec_4|7\n"
-        "prec_5|11\n"
-        "prec_6|13\n"
+        "prec_1|1\n" "prec_2|3\n" "prec_3|5\n" "prec_4|7\n" "prec_5|11\n" "prec_6|13\n"
     )
     fd.close()
-    name = 'relpoint'
+    name = "relpoint"
     grass.run_command(
-        't.create',
+        "t.create",
         overwrite=True,
-        type='strds',
-        temporaltype='relative',
+        type="strds",
+        temporaltype="relative",
         output=name,
         title="A test with input files",
-        descr="A test with input files")
+        descr="A test with input files",
+    )
 
-    grass.run_command(
-        't.register',
-        unit="day",
-        input=name,
-        file=n1,
-        overwrite=True)
+    grass.run_command("t.register", unit="day", input=name, file=n1, overwrite=True)
     return name
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
 
     test()

+ 73 - 80
gui/wxpython/animation/toolbars.py

@@ -24,58 +24,45 @@ from gui_core.simplelmgr import SimpleLmgrToolbar
 from animation.anim import ReplayMode
 
 ganimIcons = {
-    'speed': MetaIcon(
-        img='move',
-        label=_("Change animation speed")),
-    'playForward': MetaIcon(
-        img='execute',
-        label=_("Play forward")),
-    'playBack': MetaIcon(
-        img='player-back',
-        label=_("Play back")),
-    'stop': MetaIcon(
-        img='player-stop',
-        label=_("Stop")),
-    'pause': MetaIcon(
-        img='player-pause',
-        label=_("Pause")),
-    'oneDirectionReplay': MetaIcon(
-        img='redraw',
-        label=_("Repeat")),
-    'bothDirectionReplay': MetaIcon(
-        img='player-repeat-back-forward',
-        label=_("Play back and forward")),
-    'addAnimation': MetaIcon(
-        img='layer-add',
-        label=_("Add new animation"),
-        desc=_("Add new animation")),
-    'editAnimation': MetaIcon(
-        img='layer-more',
+    "speed": MetaIcon(img="move", label=_("Change animation speed")),
+    "playForward": MetaIcon(img="execute", label=_("Play forward")),
+    "playBack": MetaIcon(img="player-back", label=_("Play back")),
+    "stop": MetaIcon(img="player-stop", label=_("Stop")),
+    "pause": MetaIcon(img="player-pause", label=_("Pause")),
+    "oneDirectionReplay": MetaIcon(img="redraw", label=_("Repeat")),
+    "bothDirectionReplay": MetaIcon(
+        img="player-repeat-back-forward", label=_("Play back and forward")
+    ),
+    "addAnimation": MetaIcon(
+        img="layer-add", label=_("Add new animation"), desc=_("Add new animation")
+    ),
+    "editAnimation": MetaIcon(
+        img="layer-more",
         label=_("Add, edit or remove animation"),
-        desc=_("Add, edit or remove animation")),
-    'exportAnimation': MetaIcon(
-        img='layer-export',
-        label=_("Export animation"),
-        desc=_("Export animation"))}
+        desc=_("Add, edit or remove animation"),
+    ),
+    "exportAnimation": MetaIcon(
+        img="layer-export", label=_("Export animation"), desc=_("Export animation")
+    ),
+}
 
 SIMPLE_LMGR_STDS = 256
 
 
 simpleLmgrIcons = {
-    'addSeries': MetaIcon(
-        img='mapset-add',
+    "addSeries": MetaIcon(
+        img="mapset-add",
         label=_("Add space-time dataset or series of map layers"),
-        desc=_("Add space-time dataset or series of map layers for animation")),
+        desc=_("Add space-time dataset or series of map layers for animation"),
+    ),
 }
 
 
 class MainToolbar(BaseToolbar):
-    """Main toolbar (data management)
-    """
+    """Main toolbar (data management)"""
 
     def __init__(self, parent):
-        """Main toolbar constructor
-        """
+        """Main toolbar constructor"""
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
@@ -89,23 +76,24 @@ class MainToolbar(BaseToolbar):
         # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
         icons = ganimIcons
         return self._getToolbarData(
-            (("addAnimation", icons["addAnimation"],
-              self.parent.OnAddAnimation),
-             ("editAnimation", icons["editAnimation"],
-              self.parent.OnEditAnimation),
-             ("reload", BaseIcons["render"],
-              self.parent.Reload),
-             ("exportAnimation", icons["exportAnimation"],
-              self.parent.OnExportAnimation)))
+            (
+                ("addAnimation", icons["addAnimation"], self.parent.OnAddAnimation),
+                ("editAnimation", icons["editAnimation"], self.parent.OnEditAnimation),
+                ("reload", BaseIcons["render"], self.parent.Reload),
+                (
+                    "exportAnimation",
+                    icons["exportAnimation"],
+                    self.parent.OnExportAnimation,
+                ),
+            )
+        )
 
 
 class AnimationToolbar(BaseToolbar):
-    """Animation toolbar (to control animation)
-    """
+    """Animation toolbar (to control animation)"""
 
     def __init__(self, parent):
-        """Animation toolbar constructor
-        """
+        """Animation toolbar constructor"""
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
@@ -122,22 +110,28 @@ class AnimationToolbar(BaseToolbar):
         # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
         icons = ganimIcons
         return self._getToolbarData(
-            (("playBack", icons["playBack"],
-              self.OnPlayBack),
-             ("playForward", icons["playForward"],
-              self.OnPlayForward),
-             ("pause", icons["pause"],
-              self.OnPause, wx.ITEM_CHECK),
-             ("stop", icons["stop"],
-              self.OnStop),
-             (None,),
-             ("oneDirectionReplay", icons["oneDirectionReplay"],
-              self.OnOneDirectionReplay, wx.ITEM_CHECK),
-             ("bothDirectionReplay", icons["bothDirectionReplay"],
-              self.OnBothDirectionReplay, wx.ITEM_CHECK),
-             (None,),
-             ("adjustSpeed", icons['speed'],
-              self.parent.OnAdjustSpeed)))
+            (
+                ("playBack", icons["playBack"], self.OnPlayBack),
+                ("playForward", icons["playForward"], self.OnPlayForward),
+                ("pause", icons["pause"], self.OnPause, wx.ITEM_CHECK),
+                ("stop", icons["stop"], self.OnStop),
+                (None,),
+                (
+                    "oneDirectionReplay",
+                    icons["oneDirectionReplay"],
+                    self.OnOneDirectionReplay,
+                    wx.ITEM_CHECK,
+                ),
+                (
+                    "bothDirectionReplay",
+                    icons["bothDirectionReplay"],
+                    self.OnBothDirectionReplay,
+                    wx.ITEM_CHECK,
+                ),
+                (None,),
+                ("adjustSpeed", icons["speed"], self.parent.OnAdjustSpeed),
+            )
+        )
 
     def OnPlayForward(self, event):
         self.PlayForward()
@@ -220,12 +214,10 @@ class AnimationToolbar(BaseToolbar):
 
 
 class MiscToolbar(BaseToolbar):
-    """Toolbar with miscellaneous tools related to app
-    """
+    """Toolbar with miscellaneous tools related to app"""
 
     def __init__(self, parent):
-        """Toolbar constructor
-        """
+        """Toolbar constructor"""
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
@@ -234,13 +226,13 @@ class MiscToolbar(BaseToolbar):
 
     def _toolbarData(self):
         """Toolbar data"""
-        return self._getToolbarData((("settings", BaseIcons['settings'],
-                                      self.parent.OnPreferences),
-                                     ("help", BaseIcons['help'],
-                                      self.parent.OnHelp),
-                                     ("quit", BaseIcons['quit'],
-                                      self.parent.OnCloseWindow),
-                                     ))
+        return self._getToolbarData(
+            (
+                ("settings", BaseIcons["settings"], self.parent.OnPreferences),
+                ("help", BaseIcons["help"], self.parent.OnHelp),
+                ("quit", BaseIcons["quit"], self.parent.OnCloseWindow),
+            )
+        )
 
 
 class AnimSimpleLmgrToolbar(SimpleLmgrToolbar):
@@ -254,8 +246,9 @@ class AnimSimpleLmgrToolbar(SimpleLmgrToolbar):
     def _toolbarData(self):
         data = SimpleLmgrToolbar._toolbarData(self)
         if self._style & SIMPLE_LMGR_STDS:
-            data.insert(0, ('addSeries', simpleLmgrIcons['addSeries'],
-                            self.parent.OnAddStds))
+            data.insert(
+                0, ("addSeries", simpleLmgrIcons["addSeries"], self.parent.OnAddStds)
+            )
         return data
 
     def EnableTools(self, tools, enable=True):

+ 87 - 67
gui/wxpython/animation/utils.py

@@ -22,8 +22,10 @@ import wx
 import hashlib
 import six
 from multiprocessing import cpu_count
+
 try:
     from PIL import Image
+
     hasPIL = True
 except ImportError:
     hasPIL = False
@@ -57,20 +59,18 @@ class ReplayMode:
     REPEAT = 3
 
 
-def validateTimeseriesName(timeseries, etype='strds'):
+def validateTimeseriesName(timeseries, etype="strds"):
     """Checks if space time dataset exists and completes missing mapset.
 
     Raises GException if dataset doesn't exist.
     """
     trastDict = tgis.tlist_grouped(etype)
     if timeseries.find("@") >= 0:
-        nameShort, mapset = timeseries.split('@', 1)
+        nameShort, mapset = timeseries.split("@", 1)
         if nameShort in trastDict[mapset]:
             return timeseries
         else:
-            raise GException(
-                _("Space time dataset <%s> not found.") %
-                timeseries)
+            raise GException(_("Space time dataset <%s> not found.") % timeseries)
 
     mapsets = tgis.get_tgis_c_library_interface().available_mapsets()
     for mapset in mapsets:
@@ -92,7 +92,7 @@ def validateMapNames(names, etype):
     newNames = []
     for name in names:
         if name.find("@") >= 0:
-            nameShort, mapset = name.split('@', 1)
+            nameShort, mapset = name.split("@", 1)
             if nameShort in mapDict[mapset]:
                 newNames.append(name)
             else:
@@ -136,12 +136,12 @@ def getNameAndLayer(name):
     ('name', '2')
     """
     mapset = layer = None
-    if '@' in name:
-        name, mapset = name.split('@')
-    if ':' in name:
-        name, layer = name.split(':')
+    if "@" in name:
+        name, mapset = name.split("@")
+    if ":" in name:
+        name, layer = name.split(":")
     if mapset:
-        name = name + '@' + mapset
+        name = name + "@" + mapset
     return name, layer
 
 
@@ -153,66 +153,89 @@ def checkSeriesCompatibility(mapSeriesList=None, timeseriesList=None):
 
     :return: number of maps for animation
     """
-    timeseriesInfo = {'count': set(), 'temporalType': set(), 'mapType': set(),
-                      'mapTimes': set()}
+    timeseriesInfo = {
+        "count": set(),
+        "temporalType": set(),
+        "mapType": set(),
+        "mapTimes": set(),
+    }
 
     if timeseriesList:
         for stds, etype in timeseriesList:
             sp = tgis.open_old_stds(stds, etype)
             mapType = sp.get_map_time()  # interval, ...
             tempType = sp.get_initial_values()[0]  # absolute
-            timeseriesInfo['mapType'].add(mapType)
-            timeseriesInfo['temporalType'].add(tempType)
-            rows = sp.get_registered_maps_as_objects(where=None,
-                                                     order="start_time")
+            timeseriesInfo["mapType"].add(mapType)
+            timeseriesInfo["temporalType"].add(tempType)
+            rows = sp.get_registered_maps_as_objects(where=None, order="start_time")
 
             if rows:
                 times = []
-                timeseriesInfo['count'].add(len(rows))
+                timeseriesInfo["count"].add(len(rows))
                 for row in rows:
-                    if tempType == 'absolute':
+                    if tempType == "absolute":
                         time = row.get_absolute_time()
                     else:
                         time = row.get_relative_time()
                     times.append(time)
-                timeseriesInfo['mapTimes'].add(tuple(times))
+                timeseriesInfo["mapTimes"].add(tuple(times))
             else:
-                timeseriesInfo['mapTimes'].add(None)
-                timeseriesInfo['count'].add(None)
-
-    if len(timeseriesInfo['count']) > 1:
-        raise GException(_("The number of maps in space-time datasets "
-                           "has to be the same."))
-
-    if len(timeseriesInfo['temporalType']) > 1:
-        raise GException(_("The temporal type (absolute/relative) of space-time datasets "
-                           "has to be the same."))
-
-    if len(timeseriesInfo['mapType']) > 1:
-        raise GException(_("The map type (point/interval) of space-time datasets "
-                           "has to be the same."))
-
-    if len(timeseriesInfo['mapTimes']) > 1:
-        raise GException(_("The temporal extents of maps in space-time datasets "
-                           "have to be the same."))
+                timeseriesInfo["mapTimes"].add(None)
+                timeseriesInfo["count"].add(None)
+
+    if len(timeseriesInfo["count"]) > 1:
+        raise GException(
+            _("The number of maps in space-time datasets " "has to be the same.")
+        )
+
+    if len(timeseriesInfo["temporalType"]) > 1:
+        raise GException(
+            _(
+                "The temporal type (absolute/relative) of space-time datasets "
+                "has to be the same."
+            )
+        )
+
+    if len(timeseriesInfo["mapType"]) > 1:
+        raise GException(
+            _(
+                "The map type (point/interval) of space-time datasets "
+                "has to be the same."
+            )
+        )
+
+    if len(timeseriesInfo["mapTimes"]) > 1:
+        raise GException(
+            _(
+                "The temporal extents of maps in space-time datasets "
+                "have to be the same."
+            )
+        )
 
     if mapSeriesList:
         count = set()
         for mapSeries in mapSeriesList:
             count.add(len(mapSeries))
         if len(count) > 1:
-            raise GException(_("The number of maps to animate has to be "
-                               "the same for each map series."))
+            raise GException(
+                _(
+                    "The number of maps to animate has to be "
+                    "the same for each map series."
+                )
+            )
 
-        if timeseriesList and list(count)[0] != list(
-                timeseriesInfo['count'])[0]:
-            raise GException(_("The number of maps to animate has to be "
-                               "the same as the number of maps in temporal dataset."))
+        if timeseriesList and list(count)[0] != list(timeseriesInfo["count"])[0]:
+            raise GException(
+                _(
+                    "The number of maps to animate has to be "
+                    "the same as the number of maps in temporal dataset."
+                )
+            )
 
     if mapSeriesList:
         return list(count)[0]
     if timeseriesList:
-        return list(timeseriesInfo['count'])[0]
+        return list(timeseriesInfo["count"])[0]
 
 
 def ComputeScaledRect(sourceSize, destSize):
@@ -234,15 +257,15 @@ def ComputeScaledRect(sourceSize, destSize):
         width = int(sourceSize[0] * scale + 0.5)
         height = int(sourceSize[1] * scale + 0.5)
         x = 0
-        y = int((destSize[1] - height) / 2. + 0.5)
+        y = int((destSize[1] - height) / 2.0 + 0.5)
     else:
         scale = ratio2
         width = int(sourceSize[0] * scale + 0.5)
         height = int(sourceSize[1] * scale + 0.5)
         y = 0
-        x = int((destSize[0] - width) / 2. + 0.5)
+        x = int((destSize[0] - width) / 2.0 + 0.5)
 
-    return {'width': width, 'height': height, 'x': x, 'y': y, 'scale': scale}
+    return {"width": width, "height": height, "x": x, "y": y, "scale": scale}
 
 
 def RenderText(text, font, bgcolor, fgcolor):
@@ -264,14 +287,14 @@ def RenderText(text, font, bgcolor, fgcolor):
 
 def WxImageToPil(image):
     """Converts wx.Image to PIL image"""
-    pilImage = Image.new('RGB', (image.GetWidth(), image.GetHeight()))
+    pilImage = Image.new("RGB", (image.GetWidth(), image.GetHeight()))
     pilImage.frombytes(bytes(image.GetData()))
     return pilImage
 
 
 def HashCmd(cmd, region):
     """Returns a hash from command given as a list and a region as a dict."""
-    name = '_'.join(cmd)
+    name = "_".join(cmd)
     if region:
         name += str(sorted(region.items()))
     return hashlib.sha1(encode(name)).hexdigest()
@@ -279,20 +302,20 @@ def HashCmd(cmd, region):
 
 def HashCmds(cmds, region):
     """Returns a hash from list of commands and regions as dicts."""
-    name = ';'.join([item for sublist in cmds for item in sublist])
+    name = ";".join([item for sublist in cmds for item in sublist])
     if region:
         name += str(sorted(region.items()))
     return hashlib.sha1(encode(name)).hexdigest()
 
 
-def GetFileFromCmd(dirname, cmd, region, extension='ppm'):
+def GetFileFromCmd(dirname, cmd, region, extension="ppm"):
     """Returns file path created as a hash from command and region."""
-    return os.path.join(dirname, HashCmd(cmd, region) + '.' + extension)
+    return os.path.join(dirname, HashCmd(cmd, region) + "." + extension)
 
 
-def GetFileFromCmds(dirname, cmds, region, extension='ppm'):
+def GetFileFromCmds(dirname, cmds, region, extension="ppm"):
     """Returns file path created as a hash from list of commands and regions."""
-    return os.path.join(dirname, HashCmds(cmds, region) + '.' + extension)
+    return os.path.join(dirname, HashCmds(cmds, region) + "." + extension)
 
 
 def layerListToCmdsMatrix(layerList):
@@ -303,7 +326,7 @@ def layerListToCmdsMatrix(layerList):
     """
     count = 0
     for layer in layerList:
-        if layer.active and hasattr(layer, 'maps'):
+        if layer.active and hasattr(layer, "maps"):
             # assuming the consistency of map number is checked already
             count = len(layer.maps)
             break
@@ -311,24 +334,21 @@ def layerListToCmdsMatrix(layerList):
     for layer in layerList:
         if not layer.active:
             continue
-        if hasattr(layer, 'maps'):
+        if hasattr(layer, "maps"):
             for i, part in enumerate(layer.cmd):
-                if part.startswith('map='):
+                if part.startswith("map="):
                     cmd = layer.cmd[:]
                     cmds = []
                     for map_ in layer.maps:
                         # check if dataset uses layers instead of maps
                         mapName, mapLayer = getNameAndLayer(map_)
-                        cmd[i] = 'map={name}'.format(name=mapName)
+                        cmd[i] = "map={name}".format(name=mapName)
                         if mapLayer:
                             try:
-                                idx = cmd.index('layer')
-                                cmd[idx] = 'layer={layer}'.format(
-                                    layer=mapLayer)
+                                idx = cmd.index("layer")
+                                cmd[idx] = "layer={layer}".format(layer=mapLayer)
                             except ValueError:
-                                cmd.append(
-                                    'layer={layer}'.format(
-                                        layer=mapLayer))
+                                cmd.append("layer={layer}".format(layer=mapLayer))
                         cmds.append(cmd[:])
                     cmdsForComposition.append(cmds)
         else:
@@ -342,7 +362,7 @@ def sampleCmdMatrixAndCreateNames(cmdMatrix, sampledSeries, regions):
     to the command matrix."""
     namesList = []
     j = -1
-    lastName = ''
+    lastName = ""
     for name in sampledSeries:
         if name is not None:
             if lastName != name:
@@ -351,7 +371,7 @@ def sampleCmdMatrixAndCreateNames(cmdMatrix, sampledSeries, regions):
             namesList.append(HashCmds(cmdMatrix[j], regions[j]))
         else:
             namesList.append(None)
-    assert(j == len(cmdMatrix) - 1)
+    assert j == len(cmdMatrix) - 1
     return namesList
 
 

+ 14 - 14
gui/wxpython/core/__init__.py

@@ -1,16 +1,16 @@
 all = [
-    'debug',
-    'gcmd',
-    'treemodel',
-    'ws',
-    'menutree',
-    'gconsole',
-    'events',
-    'render',
-    'units',
-    'settings',
-    'workspace',
-    'utils',
-    'globalvar',
-    'giface',
+    "debug",
+    "gcmd",
+    "treemodel",
+    "ws",
+    "menutree",
+    "gconsole",
+    "events",
+    "render",
+    "units",
+    "settings",
+    "workspace",
+    "utils",
+    "globalvar",
+    "giface",
 ]

+ 17 - 12
gui/wxpython/core/debug.py

@@ -28,7 +28,7 @@ import grass.script as grass
 class DebugMsg:
     """wxGUI debugging
 
-        g.gisenv set=WX_DEBUG=[0-5]
+    g.gisenv set=WX_DEBUG=[0-5]
 
     """
 
@@ -39,18 +39,18 @@ class DebugMsg:
         self.SetLevel()
 
     def SetLevel(self):
-        """Initialize gui debug level
-        """
+        """Initialize gui debug level"""
         try:
-            self.debuglevel = int(grass.gisenv().get('WX_DEBUG', 0))
+            self.debuglevel = int(grass.gisenv().get("WX_DEBUG", 0))
             if self.debuglevel < 0 or self.debuglevel > 5:
-                raise ValueError(
-                    _("Wx debug level {0}.").format(
-                        self.debuglevel))
+                raise ValueError(_("Wx debug level {0}.").format(self.debuglevel))
         except ValueError as e:
             self.debuglevel = 0
             sys.stderr.write(
-                _("WARNING: Ignoring unsupported wx debug level (must be >=0 and <=5). {0}\n").format(e))
+                _(
+                    "WARNING: Ignoring unsupported wx debug level (must be >=0 and <=5). {0}\n"
+                ).format(e)
+            )
 
     def msg(self, level, message, *args):
         """Print debug message
@@ -62,16 +62,21 @@ class DebugMsg:
         # self.SetLevel()
         if self.debuglevel > 0 and level > 0 and level <= self.debuglevel:
             if args:
-                sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) +
-                                 message % args + os.linesep)
+                sys.stderr.write(
+                    "GUI D%d/%d: " % (level, self.debuglevel)
+                    + message % args
+                    + os.linesep
+                )
             else:
-                sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) +
-                                 message + os.linesep)
+                sys.stderr.write(
+                    "GUI D%d/%d: " % (level, self.debuglevel) + message + os.linesep
+                )
             sys.stderr.flush()  # force flush (required for MS Windows)
 
     def GetLevel(self):
         """Return current GUI debug level"""
         return self.debuglevel
 
+
 # Debug instance
 Debug = DebugMsg()

+ 140 - 119
gui/wxpython/core/gcmd.py

@@ -38,7 +38,7 @@ import subprocess
 from threading import Thread
 import wx
 
-is_mswindows = sys.platform == 'win32'
+is_mswindows = sys.platform == "win32"
 if is_mswindows:
     from win32file import ReadFile, WriteFile
     from win32pipe import PeekNamedPipe
@@ -89,7 +89,6 @@ def EncodeString(string):
 
 
 class GError:
-
     def __init__(self, message, parent=None, caption=None, showTraceback=True):
         """Show error message window
 
@@ -99,55 +98,43 @@ class GError:
         :param showTraceback: True to show also Python traceback
         """
         if not caption:
-            caption = _('Error')
+            caption = _("Error")
         style = wx.OK | wx.ICON_ERROR | wx.CENTRE
         exc_type, exc_value, exc_traceback = sys.exc_info()
         if exc_traceback:
             exception = traceback.format_exc()
-            reason = exception.splitlines()[-1].split(':', 1)[-1].strip()
+            reason = exception.splitlines()[-1].split(":", 1)[-1].strip()
 
         if Debug.GetLevel() > 0 and exc_traceback:
             sys.stderr.write(exception)
 
         if showTraceback and exc_traceback:
-            wx.MessageBox(parent=parent,
-                          message=message + '\n\n%s: %s\n\n%s' %
-                          (_('Reason'),
-                           reason, exception),
-                          caption=caption,
-                          style=style)
+            wx.MessageBox(
+                parent=parent,
+                message=message + "\n\n%s: %s\n\n%s" % (_("Reason"), reason, exception),
+                caption=caption,
+                style=style,
+            )
         else:
-            wx.MessageBox(parent=parent,
-                          message=message,
-                          caption=caption,
-                          style=style)
+            wx.MessageBox(parent=parent, message=message, caption=caption, style=style)
 
 
 class GWarning:
-
     def __init__(self, message, parent=None):
-        caption = _('Warning')
+        caption = _("Warning")
         style = wx.OK | wx.ICON_WARNING | wx.CENTRE
-        wx.MessageBox(parent=parent,
-                      message=message,
-                      caption=caption,
-                      style=style)
+        wx.MessageBox(parent=parent, message=message, caption=caption, style=style)
 
 
 class GMessage:
-
     def __init__(self, message, parent=None):
-        caption = _('Message')
+        caption = _("Message")
         style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
-        wx.MessageBox(parent=parent,
-                      message=message,
-                      caption=caption,
-                      style=style)
+        wx.MessageBox(parent=parent, message=message, caption=caption, style=style)
 
 
 class GException(Exception):
-
-    def __init__(self, value=''):
+    def __init__(self, value=""):
         self.value = value
 
     def __str__(self):
@@ -175,7 +162,7 @@ class Popen(subprocess.Popen):
                         if "=" in args[i]:
                             a = args[i].split("=")
                             k = a[0] + "="
-                            v = "=".join(a[1:len(a)])
+                            v = "=".join(a[1 : len(a)])
                         else:
                             k = ""
                             v = args[i]
@@ -194,12 +181,12 @@ class Popen(subprocess.Popen):
         subprocess.Popen.__init__(self, args, **kwargs)
 
     def recv(self, maxsize=None):
-        return self._recv('stdout', maxsize)
+        return self._recv("stdout", maxsize)
 
     def recv_err(self, maxsize=None):
-        return self._recv('stderr', maxsize)
+        return self._recv("stderr", maxsize)
 
-    def send_recv(self, input='', maxsize=None):
+    def send_recv(self, input="", maxsize=None):
         return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
 
     def get_conn_maxsize(self, which, maxsize):
@@ -217,28 +204,31 @@ class Popen(subprocess.Popen):
         """Try to kill running process"""
         if is_mswindows:
             import win32api
+
             handle = win32api.OpenProcess(1, 0, self.pid)
-            return (0 != win32api.TerminateProcess(handle, 0))
+            return 0 != win32api.TerminateProcess(handle, 0)
         else:
             try:
                 os.kill(-self.pid, signal.SIGTERM)  # kill whole group
             except OSError:
                 pass
 
-    if sys.platform == 'win32':
+    if sys.platform == "win32":
+
         def send(self, input):
             if not self.stdin:
                 return None
 
             import pywintypes
+
             try:
                 x = msvcrt.get_osfhandle(self.stdin.fileno())
                 (errCode, written) = WriteFile(x, input)
             except ValueError:
-                return self._close('stdin')
+                return self._close("stdin")
             except (pywintypes.error, Exception) as why:
                 if why.winerror in (109, errno.ESHUTDOWN):
-                    return self._close('stdin')
+                    return self._close("stdin")
                 raise
 
             return written
@@ -249,6 +239,7 @@ class Popen(subprocess.Popen):
                 return None
 
             import pywintypes
+
             try:
                 x = msvcrt.get_osfhandle(conn.fileno())
                 (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
@@ -268,6 +259,7 @@ class Popen(subprocess.Popen):
             return read
 
     else:
+
         def send(self, input):
             if not self.stdin:
                 return None
@@ -279,7 +271,7 @@ class Popen(subprocess.Popen):
                 written = os.write(self.stdin.fileno(), input)
             except OSError as why:
                 if why[0] == errno.EPIPE:  # broken pipe
-                    return self._close('stdin')
+                    return self._close("stdin")
                 raise
 
             return written
@@ -295,7 +287,7 @@ class Popen(subprocess.Popen):
 
             try:
                 if not select.select([conn], [], [], 0)[0]:
-                    return ''
+                    return ""
 
                 r = conn.read()
 
@@ -309,15 +301,16 @@ class Popen(subprocess.Popen):
                 if not conn.closed:
                     fcntl.fcntl(conn, fcntl.F_SETFL, flags)
 
+
 message = "Other end disconnected!"
 
 
-def recv_some(p, t=.1, e=1, tr=5, stderr=0):
+def recv_some(p, t=0.1, e=1, tr=5, stderr=0):
     if tr < 1:
         tr = 1
     x = time.time() + t
     y = []
-    r = ''
+    r = ""
     pr = p.recv
     if stderr:
         pr = p.recv_err
@@ -332,7 +325,7 @@ def recv_some(p, t=.1, e=1, tr=5, stderr=0):
             y.append(decode(r))
         else:
             time.sleep(max((x - time.time()) / tr, 0))
-    return ''.join(y)
+    return "".join(y)
 
 
 class Command:
@@ -352,9 +345,16 @@ class Command:
             print 'FAILURE (%d)' % cmd.returncode
     """
 
-    def __init__(self, cmd, stdin=None,
-                 verbose=None, wait=True, rerr=False,
-                 stdout=None, stderr=None):
+    def __init__(
+        self,
+        cmd,
+        stdin=None,
+        verbose=None,
+        wait=True,
+        rerr=False,
+        stdout=None,
+        stderr=None,
+    ):
         """
         :param cmd: command given as list
         :param stdin: standard input stream
@@ -366,7 +366,7 @@ class Command:
         :param stdout:  redirect standard output or None
         :param stderr:  redirect standard error output or None
         """
-        Debug.msg(1, "gcmd.Command(): %s" % ' '.join(cmd))
+        Debug.msg(1, "gcmd.Command(): %s" % " ".join(cmd))
         self.cmd = cmd
         self.stderr = stderr
 
@@ -374,13 +374,14 @@ class Command:
         # set verbosity level
         #
         verbose_orig = None
-        if ('--q' not in self.cmd and '--quiet' not in self.cmd) and \
-                ('--v' not in self.cmd and '--verbose' not in self.cmd):
+        if ("--q" not in self.cmd and "--quiet" not in self.cmd) and (
+            "--v" not in self.cmd and "--verbose" not in self.cmd
+        ):
             if verbose is not None:
                 if verbose == 0:
-                    self.cmd.append('--quiet')
+                    self.cmd.append("--quiet")
                 elif verbose == 3:
-                    self.cmd.append('--verbose')
+                    self.cmd.append("--verbose")
                 else:
                     verbose_orig = os.getenv("GRASS_VERBOSE")
                     os.environ["GRASS_VERBOSE"] = str(verbose)
@@ -388,8 +389,7 @@ class Command:
         #
         # create command thread
         #
-        self.cmdThread = CommandThread(cmd, stdin,
-                                       stdout, stderr)
+        self.cmdThread = CommandThread(cmd, stdin, stdout, stderr)
         self.cmdThread.start()
 
         if wait:
@@ -405,32 +405,38 @@ class Command:
 
         if self.returncode is not None:
             Debug.msg(
-                3, "Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" %
-                (' '.join(cmd), wait, self.returncode, self.cmdThread.isAlive()))
+                3,
+                "Command(): cmd='%s', wait=%s, returncode=%d, alive=%s"
+                % (" ".join(cmd), wait, self.returncode, self.cmdThread.isAlive()),
+            )
             if rerr is not None and self.returncode != 0:
                 if rerr is False:  # GUI dialog
-                    raise GException("%s '%s'%s%s%s %s%s" %
-                                     (_("Execution failed:"),
-                                      ' '.join(self.cmd),
-                                      os.linesep, os.linesep,
-                                      _("Details:"),
-                                      os.linesep,
-                                      _("Error: ") + self.__GetError()))
+                    raise GException(
+                        "%s '%s'%s%s%s %s%s"
+                        % (
+                            _("Execution failed:"),
+                            " ".join(self.cmd),
+                            os.linesep,
+                            os.linesep,
+                            _("Details:"),
+                            os.linesep,
+                            _("Error: ") + self.__GetError(),
+                        )
+                    )
                 elif rerr == sys.stderr:  # redirect message to sys
-                    stderr.write("Execution failed: '%s'" %
-                                 (' '.join(self.cmd)))
+                    stderr.write("Execution failed: '%s'" % (" ".join(self.cmd)))
                     stderr.write(
-                        "%sDetails:%s%s" %
-                        (os.linesep,
-                         _("Error: ") +
-                            self.__GetError(),
-                            os.linesep))
+                        "%sDetails:%s%s"
+                        % (os.linesep, _("Error: ") + self.__GetError(), os.linesep)
+                    )
             else:
                 pass  # nop
         else:
             Debug.msg(
-                3, "Command(): cmd='%s', wait=%s, returncode=?, alive=%s" %
-                (' '.join(cmd), wait, self.cmdThread.isAlive()))
+                3,
+                "Command(): cmd='%s', wait=%s, returncode=?, alive=%s"
+                % (" ".join(cmd), wait, self.cmdThread.isAlive()),
+            )
 
         if verbose_orig:
             os.environ["GRASS_VERBOSE"] = verbose_orig
@@ -451,7 +457,7 @@ class Command:
             line = stream.readline()
             if not line:
                 break
-            line = line.replace('%s' % os.linesep, '').strip()
+            line = line.replace("%s" % os.linesep, "").strip()
             lineList.append(line)
 
         return lineList
@@ -469,8 +475,9 @@ class Command:
         if self.stderr is None:
             lines = self.__ReadErrOutput()
         else:
-            lines = self.cmdThread.error.strip('%s' % os.linesep). \
-                split('%s' % os.linesep)
+            lines = self.cmdThread.error.strip("%s" % os.linesep).split(
+                "%s" % os.linesep
+            )
 
         msg = []
 
@@ -479,18 +486,18 @@ class Command:
         for line in lines:
             if len(line) == 0:
                 continue
-            if 'GRASS_' in line:  # error or warning
-                if 'GRASS_INFO_WARNING' in line:  # warning
+            if "GRASS_" in line:  # error or warning
+                if "GRASS_INFO_WARNING" in line:  # warning
                     type = "WARNING"
-                elif 'GRASS_INFO_ERROR' in line:  # error
+                elif "GRASS_INFO_ERROR" in line:  # error
                     type = "ERROR"
-                elif 'GRASS_INFO_END':  # end of message
+                elif "GRASS_INFO_END":  # end of message
                     msg.append((type, content))
                     type = None
                     content = ""
 
                 if type:
-                    content += line.split(':', 1)[1].strip()
+                    content += line.split(":", 1)[1].strip()
             else:  # stderr
                 msg.append((None, line.strip()))
 
@@ -499,21 +506,20 @@ class Command:
     def __GetError(self):
         """Get error message or ''"""
         if not self.cmdThread.module:
-            return _("Unable to exectute command: '%s'") % ' '.join(self.cmd)
+            return _("Unable to exectute command: '%s'") % " ".join(self.cmd)
 
         for type, msg in self.__ProcessStdErr():
-            if type == 'ERROR':
+            if type == "ERROR":
                 return msg
 
-        return ''
+        return ""
 
 
 class CommandThread(Thread):
     """Create separate thread for command. Used for commands launched
     on the background."""
 
-    def __init__(self, cmd, env=None, stdin=None,
-                 stdout=sys.stdout, stderr=sys.stderr):
+    def __init__(self, cmd, env=None, stdin=None, stdout=sys.stdout, stderr=sys.stderr):
         """
         :param cmd: command (given as list)
         :param env: environmental variables
@@ -530,7 +536,7 @@ class CommandThread(Thread):
         self.env = env
 
         self.module = None
-        self.error = ''
+        self.error = ""
 
         self._want_abort = False
         self.aborted = False
@@ -552,7 +558,7 @@ class CommandThread(Thread):
         if len(self.cmd) == 0:
             return
 
-        Debug.msg(1, "gcmd.CommandThread(): %s" % ' '.join(self.cmd))
+        Debug.msg(1, "gcmd.CommandThread(): %s" % " ".join(self.cmd))
 
         self.startTime = time.time()
 
@@ -561,7 +567,7 @@ class CommandThread(Thread):
         # also unifying this with 3 other places in code would be nice
         # changing from one chdir to get_real_command function
         args = self.cmd
-        if sys.platform == 'win32':
+        if sys.platform == "win32":
             if os.path.splitext(args[0])[1] == SCT_EXT:
                 args[0] = args[0][:-3]
             # using Python executable to run the module if it is a script
@@ -569,16 +575,18 @@ class CommandThread(Thread):
             # cannot use make_command for this now because it is used in GUI
             # The same code is in grass.script.core already twice.
             args[0] = grass.get_real_command(args[0])
-            if args[0].endswith('.py'):
+            if args[0].endswith(".py"):
                 args.insert(0, sys.executable)
 
         try:
-            self.module = Popen(args,
-                                stdin=subprocess.PIPE,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE,
-                                shell=sys.platform == "win32",
-                                env=self.env)
+            self.module = Popen(
+                args,
+                stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+                shell=sys.platform == "win32",
+                env=self.env,
+            )
 
         except OSError as e:
             self.error = str(e)
@@ -639,29 +647,39 @@ class CommandThread(Thread):
 
 
 def _formatMsg(text):
-    """Format error messages for dialogs
-    """
-    message = ''
+    """Format error messages for dialogs"""
+    message = ""
     for line in text.splitlines():
         if len(line) == 0:
             continue
-        elif 'GRASS_INFO_MESSAGE' in line:
-            message += line.split(':', 1)[1].strip() + '\n'
-        elif 'GRASS_INFO_WARNING' in line:
-            message += line.split(':', 1)[1].strip() + '\n'
-        elif 'GRASS_INFO_ERROR' in line:
-            message += line.split(':', 1)[1].strip() + '\n'
-        elif 'GRASS_INFO_END' in line:
+        elif "GRASS_INFO_MESSAGE" in line:
+            message += line.split(":", 1)[1].strip() + "\n"
+        elif "GRASS_INFO_WARNING" in line:
+            message += line.split(":", 1)[1].strip() + "\n"
+        elif "GRASS_INFO_ERROR" in line:
+            message += line.split(":", 1)[1].strip() + "\n"
+        elif "GRASS_INFO_END" in line:
             return message
         else:
-            message += line.strip() + '\n'
+            message += line.strip() + "\n"
 
     return message
 
 
-def RunCommand(prog, flags="", overwrite=False, quiet=False,
-               verbose=False, parent=None, read=False,
-               parse=None, stdin=None, getErrorMsg=False, env=None, **kwargs):
+def RunCommand(
+    prog,
+    flags="",
+    overwrite=False,
+    quiet=False,
+    verbose=False,
+    parent=None,
+    read=False,
+    parse=None,
+    stdin=None,
+    getErrorMsg=False,
+    env=None,
+    **kwargs,
+):
     """Run GRASS command
 
     :param prog: program to run
@@ -683,18 +701,19 @@ def RunCommand(prog, flags="", overwrite=False, quiet=False,
     :return: returncode, stdout, messages (read == True and getErrorMsg == True)
     :return: stdout, stderr
     """
-    cmdString = ' '.join(grass.make_command(prog, flags, overwrite,
-                                            quiet, verbose, **kwargs))
+    cmdString = " ".join(
+        grass.make_command(prog, flags, overwrite, quiet, verbose, **kwargs)
+    )
 
     Debug.msg(1, "gcmd.RunCommand(): %s" % cmdString)
 
-    kwargs['stderr'] = subprocess.PIPE
+    kwargs["stderr"] = subprocess.PIPE
 
     if read:
-        kwargs['stdout'] = subprocess.PIPE
+        kwargs["stdout"] = subprocess.PIPE
 
     if stdin:
-        kwargs['stdin'] = subprocess.PIPE
+        kwargs["stdin"] = subprocess.PIPE
 
     # Do not change the environment, only a local copy.
     if env:
@@ -703,7 +722,7 @@ def RunCommand(prog, flags="", overwrite=False, quiet=False,
         env = os.environ.copy()
 
     if parent:
-        env['GRASS_MESSAGE_FORMAT'] = 'standard'
+        env["GRASS_MESSAGE_FORMAT"] = "standard"
 
     start = time.time()
 
@@ -719,8 +738,11 @@ def RunCommand(prog, flags="", overwrite=False, quiet=False,
     stdout = decode(stdout) if read else stdout
 
     ret = ps.returncode
-    Debug.msg(1, "gcmd.RunCommand(): get return code %d (%.6f sec)" %
-              (ret, (time.time() - start)))
+    Debug.msg(
+        1,
+        "gcmd.RunCommand(): get return code %d (%.6f sec)"
+        % (ret, (time.time() - start)),
+    )
 
     if ret != 0:
         if stderr:
@@ -729,9 +751,7 @@ def RunCommand(prog, flags="", overwrite=False, quiet=False,
             Debug.msg(2, "gcmd.RunCommand(): nothing to print ???")
 
         if parent:
-            GError(parent=parent,
-                   caption=_("Error in %s") % prog,
-                   message=stderr)
+            GError(parent=parent, caption=_("Error in %s") % prog, message=stderr)
 
     if not read:
         if not getErrorMsg:
@@ -764,8 +784,8 @@ def GetDefaultEncoding(forceUTF8=False):
     :return: system encoding (can be None)
     """
     enc = locale.getdefaultlocale()[1]
-    if forceUTF8 and (enc is None or enc == 'UTF8'):
-        return 'UTF-8'
+    if forceUTF8 and (enc is None or enc == "UTF8"):
+        return "UTF-8"
 
     if enc is None:
         enc = locale.getpreferredencoding()
@@ -773,4 +793,5 @@ def GetDefaultEncoding(forceUTF8=False):
     Debug.msg(1, "GetSystemEncoding(): %s" % enc)
     return enc
 
+
 _enc = GetDefaultEncoding()  # define as global variable

+ 227 - 184
gui/wxpython/core/gconsole.py

@@ -62,12 +62,12 @@ wxCmdPrepare, EVT_CMD_PREPARE = NewEvent()
 
 def GrassCmd(cmd, env=None, stdout=None, stderr=None):
     """Return GRASS command thread"""
-    return CommandThread(cmd, env=env,
-                         stdout=stdout, stderr=stderr)
+    return CommandThread(cmd, env=env, stdout=stdout, stderr=stderr)
 
 
 class CmdThread(threading.Thread):
     """Thread for GRASS commands"""
+
     requestId = 0
 
     def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
@@ -119,41 +119,49 @@ class CmdThread(threading.Thread):
         CmdThread.requestId = id
 
     def run(self):
-        os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
+        os.environ["GRASS_MESSAGE_FORMAT"] = "gui"
         while True:
             requestId, args, kwds = self.requestQ.get()
-            for key in ('callable', 'onDone', 'onPrepare',
-                        'userData', 'addLayer', 'notification'):
+            for key in (
+                "callable",
+                "onDone",
+                "onPrepare",
+                "userData",
+                "addLayer",
+                "notification",
+            ):
                 if key in kwds:
                     vars()[key] = kwds[key]
                     del kwds[key]
                 else:
                     vars()[key] = None
 
-            if not vars()['callable']:
-                vars()['callable'] = GrassCmd
+            if not vars()["callable"]:
+                vars()["callable"] = GrassCmd
 
             requestTime = time.time()
 
             # prepare
             if self.receiver:
-                event = wxCmdPrepare(cmd=args[0],
-                                     time=requestTime,
-                                     pid=requestId,
-                                     onPrepare=vars()['onPrepare'],
-                                     userData=vars()['userData'])
+                event = wxCmdPrepare(
+                    cmd=args[0],
+                    time=requestTime,
+                    pid=requestId,
+                    onPrepare=vars()["onPrepare"],
+                    userData=vars()["userData"],
+                )
 
                 wx.PostEvent(self.receiver, event)
 
                 # run command
-                event = wxCmdRun(cmd=args[0],
-                                 pid=requestId,
-                                 notification=vars()['notification'])
+                event = wxCmdRun(
+                    cmd=args[0], pid=requestId, notification=vars()["notification"]
+                )
 
                 wx.PostEvent(self.receiver, event)
 
-            time.sleep(.1)
-            self.requestCmd = vars()['callable'](*args, **kwds)
+            time.sleep(0.1)
+            self.requestCmd = vars()["callable"](*args, **kwds)
             if self._want_abort_all and self.requestCmd is not None:
                 self.requestCmd.abort()
                 if self.requestQ.empty():
@@ -171,47 +179,54 @@ class CmdThread(threading.Thread):
             except AttributeError:
                 aborted = False
 
-            time.sleep(.1)
+            time.sleep(0.1)
 
             # set default color table for raster data
-            if UserSettings.Get(group='rasterLayer',
-                                key='colorTable', subkey='enabled') and \
-                    args[0][0][:2] == 'r.':
-                colorTable = UserSettings.Get(group='rasterLayer',
-                                              key='colorTable',
-                                              subkey='selection')
+            if (
+                UserSettings.Get(
+                    group="rasterLayer", key="colorTable", subkey="enabled"
+                )
+                and args[0][0][:2] == "r."
+            ):
+                colorTable = UserSettings.Get(
+                    group="rasterLayer", key="colorTable", subkey="selection"
+                )
                 mapName = None
-                if args[0][0] == 'r.mapcalc':
+                if args[0][0] == "r.mapcalc":
                     try:
-                        mapName = args[0][1].split('=', 1)[0].strip()
+                        mapName = args[0][1].split("=", 1)[0].strip()
                     except KeyError:
                         pass
                 else:
                     moduleInterface = GUI(show=None).ParseCommand(args[0])
-                    outputParam = moduleInterface.get_param(value='output',
-                                                            raiseError=False)
-                    if outputParam and outputParam['prompt'] == 'raster':
-                        mapName = outputParam['value']
+                    outputParam = moduleInterface.get_param(
+                        value="output", raiseError=False
+                    )
+                    if outputParam and outputParam["prompt"] == "raster":
+                        mapName = outputParam["value"]
 
                 if mapName:
                     argsColor = list(args)
-                    argsColor[0] = ['r.colors',
-                                    'map=%s' % mapName,
-                                    'color=%s' % colorTable]
-                    self.requestCmdColor = vars()['callable'](
-                        *argsColor, **kwds)
+                    argsColor[0] = [
+                        "r.colors",
+                        "map=%s" % mapName,
+                        "color=%s" % colorTable,
+                    ]
+                    self.requestCmdColor = vars()["callable"](*argsColor, **kwds)
                     self.resultQ.put((requestId, self.requestCmdColor.run()))
 
             if self.receiver:
-                event = wxCmdDone(cmd=args[0],
-                                  aborted=aborted,
-                                  returncode=returncode,
-                                  time=requestTime,
-                                  pid=requestId,
-                                  onDone=vars()['onDone'],
-                                  userData=vars()['userData'],
-                                  addLayer=vars()['addLayer'],
-                                  notification=vars()['notification'])
+                event = wxCmdDone(
+                    cmd=args[0],
+                    aborted=aborted,
+                    returncode=returncode,
+                    time=requestTime,
+                    pid=requestId,
+                    onDone=vars()["onDone"],
+                    userData=vars()["userData"],
+                    addLayer=vars()["addLayer"],
+                    notification=vars()["notification"],
+                )
 
                 # send event
                 wx.PostEvent(self.receiver, event)
@@ -248,15 +263,14 @@ class GStdout:
         pass
 
     def write(self, s):
-        if len(s) == 0 or s == '\n':
+        if len(s) == 0 or s == "\n":
             return
 
         for line in s.splitlines():
             if len(line) == 0:
                 continue
 
-            evt = wxCmdOutput(text=line + '\n',
-                              type='')
+            evt = wxCmdOutput(text=line + "\n", type="")
             wx.PostEvent(self.receiver, evt)
 
 
@@ -277,8 +291,8 @@ class GStderr:
         :param receiver: event receiver (used in PostEvent)
         """
         self.receiver = receiver
-        self.type = ''
-        self.message = ''
+        self.type = ""
+        self.message = ""
         self.printMessage = False
 
     def flush(self):
@@ -295,39 +309,37 @@ class GStderr:
             if len(line) == 0:
                 continue
 
-            if 'GRASS_INFO_PERCENT' in line:
-                value = int(line.rsplit(':', 1)[1].strip())
+            if "GRASS_INFO_PERCENT" in line:
+                value = int(line.rsplit(":", 1)[1].strip())
                 if value >= 0 and value < 100:
                     progressValue = value
                 else:
                     progressValue = 0
-            elif 'GRASS_INFO_MESSAGE' in line:
-                self.type = 'message'
-                self.message += line.split(':', 1)[1].strip() + '\n'
-            elif 'GRASS_INFO_WARNING' in line:
-                self.type = 'warning'
-                self.message += line.split(':', 1)[1].strip() + '\n'
-            elif 'GRASS_INFO_ERROR' in line:
-                self.type = 'error'
-                self.message += line.split(':', 1)[1].strip() + '\n'
-            elif 'GRASS_INFO_END' in line:
+            elif "GRASS_INFO_MESSAGE" in line:
+                self.type = "message"
+                self.message += line.split(":", 1)[1].strip() + "\n"
+            elif "GRASS_INFO_WARNING" in line:
+                self.type = "warning"
+                self.message += line.split(":", 1)[1].strip() + "\n"
+            elif "GRASS_INFO_ERROR" in line:
+                self.type = "error"
+                self.message += line.split(":", 1)[1].strip() + "\n"
+            elif "GRASS_INFO_END" in line:
                 self.printMessage = True
-            elif self.type == '':
+            elif self.type == "":
                 if len(line) == 0:
                     continue
-                evt = wxCmdOutput(text=line,
-                                  type='')
+                evt = wxCmdOutput(text=line, type="")
                 wx.PostEvent(self.receiver, evt)
             elif len(line) > 0:
-                self.message += line.strip() + '\n'
+                self.message += line.strip() + "\n"
 
             if self.printMessage and len(self.message) > 0:
-                evt = wxCmdOutput(text=self.message,
-                                  type=self.type)
+                evt = wxCmdOutput(text=self.message, type=self.type)
                 wx.PostEvent(self.receiver, evt)
 
-                self.type = ''
-                self.message = ''
+                self.type = ""
+                self.message = ""
                 self.printMessage = False
 
         # update progress message
@@ -343,8 +355,7 @@ gIgnoredCmdRun, EVT_IGNORED_CMD_RUN = NewEvent()
 
 
 class GConsole(wx.EvtHandler):
-    """
-    """
+    """"""
 
     def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None):
         """
@@ -358,17 +369,17 @@ class GConsole(wx.EvtHandler):
 
         # Signal when some map is created or updated by a module.
         # attributes: name: map name, ltype: map type,
-        self.mapCreated = Signal('GConsole.mapCreated')
+        self.mapCreated = Signal("GConsole.mapCreated")
         # emitted when map display should be re-render
-        self.updateMap = Signal('GConsole.updateMap')
+        self.updateMap = Signal("GConsole.updateMap")
         # emitted when log message should be written
-        self.writeLog = Signal('GConsole.writeLog')
+        self.writeLog = Signal("GConsole.writeLog")
         # emitted when command log message should be written
-        self.writeCmdLog = Signal('GConsole.writeCmdLog')
+        self.writeCmdLog = Signal("GConsole.writeCmdLog")
         # emitted when warning message should be written
-        self.writeWarning = Signal('GConsole.writeWarning')
+        self.writeWarning = Signal("GConsole.writeWarning")
         # emitted when error message should be written
-        self.writeError = Signal('GConsole.writeError')
+        self.writeError = Signal("GConsole.writeError")
 
         self._guiparent = guiparent
         self._giface = giface
@@ -392,8 +403,7 @@ class GConsole(wx.EvtHandler):
         self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
 
     def Redirect(self):
-        """Redirect stdout/stderr
-        """
+        """Redirect stdout/stderr"""
         if Debug.GetLevel() == 0 and grass.debug_level(force=True) == 0:
             # don't redirect when debugging is enabled
             sys.stdout = self.cmdStdOut
@@ -412,27 +422,25 @@ class GConsole(wx.EvtHandler):
                 sys.stdout = sys.__stdout__
                 sys.stderr = sys.__stderr__
 
-    def WriteLog(self, text, style=None, wrap=None,
-                 notification=Notification.HIGHLIGHT):
+    def WriteLog(
+        self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT
+    ):
         """Generic method for writing log message in
         given style
 
         :param text: text line
         :param notification: form of notification
         """
-        self.writeLog.emit(text=text, wrap=wrap,
-                           notification=notification)
+        self.writeLog.emit(text=text, wrap=wrap, notification=notification)
 
-    def WriteCmdLog(self, text, pid=None,
-                    notification=Notification.MAKE_VISIBLE):
+    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
         """Write message in selected style
 
         :param text: message to be printed
         :param pid: process pid or None
         :param notification: form of notification
         """
-        self.writeCmdLog.emit(text=text, pid=pid,
-                              notification=notification)
+        self.writeCmdLog.emit(text=text, pid=pid, notification=notification)
 
     def WriteWarning(self, text):
         """Write message in warning style"""
@@ -442,9 +450,18 @@ class GConsole(wx.EvtHandler):
         """Write message in error style"""
         self.writeError.emit(text=text)
 
-    def RunCmd(self, command, compReg=True, env=None, skipInterface=False,
-               onDone=None, onPrepare=None, userData=None, addLayer=None,
-               notification=Notification.MAKE_VISIBLE):
+    def RunCmd(
+        self,
+        command,
+        compReg=True,
+        env=None,
+        skipInterface=False,
+        onDone=None,
+        onPrepare=None,
+        userData=None,
+        addLayer=None,
+        notification=Notification.MAKE_VISIBLE,
+    ):
         """Run command typed into console command prompt (GPrompt).
 
         .. todo::
@@ -475,14 +492,17 @@ class GConsole(wx.EvtHandler):
             return
 
         # update history file
-        self.UpdateHistoryFile(' '.join(command))
+        self.UpdateHistoryFile(" ".join(command))
 
         if command[0] in globalvar.grassCmd:
             # send GRASS command without arguments to GUI command interface
             # except ignored commands (event is emitted)
-            if self._ignoredCmdPattern and \
-                    re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \
-                    '--help' not in command and '--ui' not in command:
+            if (
+                self._ignoredCmdPattern
+                and re.compile(self._ignoredCmdPattern).search(" ".join(command))
+                and "--help" not in command
+                and "--ui" not in command
+            ):
                 event = gIgnoredCmdRun(cmd=command)
                 wx.PostEvent(self, event)
                 return
@@ -492,67 +512,66 @@ class GConsole(wx.EvtHandler):
                 try:
                     task = GUI(show=None).ParseCommand(command)
                 except GException as e:
-                    GError(parent=self._guiparent,
-                           message=str(e),
-                           showTraceback=False)
+                    GError(parent=self._guiparent, message=str(e), showTraceback=False)
                     return
 
                 hasParams = False
                 if task:
                     options = task.get_options()
-                    hasParams = options['params'] and options['flags']
+                    hasParams = options["params"] and options["flags"]
                     # check for <input>=-
-                    for p in options['params']:
-                        if p.get('prompt', '') == 'input' and \
-                                p.get('element', '') == 'file' and \
-                                p.get('age', 'new') == 'old' and \
-                                p.get('value', '') == '-':
+                    for p in options["params"]:
+                        if (
+                            p.get("prompt", "") == "input"
+                            and p.get("element", "") == "file"
+                            and p.get("age", "new") == "old"
+                            and p.get("value", "") == "-"
+                        ):
                             GError(
                                 parent=self._guiparent,
                                 message=_(
                                     "Unable to run command:\n%(cmd)s\n\n"
                                     "Option <%(opt)s>: read from standard input is not "
-                                    "supported by wxGUI") % {
-                                    'cmd': ' '.join(command),
-                                    'opt': p.get(
-                                        'name',
-                                        '')})
+                                    "supported by wxGUI"
+                                )
+                                % {"cmd": " ".join(command), "opt": p.get("name", "")},
+                            )
                             return
 
                 if len(command) == 1:
-                    if command[0].startswith('g.gui.'):
+                    if command[0].startswith("g.gui."):
                         import imp
                         import inspect
+
                         pyFile = command[0]
-                        if sys.platform == 'win32':
-                            pyFile += '.py'
-                        pyPath = os.path.join(
-                            os.environ['GISBASE'], 'scripts', pyFile)
+                        if sys.platform == "win32":
+                            pyFile += ".py"
+                        pyPath = os.path.join(os.environ["GISBASE"], "scripts", pyFile)
                         if not os.path.exists(pyPath):
                             pyPath = os.path.join(
-                                os.environ['GRASS_ADDON_BASE'], 'scripts', pyFile)
+                                os.environ["GRASS_ADDON_BASE"], "scripts", pyFile
+                            )
                         if not os.path.exists(pyPath):
                             GError(
                                 parent=self._guiparent,
-                                message=_("Module <%s> not found.") %
-                                command[0])
-                        pymodule = imp.load_source(
-                            command[0].replace('.', '_'), pyPath)
+                                message=_("Module <%s> not found.") % command[0],
+                            )
+                        pymodule = imp.load_source(command[0].replace(".", "_"), pyPath)
                         try:  # PY3
                             pymain = inspect.getfullargspec(pymodule.main)
                         except AttributeError:
                             pymain = inspect.getargspec(pymodule.main)
-                        if pymain and 'giface' in pymain.args:
+                        if pymain and "giface" in pymain.args:
                             pymodule.main(self._giface)
                             return
 
                     # no arguments given
-                    if hasParams and \
-                       not isinstance(self._guiparent, FormNotebook):
+                    if hasParams and not isinstance(self._guiparent, FormNotebook):
                         # also parent must be checked, see #3135 for details
                         try:
-                            GUI(parent=self._guiparent,
-                                giface=self._giface).ParseCommand(command)
+                            GUI(
+                                parent=self._guiparent, giface=self._giface
+                            ).ParseCommand(command)
                         except GException as e:
                             print(e, file=sys.stderr)
 
@@ -568,13 +587,17 @@ class GConsole(wx.EvtHandler):
                     del env["GRASS_REGION"]
 
                 # process GRASS command with argument
-                self.cmdThread.RunCmd(command,
-                                      stdout=self.cmdStdOut,
-                                      stderr=self.cmdStdErr,
-                                      onDone=onDone, onPrepare=onPrepare,
-                                      userData=userData, addLayer=addLayer,
-                                      env=env,
-                                      notification=notification)
+                self.cmdThread.RunCmd(
+                    command,
+                    stdout=self.cmdStdOut,
+                    stderr=self.cmdStdErr,
+                    onDone=onDone,
+                    onPrepare=onPrepare,
+                    userData=userData,
+                    addLayer=addLayer,
+                    env=env,
+                    notification=notification,
+                )
                 self.cmdOutputTimer.Start(50)
 
                 # we don't need to change computational region settings
@@ -587,20 +610,21 @@ class GConsole(wx.EvtHandler):
             # of the script)
 
             # check if we ignore the command (similar to grass commands part)
-            if self._ignoredCmdPattern and \
-               re.compile(self._ignoredCmdPattern).search(' '.join(command)):
+            if self._ignoredCmdPattern and re.compile(self._ignoredCmdPattern).search(
+                " ".join(command)
+            ):
                 event = gIgnoredCmdRun(cmd=command)
                 wx.PostEvent(self, event)
                 return
 
             skipInterface = True
-            if os.path.splitext(command[0])[1] in ('.py', '.sh'):
+            if os.path.splitext(command[0])[1] in (".py", ".sh"):
                 try:
                     sfile = open(command[0], "r")
                     for line in sfile.readlines():
                         if len(line) < 2:
                             continue
-                        if line[0] == '#' and line[1] == '%':
+                        if line[0] == "#" and line[1] == "%":
                             skipInterface = False
                             break
                     sfile.close()
@@ -617,16 +641,19 @@ class GConsole(wx.EvtHandler):
 
             if task:
                 # process GRASS command without argument
-                GUI(parent=self._guiparent,
-                    giface=self._giface).ParseCommand(command)
+                GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command)
             else:
-                self.cmdThread.RunCmd(command,
-                                      stdout=self.cmdStdOut,
-                                      stderr=self.cmdStdErr,
-                                      onDone=onDone, onPrepare=onPrepare,
-                                      userData=userData, addLayer=addLayer,
-                                      env=env,
-                                      notification=notification)
+                self.cmdThread.RunCmd(
+                    command,
+                    stdout=self.cmdStdOut,
+                    stderr=self.cmdStdErr,
+                    onDone=onDone,
+                    onPrepare=onPrepare,
+                    userData=userData,
+                    addLayer=addLayer,
+                    env=env,
+                    notification=notification,
+                )
             self.cmdOutputTimer.Start(50)
 
     def GetLog(self, err=False):
@@ -653,8 +680,10 @@ class GConsole(wx.EvtHandler):
 
     def OnCmdRun(self, event):
         """Run command"""
-        self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)),
-                         notification=event.notification)
+        self.WriteCmdLog(
+            "(%s)\n%s" % (str(time.ctime()), " ".join(event.cmd)),
+            notification=event.notification,
+        )
         event.Skip()
 
     def OnCmdDone(self, event):
@@ -671,28 +700,36 @@ class GConsole(wx.EvtHandler):
             else:
                 mtime = int(ctime / 60)
                 stime = _("%(min)d min %(sec)d sec") % {
-                    'min': mtime, 'sec': int(ctime - (mtime * 60))}
+                    "min": mtime,
+                    "sec": int(ctime - (mtime * 60)),
+                }
         except KeyError:
             # stopped deamon
             stime = _("unknown")
 
         if event.aborted:
             # Thread aborted (using our convention of None return)
-            self.WriteWarning(_('Please note that the data are left in'
-                                ' inconsistent state and may be corrupted'))
-            msg = _('Command aborted')
+            self.WriteWarning(
+                _(
+                    "Please note that the data are left in"
+                    " inconsistent state and may be corrupted"
+                )
+            )
+            msg = _("Command aborted")
         else:
-            msg = _('Command finished')
+            msg = _("Command finished")
 
-        self.WriteCmdLog('(%s) %s (%s)' % (str(time.ctime()), msg, stime),
-                         notification=event.notification)
+        self.WriteCmdLog(
+            "(%s) %s (%s)" % (str(time.ctime()), msg, stime),
+            notification=event.notification,
+        )
 
         if event.onDone:
             event.onDone(event)
 
         self.cmdOutputTimer.Stop()
 
-        if event.cmd[0] == 'g.gisenv':
+        if event.cmd[0] == "g.gisenv":
             Debug.SetLevel()
             self.Redirect()
 
@@ -713,38 +750,43 @@ class GConsole(wx.EvtHandler):
             return
 
         name = task.get_name()
-        for p in task.get_options()['params']:
-            prompt = p.get('prompt', '')
-            if prompt in (
-                    'raster', 'vector', 'raster_3d') and p.get(
-                    'value', None):
-                if p.get('age', 'old') == 'new' or name in (
-                        'r.colors', 'r3.colors', 'v.colors', 'v.proj', 'r.proj'):
+        for p in task.get_options()["params"]:
+            prompt = p.get("prompt", "")
+            if prompt in ("raster", "vector", "raster_3d") and p.get("value", None):
+                if p.get("age", "old") == "new" or name in (
+                    "r.colors",
+                    "r3.colors",
+                    "v.colors",
+                    "v.proj",
+                    "r.proj",
+                ):
                     # if multiple maps (e.g. r.series.interp), we need add each
-                    if p.get('multiple', False):
-                        lnames = p.get('value').split(',')
+                    if p.get("multiple", False):
+                        lnames = p.get("value").split(",")
                         # in case multiple input (old) maps in r.colors
                         # we don't want to rerender it multiple times! just
                         # once
-                        if p.get('age', 'old') == 'old':
+                        if p.get("age", "old") == "old":
                             lnames = lnames[0:1]
                     else:
-                        lnames = [p.get('value')]
+                        lnames = [p.get("value")]
                     for lname in lnames:
-                        if '@' not in lname:
-                            lname += '@' + grass.gisenv()['MAPSET']
-                        if grass.find_file(lname, element=p.get('element'))[
-                                'fullname']:
+                        if "@" not in lname:
+                            lname += "@" + grass.gisenv()["MAPSET"]
+                        if grass.find_file(lname, element=p.get("element"))["fullname"]:
                             self.mapCreated.emit(
-                                name=lname, ltype=prompt, add=event.addLayer)
+                                name=lname, ltype=prompt, add=event.addLayer
+                            )
                             gisenv = grass.gisenv()
-                            self._giface.grassdbChanged.emit(grassdb=gisenv['GISDBASE'],
-                                                             location=gisenv['LOCATION_NAME'],
-                                                             mapset=gisenv['MAPSET'],
-                                                             action='new',
-                                                             map=lname.split('@')[0],
-                                                             element=prompt)
-        if name == 'r.mask':
+                            self._giface.grassdbChanged.emit(
+                                grassdb=gisenv["GISDBASE"],
+                                location=gisenv["LOCATION_NAME"],
+                                mapset=gisenv["MAPSET"],
+                                action="new",
+                                map=lname.split("@")[0],
+                                element=prompt,
+                            )
+        if name == "r.mask":
             self.updateMap.emit()
 
         event.Skip()
@@ -759,15 +801,16 @@ class GConsole(wx.EvtHandler):
         """
         env = grass.gisenv()
         try:
-            filePath = os.path.join(env['GISDBASE'],
-                                    env['LOCATION_NAME'],
-                                    env['MAPSET'],
-                                    '.bash_history')
-            fileHistory = codecs.open(filePath, encoding='utf-8', mode='a')
+            filePath = os.path.join(
+                env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], ".bash_history"
+            )
+            fileHistory = codecs.open(filePath, encoding="utf-8", mode="a")
         except IOError as e:
-            GError(_("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") %
-                   {'filePath': filePath, 'error': e},
-                   parent=self._guiparent)
+            GError(
+                _("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s")
+                % {"filePath": filePath, "error": e},
+                parent=self._guiparent,
+            )
             return
 
         try:

+ 41 - 41
gui/wxpython/core/giface.py

@@ -32,6 +32,7 @@ class Notification:
     The value is the suggestion how user should be notified
     about the new message.
     """
+
     NO_NOTIFICATION = 0
     HIGHLIGHT = 1
     MAKE_VISIBLE = 2
@@ -46,11 +47,11 @@ class Layer(object):
         Current implementations only provides all attributes of existing
         layer as used in lmgr.
     """
+
     pass
 
 
 class LayerList(object):
-
     def GetSelectedLayers(self, checkedOnly=True):
         """Returns list of selected layers.
 
@@ -71,8 +72,7 @@ class LayerList(object):
         """
         raise NotImplementedError()
 
-    def AddLayer(self, ltype, name=None, checked=None,
-                 opacity=1.0, cmd=None):
+    def AddLayer(self, ltype, name=None, checked=None, opacity=1.0, cmd=None):
         """Adds a new layer to the layer list.
 
         Launches property dialog if needed (raster, vector, etc.)
@@ -118,29 +118,23 @@ class GrassInterface:
     """
 
     def RunCmd(self, *args, **kwargs):
-        """Executes a command.
-        """
+        """Executes a command."""
         raise NotImplementedError()
 
     def Help(self, entry):
-        """Shows a manual page for a given entry.
-        """
+        """Shows a manual page for a given entry."""
         raise NotImplementedError()
 
     def WriteLog(self, text, wrap=None, notification=Notification.HIGHLIGHT):
-        """Writes log message.
-        """
+        """Writes log message."""
         raise NotImplementedError()
 
-    def WriteCmdLog(self, text, pid=None,
-                    notification=Notification.MAKE_VISIBLE):
-        """Writes message related to start or end of the command.
-        """
+    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
+        """Writes message related to start or end of the command."""
         raise NotImplementedError()
 
     def WriteWarning(self, text):
-        """Writes warning message for the user.
-        """
+        """Writes warning message for the user."""
         raise NotImplementedError()
 
     def WriteError(self, text):
@@ -156,8 +150,7 @@ class GrassInterface:
         raise NotImplementedError()
 
     def GetLayerList(self):
-        """Returns a layer management object.
-        """
+        """Returns a layer management object."""
         raise NotImplementedError()
 
     def GetMapDisplay(self):
@@ -220,10 +213,12 @@ class StandaloneGrassInterface(GrassInterface):
         # Used for adding/refreshing displayed layers.
         # attributes: name: map name, ltype: map type,
         # add: if map should be added to layer tree (questionable attribute)
-        self.mapCreated = Signal('StandaloneGrassInterface.mapCreated')
+        self.mapCreated = Signal("StandaloneGrassInterface.mapCreated")
 
         # Signal for communicating current mapset has been switched
-        self.currentMapsetChanged = Signal('StandaloneGrassInterface.currentMapsetChanged')
+        self.currentMapsetChanged = Signal(
+            "StandaloneGrassInterface.currentMapsetChanged"
+        )
 
         # Signal for communicating something in current grassdb has changed.
         # Parameters:
@@ -234,17 +229,16 @@ class StandaloneGrassInterface(GrassInterface):
         # mapset: mapset name, required when element is 'mapset', 'raster', 'vector' or 'raster_3d'
         # map: map name, required when element is 'raster', 'vector' or 'raster_3d'
         # newname: new name (of mapset, map), required with action='rename'
-        self.grassdbChanged = Signal('StandaloneGrassInterface.grassdbChanged')
+        self.grassdbChanged = Signal("StandaloneGrassInterface.grassdbChanged")
 
         # Signal emitted to request updating of map
-        self.updateMap = Signal('StandaloneGrassInterface.updateMap')
+        self.updateMap = Signal("StandaloneGrassInterface.updateMap")
 
         # Signal emitted when workspace is changed
-        self.workspaceChanged = Signal('StandaloneGrassInterface.workspaceChanged')
+        self.workspaceChanged = Signal("StandaloneGrassInterface.workspaceChanged")
 
         # workaround, standalone grass interface should be moved to sep. file
-        from core.gconsole import GConsole, \
-            EVT_CMD_OUTPUT, EVT_CMD_PROGRESS
+        from core.gconsole import GConsole, EVT_CMD_OUTPUT, EVT_CMD_PROGRESS
 
         self._gconsole = GConsole()
         self._gconsole.Bind(EVT_CMD_PROGRESS, self._onCmdProgress)
@@ -259,9 +253,9 @@ class StandaloneGrassInterface(GrassInterface):
         message = event.text
         style = event.type
 
-        if style == 'warning':
+        if style == "warning":
             self.WriteWarning(message)
-        elif style == 'error':
+        elif style == "error":
             self.WriteError(message)
         else:
             self.WriteLog(message)
@@ -272,9 +266,18 @@ class StandaloneGrassInterface(GrassInterface):
         grass.percent(event.value, 100, 1)
         event.Skip()
 
-    def RunCmd(self, command, compReg=True, env=None, skipInterface=False,
-               onDone=None, onPrepare=None, userData=None, addLayer=None,
-               notification=Notification.MAKE_VISIBLE):
+    def RunCmd(
+        self,
+        command,
+        compReg=True,
+        env=None,
+        skipInterface=False,
+        onDone=None,
+        onPrepare=None,
+        userData=None,
+        addLayer=None,
+        notification=Notification.MAKE_VISIBLE,
+    ):
         self._gconsole.RunCmd(
             command=command,
             compReg=compReg,
@@ -284,19 +287,18 @@ class StandaloneGrassInterface(GrassInterface):
             onPrepare=onPrepare,
             userData=userData,
             addLayer=addLayer,
-            notification=notification)
+            notification=notification,
+        )
 
     def Help(self, entry):
-        self._gconsole.RunCmd(['g.manual', 'entry=%s' % entry])
+        self._gconsole.RunCmd(["g.manual", "entry=%s" % entry])
 
-    def WriteLog(self, text, wrap=None,
-                 notification=Notification.HIGHLIGHT):
+    def WriteLog(self, text, wrap=None, notification=Notification.HIGHLIGHT):
         self._write(grass.message, text)
 
-    def WriteCmdLog(self, text, pid=None,
-                    notification=Notification.MAKE_VISIBLE):
+    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
         if pid:
-            text = '(' + str(pid) + ') ' + text
+            text = "(" + str(pid) + ") " + text
         self._write(grass.message, text)
 
     def WriteWarning(self, text):
@@ -307,7 +309,7 @@ class StandaloneGrassInterface(GrassInterface):
 
     def _write(self, function, text):
         orig = os.getenv("GRASS_MESSAGE_FORMAT")
-        os.environ["GRASS_MESSAGE_FORMAT"] = 'standard'
+        os.environ["GRASS_MESSAGE_FORMAT"] = "standard"
         function(text)
         os.environ["GRASS_MESSAGE_FORMAT"] = orig
 
@@ -318,13 +320,11 @@ class StandaloneGrassInterface(GrassInterface):
         return None
 
     def GetMapDisplay(self):
-        """Get current map display.
-        """
+        """Get current map display."""
         return None
 
     def GetAllMapDisplays(self):
-        """Get list of all map displays.
-        """
+        """Get list of all map displays."""
         return []
 
     def GetMapWindow(self):

+ 45 - 41
gui/wxpython/core/globalvar.py

@@ -74,7 +74,7 @@ def version_as_string(version):
 
 
 def CheckWxPhoenix():
-    if 'phoenix' in wx.version():
+    if "phoenix" in wx.version():
         return True
     return False
 
@@ -92,25 +92,27 @@ def CheckWxVersion(version):
 
 def CheckForWx():
     """Try to import wx module"""
-    if 'wx' in sys.modules.keys():
+    if "wx" in sys.modules.keys():
         return
 
     try:
         import wx
+
         version = parse_version_string(wx.__version__)
         if version < WXPY3_MIN_VERSION:
-            raise ValueError(
-                "Your wxPython version is {}".format(wx.__version__))
+            raise ValueError("Your wxPython version is {}".format(wx.__version__))
         return
     except ImportError as e:
-        print('ERROR: wxGUI requires wxPython. {}'.format(e),
-              file=sys.stderr)
-        print('You can still use GRASS GIS modules in'
-              ' the command line or in Python.', file=sys.stderr)
+        print("ERROR: wxGUI requires wxPython. {}".format(e), file=sys.stderr)
+        print(
+            "You can still use GRASS GIS modules in" " the command line or in Python.",
+            file=sys.stderr,
+        )
         sys.exit(1)
     except locale.Error as e:
         print("Unable to set locale:", e, file=sys.stderr)
-        os.environ['LC_ALL'] = ''
+        os.environ["LC_ALL"] = ""
+
 
 if not os.getenv("GRASS_WXBUNDLED"):
     CheckForWx()
@@ -120,31 +122,31 @@ import wx  # noqa: E402
 if CheckWxPhoenix():
     try:
         import agw.flatnotebook as FN
-    except ImportError: # if it's not there locally, try the wxPython lib.
+    except ImportError:  # if it's not there locally, try the wxPython lib.
         import wx.lib.agw.flatnotebook as FN
 else:
     import wx.lib.flatnotebook as FN
 
 
-
 """
 Query layer (generated for example by selecting item in the Attribute Table Manager)
 Deleted automatically on re-render action
 """
 # temporal query layer (removed on re-render action)
-QUERYLAYER = 'qlayer'
+QUERYLAYER = "qlayer"
 
 """Style definition for FlatNotebook pages"""
-FNPageStyle = FN.FNB_FF2 | \
-    FN.FNB_BACKGROUND_GRADIENT | \
-    FN.FNB_NODRAG | \
-    FN.FNB_TABS_BORDER_SIMPLE
-
-FNPageDStyle = FN.FNB_FANCY_TABS | \
-    FN.FNB_BOTTOM | \
-    FN.FNB_NODRAG | \
-    FN.FNB_NO_NAV_BUTTONS | \
-    FN.FNB_NO_X_BUTTON
+FNPageStyle = (
+    FN.FNB_FF2 | FN.FNB_BACKGROUND_GRADIENT | FN.FNB_NODRAG | FN.FNB_TABS_BORDER_SIMPLE
+)
+
+FNPageDStyle = (
+    FN.FNB_FANCY_TABS
+    | FN.FNB_BOTTOM
+    | FN.FNB_NODRAG
+    | FN.FNB_NO_NAV_BUTTONS
+    | FN.FNB_NO_X_BUTTON
+)
 
 FNPageColor = wx.Colour(125, 200, 175)
 
@@ -168,16 +170,16 @@ GM_WINDOW_MIN_SIZE = (525, 400)
 # use UBUNTU_MENUPROXY=0 to disbale global menu on ubuntu but in the same time
 # to get smaller lmgr
 # [1] https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationMenu#Troubleshooting
-if sys.platform in ('win32', 'darwin') or os.environ.get('UBUNTU_MENUPROXY'):
+if sys.platform in ("win32", "darwin") or os.environ.get("UBUNTU_MENUPROXY"):
     GM_WINDOW_SIZE = (GM_WINDOW_MIN_SIZE[0], 600)
 else:
     GM_WINDOW_SIZE = (625, 600)
 
-if sys.platform == 'win32':
-    BIN_EXT = '.exe'
-    SCT_EXT = '.bat'
+if sys.platform == "win32":
+    BIN_EXT = ".exe"
+    SCT_EXT = ".bat"
 else:
-    BIN_EXT = SCT_EXT = ''
+    BIN_EXT = SCT_EXT = ""
 
 
 def UpdateGRASSAddOnCommands(eList=None):
@@ -189,12 +191,12 @@ def UpdateGRASSAddOnCommands(eList=None):
     global grassCmd, grassScripts
 
     # scan addons (path)
-    addonPath = os.getenv('GRASS_ADDON_PATH', '')
-    addonBase = os.getenv('GRASS_ADDON_BASE')
+    addonPath = os.getenv("GRASS_ADDON_PATH", "")
+    addonBase = os.getenv("GRASS_ADDON_BASE")
     if addonBase:
-        addonPath += os.pathsep + os.path.join(addonBase, 'bin')
-        if sys.platform != 'win32':
-            addonPath += os.pathsep + os.path.join(addonBase, 'scripts')
+        addonPath += os.pathsep + os.path.join(addonBase, "bin")
+        if sys.platform != "win32":
+            addonPath += os.pathsep + os.path.join(addonBase, "scripts")
 
     # remove commands first
     if eList:
@@ -204,17 +206,17 @@ def UpdateGRASSAddOnCommands(eList=None):
         Debug.msg(1, "Number of removed AddOn commands: %d", len(eList))
 
     nCmd = 0
-    pathList = os.getenv('PATH', '').split(os.pathsep)
+    pathList = os.getenv("PATH", "").split(os.pathsep)
     for path in addonPath.split(os.pathsep):
         if not os.path.exists(path) or not os.path.isdir(path):
             continue
 
         # check if addon is in the path
         if pathList and path not in pathList:
-            os.environ['PATH'] = path + os.pathsep + os.environ['PATH']
+            os.environ["PATH"] = path + os.pathsep + os.environ["PATH"]
 
         for fname in os.listdir(path):
-            if fname in ['docs', 'modules.xml']:
+            if fname in ["docs", "modules.xml"]:
                 continue
             if grassScripts:  # win32
                 name, ext = os.path.splitext(fname)
@@ -225,9 +227,11 @@ def UpdateGRASSAddOnCommands(eList=None):
                         grassCmd.add(name)
                         Debug.msg(3, "AddOn commands: %s", name)
                         nCmd += 1
-                if ext == SCT_EXT and \
-                        ext in grassScripts.keys() and \
-                        name not in grassScripts[ext]:
+                if (
+                    ext == SCT_EXT
+                    and ext in grassScripts.keys()
+                    and name not in grassScripts[ext]
+                ):
                     grassScripts[ext].append(name)
             else:
                 if fname not in grassCmd:
@@ -237,6 +241,7 @@ def UpdateGRASSAddOnCommands(eList=None):
 
     Debug.msg(1, "Number of GRASS AddOn commands: %d", nCmd)
 
+
 """@brief Collected GRASS-relared binaries/scripts"""
 grassCmd, grassScripts = get_commands()
 Debug.msg(1, "Number of core GRASS commands: %d", len(grassCmd))
@@ -249,8 +254,7 @@ toolbarSize = (24, 24)
 hasAgw = CheckWxVersion([2, 8, 11, 0])
 wxPythonPhoenix = CheckWxPhoenix()
 
-gtk3 = True if 'gtk3' in wx.PlatformInfo else False
+gtk3 = True if "gtk3" in wx.PlatformInfo else False
 
 """@Add GUIDIR/scripts into path"""
-os.environ['PATH'] = os.path.join(
-    GUIDIR, 'scripts') + os.pathsep + os.environ['PATH']
+os.environ["PATH"] = os.path.join(GUIDIR, "scripts") + os.pathsep + os.environ["PATH"]

+ 17 - 13
gui/wxpython/core/gthread.py

@@ -21,6 +21,7 @@ import wx
 from wx.lib.newevent import NewEvent
 
 import sys
+
 if sys.version_info.major == 2:
     import Queue
 else:
@@ -37,6 +38,7 @@ class gThread(threading.Thread, wx.EvtHandler):
     terminating thread:
     https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/
     """
+
     requestId = 0
 
     def __init__(self, requestQ=None, resultQ=None, **kwds):
@@ -89,7 +91,7 @@ class gThread(threading.Thread, wx.EvtHandler):
     def run(self):
         while True:
             requestId, args, kwds = self.requestQ.get()
-            for key in ('callable', 'ondone', 'userdata', 'onterminate'):
+            for key in ("callable", "ondone", "userdata", "onterminate"):
                 if key in kwds:
                     vars()[key] = kwds[key]
                     del kwds[key]
@@ -100,10 +102,10 @@ class gThread(threading.Thread, wx.EvtHandler):
 
             ret = None
             exception = None
-            time.sleep(.01)
+            time.sleep(0.01)
 
             self._terminate_evt = wxThdTerminate(
-                onterminate=vars()['onterminate'],
+                onterminate=vars()["onterminate"],
                 kwds=kwds,
                 args=args,
                 pid=requestId,
@@ -112,7 +114,7 @@ class gThread(threading.Thread, wx.EvtHandler):
             if self.terminate:
                 return
 
-            ret = vars()['callable'](*args, **kwds)
+            ret = vars()["callable"](*args, **kwds)
 
             if self.terminate:
                 return
@@ -121,13 +123,15 @@ class gThread(threading.Thread, wx.EvtHandler):
 
             self.resultQ.put((requestId, ret))
 
-            event = wxCmdDone(ondone=vars()['ondone'],
-                              kwds=kwds,
-                              args=args,  # TODO expand args to kwds
-                              ret=ret,
-                              exception=exception,
-                              userdata=vars()['userdata'],
-                              pid=requestId)
+            event = wxCmdDone(
+                ondone=vars()["ondone"],
+                kwds=kwds,
+                args=args,  # TODO expand args to kwds
+                ret=ret,
+                exception=exception,
+                userdata=vars()["userdata"],
+                pid=requestId,
+            )
 
             # send event
             wx.PostEvent(self, event)
@@ -151,14 +155,14 @@ class gThread(threading.Thread, wx.EvtHandler):
         self.run = self.__run_backup
 
     def globaltrace(self, frame, event, arg):
-        if event == 'call':
+        if event == "call":
             return self.localtrace
         else:
             return None
 
     def localtrace(self, frame, event, arg):
         if self.terminate:
-            if event == 'line':
+            if event == "line":
                 # Send event
                 wx.PostEvent(self, self._terminate_evt)
                 raise SystemExit()

+ 46 - 34
gui/wxpython/core/layerlist.py

@@ -69,8 +69,17 @@ class LayerList(object):
                 layers.append(layer)
         return layers
 
-    def AddNewLayer(self, name, mapType, cmd, active=True, hidden=False,
-                    opacity=1, label=None, pos=0):
+    def AddNewLayer(
+        self,
+        name,
+        mapType,
+        cmd,
+        active=True,
+        hidden=False,
+        opacity=1,
+        label=None,
+        pos=0,
+    ):
         """Creates new layer and adds it to the list (insert to the first position).
 
         :param ltype: layer type (raster, vector, raster_3d, ...)
@@ -96,13 +105,11 @@ class LayerList(object):
         return layer
 
     def AddLayer(self, layer):
-        """Adds a layer to the layer list.
-        """
+        """Adds a layer to the layer list."""
         self._list.insert(0, layer)
 
     def InsertLayer(self, index, layer):
-        """Adds a layer to the layer list.
-        """
+        """Adds a layer to the layer list."""
         self._list.insert(index, layer)
 
     def RemoveLayer(self, layer):
@@ -149,9 +156,9 @@ class LayerList(object):
         return len(self._list)
 
     def __str__(self):
-        text = ''
+        text = ""
         for layer in self._list:
-            text += str(layer.name) + '\n'
+            text += str(layer.name) + "\n"
         return text
 
 
@@ -192,11 +199,13 @@ class Layer(object):
         self._hidden = False
         self._initialized = False
 
-        self._mapTypes = ['raster', 'vector', 'raster_3d', 'rgb']
-        self._internalTypes = {'raster': 'cell',
-                               'vector': 'vector',
-                               'raster_3d': 'grid3',
-                               'rgb': 'rgb'}
+        self._mapTypes = ["raster", "vector", "raster_3d", "rgb"]
+        self._internalTypes = {
+            "raster": "cell",
+            "vector": "vector",
+            "raster_3d": "grid3",
+            "rgb": "rgb",
+        }
 
     def GetName(self):
         return self._name
@@ -209,22 +218,21 @@ class Layer(object):
         Therefore map type has to be set first.
         """
         if not self.hidden:
-            fullName = name.split('@')
-            if len(
-                    fullName) == 1 and self._mapType != 'rgb':  # skip checking rgb maps for now
+            fullName = name.split("@")
+            if (
+                len(fullName) == 1 and self._mapType != "rgb"
+            ):  # skip checking rgb maps for now
                 if self._mapType is None:
                     raise ValueError(
-                        "To set layer name, the type of layer must be specified.")
+                        "To set layer name, the type of layer must be specified."
+                    )
 
                 res = gcore.find_file(
-                    name=fullName,
-                    element=self._internalTypes[
-                        self._mapType])
-                if not res['mapset']:
-                    raise ValueError(
-                        "Map <{name}> not found.".format(
-                            name=name))
-                self._name = name + '@' + res['mapset']
+                    name=fullName, element=self._internalTypes[self._mapType]
+                )
+                if not res["mapset"]:
+                    raise ValueError("Map <{name}> not found.".format(name=name))
+                self._name = name + "@" + res["mapset"]
             else:
                 self._name = name
         self.label = name
@@ -256,9 +264,7 @@ class Layer(object):
         :param mapType: can be 'raster', 'vector', 'raster_3d'
         """
         if mapType not in self._mapTypes:
-            raise ValueError(
-                "Wrong map type used: {mtype}".format(
-                    mtype=mapType))
+            raise ValueError("Wrong map type used: {mtype}".format(mtype=mapType))
 
         self._mapType = mapType
 
@@ -278,8 +284,8 @@ class Layer(object):
         """
         if not (0 <= opacity <= 1):
             raise ValueError(
-                "Opacity value must be between 0 and 1, not {op}.".format(
-                    op=opacity))
+                "Opacity value must be between 0 and 1, not {op}.".format(op=opacity)
+            )
         self._opacity = opacity
 
     opacity = property(fget=GetOpacity, fset=SetOpacity)
@@ -369,10 +375,16 @@ class LayerListToRendererConverter:
 
     def AddLayer(self, index, layer):
         """Adds layer to renderer (prepends)."""
-        self._renderer.AddLayer(ltype=layer.mapType, command=layer.cmd,
-                                name=layer.name, active=layer.active,
-                                hidden=False, opacity=layer.opacity,
-                                render=True, pos=-1)
+        self._renderer.AddLayer(
+            ltype=layer.mapType,
+            command=layer.cmd,
+            name=layer.name,
+            active=layer.active,
+            hidden=False,
+            opacity=layer.opacity,
+            render=True,
+            pos=-1,
+        )
 
     def RemoveLayer(self, index):
         """Removes layer from renderer."""

+ 69 - 54
gui/wxpython/core/menutree.py

@@ -38,6 +38,7 @@ from __future__ import print_function
 import os
 import sys
 import copy
+
 try:
     import xml.etree.ElementTree as etree
 except ImportError:
@@ -64,9 +65,9 @@ class MenuTreeModelBuilder:
     # message_handler=GError should be replaced by None
     def __init__(self, filename, expandAddons=True, message_handler=GError):
 
-        self.menustyle = UserSettings.Get(group='appearance',
-                                          key='menustyle',
-                                          subkey='selection')
+        self.menustyle = UserSettings.Get(
+            group="appearance", key="menustyle", subkey="selection"
+        )
 
         xmlTree = etree.parse(filename)
         if expandAddons:
@@ -80,32 +81,40 @@ class MenuTreeModelBuilder:
 
     def _createModel(self, xmlTree):
         root = xmlTree.getroot()
-        menubar = root.findall('menubar')[0]
-        menus = menubar.findall('menu')
+        menubar = root.findall("menubar")[0]
+        menus = menubar.findall("menu")
         for m in menus:
             self._createMenu(m, self.model.root)
 
     def _createMenu(self, menu, node):
-        label = _(menu.find('label').text)
-        items = menu.find('items')
+        label = _(menu.find("label").text)
+        items = menu.find("items")
         node = self.model.AppendNode(parent=node, label=label)
         for item in items:
             self._createItem(item, node)
 
     def _createItem(self, item, node):
-        if item.tag == 'separator':
-            data = dict(label='', description='', handler='',
-                        command='', keywords='', shortcut='', wxId='', icon='')
-            self.model.AppendNode(parent=node, label='', data=data)
-        elif item.tag == 'menuitem':
-            origLabel = _(item.find('label').text)
-            handler = item.find('handler').text
-            desc = item.find('help')  # optional
-            gcmd = item.find('command')  # optional
-            keywords = item.find('keywords')  # optional
-            shortcut = item.find('shortcut')  # optional
-            wxId = item.find('id')       # optional
-            icon = item.find('icon')     # optional
+        if item.tag == "separator":
+            data = dict(
+                label="",
+                description="",
+                handler="",
+                command="",
+                keywords="",
+                shortcut="",
+                wxId="",
+                icon="",
+            )
+            self.model.AppendNode(parent=node, label="", data=data)
+        elif item.tag == "menuitem":
+            origLabel = _(item.find("label").text)
+            handler = item.find("handler").text
+            desc = item.find("help")  # optional
+            gcmd = item.find("command")  # optional
+            keywords = item.find("keywords")  # optional
+            shortcut = item.find("shortcut")  # optional
+            wxId = item.find("id")  # optional
+            icon = item.find("icon")  # optional
             if gcmd is not None:
                 gcmd = gcmd.text
             else:
@@ -123,19 +132,19 @@ class MenuTreeModelBuilder:
             else:
                 shortcut = ""
             if wxId is not None:
-                wxId = eval('wx.' + wxId.text)
+                wxId = eval("wx." + wxId.text)
             else:
                 wxId = wx.ID_ANY
             if icon is not None:
                 icon = icon.text
             else:
-                icon = ''
+                icon = ""
             label = origLabel
             if gcmd:
                 if self.menustyle == 1:
-                    label += '   [' + gcmd + ']'
+                    label += "   [" + gcmd + "]"
                 elif self.menustyle == 2:
-                    label = '      [' + gcmd + ']'
+                    label = "      [" + gcmd + "]"
             data = dict(
                 label=origLabel,
                 description=desc,
@@ -144,9 +153,10 @@ class MenuTreeModelBuilder:
                 keywords=keywords,
                 shortcut=shortcut,
                 wxId=wxId,
-                icon=icon)
+                icon=icon,
+            )
             self.model.AppendNode(parent=node, label=label, data=data)
-        elif item.tag == 'menu':
+        elif item.tag == "menu":
             self._createMenu(item, node)
         else:
             raise ValueError(_("Unknow tag %s") % item.tag)
@@ -172,13 +182,13 @@ class MenuTreeModelBuilder:
         :param fh: file descriptor
         """
         className = self.__class__.__name__
-        fh.write('menustrings_%s = [\n' % className)
+        fh.write("menustrings_%s = [\n" % className)
         for child in self.model.root.children:
             printStrings(child, fh)
-        fh.write('    \'\']\n')
+        fh.write("    '']\n")
 
     def PrintCommands(self, fh):
-        printCommands(self.model.root, fh, itemSep=' | ', menuSep=' > ')
+        printCommands(self.model.root, fh, itemSep=" | ", menuSep=" > ")
 
 
 def removeSeparators(model, node=None):
@@ -194,7 +204,7 @@ def removeSeparators(model, node=None):
 def printTree(node, fh, indent=0):
     if not node.label:
         return
-    text = '%s- %s\n' % (' ' * indent, node.label.replace('&', ''))
+    text = "%s- %s\n" % (" " * indent, node.label.replace("&", ""))
     fh.write(text)
     for child in node.children:
         printTree(node=child, fh=fh, indent=indent + 2)
@@ -204,18 +214,17 @@ def printStrings(node, fh):
     # node.label  - with module in brackets
     # node.data['label'] - without module in brackets
     if node.label and not node.data:
-        fh.write('    _(%r),\n' % str(node.label))
+        fh.write("    _(%r),\n" % str(node.label))
     if node.data:
-        if 'label' in node.data and node.data['label']:
-            fh.write('    _(%r),\n' % str(node.data['label']))
-        if 'description' in node.data and node.data['description']:
-            fh.write('    _(%r),\n' % str(node.data['description']))
+        if "label" in node.data and node.data["label"]:
+            fh.write("    _(%r),\n" % str(node.data["label"]))
+        if "description" in node.data and node.data["description"]:
+            fh.write("    _(%r),\n" % str(node.data["description"]))
     for child in node.children:
         printStrings(node=child, fh=fh)
 
 
 def printCommands(node, fh, itemSep, menuSep):
-
     def collectParents(node, parents):
         parent = node.parent
         if parent.parent:
@@ -223,13 +232,13 @@ def printCommands(node, fh, itemSep, menuSep):
             collectParents(node.parent, parents)
 
     data = node.data
-    if data and 'command' in data and data['command']:
-        fh.write('%s%s' % (data['command'], itemSep))
+    if data and "command" in data and data["command"]:
+        fh.write("%s%s" % (data["command"], itemSep))
         parents = [node]
         collectParents(node, parents)
-        labels = [parent.label.replace('&', '') for parent in parents]
+        labels = [parent.label.replace("&", "") for parent in parents]
         fh.write(menuSep.join(labels))
-        fh.write('\n')
+        fh.write("\n")
 
     for child in node.children:
         printCommands(child, fh, itemSep, menuSep)
@@ -237,48 +246,54 @@ def printCommands(node, fh, itemSep, menuSep):
 
 if __name__ == "__main__":
 
-    action = 'strings'
-    menu = 'manager'
+    action = "strings"
+    menu = "manager"
 
     for arg in sys.argv:
-        if arg in ('strings', 'tree', 'commands', 'dump'):
+        if arg in ("strings", "tree", "commands", "dump"):
             action = arg
-        elif arg in ('manager', 'module_tree', 'modeler', 'psmap'):
+        elif arg in ("manager", "module_tree", "modeler", "psmap"):
             menu = arg
 
     # FIXME: cross-dependencies
-    if menu == 'manager':
+    if menu == "manager":
         from lmgr.menudata import LayerManagerMenuData
         from core.globalvar import WXGUIDIR
-        filename = os.path.join(WXGUIDIR, 'xml', 'menudata.xml')
+
+        filename = os.path.join(WXGUIDIR, "xml", "menudata.xml")
         menudata = LayerManagerMenuData(filename)
     # FIXME: since module descriptions are used again we have now the third
     # copy of the same string (one is in modules)
-    elif menu == 'module_tree':
+    elif menu == "module_tree":
         from lmgr.menudata import LayerManagerModuleTree
         from core.globalvar import WXGUIDIR
-        filename = os.path.join(WXGUIDIR, 'xml', 'module_tree_menudata.xml')
+
+        filename = os.path.join(WXGUIDIR, "xml", "module_tree_menudata.xml")
         menudata = LayerManagerModuleTree(filename)
-    elif menu == 'modeler':
+    elif menu == "modeler":
         from gmodeler.menudata import ModelerMenuData
+
         menudata = ModelerMenuData()
-    elif menu == 'psmap':
+    elif menu == "psmap":
         from psmap.menudata import PsMapMenuData
+
         menudata = PsMapMenuData()
     else:
         import grass.script.core as gscore
+
         gscore.fatal("Unknown value for parameter menu: " % menu)
 
-    if action == 'strings':
+    if action == "strings":
         menudata.PrintStrings(sys.stdout)
-    elif action == 'tree':
+    elif action == "tree":
         menudata.PrintTree(sys.stdout)
-    elif action == 'commands':
+    elif action == "commands":
         menudata.PrintCommands(sys.stdout)
-    elif action == 'dump':
+    elif action == "dump":
         print(menudata.model)
     else:
         import grass.script.core as gscore
+
         gscore.fatal("Unknown value for parameter action: " % action)
 
     sys.exit(0)

File diff ditekan karena terlalu besar
+ 430 - 336
gui/wxpython/core/render.py


File diff ditekan karena terlalu besar
+ 704 - 828
gui/wxpython/core/settings.py


+ 178 - 149
gui/wxpython/core/toolboxes.py

@@ -22,10 +22,10 @@ from xml.parsers import expat
 
 # Get the XML parsing exceptions to catch. The behavior chnaged with Python 2.7
 # and ElementTree 1.3.
-if hasattr(etree, 'ParseError'):
+if hasattr(etree, "ParseError"):
     ETREE_EXCEPTIONS = (etree.ParseError, expat.ExpatError)
 else:
-    ETREE_EXCEPTIONS = (expat.ExpatError)
+    ETREE_EXCEPTIONS = expat.ExpatError
 
 if sys.version_info[0:2] > (2, 6):
     has_xpath = True
@@ -46,10 +46,10 @@ WXGUIDIR = os.path.join(os.getenv("GISBASE"), "gui", "wxpython")
 
 
 # this could be placed to functions
-mainMenuFile = os.path.join(WXGUIDIR, 'xml', 'main_menu.xml')
-toolboxesFile = os.path.join(WXGUIDIR, 'xml', 'toolboxes.xml')
-wxguiItemsFile = os.path.join(WXGUIDIR, 'xml', 'wxgui_items.xml')
-moduleItemsFile = os.path.join(WXGUIDIR, 'xml', 'module_items.xml')
+mainMenuFile = os.path.join(WXGUIDIR, "xml", "main_menu.xml")
+toolboxesFile = os.path.join(WXGUIDIR, "xml", "toolboxes.xml")
+wxguiItemsFile = os.path.join(WXGUIDIR, "xml", "wxgui_items.xml")
+moduleItemsFile = os.path.join(WXGUIDIR, "xml", "module_items.xml")
 
 
 def GetSettingsPath():
@@ -58,6 +58,7 @@ def GetSettingsPath():
     # no need to do this
     try:
         from core.utils import GetSettingsPath as actualGetSettingsPath
+
         return actualGetSettingsPath()
     except ImportError:
         # expecting that there will be no such path
@@ -66,18 +67,14 @@ def GetSettingsPath():
 
 
 def _getUserToolboxesFile():
-    userToolboxesFile = os.path.join(
-        GetSettingsPath(),
-        'toolboxes', 'toolboxes.xml')
+    userToolboxesFile = os.path.join(GetSettingsPath(), "toolboxes", "toolboxes.xml")
     if not os.path.exists(userToolboxesFile):
         userToolboxesFile = None
     return userToolboxesFile
 
 
 def _getUserMainMenuFile():
-    userMainMenuFile = os.path.join(
-        GetSettingsPath(),
-        'toolboxes', 'main_menu.xml')
+    userMainMenuFile = os.path.join(GetSettingsPath(), "toolboxes", "main_menu.xml")
     if not os.path.exists(userMainMenuFile):
         userMainMenuFile = None
     return userMainMenuFile
@@ -109,7 +106,7 @@ def _debug(level, message):
 def toolboxesOutdated():
     """Removes auto-generated menudata.xml
     to let gui regenerate it next time it starts."""
-    path = os.path.join(GetSettingsPath(), 'toolboxes', 'menudata.xml')
+    path = os.path.join(GetSettingsPath(), "toolboxes", "menudata.xml")
     if os.path.exists(path):
         try_remove(path)
 
@@ -126,10 +123,12 @@ def getMenudataFile(userRootFile, newFile, fallback):
     _debug(
         1,
         "toolboxes.getMenudataFile: {userRootFile}, {newFile}, {fallback}".format(
-            **locals()))
+            **locals()
+        ),
+    )
 
-    distributionRootFile = os.path.join(WXGUIDIR, 'xml', userRootFile)
-    userRootFile = os.path.join(GetSettingsPath(), 'toolboxes', userRootFile)
+    distributionRootFile = os.path.join(WXGUIDIR, "xml", userRootFile)
+    userRootFile = os.path.join(GetSettingsPath(), "toolboxes", userRootFile)
     if not os.path.exists(userRootFile):
         userRootFile = None
 
@@ -147,7 +146,9 @@ def getMenudataFile(userRootFile, newFile, fallback):
             if not _getUserToolboxesFile() and not userRootFile:
                 os.remove(menudataFile)
                 _debug(
-                    2, "toolboxes.getMenudataFile: no user defined files, menudata deleted")
+                    2,
+                    "toolboxes.getMenudataFile: no user defined files, menudata deleted",
+                )
                 return fallback
 
             if bool(_getUserToolboxesFile()) != bool(userRootFile):
@@ -155,20 +156,24 @@ def getMenudataFile(userRootFile, newFile, fallback):
                 # any change
                 generateNew = True
                 _debug(
-                    2, "toolboxes.getMenudataFile: only one of the user defined files")
+                    2, "toolboxes.getMenudataFile: only one of the user defined files"
+                )
             else:
                 # if newer files -> generate new
                 menudataTime = os.path.getmtime(menudataFile)
                 if _getUserToolboxesFile():
-                    if os.path.getmtime(
-                            _getUserToolboxesFile()) > menudataTime:
+                    if os.path.getmtime(_getUserToolboxesFile()) > menudataTime:
                         _debug(
-                            2, "toolboxes.getMenudataFile: user toolboxes is newer than menudata")
+                            2,
+                            "toolboxes.getMenudataFile: user toolboxes is newer than menudata",
+                        )
                         generateNew = True
                 if userRootFile:
                     if os.path.getmtime(userRootFile) > menudataTime:
                         _debug(
-                            2, "toolboxes.getMenudataFile: user root file is newer than menudata")
+                            2,
+                            "toolboxes.getMenudataFile: user root file is newer than menudata",
+                        )
                         generateNew = True
         elif _getUserToolboxesFile() or userRootFile:
             _debug(2, "toolboxes.getMenudataFile: no menudata")
@@ -185,23 +190,28 @@ def getMenudataFile(userRootFile, newFile, fallback):
                 # toolboxes but undefined module tree file.
                 _debug(2, "toolboxes.getMenudataFile: creating a tree")
                 tree = createTree(
-                    distributionRootFile=distributionRootFile,
-                    userRootFile=userRootFile)
+                    distributionRootFile=distributionRootFile, userRootFile=userRootFile
+                )
             except ETREE_EXCEPTIONS:
-                _warning(_("Unable to parse user toolboxes XML files. "
-                           "Default files will be loaded."))
+                _warning(
+                    _(
+                        "Unable to parse user toolboxes XML files. "
+                        "Default files will be loaded."
+                    )
+                )
                 return fallback
 
             try:
                 xml = _getXMLString(tree.getroot())
-                fh = open(menudataFile, 'w')
+                fh = open(menudataFile, "w")
                 fh.write(xml)
                 fh.close()
                 return menudataFile
             except:
                 _debug(
                     2,
-                    "toolboxes.getMenudataFile: writing menudata failed, returning fallback file")
+                    "toolboxes.getMenudataFile: writing menudata failed, returning fallback file",
+                )
                 return fallback
         else:
             return menudataFile
@@ -213,7 +223,7 @@ def getMenudataFile(userRootFile, newFile, fallback):
 def _setupToolboxes():
     """Create 'toolboxes' directory if doesn't exist."""
     basePath = GetSettingsPath()
-    path = os.path.join(basePath, 'toolboxes')
+    path = os.path.join(basePath, "toolboxes")
     if not os.path.exists(basePath):
         return None
 
@@ -231,9 +241,14 @@ def _createPath(path):
             # we cannot use GError or similar because the gui doesn't start at
             # all
             gcore.warning(
-                '%(reason)s\n%(detail)s' % ({
-                    'reason': _('Unable to create toolboxes directory.'),
-                    'detail': str(e)}))
+                "%(reason)s\n%(detail)s"
+                % (
+                    {
+                        "reason": _("Unable to create toolboxes directory."),
+                        "detail": str(e),
+                    }
+                )
+            )
             return False
     return True
 
@@ -264,15 +279,16 @@ def createTree(distributionRootFile, userRootFile, userDefined=True):
     wxguiItems = etree.parse(wxguiItemsFile)
     moduleItems = etree.parse(moduleItemsFile)
 
-    return toolboxes2menudata(mainMenu=mainMenu,
-                              toolboxes=toolboxes,
-                              userToolboxes=userToolboxes,
-                              wxguiItems=wxguiItems,
-                              moduleItems=moduleItems)
+    return toolboxes2menudata(
+        mainMenu=mainMenu,
+        toolboxes=toolboxes,
+        userToolboxes=userToolboxes,
+        wxguiItems=wxguiItems,
+        moduleItems=moduleItems,
+    )
 
 
-def toolboxes2menudata(mainMenu, toolboxes, userToolboxes,
-                       wxguiItems, moduleItems):
+def toolboxes2menudata(mainMenu, toolboxes, userToolboxes, wxguiItems, moduleItems):
     """Creates XML file with data for menu.
 
     Parses toolboxes files from distribution and from users,
@@ -288,7 +304,7 @@ def toolboxes2menudata(mainMenu, toolboxes, userToolboxes,
     userHasToolboxes = False
 
     # in case user has empty toolboxes file (to avoid genereation)
-    if userToolboxes and userToolboxes.findall('.//toolbox'):
+    if userToolboxes and userToolboxes.findall(".//toolbox"):
         _expandUserToolboxesItem(root, userToolboxes)
         _expandToolboxes(root, userToolboxes)
         userHasToolboxes = True
@@ -300,8 +316,8 @@ def toolboxes2menudata(mainMenu, toolboxes, userToolboxes,
 
     # we do not expand addons here since they need to be expanded in runtime
 
-    _expandItems(root, moduleItems, 'module-item')
-    _expandItems(root, wxguiItems, 'wxgui-item')
+    _expandItems(root, moduleItems, "module-item")
+    _expandItems(root, wxguiItems, "wxgui-item")
 
     # in case of compilation there are no additional runtime modules
     # but we need to create empty elements
@@ -332,8 +348,7 @@ def _indent(elem, level=0):
 
 
 def expandAddons(tree):
-    """Expands addons element.
-    """
+    """Expands addons element."""
     root = tree.getroot()
     _expandAddonsItem(root)
     # expanding and converting is done twice, so there is some overhead
@@ -405,26 +420,26 @@ def _expandToolboxes(node, toolboxes):
     </items>
     </toolbox>
     """
-    nodes = node.findall('.//toolbox')
-    if node.tag == 'toolbox':  # root
+    nodes = node.findall(".//toolbox")
+    if node.tag == "toolbox":  # root
         nodes.append(node)
     for n in nodes:
-        if n.find('items') is None:
+        if n.find("items") is None:
             continue
-        for subtoolbox in n.findall('./items/subtoolbox'):
-            items = n.find('./items')
+        for subtoolbox in n.findall("./items/subtoolbox"):
+            items = n.find("./items")
             idx = list(items).index(subtoolbox)
 
             if has_xpath:
                 toolbox = toolboxes.find(
-                    './/toolbox[@name="%s"]' %
-                    subtoolbox.get('name'))
+                    './/toolbox[@name="%s"]' % subtoolbox.get("name")
+                )
             else:
                 toolbox = None
-                potentialToolboxes = toolboxes.findall('.//toolbox')
-                sName = subtoolbox.get('name')
+                potentialToolboxes = toolboxes.findall(".//toolbox")
+                sName = subtoolbox.get("name")
                 for pToolbox in potentialToolboxes:
-                    if pToolbox.get('name') == sName:
+                    if pToolbox.get("name") == sName:
                         toolbox = pToolbox
                         break
 
@@ -446,18 +461,16 @@ def _expandUserToolboxesItem(node, toolboxes):
     >>> etree.tostring(tree)
     b'<toolbox><items><toolbox name="GeneratedUserToolboxesList"><label>Custom toolboxes</label><items><toolbox name="UserToolbox"><items><module-item name="g.region" /></items></toolbox></items></toolbox></items></toolbox>'
     """
-    tboxes = toolboxes.findall('.//toolbox')
+    tboxes = toolboxes.findall(".//toolbox")
 
-    for n in node.findall('./items/user-toolboxes-list'):
-        items = node.find('./items')
+    for n in node.findall("./items/user-toolboxes-list"):
+        items = node.find("./items")
         idx = list(items).index(n)
-        el = etree.Element(
-            'toolbox', attrib={
-                'name': 'GeneratedUserToolboxesList'})
+        el = etree.Element("toolbox", attrib={"name": "GeneratedUserToolboxesList"})
         items.insert(idx, el)
-        label = etree.SubElement(el, 'label')
+        label = etree.SubElement(el, "label")
         label.text = _("Custom toolboxes")
-        it = etree.SubElement(el, 'items')
+        it = etree.SubElement(el, "items")
         for toolbox in tboxes:
             it.append(copy.deepcopy(toolbox))
         items.remove(n)
@@ -471,25 +484,30 @@ def _removeUserToolboxesItem(root):
     >>> etree.tostring(tree)
     b'<toolbox><items /></toolbox>'
     """
-    for n in root.findall('./items/user-toolboxes-list'):
-        items = root.find('./items')
+    for n in root.findall("./items/user-toolboxes-list"):
+        items = root.find("./items")
         items.remove(n)
 
 
 def _getAddons():
     try:
-        output = gcore.read_command('g.extension', quiet=True, flags='ag')
+        output = gcore.read_command("g.extension", quiet=True, flags="ag")
     except CalledModuleError as error:
-        _warning(_("List of addons cannot be obtained"
-                   " because g.extension failed."
-                   " Details: %s") % error)
+        _warning(
+            _(
+                "List of addons cannot be obtained"
+                " because g.extension failed."
+                " Details: %s"
+            )
+            % error
+        )
         return []
 
     flist = []
     for line in output.splitlines():
-        if not line.startswith('name'):
+        if not line.startswith("name"):
             continue
-        for fexe in line.split('=', 1)[1].split(','):
+        for fexe in line.split("=", 1)[1].split(","):
             flist.append(fexe)
 
     return sorted(flist)
@@ -498,11 +516,11 @@ def _getAddons():
 def _removeAddonsItem(node, addonsNodes):
     # TODO: change impl to be similar with the remove toolboxes
     for n in addonsNodes:
-        items = node.find('./items')
+        items = node.find("./items")
         if items is not None:
             items.remove(n)
         # because of inconsistent menudata file
-        items = node.find('./menubar')
+        items = node.find("./menubar")
         if items is not None:
             items.remove(n)
 
@@ -515,7 +533,7 @@ def _expandAddonsItem(node):
         menudata.xml file when new addons are added/removed.
     """
     # no addonsTag -> do nothing
-    addonsTags = node.findall('.//addons')
+    addonsTags = node.findall(".//addons")
     if not addonsTags:
         return
     # fetch addons
@@ -530,19 +548,19 @@ def _expandAddonsItem(node):
     # keywords and desc are handled later automatically
     for n in addonsTags:
         # find parent is not possible with implementation of etree (in 2.7)
-        items = node.find('./menubar')
+        items = node.find("./menubar")
         idx = list(items).index(n)
         # do not set name since it is already in menudata file
         # attib={'name': 'AddonsList'}
-        el = etree.Element('menu')
+        el = etree.Element("menu")
         items.insert(idx, el)
-        label = etree.SubElement(el, 'label')
+        label = etree.SubElement(el, "label")
         label.text = _("Addons")
-        it = etree.SubElement(el, 'items')
+        it = etree.SubElement(el, "items")
         for addon in addons:
-            addonItem = etree.SubElement(it, 'module-item')
-            addonItem.attrib = {'name': addon}
-            addonLabel = etree.SubElement(addonItem, 'label')
+            addonItem = etree.SubElement(it, "module-item")
+            addonItem.attrib = {"name": addon}
+            addonLabel = etree.SubElement(addonItem, "label")
             addonLabel.text = addon
         items.remove(n)
 
@@ -556,15 +574,15 @@ def _expandItems(node, items, itemTag):
     >>> etree.tostring(tree)
     b'<items><module-item name="g.region"><module>g.region</module><description>GRASS region management</description></module-item></items>'
     """
-    for moduleItem in node.findall('.//' + itemTag):
-        itemName = moduleItem.get('name')
+    for moduleItem in node.findall(".//" + itemTag):
+        itemName = moduleItem.get("name")
         if has_xpath:
             moduleNode = items.find('.//%s[@name="%s"]' % (itemTag, itemName))
         else:
             moduleNode = None
-            potentialModuleNodes = items.findall('.//%s' % itemTag)
+            potentialModuleNodes = items.findall(".//%s" % itemTag)
             for mNode in potentialModuleNodes:
-                if mNode.get('name') == itemName:
+                if mNode.get("name") == itemName:
                     moduleNode = mNode
                     break
 
@@ -596,22 +614,22 @@ def _expandRuntimeModules(node, loadMetadata=True):
     b'<items><module-item name="m.proj"><module>m.proj</module><description>Converts coordinates from one projection to another (cs2cs frontend).</description><keywords>miscellaneous,projection,transformation</keywords></module-item></items>'
     """
     hasErrors = False
-    modules = node.findall('.//module-item')
+    modules = node.findall(".//module-item")
     for module in modules:
-        name = module.get('name')
-        if module.find('module') is None:
-            n = etree.SubElement(module, 'module')
+        name = module.get("name")
+        if module.find("module") is None:
+            n = etree.SubElement(module, "module")
             n.text = name
 
-        if module.find('description') is None:
+        if module.find("description") is None:
             if loadMetadata:
                 desc, keywords = _loadMetadata(name)
             else:
-                desc, keywords = '', ''
-            n = etree.SubElement(module, 'description')
+                desc, keywords = "", ""
+            n = etree.SubElement(module, "description")
             n.text = _escapeXML(desc)
-            n = etree.SubElement(module, 'keywords')
-            n.text = _escapeXML(','.join(keywords))
+            n = etree.SubElement(module, "keywords")
+            n.text = _escapeXML(",".join(keywords))
             if loadMetadata and not desc:
                 hasErrors = True
 
@@ -620,7 +638,8 @@ def _expandRuntimeModules(node, loadMetadata=True):
         # translating causes importing globalvar, where sys.exit is called
         sys.stderr.write(
             "WARNING: Some addons failed when loading. "
-            "Please consider to update your addons by running 'g.extension.all -f'.\n")
+            "Please consider to update your addons by running 'g.extension.all -f'.\n"
+        )
 
 
 def _escapeXML(text):
@@ -632,7 +651,7 @@ def _escapeXML(text):
     >>> _escapeXML('<>&')
     '&amp;lt;&gt;&amp;'
     """
-    return text.replace('<', '&lt;').replace("&", '&amp;').replace(">", '&gt;')
+    return text.replace("<", "&lt;").replace("&", "&amp;").replace(">", "&gt;")
 
 
 def _loadMetadata(module):
@@ -645,24 +664,23 @@ def _loadMetadata(module):
         task = gtask.parse_interface(module)
     except ScriptError as e:
         sys.stderr.write("%s: %s\n" % (module, e))
-        return '', ''
+        return "", ""
 
-    return task.get_description(full=True), \
-        task.get_keywords()
+    return task.get_description(full=True), task.get_keywords()
 
 
 def _addHandlers(node):
     """Add missing handlers to modules"""
-    for n in node.findall('.//module-item'):
-        if n.find('handler') is None:
-            handlerNode = etree.SubElement(n, 'handler')
-            handlerNode.text = 'OnMenuCmd'
+    for n in node.findall(".//module-item"):
+        if n.find("handler") is None:
+            handlerNode = etree.SubElement(n, "handler")
+            handlerNode.text = "OnMenuCmd"
 
     # e.g. g.region -p
-    for n in node.findall('.//wxgui-item'):
-        if n.find('command') is not None:
-            handlerNode = etree.SubElement(n, 'handler')
-            handlerNode.text = 'RunMenuCmd'
+    for n in node.findall(".//wxgui-item"):
+        if n.find("command") is not None:
+            handlerNode = etree.SubElement(n, "handler")
+            handlerNode.text = "RunMenuCmd"
 
 
 def _convertTag(node, old, new):
@@ -674,7 +692,7 @@ def _convertTag(node, old, new):
     >>> etree.tostring(tree)
     b'<toolboxes><menu><items><menuitem /></items></menu></toolboxes>'
     """
-    for n in node.findall('.//%s' % old):
+    for n in node.findall(".//%s" % old):
         n.tag = new
 
 
@@ -687,7 +705,7 @@ def _convertTagAndRemoveAttrib(node, old, new):
     >>> etree.tostring(tree)
     b'<toolboxes><menu><items><menuitem /></items></menu></toolboxes>'
     """
-    for n in node.findall('.//%s' % old):
+    for n in node.findall(".//%s" % old):
         n.tag = new
         n.attrib = {}
 
@@ -701,23 +719,23 @@ def _convertTree(root):
     b'<menudata><menubar><menu><label>Raster</label><items><menuitem><command>g.region</command></menuitem></items></menu></menubar></menudata>'
     """
     root.attrib = {}
-    label = root.find('label')
+    label = root.find("label")
     # must check because of inconsistent XML menudata file
     if label is not None:
         root.remove(label)
-    _convertTag(root, 'description', 'help')
-    _convertTag(root, 'wx-id', 'id')
-    _convertTag(root, 'module', 'command')
-    _convertTag(root, 'related-module', 'command')
-    _convertTagAndRemoveAttrib(root, 'wxgui-item', 'menuitem')
-    _convertTagAndRemoveAttrib(root, 'module-item', 'menuitem')
-
-    root.tag = 'menudata'
-    i1 = root.find('./items')
+    _convertTag(root, "description", "help")
+    _convertTag(root, "wx-id", "id")
+    _convertTag(root, "module", "command")
+    _convertTag(root, "related-module", "command")
+    _convertTagAndRemoveAttrib(root, "wxgui-item", "menuitem")
+    _convertTagAndRemoveAttrib(root, "module-item", "menuitem")
+
+    root.tag = "menudata"
+    i1 = root.find("./items")
     # must check because of inconsistent XML menudata file
     if i1 is not None:
-        i1.tag = 'menubar'
-    _convertTagAndRemoveAttrib(root, 'toolbox', 'menu')
+        i1.tag = "menubar"
+    _convertTagAndRemoveAttrib(root, "toolbox", "menu")
 
 
 def _getXMLString(root):
@@ -728,10 +746,12 @@ def _getXMLString(root):
 
     :return: XML as string
     """
-    xml = etree.tostring(root, encoding='UTF-8')
-    return xml.replace(b"<?xml version='1.0' encoding='UTF-8'?>\n",
-                       b"<?xml version='1.0' encoding='UTF-8'?>\n"
-                       b"<!--This is an auto-generated file-->\n")
+    xml = etree.tostring(root, encoding="UTF-8")
+    return xml.replace(
+        b"<?xml version='1.0' encoding='UTF-8'?>\n",
+        b"<?xml version='1.0' encoding='UTF-8'?>\n"
+        b"<!--This is an auto-generated file-->\n",
+    )
 
 
 def do_doctest_gettext_workaround():
@@ -756,6 +776,7 @@ def do_doctest_gettext_workaround():
     workaround function is still needed when you import something from
     GRASS Python libraries.
     """
+
     def new_displayhook(string):
         """A replacement for default `sys.displayhook`"""
         if string is not None:
@@ -769,6 +790,7 @@ def do_doctest_gettext_workaround():
     sys.__displayhook__ = new_displayhook
 
     import __builtin__
+
     __builtin__._ = new_translator
 
 
@@ -778,6 +800,7 @@ def doc_test():
     :return: a number of failed tests
     """
     import doctest
+
     do_doctest_gettext_workaround()
     return doctest.testmod().failed
 
@@ -786,11 +809,11 @@ def module_test():
     """Tests the module using test files included in the current
     directory and in files from distribution.
     """
-    toolboxesFile = os.path.join(WXGUIDIR, 'xml', 'toolboxes.xml')
-    userToolboxesFile = 'data/test_toolboxes_user_toolboxes.xml'
-    menuFile = 'data/test_toolboxes_menu.xml'
-    wxguiItemsFile = os.path.join(WXGUIDIR, 'xml', 'wxgui_items.xml')
-    moduleItemsFile = os.path.join(WXGUIDIR, 'xml', 'module_items.xml')
+    toolboxesFile = os.path.join(WXGUIDIR, "xml", "toolboxes.xml")
+    userToolboxesFile = "data/test_toolboxes_user_toolboxes.xml"
+    menuFile = "data/test_toolboxes_menu.xml"
+    wxguiItemsFile = os.path.join(WXGUIDIR, "xml", "wxgui_items.xml")
+    moduleItemsFile = os.path.join(WXGUIDIR, "xml", "module_items.xml")
 
     toolboxes = etree.parse(toolboxesFile)
     userToolboxes = etree.parse(userToolboxesFile)
@@ -799,11 +822,13 @@ def module_test():
     wxguiItems = etree.parse(wxguiItemsFile)
     moduleItems = etree.parse(moduleItemsFile)
 
-    tree = toolboxes2menudata(mainMenu=menu,
-                              toolboxes=toolboxes,
-                              userToolboxes=userToolboxes,
-                              wxguiItems=wxguiItems,
-                              moduleItems=moduleItems)
+    tree = toolboxes2menudata(
+        mainMenu=menu,
+        toolboxes=toolboxes,
+        userToolboxes=userToolboxes,
+        wxguiItems=wxguiItems,
+        moduleItems=moduleItems,
+    )
     root = tree.getroot()
     tested = _getXMLString(root)
 
@@ -815,18 +840,18 @@ def module_test():
         sys.stdout.write(_getXMLString(root))
         return 0
 
-    menudataFile = 'data/test_toolboxes_menudata_ref.xml'
+    menudataFile = "data/test_toolboxes_menudata_ref.xml"
     with open(menudataFile) as correctMenudata:
         correct = str(correctMenudata.read())
 
     import difflib
+
     differ = difflib.Differ()
-    result = list(differ.compare(correct.splitlines(True),
-                                 tested.splitlines(True)))
+    result = list(differ.compare(correct.splitlines(True), tested.splitlines(True)))
 
     someDiff = False
     for line in result:
-        if line.startswith('+') or line.startswith('-'):
+        if line.startswith("+") or line.startswith("-"):
             sys.stdout.write(line)
             someDiff = True
     if someDiff:
@@ -841,8 +866,11 @@ def validate_file(filename):
     try:
         etree.parse(filename)
     except ETREE_EXCEPTIONS as error:
-        print("XML file <{name}> is not well formed: {error}".format(
-            name=filename, error=error))
+        print(
+            "XML file <{name}> is not well formed: {error}".format(
+                name=filename, error=error
+            )
+        )
         return 1
     return 0
 
@@ -854,24 +882,25 @@ def main():
     """
     # TODO: fix parameter handling
     if len(sys.argv) > 1:
-        mainFile = os.path.join(WXGUIDIR, 'xml', 'module_tree.xml')
+        mainFile = os.path.join(WXGUIDIR, "xml", "module_tree.xml")
     else:
-        mainFile = os.path.join(WXGUIDIR, 'xml', 'main_menu.xml')
-    tree = createTree(distributionRootFile=mainFile, userRootFile=None,
-                      userDefined=False)
+        mainFile = os.path.join(WXGUIDIR, "xml", "main_menu.xml")
+    tree = createTree(
+        distributionRootFile=mainFile, userRootFile=None, userDefined=False
+    )
     root = tree.getroot()
-    sys.stdout.write(decode(_getXMLString(root), encoding='UTF-8'))
+    sys.stdout.write(decode(_getXMLString(root), encoding="UTF-8"))
 
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     # TODO: fix parameter handling
     if len(sys.argv) > 1:
-        if sys.argv[1] == 'doctest':
+        if sys.argv[1] == "doctest":
             sys.exit(doc_test())
-        elif sys.argv[1] == 'test':
+        elif sys.argv[1] == "test":
             sys.exit(module_test())
-        elif sys.argv[1] == 'validate':
+        elif sys.argv[1] == "validate":
             sys.exit(validate_file(sys.argv[2]))
     sys.exit(main())

+ 11 - 8
gui/wxpython/core/treemodel.py

@@ -157,6 +157,7 @@ class TreeModel(object):
         """Filters model based on parameters in kwargs
         that are passed to node's match function.
         Copies tree and returns a filtered copy."""
+
         def _filter(node):
             if node.children:
                 to_remove = []
@@ -216,12 +217,13 @@ class DictNode(object):
         return self._children
 
     def nprint(self, text, indent=0):
-        text.append(indent * ' ' + self.label)
+        text.append(indent * " " + self.label)
         if self.data:
             for key, value in six.iteritems(self.data):
                 text.append(
-                    "%(indent)s* %(key)s : %(value)s" %
-                    {'indent': (indent + 2) * ' ', 'key': key, 'value': value})
+                    "%(indent)s* %(key)s : %(value)s"
+                    % {"indent": (indent + 2) * " ", "key": key, "value": value}
+                )
 
         if self.children:
             for child in self.children:
@@ -243,7 +245,7 @@ class ModuleNode(DictNode):
 
     def __init__(self, label=None, data=None):
         super(ModuleNode, self).__init__(data=data)
-        self._label = label if label else ''
+        self._label = label if label else ""
         if not data:
             self.data = {}
 
@@ -262,7 +264,7 @@ class ModuleNode(DictNode):
             keys = key
 
         for key in keys:
-            if key not in ('command', 'keywords', 'description'):
+            if key not in ("command", "keywords", "description"):
                 return False
             try:
                 text = self.data[key]
@@ -272,20 +274,21 @@ class ModuleNode(DictNode):
                 continue
             if case_sensitive:
                 # start supported but unused, so testing last
-                if value in text or value == '*':
+                if value in text or value == "*":
                     return True
             else:
                 # this works fully only for English and requires accents
                 # to be exact match (even Python 3 casefold() does not help)
-                if value.lower() in text.lower() or value == '*':
+                if value.lower() in text.lower() or value == "*":
                     return True
         return False
 
 
 def main():
     import doctest
+
     doctest.testmod()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()

+ 49 - 44
gui/wxpython/core/units.py

@@ -24,26 +24,28 @@ This program is free software under the GNU General Public License
 import six
 import math
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     import sys
 
 
-
 class BaseUnits:
-
     def __init__(self):
         self._units = dict()
-        self._units['length'] = {0: {'key': 'mu', 'label': _('map units')},
-                                 1: {'key': 'me', 'label': _('meters')},
-                                 2: {'key': 'km', 'label': _('kilometers')},
-                                 3: {'key': 'mi', 'label': _('miles')},
-                                 4: {'key': 'ft', 'label': _('feet')}}
-
-        self._units['area'] = {0: {'key': 'mu', 'label': _('sq map units')},
-                               1: {'key': 'me', 'label': _('sq meters')},
-                               2: {'key': 'km', 'label': _('sq kilometers')},
-                               3: {'key': 'ar', 'label': _('acres')},
-                               4: {'key': 'ht', 'label': _('hectares')}}
+        self._units["length"] = {
+            0: {"key": "mu", "label": _("map units")},
+            1: {"key": "me", "label": _("meters")},
+            2: {"key": "km", "label": _("kilometers")},
+            3: {"key": "mi", "label": _("miles")},
+            4: {"key": "ft", "label": _("feet")},
+        }
+
+        self._units["area"] = {
+            0: {"key": "mu", "label": _("sq map units")},
+            1: {"key": "me", "label": _("sq meters")},
+            2: {"key": "km", "label": _("sq kilometers")},
+            3: {"key": "ar", "label": _("acres")},
+            4: {"key": "ht", "label": _("hectares")},
+        }
 
     def GetUnitsList(self, type):
         """Get list of units (their labels)
@@ -56,7 +58,7 @@ class BaseUnits:
         try:
             keys = sorted(self._units[type].keys())
             for idx in keys:
-                result.append(self._units[type][idx]['label'])
+                result.append(self._units[type][idx]["label"])
         except KeyError:
             pass
 
@@ -68,7 +70,7 @@ class BaseUnits:
         :param type: units type ('length' or 'area')
         :param index: units index
         """
-        return self._units[type][index]['key']
+        return self._units[type][index]["key"]
 
     def GetUnitsIndex(self, type, key):
         """Get units index based on key
@@ -79,10 +81,11 @@ class BaseUnits:
         :return: index
         """
         for k, u in six.iteritems(self._units[type]):
-            if u['key'] == key:
+            if u["key"] == key:
                 return k
         return 0
 
+
 Units = BaseUnits()
 
 
@@ -99,27 +102,27 @@ def ConvertValue(value, type, units):
     # TODO
 
     f = 1
-    if type == 'length':
-        if units == 'me':
+    if type == "length":
+        if units == "me":
             f = 1.0
-        elif units == 'km':
+        elif units == "km":
             f = 1.0e-3
-        elif units == 'mi':
+        elif units == "mi":
             f = 6.21371192237334e-4
-        elif units == 'ft':
+        elif units == "ft":
             f = 3.28083989501312
     else:  # -> area
-        if units == 'me':
+        if units == "me":
             f = 1.0
-        elif units == 'km':
+        elif units == "km":
             f = 1.0e-6
-        elif units == 'mi':
+        elif units == "mi":
             f = 3.86102158542446e-7
-        elif units == 'ft':
+        elif units == "ft":
             f = 10.7639104167097
-        elif units == 'ar':
+        elif units == "ar":
             f = 2.47105381467165e-4
-        elif units == 'ht':
+        elif units == "ht":
             f = 1.0e-4
 
     return f * value
@@ -153,38 +156,38 @@ def formatDist(distance, mapunits):
 
     From code by Hamish Bowman Grass Development Team 2006.
     """
-    if mapunits == 'metres':
-        mapunits = 'meters'
+    if mapunits == "metres":
+        mapunits = "meters"
     outunits = mapunits
     distance = float(distance)
     divisor = 1.0
 
     # figure out which units to use
-    if mapunits == 'meters':
+    if mapunits == "meters":
         if distance > 2500.0:
-            outunits = 'km'
+            outunits = "km"
             divisor = 1000.0
         else:
-            outunits = 'm'
-    elif mapunits == 'feet':
+            outunits = "m"
+    elif mapunits == "feet":
         # nano-bug: we match any "feet", but US Survey feet is really
         #  5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
         #  miles the tick markers are rounded to the nearest 10th of a
         #  mile (528'), the difference in foot flavours is ignored.
         if distance > 5280.0:
-            outunits = 'miles'
+            outunits = "miles"
             divisor = 5280.0
         else:
-            outunits = 'ft'
-    elif 'degree' in mapunits:
+            outunits = "ft"
+    elif "degree" in mapunits:
         # was: 'degree' in mapunits and not haveCtypes (for unknown reason)
         if distance < 1:
-            outunits = 'min'
-            divisor = (1 / 60.0)
+            outunits = "min"
+            divisor = 1 / 60.0
         else:
-            outunits = 'deg'
+            outunits = "deg"
     else:
-        return (distance, 'units')
+        return (distance, "units")
 
     # format numbers in a nice way
     if (distance / divisor) >= 2500.0:
@@ -192,8 +195,9 @@ def formatDist(distance, mapunits):
     elif (distance / divisor) >= 1000.0:
         outdistance = round(distance / divisor, 1)
     elif (distance / divisor) > 0.0:
-        outdistance = round(distance / divisor,
-                            int(math.ceil(3 - math.log10(distance / divisor))))
+        outdistance = round(
+            distance / divisor, int(math.ceil(3 - math.log10(distance / divisor)))
+        )
     else:
         outdistance = float(distance / divisor)
 
@@ -207,9 +211,10 @@ def doc_test():
     """
     import doctest
     from core.utils import do_doctest_gettext_workaround
+
     do_doctest_gettext_workaround()
     return doctest.testmod().failed
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(doc_test())

File diff ditekan karena terlalu besar
+ 349 - 351
gui/wxpython/core/utils.py


File diff ditekan karena terlalu besar
+ 1056 - 983
gui/wxpython/core/workspace.py


+ 103 - 109
gui/wxpython/core/ws.py

@@ -40,13 +40,16 @@ from grass.pydispatch.signal import Signal
 
 
 class RenderWMSMgr(wx.EvtHandler):
-    """Fetch and prepare WMS data for rendering.
-    """
+    """Fetch and prepare WMS data for rendering."""
 
     def __init__(self, layer, env):
         if not haveGdal:
-            sys.stderr.write(_("Unable to load GDAL Python bindings.\n"
-                               "WMS layers can not be displayed without the bindings.\n"))
+            sys.stderr.write(
+                _(
+                    "Unable to load GDAL Python bindings.\n"
+                    "WMS layers can not be displayed without the bindings.\n"
+                )
+            )
 
         self.layer = layer
 
@@ -64,9 +67,9 @@ class RenderWMSMgr(wx.EvtHandler):
         self.tempMap = grass.tempfile()
         self.dstSize = {}
 
-        self.dataFetched = Signal('RenderWMSMgr.dataFetched')
-        self.updateProgress = Signal('RenderWMSMgr.updateProgress')
-        self.renderingFailed = Signal('RenderWMSMgr.renderingFailed')
+        self.dataFetched = Signal("RenderWMSMgr.dataFetched")
+        self.updateProgress = Signal("RenderWMSMgr.updateProgress")
+        self.renderingFailed = Signal("RenderWMSMgr.renderingFailed")
 
     def __del__(self):
         try_remove(self.tempMap)
@@ -81,12 +84,15 @@ class RenderWMSMgr(wx.EvtHandler):
         if not haveGdal:
             return
 
-        Debug.msg(1, "RenderWMSMgr.Render(%s): force=%d img=%s" %
-                  (self.layer, self.layer.forceRender, self.layer.mapfile))
+        Debug.msg(
+            1,
+            "RenderWMSMgr.Render(%s): force=%d img=%s"
+            % (self.layer, self.layer.forceRender, self.layer.mapfile),
+        )
 
         env = copy.copy(env)
-        self.dstSize['cols'] = int(env["GRASS_RENDER_WIDTH"])
-        self.dstSize['rows'] = int(env["GRASS_RENDER_HEIGHT"])
+        self.dstSize["cols"] = int(env["GRASS_RENDER_WIDTH"])
+        self.dstSize["rows"] = int(env["GRASS_RENDER_HEIGHT"])
 
         region = self._getRegionDict(env)
         self._fitAspect(region, self.dstSize)
@@ -95,19 +101,16 @@ class RenderWMSMgr(wx.EvtHandler):
         fetchData = True  # changed to True when calling Render()
         zoomChanged = False
 
-        if self.renderedRegion is None or \
-           cmd != self.fetched_data_cmd:
+        if self.renderedRegion is None or cmd != self.fetched_data_cmd:
             fetchData = True
         else:
-            for c in ['north', 'south', 'east', 'west']:
-                if self.renderedRegion and \
-                   region[c] != self.renderedRegion[c]:
+            for c in ["north", "south", "east", "west"]:
+                if self.renderedRegion and region[c] != self.renderedRegion[c]:
                     fetchData = True
                     break
 
-            for c in ['e-w resol', 'n-s resol']:
-                if self.renderedRegion and \
-                        region[c] != self.renderedRegion[c]:
+            for c in ["e-w resol", "n-s resol"]:
+                if self.renderedRegion and region[c] != self.renderedRegion[c]:
                     zoomChanged = True
                     break
 
@@ -128,11 +131,16 @@ class RenderWMSMgr(wx.EvtHandler):
             env["GRASS_REGION"] = self._createRegionStr(region)
 
             cmd_render = copy.deepcopy(cmd)
-            cmd_render[1]['quiet'] = True  # be quiet
+            cmd_render[1]["quiet"] = True  # be quiet
 
             self._startTime = time.time()
-            self.thread.Run(callable=self._render, cmd=cmd_render, env=env,
-                            ondone=self.OnRenderDone, userdata={'env': env})
+            self.thread.Run(
+                callable=self._render,
+                cmd=cmd_render,
+                env=env,
+                ondone=self.OnRenderDone,
+                userdata={"env": env},
+            )
             self.layer.forceRender = False
 
         self.updateProgress.emit(env=env, layer=self.layer)
@@ -147,13 +155,12 @@ class RenderWMSMgr(wx.EvtHandler):
             return 1
 
     def OnRenderDone(self, event):
-        """Fetch data
-        """
+        """Fetch data"""
         if event.pid != self.currentPid:
             return
         self.downloading = False
         if not self.updateMap:
-            self.updateProgress.emit(env=event.userdata['env'], layer=self.layer)
+            self.updateProgress.emit(env=event.userdata["env"], layer=self.layer)
             self.renderedRegion = None
             self.fetched_data_cmd = None
             return
@@ -162,51 +169,56 @@ class RenderWMSMgr(wx.EvtHandler):
             targetFile=self.layer.mapfile,
             region=self.renderedRegion,
             bandsNum=3,
-            gdalDriver='PNM',
-            fillValue=0)
+            gdalDriver="PNM",
+            fillValue=0,
+        )
         self.mapMerger.AddRasterBands(self.tempMap, {1: 1, 2: 2, 3: 3})
         del self.mapMerger
 
         add_alpha_channel = True
         mask_fill_value = 0
-        if self.fetching_cmd[1]['format'] == 'jpeg':
-            mask_fill_value = 255 # white color, g.pnmcomp doesn't apply mask (alpha channel)
+        if self.fetching_cmd[1]["format"] == "jpeg":
+            mask_fill_value = (
+                255  # white color, g.pnmcomp doesn't apply mask (alpha channel)
+            )
             add_alpha_channel = False
 
         self.maskMerger = GDALRasterMerger(
             targetFile=self.layer.maskfile,
             region=self.renderedRegion,
             bandsNum=1,
-            gdalDriver='PNM',
-            fillValue=mask_fill_value)
+            gdalDriver="PNM",
+            fillValue=mask_fill_value,
+        )
         if add_alpha_channel:
-            #{4 : 1} alpha channel (4) to first and only channel (1) in mask
+            # {4 : 1} alpha channel (4) to first and only channel (1) in mask
             self.maskMerger.AddRasterBands(self.tempMap, {4: 1})
         del self.maskMerger
 
         self.fetched_data_cmd = self.fetching_cmd
 
-        Debug.msg(1, "RenderWMSMgr.OnRenderDone(%s): ret=%d time=%f" %
-                  (self.layer, event.ret, time.time() - self._startTime))
+        Debug.msg(
+            1,
+            "RenderWMSMgr.OnRenderDone(%s): ret=%d time=%f"
+            % (self.layer, event.ret, time.time() - self._startTime),
+        )
 
-        self.dataFetched.emit(env=event.userdata['env'], layer=self.layer)
+        self.dataFetched.emit(env=event.userdata["env"], layer=self.layer)
 
     def _getRegionDict(self, env):
-        """Parse string from GRASS_REGION env variable into dict.
-        """
+        """Parse string from GRASS_REGION env variable into dict."""
         region = {}
-        parsedRegion = env["GRASS_REGION"].split(';')
+        parsedRegion = env["GRASS_REGION"].split(";")
         for r in parsedRegion:
-            r = r.split(':')
+            r = r.split(":")
             r[0] = r[0].strip()
             if len(r) < 2:
                 continue
             try:
-                if r[0] in ['e-w resol3', 'n-s resol3', 'rows3', 'cols3',
-                            'depths']:
+                if r[0] in ["e-w resol3", "n-s resol3", "rows3", "cols3", "depths"]:
                     # ignore 3D region values (causing problems in latlong locations)
                     continue
-                if r[0] in ['cols', 'rows', 'zone', 'proj']:
+                if r[0] in ["cols", "rows", "zone", "proj"]:
                     region[r[0]] = int(r[1])
                 else:
                     region[r[0]] = float(r[1])
@@ -216,42 +228,39 @@ class RenderWMSMgr(wx.EvtHandler):
         return region
 
     def _createRegionStr(self, region):
-        """Create string for GRASS_REGION env variable from  dict created by _getRegionDict.
-        """
-        regionStr = ''
+        """Create string for GRASS_REGION env variable from  dict created by _getRegionDict."""
+        regionStr = ""
         for k, v in six.iteritems(region):
-            item = k + ': ' + str(v)
+            item = k + ": " + str(v)
             if regionStr:
-                regionStr += '; '
+                regionStr += "; "
             regionStr += item
 
         return regionStr
 
     def IsDownloading(self):
-        """Is it downloading any data from WMS server?
-        """
+        """Is it downloading any data from WMS server?"""
         return self.downloading
 
     def _fitAspect(self, region, size):
-        """Compute region parameters to have direction independent resolution.
-        """
-        if region['n-s resol'] > region['e-w resol']:
-            delta = region['n-s resol'] * size['cols'] / 2
+        """Compute region parameters to have direction independent resolution."""
+        if region["n-s resol"] > region["e-w resol"]:
+            delta = region["n-s resol"] * size["cols"] / 2
 
-            center = (region['east'] - region['west']) / 2
+            center = (region["east"] - region["west"]) / 2
 
-            region['east'] = center + delta + region['west']
-            region['west'] = center - delta + region['west']
-            region['e-w resol'] = region['n-s resol']
+            region["east"] = center + delta + region["west"]
+            region["west"] = center - delta + region["west"]
+            region["e-w resol"] = region["n-s resol"]
 
         else:
-            delta = region['e-w resol'] * size['rows'] / 2
+            delta = region["e-w resol"] * size["rows"] / 2
 
-            center = (region['north'] - region['south']) / 2
+            center = (region["north"] - region["south"]) / 2
 
-            region['north'] = center + delta + region['south']
-            region['south'] = center - delta + region['south']
-            region['n-s resol'] = region['e-w resol']
+            region["north"] = center + delta + region["south"]
+            region["south"] = center - delta + region["south"]
+            region["n-s resol"] = region["e-w resol"]
 
     def Abort(self):
         """Abort rendering process"""
@@ -267,31 +276,26 @@ class RenderWMSMgr(wx.EvtHandler):
 class GDALRasterMerger:
     """Merge rasters.
 
-        Based on gdal_merge.py utility.
+    Based on gdal_merge.py utility.
     """
 
-    def __init__(self, targetFile, region, bandsNum,
-                 gdalDriver, fillValue=None):
-        """Create raster for merging.
-        """
+    def __init__(self, targetFile, region, bandsNum, gdalDriver, fillValue=None):
+        """Create raster for merging."""
         self.gdalDrvType = gdalDriver
 
-        nsRes = (region['south'] - region['north']) / region['rows']
-        ewRes = (region['east'] - region['west']) / region['cols']
+        nsRes = (region["south"] - region["north"]) / region["rows"]
+        ewRes = (region["east"] - region["west"]) / region["cols"]
 
-        self.tGeotransform = [
-            region['west'],
-            ewRes, 0, region['north'],
-            0, nsRes]
+        self.tGeotransform = [region["west"], ewRes, 0, region["north"], 0, nsRes]
 
         self.tUlx, self.tUly, self.tLrx, self.tLry = self._getCorners(
-            self.tGeotransform, region)
+            self.tGeotransform, region
+        )
 
         driver = gdal.GetDriverByName(self.gdalDrvType)
         self.tDataset = driver.Create(
-            targetFile, region['cols'],
-            region['rows'],
-            bandsNum, gdal.GDT_Byte)
+            targetFile, region["cols"], region["rows"], bandsNum, gdal.GDT_Byte
+        )
 
         if fillValue is not None:
             # fill raster bands with a constant value
@@ -299,18 +303,14 @@ class GDALRasterMerger:
                 self.tDataset.GetRasterBand(iBand).Fill(fillValue)
 
     def AddRasterBands(self, sourceFile, sTBands):
-        """Add raster bands from sourceFile into the merging raster.
-        """
+        """Add raster bands from sourceFile into the merging raster."""
         sDataset = gdal.Open(sourceFile, gdal.GA_ReadOnly)
         if sDataset is None:
             return
 
         sGeotransform = sDataset.GetGeoTransform()
 
-        sSize = {
-            'rows': sDataset.RasterYSize,
-            'cols': sDataset.RasterXSize
-        }
+        sSize = {"rows": sDataset.RasterYSize, "cols": sDataset.RasterXSize}
 
         sUlx, sUly, sLrx, sLry = self._getCorners(sGeotransform, sSize)
 
@@ -333,20 +333,16 @@ class GDALRasterMerger:
             return
 
         # compute target window in pixel coordinates.
-        tXoff = int(
-            (tIntsctUlx -
-             self.tGeotransform[0]) /
-            self.tGeotransform[1] +
-            0.1)
-        tYoff = int(
-            (tIntsctUly -
-             self.tGeotransform[3]) /
-            self.tGeotransform[5] +
-            0.1)
-        tXsize = int(
-            (tIntsctLrx - self.tGeotransform[0]) / self.tGeotransform[1] + 0.5) - tXoff
-        tYsize = int(
-            (tIntsctLry - self.tGeotransform[3]) / self.tGeotransform[5] + 0.5) - tYoff
+        tXoff = int((tIntsctUlx - self.tGeotransform[0]) / self.tGeotransform[1] + 0.1)
+        tYoff = int((tIntsctUly - self.tGeotransform[3]) / self.tGeotransform[5] + 0.1)
+        tXsize = (
+            int((tIntsctLrx - self.tGeotransform[0]) / self.tGeotransform[1] + 0.5)
+            - tXoff
+        )
+        tYsize = (
+            int((tIntsctLry - self.tGeotransform[3]) / self.tGeotransform[5] + 0.5)
+            - tYoff
+        )
 
         if tXsize < 1 or tYsize < 1:
             return
@@ -354,34 +350,32 @@ class GDALRasterMerger:
         # Compute source window in pixel coordinates.
         sXoff = int((tIntsctUlx - sGeotransform[0]) / sGeotransform[1])
         sYoff = int((tIntsctUly - sGeotransform[3]) / sGeotransform[5])
-        sXsize = int(
-            (tIntsctLrx - sGeotransform[0]) / sGeotransform[1] + 0.5) - sXoff
-        sYsize = int(
-            (tIntsctLry - sGeotransform[3]) / sGeotransform[5] + 0.5) - sYoff
+        sXsize = int((tIntsctLrx - sGeotransform[0]) / sGeotransform[1] + 0.5) - sXoff
+        sYsize = int((tIntsctLry - sGeotransform[3]) / sGeotransform[5] + 0.5) - sYoff
 
         if sXsize < 1 or sYsize < 1:
             return
 
         for sBandNnum, tBandNum in six.iteritems(sTBands):
             bandData = sDataset.GetRasterBand(sBandNnum).ReadRaster(
-                sXoff, sYoff, sXsize, sYsize, tXsize, tYsize, gdal.GDT_Byte)
+                sXoff, sYoff, sXsize, sYsize, tXsize, tYsize, gdal.GDT_Byte
+            )
             self.tDataset.GetRasterBand(tBandNum).WriteRaster(
-                tXoff, tYoff, tXsize, tYsize, bandData, tXsize, tYsize, gdal.GDT_Byte)
+                tXoff, tYoff, tXsize, tYsize, bandData, tXsize, tYsize, gdal.GDT_Byte
+            )
 
     def _getCorners(self, geoTrans, size):
 
         ulx = geoTrans[0]
         uly = geoTrans[3]
-        lrx = geoTrans[0] + size['cols'] * geoTrans[1]
-        lry = geoTrans[3] + size['rows'] * geoTrans[5]
+        lrx = geoTrans[0] + size["cols"] * geoTrans[1]
+        lry = geoTrans[3] + size["rows"] * geoTrans[5]
 
         return ulx, uly, lrx, lry
 
     def SetGeorefAndProj(self):
-        """Set georeference and projection to target file
-        """
-        projection = grass.read_command('g.proj',
-                                        flags='wf')
+        """Set georeference and projection to target file"""
+        projection = grass.read_command("g.proj", flags="wf")
         self.tDataset.SetProjection(projection)
 
         self.tDataset.SetGeoTransform(self.tGeotransform)

+ 1 - 7
gui/wxpython/datacatalog/__init__.py

@@ -1,7 +1 @@
-all = [
-    'catalog',
-    'frame',
-    'tree',
-    'dialogs',
-    'infomanager'
-]
+all = ["catalog", "frame", "tree", "dialogs", "infomanager"]

+ 58 - 26
gui/wxpython/datacatalog/catalog.py

@@ -31,18 +31,27 @@ from grass.script import gisenv
 from grass.pydispatch.signal import Signal
 
 from grass.grassdb.manage import split_mapset_path
-from grass.grassdb.checks import (get_reason_id_mapset_not_usable,
-                          is_fallback_session,
-                          is_first_time_user)
+from grass.grassdb.checks import (
+    get_reason_id_mapset_not_usable,
+    is_fallback_session,
+    is_first_time_user,
+)
 
 
 class DataCatalog(wx.Panel):
     """Data catalog panel"""
 
-    def __init__(self, parent, giface=None, id=wx.ID_ANY,
-                 title=_("Data catalog"), name='catalog', **kwargs):
+    def __init__(
+        self,
+        parent,
+        giface=None,
+        id=wx.ID_ANY,
+        title=_("Data catalog"),
+        name="catalog",
+        **kwargs,
+    ):
         """Panel constructor  """
-        self.showNotification = Signal('DataCatalog.showNotification')
+        self.showNotification = Signal("DataCatalog.showNotification")
         self.parent = parent
         self.baseTitle = title
         self.giface = giface
@@ -64,8 +73,9 @@ class DataCatalog(wx.Panel):
         self.giface.currentMapsetChanged.connect(self.dismissInfobar)
 
         # infobar manager for data catalog
-        self.infoManager = DataCatalogInfoManager(infobar=self.infoBar,
-                                                  giface=self.giface)
+        self.infoManager = DataCatalogInfoManager(
+            infobar=self.infoBar, giface=self.giface
+        )
         self.tree.showImportDataInfo.connect(self.showImportDataInfo)
 
         # some layout
@@ -111,7 +121,9 @@ class DataCatalog(wx.Panel):
         self.infoManager.ShowFallbackSessionInfo(self.reason_id)
 
     def showImportDataInfo(self):
-        self.infoManager.ShowImportDataInfo(self.OnImportOgrLayers, self.OnImportGdalLayers)
+        self.infoManager.ShowImportDataInfo(
+            self.OnImportOgrLayers, self.OnImportGdalLayers
+        )
 
     def LoadItems(self):
         self.tree.ReloadTreeItems()
@@ -130,8 +142,9 @@ class DataCatalog(wx.Panel):
 
     def OnAddGrassDB(self, event):
         """Add grass database"""
-        dlg = wx.DirDialog(self, _("Choose GRASS data directory:"),
-                           os.getcwd(), wx.DD_DEFAULT_STYLE)
+        dlg = wx.DirDialog(
+            self, _("Choose GRASS data directory:"), os.getcwd(), wx.DD_DEFAULT_STYLE
+        )
         if dlg.ShowModal() == wx.ID_OK:
             grassdatabase = dlg.GetPath()
             grassdb_node = self.tree.InsertGrassDb(name=grassdatabase)
@@ -139,11 +152,12 @@ class DataCatalog(wx.Panel):
             # Offer to create a new location
             if grassdb_node and not os.listdir(grassdatabase):
                 message = _("Do you want to create a location?")
-                dlg2 = wx.MessageDialog(self,
-                                        message=message,
-                                        caption=_("Create location?"),
-                                        style=wx.YES_NO | wx.YES_DEFAULT |
-                                        wx.ICON_QUESTION)
+                dlg2 = wx.MessageDialog(
+                    self,
+                    message=message,
+                    caption=_("Create location?"),
+                    style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
+                )
                 if dlg2.ShowModal() == wx.ID_YES:
                     self.tree.CreateLocation(grassdb_node)
                 dlg2.Destroy()
@@ -173,6 +187,7 @@ class DataCatalog(wx.Panel):
     def OnImportGdalLayers(self, event):
         """Convert multiple GDAL layers to GRASS raster map layers"""
         from modules.import_export import GdalImportDialog
+
         dlg = GdalImportDialog(parent=self, giface=self.giface)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -180,6 +195,7 @@ class DataCatalog(wx.Panel):
     def OnImportOgrLayers(self, event):
         """Convert multiple OGR layers to GRASS vector map layers"""
         from modules.import_export import OgrImportDialog
+
         dlg = OgrImportDialog(parent=self, giface=self.giface)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -187,6 +203,7 @@ class DataCatalog(wx.Panel):
     def OnLinkGdalLayers(self, event):
         """Link multiple GDAL layers to GRASS raster map layers"""
         from modules.import_export import GdalImportDialog
+
         dlg = GdalImportDialog(parent=self, giface=self.giface, link=True)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -194,6 +211,7 @@ class DataCatalog(wx.Panel):
     def OnLinkOgrLayers(self, event):
         """Links multiple OGR layers to GRASS vector map layers"""
         from modules.import_export import OgrImportDialog
+
         dlg = OgrImportDialog(parent=self, giface=self.giface, link=True)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -201,6 +219,7 @@ class DataCatalog(wx.Panel):
     def OnRasterOutputFormat(self, event):
         """Set raster output format handler"""
         from modules.import_export import GdalOutputDialog
+
         dlg = GdalOutputDialog(parent=self, ogr=False)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -208,6 +227,7 @@ class DataCatalog(wx.Panel):
     def OnVectorOutputFormat(self, event):
         """Set vector output format handler"""
         from modules.import_export import GdalOutputDialog
+
         dlg = GdalOutputDialog(parent=self, ogr=True)
         dlg.CentreOnScreen()
         dlg.Show()
@@ -231,21 +251,29 @@ class DataCatalog(wx.Panel):
         # create submenu
         subMenu = Menu()
 
-        subitem = wx.MenuItem(subMenu, wx.ID_ANY, _("Link external raster data  [r.external]"))
+        subitem = wx.MenuItem(
+            subMenu, wx.ID_ANY, _("Link external raster data  [r.external]")
+        )
         subMenu.AppendItem(subitem)
         self.Bind(wx.EVT_MENU, self.OnLinkGdalLayers, subitem)
 
-        subitem = wx.MenuItem(subMenu, wx.ID_ANY, _("Link external vector data  [v.external]"))
+        subitem = wx.MenuItem(
+            subMenu, wx.ID_ANY, _("Link external vector data  [v.external]")
+        )
         subMenu.AppendItem(subitem)
         self.Bind(wx.EVT_MENU, self.OnLinkOgrLayers, subitem)
 
         subMenu.AppendSeparator()
 
-        subitem = wx.MenuItem(subMenu, wx.ID_ANY, _("Set raster output format  [r.external.out]"))
+        subitem = wx.MenuItem(
+            subMenu, wx.ID_ANY, _("Set raster output format  [r.external.out]")
+        )
         subMenu.AppendItem(subitem)
         self.Bind(wx.EVT_MENU, self.OnRasterOutputFormat, subitem)
 
-        subitem = wx.MenuItem(subMenu, wx.ID_ANY, _("Set vector output format  [v.external.out]"))
+        subitem = wx.MenuItem(
+            subMenu, wx.ID_ANY, _("Set vector output format  [v.external.out]")
+        )
         subMenu.AppendItem(subitem)
         self.Bind(wx.EVT_MENU, self.OnVectorOutputFormat, subitem)
 
@@ -254,21 +282,25 @@ class DataCatalog(wx.Panel):
 
         item = wx.MenuItem(menu, wx.ID_ANY, _("Unpack GRASS raster map  [r.unpack]"))
         menu.AppendItem(item)
-        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand('r.unpack'), item)
+        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand("r.unpack"), item)
 
         item = wx.MenuItem(menu, wx.ID_ANY, _("Unpack GRASS vector map  [v.unpack]"))
         menu.AppendItem(item)
-        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand('v.unpack'), item)
+        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand("v.unpack"), item)
 
         menu.AppendSeparator()
 
-        item = wx.MenuItem(menu, wx.ID_ANY, _("Create raster map from x,y,z data  [r.in.xyz]"))
+        item = wx.MenuItem(
+            menu, wx.ID_ANY, _("Create raster map from x,y,z data  [r.in.xyz]")
+        )
         menu.AppendItem(item)
-        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand('r.in.xyz'), item)
+        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand("r.in.xyz"), item)
 
-        item = wx.MenuItem(menu, wx.ID_ANY, _("Create vector map from x,y,z data  [v.in.ascii]"))
+        item = wx.MenuItem(
+            menu, wx.ID_ANY, _("Create vector map from x,y,z data  [v.in.ascii]")
+        )
         menu.AppendItem(item)
-        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand('v.in.ascii'), item)
+        self.Bind(wx.EVT_MENU, lambda evt: self.GuiParseCommand("v.in.ascii"), item)
 
         menu.AppendSeparator()
         menu.AppendMenu(wx.ID_ANY, _("Link external data"), subMenu)

+ 142 - 69
gui/wxpython/datacatalog/dialogs.py

@@ -25,18 +25,33 @@ from grass.script import parse_key_val, region_env
 
 
 class CatalogReprojectionDialog(wx.Dialog):
-    def __init__(self, parent, giface, inputGisdbase, inputLocation,
-                 inputMapset, inputLayer, inputEnv,
-                 outputGisdbase, outputLocation, outputMapset, outputLayer,
-                 etype, outputEnv, callback,
-                 id=wx.ID_ANY, title=_("Reprojection"),
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+    def __init__(
+        self,
+        parent,
+        giface,
+        inputGisdbase,
+        inputLocation,
+        inputMapset,
+        inputLayer,
+        inputEnv,
+        outputGisdbase,
+        outputLocation,
+        outputMapset,
+        outputLayer,
+        etype,
+        outputEnv,
+        callback,
+        id=wx.ID_ANY,
+        title=_("Reprojection"),
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+    ):
 
         self.parent = parent
         self._giface = giface
 
-        wx.Dialog.__init__(self, parent, id, title, style=style,
-                           name="ReprojectionDialog")
+        wx.Dialog.__init__(
+            self, parent, id, title, style=style, name="ReprojectionDialog"
+        )
 
         self.panel = wx.Panel(parent=self)
         self.iGisdbase = inputGisdbase
@@ -55,19 +70,29 @@ class CatalogReprojectionDialog(wx.Dialog):
         self._widgets()
         self._doLayout()
 
-        if self.etype == 'raster':
+        if self.etype == "raster":
             self._estimateResampling()
             self._estimateResolution()
 
     def _widgets(self):
-        if self.etype == 'raster':
+        if self.etype == "raster":
             self.resolution = TextCtrl(self.panel, validator=FloatValidator())
-            self.resampling = wx.Choice(self.panel, size=(200, -1),
-                                        choices=['nearest', 'bilinear', 'bicubic', 'lanczos',
-                                                 'bilinear_f', 'bicubic_f', 'lanczos_f'])
+            self.resampling = wx.Choice(
+                self.panel,
+                size=(200, -1),
+                choices=[
+                    "nearest",
+                    "bilinear",
+                    "bicubic",
+                    "lanczos",
+                    "bilinear_f",
+                    "bicubic_f",
+                    "lanczos_f",
+                ],
+            )
         else:
             self.vsplit = TextCtrl(self.panel, validator=IntegerValidator())
-            self.vsplit.SetValue('10000')
+            self.vsplit.SetValue("10000")
 
         #
         # buttons
@@ -77,9 +102,9 @@ class CatalogReprojectionDialog(wx.Dialog):
 
         # run
         self.btn_run = Button(parent=self.panel, id=wx.ID_OK, label=_("Reproject"))
-        if self.etype == 'raster':
+        if self.etype == "raster":
             self.btn_run.SetToolTip(_("Reproject raster"))
-        elif self.etype == 'vector':
+        elif self.etype == "vector":
             self.btn_run.SetToolTip(_("Reproject vector"))
         self.btn_run.SetDefault()
         self.btn_run.Bind(wx.EVT_BUTTON, self.OnReproject)
@@ -89,27 +114,42 @@ class CatalogReprojectionDialog(wx.Dialog):
         dialogSizer = wx.BoxSizer(wx.VERTICAL)
         optionsSizer = wx.GridBagSizer(5, 5)
 
-        label = _("Map layer <{ml}> needs to be reprojected.\n"
-                  "Please review and modify reprojection parameters:").format(ml=self.iLayer)
-        dialogSizer.Add(StaticText(self.panel, label=label),
-                        flag=wx.ALL | wx.EXPAND, border=10)
-        if self.etype == 'raster':
-            optionsSizer.Add(StaticText(self.panel, label=_("Estimated resolution:")),
-                             pos=(0, 0), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+        label = _(
+            "Map layer <{ml}> needs to be reprojected.\n"
+            "Please review and modify reprojection parameters:"
+        ).format(ml=self.iLayer)
+        dialogSizer.Add(
+            StaticText(self.panel, label=label), flag=wx.ALL | wx.EXPAND, border=10
+        )
+        if self.etype == "raster":
+            optionsSizer.Add(
+                StaticText(self.panel, label=_("Estimated resolution:")),
+                pos=(0, 0),
+                flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
+            )
             optionsSizer.Add(self.resolution, pos=(0, 1), flag=wx.EXPAND)
-            optionsSizer.Add(StaticText(self.panel, label=_("Resampling method:")),
-                             pos=(1, 0), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+            optionsSizer.Add(
+                StaticText(self.panel, label=_("Resampling method:")),
+                pos=(1, 0),
+                flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
+            )
             optionsSizer.Add(self.resampling, pos=(1, 1), flag=wx.EXPAND)
         else:
-            optionsSizer.Add(StaticText(self.panel, label=_("Maximum segment length:")),
-                             pos=(1, 0), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+            optionsSizer.Add(
+                StaticText(self.panel, label=_("Maximum segment length:")),
+                pos=(1, 0),
+                flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
+            )
             optionsSizer.Add(self.vsplit, pos=(1, 1), flag=wx.EXPAND)
         optionsSizer.AddGrowableCol(1)
         dialogSizer.Add(optionsSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=10)
-        helptext = StaticText(self.panel,
-                              label="For more reprojection options,"
-                              " please see {module}".format(module='r.proj' if self.etype == 'raster'
-                                                            else 'v.proj'))
+        helptext = StaticText(
+            self.panel,
+            label="For more reprojection options,"
+            " please see {module}".format(
+                module="r.proj" if self.etype == "raster" else "v.proj"
+            ),
+        )
         dialogSizer.Add(helptext, proportion=0, flag=wx.ALL | wx.EXPAND, border=10)
         #
         # buttons
@@ -127,53 +167,86 @@ class CatalogReprojectionDialog(wx.Dialog):
         self.SetSize(self.GetBestSize())
 
     def _estimateResolution(self):
-        output = RunCommand('r.proj', flags='g', quiet=False, read=True, input=self.iLayer,
-                            dbase=self.iGisdbase, location=self.iLocation, mapset=self.iMapset,
-                            env=self.oEnv).strip()
-        params = parse_key_val(output, vsep=' ')
-        output = RunCommand('g.region', flags='ug', quiet=False, read=True, env=self.oEnv,
-                            parse=lambda x: parse_key_val(x, val_type=float), **params)
-        cell_ns = (output['n'] - output['s']) / output['rows']
-        cell_ew = (output['e'] - output['w']) / output['cols']
-        estimate = (cell_ew + cell_ns) / 2.
+        output = RunCommand(
+            "r.proj",
+            flags="g",
+            quiet=False,
+            read=True,
+            input=self.iLayer,
+            dbase=self.iGisdbase,
+            location=self.iLocation,
+            mapset=self.iMapset,
+            env=self.oEnv,
+        ).strip()
+        params = parse_key_val(output, vsep=" ")
+        output = RunCommand(
+            "g.region",
+            flags="ug",
+            quiet=False,
+            read=True,
+            env=self.oEnv,
+            parse=lambda x: parse_key_val(x, val_type=float),
+            **params,
+        )
+        cell_ns = (output["n"] - output["s"]) / output["rows"]
+        cell_ew = (output["e"] - output["w"]) / output["cols"]
+        estimate = (cell_ew + cell_ns) / 2.0
         self.resolution.SetValue(str(estimate))
         self.params = params
 
     def _estimateResampling(self):
-        output = RunCommand('r.info', flags='g', quiet=False, read=True, map=self.iLayer,
-                            env=self.iEnv, parse=parse_key_val)
-        if output['datatype'] == 'CELL':
-            self.resampling.SetStringSelection('nearest')
+        output = RunCommand(
+            "r.info",
+            flags="g",
+            quiet=False,
+            read=True,
+            map=self.iLayer,
+            env=self.iEnv,
+            parse=parse_key_val,
+        )
+        if output["datatype"] == "CELL":
+            self.resampling.SetStringSelection("nearest")
         else:
-            self.resampling.SetStringSelection('bilinear')
+            self.resampling.SetStringSelection("bilinear")
 
     def OnReproject(self, event):
         cmd = []
-        if self.etype == 'raster':
-            cmd.append('r.proj')
-            cmd.append('dbase=' + self.iGisdbase)
-            cmd.append('location=' + self.iLocation)
-            cmd.append('mapset=' + self.iMapset)
-            cmd.append('input=' + self.iLayer)
-            cmd.append('output=' + self.oLayer)
-            cmd.append('method=' + self.resampling.GetStringSelection())
-
-            self.oEnv['GRASS_REGION'] = region_env(n=self.params['n'], s=self.params['s'],
-                                                   e=self.params['e'], w=self.params['w'],
-                                                   flags='a', res=float(self.resolution.GetValue()),
-                                                   env=self.oEnv)
+        if self.etype == "raster":
+            cmd.append("r.proj")
+            cmd.append("dbase=" + self.iGisdbase)
+            cmd.append("location=" + self.iLocation)
+            cmd.append("mapset=" + self.iMapset)
+            cmd.append("input=" + self.iLayer)
+            cmd.append("output=" + self.oLayer)
+            cmd.append("method=" + self.resampling.GetStringSelection())
+
+            self.oEnv["GRASS_REGION"] = region_env(
+                n=self.params["n"],
+                s=self.params["s"],
+                e=self.params["e"],
+                w=self.params["w"],
+                flags="a",
+                res=float(self.resolution.GetValue()),
+                env=self.oEnv,
+            )
         else:
-            cmd.append('v.proj')
-            cmd.append('dbase=' + self.iGisdbase)
-            cmd.append('location=' + self.iLocation)
-            cmd.append('mapset=' + self.iMapset)
-            cmd.append('input=' + self.iLayer)
-            cmd.append('output=' + self.oLayer)
-            cmd.append('smax=' + self.vsplit.GetValue())
-
-        self._giface.RunCmd(cmd, env=self.oEnv, compReg=False, addLayer=False,
-                            onDone=self._onDone, userData=None,
-                            notification=Notification.MAKE_VISIBLE)
+            cmd.append("v.proj")
+            cmd.append("dbase=" + self.iGisdbase)
+            cmd.append("location=" + self.iLocation)
+            cmd.append("mapset=" + self.iMapset)
+            cmd.append("input=" + self.iLayer)
+            cmd.append("output=" + self.oLayer)
+            cmd.append("smax=" + self.vsplit.GetValue())
+
+        self._giface.RunCmd(
+            cmd,
+            env=self.oEnv,
+            compReg=False,
+            addLayer=False,
+            onDone=self._onDone,
+            userData=None,
+            notification=Notification.MAKE_VISIBLE,
+        )
 
         event.Skip()
 

+ 3 - 10
gui/wxpython/datacatalog/frame.py

@@ -28,15 +28,10 @@ from gui_core.wrap import Button
 class DataCatalogFrame(wx.Frame):
     """Frame for testing purposes only."""
 
-    def __init__(self, parent, giface=None, title=_('Data Catalog')):
+    def __init__(self, parent, giface=None, title=_("Data Catalog")):
         wx.Frame.__init__(self, parent=parent, title=title)
         self.SetName("DataCatalog")
-        self.SetIcon(
-            wx.Icon(
-                os.path.join(
-                    ICONDIR,
-                    'grass.ico'),
-                wx.BITMAP_TYPE_ICO))
+        self.SetIcon(wx.Icon(os.path.join(ICONDIR, "grass.ico"), wx.BITMAP_TYPE_ICO))
 
         self._giface = giface
         self.panel = wx.Panel(self)
@@ -61,9 +56,7 @@ class DataCatalogFrame(wx.Frame):
         btnSizer.AddStretchSpacer()
         btnSizer.Add(self.btnClose)
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.ALL | wx.EXPAND,
-                  border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
 
         self.panel.SetSizer(sizer)
         sizer.Fit(self.panel)

+ 3 - 1
gui/wxpython/datacatalog/g.gui.datacatalog.py

@@ -36,6 +36,7 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.giface import StandaloneGrassInterface
@@ -52,5 +53,6 @@ def main():
     frame.Show()
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

+ 75 - 72
gui/wxpython/datacatalog/toolbars.py

@@ -20,63 +20,62 @@ from gui_core.wrap import SearchCtrl
 from icons.icon import MetaIcon
 
 icons = {
-    'reloadTree': MetaIcon(
-        img='redraw',
-        label=_("Reload GRASS locations")),
-    'reloadMapset': MetaIcon(
-        img='reload',
-        label=_("Reload current GRASS mapset only")),
-    'unlocked': MetaIcon(
-        img='edit',
-        label=_("Restrict edits to the current mapset only")),
-    'locked': MetaIcon(
-        img='edit',
-        label=_("Allow edits outside of the current mapset")),
-    'addGrassDB': MetaIcon(
-        img='grassdb-add',
-        label=_("Add existing or create new database")),
-    'addMapset': MetaIcon(
-        img='mapset-add',
-        label=_("Create new mapset in current location")),
-    'addLocation': MetaIcon(
-        img='location-add',
-        label=_("Create new location in current GRASS database")),
-    'downloadLocation': MetaIcon(
-        img='location-download',
-        label=_("Download sample location to current GRASS database")),
-    'importRaster': MetaIcon(
-        img='raster-import',
-        label=_("Import raster data  [r.import]")),
-    'importVector': MetaIcon(
-        img='vector-import',
-        label=_("Import vector data  [v.import]")),
-    'importLayer': MetaIcon(
-        img='layer-import',
-        label=_("Select another import option"))
+    "reloadTree": MetaIcon(img="redraw", label=_("Reload GRASS locations")),
+    "reloadMapset": MetaIcon(img="reload", label=_("Reload current GRASS mapset only")),
+    "unlocked": MetaIcon(
+        img="edit", label=_("Restrict edits to the current mapset only")
+    ),
+    "locked": MetaIcon(
+        img="edit", label=_("Allow edits outside of the current mapset")
+    ),
+    "addGrassDB": MetaIcon(
+        img="grassdb-add", label=_("Add existing or create new database")
+    ),
+    "addMapset": MetaIcon(
+        img="mapset-add", label=_("Create new mapset in current location")
+    ),
+    "addLocation": MetaIcon(
+        img="location-add", label=_("Create new location in current GRASS database")
+    ),
+    "downloadLocation": MetaIcon(
+        img="location-download",
+        label=_("Download sample location to current GRASS database"),
+    ),
+    "importRaster": MetaIcon(
+        img="raster-import", label=_("Import raster data  [r.import]")
+    ),
+    "importVector": MetaIcon(
+        img="vector-import", label=_("Import vector data  [v.import]")
+    ),
+    "importLayer": MetaIcon(
+        img="layer-import", label=_("Select another import option")
+    ),
 }
 
 
 class DataCatalogToolbar(BaseToolbar):
-    """Main data catalog toolbar
-    """
+    """Main data catalog toolbar"""
 
     def __init__(self, parent):
-        """Main toolbar constructor
-        """
+        """Main toolbar constructor"""
 
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
         self.filter_element = None
         self.filter = SearchCtrl(parent=self)
-        self.filter.SetDescriptiveText(_('Search'))
+        self.filter.SetDescriptiveText(_("Search"))
         self.filter.ShowCancelButton(True)
         self.filter.SetSize((150, self.filter.GetBestSize()[1]))
-        self.filter.Bind(wx.EVT_TEXT,
-                         lambda event: self.parent.Filter(
-                             self.filter.GetValue(), self.filter_element))
-        self.filter.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
-                         lambda evt: self.parent.Filter(''))
+        self.filter.Bind(
+            wx.EVT_TEXT,
+            lambda event: self.parent.Filter(
+                self.filter.GetValue(), self.filter_element
+            ),
+        )
+        self.filter.Bind(
+            wx.EVT_SEARCHCTRL_CANCEL_BTN, lambda evt: self.parent.Filter("")
+        )
         self.AddControl(self.filter)
         filterMenu = wx.Menu()
         item = filterMenu.AppendRadioItem(-1, "All")
@@ -88,8 +87,10 @@ class DataCatalogToolbar(BaseToolbar):
         item = filterMenu.AppendRadioItem(-1, "3D raster maps")
         self.Bind(wx.EVT_MENU, self.OnFilterMenu, item)
         self.filter.SetMenu(filterMenu)
-        help = _("Type to search database by map type or name. "
-                 "Use Python regular expressions to refine your search.")
+        help = _(
+            "Type to search database by map type or name. "
+            "Use Python regular expressions to refine your search."
+        )
         self.SetToolShortHelp(self.filter.GetId(), help)
         # realize the toolbar
         self.Realize()
@@ -98,47 +99,49 @@ class DataCatalogToolbar(BaseToolbar):
         """Returns toolbar data (name, icon, handler)"""
         # BaseIcons are a set of often used icons. It is possible
         # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
-        return self._getToolbarData((("reloadTree", icons["reloadTree"],
-                                      self.parent.OnReloadTree),
-                                     ("reloadMapset", icons["reloadMapset"],
-                                      self.parent.OnReloadCurrentMapset),
-                                     ("lock", icons['locked'],
-                                      self.OnSetRestriction, wx.ITEM_CHECK),
-                                     ("addGrassDB", icons['addGrassDB'],
-                                      self.parent.OnAddGrassDB),
-                                     ("addLocation", icons['addLocation'],
-                                      self.parent.OnCreateLocation),
-                                     ("downloadLocation", icons['downloadLocation'],
-                                      self.parent.OnDownloadLocation),
-                                     ("addMapset", icons['addMapset'],
-                                      self.parent.OnCreateMapset),
-                                     ("importRaster", icons['importRaster'],
-                                      self.parent.OnImportGdalLayers),
-                                     ("importVector", icons['importVector'],
-                                      self.parent.OnImportOgrLayers),
-                                     ("importLayer", icons['importLayer'],
-                                      self.parent.OnImportMenu)))
+        return self._getToolbarData(
+            (
+                ("reloadTree", icons["reloadTree"], self.parent.OnReloadTree),
+                (
+                    "reloadMapset",
+                    icons["reloadMapset"],
+                    self.parent.OnReloadCurrentMapset,
+                ),
+                ("lock", icons["locked"], self.OnSetRestriction, wx.ITEM_CHECK),
+                ("addGrassDB", icons["addGrassDB"], self.parent.OnAddGrassDB),
+                ("addLocation", icons["addLocation"], self.parent.OnCreateLocation),
+                (
+                    "downloadLocation",
+                    icons["downloadLocation"],
+                    self.parent.OnDownloadLocation,
+                ),
+                ("addMapset", icons["addMapset"], self.parent.OnCreateMapset),
+                ("importRaster", icons["importRaster"], self.parent.OnImportGdalLayers),
+                ("importVector", icons["importVector"], self.parent.OnImportOgrLayers),
+                ("importLayer", icons["importLayer"], self.parent.OnImportMenu),
+            )
+        )
 
     def OnFilterMenu(self, event):
         """Decide the element to filter by"""
         filterMenu = self.filter.GetMenu().GetMenuItems()
         self.filter_element = None
         if filterMenu[1].IsChecked():
-            self.filter_element = 'raster'
+            self.filter_element = "raster"
         elif filterMenu[2].IsChecked():
-            self.filter_element = 'vector'
+            self.filter_element = "vector"
         elif filterMenu[3].IsChecked():
-            self.filter_element = 'raster_3d'
+            self.filter_element = "raster_3d"
         # trigger filter on change
         if self.filter.GetValue():
             self.parent.Filter(self.filter.GetValue(), self.filter_element)
 
     def OnSetRestriction(self, event):
         if self.GetToolState(self.lock):
-            self.SetToolNormalBitmap(self.lock, icons['unlocked'].GetBitmap())
-            self.SetToolShortHelp(self.lock, icons['unlocked'].GetLabel())
+            self.SetToolNormalBitmap(self.lock, icons["unlocked"].GetBitmap())
+            self.SetToolShortHelp(self.lock, icons["unlocked"].GetLabel())
             self.parent.SetRestriction(restrict=False)
         else:
-            self.SetToolNormalBitmap(self.lock, icons['locked'].GetBitmap())
-            self.SetToolShortHelp(self.lock, icons['locked'].GetLabel())
+            self.SetToolNormalBitmap(self.lock, icons["locked"].GetBitmap())
+            self.SetToolShortHelp(self.lock, icons["locked"].GetLabel())
             self.parent.SetRestriction(restrict=True)

File diff ditekan karena terlalu besar
+ 813 - 547
gui/wxpython/datacatalog/tree.py


+ 6 - 6
gui/wxpython/dbmgr/__init__.py

@@ -1,8 +1,8 @@
 all = [
-    'g.gui.dbmgr',
-    'sqlbuilder',
-    'vinfo',
-    'manager',
-    'base',
-    'dialogs',
+    "g.gui.dbmgr",
+    "sqlbuilder",
+    "vinfo",
+    "manager",
+    "base",
+    "dialogs",
 ]

File diff ditekan karena terlalu besar
+ 1588 - 1474
gui/wxpython/dbmgr/base.py


+ 241 - 210
gui/wxpython/dbmgr/dialogs.py

@@ -26,17 +26,22 @@ from core.gcmd import RunCommand, GError
 from core.debug import Debug
 from dbmgr.vinfo import VectorDBInfo, GetUnicodeValue, GetDbEncoding
 from gui_core.widgets import IntegerValidator, FloatValidator
-from gui_core.wrap import SpinCtrl, Button, StaticText, StaticBox, \
-    TextCtrl
+from gui_core.wrap import SpinCtrl, Button, StaticText, StaticBox, TextCtrl
 
 
 class DisplayAttributesDialog(wx.Dialog):
-
-    def __init__(self, parent, map,
-                 query=None, cats=None, line=None,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                 pos=wx.DefaultPosition,
-                 action="add", ignoreError=False):
+    def __init__(
+        self,
+        parent,
+        map,
+        query=None,
+        cats=None,
+        line=None,
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+        pos=wx.DefaultPosition,
+        action="add",
+        ignoreError=False,
+    ):
         """Standard dialog used to add/update/display attributes linked
         to the vector map.
 
@@ -75,42 +80,45 @@ class DisplayAttributesDialog(wx.Dialog):
                     message=_(
                         "No attribute table found.\n\n"
                         "Do you want to create a new attribute table "
-                        "and defined a link to vector map <%s>?") %
-                    self.map,
+                        "and defined a link to vector map <%s>?"
+                    )
+                    % self.map,
                     caption=_("Create table?"),
-                    style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                    style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
+                )
                 if dlg.ShowModal() == wx.ID_YES:
                     lmgr = self.parent.lmgr
-                    lmgr.OnShowAttributeTable(event=None, selection='layers')
+                    lmgr.OnShowAttributeTable(event=None, selection="layers")
 
                 dlg.Destroy()
 
             self.mapDBInfo = None
 
-        wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY,
-                           title="", style=style, pos=pos)
+        wx.Dialog.__init__(
+            self, parent=self.parent, id=wx.ID_ANY, title="", style=style, pos=pos
+        )
 
         # dialog body
         mainSizer = wx.BoxSizer(wx.VERTICAL)
 
         # notebook
-        self.notebook = wx.Notebook(
-            parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
+        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
 
-        self.closeDialog = wx.CheckBox(parent=self, id=wx.ID_ANY,
-                                       label=_("Close dialog on submit"))
+        self.closeDialog = wx.CheckBox(
+            parent=self, id=wx.ID_ANY, label=_("Close dialog on submit")
+        )
         self.closeDialog.SetValue(True)
-        if self.action == 'display':
+        if self.action == "display":
             self.closeDialog.Enable(False)
 
         # feature id (text/choice for duplicates)
-        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
-                                  size=(150, -1))
+        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY, size=(150, -1))
         self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
         self.fidText = StaticText(parent=self, id=wx.ID_ANY)
 
-        self.noFoundMsg = StaticText(parent=self, id=wx.ID_ANY,
-                                     label=_("No attributes found"))
+        self.noFoundMsg = StaticText(
+            parent=self, id=wx.ID_ANY, label=_("No attributes found")
+        )
 
         self.UpdateDialog(query=query, cats=cats)
 
@@ -126,7 +134,7 @@ class DisplayAttributesDialog(wx.Dialog):
         btnCancel = Button(self, wx.ID_CANCEL)
         btnReset = Button(self, wx.ID_UNDO, _("&Reload"))
         btnSubmit = Button(self, wx.ID_OK, _("&Submit"))
-        if self.action == 'display':
+        if self.action == "display":
             btnSubmit.Enable(False)
 
         btnSizer = wx.StdDialogButtonSizer()
@@ -137,28 +145,27 @@ class DisplayAttributesDialog(wx.Dialog):
         btnSizer.AddButton(btnSubmit)
         btnSizer.Realize()
 
-        mainSizer.Add(self.noFoundMsg, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-        mainSizer.Add(self.notebook, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(self.noFoundMsg, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(self.notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
         fidSizer = wx.BoxSizer(wx.HORIZONTAL)
-        fidSizer.Add(StaticText(parent=self, id=wx.ID_ANY,
-                                label=_("Feature id:")),
-                     proportion=0, border=5,
-                     flag=wx.ALIGN_CENTER_VERTICAL)
-        fidSizer.Add(self.fidMulti, proportion=0,
-                     flag=wx.EXPAND | wx.ALL, border=5)
-        fidSizer.Add(self.fidText, proportion=0,
-                     flag=wx.EXPAND | wx.ALL, border=5)
-        mainSizer.Add(fidSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
+        fidSizer.Add(
+            StaticText(parent=self, id=wx.ID_ANY, label=_("Feature id:")),
+            proportion=0,
+            border=5,
+            flag=wx.ALIGN_CENTER_VERTICAL,
+        )
+        fidSizer.Add(self.fidMulti, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
+        fidSizer.Add(self.fidText, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(
+            fidSizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
+        )
         mainSizer.Add(
             self.closeDialog,
             proportion=0,
             flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
-            border=5)
-        mainSizer.Add(btnSizer, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=5)
+            border=5,
+        )
+        mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         # bindigs
         btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
@@ -207,16 +214,16 @@ class DisplayAttributesDialog(wx.Dialog):
             table = self.mapDBInfo.GetTable(layer)
             key = self.mapDBInfo.GetKeyColumn(layer)
             columns = self.mapDBInfo.GetTableDesc(table)
-            for idx in range(len(columns[key]['values'])):  # for each category
+            for idx in range(len(columns[key]["values"])):  # for each category
                 updatedColumns = []
                 updatedValues = []
                 for name in columns.keys():
                     if name == key:
-                        cat = columns[name]['values'][idx]
+                        cat = columns[name]["values"][idx]
                         continue
-                    ctype = columns[name]['ctype']
-                    value = columns[name]['values'][idx]
-                    id = columns[name]['ids'][idx]
+                    ctype = columns[name]["ctype"]
+                    value = columns[name]["values"][idx]
+                    id = columns[name]["ids"][idx]
                     try:
                         newvalue = self.FindWindowById(id).GetValue()
                     except:
@@ -231,27 +238,34 @@ class DisplayAttributesDialog(wx.Dialog):
                         except ValueError:
                             GError(
                                 parent=self,
-                                message=_("Column <%(col)s>: Value '%(value)s' needs to be entered as %(type)s.") % {
-                                    'col': name,
-                                    'value': str(newvalue),
-                                    'type': columns[name]['type'].lower()},
-                                showTraceback=False)
+                                message=_(
+                                    "Column <%(col)s>: Value '%(value)s' needs to be entered as %(type)s."
+                                )
+                                % {
+                                    "col": name,
+                                    "value": str(newvalue),
+                                    "type": columns[name]["type"].lower(),
+                                },
+                                showTraceback=False,
+                            )
                             sqlCommands.append(None)
                             continue
                     else:
-                        if self.action == 'add':
+                        if self.action == "add":
                             continue
 
                     if newvalue != value:
                         updatedColumns.append(name)
-                        if newvalue == '':
-                            updatedValues.append('NULL')
+                        if newvalue == "":
+                            updatedValues.append("NULL")
                         else:
                             if ctype != str:
                                 updatedValues.append(str(newvalue))
                             else:
-                                updatedValues.append("'" + newvalue.replace("'", "''") + "'")
-                        columns[name]['values'][idx] = newvalue
+                                updatedValues.append(
+                                    "'" + newvalue.replace("'", "''") + "'"
+                                )
+                        columns[name]["values"][idx] = newvalue
 
                 if self.action != "add" and len(updatedValues) == 0:
                     continue
@@ -282,9 +296,7 @@ class DisplayAttributesDialog(wx.Dialog):
             # for each category
         # for each layer END
 
-        Debug.msg(
-            3, "DisplayAttributesDialog.GetSQLString(): %s" %
-            sqlCommands)
+        Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)
 
         return sqlCommands
 
@@ -294,14 +306,14 @@ class DisplayAttributesDialog(wx.Dialog):
             table = self.mapDBInfo.layers[layer]["table"]
             key = self.mapDBInfo.layers[layer]["key"]
             columns = self.mapDBInfo.tables[table]
-            for idx in range(len(columns[key]['values'])):
+            for idx in range(len(columns[key]["values"])):
                 for name in columns.keys():
-                    type = columns[name]['type']
-                    value = columns[name]['values'][idx]
+                    type = columns[name]["type"]
+                    value = columns[name]["values"][idx]
                     if value is None:
-                        value = ''
+                        value = ""
                     try:
-                        id = columns[name]['ids'][idx]
+                        id = columns[name]["ids"][idx]
                     except IndexError:
                         id = wx.NOT_FOUND
 
@@ -309,10 +321,9 @@ class DisplayAttributesDialog(wx.Dialog):
                         self.FindWindowById(id).SetValue(str(value))
 
     def OnClose(self, event):
-        """Closes dialog and removes query layer.
-        """
+        """Closes dialog and removes query layer."""
         frame = self.parent.parent
-        frame.dialogs['attributes'] = None
+        frame.dialogs["attributes"] = None
         if hasattr(self, "digit"):
             self.parent.digit.GetDisplay().SetSelected([])
             if frame.IsAutoRendered():
@@ -339,13 +350,15 @@ class DisplayAttributesDialog(wx.Dialog):
 
             driver, database = self.mapDBInfo.GetDbSettings(layer)
             Debug.msg(1, "SQL: %s" % sql)
-            RunCommand('db.execute',
-                       parent=self,
-                       quiet=True,
-                       input='-',
-                       stdin=sql,
-                       driver=driver,
-                       database=database)
+            RunCommand(
+                "db.execute",
+                parent=self,
+                quiet=True,
+                input="-",
+                stdin=sql,
+                driver=driver,
+                database=database,
+            )
 
             layer += 1
 
@@ -370,8 +383,7 @@ class DisplayAttributesDialog(wx.Dialog):
         """Get selected feature id"""
         return self.fid
 
-    def UpdateDialog(self, map=None, query=None, cats=None, fid=-1,
-                     action=None):
+    def UpdateDialog(self, map=None, query=None, cats=None, fid=-1, action=None):
         """Update dialog
 
         :param map: name of vector map
@@ -385,7 +397,7 @@ class DisplayAttributesDialog(wx.Dialog):
         """
         if action:
             self.action = action
-            if action == 'display':
+            if action == "display":
                 enabled = False
             else:
                 enabled = True
@@ -406,22 +418,21 @@ class DisplayAttributesDialog(wx.Dialog):
 
         # id of selected line
         if query:  # select by position
-            data = self.mapDBInfo.SelectByPoint(query[0],
-                                                query[1])
+            data = self.mapDBInfo.SelectByPoint(query[0], query[1])
             self.cats = {}
-            if data and 'Layer' in data:
+            if data and "Layer" in data:
                 idx = 0
-                for layer in data['Layer']:
+                for layer in data["Layer"]:
                     layer = int(layer)
-                    if data['Id'][idx] is not None:
-                        tfid = int(data['Id'][idx])
+                    if data["Id"][idx] is not None:
+                        tfid = int(data["Id"][idx])
                     else:
                         tfid = 0  # Area / Volume
                     if tfid not in self.cats:
                         self.cats[tfid] = {}
                     if layer not in self.cats[tfid]:
                         self.cats[tfid][layer] = []
-                    cat = int(data['Category'][idx])
+                    cat = int(data["Category"][idx])
                     self.cats[tfid][layer].append(cat)
                     idx += 1
         else:
@@ -458,8 +469,9 @@ class DisplayAttributesDialog(wx.Dialog):
                 if self.fid > 0 and layer in self.cats[self.fid]:
                     for cat in self.cats[self.fid][layer]:
                         nselected = self.mapDBInfo.SelectFromTable(
-                            layer, where="%s=%d" %
-                            (self.mapDBInfo.layers[layer]['key'], cat))
+                            layer,
+                            where="%s=%d" % (self.mapDBInfo.layers[layer]["key"], cat),
+                        )
                 else:
                     nselected = 0
 
@@ -475,11 +487,13 @@ class DisplayAttributesDialog(wx.Dialog):
                         for name in columns.keys():
                             if name == key:
                                 for cat in self.cats[self.fid][layer]:
-                                    self.mapDBInfo.tables[table][
-                                        name]['values'].append(cat)
+                                    self.mapDBInfo.tables[table][name]["values"].append(
+                                        cat
+                                    )
                             else:
-                                self.mapDBInfo.tables[table][
-                                    name]['values'].append(None)
+                                self.mapDBInfo.tables[table][name]["values"].append(
+                                    None
+                                )
                 else:  # change status 'add' -> 'update'
                     self.action = "update"
 
@@ -487,21 +501,23 @@ class DisplayAttributesDialog(wx.Dialog):
             key = self.mapDBInfo.layers[layer]["key"]
             columns = self.mapDBInfo.tables[table]
 
-            for idx in range(len(columns[key]['values'])):
+            for idx in range(len(columns[key]["values"])):
                 for name in columns.keys():
                     if name == key:
-                        cat = int(columns[name]['values'][idx])
+                        cat = int(columns[name]["values"][idx])
                         break
 
                 # use scrolled panel instead (and fix initial max height of the
                 # window to 480px)
                 panel = scrolled.ScrolledPanel(
-                    parent=self.notebook, id=wx.ID_ANY, size=(-1, 150))
+                    parent=self.notebook, id=wx.ID_ANY, size=(-1, 150)
+                )
                 panel.SetupScrolling(scroll_x=False)
 
                 self.notebook.AddPage(
-                    page=panel, text=" %s %d / %s %d" %
-                    (_("Layer"), layer, _("Category"), cat))
+                    page=panel,
+                    text=" %s %d / %s %d" % (_("Layer"), layer, _("Category"), cat),
+                )
 
                 # notebook body
                 border = wx.BoxSizer(wx.VERTICAL)
@@ -509,31 +525,30 @@ class DisplayAttributesDialog(wx.Dialog):
                 flexSizer = wx.FlexGridSizer(cols=3, hgap=3, vgap=3)
                 flexSizer.AddGrowableCol(2)
                 # columns (sorted by index)
-                names = [''] * len(columns.keys())
+                names = [""] * len(columns.keys())
                 for name in columns.keys():
-                    names[columns[name]['index']] = name
+                    names[columns[name]["index"]] = name
 
                 for name in names:
                     if name == key:  # skip key column (category)
                         continue
 
-                    vtype = columns[name]['type'].lower()
-                    ctype = columns[name]['ctype']
+                    vtype = columns[name]["type"].lower()
+                    ctype = columns[name]["ctype"]
 
-                    if columns[name]['values'][idx] is not None:
-                        if not isinstance(columns[name]['ctype'], six.string_types):
-                            value = str(columns[name]['values'][idx])
+                    if columns[name]["values"][idx] is not None:
+                        if not isinstance(columns[name]["ctype"], six.string_types):
+                            value = str(columns[name]["values"][idx])
                         else:
-                            value = columns[name]['values'][idx]
+                            value = columns[name]["values"][idx]
                     else:
-                        value = ''
-
-                    colName = StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=name)
-                    colType = StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="[%s]:" % vtype)
-                    colValue = TextCtrl(
-                        parent=panel, id=wx.ID_ANY, value=value)
+                        value = ""
+
+                    colName = StaticText(parent=panel, id=wx.ID_ANY, label=name)
+                    colType = StaticText(
+                        parent=panel, id=wx.ID_ANY, label="[%s]:" % vtype
+                    )
+                    colValue = TextCtrl(parent=panel, id=wx.ID_ANY, value=value)
                     colValue.SetName(name)
                     if ctype == int:
                         colValue.SetValidator(IntegerValidator())
@@ -541,26 +556,26 @@ class DisplayAttributesDialog(wx.Dialog):
                         colValue.SetValidator(FloatValidator())
 
                     self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
-                    if self.action == 'display':
+                    if self.action == "display":
                         colValue.SetWindowStyle(wx.TE_READONLY)
 
-                    flexSizer.Add(colName, proportion=0,
-                                  flag=wx.ALIGN_CENTER_VERTICAL)
+                    flexSizer.Add(colName, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
                     flexSizer.Add(
                         colType,
                         proportion=0,
-                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-                    flexSizer.Add(colValue, proportion=1,
-                                  flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT,
+                    )
+                    flexSizer.Add(
+                        colValue,
+                        proportion=1,
+                        flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
+                    )
                     # add widget reference to self.columns
-                    columns[name]['ids'].append(
-                        colValue.GetId())  # name, type, values, id
+                    columns[name]["ids"].append(
+                        colValue.GetId()
+                    )  # name, type, values, id
                 # for each attribute (including category) END
-                border.Add(
-                    flexSizer,
-                    proportion=1,
-                    flag=wx.ALL | wx.EXPAND,
-                    border=5)
+                border.Add(flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
                 panel.SetSizer(border)
             # for each category END
         # for each layer END
@@ -585,14 +600,22 @@ class DisplayAttributesDialog(wx.Dialog):
 
         for key, col in six.iteritems(columns):
             if key == column:
-                col['values'] = [col['ctype'](value), ]
+                col["values"] = [
+                    col["ctype"](value),
+                ]
                 break
 
 
 class ModifyTableRecord(wx.Dialog):
-
-    def __init__(self, parent, title, data, keyEditable=(-1, True),
-                 id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+    def __init__(
+        self,
+        parent,
+        title,
+        data,
+        keyEditable=(-1, True),
+        id=wx.ID_ANY,
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+    ):
         """Dialog for inserting/updating table record
 
         :param data: a list: [(column, value)]
@@ -608,8 +631,9 @@ class ModifyTableRecord(wx.Dialog):
 
         box = StaticBox(parent=self, id=wx.ID_ANY)
         box.Hide()
-        self.dataPanel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY,
-                                                style=wx.TAB_TRAVERSAL)
+        self.dataPanel = scrolled.ScrolledPanel(
+            parent=self, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL
+        )
         self.dataPanel.SetupScrolling(scroll_x=False)
 
         # buttons
@@ -636,11 +660,17 @@ class ModifyTableRecord(wx.Dialog):
                     continue
                 else:
                     valueWin = SpinCtrl(
-                        parent=self.dataPanel, id=wx.ID_ANY, value=value,
-                        min=-1e9, max=1e9, size=(250, -1))
+                        parent=self.dataPanel,
+                        id=wx.ID_ANY,
+                        value=value,
+                        min=-1e9,
+                        max=1e9,
+                        size=(250, -1),
+                    )
             else:
-                valueWin = TextCtrl(parent=self.dataPanel, id=wx.ID_ANY,
-                                    value=value, size=(250, -1))
+                valueWin = TextCtrl(
+                    parent=self.dataPanel, id=wx.ID_ANY, value=value, size=(250, -1)
+                )
                 if ctype == int:
                     valueWin.SetValidator(IntegerValidator())
                 elif ctype == float:
@@ -649,12 +679,11 @@ class ModifyTableRecord(wx.Dialog):
                     wx.CallAfter(valueWin.SetFocus)
                     winFocus = True
 
-            label = StaticText(parent=self.dataPanel, id=wx.ID_ANY,
-                               label=column)
-            ctype = StaticText(parent=self.dataPanel, id=wx.ID_ANY,
-                               label="[%s]:" % ctypeStr.lower())
-            self.widgets.append(
-                (label.GetId(), ctype.GetId(), valueWin.GetId()))
+            label = StaticText(parent=self.dataPanel, id=wx.ID_ANY, label=column)
+            ctype = StaticText(
+                parent=self.dataPanel, id=wx.ID_ANY, label="[%s]:" % ctypeStr.lower()
+            )
+            self.widgets.append((label.GetId(), ctype.GetId(), valueWin.GetId()))
 
             cId += 1
 
@@ -673,20 +702,22 @@ class ModifyTableRecord(wx.Dialog):
             ctype = self.FindWindowById(ctypeId)
             value = self.FindWindowById(valueId)
 
-            dataSizer.Add(label, proportion=0,
-                          flag=wx.ALIGN_CENTER_VERTICAL)
-            dataSizer.Add(ctype, proportion=0,
-                          flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-            dataSizer.Add(value, proportion=0,
-                          flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+            dataSizer.Add(label, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
+            dataSizer.Add(
+                ctype, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT
+            )
+            dataSizer.Add(
+                value, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL
+            )
 
         self.dataPanel.SetAutoLayout(True)
         self.dataPanel.SetSizer(dataSizer)
         dataSizer.Fit(self.dataPanel)
 
         if self.usebox:
-            self.boxSizer.Add(self.dataPanel, proportion=1,
-                              flag=wx.EXPAND | wx.ALL, border=5)
+            self.boxSizer.Add(
+                self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5
+            )
 
         # buttons
         btnSizer = wx.StdDialogButtonSizer()
@@ -695,14 +726,11 @@ class ModifyTableRecord(wx.Dialog):
         btnSizer.Realize()
 
         if not self.usebox:
-            sizer.Add(self.dataPanel, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
+            sizer.Add(self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
         else:
-            sizer.Add(self.boxSizer, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
+            sizer.Add(self.boxSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         framewidth = self.GetBestSize()[0] + 25
         self.SetMinSize((framewidth, 250))
@@ -722,8 +750,7 @@ class ModifyTableRecord(wx.Dialog):
         for labelId, ctypeId, valueId in self.widgets:
             column = self.FindWindowById(labelId).GetLabel()
             if columns is None or column in columns:
-                value = GetUnicodeValue(
-                    self.FindWindowById(valueId).GetValue())
+                value = GetUnicodeValue(self.FindWindowById(valueId).GetValue())
                 valueList.append(value)
 
         # add key value
@@ -734,32 +761,37 @@ class ModifyTableRecord(wx.Dialog):
 
 
 class AddColumnDialog(wx.Dialog):
-
-    def __init__(self, parent, title, id=wx.ID_ANY,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
-        """Dialog for adding column into table
-        """
+    def __init__(
+        self,
+        parent,
+        title,
+        id=wx.ID_ANY,
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+    ):
+        """Dialog for adding column into table"""
         wx.Dialog.__init__(self, parent, id, title, style=style)
 
         self.CenterOnParent()
 
         self.data = {}
-        self.data['addColName'] = TextCtrl(
-            parent=self, id=wx.ID_ANY, value='', size=(
-                150, -1), style=wx.TE_PROCESS_ENTER)
-
-        self.data['addColType'] = wx.Choice(parent=self, id=wx.ID_ANY,
-                                            choices=["integer",
-                                                     "double",
-                                                     "varchar",
-                                                     "date"])  # FIXME
-        self.data['addColType'].SetSelection(0)
-        self.data['addColType'].Bind(wx.EVT_CHOICE, self.OnTableChangeType)
-
-        self.data['addColLength'] = SpinCtrl(
-            parent=self, id=wx.ID_ANY, size=(
-                65, -1), initial=250, min=1, max=1e6)
-        self.data['addColLength'].Enable(False)
+        self.data["addColName"] = TextCtrl(
+            parent=self,
+            id=wx.ID_ANY,
+            value="",
+            size=(150, -1),
+            style=wx.TE_PROCESS_ENTER,
+        )
+
+        self.data["addColType"] = wx.Choice(
+            parent=self, id=wx.ID_ANY, choices=["integer", "double", "varchar", "date"]
+        )  # FIXME
+        self.data["addColType"].SetSelection(0)
+        self.data["addColType"].Bind(wx.EVT_CHOICE, self.OnTableChangeType)
+
+        self.data["addColLength"] = SpinCtrl(
+            parent=self, id=wx.ID_ANY, size=(65, -1), initial=250, min=1, max=1e6
+        )
+        self.data["addColLength"].Enable(False)
 
         # buttons
         self.btnCancel = Button(self, wx.ID_CANCEL)
@@ -774,48 +806,47 @@ class AddColumnDialog(wx.Dialog):
         addSizer = wx.BoxSizer(wx.HORIZONTAL)
 
         addSizer.Add(
-            StaticText(
-                parent=self,
-                id=wx.ID_ANY,
-                label=_("Column")),
+            StaticText(parent=self, id=wx.ID_ANY, label=_("Column")),
+            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+            border=5,
+        )
+        addSizer.Add(
+            self.data["addColName"],
+            proportion=1,
             flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-            border=5)
-        addSizer.Add(self.data['addColName'], proportion=1,
-                     flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-                     border=5)
+            border=5,
+        )
 
         addSizer.Add(
-            StaticText(
-                parent=self,
-                id=wx.ID_ANY,
-                label=_("Type")),
+            StaticText(parent=self, id=wx.ID_ANY, label=_("Type")),
             flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-            border=5)
-        addSizer.Add(self.data['addColType'],
-                     flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-                     border=5)
+            border=5,
+        )
+        addSizer.Add(
+            self.data["addColType"],
+            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+            border=5,
+        )
 
         addSizer.Add(
-            StaticText(
-                parent=self,
-                id=wx.ID_ANY,
-                label=_("Length")),
+            StaticText(parent=self, id=wx.ID_ANY, label=_("Length")),
+            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+            border=5,
+        )
+        addSizer.Add(
+            self.data["addColLength"],
             flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-            border=5)
-        addSizer.Add(self.data['addColLength'],
-                     flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
-                     border=5)
+            border=5,
+        )
 
-        sizer.Add(addSizer, proportion=0,
-                  flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+        sizer.Add(addSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
 
         btnSizer = wx.StdDialogButtonSizer()
         btnSizer.AddButton(self.btnCancel)
         btnSizer.AddButton(self.btnOk)
         btnSizer.Realize()
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
 
         self.SetSizer(sizer)
 
@@ -824,9 +855,9 @@ class AddColumnDialog(wx.Dialog):
     def GetData(self):
         """Get inserted data from dialog's widgets"""
         values = {}
-        values['name'] = self.data['addColName'].GetValue()
-        values['ctype'] = self.data['addColType'].GetStringSelection()
-        values['length'] = int(self.data['addColLength'].GetValue())
+        values["name"] = self.data["addColName"].GetValue()
+        values["ctype"] = self.data["addColType"].GetStringSelection()
+        values["length"] = int(self.data["addColLength"].GetValue())
 
         return values
 
@@ -834,6 +865,6 @@ class AddColumnDialog(wx.Dialog):
         """Data type for new column changed. Enable or disable
         data length widget"""
         if event.GetString() == "varchar":
-            self.data['addColLength'].Enable(True)
+            self.data["addColLength"].Enable(True)
         else:
-            self.data['addColLength'].Enable(False)
+            self.data["addColLength"].Enable(False)

+ 5 - 5
gui/wxpython/dbmgr/g.gui.dbmgr.py

@@ -37,19 +37,18 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from dbmgr.manager import AttributeManager
 
-    mapName = gscript.find_file(options['map'], element='vector')['fullname']
+    mapName = gscript.find_file(options["map"], element="vector")["fullname"]
     if not mapName:
         gscript.set_raise_on_error(False)
-        gscript.fatal(_("Vector map <%s> not found") % options['map'])
+        gscript.fatal(_("Vector map <%s> not found") % options["map"])
 
     app = wx.App()
-    gscript.message(
-        _("Loading attribute data for vector map <%s>...") %
-        mapName)
+    gscript.message(_("Loading attribute data for vector map <%s>...") % mapName)
     f = AttributeManager(
         parent=None,
         id=wx.ID_ANY,
@@ -61,5 +60,6 @@ def main():
 
     app.MainLoop()
 
+
 if __name__ == "__main__":
     main()

+ 68 - 60
gui/wxpython/dbmgr/manager.py

@@ -26,10 +26,11 @@ import os
 
 import wx
 from core import globalvar
+
 if globalvar.wxPythonPhoenix:
     try:
         import agw.flatnotebook as FN
-    except ImportError: # if it's not there locally, try the wxPython lib.
+    except ImportError:  # if it's not there locally, try the wxPython lib.
         import wx.lib.agw.flatnotebook as FN
 else:
     import wx.lib.flatnotebook as FN
@@ -41,12 +42,18 @@ from gui_core.wrap import Button, ClearButton, CloseButton
 
 
 class AttributeManager(wx.Frame, DbMgrBase):
-
-    def __init__(self, parent, id=wx.ID_ANY,
-                 title=None,
-                 base_title=None,
-                 vectorName=None, item=None, log=None,
-                 selection=None, **kwargs):
+    def __init__(
+        self,
+        parent,
+        id=wx.ID_ANY,
+        title=None,
+        base_title=None,
+        vectorName=None,
+        item=None,
+        log=None,
+        selection=None,
+        **kwargs,
+    ):
         """GRASS Attribute Table Manager window
 
         :param parent: parent window
@@ -65,10 +72,16 @@ class AttributeManager(wx.Frame, DbMgrBase):
         except:
             mapdisplay = None
 
-        DbMgrBase.__init__(self, id=id, mapdisplay=mapdisplay,
-                           vectorName=vectorName, item=item,
-                           log=log, statusbar=self,
-                           **kwargs)
+        DbMgrBase.__init__(
+            self,
+            id=id,
+            mapdisplay=mapdisplay,
+            vectorName=vectorName,
+            item=item,
+            log=log,
+            statusbar=self,
+            **kwargs,
+        )
 
         wx.Frame.__init__(self, parent, id, *kwargs)
 
@@ -76,8 +89,8 @@ class AttributeManager(wx.Frame, DbMgrBase):
         if not title:
             if not base_title:
                 base_title = _("Attribute Table Manager")
-            document = self.dbMgrData['vectName']
-            if not self.dbMgrData['editable']:
+            document = self.dbMgrData["vectName"]
+            if not self.dbMgrData["editable"]:
                 document = _("{document} (read-only)").format(document=document)
             title = "{document} - {tool_name}".format(
                 document=document, tool_name=base_title
@@ -87,48 +100,50 @@ class AttributeManager(wx.Frame, DbMgrBase):
         # icon
         self.SetIcon(
             wx.Icon(
-                os.path.join(
-                    globalvar.ICONDIR,
-                    'grass_sql.ico'),
-                wx.BITMAP_TYPE_ICO))
+                os.path.join(globalvar.ICONDIR, "grass_sql.ico"), wx.BITMAP_TYPE_ICO
+            )
+        )
 
         self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
 
-        if len(self.dbMgrData['mapDBInfo'].layers.keys()) == 0:
+        if len(self.dbMgrData["mapDBInfo"].layers.keys()) == 0:
             GMessage(
-                parent=self.parent, message=_(
+                parent=self.parent,
+                message=_(
                     "Database connection for vector map <%s> "
                     "is not defined in DB file. "
                     "You can define new connection in "
-                    "'Manage layers' tab.") %
-                self.dbMgrData['vectName'])
+                    "'Manage layers' tab."
+                )
+                % self.dbMgrData["vectName"],
+            )
 
-        busy = wx.BusyInfo(_("Please wait, loading attribute data..."),
-                           parent=self.parent)
+        busy = wx.BusyInfo(
+            _("Please wait, loading attribute data..."), parent=self.parent
+        )
         wx.SafeYield()
         self.CreateStatusBar(number=1)
 
         self.notebook = GNotebook(self.panel, style=globalvar.FNPageDStyle)
 
-        self.CreateDbMgrPage(parent=self, pageName='browse')
+        self.CreateDbMgrPage(parent=self, pageName="browse")
 
-        self.notebook.AddPage(page=self.pages['browse'], text=_("Browse data"),
-                              name='browse')
-        self.pages['browse'].SetTabAreaColour(globalvar.FNPageColor)
+        self.notebook.AddPage(
+            page=self.pages["browse"], text=_("Browse data"), name="browse"
+        )
+        self.pages["browse"].SetTabAreaColour(globalvar.FNPageColor)
 
-        self.CreateDbMgrPage(parent=self, pageName='manageTable')
+        self.CreateDbMgrPage(parent=self, pageName="manageTable")
 
         self.notebook.AddPage(
-            page=self.pages['manageTable'],
-            text=_("Manage tables"),
-            name='table')
-        self.pages['manageTable'].SetTabAreaColour(globalvar.FNPageColor)
+            page=self.pages["manageTable"], text=_("Manage tables"), name="table"
+        )
+        self.pages["manageTable"].SetTabAreaColour(globalvar.FNPageColor)
 
-        self.CreateDbMgrPage(parent=self, pageName='manageLayer')
+        self.CreateDbMgrPage(parent=self, pageName="manageLayer")
         self.notebook.AddPage(
-            page=self.pages['manageLayer'],
-            text=_("Manage layers"),
-            name='layers')
+            page=self.pages["manageLayer"], text=_("Manage layers"), name="layers"
+        )
         del busy
 
         if selection:
@@ -140,11 +155,11 @@ class AttributeManager(wx.Frame, DbMgrBase):
         self.btnClose = CloseButton(parent=self.panel)
         self.btnClose.SetToolTip(_("Close Attribute Table Manager"))
         self.btnReload = Button(parent=self.panel, id=wx.ID_REFRESH)
-        self.btnReload.SetToolTip(
-            _("Reload currently selected attribute data"))
+        self.btnReload.SetToolTip(_("Reload currently selected attribute data"))
         self.btnReset = ClearButton(parent=self.panel)
         self.btnReset.SetToolTip(
-            _("Reload all attribute data (drop current selection)"))
+            _("Reload all attribute data (drop current selection)")
+        )
 
         # bind closing to ESC
         self.Bind(wx.EVT_MENU, self.OnCloseWindow, id=wx.ID_CANCEL)
@@ -156,9 +171,7 @@ class AttributeManager(wx.Frame, DbMgrBase):
         self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
         self.btnReload.Bind(wx.EVT_BUTTON, self.OnReloadData)
         self.btnReset.Bind(wx.EVT_BUTTON, self.OnReloadDataAll)
-        self.notebook.Bind(
-            FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,
-            self.OnPageChanged)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
 
         # do layout
@@ -175,12 +188,9 @@ class AttributeManager(wx.Frame, DbMgrBase):
 
         # buttons
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
-        btnSizer.Add(self.btnReset, proportion=1,
-                     flag=wx.ALL, border=5)
-        btnSizer.Add(self.btnReload, proportion=1,
-                     flag=wx.ALL, border=5)
-        btnSizer.Add(self.btnClose, proportion=1,
-                     flag=wx.ALL, border=5)
+        btnSizer.Add(self.btnReset, proportion=1, flag=wx.ALL, border=5)
+        btnSizer.Add(self.btnReload, proportion=1, flag=wx.ALL, border=5)
+        btnSizer.Add(self.btnClose, proportion=1, flag=wx.ALL, border=5)
 
         mainSizer.Add(self.notebook, proportion=1, flag=wx.EXPAND)
         mainSizer.Add(btnSizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
@@ -192,9 +202,9 @@ class AttributeManager(wx.Frame, DbMgrBase):
 
     def OnCloseWindow(self, event):
         """Cancel button pressed"""
-        if self.parent and self.parent.GetName() == 'LayerManager':
+        if self.parent and self.parent.GetName() == "LayerManager":
             # deregister ATM
-            self.parent.dialogs['atm'].remove(self)
+            self.parent.dialogs["atm"].remove(self)
 
         if not isinstance(event, wx.CloseEvent):
             self.Destroy()
@@ -203,31 +213,29 @@ class AttributeManager(wx.Frame, DbMgrBase):
 
     def OnReloadData(self, event):
         """Reload data"""
-        if self.pages['browse']:
-            self.pages['browse'].OnDataReload(event)  # TODO replace by signal
+        if self.pages["browse"]:
+            self.pages["browse"].OnDataReload(event)  # TODO replace by signal
 
     def OnReloadDataAll(self, event):
         """Reload all data"""
-        if self.pages['browse']:
-            self.pages['browse'].ResetPage()
+        if self.pages["browse"]:
+            self.pages["browse"].ResetPage()
 
     def OnPageChanged(self, event):
         """On page in ATM is changed"""
         try:
             if self.pages["browse"]:
                 selPage = self.pages["browse"].selLayer
-                id = self.pages["browse"].layerPage[selPage]['data']
+                id = self.pages["browse"].layerPage[selPage]["data"]
             else:
                 id = None
         except KeyError:
             id = None
 
-        if event.GetSelection() == self.notebook.GetPageIndexByName('browse') and id:
+        if event.GetSelection() == self.notebook.GetPageIndexByName("browse") and id:
             win = self.FindWindowById(id)
             if win:
-                self.log.write(
-                    _("Number of loaded records: %d") %
-                    win.GetItemCount())
+                self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
             else:
                 self.log.write("")
             self.btnReload.Enable()
@@ -246,4 +254,4 @@ class AttributeManager(wx.Frame, DbMgrBase):
         """Updates dialog layout for given layer"""
         DbMgrBase.UpdateDialog(self, layer=layer)
         # set current page selection
-        self.notebook.SetSelectionByName('layers')
+        self.notebook.SetSelectionByName("layers")

File diff ditekan karena terlalu besar
+ 341 - 302
gui/wxpython/dbmgr/sqlbuilder.py


+ 60 - 60
gui/wxpython/dbmgr/vinfo.py

@@ -40,10 +40,13 @@ if sys.version_info.major >= 3:
             return value
         if isinstance(value, bytes):
             enc = GetDbEncoding()
-            return str(value, enc, errors='replace')
+            return str(value, enc, errors="replace")
         else:
             return str(value)
+
+
 else:
+
     def GetUnicodeValue(value):
         """Get unicode value
 
@@ -54,17 +57,17 @@ else:
         if isinstance(value, unicode):
             return value
         enc = GetDbEncoding()
-        return unicode(str(value), enc, errors='replace')
+        return unicode(str(value), enc, errors="replace")
 
 
 def GetDbEncoding():
     """Checks if user set DB encoding (first user settings,
     then env variable), if not assumes unicode."""
-    enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
-    if not enc and 'GRASS_DB_ENCODING' in os.environ:
-        enc = os.environ['GRASS_DB_ENCODING']
+    enc = UserSettings.Get(group="atm", key="encoding", subkey="value")
+    if not enc and "GRASS_DB_ENCODING" in os.environ:
+        enc = os.environ["GRASS_DB_ENCODING"]
     else:
-        enc = 'utf-8'  # assuming UTF-8
+        enc = "utf-8"  # assuming UTF-8
     return enc
 
 
@@ -73,31 +76,24 @@ def CreateDbInfoDesc(panel, mapDBInfo, layer):
     infoFlexSizer = wx.FlexGridSizer(cols=2, hgap=1, vgap=1)
     infoFlexSizer.AddGrowableCol(1)
 
-    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY,
-                                 label="Driver:"))
+    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY, label="Driver:"))
     infoFlexSizer.Add(
-        StaticText(
-            parent=panel,
-            id=wx.ID_ANY,
-            label=mapDBInfo.layers[layer]['driver']))
-    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY,
-                                 label="Database:"))
+        StaticText(parent=panel, id=wx.ID_ANY, label=mapDBInfo.layers[layer]["driver"])
+    )
+    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY, label="Database:"))
     infoFlexSizer.Add(
         StaticText(
-            parent=panel,
-            id=wx.ID_ANY,
-            label=mapDBInfo.layers[layer]['database']))
-    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY,
-                                 label="Table:"))
+            parent=panel, id=wx.ID_ANY, label=mapDBInfo.layers[layer]["database"]
+        )
+    )
+    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY, label="Table:"))
     infoFlexSizer.Add(
-        StaticText(
-            parent=panel,
-            id=wx.ID_ANY,
-            label=mapDBInfo.layers[layer]['table']))
-    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY,
-                                 label="Key:"))
-    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY,
-                                 label=mapDBInfo.layers[layer]['key']))
+        StaticText(parent=panel, id=wx.ID_ANY, label=mapDBInfo.layers[layer]["table"])
+    )
+    infoFlexSizer.Add(StaticText(parent=panel, id=wx.ID_ANY, label="Key:"))
+    infoFlexSizer.Add(
+        StaticText(parent=panel, id=wx.ID_ANY, label=mapDBInfo.layers[layer]["key"])
+    )
 
     return infoFlexSizer
 
@@ -112,12 +108,12 @@ class VectorDBInfo(VectorDBInfoBase):
     def GetColumns(self, table):
         """Return list of columns names (based on their index)"""
         try:
-            names = [''] * len(self.tables[table].keys())
+            names = [""] * len(self.tables[table].keys())
         except KeyError:
             return []
 
         for name, desc in six.iteritems(self.tables[table]):
-            names[desc['index']] = name
+            names[desc["index"]] = name
 
         return names
 
@@ -130,51 +126,53 @@ class VectorDBInfo(VectorDBInfoBase):
 
         try:
             data = grass.vector_what(
-                map=self.map, coord=(
-                    float(
-                        queryCoords[0]), float(
-                        queryCoords[1])), distance=float(qdist))
+                map=self.map,
+                coord=(float(queryCoords[0]), float(queryCoords[1])),
+                distance=float(qdist),
+            )
         except grass.ScriptError:
             GError(
-                parent=None, message=_(
+                parent=None,
+                message=_(
                     "Failed to query vector map <{map}>. "
-                    "Check database settings and topology.").format(
-                    map=self.map))
+                    "Check database settings and topology."
+                ).format(map=self.map),
+            )
 
-        if len(data) < 1 or all(('Table' not in record) for record in data):
+        if len(data) < 1 or all(("Table" not in record) for record in data):
             return None
 
         # process attributes
         ret = dict()
-        for key in ['Category', 'Layer', 'Table', 'Id']:
+        for key in ["Category", "Layer", "Table", "Id"]:
             ret[key] = list()
 
         for record in data:
-            if 'Table' not in record:
+            if "Table" not in record:
                 continue
 
-            table = record['Table']
-            for key, value in six.iteritems(record['Attributes']):
+            table = record["Table"]
+            for key, value in six.iteritems(record["Attributes"]):
                 if len(value) < 1:
                     value = None
                 else:
-                    if self.tables[table][key]['ctype'] != str:
-                        value = self.tables[table][key]['ctype'](value)
+                    if self.tables[table][key]["ctype"] != str:
+                        value = self.tables[table][key]["ctype"](value)
                     else:
                         value = GetUnicodeValue(value)
-                self.tables[table][key]['values'].append(value)
+                self.tables[table][key]["values"].append(value)
 
             for key, value in six.iteritems(record):
-                if key == 'Attributes':
+                if key == "Attributes":
                     continue
                 if key in ret:
                     ret[key].append(value)
-            if 'Id' not in record.keys():
-                ret['Id'].append(None)
+            if "Id" not in record.keys():
+                ret["Id"].append(None)
 
         return ret
 
-    def SelectFromTable(self, layer, cols='*', where=None):
+    def SelectFromTable(self, layer, cols="*", where=None):
         """Select records from the table
 
         Return number of selected records, -1 on error
@@ -186,32 +184,34 @@ class VectorDBInfo(VectorDBInfoBase):
 
         table = self.layers[layer]["table"]  # get table desc
         # select values (only one record)
-        if where is None or where == '':
+        if where is None or where == "":
             sql = "SELECT %s FROM %s" % (cols, table)
         else:
             sql = "SELECT %s FROM %s WHERE %s" % (cols, table, where)
 
-        ret = RunCommand('db.select',
-                         read=True,
-                         quiet=True,
-                         flags='v',
-                         sql=sql,
-                         database=self.layers[layer]["database"],
-                         driver=self.layers[layer]["driver"])
+        ret = RunCommand(
+            "db.select",
+            read=True,
+            quiet=True,
+            flags="v",
+            sql=sql,
+            database=self.layers[layer]["database"],
+            driver=self.layers[layer]["driver"],
+        )
 
         # self.tables[table][key][1] = str(cat)
         if ret:
             for line in ret.splitlines():
-                name, value = line.split('|')
+                name, value = line.split("|")
                 # casting ...
                 if value:
-                    if not isinstance('', self.tables[table][name]['ctype']):
-                        value = self.tables[table][name]['ctype'](value)
+                    if not isinstance("", self.tables[table][name]["ctype"]):
+                        value = self.tables[table][name]["ctype"](value)
                     else:
                         value = GetUnicodeValue(value)
                 else:
                     value = None
-                self.tables[table][name]['values'].append(value)
+                self.tables[table][name]["values"].append(value)
                 nselected = 1
 
         return nselected

+ 99 - 106
gui/wxpython/docs/wxgui_sphinx/conf.py

@@ -20,16 +20,11 @@ from shutil import copy
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-if not os.getenv('GISBASE'):
+if not os.getenv("GISBASE"):
     sys.exit("GISBASE not defined")
 sys.path.insert(
-    0,
-    os.path.abspath(
-        os.path.join(
-            os.environ['GISBASE'],
-            'etc',
-            'python',
-            'grass')))
+    0, os.path.abspath(os.path.join(os.environ["GISBASE"], "etc", "python", "grass"))
+)
 
 from grass.script import core
 
@@ -39,165 +34,163 @@ footer_tmpl = string.Template(
 <p><a href="../index.html">Help Index</a> | <a href="../topics.html">Topics Index</a> | <a href="../keywords.html">Keywords Index</a> | <a href="../full_index.html">Full Index</a></p>
 <p>&copy; 2003-${year} <a href="https://grass.osgeo.org">GRASS Development Team</a>, GRASS GIS ${grass_version} Reference Manual</p>
 {% endblock %}
-""")
+"""
+)
 
-grass_version = core.version()['version']
+grass_version = core.version()["version"]
 today = date.today()
 
 copy("_templates/layout.html.template", "_templates/layout.html")
 with open("_templates/layout.html", "a+b") as f:
-    f.write(
-        footer_tmpl.substitute(
-            grass_version=grass_version,
-            year=today.year))
+    f.write(footer_tmpl.substitute(grass_version=grass_version, year=today.year))
     f.close()
 
 # -- General configuration -----------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
-    'sphinx.ext.autodoc',
-    'sphinx.ext.doctest',
-    'sphinx.ext.todo',
-    'sphinx.ext.coverage',
-    'sphinx.ext.mathjax',
-    'sphinx.ext.ifconfig',
-    'sphinx.ext.viewcode',
-    'sphinx.ext.graphviz',
+    "sphinx.ext.autodoc",
+    "sphinx.ext.doctest",
+    "sphinx.ext.todo",
+    "sphinx.ext.coverage",
+    "sphinx.ext.mathjax",
+    "sphinx.ext.ifconfig",
+    "sphinx.ext.viewcode",
+    "sphinx.ext.graphviz",
 ]
 todo_include_todos = True
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'wxGUI'
-copyright = u'2014, GRASS Development Team'
+project = "wxGUI"
+copyright = "2014, GRASS Development Team"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-#version = '0.1'
+# version = '0.1'
 # The full version, including alpha/beta/rc tags.
-#release = '0.1'
+# release = '0.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-language = 'python'
+language = "python"
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ["_build"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 # If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
 
 
 # -- Options for HTML output ----------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'traditional'
+html_theme = "traditional"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
 # directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-html_sidebars = {"**": ["localtoc.html", 'relations.html', 'searchbox.html']}
+html_sidebars = {"**": ["localtoc.html", "relations.html", "searchbox.html"]}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
 html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
 html_show_sourcelink = True
@@ -211,24 +204,22 @@ html_show_copyright = True
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'wxGUIdoc'
+htmlhelp_basename = "wxGUIdoc"
 
 
 # -- Options for LaTeX output ---------------------------------------------
 
 latex_elements = {
     # The paper size ('letterpaper' or 'a4paper').
-    'papersize': 'a4paper',
-
+    "papersize": "a4paper",
     # The font size ('10pt', '11pt' or '12pt').
-    'pointsize': '10pt',
-
+    "pointsize": "10pt",
     # Additional stuff for the LaTeX preamble.
     #'preamble': '',
 }
@@ -237,42 +228,38 @@ latex_elements = {
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', 'wxGUI.tex', u'wxGUI Documentation',
-     u'GRASS Development Team', 'manual'),
+    ("index", "wxGUI.tex", "wxGUI Documentation", "GRASS Development Team", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output ---------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'wxgui', u'wxGUI Documentation',
-     [u'GRASS Development Team'], 1)
-]
+man_pages = [("index", "wxgui", "wxGUI Documentation", ["GRASS Development Team"], 1)]
 
 # If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
 
 
 # -- Options for Texinfo output -------------------------------------------
@@ -281,89 +268,95 @@ man_pages = [
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    ('index', 'wxGUI', u'wxGUI Documentation',
-     u'GRASS Development Team', 'wxGUI', 'One line description of project.',
-     'Miscellaneous'),
+    (
+        "index",
+        "wxGUI",
+        "wxGUI Documentation",
+        "GRASS Development Team",
+        "wxGUI",
+        "One line description of project.",
+        "Miscellaneous",
+    ),
 ]
 
 # Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
 
 # If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
 
 # If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
 
 
 # -- Options for Epub output ----------------------------------------------
 
 # Bibliographic Dublin Core info.
-epub_title = u'wxGUI'
-epub_author = u'GRASS Development Team'
-epub_publisher = u'GRASS Development Team'
-epub_copyright = u'2014, GRASS Development Team'
+epub_title = "wxGUI"
+epub_author = "GRASS Development Team"
+epub_publisher = "GRASS Development Team"
+epub_copyright = "2014, GRASS Development Team"
 
 # The basename for the epub file. It defaults to the project name.
-#epub_basename = u'wxGUI'
+# epub_basename = u'wxGUI'
 
 # The HTML theme for the epub output. Since the default themes are not optimized
 # for small screen space, using the same theme for HTML and epub output is
 # usually not wise. This defaults to 'epub', a theme designed to save visual
 # space.
-#epub_theme = 'epub'
+# epub_theme = 'epub'
 
 # The language of the text. It defaults to the language option
 # or en if the language is not set.
-#epub_language = ''
+# epub_language = ''
 
 # The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
+# epub_scheme = ''
 
 # The unique identifier of the text. This can be a ISBN number
 # or the project homepage.
-#epub_identifier = ''
+# epub_identifier = ''
 
 # A unique identification for the text.
-#epub_uid = ''
+# epub_uid = ''
 
 # A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
+# epub_cover = ()
 
 # A sequence of (type, uri, title) tuples for the guide element of content.opf.
-#epub_guide = ()
+# epub_guide = ()
 
 # HTML files that should be inserted before the pages created by sphinx.
 # The format is a list of tuples containing the path and title.
-#epub_pre_files = []
+# epub_pre_files = []
 
 # HTML files shat should be inserted after the pages created by sphinx.
 # The format is a list of tuples containing the path and title.
-#epub_post_files = []
+# epub_post_files = []
 
 # A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
+epub_exclude_files = ["search.html"]
 
 # The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
+# epub_tocdepth = 3
 
 # Allow duplicate toc entries.
-#epub_tocdup = True
+# epub_tocdup = True
 
 # Choose between 'default' and 'includehidden'.
-#epub_tocscope = 'default'
+# epub_tocscope = 'default'
 
 # Fix unsupported image types using the PIL.
-#epub_fix_images = False
+# epub_fix_images = False
 
 # Scale large images.
-#epub_max_image_width = 0
+# epub_max_image_width = 0
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#epub_show_urls = 'inline'
+# epub_show_urls = 'inline'
 
 # If false, no index is generated.
-#epub_use_index = True
+# epub_use_index = True

+ 3 - 3
gui/wxpython/gcp/__init__.py

@@ -1,5 +1,5 @@
 all = [
-    'manager',
-    'mapdisplay',
-    'toolbars',
+    "manager",
+    "mapdisplay",
+    "toolbars",
 ]

+ 7 - 5
gui/wxpython/gcp/g.gui.gcp.py

@@ -50,17 +50,18 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.settings import UserSettings
     from core.giface import StandaloneGrassInterface
     from gcp.manager import GCPWizard
 
-    driver = UserSettings.Get(group='display', key='driver', subkey='type')
-    if driver == 'png':
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'png'
+    driver = UserSettings.Get(group="display", key="driver", subkey="type")
+    if driver == "png":
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
     else:
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'cairo'
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
 
     app = wx.App()
 
@@ -68,5 +69,6 @@ def main():
 
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

File diff ditekan karena terlalu besar
+ 1229 - 1097
gui/wxpython/gcp/manager.py


+ 196 - 130
gui/wxpython/gcp/mapdisplay.py

@@ -44,10 +44,17 @@ class MapFrame(SingleMapFrame):
     child double buffered drawing window.
     """
 
-    def __init__(self, parent, giface,
-                 title=_("Manage Ground Control Points"),
-                 toolbars=["gcpdisp"], Map=None, auimgr=None,
-                 name='GCPMapWindow', **kwargs):
+    def __init__(
+        self,
+        parent,
+        giface,
+        title=_("Manage Ground Control Points"),
+        toolbars=["gcpdisp"],
+        Map=None,
+        auimgr=None,
+        name="GCPMapWindow",
+        **kwargs,
+    ):
         """Main map display window with toolbars, statusbar and
         DrawWindow
 
@@ -67,7 +74,8 @@ class MapFrame(SingleMapFrame):
             Map=Map,
             auimgr=auimgr,
             name=name,
-            **kwargs)
+            **kwargs,
+        )
 
         self._giface = giface
         # properties are shared in other objects, so defining here
@@ -81,11 +89,11 @@ class MapFrame(SingleMapFrame):
         for toolb in toolbars:
             self.AddToolbar(toolb)
 
-        self.activemap = self.toolbars['gcpdisp'].togglemap
+        self.activemap = self.toolbars["gcpdisp"].togglemap
         self.activemap.SetSelection(0)
 
-        self.SrcMap = self.grwiz.SrcMap       # instance of render.Map
-        self.TgtMap = self.grwiz.TgtMap       # instance of render.Map
+        self.SrcMap = self.grwiz.SrcMap  # instance of render.Map
+        self.TgtMap = self.grwiz.TgtMap  # instance of render.Map
         self._mgr.SetDockSizeConstraint(0.5, 0.5)
 
         #
@@ -93,58 +101,70 @@ class MapFrame(SingleMapFrame):
         #
 
         # items for choice
-        self.statusbarItems = [sb.SbCoordinates,
-                               sb.SbRegionExtent,
-                               sb.SbCompRegionExtent,
-                               sb.SbShowRegion,
-                               sb.SbResolution,
-                               sb.SbDisplayGeometry,
-                               sb.SbMapScale,
-                               sb.SbProjection,
-                               sbgcp.SbGoToGCP,
-                               sbgcp.SbRMSError]
+        self.statusbarItems = [
+            sb.SbCoordinates,
+            sb.SbRegionExtent,
+            sb.SbCompRegionExtent,
+            sb.SbShowRegion,
+            sb.SbResolution,
+            sb.SbDisplayGeometry,
+            sb.SbMapScale,
+            sb.SbProjection,
+            sbgcp.SbGoToGCP,
+            sbgcp.SbRMSError,
+        ]
 
         # create statusbar and its manager
         statusbar = self.CreateStatusBar(number=4, style=0)
         statusbar.SetStatusWidths([-5, -2, -1, -1])
-        self.statusbarManager = sb.SbManager(
-            mapframe=self, statusbar=statusbar)
+        self.statusbarManager = sb.SbManager(mapframe=self, statusbar=statusbar)
 
         # fill statusbar manager
         self.statusbarManager.AddStatusbarItemsByClass(
-            self.statusbarItems, mapframe=self, statusbar=statusbar)
+            self.statusbarItems, mapframe=self, statusbar=statusbar
+        )
         self.statusbarManager.AddStatusbarItem(
-            sb.SbMask(self, statusbar=statusbar, position=2))
+            sb.SbMask(self, statusbar=statusbar, position=2)
+        )
         self.statusbarManager.AddStatusbarItem(
-            sb.SbRender(self, statusbar=statusbar, position=3))
+            sb.SbRender(self, statusbar=statusbar, position=3)
+        )
 
         self.statusbarManager.SetMode(8)  # goto GCP
 
         #
         # Init map display (buffered DC & set default cursor)
         #
-        self.grwiz.SwitchEnv('source')
+        self.grwiz.SwitchEnv("source")
         self.SrcMapWindow = BufferedMapWindow(
-            parent=self, giface=self._giface, id=wx.ID_ANY,
-            properties=self.mapWindowProperties, Map=self.SrcMap)
-
-        self.grwiz.SwitchEnv('target')
+            parent=self,
+            giface=self._giface,
+            id=wx.ID_ANY,
+            properties=self.mapWindowProperties,
+            Map=self.SrcMap,
+        )
+
+        self.grwiz.SwitchEnv("target")
         self.TgtMapWindow = BufferedMapWindow(
-            parent=self, giface=self._giface, id=wx.ID_ANY,
-            properties=self.mapWindowProperties, Map=self.TgtMap)
+            parent=self,
+            giface=self._giface,
+            id=wx.ID_ANY,
+            properties=self.mapWindowProperties,
+            Map=self.TgtMap,
+        )
         self.MapWindow = self.SrcMapWindow
         self.Map = self.SrcMap
         self._setUpMapWindow(self.SrcMapWindow)
         self._setUpMapWindow(self.TgtMapWindow)
-        self.SrcMapWindow.SetNamedCursor('cross')
-        self.TgtMapWindow.SetNamedCursor('cross')
+        self.SrcMapWindow.SetNamedCursor("cross")
+        self.TgtMapWindow.SetNamedCursor("cross")
         # used to switch current map (combo box in toolbar)
         self.SrcMapWindow.mouseEntered.connect(
-            lambda:
-            self._setActiveMapWindow(self.SrcMapWindow))
+            lambda: self._setActiveMapWindow(self.SrcMapWindow)
+        )
         self.TgtMapWindow.mouseEntered.connect(
-            lambda:
-            self._setActiveMapWindow(self.TgtMapWindow))
+            lambda: self._setActiveMapWindow(self.TgtMapWindow)
+        )
 
         #
         # initialize region values
@@ -167,22 +187,47 @@ class MapFrame(SingleMapFrame):
         # windows
         self.list = self.CreateGCPList()
 
-        #self.SrcMapWindow.SetSize((300, 300))
-        #self.TgtMapWindow.SetSize((300, 300))
+        # self.SrcMapWindow.SetSize((300, 300))
+        # self.TgtMapWindow.SetSize((300, 300))
         self.list.SetSize((100, 150))
-        self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
-                          Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
-                          RightDockable(False).PinButton().FloatingSize((600, 200)).
-                          CloseButton(False).DestroyOnClose(True).
-                          Top().Layer(1).MinSize((200, 100)))
-        self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
-                          Name("source").Caption(_("Source Display")).Dockable(False).
-                          CloseButton(False).DestroyOnClose(True).Floatable(False).
-                          Centre())
-        self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
-                          Name("target").Caption(_("Target Display")).Dockable(False).
-                          CloseButton(False).DestroyOnClose(True).Floatable(False).
-                          Right().Layer(0))
+        self._mgr.AddPane(
+            self.list,
+            wx.aui.AuiPaneInfo()
+            .Name("gcplist")
+            .Caption(_("GCP List"))
+            .LeftDockable(False)
+            .RightDockable(False)
+            .PinButton()
+            .FloatingSize((600, 200))
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Top()
+            .Layer(1)
+            .MinSize((200, 100)),
+        )
+        self._mgr.AddPane(
+            self.SrcMapWindow,
+            wx.aui.AuiPaneInfo()
+            .Name("source")
+            .Caption(_("Source Display"))
+            .Dockable(False)
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Floatable(False)
+            .Centre(),
+        )
+        self._mgr.AddPane(
+            self.TgtMapWindow,
+            wx.aui.AuiPaneInfo()
+            .Name("target")
+            .Caption(_("Target Display"))
+            .Dockable(False)
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Floatable(False)
+            .Right()
+            .Layer(0),
+        )
 
         srcwidth, srcheight = self.SrcMapWindow.GetSize()
         tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
@@ -196,7 +241,7 @@ class MapFrame(SingleMapFrame):
         else:
             self.activemap.Enable(False)
         # needed by Mac OS, does not harm on Linux, breaks display on Windows
-        if platform.system() != 'Windows':
+        if platform.system() != "Windows":
             self._mgr.Update()
 
         #
@@ -219,10 +264,10 @@ class MapFrame(SingleMapFrame):
         # Re-use dialogs
         #
         self.dialogs = {}
-        self.dialogs['attributes'] = None
-        self.dialogs['category'] = None
-        self.dialogs['barscale'] = None
-        self.dialogs['legend'] = None
+        self.dialogs["attributes"] = None
+        self.dialogs["category"] = None
+        self.dialogs["barscale"] = None
+        self.dialogs["legend"] = None
 
         self.decorationDialog = None  # decoration/overlays
 
@@ -233,11 +278,11 @@ class MapFrame(SingleMapFrame):
         # TODO: almost the same implementation as for MapFrameBase (only names differ)
         # enable or disable zoom history tool
         mapWindow.zoomHistoryAvailable.connect(
-            lambda:
-            self.GetMapToolbar().Enable('zoomback', enable=True))
+            lambda: self.GetMapToolbar().Enable("zoomback", enable=True)
+        )
         mapWindow.zoomHistoryUnavailable.connect(
-            lambda:
-            self.GetMapToolbar().Enable('zoomback', enable=False))
+            lambda: self.GetMapToolbar().Enable("zoomback", enable=False)
+        )
         mapWindow.mouseMoving.connect(self.CoordinatesChanged)
 
     def AddToolbar(self, name):
@@ -252,42 +297,63 @@ class MapFrame(SingleMapFrame):
         """
         # default toolbar
         if name == "map":
-            self.toolbars['map'] = MapToolbar(self, self._toolSwitcher)
-
-            self._mgr.AddPane(self.toolbars['map'],
-                              wx.aui.AuiPaneInfo().
-                              Name("maptoolbar").Caption(_("Map Toolbar")).
-                              ToolbarPane().Top().
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2).
-                              BestSize((self.toolbars['map'].GetSize())))
+            self.toolbars["map"] = MapToolbar(self, self._toolSwitcher)
+
+            self._mgr.AddPane(
+                self.toolbars["map"],
+                wx.aui.AuiPaneInfo()
+                .Name("maptoolbar")
+                .Caption(_("Map Toolbar"))
+                .ToolbarPane()
+                .Top()
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2)
+                .BestSize((self.toolbars["map"].GetSize())),
+            )
 
         # GCP display
         elif name == "gcpdisp":
-            self.toolbars['gcpdisp'] = GCPDisplayToolbar(
-                self, self._toolSwitcher)
-
-            self._mgr.AddPane(self.toolbars['gcpdisp'],
-                              wx.aui.AuiPaneInfo().
-                              Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
-                              ToolbarPane().Top().
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2))
+            self.toolbars["gcpdisp"] = GCPDisplayToolbar(self, self._toolSwitcher)
+
+            self._mgr.AddPane(
+                self.toolbars["gcpdisp"],
+                wx.aui.AuiPaneInfo()
+                .Name("gcpdisplaytoolbar")
+                .Caption(_("GCP Display toolbar"))
+                .ToolbarPane()
+                .Top()
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2),
+            )
 
             if not self.show_target:
-                self.toolbars['gcpdisp'].Enable('zoommenu', enable=False)
-
-            self.toolbars['gcpman'] = GCPManToolbar(self)
-
-            self._mgr.AddPane(self.toolbars['gcpman'],
-                              wx.aui.AuiPaneInfo().
-                              Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
-                              ToolbarPane().Top().Row(1).
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2))
+                self.toolbars["gcpdisp"].Enable("zoommenu", enable=False)
+
+            self.toolbars["gcpman"] = GCPManToolbar(self)
+
+            self._mgr.AddPane(
+                self.toolbars["gcpman"],
+                wx.aui.AuiPaneInfo()
+                .Name("gcpmanagertoolbar")
+                .Caption(_("GCP Manager toolbar"))
+                .ToolbarPane()
+                .Top()
+                .Row(1)
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2),
+            )
 
         self._mgr.Update()
 
@@ -314,13 +380,11 @@ class MapFrame(SingleMapFrame):
         event.Skip()
 
     def OnDraw(self, event):
-        """Re-display current map composition
-        """
+        """Re-display current map composition"""
         self.MapWindow.UpdateMap(render=False)
 
     def OnRender(self, event):
-        """Re-render map composition (each map layer)
-        """
+        """Re-render map composition (each map layer)"""
         # FIXME: remove qlayer code or use RemoveQueryLayer() now in mapdisp.frame
         # delete tmp map layers (queries)
         qlayer = self.Map.GetListOfLayers(name=globalvar.QUERYLAYER)
@@ -335,13 +399,12 @@ class MapFrame(SingleMapFrame):
         self.StatusbarUpdate()
 
     def OnPointer(self, event):
-        """Pointer button clicked
-        """
+        """Pointer button clicked"""
         self.SrcMapWindow.SetModePointer()
         self.TgtMapWindow.SetModePointer()
         # change the default cursor
-        self.SrcMapWindow.SetNamedCursor('cross')
-        self.TgtMapWindow.SetNamedCursor('cross')
+        self.SrcMapWindow.SetNamedCursor("cross")
+        self.TgtMapWindow.SetNamedCursor("cross")
 
     def OnZoomIn(self, event):
         """Zoom in the map."""
@@ -372,12 +435,13 @@ class MapFrame(SingleMapFrame):
         win.EraseMap()
 
     def SaveToFile(self, event):
-        """Save map to image
-        """
+        """Save map to image"""
         img = self.MapWindow.img
         if not img:
-            GMessage(parent=self, message=_(
-                "Nothing to render (empty map). Operation canceled."))
+            GMessage(
+                parent=self,
+                message=_("Nothing to render (empty map). Operation canceled."),
+            )
             return
         filetype, ltype = GetImageHandlers(img)
 
@@ -391,11 +455,14 @@ class MapFrame(SingleMapFrame):
         dlg.Destroy()
 
         # get filename
-        dlg = wx.FileDialog(parent=self,
-                            message=_("Choose a file name to save the image "
-                                      "(no need to add extension)"),
-                            wildcard=filetype,
-                            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
+        dlg = wx.FileDialog(
+            parent=self,
+            message=_(
+                "Choose a file name to save the image " "(no need to add extension)"
+            ),
+            wildcard=filetype,
+            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
+        )
 
         if dlg.ShowModal() == wx.ID_OK:
             path = dlg.GetPath()
@@ -404,13 +471,12 @@ class MapFrame(SingleMapFrame):
                 return
 
             base, ext = os.path.splitext(path)
-            fileType = ltype[dlg.GetFilterIndex()]['type']
-            extType = ltype[dlg.GetFilterIndex()]['ext']
+            fileType = ltype[dlg.GetFilterIndex()]["type"]
+            extType = ltype[dlg.GetFilterIndex()]["ext"]
             if ext != extType:
-                path = base + '.' + extType
+                path = base + "." + extType
 
-            self.MapWindow.SaveToFile(path, fileType,
-                                      width, height)
+            self.MapWindow.SaveToFile(path, fileType, width, height)
 
         dlg.Destroy()
 
@@ -421,15 +487,15 @@ class MapFrame(SingleMapFrame):
         point = wx.GetMousePosition()
         printmenu = Menu()
         # Add items to the menu
-        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+        setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup"))
         printmenu.AppendItem(setup)
         self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
 
-        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+        preview = wx.MenuItem(printmenu, wx.ID_ANY, _("Print preview"))
         printmenu.AppendItem(preview)
         self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
 
-        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
+        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _("Print display"))
         printmenu.AppendItem(doprint)
         self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
 
@@ -457,38 +523,38 @@ class MapFrame(SingleMapFrame):
         self.MapWindow.DisplayToWind()
 
     def SaveDisplayRegion(self, event):
-        """Save display extents to named region file.
-        """
+        """Save display extents to named region file."""
         self.MapWindow.SaveDisplayRegion()
 
     def OnZoomMenu(self, event):
-        """Popup Zoom menu
-        """
+        """Popup Zoom menu"""
         point = wx.GetMousePosition()
         zoommenu = Menu()
         # Add items to the menu
 
-        zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Zoom to computational region (set with g.region)'))
+        zoomwind = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Zoom to computational region (set with g.region)")
+        )
         zoommenu.AppendItem(zoomwind)
         self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
 
-        zoomdefault = wx.MenuItem(
-            zoommenu, wx.ID_ANY, _('Zoom to default region'))
+        zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to default region"))
         zoommenu.AppendItem(zoomdefault)
         self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
 
-        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to saved region"))
         zoommenu.AppendItem(zoomsaved)
         self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
 
-        savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Set computational region from display'))
+        savewind = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Set computational region from display")
+        )
         zoommenu.AppendItem(savewind)
         self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
 
-        savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Save display geometry to named region'))
+        savezoom = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Save display geometry to named region")
+        )
         zoommenu.AppendItem(savezoom)
         self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
 
@@ -520,7 +586,7 @@ class MapFrame(SingleMapFrame):
 
     def GetMapToolbar(self):
         """Returns toolbar with zooming tools"""
-        return self.toolbars['gcpdisp']
+        return self.toolbars["gcpdisp"]
 
     def _setActiveMapWindow(self, mapWindow):
         if not self.MapWindow == mapWindow:

+ 13 - 13
gui/wxpython/gcp/statusbar.py

@@ -33,11 +33,10 @@ class SbGoToGCP(SbItem):
 
     def __init__(self, mapframe, statusbar, position=0):
         SbItem.__init__(self, mapframe, statusbar, position)
-        self.name = 'gotoGCP'
+        self.name = "gotoGCP"
         self.label = _("Go to GCP No.")
 
-        self.widget = SpinCtrl(parent=self.statusbar, id=wx.ID_ANY,
-                               value="", min=0)
+        self.widget = SpinCtrl(parent=self.statusbar, id=wx.ID_ANY, value="", min=0)
         self.widget.Hide()
 
         self.widget.Bind(wx.EVT_TEXT_ENTER, self.OnGoToGCP)
@@ -50,9 +49,9 @@ class SbGoToGCP(SbItem):
 
         # always false, spin checks it
         if gcpNumber < 0 or gcpNumber > len(mapCoords):
-            GMessage(parent=self,
-                     message="%s 1 - %s." % (_("Valid Range:"),
-                                             len(mapCoords)))
+            GMessage(
+                parent=self, message="%s 1 - %s." % (_("Valid Range:"), len(mapCoords))
+            )
             return
 
         if gcpNumber == 0:
@@ -63,9 +62,9 @@ class SbGoToGCP(SbItem):
         listCtrl.selectedkey = gcpNumber
         listCtrl.selected = listCtrl.FindItem(-1, gcpNumber)
         listCtrl.render = False
-        listCtrl.SetItemState(listCtrl.selected,
-                              wx.LIST_STATE_SELECTED,
-                              wx.LIST_STATE_SELECTED)
+        listCtrl.SetItemState(
+            listCtrl.selected, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED
+        )
         listCtrl.render = True
 
         listCtrl.EnsureVisible(listCtrl.selected)
@@ -116,12 +115,13 @@ class SbRMSError(SbTextItem):
 
     def __init__(self, mapframe, statusbar, position=0):
         SbTextItem.__init__(self, mapframe, statusbar, position)
-        self.name = 'RMSError'
+        self.name = "RMSError"
         self.label = _("RMS error")
 
     def Show(self):
         """Shows the RMS errors."""
-        self.SetValue(_("Forward: %(forw)s, Backward: %(back)s") %
-                      {'forw': self.mapFrame.GetFwdError(),
-                       'back': self.mapFrame.GetBkwError()})
+        self.SetValue(
+            _("Forward: %(forw)s, Backward: %(back)s")
+            % {"forw": self.mapFrame.GetFwdError(), "back": self.mapFrame.GetBkwError()}
+        )
         SbTextItem.Show(self)

+ 65 - 88
gui/wxpython/gcp/toolbars.py

@@ -37,68 +37,61 @@ class GCPManToolbar(BaseToolbar):
 
     def _toolbarData(self):
         icons = {
-            'gcpAdd': MetaIcon(img='gcp-add',
-                               label=_('Add new GCP to the list')),
-            'gcpDelete': MetaIcon(img='gcp-delete',
-                                  label=_('Delete selected GCP')),
-            'gcpClear': MetaIcon(img='gcp-remove',
-                                 label=_('Clear selected GCP')),
-            'gcpRms': MetaIcon(img='gcp-rms',
-                               label=_('Recalculate RMS error')),
-            'georectify': MetaIcon(img='georectify',
-                                   label=_('Georectify')),
-            'gcpSave': MetaIcon(img='gcp-save',
-                                label=_('Save GCPs to POINTS file')),
-            'gcpReload': MetaIcon(img='reload',
-                                  label=_('Reload GCPs from POINTS file')),
+            "gcpAdd": MetaIcon(img="gcp-add", label=_("Add new GCP to the list")),
+            "gcpDelete": MetaIcon(img="gcp-delete", label=_("Delete selected GCP")),
+            "gcpClear": MetaIcon(img="gcp-remove", label=_("Clear selected GCP")),
+            "gcpRms": MetaIcon(img="gcp-rms", label=_("Recalculate RMS error")),
+            "georectify": MetaIcon(img="georectify", label=_("Georectify")),
+            "gcpSave": MetaIcon(img="gcp-save", label=_("Save GCPs to POINTS file")),
+            "gcpReload": MetaIcon(
+                img="reload", label=_("Reload GCPs from POINTS file")
+            ),
         }
 
-        return self._getToolbarData((('gcpAdd', icons["gcpAdd"],
-                                      self.parent.AddGCP),
-                                     ('gcpDelete', icons["gcpDelete"],
-                                      self.parent.DeleteGCP),
-                                     ('gcpClear', icons["gcpClear"],
-                                      self.parent.ClearGCP),
-                                     (None, ),
-                                     ('rms', icons["gcpRms"],
-                                      self.parent.OnRMS),
-                                     ('georect', icons["georectify"],
-                                      self.parent.OnGeorect),
-                                     (None, ),
-                                     ('gcpSave', icons["gcpSave"],
-                                      self.parent.SaveGCPs),
-                                     ('gcpReload', icons["gcpReload"],
-                                      self.parent.ReloadGCPs))
-                                    )
+        return self._getToolbarData(
+            (
+                ("gcpAdd", icons["gcpAdd"], self.parent.AddGCP),
+                ("gcpDelete", icons["gcpDelete"], self.parent.DeleteGCP),
+                ("gcpClear", icons["gcpClear"], self.parent.ClearGCP),
+                (None,),
+                ("rms", icons["gcpRms"], self.parent.OnRMS),
+                ("georect", icons["georectify"], self.parent.OnGeorect),
+                (None,),
+                ("gcpSave", icons["gcpSave"], self.parent.SaveGCPs),
+                ("gcpReload", icons["gcpReload"], self.parent.ReloadGCPs),
+            )
+        )
 
 
 class GCPDisplayToolbar(BaseToolbar):
-    """GCP Display toolbar
-    """
+    """GCP Display toolbar"""
 
     def __init__(self, parent, toolSwitcher):
-        """GCP Display toolbar constructor
-        """
+        """GCP Display toolbar constructor"""
         BaseToolbar.__init__(self, parent, toolSwitcher)
 
         self.InitToolbar(self._toolbarData())
         self._default = self.gcpset
 
         # add tool to toggle active map window
-        self.togglemap = wx.Choice(parent=self, id=wx.ID_ANY,
-                                   choices=[_('source'), _('target')])
+        self.togglemap = wx.Choice(
+            parent=self, id=wx.ID_ANY, choices=[_("source"), _("target")]
+        )
 
         self.InsertControl(10, self.togglemap)
 
         self.SetToolShortHelp(
-            self.togglemap.GetId(), '%s %s %s' %
-            (_('Set map canvas for '),
-             BaseIcons["zoomBack"].GetLabel(),
-             _(' / Zoom to map')))
+            self.togglemap.GetId(),
+            "%s %s %s"
+            % (
+                _("Set map canvas for "),
+                BaseIcons["zoomBack"].GetLabel(),
+                _(" / Zoom to map"),
+            ),
+        )
 
         for tool in (self.gcpset, self.pan, self.zoomin, self.zoomout):
-            self.toolSwitcher.AddToolToGroup(
-                group='mouseUse', toolbar=self, tool=tool)
+            self.toolSwitcher.AddToolToGroup(group="mouseUse", toolbar=self, tool=tool)
 
         # realize the toolbar
         self.Realize()
@@ -108,50 +101,34 @@ class GCPDisplayToolbar(BaseToolbar):
     def _toolbarData(self):
         """Toolbar data"""
         icons = {
-            'gcpSet': MetaIcon(
-                img='gcp-create',
-                label=_('Update GCP coordinates'),
-                desc=_('Update GCP coordinates)')),
-            'quit': BaseIcons['quit'].SetLabel(
-                _('Quit georectification tool')),
-            'settings': BaseIcons['settings'].SetLabel(
-                _('Georectifier settings')),
-            'help': BaseIcons['help'].SetLabel(
-                _('Georectifier manual')),
+            "gcpSet": MetaIcon(
+                img="gcp-create",
+                label=_("Update GCP coordinates"),
+                desc=_("Update GCP coordinates)"),
+            ),
+            "quit": BaseIcons["quit"].SetLabel(_("Quit georectification tool")),
+            "settings": BaseIcons["settings"].SetLabel(_("Georectifier settings")),
+            "help": BaseIcons["help"].SetLabel(_("Georectifier manual")),
         }
 
-        return self._getToolbarData((("displaymap", BaseIcons["display"],
-                                      self.parent.OnDraw),
-                                     ("rendermap", BaseIcons["render"],
-                                      self.parent.OnRender),
-                                     ("erase", BaseIcons["erase"],
-                                      self.parent.OnErase),
-                                     (None, ),
-                                     ("gcpset", icons["gcpSet"],
-                                      self.parent.OnPointer,
-                                      wx.ITEM_CHECK),
-                                     ("pan", BaseIcons["pan"],
-                                      self.parent.OnPan,
-                                      wx.ITEM_CHECK),
-                                     ("zoomin", BaseIcons["zoomIn"],
-                                      self.parent.OnZoomIn,
-                                      wx.ITEM_CHECK),
-                                     ("zoomout", BaseIcons["zoomOut"],
-                                      self.parent.OnZoomOut,
-                                      wx.ITEM_CHECK),
-                                     ("zoommenu", BaseIcons["zoomMenu"],
-                                      self.parent.OnZoomMenuGCP),
-                                     (None, ),
-                                     ("zoomback", BaseIcons["zoomBack"],
-                                      self.parent.OnZoomBack),
-                                     ("zoomtomap", BaseIcons["zoomExtent"],
-                                      self.parent.OnZoomToMap),
-                                     (None, ),
-                                     ('settings', icons["settings"],
-                                      self.parent.OnSettings),
-                                     ('help', icons["help"],
-                                      self.parent.OnHelp),
-                                     (None, ),
-                                     ('quit', icons["quit"],
-                                      self.parent.OnQuit))
-                                    )
+        return self._getToolbarData(
+            (
+                ("displaymap", BaseIcons["display"], self.parent.OnDraw),
+                ("rendermap", BaseIcons["render"], self.parent.OnRender),
+                ("erase", BaseIcons["erase"], self.parent.OnErase),
+                (None,),
+                ("gcpset", icons["gcpSet"], self.parent.OnPointer, wx.ITEM_CHECK),
+                ("pan", BaseIcons["pan"], self.parent.OnPan, wx.ITEM_CHECK),
+                ("zoomin", BaseIcons["zoomIn"], self.parent.OnZoomIn, wx.ITEM_CHECK),
+                ("zoomout", BaseIcons["zoomOut"], self.parent.OnZoomOut, wx.ITEM_CHECK),
+                ("zoommenu", BaseIcons["zoomMenu"], self.parent.OnZoomMenuGCP),
+                (None,),
+                ("zoomback", BaseIcons["zoomBack"], self.parent.OnZoomBack),
+                ("zoomtomap", BaseIcons["zoomExtent"], self.parent.OnZoomToMap),
+                (None,),
+                ("settings", icons["settings"], self.parent.OnSettings),
+                ("help", icons["help"], self.parent.OnHelp),
+                (None,),
+                ("quit", icons["quit"], self.parent.OnQuit),
+            )
+        )

+ 8 - 8
gui/wxpython/gmodeler/__init__.py

@@ -1,10 +1,10 @@
 all = [
-    'preferences',
-    'g.gui.gmodeler',
-    'menudata',
-    'model',
-    'pystc',
-    'dialogs',
-    'toolbars',
-    'frame',
+    "preferences",
+    "g.gui.gmodeler",
+    "menudata",
+    "model",
+    "pystc",
+    "dialogs",
+    "toolbars",
+    "frame",
 ]

File diff ditekan karena terlalu besar
+ 341 - 277
gui/wxpython/gmodeler/dialogs.py


File diff ditekan karena terlalu besar
+ 536 - 525
gui/wxpython/gmodeler/frame.py


+ 4 - 2
gui/wxpython/gmodeler/g.gui.gmodeler.py

@@ -43,6 +43,7 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.giface import StandaloneGrassInterface
@@ -54,11 +55,12 @@ def main():
         giface=StandaloneGrassInterface(),
         title=_("Graphical Modeler - GRASS GIS"),
     )
-    if options['file']:
-        frame.LoadModelFile(options['file'])
+    if options["file"]:
+        frame.LoadModelFile(options["file"])
     frame.Show()
 
     app.MainLoop()
 
+
 if __name__ == "__main__":
     main()

+ 2 - 2
gui/wxpython/gmodeler/giface.py

@@ -17,14 +17,14 @@ This program is free software under the GNU General Public License
 from grass.pydispatch.signal import Signal
 
 
-class GraphicalModelerGrassInterface():
+class GraphicalModelerGrassInterface:
     """@implements core::giface::GrassInterface"""
 
     def __init__(self, model):
         self._model = model
 
         # Signal emitted to request updating of map (TODO)
-        self.updateMap = Signal('GraphicalModelerGrassInterface.updateMap')
+        self.updateMap = Signal("GraphicalModelerGrassInterface.updateMap")
 
     def GetLayerTree(self):
         return None

+ 1 - 3
gui/wxpython/gmodeler/menudata.py

@@ -21,10 +21,8 @@ from core.menutree import MenuTreeModelBuilder
 
 
 class ModelerMenuData(MenuTreeModelBuilder):
-
     def __init__(self, filename=None):
         if not filename:
-            filename = os.path.join(
-                globalvar.WXGUIDIR, 'xml', 'menudata_modeler.xml')
+            filename = os.path.join(globalvar.WXGUIDIR, "xml", "menudata_modeler.xml")
 
         MenuTreeModelBuilder.__init__(self, filename)

File diff ditekan karena terlalu besar
+ 883 - 812
gui/wxpython/gmodeler/model.py


File diff ditekan karena terlalu besar
+ 429 - 443
gui/wxpython/gmodeler/preferences.py


+ 55 - 74
gui/wxpython/gmodeler/toolbars.py

@@ -22,14 +22,13 @@ from icons.icon import MetaIcon
 
 
 class ModelerToolbar(BaseToolbar):
-    """Graphical modeler toolbaro (see gmodeler.py)
-    """
+    """Graphical modeler toolbaro (see gmodeler.py)"""
 
     def __init__(self, parent):
         BaseToolbar.__init__(self, parent)
 
         # workaround for http://trac.wxwidgets.org/ticket/13888
-        if sys.platform == 'darwin':
+        if sys.platform == "darwin":
             parent.SetToolBar(self)
 
         self.InitToolbar(self._toolbarData())
@@ -40,76 +39,58 @@ class ModelerToolbar(BaseToolbar):
     def _toolbarData(self):
         """Toolbar data"""
         icons = {
-            'new': MetaIcon(img='create',
-                            label=_('Create new model (Ctrl+N)')),
-            'open': MetaIcon(img='open',
-                             label=_('Load model from file (Ctrl+O)')),
-            'save': MetaIcon(img='save',
-                             label=_('Save current model to file (Ctrl+S)')),
-            'toImage': MetaIcon(img='image-export',
-                                label=_('Export model to image')),
-            'toPython': MetaIcon(img='python-export',
-                                 label=_('Export model to Python script')),
-            'actionAdd': MetaIcon(img='module-add',
-                                  label=_('Add command (GRASS module) to model')),
-            'dataAdd': MetaIcon(img='data-add',
-                                label=_('Add data to model')),
-            'relation': MetaIcon(img='relation-create',
-                                 label=_('Manually define relation between data and commands')),
-            'loop': MetaIcon(img='loop-add',
-                             label=_('Add loop/series to model')),
-            'comment': MetaIcon(img='label-add',
-                                label=_('Add comment to model')),
-            'run': MetaIcon(img='execute',
-                            label=_('Run model')),
-            'validate': MetaIcon(img='check',
-                                 label=_('Validate model')),
-            'settings': BaseIcons['settings'].SetLabel(_('Modeler settings')),
-            'properties': MetaIcon(img='options',
-                                   label=_('Show model properties')),
-            'variables': MetaIcon(img='modeler-variables',
-                                  label=_('Manage model variables')),
-            'redraw': MetaIcon(img='redraw',
-                               label=_('Redraw model canvas')),
-            'quit': BaseIcons['quit'].SetLabel(_('Quit Graphical Modeler')),
+            "new": MetaIcon(img="create", label=_("Create new model (Ctrl+N)")),
+            "open": MetaIcon(img="open", label=_("Load model from file (Ctrl+O)")),
+            "save": MetaIcon(
+                img="save", label=_("Save current model to file (Ctrl+S)")
+            ),
+            "toImage": MetaIcon(img="image-export", label=_("Export model to image")),
+            "toPython": MetaIcon(
+                img="python-export", label=_("Export model to Python script")
+            ),
+            "actionAdd": MetaIcon(
+                img="module-add", label=_("Add command (GRASS module) to model")
+            ),
+            "dataAdd": MetaIcon(img="data-add", label=_("Add data to model")),
+            "relation": MetaIcon(
+                img="relation-create",
+                label=_("Manually define relation between data and commands"),
+            ),
+            "loop": MetaIcon(img="loop-add", label=_("Add loop/series to model")),
+            "comment": MetaIcon(img="label-add", label=_("Add comment to model")),
+            "run": MetaIcon(img="execute", label=_("Run model")),
+            "validate": MetaIcon(img="check", label=_("Validate model")),
+            "settings": BaseIcons["settings"].SetLabel(_("Modeler settings")),
+            "properties": MetaIcon(img="options", label=_("Show model properties")),
+            "variables": MetaIcon(
+                img="modeler-variables", label=_("Manage model variables")
+            ),
+            "redraw": MetaIcon(img="redraw", label=_("Redraw model canvas")),
+            "quit": BaseIcons["quit"].SetLabel(_("Quit Graphical Modeler")),
         }
 
-        return self._getToolbarData((('new', icons['new'],
-                                      self.parent.OnModelNew),
-                                     ('open', icons['open'],
-                                      self.parent.OnModelOpen),
-                                     ('save', icons['save'],
-                                      self.parent.OnModelSave),
-                                     ('image', icons['toImage'],
-                                      self.parent.OnExportImage),
-                                     ('python', icons['toPython'],
-                                      self.parent.OnExportPython),
-                                     (None, ),
-                                     ('action', icons['actionAdd'],
-                                      self.parent.OnAddAction),
-                                     ('data', icons['dataAdd'],
-                                      self.parent.OnAddData),
-                                     ('relation', icons['relation'],
-                                      self.parent.OnDefineRelation),
-                                     ('loop', icons['loop'],
-                                      self.parent.OnDefineLoop),
-                                     ('comment', icons['comment'],
-                                      self.parent.OnAddComment),
-                                     (None, ),
-                                     ('redraw', icons['redraw'],
-                                      self.parent.OnCanvasRefresh),
-                                     ('validate', icons['validate'],
-                                      self.parent.OnValidateModel),
-                                     ('run', icons['run'],
-                                      self.parent.OnRunModel),
-                                     (None, ),
-                                     ("variables", icons['variables'],
-                                      self.parent.OnVariables),
-                                     ("settings", icons['settings'],
-                                      self.parent.OnPreferences),
-                                     ("help", BaseIcons['help'],
-                                      self.parent.OnHelp),
-                                     (None, ),
-                                     ('quit', icons['quit'],
-                                      self.parent.OnCloseWindow))
-                                    )
+        return self._getToolbarData(
+            (
+                ("new", icons["new"], self.parent.OnModelNew),
+                ("open", icons["open"], self.parent.OnModelOpen),
+                ("save", icons["save"], self.parent.OnModelSave),
+                ("image", icons["toImage"], self.parent.OnExportImage),
+                ("python", icons["toPython"], self.parent.OnExportPython),
+                (None,),
+                ("action", icons["actionAdd"], self.parent.OnAddAction),
+                ("data", icons["dataAdd"], self.parent.OnAddData),
+                ("relation", icons["relation"], self.parent.OnDefineRelation),
+                ("loop", icons["loop"], self.parent.OnDefineLoop),
+                ("comment", icons["comment"], self.parent.OnAddComment),
+                (None,),
+                ("redraw", icons["redraw"], self.parent.OnCanvasRefresh),
+                ("validate", icons["validate"], self.parent.OnValidateModel),
+                ("run", icons["run"], self.parent.OnRunModel),
+                (None,),
+                ("variables", icons["variables"], self.parent.OnVariables),
+                ("settings", icons["settings"], self.parent.OnPreferences),
+                ("help", BaseIcons["help"], self.parent.OnHelp),
+                (None,),
+                ("quit", icons["quit"], self.parent.OnCloseWindow),
+            )
+        )

+ 15 - 15
gui/wxpython/gui_core/__init__.py

@@ -1,17 +1,17 @@
 all = [
-    'query',
-    'widgets',
-    'preferences',
-    'menu',
-    'infobar',
-    'dialogs',
-    'mapwindow',
-    'mapdisp',
-    'gselect',
-    'ghelp',
-    'prompt',
-    'goutput',
-    'forms',
-    'toolbars',
-    'treeview',
+    "query",
+    "widgets",
+    "preferences",
+    "menu",
+    "infobar",
+    "dialogs",
+    "mapwindow",
+    "mapdisp",
+    "gselect",
+    "ghelp",
+    "prompt",
+    "goutput",
+    "forms",
+    "toolbars",
+    "treeview",
 ]

File diff ditekan karena terlalu besar
+ 740 - 735
gui/wxpython/gui_core/dialogs.py


File diff ditekan karena terlalu besar
+ 1375 - 1113
gui/wxpython/gui_core/forms.py


+ 308 - 283
gui/wxpython/gui_core/ghelp.py

@@ -26,6 +26,7 @@ import sys
 import six
 import wx
 from wx.html import HtmlWindow
+
 try:
     from wx.lib.agw.hyperlink import HyperLinkCtrl
 except ImportError:
@@ -41,8 +42,9 @@ import grass.script as grass
 from grass.exceptions import CalledModuleError
 
 # needed just for testing
-if __name__ == '__main__':
+if __name__ == "__main__":
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
 from core import globalvar
@@ -53,41 +55,34 @@ from core.debug import Debug
 
 
 class AboutWindow(wx.Frame):
-    """Create custom About Window
-    """
+    """Create custom About Window"""
 
-    def __init__(self, parent, size=(770, 460),
-                 title=_('About GRASS GIS'), **kwargs):
+    def __init__(self, parent, size=(770, 460), title=_("About GRASS GIS"), **kwargs):
         wx.Frame.__init__(
-            self,
-            parent=parent,
-            id=wx.ID_ANY,
-            title=title,
-            size=size,
-            **kwargs)
+            self, parent=parent, id=wx.ID_ANY, title=title, size=size, **kwargs
+        )
 
         self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
 
         # icon
         self.SetIcon(
-            wx.Icon(
-                os.path.join(
-                    globalvar.ICONDIR,
-                    'grass.ico'),
-                wx.BITMAP_TYPE_ICO))
+            wx.Icon(os.path.join(globalvar.ICONDIR, "grass.ico"), wx.BITMAP_TYPE_ICO)
+        )
 
         # notebook
         self.aboutNotebook = FormNotebook(self.panel, style=wx.BK_LEFT)
 
-        for title, win in ((_("Info"), self._pageInfo()),
-                           (_("Copyright"), self._pageCopyright()),
-                           (_("License"), self._pageLicense()),
-                           (_("Citation"), self._pageCitation()),
-                           (_("Authors"), self._pageCredit()),
-                           (_("Contributors"), self._pageContributors()),
-                           (_("Extra contributors"), self._pageContributors(extra=True)),
-                           (_("Translators"), self._pageTranslators()),
-                           (_("Translation status"), self._pageStats())):
+        for title, win in (
+            (_("Info"), self._pageInfo()),
+            (_("Copyright"), self._pageCopyright()),
+            (_("License"), self._pageLicense()),
+            (_("Citation"), self._pageCitation()),
+            (_("Authors"), self._pageCredit()),
+            (_("Contributors"), self._pageContributors()),
+            (_("Extra contributors"), self._pageContributors(extra=True)),
+            (_("Translators"), self._pageTranslators()),
+            (_("Translation status"), self._pageStats()),
+        ):
             self.aboutNotebook.AddPage(page=win, text=title)
         wx.CallAfter(self.aboutNotebook.SetSelection, 0)
         wx.CallAfter(self.aboutNotebook.Refresh)
@@ -103,10 +98,8 @@ class AboutWindow(wx.Frame):
         btnSizer.Add(self.btnClose, proportion=0, flag=wx.ALL, border=5)
 
         sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(self.aboutNotebook, proportion=1,
-                  flag=wx.EXPAND | wx.ALL, border=1)
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
+        sizer.Add(self.aboutNotebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=1)
+        sizer.Add(btnSizer, proportion=0, flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
 
         self.SetMinSize((400, 400))
 
@@ -127,59 +120,71 @@ class AboutWindow(wx.Frame):
         infoSizer = wx.BoxSizer(wx.VERTICAL)
         infoGridSizer = wx.GridBagSizer(vgap=5, hgap=5)
         logo = os.path.join(globalvar.ICONDIR, "grass-64x64.png")
-        logoBitmap = wx.StaticBitmap(infoTxt, wx.ID_ANY,
-                                     wx.Bitmap(name=logo, type=wx.BITMAP_TYPE_PNG))
-        infoSizer.Add(logoBitmap, proportion=0,
-                      flag=wx.ALL | wx.ALIGN_CENTER, border=20)
-
-        infoLabel = 'GRASS GIS %s' % vInfo.get('version', _('unknown version'))
-        if 'x86_64' in vInfo.get('build_platform', ''):
-            infoLabel += ' (64bit)'
-        info = StaticText(parent=infoTxt, id=wx.ID_ANY,
-                          label=infoLabel + os.linesep)
+        logoBitmap = wx.StaticBitmap(
+            infoTxt, wx.ID_ANY, wx.Bitmap(name=logo, type=wx.BITMAP_TYPE_PNG)
+        )
+        infoSizer.Add(
+            logoBitmap, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=20
+        )
+
+        infoLabel = "GRASS GIS %s" % vInfo.get("version", _("unknown version"))
+        if "x86_64" in vInfo.get("build_platform", ""):
+            infoLabel += " (64bit)"
+        info = StaticText(parent=infoTxt, id=wx.ID_ANY, label=infoLabel + os.linesep)
         info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
         info.SetForegroundColour(wx.Colour(35, 142, 35))
-        infoSizer.Add(info, proportion=0,
-                      flag=wx.BOTTOM | wx.ALIGN_CENTER, border=1)
+        infoSizer.Add(info, proportion=0, flag=wx.BOTTOM | wx.ALIGN_CENTER, border=1)
 
-        team = StaticText(parent=infoTxt, label=_grassDevTeam(1999) + '\n')
-        infoSizer.Add(team, proportion=0,
-                      flag=wx.BOTTOM | wx.ALIGN_CENTER, border=1)
+        team = StaticText(parent=infoTxt, label=_grassDevTeam(1999) + "\n")
+        infoSizer.Add(team, proportion=0, flag=wx.BOTTOM | wx.ALIGN_CENTER, border=1)
 
         row = 0
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label=_('Official GRASS site:')),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label=_("Official GRASS site:")),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
 
-        infoGridSizer.Add(HyperLinkCtrl(parent=infoTxt, id=wx.ID_ANY,
-                                        label='https://grass.osgeo.org'),
-                          pos=(row, 1),
-                          flag=wx.ALIGN_LEFT)
+        infoGridSizer.Add(
+            HyperLinkCtrl(
+                parent=infoTxt, id=wx.ID_ANY, label="https://grass.osgeo.org"
+            ),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         row += 2
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label='%s:' % _('Code Revision')),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label="%s:" % _("Code Revision")),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
 
-        infoGridSizer.Add(HyperLinkCtrl(parent=infoTxt, id=wx.ID_ANY,
-                                        label=vInfo.get('revision', '?'),
-                                        URL='https://github.com/OSGeo/grass.git'),
-                          pos=(row, 1),
-                          flag=wx.ALIGN_LEFT)
+        infoGridSizer.Add(
+            HyperLinkCtrl(
+                parent=infoTxt,
+                id=wx.ID_ANY,
+                label=vInfo.get("revision", "?"),
+                URL="https://github.com/OSGeo/grass.git",
+            ),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         row += 1
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label='%s:' % _('Build Date')),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label="%s:" % _("Build Date")),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
 
         infoGridSizer.Add(
             StaticText(
-                parent=infoTxt, id=wx.ID_ANY, label=vInfo.get(
-                    'build_date', '?')), pos=(
-                row, 1), flag=wx.ALIGN_LEFT)
+                parent=infoTxt, id=wx.ID_ANY, label=vInfo.get("build_date", "?")
+            ),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         # show only basic info
         # row += 1
@@ -195,48 +200,55 @@ class AboutWindow(wx.Frame):
         #                   flag = wx.ALIGN_LEFT)
 
         row += 2
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label='Python:'),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label="Python:"),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
 
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label=platform.python_version()),
-                          pos=(row, 1),
-                          flag=wx.ALIGN_LEFT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label=platform.python_version()),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         row += 1
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label='wxPython:'),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label="wxPython:"),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
 
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label=wx.__version__),
-                          pos=(row, 1),
-                          flag=wx.ALIGN_LEFT)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label=wx.__version__),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         infoGridSizer.AddGrowableCol(0)
         infoGridSizer.AddGrowableCol(1)
         infoSizer.Add(infoGridSizer, proportion=1, flag=wx.EXPAND)
 
         row += 2
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label="%s:" % _('Language')),
-                          pos=(row, 0),
-                          flag=wx.ALIGN_RIGHT)
-        self.langUsed = grass.gisenv().get('LANG', None)
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label="%s:" % _("Language")),
+            pos=(row, 0),
+            flag=wx.ALIGN_RIGHT,
+        )
+        self.langUsed = grass.gisenv().get("LANG", None)
         if not self.langUsed:
             import locale
+
             loc = locale.getdefaultlocale()
             if loc == (None, None):
-                self.langUsed = _('unknown')
+                self.langUsed = _("unknown")
             else:
-                self.langUsed = u'%s.%s' % (loc[0], loc[1])
-        infoGridSizer.Add(StaticText(parent=infoTxt, id=wx.ID_ANY,
-                                     label=self.langUsed),
-                          pos=(row, 1),
-                          flag=wx.ALIGN_LEFT)
+                self.langUsed = "%s.%s" % (loc[0], loc[1])
+        infoGridSizer.Add(
+            StaticText(parent=infoTxt, id=wx.ID_ANY, label=self.langUsed),
+            pos=(row, 1),
+            flag=wx.ALIGN_LEFT,
+        )
 
         infoTxt.SetSizer(infoSizer)
         infoSizer.Fit(infoTxt)
@@ -247,21 +259,25 @@ class AboutWindow(wx.Frame):
         """Copyright information"""
         copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
         if os.path.exists(copyfile):
-            copyrightFile = open(copyfile, 'r')
+            copyrightFile = open(copyfile, "r")
             copytext = copyrightFile.read()
             copyrightFile.close()
         else:
-            copytext = _('%s file missing') % 'COPYING'
+            copytext = _("%s file missing") % "COPYING"
 
         # put text into a scrolling panel
         copyrightwin = ScrolledPanel(self.aboutNotebook)
         copyrighttxt = TextCtrl(
-            copyrightwin, id=wx.ID_ANY, value=copytext,
-            style=wx.TE_MULTILINE | wx.TE_READONLY)
+            copyrightwin,
+            id=wx.ID_ANY,
+            value=copytext,
+            style=wx.TE_MULTILINE | wx.TE_READONLY,
+        )
         copyrightwin.SetAutoLayout(True)
         copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
-        copyrightwin.sizer.Add(copyrighttxt, proportion=1,
-                               flag=wx.EXPAND | wx.ALL, border=3)
+        copyrightwin.sizer.Add(
+            copyrighttxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+        )
         copyrightwin.SetSizer(copyrightwin.sizer)
         copyrightwin.Layout()
         copyrightwin.SetupScrolling()
@@ -272,20 +288,24 @@ class AboutWindow(wx.Frame):
         """Licence about"""
         licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
         if os.path.exists(licfile):
-            licenceFile = open(licfile, 'r')
-            license = ''.join(licenceFile.readlines())
+            licenceFile = open(licfile, "r")
+            license = "".join(licenceFile.readlines())
             licenceFile.close()
         else:
-            license = _('%s file missing') % 'GPL.TXT'
+            license = _("%s file missing") % "GPL.TXT"
         # put text into a scrolling panel
         licensewin = ScrolledPanel(self.aboutNotebook)
         licensetxt = TextCtrl(
-            licensewin, id=wx.ID_ANY, value=license,
-            style=wx.TE_MULTILINE | wx.TE_READONLY)
+            licensewin,
+            id=wx.ID_ANY,
+            value=license,
+            style=wx.TE_MULTILINE | wx.TE_READONLY,
+        )
         licensewin.SetAutoLayout(True)
         licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
-        licensewin.sizer.Add(licensetxt, proportion=1,
-                             flag=wx.EXPAND | wx.ALL, border=3)
+        licensewin.sizer.Add(
+            licensetxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+        )
         licensewin.SetSizer(licensewin.sizer)
         licensewin.Layout()
         licensewin.SetupScrolling()
@@ -297,21 +317,23 @@ class AboutWindow(wx.Frame):
         try:
             # import only when needed
             import grass.script as gscript
-            text = gscript.read_command('g.version', flags='x')
+
+            text = gscript.read_command("g.version", flags="x")
         except CalledModuleError as error:
-            text = _("Unable to provide citation suggestion,"
-                     " see GRASS GIS website instead."
-                     " The error was: {0}").format(error)
+            text = _(
+                "Unable to provide citation suggestion,"
+                " see GRASS GIS website instead."
+                " The error was: {0}"
+            ).format(error)
 
         # put text into a scrolling panel
         window = ScrolledPanel(self.aboutNotebook)
         stat_text = TextCtrl(
-            window, id=wx.ID_ANY, value=text,
-            style=wx.TE_MULTILINE | wx.TE_READONLY)
+            window, id=wx.ID_ANY, value=text, style=wx.TE_MULTILINE | wx.TE_READONLY
+        )
         window.SetAutoLayout(True)
         window.sizer = wx.BoxSizer(wx.VERTICAL)
-        window.sizer.Add(stat_text, proportion=1,
-                         flag=wx.EXPAND | wx.ALL, border=3)
+        window.sizer.Add(stat_text, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
         window.SetSizer(window.sizer)
         window.Layout()
         window.SetupScrolling()
@@ -323,19 +345,21 @@ class AboutWindow(wx.Frame):
         # credits
         authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
         if os.path.exists(authfile):
-            with codecs.open(authfile, encoding='utf-8', mode='r') as authorsFile:
-                authors = ''.join(authorsFile.readlines())
+            with codecs.open(authfile, encoding="utf-8", mode="r") as authorsFile:
+                authors = "".join(authorsFile.readlines())
         else:
-            authors = _('%s file missing') % 'AUTHORS'
+            authors = _("%s file missing") % "AUTHORS"
         authorwin = ScrolledPanel(self.aboutNotebook)
         authortxt = TextCtrl(
-            authorwin, id=wx.ID_ANY, value=authors,
-            style=wx.TE_MULTILINE | wx.TE_READONLY)
+            authorwin,
+            id=wx.ID_ANY,
+            value=authors,
+            style=wx.TE_MULTILINE | wx.TE_READONLY,
+        )
         authorwin.SetAutoLayout(True)
         authorwin.SetupScrolling()
         authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
-        authorwin.sizer.Add(authortxt, proportion=1,
-                            flag=wx.EXPAND | wx.ALL, border=3)
+        authorwin.sizer.Add(authortxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
         authorwin.SetSizer(authorwin.sizer)
         authorwin.Layout()
 
@@ -344,25 +368,27 @@ class AboutWindow(wx.Frame):
     def _pageContributors(self, extra=False):
         """Contributors info"""
         if extra:
-            contribfile = os.path.join(
-                os.getenv("GISBASE"),
-                "contributors_extra.csv")
+            contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
         else:
-            contribfile = os.path.join(
-                os.getenv("GISBASE"),
-                "contributors.csv")
+            contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
         if os.path.exists(contribfile):
-            contribFile = codecs.open(contribfile, encoding='utf-8', mode='r')
+            contribFile = codecs.open(contribfile, encoding="utf-8", mode="r")
             contribs = list()
             errLines = list()
             for line in contribFile.readlines()[1:]:
-                line = line.rstrip('\n')
+                line = line.rstrip("\n")
                 try:
                     if extra:
-                        name, email, country, rfc2_agreed = line.split(',')
+                        name, email, country, rfc2_agreed = line.split(",")
                     else:
-                        cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(
-                            ',')
+                        (
+                            cvs_id,
+                            name,
+                            email,
+                            country,
+                            osgeo_id,
+                            rfc2_agreed,
+                        ) = line.split(",")
                 except ValueError:
                     errLines.append(line)
                     continue
@@ -374,9 +400,13 @@ class AboutWindow(wx.Frame):
             contribFile.close()
 
             if errLines:
-                GError(parent=self, message=_("Error when reading file '%s'.") %
-                       contribfile + "\n\n" + _("Lines:") + " %s" %
-                       os.linesep.join(map(DecodeString, errLines)))
+                GError(
+                    parent=self,
+                    message=_("Error when reading file '%s'.") % contribfile
+                    + "\n\n"
+                    + _("Lines:")
+                    + " %s" % os.linesep.join(map(DecodeString, errLines)),
+                )
         else:
             contribs = None
 
@@ -387,39 +417,29 @@ class AboutWindow(wx.Frame):
 
         if not contribs:
             contribtxt = StaticText(
-                contribwin,
-                id=wx.ID_ANY,
-                label=_('%s file missing') %
-                contribfile)
-            contribwin.sizer.Add(contribtxt, proportion=1,
-                                 flag=wx.EXPAND | wx.ALL, border=3)
+                contribwin, id=wx.ID_ANY, label=_("%s file missing") % contribfile
+            )
+            contribwin.sizer.Add(
+                contribtxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+            )
         else:
             if extra:
-                items = (_('Name'), _('E-mail'), _('Country'))
+                items = (_("Name"), _("E-mail"), _("Country"))
             else:
-                items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
+                items = (_("Name"), _("E-mail"), _("Country"), _("OSGeo_ID"))
             contribBox = wx.FlexGridSizer(cols=len(items), vgap=5, hgap=5)
             for item in items:
-                text = StaticText(parent=contribwin, id=wx.ID_ANY,
-                                  label=item)
-                text.SetFont(
-                    wx.Font(
-                        10,
-                        wx.DEFAULT,
-                        wx.NORMAL,
-                        wx.BOLD,
-                        0,
-                        ""))
+                text = StaticText(parent=contribwin, id=wx.ID_ANY, label=item)
+                text.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
                 contribBox.Add(text)
             for vals in sorted(contribs, key=lambda x: x[0]):
                 for item in vals:
                     contribBox.Add(
-                        StaticText(
-                            parent=contribwin,
-                            id=wx.ID_ANY,
-                            label=item))
-            contribwin.sizer.Add(contribBox, proportion=1,
-                                 flag=wx.EXPAND | wx.ALL, border=3)
+                        StaticText(parent=contribwin, id=wx.ID_ANY, label=item)
+                    )
+            contribwin.sizer.Add(
+                contribBox, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+            )
 
         contribwin.SetSizer(contribwin.sizer)
         contribwin.Layout()
@@ -430,26 +450,30 @@ class AboutWindow(wx.Frame):
         """Translators info"""
         translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
         if os.path.exists(translatorsfile):
-            translatorsFile = codecs.open(translatorsfile, encoding='utf-8', mode='r')
+            translatorsFile = codecs.open(translatorsfile, encoding="utf-8", mode="r")
             translators = dict()
             errLines = list()
             for line in translatorsFile.readlines()[1:]:
-                line = line.rstrip('\n')
+                line = line.rstrip("\n")
                 try:
-                    name, email, languages = line.split(',')
+                    name, email, languages = line.split(",")
                 except ValueError:
                     errLines.append(line)
                     continue
-                for language in languages.split(' '):
+                for language in languages.split(" "):
                     if language not in translators:
                         translators[language] = list()
                     translators[language].append((name, email))
             translatorsFile.close()
 
             if errLines:
-                GError(parent=self, message=_("Error when reading file '%s'.") %
-                       translatorsfile + "\n\n" + _("Lines:") + " %s" %
-                       os.linesep.join(map(DecodeString, errLines)))
+                GError(
+                    parent=self,
+                    message=_("Error when reading file '%s'.") % translatorsfile
+                    + "\n\n"
+                    + _("Lines:")
+                    + " %s" % os.linesep.join(map(DecodeString, errLines)),
+                )
         else:
             translators = None
 
@@ -462,63 +486,56 @@ class AboutWindow(wx.Frame):
             translatorstxt = StaticText(
                 translatorswin,
                 id=wx.ID_ANY,
-                label=_('%s file missing') %
-                'translators.csv')
-            translatorswin.sizer.Add(translatorstxt, proportion=1,
-                                     flag=wx.EXPAND | wx.ALL, border=3)
+                label=_("%s file missing") % "translators.csv",
+            )
+            translatorswin.sizer.Add(
+                translatorstxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+            )
         else:
             translatorsBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
             languages = sorted(translators.keys())
-            tname = StaticText(parent=translatorswin, id=wx.ID_ANY,
-                               label=_('Name'))
+            tname = StaticText(parent=translatorswin, id=wx.ID_ANY, label=_("Name"))
             tname.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
             translatorsBox.Add(tname)
-            temail = StaticText(parent=translatorswin, id=wx.ID_ANY,
-                                label=_('E-mail'))
+            temail = StaticText(parent=translatorswin, id=wx.ID_ANY, label=_("E-mail"))
             temail.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
             translatorsBox.Add(temail)
-            tlang = StaticText(parent=translatorswin, id=wx.ID_ANY,
-                               label=_('Language'))
+            tlang = StaticText(parent=translatorswin, id=wx.ID_ANY, label=_("Language"))
             tlang.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
             translatorsBox.Add(tlang)
-            tnat = StaticText(parent=translatorswin, id=wx.ID_ANY,
-                              label=_('Nation'))
+            tnat = StaticText(parent=translatorswin, id=wx.ID_ANY, label=_("Nation"))
             tnat.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
             translatorsBox.Add(tnat)
             for lang in languages:
                 for translator in translators[lang]:
                     name, email = translator
                     translatorsBox.Add(
-                        StaticText(
-                            parent=translatorswin,
-                            id=wx.ID_ANY,
-                            label=name))
+                        StaticText(parent=translatorswin, id=wx.ID_ANY, label=name)
+                    )
                     translatorsBox.Add(
-                        StaticText(
-                            parent=translatorswin,
-                            id=wx.ID_ANY,
-                            label=email))
+                        StaticText(parent=translatorswin, id=wx.ID_ANY, label=email)
+                    )
                     translatorsBox.Add(
-                        StaticText(
-                            parent=translatorswin,
-                            id=wx.ID_ANY,
-                            label=lang))
+                        StaticText(parent=translatorswin, id=wx.ID_ANY, label=lang)
+                    )
                     flag = os.path.join(
-                        globalvar.ICONDIR, "flags", "%s.png" %
-                        lang.lower())
+                        globalvar.ICONDIR, "flags", "%s.png" % lang.lower()
+                    )
                     if os.path.exists(flag):
-                        flagBitmap = wx.StaticBitmap(translatorswin, wx.ID_ANY, wx.Bitmap(
-                                                     name=flag, type=wx.BITMAP_TYPE_PNG))
+                        flagBitmap = wx.StaticBitmap(
+                            translatorswin,
+                            wx.ID_ANY,
+                            wx.Bitmap(name=flag, type=wx.BITMAP_TYPE_PNG),
+                        )
                         translatorsBox.Add(flagBitmap)
                     else:
                         translatorsBox.Add(
-                            StaticText(
-                                parent=translatorswin,
-                                id=wx.ID_ANY,
-                                label=lang))
+                            StaticText(parent=translatorswin, id=wx.ID_ANY, label=lang)
+                        )
 
-            translatorswin.sizer.Add(translatorsBox, proportion=1,
-                                     flag=wx.EXPAND | wx.ALL, border=3)
+            translatorswin.sizer.Add(
+                translatorsBox, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+            )
 
         translatorswin.SetSizer(translatorswin.sizer)
         translatorswin.Layout()
@@ -529,15 +546,15 @@ class AboutWindow(wx.Frame):
         """Return string for the status of translation"""
         allStr = "%s :" % k.upper()
         try:
-            allStr += _("   %d translated" % v['good'])
+            allStr += _("   %d translated" % v["good"])
         except:
             pass
         try:
-            allStr += _("   %d fuzzy" % v['fuzzy'])
+            allStr += _("   %d fuzzy" % v["fuzzy"])
         except:
             pass
         try:
-            allStr += _("   %d untranslated" % v['bad'])
+            allStr += _("   %d untranslated" % v["bad"])
         except:
             pass
         return allStr
@@ -545,43 +562,46 @@ class AboutWindow(wx.Frame):
     def _langBox(self, par, k, v):
         """Return box"""
         langBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
-        tkey = StaticText(parent=par, id=wx.ID_ANY,
-                          label=k.upper())
+        tkey = StaticText(parent=par, id=wx.ID_ANY, label=k.upper())
         langBox.Add(tkey)
         try:
-            tgood = StaticText(parent=par, id=wx.ID_ANY,
-                               label=_("%d translated" % v['good']))
+            tgood = StaticText(
+                parent=par, id=wx.ID_ANY, label=_("%d translated" % v["good"])
+            )
             tgood.SetForegroundColour(wx.Colour(35, 142, 35))
             langBox.Add(tgood)
         except:
-            tgood = StaticText(parent=par, id=wx.ID_ANY,
-                               label="")
+            tgood = StaticText(parent=par, id=wx.ID_ANY, label="")
             langBox.Add(tgood)
         try:
-            tfuzzy = StaticText(parent=par, id=wx.ID_ANY,
-                                label=_("   %d fuzzy" % v['fuzzy']))
+            tfuzzy = StaticText(
+                parent=par, id=wx.ID_ANY, label=_("   %d fuzzy" % v["fuzzy"])
+            )
             tfuzzy.SetForegroundColour(wx.Colour(255, 142, 0))
             langBox.Add(tfuzzy)
         except:
-            tfuzzy = StaticText(parent=par, id=wx.ID_ANY,
-                                label="")
+            tfuzzy = StaticText(parent=par, id=wx.ID_ANY, label="")
             langBox.Add(tfuzzy)
         try:
-            tbad = StaticText(parent=par, id=wx.ID_ANY,
-                              label=_("   %d untranslated" % v['bad']))
+            tbad = StaticText(
+                parent=par, id=wx.ID_ANY, label=_("   %d untranslated" % v["bad"])
+            )
             tbad.SetForegroundColour(wx.Colour(255, 0, 0))
             langBox.Add(tbad)
         except:
-            tbad = StaticText(parent=par, id=wx.ID_ANY,
-                              label="")
+            tbad = StaticText(parent=par, id=wx.ID_ANY, label="")
             langBox.Add(tbad)
         return langBox
 
     def _langPanel(self, lang, js):
         """Create panel for each languages"""
-        text = self._langString(lang, js['total'])
+        text = self._langString(lang, js["total"])
         panel = wx.CollapsiblePane(
-            self.statswin, -1, label=text, style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE)
+            self.statswin,
+            -1,
+            label=text,
+            style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE,
+        )
         panel.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged)
         win = panel.GetPane()
         # TODO IT DOESN'T WORK
@@ -592,10 +612,9 @@ class AboutWindow(wx.Frame):
         # panel.Collapse(True)
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         for k, v in six.iteritems(js):
-            if k != 'total' and k != 'name':
+            if k != "total" and k != "name":
                 box = self._langBox(win, k, v)
-                pageSizer.Add(box, proportion=1,
-                              flag=wx.EXPAND | wx.ALL, border=3)
+                pageSizer.Add(box, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
 
         win.SetSizer(pageSizer)
         pageSizer.SetSizeHints(win)
@@ -614,6 +633,7 @@ class AboutWindow(wx.Frame):
         if os.path.exists(statsfile):
             statsFile = open(statsfile)
             import json
+
             jsStats = json.load(statsFile)
         else:
             jsStats = None
@@ -623,16 +643,16 @@ class AboutWindow(wx.Frame):
         if not jsStats:
             Debug.msg(5, _("File <%s> not found") % fname)
             statsSizer = wx.BoxSizer(wx.VERTICAL)
-            statstext = StaticText(self.statswin, id=wx.ID_ANY,
-                                   label=_('%s file missing') % fname)
-            statsSizer.Add(statstext, proportion=1,
-                           flag=wx.EXPAND | wx.ALL, border=3)
+            statstext = StaticText(
+                self.statswin, id=wx.ID_ANY, label=_("%s file missing") % fname
+            )
+            statsSizer.Add(statstext, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
         else:
-            languages = sorted(jsStats['langs'].keys())
+            languages = sorted(jsStats["langs"].keys())
 
             statsSizer = wx.BoxSizer(wx.VERTICAL)
             for lang in languages:
-                v = jsStats['langs'][lang]
+                v = jsStats["langs"][lang]
                 panel = self._langPanel(lang, v)
                 statsSizer.Add(panel)
 
@@ -661,8 +681,13 @@ class HelpFrame(wx.Dialog):
 
     def __init__(self, parent, id, title, size, file):
         wx.Dialog.__init__(
-            self, parent=parent, id=id, title=title, size=size,
-            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MINIMIZE_BOX)
+            self,
+            parent=parent,
+            id=id,
+            title=title,
+            size=size,
+            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MINIMIZE_BOX,
+        )
 
         sizer = wx.BoxSizer(wx.VERTICAL)
 
@@ -685,8 +710,7 @@ class HelpWindow(HtmlWindow):
     be integrated into the cmdPanel and options are obvious there.
     """
 
-    def __init__(self, parent, command, text, skipDescription,
-                 **kwargs):
+    def __init__(self, parent, command, text, skipDescription, **kwargs):
         """If command is given, the corresponding HTML help
         file will be presented, with all links pointing to absolute
         paths of local files.
@@ -712,8 +736,7 @@ class HelpWindow(HtmlWindow):
         if text is None:
             if skipDescription:
                 url = os.path.join(self.fspath, command + ".html")
-                self.fillContentsFromFile(url,
-                                          skipDescription=skipDescription)
+                self.fillContentsFromFile(url, skipDescription=skipDescription)
                 self.history.append(url)
                 self.loaded = True
             else:
@@ -726,7 +749,7 @@ class HelpWindow(HtmlWindow):
 
     def OnLinkClicked(self, linkinfo):
         url = linkinfo.GetHref()
-        if url[:4] != 'http':
+        if url[:4] != "http":
             url = os.path.join(self.fspath, url)
         self.history.append(url)
         self.historyIdx += 1
@@ -761,20 +784,20 @@ class HelpWindow(HtmlWindow):
                         if findALink is not None:
                             contents.append(
                                 aLink.sub(
-                                    findALink.group(1) +
-                                    self.fspath +
-                                    findALink.group(2),
-                                    line
+                                    findALink.group(1)
+                                    + self.fspath
+                                    + findALink.group(2),
+                                    line,
                                 )
                             )
                         findImgLink = imgLink.search(line)
                         if findImgLink is not None:
                             contents.append(
                                 imgLink.sub(
-                                    findImgLink.group(1) +
-                                    self.fspath +
-                                    findImgLink.group(2),
-                                    line
+                                    findImgLink.group(1)
+                                    + self.fspath
+                                    + findImgLink.group(2),
+                                    line,
                                 )
                             )
 
@@ -787,19 +810,17 @@ class HelpWindow(HtmlWindow):
 
 
 class HelpPanel(wx.Panel):
-
-    def __init__(self, parent, command="index", text=None,
-                 skipDescription=False, **kwargs):
+    def __init__(
+        self, parent, command="index", text=None, skipDescription=False, **kwargs
+    ):
         self.command = command
         wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
 
         self.content = HelpWindow(self, command, text, skipDescription)
 
-        self.btnNext = Button(parent=self, id=wx.ID_ANY,
-                                 label=_("&Next"))
+        self.btnNext = Button(parent=self, id=wx.ID_ANY, label=_("&Next"))
         self.btnNext.Enable(False)
-        self.btnPrev = Button(parent=self, id=wx.ID_ANY,
-                                 label=_("&Previous"))
+        self.btnPrev = Button(parent=self, id=wx.ID_ANY, label=_("&Previous"))
         self.btnPrev.Enable(False)
 
         self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
@@ -812,16 +833,12 @@ class HelpPanel(wx.Panel):
         sizer = wx.BoxSizer(wx.VERTICAL)
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
 
-        btnSizer.Add(self.btnPrev, proportion=0,
-                     flag=wx.ALL, border=5)
+        btnSizer.Add(self.btnPrev, proportion=0, flag=wx.ALL, border=5)
         btnSizer.Add(wx.Size(1, 1), proportion=1)
-        btnSizer.Add(self.btnNext, proportion=0,
-                     flag=wx.ALL, border=5)
+        btnSizer.Add(self.btnNext, proportion=0, flag=wx.ALL, border=5)
 
-        sizer.Add(self.content, proportion=1,
-                  flag=wx.EXPAND)
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.EXPAND)
+        sizer.Add(self.content, proportion=1, flag=wx.EXPAND)
+        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND)
 
         self.SetSizer(sizer)
         sizer.Fit(self)
@@ -840,10 +857,10 @@ class HelpPanel(wx.Panel):
             return fMan
 
         # check also addons
-        faMan = os.path.join(os.getenv('GRASS_ADDON_BASE'), "docs", "html",
-                             self.command + ".html")
-        if os.getenv('GRASS_ADDON_BASE') and \
-                os.path.isfile(faMan):
+        faMan = os.path.join(
+            os.getenv("GRASS_ADDON_BASE"), "docs", "html", self.command + ".html"
+        )
+        if os.getenv("GRASS_ADDON_BASE") and os.path.isfile(faMan):
             return faMan
 
         return None
@@ -894,47 +911,55 @@ def ShowAboutDialog(prgName, startYear):
     info = AboutDialogInfo()
 
     info.SetIcon(
-        wx.Icon(
-            os.path.join(
-                globalvar.ICONDIR,
-                'grass.ico'),
-            wx.BITMAP_TYPE_ICO))
+        wx.Icon(os.path.join(globalvar.ICONDIR, "grass.ico"), wx.BITMAP_TYPE_ICO)
+    )
     info.SetName(prgName)
-    info.SetWebSite('https://grass.osgeo.org')
+    info.SetWebSite("https://grass.osgeo.org")
     info.SetDescription(
-        _grassDevTeam(startYear) +
-        '\n\n' +
-        '\n'.join(
+        _grassDevTeam(startYear)
+        + "\n\n"
+        + "\n".join(
             textwrap.wrap(
-                'This program is free software under the GNU General Public License'
-                '(>=v2). Read the file COPYING that comes with GRASS for details.',
-                75)))
+                "This program is free software under the GNU General Public License"
+                "(>=v2). Read the file COPYING that comes with GRASS for details.",
+                75,
+            )
+        )
+    )
 
     AboutBox(info)
 
 
 def _grassDevTeam(start):
     try:
-        end = grass.version()['date']
+        end = grass.version()["date"]
     except KeyError:
         sys.stderr.write(_("Unable to get GRASS version\n"))
 
         from datetime import date
+
         end = date.today().year
 
-    return '%(c)s %(start)s-%(end)s by the GRASS Development Team' % {
-        'c': chr(169), 'start': start, 'end': end}
+    return "%(c)s %(start)s-%(end)s by the GRASS Development Team" % {
+        "c": chr(169),
+        "start": start,
+        "end": end,
+    }
 
 
 def main():
     """Test application (potentially useful as g.gui.gmanual)"""
     app = wx.App()
-    frame = HelpFrame(parent=None, id=wx.ID_ANY,
-                      title="Test help application",
-                      size=(600, 800), file=sys.argv[1])
+    frame = HelpFrame(
+        parent=None,
+        id=wx.ID_ANY,
+        title="Test help application",
+        size=(600, 800),
+        file=sys.argv[1],
+    )
     frame.Show()
     app.MainLoop()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()

+ 194 - 148
gui/wxpython/gui_core/goutput.py

@@ -27,18 +27,23 @@ from wx import stc
 from grass.pydispatch.signal import Signal
 
 # needed just for testing
-if __name__ == '__main__':
+if __name__ == "__main__":
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
 from core.gcmd import GError
-from core.gconsole   import GConsole, \
-    EVT_CMD_OUTPUT, EVT_CMD_PROGRESS, EVT_CMD_RUN, EVT_CMD_DONE, \
-    Notification
+from core.gconsole import (
+    GConsole,
+    EVT_CMD_OUTPUT,
+    EVT_CMD_PROGRESS,
+    EVT_CMD_RUN,
+    EVT_CMD_DONE,
+    Notification,
+)
 from core.globalvar import CheckWxVersion, wxPythonPhoenix
 from gui_core.prompt import GPromptSTC
-from gui_core.wrap import Button, ClearButton, ToggleButton, StaticText, \
-    StaticBox
+from gui_core.wrap import Button, ClearButton, ToggleButton, StaticText, StaticBox
 from core.settings import UserSettings
 
 
@@ -47,13 +52,19 @@ GC_PROMPT = 1
 
 
 class GConsoleWindow(wx.SplitterWindow):
-    """Create and manage output console for commands run by GUI.
-    """
-
-    def __init__(self, parent, giface, gconsole, menuModel=None, margin=False,
-                 style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
-                 gcstyle=GC_EMPTY,
-                 **kwargs):
+    """Create and manage output console for commands run by GUI."""
+
+    def __init__(
+        self,
+        parent,
+        giface,
+        gconsole,
+        menuModel=None,
+        margin=False,
+        style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
+        gcstyle=GC_EMPTY,
+        **kwargs,
+    ):
         """
         :param parent: gui parent
         :param gconsole: console logic
@@ -63,15 +74,13 @@ class GConsoleWindow(wx.SplitterWindow):
         :param gcstyle: GConsole style
                         (GC_EMPTY, GC_PROMPT to show command prompt)
         """
-        wx.SplitterWindow.__init__(
-            self, parent, id=wx.ID_ANY, style=style, **kwargs)
+        wx.SplitterWindow.__init__(self, parent, id=wx.ID_ANY, style=style, **kwargs)
         self.SetName("GConsole")
 
         self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY)
         self.panelProgress = wx.Panel(
-            parent=self.panelOutput,
-            id=wx.ID_ANY,
-            name='progressPanel')
+            parent=self.panelOutput, id=wx.ID_ANY, name="progressPanel"
+        )
         self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY)
         # initialize variables
         self.parent = parent  # GMFrame | CmdPanel | ?
@@ -89,9 +98,14 @@ class GConsoleWindow(wx.SplitterWindow):
         self.contentChanged = Signal("GConsoleWindow.contentChanged")
 
         # progress bar
-        self.progressbar = wx.Gauge(parent=self.panelProgress, id=wx.ID_ANY,
-                                    range=100, pos=(110, 50), size=(-1, 25),
-                                    style=wx.GA_HORIZONTAL)
+        self.progressbar = wx.Gauge(
+            parent=self.panelProgress,
+            id=wx.ID_ANY,
+            range=100,
+            pos=(110, 50),
+            size=(-1, 25),
+            style=wx.GA_HORIZONTAL,
+        )
         self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
         self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
         self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun)
@@ -104,10 +118,8 @@ class GConsoleWindow(wx.SplitterWindow):
 
         # text control for command output
         self.cmdOutput = GStc(
-            parent=self.panelOutput,
-            id=wx.ID_ANY,
-            margin=margin,
-            wrap=None)
+            parent=self.panelOutput, id=wx.ID_ANY, margin=margin, wrap=None
+        )
 
         # command prompt
         # move to the if below
@@ -115,8 +127,9 @@ class GConsoleWindow(wx.SplitterWindow):
         self.cmdPrompt = GPromptSTC(
             parent=self, giface=giface, menuModel=self._menuModel
         )
-        self.cmdPrompt.promptRunCmd.connect(lambda cmd:
-                                            self._gconsole.RunCmd(command=cmd))
+        self.cmdPrompt.promptRunCmd.connect(
+            lambda cmd: self._gconsole.RunCmd(command=cmd)
+        )
         self.cmdPrompt.showNotification.connect(self.showNotification)
 
         if not self._gcstyle & GC_PROMPT:
@@ -125,13 +138,12 @@ class GConsoleWindow(wx.SplitterWindow):
         if self._gcstyle & GC_PROMPT:
             cmdLabel = _("Command prompt")
             self.outputBox = StaticBox(
-                parent=self.panelOutput,
-                id=wx.ID_ANY,
-                label=" %s " %
-                _("Output window"))
+                parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % _("Output window")
+            )
 
-            self.cmdBox = StaticBox(parent=self.panelOutput, id=wx.ID_ANY,
-                                    label=" %s " % cmdLabel)
+            self.cmdBox = StaticBox(
+                parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % cmdLabel
+            )
 
         # buttons
         self.btnOutputClear = ClearButton(parent=self.panelOutput)
@@ -139,17 +151,21 @@ class GConsoleWindow(wx.SplitterWindow):
         self.btnCmdClear = ClearButton(parent=self.panelOutput)
         self.btnCmdClear.SetToolTip(_("Clear command prompt content"))
         self.btnOutputSave = Button(parent=self.panelOutput, id=wx.ID_SAVE)
-        self.btnOutputSave.SetToolTip(
-            _("Save output window content to the file"))
+        self.btnOutputSave.SetToolTip(_("Save output window content to the file"))
         self.btnCmdAbort = Button(parent=self.panelProgress, id=wx.ID_STOP)
         self.btnCmdAbort.SetToolTip(_("Abort running command"))
         self.btnCmdProtocol = ToggleButton(
             parent=self.panelOutput,
             id=wx.ID_ANY,
             label=_("&Log file"),
-            size=self.btnCmdClear.GetSize())
-        self.btnCmdProtocol.SetToolTip(_("Toggle to save list of executed commands into "
-                                         "a file; content saved when switching off."))
+            size=self.btnCmdClear.GetSize(),
+        )
+        self.btnCmdProtocol.SetToolTip(
+            _(
+                "Toggle to save list of executed commands into "
+                "a file; content saved when switching off."
+            )
+        )
         self.cmdFileProtocol = None
 
         if not self._gcstyle & GC_PROMPT:
@@ -178,20 +194,25 @@ class GConsoleWindow(wx.SplitterWindow):
 
         if self._gcstyle & GC_PROMPT:
             promptSizer = wx.BoxSizer(wx.VERTICAL)
-            promptSizer.Add(self.cmdPrompt, proportion=1,
-                            flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
-                            border=3)
+            promptSizer.Add(
+                self.cmdPrompt,
+                proportion=1,
+                flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
+                border=3,
+            )
             helpText = StaticText(
-                self.panelPrompt, id=wx.ID_ANY,
-                label="Press Tab to display command help, Ctrl+Space to autocomplete")
+                self.panelPrompt,
+                id=wx.ID_ANY,
+                label="Press Tab to display command help, Ctrl+Space to autocomplete",
+            )
             helpText.SetForegroundColour(
-                wx.SystemSettings.GetColour(
-                    wx.SYS_COLOUR_GRAYTEXT))
-            promptSizer.Add(helpText,
-                            proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
+                wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
+            )
+            promptSizer.Add(helpText, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
 
-        self.outputSizer.Add(self.cmdOutput, proportion=1,
-                             flag=wx.EXPAND | wx.ALL, border=3)
+        self.outputSizer.Add(
+            self.cmdOutput, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
+        )
         if self._gcstyle & GC_PROMPT:
             proportion = 1
         else:
@@ -202,40 +223,54 @@ class GConsoleWindow(wx.SplitterWindow):
             self.btnOutputClear,
             proportion=proportion,
             flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-            border=5)
+            border=5,
+        )
 
-        outBtnSizer.Add(self.btnOutputSave, proportion=proportion,
-                        flag=wx.RIGHT | wx.BOTTOM, border=5)
+        outBtnSizer.Add(
+            self.btnOutputSave,
+            proportion=proportion,
+            flag=wx.RIGHT | wx.BOTTOM,
+            border=5,
+        )
 
         cmdBtnSizer.Add(
             self.btnCmdProtocol,
             proportion=1,
-            flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-            border=5)
-        cmdBtnSizer.Add(self.btnCmdClear, proportion=1,
-                        flag=wx.ALIGN_CENTER | wx.RIGHT | wx.BOTTOM, border=5)
-        progressSizer.Add(self.btnCmdAbort, proportion=0,
-                          flag=wx.ALL | wx.ALIGN_CENTER, border=5)
+            flag=wx.ALIGN_CENTER
+            | wx.ALIGN_CENTER_VERTICAL
+            | wx.LEFT
+            | wx.RIGHT
+            | wx.BOTTOM,
+            border=5,
+        )
+        cmdBtnSizer.Add(
+            self.btnCmdClear,
+            proportion=1,
+            flag=wx.ALIGN_CENTER | wx.RIGHT | wx.BOTTOM,
+            border=5,
+        )
+        progressSizer.Add(
+            self.btnCmdAbort, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=5
+        )
         progressSizer.Add(
             self.progressbar,
             proportion=1,
             flag=wx.ALIGN_CENTER | wx.RIGHT | wx.TOP | wx.BOTTOM,
-            border=5)
+            border=5,
+        )
 
         self.panelProgress.SetSizer(progressSizer)
         progressSizer.Fit(self.panelProgress)
 
-        btnSizer.Add(outBtnSizer, proportion=1,
-                     flag=wx.ALL | wx.ALIGN_CENTER, border=5)
+        btnSizer.Add(outBtnSizer, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER, border=5)
         btnSizer.Add(
             cmdBtnSizer,
             proportion=1,
             flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT,
-            border=5)
-        self.outputSizer.Add(self.panelProgress, proportion=0,
-                             flag=wx.EXPAND)
-        self.outputSizer.Add(btnSizer, proportion=0,
-                             flag=wx.EXPAND)
+            border=5,
+        )
+        self.outputSizer.Add(self.panelProgress, proportion=0, flag=wx.EXPAND)
+        self.outputSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND)
 
         self.outputSizer.Fit(self)
         self.outputSizer.SetSizeHints(self)
@@ -273,8 +308,9 @@ class GConsoleWindow(wx.SplitterWindow):
 
         return self.panelOutput
 
-    def WriteLog(self, text, style=None, wrap=None,
-                 notification=Notification.HIGHLIGHT):
+    def WriteLog(
+        self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT
+    ):
         """Generic method for writing log message in
         given style.
 
@@ -304,7 +340,7 @@ class GConsoleWindow(wx.SplitterWindow):
             # fill space
             if len(line) < self.lineWidth:
                 diff = self.lineWidth - len(line)
-                line += diff * ' '
+                line += diff * " "
 
             self.cmdOutput.AddTextWrapped(line, wrap=wrap)  # adds '\n'
 
@@ -314,15 +350,14 @@ class GConsoleWindow(wx.SplitterWindow):
             try:
                 self.cmdOutput.StartStyling(p1)
             except TypeError:
-                self.cmdOutput.StartStyling(p1, 0xff)
+                self.cmdOutput.StartStyling(p1, 0xFF)
             self.cmdOutput.SetStyling(p2 - p1, style)
 
         self.cmdOutput.EnsureCaretVisible()
 
         self.contentChanged.emit(notification=notification)
 
-    def WriteCmdLog(self, text, pid=None,
-                    notification=Notification.MAKE_VISIBLE):
+    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
         """Write message in selected style
 
         :param text: message to be printed
@@ -330,21 +365,26 @@ class GConsoleWindow(wx.SplitterWindow):
         :param switchPage: True to switch page
         """
         if pid:
-            text = '(' + str(pid) + ') ' + text
+            text = "(" + str(pid) + ") " + text
         self.WriteLog(
-            text,
-            style=self.cmdOutput.StyleCommand,
-            notification=notification)
+            text, style=self.cmdOutput.StyleCommand, notification=notification
+        )
 
     def WriteWarning(self, text):
         """Write message in warning style"""
-        self.WriteLog(text, style=self.cmdOutput.StyleWarning,
-                      notification=Notification.MAKE_VISIBLE)
+        self.WriteLog(
+            text,
+            style=self.cmdOutput.StyleWarning,
+            notification=Notification.MAKE_VISIBLE,
+        )
 
     def WriteError(self, text):
         """Write message in error style"""
-        self.WriteLog(text, style=self.cmdOutput.StyleError,
-                      notification=Notification.MAKE_VISIBLE)
+        self.WriteLog(
+            text,
+            style=self.cmdOutput.StyleError,
+            notification=Notification.MAKE_VISIBLE,
+        )
 
     def OnOutputClear(self, event):
         """Clear content of output window"""
@@ -364,16 +404,17 @@ class GConsoleWindow(wx.SplitterWindow):
             text = self.cmdOutput.GetText()
 
         # add newline if needed
-        if len(text) > 0 and text[-1] != '\n':
-            text += '\n'
+        if len(text) > 0 and text[-1] != "\n":
+            text += "\n"
 
         dlg = wx.FileDialog(
-            self, message=_("Save file as..."),
+            self,
+            message=_("Save file as..."),
             defaultFile="grass_cmd_output.txt",
-            wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
-            {'txt': _("Text files"),
-             'files': _("Files")},
-            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
+            wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*")
+            % {"txt": _("Text files"), "files": _("Files")},
+            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
+        )
 
         # Show the dialog and retrieve the user response. If it is the OK response,
         # process the data.
@@ -385,9 +426,9 @@ class GConsoleWindow(wx.SplitterWindow):
                 output.write(text)
             except IOError as e:
                 GError(
-                    _("Unable to write file '%(path)s'.\n\nDetails: %(error)s") % {
-                        'path': path,
-                        'error': e})
+                    _("Unable to write file '%(path)s'.\n\nDetails: %(error)s")
+                    % {"path": path, "error": e}
+                )
             finally:
                 output.close()
             message = _("Command output saved into '%s'") % path
@@ -403,11 +444,11 @@ class GConsoleWindow(wx.SplitterWindow):
         """
         if copy:
             self.cmdPrompt.Bind(
-                stc.EVT_STC_PAINTED,
-                self.cmdPrompt.OnTextSelectionChanged)
+                stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged
+            )
             self.cmdOutput.Bind(
-                stc.EVT_STC_PAINTED,
-                self.cmdOutput.OnTextSelectionChanged)
+                stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged
+            )
         else:
             self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED)
             self.cmdOutput.Unbind(stc.EVT_STC_PAINTED)
@@ -422,7 +463,7 @@ class GConsoleWindow(wx.SplitterWindow):
 
         self.cmdOutput.AddStyledMessage(message, type)
 
-        if event.type in ('warning', 'error'):
+        if event.type in ("warning", "error"):
             self.contentChanged.emit(notification=Notification.MAKE_VISIBLE)
         else:
             self.contentChanged.emit(notification=Notification.HIGHLIGHT)
@@ -440,12 +481,15 @@ class GConsoleWindow(wx.SplitterWindow):
         try:
             with open(self.cmdFileProtocol, "a") as output:
                 cmds = self.cmdPrompt.GetCommands()
-                output.write('\n'.join(cmds))
+                output.write("\n".join(cmds))
                 if len(cmds) > 0:
-                    output.write('\n')
+                    output.write("\n")
         except IOError as e:
-            GError(_("Unable to write file '{filePath}'.\n\nDetails: {error}").format(
-                filePath=self.cmdFileProtocol, error=e))
+            GError(
+                _("Unable to write file '{filePath}'.\n\nDetails: {error}").format(
+                    filePath=self.cmdFileProtocol, error=e
+                )
+            )
 
         self.showNotification.emit(
             message=_("Command log saved to '{}'".format(self.cmdFileProtocol))
@@ -463,12 +507,13 @@ class GConsoleWindow(wx.SplitterWindow):
             self.cmdPrompt.ClearCommands()
             # ask for the file
             dlg = wx.FileDialog(
-                self, message=_("Save file as..."),
+                self,
+                message=_("Save file as..."),
                 defaultFile="grass_cmd_log.txt",
-                wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
-                {'txt': _("Text files"),
-                 'files': _("Files")},
-                style=wx.FD_SAVE)
+                wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*")
+                % {"txt": _("Text files"), "files": _("Files")},
+                style=wx.FD_SAVE,
+            )
             if dlg.ShowModal() == wx.ID_OK:
                 self.cmdFileProtocol = dlg.GetPath()
             else:
@@ -485,8 +530,7 @@ class GConsoleWindow(wx.SplitterWindow):
         event.Skip()
 
     def OnCmdDone(self, event):
-        """Command done (or aborted)
-        """
+        """Command done (or aborted)"""
         self.progressbar.SetValue(0)  # reset progress bar on '0%'
         wx.CallLater(100, self._hideProgress)
         event.Skip()
@@ -549,8 +593,9 @@ class GStc(stc.StyledTextCtrl):
         self.SetTabWidth(4)
         self.SetUseTabs(False)
         self.UsePopUp(True)
-        self.SetSelBackground(True,
-            wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
+        self.SetSelBackground(
+            True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+        )
         self.SetUseHorizontalScrollBar(True)
 
         #
@@ -569,17 +614,11 @@ class GStc(stc.StyledTextCtrl):
         """Set styles for styled text output windows with type face
         and point size selected by user (Courier New 10 is default)"""
 
-        typeface = UserSettings.Get(
-            group='appearance',
-            key='outputfont',
-            subkey='type')
+        typeface = UserSettings.Get(group="appearance", key="outputfont", subkey="type")
         if typeface == "":
             typeface = "Courier New"
 
-        typesize = UserSettings.Get(
-            group='appearance',
-            key='outputfont',
-            subkey='size')
+        typesize = UserSettings.Get(group="appearance", key="outputfont", subkey="size")
         if typesize is None or int(typesize) <= 0:
             typesize = 10
         typesize = float(typesize)
@@ -654,14 +693,14 @@ class GStc(stc.StyledTextCtrl):
         self.SetReadOnly(False)
 
         if wrap:
-            txt = textwrap.fill(txt, wrap) + '\n'
+            txt = textwrap.fill(txt, wrap) + "\n"
         else:
-            if txt[-1] != '\n':
-                txt += '\n'
+            if txt[-1] != "\n":
+                txt += "\n"
 
-        if '\r' in txt:
+        if "\r" in txt:
             self.linePos = -1
-            for seg in txt.split('\r'):
+            for seg in txt.split("\r"):
                 if self.linePos > -1:
                     self.SetCurrentPos(self.linePos)
                     self.ReplaceSelection(seg)
@@ -685,38 +724,38 @@ class GStc(stc.StyledTextCtrl):
                       'warning', 'error' or None
         """
         # message prefix
-        if style == 'warning':
-            message = 'WARNING: ' + message
-        elif style == 'error':
-            message = 'ERROR: ' + message
+        if style == "warning":
+            message = "WARNING: " + message
+        elif style == "error":
+            message = "ERROR: " + message
 
         p1 = self.GetEndStyled()
         self.GotoPos(p1)
 
         # is this still needed?
-        if '\b' in message:
+        if "\b" in message:
             if self.linePos < 0:
                 self.linePos = p1
-            last_c = ''
+            last_c = ""
             for c in message:
-                if c == '\b':
+                if c == "\b":
                     self.linePos -= 1
                 else:
-                    if c == '\r':
+                    if c == "\r":
                         pos = self.GetCurLine()[1]
                         # self.SetCurrentPos(pos)
                     else:
                         self.SetCurrentPos(self.linePos)
                     self.ReplaceSelection(c)
                     self.linePos = self.GetCurrentPos()
-                    if c != ' ':
+                    if c != " ":
                         last_c = c
-            if last_c not in ('0123456789'):
-                self.AddTextWrapped('\n', wrap=None)
+            if last_c not in ("0123456789"):
+                self.AddTextWrapped("\n", wrap=None)
                 self.linePos = -1
         else:
             self.linePos = -1  # don't force position
-            if '\n' not in message:
+            if "\n" not in message:
                 self.AddTextWrapped(message, wrap=60)
             else:
                 self.AddTextWrapped(message, wrap=None)
@@ -726,13 +765,13 @@ class GStc(stc.StyledTextCtrl):
             try:
                 self.StartStyling(p1)
             except TypeError:
-                self.StartStyling(p1, 0xff)
+                self.StartStyling(p1, 0xFF)
 
-            if style == 'error':
+            if style == "error":
                 self.SetStyling(p2 - p1, self.StyleError)
-            elif style == 'warning':
+            elif style == "warning":
                 self.SetStyling(p2 - p1, self.StyleWarning)
-            elif style == 'message':
+            elif style == "message":
                 self.SetStyling(p2 - p1, self.StyleMessage)
             else:  # unknown
                 self.SetStyling(p2 - p1, self.StyleUnknown)
@@ -743,25 +782,31 @@ class GStc(stc.StyledTextCtrl):
 class GConsoleFrame(wx.Frame):
     """Standalone GConsole for testing only"""
 
-    def __init__(self, parent, id=wx.ID_ANY, title="GConsole Test Frame",
-                 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, **kwargs):
+    def __init__(
+        self,
+        parent,
+        id=wx.ID_ANY,
+        title="GConsole Test Frame",
+        style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
+        **kwargs,
+    ):
         wx.Frame.__init__(self, parent=parent, id=id, title=title, style=style)
 
         panel = wx.Panel(self, id=wx.ID_ANY)
 
         from lmgr.menudata import LayerManagerMenuData
+
         menuTreeBuilder = LayerManagerMenuData()
         self.gconsole = GConsole(guiparent=self)
-        self.goutput = GConsoleWindow(parent=panel, gconsole=self.gconsole,
-                                      menuModel=menuTreeBuilder.GetModel(),
-                                      gcstyle=GC_PROMPT)
+        self.goutput = GConsoleWindow(
+            parent=panel,
+            gconsole=self.gconsole,
+            menuModel=menuTreeBuilder.GetModel(),
+            gcstyle=GC_PROMPT,
+        )
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(
-            self.goutput,
-            proportion=1,
-            flag=wx.EXPAND,
-            border=0)
+        mainSizer.Add(self.goutput, proportion=1, flag=wx.EXPAND, border=0)
 
         panel.SetSizer(mainSizer)
         mainSizer.Fit(panel)
@@ -774,5 +819,6 @@ def testGConsole():
     frame.Show()
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     testGConsole()

File diff ditekan karena terlalu besar
+ 960 - 855
gui/wxpython/gui_core/gselect.py


+ 9 - 4
gui/wxpython/gui_core/infobar.py

@@ -19,6 +19,7 @@ This program is free software under the GNU General Public License
 import sys
 import wx
 import wx.aui
+
 try:
     import wx.lib.agw.infobar as IB
 except ImportError:
@@ -30,14 +31,16 @@ def GetCloseButtonBitmap(win, size, colBg, flags=0):
     of wx.RendererNative in certain wx versions.
     See https://github.com/wxWidgets/Phoenix/issues/1425."""
     renderer = wx.RendererNative.Get()
-    if hasattr(renderer, 'DrawTitleBarBitmap'):
+    if hasattr(renderer, "DrawTitleBarBitmap"):
         bmp = wx.Bitmap(*size)
         dc = wx.MemoryDC()
         dc.SelectObject(bmp)
         dc.SetBackground(wx.Brush(colBg))
         dc.Clear()
 
-        wx.RendererNative.Get().DrawTitleBarBitmap(win, dc, wx.Rect(size), wx.TITLEBAR_BUTTON_CLOSE, flags)
+        wx.RendererNative.Get().DrawTitleBarBitmap(
+            win, dc, wx.Rect(size), wx.TITLEBAR_BUTTON_CLOSE, flags
+        )
         dc.SelectObject(wx.NullBitmap)
     else:
         bmp = wx.ArtProvider.GetBitmap(wx.ART_CLOSE, wx.ART_BUTTON)
@@ -122,7 +125,7 @@ class InfoBar(IB.InfoBar):
         button.SetBackgroundColour(self._background_color)
         button.SetForegroundColour(self._foreground_color)
 
-        if wx.Platform == '__WXMAC__':
+        if wx.Platform == "__WXMAC__":
             # smaller buttons look better in the(narrow)info bar under OS X
             button.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
 
@@ -132,7 +135,9 @@ class InfoBar(IB.InfoBar):
             self.subSizerButtons.Add(self._button, wx.SizerFlags().Centre().Border())
             self._button.Show()
         else:
-            self.subSizerButtons.Insert(num_items - 1, button, wx.SizerFlags().Centre().Border())
+            self.subSizerButtons.Insert(
+                num_items - 1, button, wx.SizerFlags().Centre().Border()
+            )
 
         if self.IsShown():
             self.UpdateParent()

+ 142 - 121
gui/wxpython/gui_core/mapdisp.py

@@ -57,9 +57,16 @@ class MapFrameBase(wx.Frame):
     AUI manager is stored in \c self._mgr.
     """
 
-    def __init__(self, parent=None, id=wx.ID_ANY, title='',
-                 style=wx.DEFAULT_FRAME_STYLE,
-                 auimgr=None, name='', **kwargs):
+    def __init__(
+        self,
+        parent=None,
+        id=wx.ID_ANY,
+        title="",
+        style=wx.DEFAULT_FRAME_STYLE,
+        auimgr=None,
+        name="",
+        **kwargs,
+    ):
         """
 
         .. warning::
@@ -77,14 +84,7 @@ class MapFrameBase(wx.Frame):
 
         self.parent = parent
 
-        wx.Frame.__init__(
-            self,
-            parent,
-            id,
-            title,
-            style=style,
-            name=name,
-            **kwargs)
+        wx.Frame.__init__(self, parent, id, title, style=style, name=name, **kwargs)
 
         #
         # set the size & system icon
@@ -94,10 +94,9 @@ class MapFrameBase(wx.Frame):
 
         self.SetIcon(
             wx.Icon(
-                os.path.join(
-                    globalvar.ICONDIR,
-                    'grass_map.ico'),
-                wx.BITMAP_TYPE_ICO))
+                os.path.join(globalvar.ICONDIR, "grass_map.ico"), wx.BITMAP_TYPE_ICO
+            )
+        )
 
         # toolbars
         self.toolbars = {}
@@ -107,6 +106,7 @@ class MapFrameBase(wx.Frame):
         #
         if auimgr is None:
             from wx.aui import AuiManager
+
             self._mgr = AuiManager(self)
         else:
             self._mgr = auimgr
@@ -122,8 +122,8 @@ class MapFrameBase(wx.Frame):
         # set accelerator table (fullscreen, close window)
         shortcuts_table = (
             (self.OnFullScreen, wx.ACCEL_NORMAL, wx.WXK_F11),
-            (self.OnCloseWindow, wx.ACCEL_CTRL, ord('W')),
-            (self.OnRender, wx.ACCEL_CTRL, ord('R')),
+            (self.OnCloseWindow, wx.ACCEL_CTRL, ord("W")),
+            (self.OnRender, wx.ACCEL_CTRL, ord("R")),
             (self.OnRender, wx.ACCEL_NORMAL, wx.WXK_F5),
         )
         accelTable = []
@@ -135,11 +135,12 @@ class MapFrameBase(wx.Frame):
         self.SetAcceleratorTable(wx.AcceleratorTable(accelTable))
 
     def _initMap(self, Map):
-        """Initialize map display, set dimensions and map region
-        """
-        if not grass.find_program('g.region', '--help'):
-            sys.exit(_("GRASS module '%s' not found. Unable to start map "
-                       "display window.") % 'g.region')
+        """Initialize map display, set dimensions and map region"""
+        if not grass.find_program("g.region", "--help"):
+            sys.exit(
+                _("GRASS module '%s' not found. Unable to start map " "display window.")
+                % "g.region"
+            )
 
         Debug.msg(2, "MapFrame._initMap():")
         Map.ChangeMapSize(self.GetClientSize())
@@ -155,7 +156,7 @@ class MapFrameBase(wx.Frame):
         self.SetSize((wf + dw, hf + dh))
 
     def _onToggleTool(self, id):
-        if self._toolSwitcher.IsToolInGroup(id, 'mouseUse'):
+        if self._toolSwitcher.IsToolInGroup(id, "mouseUse"):
             self.GetWindow().UnregisterAllHandlers()
 
     def OnSize(self, event):
@@ -205,23 +206,34 @@ class MapFrameBase(wx.Frame):
         # screen X region problem
         # user should specify ppm
         dc = wx.ScreenDC()
-        dpSizePx = wx.DisplaySize()   # display size in pixels
+        dpSizePx = wx.DisplaySize()  # display size in pixels
         dpSizeMM = wx.DisplaySizeMM()  # display size in mm (system)
         dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4)  # inches
         sysPpi = dc.GetPPI()
-        comPpi = (dpSizePx[0] / dpSizeIn[0],
-                  dpSizePx[1] / dpSizeIn[1])
+        comPpi = (dpSizePx[0] / dpSizeIn[0], dpSizePx[1] / dpSizeIn[1])
 
-        ppi = comPpi                  # pixel per inch
-        ppm = ((ppi[0] / 2.54) * 100,  # pixel per meter
-               (ppi[1] / 2.54) * 100)
+        ppi = comPpi  # pixel per inch
+        ppm = ((ppi[0] / 2.54) * 100, (ppi[1] / 2.54) * 100)  # pixel per meter
 
-        Debug.msg(4, "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
-                  "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" %
-                  (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
-                   dpSizeIn[0], dpSizeIn[1],
-                   sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
-                   ppm[0], ppm[1]))
+        Debug.msg(
+            4,
+            "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
+            "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f"
+            % (
+                dpSizePx[0],
+                dpSizePx[1],
+                dpSizeMM[0],
+                dpSizeMM[1],
+                dpSizeIn[0],
+                dpSizeIn[1],
+                sysPpi[0],
+                sysPpi[1],
+                comPpi[0],
+                comPpi[1],
+                ppm[0],
+                ppm[1],
+            ),
+        )
 
         return ppm
 
@@ -235,16 +247,15 @@ class MapFrameBase(wx.Frame):
             map = self.Map
 
         region = self.Map.region
-        dEW = value * (region['cols'] / self.GetPPM()[0])
-        dNS = value * (region['rows'] / self.GetPPM()[1])
-        region['n'] = region['center_northing'] + dNS / 2.
-        region['s'] = region['center_northing'] - dNS / 2.
-        region['w'] = region['center_easting'] - dEW / 2.
-        region['e'] = region['center_easting'] + dEW / 2.
+        dEW = value * (region["cols"] / self.GetPPM()[0])
+        dNS = value * (region["rows"] / self.GetPPM()[1])
+        region["n"] = region["center_northing"] + dNS / 2.0
+        region["s"] = region["center_northing"] - dNS / 2.0
+        region["w"] = region["center_easting"] - dEW / 2.0
+        region["e"] = region["center_easting"] + dEW / 2.0
 
         # add to zoom history
-        self.GetWindow().ZoomHistory(region['n'], region['s'],
-                                     region['e'], region['w'])
+        self.GetWindow().ZoomHistory(region["n"], region["s"], region["e"], region["w"])
 
     def GetMapScale(self, map=None):
         """Get current map scale
@@ -257,19 +268,22 @@ class MapFrameBase(wx.Frame):
         region = map.region
         ppm = self.GetPPM()
 
-        heightCm = region['rows'] / ppm[1] * 100
-        widthCm = region['cols'] / ppm[0] * 100
+        heightCm = region["rows"] / ppm[1] * 100
+        widthCm = region["cols"] / ppm[0] * 100
 
-        Debug.msg(4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" %
-                  (widthCm, heightCm))
+        Debug.msg(
+            4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" % (widthCm, heightCm)
+        )
 
-        xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
-        yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
-        scale = (xscale + yscale) / 2.
+        xscale = (region["e"] - region["w"]) / (region["cols"] / ppm[0])
+        yscale = (region["n"] - region["s"]) / (region["rows"] / ppm[1])
+        scale = (xscale + yscale) / 2.0
 
         Debug.msg(
-            3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" %
-            (xscale, yscale, scale))
+            3,
+            "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f"
+            % (xscale, yscale, scale),
+        )
 
         return scale
 
@@ -297,8 +311,7 @@ class MapFrameBase(wx.Frame):
         raise NotImplementedError("GetMapToolbar")
 
     def GetToolbar(self, name):
-        """Returns toolbar if exists and is active, else None.
-        """
+        """Returns toolbar if exists and is active, else None."""
         if name in self.toolbars and self.toolbars[name].IsShown():
             return self.toolbars[name]
 
@@ -319,13 +332,12 @@ class MapFrameBase(wx.Frame):
         return self.mapWindowProperties.autoRender
 
     def CoordinatesChanged(self):
-        """Shows current coordinates on statusbar.
-        """
+        """Shows current coordinates on statusbar."""
         # assuming that the first mode is coordinates
         # probably shold not be here but good solution is not available now
         if self.statusbarManager:
             if self.statusbarManager.GetMode() == 0:
-                self.statusbarManager.ShowItem('coordinates')
+                self.statusbarManager.ShowItem("coordinates")
 
     def StatusbarReposition(self):
         """Reposition items in statusbar"""
@@ -342,18 +354,15 @@ class MapFrameBase(wx.Frame):
         raise NotImplementedError("IsStandalone")
 
     def OnRender(self, event):
-        """Re-render map composition (each map layer)
-        """
+        """Re-render map composition (each map layer)"""
         raise NotImplementedError("OnRender")
 
     def OnDraw(self, event):
-        """Re-display current map composition
-        """
+        """Re-display current map composition"""
         self.MapWindow.UpdateMap(render=False)
 
     def OnErase(self, event):
-        """Erase the canvas
-        """
+        """Erase the canvas"""
         self.MapWindow.EraseMap()
 
     def OnZoomIn(self, event):
@@ -369,11 +378,11 @@ class MapFrameBase(wx.Frame):
         # enable or disable zoom history tool
         if self.GetMapToolbar():
             mapWindow.zoomHistoryAvailable.connect(
-                lambda:
-                self.GetMapToolbar().Enable('zoomBack', enable=True))
+                lambda: self.GetMapToolbar().Enable("zoomBack", enable=True)
+            )
             mapWindow.zoomHistoryUnavailable.connect(
-                lambda:
-                self.GetMapToolbar().Enable('zoomBack', enable=False))
+                lambda: self.GetMapToolbar().Enable("zoomBack", enable=False)
+            )
         mapWindow.mouseMoving.connect(self.CoordinatesChanged)
 
     def OnPointer(self, event):
@@ -381,13 +390,11 @@ class MapFrameBase(wx.Frame):
         self.MapWindow.SetModePointer()
 
     def OnPan(self, event):
-        """Panning, set mouse to drag
-        """
+        """Panning, set mouse to drag"""
         self.MapWindow.SetModePan()
 
     def OnZoomBack(self, event):
-        """Zoom last (previously stored position)
-        """
+        """Zoom last (previously stored position)"""
         self.MapWindow.ZoomBack()
 
     def OnZoomToMap(self, event):
@@ -404,8 +411,7 @@ class MapFrameBase(wx.Frame):
         self.MapWindow.ZoomToWind()
 
     def OnZoomToDefault(self, event):
-        """Set display geometry to match default region settings
-        """
+        """Set display geometry to match default region settings"""
         self.MapWindow.ZoomToDefault()
 
 
@@ -421,10 +427,18 @@ class SingleMapFrame(MapFrameBase):
     (when using class or when writing class itself).
     """
 
-    def __init__(self, parent=None, giface=None, id=wx.ID_ANY, title='',
-                 style=wx.DEFAULT_FRAME_STYLE,
-                 Map=None,
-                 auimgr=None, name='', **kwargs):
+    def __init__(
+        self,
+        parent=None,
+        giface=None,
+        id=wx.ID_ANY,
+        title="",
+        style=wx.DEFAULT_FRAME_STYLE,
+        Map=None,
+        auimgr=None,
+        name="",
+        **kwargs,
+    ):
         """
 
         :param parent: gui parent
@@ -436,11 +450,18 @@ class SingleMapFrame(MapFrameBase):
         :param kwargs: arguments passed to MapFrameBase
         """
 
-        MapFrameBase.__init__(self, parent=parent, id=id, title=title,
-                              style=style,
-                              auimgr=auimgr, name=name, **kwargs)
+        MapFrameBase.__init__(
+            self,
+            parent=parent,
+            id=id,
+            title=title,
+            style=style,
+            auimgr=auimgr,
+            name=name,
+            **kwargs,
+        )
 
-        self.Map = Map       # instance of render.Map
+        self.Map = Map  # instance of render.Map
 
         #
         # initialize region values
@@ -461,8 +482,7 @@ class SingleMapFrame(MapFrameBase):
         return [self.MapWindow]
 
     def OnRender(self, event):
-        """Re-render map composition (each map layer)
-        """
+        """Re-render map composition (each map layer)"""
         self.GetWindow().UpdateMap(render=True, renderVector=True)
 
         # update statusbar
@@ -490,10 +510,18 @@ class DoubleMapFrame(MapFrameBase):
         and GCP MapFrame will be necessary).
     """
 
-    def __init__(self, parent=None, id=wx.ID_ANY, title=None,
-                 style=wx.DEFAULT_FRAME_STYLE,
-                 firstMap=None, secondMap=None,
-                 auimgr=None, name=None, **kwargs):
+    def __init__(
+        self,
+        parent=None,
+        id=wx.ID_ANY,
+        title=None,
+        style=wx.DEFAULT_FRAME_STYLE,
+        firstMap=None,
+        secondMap=None,
+        auimgr=None,
+        name=None,
+        **kwargs,
+    ):
         """
 
         \a firstMap is set as active (by assign it to \c self.Map).
@@ -508,9 +536,16 @@ class DoubleMapFrame(MapFrameBase):
         :param kwargs: arguments passed to MapFrameBase
         """
 
-        MapFrameBase.__init__(self, parent=parent, id=id, title=title,
-                              style=style,
-                              auimgr=auimgr, name=name, **kwargs)
+        MapFrameBase.__init__(
+            self,
+            parent=parent,
+            id=id,
+            title=title,
+            style=style,
+            auimgr=auimgr,
+            name=name,
+            **kwargs,
+        )
 
         self.firstMap = firstMap
         self.secondMap = secondMap
@@ -528,18 +563,16 @@ class DoubleMapFrame(MapFrameBase):
         self.GetSecondWindow().Bind(wx.EVT_ENTER_WINDOW, self.ActivateSecondMap)
 
     def _onToggleTool(self, id):
-        if self._toolSwitcher.IsToolInGroup(id, 'mouseUse'):
+        if self._toolSwitcher.IsToolInGroup(id, "mouseUse"):
             self.GetFirstWindow().UnregisterAllHandlers()
             self.GetSecondWindow().UnregisterAllHandlers()
 
     def GetFirstMap(self):
-        """Returns first Map instance
-        """
+        """Returns first Map instance"""
         return self.firstMap
 
     def GetSecondMap(self):
-        """Returns second Map instance
-        """
+        """Returns second Map instance"""
         return self.secondMap
 
     def GetFirstWindow(self):
@@ -582,8 +615,7 @@ class DoubleMapFrame(MapFrameBase):
         # bind/unbind regions
         if self._bindRegions:
             self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
-            self.secondMapWindow.zoomChanged.disconnect(
-                self.OnZoomChangedSecondMap)
+            self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
 
     def ActivateSecondMap(self, event=None):
         """Make second Map and MapWindow active and (un)bind regions of the two Maps."""
@@ -595,10 +627,8 @@ class DoubleMapFrame(MapFrameBase):
         self.GetMapToolbar().SetActiveMap(1)
 
         if self._bindRegions:
-            self.secondMapWindow.zoomChanged.connect(
-                self.OnZoomChangedSecondMap)
-            self.firstMapWindow.zoomChanged.disconnect(
-                self.OnZoomChangedFirstMap)
+            self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
+            self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
 
     def SetBindRegions(self, on):
         """Set or unset binding display regions."""
@@ -606,18 +636,14 @@ class DoubleMapFrame(MapFrameBase):
 
         if on:
             if self.MapWindow == self.firstMapWindow:
-                self.firstMapWindow.zoomChanged.connect(
-                    self.OnZoomChangedFirstMap)
+                self.firstMapWindow.zoomChanged.connect(self.OnZoomChangedFirstMap)
             else:
-                self.secondMapWindow.zoomChanged.connect(
-                    self.OnZoomChangedSecondMap)
+                self.secondMapWindow.zoomChanged.connect(self.OnZoomChangedSecondMap)
         else:
             if self.MapWindow == self.firstMapWindow:
-                self.firstMapWindow.zoomChanged.disconnect(
-                    self.OnZoomChangedFirstMap)
+                self.firstMapWindow.zoomChanged.disconnect(self.OnZoomChangedFirstMap)
             else:
-                self.secondMapWindow.zoomChanged.disconnect(
-                    self.OnZoomChangedSecondMap)
+                self.secondMapWindow.zoomChanged.disconnect(self.OnZoomChangedSecondMap)
 
     def OnZoomChangedFirstMap(self):
         """Display region of the first window (Map) changed.
@@ -665,38 +691,33 @@ class DoubleMapFrame(MapFrameBase):
         self.GetSecondWindow().SetModeQuery()
 
     def OnRender(self, event):
-        """Re-render map composition (each map layer)
-        """
+        """Re-render map composition (each map layer)"""
         self.Render(mapToRender=self.GetFirstWindow())
         self.Render(mapToRender=self.GetSecondWindow())
 
     def Render(self, mapToRender):
         """Re-render map composition"""
         mapToRender.UpdateMap(
-            render=True,
-            renderVector=mapToRender == self.GetFirstWindow())
+            render=True, renderVector=mapToRender == self.GetFirstWindow()
+        )
 
         # update statusbar
         self.StatusbarUpdate()
 
     def OnErase(self, event):
-        """Erase the canvas
-        """
+        """Erase the canvas"""
         self.Erase(mapToErase=self.GetFirstWindow())
         self.Erase(mapToErase=self.GetSecondWindow())
 
     def Erase(self, mapToErase):
-        """Erase the canvas
-        """
+        """Erase the canvas"""
         mapToErase.EraseMap()
 
     def OnDraw(self, event):
-        """Re-display current map composition
-        """
+        """Re-display current map composition"""
         self.Draw(mapToDraw=self.GetFirstWindow())
         self.Draw(mapToDraw=self.GetSecondWindow())
 
     def Draw(self, mapToDraw):
-        """Re-display current map composition
-        """
+        """Re-display current map composition"""
         mapToDraw.UpdateMap(render=False)

+ 71 - 59
gui/wxpython/gui_core/menu.py

@@ -35,7 +35,6 @@ from grass.pydispatch.signal import Signal
 
 
 class Menu(wx.MenuBar):
-
     def __init__(self, parent, model):
         """Creates menubar"""
         wx.MenuBar.__init__(self)
@@ -57,7 +56,7 @@ class Menu(wx.MenuBar):
                 menu.AppendMenu(wx.ID_ANY, label, subMenu)
             else:
                 data = child.data.copy()
-                data.pop('label')
+                data.pop("label")
 
                 self._createMenuItem(menu, label=child.label, **data)
 
@@ -66,8 +65,18 @@ class Menu(wx.MenuBar):
         return menu
 
     def _createMenuItem(
-            self, menu, label, description, handler, command, keywords,
-            shortcut='', icon='', wxId=wx.ID_ANY, kind=wx.ITEM_NORMAL):
+        self,
+        menu,
+        label,
+        description,
+        handler,
+        command,
+        keywords,
+        shortcut="",
+        icon="",
+        wxId=wx.ID_ANY,
+        kind=wx.ITEM_NORMAL,
+    ):
         """Creates menu items
         There are three menu styles (menu item text styles).
         1 -- label only, 2 -- label and cmd name, 3 -- cmd name only
@@ -77,12 +86,12 @@ class Menu(wx.MenuBar):
             return
 
         if command:
-            helpString = command + ' -- ' + description
+            helpString = command + " -- " + description
         else:
             helpString = description
 
         if shortcut:
-            label += '\t' + shortcut
+            label += "\t" + shortcut
 
         menuItem = wx.MenuItem(menu, wxId, label, helpString, kind)
         if icon:
@@ -98,11 +107,14 @@ class Menu(wx.MenuBar):
                 cmd = utils.split(EncodeString((command)))
             # disable only grass commands which are not present (e.g.
             # r.in.lidar)
-            if cmd and cmd[0] not in globalvar.grassCmd and \
-               re.match('[rvdipmgt][3bs]?\.([a-z0-9\.])+', cmd[0]):
+            if (
+                cmd
+                and cmd[0] not in globalvar.grassCmd
+                and re.match("[rvdipmgt][3bs]?\.([a-z0-9\.])+", cmd[0])
+            ):
                 menuItem.Enable(False)
 
-        rhandler = eval('self.parent.' + handler)
+        rhandler = eval("self.parent." + handler)
         self.parent.Bind(wx.EVT_MENU, rhandler, menuItem)
 
     def GetData(self):
@@ -137,44 +149,41 @@ class SearchModuleWindow(wx.Panel):
         showNotification - attribute 'message'
     """
 
-    def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY,
-                 **kwargs):
+    def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY, **kwargs):
         self.parent = parent
         self._handlerObj = handlerObj
         self._giface = giface
         self._model = model
 
-        self.showNotification = Signal('SearchModuleWindow.showNotification')
+        self.showNotification = Signal("SearchModuleWindow.showNotification")
         wx.Panel.__init__(self, parent=parent, id=id, **kwargs)
 
         # search widget
         self._search = SearchCtrl(self)
-        self._search.SetDescriptiveText(_('Search'))
+        self._search.SetDescriptiveText(_("Search"))
         self._search.ShowCancelButton(True)
-        self._btnAdvancedSearch = Button(self, id=wx.ID_ANY,
-                                         label=_("Adva&nced search..."))
+        self._btnAdvancedSearch = Button(
+            self, id=wx.ID_ANY, label=_("Adva&nced search...")
+        )
         self._btnAdvancedSearch.SetToolTip(
-            _("Do advanced search using %s module") % 'g.search.module')
+            _("Do advanced search using %s module") % "g.search.module"
+        )
         # tree
         self._tree = CTreeView(model=model, parent=self)
-        self._tree.SetToolTip(
-            _("Double-click to run selected module"))
+        self._tree.SetToolTip(_("Double-click to run selected module"))
 
         # buttons
         self._btnRun = Button(self, id=wx.ID_OK, label=_("&Run..."))
         self._btnRun.SetToolTip(_("Run selected module from the tree"))
         self._btnHelp = Button(self, id=wx.ID_ANY, label=_("H&elp"))
-        self._btnHelp.SetToolTip(
-            _("Show manual for selected module from the tree"))
+        self._btnHelp.SetToolTip(_("Show manual for selected module from the tree"))
 
         # bindings
         self._search.Bind(wx.EVT_TEXT, lambda evt: self.Filter(evt.GetString()))
-        self._search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
-                          lambda evt: self.Filter(''))
+        self._search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, lambda evt: self.Filter(""))
         self._btnRun.Bind(wx.EVT_BUTTON, lambda evt: self.Run())
         self._btnHelp.Bind(wx.EVT_BUTTON, lambda evt: self.Help())
-        self._btnAdvancedSearch.Bind(wx.EVT_BUTTON,
-                                     lambda evt: self.AdvancedSearch())
+        self._btnAdvancedSearch.Bind(wx.EVT_BUTTON, lambda evt: self.AdvancedSearch())
 
         self._tree.selectionChanged.connect(self.OnItemSelected)
         self._tree.itemActivated.connect(lambda node: self.Run(node))
@@ -189,25 +198,21 @@ class SearchModuleWindow(wx.Panel):
 
         # search
         searchSizer = wx.BoxSizer(wx.HORIZONTAL)
-        searchSizer.Add(self._search, proportion=1,
-                        flag=wx.EXPAND | wx.RIGHT, border=5)
-        searchSizer.Add(self._btnAdvancedSearch, proportion=0,
-                        flag=wx.EXPAND)
-        sizer.Add(searchSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        searchSizer.Add(self._search, proportion=1, flag=wx.EXPAND | wx.RIGHT, border=5)
+        searchSizer.Add(self._btnAdvancedSearch, proportion=0, flag=wx.EXPAND)
+        sizer.Add(searchSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
         # body
-        sizer.Add(self._tree, proportion=1,
-                  flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
+        sizer.Add(
+            self._tree, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
+        )
 
         # buttons
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
         btnSizer.AddStretchSpacer()
-        btnSizer.Add(self._btnHelp, proportion=0,
-                     flag=wx.EXPAND | wx.RIGHT, border=5)
+        btnSizer.Add(self._btnHelp, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
         btnSizer.Add(self._btnRun, proportion=0)
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         self.SetSizerAndFit(sizer)
         self.SetAutoLayout(True)
@@ -215,8 +220,9 @@ class SearchModuleWindow(wx.Panel):
 
     def Filter(self, text):
         if text:
-            model = self._model.Filtered(key=['command', 'keywords', 'description'],
-                                         value=text)
+            model = self._model.Filtered(
+                key=["command", "keywords", "description"], value=text
+            )
             self._tree.SetModel(model)
             self._tree.ExpandAll()
         else:
@@ -249,10 +255,10 @@ class SearchModuleWindow(wx.Panel):
             return
 
         # extract name of the handler and create a new call
-        handler = 'self._handlerObj.' + data['handler'].lstrip('self.')
+        handler = "self._handlerObj." + data["handler"].lstrip("self.")
 
-        if data['command']:
-            eval(handler)(event=None, cmd=data['command'].split())
+        if data["command"]:
+            eval(handler)(event=None, cmd=data["command"].split())
         else:
             eval(handler)(event=None)
 
@@ -268,32 +274,32 @@ class SearchModuleWindow(wx.Panel):
         if not data:
             return
 
-        if not data['command']:
+        if not data["command"]:
             # showing nothing for non-modules
             return
         # strip parameters from command if present
-        name = data['command'].split()[0]
+        name = data["command"].split()[0]
         self._giface.Help(name)
         self.showNotification.emit(
-            message=_("Documentation for %s is now open in the web browser")
-            % name)
+            message=_("Documentation for %s is now open in the web browser") % name
+        )
 
     def AdvancedSearch(self):
         """Show advanced search window"""
-        self._handlerObj.RunMenuCmd(cmd=['g.search.modules'])
+        self._handlerObj.RunMenuCmd(cmd=["g.search.modules"])
 
     def OnItemSelected(self, node):
         """Item selected"""
         data = node.data
-        if not data or 'command' not in data:
+        if not data or "command" not in data:
             return
 
-        if data['command']:
-            label = data['command']
-            if data['description']:
-                label += ' -- ' + data['description']
+        if data["command"]:
+            label = data["command"]
+            if data["description"]:
+                label += " -- " + data["description"]
         else:
-            label = data['description']
+            label = data["description"]
 
         self.showNotification.emit(message=label)
 
@@ -318,14 +324,14 @@ class RecentFilesMenu:
     into the .recent_files file to app name group
     """
 
-    recent_files = '.recent_files'
+    recent_files = ".recent_files"
 
     def __init__(self, app_name, parent_menu, pos, history_len=10):
         self._history_len = history_len
         self._parent_menu = parent_menu
         self._pos = pos
 
-        self.file_requested = Signal('RecentFilesMenu.FileRequested')
+        self.file_requested = Signal("RecentFilesMenu.FileRequested")
 
         self._filehistory = wx.FileHistory(maxFiles=history_len)
         # Recent files path stored in GRASS GIS config dir in the
@@ -333,7 +339,8 @@ class RecentFilesMenu:
         self._config = wx.FileConfig(
             style=wx.CONFIG_USE_LOCAL_FILE,
             localFilename=os.path.join(
-                utils.GetSettingsPath(), self.recent_files,
+                utils.GetSettingsPath(),
+                self.recent_files,
             ),
         )
         self._config.SetPath(strPath=app_name)
@@ -353,12 +360,16 @@ class RecentFilesMenu:
         specified position if count of menu items > 0"""
 
         self._parent_menu.Insert(
-            pos=self._pos, id=wx.ID_ANY, text=_('&Recent Files'),
+            pos=self._pos,
+            id=wx.ID_ANY,
+            text=_("&Recent Files"),
             submenu=self.recent,
         )
         self.recent.Bind(
-            wx.EVT_MENU_RANGE, self._onFileHistory,
-            id=wx.ID_FILE1, id2=wx.ID_FILE + self._history_len,
+            wx.EVT_MENU_RANGE,
+            self._onFileHistory,
+            id=wx.ID_FILE1,
+            id2=wx.ID_FILE + self._history_len,
         )
 
     def _onFileHistory(self, event):
@@ -372,7 +383,8 @@ class RecentFilesMenu:
             self.RemoveFileFromHistory(file_index)
             file_exists = False
         self.file_requested.emit(
-            path=path, file_exists=file_exists,
+            path=path,
+            file_exists=file_exists,
             file_history=self._filehistory,
         )
 

File diff ditekan karena terlalu besar
+ 1028 - 1096
gui/wxpython/gui_core/preferences.py


+ 154 - 147
gui/wxpython/gui_core/prompt.py

@@ -45,10 +45,10 @@ class GPrompt(object):
     """
 
     def __init__(self, parent, giface, menuModel):
-        self.parent = parent                 # GConsole
+        self.parent = parent  # GConsole
         self.panel = self.parent.GetPanel()
 
-        self.promptRunCmd = Signal('GPrompt.promptRunCmd')
+        self.promptRunCmd = Signal("GPrompt.promptRunCmd")
 
         # probably only subclasses need this
         self._menuModel = menuModel
@@ -77,19 +77,21 @@ class GPrompt(object):
         try:
             fileHistory = codecs.open(
                 os.path.join(
-                    env['GISDBASE'],
-                    env['LOCATION_NAME'],
-                    env['MAPSET'],
-                    '.bash_history'),
-                encoding='utf-8',
-                mode='r',
-                errors='replace')
+                    env["GISDBASE"],
+                    env["LOCATION_NAME"],
+                    env["MAPSET"],
+                    ".bash_history",
+                ),
+                encoding="utf-8",
+                mode="r",
+                errors="replace",
+            )
         except IOError:
             return hist
 
         try:
             for line in fileHistory.readlines():
-                hist.append(line.replace('\n', ''))
+                hist.append(line.replace("\n", ""))
         finally:
             fileHistory.close()
 
@@ -103,8 +105,8 @@ class GPrompt(object):
     def _getListOfMaps(self):
         """Get list of maps"""
         result = dict()
-        result['raster'] = grass.list_strings('raster')
-        result['vector'] = grass.list_strings('vector')
+        result["raster"] = grass.list_strings("raster")
+        result["vector"] = grass.list_strings("vector")
 
         return result
 
@@ -126,7 +128,7 @@ class GPrompt(object):
         self.promptRunCmd.emit(cmd=cmd)
 
         self.OnCmdErase(None)
-        self.ShowStatusText('')
+        self.ShowStatusText("")
 
     def GetCommands(self):
         """Get list of launched commands"""
@@ -193,14 +195,14 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
         self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected)
         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged)
-        if sys.platform != 'darwin':  # unstable on Mac with wxPython 3
+        if sys.platform != "darwin":  # unstable on Mac with wxPython 3
             self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
 
         # signal which requests showing of a notification
-        self.showNotification = Signal('GPromptSTC.showNotification')
+        self.showNotification = Signal("GPromptSTC.showNotification")
 
         # signal to notify selected command
-        self.commandSelected = Signal('GPromptSTC.commandSelected')
+        self.commandSelected = Signal("GPromptSTC.commandSelected")
 
     def OnTextSelectionChanged(self, event):
         """Copy selected text to clipboard and skip event.
@@ -213,48 +215,50 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         """Change text in statusbar
         if the item selection in the auto-completion list is changed"""
         # list of commands
-        if self.toComplete['entity'] == 'command':
-            item = self.toComplete['cmd'].rpartition(
-                '.')[0] + '.' + self.autoCompList[event.GetIndex()]
+        if self.toComplete["entity"] == "command":
+            item = (
+                self.toComplete["cmd"].rpartition(".")[0]
+                + "."
+                + self.autoCompList[event.GetIndex()]
+            )
             try:
-                nodes = self._menuModel.SearchNodes(key='command', value=item)
-                desc = ''
+                nodes = self._menuModel.SearchNodes(key="command", value=item)
+                desc = ""
                 if nodes:
                     self.commandSelected.emit(command=item)
-                    desc = nodes[0].data['description']
+                    desc = nodes[0].data["description"]
             except KeyError:
-                desc = ''
+                desc = ""
             self.ShowStatusText(desc)
         # list of flags
-        elif self.toComplete['entity'] == 'flags':
-            desc = self.cmdDesc.get_flag(
-                self.autoCompList[
-                    event.GetIndex()])['description']
+        elif self.toComplete["entity"] == "flags":
+            desc = self.cmdDesc.get_flag(self.autoCompList[event.GetIndex()])[
+                "description"
+            ]
             self.ShowStatusText(desc)
         # list of parameters
-        elif self.toComplete['entity'] == 'params':
+        elif self.toComplete["entity"] == "params":
             item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
-            desc = item['name'] + '=' + item['type']
-            if not item['required']:
-                desc = '[' + desc + ']'
-            desc += ': ' + item['description']
+            desc = item["name"] + "=" + item["type"]
+            if not item["required"]:
+                desc = "[" + desc + "]"
+            desc += ": " + item["description"]
             self.ShowStatusText(desc)
         # list of flags and commands
-        elif self.toComplete['entity'] == 'params+flags':
-            if self.autoCompList[event.GetIndex()][0] == '-':
+        elif self.toComplete["entity"] == "params+flags":
+            if self.autoCompList[event.GetIndex()][0] == "-":
                 desc = self.cmdDesc.get_flag(
-                    self.autoCompList[
-                        event.GetIndex()].strip('-'))['description']
+                    self.autoCompList[event.GetIndex()].strip("-")
+                )["description"]
             else:
-                item = self.cmdDesc.get_param(
-                    self.autoCompList[event.GetIndex()])
-                desc = item['name'] + '=' + item['type']
-                if not item['required']:
-                    desc = '[' + desc + ']'
-                desc += ': ' + item['description']
+                item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
+                desc = item["name"] + "=" + item["type"]
+                if not item["required"]:
+                    desc = "[" + desc + "]"
+                desc += ": " + item["description"]
             self.ShowStatusText(desc)
         else:
-            self.ShowStatusText('')
+            self.ShowStatusText("")
 
     def OnItemSelected(self, event):
         """Item selected from the list"""
@@ -262,16 +266,15 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         # to insert selection correctly if selected word partly matches written
         # text
         match = difflib.SequenceMatcher(None, event.GetText(), lastWord)
-        matchTuple = match.find_longest_match(
-            0, len(event.GetText()), 0, len(lastWord))
+        matchTuple = match.find_longest_match(0, len(event.GetText()), 0, len(lastWord))
 
-        compl = event.GetText()[matchTuple[2]:]
+        compl = event.GetText()[matchTuple[2] :]
         text = self.GetTextLeft() + compl
         # add space or '=' at the end
-        end = '='
-        for char in ('.', '-', '='):
-            if text.split(' ')[-1].find(char) >= 0:
-                end = ' '
+        end = "="
+        for char in (".", "-", "="):
+            if text.split(" ")[-1].find(char) >= 0:
+                end = " "
 
         compl += end
         text += end
@@ -280,7 +283,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         pos = len(text)
         self.SetCurrentPos(pos)
 
-        cmd = text.strip().split(' ')[0]
+        cmd = text.strip().split(" ")[0]
 
         if not self.cmdDesc or cmd != self.cmdDesc.get_name():
             try:
@@ -333,23 +336,24 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         except ValueError:  # No closing quotation error
             return toComplete
         if len(splitted) > 0 and cmd in globalvar.grassCmd:
-            toComplete['cmd'] = cmd
-            if entry[-1] == ' ':
-                words = entry.split(' ')
-                if any(word.startswith('-') for word in words):
-                    toComplete['entity'] = 'params'
+            toComplete["cmd"] = cmd
+            if entry[-1] == " ":
+                words = entry.split(" ")
+                if any(word.startswith("-") for word in words):
+                    toComplete["entity"] = "params"
                 else:
-                    toComplete['entity'] = 'params+flags'
+                    toComplete["entity"] = "params+flags"
             else:
                 # get word left from current position
                 word = self.GetWordLeft(withDelimiter=True)
 
-                if word[0] == '=' and word[-1] == '@':
-                    toComplete['entity'] = 'mapsets'
-                elif word[0] == '=':
+                if word[0] == "=" and word[-1] == "@":
+                    toComplete["entity"] = "mapsets"
+                elif word[0] == "=":
                     # get name of parameter
                     paramName = self.GetWordLeft(
-                        withDelimiter=False, ignoredDelimiter='=').strip('=')
+                        withDelimiter=False, ignoredDelimiter="="
+                    ).strip("=")
                     if paramName:
                         try:
                             param = self.cmdDesc.get_param(paramName)
@@ -358,19 +362,19 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
                     else:
                         return toComplete
 
-                    if param['values']:
-                        toComplete['entity'] = 'param values'
-                    elif param['prompt'] == 'raster' and param['element'] == 'cell':
-                        toComplete['entity'] = 'raster map'
-                    elif param['prompt'] == 'vector' and param['element'] == 'vector':
-                        toComplete['entity'] = 'vector map'
-                elif word[0] == '-':
-                    toComplete['entity'] = 'flags'
-                elif word[0] == ' ':
-                    toComplete['entity'] = 'params'
+                    if param["values"]:
+                        toComplete["entity"] = "param values"
+                    elif param["prompt"] == "raster" and param["element"] == "cell":
+                        toComplete["entity"] = "raster map"
+                    elif param["prompt"] == "vector" and param["element"] == "vector":
+                        toComplete["entity"] = "vector map"
+                elif word[0] == "-":
+                    toComplete["entity"] = "flags"
+                elif word[0] == " ":
+                    toComplete["entity"] = "params"
         else:
-            toComplete['entity'] = 'command'
-            toComplete['cmd'] = cmd
+            toComplete["entity"] = "command"
+            toComplete["cmd"] = cmd
 
         return toComplete
 
@@ -385,11 +389,11 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
 
         parts = list()
         if ignoredDelimiter is None:
-            ignoredDelimiter = ''
+            ignoredDelimiter = ""
 
-        for char in set(' .,-=') - set(ignoredDelimiter):
+        for char in set(" .,-=") - set(ignoredDelimiter):
             if not withDelimiter:
-                delimiter = ''
+                delimiter = ""
             else:
                 delimiter = char
             parts.append(delimiter + textLeft.rpartition(char)[2])
@@ -399,7 +403,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         """Show sorted auto-completion list if it is not empty"""
         if len(self.autoCompList) > 0:
             self.autoCompList.sort()
-            self.AutoCompShow(0, itemList=' '.join(self.autoCompList))
+            self.AutoCompShow(0, itemList=" ".join(self.autoCompList))
 
     def OnKeyPressed(self, event):
         """Key pressed capture special treatment for tabulator to show help"""
@@ -410,7 +414,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             try:
                 cmd = entry.split()[0].strip()
             except IndexError:
-                cmd = ''
+                cmd = ""
 
             if cmd not in globalvar.grassCmd:
                 return
@@ -419,13 +423,16 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
 
             self.CallTipSetBackground("#f4f4d1")
             self.CallTipSetForeground("BLACK")
-            self.CallTipShow(pos, info['usage'] + '\n\n' + info['description'])
-        elif event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER) and \
-                not self.AutoCompActive():
+            self.CallTipShow(pos, info["usage"] + "\n\n" + info["description"])
+        elif (
+            event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER)
+            and not self.AutoCompActive()
+        ):
             # run command on line when <return> is pressed
             self._runCmd(self.GetCurLine()[0].strip())
-        elif event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and \
-                not self.AutoCompActive():
+        elif (
+            event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and not self.AutoCompActive()
+        ):
             # Command history using up and down
             if len(self.cmdbuffer) < 1:
                 return
@@ -446,7 +453,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
                 # without strip causes problem on windows
                 txt = self.cmdbuffer[self.cmdindex].strip()
             except KeyError:
-                txt = ''
+                txt = ""
 
             # clear current line and insert command history
             self.DelLineLeft()
@@ -455,7 +462,7 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             self.InsertText(pos, txt)
             self.LineEnd()
 
-            self.ShowStatusText('')
+            self.ShowStatusText("")
         else:
             event.Skip()
 
@@ -471,71 +478,72 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
         if event.GetKeyCode() == 46:
             self.autoCompList = list()
             entry = self.GetTextLeft()
-            self.InsertText(pos, '.')
+            self.InsertText(pos, ".")
             self.CharRight()
             self.toComplete = self.EntityToComplete()
             try:
-                if self.toComplete['entity'] == 'command':
+                if self.toComplete["entity"] == "command":
                     for command in globalvar.grassCmd:
                         try:
-                            if command.find(self.toComplete['cmd']) == 0:
-                                dotNumber = list(
-                                    self.toComplete['cmd']).count('.')
+                            if command.find(self.toComplete["cmd"]) == 0:
+                                dotNumber = list(self.toComplete["cmd"]).count(".")
                                 self.autoCompList.append(
-                                    command.split('.', dotNumber)[-1])
+                                    command.split(".", dotNumber)[-1]
+                                )
                         except UnicodeDecodeError as error:
-                            sys.stderr.write(
-                                DecodeString(command) + ": " + str(error))
+                            sys.stderr.write(DecodeString(command) + ": " + str(error))
 
             except (KeyError, TypeError):
                 return
             self.ShowList()
 
         # complete flags after pressing '-'
-        elif (event.GetKeyCode() == 45) \
-                or event.GetKeyCode() == wx.WXK_NUMPAD_SUBTRACT \
-                or event.GetKeyCode() == wx.WXK_SUBTRACT:
+        elif (
+            (event.GetKeyCode() == 45)
+            or event.GetKeyCode() == wx.WXK_NUMPAD_SUBTRACT
+            or event.GetKeyCode() == wx.WXK_SUBTRACT
+        ):
             self.autoCompList = list()
             entry = self.GetTextLeft()
-            self.InsertText(pos, '-')
+            self.InsertText(pos, "-")
             self.CharRight()
             self.toComplete = self.EntityToComplete()
-            if self.toComplete['entity'] == 'flags' and self.cmdDesc:
-                if self.GetTextLeft()[-2:] == ' -':  # complete e.g. --quite
-                    for flag in self.cmdDesc.get_options()['flags']:
-                        if len(flag['name']) == 1:
-                            self.autoCompList.append(flag['name'])
+            if self.toComplete["entity"] == "flags" and self.cmdDesc:
+                if self.GetTextLeft()[-2:] == " -":  # complete e.g. --quite
+                    for flag in self.cmdDesc.get_options()["flags"]:
+                        if len(flag["name"]) == 1:
+                            self.autoCompList.append(flag["name"])
                 else:
-                    for flag in self.cmdDesc.get_options()['flags']:
-                        if len(flag['name']) > 1:
-                            self.autoCompList.append(flag['name'])
+                    for flag in self.cmdDesc.get_options()["flags"]:
+                        if len(flag["name"]) > 1:
+                            self.autoCompList.append(flag["name"])
             self.ShowList()
 
         # complete map or values after parameter
         elif event.GetKeyCode() == 61:
             self.autoCompList = list()
-            self.InsertText(pos, '=')
+            self.InsertText(pos, "=")
             self.CharRight()
             self.toComplete = self.EntityToComplete()
-            if self.toComplete['entity'] == 'raster map':
-                self.autoCompList = self.mapList['raster']
-            elif self.toComplete['entity'] == 'vector map':
-                self.autoCompList = self.mapList['vector']
-            elif self.toComplete['entity'] == 'param values':
+            if self.toComplete["entity"] == "raster map":
+                self.autoCompList = self.mapList["raster"]
+            elif self.toComplete["entity"] == "vector map":
+                self.autoCompList = self.mapList["vector"]
+            elif self.toComplete["entity"] == "param values":
                 param = self.GetWordLeft(
-                    withDelimiter=False,
-                    ignoredDelimiter='=').strip(' =')
-                self.autoCompList = self.cmdDesc.get_param(param)['values']
+                    withDelimiter=False, ignoredDelimiter="="
+                ).strip(" =")
+                self.autoCompList = self.cmdDesc.get_param(param)["values"]
             self.ShowList()
 
         # complete mapset ('@')
         elif event.GetKeyCode() == 64:
             self.autoCompList = list()
-            self.InsertText(pos, '@')
+            self.InsertText(pos, "@")
             self.CharRight()
             self.toComplete = self.EntityToComplete()
 
-            if self.toComplete['entity'] == 'mapsets':
+            if self.toComplete["entity"] == "mapsets":
                 self.autoCompList = self.mapsetList
             self.ShowList()
 
@@ -545,61 +553,59 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             self.toComplete = self.EntityToComplete()
 
             # complete command
-            if self.toComplete['entity'] == 'command':
+            if self.toComplete["entity"] == "command":
                 for command in globalvar.grassCmd:
-                    if command.find(self.toComplete['cmd']) == 0:
-                        dotNumber = list(self.toComplete['cmd']).count('.')
-                        self.autoCompList.append(
-                            command.split('.', dotNumber)[-1])
+                    if command.find(self.toComplete["cmd"]) == 0:
+                        dotNumber = list(self.toComplete["cmd"]).count(".")
+                        self.autoCompList.append(command.split(".", dotNumber)[-1])
 
             # complete flags in such situations (| is cursor):
             # r.colors -| ...w, q, l
             # r.colors -w| ...w, q, l
-            elif self.toComplete['entity'] == 'flags' and self.cmdDesc:
-                for flag in self.cmdDesc.get_options()['flags']:
-                    if len(flag['name']) == 1:
-                        self.autoCompList.append(flag['name'])
+            elif self.toComplete["entity"] == "flags" and self.cmdDesc:
+                for flag in self.cmdDesc.get_options()["flags"]:
+                    if len(flag["name"]) == 1:
+                        self.autoCompList.append(flag["name"])
 
             # complete parameters in such situations (| is cursor):
             # r.colors -w | ...color, map, rast, rules
             # r.colors col| ...color
-            elif self.toComplete['entity'] == 'params' and self.cmdDesc:
-                for param in self.cmdDesc.get_options()['params']:
-                    if param['name'].find(
-                            self.GetWordLeft(withDelimiter=False)) == 0:
-                        self.autoCompList.append(param['name'])
+            elif self.toComplete["entity"] == "params" and self.cmdDesc:
+                for param in self.cmdDesc.get_options()["params"]:
+                    if param["name"].find(self.GetWordLeft(withDelimiter=False)) == 0:
+                        self.autoCompList.append(param["name"])
 
             # complete flags or parameters in such situations (| is cursor):
             # r.colors | ...-w, -q, -l, color, map, rast, rules
             # r.colors color=grey | ...-w, -q, -l, color, map, rast, rules
-            elif self.toComplete['entity'] == 'params+flags' and self.cmdDesc:
+            elif self.toComplete["entity"] == "params+flags" and self.cmdDesc:
                 self.autoCompList = list()
 
-                for param in self.cmdDesc.get_options()['params']:
-                    self.autoCompList.append(param['name'])
-                for flag in self.cmdDesc.get_options()['flags']:
-                    if len(flag['name']) == 1:
-                        self.autoCompList.append('-' + flag['name'])
+                for param in self.cmdDesc.get_options()["params"]:
+                    self.autoCompList.append(param["name"])
+                for flag in self.cmdDesc.get_options()["flags"]:
+                    if len(flag["name"]) == 1:
+                        self.autoCompList.append("-" + flag["name"])
                     else:
-                        self.autoCompList.append('--' + flag['name'])
+                        self.autoCompList.append("--" + flag["name"])
 
                 self.ShowList()
 
             # complete map or values after parameter
             # r.buffer input=| ...list of raster maps
             # r.buffer units=| ... feet, kilometers, ...
-            elif self.toComplete['entity'] == 'raster map':
+            elif self.toComplete["entity"] == "raster map":
                 self.autoCompList = list()
-                self.autoCompList = self.mapList['raster']
-            elif self.toComplete['entity'] == 'vector map':
+                self.autoCompList = self.mapList["raster"]
+            elif self.toComplete["entity"] == "vector map":
                 self.autoCompList = list()
-                self.autoCompList = self.mapList['vector']
-            elif self.toComplete['entity'] == 'param values':
+                self.autoCompList = self.mapList["vector"]
+            elif self.toComplete["entity"] == "param values":
                 self.autoCompList = list()
                 param = self.GetWordLeft(
-                    withDelimiter=False,
-                    ignoredDelimiter='=').strip(' =')
-                self.autoCompList = self.cmdDesc.get_param(param)['values']
+                    withDelimiter=False, ignoredDelimiter="="
+                ).strip(" =")
+                self.autoCompList = self.cmdDesc.get_param(param)["values"]
 
             self.ShowList()
 
@@ -607,8 +613,9 @@ class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
             items = self.GetTextLeft().split()
             if len(items) == 1:
                 cmd = items[0].strip()
-                if cmd in globalvar.grassCmd and \
-                        (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
+                if cmd in globalvar.grassCmd and (
+                    not self.cmdDesc or cmd != self.cmdDesc.get_name()
+                ):
                     try:
                         self.cmdDesc = gtask.parse_interface(cmd)
                     except IOError:

+ 152 - 118
gui/wxpython/gui_core/pyedit.py

@@ -13,6 +13,7 @@ for details.
 import sys
 import os
 import stat
+
 try:
     from StringIO import StringIO
 except ImportError:
@@ -25,8 +26,9 @@ import grass.script as gscript
 from grass.script.utils import try_remove
 
 # needed just for testing
-if __name__ == '__main__':
+if __name__ == "__main__":
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
 from core.gcmd import GError
@@ -66,12 +68,13 @@ if __name__ == '__main__':
 def module_template():
     """Template from which to start writing GRASS module"""
     import getpass
+
     author = getpass.getuser()
 
     properties = {}
-    properties['name'] = 'module name'
-    properties['author'] = author
-    properties['description'] = 'Module description'
+    properties["name"] = "module name"
+    properties["author"] = author
+    properties["description"] = "Module description"
 
     output = StringIO()
     # header
@@ -89,12 +92,16 @@ def module_template():
 # DATE:         %s
 #
 #%s
-""" % ('#' * 72,
-            properties['name'],
-            properties['author'],
-            '\n# '.join(properties['description'].splitlines()),
+"""
+        % (
+            "#" * 72,
+            properties["name"],
+            properties["author"],
+            "\n# ".join(properties["description"].splitlines()),
             time.asctime(),
-            '#' * 72))
+            "#" * 72,
+        )
+    )
 
     # UI
     output.write(
@@ -102,7 +109,9 @@ def module_template():
 # %%module
 # %% description: %s
 # %%end
-""" % (' '.join(properties['description'].splitlines())))
+"""
+        % (" ".join(properties["description"].splitlines()))
+    )
 
     # import modules
     output.write(
@@ -112,7 +121,8 @@ import os
 import atexit
 
 import grass.script as gs
-""")
+"""
+    )
 
     # cleanup()
     output.write(
@@ -120,17 +130,20 @@ import grass.script as gs
 RAST_REMOVE = []
 
 def cleanup():
-""")
+"""
+    )
     output.write(
         r"""    gs.run_command('g.remove', flags='f', type='raster',
                    name=RAST_REMOVE)
-""")
+"""
+    )
     output.write("\ndef main():\n")
     output.write(
         r"""    options, flags = gs.parser()
     gs.run_command('g.remove', flags='f', type='raster',
                    name=RAST_REMOVE)
-""")
+"""
+    )
 
     output.write("\n    return 0\n")
 
@@ -139,7 +152,8 @@ def cleanup():
 if __name__ == "__main__":
     atexit.register(cleanup)
     sys.exit(main())
-""")
+"""
+    )
     return output.getvalue()
 
 
@@ -253,6 +267,7 @@ if __name__ == "__main__":
 
 def open_url(url):
     import webbrowser
+
     webbrowser.open(url)
 
 
@@ -273,8 +288,10 @@ class PyEditController(object):
         # Get first (File) menu
         menu = guiparent.menubar.GetMenu(0)
         self.recent_files = RecentFilesMenu(
-            app_name='pyedit', parent_menu=menu, pos=1,
-        ) # pos=1 recent files menu position (index) in the parent (File) menu
+            app_name="pyedit",
+            parent_menu=menu,
+            pos=1,
+        )  # pos=1 recent files menu position (index) in the parent (File) menu
 
         self.recent_files.file_requested.connect(self.OpenRecentFile)
 
@@ -286,7 +303,7 @@ class PyEditController(object):
         :return str or None: file content or None
         """
         try:
-            with open(file_path, 'r') as f:
+            with open(file_path, "r") as f:
                 content = f.read()
                 return content
         except PermissionError:
@@ -304,7 +321,7 @@ class PyEditController(object):
                 parent=self.guiparent,
             )
 
-    def _writeFile(self, file_path, content, additional_err_message=''):
+    def _writeFile(self, file_path, content, additional_err_message=""):
         """Try open file and write content
 
         :param str file_path: file path
@@ -314,7 +331,7 @@ class PyEditController(object):
         :return None or True: file written or None
         """
         try:
-            with open(file_path, 'w') as f:
+            with open(file_path, "w") as f:
                 f.write(content)
                 return True
         except PermissionError:
@@ -322,7 +339,8 @@ class PyEditController(object):
                 message=_(
                     "Permission denied <{}>. Please change file "
                     "permission for writting.{}".format(
-                        file_path, additional_err_message,
+                        file_path,
+                        additional_err_message,
                     ),
                 ),
                 parent=self.guiparent,
@@ -331,8 +349,9 @@ class PyEditController(object):
         except IOError:
             GError(
                 message=_(
-                    "Couldn't write file <{}>.{}".
-                    format(file_path, additional_err_message),
+                    "Couldn't write file <{}>.{}".format(
+                        file_path, additional_err_message
+                    ),
                 ),
                 parent=self.guiparent,
             )
@@ -340,12 +359,12 @@ class PyEditController(object):
     def OnRun(self, event):
         """Run Python script"""
         if not self.filename:
-            self.filename = gscript.tempfile() + '.py'
+            self.filename = gscript.tempfile() + ".py"
             self.tempfile = True
             file_is_written = self._writeFile(
-                file_path=self.filename, content=self.body.GetText(),
+                file_path=self.filename,
+                content=self.body.GetText(),
                 additional_err_message=" Unable to launch Python script.",
-
             )
             if file_is_written:
                 mode = stat.S_IMODE(os.lstat(self.filename)[stat.ST_MODE])
@@ -353,7 +372,8 @@ class PyEditController(object):
         else:
             # always save automatically before running
             file_is_written = self._writeFile(
-                file_path=self.filename, content=self.body.GetText(),
+                file_path=self.filename,
+                content=self.body.GetText(),
                 additional_err_message=" Unable to launch Python script.",
             )
             if file_is_written:
@@ -366,7 +386,7 @@ class PyEditController(object):
             # carries variables over to the next execution
             env = os.environ.copy()
             if self.overwrite:
-                env['GRASS_OVERWRITE'] = '1'
+                env["GRASS_OVERWRITE"] = "1"
             cmd = [self.filename]
             if self.parameters:
                 cmd.extend(self.parameters)
@@ -379,11 +399,13 @@ class PyEditController(object):
             self.tempfile = False
 
         filename = None
-        dlg = wx.FileDialog(parent=self.guiparent,
-                            message=_("Choose file to save"),
-                            defaultDir=os.getcwd(),
-                            wildcard=_("Python script (*.py)|*.py"),
-                            style=wx.FD_SAVE)
+        dlg = wx.FileDialog(
+            parent=self.guiparent,
+            message=_("Choose file to save"),
+            defaultDir=os.getcwd(),
+            wildcard=_("Python script (*.py)|*.py"),
+            style=wx.FD_SAVE,
+        )
 
         if dlg.ShowModal() == wx.ID_OK:
             filename = dlg.GetPath()
@@ -398,10 +420,13 @@ class PyEditController(object):
         if os.path.exists(filename):
             dlg = wx.MessageDialog(
                 parent=self.guiparent,
-                message=_("File <%s> already exists. "
-                          "Do you want to overwrite this file?") % filename,
+                message=_(
+                    "File <%s> already exists. " "Do you want to overwrite this file?"
+                )
+                % filename,
                 caption=_("Save file"),
-                style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+                style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
+            )
             if dlg.ShowModal() == wx.ID_NO:
                 dlg.Destroy()
                 return
@@ -416,8 +441,9 @@ class PyEditController(object):
         """Save current content to a file and set executable permissions"""
         assert self.filename
         file_is_written = self._writeFile(
-            file_path=self.filename, content=self.body.GetText(),
-            )
+            file_path=self.filename,
+            content=self.body.GetText(),
+        )
         if file_is_written:
             # executable file
             os.chmod(self.filename, stat.S_IRWXU | stat.S_IWUSR)
@@ -443,12 +469,14 @@ class PyEditController(object):
 
     def Open(self):
         """Ask for a filename and load its content"""
-        filename = ''
-        dlg = wx.FileDialog(parent=self.guiparent,
-                            message=_("Open file"),
-                            defaultDir=os.getcwd(),
-                            wildcard=_("Python script (*.py)|*.py"),
-                            style=wx.FD_OPEN)
+        filename = ""
+        dlg = wx.FileDialog(
+            parent=self.guiparent,
+            message=_("Open file"),
+            defaultDir=os.getcwd(),
+            wildcard=_("Python script (*.py)|*.py"),
+            style=wx.FD_OPEN,
+        )
 
         if dlg.ShowModal() == wx.ID_OK:
             filename = dlg.GetPath()
@@ -467,7 +495,7 @@ class PyEditController(object):
 
     def OnOpen(self, event):
         """Handle open event but ask about replacing content first"""
-        if self.CanReplaceContent('file'):
+        if self.CanReplaceContent("file"):
             self.Open()
             if self.filename:
                 self.recent_files.AddFileToHistory(
@@ -492,7 +520,7 @@ class PyEditController(object):
                 parent=self.guiparent,
             )
         else:
-            if self.CanReplaceContent(by_message='file'):
+            if self.CanReplaceContent(by_message="file"):
                 self.filename = path
                 content = self._openFile(file_path=path)
                 if content:
@@ -510,32 +538,35 @@ class PyEditController(object):
         Used for example to check if content should be saved before closing.
         The content is not valuable for example if it already saved in a file.
         """
-        Debug.msg(2, "pyedit IsContentValuable? empty=%s, modified=%s" % (
-                  self.IsEmpty(), self.IsModified()))
+        Debug.msg(
+            2,
+            "pyedit IsContentValuable? empty=%s, modified=%s"
+            % (self.IsEmpty(), self.IsModified()),
+        )
         return not self.IsEmpty() and self.IsModified()
 
     def SetScriptTemplate(self, event):
-        if self.CanReplaceContent('template'):
+        if self.CanReplaceContent("template"):
             self.body.SetText(script_template())
             self.filename = None
 
     def SetModuleTemplate(self, event):
-        if self.CanReplaceContent('template'):
+        if self.CanReplaceContent("template"):
             self.body.SetText(module_template())
             self.filename = None
 
     def SetScriptExample(self, event):
-        if self.CanReplaceContent('example'):
+        if self.CanReplaceContent("example"):
             self.body.SetText(script_example())
             self.filename = None
 
     def SetModuleExample(self, event):
-        if self.CanReplaceContent('example'):
+        if self.CanReplaceContent("example"):
             self.body.SetText(module_example())
             self.filename = None
 
     def SetModuleErrorHandlingExample(self, event):
-        if self.CanReplaceContent('example'):
+        if self.CanReplaceContent("example"):
             self.body.SetText(module_error_handling_example())
             self.filename = None
 
@@ -550,19 +581,21 @@ class PyEditController(object):
             'example' and 'file' which will use predefined messages, otherwise
             a translatable, user visible string should be used.
         """
-        if by_message == 'template':
+        if by_message == "template":
             message = _("Replace the content by the template?")
-        elif by_message == 'example':
+        elif by_message == "example":
             message = _("Replace the content by the example?")
-        elif by_message == 'file':
+        elif by_message == "file":
             message = _("Replace the current content by the file content?")
         else:
             message = by_message
         if self.IsContentValuable():
             dlg = wx.MessageDialog(
-                parent=self.guiparent, message=message,
+                parent=self.guiparent,
+                message=message,
                 caption=_("Replace content"),
-                style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+                style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
+            )
             if dlg.ShowModal() == wx.ID_NO:
                 dlg.Destroy()
                 return False
@@ -574,7 +607,9 @@ class PyEditController(object):
         dlg = wx.TextEntryDialog(
             parent=self.guiparent,
             caption=_("Set parameters for the script"),
-            message=_("Specify command line parameters for the script separated by spaces:"),
+            message=_(
+                "Specify command line parameters for the script separated by spaces:"
+            ),
         )
         if self.parameters:
             dlg.SetValue(" ".join(self.parameters))
@@ -590,27 +625,26 @@ class PyEditController(object):
     def OnHelp(self, event):
         # inspired by g.manual but simple not using GRASS_HTML_BROWSER
         # not using g.manual because it does not show
-        entry = 'libpython/script_intro.html'
-        major, minor, patch = gscript.version()['version'].split('.')
-        url = 'https://grass.osgeo.org/grass%s%s/manuals/%s' % (
-            major, minor, entry)
+        entry = "libpython/script_intro.html"
+        major, minor, patch = gscript.version()["version"].split(".")
+        url = "https://grass.osgeo.org/grass%s%s/manuals/%s" % (major, minor, entry)
         open_url(url)
 
     def OnPythonHelp(self, event):
-        url = 'https://docs.python.org/%s/tutorial/' % sys.version_info[0]
+        url = "https://docs.python.org/%s/tutorial/" % sys.version_info[0]
         open_url(url)
 
     def OnModulesHelp(self, event):
-        self.giface.Help('full_index')
+        self.giface.Help("full_index")
 
     def OnSubmittingHelp(self, event):
-        open_url('https://trac.osgeo.org/grass/wiki/Submitting/Python')
+        open_url("https://trac.osgeo.org/grass/wiki/Submitting/Python")
 
     def OnAddonsHelp(self, event):
-        open_url('https://grass.osgeo.org/development/code-submission/')
+        open_url("https://grass.osgeo.org/development/code-submission/")
 
     def OnSupport(self, event):
-        open_url('https://grass.osgeo.org/support/')
+        open_url("https://grass.osgeo.org/support/")
 
 
 class PyEditToolbar(BaseToolbar):
@@ -623,23 +657,17 @@ class PyEditToolbar(BaseToolbar):
         BaseToolbar.__init__(self, parent)
 
         self.icons = {
-            'open': MetaIcon(img='open',
-                             label=_('Open (Ctrl+O)')),
-            'save': MetaIcon(img='save',
-                             label=_('Save (Ctrl+S)')),
-            'run': MetaIcon(img='execute',
-                            label=_('Run (Ctrl+R)')),
+            "open": MetaIcon(img="open", label=_("Open (Ctrl+O)")),
+            "save": MetaIcon(img="save", label=_("Save (Ctrl+S)")),
+            "run": MetaIcon(img="execute", label=_("Run (Ctrl+R)")),
             # TODO: better icons for overwrite modes
-            'overwriteTrue': MetaIcon(img='locked',
-                                      label=_('Activate overwrite')),
-            'overwriteFalse': MetaIcon(img='unlocked',
-                                       label=_('Deactive overwrite')),
-            'quit': MetaIcon(img='quit',
-                             label=_('Quit Simple Python Editor')),
+            "overwriteTrue": MetaIcon(img="locked", label=_("Activate overwrite")),
+            "overwriteFalse": MetaIcon(img="unlocked", label=_("Deactive overwrite")),
+            "quit": MetaIcon(img="quit", label=_("Quit Simple Python Editor")),
         }
 
         # workaround for http://trac.wxwidgets.org/ticket/13888
-        if sys.platform == 'darwin':
+        if sys.platform == "darwin":
             parent.SetToolBar(self)
 
         self.InitToolbar(self._toolbarData())
@@ -649,35 +677,41 @@ class PyEditToolbar(BaseToolbar):
 
     def _toolbarData(self):
         """Toolbar data"""
-        return self._getToolbarData((('open', self.icons['open'],
-                                      self.parent.OnOpen),
-                                     ('save', self.icons['save'],
-                                      self.parent.OnSave),
-                                     (None, ),
-                                     ('run', self.icons['run'],
-                                      self.parent.OnRun),
-                                     ('overwrite', self.icons['overwriteTrue'],
-                                      self.OnSetOverwrite, wx.ITEM_CHECK),
-                                     (None, ),
-                                     ("help", BaseIcons['help'],
-                                      self.parent.OnHelp),
-                                     ('quit', self.icons['quit'],
-                                      self.parent.OnClose),
-                                     ))
+        return self._getToolbarData(
+            (
+                ("open", self.icons["open"], self.parent.OnOpen),
+                ("save", self.icons["save"], self.parent.OnSave),
+                (None,),
+                ("run", self.icons["run"], self.parent.OnRun),
+                (
+                    "overwrite",
+                    self.icons["overwriteTrue"],
+                    self.OnSetOverwrite,
+                    wx.ITEM_CHECK,
+                ),
+                (None,),
+                ("help", BaseIcons["help"], self.parent.OnHelp),
+                ("quit", self.icons["quit"], self.parent.OnClose),
+            )
+        )
 
     # TODO: add overwrite also to the menu and sync with toolbar
     def OnSetOverwrite(self, event):
         if self.GetToolState(self.overwrite):
-            self.SetToolNormalBitmap(self.overwrite,
-                                     self.icons['overwriteFalse'].GetBitmap())
-            self.SetToolShortHelp(self.overwrite,
-                                  self.icons['overwriteFalse'].GetLabel())
+            self.SetToolNormalBitmap(
+                self.overwrite, self.icons["overwriteFalse"].GetBitmap()
+            )
+            self.SetToolShortHelp(
+                self.overwrite, self.icons["overwriteFalse"].GetLabel()
+            )
             self.parent.overwrite = True
         else:
-            self.SetToolNormalBitmap(self.overwrite,
-                                     self.icons['overwriteTrue'].GetBitmap())
-            self.SetToolShortHelp(self.overwrite,
-                                  self.icons['overwriteTrue'].GetLabel())
+            self.SetToolNormalBitmap(
+                self.overwrite, self.icons["overwriteTrue"].GetBitmap()
+            )
+            self.SetToolShortHelp(
+                self.overwrite, self.icons["overwriteTrue"].GetLabel()
+            )
             self.parent.overwrite = False
 
 
@@ -687,37 +721,36 @@ class PyEditFrame(wx.Frame):
     # pylint: disable=too-many-public-methods
     # pylint: disable=invalid-name
 
-    def __init__(self, parent, giface, id=wx.ID_ANY,
-                 title=_("Simple Python Editor"),
-                 **kwargs):
+    def __init__(
+        self, parent, giface, id=wx.ID_ANY, title=_("Simple Python Editor"), **kwargs
+    ):
         wx.Frame.__init__(self, parent=parent, id=id, title=title, **kwargs)
         self.parent = parent
 
-        filename = os.path.join(
-            globalvar.WXGUIDIR, 'xml', 'menudata_pyedit.xml')
+        filename = os.path.join(globalvar.WXGUIDIR, "xml", "menudata_pyedit.xml")
         self.menubar = Menu(
-            parent=self,
-            model=MenuTreeModelBuilder(filename).GetModel(separators=True))
+            parent=self, model=MenuTreeModelBuilder(filename).GetModel(separators=True)
+        )
         self.SetMenuBar(self.menubar)
 
         self.toolbar = PyEditToolbar(parent=self)
         # workaround for http://trac.wxwidgets.org/ticket/13888
         # TODO: toolbar is set in toolbar and here
-        if sys.platform != 'darwin':
+        if sys.platform != "darwin":
             self.SetToolBar(self.toolbar)
 
         self.panel = PyStc(parent=self)
         if IsDark():
             SetDarkMode(self.panel)
         self.controller = PyEditController(
-            panel=self.panel, guiparent=self, giface=giface)
+            panel=self.panel, guiparent=self, giface=giface
+        )
 
         # don't start with an empty page
         self.panel.SetText(script_template())
 
         sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(self.panel, proportion=1,
-                  flag=wx.EXPAND)
+        sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)
         sizer.Fit(self)
         sizer.SetSizeHints(self)
         self.SetSizer(sizer)
@@ -785,8 +818,9 @@ class PyEditFrame(wx.Frame):
     def _set_overwrite(self, overwrite):
         self.controller.overwrite = overwrite
 
-    overwrite = property(_get_overwrite, _set_overwrite,
-                         doc="Tells if overwrite should be used")
+    overwrite = property(
+        _get_overwrite, _set_overwrite, doc="Tells if overwrite should be used"
+    )
 
     def OnSetParameters(self, *args, **kwargs):
         self.controller.OnSetParameters(*args, **kwargs)
@@ -804,5 +838,5 @@ def main():
     app.MainLoop()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()

+ 78 - 107
gui/wxpython/gui_core/pystc.py

@@ -93,25 +93,24 @@ class PyStc(stc.StyledTextCtrl):
 
         # this is supposed to get monospace
         font = wx.Font(
-            9,
-            wx.FONTFAMILY_MODERN,
-            wx.FONTSTYLE_NORMAL,
-            wx.FONTWEIGHT_NORMAL)
+            9, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
+        )
         face = font.GetFaceName()
         size = font.GetPointSize()
 
         # setting the monospace here to not mess with the rest of the code
         # TODO: review the whole styling
-        self.faces = {'times': face,
-                      'mono': face,
-                      'helv': face,
-                      'other': face,
-                      'size': 10,
-                      'size2': 8,
-                      }
-
-        self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
-        self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
+        self.faces = {
+            "times": face,
+            "mono": face,
+            "helv": face,
+            "other": face,
+            "size": 10,
+            "size2": 8,
+        }
+
+        self.CmdKeyAssign(ord("B"), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
+        self.CmdKeyAssign(ord("N"), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
 
         self.SetLexer(stc.STC_LEX_PYTHON)
         self.SetKeyWords(0, " ".join(keyword.kwlist))
@@ -138,40 +137,29 @@ class PyStc(stc.StyledTextCtrl):
 
         # like a flattened tree control using square headers
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDEROPEN,
-            stc.STC_MARK_BOXMINUS,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080"
+        )
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDER,
-            stc.STC_MARK_BOXPLUS,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080"
+        )
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDERSUB,
-            stc.STC_MARK_VLINE,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080"
+        )
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDERTAIL,
-            stc.STC_MARK_LCORNER,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080"
+        )
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDEREND,
-            stc.STC_MARK_BOXPLUSCONNECTED,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080"
+        )
         self.MarkerDefine(
             stc.STC_MARKNUM_FOLDEROPENMID,
             stc.STC_MARK_BOXMINUSCONNECTED,
             "white",
-            "#808080")
+            "#808080",
+        )
         self.MarkerDefine(
-            stc.STC_MARKNUM_FOLDERMIDTAIL,
-            stc.STC_MARK_TCORNER,
-            "white",
-            "#808080")
+            stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080"
+        )
 
         self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
         self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
@@ -188,102 +176,77 @@ class PyStc(stc.StyledTextCtrl):
 
         # global default styles for all languages
         self.StyleSetSpec(
-            stc.STC_STYLE_DEFAULT,
-            "face:%(helv)s,size:%(size)d" %
-            self.faces)
+            stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % self.faces
+        )
         self.StyleClearAll()  # reset all to be like the default
 
         # global default styles for all languages
         self.StyleSetSpec(
-            stc.STC_STYLE_DEFAULT,
-            "face:%(helv)s,size:%(size)d" %
-            self.faces)
+            stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % self.faces
+        )
         self.StyleSetSpec(
             stc.STC_STYLE_LINENUMBER,
-            "back:#C0C0C0,face:%(helv)s,size:%(size2)d" %
-            self.faces)
-        self.StyleSetSpec(
-            stc.STC_STYLE_CONTROLCHAR,
-            "face:%(other)s" %
-            self.faces)
-        self.StyleSetSpec(
-            stc.STC_STYLE_BRACELIGHT,
-            "fore:#FFFFFF,back:#0000FF,bold")
-        self.StyleSetSpec(
-            stc.STC_STYLE_BRACEBAD,
-            "fore:#000000,back:#FF0000,bold")
+            "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % self.faces,
+        )
+        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % self.faces)
+        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
+        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
 
         # Python styles
         # Default
         self.StyleSetSpec(
-            stc.STC_P_DEFAULT,
-            "fore:#000000,face:%(helv)s,size:%(size)d" %
-            self.faces)
+            stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces
+        )
         # Comments
         self.StyleSetSpec(
             stc.STC_P_COMMENTLINE,
-            "fore:#007F00,face:%(other)s,size:%(size)d" %
-            self.faces)
+            "fore:#007F00,face:%(other)s,size:%(size)d" % self.faces,
+        )
         # Number
-        self.StyleSetSpec(
-            stc.STC_P_NUMBER,
-            "fore:#007F7F,size:%(size)d" %
-            self.faces)
+        self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % self.faces)
         # String
         self.StyleSetSpec(
-            stc.STC_P_STRING,
-            "fore:#7F007F,face:%(helv)s,size:%(size)d" %
-            self.faces)
+            stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces
+        )
         # Single quoted string
         self.StyleSetSpec(
-            stc.STC_P_CHARACTER,
-            "fore:#7F007F,face:%(helv)s,size:%(size)d" %
-            self.faces)
+            stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces
+        )
         # Keyword
         self.StyleSetSpec(
-            stc.STC_P_WORD,
-            "fore:#00007F,bold,size:%(size)d" %
-            self.faces)
+            stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % self.faces
+        )
         # Triple quotes
-        self.StyleSetSpec(
-            stc.STC_P_TRIPLE,
-            "fore:#7F0000,size:%(size)d" %
-            self.faces)
+        self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % self.faces)
         # Triple double quotes
         self.StyleSetSpec(
-            stc.STC_P_TRIPLEDOUBLE,
-            "fore:#7F0000,size:%(size)d" %
-            self.faces)
+            stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % self.faces
+        )
         # Class name definition
         self.StyleSetSpec(
             stc.STC_P_CLASSNAME,
-            "fore:#0000FF,bold,underline,size:%(size)d" %
-            self.faces)
+            "fore:#0000FF,bold,underline,size:%(size)d" % self.faces,
+        )
         # Function or method name definition
         self.StyleSetSpec(
-            stc.STC_P_DEFNAME,
-            "fore:#007F7F,bold,size:%(size)d" %
-            self.faces)
+            stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % self.faces
+        )
         # Operators
-        self.StyleSetSpec(
-            stc.STC_P_OPERATOR,
-            "bold,size:%(size)d" %
-            self.faces)
+        self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % self.faces)
         # Identifiers
         self.StyleSetSpec(
             stc.STC_P_IDENTIFIER,
-            "fore:#000000,face:%(helv)s,size:%(size)d" %
-            self.faces)
+            "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces,
+        )
         # Comment-blocks
         self.StyleSetSpec(
-            stc.STC_P_COMMENTBLOCK,
-            "fore:#7F7F7F,size:%(size)d" %
-            self.faces)
+            stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % self.faces
+        )
         # End of line where string is not closed
         self.StyleSetSpec(
             stc.STC_P_STRINGEOL,
-            "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" %
-            self.faces)
+            "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % self.faces,
+        )
 
         self.SetCaretForeground("BLUE")
 
@@ -297,7 +260,8 @@ class PyStc(stc.StyledTextCtrl):
             self.modified = True
             if self.statusbar:
                 self.statusbar.SetStatusText(
-                    _('Python script contains local modifications'), 0)
+                    _("Python script contains local modifications"), 0
+                )
 
         event.Skip()
 
@@ -313,8 +277,11 @@ class PyStc(stc.StyledTextCtrl):
             styleBefore = self.GetStyleAt(caretPos - 1)
 
         # check before
-        if charBefore and chr(
-                charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
+        if (
+            charBefore
+            and chr(charBefore) in "[]{}()"
+            and styleBefore == stc.STC_P_OPERATOR
+        ):
             braceAtCaret = caretPos - 1
 
         # check after
@@ -322,8 +289,11 @@ class PyStc(stc.StyledTextCtrl):
             charAfter = self.GetCharAt(caretPos)
             styleAfter = self.GetStyleAt(caretPos)
 
-            if charAfter and chr(
-                    charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
+            if (
+                charAfter
+                and chr(charAfter) in "[]{}()"
+                and styleAfter == stc.STC_P_OPERATOR
+            ):
                 braceAtCaret = caretPos
 
         if braceAtCaret >= 0:
@@ -342,8 +312,7 @@ class PyStc(stc.StyledTextCtrl):
             else:
                 lineClicked = self.LineFromPosition(evt.GetPosition())
 
-                if self.GetFoldLevel(
-                        lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
+                if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
                     if evt.GetShift():
                         self.SetFoldExpanded(lineClicked, True)
                         self.Expand(lineClicked, True, True, 1)
@@ -370,8 +339,10 @@ class PyStc(stc.StyledTextCtrl):
         lineNum = 0
         while lineNum < lineCount:
             level = self.GetFoldLevel(lineNum)
-            if level & stc.STC_FOLDLEVELHEADERFLAG and \
-               (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
+            if (
+                level & stc.STC_FOLDLEVELHEADERFLAG
+                and (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE
+            ):
 
                 if expanding:
                     self.SetFoldExpanded(lineNum, True)

+ 72 - 85
gui/wxpython/gui_core/query.py

@@ -24,75 +24,70 @@ from grass.pydispatch.signal import Signal
 
 
 class QueryDialog(wx.Dialog):
-
     def __init__(self, parent, data=None):
-        wx.Dialog.__init__(self, parent, id=wx.ID_ANY,
-                           title=_("Query results"),
-                           size=(420, 400),
-                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+        wx.Dialog.__init__(
+            self,
+            parent,
+            id=wx.ID_ANY,
+            title=_("Query results"),
+            size=(420, 400),
+            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+        )
         # send query output to console
-        self.redirectOutput = Signal('QueryDialog.redirectOutput')
+        self.redirectOutput = Signal("QueryDialog.redirectOutput")
 
         self.data = data
 
         self.panel = wx.Panel(self, id=wx.ID_ANY)
         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
 
-        helpText = StaticText(self.panel, wx.ID_ANY, label=_(
-            "Right click to copy selected values to clipboard."))
+        helpText = StaticText(
+            self.panel,
+            wx.ID_ANY,
+            label=_("Right click to copy selected values to clipboard."),
+        )
         helpText.SetForegroundColour(
-            wx.SystemSettings.GetColour(
-                wx.SYS_COLOUR_GRAYTEXT))
+            wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
+        )
         self.mainSizer.Add(helpText, proportion=0, flag=wx.ALL, border=5)
 
         self._colNames = [_("Feature"), _("Value")]
         self._model = QueryTreeBuilder(self.data, column=self._colNames[1])
-        self.tree = TreeListView(model=self._model, parent=self.panel,
-                                 columns=self._colNames,
-                                 style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT |
-                                 wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_MULTIPLE)
+        self.tree = TreeListView(
+            model=self._model,
+            parent=self.panel,
+            columns=self._colNames,
+            style=wx.TR_DEFAULT_STYLE
+            | wx.TR_HIDE_ROOT
+            | wx.TR_FULL_ROW_HIGHLIGHT
+            | wx.TR_MULTIPLE,
+        )
 
         self.tree.SetColumnWidth(0, 220)
         self.tree.SetColumnWidth(1, 1000)
         self.tree.ExpandAll()
         self.tree.RefreshItems()
         self.tree.contextMenu.connect(self.ShowContextMenu)
-        self.mainSizer.Add(
-            self.tree,
-            proportion=1,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
+        self.mainSizer.Add(self.tree, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
 
         close = Button(self.panel, id=wx.ID_CLOSE)
         close.Bind(wx.EVT_BUTTON, lambda event: self.Close())
-        copy = Button(
-            self.panel,
-            id=wx.ID_ANY,
-            label=_("Copy all to clipboard"))
+        copy = Button(self.panel, id=wx.ID_ANY, label=_("Copy all to clipboard"))
         copy.Bind(wx.EVT_BUTTON, self.Copy)
         self.Bind(wx.EVT_CLOSE, self.OnClose)
         self.redirect = wx.CheckBox(self.panel, label=_("Redirect to console"))
         self.redirect.SetValue(False)
         self.redirect.Bind(
-            wx.EVT_CHECKBOX,
-            lambda evt: self._onRedirect(
-                evt.IsChecked()))
+            wx.EVT_CHECKBOX, lambda evt: self._onRedirect(evt.IsChecked())
+        )
 
         hbox = wx.BoxSizer(wx.HORIZONTAL)
-        hbox.Add(
-            self.redirect,
-            proportion=0,
-            flag=wx.EXPAND | wx.RIGHT,
-            border=5)
+        hbox.Add(self.redirect, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
         hbox.AddStretchSpacer(1)
         hbox.Add(copy, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
         hbox.Add(close, proportion=0, flag=wx.EXPAND | wx.ALL, border=0)
 
-        self.mainSizer.Add(
-            hbox,
-            proportion=0,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
+        self.mainSizer.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
         self.panel.SetSizer(self.mainSizer)
         self.mainSizer.Fit(self.panel)
         # for Windows
@@ -127,39 +122,29 @@ class QueryDialog(wx.Dialog):
             values = []
             for node in nodes:
                 values.append(
-                    (node.label, node.data[
-                        self._colNames[1]] if node.data else ''))
-            col1 = '\n'.join([val[1] for val in values if val[1]])
-            col2 = '\n'.join([val[0] for val in values if val[0]])
-            table = '\n'.join([val[0] + ': ' + val[1] for val in values])
-            texts.append(
-                (_("Copy from '%s' column") %
-                 self._colNames[1], col1))
-            texts.append(
-                (_("Copy from '%s' column") %
-                 self._colNames[0], col2))
+                    (node.label, node.data[self._colNames[1]] if node.data else "")
+                )
+            col1 = "\n".join([val[1] for val in values if val[1]])
+            col2 = "\n".join([val[0] for val in values if val[0]])
+            table = "\n".join([val[0] + ": " + val[1] for val in values])
+            texts.append((_("Copy from '%s' column") % self._colNames[1], col1))
+            texts.append((_("Copy from '%s' column") % self._colNames[0], col2))
             texts.append((_("Copy selected lines"), table))
         else:
             label1 = nodes[0].label
             texts.append((_("Copy '%s'" % self._cutLabel(label1)), label1))
             if nodes[0].data and nodes[0].data[self._colNames[1]]:
                 label2 = nodes[0].data[self._colNames[1]]
-                texts.insert(
-                    0, (_(
-                        "Copy '%s'" %
-                        self._cutLabel(label2)), label2))
-                texts.append((_("Copy line"), label1 + ': ' + label2))
+                texts.insert(0, (_("Copy '%s'" % self._cutLabel(label2)), label2))
+                texts.append((_("Copy line"), label1 + ": " + label2))
 
         ids = []
         for text in texts:
             id = NewId()
             ids.append(id)
             self.Bind(
-                wx.EVT_MENU,
-                lambda evt,
-                t=text[1],
-                id=id: self._copyText(t),
-                id=id)
+                wx.EVT_MENU, lambda evt, t=text[1], id=id: self._copyText(t), id=id
+            )
 
             menu.Append(id, text[0])
 
@@ -175,20 +160,20 @@ class QueryDialog(wx.Dialog):
         :param redirect: True to start redirecting, False to stop
         """
         if redirect:
-            self.redirectOutput.emit(output=_("Query results:"), style='cmd')
+            self.redirectOutput.emit(output=_("Query results:"), style="cmd")
             self.redirectOutput.emit(output=self._textToRedirect())
         else:
-            self.redirectOutput.emit(output=_(" "), style='cmd')
+            self.redirectOutput.emit(output=_(" "), style="cmd")
 
     def _textToRedirect(self):
         text = printResults(self._model, self._colNames[1])
-        text += '\n' + "-" * 50 + '\n'
+        text += "\n" + "-" * 50 + "\n"
         return text
 
     def _cutLabel(self, label):
         limit = 15
         if len(label) > limit:
-            return label[:limit] + '...'
+            return label[:limit] + "..."
 
         return label
 
@@ -215,6 +200,7 @@ def QueryTreeBuilder(data, column):
 
     :return: tree model
     """
+
     def addNode(parent, data, model):
         for k, v in six.iteritems(data):
             if isinstance(v, dict):
@@ -223,8 +209,7 @@ def QueryTreeBuilder(data, column):
             else:
                 if not isinstance(v, six.string_types):
                     v = str(v)
-                node = model.AppendNode(parent=parent,
-                                        data={"label": k, column: v})
+                node = model.AppendNode(parent=parent, data={"label": k, column: v})
 
     model = TreeModel(DictNode)
     for part in data:
@@ -239,21 +224,21 @@ def printResults(model, valueCol):
     :param model: results tree model
     :param valueCol: column name with value to be printed
     """
+
     def printTree(node, textList, valueCol, indent=0):
-        if node.data.get(valueCol, '') or node.children:
+        if node.data.get(valueCol, "") or node.children:
             textList.append(
-                indent * ' ' + node.label + ': ' + node.data.get(valueCol, ''))
+                indent * " " + node.label + ": " + node.data.get(valueCol, "")
+            )
         for child in node.children:
             printTree(
-                node=child,
-                textList=textList,
-                valueCol=valueCol,
-                indent=indent + 2)
+                node=child, textList=textList, valueCol=valueCol, indent=indent + 2
+            )
 
     textList = []
     for child in model.root.children:
         printTree(node=child, textList=textList, valueCol=valueCol)
-    return '\n'.join(textList)
+    return "\n".join(textList)
 
 
 def PrepareQueryResults(coordinates, result):
@@ -264,12 +249,12 @@ def PrepareQueryResults(coordinates, result):
     data = []
     data.append({_("east, north"): ", ".join(map(str, coordinates))})
     for part in result:
-        if 'Map' in part:
-            itemText = part['Map']
-            if 'Mapset' in part:
-                itemText += '@' + part['Mapset']
-                del part['Mapset']
-            del part['Map']
+        if "Map" in part:
+            itemText = part["Map"]
+            if "Mapset" in part:
+                itemText += "@" + part["Mapset"]
+                del part["Mapset"]
+            del part["Map"]
             if part:
                 data.append({itemText: part})
             else:
@@ -283,26 +268,28 @@ def test():
     app = wx.App()
     from grass.script import vector as gvect
     from grass.script import raster as grast
+
     testdata1 = grast.raster_what(
-        map=('elevation_shade@PERMANENT', 'landclass96'),
+        map=("elevation_shade@PERMANENT", "landclass96"),
         coord=[(638509.051416, 224742.348346)],
-        localized=True)
+        localized=True,
+    )
 
     testdata2 = gvect.vector_what(
-        map=(
-            'firestations', 'bridges'), coord=(
-            633177.897487, 221352.921257), distance=10)
+        map=("firestations", "bridges"),
+        coord=(633177.897487, 221352.921257),
+        distance=10,
+    )
 
     testdata = testdata1 + testdata2
     data = PrepareQueryResults(
-        coordinates=(
-            638509.051416,
-            224742.348346),
-        result=testdata)
+        coordinates=(638509.051416, 224742.348346), result=testdata
+    )
     frame = QueryDialog(parent=None, data=data)
     frame.ShowModal()
     frame.Destroy()
     app.MainLoop()
 
+
 if __name__ == "__main__":
     test()

+ 132 - 126
gui/wxpython/gui_core/simplelmgr.py

@@ -21,8 +21,9 @@ import wx.aui
 from grass.pydispatch.signal import Signal
 
 # needed just for testing
-if __name__ == '__main__':
+if __name__ == "__main__":
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
 from gui_core.toolbars import BaseToolbar, BaseIcons
@@ -50,10 +51,14 @@ class SimpleLayerManager(wx.Panel):
     Layertree, but it's just list, not tree."""
 
     def __init__(
-            self, parent, layerList, lmgrStyle=SIMPLE_LMGR_RASTER |
-            SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT, toolbarCls=None,
-            modal=False):
-        wx.Panel.__init__(self, parent=parent, name='SimpleLayerManager')
+        self,
+        parent,
+        layerList,
+        lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT,
+        toolbarCls=None,
+        modal=False,
+    ):
+        wx.Panel.__init__(self, parent=parent, name="SimpleLayerManager")
 
         self._style = lmgrStyle
         self._layerList = layerList
@@ -72,41 +77,45 @@ class SimpleLayerManager(wx.Panel):
         # needed in order not to change selection when moving layers
         self._blockSelectionChanged = False
 
-        self._checkList.Bind(
-            wx.EVT_LISTBOX,
-            lambda evt: self._selectionChanged())
-        self._checkList.Bind(
-            wx.EVT_LISTBOX_DCLICK,
-            self.OnLayerChangeProperties)
+        self._checkList.Bind(wx.EVT_LISTBOX, lambda evt: self._selectionChanged())
+        self._checkList.Bind(wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties)
         self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked)
         self._checkList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
 
         # signal emitted when somethin in layer list changes
-        self.opacityChanged = Signal('SimpleLayerManager.opacityChanged')
-        self.cmdChanged = Signal('SimpleLayerManager.cmdChanged')
-        self.layerAdded = Signal('SimpleLayerManager.layerAdded')
-        self.layerRemoved = Signal('SimpleLayerManager.layerRemoved')
-        self.layerActivated = Signal('SimpleLayerManager.layerActivated')
-        self.layerMovedUp = Signal('SimpleLayerManager.layerMovedUp')
-        self.layerMovedDown = Signal('SimpleLayerManager.layerMovedDown')
+        self.opacityChanged = Signal("SimpleLayerManager.opacityChanged")
+        self.cmdChanged = Signal("SimpleLayerManager.cmdChanged")
+        self.layerAdded = Signal("SimpleLayerManager.layerAdded")
+        self.layerRemoved = Signal("SimpleLayerManager.layerRemoved")
+        self.layerActivated = Signal("SimpleLayerManager.layerActivated")
+        self.layerMovedUp = Signal("SimpleLayerManager.layerMovedUp")
+        self.layerMovedDown = Signal("SimpleLayerManager.layerMovedDown")
         # emitted by any change (e.g. for rerendering)
-        self.anyChange = Signal('SimpleLayerManager.layerChange')
+        self.anyChange = Signal("SimpleLayerManager.layerChange")
 
         self._layout()
         self.SetMinSize((200, -1))
         self._update()
 
     def _layout(self):
-        self._auimgr.AddPane(self._checkList,
-                             wx.aui.AuiPaneInfo().
-                             Name("checklist").
-                             CenterPane().
-                             CloseButton(False).
-                             BestSize((self._checkList.GetBestSize())))
-        paneInfo = wx.aui.AuiPaneInfo(). \
-            Name("toolbar").Caption(_("Toolbar")).ToolbarPane(). \
-            CloseButton(False).Layer(1).Gripper(False). \
-            BestSize((self._toolbar.GetBestSize()))
+        self._auimgr.AddPane(
+            self._checkList,
+            wx.aui.AuiPaneInfo()
+            .Name("checklist")
+            .CenterPane()
+            .CloseButton(False)
+            .BestSize((self._checkList.GetBestSize())),
+        )
+        paneInfo = (
+            wx.aui.AuiPaneInfo()
+            .Name("toolbar")
+            .Caption(_("Toolbar"))
+            .ToolbarPane()
+            .CloseButton(False)
+            .Layer(1)
+            .Gripper(False)
+            .BestSize((self._toolbar.GetBestSize()))
+        )
         if self._style & SIMPLE_LMGR_TB_LEFT:
             paneInfo.Left()
         elif self._style & SIMPLE_LMGR_TB_RIGHT:
@@ -143,17 +152,14 @@ class SimpleLayerManager(wx.Panel):
 
         menu = Menu()
         llist = [layer.name for layer in self._layerList]
-        texts = [','.join(llist), ','.join(reversed(llist))]
-        labels = [_("Copy map names to clipboard (top to bottom)"),
-                  _("Copy map names to clipboard (bottom to top)")]
+        texts = [",".join(llist), ",".join(reversed(llist))]
+        labels = [
+            _("Copy map names to clipboard (top to bottom)"),
+            _("Copy map names to clipboard (bottom to top)"),
+        ]
         for label, text in zip(labels, texts):
             id = NewId()
-            self.Bind(
-                wx.EVT_MENU,
-                lambda evt,
-                t=text,
-                id=id: self._copyText(t),
-                id=id)
+            self.Bind(wx.EVT_MENU, lambda evt, t=text, id=id: self._copyText(t), id=id)
 
             menu.Append(id, label)
 
@@ -189,38 +195,42 @@ class SimpleLayerManager(wx.Panel):
     def OnAddRaster(self, event):
         """Opens d.rast dialog and adds layer.
         Dummy layer is added first."""
-        cmd = ['d.rast']
-        layer = self.AddRaster(name='', cmd=cmd, hidden=True, dialog=None)
+        cmd = ["d.rast"]
+        layer = self.AddRaster(name="", cmd=cmd, hidden=True, dialog=None)
         GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
-            cmd=cmd, completed=(self.GetOptData, layer, ''))
+            cmd=cmd, completed=(self.GetOptData, layer, "")
+        )
         event.Skip()
 
     def OnAddVector(self, event):
         """Opens d.vect dialog and adds layer.
         Dummy layer is added first."""
-        cmd = ['d.vect']
+        cmd = ["d.vect"]
 
-        layer = self.AddVector(name='', cmd=cmd, hidden=True, dialog=None)
+        layer = self.AddVector(name="", cmd=cmd, hidden=True, dialog=None)
         GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
-            cmd=cmd, completed=(self.GetOptData, layer, ''))
+            cmd=cmd, completed=(self.GetOptData, layer, "")
+        )
         event.Skip()
 
     def OnAddRast3d(self, event):
         """Opens d.rast3d dialog and adds layer.
         Dummy layer is added first."""
-        cmd = ['d.rast3d']
-        layer = self.AddRast3d(name='', cmd=cmd, hidden=True, dialog=None)
+        cmd = ["d.rast3d"]
+        layer = self.AddRast3d(name="", cmd=cmd, hidden=True, dialog=None)
         GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
-            cmd=cmd, completed=(self.GetOptData, layer, ''))
+            cmd=cmd, completed=(self.GetOptData, layer, "")
+        )
         event.Skip()
 
     def OnAddRGB(self, event):
         """Opens d.rgb dialog and adds layer.
         Dummy layer is added first."""
-        cmd = ['d.rgb']
-        layer = self.AddRGB(name='', cmd=cmd, hidden=True, dialog=None)
+        cmd = ["d.rgb"]
+        layer = self.AddRGB(name="", cmd=cmd, hidden=True, dialog=None)
         GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
-            cmd=cmd, completed=(self.GetOptData, layer, ''))
+            cmd=cmd, completed=(self.GetOptData, layer, "")
+        )
         event.Skip()
 
     def OnRemove(self, event):
@@ -228,7 +238,8 @@ class SimpleLayerManager(wx.Panel):
         layers = self._layerList.GetSelectedLayers(activeOnly=False)
         for layer in layers:
             self.layerRemoved.emit(
-                index=self._layerList.GetLayerIndex(layer), layer=layer)
+                index=self._layerList.GetLayerIndex(layer), layer=layer
+            )
             self._layerList.RemoveLayer(layer)
         self._update()
         self.anyChange.emit()
@@ -260,7 +271,8 @@ class SimpleLayerManager(wx.Panel):
             idx = self._layerList.GetLayerIndex(layer)
             if idx < len(self._layerList) - 1:
                 self.layerMovedDown.emit(
-                    index=self._layerList.GetLayerIndex(layer), layer=layer)
+                    index=self._layerList.GetLayerIndex(layer), layer=layer
+                )
                 self._layerList.MoveLayerDown(layer)
         self._update()
         self._blockSelectionChanged = False
@@ -278,7 +290,8 @@ class SimpleLayerManager(wx.Panel):
     def _layerChangeProperties(self, layer):
         """Opens new module dialog or recycles it."""
         GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
-            cmd=layer.cmd, completed=(self.GetOptData, layer, ''))
+            cmd=layer.cmd, completed=(self.GetOptData, layer, "")
+        )
 
     def OnLayerChangeOpacity(self, event):
         """Opacity of a layer is changing."""
@@ -286,10 +299,10 @@ class SimpleLayerManager(wx.Panel):
         if not layers or len(layers) > 1:
             return
         layer = layers[0]
-        dlg = SetOpacityDialog(self, opacity=layer.opacity,
-                               title=_("Set opacity of <%s>") % layer.name)
-        dlg.applyOpacity.connect(lambda value:
-                                 self._setLayerOpacity(layer, value))
+        dlg = SetOpacityDialog(
+            self, opacity=layer.opacity, title=_("Set opacity of <%s>") % layer.name
+        )
+        dlg.applyOpacity.connect(lambda value: self._setLayerOpacity(layer, value))
         dlg.CentreOnParent()
 
         if dlg.ShowModal() == wx.ID_OK:
@@ -302,8 +315,8 @@ class SimpleLayerManager(wx.Panel):
         layer.opacity = value
         self._update()
         self.opacityChanged.emit(
-            index=self._layerList.GetLayerIndex(layer),
-            layer=layer)
+            index=self._layerList.GetLayerIndex(layer), layer=layer
+        )
         self.anyChange.emit()
 
     def _update(self):
@@ -321,8 +334,9 @@ class SimpleLayerManager(wx.Panel):
             if layer.opacity < 1:
                 items.append(
                     "{name} (opacity {opacity}%)".format(
-                        name=layer.name, opacity=int(
-                            layer.opacity * 100)))
+                        name=layer.name, opacity=int(layer.opacity * 100)
+                    )
+                )
             else:
                 items.append(layer.name)
             active.append(layer.IsActive())
@@ -353,44 +367,40 @@ class SimpleLayerManager(wx.Panel):
                         signal = self.cmdChanged
 
                     layer.name = mapName
-                    signal.emit(
-                        index=self._layerList.GetLayerIndex(layer),
-                        layer=layer)
+                    signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
                 except ValueError as e:
                     self._layerList.RemoveLayer(layer)
-                    GError(parent=self,
-                           message=str(e),
-                           showTraceback=False)
+                    GError(parent=self, message=str(e), showTraceback=False)
 
             self._update()
             self.anyChange.emit()
 
     def AddRaster(self, name, cmd, hidden, dialog):
         """Ads new raster layer."""
-        layer = self._layerList.AddNewLayer(name=name, mapType='raster',
-                                            active=True,
-                                            cmd=cmd, hidden=hidden)
+        layer = self._layerList.AddNewLayer(
+            name=name, mapType="raster", active=True, cmd=cmd, hidden=hidden
+        )
         return layer
 
     def AddRast3d(self, name, cmd, hidden, dialog):
         """Ads new raster3d layer."""
-        layer = self._layerList.AddNewLayer(name=name, mapType='raster_3d',
-                                            active=True,
-                                            cmd=cmd, hidden=hidden)
+        layer = self._layerList.AddNewLayer(
+            name=name, mapType="raster_3d", active=True, cmd=cmd, hidden=hidden
+        )
         return layer
 
     def AddVector(self, name, cmd, hidden, dialog):
         """Ads new vector layer."""
-        layer = self._layerList.AddNewLayer(name=name, mapType='vector',
-                                            active=True,
-                                            cmd=cmd, hidden=hidden)
+        layer = self._layerList.AddNewLayer(
+            name=name, mapType="vector", active=True, cmd=cmd, hidden=hidden
+        )
         return layer
 
     def AddRGB(self, name, cmd, hidden, dialog):
         """Ads new vector layer."""
-        layer = self._layerList.AddNewLayer(name=name, mapType='rgb',
-                                            active=True,
-                                            cmd=cmd, hidden=hidden)
+        layer = self._layerList.AddNewLayer(
+            name=name, mapType="rgb", active=True, cmd=cmd, hidden=hidden
+        )
         return layer
 
     def GetLayerInfo(self, layer, key):
@@ -398,7 +408,7 @@ class SimpleLayerManager(wx.Panel):
         value = getattr(layer, key)
         # hack to return empty list, required in OnCancel in forms
         # not sure why it should be empty
-        if key == 'cmd' and len(value) == 1:
+        if key == "cmd" and len(value) == 1:
             return []
         return value
 
@@ -415,8 +425,7 @@ class SimpleLmgrToolbar(BaseToolbar):
     """
 
     def __init__(self, parent, lmgrStyle):
-        """Toolbar constructor
-        """
+        """Toolbar constructor"""
         self._style = lmgrStyle
         if lmgrStyle & (SIMPLE_LMGR_TB_LEFT | SIMPLE_LMGR_TB_RIGHT):
             direction = wx.TB_VERTICAL
@@ -431,63 +440,59 @@ class SimpleLmgrToolbar(BaseToolbar):
 
     def _toolbarData(self):
         """Toolbar data"""
-        data = [('edit', icons['edit'],
-                 self.parent.OnLayerChangeProperties),
-                ('remove', icons['remove'],
-                 self.parent.OnRemove),
-                (None, ),
-                ('up', icons['up'],
-                 self.parent.OnLayerUp),
-                ('down', icons['down'],
-                 self.parent.OnLayerDown),
-                (None, ),
-                ('opacity', icons['opacity'],
-                 self.parent.OnLayerChangeOpacity),
-                ]
+        data = [
+            ("edit", icons["edit"], self.parent.OnLayerChangeProperties),
+            ("remove", icons["remove"], self.parent.OnRemove),
+            (None,),
+            ("up", icons["up"], self.parent.OnLayerUp),
+            ("down", icons["down"], self.parent.OnLayerDown),
+            (None,),
+            ("opacity", icons["opacity"], self.parent.OnLayerChangeOpacity),
+        ]
         if self._style & SIMPLE_LMGR_RASTER3D:
-            data.insert(0, ('addRaster3d', icons['addRast3d'],
-                            self.parent.OnAddRast3d))
+            data.insert(0, ("addRaster3d", icons["addRast3d"], self.parent.OnAddRast3d))
         if self._style & SIMPLE_LMGR_RGB:
-            data.insert(0, ('addRGB', icons['addRGB'],
-                            self.parent.OnAddRGB))
+            data.insert(0, ("addRGB", icons["addRGB"], self.parent.OnAddRGB))
         if self._style & SIMPLE_LMGR_VECTOR:
-            data.insert(0, ('addVector', BaseIcons['addVect'],
-                            self.parent.OnAddVector))
+            data.insert(0, ("addVector", BaseIcons["addVect"], self.parent.OnAddVector))
         if self._style & SIMPLE_LMGR_RASTER:
-            data.insert(0, ('addRaster', BaseIcons['addRast'],
-                            self.parent.OnAddRaster))
+            data.insert(0, ("addRaster", BaseIcons["addRast"], self.parent.OnAddRaster))
 
         return data
 
 
 icons = {
-    'remove': MetaIcon(img='layer-remove',
-                       label=_("Remove"),
-                       desc=_("Remove selected map(s) from list")),
-    'up': MetaIcon(img='layer-up',
-                   label=_("Layer up"),
-                   desc=_("Move selected layer(s) up")),
-    'down': MetaIcon(img='layer-down',
-                     label=_("Layer down"),
-                     desc=_("Move selected layer(s) down")),
-    'edit': MetaIcon(img='layer-edit',
-                     label=_("Edit layer properties"),
-                     desc=_("Edit layer properties")),
-    'opacity': MetaIcon(img='layer-opacity',
-                        label=_("Change opacity"),
-                        desc=_("Change layer opacity")),
-    'addRast3d': MetaIcon(img='layer-raster3d-add',
-                          label=_("Add 3D raster map layer"),
-                          desc=_("Add 3D raster map layer")),
-    'addRGB': MetaIcon(img='layer-rgb-add', label=_('Add RGB map layer'))
+    "remove": MetaIcon(
+        img="layer-remove",
+        label=_("Remove"),
+        desc=_("Remove selected map(s) from list"),
+    ),
+    "up": MetaIcon(
+        img="layer-up", label=_("Layer up"), desc=_("Move selected layer(s) up")
+    ),
+    "down": MetaIcon(
+        img="layer-down", label=_("Layer down"), desc=_("Move selected layer(s) down")
+    ),
+    "edit": MetaIcon(
+        img="layer-edit",
+        label=_("Edit layer properties"),
+        desc=_("Edit layer properties"),
+    ),
+    "opacity": MetaIcon(
+        img="layer-opacity", label=_("Change opacity"), desc=_("Change layer opacity")
+    ),
+    "addRast3d": MetaIcon(
+        img="layer-raster3d-add",
+        label=_("Add 3D raster map layer"),
+        desc=_("Add 3D raster map layer"),
+    ),
+    "addRGB": MetaIcon(img="layer-rgb-add", label=_("Add RGB map layer")),
 }
 
 
 class TestFrame(wx.Frame):
-
     def __init__(self, parent):
-        wx.Frame.__init__(self, parent=parent,
-                          title="Simple layer manager test")
+        wx.Frame.__init__(self, parent=parent, title="Simple layer manager test")
         SimpleLayerManager(parent=self, layerList=LayerList())
 
 
@@ -497,5 +502,6 @@ def test():
     frame.Show()
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     test()

+ 98 - 90
gui/wxpython/gui_core/toolbars.py

@@ -32,56 +32,53 @@ from grass.pydispatch.signal import Signal
 
 
 BaseIcons = {
-    'display': MetaIcon(img='show',
-                        label=_('Display map'),
-                        desc=_('Re-render modified map layers only')),
-    'render': MetaIcon(img='layer-redraw',
-                       label=_('Render map'),
-                       desc=_('Force re-rendering all map layers')),
-    'erase': MetaIcon(img='erase',
-                      label=_('Erase display'),
-                      desc=_('Erase display canvas with given background color')),
-    'pointer': MetaIcon(img='pointer',
-                        label=_('Pointer')),
-    'zoomIn': MetaIcon(img='zoom-in',
-                       label=_('Zoom in'),
-                       desc=_('Drag or click mouse to zoom')),
-    'zoomOut': MetaIcon(img='zoom-out',
-                        label=_('Zoom out'),
-                        desc=_('Drag or click mouse to unzoom')),
-    'zoomBack': MetaIcon(img='zoom-last',
-                         label=_('Return to previous zoom')),
-    'zoomMenu': MetaIcon(img='zoom-more',
-                         label=_('Various zoom options'),
-                         desc=_('Zoom to default or saved region, save to named region, ...')),
-    'zoomExtent': MetaIcon(img='zoom-extent',
-                           label=_('Zoom to selected map layer(s)')),
-    'zoomRegion': MetaIcon(img='zoom-region',
-                           label=_('Zoom to computational region extent')),
-    'pan': MetaIcon(img='pan',
-                    label=_('Pan'),
-                    desc=_('Drag with mouse to pan')),
-    'saveFile': MetaIcon(img='map-export',
-                         label=_('Save display to file')),
-    'print': MetaIcon(img='print',
-                      label=_('Print display')),
-    'font': MetaIcon(img='font',
-                     label=_('Select font')),
-    'help': MetaIcon(img='help',
-                     label=_('Show manual')),
-    'quit': MetaIcon(img='quit',
-                     label=_('Quit')),
-    'addRast': MetaIcon(img='layer-raster-add',
-                        label=_('Add raster map layer')),
-    'addVect': MetaIcon(img='layer-vector-add',
-                        label=_('Add vector map layer')),
-    'overlay': MetaIcon(img='overlay-add',
-                        label=_('Add map elements'),
-                        desc=_('Overlay elements like scale and legend onto map')),
-    'histogramD': MetaIcon(img='layer-raster-histogram',
-                           label=_('Create histogram with d.histogram')),
-    'settings': MetaIcon(img='settings',
-                         label=_("Settings")),
+    "display": MetaIcon(
+        img="show", label=_("Display map"), desc=_("Re-render modified map layers only")
+    ),
+    "render": MetaIcon(
+        img="layer-redraw",
+        label=_("Render map"),
+        desc=_("Force re-rendering all map layers"),
+    ),
+    "erase": MetaIcon(
+        img="erase",
+        label=_("Erase display"),
+        desc=_("Erase display canvas with given background color"),
+    ),
+    "pointer": MetaIcon(img="pointer", label=_("Pointer")),
+    "zoomIn": MetaIcon(
+        img="zoom-in", label=_("Zoom in"), desc=_("Drag or click mouse to zoom")
+    ),
+    "zoomOut": MetaIcon(
+        img="zoom-out", label=_("Zoom out"), desc=_("Drag or click mouse to unzoom")
+    ),
+    "zoomBack": MetaIcon(img="zoom-last", label=_("Return to previous zoom")),
+    "zoomMenu": MetaIcon(
+        img="zoom-more",
+        label=_("Various zoom options"),
+        desc=_("Zoom to default or saved region, save to named region, ..."),
+    ),
+    "zoomExtent": MetaIcon(img="zoom-extent", label=_("Zoom to selected map layer(s)")),
+    "zoomRegion": MetaIcon(
+        img="zoom-region", label=_("Zoom to computational region extent")
+    ),
+    "pan": MetaIcon(img="pan", label=_("Pan"), desc=_("Drag with mouse to pan")),
+    "saveFile": MetaIcon(img="map-export", label=_("Save display to file")),
+    "print": MetaIcon(img="print", label=_("Print display")),
+    "font": MetaIcon(img="font", label=_("Select font")),
+    "help": MetaIcon(img="help", label=_("Show manual")),
+    "quit": MetaIcon(img="quit", label=_("Quit")),
+    "addRast": MetaIcon(img="layer-raster-add", label=_("Add raster map layer")),
+    "addVect": MetaIcon(img="layer-vector-add", label=_("Add vector map layer")),
+    "overlay": MetaIcon(
+        img="overlay-add",
+        label=_("Add map elements"),
+        desc=_("Overlay elements like scale and legend onto map"),
+    ),
+    "histogramD": MetaIcon(
+        img="layer-raster-histogram", label=_("Create histogram with d.histogram")
+    ),
+    "settings": MetaIcon(img="settings", label=_("Settings")),
 }
 
 
@@ -104,11 +101,11 @@ class BaseToolbar(ToolBar):
 
     """
 
-    def __init__(self, parent, toolSwitcher=None,
-                 style=wx.NO_BORDER | wx.TB_HORIZONTAL):
+    def __init__(
+        self, parent, toolSwitcher=None, style=wx.NO_BORDER | wx.TB_HORIZONTAL
+    ):
         self.parent = parent
-        wx.ToolBar.__init__(self, parent=self.parent, id=wx.ID_ANY,
-                            style=style)
+        wx.ToolBar.__init__(self, parent=self.parent, id=wx.ID_ANY, style=style)
 
         self._default = None
         self.SetToolBitmapSize(globalvar.toolbarSize)
@@ -117,8 +114,7 @@ class BaseToolbar(ToolBar):
         self.handlers = {}
 
     def InitToolbar(self, toolData):
-        """Initialize toolbar, add tools to the toolbar
-        """
+        """Initialize toolbar, add tools to the toolbar"""
         for tool in toolData:
             self.CreateTool(*tool)
 
@@ -128,8 +124,7 @@ class BaseToolbar(ToolBar):
         """Toolbar data (virtual)"""
         return None
 
-    def CreateTool(self, label, bitmap, kind,
-                   shortHelp, longHelp, handler, pos=-1):
+    def CreateTool(self, label, bitmap, kind, shortHelp, longHelp, handler, pos=-1):
         """Add tool to the toolbar
 
         :param pos: if -1 add tool, if > 0 insert at given pos
@@ -139,16 +134,17 @@ class BaseToolbar(ToolBar):
         tool = -1
         if label:
             tool = vars(self)[label] = NewId()
-            Debug.msg(3, "CreateTool(): tool=%d, label=%s bitmap=%s" %
-                      (tool, label, bitmap))
+            Debug.msg(
+                3, "CreateTool(): tool=%d, label=%s bitmap=%s" % (tool, label, bitmap)
+            )
             if pos < 0:
-                toolWin = self.AddLabelTool(tool, label, bitmap,
-                                            bmpDisabled, kind,
-                                            shortHelp, longHelp)
+                toolWin = self.AddLabelTool(
+                    tool, label, bitmap, bmpDisabled, kind, shortHelp, longHelp
+                )
             else:
-                toolWin = self.InsertLabelTool(pos, tool, label, bitmap,
-                                               bmpDisabled, kind,
-                                               shortHelp, longHelp)
+                toolWin = self.InsertLabelTool(
+                    pos, tool, label, bitmap, bmpDisabled, kind, shortHelp, longHelp
+                )
             self.handlers[tool] = handler
             self.Bind(wx.EVT_TOOL, handler, toolWin)
             self.Bind(wx.EVT_TOOL, self.OnTool, toolWin)
@@ -163,7 +159,7 @@ class BaseToolbar(ToolBar):
         :param enable: True for enable otherwise disable
         """
         for tool in self._data:
-            if tool[0] == '':  # separator
+            if tool[0] == "":  # separator
                 continue
 
             if enable:
@@ -172,8 +168,7 @@ class BaseToolbar(ToolBar):
                 self.SetToolLongHelp(vars(self)[tool[0]], "")
 
     def OnTool(self, event):
-        """Tool selected
-        """
+        """Tool selected"""
         if self.toolSwitcher:
             Debug.msg(3, "BaseToolbar.OnTool(): id = %s" % event.GetId())
             self.toolSwitcher.ToolChanged(event.GetId())
@@ -195,7 +190,7 @@ class BaseToolbar(ToolBar):
         .. todo::
             Determine why combobox causes problems here
         """
-        if platform.system() == 'Windows':
+        if platform.system() == "Windows":
             size = self.GetBestSize()
             self.SetSize((size[0] + width, size[1]))
 
@@ -226,21 +221,26 @@ class BaseToolbar(ToolBar):
             self.Enable(item[0], enable)
 
     def _getToolbarData(self, data):
-        """Define tool
-        """
+        """Define tool"""
         retData = list()
         for args in data:
             retData.append(self._defineTool(*args))
         return retData
 
-    def _defineTool(self, name=None, icon=None, handler=None,
-                    item=wx.ITEM_NORMAL, pos=-1):
-        """Define tool
-        """
+    def _defineTool(
+        self, name=None, icon=None, handler=None, item=wx.ITEM_NORMAL, pos=-1
+    ):
+        """Define tool"""
         if name:
-            return (name, icon.GetBitmap(),
-                    item, icon.GetLabel(), icon.GetDesc(),
-                    handler, pos)
+            return (
+                name,
+                icon.GetBitmap(),
+                item,
+                icon.GetLabel(),
+                icon.GetDesc(),
+                handler,
+                pos,
+            )
         return ("", "", "", "", "", "")  # separator
 
     def _onMenu(self, data):
@@ -261,14 +261,20 @@ class BaseToolbar(ToolBar):
 
         Button must be custom (not toolbar tool) to set smaller width.
         """
-        arrowPath = os.path.join(IMGDIR, 'small_down_arrow.png')
+        arrowPath = os.path.join(IMGDIR, "small_down_arrow.png")
         if os.path.isfile(arrowPath) and os.path.getsize(arrowPath):
             bitmap = wx.Bitmap(name=arrowPath)
         else:
             bitmap = wx.ArtProvider.GetBitmap(
-                id=wx.ART_MISSING_IMAGE, client=wx.ART_TOOLBAR)
-        button = BitmapButton(parent=self, id=wx.ID_ANY, size=(
-            (-1, self.GetToolSize()[1])), bitmap=bitmap, style=wx.NO_BORDER)
+                id=wx.ART_MISSING_IMAGE, client=wx.ART_TOOLBAR
+            )
+        button = BitmapButton(
+            parent=self,
+            id=wx.ID_ANY,
+            size=((-1, self.GetToolSize()[1])),
+            bitmap=bitmap,
+            style=wx.NO_BORDER,
+        )
         button.SetToolTip(tooltip)
 
         return button
@@ -282,7 +288,7 @@ class ToolSwitcher:
         self._toolsGroups = defaultdict(list)
 
         # emitted when tool is changed
-        self.toggleToolChanged = Signal('ToolSwitcher.toggleToolChanged')
+        self.toggleToolChanged = Signal("ToolSwitcher.toggleToolChanged")
 
     def AddToolToGroup(self, group, toolbar, tool):
         """Adds tool from toolbar to group of exclusive tools.
@@ -301,7 +307,7 @@ class ToolSwitcher:
         :param btnId: id of a tool (typically button)
         :param toggleHandler: handler to be called to switch the button
         """
-        self._groups[group]['custom'].append((btnId, toggleHandler))
+        self._groups[group]["custom"].append((btnId, toggleHandler))
         self._toolsGroups[btnId].append(group)
 
     def RemoveCustomToolFromGroup(self, tool):
@@ -312,9 +318,11 @@ class ToolSwitcher:
         if tool not in self._toolsGroups:
             return
         for group in self._toolsGroups[tool]:
-            self._groups[group]['custom'] = \
-                [(bid, hdlr) for (bid, hdlr)
-                 in self._groups[group]['custom'] if bid != tool]
+            self._groups[group]["custom"] = [
+                (bid, hdlr)
+                for (bid, hdlr) in self._groups[group]["custom"]
+                if bid != tool
+            ]
 
     def RemoveToolbarFromGroup(self, group, toolbar):
         """Removes toolbar from group.
@@ -338,7 +346,7 @@ class ToolSwitcher:
         """
         for group in self._toolsGroups[tool]:
             for tb in self._groups[group]:
-                if tb == 'custom':
+                if tb == "custom":
                     for bid, handler in self._groups[group][tb]:
                         if tool == bid:
                             return True
@@ -353,7 +361,7 @@ class ToolSwitcher:
         """
         for group in self._toolsGroups[tool]:
             for tb in self._groups[group]:
-                if tb == 'custom':
+                if tb == "custom":
                     for btnId, handler in self._groups[group][tb]:
                         if btnId != tool:
                             handler(False)

+ 58 - 41
gui/wxpython/gui_core/treeview.py

@@ -19,6 +19,7 @@ from __future__ import print_function
 import wx
 from wx.lib.mixins.treemixin import VirtualTree, ExpansionState
 from core.globalvar import hasAgw, wxPythonPhoenix
+
 try:
     import wx.lib.agw.customtreectrl as CT
 except ImportError:
@@ -26,14 +27,15 @@ except ImportError:
 if wxPythonPhoenix:
     try:
         from agw.hypertreelist import HyperTreeList as TreeListCtrl
-    except ImportError: # if it's not there locally, try the wxPython lib.
+    except ImportError:  # if it's not there locally, try the wxPython lib.
         from wx.lib.agw.hypertreelist import HyperTreeList as TreeListCtrl
 else:
     from wx.gizmos import TreeListCtrl
 
 # needed just for testing
-if __name__ == '__main__':
+if __name__ == "__main__":
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
 from core.treemodel import TreeModel, DictNode
@@ -58,16 +60,22 @@ class AbstractTreeViewMixin(VirtualTree):
         self._model = model
         super(AbstractTreeViewMixin, self).__init__(parent=parent, *args, **kw)
 
-        self.selectionChanged = Signal('TreeView.selectionChanged')
-        self.itemActivated = Signal('TreeView.itemActivated')
-        self.contextMenu = Signal('TreeView.contextMenu')
-
-        self.Bind(wx.EVT_TREE_SEL_CHANGED, lambda evt:
-                  self._emitSignal(evt.GetItem(), self.selectionChanged))
-        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, lambda evt:
-                  self._emitSignal(evt.GetItem(), self.itemActivated))
-        self.Bind(wx.EVT_TREE_ITEM_MENU, lambda evt:
-                  self._emitSignal(evt.GetItem(), self.contextMenu))
+        self.selectionChanged = Signal("TreeView.selectionChanged")
+        self.itemActivated = Signal("TreeView.itemActivated")
+        self.contextMenu = Signal("TreeView.contextMenu")
+
+        self.Bind(
+            wx.EVT_TREE_SEL_CHANGED,
+            lambda evt: self._emitSignal(evt.GetItem(), self.selectionChanged),
+        )
+        self.Bind(
+            wx.EVT_TREE_ITEM_ACTIVATED,
+            lambda evt: self._emitSignal(evt.GetItem(), self.itemActivated),
+        )
+        self.Bind(
+            wx.EVT_TREE_ITEM_MENU,
+            lambda evt: self._emitSignal(evt.GetItem(), self.contextMenu),
+        )
 
     def SetModel(self, model):
         """Set tree model and refresh.
@@ -85,7 +93,7 @@ class AbstractTreeViewMixin(VirtualTree):
         """
         node = self._model.GetNodeByIndex(index)
         # remove & because of & needed in menu (&Files)
-        label = node.label.replace('&', '')
+        label = node.label.replace("&", "")
         return label
 
     def OnGetChildrenCount(self, index):
@@ -134,8 +142,8 @@ class AbstractTreeViewMixin(VirtualTree):
         self.EnsureVisible(item)
 
     def ExpandAll(self):
-        """Expand all items.
-        """
+        """Expand all items."""
+
         def _expand(item, root=False):
             if not root:
                 self.Expand(item)
@@ -205,35 +213,40 @@ class CTreeView(AbstractTreeViewMixin, CustomTreeCtrl):
 
     def __init__(self, model, parent, **kw):
         if hasAgw:
-            style = 'agwStyle'
+            style = "agwStyle"
         else:
-            style = 'style'
+            style = "style"
 
         if style not in kw:
-            kw[style] = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT |\
-                CT.TR_HAS_BUTTONS | CT.TR_LINES_AT_ROOT | CT.TR_SINGLE
+            kw[style] = (
+                CT.TR_HIDE_ROOT
+                | CT.TR_FULL_ROW_HIGHLIGHT
+                | CT.TR_HAS_BUTTONS
+                | CT.TR_LINES_AT_ROOT
+                | CT.TR_SINGLE
+            )
         super(CTreeView, self).__init__(parent=parent, model=model, **kw)
-        self.SetBackgroundColour(
-            wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW))
+        self.SetBackgroundColour(wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW))
         self.RefreshItems()
 
 
 class TreeListView(AbstractTreeViewMixin, ExpansionState, TreeListCtrl):
-
     def __init__(self, model, parent, columns, **kw):
         self._columns = columns
-        if wxPythonPhoenix and 'style' in kw:
-            flags = kw['style']
-            kw['agwStyle'] = flags
-            del kw['style']
+        if wxPythonPhoenix and "style" in kw:
+            flags = kw["style"]
+            kw["agwStyle"] = flags
+            del kw["style"]
         super(TreeListView, self).__init__(parent=parent, model=model, **kw)
         for column in columns:
             self.AddColumn(column)
         self.SetMainColumn(0)
         self.RefreshItems()
         # to solve events inconsitency
-        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda evt:
-                  self._emitSignal(evt.GetItem(), self.contextMenu))
+        self.Bind(
+            wx.EVT_TREE_ITEM_RIGHT_CLICK,
+            lambda evt: self._emitSignal(evt.GetItem(), self.contextMenu),
+        )
         self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
 
     def OnGetItemText(self, index, column=0):
@@ -245,9 +258,9 @@ class TreeListView(AbstractTreeViewMixin, ExpansionState, TreeListCtrl):
         node = self._model.GetNodeByIndex(index)
         # remove & because of & needed in menu (&Files)
         if column > 0:
-            return node.data.get(self._columns[column], '')
+            return node.data.get(self._columns[column], "")
         else:
-            label = node.label.replace('&', '')
+            label = node.label.replace("&", "")
             return label
 
     def OnRightClick(self, event):
@@ -265,11 +278,11 @@ class TreeFrame(wx.Frame):
     """Frame for testing purposes only."""
 
     def __init__(self, model=None):
-        wx.Frame.__init__(self, None, title='Test tree')
+        wx.Frame.__init__(self, None, title="Test tree")
 
         panel = wx.Panel(self)
-#        self.tree = TreeListView(model=model, parent=panel, columns=['col1', 'xxx'])
-#        self.tree = TreeView(model=model, parent=panel)
+        #        self.tree = TreeListView(model=model, parent=panel, columns=['col1', 'xxx'])
+        #        self.tree = TreeView(model=model, parent=panel)
         self.tree = CTreeView(model=model, parent=panel)
         self.tree.selectionChanged.connect(self.OnSelChanged)
         self.tree.itemActivated.connect(self.OnItemActivated)
@@ -281,11 +294,12 @@ class TreeFrame(wx.Frame):
         szr.SetSizeHints(self)
 
     def OnSelChanged(self):
-        print('selected items: ' +
-              str([node.label for node in self.tree.GetSelected()]))
+        print(
+            "selected items: " + str([node.label for node in self.tree.GetSelected()])
+        )
 
     def OnItemActivated(self, node):
-        print('activated: ' + node.label)
+        print("activated: " + node.label)
 
 
 def main():
@@ -296,18 +310,21 @@ def main():
     n3 = tree.AppendNode(parent=root, data={"label": "node3"})  # pylint: disable=W0612
     n11 = tree.AppendNode(parent=n1, data={"label": "node11", "xxx": "A"})
     n12 = tree.AppendNode(
-        parent=n1, data={"label": "node12", "xxx": "B"})  # pylint: disable=W0612
+        parent=n1, data={"label": "node12", "xxx": "B"}
+    )  # pylint: disable=W0612
     n21 = tree.AppendNode(
-        parent=n2, data={"label": "node21", "xxx": "A"})  # pylint: disable=W0612
+        parent=n2, data={"label": "node21", "xxx": "A"}
+    )  # pylint: disable=W0612
     n111 = tree.AppendNode(
-        parent=n11, data={"label": "node111", "xxx": "A"})  # pylint: disable=W0612
+        parent=n11, data={"label": "node111", "xxx": "A"}
+    )  # pylint: disable=W0612
 
     app = wx.App()
     frame = TreeFrame(model=tree)
-#    frame.tree.Select(n111)
+    #    frame.tree.Select(n111)
     frame.Show()
     app.MainLoop()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()

+ 103 - 86
gui/wxpython/gui_core/vselect.py

@@ -34,19 +34,16 @@ from grass.pydispatch.signal import Signal
 
 
 class VectorSelectList(ListCtrl, listmix.ListCtrlAutoWidthMixin):
-    """Widget for managing vector features selected from map display
-    """
+    """Widget for managing vector features selected from map display"""
 
     def __init__(self, parent):
         ListCtrl.__init__(
-            self,
-            parent=parent,
-            id=wx.ID_ANY,
-            style=wx.LC_REPORT | wx.BORDER_SUNKEN)
+            self, parent=parent, id=wx.ID_ANY, style=wx.LC_REPORT | wx.BORDER_SUNKEN
+        )
         listmix.ListCtrlAutoWidthMixin.__init__(self)
 
-        self.InsertColumn(col=0, heading=_('category'))
-        self.InsertColumn(col=1, heading=_('type'))
+        self.InsertColumn(col=0, heading=_("category"))
+        self.InsertColumn(col=1, heading=_("type"))
         self.SetColumnWidth(0, 100)
         self.SetColumnWidth(1, 100)
 
@@ -54,15 +51,15 @@ class VectorSelectList(ListCtrl, listmix.ListCtrlAutoWidthMixin):
         self.dictIndex = {}
 
     def AddItem(self, item):
-        if 'Category' not in item:
+        if "Category" not in item:
             return
 
-        pos = self.InsertItem(0, str(item['Category']))
-        self.SetItem(pos, 1, str(item['Type']))
-        self.dictIndex[str(item['Category'])] = pos
+        pos = self.InsertItem(0, str(item["Category"]))
+        self.SetItem(pos, 1, str(item["Type"]))
+        self.dictIndex[str(item["Category"])] = pos
 
     def RemoveItem(self, item):
-        index = self.dictIndex.get(str(item['Category']), -1)
+        index = self.dictIndex.get(str(item["Category"]), -1)
         if index > -1:
             self.DeleteItem(index)
 
@@ -77,7 +74,8 @@ class VectorSelectDialog(wx.Dialog):
             id=wx.ID_ANY,
             title=title,
             size=size,
-            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+        )
 
         self._layout()
 
@@ -92,7 +90,7 @@ class VectorSelectDialog(wx.Dialog):
         self.Show()
 
 
-class VectorSelectBase():
+class VectorSelectBase:
     """@brief Main class of vector selection function
 
     It allows selecting vector features from map display and to export
@@ -117,7 +115,7 @@ class VectorSelectBase():
         self._dialog = None
         self.onCloseDialog = None
 
-        self.updateLayer = Signal('VectorSelectBase.updateLayer')
+        self.updateLayer = Signal("VectorSelectBase.updateLayer")
 
         self.painter = VectorSelectHighlighter(self.mapDisp, giface)
 
@@ -132,8 +130,7 @@ class VectorSelectBase():
         self._dialog = VectorSelectDialog(parent=self.parent)
         self._dialog.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
         if createButton:
-            createMap = Button(
-                self._dialog, wx.ID_ANY, _("Create a new map"))
+            createMap = Button(self._dialog, wx.ID_ANY, _("Create a new map"))
             createMap.Bind(wx.EVT_BUTTON, self.OnExportMap)
             self._dialog.AddWidget(createMap, proportion=0.1)
         self.slist = VectorSelectList(self._dialog)
@@ -141,41 +138,40 @@ class VectorSelectBase():
         self.slist.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnDeleteRow)
         self._dialog.AddWidget(self.slist)
 
-        self.onCloseDialog = Signal('VectorSelectBase.onCloseDialog')
+        self.onCloseDialog = Signal("VectorSelectBase.onCloseDialog")
 
     def OnDeleteRow(self, event=None):
-        """Delete row in widget
-        """
+        """Delete row in widget"""
         index = self.slist.GetFocusedItem()
         if index < 0:
             return
         category = self.slist.GetItemText(index)
         for item in self.selectedFeatures:
-            if int(item['Category']) == int(category):
+            if int(item["Category"]) == int(category):
                 self.selectedFeatures.remove(item)
                 break
         self.slist.DeleteItem(index)
         self._draw()
 
     def OnDelete(self, event):
-        """Delete row in widget by press key(delete)
-        """
+        """Delete row in widget by press key(delete)"""
         keycode = event.GetKeyCode()
         if keycode == wx.WXK_DELETE:
             self.OnDeleteRow()
 
     def RegisterMapEvtHandler(self):
         if not self.register:
-            self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
-                                                  self._onMapClickHandler,
-                                                  'cross')
+            self.mapWin.RegisterMouseEventHandler(
+                wx.EVT_LEFT_DOWN, self._onMapClickHandler, "cross"
+            )
         self.register = True
 
     def UnregisterMapEvtHandler(self):
         """Unregistrates _onMapClickHandler from mapWin"""
         if self.register:
-            self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
-                                                    self._onMapClickHandler)
+            self.mapWin.UnregisterMouseEventHandler(
+                wx.EVT_LEFT_DOWN, self._onMapClickHandler
+            )
         self.register = False
 
     def OnClose(self):
@@ -202,12 +198,11 @@ class VectorSelectBase():
         self.RegisterMapEvtHandler()
 
     def _onMapClickHandler(self, event):
-        """Registred handler for clicking on grass disp
-        """
+        """Registred handler for clicking on grass disp"""
         if event == "unregistered":
             return
         vWhatDic = self.QuerySelectedMap()
-        if 'Category' in vWhatDic:
+        if "Category" in vWhatDic:
             self.AddVecInfo(vWhatDic)
             self._draw()
             if self._dialog:
@@ -221,8 +216,9 @@ class VectorSelectBase():
         """
         if len(self.selectedFeatures) > 0:
             for sel in self.selectedFeatures:
-                if sel['Category'] == vInfoDictTMP[
-                        'Category']:  # features is selected=> remove features
+                if (
+                    sel["Category"] == vInfoDictTMP["Category"]
+                ):  # features is selected=> remove features
                     self.selectedFeatures.remove(sel)
                     if self._dialog:  # if dialog initilized->update dialog
                         self.slist.RemoveItem(vInfoDictTMP)
@@ -245,13 +241,15 @@ class VectorSelectBase():
         """Call class 'VectorSelectHighlighter' to draw selected features"""
         self.updateLayer.emit()
         if len(self.selectedFeatures) > 0:
-            self.painter.SetLayer(self.selectedFeatures[0]['Layer'])
+            self.painter.SetLayer(self.selectedFeatures[0]["Layer"])
             self.painter.SetMap(
-                self.selectedFeatures[0]['Map'] + '@' + self.selectedFeatures[0]['Mapset']
+                self.selectedFeatures[0]["Map"]
+                + "@"
+                + self.selectedFeatures[0]["Mapset"]
             )
             tmp = list()
             for i in self.selectedFeatures:
-                tmp.append(i['Category'])
+                tmp.append(i["Category"])
 
             self.painter.SetCats(tmp)
             self.painter.DrawSelected()
@@ -266,12 +264,18 @@ class VectorSelectBase():
             return None
 
         if not layerSelected.maplayer.IsActive():
-            GWarning(_("Selected map <%s> has been disabled for rendering. "
-                       "Operation canceled.") % str(layerSelected), parent=self.mapWin)
+            GWarning(
+                _(
+                    "Selected map <%s> has been disabled for rendering. "
+                    "Operation canceled."
+                )
+                % str(layerSelected),
+                parent=self.mapWin,
+            )
             return None
 
         if layerSelected:
-            if layerSelected.type != 'vector':
+            if layerSelected.type != "vector":
                 mapName = None
                 self.UnregisterMapEvtHandler()
                 GError(_("No vector map layer selected. Operation canceled."))
@@ -287,37 +291,37 @@ class VectorSelectBase():
         return mapName
 
     def QuerySelectedMap(self):
-        """Return w.what info from last clicked coords on display
-
-        """
+        """Return w.what info from last clicked coords on display"""
         self.mapName = self.GetSelectedMap()
         if not self.mapName:
             return {}
 
         mapInfo = self.mapWin.GetMap()
-        threshold = 10.0 * (
-            (mapInfo.region['e'] - mapInfo.region['w']) / mapInfo.width)
+        threshold = 10.0 * ((mapInfo.region["e"] - mapInfo.region["w"]) / mapInfo.width)
         try:
-            query = grass.vector_what(map=[self.mapName],
-                                      coord=self.mapWin.GetLastEN(),
-                                      distance=threshold, skip_attributes=True)
+            query = grass.vector_what(
+                map=[self.mapName],
+                coord=self.mapWin.GetLastEN(),
+                distance=threshold,
+                skip_attributes=True,
+            )
         except grass.ScriptError:
-            GError(parent=self,
-                   message=_("Failed to query vector map(s) <%s>.") % self.map)
+            GError(
+                parent=self, message=_("Failed to query vector map(s) <%s>.") % self.map
+            )
             return None
 
         return query[0]
 
     def GetLineStringSelectedCats(self):
         """Return line of categories separated by comma"""
-        strTMP = ''
+        strTMP = ""
         for cat in self.selectedFeatures:
-            strTMP += str(cat['Category']) + ','
+            strTMP += str(cat["Category"]) + ","
         return strTMP[:-1]
 
-    def _id_generator(self, size=6,
-                      chars=string.ascii_uppercase + string.digits):
-        return ''.join(random.choice(chars) for _ in range(size))
+    def _id_generator(self, size=6, chars=string.ascii_uppercase + string.digits):
+        return "".join(random.choice(chars) for _ in range(size))
 
     def OnExportMap(self, event):
         """Export selected features to a new map
@@ -328,31 +332,43 @@ class VectorSelectBase():
         """
 
         if len(self.selectedFeatures) == 0:
-            GMessage(_('No features selected'))
+            GMessage(_("No features selected"))
             return
-        lst = ''
-        for cat in self.selectedFeatures:  # build text string of categories for v.extract input
-            lst += str(cat['Category']) + ','
+        lst = ""
+        for (
+            cat
+        ) in (
+            self.selectedFeatures
+        ):  # build text string of categories for v.extract input
+            lst += str(cat["Category"]) + ","
         lst = lst[:-1]
-        outMap = str(self.selectedFeatures[0][
-                     'Map']) + '_selection' + str(self._id_generator(3))
-        ret, err = RunCommand('v.extract',
-                              input=self.selectedFeatures[0]['Map'],
-                              layer=self.selectedFeatures[0]['Layer'],
-                              output=outMap,
-                              cats=lst,
-                              getErrorMsg=True)
+        outMap = (
+            str(self.selectedFeatures[0]["Map"])
+            + "_selection"
+            + str(self._id_generator(3))
+        )
+        ret, err = RunCommand(
+            "v.extract",
+            input=self.selectedFeatures[0]["Map"],
+            layer=self.selectedFeatures[0]["Layer"],
+            output=outMap,
+            cats=lst,
+            getErrorMsg=True,
+        )
         if ret == 0:
             tree = self._giface.GetLayerTree()
             if tree:
-                tree.AddLayer(ltype='vector', lname=outMap,
-                              lcmd=['d.vect', 'map=%s' % outMap],
-                              lchecked=True)
+                tree.AddLayer(
+                    ltype="vector",
+                    lname=outMap,
+                    lcmd=["d.vect", "map=%s" % outMap],
+                    lchecked=True,
+                )
 
                 # TODO colorize new map
                 self.Reset()
             else:
-                GMessage(_('Vector map <%s> was created') % outMap)
+                GMessage(_("Vector map <%s> was created") % outMap)
                 self.Reset()
         else:
             GError(_("Unable to create a new vector map.\n\nReason: %s") % err)
@@ -376,7 +392,7 @@ class VectorSelectBase():
     """
 
 
-class VectorSelectHighlighter():
+class VectorSelectHighlighter:
     """Class for highlighting selected features on display
 
     :param mapdisp: Map display frame
@@ -388,29 +404,29 @@ class VectorSelectHighlighter():
         self.giface = giface
         self.layerCat = {}
         self.data = {}
-        self.data['Category'] = list()
-        self.data['Map'] = None
-        self.data['Layer'] = None
+        self.data["Category"] = list()
+        self.data["Map"] = None
+        self.data["Layer"] = None
 
     def SetMap(self, map):
-        self.data['Map'] = map
+        self.data["Map"] = map
 
     def SetLayer(self, layer):
-        self.data['Layer'] = layer
+        self.data["Layer"] = layer
 
     def SetCats(self, cats):
-        self.data['Category'] = cats
+        self.data["Category"] = cats
 
     def Clear(self):
-        self.data['Category'] = list()
-        self.data['Map'] = None
-        self.data['Layer'] = None
+        self.data["Category"] = list()
+        self.data["Map"] = None
+        self.data["Layer"] = None
         self.mapdisp.RemoveQueryLayer()
         self.giface.GetMapWindow().UpdateMap(render=False)
 
     def DrawSelected(self):
         """Highlight selected features"""
-        self.layerCat[int(self.data['Layer'])] = self.data['Category']
+        self.layerCat[int(self.data["Layer"])] = self.data["Category"]
 
         # add map layer with higlighted vector features
         self.AddQueryMapLayer()  # -> self.qlayer
@@ -428,11 +444,12 @@ class VectorSelectHighlighter():
         if self.qlayer:
             self.qlayer.SetCmd(
                 self.mapdisp.AddTmpVectorMapLayer(
-                    self.data['Map'],
-                    self.layerCat,
-                    addLayer=False))
+                    self.data["Map"], self.layerCat, addLayer=False
+                )
+            )
         else:
             self.qlayer = self.mapdisp.AddTmpVectorMapLayer(
-                self.data['Map'], self.layerCat)
+                self.data["Map"], self.layerCat
+            )
 
         return self.qlayer

File diff ditekan karena terlalu besar
+ 213 - 236
gui/wxpython/gui_core/widgets.py


+ 142 - 42
gui/wxpython/gui_core/wrap.py

@@ -23,6 +23,7 @@ import wx.lib.filebrowsebutton as filebrowse
 import wx.lib.scrolledpanel as scrolled
 from wx.lib import expando
 from wx.lib import buttons
+
 try:
     import wx.lib.agw.customtreectrl as CT
 except ImportError:
@@ -37,6 +38,7 @@ if wxPythonPhoenix:
     from wx.adv import BitmapComboBox as BitmapComboBox_
     from wx.adv import HyperlinkCtrl as HyperlinkCtrl_
     from wx.adv import HL_ALIGN_LEFT, HL_CONTEXTMENU
+
     ComboPopup = wx.ComboPopup
     wxComboCtrl = wx.ComboCtrl
 else:
@@ -46,6 +48,7 @@ else:
     from wx.combo import BitmapComboBox as BitmapComboBox_
     from wx import HyperlinkCtrl as HyperlinkCtrl_
     from wx import HL_ALIGN_LEFT, HL_CONTEXTMENU
+
     ComboPopup = wx.combo.ComboPopup
     wxComboCtrl = wx.combo.ComboCtrl
 
@@ -58,10 +61,11 @@ else:
 def IsDark():
     """Detects if used theme is dark.
     Wraps wx method for different versions."""
+
     def luminance(c):
         return (0.299 * c.Red() + 0.587 * c.Green() + 0.114 * c.Blue()) / 255
 
-    if hasattr(wx.SystemSettings, 'GetAppearance'):
+    if hasattr(wx.SystemSettings, "GetAppearance"):
         return wx.SystemSettings.GetAppearance().IsDark()
 
     # for older wx
@@ -147,10 +151,12 @@ class SpinCtrl(wx.SpinCtrl):
 
     def __init__(self, *args, **kwargs):
         if gtk3:
-            if 'size' in kwargs:
-                kwargs['size'] = wx.Size(max(self.gtk3MinSize, kwargs['size'][0]), kwargs['size'][1])
+            if "size" in kwargs:
+                kwargs["size"] = wx.Size(
+                    max(self.gtk3MinSize, kwargs["size"][0]), kwargs["size"][1]
+                )
             else:
-                kwargs['size'] = wx.Size(self.gtk3MinSize, -1)
+                kwargs["size"] = wx.Size(self.gtk3MinSize, -1)
 
         wx.SpinCtrl.__init__(self, *args, **kwargs)
 
@@ -169,10 +175,12 @@ class FloatSpin(fs.FloatSpin):
 
     def __init__(self, *args, **kwargs):
         if gtk3:
-            if 'size' in kwargs:
-                kwargs['size'] = wx.Size(max(self.gtk3MinSize, kwargs['size'][0]), kwargs['size'][1])
+            if "size" in kwargs:
+                kwargs["size"] = wx.Size(
+                    max(self.gtk3MinSize, kwargs["size"][0]), kwargs["size"][1]
+                )
             else:
-                kwargs['size'] = wx.Size(self.gtk3MinSize, -1)
+                kwargs["size"] = wx.Size(self.gtk3MinSize, -1)
 
         fs.FloatSpin.__init__(self, *args, **kwargs)
 
@@ -222,6 +230,7 @@ class CancelButton(Button):
         else:
             self.SetLabel(_("&Cancel"))
 
+
 class CloseButton(Button):
     """Wrapper around a Close labeled Button with stock id wx.ID_CANCEL
     to disable default key binding on certain platforms/wxpython versions"""
@@ -234,6 +243,7 @@ class CloseButton(Button):
         else:
             self.SetLabel(_("&Close"))
 
+
 class ApplyButton(Button):
     """Wrapper around a Button with stock id wx.ID_APPLY,
     to disable default key binding on certain platforms"""
@@ -382,39 +392,50 @@ class ListCtrl(wx.ListCtrl):
 
     def InsertItem(self, index, label, imageIndex=-1):
         if wxPythonPhoenix:
-            return wx.ListCtrl.InsertItem(self, index=index, label=label, imageIndex=imageIndex)
+            return wx.ListCtrl.InsertItem(
+                self, index=index, label=label, imageIndex=imageIndex
+            )
         else:
-            return wx.ListCtrl.InsertStringItem(self, index=index, label=label, imageIndex=imageIndex)
+            return wx.ListCtrl.InsertStringItem(
+                self, index=index, label=label, imageIndex=imageIndex
+            )
 
     def SetItem(self, index, column, label, imageId=-1):
         if wxPythonPhoenix:
-            return wx.ListCtrl.SetItem(self, index=index, column=column, label=label, imageId=imageId)
+            return wx.ListCtrl.SetItem(
+                self, index=index, column=column, label=label, imageId=imageId
+            )
         else:
-            return wx.ListCtrl.SetStringItem(self, index=index, col=column, label=label, imageId=imageId)
+            return wx.ListCtrl.SetStringItem(
+                self, index=index, col=column, label=label, imageId=imageId
+            )
 
     def CheckItem(self, item, check=True):
         """Uses either deprecated listmix.CheckListCtrlMixin
         or new checkbox implementation in wx.ListCtrl since 4.1.0"""
-        if hasattr(self, 'HasCheckBoxes'):
+        if hasattr(self, "HasCheckBoxes"):
             wx.ListCtrl.CheckItem(self, item, check)
         else:
             super(ListCtrl, self).CheckItem(item, check)
 
     def IsItemChecked(self, item):
-        if hasattr(self, 'HasCheckBoxes'):
+        if hasattr(self, "HasCheckBoxes"):
             return wx.ListCtrl.IsItemChecked(self, item)
         else:
             return super(ListCtrl, self).IsChecked(item)
 
 
 if CheckWxVersion([4, 1, 0]):
-    class CheckListCtrlMixin():
+
+    class CheckListCtrlMixin:
         """This class pretends to be deprecated CheckListCtrlMixin mixin and
         only enables checkboxes in new versions of ListCtrl"""
 
         def __init__(self):
             self.EnableCheckBoxes(True)
             self.AssignImageList(wx.ImageList(16, 16), wx.IMAGE_LIST_SMALL)
+
+
 else:
     import wx.lib.mixins.listctrl as listmix
 
@@ -436,7 +457,9 @@ class TreeCtrl(wx.TreeCtrl):
         if wxPythonPhoenix:
             return wx.TreeCtrl.AppendItem(self, parent, text, image, selImage, data)
         else:
-            return wx.TreeCtrl.AppendItem(self, parent, text, image, selImage, wx.TreeItemData(data))
+            return wx.TreeCtrl.AppendItem(
+                self, parent, text, image, selImage, wx.TreeItemData(data)
+            )
 
     def GetItemData(self, item):
         if wxPythonPhoenix:
@@ -466,25 +489,80 @@ class ToolBar(wx.ToolBar):
     def __init__(self, *args, **kwargs):
         wx.ToolBar.__init__(self, *args, **kwargs)
 
-    def AddLabelTool(self, toolId, label, bitmap, bmpDisabled=wx.NullBitmap, kind=0,
-                     shortHelpString='', longHelpString='', clientData=None):
-        if wxPythonPhoenix:
-            return wx.ToolBar.AddTool(self, toolId=toolId, label=label, bitmap=bitmap, bmpDisabled=bmpDisabled,
-                                      kind=kind, shortHelp=shortHelpString, longHelp=longHelpString,
-                                      clientData=clientData)
-        else:
-            return wx.ToolBar.AddLabelTool(self, toolId, label, bitmap, bmpDisabled, kind,
-                                           shortHelpString, longHelpString, clientData)
-
-    def InsertLabelTool(self, pos, toolId, label, bitmap, bmpDisabled=wx.NullBitmap, kind=0,
-                        shortHelpString='', longHelpString='', clientData=None):
-        if wxPythonPhoenix:
-            return wx.ToolBar.InsertTool(self, pos, toolId=toolId, label=label, bitmap=bitmap, bmpDisabled=bmpDisabled,
-                                         kind=kind, shortHelp=shortHelpString, longHelp=longHelpString,
-                                         clientData=clientData)
-        else:
-            return wx.ToolBar.InsertLabelTool(self, pos, toolId, label, bitmap, bmpDisabled, kind,
-                                              shortHelpString, longHelpString, clientData)
+    def AddLabelTool(
+        self,
+        toolId,
+        label,
+        bitmap,
+        bmpDisabled=wx.NullBitmap,
+        kind=0,
+        shortHelpString="",
+        longHelpString="",
+        clientData=None,
+    ):
+        if wxPythonPhoenix:
+            return wx.ToolBar.AddTool(
+                self,
+                toolId=toolId,
+                label=label,
+                bitmap=bitmap,
+                bmpDisabled=bmpDisabled,
+                kind=kind,
+                shortHelp=shortHelpString,
+                longHelp=longHelpString,
+                clientData=clientData,
+            )
+        else:
+            return wx.ToolBar.AddLabelTool(
+                self,
+                toolId,
+                label,
+                bitmap,
+                bmpDisabled,
+                kind,
+                shortHelpString,
+                longHelpString,
+                clientData,
+            )
+
+    def InsertLabelTool(
+        self,
+        pos,
+        toolId,
+        label,
+        bitmap,
+        bmpDisabled=wx.NullBitmap,
+        kind=0,
+        shortHelpString="",
+        longHelpString="",
+        clientData=None,
+    ):
+        if wxPythonPhoenix:
+            return wx.ToolBar.InsertTool(
+                self,
+                pos,
+                toolId=toolId,
+                label=label,
+                bitmap=bitmap,
+                bmpDisabled=bmpDisabled,
+                kind=kind,
+                shortHelp=shortHelpString,
+                longHelp=longHelpString,
+                clientData=clientData,
+            )
+        else:
+            return wx.ToolBar.InsertLabelTool(
+                self,
+                pos,
+                toolId,
+                label,
+                bitmap,
+                bmpDisabled,
+                kind,
+                shortHelpString,
+                longHelpString,
+                clientData,
+            )
 
 
 class Menu(wx.Menu):
@@ -615,14 +693,33 @@ class TextEntryDialog(wx.TextEntryDialog):
     """Wrapper around wx.TextEntryDialog to have more control
     over the widget on different platforms/wxpython versions"""
 
-    def __init__(self, parent, message, caption="Please enter text", value="",
-                 style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition):
-        if wxPythonPhoenix:
-            super(TextEntryDialog, self).__init__(parent=parent, message=message, caption=caption,
-                                                  value=value, style=style, pos=pos)
-        else:
-            super(TextEntryDialog, self).__init__(parent=parent, message=message, caption=caption,
-                                                  defaultValue=value, style=style, pos=pos)
+    def __init__(
+        self,
+        parent,
+        message,
+        caption="Please enter text",
+        value="",
+        style=wx.OK | wx.CANCEL | wx.CENTRE,
+        pos=wx.DefaultPosition,
+    ):
+        if wxPythonPhoenix:
+            super(TextEntryDialog, self).__init__(
+                parent=parent,
+                message=message,
+                caption=caption,
+                value=value,
+                style=style,
+                pos=pos,
+            )
+        else:
+            super(TextEntryDialog, self).__init__(
+                parent=parent,
+                message=message,
+                caption=caption,
+                defaultValue=value,
+                style=style,
+                pos=pos,
+            )
 
 
 class ColourSelect(csel.ColourSelect):
@@ -669,6 +766,7 @@ class Notebook(wx.Notebook):
 class OwnerDrawnComboBox(OwnerDrawnComboBox_):
     """Wrapper around OwnerDrawnComboBox to have more control
     over the widget on different platforms/wxpython versions"""
+
     ODCB_PAINTING_CONTROL = ODCB_PAINTING_CONTROL
     ODCB_PAINTING_SELECTED = ODCB_PAINTING_SELECTED
 
@@ -729,6 +827,7 @@ class DirBrowseButton(filebrowse.DirBrowseButton):
 class ExpandoTextCtrl(expando.ExpandoTextCtrl):
     """Wrapper around expando.ExpandoTextCtrl to have more control
     over the widget on different platforms/wxpython versions"""
+
     EVT_ETC_LAYOUT_NEEDED = expando.EVT_ETC_LAYOUT_NEEDED
 
     def __init__(self, *args, **kwargs):
@@ -760,6 +859,7 @@ class ListBox(wx.ListBox):
 class HyperlinkCtrl(HyperlinkCtrl_):
     """Wrapper around HyperlinkCtrl to have more control
     over the widget on different platforms/wxpython versions"""
+
     HL_ALIGN_LEFT = HL_ALIGN_LEFT
     HL_CONTEXTMENU = HL_CONTEXTMENU
 

+ 7 - 7
gui/wxpython/iclass/__init__.py

@@ -1,9 +1,9 @@
 all = [
-    'digit',
-    'plots',
-    'dialogs',
-    'g.gui.iclass',
-    'toolbars',
-    'statistics',
-    'frame',
+    "digit",
+    "plots",
+    "dialogs",
+    "g.gui.iclass",
+    "toolbars",
+    "statistics",
+    "frame",
 ]

+ 191 - 175
gui/wxpython/iclass/dialogs.py

@@ -31,8 +31,16 @@ from core.gcmd import GError, RunCommand, GMessage
 from gui_core.dialogs import SimpleDialog, GroupDialog
 from gui_core import gselect
 from gui_core.widgets import SimpleValidator
-from gui_core.wrap import CheckBox, Button, StaticText, \
-    StaticBox, TextCtrl, Menu, NewId, ListCtrl
+from gui_core.wrap import (
+    CheckBox,
+    Button,
+    StaticText,
+    StaticBox,
+    TextCtrl,
+    Menu,
+    NewId,
+    ListCtrl,
+)
 
 import grass.script as grass
 
@@ -40,8 +48,14 @@ import grass.script as grass
 class IClassGroupDialog(SimpleDialog):
     """Dialog for imagery group selection"""
 
-    def __init__(self, parent, group=None, subgroup=None,
-                 title=_("Select imagery group"), id=wx.ID_ANY):
+    def __init__(
+        self,
+        parent,
+        group=None,
+        subgroup=None,
+        title=_("Select imagery group"),
+        id=wx.ID_ANY,
+    ):
         """
         Does post init and layout.
 
@@ -55,12 +69,11 @@ class IClassGroupDialog(SimpleDialog):
 
         self.groupSelect = gselect.Select(
             parent=self.panel,
-            type='group',
-            mapsets=[
-                grass.gisenv()['MAPSET']],
+            type="group",
+            mapsets=[grass.gisenv()["MAPSET"]],
             size=globalvar.DIALOG_GSELECT_SIZE,
-            validator=SimpleValidator(
-                callback=self.ValidatorCallback))
+            validator=SimpleValidator(callback=self.ValidatorCallback),
+        )
 
         # TODO use when subgroup will be optional
         # self.subg_chbox = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
@@ -74,13 +87,14 @@ class IClassGroupDialog(SimpleDialog):
         if subgroup:
             self.subGroupSelect.SetValue(subgroup)
 
-        self.editGroup = Button(parent=self.panel, id=wx.ID_ANY,
-                                   label=_("Create/edit group..."))
+        self.editGroup = Button(
+            parent=self.panel, id=wx.ID_ANY, label=_("Create/edit group...")
+        )
 
         self.editGroup.Bind(wx.EVT_BUTTON, self.OnEditGroup)
         self.groupSelect.GetTextCtrl().Bind(
-            wx.EVT_TEXT, lambda event: wx.CallAfter(
-                self.GroupSelected))
+            wx.EVT_TEXT, lambda event: wx.CallAfter(self.GroupSelected)
+        )
 
         self.warning = _("Name of imagery group is missing.")
         self._layout()
@@ -88,38 +102,38 @@ class IClassGroupDialog(SimpleDialog):
 
     def _layout(self):
         """Do layout"""
-        self.dataSizer.Add(StaticText(self.panel, id=wx.ID_ANY,
-                                      label=_("Name of imagery group:")),
-                           proportion=0,
-                           flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
-                           border=5)
-        self.dataSizer.Add(self.groupSelect, proportion=0,
-                           flag=wx.EXPAND | wx.ALL, border=5)
+        self.dataSizer.Add(
+            StaticText(self.panel, id=wx.ID_ANY, label=_("Name of imagery group:")),
+            proportion=0,
+            flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
+            border=5,
+        )
+        self.dataSizer.Add(
+            self.groupSelect, proportion=0, flag=wx.EXPAND | wx.ALL, border=5
+        )
 
         # TODO use when subgroup will be optional
         # self.dataSizer.Add(self.subg_chbox, proportion = 0,
         # flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
 
         self.dataSizer.Add(
-            StaticText(
-                self.panel,
-                id=wx.ID_ANY,
-                label=_("Name of imagery subgroup:")),
+            StaticText(self.panel, id=wx.ID_ANY, label=_("Name of imagery subgroup:")),
             proportion=0,
             flag=wx.EXPAND | wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
-            border=5)
-        self.dataSizer.Add(self.subGroupSelect, proportion=0,
-                           flag=wx.EXPAND | wx.ALL, border=5)
+            border=5,
+        )
+        self.dataSizer.Add(
+            self.subGroupSelect, proportion=0, flag=wx.EXPAND | wx.ALL, border=5
+        )
 
-        self.dataSizer.Add(self.editGroup, proportion=0,
-                           flag=wx.ALL, border=5)
+        self.dataSizer.Add(self.editGroup, proportion=0, flag=wx.ALL, border=5)
 
         self.panel.SetSizer(self.sizer)
         self.sizer.Fit(self)
 
         # TODO use when subgroup will be optional
         # self.subg_panel.Show(False)
-        #self.subg_chbox.Bind(wx.EVT_CHECKBOX, self.OnSubgChbox)
+        # self.subg_chbox.Bind(wx.EVT_CHECKBOX, self.OnSubgChbox)
 
     def OnSubgChbox(self, event):
         self.use_subg = self.subg_chbox.GetValue()
@@ -162,42 +176,43 @@ class IClassGroupDialog(SimpleDialog):
 
     def GetSelectedGroup(self):
         """Return currently selected group (without mapset)"""
-        return self.groupSelect.GetValue().split('@')[0]
+        return self.groupSelect.GetValue().split("@")[0]
 
     def GetGroupBandsErr(self, parent):
         """Get list of raster bands which are in the soubgroup of group with both having same name.
-           If the group does not exists or it does not contain any bands in subgoup with same name,
-           error dialog is shown.
+        If the group does not exists or it does not contain any bands in subgoup with same name,
+        error dialog is shown.
         """
         gr, s = self.GetData()
 
-        group = grass.find_file(name=gr, element='group')
+        group = grass.find_file(name=gr, element="group")
 
         bands = []
-        g = group['name']
+        g = group["name"]
 
         if g:
             if self.use_subg:
-                if s == '':
+                if s == "":
                     GError(_("Please choose a subgroup."), parent=parent)
                     return bands
                 if s not in self.GetSubgroups(g):
                     GError(
-                        _("Subgroup <%s> not found in group <%s>") %
-                        (s, g), parent=parent)
+                        _("Subgroup <%s> not found in group <%s>") % (s, g),
+                        parent=parent,
+                    )
                     return bands
 
             bands = self.GetGroupBands(g, s)
             if not bands:
                 if self.use_subg:
-                    GError(_("No data found in subgroup <%s> of group <%s>.\n"
-                             ".")
-                           % (s, g), parent=parent)
+                    GError(
+                        _("No data found in subgroup <%s> of group <%s>.\n" ".")
+                        % (s, g),
+                        parent=parent,
+                    )
 
                 else:
-                    GError(_("No data found in group <%s>.\n"
-                             ".")
-                           % g, parent=parent)
+                    GError(_("No data found in group <%s>.\n" ".") % g, parent=parent)
         else:
             GError(_("Group <%s> not found") % gr, parent=parent)
 
@@ -208,21 +223,17 @@ class IClassGroupDialog(SimpleDialog):
 
         kwargs = {}
         if subgroup:
-            kwargs['subgroup'] = subgroup
+            kwargs["subgroup"] = subgroup
 
-        res = RunCommand('i.group',
-                         flags='g',
-                         group=group,
-                         read=True, **kwargs).strip()
+        res = RunCommand("i.group", flags="g", group=group, read=True, **kwargs).strip()
         bands = None
-        if res.split('\n')[0]:
-            bands = res.split('\n')
+        if res.split("\n")[0]:
+            bands = res.split("\n")
 
         return bands
 
     def GetSubgroups(self, group):
-        return RunCommand('i.group', group=group,
-                          read=True, flags='sg').splitlines()
+        return RunCommand("i.group", group=group, read=True, flags="sg").splitlines()
 
 
 class IClassMapDialog(SimpleDialog):
@@ -243,8 +254,8 @@ class IClassMapDialog(SimpleDialog):
             parent=self.panel,
             type=element,
             size=globalvar.DIALOG_GSELECT_SIZE,
-            validator=SimpleValidator(
-                callback=self.ValidatorCallback))
+            validator=SimpleValidator(callback=self.ValidatorCallback),
+        )
         self.element.SetFocus()
 
         self.warning = _("Name of map is missing.")
@@ -253,15 +264,19 @@ class IClassMapDialog(SimpleDialog):
 
     def _layout(self):
         """Do layout"""
-        if self.elementType == 'raster':
+        if self.elementType == "raster":
             label = _("Name of raster map:")
-        elif self.elementType == 'vector':
+        elif self.elementType == "vector":
             label = _("Name of vector map:")
-        self.dataSizer.Add(StaticText(self.panel, id=wx.ID_ANY,
-                                      label=label),
-                           proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
-        self.dataSizer.Add(self.element, proportion=0,
-                           flag=wx.EXPAND | wx.ALL, border=5)
+        self.dataSizer.Add(
+            StaticText(self.panel, id=wx.ID_ANY, label=label),
+            proportion=0,
+            flag=wx.EXPAND | wx.ALL,
+            border=5,
+        )
+        self.dataSizer.Add(
+            self.element, proportion=0, flag=wx.EXPAND | wx.ALL, border=5
+        )
 
         self.panel.SetSizer(self.sizer)
         self.sizer.Fit(self)
@@ -291,44 +306,29 @@ class IClassCategoryManagerDialog(wx.Dialog):
         panel = wx.Panel(parent=self, id=wx.ID_ANY)
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        box = StaticBox(panel, id=wx.ID_ANY,
-                        label=" %s " % _("Classes"))
+        box = StaticBox(panel, id=wx.ID_ANY, label=" %s " % _("Classes"))
         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
-        self.catList = CategoryListCtrl(panel, mapwindow=parent,
-                                        stats_data=parent.stats_data)
+        self.catList = CategoryListCtrl(
+            panel, mapwindow=parent, stats_data=parent.stats_data
+        )
         addButton = Button(panel, id=wx.ID_ADD)
         deleteButton = Button(panel, id=wx.ID_DELETE)
 
-        gridSizer.Add(
-            self.catList, pos=(
-                0, 0), span=(
-                3, 1), flag=wx.EXPAND)
+        gridSizer.Add(self.catList, pos=(0, 0), span=(3, 1), flag=wx.EXPAND)
         gridSizer.Add(addButton, pos=(0, 1), flag=wx.EXPAND)
         gridSizer.Add(deleteButton, pos=(1, 1), flag=wx.EXPAND)
 
         gridSizer.AddGrowableCol(0)
         gridSizer.AddGrowableRow(2)
-        sizer.Add(
-            gridSizer,
-            proportion=1,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
-        mainSizer.Add(
-            sizer,
-            proportion=1,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
+        sizer.Add(gridSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(sizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
 
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
         closeButton = Button(panel, id=wx.ID_CLOSE)
         btnSizer.Add(wx.Size(-1, -1), proportion=1, flag=wx.EXPAND)
         btnSizer.Add(closeButton, proportion=0)
-        mainSizer.Add(
-            btnSizer,
-            proportion=0,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
+        mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         addButton.Bind(wx.EVT_BUTTON, self.OnAddCategory)
         deleteButton.Bind(wx.EVT_BUTTON, self.OnDeleteCategory)
@@ -348,8 +348,8 @@ class IClassCategoryManagerDialog(wx.Dialog):
         else:
             cat = 1
         # intentionally not translatable
-        defaultName = 'class' + '_' + str(cat)
-        defaultColor = '0:0:0'
+        defaultName = "class" + "_" + str(cat)
+        defaultColor = "0:0:0"
         self.catList.AddCategory(cat=cat, name=defaultName, color=defaultColor)
 
     def OnDeleteCategory(self, event):
@@ -369,9 +369,7 @@ class IClassCategoryManagerDialog(wx.Dialog):
         return self.catList
 
 
-class CategoryListCtrl(ListCtrl,
-                       listmix.ListCtrlAutoWidthMixin,
-                       listmix.TextEditMixin):
+class CategoryListCtrl(ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.TextEditMixin):
     """Widget for controlling list of classes (categories).
 
     CategoryListCtrl updates choice in mapwindow and removes raster map
@@ -390,10 +388,12 @@ class CategoryListCtrl(ListCtrl,
         :param id: wx id
         """
         ListCtrl.__init__(
-            self, parent, id, style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES |
-            wx.LC_VRULES)
-        self.columns = ((_('Class name'), 'name'),
-                        (_('Color'), 'color'))
+            self,
+            parent,
+            id,
+            style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES,
+        )
+        self.columns = ((_("Class name"), "name"), (_("Color"), "color"))
         self.Populate(columns=self.columns)
         self.mapWindow = mapwindow
         self.stats_data = stats_data
@@ -421,24 +421,22 @@ class CategoryListCtrl(ListCtrl,
 
     def SetVirtualData(self, row, column, text):
         attr = self.columns[column][1]
-        if attr == 'name':
+        if attr == "name":
             try:
-                text.encode('ascii')
+                text.encode("ascii")
             except UnicodeEncodeError:
-                GMessage(parent=self, message=_(
-                    "Please use only ASCII characters."))
+                GMessage(parent=self, message=_("Please use only ASCII characters."))
                 return
 
         cat = self.stats_data.GetCategories()[row]
         self.stats_data.GetStatistics(cat).SetStatistics(stats={attr: text})
 
-        toolbar = self.mapWindow.toolbars['iClass']
+        toolbar = self.mapWindow.toolbars["iClass"]
         toolbar.choice.SetSelection(row)
         self.Select(row)
 
-        if attr == 'name':
-            self.mapWindow.UpdateRasterName(
-                text, toolbar.GetSelectedCategoryIdx())
+        if attr == "name":
+            self.mapWindow.UpdateRasterName(text, toolbar.GetSelectedCategoryIdx())
 
         self.mapWindow.UpdateChangeState(changes=True)
 
@@ -496,7 +494,7 @@ class CategoryListCtrl(ListCtrl,
         currentCol = event.GetColumn()
         if currentCol == 1:
             col = self.OnGetItemText(currentItem, currentCol)
-            col = map(int, col.split(':'))
+            col = map(int, col.split(":"))
 
             col_data = wx.ColourData()
             col_data.SetColour(wx.Colour(*col))
@@ -506,7 +504,7 @@ class CategoryListCtrl(ListCtrl,
 
             if dlg.ShowModal() == wx.ID_OK:
                 color = dlg.GetColourData().GetColour().Get()
-                color = ':'.join(map(str, color))
+                color = ":".join(map(str, color))
                 self.SetVirtualData(currentItem, currentCol, color)
             dlg.Destroy()
             wx.CallAfter(self.SetFocus)
@@ -533,16 +531,13 @@ class CategoryListCtrl(ListCtrl,
 
         if not hasattr(self, "popupZoomtoAreas"):
             self.popupZoomtoAreas = NewId()
-            self.Bind(
-                wx.EVT_MENU,
-                self.OnZoomToAreasByCat,
-                id=self.popupZoomtoAreas)
+            self.Bind(wx.EVT_MENU, self.OnZoomToAreasByCat, id=self.popupZoomtoAreas)
 
         # generate popup-menu
         menu = Menu()
         menu.Append(
-            self.popupZoomtoAreas,
-            _("Zoom to training areas of selected class"))
+            self.popupZoomtoAreas, _("Zoom to training areas of selected class")
+        )
 
         self.PopupMenu(menu)
         menu.Destroy()
@@ -571,7 +566,7 @@ class CategoryListCtrl(ListCtrl,
 
     def OnGetItemAttr(self, item):
         """Set correct class color for a item"""
-        back_c = wx.Colour(*map(int, self.OnGetItemText(item, 1).split(':')))
+        back_c = wx.Colour(*map(int, self.OnGetItemText(item, 1).split(":")))
         text_c = wx.Colour(*ContrastColor(back_c))
 
         # if it is in scope of the method, gui falls, using self solved it
@@ -601,11 +596,17 @@ def ContrastColor(color):
 
 
 class IClassSignatureFileDialog(wx.Dialog):
-
-    def __init__(self, parent, group, subgroup,
-                 file=None, title=_("Save signature file"), id=wx.ID_ANY,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                 **kwargs):
+    def __init__(
+        self,
+        parent,
+        group,
+        subgroup,
+        file=None,
+        title=_("Save signature file"),
+        id=wx.ID_ANY,
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+        **kwargs,
+    ):
         """Dialog for saving signature file
 
         :param parent: window
@@ -622,12 +623,16 @@ class IClassSignatureFileDialog(wx.Dialog):
         # inconsistent group and subgroup name
         # path:
         # grassdata/nc_spm_08/landsat/group/test_group/subgroup/test_group/sig/sigFile
-        self.baseFilePath = os.path.join(env['GISDBASE'],
-                                         env['LOCATION_NAME'],
-                                         env['MAPSET'],
-                                         'group', group,
-                                         'subgroup', subgroup,
-                                         'sig')
+        self.baseFilePath = os.path.join(
+            env["GISDBASE"],
+            env["LOCATION_NAME"],
+            env["MAPSET"],
+            "group",
+            group,
+            "subgroup",
+            subgroup,
+            "sig",
+        )
         self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
 
         self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
@@ -664,35 +669,40 @@ class IClassSignatureFileDialog(wx.Dialog):
             StaticText(
                 parent=self.panel,
                 id=wx.ID_ANY,
-                label=_("Enter name of signature file:")),
+                label=_("Enter name of signature file:"),
+            ),
             proportion=0,
             flag=wx.ALL,
-            border=3)
-        self.fileNameCtrl = TextCtrl(
-            parent=self.panel, id=wx.ID_ANY, size=(400, -1))
+            border=3,
+        )
+        self.fileNameCtrl = TextCtrl(parent=self.panel, id=wx.ID_ANY, size=(400, -1))
         if self.fileName:
             self.fileNameCtrl.SetValue(self.fileName)
-        dataSizer.Add(self.fileNameCtrl,
-                      proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
+        dataSizer.Add(
+            self.fileNameCtrl, proportion=0, flag=wx.ALL | wx.EXPAND, border=3
+        )
 
-        dataSizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
-                                 label=_("Signature file path:")),
-                      proportion=0, flag=wx.ALL, border=3)
+        dataSizer.Add(
+            StaticText(
+                parent=self.panel, id=wx.ID_ANY, label=_("Signature file path:")
+            ),
+            proportion=0,
+            flag=wx.ALL,
+            border=3,
+        )
 
         self.pathPanel = scrolled.ScrolledPanel(self.panel, size=(-1, 40))
         pathSizer = wx.BoxSizer()
-        self.filePathText = StaticText(parent=self.pathPanel, id=wx.ID_ANY,
-                                       label=self.baseFilePath)
+        self.filePathText = StaticText(
+            parent=self.pathPanel, id=wx.ID_ANY, label=self.baseFilePath
+        )
         pathSizer.Add(
-            self.filePathText,
-            proportion=1,
-            flag=wx.ALL | wx.EXPAND,
-            border=1)
+            self.filePathText, proportion=1, flag=wx.ALL | wx.EXPAND, border=1
+        )
         self.pathPanel.SetupScrolling(scroll_x=True, scroll_y=False)
         self.pathPanel.SetSizer(pathSizer)
 
-        dataSizer.Add(self.pathPanel,
-                      proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
+        dataSizer.Add(self.pathPanel, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
 
         # buttons
         btnSizer = wx.StdDialogButtonSizer()
@@ -700,11 +710,9 @@ class IClassSignatureFileDialog(wx.Dialog):
         btnSizer.AddButton(self.btnOK)
         btnSizer.Realize()
 
-        sizer.Add(dataSizer, proportion=1,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(dataSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         self.panel.SetSizer(sizer)
         sizer.Fit(self)
@@ -717,18 +725,21 @@ class IClassSignatureFileDialog(wx.Dialog):
         :param fullPath: return full path of sig. file
         """
         if fullPath:
-            return os.path.join(self.baseFilePath,
-                                self.fileNameCtrl.GetValue())
+            return os.path.join(self.baseFilePath, self.fileNameCtrl.GetValue())
 
         return self.fileNameCtrl.GetValue()
 
 
 class IClassExportAreasDialog(wx.Dialog):
-
-    def __init__(self, parent, vectorName=None,
-                 title=_("Export training areas"),
-                 id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                 **kwargs):
+    def __init__(
+        self,
+        parent,
+        vectorName=None,
+        title=_("Export training areas"),
+        id=wx.ID_ANY,
+        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+        **kwargs,
+    ):
         """Dialog for export of training areas to vector layer
 
         :param parent: window
@@ -773,26 +784,32 @@ class IClassExportAreasDialog(wx.Dialog):
             StaticText(
                 parent=self.panel,
                 id=wx.ID_ANY,
-                label=_("Enter name of new vector map:")),
+                label=_("Enter name of new vector map:"),
+            ),
             proportion=0,
             flag=wx.ALL,
-            border=3)
+            border=3,
+        )
         self.vectorNameCtrl = gselect.Select(
-            parent=self.panel, type='vector',
-            mapsets=[grass.gisenv()['MAPSET']],
-            size=globalvar.DIALOG_GSELECT_SIZE)
+            parent=self.panel,
+            type="vector",
+            mapsets=[grass.gisenv()["MAPSET"]],
+            size=globalvar.DIALOG_GSELECT_SIZE,
+        )
         if self.vectorName:
             self.vectorNameCtrl.SetValue(self.vectorName)
-        dataSizer.Add(self.vectorNameCtrl,
-                      proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
-        self.withTableCtrl = CheckBox(parent=self.panel, id=wx.ID_ANY,
-                                         label=_("Export attribute table"))
+        dataSizer.Add(
+            self.vectorNameCtrl, proportion=0, flag=wx.ALL | wx.EXPAND, border=3
+        )
+        self.withTableCtrl = CheckBox(
+            parent=self.panel, id=wx.ID_ANY, label=_("Export attribute table")
+        )
         self.withTableCtrl.SetValue(True)
         self.withTableCtrl.SetToolTip(
-            _("Export attribute table containing" " computed statistical data"))
+            _("Export attribute table containing" " computed statistical data")
+        )
 
-        dataSizer.Add(self.withTableCtrl,
-                      proportion=0, flag=wx.ALL, border=3)
+        dataSizer.Add(self.withTableCtrl, proportion=0, flag=wx.ALL, border=3)
 
         # buttons
         btnSizer = wx.StdDialogButtonSizer()
@@ -800,11 +817,9 @@ class IClassExportAreasDialog(wx.Dialog):
         btnSizer.AddButton(self.btnOK)
         btnSizer.Realize()
 
-        sizer.Add(dataSizer, proportion=1,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(dataSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
 
-        sizer.Add(btnSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
 
         self.panel.SetSizer(sizer)
         sizer.Fit(self)
@@ -821,18 +836,19 @@ class IClassExportAreasDialog(wx.Dialog):
 
     def OnOK(self, event):
         """Checks if map exists and can be overwritten."""
-        overwrite = UserSettings.Get(
-            group='cmd', key='overwrite', subkey='enabled')
+        overwrite = UserSettings.Get(group="cmd", key="overwrite", subkey="enabled")
         vName = self.GetVectorName()
-        res = grass.find_file(vName, element='vector')
-        if res['fullname'] and overwrite is False:
+        res = grass.find_file(vName, element="vector")
+        if res["fullname"] and overwrite is False:
             qdlg = wx.MessageDialog(
-                parent=self, message=_(
+                parent=self,
+                message=_(
                     "Vector map <%s> already exists."
-                    " Do you want to overwrite it?" %
-                    vName), caption=_(
-                    "Vector <%s> exists" %
-                    vName), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+                    " Do you want to overwrite it?" % vName
+                ),
+                caption=_("Vector <%s> exists" % vName),
+                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE,
+            )
             if qdlg.ShowModal() == wx.ID_YES:
                 event.Skip()
             qdlg.Destroy()

+ 21 - 20
gui/wxpython/iclass/digit.py

@@ -22,6 +22,7 @@ from vdigit.mapwindow import VDigitWindow
 from vdigit.wxdigit import IVDigit
 from vdigit.wxdisplay import DisplayDriver, TYPE_AREA
 from core.gcmd import GWarning
+
 try:
     from grass.lib.gis import G_verbose, G_set_verbose
     from grass.lib.vector import *
@@ -43,8 +44,9 @@ class IClassVDigitWindow(VDigitWindow):
         :param parent: gui parent
         :param map: map renderer instance
         """
-        VDigitWindow.__init__(self, parent=parent, giface=giface,
-                              Map=map, properties=properties)
+        VDigitWindow.__init__(
+            self, parent=parent, giface=giface, Map=map, properties=properties
+        )
 
     def _onLeftDown(self, event):
         action = self.toolbar.GetAction()
@@ -53,13 +55,17 @@ class IClassVDigitWindow(VDigitWindow):
 
         region = grass.region()
         e, n = self.Pixel2Cell(event.GetPosition())
-        if not((region['s'] <= n <= region['n'])
-               and(region['w'] <= e <= region['e'])):
+        if not (
+            (region["s"] <= n <= region["n"]) and (region["w"] <= e <= region["e"])
+        ):
             GWarning(
-                parent=self.parent, message=_(
+                parent=self.parent,
+                message=_(
                     "You are trying to create a training area "
                     "outside the computational region. "
-                    "Please, use g.region to set the appropriate region first."))
+                    "Please, use g.region to set the appropriate region first."
+                ),
+            )
             return
 
         cat = self.GetCurrentCategory()
@@ -71,9 +77,11 @@ class IClassVDigitWindow(VDigitWindow):
                     "In order to create a training area, "
                     "you have to select class first.\n\n"
                     "There is no class yet, "
-                    "do you want to create one?"),
+                    "do you want to create one?"
+                ),
                 caption=_("No class selected"),
-                style=wx.YES_NO)
+                style=wx.YES_NO,
+            )
             if dlg.ShowModal() == wx.ID_YES:
                 self.parent.OnCategoryManager(None)
 
@@ -103,7 +111,7 @@ class IClassVDigitWindow(VDigitWindow):
 
     def GetCategoryColor(self, cat):
         """Get color associated with given category"""
-        r, g, b = [int(x) for x in self.parent.GetClassColor(cat).split(':')][:3]
+        r, g, b = [int(x) for x in self.parent.GetClassColor(cat).split(":")][:3]
         return wx.Colour(r, g, b)
 
 
@@ -112,7 +120,7 @@ class IClassVDigit(IVDigit):
 
     def __init__(self, mapwindow):
         IVDigit.__init__(self, mapwindow, driver=IClassDisplayDriver)
-        self._settings['closeBoundary'] = True  # snap to the first node
+        self._settings["closeBoundary"] = True  # snap to the first node
 
     def _getNewFeaturesLayer(self):
         return 1
@@ -160,7 +168,7 @@ class IClassVDigit(IVDigit):
                 open_fn = Vect_open_tmp_new
 
         if update:
-            if open_fn(poMapInfoNew, name, '') == -1:
+            if open_fn(poMapInfoNew, name, "") == -1:
                 return -1
         else:
             is3D = bool(Vect_is_3d(self.poMapInfo))
@@ -168,7 +176,7 @@ class IClassVDigit(IVDigit):
                 return -1
 
         verbose = G_verbose()
-        G_set_verbose(-1)      # be silent
+        G_set_verbose(-1)  # be silent
 
         if Vect_copy_map_lines(self.poMapInfo, poMapInfoNew) == 1:
             G_set_verbose(verbose)
@@ -195,14 +203,7 @@ class IClassDisplayDriver(DisplayDriver):
     """
 
     def __init__(self, device, deviceTmp, mapObj, window, glog, gprogress):
-        DisplayDriver.__init__(
-            self,
-            device,
-            deviceTmp,
-            mapObj,
-            window,
-            glog,
-            gprogress)
+        DisplayDriver.__init__(self, device, deviceTmp, mapObj, window, glog, gprogress)
         self._cat = -1
 
     def _drawObject(self, robj):

File diff ditekan karena terlalu besar
+ 485 - 360
gui/wxpython/iclass/frame.py


+ 28 - 29
gui/wxpython/iclass/g.gui.iclass.py

@@ -60,6 +60,7 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.settings import UserSettings
@@ -68,40 +69,37 @@ def main():
 
     group_name = subgroup_name = map_name = trainingmap_name = None
 
-    if options['group']:
-        if not options['subgroup']:
+    if options["group"]:
+        if not options["subgroup"]:
             gscript.fatal(_("Name of subgroup required"))
-        group_name = gscript.find_file(name=options['group'],
-                                       element='group')['name']
+        group_name = gscript.find_file(name=options["group"], element="group")["name"]
         if not group_name:
-            gscript.fatal(_("Group <%s> not found") % options['group'])
-        subgroups = gscript.read_command('i.group',
-                                         group=group_name,
-                                         flags='sg').splitlines()
-        if options['subgroup'] not in subgroups:
-            gscript.fatal(_("Subgroup <%s> not found") % options['subgroup'])
-        subgroup_name = options['subgroup']
-
-    if options['map']:
-        map_name = gscript.find_file(name=options['map'],
-                                     element='cell')['fullname']
+            gscript.fatal(_("Group <%s> not found") % options["group"])
+        subgroups = gscript.read_command(
+            "i.group", group=group_name, flags="sg"
+        ).splitlines()
+        if options["subgroup"] not in subgroups:
+            gscript.fatal(_("Subgroup <%s> not found") % options["subgroup"])
+        subgroup_name = options["subgroup"]
+
+    if options["map"]:
+        map_name = gscript.find_file(name=options["map"], element="cell")["fullname"]
         if not map_name:
-            gscript.fatal(_("Raster map <%s> not found") % options['map'])
+            gscript.fatal(_("Raster map <%s> not found") % options["map"])
 
-    if options['trainingmap']:
-        trainingmap_name = gscript.find_file(name=options['trainingmap'],
-                                             element='vector')['fullname']
+    if options["trainingmap"]:
+        trainingmap_name = gscript.find_file(
+            name=options["trainingmap"], element="vector"
+        )["fullname"]
         if not trainingmap_name:
-            gscript.fatal(
-                _("Vector map <%s> not found") %
-                options['trainingmap'])
+            gscript.fatal(_("Vector map <%s> not found") % options["trainingmap"])
 
     # define display driver
-    driver = UserSettings.Get(group='display', key='driver', subkey='type')
-    if driver == 'png':
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'png'
+    driver = UserSettings.Get(group="display", key="driver", subkey="type")
+    if driver == "png":
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
     else:
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'cairo'
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
 
     # launch application
     app = wx.App()
@@ -113,7 +111,7 @@ def main():
         giface=giface,
         title=_("Supervised Classification Tool - GRASS GIS"),
     )
-    if not flags['m']:
+    if not flags["m"]:
         frame.CenterOnScreen()
     if group_name:
         frame.SetGroup(group_name, subgroup_name)
@@ -125,9 +123,10 @@ def main():
         frame.ImportAreas(trainingmap_name)
 
     frame.Show()
-    if flags['m']:
+    if flags["m"]:
         frame.Maximize()
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

+ 36 - 52
gui/wxpython/iclass/plots.py

@@ -59,50 +59,41 @@ class PlotPanel(scrolled.ScrolledPanel):
     def _createPlotPanel(self):
 
         self.canvasPanel = wx.Panel(parent=self)
-        self.mainSizer.Add(
-            self.canvasPanel,
-            proportion=1,
-            flag=wx.EXPAND,
-            border=0)
+        self.mainSizer.Add(self.canvasPanel, proportion=1, flag=wx.EXPAND, border=0)
         self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
         self.canvasPanel.SetSizer(self.canvasSizer)
 
     def _createControlPanel(self):
-        self.plotSwitch = wx.Choice(self, id=wx.ID_ANY,
-                                    choices=[_("Histograms"),
-                                             _("Coincident plots"),
-                                             _("Scatter plots")])
+        self.plotSwitch = wx.Choice(
+            self,
+            id=wx.ID_ANY,
+            choices=[_("Histograms"), _("Coincident plots"), _("Scatter plots")],
+        )
         self.mainSizer.Add(
-            self.plotSwitch,
-            proportion=0,
-            flag=wx.EXPAND | wx.ALL,
-            border=5)
+            self.plotSwitch, proportion=0, flag=wx.EXPAND | wx.ALL, border=5
+        )
         self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
 
     def _createScatterPlotPanel(self):
-        """Init interactive scatter plot tool
-        """
+        """Init interactive scatter plot tool"""
         try:
             from iscatt.frame import IClassIScattPanel
+
             self.iscatt_panel = IClassIScattPanel(
-                parent=self, giface=self._giface,
-                iclass_mapwin=self.parent.GetFirstWindow())
+                parent=self,
+                giface=self._giface,
+                iclass_mapwin=self.parent.GetFirstWindow(),
+            )
             self.mainSizer.Add(
-                self.iscatt_panel,
-                proportion=1,
-                flag=wx.EXPAND,
-                border=0)
+                self.iscatt_panel, proportion=1, flag=wx.EXPAND, border=0
+            )
             self.iscatt_panel.Hide()
         except ImportError as e:
             self.scatt_error = _(
                 "Scatter plot functionality is disabled.\n\nReason: "
-                "Unable to import packages needed for scatter plot.\n%s" %
-                e)
-            wx.CallAfter(
-                GError,
-                self.scatt_error,
-                showTraceback=False,
-                parent=self)
+                "Unable to import packages needed for scatter plot.\n%s" % e
+            )
+            wx.CallAfter(GError, self.scatt_error, showTraceback=False, parent=self)
             self.iscatt_panel = None
 
     def OnPlotTypeSelected(self, event):
@@ -148,7 +139,7 @@ class PlotPanel(scrolled.ScrolledPanel):
         for canvas in self.canvasList:
             canvas.enableZoom = enable
 
-        #canvas.zoom = type
+        # canvas.zoom = type
 
     def EnablePan(self, enable=True):
         for canvas in self.canvasList:
@@ -181,11 +172,7 @@ class PlotPanel(scrolled.ScrolledPanel):
             canvas.fontSizeAxis = 8
             self.canvasList.append(canvas)
 
-            self.canvasSizer.Add(
-                canvas,
-                proportion=1,
-                flag=wx.EXPAND,
-                border=0)
+            self.canvasSizer.Add(canvas, proportion=1, flag=wx.EXPAND, border=0)
 
         self.SetVirtualSize(self.GetBestVirtualSize())
         self.Layout()
@@ -218,7 +205,7 @@ class PlotPanel(scrolled.ScrolledPanel):
     def DrawCoincidencePlots(self):
         """Draw coincidence plots"""
         for bandIdx in range(len(self.bandList)):
-            self.canvasList[bandIdx].ySpec = 'none'
+            self.canvasList[bandIdx].ySpec = "none"
             lines = []
             level = 0.5
             lines.append(self.DrawInvisibleLine(level))
@@ -230,8 +217,7 @@ class PlotPanel(scrolled.ScrolledPanel):
                     continue
                 color = stat.color
                 level = i + 1
-                line = self.DrawCoincidenceLine(
-                    level, color, stat.bands[bandIdx])
+                line = self.DrawCoincidenceLine(level, color, stat.bands[bandIdx])
                 lines.append(line)
 
             # invisible
@@ -251,7 +237,7 @@ class PlotPanel(scrolled.ScrolledPanel):
         minim = bandValues.min
         maxim = bandValues.max
         points = [(minim, level), (maxim, level)]
-        color = wx.Colour(*map(int, color.split(':')))
+        color = wx.Colour(*map(int, color.split(":")))
         return plot.PolyLine(points, colour=color, width=4)
 
     def DrawInvisibleLine(self, level):
@@ -267,9 +253,10 @@ class PlotPanel(scrolled.ScrolledPanel):
         self.histogramLines = []
         for bandIdx in range(len(self.bandList)):
             self.canvasList[bandIdx].Clear()
-            self.canvasList[bandIdx].ySpec = 'auto'
+            self.canvasList[bandIdx].ySpec = "auto"
             histgramLine = self.CreateHistogramLine(
-                bandValues=statistics.bands[bandIdx])
+                bandValues=statistics.bands[bandIdx]
+            )
 
             meanLine = self.CreateMean(bandValues=statistics.bands[bandIdx])
 
@@ -277,17 +264,15 @@ class PlotPanel(scrolled.ScrolledPanel):
 
             maxLine = self.CreateMax(bandValues=statistics.bands[bandIdx])
 
-            self.histogramLines.append(
-                [histgramLine, meanLine, minLine, maxLine])
+            self.histogramLines.append([histgramLine, meanLine, minLine, maxLine])
 
-            maxRangeLine = self.CreateMaxRange(
-                bandValues=statistics.bands[bandIdx])
-            minRangeLine = self.CreateMinRange(
-                bandValues=statistics.bands[bandIdx])
+            maxRangeLine = self.CreateMaxRange(bandValues=statistics.bands[bandIdx])
+            minRangeLine = self.CreateMinRange(bandValues=statistics.bands[bandIdx])
 
             plotGraph = plot.PlotGraphics(
                 self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
-                title=self.bandList[bandIdx])
+                title=self.bandList[bandIdx],
+            )
             self.canvasList[bandIdx].Draw(plotGraph)
 
     def CreateMinRange(self, bandValues):
@@ -344,12 +329,11 @@ class PlotPanel(scrolled.ScrolledPanel):
         """
         for bandIdx in range(len(self.bandList)):
             self.canvasList[bandIdx].Clear()
-            maxRangeLine = self.CreateMaxRange(
-                bandValues=statistics.bands[bandIdx])
-            minRangeLine = self.CreateMinRange(
-                bandValues=statistics.bands[bandIdx])
+            maxRangeLine = self.CreateMaxRange(bandValues=statistics.bands[bandIdx])
+            minRangeLine = self.CreateMinRange(bandValues=statistics.bands[bandIdx])
 
             plotGraph = plot.PlotGraphics(
                 self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
-                title=self.bandList[bandIdx])
+                title=self.bandList[bandIdx],
+            )
             self.canvasList[bandIdx].Draw(plotGraph)

+ 6 - 8
gui/wxpython/iclass/statistics.py

@@ -32,8 +32,7 @@ from grass.pydispatch.signal import Signal
 
 
 class StatisticsData:
-    """Stores all statistics.
-    """
+    """Stores all statistics."""
 
     def __init__(self):
         self.statisticsDict = {}
@@ -41,8 +40,7 @@ class StatisticsData:
 
         self.statisticsAdded = Signal("StatisticsData.statisticsAdded")
         self.statisticsDeleted = Signal("StatisticsData.statisticsDeleted")
-        self.allStatisticsDeleted = Signal(
-            "StatisticsData.allStatisticsDeleted")
+        self.allStatisticsDeleted = Signal("StatisticsData.allStatisticsDeleted")
 
         self.statisticsSet = Signal("StatisticsData.statisticsSet")
 
@@ -53,8 +51,8 @@ class StatisticsData:
         st = Statistics()
         st.SetBaseStatistics(cat=cat, name=name, color=color)
         st.statisticsSet.connect(
-            lambda stats: self.statisticsSet.emit(
-                cat=cat, stats=stats))
+            lambda stats: self.statisticsSet.emit(cat=cat, stats=stats)
+        )
 
         self.statisticsDict[cat] = st
         self.statisticsList.append(cat)
@@ -118,8 +116,8 @@ class Statistics:
         self.color = color
 
         rasterPath = grass.tempfile(create=False)
-        name = name.replace(' ', '_')
-        self.rasterName = name + '_' + os.path.basename(rasterPath)
+        name = name.replace(" ", "_")
+        self.rasterName = name + "_" + os.path.basename(rasterPath)
 
     def SetFromcStatistics(self, cStatistics):
         """Sets all statistical values.

+ 98 - 127
gui/wxpython/iclass/toolbars.py

@@ -31,62 +31,53 @@ from gui_core.wrap import StaticText
 import grass.script as grass
 
 iClassIcons = {
-    'opacity': MetaIcon(
-        img='layer-opacity',
-        label=_('Set opacity level')),
-    'classManager': MetaIcon(
-        img='table-manager',
-        label=_('Class manager')),
-    'selectGroup': MetaIcon(
-        img='layer-group-add',
-        label=_('Select imagery group')),
-    'run': MetaIcon(
-        img='execute',
-        label=_('Run analysis, update histogram and coincidence plots')),
-    'sigFile': MetaIcon(
-        img='script-save',
-        label=_('Save signature file for i.maxlik')),
-    'delCmd': MetaIcon(
-        img='layer-remove',
-        label=_('Remove selected map layer')),
-    'exportAreas': MetaIcon(
-        img='layer-export',
-        label=_('Export training areas to vector map')),
-    'importAreas': MetaIcon(
-        img='layer-import',
-        label=_('Import training areas from vector map')),
-    'addRgb': MetaIcon(
-        img='layer-rgb-add',
-        label=_('Add RGB map layer'))}
+    "opacity": MetaIcon(img="layer-opacity", label=_("Set opacity level")),
+    "classManager": MetaIcon(img="table-manager", label=_("Class manager")),
+    "selectGroup": MetaIcon(img="layer-group-add", label=_("Select imagery group")),
+    "run": MetaIcon(
+        img="execute", label=_("Run analysis, update histogram and coincidence plots")
+    ),
+    "sigFile": MetaIcon(img="script-save", label=_("Save signature file for i.maxlik")),
+    "delCmd": MetaIcon(img="layer-remove", label=_("Remove selected map layer")),
+    "exportAreas": MetaIcon(
+        img="layer-export", label=_("Export training areas to vector map")
+    ),
+    "importAreas": MetaIcon(
+        img="layer-import", label=_("Import training areas from vector map")
+    ),
+    "addRgb": MetaIcon(img="layer-rgb-add", label=_("Add RGB map layer")),
+}
 
 
 class IClassMapToolbar(BaseToolbar):
-    """IClass Map toolbar
-    """
+    """IClass Map toolbar"""
 
     def __init__(self, parent, toolSwitcher):
-        """IClass Map toolbar constructor
-        """
+        """IClass Map toolbar constructor"""
         BaseToolbar.__init__(self, parent, toolSwitcher)
 
         self.InitToolbar(self._toolbarData())
         self._default = self.pan
 
         # add tool to toggle active map window
-        self.togglemap = wx.Choice(parent=self, id=wx.ID_ANY,
-                                   choices=[_('Training'), _('Preview')])
+        self.togglemap = wx.Choice(
+            parent=self, id=wx.ID_ANY, choices=[_("Training"), _("Preview")]
+        )
 
         self.InsertControl(9, self.togglemap)
 
         self.SetToolShortHelp(
-            self.togglemap.GetId(), '%s %s %s' %
-            (_('Set map canvas for '),
-             BaseIcons["zoomBack"].GetLabel(),
-             _('/ Zoom to map')))
+            self.togglemap.GetId(),
+            "%s %s %s"
+            % (
+                _("Set map canvas for "),
+                BaseIcons["zoomBack"].GetLabel(),
+                _("/ Zoom to map"),
+            ),
+        )
 
         for tool in (self.pan, self.zoomIn, self.zoomOut):
-            self.toolSwitcher.AddToolToGroup(
-                group='mouseUse', toolbar=self, tool=tool)
+            self.toolSwitcher.AddToolToGroup(group="mouseUse", toolbar=self, tool=tool)
         # realize the toolbar
         self.Realize()
 
@@ -107,41 +98,29 @@ class IClassMapToolbar(BaseToolbar):
     def _toolbarData(self):
         """Toolbar data"""
         icons = BaseIcons
-        return self._getToolbarData((("displaymap", icons["display"],
-                                      self.parent.OnDraw),
-                                     ("rendermap", icons["render"],
-                                      self.parent.OnRender),
-                                     ("erase", icons["erase"],
-                                      self.parent.OnErase),
-                                     (None, ),
-                                     ("pan", icons["pan"],
-                                      self.parent.OnPan,
-                                      wx.ITEM_CHECK),
-                                     ("zoomIn", icons["zoomIn"],
-                                      self.parent.OnZoomIn,
-                                      wx.ITEM_CHECK),
-                                     ("zoomOut", icons["zoomOut"],
-                                      self.parent.OnZoomOut,
-                                      wx.ITEM_CHECK),
-                                     ("zoomRegion", icons["zoomRegion"],
-                                      self.parent.OnZoomToWind),
-                                     ("zoomMenu", icons["zoomMenu"],
-                                      self.parent.OnZoomMenu),
-                                     (None, ),
-                                     ("zoomBack", icons["zoomBack"],
-                                      self.parent.OnZoomBack),
-                                     ("zoomToMap", icons["zoomExtent"],
-                                      self.parent.OnZoomToMap)
-                                     ))
+        return self._getToolbarData(
+            (
+                ("displaymap", icons["display"], self.parent.OnDraw),
+                ("rendermap", icons["render"], self.parent.OnRender),
+                ("erase", icons["erase"], self.parent.OnErase),
+                (None,),
+                ("pan", icons["pan"], self.parent.OnPan, wx.ITEM_CHECK),
+                ("zoomIn", icons["zoomIn"], self.parent.OnZoomIn, wx.ITEM_CHECK),
+                ("zoomOut", icons["zoomOut"], self.parent.OnZoomOut, wx.ITEM_CHECK),
+                ("zoomRegion", icons["zoomRegion"], self.parent.OnZoomToWind),
+                ("zoomMenu", icons["zoomMenu"], self.parent.OnZoomMenu),
+                (None,),
+                ("zoomBack", icons["zoomBack"], self.parent.OnZoomBack),
+                ("zoomToMap", icons["zoomExtent"], self.parent.OnZoomToMap),
+            )
+        )
 
 
 class IClassToolbar(BaseToolbar):
-    """IClass toolbar
-    """
+    """IClass toolbar"""
 
     def __init__(self, parent, stats_data):
-        """IClass toolbar constructor
-        """
+        """IClass toolbar constructor"""
         self.stats_data = stats_data
 
         BaseToolbar.__init__(self, parent)
@@ -153,10 +132,11 @@ class IClassToolbar(BaseToolbar):
         self.choice.Bind(wx.EVT_CHOICE, self.OnSelectCategory)
 
         # stupid workaround to insert small space between controls
-        self.InsertControl(4, StaticText(self, id=wx.ID_ANY, label=' '))
+        self.InsertControl(4, StaticText(self, id=wx.ID_ANY, label=" "))
 
-        self.combo = wx.ComboBox(self, id=wx.ID_ANY, size=(130, -1),
-                                 style=wx.TE_PROCESS_ENTER)
+        self.combo = wx.ComboBox(
+            self, id=wx.ID_ANY, size=(130, -1), style=wx.TE_PROCESS_ENTER
+        )
         self.InitStddev()
         self.InsertControl(5, self.combo)
 
@@ -176,22 +156,23 @@ class IClassToolbar(BaseToolbar):
     def _toolbarData(self):
         """Toolbar data"""
         icons = iClassIcons
-        return self._getToolbarData((("selectGroup", icons['selectGroup'],
-                                      lambda event: self.parent.AddBands()),
-                                     (None, ),
-                                     ("classManager", icons['classManager'],
-                                      self.parent.OnCategoryManager),
-                                     (None, ),
-                                     ("runAnalysis", icons['run'],
-                                      self.parent.OnRunAnalysis),
-                                     (None, ),
-                                     ("importAreas", icons['importAreas'],
-                                      self.parent.OnImportAreas),
-                                     ("exportAreas", icons['exportAreas'],
-                                      self.parent.OnExportAreas),
-                                     ("sigFile", icons['sigFile'],
-                                      self.parent.OnSaveSigFile),
-                                     ))
+        return self._getToolbarData(
+            (
+                (
+                    "selectGroup",
+                    icons["selectGroup"],
+                    lambda event: self.parent.AddBands(),
+                ),
+                (None,),
+                ("classManager", icons["classManager"], self.parent.OnCategoryManager),
+                (None,),
+                ("runAnalysis", icons["run"], self.parent.OnRunAnalysis),
+                (None,),
+                ("importAreas", icons["importAreas"], self.parent.OnImportAreas),
+                ("exportAreas", icons["exportAreas"], self.parent.OnExportAreas),
+                ("sigFile", icons["sigFile"], self.parent.OnSaveSigFile),
+            )
+        )
 
     def OnMotion(self, event):
         print(self.choice.GetStringSelection())
@@ -207,7 +188,7 @@ class IClassToolbar(BaseToolbar):
 
         if cat:
             stat = self.stats_data.GetStatistics(cat)
-            back_c = wx.Colour([int(x) for x in stat.color.split(':')])
+            back_c = wx.Colour([int(x) for x in stat.color.split(":")])
             text_c = wx.Colour(*ContrastColor(back_c))
         else:
             back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
@@ -258,17 +239,14 @@ class IClassToolbar(BaseToolbar):
         self.parent.StddevChanged(cat=idx, nstd=nstd)
 
     def UpdateStddev(self, nstd):
-        self.combo.SetValue(' '.join(("%.2f" % nstd, _('std dev'))))
+        self.combo.SetValue(" ".join(("%.2f" % nstd, _("std dev"))))
 
     def InitStddev(self):
         for nstd in range(50, 250, 25):
-            nstd /= 100.
+            nstd /= 100.0
             self.combo.Append(
-                item=' '.join(
-                    ("%.2f" %
-                     nstd,
-                     _('std dev'))),
-                clientData=nstd)
+                item=" ".join(("%.2f" % nstd, _("std dev"))), clientData=nstd
+            )
         self.combo.SetSelection(4)  # 1.5
 
     def EnableControls(self, enable=True):
@@ -304,12 +282,10 @@ class IClassToolbar(BaseToolbar):
 
 
 class IClassMapManagerToolbar(BaseToolbar):
-    """IClass toolbar
-    """
+    """IClass toolbar"""
 
     def __init__(self, parent, mapManager):
-        """IClass toolbar constructor
-        """
+        """IClass toolbar constructor"""
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
@@ -325,35 +301,31 @@ class IClassMapManagerToolbar(BaseToolbar):
 
     def _toolbarData(self):
         """Toolbar data"""
-        return self._getToolbarData((("addRast", BaseIcons['addRast'],
-                                      self.OnAddRast),
-                                     ('addRgb', iClassIcons['addRgb'],
-                                      self.OnAddRGB),
-                                     ("delRast", iClassIcons['delCmd'],
-                                      self.OnDelRast),
-                                     ("setOpacity", iClassIcons['opacity'],
-                                      self.OnSetOpacity),
-                                     ))
+        return self._getToolbarData(
+            (
+                ("addRast", BaseIcons["addRast"], self.OnAddRast),
+                ("addRgb", iClassIcons["addRgb"], self.OnAddRGB),
+                ("delRast", iClassIcons["delCmd"], self.OnDelRast),
+                ("setOpacity", iClassIcons["opacity"], self.OnSetOpacity),
+            )
+        )
 
     def OnSelectLayer(self, event):
         layer = self.choice.GetStringSelection()
         self.mapManager.SelectLayer(name=layer)
 
     def OnAddRast(self, event):
-        dlg = IClassMapDialog(
-            self, title=_("Add raster map"),
-            element='raster')
+        dlg = IClassMapDialog(self, title=_("Add raster map"), element="raster")
         if dlg.ShowModal() == wx.ID_OK:
-            raster = grass.find_file(name=dlg.GetMap(), element='cell')
-            if raster['fullname']:
-                self.mapManager.AddLayer(name=raster['fullname'])
+            raster = grass.find_file(name=dlg.GetMap(), element="cell")
+            if raster["fullname"]:
+                self.mapManager.AddLayer(name=raster["fullname"])
 
         dlg.Destroy()
 
     def OnAddRGB(self, event):
-        cmd = ['d.rgb']
-        GUI(parent=self.parent).ParseCommand(
-            cmd, completed=(self.GetOptData, '', ''))
+        cmd = ["d.rgb"]
+        GUI(parent=self.parent).ParseCommand(cmd, completed=(self.GetOptData, "", ""))
 
     def GetOptData(self, dcmd, layer, params, propwin):
         if dcmd:
@@ -375,12 +347,10 @@ class IClassMapManagerToolbar(BaseToolbar):
 
 
 class IClassMiscToolbar(BaseToolbar):
-    """IClass toolbar
-    """
+    """IClass toolbar"""
 
     def __init__(self, parent):
-        """IClass toolbar constructor
-        """
+        """IClass toolbar constructor"""
         BaseToolbar.__init__(self, parent)
 
         self.InitToolbar(self._toolbarData())
@@ -390,8 +360,9 @@ class IClassMiscToolbar(BaseToolbar):
     def _toolbarData(self):
         """Toolbar data"""
         icons = BaseIcons
-        return self._getToolbarData((("help", icons['help'],
-                                      self.parent.OnHelp),
-                                     ("quit", icons['quit'],
-                                      self.parent.OnCloseWindow),
-                                     ))
+        return self._getToolbarData(
+            (
+                ("help", icons["help"], self.parent.OnHelp),
+                ("quit", icons["quit"], self.parent.OnCloseWindow),
+            )
+        )

+ 1 - 1
gui/wxpython/icons/grass_icons.py

@@ -15,6 +15,6 @@ iconSet = dict()
 
 for icon in os.listdir(iconPath):
     name, ext = os.path.splitext(icon)
-    if ext != '.png':
+    if ext != ".png":
         continue
     iconSet[name] = icon

+ 18 - 24
gui/wxpython/icons/icon.py

@@ -27,17 +27,15 @@ from core.settings import UserSettings
 # default icon set
 from .grass_icons import iconSet as g_iconSet
 from .grass_icons import iconPath as g_iconPath
+
 iconSetDefault = g_iconSet
 iconPathDefault = g_iconPath
 
-iconTheme = UserSettings.Get(
-    group='appearance',
-    key='iconTheme',
-    subkey='type')
-if iconTheme != 'grass':
+iconTheme = UserSettings.Get(group="appearance", key="iconTheme", subkey="type")
+if iconTheme != "grass":
     sys.stderr.write(
-        _("Unknown iconset '%s', using default 'grass'...\n") %
-        (iconTheme))
+        _("Unknown iconset '%s', using default 'grass'...\n") % (iconTheme)
+    )
 
 iconSet = iconSetDefault
 iconPath = iconPathDefault
@@ -48,8 +46,7 @@ try:
         raise OSError
 
     for key, img in six.iteritems(iconSet):
-        if key not in iconSet or \
-                iconSet[key] is None:  # add key
+        if key not in iconSet or iconSet[key] is None:  # add key
             iconSet[key] = img
 
         iconSet[key] = os.path.join(iconPath, iconSet[key])
@@ -58,40 +55,37 @@ except Exception as e:
 
 
 class MetaIcon:
-    """Handle icon metadata (image path, tooltip, ...)
-    """
+    """Handle icon metadata (image path, tooltip, ...)"""
 
     def __init__(self, img, label=None, desc=None):
         self.imagepath = iconSet.get(img, wx.ART_MISSING_IMAGE)
         if not self.imagepath:
-            self.type = 'unknown'
+            self.type = "unknown"
         else:
-            if self.imagepath.find('wxART_') > -1:
-                self.type = 'wx'
+            if self.imagepath.find("wxART_") > -1:
+                self.type = "wx"
             else:
-                self.type = 'img'
+                self.type = "img"
 
         self.label = label
 
         if desc:
             self.description = desc
         else:
-            self.description = ''
+            self.description = ""
 
     def __str__(self):
-        return "label=%s, img=%s, type=%s" % (
-            self.label, self.imagepath, self.type)
+        return "label=%s, img=%s, type=%s" % (self.label, self.imagepath, self.type)
 
     def GetBitmap(self, size=None):
         bmp = None
 
-        if self.type == 'wx':
+        if self.type == "wx":
             bmp = wx.ArtProvider.GetBitmap(
-                id=self.imagepath, client=wx.ART_TOOLBAR, size=size)
-        elif self.type == 'img':
-            if os.path.isfile(
-                    self.imagepath) and os.path.getsize(
-                    self.imagepath):
+                id=self.imagepath, client=wx.ART_TOOLBAR, size=size
+            )
+        elif self.type == "img":
+            if os.path.isfile(self.imagepath) and os.path.getsize(self.imagepath):
                 if size and len(size) == 2:
                     image = wx.Image(name=self.imagepath)
                     image.Rescale(size[0], size[1])

+ 3 - 3
gui/wxpython/image2target/__init__.py

@@ -1,5 +1,5 @@
 all = [
-    'ii2t_manager',
-    'ii2t_mapdisplay',
-    'ii2t_toolbars',
+    "ii2t_manager",
+    "ii2t_mapdisplay",
+    "ii2t_toolbars",
 ]

+ 50 - 49
gui/wxpython/image2target/g.gui.image2target.py

@@ -116,67 +116,68 @@ def main():
     import wx
 
     from grass.script.setup import set_gui_path
+
     set_gui_path()
 
     from core.settings import UserSettings
     from core.giface import StandaloneGrassInterface
     from image2target.ii2t_manager import GCPWizard
 
-    driver = UserSettings.Get(group='display', key='driver', subkey='type')
-    if driver == 'png':
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'png'
+    driver = UserSettings.Get(group="display", key="driver", subkey="type")
+    if driver == "png":
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
     else:
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'cairo'
-
-#    if options['source_location']:
-#        src_loc = options['source_location']
-#    else:
-#        gscript.fatal(_("No georeferenced source location provided"))
-
-#    if options['source_mapset']:
-#        src_mpt = options['source_mapset']
-#    else:
-#        gscript.fatal(_("No georeferenced source mapset provided"))
-
-#    if options['source_group']:
-#        src_grp = options['source_group']
-#    else:
-#        gscript.fatal(_("Please provide a source group name to process"))
-
-#    if options['source_image']:
-#        src_ras = options['source_image']
-#    else:
-#        gscript.fatal(_("Please provide a source image map name to process"))
-
-#    if options['target_image']:
-#        tgt_ras = options['target_image']
-#    else:
-#        gscript.fatal(_("No georeferenced target map provided"))
-
-#    if options['camera']:
-#        camera = options['camera']
-#    else:
-#        gscript.fatal(_("Please provide a camera name (generated by i.ortho.camera)"))
-
-#    if options['order']:
-#        order = options['order']
-#    else:
-#        gscript.fatal(_("Please provive an order value"))
-
-#    if options['extension']:
-#        extension = options['extension']
-#    else:
-#        gscript.fatal(_("Please provide an output file extension"))
-
+        os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
+
+    #    if options['source_location']:
+    #        src_loc = options['source_location']
+    #    else:
+    #        gscript.fatal(_("No georeferenced source location provided"))
+
+    #    if options['source_mapset']:
+    #        src_mpt = options['source_mapset']
+    #    else:
+    #        gscript.fatal(_("No georeferenced source mapset provided"))
+
+    #    if options['source_group']:
+    #        src_grp = options['source_group']
+    #    else:
+    #        gscript.fatal(_("Please provide a source group name to process"))
+
+    #    if options['source_image']:
+    #        src_ras = options['source_image']
+    #    else:
+    #        gscript.fatal(_("Please provide a source image map name to process"))
+
+    #    if options['target_image']:
+    #        tgt_ras = options['target_image']
+    #    else:
+    #        gscript.fatal(_("No georeferenced target map provided"))
+
+    #    if options['camera']:
+    #        camera = options['camera']
+    #    else:
+    #        gscript.fatal(_("Please provide a camera name (generated by i.ortho.camera)"))
+
+    #    if options['order']:
+    #        order = options['order']
+    #    else:
+    #        gscript.fatal(_("Please provive an order value"))
+
+    #    if options['extension']:
+    #        extension = options['extension']
+    #    else:
+    #        gscript.fatal(_("Please provide an output file extension"))
 
     app = wx.App()
 
-#    wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface(),
-#            srcloc=src_loc,srcmpt=src_mpt,srcgrp=src_grp,srcras=src_ras,
-#            tgtras=tgt_ras,camera=camera, order=order, extension=extension)
+    #    wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface(),
+    #            srcloc=src_loc,srcmpt=src_mpt,srcgrp=src_grp,srcras=src_ras,
+    #            tgtras=tgt_ras,camera=camera, order=order, extension=extension)
 
     wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface())
     app.MainLoop()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

File diff ditekan karena terlalu besar
+ 465 - 356
gui/wxpython/image2target/ii2t_gis_set.py


+ 3 - 4
gui/wxpython/image2target/ii2t_gis_set_error.py

@@ -22,15 +22,14 @@ def main():
     if len(sys.argv) == 1:
         msg = "Unknown reason"
     else:
-        msg = ''
+        msg = ""
         for m in sys.argv[1:]:
             msg += m
 
-    wx.MessageBox(caption="Error",
-                  message=msg,
-                  style=wx.OK | wx.ICON_ERROR)
+    wx.MessageBox(caption="Error", message=msg, style=wx.OK | wx.ICON_ERROR)
 
     app.MainLoop()
 
+
 if __name__ == "__main__":
     main()

File diff ditekan karena terlalu besar
+ 1226 - 1069
gui/wxpython/image2target/ii2t_manager.py


+ 196 - 130
gui/wxpython/image2target/ii2t_mapdisplay.py

@@ -44,10 +44,17 @@ class MapFrame(SingleMapFrame):
     child double buffered drawing window.
     """
 
-    def __init__(self, parent, giface,
-                 title=_("GRASS GIS Manage Ground Control Points"),
-                 toolbars=["gcpdisp"], Map=None, auimgr=None,
-                 name='GCPMapWindow', **kwargs):
+    def __init__(
+        self,
+        parent,
+        giface,
+        title=_("GRASS GIS Manage Ground Control Points"),
+        toolbars=["gcpdisp"],
+        Map=None,
+        auimgr=None,
+        name="GCPMapWindow",
+        **kwargs,
+    ):
         """Main map display window with toolbars, statusbar and
         DrawWindow
 
@@ -67,7 +74,8 @@ class MapFrame(SingleMapFrame):
             Map=Map,
             auimgr=auimgr,
             name=name,
-            **kwargs)
+            **kwargs,
+        )
 
         self._giface = giface
         # properties are shared in other objects, so defining here
@@ -81,11 +89,11 @@ class MapFrame(SingleMapFrame):
         for toolb in toolbars:
             self.AddToolbar(toolb)
 
-        self.activemap = self.toolbars['gcpdisp'].togglemap
+        self.activemap = self.toolbars["gcpdisp"].togglemap
         self.activemap.SetSelection(0)
 
-        self.SrcMap = self.grwiz.SrcMap       # instance of render.Map
-        self.TgtMap = self.grwiz.TgtMap       # instance of render.Map
+        self.SrcMap = self.grwiz.SrcMap  # instance of render.Map
+        self.TgtMap = self.grwiz.TgtMap  # instance of render.Map
         self._mgr.SetDockSizeConstraint(0.5, 0.5)
 
         #
@@ -93,58 +101,70 @@ class MapFrame(SingleMapFrame):
         #
 
         # items for choice
-        self.statusbarItems = [sb.SbCoordinates,
-                               sb.SbRegionExtent,
-                               sb.SbCompRegionExtent,
-                               sb.SbShowRegion,
-                               sb.SbResolution,
-                               sb.SbDisplayGeometry,
-                               sb.SbMapScale,
-                               sb.SbProjection,
-                               sbgcp.SbGoToGCP,
-                               sbgcp.SbRMSError]
+        self.statusbarItems = [
+            sb.SbCoordinates,
+            sb.SbRegionExtent,
+            sb.SbCompRegionExtent,
+            sb.SbShowRegion,
+            sb.SbResolution,
+            sb.SbDisplayGeometry,
+            sb.SbMapScale,
+            sb.SbProjection,
+            sbgcp.SbGoToGCP,
+            sbgcp.SbRMSError,
+        ]
 
         # create statusbar and its manager
         statusbar = self.CreateStatusBar(number=4, style=0)
         statusbar.SetStatusWidths([-5, -2, -1, -1])
-        self.statusbarManager = sb.SbManager(
-            mapframe=self, statusbar=statusbar)
+        self.statusbarManager = sb.SbManager(mapframe=self, statusbar=statusbar)
 
         # fill statusbar manager
         self.statusbarManager.AddStatusbarItemsByClass(
-            self.statusbarItems, mapframe=self, statusbar=statusbar)
+            self.statusbarItems, mapframe=self, statusbar=statusbar
+        )
         self.statusbarManager.AddStatusbarItem(
-            sb.SbMask(self, statusbar=statusbar, position=2))
+            sb.SbMask(self, statusbar=statusbar, position=2)
+        )
         self.statusbarManager.AddStatusbarItem(
-            sb.SbRender(self, statusbar=statusbar, position=3))
+            sb.SbRender(self, statusbar=statusbar, position=3)
+        )
 
         self.statusbarManager.SetMode(8)  # goto GCP
 
         #
         # Init map display (buffered DC & set default cursor)
         #
-        self.grwiz.SwitchEnv('source')
+        self.grwiz.SwitchEnv("source")
         self.SrcMapWindow = BufferedMapWindow(
-            parent=self, giface=self._giface, id=wx.ID_ANY,
-            properties=self.mapWindowProperties, Map=self.SrcMap)
-
-        self.grwiz.SwitchEnv('target')
+            parent=self,
+            giface=self._giface,
+            id=wx.ID_ANY,
+            properties=self.mapWindowProperties,
+            Map=self.SrcMap,
+        )
+
+        self.grwiz.SwitchEnv("target")
         self.TgtMapWindow = BufferedMapWindow(
-            parent=self, giface=self._giface, id=wx.ID_ANY,
-            properties=self.mapWindowProperties, Map=self.TgtMap)
+            parent=self,
+            giface=self._giface,
+            id=wx.ID_ANY,
+            properties=self.mapWindowProperties,
+            Map=self.TgtMap,
+        )
         self.MapWindow = self.SrcMapWindow
         self.Map = self.SrcMap
         self._setUpMapWindow(self.SrcMapWindow)
         self._setUpMapWindow(self.TgtMapWindow)
-        self.SrcMapWindow.SetNamedCursor('cross')
-        self.TgtMapWindow.SetNamedCursor('cross')
+        self.SrcMapWindow.SetNamedCursor("cross")
+        self.TgtMapWindow.SetNamedCursor("cross")
         # used to switch current map (combo box in toolbar)
         self.SrcMapWindow.mouseEntered.connect(
-            lambda:
-            self._setActiveMapWindow(self.SrcMapWindow))
+            lambda: self._setActiveMapWindow(self.SrcMapWindow)
+        )
         self.TgtMapWindow.mouseEntered.connect(
-            lambda:
-            self._setActiveMapWindow(self.TgtMapWindow))
+            lambda: self._setActiveMapWindow(self.TgtMapWindow)
+        )
 
         #
         # initialize region values
@@ -167,22 +187,47 @@ class MapFrame(SingleMapFrame):
         # windows
         self.list = self.CreateGCPList()
 
-        #self.SrcMapWindow.SetSize((300, 300))
-        #self.TgtMapWindow.SetSize((300, 300))
+        # self.SrcMapWindow.SetSize((300, 300))
+        # self.TgtMapWindow.SetSize((300, 300))
         self.list.SetSize((100, 150))
-        self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
-                          Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
-                          RightDockable(False).PinButton().FloatingSize((600, 200)).
-                          CloseButton(False).DestroyOnClose(True).
-                          Top().Layer(1).MinSize((200, 100)))
-        self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
-                          Name("source").Caption(_("Source Display")).Dockable(False).
-                          CloseButton(False).DestroyOnClose(True).Floatable(False).
-                          Centre())
-        self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
-                          Name("target").Caption(_("Target Display")).Dockable(False).
-                          CloseButton(False).DestroyOnClose(True).Floatable(False).
-                          Right().Layer(0))
+        self._mgr.AddPane(
+            self.list,
+            wx.aui.AuiPaneInfo()
+            .Name("gcplist")
+            .Caption(_("GCP List"))
+            .LeftDockable(False)
+            .RightDockable(False)
+            .PinButton()
+            .FloatingSize((600, 200))
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Top()
+            .Layer(1)
+            .MinSize((200, 100)),
+        )
+        self._mgr.AddPane(
+            self.SrcMapWindow,
+            wx.aui.AuiPaneInfo()
+            .Name("source")
+            .Caption(_("Source Display"))
+            .Dockable(False)
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Floatable(False)
+            .Centre(),
+        )
+        self._mgr.AddPane(
+            self.TgtMapWindow,
+            wx.aui.AuiPaneInfo()
+            .Name("target")
+            .Caption(_("Target Display"))
+            .Dockable(False)
+            .CloseButton(False)
+            .DestroyOnClose(True)
+            .Floatable(False)
+            .Right()
+            .Layer(0),
+        )
 
         srcwidth, srcheight = self.SrcMapWindow.GetSize()
         tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
@@ -196,7 +241,7 @@ class MapFrame(SingleMapFrame):
         else:
             self.activemap.Enable(False)
         # needed by Mac OS, does not harm on Linux, breaks display on Windows
-        if platform.system() != 'Windows':
+        if platform.system() != "Windows":
             self._mgr.Update()
 
         #
@@ -219,10 +264,10 @@ class MapFrame(SingleMapFrame):
         # Re-use dialogs
         #
         self.dialogs = {}
-        self.dialogs['attributes'] = None
-        self.dialogs['category'] = None
-        self.dialogs['barscale'] = None
-        self.dialogs['legend'] = None
+        self.dialogs["attributes"] = None
+        self.dialogs["category"] = None
+        self.dialogs["barscale"] = None
+        self.dialogs["legend"] = None
 
         self.decorationDialog = None  # decoration/overlays
 
@@ -233,11 +278,11 @@ class MapFrame(SingleMapFrame):
         # TODO: almost the same implementation as for MapFrameBase (only names differ)
         # enable or disable zoom history tool
         mapWindow.zoomHistoryAvailable.connect(
-            lambda:
-            self.GetMapToolbar().Enable('zoomback', enable=True))
+            lambda: self.GetMapToolbar().Enable("zoomback", enable=True)
+        )
         mapWindow.zoomHistoryUnavailable.connect(
-            lambda:
-            self.GetMapToolbar().Enable('zoomback', enable=False))
+            lambda: self.GetMapToolbar().Enable("zoomback", enable=False)
+        )
         mapWindow.mouseMoving.connect(self.CoordinatesChanged)
 
     def AddToolbar(self, name):
@@ -252,42 +297,63 @@ class MapFrame(SingleMapFrame):
         """
         # default toolbar
         if name == "map":
-            self.toolbars['map'] = MapToolbar(self, self._toolSwitcher)
-
-            self._mgr.AddPane(self.toolbars['map'],
-                              wx.aui.AuiPaneInfo().
-                              Name("maptoolbar").Caption(_("Map Toolbar")).
-                              ToolbarPane().Top().
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2).
-                              BestSize((self.toolbars['map'].GetSize())))
+            self.toolbars["map"] = MapToolbar(self, self._toolSwitcher)
+
+            self._mgr.AddPane(
+                self.toolbars["map"],
+                wx.aui.AuiPaneInfo()
+                .Name("maptoolbar")
+                .Caption(_("Map Toolbar"))
+                .ToolbarPane()
+                .Top()
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2)
+                .BestSize((self.toolbars["map"].GetSize())),
+            )
 
         # GCP display
         elif name == "gcpdisp":
-            self.toolbars['gcpdisp'] = GCPDisplayToolbar(
-                self, self._toolSwitcher)
-
-            self._mgr.AddPane(self.toolbars['gcpdisp'],
-                              wx.aui.AuiPaneInfo().
-                              Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
-                              ToolbarPane().Top().
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2))
+            self.toolbars["gcpdisp"] = GCPDisplayToolbar(self, self._toolSwitcher)
+
+            self._mgr.AddPane(
+                self.toolbars["gcpdisp"],
+                wx.aui.AuiPaneInfo()
+                .Name("gcpdisplaytoolbar")
+                .Caption(_("GCP Display toolbar"))
+                .ToolbarPane()
+                .Top()
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2),
+            )
 
             if not self.show_target:
-                self.toolbars['gcpdisp'].Enable('zoommenu', enable=False)
-
-            self.toolbars['gcpman'] = GCPManToolbar(self)
-
-            self._mgr.AddPane(self.toolbars['gcpman'],
-                              wx.aui.AuiPaneInfo().
-                              Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
-                              ToolbarPane().Top().Row(1).
-                              LeftDockable(False).RightDockable(False).
-                              BottomDockable(False).TopDockable(True).
-                              CloseButton(False).Layer(2))
+                self.toolbars["gcpdisp"].Enable("zoommenu", enable=False)
+
+            self.toolbars["gcpman"] = GCPManToolbar(self)
+
+            self._mgr.AddPane(
+                self.toolbars["gcpman"],
+                wx.aui.AuiPaneInfo()
+                .Name("gcpmanagertoolbar")
+                .Caption(_("GCP Manager toolbar"))
+                .ToolbarPane()
+                .Top()
+                .Row(1)
+                .LeftDockable(False)
+                .RightDockable(False)
+                .BottomDockable(False)
+                .TopDockable(True)
+                .CloseButton(False)
+                .Layer(2),
+            )
 
         self._mgr.Update()
 
@@ -314,13 +380,11 @@ class MapFrame(SingleMapFrame):
         event.Skip()
 
     def OnDraw(self, event):
-        """Re-display current map composition
-        """
+        """Re-display current map composition"""
         self.MapWindow.UpdateMap(render=False)
 
     def OnRender(self, event):
-        """Re-render map composition (each map layer)
-        """
+        """Re-render map composition (each map layer)"""
         # FIXME: remove qlayer code or use RemoveQueryLayer() now in mapdisp.frame
         # delete tmp map layers (queries)
         qlayer = self.Map.GetListOfLayers(name=globalvar.QUERYLAYER)
@@ -335,13 +399,12 @@ class MapFrame(SingleMapFrame):
         self.StatusbarUpdate()
 
     def OnPointer(self, event):
-        """Pointer button clicked
-        """
+        """Pointer button clicked"""
         self.SrcMapWindow.SetModePointer()
         self.TgtMapWindow.SetModePointer()
         # change the default cursor
-        self.SrcMapWindow.SetNamedCursor('cross')
-        self.TgtMapWindow.SetNamedCursor('cross')
+        self.SrcMapWindow.SetNamedCursor("cross")
+        self.TgtMapWindow.SetNamedCursor("cross")
 
     def OnZoomIn(self, event):
         """Zoom in the map."""
@@ -372,12 +435,13 @@ class MapFrame(SingleMapFrame):
         win.EraseMap()
 
     def SaveToFile(self, event):
-        """Save map to image
-        """
+        """Save map to image"""
         img = self.MapWindow.img
         if not img:
-            GMessage(parent=self, message=_(
-                "Nothing to render (empty map). Operation canceled."))
+            GMessage(
+                parent=self,
+                message=_("Nothing to render (empty map). Operation canceled."),
+            )
             return
         filetype, ltype = GetImageHandlers(img)
 
@@ -391,11 +455,14 @@ class MapFrame(SingleMapFrame):
         dlg.Destroy()
 
         # get filename
-        dlg = wx.FileDialog(parent=self,
-                            message=_("Choose a file name to save the image "
-                                      "(no need to add extension)"),
-                            wildcard=filetype,
-                            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
+        dlg = wx.FileDialog(
+            parent=self,
+            message=_(
+                "Choose a file name to save the image " "(no need to add extension)"
+            ),
+            wildcard=filetype,
+            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
+        )
 
         if dlg.ShowModal() == wx.ID_OK:
             path = dlg.GetPath()
@@ -404,13 +471,12 @@ class MapFrame(SingleMapFrame):
                 return
 
             base, ext = os.path.splitext(path)
-            fileType = ltype[dlg.GetFilterIndex()]['type']
-            extType = ltype[dlg.GetFilterIndex()]['ext']
+            fileType = ltype[dlg.GetFilterIndex()]["type"]
+            extType = ltype[dlg.GetFilterIndex()]["ext"]
             if ext != extType:
-                path = base + '.' + extType
+                path = base + "." + extType
 
-            self.MapWindow.SaveToFile(path, fileType,
-                                      width, height)
+            self.MapWindow.SaveToFile(path, fileType, width, height)
 
         dlg.Destroy()
 
@@ -421,15 +487,15 @@ class MapFrame(SingleMapFrame):
         point = wx.GetMousePosition()
         printmenu = Menu()
         # Add items to the menu
-        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+        setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup"))
         printmenu.AppendItem(setup)
         self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
 
-        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+        preview = wx.MenuItem(printmenu, wx.ID_ANY, _("Print preview"))
         printmenu.AppendItem(preview)
         self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
 
-        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
+        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _("Print display"))
         printmenu.AppendItem(doprint)
         self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
 
@@ -457,38 +523,38 @@ class MapFrame(SingleMapFrame):
         self.MapWindow.DisplayToWind()
 
     def SaveDisplayRegion(self, event):
-        """Save display extents to named region file.
-        """
+        """Save display extents to named region file."""
         self.MapWindow.SaveDisplayRegion()
 
     def OnZoomMenu(self, event):
-        """Popup Zoom menu
-        """
+        """Popup Zoom menu"""
         point = wx.GetMousePosition()
         zoommenu = Menu()
         # Add items to the menu
 
-        zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Zoom to computational region (set with g.region)'))
+        zoomwind = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Zoom to computational region (set with g.region)")
+        )
         zoommenu.AppendItem(zoomwind)
         self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
 
-        zoomdefault = wx.MenuItem(
-            zoommenu, wx.ID_ANY, _('Zoom to default region'))
+        zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to default region"))
         zoommenu.AppendItem(zoomdefault)
         self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
 
-        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to saved region"))
         zoommenu.AppendItem(zoomsaved)
         self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
 
-        savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Set computational region from display'))
+        savewind = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Set computational region from display")
+        )
         zoommenu.AppendItem(savewind)
         self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
 
-        savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _(
-            'Save display geometry to named region'))
+        savezoom = wx.MenuItem(
+            zoommenu, wx.ID_ANY, _("Save display geometry to named region")
+        )
         zoommenu.AppendItem(savezoom)
         self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
 
@@ -520,7 +586,7 @@ class MapFrame(SingleMapFrame):
 
     def GetMapToolbar(self):
         """Returns toolbar with zooming tools"""
-        return self.toolbars['gcpdisp']
+        return self.toolbars["gcpdisp"]
 
     def _setActiveMapWindow(self, mapWindow):
         if not self.MapWindow == mapWindow:

+ 0 - 0
gui/wxpython/image2target/ii2t_statusbar.py


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini