Переглянути джерело

wxGUI major code reorganization

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@49347 15284696-431f-4ddb-bdfa-cd5b030d7da7
Martin Landa 13 роки тому
батько
коміт
c730ae7e6f
86 змінених файлів з 13810 додано та 13505 видалено
  1. 13 4
      gui/wxpython/Makefile
  2. 5 7
      gui/wxpython/gui_modules/debug.py
  3. 38 10
      gui/wxpython/gui_modules/gcmd.py
  4. 2 6
      gui/wxpython/gui_modules/globalvar.py
  5. 10 69
      gui/wxpython/gui_modules/menudata.py
  6. 52 54
      gui/wxpython/gui_modules/render.py
  7. 1046 0
      gui/wxpython/core/settings.py
  8. 7 9
      gui/wxpython/gui_modules/units.py
  9. 42 77
      gui/wxpython/gui_modules/utils.py
  10. 6 347
      gui/wxpython/gui_modules/workspace.py
  11. 33 0
      gui/wxpython/create__init__.py
  12. 15 17
      gui/wxpython/gui_modules/dbm_dialogs.py
  13. 179 177
      gui/wxpython/gui_modules/dbm.py
  14. 17 20
      gui/wxpython/gui_modules/sqlbuilder.py
  15. 17 19
      gui/wxpython/gui_modules/dbm_base.py
  16. 131 146
      gui/wxpython/gui_modules/gcpmanager.py
  17. 27 57
      gui/wxpython/gui_modules/gcpmapdisp.py
  18. 127 0
      gui/wxpython/gcp/toolbars.py
  19. 628 0
      gui/wxpython/gmodeler/dialogs.py
  20. 2656 0
      gui/wxpython/gmodeler/frame.py
  21. 731 0
      gui/wxpython/gmodeler/model.py
  22. 695 0
      gui/wxpython/gmodeler/model_file.py
  23. 527 0
      gui/wxpython/gmodeler/preferences.py
  24. 106 106
      gui/wxpython/gui_modules/gdialogs.py
  25. 15 65
      gui/wxpython/gui_modules/menuform.py
  26. 25 24
      gui/wxpython/gui_modules/ghelp.py
  27. 33 35
      gui/wxpython/gui_modules/goutput.py
  28. 76 76
      gui/wxpython/gui_modules/gselect.py
  29. 337 0
      gui/wxpython/gui_core/mapdisp.py
  30. 238 0
      gui/wxpython/gui_core/mapwindow.py
  31. 5 5
      gui/wxpython/gui_modules/menu.py
  32. 21 1044
      gui/wxpython/gui_modules/preferences.py
  33. 6 8
      gui/wxpython/gui_modules/prompt.py
  34. 177 0
      gui/wxpython/gui_core/toolbars.py
  35. 231 0
      gui/wxpython/gui_core/widgets.py
  36. 0 46
      gui/wxpython/gui_modules/__init__.py
  37. 0 5092
      gui/wxpython/gui_modules/gmodeler.py
  38. 0 1741
      gui/wxpython/gui_modules/toolbars.py
  39. 0 1317
      gui/wxpython/gui_modules/wxplot.py
  40. 1 1
      gui/wxpython/icons/grass_icons.py
  41. 1 1
      gui/wxpython/icons/icon.py
  42. 69 83
      gui/wxpython/gui_modules/layertree.py
  43. 70 0
      gui/wxpython/lmgr/menudata.py
  44. 5 7
      gui/wxpython/gui_modules/gpyshell.py
  45. 210 0
      gui/wxpython/lmgr/toolbars.py
  46. 45 0
      gui/wxpython/location_wizard/base.py
  47. 612 0
      gui/wxpython/location_wizard/dialogs.py
  48. 59 680
      gui/wxpython/gui_modules/location_wizard.py
  49. 73 387
      gui/wxpython/gui_modules/mapdisp.py
  50. 17 19
      gui/wxpython/gui_modules/disp_print.py
  51. 34 261
      gui/wxpython/gui_modules/mapdisp_window.py
  52. 12 12
      gui/wxpython/gui_modules/mapdisp_statusbar.py
  53. 211 0
      gui/wxpython/mapdisp/toolbars.py
  54. 78 81
      gui/wxpython/gui_modules/colorrules.py
  55. 54 24
      gui/wxpython/gui_modules/histogram.py
  56. 20 28
      gui/wxpython/gui_modules/mcalc_builder.py
  57. 9 11
      gui/wxpython/gui_modules/ogc_services.py
  58. 25 32
      gui/wxpython/gui_modules/vclean.py
  59. 3 3
      gui/wxpython/gui_modules/nviz_animation.py
  60. 8 14
      gui/wxpython/gui_modules/nviz.py
  61. 21 23
      gui/wxpython/gui_modules/nviz_mapdisp.py
  62. 4 5
      gui/wxpython/gui_modules/nviz_preferences.py
  63. 35 206
      gui/wxpython/gui_modules/nviz_tools.py
  64. 342 0
      gui/wxpython/nviz/workspace.py
  65. 17 7
      gui/wxpython/gui_modules/wxnviz.py
  66. 17 22
      gui/wxpython/gui_modules/psmap_dialogs.py
  67. 15 21
      gui/wxpython/gui_modules/psmap.py
  68. 39 0
      gui/wxpython/psmap/menudata.py
  69. 93 0
      gui/wxpython/psmap/toolbars.py
  70. 3 3
      gui/wxpython/scripts/r.li.setup.py
  71. 4 4
      gui/wxpython/scripts/vkrige.py
  72. 0 0
      gui/wxpython/states.txt
  73. 1 1
      gui/wxpython/tools/update_menudata.py
  74. 753 0
      gui/wxpython/vdigit/dialogs.py
  75. 34 0
      gui/wxpython/vdigit/main.py
  76. 39 45
      gui/wxpython/gui_modules/mapdisp_vdigit.py
  77. 10 770
      gui/wxpython/gui_modules/vdigit.py
  78. 815 0
      gui/wxpython/vdigit/toolbars.py
  79. 14 12
      gui/wxpython/gui_modules/wxvdigit.py
  80. 9 8
      gui/wxpython/gui_modules/wxvdriver.py
  81. 132 143
      gui/wxpython/wxgui.py
  82. 529 0
      gui/wxpython/wxplot/base.py
  83. 8 13
      gui/wxpython/gui_modules/wxplot_dialogs.py
  84. 260 0
      gui/wxpython/wxplot/histogram.py
  85. 427 0
      gui/wxpython/wxplot/profile.py
  86. 299 0
      gui/wxpython/wxplot/scatter.py

+ 13 - 4
gui/wxpython/Makefile

@@ -9,21 +9,30 @@ include $(MODULE_TOPDIR)/include/Make/Python.make
 
 ETCDIR = $(ETC)/gui/wxpython
 
-SRCFILES := $(wildcard scripts/* compat/* gui_modules/* icons/*.* xml/*) gis_set.py gis_set_error.py wxgui.py README
+SRCFILES := $(wildcard compat/* icons/*.* scripts/* xml/*) \
+	$(wildcard core/* dbm/* gcp/* gmodeler/* gui_core/* lmgr/* \
+	mapdisp/* modules/* nviz/* psmap/* vdigit/* wxplot/* ) \
+	gis_set.py gis_set_error.py wxgui.py README
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
-DSTDIRS := $(patsubst %,$(ETCDIR)/%,compat gui_modules icons scripts xml)
+PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,core dbm gcp gmodeler gui_core lmgr \
+	mapdisp modules nviz psmap vdigit wxplot)
+DSTDIRS := $(patsubst %,$(ETCDIR)/%,compat icons scripts xml)
 
 default: $(DSTFILES) menustrings.py
 	$(MAKE) parsubdirs
 
-$(ETCDIR)/%: % | $(DSTDIRS)
+$(ETCDIR)/%: % | $(PYDSTDIRS) $(DSTDIRS)
 	$(INSTALL_DATA) $< $@
 
-menustrings.py: gui_modules/menudata.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml 
+menustrings.py: core/menudata.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml 
 	$(call run_grass,$(PYTHON) $< > $@)
 	$(call run_grass,$(PYTHON) $< "modeler" >> $@)
 
+$(PYDSTDIRS): %: | $(ETCDIR)
+	$(MKDIR) $@
+	$(call run_grass,$(PYTHON) create__init__.py $@)
+
 $(DSTDIRS): %: | $(ETCDIR)
 	$(MKDIR) $@
 

+ 5 - 7
gui/wxpython/gui_modules/debug.py

@@ -1,17 +1,17 @@
 """!
-@package debug
+@package core.debug
 
-@brief Debugging
+@brief wxGUI debugging
 
 Classes:
  - DebugMsg
 
 @code
-from debug import Debug as Debug
+from core.debug import Debug
 Debug.msg (3, 'debug message')
 @endcode
          
-COPYRIGHT: (C) 2007-2009, 2011 by the GRASS Development Team
+(C) 2007-2009, 2011 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.
 
@@ -21,8 +21,6 @@ This program is free software under the GNU General Public License
 import os
 import sys
 
-import globalvar
-
 import grass.script as grass
 
 class DebugMsg:
@@ -69,7 +67,7 @@ Debug = DebugMsg()
 
 # testing
 if __name__ == "__main__":
-    import gcmd
+    from core import cmd as gcmd
     gcmd.RunCommand('g.gisenv',
                     set = 'DEBUG=3')
                 

+ 38 - 10
gui/wxpython/gui_modules/gcmd.py

@@ -1,5 +1,5 @@
 """!
-@package gcmd
+@package core.gcmd
 
 @brief wxGUI command interface
 
@@ -31,8 +31,6 @@ import errno
 import signal
 import locale
 import traceback
-import types
-import time
 
 import wx
 
@@ -51,13 +49,43 @@ else:
     import fcntl
 from threading import Thread
 
-import globalvar
-grassPath = os.path.join(globalvar.ETCDIR, "python")
-sys.path.append(grassPath)
 from grass.script import core as grass
 
-import utils
-from debug import Debug as Debug
+from core       import globalvar
+from core.debug import Debug
+
+def DecodeString(string):
+    """!Decode string using system encoding
+    
+    @param string string to be decoded
+    
+    @return decoded string
+    """
+    if not string:
+        return string
+    
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "DecodeString(): enc=%s" % enc)
+        return string.decode(enc)
+    
+    return string
+
+def EncodeString(string):
+    """!Return encoded string using system locales
+    
+    @param string string to be encoded
+    
+    @return encoded string
+    """
+    if not string:
+        return string
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "EncodeString(): enc=%s" % enc)
+        return string.encode(enc)
+    
+    return string
 
 class GError:
     def __init__(self, message, parent = None, caption = None, showTraceback = True):
@@ -114,7 +142,7 @@ class Popen(subprocess.Popen):
     """!Subclass subprocess.Popen"""
     def __init__(self, args, **kwargs):
         if subprocess.mswindows:
-            args = map(utils.EncodeString, args)
+            args = map(EncodeString, args)
         
         subprocess.Popen.__init__(self, args, **kwargs)
         
@@ -604,7 +632,7 @@ def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = Fal
         ps.stdin = None
     
     Debug.msg(3, "gcmd.RunCommand(): decoding string")
-    stdout, stderr = map(utils.DecodeString, ps.communicate())
+    stdout, stderr = map(DecodeString, ps.communicate())
     
     ret = ps.returncode
     Debug.msg(1, "gcmd.RunCommand(): get return code %d (%.6f sec)" % \

+ 2 - 6
gui/wxpython/gui_modules/globalvar.py

@@ -1,13 +1,9 @@
 """!
-@package global.py
+@package core.globalvar
 
-@brief Global variables
-
-This module provide the space for global variables
-used in the code.
+@brief Global variables used by wxGUI
 
 (C) 2007-2011 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.
 

+ 10 - 69
gui/wxpython/gui_modules/menudata.py

@@ -1,13 +1,10 @@
 """!
-@package menudata.py
+@package core.menudata
 
-@brief Complex list for menu entries for wxGUI.
+@brief Complex list for menu entries for wxGUI
 
 Classes:
  - MenuData
- - ManagerData
- - ModelerData
- - PsMapData
 
 Usage:
 @code
@@ -28,24 +25,21 @@ This program is free software under the GNU General Public License
 @author Yann Chemin <yann.chemin gmail.com>
 @author Martin Landa <landa.martin gmail.com>
 @author Glynn Clements
-@author Anna Kratochvilova <kratochanna gmail.com>
 """
 
 import os
 import sys
 import pprint
 try:
-    import xml.etree.ElementTree as etree
+    import xml.etree.ElementTree   as etree
 except ImportError:
     import elementtree.ElementTree as etree # Python <= 2.4
 
-import wx
+from wx import ID_ANY
 
 if not os.getenv("GISBASE"):
     sys.exit("GRASS is not running. Exiting...")
 
-etcwxdir = os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython")
-
 class MenuData:
     """!Abstract menu data class"""
     def __init__(self, filename):
@@ -81,7 +75,7 @@ class MenuData:
             if wxId != None:
                 wxId = eval('wx.' + wxId.text)
             else:
-                wxId = wx.ID_ANY
+                wxId = ID_ANY
 	    return (label, help, handler, gcmd, keywords, shortcut, wxId)
 	elif mi.tag == 'menu':
 	    return self._getMenu(mi)
@@ -200,66 +194,10 @@ class MenuData:
                     fh.write('%s%s' % (menuSep, eachItem[0]))
                     fh.write('\n')
 
-class ManagerData(MenuData):
-    def __init__(self, filename = None):
-        if not filename:
-            gisbase = os.getenv('GISBASE')
-            global etcwxdir
-	    filename = os.path.join(etcwxdir, 'xml', 'menudata.xml')
-        
-        MenuData.__init__(self, filename)
-        
-    def GetModules(self):
-        """!Create dictionary of modules used to search module by
-        keywords, description, etc."""
-        modules = dict()
-        
-        for node in self.tree.getiterator():
-            if node.tag == 'menuitem':
-                module = description = ''
-                keywords = []
-                for child in node.getchildren():
-                    if child.tag == 'help':
-                        description = child.text
-                    if child.tag == 'command':
-                        module = child.text
-                    if child.tag == 'keywords':
-                        if child.text:
-                            keywords = child.text.split(',')
-                    
-                if module:
-                    modules[module] = { 'desc': description,
-                                        'keywords' : keywords }
-                    if len(keywords) < 1:
-                        print >> sys.stderr, "WARNING: Module <%s> has no keywords" % module
-                
-        return modules
-
-class ModelerData(MenuData):
-    def __init__(self, filename = None):
-        if not filename:
-            gisbase = os.getenv('GISBASE')
-            global etcwxdir
-	    filename = os.path.join(etcwxdir, 'xml', 'menudata_modeler.xml')
-        
-        MenuData.__init__(self, filename)
-
-class PsMapData(MenuData):
-    def __init__(self, path = None):
-        """!Menu for Cartographic Composer (psmap.py)
-        
-        @path path to XML to be read (None for menudata_psmap.xml)
-        """
-        if not path:
-            gisbase = os.getenv('GISBASE')
-            global etcwxdir
-        path = os.path.join(etcwxdir, 'xml', 'menudata_psmap.xml')
-        
-        MenuData.__init__(self, path)
-
 if __name__ == "__main__":
+    import os
     import sys
-
+    
     # i18N
     import gettext
     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
@@ -273,9 +211,12 @@ if __name__ == "__main__":
         elif arg in ('manager', 'modeler'):
             menu = arg
     
+    sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython"))
     if menu == 'manager':
+        from lmgr.menudata import ManagerData
         data = ManagerData()
     else:
+        from gmodeler.frame import ModelerData
         data = ModelerData()
     
     if action == 'strings':

+ 52 - 54
gui/wxpython/gui_modules/render.py

@@ -1,7 +1,7 @@
 """!
-@package render
+@package core.render
 
-Rendering map layers and overlays into map composition image.
+@brief Rendering map layers and overlays into map composition image.
 
 Classes:
  - Layer
@@ -25,7 +25,6 @@ import glob
 import math
 import copy
 import tempfile
-import stat
 import types
 
 import wx
@@ -33,11 +32,10 @@ from wx.lib.newevent import NewEvent
 
 from grass.script import core as grass
 
-import globalvar
-import utils
-import gcmd
-from debug import Debug as Debug
-from preferences import globalSettings as UserSettings
+from core          import utils
+from core.gcmd     import GException, GError, RunCommand
+from core.debug    import Debug
+from core.settings import UserSettings
 
 wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent()
 
@@ -125,8 +123,8 @@ class Layer(object):
                       'overlay')
         
         if self.type not in layertypes:
-            raise gcmd.GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
-                                      {'type' : self.type, 'name' : self.name})
+            raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
+                                 {'type' : self.type, 'name' : self.name})
         
         # start monitor
 	if self.mapfile:
@@ -137,7 +135,7 @@ class Layer(object):
             if self.type == 'command':
                 read = False
                 for c in self.cmd:
-                    ret, msg = gcmd.RunCommand(c[0],
+                    ret, msg = RunCommand(c[0],
                                           getErrorMsg = True,
                                           quiet = True,
                                           **c[1])
@@ -148,18 +146,18 @@ class Layer(object):
                 
                 os.environ["GRASS_PNG_READ"] = "FALSE"
             else:
-                ret, msg = gcmd.RunCommand(self.cmd[0],
-                                           getErrorMsg = True,
-                                           quiet = True,
-                                           **self.cmd[1])
+                ret, msg = RunCommand(self.cmd[0],
+                                      getErrorMsg = True,
+                                      quiet = True,
+                                      **self.cmd[1])
                 
             if msg:
                 sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
                 sys.stderr.write(_("Details: %s\n") % msg)
             if ret != 0:
-                raise gcmd.GException()
+                raise GException()
         
-        except gcmd.GException:
+        except GException:
             # clean up after problems
             for f in [self.mapfile, self.maskfile]:
                 if not f:
@@ -247,7 +245,7 @@ class Layer(object):
                         'shaded', 'rgb', 'his', 'rastarrow', 'rastnum','maplegend',
                         'thememap', 'themechart', 'grid', 'labels',
                         'geodesic','rhumb'):
-            raise gcmd.GException(_("Unsupported map layer type '%s'") % type)
+            raise GException(_("Unsupported map layer type '%s'") % type)
         
         self.type = type
 
@@ -426,7 +424,7 @@ class Map(object):
             sys.exit(_("GRASS module '%s' not found. Unable to start map "
                        "display window.") % 'g.proj')
         
-        ret = self._runCommand(gcmd.RunCommand, prog = 'g.proj',
+        ret = self._runCommand(RunCommand, prog = 'g.proj',
                                read = True, flags = 'p')
         
         if not ret:
@@ -647,10 +645,10 @@ class Map(object):
         if vect:
             cmd['vect'] = ','.join(vect)
         
-        ret, reg, msg = gcmd.RunCommand('g.region',
-                                        read = True,
-                                        getErrorMsg = True,
-                                        **cmd)
+        ret, reg, msg = RunCommand('g.region',
+                                   read = True,
+                                   getErrorMsg = True,
+                                   **cmd)
         
         if ret != 0:
             if rast:
@@ -663,7 +661,7 @@ class Map(object):
                 message = _("Unable to get current geographic extent. "
                             "Force quiting wxGUI. Please manually run g.region to "
                             "fix the problem.")
-            gcmd.GError(message)
+            GError(message)
             return self.region
         
         for r in reg.splitlines():
@@ -911,17 +909,17 @@ class Map(object):
             fd = open(self.cmdfile, 'r')
             grass.try_remove(self.mapfile)
             cmdLines = fd.readlines()
-            gcmd.RunCommand('g.gisenv',
-                            set = 'MONITOR_%s_CMDFILE=' % self.monitor)
+            RunCommand('g.gisenv',
+                       set = 'MONITOR_%s_CMDFILE=' % self.monitor)
 
             for cmd in cmdLines:
                 cmdStr = utils.split(cmd.strip())
                 cmd = utils.CmdToTuple(cmdStr)
-                gcmd.RunCommand(cmd[0], **cmd[1])
+                RunCommand(cmd[0], **cmd[1])
                 nlayers += 1
             
-            gcmd.RunCommand('g.gisenv',
-                            set = 'MONITOR_%s_CMDFILE=%s' % (self.monitor, self.cmdfile))
+            RunCommand('g.gisenv',
+                       set = 'MONITOR_%s_CMDFILE=%s' % (self.monitor, self.cmdfile))
         except IOError, e:
             grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
                               { 'cmd' : self.cmdfile, 'det' : e })
@@ -944,15 +942,15 @@ class Map(object):
         self._writeEnvFile({'GRASS_REGION' : region})
         currMon = grass.gisenv()['MONITOR']
         if currMon != self.monitor:
-            gcmd.RunCommand('g.gisenv',
-                            set = 'MONITOR=%s' % self.monitor)
+            RunCommand('g.gisenv',
+                       set = 'MONITOR=%s' % self.monitor)
         
         grass.try_remove(self.mapfileCmd) # GRASS_PNG_READ is TRUE
         
         nlayers = self._parseCmdFile()
         if self.overlays:
-            gcmd.RunCommand('g.gisenv',
-                            unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
+            RunCommand('g.gisenv',
+                       unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
             driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
             if driver == 'png':
                 os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
@@ -960,12 +958,12 @@ class Map(object):
                 os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
             self._renderLayers(overlaysOnly = True)
             del os.environ["GRASS_RENDER_IMMEDIATE"]
-            gcmd.RunCommand('g.gisenv',
-                            set = 'MONITOR=%s' % currMon)
+            RunCommand('g.gisenv',
+                       set = 'MONITOR=%s' % currMon)
         
         if currMon != self.monitor:
-            gcmd.RunCommand('g.gisenv',
-                            set = 'MONITOR=%s' % currMon)
+            RunCommand('g.gisenv',
+                       set = 'MONITOR=%s' % currMon)
             
         if nlayers > 0:
             return ([self.mapfileCmd],
@@ -1028,16 +1026,16 @@ class Map(object):
                                                      subkey = 'color')))
         
         if maps:
-            ret, msg = gcmd.RunCommand('g.pnmcomp',
-                                       getErrorMsg = True,
-                                       overwrite = True,
-                                       input = '%s' % ",".join(maps),
-                                       mask = '%s' % ",".join(masks),
-                                       opacity = '%s' % ",".join(opacities),
-                                       bgcolor = bgcolor,
-                                       width = self.width,
-                                       height = self.height,
-                                       output = self.mapfile)
+            ret, msg = RunCommand('g.pnmcomp',
+                                  getErrorMsg = True,
+                                  overwrite = True,
+                                  input = '%s' % ",".join(maps),
+                                  mask = '%s' % ",".join(masks),
+                                  opacity = '%s' % ",".join(opacities),
+                                  bgcolor = bgcolor,
+                                  width = self.width,
+                                  height = self.height,
+                                  output = self.mapfile)
             
             if ret != 0:
                 print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
@@ -1097,7 +1095,7 @@ class Map(object):
         Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
         if l_render:
             if not layer.Render():
-                raise gcmd.GException(_("Unable to render map layer <%s>.") % name)
+                raise GException(_("Unable to render map layer <%s>.") % name)
         
         wx.EndBusyCursor()
         
@@ -1190,8 +1188,8 @@ class Map(object):
             layer.SetOpacity(kargs['opacity'])
         
         if render and not layer.Render():
-            raise gcmd.GException(_("Unable to render map layer <%s>.") % 
-                                  name)
+            raise GException(_("Unable to render map layer <%s>.") % 
+                             name)
         
         return layer
 
@@ -1299,8 +1297,8 @@ class Map(object):
         self.overlays.append(overlay)
         
         if l_render and command != '' and not overlay.Render():
-            raise gcmd.GException(_("Unable render overlay <%s>.") % 
-                                  name)
+            raise GException(_("Unable render overlay <%s>.") % 
+                             name)
         
         return self.overlays[-1]
 
@@ -1338,8 +1336,8 @@ class Map(object):
             overlay.SetOpacity(kargs['opacity'])
         
         if render and overlay.GetCmd() != [] and not overlay.Render():
-            raise gcmd.GException(_("Unable render overlay <%s>") % 
-                                  name)
+            raise GException(_("Unable render overlay <%s>") % 
+                             name)
         
         return overlay
 

Різницю між файлами не показано, бо вона завелика
+ 1046 - 0
gui/wxpython/core/settings.py


+ 7 - 9
gui/wxpython/gui_modules/units.py

@@ -1,24 +1,22 @@
 """!
-@package units
+@package core.units
 
 @brief Units management
 
-Probably will be replaced by Python SWIG fns in the near future(?)
+@todo Probably will be replaced by Python ctypes fns in the near
+future(?)
 
 Usage:
-
 @code
-from units import Units
+from core.units import Units
 @endcode
 
 Classes:
  - BaseUnits
 
-(C) 2009 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.
+(C) 2009, 2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """

+ 42 - 77
gui/wxpython/gui_modules/utils.py

@@ -1,10 +1,9 @@
 """!
-@package utils.py
+@package core.utils
 
 @brief Misc utilities for wxGUI
 
 (C) 2007-2009, 2011 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.
 
@@ -17,18 +16,17 @@ import sys
 import platform
 import string
 import glob
-import locale
 import shlex
 import re
 
-import globalvar
-sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
+from core.globalvar import ETCDIR
+sys.path.append(os.path.join(ETCDIR, "python"))
 
 from grass.script import core as grass
 from grass.script import task as gtask
 
-import gcmd
-from debug import Debug
+from core.gcmd  import RunCommand
+from core.debug import Debug
 
 def normalize_whitespace(text):
     """!Remove redundant whitespace from a string"""
@@ -52,11 +50,11 @@ def GetTempfile(pref=None):
 
     @return Path to file name (string) or None
     """
-    import gcmd
+    from core import cmd as gcmd
     
-    ret = gcmd.RunCommand('g.tempfile',
-                          read = True,
-                          pid = os.getpid())
+    ret = RunCommand('g.tempfile',
+                     read = True,
+                     pid = os.getpid())
 
     tempfile = ret.splitlines()[0].strip()
 
@@ -244,11 +242,11 @@ def ListOfMapsets(get = 'ordered'):
     mapsets = []
     
     if get == 'all' or get == 'ordered':
-        ret = gcmd.RunCommand('g.mapsets',
-                              read = True,
-                              quiet = True,
-                              flags = 'l',
-                              fs = 'newline')
+        ret = RunCommand('g.mapsets',
+                         read = True,
+                         quiet = True,
+                         flags = 'l',
+                         fs = 'newline')
         
         if ret:
             mapsets = ret.splitlines()
@@ -257,11 +255,11 @@ def ListOfMapsets(get = 'ordered'):
             return None
         
     if get == 'accessible' or get == 'ordered':
-        ret = gcmd.RunCommand('g.mapsets',
-                              read = True,
-                              quiet = True,
-                              flags = 'p',
-                              fs = 'newline')
+        ret = RunCommand('g.mapsets',
+                         read = True,
+                         quiet = True,
+                         flags = 'p',
+                         fs = 'newline')
         if ret:
             if get == 'accessible':
                 mapsets = ret.splitlines()
@@ -290,12 +288,12 @@ def GetVectorNumberOfLayers(parent, vector):
         Debug.msg(5, "utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
         return layers
     
-    ret, out, msg = gcmd.RunCommand('v.db.connect',
-                                    getErrorMsg = True,
-                                    read = True,
-                                    flags = 'g',
-                                    map = fullname,
-                                    fs = ';')
+    ret, out, msg = RunCommand('v.db.connect',
+                               getErrorMsg = True,
+                               read = True,
+                               flags = 'g',
+                               map = fullname,
+                               fs = ';')
     if ret != 0:
         sys.stderr.write(_("Vector map <%(map)s>: %(msg)s\n") % { 'map' : fullname, 'msg' : msg })
         return layers
@@ -558,14 +556,14 @@ def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
 
     @return reprojected coordinates (returned as tuple)
     """
-    coors = gcmd.RunCommand('m.proj',
-                            flags = flags,
-                            input = '-',
-                            proj_input = projIn,
-                            proj_output = projOut,
-                            fs = ';',
-                            stdin = '%f;%f' % (coord[0], coord[1]),
-                            read = True)
+    coors = RunCommand('m.proj',
+                       flags = flags,
+                       input = '-',
+                       proj_input = projIn,
+                       proj_output = projOut,
+                       fs = ';',
+                       stdin = '%f;%f' % (coord[0], coord[1]),
+                       read = True)
     if coors:
         coors = coors.split(';')
         e = coors[0]
@@ -619,11 +617,11 @@ def GetListOfMapsets(dbase, location, selectable = False):
     listOfMapsets = list()
     
     if selectable:
-        ret = gcmd.RunCommand('g.mapset',
-                              read = True,
-                              flags = 'l',
-                              location = location,
-                              gisdbase = dbase)
+        ret = RunCommand('g.mapset',
+                         read = True,
+                         flags = 'l',
+                         location = location,
+                         gisdbase = dbase)
         
         if not ret:
             return listOfMapsets
@@ -641,47 +639,14 @@ def GetListOfMapsets(dbase, location, selectable = False):
 
 def GetColorTables():
     """!Get list of color tables"""
-    ret = gcmd.RunCommand('r.colors',
-                          read = True,
-                          flags = 'l')
+    ret = RunCommand('r.colors',
+                     read = True,
+                     flags = 'l')
     if not ret:
         return list()
     
     return ret.splitlines()
 
-def DecodeString(string):
-    """!Decode string using system encoding
-    
-    @param string string to be decoded
-    
-    @return decoded string
-    """
-    if not string:
-        return string
-    
-    enc = locale.getdefaultlocale()[1]
-    if enc:
-        Debug.msg(5, "DecodeString(): enc=%s" % enc)
-        return string.decode(enc)
-    
-    return string
-
-def EncodeString(string):
-    """!Return encoded string using system locales
-    
-    @param string string to be encoded
-    
-    @return encoded string
-    """
-    if not string:
-        return string
-    enc = locale.getdefaultlocale()[1]
-    if enc:
-        Debug.msg(5, "EncodeString(): enc=%s" % enc)
-        return string.encode(enc)
-    
-    return string
-
 def _getGDALFormats():
     """!Get dictionary of avaialble GDAL drivers"""
     ret = grass.read_command('r.in.gdal',
@@ -766,7 +731,7 @@ def GetSettingsPath():
     """!Get full path to the settings directory
     """
     try:
-        verFd = open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER"))
+        verFd = open(os.path.join(ETCDIR, "VERSIONNUMBER"))
         version = int(verFd.readlines()[0].split(' ')[0].split('.')[0])
     except (IOError, ValueError, TypeError, IndexError), e:
         sys.exit(_("ERROR: Unable to determine GRASS version. Details: %s") % e)

+ 6 - 347
gui/wxpython/gui_modules/workspace.py

@@ -1,48 +1,26 @@
 """!
-@package workspace.py
+@package core.workspace
 
 @brief Open/save workspace definition file
 
 Classes:
  - ProcessWorkspaceFile
- - Nviz
  - WriteWorkspaceFile
  - ProcessGrcFile
 
 (C) 2007-2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
-@author Anna Kratochvilova <kratochanna gmail.com> (wxNviz / Google SoC 2011)
 """
 
 import os
-import sys
-import copy
-import types
 
 import wx
 
-### for gxw (workspace file) parsering
-# xmlproc not available on Mac OS
-# from xml.parsers.xmlproc import xmlproc
-# from xml.parsers.xmlproc import xmlval
-# from xml.parsers.xmlproc import xmldtd
-try:
-    import xml.etree.ElementTree as etree
-except ImportError:
-    import elementtree.ElementTree as etree # Python <= 2.4
-
-import utils
-import globalvar
-from preferences import globalSettings as UserSettings
-
-try:
-    import wxnviz
-except ImportError:
-    wxnviz = None
+from core.utils    import normalize_whitespace
+from core.settings import UserSettings
 
 class ProcessWorkspaceFile:
     def __init__(self, tree):
@@ -92,7 +70,7 @@ class ProcessWorkspaceFile:
         """!Get node text"""
         p = node.find(tag)
         if p is not None:
-            return utils.normalize_whitespace(p.text)
+            return normalize_whitespace(p.text)
         
         return default
     
@@ -564,326 +542,7 @@ class ProcessWorkspaceFile:
                 plane['object'] = {}
                 constants.append({'constant': plane})
         self.nviz_state['constants'] = constants    
-        
-class Nviz:
-    def __init__(self):
-        """Default 3D settings"""
-        UserSettings.Reset('nviz')
-        UserSettings.ReadSettingsFile()
-        
-    def SetConstantDefaultProp(self):
-        """Set default constant data properties"""
-        data = dict()
-        for key, value in UserSettings.Get(group='nviz', key='constant').iteritems():
-            data[key] = value
-        color = str(data['color'][0]) + ':' + str(data['color'][1]) + ':' + str(data['color'][2])
-        data['color'] = color
-
-        return data
-    
-    def SetSurfaceDefaultProp(self, data = None):
-        """Set default surface data properties"""
-        if not data:
-            data = dict()
-        for sec in ('attribute', 'draw', 'mask', 'position'):
-            data[sec] = {}
-        
-        #
-        # attributes
-        #
-        for attrb in ('shine', ):
-            data['attribute'][attrb] = {}
-            for key, value in UserSettings.Get(group='nviz', key='surface',
-                                               subkey=attrb).iteritems():
-                data['attribute'][attrb][key] = value
-            data['attribute'][attrb]['update'] = None
-        
-        #
-        # draw
-        #
-        data['draw']['all'] = False # apply only for current surface
-        for control, value in UserSettings.Get(group='nviz', key='surface', subkey='draw').iteritems():
-            if control[:3] == 'res':
-                if 'resolution' not in data['draw']:
-                    data['draw']['resolution'] = {}
-                if 'update' not in data['draw']['resolution']:
-                    data['draw']['resolution']['update'] = None
-                data['draw']['resolution'][control[4:]] = value
-                continue
-            
-            if control == 'wire-color':
-                value = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
-            elif control in ('mode', 'style', 'shading'):
-                if 'mode' not in data['draw']:
-                    data['draw']['mode'] = {}
-                continue
 
-            data['draw'][control] = { 'value' : value }
-            data['draw'][control]['update'] = None
-        
-        value, desc = self.GetDrawMode(UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'mode']),
-                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'style']),
-                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'shading']))
-    
-        data['draw']['mode'] = { 'value' : value,
-                                 'desc' : desc, 
-                                 'update': None }
-        # position
-        for coord in ('x', 'y', 'z'):
-            data['position'][coord] = UserSettings.Get(group='nviz', key='surface', subkey=['position', coord])
-        data['position']['update'] = None
-            
-        return data
-    
-    def SetVolumeDefaultProp(self):
-        """Set default volume data properties"""
-        data = dict()
-        for sec in ('attribute', 'draw', 'position'):
-            data[sec] = dict()
-            for sec in ('isosurface', 'slice'):
-                    data[sec] = list()
-        
-        #
-        # draw
-        #
-        for control, value in UserSettings.Get(group='nviz', key='volume', subkey='draw').iteritems():
-            if control == 'shading':
-                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'shading'])
-                value, desc = self.GetDrawMode(shade=sel, string=False)
-                
-                data['draw']['shading'] = {}
-                data['draw']['shading']['isosurface'] = { 'value' : value,
-                                                          'desc' : desc['shading'] }
-                data['draw']['shading']['slice'] = { 'value' : value,
-                                                     'desc' : desc['shading'] }
-            elif control == 'mode':
-                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'mode'])
-                if sel == 0:
-                    desc = 'isosurface'
-                else:
-                    desc = 'slice'
-                data['draw']['mode'] = { 'value' : sel,
-                                         'desc' : desc, }
-            else:
-                data['draw'][control] = {}
-                data['draw'][control]['isosurface'] = { 'value' : value }
-                data['draw'][control]['slice'] = { 'value' : value }
-
-            if 'update' not in data['draw'][control]:
-                data['draw'][control]['update'] = None
-        
-        #
-        # isosurface attributes
-        #
-        for attrb in ('shine', ):
-            data['attribute'][attrb] = {}
-            for key, value in UserSettings.Get(group='nviz', key='volume',
-                                               subkey=attrb).iteritems():
-                data['attribute'][attrb][key] = value
-        
-        return data
-    
-    def SetIsosurfaceDefaultProp(self):
-        """!Set default isosurface properties"""
-        data = dict()
-        for attr in ('shine', 'topo', 'transp', 'color'):
-            data[attr] = {}
-            for key, value in UserSettings.Get(group = 'nviz', key = 'volume',
-                                               subkey = attr).iteritems():
-                data[attr][key] = value
-            data[attr]['update'] = None
-        return data
-    
-    def SetSliceDefaultProp(self):
-        """!Set default slice properties"""
-        data = dict()
-        data['position'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
-                                               subkey = 'slice_position'))
-        data['position']['update'] = None
-        
-        data['transp'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
-                                               subkey = 'transp'))
-        return data
-    
-    def SetVectorDefaultProp(self, data = None):
-        """Set default vector data properties"""
-        if not data:
-            data = dict()
-        for sec in ('lines', 'points'):
-            data[sec] = {}
-        
-        self.SetVectorLinesDefaultProp(data['lines'])
-        self.SetVectorPointsDefaultProp(data['points'])
-
-        return data
-    
-    def SetVectorLinesDefaultProp(self, data):
-        """Set default vector properties -- lines"""
-        # width
-        data['width'] = {'value' : UserSettings.Get(group='nviz', key='vector',
-                                                    subkey=['lines', 'width']) }
-        
-        # color
-        value = UserSettings.Get(group='nviz', key='vector',
-                                 subkey=['lines', 'color'])
-        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
-        data['color'] = { 'value' : color }
-
-        # mode
-        if UserSettings.Get(group='nviz', key='vector',
-                            subkey=['lines', 'flat']):
-            type = 'flat'
-            
-        else:
-            type = 'surface'
-            
-        data['mode'] = {}
-        data['mode']['type'] = type
-        data['mode']['update'] = None
-    
-        # height
-        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['lines', 'height']) }
-        # thematic
-        data['thematic'] = {'rgbcolumn' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['lines', 'rgbcolumn']),
-                            'sizecolumn' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['lines', 'sizecolumn']),
-                            'layer': 1,
-                            'usecolor' : False,
-                            'usewidth' : False}
-        if 'object' in data:
-            for attrb in ('color', 'width', 'mode', 'height', 'thematic'):
-                data[attrb]['update'] = None
-        
-    def SetVectorPointsDefaultProp(self, data):
-        """Set default vector properties -- points"""
-        # size
-        data['size'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
-                                                    subkey=['points', 'size']) }
-
-        # width
-        data['width'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
-                                                     subkey=['points', 'width']) }
-
-        # marker
-        data['marker'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['points', 'marker']) }
-
-        # color
-        value = UserSettings.Get(group='nviz', key='vector',
-                                 subkey=['points', 'color'])
-        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
-        data['color'] = { 'value' : color }
-
-        # mode
-        data['mode'] = { 'type' : 'surface'}
-##                         'surface' : '', }
-        
-        # height
-        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['points', 'height']) }
-        
-        data['thematic'] = {'rgbcolumn' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['points', 'rgbcolumn']),
-                            'sizecolumn' : UserSettings.Get(group='nviz', key='vector',
-                                                      subkey=['points', 'sizecolumn']),
-                            'layer': 1,
-                            'usecolor' : False,
-                            'usesize' : False}
-        if 'object' in data:
-            for attrb in ('size', 'width', 'marker',
-                          'color', 'height', 'thematic'):
-                data[attrb]['update'] = None
-        
-    def GetDrawMode(self, mode=None, style=None, shade=None, string=False):
-        """Get surface draw mode (value) from description/selection
-
-        @param mode,style,shade modes
-        @param string if True input parameters are strings otherwise
-        selections
-        """
-        if not wxnviz:
-            return None
-        
-        value = 0
-        desc = {}
-
-        if string:
-            if mode is not None:
-                if mode == 'coarse':
-                    value |= wxnviz.DM_WIRE
-                elif mode == 'fine':
-                    value |= wxnviz.DM_POLY
-                else: # both
-                    value |= wxnviz.DM_WIRE_POLY
-
-            if style is not None:
-                if style == 'wire':
-                    value |= wxnviz.DM_GRID_WIRE
-                else: # surface
-                    value |= wxnviz.DM_GRID_SURF
-                    
-            if shade is not None:
-                if shade == 'flat':
-                    value |= wxnviz.DM_FLAT
-                else: # surface
-                    value |= wxnviz.DM_GOURAUD
-
-            return value
-
-        # -> string is False
-        if mode is not None:
-            if mode == 0: # coarse
-                value |= wxnviz.DM_WIRE
-                desc['mode'] = 'coarse'
-            elif mode == 1: # fine
-                value |= wxnviz.DM_POLY
-                desc['mode'] = 'fine'
-            else: # both
-                value |= wxnviz.DM_WIRE_POLY
-                desc['mode'] = 'both'
-
-        if style is not None:
-            if style == 0: # wire
-                value |= wxnviz.DM_GRID_WIRE
-                desc['style'] = 'wire'
-            else: # surface
-                value |= wxnviz.DM_GRID_SURF
-                desc['style'] = 'surface'
-
-        if shade is not None:
-            if shade == 0:
-                value |= wxnviz.DM_FLAT
-                desc['shading'] = 'flat'
-            else: # surface
-                value |= wxnviz.DM_GOURAUD
-                desc['shading'] = 'gouraud'
-        
-        return (value, desc)
-    
-    def SetDecorDefaultProp(self, type):
-        """!Set default arrow properties
-        """
-        data = {}
-        
-        # arrow
-        if type == 'arrow':
-            data['arrow'] = UserSettings.Get(group = 'nviz', key = 'arrow')
-            data['arrow']['color'] = "%d:%d:%d" % (
-                UserSettings.Get(group = 'nviz', key = 'arrow', subkey = 'color')[:3])
-            data['arrow'].update(UserSettings.Get(group = 'nviz', key = 'arrow', internal = True))
-            data['arrow']['show'] = False
-        
-        # arrow
-        if type == 'scalebar':
-            data['scalebar'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar'))
-            data['scalebar']['color'] = "%d:%d:%d" % (
-                UserSettings.Get(group = 'nviz', key = 'scalebar', subkey = 'color')[:3])
-            data['scalebar'].update(copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar', internal = True)))
-            data['scalebar']['id'] = 0
-        return data
-    
 class WriteWorkspaceFile(object):
     """!Generic class for writing workspace file"""
     def __init__(self, lmgr, file):

+ 33 - 0
gui/wxpython/create__init__.py

@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import glob
+
+def main(path):
+    if not os.path.exists(path) or not os.path.isdir(path):
+        print >> sys.stderr, "'%s' is not a directory" % path
+        return 1
+    
+    modules = []
+    for f in glob.glob(os.path.join(os.path.basename(path), '*.py')):
+        if f[-5:-3] == '__':
+            continue
+        modules.append(os.path.splitext(os.path.basename(f))[0])
+        
+    fd = open(os.path.join(path, '__init__.py'), 'w')
+    try:
+        fd.write('all = [%s' % os.linesep)
+        for m in modules:
+            fd.write("    '%s',%s" % (m, os.linesep))
+        fd.write('    ]%s' % os.linesep)
+    finally:
+        fd.close()
+    
+    return 0
+
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        sys.exit("usage: %s path/to/gui_modules" % sys.argv[0])
+    
+    sys.exit(main(sys.argv[1]))

+ 15 - 17
gui/wxpython/gui_modules/dbm_dialogs.py

@@ -1,5 +1,5 @@
 """!
-@package dbm_dialogs.py
+@package dbm.dialogs
 
 @brief DBM-related dialogs
 
@@ -8,24 +8,22 @@ List of classes:
  - ModifyTableRecord
 
 (C) 2007-2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """
 
 import os
 
-import globalvar
+from core import globalvar
 import wx
 import wx.lib.scrolledpanel as scrolled
 
-import gcmd
-from debug import Debug
-from preferences import globalSettings as UserSettings
-from dbm_base    import VectorDBInfo
+from core.gcmd     import RunCommand
+from core.debug    import Debug
+from core.settings import UserSettings
+from dbm.vinfo     import VectorDBInfo
 
 class DisplayAttributesDialog(wx.Dialog):
     def __init__(self, parent, map,
@@ -299,13 +297,13 @@ class DisplayAttributesDialog(wx.Dialog):
 
             driver, database = self.mapDBInfo.GetDbSettings(layer)
             Debug.msg(1, "SQL: %s" % sql)
-            gcmd.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
         

+ 179 - 177
gui/wxpython/gui_modules/dbm.py

@@ -1,5 +1,5 @@
 """!
-@package dbm.py
+@package dbm.manager
 
 @brief GRASS Attribute Table Manager
 
@@ -17,12 +17,13 @@ List of classes:
  - Log
  - VirtualAttributeList
  - AttributeManager
+ - TableListCtrl
+ - LayerListCtrl
+ - LayerBook
 
 (C) 2007-2009, 2011 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.
 
 @author Jachym Cepicky <jachym.cepicky gmail.com>
 @author Martin Landa <landa.martin gmail.com>
@@ -35,22 +36,23 @@ import tempfile
 import copy
 import types
 
-import globalvar
+from core import globalvar
 import wx
 import wx.lib.mixins.listctrl as listmix
-import wx.lib.flatnotebook as FN
+import wx.lib.flatnotebook    as FN
 
 import grass.script as grass
 
-import sqlbuilder
-import gcmd
-import utils
-import gdialogs
-import dbm_base
-from debug       import Debug
-from dbm_dialogs import ModifyTableRecord
-from preferences import globalSettings as UserSettings
-from menuform    import GNotebook
+from dbm.sqlbuilder   import SQLFrame
+from core.gcmd        import RunCommand, GException, GError
+from core.utils       import ListOfCatsToRange
+from gui_core.dialogs import CreateNewVector
+from dbm.vinfo        import VectorDBInfo, unicodeValue, createDbInfoDesc
+from core.debug       import Debug
+from dbm.dialogs      import ModifyTableRecord
+from core.settings    import UserSettings
+from gui_core.widgets import GNotebook
+
 class Log:
     """
     The log output is redirected to the status bar of the containing frame.
@@ -86,7 +88,7 @@ class VirtualAttributeList(wx.ListCtrl,
         
         try:
             keyColumn = self.LoadData(layer)
-        except gcmd.GException, e:
+        except GException, e:
             GError(parent = self,
                    message = e.value)
             return
@@ -144,7 +146,7 @@ class VirtualAttributeList(wx.ListCtrl,
         try:
             self.columns = self.mapDBInfo.tables[tableName]
         except KeyError:
-            raise gcmd.GException(_("Attribute table <%s> not found. "
+            raise GException(_("Attribute table <%s> not found. "
                                     "For creating the table switch to "
                                     "'Manage layers' tab.") % tableName)
         
@@ -177,32 +179,32 @@ class VirtualAttributeList(wx.ListCtrl,
         outFile = tempfile.NamedTemporaryFile(mode='w+b')
         
         if sql:
-            ret = gcmd.RunCommand('db.select',
-                                  quiet = True,
-                                  parent = self,
-                                  flags = 'c',
-                                  sql = sql,
-                                  output = outFile.name)
+            ret = RunCommand('db.select',
+                             quiet = True,
+                             parent = self,
+                             flags = 'c',
+                             sql = sql,
+                             output = outFile.name)
         else:
             if columns:
-                ret = gcmd.RunCommand('v.db.select',
-                                      quiet = True,
-                                      parent = self,
-                                      flags = 'c',
-                                      map = self.mapDBInfo.map,
-                                      layer = layer,
-                                      columns = ','.join(columns),
-                                      where = where,
-                                      stdout = outFile)
+                ret = RunCommand('v.db.select',
+                                 quiet = True,
+                                 parent = self,
+                                 flags = 'c',
+                                 map = self.mapDBInfo.map,
+                                 layer = layer,
+                                 columns = ','.join(columns),
+                                 where = where,
+                                 stdout = outFile)
             else:
-                ret = gcmd.RunCommand('v.db.select',
-                                      quiet = True,
-                                      parent = self,
-                                      flags = 'c',
-                                      map = self.mapDBInfo.map,
-                                      layer = layer,
-                                      where = where,
-                                      stdout = outFile) 
+                ret = RunCommand('v.db.select',
+                                 quiet = True,
+                                 parent = self,
+                                 flags = 'c',
+                                 map = self.mapDBInfo.map,
+                                 layer = layer,
+                                 where = where,
+                                 stdout = outFile) 
         
         # These two should probably be passed to init more cleanly
         # setting the numbers of items = number of elements in the dictionary
@@ -288,7 +290,7 @@ class VirtualAttributeList(wx.ListCtrl,
             else:
                 # encode string values
                 try:
-                    self.itemDataMap[i].append(dbm_base.unicodeValue(value))
+                    self.itemDataMap[i].append(unicodeValue(value))
                 except UnicodeDecodeError:
                     self.itemDataMap[i].append(_("Unable to decode value. "
                                                  "Set encoding in GUI preferences ('Attributes')."))
@@ -298,13 +300,13 @@ class VirtualAttributeList(wx.ListCtrl,
                     cat = self.columns[columns[j]]['ctype'] (value)
                 except ValueError, e:
                     cat = -1
-                    gcmd.GError(parent = self,
-                                message=_("Error loading attribute data. "
-                                          "Record number: %(rec)d. Unable to convert value '%(val)s' in "
-                                          "key column (%(key)s) to integer.\n\n"
-                                          "Details: %(detail)s") % \
-                                    { 'rec' : i + 1, 'val' : value,
-                                      'key' : keyColumn, 'detail' : e})
+                    gGError(parent = self,
+                            message=_("Error loading attribute data. "
+                                      "Record number: %(rec)d. Unable to convert value '%(val)s' in "
+                                      "key column (%(key)s) to integer.\n\n"
+                                      "Details: %(detail)s") % \
+                                { 'rec' : i + 1, 'val' : value,
+                                  'key' : keyColumn, 'detail' : e})
             j += 1
         
         self.itemIndexMap.append(i)
@@ -456,12 +458,12 @@ class VirtualAttributeList(wx.ListCtrl,
         if not option:
             return
         
-        gcmd.RunCommand('v.to.db',
-                        parent = self.parent,
-                        map = self.mapDBInfo.map,
-                        layer = self.layer, 
-                        option = option,
-                        columns = self.GetColumn(self._col).GetText())
+        RunCommand('v.to.db',
+                   parent = self.parent,
+                   map = self.mapDBInfo.map,
+                   layer = self.layer, 
+                   option = option,
+                   columns = self.GetColumn(self._col).GetText())
         
         self.LoadData(self.layer)
         
@@ -581,7 +583,7 @@ class AttributeManager(wx.Frame):
         self.qlayer = None
 
         # -> layers / tables description
-        self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
+        self.mapDBInfo = VectorDBInfo(self.vectorName)
 
         # sqlbuilder
         self.builder = None
@@ -837,7 +839,7 @@ class AttributeManager(wx.Frame):
             dbBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
                                           label=" %s " % _("Database connection"))
             dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
-            dbSizer.Add(item=dbm_base.createDbInfoDesc(panel, self.mapDBInfo, layer),
+            dbSizer.Add(item=createDbInfoDesc(panel, self.mapDBInfo, layer),
                         proportion=1,
                         flag=wx.EXPAND | wx.ALL,
                         border=3)
@@ -1272,7 +1274,7 @@ class AttributeManager(wx.Frame):
             if zoom:
                 keyColumn = self.mapDBInfo.layers[self.layer]['key']
                 where = ''
-                for range in utils.ListOfCatsToRange(cats).split(','):
+                for range in ListOfCatsToRange(cats).split(','):
                     if '-' in range:
                         min, max = range.split('-')
                         where += '%s >= %d and %s <= %d or ' % \
@@ -1282,14 +1284,14 @@ class AttributeManager(wx.Frame):
                         where += '%s = %d or ' % (keyColumn, int(range))
                 where = where.rstrip('or ')
                 
-                select = gcmd.RunCommand('v.db.select',
-                                         parent = self,
-                                         read = True,
-                                         quiet = True,
-                                         flags = 'r',
-                                         map = self.mapDBInfo.map,
-                                         layer = int(self.layer),
-                                         where = where)
+                select = RunCommand('v.db.select',
+                                    parent = self,
+                                    read = True,
+                                    quiet = True,
+                                    flags = 'r',
+                                    map = self.mapDBInfo.map,
+                                    layer = int(self.layer),
+                                    where = where)
                 
                 region = {}
                 for line in select.splitlines():
@@ -1744,9 +1746,9 @@ class AttributeManager(wx.Frame):
         name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
         
         if not name:
-            gcmd.GError(parent = self,
-                        message = _("Unable to add column to the table. "
-                                    "No column name defined."))
+            GError(parent = self,
+                   message = _("Unable to add column to the table. "
+                               "No column name defined."))
             return
         
         ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
@@ -1765,10 +1767,10 @@ class AttributeManager(wx.Frame):
         tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
         # check for duplicate items
         if tlist.FindItem(start=-1, str=name) > -1:
-            gcmd.GError(parent = self,
-                        message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
-                            {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]}
-                        )
+            GError(parent = self,
+                   message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
+                       {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]}
+                   )
             return
         index = tlist.InsertStringItem(sys.maxint, str(name))
         tlist.SetStringItem(index, 0, str(name))
@@ -1860,12 +1862,12 @@ class AttributeManager(wx.Frame):
         
         if len(self.listOfCommands) > 0:
             for cmd in self.listOfCommands:
-                gcmd.RunCommand(prog = cmd[0],
-                                quiet = True,
-                                parent = self,
-                                **cmd[1])
+                RunCommand(prog = cmd[0],
+                           quiet = True,
+                           parent = self,
+                           **cmd[1])
             
-            self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
+            self.mapDBInfo = VectorDBInfo(self.vectorName)
             table = self.mapDBInfo.layers[self.layer]['table']
 
             # update table description
@@ -1901,11 +1903,11 @@ class AttributeManager(wx.Frame):
             Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
                       ';'.join(["%s" % s for s in self.listOfSQLStatements]))
             
-            gcmd.RunCommand('db.execute',
-                            parent = self,
-                            input = sqlFile.name,
-                            driver = driver,
-                            database = database)
+            RunCommand('db.execute',
+                       parent = self,
+                       input = sqlFile.name,
+                       driver = driver,
+                       database = database)
             
             # reset list of statements
             self.listOfSQLStatements = []
@@ -1929,9 +1931,9 @@ class AttributeManager(wx.Frame):
                     keyColumn = listWin.LoadData(self.layer, where=whereCol + whereVal)
                 else:
                     keyColumn = listWin.LoadData(self.layer)
-            except gcmd.GException, e:
-                gcmd.GError(parent = self,
-                            message = _("Loading attribute data failed.\n\n%s") % e.value)
+            except GException, e:
+                GError(parent = self,
+                       message = _("Loading attribute data failed.\n\n%s") % e.value)
                 self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('')
         else:
             # advanced sql statement
@@ -1953,9 +1955,9 @@ class AttributeManager(wx.Frame):
                 try:
                     keyColumn = listWin.LoadData(self.layer, columns=cols,
                                                  where=where, sql=sql)
-                except gcmd.GException, e:
-                    gcmd.GError(parent = self,
-                                message = _("Loading attribute data failed.\n\n%s") % e.value)
+                except GException, e:
+                    GError(parent = self,
+                           message = _("Loading attribute data failed.\n\n%s") % e.value)
                     win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
         
         # sort by key column
@@ -2026,10 +2028,10 @@ class AttributeManager(wx.Frame):
     def OnBuilder(self,event):
         """!SQL Builder button pressed -> show the SQLBuilder dialog"""
         if not self.builder:
-            self.builder = sqlbuilder.SQLFrame(parent = self, id = wx.ID_ANY,
-                                               title = _("SQL Builder"),
-                                               vectmap = self.vectorName,
-                                               evtheader = self.OnBuilderEvt)
+            self.builder = SQLFrame(parent = self, id = wx.ID_ANY,
+                                    title = _("SQL Builder"),
+                                    vectmap = self.vectorName,
+                                    evtheader = self.OnBuilderEvt)
             self.builder.Show()
         else:
             self.builder.Raise()
@@ -2066,13 +2068,13 @@ class AttributeManager(wx.Frame):
             return
         else:
             # dialog to get file name
-            dlg = gdialogs.CreateNewVector(parent = self, title = _('Extract selected features'),
-                                           log = self.cmdLog,
-                                           cmd = (('v.extract',
-                                                   { 'input' : self.vectorName,
-                                                     'cats' : utils.ListOfCatsToRange(cats) },
-                                                   'output')),
-                                           disableTable = True)
+            dlg = CreateNewVector(parent = self, title = _('Extract selected features'),
+                                  log = self.cmdLog,
+                                  cmd = (('v.extract',
+                                          { 'input' : self.vectorName,
+                                            'cats' : ListOfCatsToRange(cats) },
+                                          'output')),
+                                  disableTable = True)
             if not dlg:
                 return
             
@@ -2102,12 +2104,12 @@ class AttributeManager(wx.Frame):
                 self.mapdisplay.digit.driver.SetSelected(map(int, cats), field=self.layer)
                 self.mapdisplay.digit.DeleteSelectedLines()
             else:
-                gcmd.RunCommand('v.edit',
-                                parent = self,
-                                quiet = True,
-                                map = self.vectorName,
-                                tool = 'delete',
-                                cats = utils.ListOfCatsToRange(cats))
+                RunCommand('v.edit',
+                           parent = self,
+                           quiet = True,
+                           map = self.vectorName,
+                           tool = 'delete',
+                           cats = ListOfCatsToRange(cats))
                 
             self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
         
@@ -2146,7 +2148,7 @@ class AttributeManager(wx.Frame):
             self.notebook.SetSelectionByName('layers')
             
         # fetch fresh db info
-        self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)    
+        self.mapDBInfo = VectorDBInfo(self.vectorName)    
 
         #
         # add new page
@@ -2336,10 +2338,10 @@ class LayerBook(wx.Notebook):
         #
         # drivers
         #
-        drivers = gcmd.RunCommand('db.drivers',
-                                  quiet = True,
-                                  read = True,
-                                  flags = 'p')
+        drivers = RunCommand('db.drivers',
+                             quiet = True,
+                             read = True,
+                             flags = 'p')
         
         self.listOfDrivers = []
         for drv in drivers.splitlines():
@@ -2349,10 +2351,10 @@ class LayerBook(wx.Notebook):
         # get default values
         #
         self.defaultConnect = {}
-        connect = gcmd.RunCommand('db.connect',
-                                  flags = 'p',
-                                  read = True,
-                                  quiet = True)
+        connect = RunCommand('db.connect',
+                             flags = 'p',
+                             read = True,
+                             quiet = True)
         
         for line in connect.splitlines():
             item, value = line.split(':', 1)
@@ -2753,12 +2755,12 @@ class LayerBook(wx.Notebook):
         """!Get list of tables for given driver and database"""
         tables = []
 
-        ret = gcmd.RunCommand('db.tables',
-                              parent = self,
-                              read = True,
-                              flags = 'p',
-                              driver = driver,
-                              database = database)
+        ret = RunCommand('db.tables',
+                         parent = self,
+                         read = True,
+                         flags = 'p',
+                         driver = driver,
+                         database = database)
         
         if ret is None:
             wx.MessageBox(parent=self,
@@ -2777,13 +2779,13 @@ class LayerBook(wx.Notebook):
         """!Get list of column of given table"""
         columns = []
 
-        ret = gcmd.RunCommand('db.columns',
-                              parent = self,
-                              quiet = True,
-                              read = True,
-                              driver = driver,
-                              database = database,
-                              table = table)
+        ret = RunCommand('db.columns',
+                         parent = self,
+                         quiet = True,
+                         read = True,
+                         driver = driver,
+                         database = database,
+                         table = table)
         
         if ret == None:
             return columns
@@ -2875,13 +2877,13 @@ class LayerBook(wx.Notebook):
         # create table
         sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
 
-        gcmd.RunCommand('db.execute',
-                        quiet = True,
-                        parent = self,
-                        stdin = sql,
-                        input = '-',
-                        driver = driver,
-                        database = database)
+        RunCommand('db.execute',
+                   quiet = True,
+                   parent = self,
+                   stdin = sql,
+                   input = '-',
+                   driver = driver,
+                   database = database)
         
         # update list of tables
         tableList = self.addLayerWidgets['table'][1]
@@ -2913,26 +2915,26 @@ class LayerBook(wx.Notebook):
             return
 
         # add new layer
-        ret = gcmd.RunCommand('v.db.connect',
-                              parent = self,
-                              quiet = True,
-                              map = self.mapDBInfo.map,
-                              driver = driver,
-                              database = database,
-                              table = table,
-                              key = key,
-                              layer = layer)
+        ret = RunCommand('v.db.connect',
+                         parent = self,
+                         quiet = True,
+                         map = self.mapDBInfo.map,
+                         driver = driver,
+                         database = database,
+                         table = table,
+                         key = key,
+                         layer = layer)
         
         # insert records into table if required
         if self.addLayerWidgets['addCat'][0].IsChecked():
-            gcmd.RunCommand('v.to.db',
-                            parent = self,
-                            quiet = True,
-                            map = self.mapDBInfo.map,
-                            layer = layer,
-                            qlayer = layer,
-                            option = 'cat',
-                            columns = key)
+            RunCommand('v.to.db',
+                       parent = self,
+                       quiet = True,
+                       map = self.mapDBInfo.map,
+                       layer = layer,
+                       qlayer = layer,
+                       option = 'cat',
+                       columns = key)
 
         if ret == 0:
             # update dialog (only for new layer)
@@ -2956,11 +2958,11 @@ class LayerBook(wx.Notebook):
         except:
             return
 
-        gcmd.RunCommand('v.db.connect',
-                        parent = self,
-                        flags = 'd',
-                        map = self.mapDBInfo.map,
-                        layer = layer)
+        RunCommand('v.db.connect',
+                   parent = self,
+                   flags = 'd',
+                   map = self.mapDBInfo.map,
+                   layer = layer)
 
         # drop also table linked to layer which is deleted
         if self.deleteTable.IsChecked():
@@ -2969,12 +2971,12 @@ class LayerBook(wx.Notebook):
             table    = self.mapDBInfo.layers[layer]['table']
             sql      = 'DROP TABLE %s' % (table)
 
-            gcmd.RunCommand('db.execute',
-                            parent = self,
-                            stdin = sql,
-                            quiet = True,
-                            driver = driver,
-                            database = database)
+            RunCommand('db.execute',
+                       parent = self,
+                       stdin = sql,
+                       quiet = True,
+                       driver = driver,
+                       database = database)
             
             # update list of tables
             tableList = self.addLayerWidgets['table'][1]
@@ -3039,22 +3041,22 @@ class LayerBook(wx.Notebook):
 
         if modify:
             # delete layer
-            gcmd.RunCommand('v.db.connect',
-                            parent = self,
-                            quiet = True,
-                            flag = 'd',
-                            map = self.mapDBInfo.map,
-                            layer = layer)
+            RunCommand('v.db.connect',
+                       parent = self,
+                       quiet = True,
+                       flag = 'd',
+                       map = self.mapDBInfo.map,
+                       layer = layer)
 
             # add modified layer
-            gcmd.RunCommand('v.db.connect',
-                            quiet = True,
-                            map = self.mapDBInfo.map,
-                            driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
-                            database = self.modifyLayerWidgets['database'][1].GetValue(),
-                            table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
-                            key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
-                            layer = int(layer))
+            RunCommand('v.db.connect',
+                       quiet = True,
+                       map = self.mapDBInfo.map,
+                       driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
+                       database = self.modifyLayerWidgets['database'][1].GetValue(),
+                       table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
+                       key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
+                       layer = int(layer))
             
             # update dialog (only for new layer)
             self.parentDialog.UpdateDialog(layer=layer) 

+ 17 - 20
gui/wxpython/gui_modules/sqlbuilder.py

@@ -1,5 +1,5 @@
 """!
-@package sqlbuilder.py
+@package dbm.sqlbuilder
 
 @brief GRASS SQL Builder
 
@@ -12,10 +12,8 @@ python sqlbuilder.py vector_map
 @endcode
 
 (C) 2007-2009, 2011 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.
 
 @author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
 @author Martin Landa <landa.martin gmail.com>
@@ -24,15 +22,14 @@ for details.
 
 import os
 import sys
-import time
 
-import globalvar
+from core import globalvar
 import wx
 
 import grass.script as grass
 
-import gcmd
-import dbm_base
+from core.gcmd import RunCommand, GError
+from dbm.vinfo import createDbInfoDesc, VectorDBInfo
 
 class SQLFrame(wx.Frame):
     """!SQL Frame class"""
@@ -57,7 +54,7 @@ class SQLFrame(wx.Frame):
         
         # db info
         self.layer = layer
-        self.dbInfo = dbm_base.VectorDBInfo(self.vectmap)
+        self.dbInfo = VectorDBInfo(self.vectmap)
         self.tablename = self.dbInfo.GetTable(self.layer)
         self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
         
@@ -86,7 +83,7 @@ class SQLFrame(wx.Frame):
         databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
                                    label = " %s " % _("Database connection"))
         databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
-        databaseboxsizer.Add(item=dbm_base.createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
+        databaseboxsizer.Add(item=createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
                              proportion=1,
                              flag=wx.EXPAND | wx.ALL,
                              border=3)
@@ -415,18 +412,18 @@ class SQLFrame(wx.Frame):
     
     def OnVerify(self, event):
         """!Verify button pressed"""
-        ret, msg = gcmd.RunCommand('db.select',
-                                   getErrorMsg = True,
-                                   table = self.tablename,
-                                   sql = self.text_sql.GetValue(),
-                                   flags = 't',
-                                   driver = self.driver,
-                                   database = self.database)
+        ret, msg = RunCommand('db.select',
+                              getErrorMsg = True,
+                              table = self.tablename,
+                              sql = self.text_sql.GetValue(),
+                              flags = 't',
+                              driver = self.driver,
+                              database = self.database)
         
         if ret != 0 and msg:
             self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
-            gcmd.GError(parent = self,
-                        message = _("SQL statement is not valid.\n\n%s") % msg)
+            GError(parent = self,
+                   message = _("SQL statement is not valid.\n\n%s") % msg)
         else:
             self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
                         

+ 17 - 19
gui/wxpython/gui_modules/dbm_base.py

@@ -1,16 +1,14 @@
 """
-@package dbm_base.py
+@package dbm.vinfo
 
-@brief Support classes for dbm.py
+@brief Support classes for Database Manager
 
 List of classes:
  - VectorDBInfo
 
 (C) 2007-2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """
@@ -20,9 +18,9 @@ import types
 
 import wx
 
-import gselect
-import gcmd
-from preferences import globalSettings as UserSettings
+from gui_core.gselect import VectorDBInfo
+from core.gcmd        import RunCommand
+from core.settings    import UserSettings
 
 import grass.script as grass
 
@@ -68,11 +66,11 @@ def createDbInfoDesc(panel, mapDBInfo, layer):
     
     return infoFlexSizer
         
-class VectorDBInfo(gselect.VectorDBInfo):
+class VectorDBInfo(VectorDBInfo):
     """!Class providing information about attribute tables
     linked to the vector map"""
     def __init__(self, map):
-        gselect.VectorDBInfo.__init__(self, map)
+        VectorDBInfo.__init__(self, map)
         
     def GetColumns(self, table):
         """!Return list of columns names (based on their index)"""
@@ -138,14 +136,14 @@ class VectorDBInfo(gselect.VectorDBInfo):
         else:
             sql = "SELECT %s FROM %s WHERE %s" % (cols, table, where)
         
-        ret = gcmd.RunCommand('db.select',
-                              parent = self,
-                              read = True,
-                              quiet = True,
-                              flags = 'v',
-                              sql= sql,
-                              database = self.layers[layer]["database"],
-                              driver = self.layers[layer]["driver"])
+        ret = RunCommand('db.select',
+                         parent = self,
+                         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:

+ 131 - 146
gui/wxpython/gui_modules/gcpmanager.py

@@ -1,5 +1,5 @@
 """!
-@package gcpmanager.py
+@package gcp.manager
 
 @brief Georectification module for GRASS GIS. Includes ground control
 point management and interactive point and click GCP creation
@@ -26,41 +26,26 @@ This program is free software under the GNU General Public License
 
 import os
 import sys
-import tempfile
 import shutil
-import time
-import cStringIO
 
 import wx
 from wx.lib.mixins.listctrl import CheckListCtrlMixin, ColumnSorterMixin, ListCtrlAutoWidthMixin
-import wx.lib.colourselect as  csel
-import wx.wizard as wiz
+import wx.lib.colourselect as csel
+import wx.wizard           as wiz
 
 import grass.script as grass
 
-import globalvar
-import render
-import toolbars
-import menuform
-import gselect
-import gdialogs
-import gcmd
-import utils
-from debug import Debug as Debug
-from icon import Icons as Icons
-from location_wizard import TitledPage as TitledPage
-from preferences import globalSettings as UserSettings
-from gcpmapdisp import MapFrame
-from mapdisp_window import BufferedWindow
-
-try:
-    import subprocess # Not needed if GRASS commands could actually be quiet
-except:
-    CompatPath = globalvar.ETCWXDIR
-    sys.path.append(CompatPath)
-    from compat import subprocess
-
-sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+from core              import globalvar
+from core              import utils
+from core.render       import Map
+from gui_core.gselect  import Select, LocationSelect, MapsetSelect
+from gui_core.gdialogs import GroupDialog
+from core.gcmd         import RunCommand, GMessage, GError, GWarning
+from core.settings     import UserSettings
+from gcp.mapdisp       import MapFrame
+from mapdisp.window    import BufferedWindow
+
+from location_wizard.wizard   import TitledPage as TitledPage
 
 #
 # global variables
@@ -173,12 +158,12 @@ class GCPWizard(object):
         if self.wizard.RunWizard(self.startpage):
             success = self.OnWizFinished()
             if success == False:
-                gcmd.GMessage(parent = self.parent,
-                              message = _("Georectifying setup canceled."))
+                GMessage(parent = self.parent,
+                         message = _("Georectifying setup canceled."))
                 self.Cleanup()
         else:
-            gcmd.GMessage(parent = self.parent,
-                          message = _("Georectifying setup canceled."))
+            GMessage(parent = self.parent,
+                     message = _("Georectifying setup canceled."))
             self.Cleanup()
 
         #
@@ -187,9 +172,9 @@ class GCPWizard(object):
         if success != False:
             # instance of render.Map to be associated with display
             self.SwitchEnv('source')
-            self.SrcMap = render.Map(gisrc=self.source_gisrc) 
+            self.SrcMap = Map(gisrc=self.source_gisrc) 
             self.SwitchEnv('target')
-            self.TgtMap = render.Map(gisrc=self.target_gisrc)
+            self.TgtMap = Map(gisrc=self.target_gisrc)
             self.Map = self.SrcMap
             
             #
@@ -336,7 +321,7 @@ class LocationPage(TitledPage):
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source location:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 1))
-        self.cb_location = gselect.LocationSelect(parent = self, gisdbase = self.grassdatabase)
+        self.cb_location = LocationSelect(parent = self, gisdbase = self.grassdatabase)
         self.sizer.Add(item=self.cb_location,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 2))
@@ -345,7 +330,7 @@ class LocationPage(TitledPage):
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source mapset:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(3, 1))
-        self.cb_mapset = gselect.MapsetSelect(parent = self, gisdbase = self.grassdatabase,
+        self.cb_mapset = MapsetSelect(parent = self, gisdbase = self.grassdatabase,
                                               setItems = False)
         self.sizer.Add(item=self.cb_mapset,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
@@ -395,9 +380,9 @@ class LocationPage(TitledPage):
     def OnMapset(self, event):
         """!Sets source mapset for map(s) to georectify"""
         if self.xylocation == '':
-            gcmd.GMessage(_('You must select a valid location '
-                            'before selecting a mapset'),
-                          parent = self)
+            GMessage(_('You must select a valid location '
+                       'before selecting a mapset'),
+                     parent = self)
             return
 
         self.xymapset = event.GetString()
@@ -408,7 +393,7 @@ class LocationPage(TitledPage):
     def OnPageChanging(self, event=None):
         if event.GetDirection() and \
                (self.xylocation == '' or self.xymapset == ''):
-            gcmd.GMessage(_('You must select a valid location '
+            GMessage(_('You must select a valid location '
                             'and mapset in order to continue'),
                           parent = self)
             event.Veto()
@@ -500,7 +485,7 @@ class GroupPage(TitledPage):
         
     def OnMkGroup(self, event):
         """!Create new group in source location/mapset"""
-        dlg = gdialogs.GroupDialog(parent = self, defaultGroup = self.xygroup)
+        dlg = GroupDialog(parent = self, defaultGroup = self.xygroup)
 
         dlg.ShowModal()
         gr = dlg.GetSelectedGroup()
@@ -533,16 +518,16 @@ class GroupPage(TitledPage):
 
     def OnPageChanging(self, event=None):
         if event.GetDirection() and self.xygroup == '':
-            gcmd.GMessage(_('You must select a valid image/map '
-                            'group in order to continue'),
-                          parent = self)
+            GMessage(_('You must select a valid image/map '
+                       'group in order to continue'),
+                     parent = self)
             event.Veto()
             return
 
         if event.GetDirection() and self.extension == '':
-            gcmd.GMessage(_('You must enter an map name '
-                            'extension in order to continue'),
-                          parent = self)
+            GMessage(_('You must enter an map name '
+                       'extension in order to continue'),
+                     parent = self)
             event.Veto()
             return
 
@@ -618,8 +603,8 @@ class DispMapPage(TitledPage):
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(1, 1))
         
-        self.srcselection = gselect.Select(self, id=wx.ID_ANY,
-                                    size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
+        self.srcselection = Select(self, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
         
         self.sizer.Add(item=self.srcselection,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
@@ -629,8 +614,8 @@ class DispMapPage(TitledPage):
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 1))
 
-        self.tgtselection = gselect.Select(self, id=wx.ID_ANY,
-                                        size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
+        self.tgtselection = Select(self, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
         
         self.sizer.Add(item=self.tgtselection,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
@@ -660,9 +645,9 @@ class DispMapPage(TitledPage):
         try:
         # set computational region to match selected map and zoom display to region
             if maptype == 'cell':
-                p = gcmd.Command(['g.region', 'rast=src_map'])
+                p = RunCommand('g.region', 'rast=src_map')
             elif maptype == 'vector':
-                p = gcmd.Command(['g.region', 'vect=src_map'])
+                p = RunCommand('g.region', 'vect=src_map')
             
             if p.returncode == 0:
                 print 'returncode = ', str(p.returncode)
@@ -681,9 +666,9 @@ class DispMapPage(TitledPage):
         global tgt_map
 
         if event.GetDirection() and (src_map == ''):
-            gcmd.GMessage(_('You must select a source map '
-                            'in order to continue'),
-                          parent = self)
+            GMessage(_('You must select a source map '
+                       'in order to continue'),
+                     parent = self)
             event.Veto()
             return
 
@@ -695,19 +680,19 @@ class DispMapPage(TitledPage):
         global tgt_map
 
         self.srcselection.SetElementList(maptype)
-        ret = gcmd.RunCommand('i.group',
-                              parent = self,
-                              read = True,
-                              group = self.parent.grouppage.xygroup,
-                              flags = 'g')            
+        ret = RunCommand('i.group',
+                         parent = self,
+                         read = True,
+                         group = self.parent.grouppage.xygroup,
+                         flags = 'g')            
 
         if ret:
             self.parent.src_maps = ret.splitlines()
         else:
-            gcmd.GError(parent = self,
-                        message = _('No maps in selected group <%s>.\n'
-                                    'Please edit group or select another group.') %
-                        self.parent.grouppage.xygroup)
+            GError(parent = self,
+                   message = _('No maps in selected group <%s>.\n'
+                               'Please edit group or select another group.') %
+                   self.parent.grouppage.xygroup)
             return
 
         # filter out all maps not in group
@@ -924,17 +909,17 @@ class GCP(MapFrame, ColumnSorterMixin):
         """
         # check to see if we are georectifying map in current working location/mapset
         if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
-            gcmd.RunCommand('i.target',
-                            parent = self,
-                            flags = 'c',
-                            group = tgroup)
+            RunCommand('i.target',
+                       parent = self,
+                       flags = 'c',
+                       group = tgroup)
         else:
             self.grwiz.SwitchEnv('source')
-            gcmd.RunCommand('i.target',
-                            parent = self,
-                            group = tgroup,
-                            location = tlocation,
-                            mapset = tmapset)
+            RunCommand('i.target',
+                       parent = self,
+                       group = tgroup,
+                       location = tlocation,
+                       mapset = tmapset)
             self.grwiz.SwitchEnv('target')
 
     def AddGCP(self, event):
@@ -961,8 +946,8 @@ class GCP(MapFrame, ColumnSorterMixin):
         minNumOfItems = self.OnGROrder(None)
 
         if self.list.GetItemCount() <= minNumOfItems:
-            gcmd.GMessage(parent = self,
-                          message=_("At least %d GCPs required. Operation cancelled.") % minNumOfItems)
+            GMessage(parent = self,
+                     message=_("At least %d GCPs required. Operation cancelled.") % minNumOfItems)
             return
 
         key = self.list.DeleteGCPItem()
@@ -1048,9 +1033,9 @@ class GCP(MapFrame, ColumnSorterMixin):
             n_idx = 4
 
         if not mapWin:
-            gcmd.GError(parent = self,
-                        message="%s%s." % (_("mapwin not defined for "),
-                                           str(idx)))
+            GError(parent = self,
+                   message="%s%s." % (_("mapwin not defined for "),
+                                      str(idx)))
             return
 
         #for gcp in self.mapcoordlist:
@@ -1187,9 +1172,9 @@ class GCP(MapFrame, ColumnSorterMixin):
                 f.write(coord0 + ' ' + coord1 + '     ' + coord2 + ' ' + coord3 + '     ' + check + '\n')
 
         except IOError, err:
-            gcmd.GError(parent = self,
-                        message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
-                                                   self.file['points'], os.linesep, err))
+            GError(parent = self,
+                   message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
+                                              self.file['points'], os.linesep, err))
             return
 
         f.close()
@@ -1212,14 +1197,14 @@ class GCP(MapFrame, ColumnSorterMixin):
         #targetMapWin = self.parent.curr_page.maptree.mapdisplay.MapWindow
 
         if not sourceMapWin:
-            gcmd.GError(parent = self,
-                        message = "%s. %s%s" % (_("source mapwin not defined"),
-                                                os.linesep, err))
+            GError(parent = self,
+                   message = "%s. %s%s" % (_("source mapwin not defined"),
+                                           os.linesep, err))
         
         if not targetMapWin:
-            gcmd.GError(parent = self,
-                        message="%s. %s%s" % (_("target mapwin not defined"),
-                                              os.linesep, err))
+            GError(parent = self,
+                   message="%s. %s%s" % (_("target mapwin not defined"),
+                                         os.linesep, err))
         
         try:
             f = open(self.file['points'], 'r')
@@ -1245,9 +1230,9 @@ class GCP(MapFrame, ColumnSorterMixin):
                 GCPcnt += 1
 
         except IOError, err:
-            gcmd.GError(parent = self,
-                        message = "%s <%s>. %s%s" % (_("Reading POINTS file failed"),
-                                                     self.file['points'], os.linesep, err))
+            GError(parent = self,
+                   message = "%s <%s>. %s%s" % (_("Reading POINTS file failed"),
+                                                self.file['points'], os.linesep, err))
             return
 
         f.close()
@@ -1316,12 +1301,12 @@ class GCP(MapFrame, ColumnSorterMixin):
             (self.GCPcount < 6 and self.gr_order == 2) or \
             (self.GCPcount < 10 and self.gr_order == 3):
             if msg:
-                gcmd.GWarning(parent = self,
-                              message=_('Insufficient points defined and active (checked) '
-                                        'for selected rectification method.\n'
-                                        '3+ points needed for 1st order,\n'
-                                        '6+ points for 2nd order, and\n'
-                                        '10+ points for 3rd order.'))
+                GWarning(parent = self,
+                         message=_('Insufficient points defined and active (checked) '
+                                   'for selected rectification method.\n'
+                                   '3+ points needed for 1st order,\n'
+                                   '6+ points for 2nd order, and\n'
+                                   '10+ points for 3rd order.'))
                 return False
         else:
             return True
@@ -1348,7 +1333,7 @@ class GCP(MapFrame, ColumnSorterMixin):
                                parent=self)
             wx.Yield()
 
-            ret, msg = gcmd.RunCommand('i.rectify',
+            ret, msg = RunCommand('i.rectify',
                                   parent = self,
                                   getErrorMsg = True,
                                   quiet = True,
@@ -1400,12 +1385,12 @@ class GCP(MapFrame, ColumnSorterMixin):
                                              switchPage = True)
                 msg = err = ''
                 
-                ret, out, err = gcmd.RunCommand('v.transform',
-                                                overwrite = True,
-                                                input = vect,
-                                                output = self.outname,
-                                                pointsfile = self.file['points'],
-                                                getErrorMsg = True, read = True) 
+                ret, out, err = RunCommand('v.transform',
+                                           overwrite = True,
+                                           input = vect,
+                                           output = self.outname,
+                                           pointsfile = self.file['points'],
+                                           getErrorMsg = True, read = True) 
                 
                 if ret == 0:
                     self.VectGRList.append(self.outname)
@@ -1465,10 +1450,10 @@ class GCP(MapFrame, ColumnSorterMixin):
 
                 # TODO: connect vectors to copied tables with v.db.connect
                                                    
-            gcmd.GMessage(_('For all vector maps georectified successfully,') + '\n' +
-                          _('you will need to copy any attribute tables') + '\n' +
-                          _('and reconnect them to the georectified vectors'),
-                          parent = self)
+            GMessage(_('For all vector maps georectified successfully,') + '\n' +
+                     _('you will need to copy any attribute tables') + '\n' +
+                     _('and reconnect them to the georectified vectors'),
+                     parent = self)
         
         self.grwiz.SwitchEnv('target')
 
@@ -1603,20 +1588,20 @@ class GCP(MapFrame, ColumnSorterMixin):
         # get list of forward and reverse rms error values for each point
         self.grwiz.SwitchEnv('source')
         
-        ret = gcmd.RunCommand('m.transform',
-                              parent = self,
-                              read = True,
-                              group = xygroup,
-                              order = order)
+        ret = RunCommand('m.transform',
+                         parent = self,
+                         read = True,
+                         group = xygroup,
+                         order = order)
         
         self.grwiz.SwitchEnv('target')
 
         if ret:
             errlist = ret.splitlines()
         else:
-            gcmd.GError(parent = self,
-                        message=_('Could not calculate RMS Error.\n'
-                                  'Possible error with m.transform.'))
+            GError(parent = self,
+                   message=_('Could not calculate RMS Error.\n'
+                             'Possible error with m.transform.'))
             return
         
         # insert error values into GCP list for checked items
@@ -1714,23 +1699,23 @@ class GCP(MapFrame, ColumnSorterMixin):
         self.grwiz.SwitchEnv('source')
         
         if map == 'source':
-            ret = gcmd.RunCommand('m.transform',
-                                  parent = self,
-                                  read = True,
-                                  group = self.xygroup,
-                                  order = 1,
-                                  format = 'dst',
-                                  coords = coord_file)
+            ret = RunCommand('m.transform',
+                             parent = self,
+                             read = True,
+                             group = self.xygroup,
+                             order = 1,
+                             format = 'dst',
+                             coords = coord_file)
 
         elif map == 'target':
-            ret = gcmd.RunCommand('m.transform',
-                                  parent = self,
-                                  read = True,
-                                  group = self.xygroup,
-                                  order = 1,
-                                  flags = 'r',
-                                  format = 'src',
-                                  coords = coord_file)
+            ret = RunCommand('m.transform',
+                             parent = self,
+                             read = True,
+                             group = self.xygroup,
+                             order = 1,
+                             flags = 'r',
+                             format = 'src',
+                             coords = coord_file)
 
         os.unlink(coord_file)
         
@@ -1739,9 +1724,9 @@ class GCP(MapFrame, ColumnSorterMixin):
         if ret:
             errlist = ret.splitlines()
         else:
-            gcmd.GError(parent = self,
-                        message=_('Could not calculate new extends.\n'
-                                  'Possible error with m.transform.'))
+            GError(parent = self,
+                   message=_('Could not calculate new extends.\n'
+                             'Possible error with m.transform.'))
             return
 
         # fist corner
@@ -2085,8 +2070,8 @@ class GCPList(wx.ListCtrl,
             values = dlg.GetValues() # string
             
             if len(values) == 0:
-                gcmd.GError(parent = self,
-                            message=_("Invalid coordinate value. Operation cancelled."))
+                GError(parent = self,
+                       message=_("Invalid coordinate value. Operation cancelled."))
             else:
                 for i in range(len(values)):
                     if values[i] != coords[i]:
@@ -2556,16 +2541,16 @@ class GrSettingsDialog(wx.Dialog):
         # maps to display
         #
         # source map to display
-        self.srcselection = gselect.Select(panel, id=wx.ID_ANY,
-                    size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
+        self.srcselection = Select(panel, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
         self.parent.grwiz.SwitchEnv('source')
         self.srcselection.SetElementList(maptype)
         # filter out all maps not in group
         self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
 
         # target map to display
-        self.tgtselection = gselect.Select(panel, id=wx.ID_ANY,
-                    size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
+        self.tgtselection = Select(panel, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
         self.parent.grwiz.SwitchEnv('target')
         self.tgtselection.SetElementList(maptype)
         self.tgtselection.GetElementList()
@@ -2660,12 +2645,12 @@ class GrSettingsDialog(wx.Dialog):
         self.sdfactor = float(event.GetString())
 
         if self.sdfactor <= 0:
-            gcmd.GError(parent = self,
-                        message=_('RMS threshold factor must be > 0'))
+            GError(parent = self,
+                   message=_('RMS threshold factor must be > 0'))
         elif self.sdfactor < 1:
-            gcmd.GError(parent = self,
-                        message=_('RMS threshold factor is < 1\n'
-                                  'Too many points might be highlighted'))
+            GError(parent = self,
+                   message=_('RMS threshold factor is < 1\n'
+                             'Too many points might be highlighted'))
         
     def OnSrcSelection(self,event):
         """!Source map to display selected"""

+ 27 - 57
gui/wxpython/gui_modules/gcpmapdisp.py

@@ -1,66 +1,38 @@
 """!
-@package gcpmapdisp.py
+@package gcp.mapdisp
 
-@brief display to manage ground control points with two toolbars, one for
-various display management functions, one for manipulating GCPs.
+@brief Display to manage ground control points with two toolbars, one
+for various display management functions, one for manipulating GCPs.
 
 Classes:
 - MapFrame
 
-(C) 2006-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.
-
-Derived from mapdisp.py
+(C) 2006-2011 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.
 
 @author Markus Metz
 """
 
 import os
-import sys
-import glob
 import math
-import tempfile
-import copy
 import platform
 
-import globalvar
+from core import globalvar
 import wx
 import wx.aui
 
-try:
-    import subprocess
-except:
-    CompatPath = os.path.join(globalvar.ETCWXDIR)
-    sys.path.append(CompatPath)
-    from compat import subprocess
-
-gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
-sys.path.append(gmpath)
-
-grassPath = os.path.join(globalvar.ETCDIR, "python")
-sys.path.append(grassPath)
-
-import render
-import toolbars
-import menuform
-import gselect
-import disp_print
-import gcmd
-import dbm
-import dbm_dialogs
-import globalvar
-import utils
-import gdialogs
-import mapdisp_statusbar as sb
-from mapdisp import MapFrameBase
-from grass.script import core as grass
-from debug import Debug
-from icon  import Icons
-from preferences import globalSettings as UserSettings
-
-from mapdisp_window import BufferedWindow
+from core.render      import EVT_UPDATE_PRGBAR
+from mapdisp.toolbars import MapToolbar
+from gcp.toolbars     import GCPDisplayToolbar, GCPManToolbar
+from mapdisp.gprint   import PrintOptions
+from core.gcmd        import GMessage
+from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
+from gui_core.mapdisp import MapFrameBase
+from core.settings    import UserSettings
+from mapdisp.window   import BufferedWindow
+
+import mapdisp.statusbar as sb
 
 # for standalone app
 cmdfilename = None
@@ -161,7 +133,7 @@ class MapFrame(MapFrameBase):
         # Bind various events
         #
         self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
-        self.Bind(render.EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+        self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
         self.Bind(wx.EVT_SIZE,     self.OnDispResize)
         self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
         
@@ -206,7 +178,7 @@ class MapFrame(MapFrameBase):
         #
         # Init print module and classes
         #
-        self.printopt = disp_print.PrintOptions(self, self.MapWindow)
+        self.printopt = PrintOptions(self, self.MapWindow)
         
         #
         # Initialization of digitization tool
@@ -242,7 +214,7 @@ class MapFrame(MapFrameBase):
         """
         # default toolbar
         if name == "map":
-            self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
+            self.toolbars['map'] = MapToolbar(self, self.Map)
 
             self._mgr.AddPane(self.toolbars['map'],
                               wx.aui.AuiPaneInfo().
@@ -255,7 +227,7 @@ class MapFrame(MapFrameBase):
 
         # GCP display
         elif name == "gcpdisp":
-            self.toolbars['gcpdisp'] = toolbars.GCPDisplayToolbar(self)
+            self.toolbars['gcpdisp'] = GCPDisplayToolbar(self)
 
             self._mgr.AddPane(self.toolbars['gcpdisp'],
                               wx.aui.AuiPaneInfo().
@@ -268,7 +240,7 @@ class MapFrame(MapFrameBase):
             if self.show_target == False:
                 self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
 
-            self.toolbars['gcpman'] = toolbars.GCPManToolbar(self)
+            self.toolbars['gcpman'] = GCPManToolbar(self)
 
             self._mgr.AddPane(self.toolbars['gcpman'],
                               wx.aui.AuiPaneInfo().
@@ -463,13 +435,13 @@ class MapFrame(MapFrameBase):
         """
         img = self.MapWindow.img
         if not img:
-            gcmd.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 = gdialogs.GetImageHandlers(img)
+        filetype, ltype = GetImageHandlers(img)
 
         # get size
-        dlg = gdialogs.ImageSizeDialog(self)
+        dlg = ImageSizeDialog(self)
         dlg.CentreOnParent()
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
@@ -655,5 +627,3 @@ class MapFrame(MapFrameBase):
     def GetMapToolbar(self):
         """!Returns toolbar with zooming tools"""
         return self.toolbars['gcpdisp']
-        
-# end of class MapFrame

+ 127 - 0
gui/wxpython/gcp/toolbars.py

@@ -0,0 +1,127 @@
+"""!
+@package gcp.toolbars
+
+@brief Georectification module - toolbars
+
+Classes:
+ - GCPMapToolbar
+ - GCPDisplayToolbar
+
+(C) 2007-2011 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.
+
+@author Markus Metz
+"""
+
+import os
+import sys
+
+import wx
+
+from core              import globalvar
+from gui_core.toolbars import BaseToolbar
+
+sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+from icon              import Icons
+    
+class GCPManToolbar(BaseToolbar):
+    """!Toolbar for managing ground control points
+
+    @param parent reference to GCP widget
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        icons = Icons['georectify']
+        return self._getToolbarData((('gcpSave', icons["gcpSave"],
+                                      self.parent.SaveGCPs),
+                                     ('gcpReload', icons["gcpReload"],
+                                      self.parent.ReloadGCPs),
+                                     (None, ),
+                                     ('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))
+                                    )
+    
+class GCPDisplayToolbar(BaseToolbar):
+    """!GCP Display toolbar
+    """
+    def __init__(self, parent):
+        """!GCP Display toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # add tool to toggle active map window
+        self.togglemapid = wx.NewId()
+        self.togglemap = wx.Choice(parent = self, id = self.togglemapid,
+                                   choices = [_('source'), _('target')],
+                                   style = wx.CB_READONLY)
+
+        self.InsertControl(10, self.togglemap)
+
+        self.SetToolShortHelp(self.togglemapid, '%s %s %s' % (_('Set map canvas for '),
+                                                              Icons['displayWindow']["zoomBack"].GetLabel(),
+                                                              _(' / Zoom to map')))
+
+        # realize the toolbar
+        self.Realize()
+        
+        self.action = { 'id' : self.gcpset }
+        self.defaultAction = { 'id' : self.gcpset,
+                               'bind' : self.parent.OnPointer }
+        
+        self.OnTool(None)
+        
+        self.EnableTool(self.zoomback, False)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['displayWindow']
+        return self._getToolbarData((("displaymap", icons["display"],
+                                      self.parent.OnDraw),
+                                     ("rendermap", icons["render"],
+                                      self.parent.OnRender),
+                                     ("erase", icons["erase"],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ("gcpset", Icons["georectify"]["gcpSet"],
+                                      self.parent.OnPointer),
+                                     ("pan", icons["pan"],
+                                      self.parent.OnPan),
+                                     ("zoomin", icons["zoomIn"],
+                                      self.parent.OnZoomIn),
+                                     ("zoomout", icons["zoomOut"],
+                                      self.parent.OnZoomOut),
+                                     ("zoommenu", icons["zoomMenu"],
+                                      self.parent.OnZoomMenuGCP),
+                                     (None, ),
+                                     ("zoomback", icons["zoomBack"],
+                                      self.parent.OnZoomBack),
+                                     ("zoomtomap", icons["zoomExtent"],
+                                      self.parent.OnZoomToMap),
+                                     (None, ),
+                                     ('settings', Icons["georectify"]["settings"],
+                                      self.parent.OnSettings),
+                                     ('help', Icons["misc"]["help"],
+                                      self.parent.OnHelp),
+                                     (None, ),
+                                     ('quit', Icons["georectify"]["quit"],
+                                      self.parent.OnQuit))
+                                    )

+ 628 - 0
gui/wxpython/gmodeler/dialogs.py

@@ -0,0 +1,628 @@
+"""!
+@package gmodeler.dialogs
+
+@brief wxGUI Graphical Modeler - dialogs
+
+Classes:
+ - ModelDataDialog
+ - ModelSearchDialog
+ - ModelRelationDialog
+ - ModelParamDialog
+ - ModelItemDialog
+ - ModelLoopDialog
+ - ModelConditionDialog
+
+(C) 2010-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+import wx
+
+from core                 import globalvar
+from gui_core.widgets     import GNotebook
+from core.gcmd            import GError, EncodeString
+from gui_core.dialogs     import ElementDialog, MapLayersDialog
+from gui_core.ghelp       import SearchModuleWindow
+
+from grass.script import task as gtask
+
+class ModelDataDialog(ElementDialog):
+    """!Data item properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Data properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        self.parent = parent
+        self.shape = shape
+        
+        label, etype = self._getLabel()
+        ElementDialog.__init__(self, parent, title, label = label, etype = etype)
+                
+        self.element = gselect.Select(parent = self.panel,
+                                      type = prompt)
+        self.element.SetValue(shape.GetValue())
+        
+        self.Bind(wx.EVT_BUTTON, self.OnOK,     self.btnOK)
+        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
+        
+        self.PostInit()
+        
+        if shape.GetValue():
+            self.btnOK.Enable()
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        
+    def _getLabel(self):
+        etype = False
+        prompt = self.shape.GetPrompt()
+        if prompt == 'raster':
+            label = _('Name of raster map:')
+        elif prompt == 'vector':
+            label = _('Name of vector map:')
+        else:
+            etype = True
+            label = _('Name of element:')
+
+        return label, etype
+    
+    def _layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=1)
+        
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def OnOK(self, event):
+        """!Ok pressed"""
+        self.shape.SetValue(self.GetElement())
+        if self.etype:
+            elem = self.GetType()
+            if elem == 'rast':
+                self.shape.SetPrompt('raster')
+            elif elem == 'vect':
+                self.shape.SetPrompt('raster')
+        
+        self.parent.canvas.Refresh()
+        self.parent.SetStatusText('', 0)
+        self.shape.SetPropDialog(None)
+        
+        if self.IsModal():
+            event.Skip() 
+        else:
+            self.Destroy()
+    
+    def OnCancel(self, event):
+        """!Cancel pressed"""
+        self.shape.SetPropDialog(None)
+        if self.IsModal():
+            event.Skip()
+        else:
+            self.Destroy()
+class ModelSearchDialog(wx.Dialog):
+    def __init__(self, parent, id = wx.ID_ANY, title = _("Add new GRASS module to the model"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Graphical modeler module search window
+        
+        @param parent parent window
+        @param id window id
+        @param title window title
+        @param kwargs wx.Dialogs' arguments
+        """
+        self.parent = parent
+        
+        wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetName("ModelerDialog")
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                   label=" %s " % _("Command"))
+        
+        self.cmd_prompt = prompt.GPromptSTC(parent = self)
+        self.search = SearchModuleWindow(parent = self.panel, cmdPrompt = self.cmd_prompt, showTip = True)
+        wx.CallAfter(self.cmd_prompt.SetFocus)
+        
+        # get commands
+        items = self.cmd_prompt.GetCommandItems()
+        
+        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+        self.btnOk     = wx.Button(self.panel, wx.ID_OK)
+        self.btnOk.SetDefault()
+        self.btnOk.Enable(False)
+
+        self.cmd_prompt.Bind(wx.EVT_KEY_UP, self.OnText)
+        self.search.searchChoice.Bind(wx.EVT_CHOICE, self.OnText)
+        self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk)
+        
+        self._layout()
+        
+        self.SetSize((500, 275))
+        
+    def _layout(self):
+        cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
+        cmdSizer.Add(item = self.cmd_prompt, proportion = 1,
+                     flag = wx.EXPAND)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.search, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 3)
+        mainSizer.Add(item = cmdSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.panel.SetSizer(mainSizer)
+        mainSizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def GetPanel(self):
+        """!Get dialog panel"""
+        return self.panel
+
+    def GetCmd(self):
+        """!Get command"""
+        line = self.cmd_prompt.GetCurLine()[0].strip()
+        if len(line) == 0:
+            list()
+        
+        try:
+            cmd = utils.split(str(line))
+        except UnicodeError:
+            cmd = utils.split(EncodeString((line)))
+            
+        return cmd
+    
+    def OnOk(self, event):
+        """!Button 'OK' pressed"""
+        self.btnOk.SetFocus()
+        cmd = self.GetCmd()
+        
+        if len(cmd) < 1:
+            GError(parent = self,
+                   message = _("Command not defined.\n\n"
+                               "Unable to add new action to the model."))
+            return
+        
+        if cmd[0] not in globalvar.grassCmd['all']:
+            GError(parent = self,
+                   message = _("'%s' is not a GRASS module.\n\n"
+                               "Unable to add new action to the model.") % cmd[0])
+            return
+        
+        self.EndModal(wx.ID_OK)
+        
+    def OnText(self, event):
+        """!Text in prompt changed"""
+        if self.cmd_prompt.AutoCompActive():
+            event.Skip()
+            return
+        
+        if isinstance(event, wx.KeyEvent):
+            entry = self.cmd_prompt.GetTextLeft()
+        elif isinstance(event, wx.stc.StyledTextEvent):
+            entry = event.GetText()
+        else:
+            entry = event.GetString()
+        
+        if entry:
+            self.btnOk.Enable()
+        else:
+            self.btnOk.Enable(False)
+            
+        event.Skip()
+        
+    def Reset(self):
+        """!Reset dialog"""
+        self.search.Reset()
+        self.cmd_prompt.OnCmdErase(None)
+        self.btnOk.Enable(False)
+        self.cmd_prompt.SetFocus()
+
+class ModelRelationDialog(wx.Dialog):
+    """!Relation properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Relation properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        self.parent = parent
+        self.shape = shape
+        
+        options = self._getOptions()
+        if not options:
+            self.valid = False
+            return
+        
+        self.valid = True
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.fromBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label = " %s " % _("From"))
+        self.toBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                  label = " %s " % _("To"))
+        
+        self.option = wx.ComboBox(parent = self.panel, id = wx.ID_ANY,
+                                  style = wx.CB_READONLY,
+                                  choices = options)
+        self.option.Bind(wx.EVT_COMBOBOX, self.OnOption)
+        
+        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+        self.btnOk     = wx.Button(self.panel, wx.ID_OK)
+        self.btnOk.Enable(False)
+        
+        self._layout()
+
+    def _layout(self):
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+        fromSizer = wx.StaticBoxSizer(self.fromBox, wx.VERTICAL)
+        self._layoutShape(shape = self.shape.GetFrom(), sizer = fromSizer)
+        toSizer = wx.StaticBoxSizer(self.toBox, wx.VERTICAL)
+        self._layoutShape(shape = self.shape.GetTo(), sizer = toSizer)
+
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+        
+        mainSizer.Add(item = fromSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = toSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.panel.SetSizer(mainSizer)
+        mainSizer.Fit(self.panel)
+        
+        self.Layout()
+        self.SetSize(self.GetBestSize())
+        
+    def _layoutShape(self, shape, sizer):
+        if isinstance(shape, ModelData):
+            sizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                           label = _("Data: %s") % shape.GetLog()),
+                      proportion = 1, flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        elif isinstance(shape, ModelAction):
+            gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = _("Command:")),
+                          pos = (0, 0))
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = shape.GetName()),
+                          pos = (0, 1))
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = _("Option:")),
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 0))
+            gridSizer.Add(item = self.option,
+                          pos = (1, 1))
+            sizer.Add(item = gridSizer,
+                      proportion = 1, flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+            
+    def _getOptions(self):
+        """!Get relevant options"""
+        items = []
+        fromShape = self.shape.GetFrom()
+        if not isinstance(fromShape, ModelData):
+            GError(parent = self.parent,
+                   message = _("Relation doesn't start with data item.\n"
+                               "Unable to add relation."))
+            return items
+        
+        toShape = self.shape.GetTo()
+        if not isinstance(toShape, ModelAction):
+            GError(parent = self.parent,
+                   message = _("Relation doesn't point to GRASS command.\n"
+                               "Unable to add relation."))
+            return items
+        
+        prompt = fromShape.GetPrompt()
+        task = toShape.GetTask()
+        for p in task.get_options()['params']:
+            if p.get('prompt', '') == prompt and \
+                    'name' in p:
+                items.append(p['name'])
+        
+        if not items:
+            GError(parent = self.parent,
+                   message = _("No relevant option found.\n"
+                               "Unable to add relation."))
+        return items
+    
+    def GetOption(self):
+        """!Get selected option"""
+        return self.option.GetStringSelection()
+    
+    def IsValid(self):
+        """!Check if relation is valid"""
+        return self.valid
+    
+    def OnOption(self, event):
+        """!Set option"""
+        if event.GetString():
+            self.btnOk.Enable()
+        else:
+            self.btnOk.Enable(False)
+
+class ModelParamDialog(wx.Dialog):
+    def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Model parameters dialog
+        """
+        self.parent = parent
+        self.params = params
+        self.tasks  = list() # list of tasks/pages
+        
+        wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs)
+        
+        self.notebook = GNotebook(parent = self, 
+                                  style = globalvar.FNPageDStyle)
+        
+        panel = self._createPages()
+        wx.CallAfter(self.notebook.SetSelection, 0)
+        
+        self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        self.btnRun    = wx.Button(parent = self, id = wx.ID_OK,
+                                   label = _("&Run"))
+        self.btnRun.SetDefault()
+        
+        self._layout()
+        
+        size = self.GetBestSize()
+        self.SetMinSize(size)
+        self.SetSize((size.width, size.height +
+                      panel.constrained_size[1] -
+                      panel.panelMinHeight))
+                
+    def _layout(self):
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnRun)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1,
+                      flag = wx.EXPAND)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def _createPages(self):
+        """!Create for each parameterized module its own page"""
+        nameOrdered = [''] * len(self.params.keys())
+        for name, params in self.params.iteritems():
+            nameOrdered[params['idx']] = name
+        for name in nameOrdered:
+            params = self.params[name]
+            panel = self._createPage(name, params)
+            if name == 'variables':
+                name = _('Variables')
+            self.notebook.AddPage(page = panel, text = name)
+        
+        return panel
+    
+    def _createPage(self, name, params):
+        """!Define notebook page"""
+        if name in globalvar.grassCmd['all']:
+            task = gtask.grassTask(name)
+        else:
+            task = gtask.grassTask()
+        task.flags  = params['flags']
+        task.params = params['params']
+        
+        panel = menuform.cmdPanel(parent = self, id = wx.ID_ANY, task = task)
+        self.tasks.append(task)
+        
+        return panel
+
+    def GetErrors(self):
+        """!Check for errors, get list of messages"""
+        errList = list()
+        for task in self.tasks:
+            errList += task.get_cmd_error()
+        
+        return errList
+
+class ModelItemDialog(wx.Dialog):
+    """!Abstract item properties dialog"""
+    def __init__(self, parent, shape, title, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        self.parent = parent
+        self.shape = shape
+        
+        wx.Dialog.__init__(self, parent, id, title = title, style = style, **kwargs)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.condBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("Condition"))
+        self.condText = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+                                    value = shape.GetText())
+        
+        self.itemList = ItemCheckListCtrl(parent = self.panel,
+                                          window = self,
+                                          columns = [_("ID"), _("Name"),
+                                                     _("Command")],
+                                          shape = shape)
+        self.itemList.Populate(self.parent.GetModel().GetItems())
+        
+        self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        self.btnOk     = wx.Button(parent = self.panel, id = wx.ID_OK)
+        self.btnOk.SetDefault()
+        
+    def _layout(self):
+        """!Do layout (virtual method)"""
+        pass
+    
+    def GetCondition(self):
+        """!Get loop condition"""
+        return self.condText.GetValue()
+
+class ModelLoopDialog(ModelItemDialog):
+    """!Loop properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Loop properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        ModelItemDialog.__init__(self, parent, shape, title,
+                                 style = style, **kwargs)
+        
+        self.listBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("List of items in loop"))
+        
+        self.btnSeries = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                   label = _("Series"))
+        self.btnSeries.SetToolTipString(_("Define map series as condition for the loop"))
+        self.btnSeries.Bind(wx.EVT_BUTTON, self.OnSeries)
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        self.SetSize((500, 400))
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        condSizer = wx.StaticBoxSizer(self.condBox, wx.HORIZONTAL)
+        condSizer.Add(item = self.condText, proportion = 1,
+                      flag = wx.ALL, border = 3)
+        condSizer.Add(item = self.btnSeries, proportion = 0,
+                      flag = wx.EXPAND)
+
+        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+        listSizer.Add(item = self.itemList, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item = condSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        sizer.Add(item = listSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+        
+    def GetItems(self):
+        """!Get list of selected actions"""
+        return self.itemList.GetItems()
+
+    def OnSeries(self, event):
+        """!Define map series as condition"""
+        dialog = MapLayersDialog(parent = self, title = _("Define series of maps"), modeler = True)
+        if dialog.ShowModal() != wx.ID_OK:
+            dialog.Destroy()
+            return
+        
+        cond = dialog.GetDSeries()
+        if not cond:
+            cond = 'map in %s' % map(lambda x: str(x), dialog.GetMapLayers())
+        
+        self.condText.SetValue(cond)
+                               
+        dialog.Destroy()
+
+class ModelConditionDialog(ModelItemDialog):
+    """!Condition properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("If-else properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        ModelItemDialog.__init__(self, parent, shape, title,
+                                 style = style, **kwargs)
+        
+        self.listBoxIf = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                      label=" %s " % _("List of items in 'if' block"))
+        self.itemListIf = self.itemList
+        self.itemListIf.SetName('IfBlockList')
+        
+        self.listBoxElse = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                        label=" %s " % _("List of items in 'else' block"))
+        self.itemListElse = ItemCheckListCtrl(parent = self.panel,
+                                              window = self,
+                                              columns = [_("ID"), _("Name"),
+                                                         _("Command")],
+                                              shape = shape)
+        self.itemListElse.SetName('ElseBlockList')
+        self.itemListElse.Populate(self.parent.GetModel().GetItems())
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        self.SetSize((500, 400))
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL)
+        condSizer.Add(item = self.condText, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        listIfSizer = wx.StaticBoxSizer(self.listBoxIf, wx.VERTICAL)
+        listIfSizer.Add(item = self.itemListIf, proportion = 1,
+                        flag = wx.EXPAND)
+        listElseSizer = wx.StaticBoxSizer(self.listBoxElse, wx.VERTICAL)
+        listElseSizer.Add(item = self.itemListElse, proportion = 1,
+                          flag = wx.EXPAND)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item = condSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        sizer.Add(item = listIfSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = listElseSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def OnCheckItemIf(self, index, flag):
+        """!Item in if-block checked/unchecked"""
+        if flag is False:
+            return
+        
+        aId = int(self.itemListIf.GetItem(index, 0).GetText())
+        if aId in self.itemListElse.GetItems()['checked']:
+            self.itemListElse.CheckItemById(aId, False)
+            
+    def OnCheckItemElse(self, index, flag):
+        """!Item in else-block checked/unchecked"""
+        if flag is False:
+            return
+        
+        aId = int(self.itemListElse.GetItem(index, 0).GetText())
+        if aId in self.itemListIf.GetItems()['checked']:
+            self.itemListIf.CheckItemById(aId, False)
+        
+    def GetItems(self):
+        """!Get items"""
+        return { 'if'   : self.itemListIf.GetItems(),
+                 'else' : self.itemListElse.GetItems() }

Різницю між файлами не показано, бо вона завелика
+ 2656 - 0
gui/wxpython/gmodeler/frame.py


+ 731 - 0
gui/wxpython/gmodeler/model.py

@@ -0,0 +1,731 @@
+"""!
+@package gmodeler.model
+
+@brief wxGUI Graphical Modeler (base classes)
+
+Classes:
+ - Model
+
+(C) 2010-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import getpass
+import copy
+import re
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+import wx
+
+from core.globalvar import ETCWXDIR
+from core.gcmd      import GMessage, GException, GError, RunCommand
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+class Model(object):
+    """!Class representing the model"""
+    def __init__(self, canvas = None):
+        self.items      = list() # list of actions/loops/...
+        
+        # model properties
+        self.properties = { 'name'        : _("model"),
+                            'description' : _("Script generated by wxGUI Graphical Modeler."),
+                            'author'      : getpass.getuser() }
+        # model variables
+        self.variables = dict()
+        self.variablesParams = dict()
+        
+        self.canvas  = canvas
+        
+    def GetCanvas(self):
+        """!Get canvas or None"""
+        return self.canvas
+    
+    def GetItems(self, objType = None):
+        """!Get list of model items
+
+        @param objType Object type to filter model objects
+        """
+        if not objType:
+            return self.items
+        
+        result = list()
+        for item in self.items:
+            if isinstance(item, objType):
+                result.append(item)
+        
+        return result
+
+    def GetItem(self, aId):
+        """!Get item of given id
+
+        @param aId item id
+        
+        @return Model* instance
+        @return None if no item found
+        """
+        ilist = self.GetItems()
+        for item in ilist:
+            if item.GetId() == aId:
+                return item
+        
+        return None
+
+    def GetNumItems(self, actionOnly = False):
+        """!Get number of items"""
+        if actionOnly:
+            return len(self.GetItems(objType = ModelAction))
+        
+        return len(self.GetItems())
+
+    def GetNextId(self):
+        """!Get next id (data ignored)
+
+        @return next id to be used (default: 1)
+        """
+        if len(self.items) < 1:
+            return 1
+        
+        currId = self.items[-1].GetId()
+        if currId > 0:
+            return currId + 1
+        
+        return 1
+
+    def GetProperties(self):
+        """!Get model properties"""
+        return self.properties
+
+    def GetVariables(self, params = False):
+        """!Get model variables"""
+        if params:
+            return self.variablesParams
+        
+        return self.variables
+    
+    def SetVariables(self, data):
+        """!Set model variables"""
+        self.variables = data
+    
+    def Reset(self):
+        """!Reset model"""
+        self.items = list()
+        
+    def RemoveItem(self, item):
+        """!Remove item from model
+        
+        @return list of related items to remove/update
+        """
+        relList = list()
+        upList = list()
+        
+        if not isinstance(item, ModelData):
+            self.items.remove(item)
+        
+        if isinstance(item, ModelAction):
+            for rel in item.GetRelations():
+                relList.append(rel)
+                data = rel.GetData()
+                if len(data.GetRelations()) < 2:
+                    relList.append(data)
+                else:
+                    upList.append(data)
+            
+        elif isinstance(item, ModelData):
+            for rel in item.GetRelations():
+                relList.append(rel)
+                if rel.GetFrom() == self:
+                    relList.append(rel.GetTo())
+                else:
+                    relList.append(rel.GetFrom())
+        
+        elif isinstance(item, ModelLoop):
+            for rel in item.GetRelations():
+                relList.append(rel)
+            for action in self.GetItems():
+                action.UnSetBlock(item)
+        
+        return relList, upList
+    
+    def FindAction(self, aId):
+        """!Find action by id"""
+        alist = self.GetItems(objType = ModelAction)
+        for action in alist:
+            if action.GetId() == aId:
+                return action
+        
+        return None
+
+    def GetData(self):
+        """!Get list of data items"""
+        result = list()
+        dataItems = self.GetItems(objType = ModelData)
+        
+        for action in self.GetItems(objType = ModelAction):
+            for rel in action.GetRelations():
+                dataItem = rel.GetData()
+                if dataItem not in result:
+                    result.append(dataItem)
+                if dataItem in dataItems:
+                    dataItems.remove(dataItem)
+        
+        # standalone data
+        if dataItems:
+            result += dataItems
+        
+        return result
+
+    def FindData(self, value, prompt):
+        """!Find data item in the model
+
+        @param value value
+        @param prompt prompt
+
+        @return ModelData instance
+        @return None if not found
+        """
+        for data in self.GetData():
+            if data.GetValue() == value and \
+                    data.GetPrompt() == prompt:
+                return data
+        
+        return None
+                
+    def LoadModel(self, filename):
+        """!Load model definition stored in GRASS Model XML file (gxm)
+        
+        @todo Validate against DTD
+        
+        Raise exception on error.
+        """
+        dtdFilename = os.path.join(ETCWXDIR, "xml", "grass-gxm.dtd")
+        
+        # parse workspace file
+        try:
+            gxmXml = ProcessModelFile(etree.parse(filename))
+        except StandardError, e:
+            raise GException(e)
+        
+        if self.canvas:
+            win = self.canvas.parent
+            if gxmXml.pos:
+                win.SetPosition(gxmXml.pos)
+            if gxmXml.size:
+                win.SetSize(gxmXml.size)
+        
+        # load properties
+        self.properties = gxmXml.properties
+        self.variables  = gxmXml.variables
+        
+        # load model.GetActions()
+        for action in gxmXml.actions:
+            actionItem = ModelAction(parent = self, 
+                                     x = action['pos'][0],
+                                     y = action['pos'][1],
+                                     width = action['size'][0],
+                                     height = action['size'][1],
+                                     task = action['task'],
+                                     id = action['id'])
+            
+            if action['disabled']:
+                actionItem.Enable(False)
+            
+            self.AddItem(actionItem)
+            
+            actionItem.SetValid(actionItem.GetTask().get_options())
+            actionItem.GetLog() # substitute variables (-> valid/invalid)
+        
+        # load data & relations
+        for data in gxmXml.data:
+            dataItem = ModelData(parent = self, 
+                                 x = data['pos'][0],
+                                 y = data['pos'][1],
+                                 width = data['size'][0],
+                                 height = data['size'][1],
+                                 prompt = data['prompt'],
+                                 value = data['value'])
+            dataItem.SetIntermediate(data['intermediate'])
+            
+            for rel in data['rels']:
+                actionItem = self.FindAction(rel['id'])
+                if rel['dir'] == 'from':
+                    relation = ModelRelation(parent = self, fromShape = dataItem,
+                                             toShape = actionItem, param = rel['name'])
+                else:
+                    relation = ModelRelation(parent = self, fromShape = actionItem,
+                                             toShape = dataItem, param = rel['name'])
+                relation.SetControlPoints(rel['points'])
+                actionItem.AddRelation(relation)
+                dataItem.AddRelation(relation)
+
+            if self.canvas:
+                dataItem.Update()
+           
+        # load loops
+        for loop in gxmXml.loops:
+            loopItem = ModelLoop(parent = self, 
+                                 x = loop['pos'][0],
+                                 y = loop['pos'][1],
+                                 width = loop['size'][0],
+                                 height = loop['size'][1],
+                                 text = loop['text'],
+                                 id = loop['id'])
+            self.AddItem(loopItem)
+
+        # load conditions
+        for condition in gxmXml.conditions:
+            conditionItem = ModelCondition(parent = self, 
+                                           x = condition['pos'][0],
+                                           y = condition['pos'][1],
+                                           width = condition['size'][0],
+                                           height = condition['size'][1],
+                                           text = condition['text'],
+                                           id = condition['id'])
+            self.AddItem(conditionItem)
+
+        # define loops & if/else items
+        for loop in gxmXml.loops:
+            alist = list()
+            for aId in loop['items']:
+                action = self.GetItem(aId)
+                alist.append(action)
+            
+            loopItem = self.GetItem(loop['id'])
+            loopItem.SetItems(alist)
+            
+            for action in loopItem.GetItems():
+                action.SetBlock(loopItem)
+        
+        for condition in gxmXml.conditions:
+            conditionItem = self.GetItem(condition['id'])
+            for b in condition['items'].keys():
+                alist = list()
+                for aId in condition['items'][b]:
+                    action = self.GetItem(aId)
+                    alist.append(action)
+                conditionItem.SetItems(alist, branch = b)
+            
+            items = conditionItem.GetItems()
+            for b in items.keys():
+                for action in items[b]:
+                    action.SetBlock(conditionItem)
+        
+    def AddItem(self, newItem):
+        """!Add item to the list"""
+        iId = newItem.GetId()
+        
+        i  = 0
+        for item in self.items:
+            if item.GetId() > iId:
+                self.items.insert(i, newItem)
+                return
+            i += 1
+        
+        self.items.append(newItem)
+        
+    def IsValid(self):
+        """Return True if model is valid"""
+        if self.Validate():
+            return False
+        
+        return True
+    
+    def Validate(self):
+        """!Validate model, return None if model is valid otherwise
+        error string"""
+        errList = list()
+
+        variables = self.GetVariables().keys()
+        pattern = re.compile(r'(.*)(%.+\s?)(.*)')
+        for action in self.GetItems(objType = ModelAction):
+            cmd = action.GetLog(string = False)
+            
+            task = menuform.GUI(show = None).ParseCommand(cmd = cmd)
+            errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error())
+            
+            # check also variables
+            for opt in cmd[1:]:
+                if '=' not in opt:
+                    continue
+                key, value = opt.split('=', 1)
+                sval = pattern.search(value)
+                if sval:
+                    var = sval.group(2).strip()[1:] # ignore '%'
+                    if var not in variables:
+                        report = True
+                        for item in filter(lambda x: isinstance(x, ModelLoop), action.GetBlock()):
+                            if var in item.GetText():
+                                report = False
+                                break
+                        if report:
+                            errList.append(_("%s: undefined variable '%s'") % (cmd[0], var))
+            ### TODO: check variables in file only optionally
+            ### errList += self._substituteFile(action, checkOnly = True)
+        
+        return errList
+
+    def _substituteFile(self, item, params = None, checkOnly = False):
+        """!Subsitute variables in command file inputs
+
+        @param checkOnly tuble - True to check variable, don't touch files
+        
+        @return list of undefined variables
+        """
+        errList = list()
+        
+        self.fileInput = dict()
+        
+        # collect ascii inputs
+        for p in item.GetParams()['params']:
+            if p.get('element', '') == 'file' and \
+                    p.get('prompt', '') == 'input' and \
+                    p.get('age', '') == 'old':
+                filename = p.get('value', p.get('default', ''))
+                if filename and \
+                        mimetypes.guess_type(filename)[0] == 'text/plain':
+                    self.fileInput[filename] = None
+        
+        for finput in self.fileInput:
+            # read lines
+            fd = open(finput, "r")
+            try:
+                data = self.fileInput[finput] = fd.read()
+            finally:
+                fd.close()
+            
+            # substitute variables
+            write = False
+            variables = self.GetVariables()
+            for variable in variables:
+                pattern = re.compile('%' + variable)
+                value = ''
+                if params and 'variables' in params:
+                    for p in params['variables']['params']:
+                        if variable == p.get('name', ''):
+                            if p.get('type', 'string') == 'string':
+                                value = p.get('value', '')
+                            else:
+                                value = str(p.get('value', ''))
+                            break
+                
+                if not value:
+                    value = variables[variable].get('value', '')
+                
+                data = pattern.sub(value, data)
+                if not checkOnly:
+                    write = True
+            
+            pattern = re.compile(r'(.*)(%.+\s?)(.*)')
+            sval = pattern.search(data)
+            if sval:
+                var = sval.group(2).strip()[1:] # ignore '%'
+                cmd = item.GetLog(string = False)[0]
+                errList.append(_("%s: undefined variable '%s'") % (cmd, var))
+            
+            if not checkOnly:
+                if write:
+                    fd = open(finput, "w")
+                    try:
+                        fd.write(data)
+                    finally:
+                        fd.close()
+                else:
+                    self.fileInput[finput] = None
+        
+        return errList
+    
+    def OnPrepare(self, item, params):
+        self._substituteFile(item, params, checkOnly = False)
+
+    def RunAction(self, item, params, log, onDone, onPrepare = None, statusbar = None):
+        """!Run given action
+
+        @param item action item
+        @param params parameters dict
+        @param log logging window
+        @param onDone on-done method
+        @param onPrepare on-prepare method
+        @param statusbar wx.StatusBar instance or None
+        """
+        name = item.GetName()
+        if name in params:
+            paramsOrig = item.GetParams(dcopy = True)
+            item.MergeParams(params[name])
+        
+        if statusbar:
+            statusbar.SetStatusText(_('Running model...'), 0)
+            
+        data = { 'item' : item,
+                 'params' : copy.deepcopy(params) }
+        log.RunCmd(command = item.GetLog(string = False, substitute = params),
+                   onDone = onDone, onPrepare = self.OnPrepare, userData = data)
+        
+        if name in params:
+            item.SetParams(paramsOrig)
+
+    def Run(self, log, onDone, parent = None):
+        """!Run model
+
+        @param log logging window (see goutput.GMConsole)
+        @param onDone on-done method
+        @param parent window for messages or None
+        """
+        if self.GetNumItems() < 1:
+            GMessage(parent = parent,
+                     message = _('Model is empty. Nothing to run.'))
+            return
+        
+        statusbar = None
+        if isinstance(parent, wx.Frame):
+            statusbar = parent.GetStatusBar()
+        
+        # validation
+        if statusbar:
+            statusbar.SetStatusText(_('Validating model...'), 0)
+        errList = self.Validate()
+        if statusbar:
+            statusbar.SetStatusText('', 0)
+        if errList:
+            dlg = wx.MessageDialog(parent = parent,
+                                   message = _('Model is not valid. Do you want to '
+                                               'run the model anyway?\n\n%s') % '\n'.join(errList),
+                                   caption = _("Run model?"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            if ret != wx.ID_YES:
+                return
+        
+        # parametrization
+        params = self.Parameterize()
+        if params:
+            dlg = ModelParamDialog(parent = parent,
+                                   params = params)
+            dlg.CenterOnParent()
+            
+            ret = dlg.ShowModal()
+            if ret != wx.ID_OK:
+                dlg.Destroy()
+                return
+            
+            err = dlg.GetErrors()
+            if err:
+                GError(parent = parent, message = unicode('\n'.join(err)))
+                return
+        
+            err = list()
+            for key, item in params.iteritems():
+                for p in item['params']:
+                    if p.get('value', '') == '':
+                        err.append((key, p.get('name', ''), p.get('description', '')))
+            if err:
+                GError(parent = parent,
+                       message = _("Variables below not defined:") + \
+                           "\n\n" + unicode('\n'.join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err))))
+                return
+        
+        log.cmdThread.SetId(-1)
+        for item in self.GetItems():
+            if not item.IsEnabled():
+                continue
+            if isinstance(item, ModelAction):
+                if item.GetBlockId():
+                    continue
+                self.RunAction(item, params, log, onDone)
+            elif isinstance(item, ModelLoop):
+                cond = item.GetText()
+                # substitute variables in condition
+                variables = self.GetVariables()
+                for variable in variables:
+                    pattern = re.compile('%' + variable)
+                    if pattern.search(cond):
+                        value = ''
+                        if params and 'variables' in params:
+                            for p in params['variables']['params']:
+                                if variable == p.get('name', ''):
+                                    value = p.get('value', '')
+                                    break
+                        
+                        if not value:
+                            value = variables[variable].get('value', '')
+                        
+                        if not value:
+                            continue
+                        
+                        vtype = variables[variable].get('type', 'string')
+                        if vtype == 'string':
+                            value = '"' + value + '"'
+                        cond = pattern.sub(value, cond)
+                
+                # split condition
+                condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
+                pattern = re.compile('%' + condVar)
+                ### for vars()[condVar] in eval(condText): ?
+                if condText[0] == '`' and condText[-1] == '`':
+                    # run command
+                    cmd, dcmd = utils.CmdToTuple(condText[1:-1].split(' '))
+                    ret = RunCommand(cmd,
+                                     read = True,
+                                     **dcmd)
+                    if ret:
+                        vlist = ret.splitlines()
+                else:
+                    vlist = eval(condText)
+                
+                if 'variables' not in params:
+                    params['variables'] = { 'params' : [] }
+                varDict = { 'name' : condVar, 'value' : '' }
+                params['variables']['params'].append(varDict)
+                                
+                for var in vlist:
+                    for action in item.GetItems():
+                        if not isinstance(action, ModelAction) or \
+                                not action.IsEnabled():
+                            continue
+                        
+                        varDict['value'] = var
+                                                
+                        self.RunAction(item = action, params = params,
+                                       log = log, onDone = onDone)
+                params['variables']['params'].remove(varDict)
+                
+        # discard values
+        if params:
+            for item in params.itervalues():
+                for p in item['params']:
+                    p['value'] = ''
+        
+        if params:
+            dlg.Destroy()
+        
+    def DeleteIntermediateData(self, log):
+        """!Detele intermediate data"""
+        rast, vect, rast3d, msg = self.GetIntermediateData()
+        
+        if rast:
+            log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
+        if rast3d:
+            log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
+        if vect:
+            log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
+        
+    def GetIntermediateData(self):
+        """!Get info about intermediate data"""
+        rast = list()
+        rast3d = list()
+        vect = list()
+        for data in self.GetData():
+            if not data.IsIntermediate():
+                continue
+            name = data.GetValue()
+            prompt = data.GetPrompt()
+            if prompt == 'raster':
+                rast.append(name)
+            elif prompt == 'vector':
+                vect.append(name)
+            elif prompt == 'rast3d':
+                rast3d.append(name)
+        
+        msg = ''
+        if rast:
+            msg += '\n\n%s: ' % _('Raster maps')
+            msg += ', '.join(rast)
+        if rast3d:
+            msg += '\n\n%s: ' % _('3D raster maps')
+            msg += ', '.join(rast3d)
+        if vect:
+            msg += '\n\n%s: ' % _('Vector maps')
+            msg += ', '.join(vect)
+        
+        return rast, vect, rast3d, msg
+
+    def Update(self):
+        """!Update model"""
+        for item in self.items:
+            item.Update()
+        
+    def IsParameterized(self):
+        """!Return True if model is parameterized"""
+        if self.Parameterize():
+            return True
+        
+        return False
+    
+    def Parameterize(self):
+        """!Return parameterized options"""
+        result = dict()
+        idx = 0
+        if self.variables:
+            params = list()
+            result["variables"] = { 'flags'  : list(),
+                                    'params' : params,
+                                    'idx'    : idx }
+            for name, values in self.variables.iteritems():
+                gtype = values.get('type', 'string')
+                if gtype in ('raster', 'vector', 'mapset', 'file'):
+                    gisprompt = True
+                    prompt = gtype
+                    if gtype == 'raster':
+                        element = 'cell'
+                    else:
+                        element = gtype
+                    ptype = 'string'
+                else:
+                    gisprompt = False
+                    prompt = None
+                    element = None
+                    ptype = gtype
+                params.append({ 'gisprompt' : gisprompt,
+                                'multiple'  : False,
+                                'description' : values.get('description', ''),
+                                'guidependency' : '',
+                                'default' : '',
+                                'age' : None,
+                                'required' : True,
+                                'value' : values.get('value', ''),
+                                'label' : '',
+                                'guisection' : '',
+                                'key_desc' : '',
+                                'values' : list(),
+                                'parameterized' : False,
+                                'values_desc' : list(),
+                                'prompt' : prompt,
+                                'element' : element,
+                                'type' : ptype,
+                                'name' : name })
+            
+            idx += 1
+        
+        for action in self.GetItems(objType = ModelAction):
+            if not action.IsEnabled():
+                continue
+            name   = action.GetName()
+            params = action.GetParams()
+            for f in params['flags']:
+                if f.get('parameterized', False):
+                    if name not in result:
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['flags'].append(f)
+            for p in params['params']:
+                if p.get('parameterized', False):
+                    if name not in result:
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['params'].append(p)
+            if name in result:
+                idx += 1
+        
+        self.variablesParams = result # record parameters
+        
+        return result

+ 695 - 0
gui/wxpython/gmodeler/model_file.py

@@ -0,0 +1,695 @@
+"""!
+@package gmodeler.model_file
+
+@brief wxGUI Graphical Modeler - model definition file
+
+Classes:
+ - ProcessModelFile
+ - WriteModelFile
+ - WritePythonFile
+
+(C) 2010-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import time
+import re
+
+from gui_core.forms import GUI
+from core.gcmd      import GWarning, EncodeString
+
+class ProcessModelFile:
+    """!Process GRASS model file (gxm)"""
+    def __init__(self, tree):
+        """!A ElementTree handler for the GXM XML file, as defined in
+        grass-gxm.dtd.
+        """
+        self.tree = tree
+        self.root = self.tree.getroot()
+        
+        # list of actions, data
+        self.properties = dict()
+        self.variables  = dict() 
+        self.actions = list()
+        self.data    = list()
+        self.loops   = list()
+        self.conditions = list()
+        
+        self._processWindow()
+        self._processProperties()
+        self._processVariables()
+        self._processItems()
+        self._processData()
+        
+    def _filterValue(self, value):
+        """!Filter value
+        
+        @param value
+        """
+        value = value.replace('&lt;', '<')
+        value = value.replace('&gt;', '>')
+        
+        return value
+        
+    def _getNodeText(self, node, tag, default = ''):
+        """!Get node text"""
+        p = node.find(tag)
+        if p is not None:
+            if p.text:
+                return utils.normalize_whitespace(p.text)
+            else:
+                return ''
+        
+        return default
+    
+    def _processWindow(self):
+        """!Process window properties"""
+        node = self.root.find('window')
+        if node is None:
+            self.pos = self.size = None
+            return
+        
+        self.pos, self.size = self._getDim(node)
+        
+    def _processProperties(self):
+        """!Process model properties"""
+        node = self.root.find('properties')
+        if node is None:
+            return
+        for key in ('name', 'description', 'author'):
+            self._processProperty(node, key)
+        
+        for f in node.findall('flag'):
+            name = f.get('name', '')
+            if name == 'overwrite':
+                self.properties['overwrite'] = True
+        
+    def _processProperty(self, pnode, name):
+        """!Process given property"""
+        node = pnode.find(name)
+        if node is not None:
+            self.properties[name] = node.text
+        else:
+            self.properties[name] = ''
+
+    def _processVariables(self):
+        """!Process model variables"""
+        vnode = self.root.find('variables')
+        if vnode is None:
+            return
+        for node in vnode.findall('variable'):
+            name = node.get('name', '')
+            if not name:
+                continue # should not happen
+            self.variables[name] = { 'type' : node.get('type', 'string') }
+            for key in ('description', 'value'):
+                self._processVariable(node, name, key)
+        
+    def _processVariable(self, pnode, name, key):
+        """!Process given variable"""
+        node = pnode.find(key)
+        if node is not None:
+            if node.text:
+                self.variables[name][key] = node.text
+
+    def _processItems(self):
+        """!Process model items (actions, loops, conditions)"""
+        self._processActions()
+        self._processLoops()
+        self._processConditions()
+        
+    def _processActions(self):
+        """!Process model file"""
+        for action in self.root.findall('action'):
+            pos, size = self._getDim(action)
+            disabled  = False
+            
+            task = action.find('task')
+            if task is not None:
+                if task.find('disabled') is not None:
+                    disabled = True
+                task = self._processTask(task)
+            else:
+                task = None
+            
+            aId = int(action.get('id', -1))
+            
+            self.actions.append({ 'pos'      : pos,
+                                  'size'     : size,
+                                  'task'     : task,
+                                  'id'       : aId,
+                                  'disabled' : disabled })
+            
+    def _getDim(self, node):
+        """!Get position and size of shape"""
+        pos = size = None
+        posAttr = node.get('pos', None)
+        if posAttr:
+            posVal = map(int, posAttr.split(','))
+            try:
+                pos = (posVal[0], posVal[1])
+            except:
+                pos = None
+        
+        sizeAttr = node.get('size', None)
+        if sizeAttr:
+            sizeVal = map(int, sizeAttr.split(','))
+            try:
+                size = (sizeVal[0], sizeVal[1])
+            except:
+                size = None
+        
+        return pos, size        
+    
+    def _processData(self):
+        """!Process model file"""
+        for data in self.root.findall('data'):
+            pos, size = self._getDim(data)
+            param = data.find('data-parameter')
+            prompt = value = None
+            if param is not None:
+                prompt = param.get('prompt', None)
+                value = self._filterValue(self._getNodeText(param, 'value'))
+            
+            if data.find('intermediate') is None:
+                intermediate = False
+            else:
+                intermediate = True
+            
+            rels = list()
+            for rel in data.findall('relation'):
+                defrel = { 'id'  : int(rel.get('id', -1)),
+                           'dir' : rel.get('dir', 'to'),
+                           'name' : rel.get('name', '') }
+                points = list()
+                for point in rel.findall('point'):
+                    x = self._filterValue(self._getNodeText(point, 'x'))
+                    y = self._filterValue(self._getNodeText(point, 'y'))
+                    points.append((float(x), float(y)))
+                defrel['points'] = points
+                rels.append(defrel)
+            
+            self.data.append({ 'pos' : pos,
+                               'size': size,
+                               'prompt' : prompt,
+                               'value' : value,
+                               'intermediate' : intermediate,
+                               'rels' : rels })
+        
+    def _processTask(self, node):
+        """!Process task
+
+        @return grassTask instance
+        @return None on error
+        """
+        cmd = list()
+        parameterized = list()
+        
+        name = node.get('name', None)
+        if not name:
+            return None
+        
+        cmd.append(name)
+        
+        # flags
+        for f in node.findall('flag'):
+            flag = f.get('name', '')
+            if f.get('parameterized', '0') == '1':
+                parameterized.append(('flag', flag))
+                if f.get('value', '1') == '0':
+                    continue
+            if len(flag) > 1:
+                cmd.append('--' + flag)
+            else:
+                cmd.append('-' + flag)
+        # parameters
+        for p in node.findall('parameter'):
+            name = p.get('name', '')
+            if p.find('parameterized') is not None:
+                parameterized.append(('param', name))
+            cmd.append('%s=%s' % (name,
+                                  self._filterValue(self._getNodeText(p, 'value'))))
+        
+        task, err = GUI(show = None, checkError = True).ParseCommand(cmd = cmd)
+        if err:
+            GWarning(os.linesep.join(err))
+        
+        for opt, name in parameterized:
+            if opt == 'flag':
+                task.set_flag(name, True, element = 'parameterized')
+            else:
+                task.set_param(name, True, element = 'parameterized')
+        
+        return task
+
+    def _processLoops(self):
+        """!Process model loops"""
+        for node in self.root.findall('loop'):
+            pos, size = self._getDim(node)
+            text = self._filterValue(self._getNodeText(node, 'condition')).strip()
+            aid = list()
+            for anode in node.findall('item'):
+                try:
+                    aid.append(int(anode.text))
+                except ValueError:
+                    pass
+            
+            self.loops.append({ 'pos'     : pos,
+                                'size'    : size,
+                                'text'    : text,
+                                'id'      : int(node.get('id', -1)),
+                                'items'   : aid })
+        
+    def _processConditions(self):
+        """!Process model conditions"""
+        for node in self.root.findall('if-else'):
+            pos, size = self._getDim(node)
+            text = self._filterValue(self._getNodeText(node, 'condition')).strip()
+            aid = { 'if'   : list(),
+                    'else' : list() }
+            for b in aid.keys():
+                bnode = node.find(b)
+                if bnode is None:
+                    continue
+                for anode in bnode.findall('item'):
+                    try:
+                        aid[b].append(int(anode.text))
+                    except ValueError:
+                        pass
+            
+            self.conditions.append({ 'pos'     : pos,
+                                     'size'    : size,
+                                     'text'    : text,
+                                     'id'      : int(node.get('id', -1)),
+                                     'items'   : aid })
+        
+class WriteModelFile:
+    """!Generic class for writing model file"""
+    def __init__(self, fd, model):
+        self.fd         = fd
+        self.model      = model
+        self.properties = model.GetProperties()
+        self.variables  = model.GetVariables()
+        self.items      = model.GetItems()
+        
+        self.indent = 0
+        
+        self._header()
+        
+        self._window()
+        self._properties()
+        self._variables()
+        self._items()
+        
+        dataList = list()
+        for action in model.GetItems(objType = ModelAction):
+            for rel in action.GetRelations():
+                dataItem = rel.GetData()
+                if dataItem not in dataList:
+                    dataList.append(dataItem)
+        self._data(dataList)
+        
+        self._footer()
+
+    def _filterValue(self, value):
+        """!Make value XML-valid"""
+        value = value.replace('<', '&lt;')
+        value = value.replace('>', '&gt;')
+        
+        return value
+        
+    def _header(self):
+        """!Write header"""
+        self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+        self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n')
+        self.fd.write('%s<gxm>\n' % (' ' * self.indent))
+        self.indent += 4
+                
+    def _footer(self):
+        """!Write footer"""
+        self.indent -= 4
+        self.fd.write('%s</gxm>\n' % (' ' * self.indent))
+
+    def _window(self):
+        """!Write window properties"""
+        canvas = self.model.GetCanvas()
+        if canvas is None:
+            return
+        win  = canvas.parent
+        pos  = win.GetPosition()
+        size = win.GetSize()
+        self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \
+                          (' ' * self.indent, pos[0], pos[1], size[0], size[1]))
+        
+    def _properties(self):
+        """!Write model properties"""
+        self.fd.write('%s<properties>\n' % (' ' * self.indent))
+        self.indent += 4
+        if self.properties['name']:
+            self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name']))
+        if self.properties['description']:
+            self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent,
+                                                                 EncodeString(self.properties['description'])))
+        if self.properties['author']:
+            self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent,
+                                                       EncodeString(self.properties['author'])))
+        
+        if 'overwrite' in self.properties and \
+                self.properties['overwrite']:
+            self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</properties>\n' % (' ' * self.indent))
+
+    def _variables(self):
+        """!Write model variables"""
+        if not self.variables:
+            return
+        self.fd.write('%s<variables>\n' % (' ' * self.indent))
+        self.indent += 4
+        for name, values in self.variables.iteritems():
+            self.fd.write('%s<variable name="%s" type="%s">\n' % \
+                              (' ' * self.indent, name, values['type']))
+            self.indent += 4
+            if 'value' in values:
+                self.fd.write('%s<value>%s</value>\n' % \
+                                  (' ' * self.indent, values['value']))
+            if 'description' in values:
+                self.fd.write('%s<description>%s</description>\n' % \
+                                  (' ' * self.indent, values['description']))
+            self.indent -= 4
+            self.fd.write('%s</variable>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</variables>\n' % (' ' * self.indent))
+        
+    def _items(self):
+        """!Write actions/loops/conditions"""
+        for item in self.items:
+            if isinstance(item, ModelAction):
+                self._action(item)
+            elif isinstance(item, ModelLoop):
+                self._loop(item)
+            elif isinstance(item, ModelCondition):
+                self._condition(item)
+        
+    def _action(self, action):
+        """!Write actions"""
+        self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(),
+                           action.GetWidth(), action.GetHeight()))
+        self.indent += 4
+        self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0]))
+        self.indent += 4
+        if not action.IsEnabled():
+            self.fd.write('%s<disabled />\n' % (' ' * self.indent))
+        for key, val in action.GetParams().iteritems():
+            if key == 'flags':
+                for f in val:
+                    if f.get('value', False) or f.get('parameterized', False):
+                        if f.get('parameterized', False):
+                            if f.get('value', False) == False:
+                                self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' %
+                                              (' ' * self.indent, f.get('name', '')))
+                            else:
+                                self.fd.write('%s<flag name="%s" parameterized="1" />\n' %
+                                              (' ' * self.indent, f.get('name', '')))
+                        else:
+                            self.fd.write('%s<flag name="%s" />\n' %
+                                          (' ' * self.indent, f.get('name', '')))
+            else: # parameter
+                for p in val:
+                    if not p.get('value', '') and not p.get('parameterized', False):
+                        continue
+                    self.fd.write('%s<parameter name="%s">\n' %
+                                  (' ' * self.indent, p.get('name', '')))
+                    self.indent += 4
+                    if p.get('parameterized', False):
+                        self.fd.write('%s<parameterized />\n' % (' ' * self.indent))
+                    self.fd.write('%s<value>%s</value>\n' %
+                                  (' ' * self.indent, self._filterValue(p.get('value', ''))))
+                    self.indent -= 4
+                    self.fd.write('%s</parameter>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</task>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</action>\n' % (' ' * self.indent))
+                
+    def _data(self, dataList):
+        """!Write data"""
+        for data in dataList:
+            self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \
+                              (' ' * self.indent, data.GetX(), data.GetY(),
+                               data.GetWidth(), data.GetHeight()))
+            self.indent += 4
+            self.fd.write('%s<data-parameter prompt="%s">\n' % \
+                              (' ' * self.indent, data.GetPrompt()))
+            self.indent += 4
+            self.fd.write('%s<value>%s</value>\n' %
+                          (' ' * self.indent, self._filterValue(data.GetValue())))
+            self.indent -= 4
+            self.fd.write('%s</data-parameter>\n' % (' ' * self.indent))
+            
+            if data.IsIntermediate():
+                self.fd.write('%s<intermediate />\n' % (' ' * self.indent))
+
+            # relations
+            for ft in ('from', 'to'):
+                for rel in data.GetRelations(ft):
+                    if ft == 'from':
+                        aid = rel.GetTo().GetId()
+                    else:
+                        aid  = rel.GetFrom().GetId()
+                    self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \
+                                      (' ' * self.indent, ft, aid, rel.GetName()))
+                    self.indent += 4
+                    for point in rel.GetLineControlPoints()[1:-1]:
+                        self.fd.write('%s<point>\n' % (' ' * self.indent))
+                        self.indent += 4
+                        x, y = point.Get()
+                        self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x)))
+                        self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y)))
+                        self.indent -= 4
+                        self.fd.write('%s</point>\n' % (' ' * self.indent))
+                    self.indent -= 4
+                    self.fd.write('%s</relation>\n' % (' ' * self.indent))
+                
+            self.indent -= 4
+            self.fd.write('%s</data>\n' % (' ' * self.indent))
+
+    def _loop(self, loop):
+        """!Write loops"""
+        self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(),
+                           loop.GetWidth(), loop.GetHeight()))
+        text = loop.GetText()
+        self.indent += 4
+        if text:
+            self.fd.write('%s<condition>%s</condition>\n' %
+                          (' ' * self.indent, self._filterValue(text)))
+        for item in loop.GetItems():
+            self.fd.write('%s<item>%d</item>\n' %
+                          (' ' * self.indent, item.GetId()))
+        self.indent -= 4
+        self.fd.write('%s</loop>\n' % (' ' * self.indent))
+
+    def _condition(self, condition):
+        """!Write conditions"""
+        bbox = condition.GetBoundingBoxMin()
+        self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(),
+                           bbox[0], bbox[1]))
+        text = condition.GetText()
+        self.indent += 4
+        if text:
+            self.fd.write('%s<condition>%s</condition>\n' %
+                          (' ' * self.indent, self._filterValue(text)))
+        items = condition.GetItems()
+        for b in items.keys():
+            if len(items[b]) < 1:
+                continue
+            self.fd.write('%s<%s>\n' % (' ' * self.indent, b))
+            self.indent += 4
+            for item in items[b]:
+                self.fd.write('%s<item>%d</item>\n' %
+                              (' ' * self.indent, item.GetId()))
+            self.indent -= 4
+            self.fd.write('%s</%s>\n' % (' ' * self.indent, b))
+        
+        self.indent -= 4
+        self.fd.write('%s</if-else>\n' % (' ' * self.indent))
+        
+class WritePythonFile:
+    def __init__(self, fd, model):
+        """!Class for exporting model to Python script
+
+        @param fd file desciptor
+        """
+        self.fd     = fd
+        self.model  = model
+        self.indent = 4
+
+        self._writePython()
+        
+    def _writePython(self):
+        """!Write model to file"""
+        properties = self.model.GetProperties()
+        
+        self.fd.write(
+r"""#!/usr/bin/env python
+#
+############################################################################
+#
+# MODULE:       %s
+#
+# AUTHOR(S):	%s
+#               
+# PURPOSE:      %s
+#
+# DATE:         %s
+#
+#############################################################################
+""" % (properties['name'],
+       properties['author'],
+       properties['description'],
+       time.asctime()))
+        
+        self.fd.write(
+r"""
+import sys
+import os
+import atexit
+
+import grass.script as grass
+""")
+        
+        # cleanup()
+        rast, vect, rast3d, msg = self.model.GetIntermediateData()
+        self.fd.write(
+r"""
+def cleanup():
+""")
+        if rast:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      rast=%s)
+""" % ','.join(map(lambda x: "'" + x + "'", rast)))
+        if vect:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      vect = %s)
+""" % ','.join(map(lambda x: "'" + x + "'", vect)))
+        if rast3d:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      rast3d = %s)
+""" % ','.join(map(lambda x: "'" + x + "'", rast3d)))
+        if not rast and not vect and not rast3d:
+            self.fd.write('    pass\n')
+        
+        self.fd.write("\ndef main():\n")
+        for item in self.model.GetItems():
+            self._writePythonItem(item)
+        
+        self.fd.write("\n    return 0\n")
+        
+        self.fd.write(
+r"""
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    atexit.register(cleanup)
+    sys.exit(main())
+""")
+        
+    def _writePythonItem(self, item, ignoreBlock = True, variables = []):
+        """!Write model object to Python file"""
+        if isinstance(item, ModelAction):
+            if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions
+                return
+            self._writePythonAction(item, variables = variables)
+        elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
+            # substitute condition
+            variables = self.model.GetVariables()
+            cond = item.GetText()
+            for variable in variables:
+                pattern = re.compile('%' + variable)
+                if pattern.search(cond):
+                    value = variables[variable].get('value', '')
+                    if variables[variable].get('type', 'string') == 'string':
+                        value = '"' + value + '"'
+                    cond = pattern.sub(value, cond)
+            if isinstance(item, ModelLoop):
+                condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
+                cond = "%sfor %s in " % (' ' * self.indent, condVar)
+                if condText[0] == '`' and condText[-1] == '`':
+                    task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1]))
+                    cond += "grass.read_command("
+                    cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()"
+                else:
+                    cond += condText
+                self.fd.write('%s:\n' % cond)
+                self.indent += 4
+                for action in item.GetItems():
+                    self._writePythonItem(action, ignoreBlock = False, variables = [condVar])
+                self.indent -= 4
+            else: # ModelCondition
+                self.fd.write('%sif %s:\n' % (' ' * self.indent, cond))
+                self.indent += 4
+                condItems = item.GetItems()
+                for action in condItems['if']:
+                    self._writePythonItem(action, ignoreBlock = False)
+                if condItems['else']:
+                    self.indent -= 4
+                    self.fd.write('%selse:\n' % (' ' * self.indent))
+                    self.indent += 4
+                    for action in condItems['else']:
+                        self._writePythonItem(action, ignoreBlock = False)
+                self.indent += 4
+        
+    def _writePythonAction(self, item, variables = []):
+        """!Write model action to Python file"""
+        task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False))
+        strcmd = "%sgrass.run_command(" % (' ' * self.indent)
+        self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n')
+        
+    def _getPythonActionCmd(self, task, cmdIndent, variables = []):
+        opts = task.get_options()
+        
+        ret = ''
+        flags = ''
+        params = list()
+        
+        for f in opts['flags']:
+            if f.get('value', False):
+                name = f.get('name', '')
+                if len(name) > 1:
+                    params.append('%s = True' % name)
+                else:
+                    flags += name
+            
+        for p in opts['params']:
+            name = p.get('name', None)
+            value = p.get('value', None)
+            if name and value:
+                ptype = p.get('type', 'string')
+                if value[0] == '%':
+                    params.append("%s = %s" % (name, value[1:]))
+                elif ptype == 'string':
+                    params.append('%s = "%s"' % (name, value))
+                else:
+                    params.append("%s = %s" % (name, value))
+        
+        ret += '"%s"' % task.get_name()
+        if flags:
+            ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags)
+        if len(params) > 0:
+            ret += ",\n"
+            for opt in params[:-1]:
+                ret += "%s%s,\n" % (' ' * cmdIndent, opt)
+            ret += "%s%s)" % (' ' * cmdIndent, params[-1])
+        else:
+            ret += ")"
+        
+        return ret

+ 527 - 0
gui/wxpython/gmodeler/preferences.py

@@ -0,0 +1,527 @@
+"""!
+@package gmodeler.preferences
+
+@brief wxGUI Graphical Modeler - preferences
+
+Classes:
+ - PreferencesDialog
+ - PropertiesDialog
+
+(C) 2010-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+import wx.lib.colourselect    as csel
+
+from core                 import globalvar
+from gui_core.preferences import PreferencesBaseDialog
+from core.settings        import UserSettings
+
+class PreferencesDialog(PreferencesBaseDialog):
+    """!User preferences dialog"""
+    def __init__(self, parent, settings = UserSettings,
+                 title = _("Modeler settings")):
+        
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        
+        # create notebook pages
+        self._createGeneralPage(self.notebook)
+        self._createActionPage(self.notebook)
+        self._createDataPage(self.notebook)
+        self._createLoopPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+
+    def _createGeneralPage(self, notebook):
+        """!Create notebook page for action settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("General"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Item properties"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Disabled:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='disabled', subkey='color'),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:disabled:color'] = rColor.GetId()
+        
+        gridSizer.Add(item = rCoxolor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createActionPage(self, notebook):
+        """!Create notebook page for action settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Action"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Color"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Valid:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'valid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:action:color:valid'] = vColor.GetId()
+        
+        gridSizer.Add(item = vColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Invalid:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        iColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'invalid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        iColor.SetName('GetColour')
+        self.winId['modeler:action:color:invalid'] = iColor.GetId()
+        
+        gridSizer.Add(item = iColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Running:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'running')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:action:color:running'] = rColor.GetId()
+        
+        gridSizer.Add(item = rColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Shape size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                            min = 0, max = 500,
+                            initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:action:size:width'] = width.GetId()
+        
+        gridSizer.Add(item = width,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Height:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        
+        height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                             min = 0, max = 500,
+                             initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:action:size:height'] = height.GetId()
+        
+        gridSizer.Add(item = height,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+                
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createDataPage(self, notebook):
+        """!Create notebook page for data settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Data"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Type"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Raster:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:data:color:raster'] = rColor.GetId()
+        
+        gridSizer.Add(item = rColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("3D raster:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        r3Color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                    colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster3d')),
+                                    size = globalvar.DIALOG_COLOR_SIZE)
+        r3Color.SetName('GetColour')
+        self.winId['modeler:data:color:raster3d'] = r3Color.GetId()
+        
+        gridSizer.Add(item = r3Color,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Vector:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'vector')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:data:color:vector'] = vColor.GetId()
+        
+        gridSizer.Add(item = vColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Shape size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                            min = 0, max = 500,
+                            initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:data:size:width'] = width.GetId()
+        
+        gridSizer.Add(item = width,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Height:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        
+        height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                             min = 0, max = 500,
+                             initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:data:size:height'] = height.GetId()
+        
+        gridSizer.Add(item = height,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+        
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createLoopPage(self, notebook):
+        """!Create notebook page for loop settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Loop"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Color"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Valid:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='loop', subkey=('color', 'valid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:loop:color:valid'] = vColor.GetId()
+        
+        gridSizer.Add(item = vColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Shape size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                            min = 0, max = 500,
+                            initial = self.settings.Get(group='modeler', key='loop', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:loop:size:width'] = width.GetId()
+        
+        gridSizer.Add(item = width,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Height:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        
+        height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                             min = 0, max = 500,
+                             initial = self.settings.Get(group='modeler', key='loop', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:loop:size:height'] = height.GetId()
+        
+        gridSizer.Add(item = height,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+                
+        panel.SetSizer(border)
+        
+        return panel
+
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        PreferencesBaseDialog.OnApply(self, event)
+        
+        self.parent.GetModel().Update()
+        self.parent.GetCanvas().Refresh()
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        PreferencesBaseDialog.OnSave(self, event)
+        
+        self.parent.GetModel().Update()
+        self.parent.GetCanvas().Refresh()
+
+class PropertiesDialog(wx.Dialog):
+    """!Model properties dialog
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _('Model properties'),
+                 size = (350, 400),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        wx.Dialog.__init__(self, parent, id, title, size = size,
+                           style = style)
+        
+        self.metaBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label=" %s " % _("Metadata"))
+        self.cmdBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                   label=" %s " % _("Commands"))
+        
+        self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                size = (300, 25))
+        self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                style = wx.TE_MULTILINE,
+                                size = (300, 50))
+        self.author = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                size = (300, 25))
+        
+        # commands
+        self.overwrite = wx.CheckBox(parent = self, id=wx.ID_ANY,
+                                     label=_("Allow output files to overwrite existing files"))
+        self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
+        
+        # buttons
+        self.btnOk     = wx.Button(self, wx.ID_OK)
+        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+        self.btnOk.SetDefault()
+        
+        self.btnOk.SetToolTipString(_("Apply properties"))
+        self.btnOk.SetDefault()
+        self.btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+        
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        
+        self._layout()
+
+    def _layout(self):
+        metaSizer = wx.StaticBoxSizer(self.metaBox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        gridSizer.AddGrowableRow(1)
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Name:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 0))
+        gridSizer.Add(item = self.name,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (0, 1))
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Description:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 0))
+        gridSizer.Add(item = self.desc,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (1, 1))
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Author(s):")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 0))
+        gridSizer.Add(item = self.author,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (2, 1))
+        metaSizer.Add(item = gridSizer)
+        
+        cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
+        cmdSizer.Add(item = self.overwrite,
+                     flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        btnStdSizer = wx.StdDialogButtonSizer()
+        btnStdSizer.AddButton(self.btnCancel)
+        btnStdSizer.AddButton(self.btnOk)
+        btnStdSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item=metaSizer, proportion=1,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(item=cmdSizer, proportion=0,
+                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
+        mainSizer.Add(item=btnStdSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+    def OnCloseWindow(self, event):
+        self.Hide()
+        
+    def GetValues(self):
+        """!Get values"""
+        return { 'name'        : self.name.GetValue(),
+                 'description' : self.desc.GetValue(),
+                 'author'      : self.author.GetValue(),
+                 'overwrite'   : self.overwrite.IsChecked() }
+    
+    def Init(self, prop):
+        """!Initialize dialog"""
+        self.name.SetValue(prop['name'])
+        self.desc.SetValue(prop['description'])
+        self.author.SetValue(prop['author'])
+        if 'overwrite' in prop:
+            self.overwrite.SetValue(prop['overwrite'])

+ 106 - 106
gui/wxpython/gui_modules/gdialogs.py

@@ -1,5 +1,5 @@
 """!
-@package gdialogs.py
+@package gui_core.gdialogs
 
 @brief Various dialogs used in wxGUI.
 
@@ -45,13 +45,13 @@ import wx.lib.mixins.listctrl as listmix
 from grass.script import core as grass
 from grass.script import task as gtask
 
-import gcmd
-import globalvar
-import gselect
-import menuform
-import utils
-from preferences import globalSettings as UserSettings
-from debug       import Debug
+from core             import globalvar
+from core.gcmd        import GError, RunCommand, GMessage
+from gui_core.gselect import ElementSelect, LocationSelect, MapsetSelect, Select, OgrTypeSelect, GdalSelect
+from core.forms       import GUI
+from core.utils       import GetListOfMapsets, GetLayerNameFromCmd, GetValidLayerName
+from core.settings    import UserSettings
+from core.debug       import Debug
 
 class ElementDialog(wx.Dialog):
     def __init__(self, parent, title, label, id = wx.ID_ANY,
@@ -77,8 +77,8 @@ class ElementDialog(wx.Dialog):
         self.btnOK.Enable(False)
         
         if self.etype:
-            self.typeSelect = gselect.ElementSelect(parent = self.panel,
-                                                    size = globalvar.DIALOG_GSELECT_SIZE)
+            self.typeSelect = ElementSelect(parent = self.panel,
+                                            size = globalvar.DIALOG_GSELECT_SIZE)
             self.typeSelect.Bind(wx.EVT_CHOICE, self.OnType)
         
         self.element = None # must be defined 
@@ -145,12 +145,12 @@ class LocationDialog(ElementDialog):
     def __init__(self, parent, title = _("Select GRASS location and mapset"), id =  wx.ID_ANY):
         ElementDialog.__init__(self, parent, title, label = _("Name of GRASS location:"))
 
-        self.element = gselect.LocationSelect(parent = self.panel, id = wx.ID_ANY,
-                                              size = globalvar.DIALOG_GSELECT_SIZE)
-
-        self.element1 = gselect.MapsetSelect(parent = self.panel, id = wx.ID_ANY,
-                                             size = globalvar.DIALOG_GSELECT_SIZE,
-                                             setItems = False)
+        self.element = LocationSelect(parent = self.panel, id = wx.ID_ANY,
+                                      size = globalvar.DIALOG_GSELECT_SIZE)
+        
+        self.element1 = MapsetSelect(parent = self.panel, id = wx.ID_ANY,
+                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                     setItems = False)
         
         self.PostInit()
         
@@ -178,7 +178,7 @@ class LocationDialog(ElementDialog):
         
         if location:
             dbase = grass.gisenv()['GISDBASE']
-            self.element1.SetItems(utils.GetListOfMapsets(dbase, location, selectable = True))
+            self.element1.SetItems(GetListOfMapsets(dbase, location, selectable = True))
             self.element1.SetSelection(0)
             mapset = self.element1.GetStringSelection()
         
@@ -239,12 +239,12 @@ class NewVectorDialog(ElementDialog):
         """
         ElementDialog.__init__(self, parent, title, label = _("Name for new vector map:"))
         
-        self.element = gselect.Select(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
-                                      type = 'vector', mapsets = [grass.gisenv()['MAPSET'],])
+        self.element = Select(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                              type = 'vector', mapsets = [grass.gisenv()['MAPSET'],])
         
         # determine output format
         if showType:
-            self.ftype = gselect.OgrTypeSelect(parent = self, panel = self.panel)
+            self.ftype = OgrTypeSelect(parent = self, panel = self.panel)
         else:
             self.ftype = None
         
@@ -395,14 +395,14 @@ def CreateNewVector(parent, cmd, title = _('Create new vector map'),
     outmap = dlg.GetName()
     key    = dlg.GetKey()
     if outmap == exceptMap:
-        gcmd.GError(parent = parent,
-                    message = _("Unable to create vector map <%s>.") % outmap)
+        GError(parent = parent,
+               message = _("Unable to create vector map <%s>.") % outmap)
         dlg.Destroy()
         return None
     if dlg.table.IsEnabled() and not key:
-        gcmd.GError(parent = parent,
-                    message = _("Invalid or empty key column.\n"
-                                "Unable to create vector map <%s>.") % outmap)
+        GError(parent = parent,
+               message = _("Invalid or empty key column.\n"
+                           "Unable to create vector map <%s>.") % outmap)
         dlg.Destroy()
         return
         
@@ -418,12 +418,12 @@ def CreateNewVector(parent, cmd, title = _('Create new vector map'),
     if isNative:
         listOfVectors = grass.list_grouped('vect')[grass.gisenv()['MAPSET']]
     else:
-        listOfVectors = gcmd.RunCommand('v.external',
-                                        quiet = True,
-                                        parent = parent,
-                                        read = True,
-                                        flags = 'l',
-                                        dsn = vExternalOut['dsn']).splitlines()
+        listOfVectors = RunCommand('v.external',
+                                   quiet = True,
+                                   parent = parent,
+                                   read = True,
+                                   flags = 'l',
+                                   dsn = vExternalOut['dsn']).splitlines()
     
     overwrite = False
     if not UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled') and \
@@ -443,44 +443,44 @@ def CreateNewVector(parent, cmd, title = _('Create new vector map'),
     if UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'):
         overwrite = True
         
-    ret = gcmd.RunCommand(prog = cmd[0],
-                          parent = parent,
-                          overwrite = overwrite,
-                          **cmd[1])
+    ret = RunCommand(prog = cmd[0],
+                     parent = parent,
+                     overwrite = overwrite,
+                     **cmd[1])
     if ret != 0:
         dlg.Destroy()
         return None
     
     if not isNative:
         # create link for OGR layers
-        gcmd.RunCommand('v.external',
-                        overwrite = overwrite,
-                        parent = parent,
-                        dsn = vExternalOut['dsn'],
-                        layer = outmap)
+        RunCommand('v.external',
+                   overwrite = overwrite,
+                   parent = parent,
+                   dsn = vExternalOut['dsn'],
+                   layer = outmap)
         
     # create attribute table
     if dlg.table.IsEnabled() and dlg.table.IsChecked():
         if isNative:
             sql = 'CREATE TABLE %s (%s INTEGER)' % (outmap, key)
             
-            gcmd.RunCommand('db.connect',
-                            flags = 'c')
+            RunCommand('db.connect',
+                       flags = 'c')
             
             Debug.msg(1, "SQL: %s" % sql)
-            gcmd.RunCommand('db.execute',
-                            quiet = True,
-                            parent = parent,
-                            input = '-',
-                            stdin = sql)
+            RunCommand('db.execute',
+                       quiet = True,
+                       parent = parent,
+                       input = '-',
+                       stdin = sql)
             
-            gcmd.RunCommand('v.db.connect',
-                            quiet = True,
-                            parent = parent,
-                            map = outmap,
-                            table = outmap,
-                            key = key,
-                            layer = '1')
+            RunCommand('v.db.connect',
+                       quiet = True,
+                       parent = parent,
+                       map = outmap,
+                       table = outmap,
+                       key = key,
+                       layer = '1')
         # TODO: how to deal with attribute tables for OGR layers?
             
     # return fully qualified map name
@@ -511,12 +511,12 @@ class SavedRegion(wx.Dialog):
         box.Add(item = label, proportion = 0, flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
         if loadsave == 'load':
             label.SetLabel(_("Load region:"))
-            selection = gselect.Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
-                                       type = 'windows')
+            selection = Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                               type = 'windows')
         elif loadsave == 'save':
             label.SetLabel(_("Save region:"))
-            selection = gselect.Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
-                                       type = 'windows', mapsets  =  [grass.gisenv()['MAPSET']])
+            selection = Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                               type = 'windows', mapsets  =  [grass.gisenv()['MAPSET']])
         
         box.Add(item = selection, proportion = 0, flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
         selection.SetFocus()
@@ -642,7 +642,7 @@ class DecorationDialog(wx.Dialog):
         
         if len(self.parent.MapWindow.overlays[self.ovlId]['cmd']) > 1:
             if name == 'legend':
-                mapName, found = utils.GetLayerNameFromCmd(self.parent.MapWindow.overlays[self.ovlId]['cmd'])
+                mapName, found = GetLayerNameFromCmd(self.parent.MapWindow.overlays[self.ovlId]['cmd'])
                 if found:
                     # enable 'OK' button
                     self.btnOK.Enable()
@@ -678,8 +678,8 @@ class DecorationDialog(wx.Dialog):
         """
         if self.parent.MapWindow.overlays[self.ovlId]['propwin'] is None:
             # build properties dialog
-            menuform.GUI(parent = self.parent).ParseCommand(cmd = self.cmd,
-                                                            completed = (self.GetOptData, self.name, ''))
+            GUI(parent = self.parent).ParseCommand(cmd = self.cmd,
+                                                   completed = (self.GetOptData, self.name, ''))
             
         else:
             if self.parent.MapWindow.overlays[self.ovlId]['propwin'].IsShown():
@@ -976,9 +976,9 @@ class GroupDialog(wx.Dialog):
                                            label = _("Select the group you want to edit or "
                                                      "enter name of new group:")),
                       flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP, border = 10)
-        self.groupSelect = gselect.Select(parent = self, type = 'group',
-                                          mapsets = [grass.gisenv()['MAPSET']],
-                                          size = globalvar.DIALOG_GSELECT_SIZE) # searchpath?
+        self.groupSelect = Select(parent = self, type = 'group',
+                                  mapsets = [grass.gisenv()['MAPSET']],
+                                  size = globalvar.DIALOG_GSELECT_SIZE) # searchpath?
             
         bodySizer.Add(item = self.groupSelect, flag = wx.TOP | wx.EXPAND, border = 5)
         
@@ -1097,27 +1097,27 @@ class GroupDialog(wx.Dialog):
         
         ret = None
         if remove:
-            ret = gcmd.RunCommand('i.group',
-                                  parent = self,
-                                  group = group,
-                                  flags = 'r',
-                                  input = ','.join(remove))
+            ret = RunCommand('i.group',
+                             parent = self,
+                             group = group,
+                             flags = 'r',
+                             input = ','.join(remove))
                         
         if add:
-            ret = gcmd.RunCommand('i.group',
-                                  parent = self,
-                                  group = group,
-                                  input = ','.join(add))
+            ret = RunCommand('i.group',
+                             parent = self,
+                             group = group,
+                             input = ','.join(add))
                             
         return ret
         
     def CreateNewGroup(self, group):
         """!Create new group"""
         layers = self.GetLayers()
-        ret = gcmd.RunCommand('i.group',
-                              parent = self,
-                              group = group,
-                              input = layers)
+        ret = RunCommand('i.group',
+                         parent = self,
+                         group = group,
+                         input = layers)
         return ret
         
                         
@@ -1150,11 +1150,11 @@ class GroupDialog(wx.Dialog):
         
     def GetGroupLayers(self, group):
         """!Get layers in group"""
-        res = gcmd.RunCommand('i.group',
-                              parent = self,
-                              flags = 'g',
-                              group = group,
-                              read = True).strip()
+        res = RunCommand('i.group',
+                         parent = self,
+                         flags = 'g',
+                         group = group,
+                         read = True).strip()
         if res.split('\n')[0]:
             return res.split('\n')
         return []
@@ -1167,8 +1167,8 @@ class GroupDialog(wx.Dialog):
         """!Create or edit group"""
         group = self.currentGroup
         if not group:
-            gcmd.GMessage(parent = self,
-                          message = _("No group selected."))
+            GMessage(parent = self,
+                     message = _("No group selected."))
             return False
         
         groups = self.GetExistGroups()
@@ -1299,7 +1299,7 @@ class MapLayersDialog(wx.Dialog):
                       flag = wx.ALIGN_CENTER_VERTICAL,
                       pos = (1,0))
         
-        self.mapset = gselect.MapsetSelect(parent = self, searchPath = True)
+        self.mapset = MapsetSelect(parent = self, searchPath = True)
         self.mapset.SetStringSelection(grass.gisenv()['MAPSET'])
         bodySizer.Add(item = self.mapset,
                       pos = (1,1), span = (1, 2))
@@ -1704,7 +1704,7 @@ class GdalImportDialog(ImportDialog):
             else:
                 self.SetTitle(_("Import raster data"))
         
-        self.dsnInput = gselect.GdalSelect(parent = self, panel = self.panel, ogr = ogr)
+        self.dsnInput = GdalSelect(parent = self, panel = self.panel, ogr = ogr)
         
         if link:
             self.add.SetLabel(_("Add linked layers into layer tree"))
@@ -1803,7 +1803,7 @@ class GdalImportDialog(ImportDialog):
     def OnCmdDialog(self, event):
         """!Show command dialog"""
         name = self._getCommand()
-        menuform.GUI(parent = self, modal = False).ParseCommand(cmd = [name])
+        GUI(parent = self, modal = False).ParseCommand(cmd = [name])
 
 class GdalOutputDialog(wx.Dialog):
     def __init__(self, parent, id = wx.ID_ANY, ogr = False,
@@ -1837,9 +1837,9 @@ class GdalOutputDialog(wx.Dialog):
         self.btnOk.SetDefault()
         self.btnOk.Enable(False)
         
-        self.dsnInput = gselect.GdalSelect(parent = self, panel = self.panel,
-                                           ogr = ogr,
-                                           exclude = ['file', 'protocol'], dest = True)
+        self.dsnInput = GdalSelect(parent = self, panel = self.panel,
+                                   ogr = ogr,
+                                   exclude = ['file', 'protocol'], dest = True)
         
         self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
         self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOk)
@@ -1877,25 +1877,25 @@ class GdalOutputDialog(wx.Dialog):
         self.Layout()
         
     def OnCmdDialog(self, event):
-        menuform.GUI(parent = self, modal = True).ParseCommand(cmd = ['v.external.out'])
+        GUI(parent = self, modal = True).ParseCommand(cmd = ['v.external.out'])
     
     def OnCancel(self, event):
         self.Destroy()
         
     def OnOK(self, event):
         if self.dsnInput.GetType() == 'native':
-            gcmd.RunCommand('v.external.out',
-                            parent = self,
-                            flags = 'r')
+            RunCommand('v.external.out',
+                       parent = self,
+                       flags = 'r')
         else:
             dsn = self.dsnInput.GetDsn()
             frmt = self.dsnInput.GetFormat()
             options = self.dsnInput.GetOptions()
             
-            gcmd.RunCommand('v.external.out',
-                            parent = self,
-                            dsn = dsn, format = frmt,
-                            options = options)
+            RunCommand('v.external.out',
+                       parent = self,
+                       dsn = dsn, format = frmt,
+                       options = options)
         self.Close()
         
 class DxfImportDialog(ImportDialog):
@@ -1958,12 +1958,12 @@ class DxfImportDialog(ImportDialog):
             return 
         
         data = list()        
-        ret = gcmd.RunCommand('v.in.dxf',
-                              quiet = True,
-                              parent = self,
-                              read = True,
-                              flags = 'l',
-                              input = path)
+        ret = RunCommand('v.in.dxf',
+                         quiet = True,
+                         parent = self,
+                         read = True,
+                         flags = 'l',
+                         input = path)
         if not ret:
             self.list.LoadData()
             self.btn_run.Enable(False)
@@ -1972,7 +1972,7 @@ class DxfImportDialog(ImportDialog):
         for line in ret.splitlines():
             layerId = line.split(':')[0].split(' ')[1]
             layerName = line.split(':')[1].strip()
-            grassName = utils.GetValidLayerName(layerName)
+            grassName = GetValidLayerName(layerName)
             data.append((layerId, layerName.strip(), grassName.strip()))
         
         self.list.LoadData(data)
@@ -1983,7 +1983,7 @@ class DxfImportDialog(ImportDialog):
 
     def OnCmdDialog(self, event):
         """!Show command dialog"""
-        menuform.GUI(parent = self, modal = True).ParseCommand(cmd = ['v.in.dxf'])
+        GUI(parent = self, modal = True).ParseCommand(cmd = ['v.in.dxf'])
                 
 class LayersList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,
                  listmix.CheckListCtrlMixin, listmix.TextEditMixin):

+ 15 - 65
gui/wxpython/gui_modules/menuform.py

@@ -1,6 +1,6 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
 """
+@package gui_core.forms
+
 @brief Construct simple wxPython GUI from a GRASS command interface
 description.
 
@@ -51,25 +51,21 @@ import string
 import textwrap
 import os
 import time
-import types
 import copy
 import locale
-import types
 from threading import Thread
 import Queue
-import tempfile
 
-import globalvar
+from core import globalvar
 import wx
-import wx.html
 try:
     import wx.lib.agw.flatnotebook as FN
 except ImportError:
     import wx.lib.flatnotebook as FN
-import wx.lib.colourselect as csel
+import wx.lib.colourselect     as csel
 import wx.lib.filebrowsebutton as filebrowse
-import wx.lib.scrolledpanel as scrolled
-from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+import wx.lib.scrolledpanel    as scrolled
+from wx.lib.expando  import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
 from wx.lib.newevent import NewEvent
 
 try:
@@ -77,8 +73,8 @@ try:
 except ImportError:
     import elementtree.ElementTree as etree # Python <= 2.4
 
-import gdialogs
-from ghelp import HelpPanel
+from gui_core.dialogs import StaticWrapText
+from gui_core.ghelp   import HelpPanel
 
 gisbase = os.getenv("GISBASE")
 if gisbase is None:
@@ -93,11 +89,11 @@ sys.path.append(wxbase)
 from grass.script import core as grass
 from grass.script import task as gtask
 
-import gselect
-import gcmd
-import goutput
-import utils
-from preferences import globalSettings as UserSettings
+from gui_core import gselect
+from core import cmd as gcmd
+from gui_core import goutput
+from core import utils
+from core.settings import UserSettings
 try:
     import subprocess
 except:
@@ -451,8 +447,8 @@ class mainFrame(wx.Frame):
             module_desc = self.task.label + ' ' + self.task.description
         else:
             module_desc = self.task.description
-        self.description = gdialogs.StaticWrapText(parent = self.panel,
-                                                   label = module_desc)
+        self.description = StaticWrapText(parent = self.panel,
+                                          label = module_desc)
         topsizer.Add(item = self.description, proportion = 1, border = 5,
                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
         
@@ -2164,52 +2160,6 @@ class FloatValidator(wx.PyValidator):
     def TransferFromWindow(self):
         return True # Prevent wxDialog from complaining.
 
-class GNotebook(FN.FlatNotebook):
-    """!Generic notebook widget
-    """
-    def __init__(self, parent, style, **kwargs):
-        if globalvar.hasAgw:
-            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, agwStyle = style, **kwargs)
-        else:
-            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, style = style, **kwargs)
-        
-        self.notebookPages = {}
-            
-    def AddPage(self, **kwargs):
-        """!Add a new page
-        """
-        if 'name' in kwargs:
-            self.notebookPages[kwargs['name']] = kwargs['page']
-            del kwargs['name']
-        super(GNotebook, self).AddPage(**kwargs)
-
-    def InsertPage(self, **kwargs):
-        """!Insert a new page
-        """
-        if 'name' in kwargs:
-            self.notebookPages[kwargs['name']] = kwargs['page']
-            del kwargs['name']
-        super(GNotebook, self).InsertPage(**kwargs)
-
-    def SetSelectionByName(self, page):
-        """!Set notebook
-        
-        @param page names, eg. 'layers', 'output', 'search', 'pyshell', 'nviz'
-        """
-        idx = self.GetPageIndexByName(page)
-        if self.GetSelection() != idx:
-            self.SetSelection(idx)
-        
-    def GetPageIndexByName(self, page):
-        """!Get notebook page index
-        
-        @param page name
-        """
-        if page not in self.notebookPages:
-            return -1
-        
-        return self.GetPageIndex(self.notebookPages[page])
-    
 if __name__ ==  "__main__":
     import gettext
     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)

+ 25 - 24
gui/wxpython/gui_modules/ghelp.py

@@ -1,5 +1,5 @@
 """!
-@package help.py
+@package gui_core.ghelp
 
 @brief Help window
 
@@ -38,12 +38,13 @@ import  wx.lib.scrolledpanel as scrolled
 import grass.script as grass
 from grass.script import task as gtask
 
-import menudata
-import gcmd
-import globalvar
-import gdialogs
-import utils
-import menuform
+from core             import globalvar
+from core             import utils
+from lmgr.menudata    import ManagerData
+from core.gcmd        import GError, RunCommand, DecodeString
+from gui_core.widgets import GNotebook
+from core.forms       import GUI
+from gui_core.dialogs import StaticWrapText
 
 class HelpFrame(wx.Frame):
     """!GRASS Quickstart help window"""
@@ -91,8 +92,8 @@ class SearchModuleWindow(wx.Panel):
         self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
         
         if self.showTip:
-            self.searchTip = gdialogs.StaticWrapText(parent = self, id = wx.ID_ANY,
-                                                     size = (-1, 35))
+            self.searchTip = StaticWrapText(parent = self, id = wx.ID_ANY,
+                                            size = (-1, 35))
         
         if self.showChoice:
             self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
@@ -219,7 +220,7 @@ class MenuTreeWindow(wx.Panel):
         self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
                                     label = " %s " % _("Menu tree (double-click to run command)"))
         # tree
-        self.tree = MenuTree(parent = self, data = menudata.ManagerData())
+        self.tree = MenuTree(parent = self, data = ManagerData())
         self.tree.Load()
 
         # search widget
@@ -550,7 +551,7 @@ class AboutWindow(wx.Frame):
                       border = 25)
         
         # create a flat notebook for displaying information about GRASS
-        aboutNotebook = menuform.GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 
+        aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 
         aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
         
         for title, win in ((_("Info"), infoTxt),
@@ -682,10 +683,10 @@ class AboutWindow(wx.Frame):
             contribFile.close()
             
             if errLines:
-                gcmd.GError(parent = self,
-                            message = _("Error when reading file '%s'.") % contribfile + \
-                                "\n\n" + _("Lines:") + " %s" % \
-                                os.linesep.join(map(utils.DecodeString, errLines)))
+                GError(parent = self,
+                       message = _("Error when reading file '%s'.") % contribfile + \
+                           "\n\n" + _("Lines:") + " %s" % \
+                           os.linesep.join(map(utils.DecodeString, errLines)))
         else:
             contribs = None
         
@@ -742,10 +743,10 @@ class AboutWindow(wx.Frame):
             translatorsFile.close()
             
             if errLines:
-                gcmd.GError(parent = self,
-                            message = _("Error when reading file '%s'.") % translatorsfile + \
-                                "\n\n" + _("Lines:") + " %s" % \
-                                os.linesep.join(map(utils.DecodeString, errLines)))
+                GError(parent = self,
+                       message = _("Error when reading file '%s'.") % translatorsfile + \
+                           "\n\n" + _("Lines:") + " %s" % \
+                           os.linesep.join(map(utils.DecodeString, errLines)))
         else:
             translators = None
         
@@ -915,7 +916,7 @@ class InstallExtensionWindow(wx.Frame):
         
         name = self.tree.GetItemText(item)
         if not name:
-            gcmd.GError(_("Extension not defined"), parent = self)
+            GError(_("Extension not defined"), parent = self)
             return
         
         flags = list()
@@ -999,7 +1000,7 @@ class InstallExtensionWindow(wx.Frame):
 
     def OnCmdDialog(self, event):
         """!Shows command dialog"""
-        menuform.GUI(parent = self).ParseCommand(cmd = self._getCmd())
+        GUI(parent = self).ParseCommand(cmd = self._getCmd())
         
 class ExtensionTree(ItemTree):
     """!List of available extensions"""
@@ -1064,9 +1065,9 @@ class ExtensionTree(ItemTree):
             flags = 'g'
         else:
             flags = 'l'
-        ret = gcmd.RunCommand('g.extension', read = True,
-                              svnurl = url,
-                              flags = flags, quiet = True)
+        ret = RunCommand('g.extension', read = True,
+                         svnurl = url,
+                         flags = flags, quiet = True)
         if not ret:
             return
         

+ 33 - 35
gui/wxpython/gui_modules/goutput.py

@@ -1,13 +1,14 @@
 """!
-@package goutput
+@package gui_core.goutput
 
-@brief Command output log widget
+@brief Command output widgets
 
 Classes:
-- GMConsole
-- GMStc
-- GMStdout
-- GMStderr
+ - CmdThread
+ - GMConsole
+ - GMStc
+ - GMStdout
+ - GMStderr
 
 (C) 2007-2011 by the GRASS Development Team
 This program is free software under the GNU General Public
@@ -35,16 +36,14 @@ from wx.lib.newevent import NewEvent
 import grass.script as grass
 from   grass.script import task as gtask
 
-import globalvar
-import gcmd
-import utils
-import preferences
-import menuform
-import prompt
-
-from debug       import Debug
-from preferences import globalSettings as UserSettings
-from ghelp       import SearchModuleWindow
+from core            import globalvar
+from core            import utils
+from core.gcmd       import CommandThread, GMessage, GError, GException, EncodeString
+from gui_core.forms  import GUI
+from gui_core.prompt import GPromptSTC
+from core.debug      import Debug
+from core.settings   import UserSettings, Settings
+from gui_core.ghelp  import SearchModuleWindow
 
 wxCmdOutput,   EVT_CMD_OUTPUT   = NewEvent()
 wxCmdProgress, EVT_CMD_PROGRESS = NewEvent()
@@ -55,8 +54,8 @@ wxCmdPrepare,  EVT_CMD_PREPARE  = NewEvent()
 
 def GrassCmd(cmd, stdout = None, stderr = None):
     """!Return GRASS command thread"""
-    return gcmd.CommandThread(cmd,
-                              stdout = stdout, stderr = stderr)
+    return CommandThread(cmd,
+                         stdout = stdout, stderr = stderr)
 
 class CmdThread(threading.Thread):
     """!Thread for GRASS commands"""
@@ -148,7 +147,7 @@ class CmdThread(threading.Thread):
                     except KeyError:
                         pass
                 else:
-                    moduleInterface = menuform.GUI(show = None).ParseCommand(args[0])
+                    moduleInterface = GUI(show = None).ParseCommand(args[0])
                     outputParam = moduleInterface.get_param(value = 'output', raiseError = False)
                     if outputParam and outputParam['prompt'] == 'raster':
                         mapName = outputParam['value']
@@ -225,7 +224,7 @@ class GMConsole(wx.SplitterWindow):
         self.Bind(EVT_CMD_PREPARE, self.OnCmdPrepare)
         
         # search & command prompt
-        self.cmdPrompt = prompt.GPromptSTC(parent = self)
+        self.cmdPrompt = GPromptSTC(parent = self)
         
         if self.parent.GetName() != 'LayerManager':
             self.search = None
@@ -514,9 +513,9 @@ class GMConsole(wx.SplitterWindow):
                                  'd.barscale'     : 'barscale',
                                  'd.redraw'       : 'redraw'}[command[0]]
                 except KeyError:
-                    gcmd.GMessage(parent = self.parent,
-                                  message = _("Command '%s' not yet implemented in the WxGUI. "
-                                              "Try adding it as a command layer instead.") % command[0])
+                    GMessage(parent = self.parent,
+                             message = _("Command '%s' not yet implemented in the WxGUI. "
+                                         "Try adding it as a command layer instead.") % command[0])
                     return 1
                 
                 if layertype == 'barscale':
@@ -538,10 +537,10 @@ class GMConsole(wx.SplitterWindow):
                 # other GRASS commands (r|v|g|...)
                 if len(command) == 1 and command[0] != 'v.krige':
                     # no arguments given
-                    menuform.GUI(parent = self).ParseCommand(command)
+                    GUI(parent = self).ParseCommand(command)
                     return 0
                 
-                task = menuform.GUI(show = None).ParseCommand(command)
+                task = GUI(show = None).ParseCommand(command)
                 if task:
                     # check for <input>=-
                     for p in task.get_options()['params']:
@@ -549,12 +548,11 @@ class GMConsole(wx.SplitterWindow):
                                 p.get('element', '') == 'file' and \
                                 p.get('age', 'new') == 'old' and \
                                 p.get('value', '') == '-':
-                            gcmd.GError(parent = self,
-                                        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', '') }
-                                        )
+                            GError(parent = self,
+                                   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', '') })
                             return 1
                 
                 # switch to 'Command output' if required
@@ -592,7 +590,7 @@ class GMConsole(wx.SplitterWindow):
                 
             if task:
                 # process GRASS command without argument
-                menuform.GUI(parent = self).ParseCommand(command)
+                GUI(parent = self).ParseCommand(command)
             else:
                 self.cmdThread.RunCmd(command, stdout = self.cmdStdOut, stderr = self.cmdStdErr,
                                       onDone = onDone, onPrepare = onPrepare, userData = userData)
@@ -819,8 +817,8 @@ class GMConsole(wx.SplitterWindow):
                             display.GetMap().GetListOfLayers(l_type = 'vector'))
             
             try:
-                task = menuform.GUI(show = None).ParseCommand(event.cmd)
-            except gcmd.GException:
+                task = GUI(show = None).ParseCommand(event.cmd)
+            except GException:
                 task = None
                 return
             
@@ -1066,7 +1064,7 @@ class GMStc(wx.stc.StyledTextCtrl):
         """!Set styles for styled text output windows with type face 
         and point size selected by user (Courier New 10 is default)"""
 
-        settings = preferences.Settings()
+        settings = Settings()
         
         typeface = settings.Get(group = 'appearance', key = 'outputfont', subkey = 'type')   
         if typeface == "":

+ 76 - 76
gui/wxpython/gui_modules/gselect.py

@@ -1,5 +1,5 @@
 """!
-@package gselect
+@package gui_core.gselect
 
 @brief Custom control that selects elements
 
@@ -40,16 +40,16 @@ import wx.combo
 import wx.lib.filebrowsebutton as filebrowse
 from wx.lib.newevent import NewEvent
 
-import globalvar
+from core import globalvar
 
-sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
 import grass.script as grass
 from   grass.script import task as gtask
 
-import gcmd
-import utils
-from preferences import globalSettings as UserSettings
-from debug       import Debug
+from core.gcmd     import RunCommand, GError, GMessage
+from core.utils    import GetListOfLocations, GetListOfMapsets, GetFormats
+from core.utils    import GetSettingsPath, GetValidLayerName, ListSortLower
+from core.settings import UserSettings
+from core.debug    import Debug
 
 wxGdalSelect, EVT_GDALSELECT = NewEvent()
 
@@ -685,12 +685,12 @@ class LayerSelect(wx.ComboBox):
             # currently the following is identical to
             # layers = utils.GetVectorNumberOfLayers(self, vector)
 
-            ret = gcmd.RunCommand('v.db.connect',
-                                  read = True,
-                                  quiet = True,
-                                  fs = '|',
-                                  flags = 'g',
-                                  map = vector)
+            ret = RunCommand('v.db.connect',
+                             read = True,
+                             quiet = True,
+                             fs = '|',
+                             flags = 'g',
+                             map = vector)
             if ret:
                 for line in ret.splitlines():
                     layerinfo = line.split('|')
@@ -702,11 +702,11 @@ class LayerSelect(wx.ComboBox):
                     layers.append(layername[0])
 
         elif dsn:
-            ret = gcmd.RunCommand('v.in.ogr',
-                                  read = True,
-                                  quiet = True,
-                                  flags = 'l',
-                                  dsn = dsn)
+            ret = RunCommand('v.in.ogr',
+                             read = True,
+                             quiet = True,
+                             flags = 'l',
+                             dsn = dsn)
             if ret:
                 layers = ret.splitlines()
     
@@ -770,11 +770,11 @@ class TableSelect(wx.ComboBox):
             driver = connect['driver']
             database = connect['database']
         
-        ret = gcmd.RunCommand('db.tables',
-                              flags = 'p',
-                              read = True,
-                              driver = driver,
-                              database = database)
+        ret = RunCommand('db.tables',
+                         flags = 'p',
+                         read = True,
+                         driver = driver,
+                         database = database)
         
         if ret:
             for table in ret.splitlines():
@@ -858,11 +858,11 @@ class ColumnSelect(wx.ComboBox):
         """
         columns = list()
         
-        ret = gcmd.RunCommand('db.columns',
-                              read = True,
-                              driver = driver,
-                              database = database,
-                              table = table)
+        ret = RunCommand('db.columns',
+                         read = True,
+                         driver = driver,
+                         database = database,
+                         table = table)
         
         if ret:
             columns = ret.splitlines()
@@ -896,7 +896,7 @@ class LocationSelect(wx.ComboBox):
         else:
             self.gisdbase = gisdbase
         
-        self.SetItems(utils.GetListOfLocations(self.gisdbase))
+        self.SetItems(GetListOfLocations(self.gisdbase))
 
     def UpdateItems(self, dbase):
         """!Update list of locations
@@ -905,7 +905,7 @@ class LocationSelect(wx.ComboBox):
         """
         self.gisdbase = dbase
         if dbase:
-            self.SetItems(utils.GetListOfLocations(self.gisdbase))
+            self.SetItems(GetListOfLocations(self.gisdbase))
         else:
             self.SetItems([])
         
@@ -951,12 +951,12 @@ class MapsetSelect(wx.ComboBox):
      
     def _getMapsets(self):
         if self.searchPath:
-            return gcmd.RunCommand('g.mapsets',
-                                   read = True,
-                                   flags = 'p',
-                                   fs = 'newline').splitlines()
+            return RunCommand('g.mapsets',
+                              read = True,
+                              flags = 'p',
+                              fs = 'newline').splitlines()
         
-        return utils.GetListOfMapsets(self.gisdbase, self.location, selectable = False)
+        return GetListOfMapsets(self.gisdbase, self.location, selectable = False)
             
 class SubGroupSelect(wx.ComboBox):
     """!Widget for selecting subgroups"""
@@ -1005,7 +1005,7 @@ class FormatSelect(wx.Choice):
             ftype = 'gdal'
         
         formats = list()
-        for f in utils.GetFormats()[ftype].values():
+        for f in GetFormats()[ftype].values():
             formats += f
         self.SetItems(formats)
         
@@ -1134,9 +1134,9 @@ class GdalSelect(wx.Panel):
             self.sourceMap['pro'] = idx
         
         if self.ogr:
-            self.settingsFile = os.path.join(utils.GetSettingsPath(), 'wxOGR')
+            self.settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
         else:
-            self.settingsFile = os.path.join(utils.GetSettingsPath(), 'wxGDAL')
+            self.settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
         
         self._settings = self._loadSettings()
         self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
@@ -1220,16 +1220,16 @@ class GdalSelect(wx.Panel):
             fType = 'gdal'
         self.input = { 'file' : [_("File:"),
                                  dsnFile,
-                                 utils.GetFormats(writableOnly = dest)[fType]['file']],
+                                 GetFormats(writableOnly = dest)[fType]['file']],
                        'dir'  : [_("Name:"),
                                  dsnDir,
-                                 utils.GetFormats(writableOnly = dest)[fType]['file']],
+                                 GetFormats(writableOnly = dest)[fType]['file']],
                        'db'   : [_("Name:"),
                                  dsnDbText,
-                                 utils.GetFormats(writableOnly = dest)[fType]['database']],
+                                 GetFormats(writableOnly = dest)[fType]['database']],
                        'pro'  : [_("Protocol:"),
                                  dsnPro,
-                                 utils.GetFormats(writableOnly = dest)[fType]['protocol']],
+                                 GetFormats(writableOnly = dest)[fType]['protocol']],
                        'db-win' : { 'file'   : dsnDbFile,
                                     'text'   : dsnDbText,
                                     'choice' : dsnDbChoice },
@@ -1237,13 +1237,13 @@ class GdalSelect(wx.Panel):
                        }
         
         if self.dest:
-            current = gcmd.RunCommand('v.external.out',
-                                      parent = self,
-                                      read = True, parse = grass.parse_key_val,
-                                      flags = 'g')
+            current = RunCommand('v.external.out',
+                                 parent = self,
+                                 read = True, parse = grass.parse_key_val,
+                                 flags = 'g')
             if current['format'] == 'native':
                 self.dsnType = 'native'
-            elif current['format'] in utils.GetFormats()['ogr']['database']:
+            elif current['format'] in GetFormats()['ogr']['database']:
                 self.dsnType = 'db'
             else:
                 self.dsnType = 'dir'
@@ -1358,8 +1358,8 @@ class GdalSelect(wx.Panel):
         """!Load named settings"""
         name = event.GetString()
         if name not in self._settings:
-            gcmd.GError(parent = self,
-                        message = _("Settings <%s> not found") % name)
+            GError(parent = self,
+                   message = _("Settings <%s> not found") % name)
             return
         data = self._settings[name]
         self.OnSetType(event = None, sel = self.sourceMap[data[0]])
@@ -1376,8 +1376,8 @@ class GdalSelect(wx.Panel):
             return
         
         if not dlg.GetValue():
-            gcmd.GMessage(parent = self,
-                          message = _("Name not given, settings is not saved."))
+            GMessage(parent = self,
+                     message = _("Name not given, settings is not saved."))
             return
         
         name = dlg.GetValue()
@@ -1388,8 +1388,8 @@ class GdalSelect(wx.Panel):
                      self.format.GetStringSelection())
             fd.write('\n')
         except IOError:
-            gcmd.GError(parent = self,
-                        message = _("Unable to save settings"))
+            GError(parent = self,
+                   message = _("Unable to save settings"))
             return
         fd.close()
         
@@ -1559,11 +1559,11 @@ class GdalSelect(wx.Panel):
         layerId = 1
         
         if self.ogr:
-            ret = gcmd.RunCommand('v.in.ogr',
-                                  quiet = True,
-                                  read = True,
-                                  flags = 'a',
-                                  dsn = dsn)
+            ret = RunCommand('v.in.ogr',
+                             quiet = True,
+                             read = True,
+                             flags = 'a',
+                             dsn = dsn)
                 
             if not ret:
                 self.parent.list.LoadData()
@@ -1574,19 +1574,19 @@ class GdalSelect(wx.Panel):
             layerId = 1
             for line in ret.splitlines():
                 layerName, featureType = map(lambda x: x.strip(), line.split(' ', 1))
-                grassName = utils.GetValidLayerName(layerName)
+                grassName = GetValidLayerName(layerName)
                 data.append((layerId, layerName, featureType, grassName))
                 layerId += 1
         else:
             if self.dsnType == 'file':
                 baseName = os.path.basename(dsn)
-                grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
+                grassName = GetValidLayerName(baseName.split('.', -1)[0])
                 data.append((layerId, baseName, grassName))
             elif self.dsnType == 'dir':
                 ext = self.extension.GetValue()
                 for filename in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
                     baseName = os.path.basename(filename)
-                    grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
+                    grassName = GetValidLayerName(baseName.split('.', -1)[0])
                     data.append((layerId, baseName, grassName))
                     layerId += 1
         if self.ogr:
@@ -1744,26 +1744,26 @@ class ProjSelect(wx.ComboBox):
         if not mapset:
             mapset = grass.gisenv()['MAPSET']
         if self.isRaster:
-            ret = gcmd.RunCommand('r.proj',
-                                  quiet = True,
-                                  read = True,
-                                  flags = 'l',
-                                  dbase = dbase,
-                                  location = location,
-                                  mapset = mapset)
+            ret = RunCommand('r.proj',
+                             quiet = True,
+                             read = True,
+                             flags = 'l',
+                             dbase = dbase,
+                             location = location,
+                             mapset = mapset)
         else:
-            ret = gcmd.RunCommand('v.proj',
-                                  quiet = True,
-                                  read = True,
-                                  flags = 'l',
-                                  dbase = dbase,
-                                  location = location,
-                                  mapset = mapset)
+            ret = RunCommand('v.proj',
+                             quiet = True,
+                             read = True,
+                             flags = 'l',
+                             dbase = dbase,
+                             location = location,
+                             mapset = mapset)
         listMaps = list()
         if ret:
             for line in ret.splitlines():
                 listMaps.append(line.strip())
-        utils.ListSortLower(listMaps)
+        ListSortLower(listMaps)
         
         self.SetItems(listMaps)
         self.SetValue('')

+ 337 - 0
gui/wxpython/gui_core/mapdisp.py

@@ -0,0 +1,337 @@
+"""!
+@package gui_core.mapdisp
+
+@brief Base classes for Map display window
+
+Classes:
+ - MapFrameBase
+
+(C) 2009-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+@author Michael Barton <michael.barton@asu.edu>
+"""
+
+import os
+
+import wx
+
+from core       import globalvar
+from core.debug import Debug
+
+from grass.script import core as grass
+
+class MapFrameBase(wx.Frame):
+    """!Base class for map display window
+    
+    Derived class must use statusbarManager or override
+    GetProperty, SetProperty and HasProperty methods.
+    If derived class enables and disables auto-rendering,
+    it should override IsAutoRendered method.
+    """
+    def __init__(self, parent = None, id = wx.ID_ANY, title = None,
+                 style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
+                 Map = None, auimgr = None, name = None, **kwargs):
+        """!
+        @param toolbars array of activated toolbars, e.g. ['map', 'digit']
+        @param Map instance of render.Map
+        @param auimgs AUI manager
+        @param name frame name
+        @param kwargs wx.Frame attributes
+        """
+        
+
+        self.Map        = Map       # instance of render.Map
+        self.parent     = parent
+        
+        wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
+        
+        # available cursors
+        self.cursors = {
+            # default: cross
+            # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
+            "default" : wx.StockCursor(wx.CURSOR_ARROW),
+            "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
+            "hand"    : wx.StockCursor(wx.CURSOR_HAND),
+            "pencil"  : wx.StockCursor(wx.CURSOR_PENCIL),
+            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
+            }
+                
+        #
+        # set the size & system icon
+        #
+        self.SetClientSize(self.GetSize())
+        self.iconsize = (16, 16)
+
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
+        
+        # toolbars
+        self.toolbars = {}
+        
+        #
+        # Fancy gui
+        #
+        self._mgr = wx.aui.AuiManager(self)
+        
+    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')
+        
+        self.width, self.height = self.GetClientSize()
+        
+        Debug.msg(2, "MapFrame._initMap():")
+        map.ChangeMapSize(self.GetClientSize())
+        map.region = map.GetRegion() # g.region -upgc
+        # self.Map.SetRegion() # adjust region to match display window
+        
+    def SetProperty(self, name, value):
+        """!Sets property"""
+        self.statusbarManager.SetProperty(name, value)
+        
+    def GetProperty(self, name):
+        """!Returns property"""
+        return self.statusbarManager.GetProperty(name)
+        
+    def HasProperty(self, name):
+        """!Checks whether object has property"""
+        return self.statusbarManager.HasProperty(name)
+    
+    def GetPPM(self):
+        """! Get pixel per meter
+        
+        @todo now computed every time, is it necessary?
+        @todo enable user to specify ppm (and store it in UserSettings)
+        """
+        # TODO: need to be fixed...
+        ### screen X region problem
+        ### user should specify ppm
+        dc = wx.ScreenDC()
+        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])
+
+        ppi = comPpi                  # pixel per inch
+        ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
+                    (ppi[1] / 2.54) * 100)
+        
+        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
+    
+    def SetMapScale(self, value, map = None):
+        """! Set current map scale
+        
+        @param value scale value (n if scale is 1:n)
+        @param map Map instance (if none self.Map is used)
+        """
+        if not map:
+            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.
+        
+        # add to zoom history
+        self.GetWindow().ZoomHistory(region['n'], region['s'],
+                                   region['e'], region['w'])
+    
+    def GetMapScale(self, map = None):
+        """! Get current map scale
+        
+        @param map Map instance (if none self.Map is used)
+        """
+        if not map:
+            map = self.Map
+        
+        region = map.region
+        ppm = self.GetPPM()
+
+        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))
+
+        xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
+        yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
+        scale = (xscale + yscale) / 2.
+        
+        Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
+                      (xscale, yscale, scale))
+        
+        return scale
+        
+    def GetProgressBar(self):
+        """!Returns progress bar
+        
+        Progress bar can be used by other classes.
+        """
+        return self.statusbarManager.GetProgressBar()
+        
+    def GetMap(self):
+        """!Returns current Map instance
+        """
+        return self.Map
+
+    def GetWindow(self):
+        """!Get map window"""
+        return self.MapWindow
+        
+    def GetMapToolbar(self):
+       """!Returns toolbar with zooming tools"""
+       raise NotImplementedError()
+       
+    def GetToolbar(self, name):
+        """!Returns toolbar if exists else None.
+        
+        Toolbars dictionary contains currently used toolbars only.
+        """
+        if name in self.toolbars:
+            return self.toolbars[name]
+        
+        return None
+       
+    def StatusbarUpdate(self):
+        """!Update statusbar content"""
+        self.statusbarManager.Update()
+        
+    def IsAutoRendered(self):
+        """!Check if auto-rendering is enabled"""
+        return self.GetProperty('render')
+        
+    def CoordinatesChanged(self):
+        """!Shows current coordinates on statusbar.
+        
+        Used in BufferedWindow to report change of map coordinates (under mouse cursor).
+        """
+        self.statusbarManager.ShowItem('coordinates')
+        
+    def StatusbarReposition(self):
+        """!Reposition items in statusbar"""
+        self.statusbarManager.Reposition()
+        
+    def StatusbarEnableLongHelp(self, enable = True):
+        """!Enable/disable toolbars long help"""
+        for toolbar in self.toolbars.itervalues():
+            toolbar.EnableLongHelp(enable)
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        raise NotImplementedError("IsStandalone")
+   
+    def OnRender(self, event):
+        """!Re-render map composition (each map layer)
+        """
+        raise NotImplementedError("OnRender")
+        
+    def OnDraw(self, event):
+        """!Re-display current map composition
+        """
+        self.MapWindow.UpdateMap(render = False)
+        
+    def OnErase(self, event):
+        """!Erase the canvas
+        """
+        self.MapWindow.EraseMap()
+        
+    def OnZoomIn(self, event):
+        """!Zoom in the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._prepareZoom(mapWindow = win, zoomType = 1)
+        
+    def OnZoomOut(self, event):
+        """!Zoom out the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._prepareZoom(mapWindow = win, zoomType = -1)
+        
+    def _prepareZoom(self, mapWindow, zoomType):
+        """!Prepares MapWindow for zoom, toggles toolbar
+        
+        @param mapWindow MapWindow to prepare
+        @param zoomType 1 for zoom in, -1 for zoom out
+        """
+        mapWindow.mouse['use'] = "zoom"
+        mapWindow.mouse['box'] = "box"
+        mapWindow.zoomtype = zoomType
+        mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
+        
+        # change the cursor
+        mapWindow.SetCursor(self.cursors["cross"])
+    
+    def _switchTool(self, toolbar, event):
+        """!Helper function to switch tools"""
+        if toolbar:
+            toolbar.OnTool(event)
+            toolbar.action['desc'] = ''
+            
+    def OnPan(self, event):
+        """!Panning, set mouse to drag
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._preparePan(mapWindow = win)
+    
+    def _preparePan(self, mapWindow):
+        """!Prepares MapWindow for pan, toggles toolbar
+        
+        @param mapWindow MapWindow to prepare
+        """
+        mapWindow.mouse['use'] = "pan"
+        mapWindow.mouse['box'] = "pan"
+        mapWindow.zoomtype = 0
+        
+        # change the cursor
+        mapWindow.SetCursor(self.cursors["hand"])
+        
+    def OnZoomBack(self, event):
+        """!Zoom last (previously stored position)
+        """
+        self.MapWindow.ZoomBack()
+        
+    def OnZoomToMap(self, event):
+        """!
+        Set display extents to match selected raster (including NULLs)
+        or vector map.
+        """
+        self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
+    
+    def OnZoomToWind(self, event):
+        """!Set display geometry to match computational region
+        settings (set with g.region)
+        """
+        self.MapWindow.ZoomToWind()
+        
+    def OnZoomToDefault(self, event):
+        """!Set display geometry to match default region settings
+        """
+        self.MapWindow.ZoomToDefault()

+ 238 - 0
gui/wxpython/gui_core/mapwindow.py

@@ -0,0 +1,238 @@
+"""!
+@package gui_core.mapwindow
+
+@brief Map display canvas - base class for buffered window.
+
+Classes:
+ - MapWindow
+
+(C) 2006-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+@author Michael Barton
+@author Jachym Cepicky
+"""
+
+import wx
+
+class MapWindow(object):
+    """!Abstract map display window class
+    
+    Superclass for BufferedWindow class (2D display mode), and GLWindow
+    (3D display mode).
+    
+    Subclasses have to define
+     - _bindMouseEvents method which binds MouseEvent handlers
+     - Pixel2Cell
+     - Cell2Pixel (if it is possible)
+    
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None, **kwargs):
+        self.parent = parent # MapFrame
+        self.Map    = Map
+        self.tree   = tree
+        self.lmgr   = lmgr
+        
+        # mouse attributes -- position on the screen, begin and end of
+        # dragging, and type of drawing
+        self.mouse = {
+            'begin': [0, 0], # screen coordinates
+            'end'  : [0, 0],
+            'use'  : "pointer",
+            'box'  : "point"
+            }
+        # last east, north coordinates, changes on mouse motion
+        self.lastEN = None 
+        
+        # stores overridden cursor
+        self._overriddenCursor = None
+
+    def RegisterMouseEventHandler(self, event, handler, cursor = None):
+        """!Binds event handler
+        
+        Call event.Skip() in handler to allow default processing in MapWindow.
+        
+        @code
+        # your class methods
+        def OnButton(self, event):
+            # current map display's map window
+            # expects LayerManager to be the parent
+            self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
+            if self.mapwin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
+                                                     wx.StockCursor(wx.CURSOR_CROSS)):
+                self.parent.GetLayerTree().GetMapDisplay().Raise()
+            else:
+                # handle that you cannot get coordinates
+        
+        def OnMouseAction(self, event):
+            # get real world coordinates of mouse click
+            coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
+            self.text.SetLabel('Coor: ' + str(coor))
+            self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN)
+            event.Skip()
+        @endcode
+        
+        @param event one of mouse events
+        @param handler function to handle event
+        @param cursor cursor which temporary overrides current cursor
+        
+        @return True if successful
+        @return False if event cannot be bind
+        """
+        
+        # if it is a VDigitWindow it cannot be used
+        # hasattr is ugly
+        if hasattr(self, "digit"):
+            return False
+        
+        self.Bind(event, handler)
+        self.mouse['useBeforeGenericEvent'] = self.mouse['use']
+        self.mouse['use'] = 'genericEvent'
+        
+        if cursor:
+            self._overriddenCursor = self.GetCursor()
+            self.SetCursor(cursor)
+        
+        return True
+
+
+    def UnregisterMouseEventHandler(self, event):
+        """!Unbinds event handler a restores previous state
+        
+        You should unbind to restore normal MapWindow behaviour.
+        Note that this operation will unbind any other external (non-MapWindow) handlers.
+        
+        @param event event to unbind
+        
+        @return True if successful
+        @return False if event cannot be unbind
+        """
+        if hasattr(self, "digit"):
+            return False
+        
+        # it is not yet possible in wxPython to unbind exact event
+        ret = self.Unbind(event)
+        
+        # restore bind state
+        self._bindMouseEvents()
+        
+        # restore mouse use (previous state)
+        self.mouse['use'] = self.mouse['useBeforeGenericEvent']
+        
+        # restore overridden cursor
+        if self._overriddenCursor:
+            self.SetCursor(self._overriddenCursor)
+        
+        return ret
+    
+    def Pixel2Cell(self, (x, y)):
+        raise NotImplementedError()
+    
+    def Cell2Pixel(self, (east, north)):
+        raise NotImplementedError()
+
+    def OnMotion(self, event):
+        """!Tracks mouse motion and update statusbar
+        
+        @see GetLastEN
+        """
+        try:
+            self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
+        except (ValueError):
+            self.lastEN = None
+        # FIXME: special case for vdigit and access to statusbarManager
+        if self.parent.statusbarManager.GetMode() == 0: # Coordinates            
+            updated = False
+            if hasattr(self, "digit"):
+                precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+                updated = self._onMotion(self.lastEN, precision)
+
+            if not updated:
+                self.parent.CoordinatesChanged()
+        
+        event.Skip()
+
+    def GetLastEN(self):
+        """!Returns last coordinates of mouse cursor.
+        
+        @see OnMotion
+        """
+        return self.lastEN
+    
+    def GetLayerByName(self, name, mapType, dataType = 'layer'):
+        """!Get layer from layer tree by nam
+        
+        @param name layer name
+        @param type 'item' / 'layer' / 'nviz'
+
+        @return layer / map layer properties / nviz properties
+        @return None
+        """
+        if not self.tree:
+            return None
+        
+        try:
+            mapLayer = self.Map.GetListOfLayers(l_type = mapType, l_name = name)[0]
+        except IndexError:
+            return None
+        
+        if dataType == 'layer':
+            return mapLayer
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        if not item:
+            return None
+        if dataType == 'nviz':
+            return self.tree.GetPyData(item)[0]['nviz']
+        
+        return item
+        
+    def GetSelectedLayer(self, type = 'layer', multi = False):
+        """!Get selected layer from layer tree
+        
+        @param type 'item' / 'layer' / 'nviz'
+        @param multi return first selected layer or all
+        
+        @return layer / map layer properties / nviz properties
+        @return None / [] on failure
+        """
+        ret = []
+        if not self.tree or \
+                not self.tree.GetSelection():
+            if multi:
+                return []
+            else:
+                return None
+        
+        if multi and \
+                type == 'item':
+            return self.tree.GetSelections()
+        
+        for item in self.tree.GetSelections():
+            if not item.IsChecked():
+                if multi:
+                    continue
+                else:
+                    return None
+
+            if type == 'item': # -> multi = False
+                return item
+        
+            try:
+                if type == 'nviz':
+                    layer = self.tree.GetPyData(item)[0]['nviz']
+                else:
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+            except:
+                layer = None
+
+            if multi:
+                ret.append(layer)
+            else:
+                return layer
+            
+        return ret

+ 5 - 5
gui/wxpython/gui_modules/menu.py

@@ -1,5 +1,5 @@
 """!
-@package menu.py
+@package gui_core.menu
 
 @brief Menu classes for wxGUI
 
@@ -19,10 +19,10 @@ This program is free software under the GNU General Public License
 
 import wx
 
-import globalvar
-import utils
-
-from preferences import globalSettings as UserSettings
+from core          import globalvar
+from core          import utils
+from core.gcmd     import EncodeString
+from core.settings import UserSettings
 
 class Menu(wx.MenuBar):
     def __init__(self, parent, data):

Різницю між файлами не показано, бо вона завелика
+ 21 - 1044
gui/wxpython/gui_modules/preferences.py


+ 6 - 8
gui/wxpython/gui_modules/prompt.py

@@ -1,5 +1,5 @@
 """!
-@package prompt.py
+@package gui_core.prompt
 
 @brief wxGUI command prompt
 
@@ -22,7 +22,6 @@ for details.
 
 import os
 import sys
-import copy
 import difflib
 import codecs
 
@@ -33,11 +32,10 @@ import wx.lib.mixins.listctrl as listmix
 from grass.script import core as grass
 from grass.script import task as gtask
 
-import globalvar
-import menudata
-import menuform
-import gcmd
-import utils
+from core          import globalvar
+from core          import utils
+from lmgr.menudata import ManagerData
+from core.gcmd     import EncodeString, DecodeString
 
 class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
     """!PopUp window used by GPromptPopUp"""
@@ -484,7 +482,7 @@ class GPrompt(object):
         # dictionary of modules (description, keywords, ...)
         if not self.standAlone:
             if self.parent.parent.GetName() == 'Modeler':
-                self.moduleDesc = menudata.ManagerData().GetModules()
+                self.moduleDesc = ManagerData().GetModules()
             else:
                 self.moduleDesc = parent.parent.menubar.GetData().GetModules()
             self.moduleList = self._getListOfModules()

+ 177 - 0
gui/wxpython/gui_core/toolbars.py

@@ -0,0 +1,177 @@
+"""!
+@package gui_core.toolbars
+
+@brief Base classes toolbar widgets
+
+Classes:
+ - BaseToolbar
+
+(C) 2007-2011 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.
+
+@author Michael Barton
+@author Jachym Cepicky
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import platform
+
+import wx
+
+from core               import globalvar
+from core.debug         import Debug
+
+class BaseToolbar(wx.ToolBar):
+    """!Abstract toolbar class"""
+    def __init__(self, parent):
+        self.parent = parent
+        wx.ToolBar.__init__(self, parent = self.parent, id = wx.ID_ANY)
+        
+        self.action = dict()
+        
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
+        self.SetToolBitmapSize(globalvar.toolbarSize)
+        
+    def InitToolbar(self, toolData):
+        """!Initialize toolbar, add tools to the toolbar
+        """
+        for tool in toolData:
+            self.CreateTool(*tool)
+        
+        self._data = toolData
+        
+    def _toolbarData(self):
+        """!Toolbar data (virtual)"""
+        return None
+    
+    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
+        @return id of tool
+        """
+        bmpDisabled = wx.NullBitmap
+        tool = -1
+        if label:
+            tool = vars(self)[label] = wx.NewId()
+            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)
+            else:
+                toolWin = self.InsertLabelTool(pos, tool, label, bitmap,
+                                            bmpDisabled, kind,
+                                            shortHelp, longHelp)
+            self.Bind(wx.EVT_TOOL, handler, toolWin)
+        else: # separator
+            self.AddSeparator()
+        
+        return tool
+    
+    def EnableLongHelp(self, enable = True):
+        """!Enable/disable long help
+        
+        @param enable True for enable otherwise disable
+        """
+        for tool in self._data:
+            if tool[0] == '': # separator
+                continue
+            
+            if enable:
+                self.SetToolLongHelp(vars(self)[tool[0]], tool[4])
+            else:
+                self.SetToolLongHelp(vars(self)[tool[0]], "")
+        
+    def OnTool(self, event):
+        """!Tool selected
+        """
+        if self.parent.GetName() == "GCPFrame":
+            return
+        
+        if hasattr(self.parent, 'toolbars'):
+            if self.parent.GetToolbar('vdigit'):
+                # update vdigit toolbar (unselect currently selected tool)
+                id = self.parent.toolbars['vdigit'].GetAction(type = 'id')
+                self.parent.toolbars['vdigit'].ToggleTool(id, False)
+        
+        if event:
+            # deselect previously selected tool
+            id = self.action.get('id', -1)
+            if id != event.GetId():
+                self.ToggleTool(self.action['id'], False)
+            else:
+                self.ToggleTool(self.action['id'], True)
+            
+            self.action['id'] = event.GetId()
+            
+            event.Skip()
+        else:
+            # initialize toolbar
+            self.ToggleTool(self.action['id'], True)
+        
+    def GetAction(self, type = 'desc'):
+        """!Get current action info"""
+        return self.action.get(type, '')
+    
+    def SelectDefault(self, event):
+        """!Select default tool"""
+        self.ToggleTool(self.defaultAction['id'], True)
+        self.defaultAction['bind'](event)
+        self.action = { 'id' : self.defaultAction['id'],
+                        'desc' : self.defaultAction.get('desc', '') }
+        
+    def FixSize(self, width):
+        """!Fix toolbar width on Windows
+            
+        @todo Determine why combobox causes problems here
+        """
+        if platform.system() == 'Windows':
+            size = self.GetBestSize()
+            self.SetSize((size[0] + width, size[1]))
+
+    def Enable(self, tool, enable = True):
+        """!Enable/Disable defined tool
+        
+        @param tool name
+        @param enable True to enable otherwise disable tool
+        """
+        try:
+            id = getattr(self, tool)
+        except AttributeError:
+            return
+        
+        self.EnableTool(id, enable)
+
+    def EnableAll(self, enable = True):
+        """!Enable/Disable all tools
+        
+        @param enable True to enable otherwise disable tool
+        """
+        for item in self._toolbarData():
+            if not item[0]:
+                continue
+            self.Enable(item[0], enable)
+        
+    def _getToolbarData(self, data):
+        """!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
+        """
+        if name:
+            return (name, icon.GetBitmap(),
+                    item, icon.GetLabel(), icon.GetDesc(),
+                    handler, pos)
+        return ("", "", "", "", "", "") # separator

+ 231 - 0
gui/wxpython/gui_core/widgets.py

@@ -0,0 +1,231 @@
+"""!
+@package gui_core.widgets
+
+@brief Core GUI widgets
+
+Classes:
+ - ScrolledPanel
+ - NTCValidator
+ - NumTextCtrl
+ - FloatSlider
+ - SymbolButton
+
+(C) 2008-2011 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.
+
+@author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+@author Enhancements by Michael Barton <michael.barton asu.edu>
+@author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
+"""
+
+import string
+
+import wx
+import wx.lib.scrolledpanel as SP
+try:
+    import wx.lib.agw.flatnotebook   as FN
+except ImportError:
+    import wx.lib.flatnotebook   as FN
+try:
+    from wx.lib.buttons import ThemedGenBitmapTextButton as BitmapTextButton
+except ImportError: # not sure about TGBTButton version
+    from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton
+
+from core import globalvar
+
+class GNotebook(FN.FlatNotebook):
+    """!Generic notebook widget
+    """
+    def __init__(self, parent, style, **kwargs):
+        if globalvar.hasAgw:
+            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, agwStyle = style, **kwargs)
+        else:
+            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, style = style, **kwargs)
+        
+        self.notebookPages = {}
+            
+    def AddPage(self, **kwargs):
+        """!Add a new page
+        """
+        if 'name' in kwargs:
+            self.notebookPages[kwargs['name']] = kwargs['page']
+            del kwargs['name']
+        super(GNotebook, self).AddPage(**kwargs)
+
+    def InsertPage(self, **kwargs):
+        """!Insert a new page
+        """
+        if 'name' in kwargs:
+            self.notebookPages[kwargs['name']] = kwargs['page']
+            del kwargs['name']
+        super(GNotebook, self).InsertPage(**kwargs)
+
+    def SetSelectionByName(self, page):
+        """!Set notebook
+        
+        @param page names, eg. 'layers', 'output', 'search', 'pyshell', 'nviz'
+        """
+        idx = self.GetPageIndexByName(page)
+        if self.GetSelection() != idx:
+            self.SetSelection(idx)
+        
+    def GetPageIndexByName(self, page):
+        """!Get notebook page index
+        
+        @param page name
+        """
+        if page not in self.notebookPages:
+            return -1
+        
+        return self.GetPageIndex(self.notebookPages[page])
+
+class ScrolledPanel(SP.ScrolledPanel):
+    """!Custom ScrolledPanel to avoid strange behaviour concerning focus"""
+    def __init__(self, parent):
+        SP.ScrolledPanel.__init__(self, parent = parent, id = wx.ID_ANY)
+    def OnChildFocus(self, event):
+        pass
+        
+        
+class NTCValidator(wx.PyValidator):
+    """!validates input in textctrls, taken from wxpython demo"""
+    def __init__(self, flag = None):
+        wx.PyValidator.__init__(self)
+        self.flag = flag
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+
+    def Clone(self):
+        return NTCValidator(self.flag)
+
+    def OnChar(self, event):
+        key = event.GetKeyCode()
+        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
+            event.Skip()
+            return
+        if self.flag == 'DIGIT_ONLY' and chr(key) in string.digits + '.-':
+            event.Skip()
+            return
+        if not wx.Validator_IsSilent():
+            wx.Bell()
+        # Returning without calling even.Skip eats the event before it
+        # gets to the text control
+        return  
+    
+class NumTextCtrl(wx.TextCtrl):
+    """!Class derived from wx.TextCtrl for numerical values only"""
+    def __init__(self, parent,  **kwargs):
+##        self.precision = kwargs.pop('prec')
+        wx.TextCtrl.__init__(self, parent = parent,
+            validator = NTCValidator(flag = 'DIGIT_ONLY'), **kwargs)
+        
+            
+    def SetValue(self, value):
+        super(NumTextCtrl, self).SetValue( str(value))
+        
+    def GetValue(self):
+        val = super(NumTextCtrl, self).GetValue()
+        if val == '':
+            val = '0'
+        try:
+            return float(val)
+        except ValueError:
+            val = ''.join(''.join(val.split('-')).split('.'))
+            return float(val)
+        
+    def SetRange(self, min, max):
+        pass
+   
+class FloatSlider(wx.Slider):
+    """!Class derived from wx.Slider for floats"""
+    def __init__(self, **kwargs):
+        Debug.msg(1, "FloatSlider.__init__()")
+        wx.Slider.__init__(self, **kwargs)
+        self.coef = 1.
+        #init range
+        self.minValueOrig = 0
+        self.maxValueOrig = 1
+        
+    def SetValue(self, value):
+        value *= self.coef 
+        if abs(value) < 1 and value != 0:
+            while abs(value) < 1:
+                value *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetRange(self.minValueOrig * self.coef, self.maxValueOrig * self.coef)
+        super(FloatSlider, self).SetValue(value)
+        
+        Debug.msg(4, "FloatSlider.SetValue(): value = %f" % value)
+        
+    def SetRange(self, minValue, maxValue):
+        self.coef = 1.
+        self.minValueOrig = minValue
+        self.maxValueOrig = maxValue
+        if abs(minValue) < 1 or abs(maxValue) < 1:
+            while (abs(minValue) < 1 and minValue != 0) or (abs(maxValue) < 1 and maxValue != 0):
+                minValue *= 100
+                maxValue *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetValue(super(FloatSlider, self).GetValue() * self.coef)
+        super(FloatSlider, self).SetRange(minValue, maxValue)
+        Debug.msg(4, "FloatSlider.SetRange(): minValue = %f, maxValue = %f" % (minValue, maxValue))
+            
+    def GetValue(self):
+        val = super(FloatSlider, self).GetValue()
+        Debug.msg(4, "FloatSlider.GetValue(): value = %f" % (val/self.coef))
+        return val/self.coef
+        
+        
+class SymbolButton(BitmapTextButton):
+    """!Button with symbol and label."""
+    def __init__(self, parent, usage, label, **kwargs):
+        """!Constructor
+        
+        @param parent parent (usually wx.Panel)
+        @param usage determines usage and picture
+        @param label displayed label
+        """
+        BitmapTextButton.__init__(self, parent = parent, label = " " + label, **kwargs)
+        
+        size = (15, 15)
+        buffer = wx.EmptyBitmap(*size)
+        dc = wx.MemoryDC()
+        dc.SelectObject(buffer)
+        maskColor = wx.Color(255, 255, 255)
+        dc.SetBrush(wx.Brush(maskColor))
+        dc.Clear()
+        
+        if usage == 'record':
+            self.DrawRecord(dc, size)
+        elif usage == 'stop':
+            self.DrawStop(dc, size)
+        elif usage == 'play':
+            self.DrawPlay(dc, size)
+        elif usage == 'pause':
+            self.DrawPause(dc, size)
+
+        buffer.SetMaskColour(maskColor)
+        self.SetBitmapLabel(buffer)
+        dc.SelectObject(wx.NullBitmap)
+        
+    def DrawRecord(self, dc, size):
+        """!Draw record symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(255, 0, 0)))
+        dc.DrawCircle(size[0]/2, size[1] / 2, size[0] / 2)
+        
+    def DrawStop(self, dc, size):
+        """!Draw stop symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, size[0], size[1])
+        
+    def DrawPlay(self, dc, size):
+        """!Draw play symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(0, 255, 0)))
+        points = (wx.Point(0, 0), wx.Point(0, size[1]), wx.Point(size[0], size[1] / 2))
+        dc.DrawPolygon(points)
+        
+    def DrawPause(self, dc, size):
+        """!Draw pause symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, 2 * size[0] / 5, size[1])
+        dc.DrawRectangle(3 * size[0] / 5, 0, 2 * size[0] / 5, size[1])

+ 0 - 46
gui/wxpython/gui_modules/__init__.py

@@ -1,46 +0,0 @@
-all = [
-    "colorrules.py",
-    "dbm.py",
-    "dbm_base.py",
-    "dbm_dialogs.py",
-    "debug.py",
-    "disp_print.py",
-    "gcmd.py",
-    "gcpmanager.py",
-    "gcpmapdisp.py",
-    "gdialogs.py",
-    "georect.py",
-    "ghelp.py"
-    "globalvar.py",
-    "goutput.py",
-    "gselect.py",
-    "histogram.py",
-    "layertree.py",
-    "location_wizard.py",
-    "mapdisp_window.py",
-    "mapdisp.py",
-    "mapdisp_statusbar.py",
-    "mcalc_builder.py",
-    "menu.py",
-    "menudata.py",
-    "menuform.py",
-    "nviz_mapdisp.py",
-    "nviz_preferences.py",
-    "nviz_tools.py",
-    "ogc_services.py",
-    "preferences.py",
-    "render.py",
-    "rules.py",
-    "sqlbuilder.py",
-    "toolbars.py",
-    "units.py",
-    "utils.py",
-    "vclean.py",
-    "vdigit.py",
-    "workspace.py",
-    "wxnviz.py",
-    "wxplot.py",
-    "wxplot_dialogs.py",
-    "wxvdriver.py",
-    "wxvdigit.py",
-]

Різницю між файлами не показано, бо вона завелика
+ 0 - 5092
gui/wxpython/gui_modules/gmodeler.py


Різницю між файлами не показано, бо вона завелика
+ 0 - 1741
gui/wxpython/gui_modules/toolbars.py


Різницю між файлами не показано, бо вона завелика
+ 0 - 1317
gui/wxpython/gui_modules/wxplot.py


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

@@ -7,7 +7,7 @@ __author__ = "Robert Szczepanek"
 
 import os
 
-import globalvar
+from core import globalvar
 
 iconPath = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass")
 

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

@@ -26,7 +26,7 @@ sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython", "gu
 
 import wx
 
-from preferences import globalSettings as UserSettings
+from core.settings import UserSettings
 
 # default icon set
 import grass_icons

+ 69 - 83
gui/wxpython/gui_modules/layertree.py

@@ -1,57 +1,46 @@
 """!
-@package layertree.py
+@package lmgr.layertree
 
 @brief Utility classes for map layer management.
 
 Classes:
- - AbstractLayer
- - Layer
  - LayerTree
 
 (C) 2007-2011 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.
  
 @author Michael Barton (Arizona State University)
 @author Jachym Cepicky (Mendel University of Agriculture)
 @author Martin Landa <landa.martin gmail.com>
 """
 
-import os
-import sys
-import string
-
 import wx
 try:
     import wx.lib.agw.customtreectrl as CT
 except ImportError:
     import wx.lib.customtreectrl as CT
-import wx.combo
-import wx.lib.newevent
-import wx.lib.buttons  as  buttons
+import wx.lib.buttons  as buttons
 try:
     import treemixin 
 except ImportError:
     from wx.lib.mixins import treemixin
 
-import globalvar
-
 from grass.script import core as grass
 
-import gdialogs
-import menuform
-import toolbars
-import mapdisp
-import render
-import histogram
-import utils
-from wxplot      import ProfileFrame
-from debug       import Debug
-from icon        import Icons
-from preferences import globalSettings as UserSettings
-from vdigit      import haveVDigit
-from gcmd        import GWarning
+from core                import globalvar
+from gui_core.dialogs    import SqlQueryFrame, SetOpacityDialog
+from core.forms          import GUI
+from mapdisp.frame       import MapFrame
+from core.render         import Map
+from modules.histogram   import HistogramFrame
+from core.utils          import GetLayerNameFromCmd
+from wxplot.profile      import ProfileFrame
+from core.debug          import Debug
+from icon                import Icons
+from core.settings       import UserSettings
+from vdigit.main         import haveVDigit
+from core.gcmd           import GWarning
 
 TREE_ITEM_HEIGHT = 25
 
@@ -78,7 +67,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         showMapDisplay = kwargs['showMapDisplay']
         del kwargs['showMapDisplay']
         self.treepg = parent                 # notebook page holding layer tree
-        self.Map = render.Map()              # instance of render.Map to be associated with display
+        self.Map = Map()                     # instance of render.Map to be associated with display
         self.root = None                     # ID of layer tree root node
         self.groupnode = 0                   # index value for layers
         self.optpage = {}                    # dictionary of notebook option pages for each map layer
@@ -109,13 +98,12 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         
         # init associated map display
         pos = wx.Point((self.disp_idx + 1) * 25, (self.disp_idx + 1) * 25)
-        self.mapdisplay = mapdisp.MapFrame(self,
-                                           id = wx.ID_ANY, pos = pos,
-                                           size = globalvar.MAP_WINDOW_SIZE,
-                                           style = wx.DEFAULT_FRAME_STYLE,
-                                           tree = self, notebook = self.notebook,
-                                           lmgr = self.lmgr, page = self.treepg,
-                                           Map = self.Map, auimgr = self.auimgr)
+        self.mapdisplay = MapFrame(self, id = wx.ID_ANY, pos = pos,
+                                   size = globalvar.MAP_WINDOW_SIZE,
+                                   style = wx.DEFAULT_FRAME_STYLE,
+                                   tree = self, notebook = self.notebook,
+                                   lmgr = self.lmgr, page = self.treepg,
+                                   Map = self.Map, auimgr = self.auimgr)
         
         # title
         self.mapdisplay.SetTitle(_("GRASS GIS Map Display: %(id)d  - Location: %(loc)s") % \
@@ -440,7 +428,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
     def OnSqlQuery(self, event):
         """!Show SQL query window for PostGIS layers
         """
-        dlg = gdialogs.SqlQueryFrame(parent = self)
+        dlg = SqlQueryFrame(parent = self)
         dlg.CentreOnScreen()
         dlg.Show()
         
@@ -529,14 +517,14 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
     def OnRasterColorTable(self, event):
         """!Set color table for raster map"""
         name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
-        menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['r.colors',
-                                                                          'map=%s' % name])
+        GUI(parent = self, centreOnParent = False).ParseCommand(['r.colors',
+                                                                 'map=%s' % name])
 
     def OnVectorColorTable(self, event):
         """!Set color table for vector map"""
         name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
-        menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['v.colors',
-                                                                          'map=%s' % name])
+        GUI(parent = self, centreOnParent = False).ParseCommand(['v.colors',
+                                                                 'map=%s' % name])
         
     def OnHistogram(self, event):
         """
@@ -557,10 +545,8 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
             self.histogramFrame = self.mapdisplay.histogram
 
         if not self.histogramFrame:
-            self.histogramFrame = histogram.HistFrame(self,
-                                                      id = wx.ID_ANY,
-                                                      pos = wx.DefaultPosition, size = globalvar.HIST_WINDOW_SIZE,
-                                                      style = wx.DEFAULT_FRAME_STYLE)
+            self.histogramFrame = HistFrame(self, size = globalvar.HIST_WINDOW_SIZE,
+                                            style = wx.DEFAULT_FRAME_STYLE)
             self.histogramFrame.CentreOnScreen()
             # show new display
             self.histogramFrame.Show()
@@ -575,8 +561,8 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
     def OnUnivariateStats(self, event):
         """!Univariate raster statistics"""
         name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
-        menuform.GUI(parent = self).ParseCommand(['r.univar',
-                                                  'map=%s' % name])
+        GUI(parent = self).ParseCommand(['r.univar',
+                                         'map=%s' % name])
 
     def OnStartEditing(self, event):
         """!Start editing vector map layer requested by the user
@@ -657,8 +643,8 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
         current_opacity = maplayer.GetOpacity()
         
-        dlg = gdialogs.SetOpacityDialog(self, opacity = current_opacity,
-                                        title = _("Set opacity <%s>") % maplayer.GetName())
+        dlg = SetOpacityDialog(self, opacity = current_opacity,
+                               title = _("Set opacity <%s>") % maplayer.GetName())
         dlg.CentreOnParent()
 
         if dlg.ShowModal() == wx.ID_OK:
@@ -855,7 +841,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
             if lcmd and len(lcmd) > 1:
                 cmd = lcmd
                 render = False
-                name, found = utils.GetLayerNameFromCmd(lcmd)
+                name, found = GetLayerNameFromCmd(lcmd)
             else:
                 cmd = []
                 if ltype == 'command' and lname:
@@ -960,7 +946,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
                    ltype)
         
         if self.GetPyData(layer)[0]['cmd']:
-            module = menuform.GUI(parent = self, show = show, centreOnParent = False)
+            module = GUI(parent = self, show = show, centreOnParent = False)
             module.ParseCommand(self.GetPyData(layer)[0]['cmd'],
                                 completed = (self.GetOptData,layer,params))
             
@@ -970,33 +956,33 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
             
             if UserSettings.Get(group = 'cmd', key = 'rasterOpaque', subkey = 'enabled'):
                 cmd.append('-n')
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
+                                                                    completed = (self.GetOptData,layer,params))
                          
         elif ltype == '3d-raster':
             cmd = ['d.rast3d']
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
+                                                                    completed = (self.GetOptData,layer,params))
                                         
         elif ltype == 'rgb':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rgb'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.rgb'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'his':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.his'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.his'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'shaded':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.shadedmap'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.shadedmap'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'rastarrow':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.arrow'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.arrow'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'rastnum':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.num'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.rast.num'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'vector':
             types = list()
@@ -1004,34 +990,34 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
                 if UserSettings.Get(group = 'cmd', key = 'showType', subkey = [ftype, 'enabled']):
                     types.append(ftype)
             
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect', 'type=%s' % ','.join(types)],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect', 'type=%s' % ','.join(types)],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'thememap':
             # -s flag requested, otherwise only first thematic category is displayed
             # should be fixed by C-based d.thematic.* modules
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.thematic.area'], 
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.thematic.area'], 
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'themechart':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect.chart'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.vect.chart'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'grid':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.grid'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.grid'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'geodesic':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.geodesic'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.geodesic'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'rhumb':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.rhumbline'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.rhumbline'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'labels':
-            menuform.GUI(parent = self, centreOnParent = False).ParseCommand(['d.labels'],
-                                                                             completed = (self.GetOptData,layer,params))
+            GUI(parent = self, centreOnParent = False).ParseCommand(['d.labels'],
+                                                                    completed = (self.GetOptData,layer,params))
             
         elif ltype == 'cmdlayer':
             pass
@@ -1425,8 +1411,8 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         opacity  = int(mapLayer.GetOpacity(float = True) * 100)
         if not lname:
             dcmd    = self.GetPyData(item)[0]['cmd']
-            lname, found = utils.GetLayerNameFromCmd(dcmd, layerType = mapLayer.GetType(),
-                                                     fullyQualified = True)
+            lname, found = GetLayerNameFromCmd(dcmd, layerType = mapLayer.GetType(),
+                                               fullyQualified = True)
             if not found:
                 return None
         
@@ -1442,7 +1428,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
         if dcmd:
             self.GetPyData(layer)[0]['cmd'] = dcmd
             mapText  = self._getLayerName(layer)
-            mapName, found = utils.GetLayerNameFromCmd(dcmd)
+            mapName, found = GetLayerNameFromCmd(dcmd)
             mapLayer = self.GetPyData(layer)[0]['maplayer']
             self.SetItemText(layer, mapName)
             
@@ -1553,7 +1539,7 @@ class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
                 chk = self.IsItemChecked(item)
                 hidden = not self.IsVisible(item)
                 # determine layer name
-                layerName, found = utils.GetLayerNameFromCmd(cmdlist, fullyQualified = True)
+                layerName, found = GetLayerNameFromCmd(cmdlist, fullyQualified = True)
                 if not found:
                     layerName = self.GetItemText(item)
         

+ 70 - 0
gui/wxpython/lmgr/menudata.py

@@ -0,0 +1,70 @@
+"""!
+@package lmrg.menudata
+
+@brief Complex list for menu entries for wxGUI
+
+Classes:
+ - MenuData
+
+Usage:
+@code
+python menudata.py [action] [manager|modeler]
+@endcode
+
+where <i>action</i>:
+ - strings (default)
+ - tree
+ - commands
+ - dump
+
+(C) 2007-2011 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.
+
+@author Michael Barton (Arizona State University)
+@author Yann Chemin <yann.chemin gmail.com>
+@author Martin Landa <landa.martin gmail.com>
+@author Glynn Clements
+@author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+from core.globalvar import ETCWXDIR
+from core.menudata  import MenuData
+
+class ManagerData(MenuData):
+    def __init__(self, filename = None):
+        if not filename:
+            gisbase = os.getenv('GISBASE')
+            global etcwxdir
+	    filename = os.path.join(ETCWXDIR, 'xml', 'menudata.xml')
+        
+        MenuData.__init__(self, filename)
+        
+    def GetModules(self):
+        """!Create dictionary of modules used to search module by
+        keywords, description, etc."""
+        modules = dict()
+        
+        for node in self.tree.getiterator():
+            if node.tag == 'menuitem':
+                module = description = ''
+                keywords = []
+                for child in node.getchildren():
+                    if child.tag == 'help':
+                        description = child.text
+                    if child.tag == 'command':
+                        module = child.text
+                    if child.tag == 'keywords':
+                        if child.text:
+                            keywords = child.text.split(',')
+                    
+                if module:
+                    modules[module] = { 'desc': description,
+                                        'keywords' : keywords }
+                    if len(keywords) < 1:
+                        print >> sys.stderr, "WARNING: Module <%s> has no keywords" % module
+                
+        return modules

+ 5 - 7
gui/wxpython/gui_modules/gpyshell.py

@@ -1,7 +1,7 @@
 """!
-@package gpyshell.py
+@package lmgr.pyshell
 
-@brief wxGUI Interactive Python Shell
+@brief wxGUI Interactive Python Shell for Layer Manager
 
 Classes:
  - PyShellWindow
@@ -10,18 +10,16 @@ Classes:
 & design the widget communicate back and forth with it
 
 (C) 2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """
 
-import os
 import sys
 
 import wx
-from wx.py.shell import Shell as PyShell
+from wx.py.shell   import Shell as PyShell
 from wx.py.version import VERSION
 
 import grass.script as grass

+ 210 - 0
gui/wxpython/lmgr/toolbars.py

@@ -0,0 +1,210 @@
+"""!
+@package lmgr.toolbars
+
+@brief wxGUI Layer Manager - toolbars
+
+Classes:
+ - LMWorkspaceToolbar
+ - LMDataToolbar
+ - LMToolsToolbar
+ - LMMiscToolbar
+ - LMVectorToolbar
+ - LMNvizToolbar
+
+(C) 2007-2011 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.
+
+@author Michael Barton
+@author Jachym Cepicky
+@author Martin Landa <landa.martin gmail.com>
+@author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+from core               import globalvar
+from core.gcmd          import RunCommand
+from nviz.preferences   import NvizPreferencesDialog
+from gui_core.toolbars  import BaseToolbar
+
+sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+from icons.icon        import Icons
+
+class LMWorkspaceToolbar(BaseToolbar):
+    """!Layer Manager `workspace` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['layerManager']
+        return self._getToolbarData((('newdisplay', icons["newdisplay"],
+                                      self.parent.OnNewDisplay),
+                                     (None, ),
+                                     ('workspaceNew', icons["workspaceNew"],
+                                      self.parent.OnWorkspaceNew),
+                                     ('workspaceOpen', icons["workspaceOpen"],
+                                      self.parent.OnWorkspaceOpen),
+                                     ('workspaceSave', icons["workspaceSave"],
+                                      self.parent.OnWorkspaceSave),
+                                     ))
+
+class LMDataToolbar(BaseToolbar):
+    """!Layer Manager `data` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['layerManager']
+        return self._getToolbarData((('addMulti', icons["addMulti"],
+                                      self.parent.OnAddMaps),
+                                     ('addrast', icons["addRast"],
+                                      self.parent.OnAddRaster),
+                                     ('rastmisc', icons["rastMisc"],
+                                      self.parent.OnAddRasterMisc),
+                                     ('addvect', icons["addVect"],
+                                      self.parent.OnAddVector),
+                                     ('vectmisc', icons["vectMisc"],
+                                      self.parent.OnAddVectorMisc),
+                                     ('addgrp',  icons["addGroup"],
+                                      self.parent.OnAddGroup),
+                                     ('addovl',  icons["addOverlay"],
+                                      self.parent.OnAddOverlay),
+                                     ('delcmd',  icons["delCmd"],
+                                      self.parent.OnDeleteLayer),
+                                     ))
+
+class LMToolsToolbar(BaseToolbar):
+    """!Layer Manager `tools` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['layerManager']
+        return self._getToolbarData((('importMap', icons["import"],
+                                      self.parent.OnImportMenu),
+                                     (None, ),
+                                     ('mapCalc', icons["mapcalc"],
+                                      self.parent.OnMapCalculator),
+                                     ('georect', Icons["georectify"]["georectify"],
+                                      self.parent.OnGCPManager),
+                                     ('modeler', icons["modeler"],
+                                      self.parent.OnGModeler),
+                                     ('mapOutput', icons['mapOutput'],
+                                      self.parent.OnPsMap)
+                                     ))
+
+class LMMiscToolbar(BaseToolbar):
+    """!Layer Manager `misc` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['layerManager']
+        return self._getToolbarData((('settings', icons["settings"],
+                                      self.parent.OnPreferences),
+                                     ('help', Icons["misc"]["help"],
+                                      self.parent.OnHelp),
+                                     ))
+
+class LMVectorToolbar(BaseToolbar):
+    """!Layer Manager `vector` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['layerManager']
+        return self._getToolbarData((('vdigit', icons["vdigit"],
+                                      self.parent.OnVDigit),
+                                     ('attribute', icons["attrTable"],
+                                      self.parent.OnShowAttributeTable),
+                                     ))
+
+class LMNvizToolbar(BaseToolbar):
+    """!Nviz toolbar
+    """
+    def __init__(self, parent):
+        self.lmgr = parent
+        
+        BaseToolbar.__init__(self, parent)
+        
+        # only one dialog can be open
+        self.settingsDialog   = None
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['nviz']
+        return self._getToolbarData((("nvizCmd", icons['nvizCmd'],
+                                      self.OnNvizCmd),
+                                     (None, ),
+                                     ("settings", icons["settings"],
+                                      self.OnSettings),
+                                     ("help", icons["help"],
+                                      self.OnHelp))
+                                    )
+        
+    def OnNvizCmd(self, event):
+        """!Show m.nviz.image command"""
+        self.lmgr.GetLayerTree().GetMapDisplay().GetWindow().OnNvizCmd()
+        
+    def OnHelp(self, event):
+        """!Show 3D view mode help"""
+        if not self.lmgr:
+            RunCommand('g.manual',
+                       entry = 'wxGUI.Nviz')
+        else:
+            log = self.lmgr.GetLogWindow()
+            log.RunCmd(['g.manual',
+                        'entry=wxGUI.Nviz'])
+        
+    def OnSettings(self, event):
+        """!Show nviz notebook page"""
+        if not self.settingsDialog:
+            self.settingsDialog = NvizPreferencesDialog(parent = self.parent)
+        self.settingsDialog.Show()

+ 45 - 0
gui/wxpython/location_wizard/base.py

@@ -0,0 +1,45 @@
+"""!
+@package location_wizard.base
+
+@brief Location wizard - base classes
+
+Classes:
+ - BaseClass
+
+(C) 2007-2011 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.
+
+@author Michael Barton
+@author Jachym Cepicky
+@author Martin Landa <landa.martin gmail.com>   
+"""
+
+import wx
+
+class BaseClass(wx.Object):
+    """!Base class providing basic methods"""
+    def __init__(self):
+        pass
+
+    def MakeLabel(self, text = "", style = wx.ALIGN_LEFT, parent = None):
+        """!Make aligned label"""
+        if not parent:
+            parent = self
+        return wx.StaticText(parent = parent, id = wx.ID_ANY, label = text,
+                             style = style)
+
+    def MakeTextCtrl(self, text = '', size = (100,-1), style = 0, parent = None):
+        """!Generic text control"""
+        if not parent:
+            parent = self
+        return wx.TextCtrl(parent = parent, id = wx.ID_ANY, value = text,
+                           size = size, style = style)
+
+    def MakeButton(self, text, id = wx.ID_ANY, size = (-1,-1), parent = None):
+        """!Generic button"""
+        if not parent:
+            parent = self
+        return wx.Button(parent = parent, id = id, label = text,
+                         size = size)

+ 612 - 0
gui/wxpython/location_wizard/dialogs.py

@@ -0,0 +1,612 @@
+"""!
+@package location_wizard.dialogs
+
+@brief Location wizard - dialogs
+
+Classes:
+ - RegionDef
+ - TransList
+ - SelectTransformDialog
+
+(C) 2007-2011 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.
+
+@author Michael Barton
+@author Jachym Cepicky
+@author Martin Landa <landa.martin gmail.com>   
+"""
+import os
+import sys
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+from core                 import globalvar
+from core.gcmd            import RunCommand
+from location_wizard.base import BaseClass
+
+from grass.script import core as grass
+
+class RegionDef(BaseClass, wx.Frame):
+    """!Page for setting default region extents and resolution
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Set default region extent and resolution"), location = None):
+        wx.Frame.__init__(self, parent, id, title, size = (650,300))
+        panel = wx.Panel(self, id = wx.ID_ANY)
+        
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.parent = parent
+        self.location = location
+        
+        #
+        # default values
+        #
+        # 2D
+        self.north = 1.0
+        self.south = 0.0
+        self.east = 1.0
+        self.west = 0.0
+        self.nsres = 1.0
+        self.ewres = 1.0
+        # 3D
+        self.top = 1.0
+        self.bottom = 0.0
+        #         self.nsres3 = 1.0
+        #         self.ewres3 = 1.0
+        self.tbres  = 1.0
+        
+        #
+        # inputs
+        #
+        # 2D
+        self.tnorth = self.MakeTextCtrl(text = str(self.north), size = (150, -1), parent = panel)
+        self.tsouth = self.MakeTextCtrl(str(self.south), size = (150, -1), parent = panel)
+        self.twest = self.MakeTextCtrl(str(self.west), size = (150, -1), parent = panel)
+        self.teast = self.MakeTextCtrl(str(self.east), size = (150, -1), parent = panel)
+        self.tnsres = self.MakeTextCtrl(str(self.nsres), size = (150, -1), parent = panel)
+        self.tewres = self.MakeTextCtrl(str(self.ewres), size = (150, -1), parent = panel)
+        
+        #
+        # labels
+        #
+        self.lrows  = self.MakeLabel(parent = panel)
+        self.lcols  = self.MakeLabel(parent = panel)
+        self.lcells = self.MakeLabel(parent = panel)
+        
+        #
+        # buttons
+        #
+        self.bset = self.MakeButton(text = _("&Set region"), id = wx.ID_OK, parent = panel)
+        self.bcancel = wx.Button(panel, id = wx.ID_CANCEL)
+        self.bset.SetDefault()
+        
+        #
+        # image
+        #
+        self.img = wx.Image(os.path.join(globalvar.ETCIMGDIR, "qgis_world.png"),
+                            wx.BITMAP_TYPE_PNG).ConvertToBitmap()
+        
+        #
+        # set current working environment to PERMANENT mapset
+        # in selected location in order to set default region (WIND)
+        #
+        envval = {}
+        ret = RunCommand('g.gisenv',
+                         read = True)
+        if ret:
+            for line in ret.splitlines():
+                key, val = line.split('=')
+                envval[key] = val
+            self.currlocation = envval['LOCATION_NAME'].strip("';")
+            self.currmapset = envval['MAPSET'].strip("';")
+            if self.currlocation != self.location or self.currmapset != 'PERMANENT':
+                RunCommand('g.gisenv',
+                           set = 'LOCATION_NAME=%s' % self.location)
+                RunCommand('g.gisenv',
+                           set = 'MAPSET=PERMANENT')
+        else:
+            dlg = wx.MessageBox(parent = self,
+                                message = _('Invalid location selected.'),
+                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
+            return
+        
+        #
+        # get current region settings
+        #
+        region = {}
+        ret = RunCommand('g.region',
+                         read = True,
+                         flags = 'gp3')
+        if ret:
+            for line in ret.splitlines():
+                key, val = line.split('=')
+                region[key] = float(val)
+        else:
+            dlg = wx.MessageBox(parent = self,
+                                message = _("Invalid region"),
+                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+        
+        #
+        # update values
+        # 2D
+        self.north = float(region['n'])
+        self.south = float(region['s'])
+        self.east = float(region['e'])
+        self.west = float(region['w'])
+        self.nsres = float(region['nsres'])
+        self.ewres = float(region['ewres'])
+        self.rows = int(region['rows'])
+        self.cols = int(region['cols'])
+        self.cells = int(region['cells'])
+        # 3D
+        self.top = float(region['t'])
+        self.bottom = float(region['b'])
+        #         self.nsres3 = float(region['nsres3'])
+        #         self.ewres3 = float(region['ewres3'])
+        self.tbres = float(region['tbres'])
+        self.depth = int(region['depths'])
+        self.cells3 = int(region['cells3'])
+        
+        #
+        # 3D box collapsable
+        #
+        self.infoCollapseLabelExp = _("Click here to show 3D settings")
+        self.infoCollapseLabelCol = _("Click here to hide 3D settings")
+        self.settings3D = wx.CollapsiblePane(parent = panel,
+                                             label = self.infoCollapseLabelExp,
+                                             style = wx.CP_DEFAULT_STYLE |
+                                             wx.CP_NO_TLW_RESIZE | wx.EXPAND)
+        self.MakeSettings3DPaneContent(self.settings3D.GetPane())
+        self.settings3D.Collapse(False) # FIXME
+        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSettings3DPaneChanged, self.settings3D)
+        
+        #
+        # set current region settings
+        #
+        self.tnorth.SetValue(str(self.north))
+        self.tsouth.SetValue(str(self.south))
+        self.twest.SetValue(str(self.west))
+        self.teast.SetValue(str(self.east))
+        self.tnsres.SetValue(str(self.nsres))
+        self.tewres.SetValue(str(self.ewres))
+        self.ttop.SetValue(str(self.top))
+        self.tbottom.SetValue(str(self.bottom))
+        #         self.tnsres3.SetValue(str(self.nsres3))
+        #         self.tewres3.SetValue(str(self.ewres3))
+        self.ttbres.SetValue(str(self.tbres))
+        self.lrows.SetLabel(_("Rows: %d") % self.rows)
+        self.lcols.SetLabel(_("Cols: %d") % self.cols)
+        self.lcells.SetLabel(_("Cells: %d") % self.cells)
+        
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_BUTTON, self.OnSetButton, self.bset)
+        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.bcancel)
+        self.tnorth.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.tsouth.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.teast.Bind(wx.EVT_TEXT,    self.OnValue)
+        self.twest.Bind(wx.EVT_TEXT,    self.OnValue)
+        self.tnsres.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.tewres.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.ttop.Bind(wx.EVT_TEXT,     self.OnValue)
+        self.tbottom.Bind(wx.EVT_TEXT,  self.OnValue)
+        #         self.tnsres3.Bind(wx.EVT_TEXT,  self.OnValue)
+        #         self.tewres3.Bind(wx.EVT_TEXT,  self.OnValue)
+        self.ttbres.Bind(wx.EVT_TEXT,   self.OnValue)
+        
+        self.__DoLayout(panel)
+        self.SetMinSize(self.GetBestSize())
+        self.minWindowSize = self.GetMinSize()
+    
+    def MakeSettings3DPaneContent(self, pane):
+        """!Create 3D region settings pane"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
+
+        # inputs
+        self.ttop = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.top),
+                                size = (150, -1))
+        self.tbottom = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.bottom),
+                                size = (150, -1))
+        self.ttbres = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.tbres),
+                                size = (150, -1))
+        #         self.tnsres3 = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.nsres3),
+        #                                    size = (150, -1))
+        #         self.tewres3  =  wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.ewres3),
+        #                                    size = (150, -1))
+
+        #labels
+        self.ldepth = wx.StaticText(parent = pane, label = _("Depth: %d") % self.depth)
+        self.lcells3  =  wx.StaticText(parent = pane, label = _("3D Cells: %d") % self.cells3)
+
+        # top
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Top")),
+                      flag = wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 1))
+        gridSizer.Add(item = self.ttop,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 1))
+        # bottom
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Bottom")),
+                      flag = wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 2))
+        gridSizer.Add(item = self.tbottom,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 2))
+        # tbres
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("T-B resolution")),
+                      flag = wx.ALIGN_CENTER | 
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 3))
+        gridSizer.Add(item = self.ttbres,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 3))
+
+        # res
+        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D N-S resolution")),
+        #                       flag = wx.ALIGN_CENTER |
+        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+        #                       pos = (2, 1))
+        #         gridSizer.Add(item = self.tnsres3,
+        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
+        #                       wx.ALL, border = 5, pos = (3, 1))
+        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D E-W resolution")),
+        #                       flag = wx.ALIGN_CENTER |
+        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+        #                       pos = (2, 3))
+        #         gridSizer.Add(item = self.tewres3,
+        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
+        #                       wx.ALL, border = 5, pos = (3, 3))
+
+        # rows/cols/cells
+        gridSizer.Add(item = self.ldepth,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (2, 1))
+
+        gridSizer.Add(item = self.lcells3,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (2, 2))
+
+        border.Add(item = gridSizer, proportion = 1,
+                   flag = wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, border = 5)
+
+        pane.SetSizer(border)
+        border.Fit(pane)
+
+    def OnSettings3DPaneChanged(self, event):
+        """!Collapse 3D settings box"""
+
+        if self.settings3D.IsExpanded():
+            self.settings3D.SetLabel(self.infoCollapseLabelCol)
+            self.Layout()
+            self.SetSize(self.GetBestSize())
+            self.SetMinSize(self.GetSize())
+        else:
+            self.settings3D.SetLabel(self.infoCollapseLabelExp)
+            self.Layout()
+            self.SetSize(self.minWindowSize)
+            self.SetMinSize(self.minWindowSize)
+
+        self.SendSizeEvent()
+
+    def __DoLayout(self, panel):
+        """!Window layout"""
+        frameSizer = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
+        settings3DSizer = wx.BoxSizer(wx.VERTICAL)
+        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        # north
+        gridSizer.Add(item = self.MakeLabel(text = _("North"), parent = panel),
+                      flag = wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (0, 2))
+        gridSizer.Add(item = self.tnorth,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (1, 2))
+        # west
+        gridSizer.Add(item = self.MakeLabel(text = _("West"), parent = panel),
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.LEFT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 0))
+        gridSizer.Add(item = self.twest,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (2, 1))
+
+        gridSizer.Add(item = wx.StaticBitmap(panel, wx.ID_ANY, self.img, (-1, -1),
+                                           (self.img.GetWidth(), self.img.GetHeight())),
+                      flag = wx.ALIGN_CENTER |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (2, 2))
+
+        # east
+        gridSizer.Add(item = self.teast,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (2, 3))
+        gridSizer.Add(item = self.MakeLabel(text = _("East"), parent = panel),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.RIGHT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 4))
+        # south
+        gridSizer.Add(item = self.tsouth,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (3, 2))
+        gridSizer.Add(item = self.MakeLabel(text = _("South"), parent = panel),
+                      flag = wx.ALIGN_TOP | wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5, pos = (4, 2))
+        # ns-res
+        gridSizer.Add(item = self.MakeLabel(text = _("N-S resolution"), parent = panel),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 1))
+        gridSizer.Add(item = self.tnsres,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (6, 1))
+        # ew-res
+        gridSizer.Add(item = self.MakeLabel(text = _("E-W resolution"), parent = panel),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 3))
+        gridSizer.Add(item = self.tewres,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (6, 3))
+        # rows/cols/cells
+        gridSizer.Add(item = self.lrows,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 1))
+
+        gridSizer.Add(item = self.lcells,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 2))
+
+        gridSizer.Add(item = self.lcols,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 3))
+
+        # 3D
+        settings3DSizer.Add(item = self.settings3D,
+                            flag = wx.ALL,
+                            border = 5)
+
+        # buttons
+        buttonSizer.Add(item = self.bcancel, proportion = 1,
+                        flag = wx.ALIGN_RIGHT |
+                        wx.ALIGN_CENTER_VERTICAL |
+                        wx.ALL, border = 10)
+        buttonSizer.Add(item = self.bset, proportion = 1,
+                        flag = wx.ALIGN_CENTER |
+                        wx.ALIGN_CENTER_VERTICAL |
+                        wx.ALL, border = 10)
+
+        frameSizer.Add(item = gridSizer, proportion = 1,
+                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        frameSizer.Add(item = settings3DSizer, proportion = 0,
+                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        frameSizer.Add(item = buttonSizer, proportion = 0,
+                       flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+        self.SetAutoLayout(True)
+        panel.SetSizer(frameSizer)
+        frameSizer.Fit(panel)
+        self.Layout()
+
+    def OnValue(self, event):
+        """!Set given value"""
+        try:
+            if event.GetId() == self.tnorth.GetId():
+                self.north = float(event.GetString())
+            elif event.GetId() == self.tsouth.GetId():
+                self.south = float(event.GetString())
+            elif event.GetId() == self.teast.GetId():
+                self.east = float(event.GetString())
+            elif event.GetId() == self.twest.GetId():
+                self.west = float(event.GetString())
+            elif event.GetId() == self.tnsres.GetId():
+                self.nsres = float(event.GetString())
+            elif event.GetId() == self.tewres.GetId():
+                self.ewres = float(event.GetString())
+            elif event.GetId() == self.ttop.GetId():
+                self.top = float(event.GetString())
+            elif event.GetId() == self.tbottom.GetId():
+                self.bottom = float(event.GetString())
+            #             elif event.GetId() == self.tnsres3.GetId():
+            #                 self.nsres3 = float(event.GetString())
+            #             elif event.GetId() == self.tewres3.GetId():
+            #                 self.ewres3 = float(event.GetString())
+            elif event.GetId() == self.ttbres.GetId():
+                self.tbres = float(event.GetString())
+
+            self.__UpdateInfo()
+
+        except ValueError, e:
+            if len(event.GetString()) > 0 and event.GetString() != '-':
+                dlg = wx.MessageBox(parent = self,
+                                    message = _("Invalid value: %s") % e,
+                                    caption = _("Error"),
+                                    style = wx.OK | wx.ICON_ERROR)
+                # reset values
+                self.tnorth.SetValue(str(self.north))
+                self.tsouth.SetValue(str(self.south))
+                self.teast.SetValue(str(self.east))
+                self.twest.SetValue(str(self.west))
+                self.tnsres.SetValue(str(self.nsres))
+                self.tewres.SetValue(str(self.ewres))
+                self.ttop.SetValue(str(self.top))
+                self.tbottom.SetValue(str(self.bottom))
+                self.ttbres.SetValue(str(self.tbres))
+                # self.tnsres3.SetValue(str(self.nsres3))
+                # self.tewres3.SetValue(str(self.ewres3))
+
+        event.Skip()
+
+    def __UpdateInfo(self):
+        """!Update number of rows/cols/cells"""
+        self.rows = int((self.north - self.south) / self.nsres)
+        self.cols = int((self.east - self.west) / self.ewres)
+        self.cells = self.rows * self.cols
+
+        self.depth = int((self.top - self.bottom) / self.tbres)
+        self.cells3 = self.rows * self.cols * self.depth
+
+        # 2D
+        self.lrows.SetLabel(_("Rows: %d") % self.rows)
+        self.lcols.SetLabel(_("Cols: %d") % self.cols)
+        self.lcells.SetLabel(_("Cells: %d") % self.cells)
+        # 3D
+        self.ldepth.SetLabel(_("Depth: %d" % self.depth))
+        self.lcells3.SetLabel(_("3D Cells: %d" % self.cells3))
+
+    def OnSetButton(self, event = None):
+        """!Set default region"""
+        ret = RunCommand('g.region',
+                         flags = 'sgpa',
+                         n = self.north,
+                         s = self.south,
+                         e = self.east,
+                         w = self.west,
+                         nsres = self.nsres,
+                         ewres = self.ewres,
+                         t = self.top,
+                         b = self.bottom,
+                         tbres = self.tbres)
+        if ret == 0:
+            self.Destroy()
+
+    def OnCancel(self, event):
+        self.Destroy()
+
+class TransList(wx.VListBox):
+    """!Creates a multiline listbox for selecting datum transforms"""
+        
+    def OnDrawItem(self, dc, rect, n):
+        if self.GetSelection() == n:
+            c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
+        else:
+            c = self.GetForegroundColour()
+        dc.SetFont(self.GetFont())
+        dc.SetTextForeground(c)
+        dc.DrawLabel(self._getItemText(n), rect,
+                     wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
+
+    def OnMeasureItem(self, n):
+        height = 0
+        if self._getItemText(n) == None:
+            return
+        for line in self._getItemText(n).splitlines():
+            w, h = self.GetTextExtent(line)
+            height += h
+        return height + 5
+
+    def _getItemText(self, item):
+        global transformlist
+        transitem = transformlist[item]
+        if transitem.strip() !='':
+            return transitem
+
+class SelectTransformDialog(wx.Dialog):
+    """!Dialog for selecting datum transformations"""
+    def __init__(self, parent, transforms, title = _("Select datum transformation"),
+                 pos = wx.DefaultPosition, size = wx.DefaultSize, 
+                 style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
+
+        wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+
+        global transformlist
+        self.CentreOnParent()
+        
+        # default transform number
+        self.transnum = 0
+        
+        panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # set panel sizer
+        #
+        panel.SetSizer(sizer)
+        panel.SetupScrolling()
+
+        #
+        # dialog body
+        #
+        bodyBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                               label = " %s " % _("Select from list of datum transformations"))
+        bodySizer = wx.StaticBoxSizer(bodyBox)       
+        
+        # add no transform option
+        transforms = '---\n\n0\nDo not apply any datum transformations\n\n' + transforms
+        
+        transformlist = transforms.split('---')
+        tlistlen = len(transformlist)
+        
+        # calculate size for transform list
+        height = 0
+        width = 0
+        for line in transforms.splitlines():
+            w, h = self.GetTextExtent(line)
+            height += h
+            width = max(width, w)
+            
+        height = height + 5
+        if height > 400: height = 400
+        width = width + 5
+        if width > 400: width = 400
+
+        #
+        # VListBox for displaying and selecting transformations
+        #
+        self.translist = TransList(panel, id = -1, size = (width, height), style = wx.SUNKEN_BORDER)
+        self.translist.SetItemCount(tlistlen)
+        self.translist.SetSelection(2)
+        self.translist.SetFocus()
+        
+        self.Bind(wx.EVT_LISTBOX, self.ClickTrans, self.translist)
+
+        bodySizer.Add(item = self.translist, proportion = 1, flag = wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
+
+        #
+        # buttons
+        #
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btn = wx.Button(parent = panel, id = wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(parent = panel, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btn)
+        btnsizer.Realize()
+
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag =  wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+        sizer.Fit(panel)
+
+        self.SetSize(self.GetBestSize())
+        self.Layout()
+        
+    def ClickTrans(self, event):
+        """!Get the number of the datum transform to use in g.proj"""
+        self.transnum = event.GetSelection()
+        self.transnum = self.transnum - 1
+    
+    def GetTransform(self):
+        """!Get the number of the datum transform to use in g.proj"""
+        self.transnum = self.translist.GetSelection()
+        self.transnum = self.transnum - 1
+        return self.transnum

+ 59 - 680
gui/wxpython/gui_modules/location_wizard.py

@@ -1,11 +1,10 @@
 """!
-@package location_wizard.py
+@package location_wizard.wizard
 
 @brief Location wizard - creates a new GRASS Location. User can choose
 from multiple methods.
 
 Classes:
- - BaseClass
  - TitledPage
  - DatabasePage
  - CoordinateSystemPage
@@ -18,9 +17,7 @@ Classes:
  - EPSGPage
  - CustomPage
  - SummaryPage
- - RegionDef
  - LocationWizard
- - SelectTransformDialog
 
 (C) 2007-2011 by the GRASS Development Team
 
@@ -32,28 +29,20 @@ This program is free software under the GNU General Public License
 @author Martin Landa <landa.martin gmail.com>   
 """
 import os
-import shutil
-import string
-import sys
 import locale
-import platform
 
 import wx
 import wx.lib.mixins.listctrl as listmix
 import wx.wizard as wiz
 import wx.lib.scrolledpanel as scrolled
-import time
 
-import gcmd
-import globalvar
-import utils
+from core                    import globalvar
+from core                    import utils
+from core.gcmd               import RunCommand, GError, GMessage, GWarning
+from location_wizard.base    import BaseClass
+from location_wizard.dialogs import RegionDef, SelectTransformDialog
+
 from grass.script import core as grass
-try:
-    import subprocess
-except:
-    CompatPath = os.path.join(globalvar.ETCWXDIR)
-    sys.path.append(CompatPath)
-    from compat import subprocess
 
 global coordsys
 global north
@@ -64,32 +53,6 @@ global resolution
 global wizerror
 global translist
 
-class BaseClass(wx.Object):
-    """!Base class providing basic methods"""
-    def __init__(self):
-        pass
-
-    def MakeLabel(self, text = "", style = wx.ALIGN_LEFT, parent = None):
-        """!Make aligned label"""
-        if not parent:
-            parent = self
-        return wx.StaticText(parent = parent, id = wx.ID_ANY, label = text,
-                             style = style)
-
-    def MakeTextCtrl(self, text = '', size = (100,-1), style = 0, parent = None):
-        """!Generic text control"""
-        if not parent:
-            parent = self
-        return wx.TextCtrl(parent = parent, id = wx.ID_ANY, value = text,
-                           size = size, style = style)
-
-    def MakeButton(self, text, id = wx.ID_ANY, size = (-1,-1), parent = None):
-        """!Generic button"""
-        if not parent:
-            parent = self
-        return wx.Button(parent = parent, id = id, label = text,
-                         size = size)
-
 class TitledPage(BaseClass, wiz.WizardPageSimple):
     """!Class to make wizard pages. Generic methods to make labels,
     text entries, and buttons.
@@ -211,11 +174,11 @@ class DatabasePage(TitledPage):
             error = _("Location already exists in GRASS Database.")
 
         if error:
-            gcmd.GError(parent = self,
-                        message="%s <%s>.%s%s" % (_("Unable to create location"),
-                                                  str(self.tlocation.GetValue()),
-                                                  os.linesep,
-                                                  error))
+            GError(parent = self,
+                   message="%s <%s>.%s%s" % (_("Unable to create location"),
+                                             str(self.tlocation.GetValue()),
+                                             os.linesep,
+                                             error))
             event.Veto()
             return
 
@@ -224,9 +187,9 @@ class DatabasePage(TitledPage):
         self.locTitle      = self.tlocTitle.GetValue()
         if os.linesep in self.locTitle or \
                 len(self.locTitle) > 255:
-            gcmd.GWarning(parent = self,
-                          message = _("Title of the location is limited only to one line and "
-                                      "256 characters. The rest of the text will be ignored."))
+            GWarning(parent = self,
+                     message = _("Title of the location is limited only to one line and "
+                                 "256 characters. The rest of the text will be ignored."))
             self.locTitle = self.locTitle.split(os.linesep)[0][:255]
             
 class CoordinateSystemPage(TitledPage):
@@ -921,10 +884,10 @@ class DatumPage(TitledPage):
             else:
                 # check for datum tranforms            
 #                proj4string = self.parent.CreateProj4String() + ' +datum=%s' % self.datum
-                ret = gcmd.RunCommand('g.proj',
-                                      read = True,
-                                      proj4 = '%s +datum=%s' % (proj, self.datum), 
-                                      datumtrans = '-1')
+                ret = RunCommand('g.proj',
+                                 read = True,
+                                 proj4 = '%s +datum=%s' % (proj, self.datum), 
+                                 datumtrans = '-1')
                 if ret != '':
                     dtrans = ''
                     # open a dialog to select datum transform number
@@ -1364,10 +1327,10 @@ class EPSGPage(TitledPage):
                 return
             else:              
                 # check for datum transforms
-                ret = gcmd.RunCommand('g.proj',
-                                      read = True,
-                                      epsg = self.epsgcode,
-                                      datumtrans = '-1')
+                ret = RunCommand('g.proj',
+                                 read = True,
+                                 epsg = self.epsgcode,
+                                 datumtrans = '-1')
                 
                 if ret != '':
                     dtrans = ''
@@ -1516,10 +1479,10 @@ class CustomPage(TitledPage):
     def OnPageChanging(self, event):
         if event.GetDirection():
         # check for datum tranforms            
-            ret, out, err = gcmd.RunCommand('g.proj',
-                                            read = True, getErrorMsg = True,
-                                            proj4 = self.customstring, 
-                                            datumtrans = '-1')
+            ret, out, err = RunCommand('g.proj',
+                                       read = True, getErrorMsg = True,
+                                       proj4 = self.customstring, 
+                                       datumtrans = '-1')
             if ret != 0:
                 wx.MessageBox(parent = self,
                               message = err,
@@ -1653,28 +1616,28 @@ class SummaryPage(TitledPage):
         global coordsys
         if coordsys in ('proj', 'epsg'):
             if coordsys == 'proj':
-                ret, projlabel, err = gcmd.RunCommand('g.proj',
-                                                      flags = 'jf',
-                                                      proj4 = proj4string,
-                                                      datumtrans = dtrans,
-                                                      location = location,
-                                                      getErrorMsg = True,
-                                                      read = True)
+                ret, projlabel, err = RunCommand('g.proj',
+                                                 flags = 'jf',
+                                                 proj4 = proj4string,
+                                                 datumtrans = dtrans,
+                                                 location = location,
+                                                 getErrorMsg = True,
+                                                 read = True)
             elif coordsys == 'epsg':
-                ret, projlabel, err = gcmd.RunCommand('g.proj',
-                                                      flags = 'jf',
-                                                      epsg = epsgcode,
-                                                      datumtrans = dtrans,
-                                                      location = location,
-                                                      getErrorMsg = True,
-                                                      read = True)
+                ret, projlabel, err = RunCommand('g.proj',
+                                                 flags = 'jf',
+                                                 epsg = epsgcode,
+                                                 datumtrans = dtrans,
+                                                 location = location,
+                                                 getErrorMsg = True,
+                                                 read = True)
 
             finishButton = wx.FindWindowById(wx.ID_FORWARD)
             if ret == 0:
                 self.lproj4string.SetLabel(projlabel.replace(' ', os.linesep))
                 finishButton.Enable(True)
             else:
-                gcmd.GError(err, parent = self)
+                GError(err, parent = self)
                 self.lproj4string.SetLabel('')
                 finishButton.Enable(False)
         
@@ -1846,17 +1809,17 @@ class LocationWizard(wx.Object):
                         dlg.Destroy()
             else: # -> error
                 self.wizard.Destroy()
-                gcmd.GError(parent = self.parent,
-                            message = "%s" % _("Unable to create new location. "
-                                               "Location <%(loc)s> not created.\n\n"
-                                               "Details: %(err)s") % \
-                                  { 'loc' : self.startpage.location,
-                                    'err' : msg })
+                GError(parent = self.parent,
+                       message = "%s" % _("Unable to create new location. "
+                                          "Location <%(loc)s> not created.\n\n"
+                                          "Details: %(err)s") % \
+                           { 'loc' : self.startpage.location,
+                             'err' : msg })
         else: # -> cancelled
             self.wizard.Destroy()
-            gcmd.GMessage(parent = self.parent,
-                          message = _("Location wizard canceled. "
-                                    "Location not created."))
+            GMessage(parent = self.parent,
+                     message = _("Location wizard canceled. "
+                                 "Location not created."))
             
         self.__cleanUp()
         
@@ -1960,11 +1923,11 @@ class LocationWizard(wx.Object):
         
         # location already exists?
         if os.path.isdir(os.path.join(database,location)):
-            gcmd.GError(parent = self.wizard,
-                        message = "%s <%s>: %s" % \
-                            (_("Unable to create new location"),
-                             os.path.join(database, location),
-                             _("Location already exists in GRASS Database.")))
+            GError(parent = self.wizard,
+                   message = "%s <%s>: %s" % \
+                       (_("Unable to create new location"),
+                        os.path.join(database, location),
+                        _("Location already exists in GRASS Database.")))
             return None
         
         # current GISDbase or a new one?
@@ -1976,9 +1939,9 @@ class LocationWizard(wx.Object):
                 os.mkdir(database)
             
             # change to new GISDbase directory
-            gcmd.RunCommand('g.gisenv',
-                            parent = self.wizard,
-                            set = 'GISDBASE=%s' % database)
+            RunCommand('g.gisenv',
+                       parent = self.wizard,
+                       set = 'GISDBASE=%s' % database)
             
             wx.MessageBox(parent = self.wizard,
                           message = _("Location <%(loc)s> will be created "
@@ -2084,587 +2047,3 @@ class LocationWizard(wx.Object):
         proj4string = '%s +no_defs' % proj4string
         
         return proj4string
-
-class RegionDef(BaseClass, wx.Frame):
-    """!Page for setting default region extents and resolution
-    """
-    def __init__(self, parent, id = wx.ID_ANY,
-                 title = _("Set default region extent and resolution"), location = None):
-        wx.Frame.__init__(self, parent, id, title, size = (650,300))
-        panel = wx.Panel(self, id = wx.ID_ANY)
-        
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-        
-        self.parent = parent
-        self.location = location
-        
-        #
-        # default values
-        #
-        # 2D
-        self.north = 1.0
-        self.south = 0.0
-        self.east = 1.0
-        self.west = 0.0
-        self.nsres = 1.0
-        self.ewres = 1.0
-        # 3D
-        self.top = 1.0
-        self.bottom = 0.0
-        #         self.nsres3 = 1.0
-        #         self.ewres3 = 1.0
-        self.tbres  = 1.0
-        
-        #
-        # inputs
-        #
-        # 2D
-        self.tnorth = self.MakeTextCtrl(text = str(self.north), size = (150, -1), parent = panel)
-        self.tsouth = self.MakeTextCtrl(str(self.south), size = (150, -1), parent = panel)
-        self.twest = self.MakeTextCtrl(str(self.west), size = (150, -1), parent = panel)
-        self.teast = self.MakeTextCtrl(str(self.east), size = (150, -1), parent = panel)
-        self.tnsres = self.MakeTextCtrl(str(self.nsres), size = (150, -1), parent = panel)
-        self.tewres = self.MakeTextCtrl(str(self.ewres), size = (150, -1), parent = panel)
-        
-        #
-        # labels
-        #
-        self.lrows  = self.MakeLabel(parent = panel)
-        self.lcols  = self.MakeLabel(parent = panel)
-        self.lcells = self.MakeLabel(parent = panel)
-        
-        #
-        # buttons
-        #
-        self.bset = self.MakeButton(text = _("&Set region"), id = wx.ID_OK, parent = panel)
-        self.bcancel = wx.Button(panel, id = wx.ID_CANCEL)
-        self.bset.SetDefault()
-        
-        #
-        # image
-        #
-        self.img = wx.Image(os.path.join(globalvar.ETCIMGDIR, "qgis_world.png"),
-                            wx.BITMAP_TYPE_PNG).ConvertToBitmap()
-        
-        #
-        # set current working environment to PERMANENT mapset
-        # in selected location in order to set default region (WIND)
-        #
-        envval = {}
-        ret = gcmd.RunCommand('g.gisenv',
-                              read = True)
-        if ret:
-            for line in ret.splitlines():
-                key, val = line.split('=')
-                envval[key] = val
-            self.currlocation = envval['LOCATION_NAME'].strip("';")
-            self.currmapset = envval['MAPSET'].strip("';")
-            if self.currlocation != self.location or self.currmapset != 'PERMANENT':
-                gcmd.RunCommand('g.gisenv',
-                                set = 'LOCATION_NAME=%s' % self.location)
-                gcmd.RunCommand('g.gisenv',
-                                set = 'MAPSET=PERMANENT')
-        else:
-            dlg = wx.MessageBox(parent = self,
-                                message = _('Invalid location selected.'),
-                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
-            return
-        
-        #
-        # get current region settings
-        #
-        region = {}
-        ret = gcmd.RunCommand('g.region',
-                              read = True,
-                              flags = 'gp3')
-        if ret:
-            for line in ret.splitlines():
-                key, val = line.split('=')
-                region[key] = float(val)
-        else:
-            dlg = wx.MessageBox(parent = self,
-                                message = _("Invalid region"),
-                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
-            dlg.ShowModal()
-            dlg.Destroy()
-            return
-        
-        #
-        # update values
-        # 2D
-        self.north = float(region['n'])
-        self.south = float(region['s'])
-        self.east = float(region['e'])
-        self.west = float(region['w'])
-        self.nsres = float(region['nsres'])
-        self.ewres = float(region['ewres'])
-        self.rows = int(region['rows'])
-        self.cols = int(region['cols'])
-        self.cells = int(region['cells'])
-        # 3D
-        self.top = float(region['t'])
-        self.bottom = float(region['b'])
-        #         self.nsres3 = float(region['nsres3'])
-        #         self.ewres3 = float(region['ewres3'])
-        self.tbres = float(region['tbres'])
-        self.depth = int(region['depths'])
-        self.cells3 = int(region['cells3'])
-        
-        #
-        # 3D box collapsable
-        #
-        self.infoCollapseLabelExp = _("Click here to show 3D settings")
-        self.infoCollapseLabelCol = _("Click here to hide 3D settings")
-        self.settings3D = wx.CollapsiblePane(parent = panel,
-                                             label = self.infoCollapseLabelExp,
-                                             style = wx.CP_DEFAULT_STYLE |
-                                             wx.CP_NO_TLW_RESIZE | wx.EXPAND)
-        self.MakeSettings3DPaneContent(self.settings3D.GetPane())
-        self.settings3D.Collapse(False) # FIXME
-        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSettings3DPaneChanged, self.settings3D)
-        
-        #
-        # set current region settings
-        #
-        self.tnorth.SetValue(str(self.north))
-        self.tsouth.SetValue(str(self.south))
-        self.twest.SetValue(str(self.west))
-        self.teast.SetValue(str(self.east))
-        self.tnsres.SetValue(str(self.nsres))
-        self.tewres.SetValue(str(self.ewres))
-        self.ttop.SetValue(str(self.top))
-        self.tbottom.SetValue(str(self.bottom))
-        #         self.tnsres3.SetValue(str(self.nsres3))
-        #         self.tewres3.SetValue(str(self.ewres3))
-        self.ttbres.SetValue(str(self.tbres))
-        self.lrows.SetLabel(_("Rows: %d") % self.rows)
-        self.lcols.SetLabel(_("Cols: %d") % self.cols)
-        self.lcells.SetLabel(_("Cells: %d") % self.cells)
-        
-        #
-        # bindings
-        #
-        self.Bind(wx.EVT_BUTTON, self.OnSetButton, self.bset)
-        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.bcancel)
-        self.tnorth.Bind(wx.EVT_TEXT,   self.OnValue)
-        self.tsouth.Bind(wx.EVT_TEXT,   self.OnValue)
-        self.teast.Bind(wx.EVT_TEXT,    self.OnValue)
-        self.twest.Bind(wx.EVT_TEXT,    self.OnValue)
-        self.tnsres.Bind(wx.EVT_TEXT,   self.OnValue)
-        self.tewres.Bind(wx.EVT_TEXT,   self.OnValue)
-        self.ttop.Bind(wx.EVT_TEXT,     self.OnValue)
-        self.tbottom.Bind(wx.EVT_TEXT,  self.OnValue)
-        #         self.tnsres3.Bind(wx.EVT_TEXT,  self.OnValue)
-        #         self.tewres3.Bind(wx.EVT_TEXT,  self.OnValue)
-        self.ttbres.Bind(wx.EVT_TEXT,   self.OnValue)
-        
-        self.__DoLayout(panel)
-        self.SetMinSize(self.GetBestSize())
-        self.minWindowSize = self.GetMinSize()
-    
-    def MakeSettings3DPaneContent(self, pane):
-        """!Create 3D region settings pane"""
-        border = wx.BoxSizer(wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
-
-        # inputs
-        self.ttop = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.top),
-                                size = (150, -1))
-        self.tbottom = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.bottom),
-                                size = (150, -1))
-        self.ttbres = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.tbres),
-                                size = (150, -1))
-        #         self.tnsres3 = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.nsres3),
-        #                                    size = (150, -1))
-        #         self.tewres3  =  wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.ewres3),
-        #                                    size = (150, -1))
-
-        #labels
-        self.ldepth = wx.StaticText(parent = pane, label = _("Depth: %d") % self.depth)
-        self.lcells3  =  wx.StaticText(parent = pane, label = _("3D Cells: %d") % self.cells3)
-
-        # top
-        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Top")),
-                      flag = wx.ALIGN_CENTER |
-                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
-                      pos = (0, 1))
-        gridSizer.Add(item = self.ttop,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALL, border = 5, pos = (1, 1))
-        # bottom
-        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Bottom")),
-                      flag = wx.ALIGN_CENTER |
-                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
-                      pos = (0, 2))
-        gridSizer.Add(item = self.tbottom,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALL, border = 5, pos = (1, 2))
-        # tbres
-        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("T-B resolution")),
-                      flag = wx.ALIGN_CENTER | 
-                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
-                      pos = (0, 3))
-        gridSizer.Add(item = self.ttbres,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALL, border = 5, pos = (1, 3))
-
-        # res
-        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D N-S resolution")),
-        #                       flag = wx.ALIGN_CENTER |
-        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
-        #                       pos = (2, 1))
-        #         gridSizer.Add(item = self.tnsres3,
-        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
-        #                       wx.ALL, border = 5, pos = (3, 1))
-        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D E-W resolution")),
-        #                       flag = wx.ALIGN_CENTER |
-        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
-        #                       pos = (2, 3))
-        #         gridSizer.Add(item = self.tewres3,
-        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
-        #                       wx.ALL, border = 5, pos = (3, 3))
-
-        # rows/cols/cells
-        gridSizer.Add(item = self.ldepth,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.ALL, border = 5, pos = (2, 1))
-
-        gridSizer.Add(item = self.lcells3,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.ALL, border = 5, pos = (2, 2))
-
-        border.Add(item = gridSizer, proportion = 1,
-                   flag = wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, border = 5)
-
-        pane.SetSizer(border)
-        border.Fit(pane)
-
-    def OnSettings3DPaneChanged(self, event):
-        """!Collapse 3D settings box"""
-
-        if self.settings3D.IsExpanded():
-            self.settings3D.SetLabel(self.infoCollapseLabelCol)
-            self.Layout()
-            self.SetSize(self.GetBestSize())
-            self.SetMinSize(self.GetSize())
-        else:
-            self.settings3D.SetLabel(self.infoCollapseLabelExp)
-            self.Layout()
-            self.SetSize(self.minWindowSize)
-            self.SetMinSize(self.minWindowSize)
-
-        self.SendSizeEvent()
-
-    def __DoLayout(self, panel):
-        """!Window layout"""
-        frameSizer = wx.BoxSizer(wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
-        settings3DSizer = wx.BoxSizer(wx.VERTICAL)
-        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
-
-        # north
-        gridSizer.Add(item = self.MakeLabel(text = _("North"), parent = panel),
-                      flag = wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (0, 2))
-        gridSizer.Add(item = self.tnorth,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5, pos = (1, 2))
-        # west
-        gridSizer.Add(item = self.MakeLabel(text = _("West"), parent = panel),
-                      flag = wx.ALIGN_RIGHT |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.LEFT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 0))
-        gridSizer.Add(item = self.twest,
-                      flag = wx.ALIGN_RIGHT |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5,  pos = (2, 1))
-
-        gridSizer.Add(item = wx.StaticBitmap(panel, wx.ID_ANY, self.img, (-1, -1),
-                                           (self.img.GetWidth(), self.img.GetHeight())),
-                      flag = wx.ALIGN_CENTER |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5, pos = (2, 2))
-
-        # east
-        gridSizer.Add(item = self.teast,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5,  pos = (2, 3))
-        gridSizer.Add(item = self.MakeLabel(text = _("East"), parent = panel),
-                      flag = wx.ALIGN_LEFT |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.RIGHT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 4))
-        # south
-        gridSizer.Add(item = self.tsouth,
-                      flag = wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5, pos = (3, 2))
-        gridSizer.Add(item = self.MakeLabel(text = _("South"), parent = panel),
-                      flag = wx.ALIGN_TOP | wx.ALIGN_CENTER_HORIZONTAL |
-                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5, pos = (4, 2))
-        # ns-res
-        gridSizer.Add(item = self.MakeLabel(text = _("N-S resolution"), parent = panel),
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 1))
-        gridSizer.Add(item = self.tnsres,
-                      flag = wx.ALIGN_RIGHT |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5,  pos = (6, 1))
-        # ew-res
-        gridSizer.Add(item = self.MakeLabel(text = _("E-W resolution"), parent = panel),
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 3))
-        gridSizer.Add(item = self.tewres,
-                      flag = wx.ALIGN_RIGHT |
-                      wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALL, border = 5,  pos = (6, 3))
-        # rows/cols/cells
-        gridSizer.Add(item = self.lrows,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.ALL, border = 5, pos = (7, 1))
-
-        gridSizer.Add(item = self.lcells,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.ALL, border = 5, pos = (7, 2))
-
-        gridSizer.Add(item = self.lcols,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
-                      wx.ALL, border = 5, pos = (7, 3))
-
-        # 3D
-        settings3DSizer.Add(item = self.settings3D,
-                            flag = wx.ALL,
-                            border = 5)
-
-        # buttons
-        buttonSizer.Add(item = self.bcancel, proportion = 1,
-                        flag = wx.ALIGN_RIGHT |
-                        wx.ALIGN_CENTER_VERTICAL |
-                        wx.ALL, border = 10)
-        buttonSizer.Add(item = self.bset, proportion = 1,
-                        flag = wx.ALIGN_CENTER |
-                        wx.ALIGN_CENTER_VERTICAL |
-                        wx.ALL, border = 10)
-
-        frameSizer.Add(item = gridSizer, proportion = 1,
-                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
-        frameSizer.Add(item = settings3DSizer, proportion = 0,
-                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
-        frameSizer.Add(item = buttonSizer, proportion = 0,
-                       flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
-
-        self.SetAutoLayout(True)
-        panel.SetSizer(frameSizer)
-        frameSizer.Fit(panel)
-        self.Layout()
-
-    def OnValue(self, event):
-        """!Set given value"""
-        try:
-            if event.GetId() == self.tnorth.GetId():
-                self.north = float(event.GetString())
-            elif event.GetId() == self.tsouth.GetId():
-                self.south = float(event.GetString())
-            elif event.GetId() == self.teast.GetId():
-                self.east = float(event.GetString())
-            elif event.GetId() == self.twest.GetId():
-                self.west = float(event.GetString())
-            elif event.GetId() == self.tnsres.GetId():
-                self.nsres = float(event.GetString())
-            elif event.GetId() == self.tewres.GetId():
-                self.ewres = float(event.GetString())
-            elif event.GetId() == self.ttop.GetId():
-                self.top = float(event.GetString())
-            elif event.GetId() == self.tbottom.GetId():
-                self.bottom = float(event.GetString())
-            #             elif event.GetId() == self.tnsres3.GetId():
-            #                 self.nsres3 = float(event.GetString())
-            #             elif event.GetId() == self.tewres3.GetId():
-            #                 self.ewres3 = float(event.GetString())
-            elif event.GetId() == self.ttbres.GetId():
-                self.tbres = float(event.GetString())
-
-            self.__UpdateInfo()
-
-        except ValueError, e:
-            if len(event.GetString()) > 0 and event.GetString() != '-':
-                dlg = wx.MessageBox(parent = self,
-                                    message = _("Invalid value: %s") % e,
-                                    caption = _("Error"),
-                                    style = wx.OK | wx.ICON_ERROR)
-                # reset values
-                self.tnorth.SetValue(str(self.north))
-                self.tsouth.SetValue(str(self.south))
-                self.teast.SetValue(str(self.east))
-                self.twest.SetValue(str(self.west))
-                self.tnsres.SetValue(str(self.nsres))
-                self.tewres.SetValue(str(self.ewres))
-                self.ttop.SetValue(str(self.top))
-                self.tbottom.SetValue(str(self.bottom))
-                self.ttbres.SetValue(str(self.tbres))
-                # self.tnsres3.SetValue(str(self.nsres3))
-                # self.tewres3.SetValue(str(self.ewres3))
-
-        event.Skip()
-
-    def __UpdateInfo(self):
-        """!Update number of rows/cols/cells"""
-        self.rows = int((self.north - self.south) / self.nsres)
-        self.cols = int((self.east - self.west) / self.ewres)
-        self.cells = self.rows * self.cols
-
-        self.depth = int((self.top - self.bottom) / self.tbres)
-        self.cells3 = self.rows * self.cols * self.depth
-
-        # 2D
-        self.lrows.SetLabel(_("Rows: %d") % self.rows)
-        self.lcols.SetLabel(_("Cols: %d") % self.cols)
-        self.lcells.SetLabel(_("Cells: %d") % self.cells)
-        # 3D
-        self.ldepth.SetLabel(_("Depth: %d" % self.depth))
-        self.lcells3.SetLabel(_("3D Cells: %d" % self.cells3))
-
-    def OnSetButton(self, event = None):
-        """!Set default region"""
-        ret = gcmd.RunCommand('g.region',
-                              flags = 'sgpa',
-                              n = self.north,
-                              s = self.south,
-                              e = self.east,
-                              w = self.west,
-                              nsres = self.nsres,
-                              ewres = self.ewres,
-                              t = self.top,
-                              b = self.bottom,
-                              tbres = self.tbres)
-        if ret == 0:
-            self.Destroy()
-
-    def OnCancel(self, event):
-        self.Destroy()
-        
-class TransList(wx.VListBox):
-    """!Creates a multiline listbox for selecting datum transforms"""
-        
-    def OnDrawItem(self, dc, rect, n):
-        if self.GetSelection() == n:
-            c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
-        else:
-            c = self.GetForegroundColour()
-        dc.SetFont(self.GetFont())
-        dc.SetTextForeground(c)
-        dc.DrawLabel(self._getItemText(n), rect,
-                     wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
-
-    def OnMeasureItem(self, n):
-        height = 0
-        if self._getItemText(n) == None:
-            return
-        for line in self._getItemText(n).splitlines():
-            w, h = self.GetTextExtent(line)
-            height += h
-        return height + 5
-
-    def _getItemText(self, item):
-        global transformlist
-        transitem = transformlist[item]
-        if transitem.strip() !='':
-            return transitem
-
-
-class SelectTransformDialog(wx.Dialog):
-    """!Dialog for selecting datum transformations"""
-    def __init__(self, parent, transforms, title = _("Select datum transformation"),
-                 pos = wx.DefaultPosition, size = wx.DefaultSize, 
-                 style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
-
-        wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
-
-        global transformlist
-        self.CentreOnParent()
-        
-        # default transform number
-        self.transnum = 0
-        
-        panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
-        sizer = wx.BoxSizer(wx.VERTICAL)
-
-        #
-        # set panel sizer
-        #
-        panel.SetSizer(sizer)
-        panel.SetupScrolling()
-
-        #
-        # dialog body
-        #
-        bodyBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
-                               label = " %s " % _("Select from list of datum transformations"))
-        bodySizer = wx.StaticBoxSizer(bodyBox)       
-        
-        # add no transform option
-        transforms = '---\n\n0\nDo not apply any datum transformations\n\n' + transforms
-        
-        transformlist = transforms.split('---')
-        tlistlen = len(transformlist)
-        
-        # calculate size for transform list
-        height = 0
-        width = 0
-        for line in transforms.splitlines():
-            w, h = self.GetTextExtent(line)
-            height += h
-            width = max(width, w)
-            
-        height = height + 5
-        if height > 400: height = 400
-        width = width + 5
-        if width > 400: width = 400
-
-        #
-        # VListBox for displaying and selecting transformations
-        #
-        self.translist = TransList(panel, id = -1, size = (width, height), style = wx.SUNKEN_BORDER)
-        self.translist.SetItemCount(tlistlen)
-        self.translist.SetSelection(2)
-        self.translist.SetFocus()
-        
-        self.Bind(wx.EVT_LISTBOX, self.ClickTrans, self.translist)
-
-        bodySizer.Add(item = self.translist, proportion = 1, flag = wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
-
-        #
-        # buttons
-        #
-        btnsizer = wx.StdDialogButtonSizer()
-
-        btn = wx.Button(parent = panel, id = wx.ID_OK)
-        btn.SetDefault()
-        btnsizer.AddButton(btn)
-
-        btn = wx.Button(parent = panel, id = wx.ID_CANCEL)
-        btnsizer.AddButton(btn)
-        btnsizer.Realize()
-
-        sizer.Add(item = bodySizer, proportion = 1,
-                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-
-        sizer.Add(item = btnsizer, proportion = 0,
-                  flag =  wx.ALL | wx.ALIGN_RIGHT, border = 5)
-
-        sizer.Fit(panel)
-
-        self.SetSize(self.GetBestSize())
-        self.Layout()
-        
-    def ClickTrans(self, event):
-        """!Get the number of the datum transform to use in g.proj"""
-        self.transnum = event.GetSelection()
-        self.transnum = self.transnum - 1
-    
-    def GetTransform(self):
-        """!Get the number of the datum transform to use in g.proj"""
-        self.transnum = self.translist.GetSelection()
-        self.transnum = self.transnum - 1
-        return self.transnum

+ 73 - 387
gui/wxpython/gui_modules/mapdisp.py

@@ -1,5 +1,5 @@
 """!
-@package mapdisp.py
+@package mapdisp.frame
 
 @brief Map display with toolbar for various display management
 functions, and additional toolbars (vector digitizer, 3d view).
@@ -7,7 +7,6 @@ functions, and additional toolbars (vector digitizer, 3d view).
 Can be used either from Layer Manager or as d.mon backend.
 
 Classes:
- - MapFrameBase
  - MapFrame
  - MapApp
 
@@ -28,37 +27,36 @@ for details.
 
 import os
 import sys
-import glob
 import math
-import tempfile
 import copy
 
-import globalvar
+from core import globalvar
 import wx
 import wx.aui
 
 sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
 sys.path.append(os.path.join(globalvar.ETCDIR,   "python"))
 
-import render
-import toolbars
-import menuform
-import gselect
-import disp_print
-import gcmd
-import dbm
-import dbm_dialogs
-import globalvar
-import utils
-import gdialogs
-import mapdisp_statusbar as sb
-from debug       import Debug
-from icon        import Icons
-from preferences import globalSettings as UserSettings
-
-from mapdisp_window  import BufferedWindow
-from histogram       import HistFrame
-from wxplot          import HistFrame as HistFramePyPlot, ProfileFrame, ScatterFrame
+from core               import globalvar
+from core.render        import EVT_UPDATE_PRGBAR, Map
+from vdigit.toolbars    import VDigitToolbar
+from mapdisp.toolbars   import MapToolbar
+from mapdisp.gprint     import PrintOptions
+from core.gcmd          import GError, GMessage, RunCommand
+from dbm.dialogs        import DisplayAttributesDialog
+from core.utils         import ListOfCatsToRange, GetLayerNameFromCmd
+from gui_core.dialogs   import GetImageHandlers, ImageSizeDialog, DecorationDialog, TextLayerDialog
+from core.debug         import Debug
+from icon               import Icons
+from core.settings      import UserSettings
+from gui_core.mapdisp   import MapFrameBase
+from mapdisp.window     import BufferedWindow
+from modules.histogram  import HistFrame
+from wxplot.histogram   import HistFrame as HistFramePyPlot
+from wxplot.profile     import ProfileFrame
+from wxplot.scatter     import ScatterFrame
+
+from mapdisp import statusbar as sb
 
 from grass.script import core as grass
 
@@ -72,318 +70,6 @@ monSize = list(globalvar.MAP_WINDOW_SIZE)
 
 haveCtypes = False
 
-class MapFrameBase(wx.Frame):
-    """!Base class for map display window
-    
-    Derived class must use statusbarManager or override
-    GetProperty, SetProperty and HasProperty methods.
-    If derived class enables and disables auto-rendering,
-    it should override IsAutoRendered method.
-    """
-    def __init__(self, parent = None, id = wx.ID_ANY, title = None,
-                 style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
-                 Map = None, auimgr = None, name = None, **kwargs):
-        """!
-        @param toolbars array of activated toolbars, e.g. ['map', 'digit']
-        @param Map instance of render.Map
-        @param auimgs AUI manager
-        @param name frame name
-        @param kwargs wx.Frame attributes
-        """
-        
-
-        self.Map        = Map       # instance of render.Map
-        self.parent     = parent
-        
-        wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
-        
-        # available cursors
-        self.cursors = {
-            # default: cross
-            # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
-            "default" : wx.StockCursor(wx.CURSOR_ARROW),
-            "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
-            "hand"    : wx.StockCursor(wx.CURSOR_HAND),
-            "pencil"  : wx.StockCursor(wx.CURSOR_PENCIL),
-            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
-            }
-                
-        #
-        # set the size & system icon
-        #
-        self.SetClientSize(self.GetSize())
-        self.iconsize = (16, 16)
-
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
-        
-        # toolbars
-        self.toolbars = {}
-        
-        #
-        # Fancy gui
-        #
-        self._mgr = wx.aui.AuiManager(self)
-        
-    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')
-        
-        self.width, self.height = self.GetClientSize()
-        
-        Debug.msg(2, "MapFrame._initMap():")
-        map.ChangeMapSize(self.GetClientSize())
-        map.region = map.GetRegion() # g.region -upgc
-        # self.Map.SetRegion() # adjust region to match display window
-        
-    def SetProperty(self, name, value):
-        """!Sets property"""
-        self.statusbarManager.SetProperty(name, value)
-        
-    def GetProperty(self, name):
-        """!Returns property"""
-        return self.statusbarManager.GetProperty(name)
-        
-    def HasProperty(self, name):
-        """!Checks whether object has property"""
-        return self.statusbarManager.HasProperty(name)
-    
-    def GetPPM(self):
-        """! Get pixel per meter
-        
-        @todo now computed every time, is it necessary?
-        @todo enable user to specify ppm (and store it in UserSettings)
-        """
-        # TODO: need to be fixed...
-        ### screen X region problem
-        ### user should specify ppm
-        dc = wx.ScreenDC()
-        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])
-
-        ppi = comPpi                  # pixel per inch
-        ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
-                    (ppi[1] / 2.54) * 100)
-        
-        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
-    
-    def SetMapScale(self, value, map = None):
-        """! Set current map scale
-        
-        @param value scale value (n if scale is 1:n)
-        @param map Map instance (if none self.Map is used)
-        """
-        if not map:
-            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.
-        
-        # add to zoom history
-        self.GetWindow().ZoomHistory(region['n'], region['s'],
-                                   region['e'], region['w'])
-    
-    def GetMapScale(self, map = None):
-        """! Get current map scale
-        
-        @param map Map instance (if none self.Map is used)
-        """
-        if not map:
-            map = self.Map
-        
-        region = map.region
-        ppm = self.GetPPM()
-
-        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))
-
-        xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
-        yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
-        scale = (xscale + yscale) / 2.
-        
-        Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
-                      (xscale, yscale, scale))
-        
-        return scale
-        
-    def GetProgressBar(self):
-        """!Returns progress bar
-        
-        Progress bar can be used by other classes.
-        """
-        return self.statusbarManager.GetProgressBar()
-        
-    def GetMap(self):
-        """!Returns current Map instance
-        """
-        return self.Map
-
-    def GetWindow(self):
-        """!Get map window"""
-        return self.MapWindow
-        
-    def GetMapToolbar(self):
-       """!Returns toolbar with zooming tools"""
-       raise NotImplementedError()
-       
-    def GetToolbar(self, name):
-        """!Returns toolbar if exists else None.
-        
-        Toolbars dictionary contains currently used toolbars only.
-        """
-        if name in self.toolbars:
-            return self.toolbars[name]
-        
-        return None
-       
-    def StatusbarUpdate(self):
-        """!Update statusbar content"""
-        self.statusbarManager.Update()
-        
-    def IsAutoRendered(self):
-        """!Check if auto-rendering is enabled"""
-        return self.GetProperty('render')
-        
-    def CoordinatesChanged(self):
-        """!Shows current coordinates on statusbar.
-        
-        Used in BufferedWindow to report change of map coordinates (under mouse cursor).
-        """
-        self.statusbarManager.ShowItem('coordinates')
-        
-    def StatusbarReposition(self):
-        """!Reposition items in statusbar"""
-        self.statusbarManager.Reposition()
-        
-    def StatusbarEnableLongHelp(self, enable = True):
-        """!Enable/disable toolbars long help"""
-        for toolbar in self.toolbars.itervalues():
-            toolbar.EnableLongHelp(enable)
-        
-    def IsStandalone(self):
-        """!Check if Map display is standalone"""
-        raise NotImplementedError("IsStandalone")
-   
-    def OnRender(self, event):
-        """!Re-render map composition (each map layer)
-        """
-        raise NotImplementedError("OnRender")
-        
-    def OnDraw(self, event):
-        """!Re-display current map composition
-        """
-        self.MapWindow.UpdateMap(render = False)
-        
-    def OnErase(self, event):
-        """!Erase the canvas
-        """
-        self.MapWindow.EraseMap()
-        
-    def OnZoomIn(self, event):
-        """!Zoom in the map.
-        Set mouse cursor, zoombox attributes, and zoom direction
-        """
-        toolbar = self.GetMapToolbar()
-        self._switchTool(toolbar, event)
-        
-        win = self.GetWindow()
-        self._prepareZoom(mapWindow = win, zoomType = 1)
-        
-    def OnZoomOut(self, event):
-        """!Zoom out the map.
-        Set mouse cursor, zoombox attributes, and zoom direction
-        """
-        toolbar = self.GetMapToolbar()
-        self._switchTool(toolbar, event)
-        
-        win = self.GetWindow()
-        self._prepareZoom(mapWindow = win, zoomType = -1)
-        
-    def _prepareZoom(self, mapWindow, zoomType):
-        """!Prepares MapWindow for zoom, toggles toolbar
-        
-        @param mapWindow MapWindow to prepare
-        @param zoomType 1 for zoom in, -1 for zoom out
-        """
-        mapWindow.mouse['use'] = "zoom"
-        mapWindow.mouse['box'] = "box"
-        mapWindow.zoomtype = zoomType
-        mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
-        
-        # change the cursor
-        mapWindow.SetCursor(self.cursors["cross"])
-    
-    def _switchTool(self, toolbar, event):
-        """!Helper function to switch tools"""
-        if toolbar:
-            toolbar.OnTool(event)
-            toolbar.action['desc'] = ''
-            
-    def OnPan(self, event):
-        """!Panning, set mouse to drag
-        """
-        toolbar = self.GetMapToolbar()
-        self._switchTool(toolbar, event)
-        
-        win = self.GetWindow()
-        self._preparePan(mapWindow = win)
-    
-    def _preparePan(self, mapWindow):
-        """!Prepares MapWindow for pan, toggles toolbar
-        
-        @param mapWindow MapWindow to prepare
-        """
-        mapWindow.mouse['use'] = "pan"
-        mapWindow.mouse['box'] = "pan"
-        mapWindow.zoomtype = 0
-        
-        # change the cursor
-        mapWindow.SetCursor(self.cursors["hand"])
-        
-    def OnZoomBack(self, event):
-        """!Zoom last (previously stored position)
-        """
-        self.MapWindow.ZoomBack()
-        
-    def OnZoomToMap(self, event):
-        """!
-        Set display extents to match selected raster (including NULLs)
-        or vector map.
-        """
-        self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
-    
-    def OnZoomToWind(self, event):
-        """!Set display geometry to match computational region
-        settings (set with g.region)
-        """
-        self.MapWindow.ZoomToWind()
-        
-    def OnZoomToDefault(self, event):
-        """!Set display geometry to match default region settings
-        """
-        self.MapWindow.ZoomToDefault()
-        
 class MapFrame(MapFrameBase):
     """!Main frame for map display window. Drawing takes place in
     child double buffered drawing window.
@@ -474,7 +160,7 @@ class MapFrame(MapFrameBase):
         #
         self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
         self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
-        self.Bind(render.EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+        self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
         
         #
         # Update fancy gui style
@@ -488,7 +174,7 @@ class MapFrame(MapFrameBase):
         #
         # Init print module and classes
         #
-        self.printopt = disp_print.PrintOptions(self, self.MapWindow)
+        self.printopt = PrintOptions(self, self.MapWindow)
         
         #
         # Init zoom history
@@ -558,9 +244,9 @@ class MapFrame(MapFrameBase):
         elif self._mgr.GetPane('3d').IsShown():
             self._mgr.GetPane('3d').Hide()
         self._mgr.GetPane('vdigit').Show()
-        self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent = self, MapWindow = self.MapWindow,
-                                                         layerTree = self.tree,
-                                                         log = log)
+        self.toolbars['vdigit'] = VDigitToolbar(parent = self, MapWindow = self.MapWindow,
+                                                layerTree = self.tree,
+                                                log = log)
         self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
         
         self._mgr.AddPane(self.toolbars['vdigit'],
@@ -585,10 +271,10 @@ class MapFrame(MapFrameBase):
         # check for GLCanvas and OpenGL
         if not nviz.haveNviz:
             self.toolbars['map'].combo.SetValue(_("2D view"))
-            gcmd.GError(parent = self,
-                        message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
-                                    "was not found or loaded properly.\n"
-                                    "Switching back to 2D display mode.\n\nDetails: %s" % nviz.errorMsg))
+            GError(parent = self,
+                   message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
+                               "was not found or loaded properly.\n"
+                               "Switching back to 2D display mode.\n\nDetails: %s" % nviz.errorMsg))
             return
         
         # disable 3D mode for other displays
@@ -697,7 +383,7 @@ class MapFrame(MapFrameBase):
         """
         # default toolbar
         if name == "map":
-            self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
+            self.toolbars['map'] = MapToolbar(self, self.Map)
             
             self._mgr.AddPane(self.toolbars['map'],
                               wx.aui.AuiPaneInfo().
@@ -870,13 +556,13 @@ class MapFrame(MapFrameBase):
         else:
             img = self.MapWindow.img
             if not img:
-                gcmd.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 = gdialogs.GetImageHandlers(img)
+            filetype, ltype = GetImageHandlers(img)
         
         # get size
-        dlg = gdialogs.ImageSizeDialog(self)
+        dlg = ImageSizeDialog(self)
         dlg.CentreOnParent()
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
@@ -973,8 +659,8 @@ class MapFrame(MapFrameBase):
                     num += 1
             
             if num < 1:
-                gcmd.GMessage(parent = self,
-                              message = _('No raster or vector map layer selected for querying.'))
+                GMessage(parent = self,
+                         message = _('No raster or vector map layer selected for querying.'))
                 return
         
         rast = list()
@@ -988,7 +674,7 @@ class MapFrame(MapFrameBase):
             for layer in self.tree.GetSelections():
                 ltype = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
                 dcmd = self.tree.GetPyData(layer)[0]['cmd']
-                name, found = utils.GetLayerNameFromCmd(dcmd)
+                name, found = GetLayerNameFromCmd(dcmd)
                 
                 if not found:
                     continue
@@ -1047,9 +733,9 @@ class MapFrame(MapFrameBase):
                                                   onDone = self._QueryMapDone)
         else:
             if rast:
-                gcmd.RunCommand(rcmd)
+                RunCommand(rcmd)
             if vect:
-                gcmd.RunCommand(vcmd)
+                RunCommand(vcmd)
         
     def _QueryMapDone(self, cmd, returncode):
         """!Restore settings after querying (restore GRASS_REGION)
@@ -1075,8 +761,8 @@ class MapFrame(MapFrameBase):
         """
         if not self.tree.layer_selected or \
                 self.tree.GetPyData(self.tree.layer_selected)[0]['type'] != 'vector':
-            gcmd.GMessage(parent = self,
-                          message = _("No map layer selected for querying."))
+            GMessage(parent = self,
+                     message = _("No map layer selected for querying."))
             return
         
         posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
@@ -1096,7 +782,7 @@ class MapFrame(MapFrameBase):
             mode = 'update'
         
         if self.dialogs['attributes'] is None:
-            dlg = dbm_dialogs.DisplayAttributesDialog(parent = self.MapWindow,
+            dlg = DisplayAttributesDialog(parent = self.MapWindow,
                                                       map = mapName,
                                                       query = ((east, north), qdist),
                                                       pos = posWindow,
@@ -1209,7 +895,7 @@ class MapFrame(MapFrameBase):
                 cmd.append(copy.copy(pattern))
                 lcats = cats[layer]
                 cmd[-1].append("layer=%d" % layer)
-                cmd[-1].append("cats=%s" % utils.ListOfCatsToRange(lcats))
+                cmd[-1].append("cats=%s" % ListOfCatsToRange(lcats))
         
         if addLayer:
             if useId:
@@ -1510,14 +1196,14 @@ class MapFrame(MapFrameBase):
 
         # decoration overlay control dialog
         self.dialogs['barscale'] = \
-            gdialogs.DecorationDialog(parent = self, title = _('Scale and North arrow'),
-                                      size = (350, 200),
-                                      style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
-                                      cmd = ['d.barscale', 'at=0,95'],
-                                      ovlId = id,
-                                      name = 'barscale',
-                                      checktxt = _("Show/hide scale and North arrow"),
-                                      ctrltxt = _("scale object"))
+            DecorationDialog(parent = self, title = _('Scale and North arrow'),
+                             size = (350, 200),
+                             style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
+                             cmd = ['d.barscale', 'at=0,95'],
+                             ovlId = id,
+                             name = 'barscale',
+                             checktxt = _("Show/hide scale and North arrow"),
+                             ctrltxt = _("scale object"))
 
         self.dialogs['barscale'].CentreOnParent()
         ### dialog cannot be show as modal - in the result d.barscale is not selectable
@@ -1541,15 +1227,15 @@ class MapFrame(MapFrameBase):
         
         # Decoration overlay control dialog
         self.dialogs['legend'] = \
-            gdialogs.DecorationDialog(parent = self, title = ('Legend'),
-                                      size = (350, 200),
-                                      style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
-                                      cmd = cmd,
-                                      ovlId = id,
-                                      name = 'legend',
-                                      checktxt = _("Show/hide legend"),
-                                      ctrltxt = _("legend object")) 
-    
+            DecorationDialog(parent = self, title = ('Legend'),
+                             size = (350, 200),
+                             style = wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
+                             cmd = cmd,
+                             ovlId = id,
+                             name = 'legend',
+                             checktxt = _("Show/hide legend"),
+                             ctrltxt = _("legend object")) 
+            
         self.dialogs['legend'].CentreOnParent() 
         ### dialog cannot be show as modal - in the result d.legend is not selectable
         ### self.dialogs['legend'].ShowModal()
@@ -1569,9 +1255,9 @@ class MapFrame(MapFrameBase):
             else:
                 id = 101
         
-        self.dialogs['text'] = gdialogs.TextLayerDialog(parent = self, ovlId = id, 
-                                                        title = _('Add text layer'),
-                                                        size = (400, 200))
+        self.dialogs['text'] = TextLayerDialog(parent = self, ovlId = id, 
+                                               title = _('Add text layer'),
+                                               size = (400, 200))
         self.dialogs['text'].CenterOnParent()
 
         # If OK button pressed in decoration control dialog
@@ -1725,8 +1411,8 @@ class MapApp(wx.App):
         wx.InitAllImageHandlers()
         if __name__ == "__main__":
             self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
-            Map = render.Map(cmdfile = monFile['cmd'], mapfile = monFile['map'],
-                             envfile = monFile['env'], monitor = monName)
+            Map = Map(cmdfile = monFile['cmd'], mapfile = monFile['map'],
+                      envfile = monFile['env'], monitor = monName)
         else:
             Map = None
         
@@ -1792,8 +1478,8 @@ if __name__ == "__main__":
     
     grass.verbose(_("Starting map display <%s>...") % (monName))
 
-    gcmd.RunCommand('g.gisenv',
-                    set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
+    RunCommand('g.gisenv',
+               set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
     
     gm_map = MapApp(0)
     # set title
@@ -1810,10 +1496,10 @@ if __name__ == "__main__":
     env_name = 'MONITOR_%s' % monName
     for key in env.keys():
         if key.find(env_name) == 0:
-            gcmd.RunCommand('g.gisenv',
-                              set = '%s=' % key)
+            RunCommand('g.gisenv',
+                       set = '%s=' % key)
         if key == 'MONITOR' and env[key] == monName:
-            gcmd.RunCommand('g.gisenv',
-                            set = '%s=' % key)
+            RunCommand('g.gisenv',
+                       set = '%s=' % key)
     
     sys.exit(0)

+ 17 - 19
gui/wxpython/gui_modules/disp_print.py

@@ -1,27 +1,24 @@
-"""
-MODULE:     disp_print.py
-
-CLASSES:
-    * MapPrint
-    * PrintOptions
+"""!
+@package mapdisp.gprint
 
-PURPOSE:    Print context and utility functions for printing
-            contents of map display window.
+@brief Print context and utility functions for printing
+contents of map display window.
 
-AUTHORS:    The GRASS Development Team
-            Michael Barton (Arizona State University)
-            Based on generic example code from wxPython
-            demo program by Robin Dunn
+Classes:
+ - MapPrint
+ - PrintOptions
 
-COPYRIGHT:  (C) 2007 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.
+(C) 2007 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.
 
+@author Michael Barton (Arizona State University)
 """
 
 import  wx
 
+from core.gcmd import GMessage
 
 class MapPrint(wx.Printout):
     def __init__(self, canvas):
@@ -90,11 +87,11 @@ class MapPrint(wx.Printout):
         self.canvas.pdc.DrawToDC(dc)
 
         # prints a page number on the page
-#        dc.DrawText("Page: %d" % page, marginX/2, maxY-marginY)
+        # dc.DrawText("Page: %d" % page, marginX/2, maxY-marginY)
 
         return True
 
-class PrintOptions:
+class PrintOptions(wx.Object):
     def __init__(self, parent, mapwin):
         self.mapframe = parent
         self.mapwin = mapwin
@@ -152,7 +149,8 @@ class PrintOptions:
         printout = MapPrint(self.mapwin)
 
         if not printer.Print(self.mapframe, printout, True):
-            wx.MessageBox("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", wx.OK)
+            GMessage(_("There was a problem printing.\n"
+                       "Perhaps your current printer is not set correctly?"))
         else:
             self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() )
         printout.Destroy()

+ 34 - 261
gui/wxpython/gui_modules/mapdisp_window.py

@@ -1,10 +1,9 @@
 """!
-@package mapdisp_window.py
+@package mapdisp.mapwindow
 
 @brief Map display canvas - buffered window.
 
 Classes:
- - MapWindow
  - BufferedWindow
 
 (C) 2006-2011 by the GRASS Development Team
@@ -21,248 +20,22 @@ import os
 import time
 import math
 import sys
-import tempfile
-import traceback
 
 import wx
 
 import grass.script as grass
 
-import dbm
-import gdialogs
-import gcmd
-import utils
-import globalvar
-import gselect
-from debug import Debug
-from preferences import globalSettings as UserSettings
-from units import ConvertValue as UnitsConvertValue
-
+from gui_core.dialogs   import SavedRegion
+from core.gcmd          import RunCommand, GException, GError
+from core.debug         import Debug
+from core.settings      import UserSettings
+from gui_core.mapwindow import MapWindow
 try:
     import grass.lib.gis as gislib
     haveCtypes = True
 except ImportError:
     haveCtypes = False
 
-class MapWindow(object):
-    """!Abstract map display window class
-    
-    Superclass for BufferedWindow class (2D display mode), and GLWindow
-    (3D display mode).
-    
-    Subclasses have to define
-     - _bindMouseEvents method which binds MouseEvent handlers
-     - Pixel2Cell
-     - Cell2Pixel (if it is possible)
-    
-    """
-    def __init__(self, parent, id = wx.ID_ANY,
-                 Map = None, tree = None, lmgr = None, **kwargs):
-        self.parent = parent # MapFrame
-        self.Map    = Map
-        self.tree   = tree
-        self.lmgr   = lmgr
-        
-        # mouse attributes -- position on the screen, begin and end of
-        # dragging, and type of drawing
-        self.mouse = {
-            'begin': [0, 0], # screen coordinates
-            'end'  : [0, 0],
-            'use'  : "pointer",
-            'box'  : "point"
-            }
-        # last east, north coordinates, changes on mouse motion
-        self.lastEN = None 
-        
-        # stores overridden cursor
-        self._overriddenCursor = None
-
-    def RegisterMouseEventHandler(self, event, handler, cursor = None):
-        """!Binds event handler
-        
-        Call event.Skip() in handler to allow default processing in MapWindow.
-        
-        \code
-        # your class methods
-        def OnButton(self, event):
-            # current map display's map window
-            # expects LayerManager to be the parent
-            self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
-            if self.mapwin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
-                                                     wx.StockCursor(wx.CURSOR_CROSS)):
-                self.parent.GetLayerTree().GetMapDisplay().Raise()
-            else:
-                # handle that you cannot get coordinates
-        
-        def OnMouseAction(self, event):
-            # get real world coordinates of mouse click
-            coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
-            self.text.SetLabel('Coor: ' + str(coor))
-            self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN)
-            event.Skip()
-        \endcode
-        
-        @param event one of mouse events
-        @param handler function to handle event
-        @param cursor cursor which temporary overrides current cursor
-        
-        @return True if successful
-        @return False if event cannot be bind
-        """
-        
-        # if it is a VDigitWindow it cannot be used
-        # hasattr is ugly
-        if hasattr(self, "digit"):
-            return False
-        
-        self.Bind(event, handler)
-        self.mouse['useBeforeGenericEvent'] = self.mouse['use']
-        self.mouse['use'] = 'genericEvent'
-        
-        if cursor:
-            self._overriddenCursor = self.GetCursor()
-            self.SetCursor(cursor)
-        
-        return True
-
-
-    def UnregisterMouseEventHandler(self, event):
-        """!Unbinds event handler a restores previous state
-        
-        You should unbind to restore normal MapWindow behaviour.
-        Note that this operation will unbind any other external (non-MapWindow) handlers.
-        
-        @param event event to unbind
-        
-        @return True if successful
-        @return False if event cannot be unbind
-        """
-        if hasattr(self, "digit"):
-            return False
-        
-        # it is not yet possible in wxPython to unbind exact event
-        ret = self.Unbind(event)
-        
-        # restore bind state
-        self._bindMouseEvents()
-        
-        # restore mouse use (previous state)
-        self.mouse['use'] = self.mouse['useBeforeGenericEvent']
-        
-        # restore overridden cursor
-        if self._overriddenCursor:
-            self.SetCursor(self._overriddenCursor)
-        
-        return ret
-    
-    def Pixel2Cell(self, (x, y)):
-        raise NotImplementedError()
-    
-    def Cell2Pixel(self, (east, north)):
-        raise NotImplementedError()
-
-    def OnMotion(self, event):
-        """!Tracks mouse motion and update statusbar
-        
-        @see GetLastEN
-        """
-        try:
-            self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
-        except (ValueError):
-            self.lastEN = None
-        # FIXME: special case for vdigit and access to statusbarManager
-        if self.parent.statusbarManager.GetMode() == 0: # Coordinates            
-            updated = False
-            if hasattr(self, "digit"):
-                precision = int(UserSettings.Get(group = 'projection', key = 'format',
-                                             subkey = 'precision'))
-                updated = self._onMotion(self.lastEN, precision)
-
-            if not updated:
-                self.parent.CoordinatesChanged()
-        
-        event.Skip()
-
-    def GetLastEN(self):
-        """!Returns last coordinates of mouse cursor.
-        
-        @see OnMotion
-        """
-        return self.lastEN
-    
-    def GetLayerByName(self, name, mapType, dataType = 'layer'):
-        """!Get layer from layer tree by nam
-        
-        @param name layer name
-        @param type 'item' / 'layer' / 'nviz'
-
-        @return layer / map layer properties / nviz properties
-        @return None
-        """
-        if not self.tree:
-            return None
-        
-        try:
-            mapLayer = self.Map.GetListOfLayers(l_type = mapType, l_name = name)[0]
-        except IndexError:
-            return None
-        
-        if dataType == 'layer':
-            return mapLayer
-        item = self.tree.FindItemByData('maplayer', mapLayer)
-        if not item:
-            return None
-        if dataType == 'nviz':
-            return self.tree.GetPyData(item)[0]['nviz']
-        
-        return item
-        
-    def GetSelectedLayer(self, type = 'layer', multi = False):
-        """!Get selected layer from layer tree
-        
-        @param type 'item' / 'layer' / 'nviz'
-        @param multi return first selected layer or all
-        
-        @return layer / map layer properties / nviz properties
-        @return None / [] on failure
-        """
-        ret = []
-        if not self.tree or \
-                not self.tree.GetSelection():
-            if multi:
-                return []
-            else:
-                return None
-        
-        if multi and \
-                type == 'item':
-            return self.tree.GetSelections()
-        
-        for item in self.tree.GetSelections():
-            if not item.IsChecked():
-                if multi:
-                    continue
-                else:
-                    return None
-
-            if type == 'item': # -> multi = False
-                return item
-        
-            try:
-                if type == 'nviz':
-                    layer = self.tree.GetPyData(item)[0]['nviz']
-                else:
-                    layer = self.tree.GetPyData(item)[0]['maplayer']
-            except:
-                layer = None
-
-            if multi:
-                ret.append(layer)
-            else:
-                return layer
-            
-        return ret
-    
 class BufferedWindow(MapWindow, wx.Window):
     """!A Buffered window class (2D view mode)
 
@@ -766,8 +539,8 @@ class BufferedWindow(MapWindow, wx.Window):
             else:
                 self.mapfile = self.Map.Render(force = False, mapWindow = self.parent)
             
-        except gcmd.GException, e:
-            gcmd.GError(message = e.value)
+        except GException, e:
+            GError(message = e.value)
             self.mapfile = None
         
         self.img = self.GetImage() # id=99
@@ -1793,15 +1566,15 @@ class BufferedWindow(MapWindow, wx.Window):
         # We ONLY want to set extents here. Don't mess with resolution. Leave that
         # for user to set explicitly with g.region
         new = self.Map.AlignResolution()
-        gcmd.RunCommand('g.region',
-                        parent = self,
-                        overwrite = True,
-                        n = new['n'],
-                        s = new['s'],
-                        e = new['e'],
-                        w = new['w'],
-                        rows = int(new['rows']),
-                        cols = int(new['cols']))
+        RunCommand('g.region',
+                   parent = self,
+                   overwrite = True,
+                   n = new['n'],
+                   s = new['s'],
+                   e = new['e'],
+                   w = new['w'],
+                   rows = int(new['rows']),
+                   cols = int(new['cols']))
         
         if tmpreg:
             os.environ["GRASS_REGION"] = tmpreg
@@ -1810,9 +1583,9 @@ class BufferedWindow(MapWindow, wx.Window):
         """!Set display geometry to match extents in
         saved region file
         """
-        dlg = gdialogs.SavedRegion(parent = self,
-                                   title = _("Zoom to saved region extents"),
-                                   loadsave='load')
+        dlg = SavedRegion(parent = self,
+                          title = _("Zoom to saved region extents"),
+                          loadsave='load')
         
         if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
             dlg.Destroy()
@@ -1840,9 +1613,9 @@ class BufferedWindow(MapWindow, wx.Window):
     def SaveDisplayRegion(self):
         """!Save display extents to named region file.
         """
-        dlg = gdialogs.SavedRegion(parent = self,
-                                   title = _("Save display extents to region file"),
-                                   loadsave='save')
+        dlg = SavedRegion(parent = self,
+                          title = _("Save display extents to region file"),
+                          loadsave='save')
         
         if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
             dlg.Destroy()
@@ -1872,17 +1645,17 @@ class BufferedWindow(MapWindow, wx.Window):
         if tmpreg:
             del os.environ["GRASS_REGION"]
         
-        gcmd.RunCommand('g.region',
-                        overwrite = True,
-                        parent = self,
-                        flags = 'u',
-                        n = new['n'],
-                        s = new['s'],
-                        e = new['e'],
-                        w = new['w'],
-                        rows = int(new['rows']),
-                        cols = int(new['cols']),
-                        save = wind)
+        RunCommand('g.region',
+                   overwrite = True,
+                   parent = self,
+                   flags = 'u',
+                   n = new['n'],
+                   s = new['s'],
+                   e = new['e'],
+                   w = new['w'],
+                   rows = int(new['rows']),
+                   cols = int(new['cols']),
+                   save = wind)
         
         if tmpreg:
             os.environ["GRASS_REGION"] = tmpreg

+ 12 - 12
gui/wxpython/gui_modules/mapdisp_statusbar.py

@@ -1,5 +1,5 @@
 """!
-@package mapdisp_statusbar.py
+@package mapdisp.statusbar
 
 @brief Classes for statusbar management
 
@@ -22,7 +22,6 @@ Classes:
  - SbCompRegionExtent
  - SbProgress
 
-
 (C) 2006-2011 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
@@ -34,10 +33,11 @@ for details.
 
 import wx
 
-import utils
-import gcmd
-from grass.script import core as grass
-from preferences import globalSettings as UserSettings
+from core          import utils
+from core.gcmd     import GMessage, RunCommand
+from core.settings import UserSettings
+
+from grass.script  import core as grass
 
 class SbException:
     """! Exception class used in SbManager and SbItems"""
@@ -572,9 +572,9 @@ class SbGoTo(SbItem):
             else:
                 # reproject values
                 projIn = settings
-                projOut = gcmd.RunCommand('g.proj',
-                                          flags = 'jf',
-                                          read = True)
+                projOut = RunCommand('g.proj',
+                                     flags = 'jf',
+                                     read = True)
                 proj = projIn.split(' ')[0].split('=')[1]
                 if proj in ('ll', 'latlong', 'longlat'):
                     e, n = utils.DMS2Deg(e, n)
@@ -989,9 +989,9 @@ class SbGoToGCP(SbItem):
         mapCoords = self.mapFrame.GetMapCoordList()
         
         if GCPNo < 0 or GCPNo > len(mapCoords): # always false, spin checks it
-            gcmd.GMessage(parent=self,
-                          message="%s 1 - %s." % (_("Valid Range:"),
-                          len(mapCoords)))
+            GMessage(parent=self,
+                     message="%s 1 - %s." % (_("Valid Range:"),
+                                             len(mapCoords)))
             return
 
         if GCPNo == 0:

+ 211 - 0
gui/wxpython/mapdisp/toolbars.py

@@ -0,0 +1,211 @@
+"""!
+@package mapdisp.toolbars
+
+@brief Map display frame - toolbars
+
+Classes:
+ - MapToolbar
+
+(C) 2007-2011 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.
+
+@author Michael Barton
+@author Jachym Cepicky
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+
+from gui_core.toolbars import BaseToolbar
+from icons.icon        import Icons
+from nviz.main         import haveNviz
+from vdigit.main       import haveVDigit
+
+class MapToolbar(BaseToolbar):
+    """!Map Display toolbar
+    """
+    def __init__(self, parent, mapcontent):
+        """!Map Display constructor
+
+        @param parent reference to MapFrame
+        @param mapcontent reference to render.Map (registred by MapFrame)
+        """
+        self.mapcontent = mapcontent # render.Map
+        BaseToolbar.__init__(self, parent = parent) # MapFrame
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # optional tools
+        choices = [ _('2D view'), ]
+        self.toolId = { '2d' : 0 }
+        if self.parent.GetLayerManager():
+            log = self.parent.GetLayerManager().GetLogWindow()
+        
+        if haveNviz:
+            choices.append(_('3D view'))
+            self.toolId['3d'] = 1
+        else:
+            from nviz.main import errorMsg
+            log.WriteCmdLog(_('3D view mode not available'))
+            log.WriteWarning(_('Reason: %s') % str(errorMsg))
+            log.WriteLog(_('Note that the wxGUI\'s 3D view mode is currently disabled '
+                           'on MS Windows (hopefully this will be fixed soon). '
+                           'Please keep an eye out for updated versions of GRASS. '
+                           'In the meantime you can use "NVIZ" from the File menu.'), wrap = 60)
+            
+            self.toolId['3d'] = -1
+
+        if haveVDigit:
+            choices.append(_('Digitize'))
+            if self.toolId['3d'] > -1:
+                self.toolId['vdigit'] = 2
+            else:
+                self.toolId['vdigit'] = 1
+        else:
+            from vdigit.main import errorMsg
+            log.WriteCmdLog(_('Vector digitizer not available'))
+            log.WriteWarning(_('Reason: %s') % errorMsg)
+            log.WriteLog(_('Note that the wxGUI\'s vector digitizer is currently disabled '
+                           '(hopefully this will be fixed soon). '
+                           'Please keep an eye out for updated versions of GRASS. '
+                           'In the meantime you can use "v.digit" from the Develop Vector menu.'), wrap = 60)
+            
+            self.toolId['vdigit'] = -1
+        
+        self.combo = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                 choices = choices,
+                                 style = wx.CB_READONLY, size = (110, -1))
+        self.combo.SetSelection(0)
+        
+        self.comboid = self.AddControl(self.combo)
+        self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
+        
+        # realize the toolbar
+        self.Realize()
+        
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.combo.Hide()
+        self.combo.Show()
+        
+        self.action = { 'id' : self.pointer }
+        self.defaultAction = { 'id' : self.pointer,
+                               'bind' : self.parent.OnPointer }
+        
+        self.OnTool(None)
+        
+        self.EnableTool(self.zoomback, False)
+        
+        self.FixSize(width = 90)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['displayWindow']
+        return self._getToolbarData((('displaymap', icons['display'],
+                                      self.parent.OnDraw),
+                                     ('rendermap', icons['render'],
+                                      self.parent.OnRender),
+                                     ('erase', icons['erase'],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ('pointer', icons['pointer'],
+                                      self.parent.OnPointer,
+                                      wx.ITEM_CHECK),
+                                     ('query', icons['query'],
+                                      self.parent.OnQuery,
+                                      wx.ITEM_CHECK),
+                                     ('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),
+                                     ('zoomextent', icons['zoomExtent'],
+                                      self.parent.OnZoomToMap),
+                                     ('zoomback', icons['zoomBack'],
+                                      self.parent.OnZoomBack),
+                                     ('zoommenu', icons['zoomMenu'],
+                                      self.parent.OnZoomMenu),
+                                     (None, ),
+                                     ('analyze', icons['analyze'],
+                                      self.parent.OnAnalyze),
+                                     (None, ),
+                                     ('dec', icons['overlay'],
+                                      self.parent.OnDecoration),
+                                     (None, ),
+                                     ('savefile', icons['saveFile'],
+                                      self.parent.SaveToFile),
+                                     ('printmap', icons['print'],
+                                      self.parent.PrintMenu),
+                                     (None, ))
+                                    )
+    def InsertTool(self, data):
+        """!Insert tool to toolbar
+        
+        @param data toolbar data"""
+        data = self._getToolbarData(data)
+        for tool in data:
+            self.CreateTool(*tool)
+        self.Realize()
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def RemoveTool(self, tool):
+        """!Remove tool from toolbar
+        
+        @param tool tool id"""
+        self.DeleteTool(tool)
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def ChangeToolsDesc(self, mode2d):
+        """!Change description of zoom tools for 2D/3D view"""
+        if mode2d:
+            set = 'displayWindow'
+        else:
+            set = 'nviz'
+        for i, data in enumerate(self._data):
+            for tool, toolname in (('zoomin', 'zoomIn'),('zoomout', 'zoomOut')):
+                if data[0] == tool:
+                    tmp = list(data)
+                    tmp[4] = Icons[set][toolname].GetDesc()
+                    self._data[i] = tuple(tmp)
+                
+    def OnSelectTool(self, event):
+        """!Select / enable tool available in tools list
+        """
+        tool =  event.GetSelection()
+        
+        if tool == self.toolId['2d']:
+            self.ExitToolbars()
+            self.Enable2D(True)
+            self.ChangeToolsDesc(mode2d = True)            
+        
+        elif tool == self.toolId['3d'] and \
+                not (self.parent.MapWindow3D and self.parent.IsPaneShown('3d')):
+            self.ExitToolbars()
+            self.parent.AddNviz()
+            
+        elif tool == self.toolId['vdigit'] and \
+                not self.parent.GetToolbar('vdigit'):
+            self.ExitToolbars()
+            self.parent.AddToolbar("vdigit")
+            self.parent.MapWindow.SetFocus()
+        
+    def ExitToolbars(self):
+        if self.parent.GetToolbar('vdigit'):
+            self.parent.toolbars['vdigit'].OnExit()
+        if self.parent.GetLayerManager().IsPaneShown('toolbarNviz'):
+            self.parent.RemoveNviz()
+        
+    def Enable2D(self, enabled):
+        """!Enable/Disable 2D display mode specific tools"""
+        for tool in (self.zoommenu,
+                     self.analyze,
+                     self.printmap):
+            self.EnableTool(tool, enabled)

+ 78 - 81
gui/wxpython/gui_modules/colorrules.py

@@ -1,8 +1,8 @@
 """
-@package colorrules.py
+@package module.colorrules
 
-@brief Dialog for interactive management of raster color tables and
-vector rgb_column attributes.
+@brief Dialog for interactive management of raster/vector color tables
+and color rules.
 
 Classes:
  - RulesPanel
@@ -22,29 +22,26 @@ This program is free software under the GNU General Public License
 """
 
 import os
-import sys
 import shutil
 import copy
 import tempfile
 
 import wx
-import wx.lib.colourselect as csel
-import wx.lib.scrolledpanel as scrolled
+import wx.lib.colourselect     as csel
+import wx.lib.scrolledpanel    as scrolled
 import wx.lib.filebrowsebutton as filebrowse
 
 import grass.script as grass
 
-import dbm
-import gcmd
-import globalvar
-import gselect
-import render
-import utils
-import menuform
-from debug import Debug as Debug
-from preferences import globalSettings as UserSettings
-from nviz_mapdisp import wxUpdateProperties
-
+from core             import globalvar
+from core             import utils
+from core.gcmd        import GMessage, RunCommand
+from gui_core.gselect import Select, LayerSelect, ColumnSelect, VectorDBInfo
+from core.render      import Map
+from core.forms       import GUI
+from core.debug       import Debug as Debug
+from core.settings    import UserSettings
+from nviz.mapwindow   import wxUpdateProperties
 
 class RulesPanel:
     def __init__(self, parent, mapType, attributeType, properties, panelWidth = 180):
@@ -294,7 +291,7 @@ class RulesPanel:
                 continue
                 
         if message:
-            gcmd.GMessage(parent = self.parent, message = message)
+            GMessage(parent = self.parent, message = message)
             return False
         
         return True
@@ -326,7 +323,7 @@ class ColorTable(wx.Frame):
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
         # instance of render.Map to be associated with display
-        self.Map  = render.Map() 
+        self.Map  = Map() 
         
         # input map to change
         self.inmap = ''
@@ -375,9 +372,9 @@ class ColorTable(wx.Frame):
                                 label = " %s " % maplabel)
         inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
 
-        self.selectionInput = gselect.Select(parent, id = wx.ID_ANY,
-                                             size = globalvar.DIALOG_GSELECT_SIZE,
-                                             type = self.mapType)
+        self.selectionInput = Select(parent, id = wx.ID_ANY,
+                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                     type = self.mapType)
         # layout
         inputSizer.Add(item = self.selectionInput,
                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
@@ -531,7 +528,7 @@ class ColorTable(wx.Frame):
         """
         ret = self.CreateColorTable()
         if not ret:
-            gcmd.GMessage(parent = self, message = _("No valid color rules given."))
+            GMessage(parent = self, message = _("No valid color rules given."))
         
         if ret:
             display = self.parent.GetLayerTree().GetMapDisplay()
@@ -563,8 +560,8 @@ class ColorTable(wx.Frame):
                 continue
             rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
         if not rulestxt:
-            gcmd.GMessage(message = _("Nothing to save."),
-                          parent = self)
+            GMessage(message = _("Nothing to save."),
+                     parent = self)
             return
         
         fd = open(path, 'w')
@@ -596,7 +593,7 @@ class ColorTable(wx.Frame):
             try:
                 value, color = map(lambda x: x.strip(), line.split(' '))
             except ValueError:
-                gcmd.GMessage(parent = self, message = _("Invalid color table format"))
+                GMessage(parent = self, message = _("Invalid color table format"))
                 self.rulesPanel.Clear()
                 return
             
@@ -651,7 +648,7 @@ class ColorTable(wx.Frame):
         cmd = utils.CmdToTuple(cmd)
         
         if self.inmap:
-            ctable = gcmd.RunCommand(cmd[0], **cmd[1])
+            ctable = RunCommand(cmd[0], **cmd[1])
         else:
             self.OnPreview()
             return
@@ -673,8 +670,8 @@ class ColorTable(wx.Frame):
             if rule['value'] not in ('nv', 'default') and \
                     rule['value'][-1] != '%' and \
                     not self._IsNumber(rule['value']):
-                gcmd.GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
-                            parent = self)
+                GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
+                       parent = self)
                 return False
             
             rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
@@ -696,7 +693,7 @@ class ColorTable(wx.Frame):
                                     and self.properties['sourceColumn'] != 'cat':
             cmd.append('column=%s' % self.properties['sourceColumn'])
         cmd = utils.CmdToTuple(cmd)
-        ret = gcmd.RunCommand(cmd[0], **cmd[1])               
+        ret = RunCommand(cmd[0], **cmd[1])               
         if ret != 0:
             return False
         
@@ -718,10 +715,10 @@ class ColorTable(wx.Frame):
         
     def RunHelp(self, cmd):
         """!Show GRASS manual page"""
-        gcmd.RunCommand('g.manual',
-                        quiet = True,
-                        parent = self,
-                        entry = cmd)
+        RunCommand('g.manual',
+                   quiet = True,
+                   parent = self,
+                   entry = cmd)
         
     def _IsNumber(self, s):
         """!Check if 's' is a number"""
@@ -877,10 +874,10 @@ class RasterColorTable(ColorTable):
                 shutil.copyfile(colrtemp, old_colrtable)
                 os.remove(colrtemp)
             else:
-                gcmd.RunCommand('r.colors',
-                                parent = self,
-                                flags = 'r',
-                                map = self.inmap)
+                RunCommand('r.colors',
+                           parent = self,
+                           flags = 'r',
+                           map = self.inmap)
         
     def OnHelp(self, event):
         """!Show GRASS manual page"""
@@ -969,10 +966,10 @@ class VectorColorTable(ColorTable):
                                             label = labels[1])
                                                 
         self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)
-        self.layerSelect = gselect.LayerSelect(parent)
-        self.sourceColumn = gselect.ColumnSelect(parent)
-        self.fromColumn = gselect.ColumnSelect(parent)
-        self.toColumn = gselect.ColumnSelect(parent)
+        self.layerSelect = LayerSelect(parent)
+        self.sourceColumn = ColumnSelect(parent)
+        self.fromColumn = ColumnSelect(parent)
+        self.toColumn = ColumnSelect(parent)
         self.addColumn = wx.Button(parent, id = wx.ID_ANY,
                                              label = _('Add column'))
         self.addColumn.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
@@ -1097,8 +1094,8 @@ class VectorColorTable(ColorTable):
                                 style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
         if dlg.ShowModal() == wx.ID_YES:
             dlg.Destroy()
-            menuform.GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap], 
-                                                      completed = (self.CreateAttrTable, self.inmap, ''))
+            GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap], 
+                                            completed = (self.CreateAttrTable, self.inmap, ''))
         else:
             dlg.Destroy()
   
@@ -1168,12 +1165,12 @@ class VectorColorTable(ColorTable):
                 message = _("Selected map <%s> is not in current mapset <%s>. "
                             "Attribute table cannot be edited.") % \
                             (self.inmap, grass.gisenv()['MAPSET'])
-                wx.CallAfter(gcmd.GMessage, parent = self, message = message)
+                wx.CallAfter(GMessage, parent = self, message = message)
                 self.DisableClearAll()
                 return
                 
         # check for db connection
-        self.dbInfo = gselect.VectorDBInfo(self.inmap)
+        self.dbInfo = VectorDBInfo(self.inmap)
         enable = True
         if not len(self.dbInfo.layers): # no connection
             if not (self.version7 and self.attributeType == 'color'): # otherwise it doesn't matter
@@ -1234,11 +1231,11 @@ class VectorColorTable(ColorTable):
             modul = 'v.db.addcolumn'
         else:
             modul = 'v.db.addcol'
-        ret = gcmd.RunCommand(modul,
-                              parent = self,
-                              map = self.inmap,
-                              layer = self.properties['layer'],
-                              column = '%s %s' % (self.properties['tmpColumn'], type))
+        ret = RunCommand(modul,
+                         parent = self,
+                         map = self.inmap,
+                         layer = self.properties['layer'],
+                         column = '%s %s' % (self.properties['tmpColumn'], type))
         
     def DeleteTemporaryColumn(self):
         """!Delete temporary column"""
@@ -1250,10 +1247,10 @@ class VectorColorTable(ColorTable):
                 modul = 'v.db.dropcolumn'
             else:
                 modul = 'v.db.dropcol'
-            ret = gcmd.RunCommand(modul,
-                                  map = self.inmap,
-                                  layer = self.properties['layer'],
-                                  column = self.properties['tmpColumn'])
+            ret = RunCommand(modul,
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             column = self.properties['tmpColumn'])
         
     def OnLayerSelection(self, event):
         # reset choices in column selection comboboxes if layer changes
@@ -1299,11 +1296,11 @@ class VectorColorTable(ColorTable):
                 modul = 'v.db.addcolumn'
             else:
                 modul = 'v.db.addcol'
-            ret = gcmd.RunCommand(modul,
-                                  map = self.inmap,
-                                  layer = self.properties['layer'],
-                                  columns = '%s %s' % (self.columnsProp[self.attributeType]['name'],
-                                            self.columnsProp[self.attributeType]['type1']))
+            ret = RunCommand(modul,
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             columns = '%s %s' % (self.columnsProp[self.attributeType]['name'],
+                                                  self.columnsProp[self.attributeType]['type1']))
             self.toColumn.InsertColumns(self.inmap, self.properties['layer'],
                                         type = self.columnsProp[self.attributeType]['type2'])
             self.toColumn.SetStringSelection(self.columnsProp[self.attributeType]['name'])
@@ -1311,15 +1308,15 @@ class VectorColorTable(ColorTable):
             
             self.LoadTable()
         else:
-            gcmd.GMessage(parent = self,
-                          message = _("%s column already exists.") % \
-                                    self.columnsProp[self.attributeType]['name'])
+            GMessage(parent = self,
+                     message = _("%s column already exists.") % \
+                         self.columnsProp[self.attributeType]['name'])
                         
     def CreateAttrTable(self, dcmd, layer, params, propwin):
         """!Create attribute table"""
         if dcmd:
             cmd = utils.CmdToTuple(dcmd)
-            ret = gcmd.RunCommand(cmd[0], **cmd[1])
+            ret = RunCommand(cmd[0], **cmd[1])
             if ret == 0:
                 self.OnSelectionInput(None)
                 return True
@@ -1355,14 +1352,14 @@ class VectorColorTable(ColorTable):
         if self.inmap:
             outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
             sep = '|'
-            ret = gcmd.RunCommand('v.db.select',
-                                  quiet = True,
-                                  flags = 'c',
-                                  map = self.inmap,
-                                  layer = self.properties['layer'],
-                                  columns = columns,
-                                  fs = sep,
-                                  stdout = outFile)
+            ret = RunCommand('v.db.select',
+                             quiet = True,
+                             flags = 'c',
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             columns = columns,
+                             fs = sep,
+                             stdout = outFile)
         else:
             self.preview.EraseMap()
             busy.Destroy()
@@ -1512,10 +1509,10 @@ class VectorColorTable(ColorTable):
                 shutil.copyfile(colrtemp, old_colrtable)
                 os.remove(colrtemp)
             else:
-                gcmd.RunCommand('v.colors',
-                                parent = self,
-                                flags = 'r',
-                                map = self.inmap)
+                RunCommand('v.colors',
+                           parent = self,
+                           flags = 'r',
+                           map = self.inmap)
     def OnColumnPreview(self):
         """!Update preview (based on computational region)"""
         if not self.inmap or not self.properties['tmpColumn']:
@@ -1595,7 +1592,7 @@ class VectorColorTable(ColorTable):
             else:
                 rgb_col = self.properties['storeColumn']
                 if not self.properties['storeColumn']:
-                    gcmd.GMessage(self.parent, message = _("Please select column to save values to."))
+                    GMessage(self.parent, message = _("Please select column to save values to."))
                     
             rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
                                                                 rgb_col,
@@ -1611,9 +1608,9 @@ class VectorColorTable(ColorTable):
         finally:
             output.close()
         
-        gcmd.RunCommand('db.execute',
-                        parent = self,
-                        input = gtemp)
+        RunCommand('db.execute',
+                   parent = self,
+                   input = gtemp)
         return True
     
     def OnCancel(self, event):
@@ -1656,7 +1653,7 @@ class ThematicVectorTable(VectorColorTable):
         """
         ret = self.CreateColorTable()
         if not ret:
-            gcmd.GMessage(parent = self, message = _("No valid color rules given."))
+            GMessage(parent = self, message = _("No valid color rules given."))
         
         data = self.parent.GetLayerData(nvizType = 'vector')
         data['vector']['points']['thematic']['layer'] = int(self.properties['layer'])

+ 54 - 24
gui/wxpython/gui_modules/histogram.py

@@ -1,11 +1,12 @@
 """!
-@package histogram.py
+@package modules.histogram
 
-Plotting histogram
+Plotting histogram based on d.histogram
 
 Classes:
  - BufferedWindow
- - HistFrame
+ - HistogramFrame
+ - HistogramToolbar
 
 (C) 2007, 2010-2011 by the GRASS Development Team
 This program is free software under the GNU General Public License
@@ -16,22 +17,20 @@ This program is free software under the GNU General Public License
 """
 
 import os
-import sys
 
 import wx
 
-import render
-import menuform
-import disp_print
-import utils
-import gdialogs
-import globalvar
-
-from toolbars    import HistogramToolbar
-from preferences import DefaultFontDialog
-from debug       import Debug
-from icon        import Icons
-from gcmd        import GError
+from core                 import globalvar
+from core.render          import Map
+from core.forms           import GUI
+from mapdisp.gprint       import PrintOptions
+from core.utils           import GetLayerNameFromCmd
+from gui_core.dialogs     import GetImageHandlers, ImageSizeDialog
+from gui_core.preferences import DefaultFontDialog
+from core.debug           import Debug
+from icon                 import Icons
+from core.gcmd            import GError
+from gui_core.toolbars    import BaseToolbar
 
 class BufferedWindow(wx.Window):
     """!A Buffered window class.
@@ -269,7 +268,7 @@ class HistFrame(wx.Frame):
         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
-        self.Map   = render.Map()  # instance of render.Map to be associated with display
+        self.Map   = Map()         # instance of render.Map to be associated with display
         self.layer = None          # reference to layer with histogram
         
         # Init variables
@@ -300,7 +299,7 @@ class HistFrame(wx.Frame):
         self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
         
         # Init print module and classes
-        self.printopt = disp_print.PrintOptions(self, self.HistWindow)
+        self.printopt = PrintOptions(self, self.HistWindow)
         
         # Add layer to the map
         self.layer = self.Map.AddLayer(type = "command", name = 'histogram', command = ['d.histogram'],
@@ -318,16 +317,16 @@ class HistFrame(wx.Frame):
         if self.mapname != '':
             cmd.append('map=%s' % self.mapname)
         
-        menuform.GUI(parent = self).ParseCommand(cmd,
-                                                 completed = (self.GetOptData, None, self.params))
+        GUI(parent = self).ParseCommand(cmd,
+                                        completed = (self.GetOptData, None, self.params))
         
     def GetOptData(self, dcmd, layer, params, propwin):
         """!Callback method for histogram command generated by dialog
         created in menuform.py
         """
         if dcmd:
-            name, found = utils.GetLayerNameFromCmd(dcmd, fullyQualified = True,
-                                                    layerType = 'raster')
+            name, found = GetLayerNameFromCmd(dcmd, fullyQualified = True,
+                                              layerType = 'raster')
             if not found:
                 GError(parent = propwin,
                        message = _("Raster map <%s> not found") % name)
@@ -388,10 +387,10 @@ class HistFrame(wx.Frame):
     def SaveToFile(self, event):
         """!Save to file
         """
-        filetype, ltype = gdialogs.GetImageHandlers(self.HistWindow.img)
+        filetype, ltype = GetImageHandlers(self.HistWindow.img)
         
         # get size
-        dlg = gdialogs.ImageSizeDialog(self)
+        dlg = ImageSizeDialog(self)
         dlg.CentreOnParent()
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
@@ -461,3 +460,34 @@ class HistFrame(wx.Frame):
         self.Map.Clean()
         self.Destroy()
         
+class HistogramToolbar(BaseToolbar):
+    """!Histogram toolbar (see histogram.py)
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['displayWindow']
+        return self._getToolbarData((('histogram', icons["histogram"],
+                                      self.parent.OnOptions),
+                                     ('rendermao', icons["display"],
+                                      self.parent.OnRender),
+                                     ('erase', icons["erase"],
+                                      self.parent.OnErase),
+                                     ('font', Icons['misc']["font"],
+                                      self.parent.SetHistFont),
+                                     (None, ),
+                                     ('save', icons["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('hprint', icons["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('quit', Icons['misc']["quit"],
+                                      self.parent.OnQuit))
+                                    )

+ 20 - 28
gui/wxpython/gui_modules/mcalc_builder.py

@@ -1,13 +1,12 @@
 """!
-@package mcalc_builder.py
+@package modules mcalc_builder
 
-@brief Map calculator, wrapper for r.mapcalc
+@brief Map calculator, GUI wrapper for r.mapcalc
 
 Classes:
  - MapCalcFrame
 
 (C) 2008, 2011 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.
 
@@ -17,23 +16,16 @@ This program is free software under the GNU General Public License
 """
 
 import os
-import sys
-import time
 
-import globalvar
+from core import globalvar
 import wx
 
 import grass.script as grass
 
-import gcmd
-import gselect
-import menuform
-try:
-    import subprocess
-except:
-    sys.path.append(os.path.join(globalvar.ETCWXDIR, "compat"))
-    import subprocess
-from preferences import globalSettings as UserSettings
+from core.gcmd        import GError, RunCommand
+from gui_core.gselect import Select
+from core.forms       import GUI
+from core.settings    import UserSettings
 
 class MapCalcFrame(wx.Frame):
     """!Mapcalc Frame class. Calculator-style window to create and run
@@ -215,8 +207,8 @@ class MapCalcFrame(wx.Frame):
             self.mapsellabel.SetLabel(_('Insert existing 3D raster map'))
         else:
             self.mapsellabel.SetLabel(_('Insert existing raster map'))
-        self.mapselect = gselect.Select(parent = self.panel, id = wx.ID_ANY, size = (250, -1),
-                                        type = element, multiple = False)
+        self.mapselect = Select(parent = self.panel, id = wx.ID_ANY, size = (250, -1),
+                                type = element, multiple = False)
         self.functlabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
                                         label = _('Insert mapcalc function'))
         self.function = wx.ComboBox(parent = self.panel, id = wx.ID_ANY, 
@@ -453,16 +445,16 @@ class MapCalcFrame(wx.Frame):
         """
         name = self.newmaptxt.GetValue().strip()
         if not name:
-            gcmd.GError(parent = self,
-                        message = _("You must enter the name of "
-                                    "a new raster map to create."))
+            GError(parent = self,
+                   message = _("You must enter the name of "
+                               "a new raster map to create."))
             return
         
         expr = self.text_mcalc.GetValue().strip().replace("\n", " ")
         if not expr:
-            gcmd.GError(parent = self,
-                        message = _("You must enter an expression "
-                                    "to create a new raster map."))
+            GError(parent = self,
+                   message = _("You must enter an expression "
+                               "to create a new raster map."))
             return
         
         if self.log:
@@ -476,9 +468,9 @@ class MapCalcFrame(wx.Frame):
                 overwrite = True
             else:
                 overwrite = False
-            gcmd.RunCommand(self.cmd,
-                            expression = "%s=%s" % (name, expr),
-                            overwrite = overwrite)
+            RunCommand(self.cmd,
+                       expression = "%s=%s" % (name, expr),
+                       overwrite = overwrite)
         
     def OnDone(self, cmd, returncode):
         """!Add create map to the layer tree"""
@@ -560,7 +552,7 @@ class MapCalcFrame(wx.Frame):
     def OnHelp(self, event):
         """!Launches r.mapcalc help
         """
-        gcmd.RunCommand('g.manual', parent = self, entry = self.cmd)
+        RunCommand('g.manual', parent = self, entry = self.cmd)
         
     def OnClose(self,event):
         """!Close window"""
@@ -576,7 +568,7 @@ class MapCalcFrame(wx.Frame):
             expr += '='
         expr += mctxt
         
-        menuform.GUI(parent = self).ParseCommand(cmd = [self.cmd, 'expression=' + expr])
+        GUI(parent = self).ParseCommand(cmd = [self.cmd, 'expression=' + expr])
         
 if __name__ == "__main__":
     import gettext

+ 9 - 11
gui/wxpython/gui_modules/ogc_services.py

@@ -1,5 +1,5 @@
 """!
-@package ogc_services.py
+@package module.ogc_services
 
 @brief Dialogs for OGC services
 
@@ -10,7 +10,6 @@ List of classes:
  - LayersList
 
 (C) 2009 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.
 
@@ -21,9 +20,8 @@ import wx
 from wx.gizmos import TreeListCtrl
 import wx.lib.mixins.listctrl as listmix
 
-import gcmd
-
-from preferences import globalSettings as UserSettings
+from core.gcmd     import RunCommand
+from core.settings import UserSettings
 
 class WMSDialog(wx.Dialog):
     def __init__(self, parent, service = 'wms',
@@ -167,12 +165,12 @@ class WMSDialog(wx.Dialog):
             return # not reachable
 
         layers = {}
-        ret = gcmd.RunCommand('r.in.wms',
-                              quiet = True,
-                              parent = self,
-                              read = True,
-                              flags = 'l',
-                              mapserver = server)
+        ret = RunCommand('r.in.wms',
+                         quiet = True,
+                         parent = self,
+                         read = True,
+                         flags = 'l',
+                         mapserver = server)
         
         if not ret:
             self.list.LoadData()

+ 25 - 32
gui/wxpython/gui_modules/vclean.py

@@ -1,5 +1,5 @@
 """
-@package vclean.py
+@package modules.vclean
 
 @brief Dialog for interactive construction of vector cleaning
 operations
@@ -8,7 +8,6 @@ Classes:
  - VectorCleaningFrame
 
 (C) 2010-2011 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.
 
@@ -16,27 +15,21 @@ This program is free software under the GNU General Public License
 """
 
 import os
-import sys
-import shutil
 
 import wx
 import wx.lib.scrolledpanel as scrolled
 
 from grass.script import core as grass
 
-import dbm
-import gcmd
-import globalvar
-import gselect
-import render
-import utils
-from debug import Debug as Debug
-from preferences import globalSettings as UserSettings
+from core.gcmd        import RunCommand
+from core             import globalvar
+from gui_core.gselect import Select
+from core.debug       import Debug
+from core.settings    import UserSettings
 
 class VectorCleaningFrame(wx.Frame):
-    def __init__(self, parent, id=wx.ID_ANY, title=_('set up vector cleaning tools'),
-                 pos=wx.DefaultPosition, size=(-1, -1),
-                 style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
+    def __init__(self, parent, id = wx.ID_ANY, title = _('Set up vector cleaning tools'),
+                 style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
                  **kwargs):
         """!
         Dialog for interactively defining vector cleaning tools
@@ -126,9 +119,9 @@ class VectorCleaningFrame(wx.Frame):
         # top controls
         self.inmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY,
                                          label= _('Select input vector map:'))
-        self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY,
-                                             size=globalvar.DIALOG_GSELECT_SIZE,
-                                             type='vector')
+        self.selectionInput = Select(parent=self, id=wx.ID_ANY,
+                                     size=globalvar.DIALOG_GSELECT_SIZE,
+                                     type='vector')
 	self.ftype_check = {}
         ftypeBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
                                 label=_(' Feature type: '))
@@ -136,9 +129,9 @@ class VectorCleaningFrame(wx.Frame):
 
         self.outmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY,
                                          label= _('Select output vector map:'))
-        self.selectionOutput = gselect.Select(parent=self, id=wx.ID_ANY,
-                                             size=globalvar.DIALOG_GSELECT_SIZE,
-                                             type='vector')
+        self.selectionOutput = Select(parent=self, id=wx.ID_ANY,
+                                      size=globalvar.DIALOG_GSELECT_SIZE,
+                                      type='vector')
         
         self.overwrite = wx.CheckBox(parent=self, id=wx.ID_ANY,
                                        label=_('Allow output files to overwrite existing files'))
@@ -477,23 +470,23 @@ class VectorCleaningFrame(wx.Frame):
 	    else:
 		overwrite = False
             
-	    gcmd.RunCommand(self.cmd,
-			    input = self.inmap,
-			    output = self.outmap,
-			    type = self.ftype_string,
-			    tool = self.tools_string,
-			    thresh = self.thresh_string,
-			    overwrite = overwrite)
+	    RunCommand(self.cmd,
+                       input = self.inmap,
+                       output = self.outmap,
+                       type = self.ftype_string,
+                       tool = self.tools_string,
+                       thresh = self.thresh_string,
+                       overwrite = overwrite)
 
     def OnClose(self, event):
         self.Destroy()
         
     def OnHelp(self, event):
         """!Show GRASS manual page"""
-        gcmd.RunCommand('g.manual',
-                        quiet = True,
-                        parent = self,
-                        entry = self.cmd)
+        RunCommand('g.manual',
+                   quiet = True,
+                   parent = self,
+                   entry = self.cmd)
         
     def OnCopy(self, event):
         """!Copy the command"""

+ 3 - 3
gui/wxpython/gui_modules/nviz_animation.py

@@ -1,5 +1,5 @@
 """!
-@package nviz_animation.py
+@package nviz.animation
 
 @brief Nviz (3D view) animation
 
@@ -14,16 +14,16 @@ for details.
 
 @author Anna Kratochvilova <kratochanna gmail.com> 
 """
+
 import os
 import copy
 
 import wx
 from wx.lib.newevent import NewEvent
 
-wxAnimationFinished, EVT_ANIM_FIN = NewEvent()
+wxAnimationFinished,    EVT_ANIM_FIN        = NewEvent()
 wxAnimationUpdateIndex, EVT_ANIM_UPDATE_IDX = NewEvent()
 
-
 class Animation:
     """!Class represents animation as a sequence of states (views).
     It enables to record, replay the sequence and finally generate

+ 8 - 14
gui/wxpython/gui_modules/nviz.py

@@ -1,5 +1,5 @@
 """!
-@package nviz.py
+@package nviz.main
 
 @brief Nviz (3D view) module
 
@@ -10,9 +10,8 @@ Map Display supports standard 2D view mode ('mapdisp' module) and
 
 (C) 2008, 2010-2011 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.
 
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
 @author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
@@ -20,15 +19,10 @@ for details.
 
 errorMsg = ''
 
-import os
-import sys
-
-import wx
-import globalvar
 try:
     from wx import glcanvas
-    import nviz_mapdisp
-    import nviz_tools
+    import nviz.mapdisp
+    import nviz.tools
     import wxnviz
     haveNviz = True
 except ImportError, err:
@@ -36,8 +30,8 @@ except ImportError, err:
     errorMsg = err
 
 if haveNviz:
-    GLWindow = nviz_mapdisp.GLWindow
-    NvizToolWindow = nviz_tools.NvizToolWindow
+    GLWindow       = nviz.mapdisp.GLWindow
+    NvizToolWindow = nviz.tools.NvizToolWindow
 else:
-    GLWindow = None
+    GLWindow       = None
     NvizToolWindow = None

+ 21 - 23
gui/wxpython/gui_modules/nviz_mapdisp.py

@@ -1,5 +1,5 @@
 """!
-@package nviz_mapdisp.py
+@package nviz.mapwindow
 
 @brief wxGUI 3D view mode (map canvas)
 
@@ -25,26 +25,24 @@ import time
 import copy
 import math
 import types
-import tempfile
 
 from threading import Thread
 
 import wx
-import wx.lib.scrolledpanel as scrolled
-from wx.lib.newevent import NewEvent
-from wx import glcanvas
+from   wx.lib.newevent import NewEvent
+from   wx              import glcanvas
 
-import gcmd
-import globalvar
 import grass.script as grass
-from debug          import Debug
-from mapdisp_window import MapWindow
-from goutput        import wxCmdOutput
-from preferences    import globalSettings as UserSettings
-from workspace      import Nviz as NvizDefault
-from nviz_animation import Animation
 
-import wxnviz
+from core.gcmd        import GMessage, GException, GError
+from core.debug       import Debug
+from mapdisp.window   import MapWindow
+from gui_core.goutput import wxCmdOutput
+from core.settings    import UserSettings
+from nviz.workspace   import NvizSettings
+from nviz.animation   import Animation
+
+import nviz.wxcore as wxnviz
 
 wxUpdateProperties, EVT_UPDATE_PROP  = NewEvent()
 wxUpdateView,       EVT_UPDATE_VIEW  = NewEvent()
@@ -150,7 +148,7 @@ class GLWindow(MapWindow, glcanvas.GLCanvas):
         self.size = (0, 0)
         
         # default values
-        self.nvizDefault = NvizDefault()
+        self.nvizDefault = NvizSettings()
         self.view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
         self.iview = UserSettings.Get(group = 'nviz', key = 'view', internal = True)
         self.light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
@@ -505,9 +503,9 @@ class GLWindow(MapWindow, glcanvas.GLCanvas):
             texture.SetBounds(bbox)
             
         if not texture.textureId: # texture too big
-            gcmd.GMessage(parent = self, message = 
-                          _("Image is too large, your OpenGL implementation "
-                            "supports maximum texture size %d px.") % texture.maxSize)
+            GMessage(parent = self, message = 
+                     _("Image is too large, your OpenGL implementation "
+                       "supports maximum texture size %d px.") % texture.maxSize)
             return texture
             
         self.imagelist.append(texture)
@@ -1293,7 +1291,7 @@ class GLWindow(MapWindow, glcanvas.GLCanvas):
                         self.LoadVector(item, points = True)
                     if nlines > 0:
                         self.LoadVector(item, points = False)
-            except gcmd.GException, e:
+            except GException, e:
                 GError(parent = self,
                        message = e.value)
             self.init = False
@@ -1336,9 +1334,9 @@ class GLWindow(MapWindow, glcanvas.GLCanvas):
                     if nlines > 0:
                         self.UnloadVector(layer, points = False)
                         
-            except gcmd.GException, e:
-                gcmd.GError(parent = self,
-                            message = e.value)
+            except GException, e:
+                GError(parent = self,
+                       message = e.value)
         
         if force and self.baseId > 0: # unload base surface when quitting
             ret = self._display.UnloadSurface(self.baseId)
@@ -2084,7 +2082,7 @@ class GLWindow(MapWindow, glcanvas.GLCanvas):
                 error = _("Unable to set data layer properties (id = %d)") % id
 
             if error:
-                raise gcmd.GException(_("Setting data layer properties failed.\n\n%s") % error)
+                raise GException(_("Setting data layer properties failed.\n\n%s") % error)
             
             for prop in ('size', 'width', 'marker', 'color'):
                 if 'update' in data[prop]:

+ 4 - 5
gui/wxpython/gui_modules/nviz_preferences.py

@@ -1,5 +1,5 @@
 """
-@package nviz_preferences.py
+@package nviz.preferences
 
 @brief Nviz (3D view) preferences window
 
@@ -16,15 +16,14 @@ This program is free software under the GNU General Public License
 @author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
 """
 
-import types
 import copy
 
 import wx
 import wx.lib.colourselect as csel
 
-import globalvar
-from preferences import globalSettings as UserSettings
-from preferences import PreferencesBaseDialog
+from core                 import globalvar
+from core.settings        import UserSettings
+from gui_core.preferences import PreferencesBaseDialog
 
 class NvizPreferencesDialog(PreferencesBaseDialog):
     """!Nviz preferences dialog"""

+ 35 - 206
gui/wxpython/gui_modules/nviz_tools.py

@@ -1,14 +1,9 @@
 """!
-@package nviz_tools.py
+@package nviz.tools
 
 @brief Nviz (3D view) tools window
 
 Classes:
- - ScrolledPanel
- - NTCValidator
- - NumTextCtrl
- - FloatSlider
- - SymbolButton
  - NvizToolWindow
  - PositionWindow
  - ViewPositionWindow
@@ -26,21 +21,13 @@ for details.
 """
 
 import os
-import sys
 import copy
 import types
-import string
 
 import wx
 import wx.lib.colourselect  as csel
 import wx.lib.scrolledpanel as SP
 import wx.lib.filebrowsebutton as filebrowse
-
-try:
-    from wx.lib.buttons import ThemedGenBitmapTextButton as BitmapTextButton
-except ImportError: # not sure about TGBTButton version
-    from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton
-    
 try:
     import wx.lib.agw.flatnotebook as FN
 except ImportError:
@@ -55,175 +42,20 @@ except ImportError: # if it's not there locally, try the wxPython lib.
         
 import grass.script as grass
 
-import globalvar
-import gselect
-import gcmd
-import colorrules
-from preferences import globalSettings as UserSettings
-from gselect import VectorDBInfo
-
-from nviz_animation import EVT_ANIM_FIN, EVT_ANIM_UPDATE_IDX
+from core               import globalvar
+from gui_core.gselect   import VectorDBInfo
+from core.gcmd          import GMessage, RunCommand
+from modules.colorrules import ThematicVectorTable
+from core.settings      import UserSettings
+from nviz.animation     import EVT_ANIM_FIN, EVT_ANIM_UPDATE_IDX
+from gui_core.widgets   import ScrolledPanel, NumTextCtrl, FloatSlider, SymbolButton
+from core.debug         import Debug
 try:
-    from nviz_mapdisp import wxUpdateView, wxUpdateLight, wxUpdateProperties,\
-                            wxUpdateCPlane
-    import wxnviz
+    from nviz.mapwindow import wxUpdateProperties
+    import nviz.main as wxnviz
 except ImportError:
     pass
 
-from debug import Debug
-
-
-class ScrolledPanel(SP.ScrolledPanel):
-    """!Custom ScrolledPanel to avoid strange behaviour concerning focus"""
-    def __init__(self, parent):
-        SP.ScrolledPanel.__init__(self, parent = parent, id = wx.ID_ANY)
-    def OnChildFocus(self, event):
-        pass
-        
-        
-class NTCValidator(wx.PyValidator):
-    """!validates input in textctrls, taken from wxpython demo"""
-    def __init__(self, flag = None):
-        wx.PyValidator.__init__(self)
-        self.flag = flag
-        self.Bind(wx.EVT_CHAR, self.OnChar)
-
-    def Clone(self):
-        return NTCValidator(self.flag)
-
-    def OnChar(self, event):
-        key = event.GetKeyCode()
-        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
-            event.Skip()
-            return
-        if self.flag == 'DIGIT_ONLY' and chr(key) in string.digits + '.-':
-            event.Skip()
-            return
-        if not wx.Validator_IsSilent():
-            wx.Bell()
-        # Returning without calling even.Skip eats the event before it
-        # gets to the text control
-        return  
-    
-class NumTextCtrl(wx.TextCtrl):
-    """!Class derived from wx.TextCtrl for numerical values only"""
-    def __init__(self, parent,  **kwargs):
-##        self.precision = kwargs.pop('prec')
-        wx.TextCtrl.__init__(self, parent = parent,
-            validator = NTCValidator(flag = 'DIGIT_ONLY'), **kwargs)
-        
-            
-    def SetValue(self, value):
-        super(NumTextCtrl, self).SetValue( str(value))
-        
-    def GetValue(self):
-        val = super(NumTextCtrl, self).GetValue()
-        if val == '':
-            val = '0'
-        try:
-            return float(val)
-        except ValueError:
-            val = ''.join(''.join(val.split('-')).split('.'))
-            return float(val)
-        
-    def SetRange(self, min, max):
-        pass
-   
-class FloatSlider(wx.Slider):
-    """!Class derived from wx.Slider for floats"""
-    def __init__(self, **kwargs):
-        Debug.msg(1, "FloatSlider.__init__()")
-        wx.Slider.__init__(self, **kwargs)
-        self.coef = 1.
-        #init range
-        self.minValueOrig = 0
-        self.maxValueOrig = 1
-        
-    def SetValue(self, value):
-        value *= self.coef 
-        if abs(value) < 1 and value != 0:
-            while abs(value) < 1:
-                value *= 100
-                self.coef *= 100
-            super(FloatSlider, self).SetRange(self.minValueOrig * self.coef, self.maxValueOrig * self.coef)
-        super(FloatSlider, self).SetValue(value)
-        
-        Debug.msg(4, "FloatSlider.SetValue(): value = %f" % value)
-        
-    def SetRange(self, minValue, maxValue):
-        self.coef = 1.
-        self.minValueOrig = minValue
-        self.maxValueOrig = maxValue
-        if abs(minValue) < 1 or abs(maxValue) < 1:
-            while (abs(minValue) < 1 and minValue != 0) or (abs(maxValue) < 1 and maxValue != 0):
-                minValue *= 100
-                maxValue *= 100
-                self.coef *= 100
-            super(FloatSlider, self).SetValue(super(FloatSlider, self).GetValue() * self.coef)
-        super(FloatSlider, self).SetRange(minValue, maxValue)
-        Debug.msg(4, "FloatSlider.SetRange(): minValue = %f, maxValue = %f" % (minValue, maxValue))
-            
-    def GetValue(self):
-        val = super(FloatSlider, self).GetValue()
-        Debug.msg(4, "FloatSlider.GetValue(): value = %f" % (val/self.coef))
-        return val/self.coef
-        
-        
-class SymbolButton(BitmapTextButton):
-    """!Button with symbol and label."""
-    def __init__(self, parent, usage, label, **kwargs):
-        """!Constructor
-        
-        @param parent parent (usually wx.Panel)
-        @param usage determines usage and picture
-        @param label displayed label
-        """
-        size = (15, 15)
-        buffer = wx.EmptyBitmap(*size)
-        BitmapTextButton.__init__(self, parent = parent, label = " " + label, bitmap = buffer, **kwargs)
-        
-        dc = wx.MemoryDC()
-        dc.SelectObject(buffer)
-        maskColor = wx.Color(255, 255, 255)
-        dc.SetBrush(wx.Brush(maskColor))
-        dc.Clear()
-        
-        if usage == 'record':
-            self.DrawRecord(dc, size)
-        elif usage == 'stop':
-            self.DrawStop(dc, size)
-        elif usage == 'play':
-            self.DrawPlay(dc, size)
-        elif usage == 'pause':
-            self.DrawPause(dc, size)
-
-        buffer.SetMaskColour(maskColor)
-        self.SetBitmapLabel(buffer)
-        dc.SelectObject(wx.NullBitmap)
-        
-    def DrawRecord(self, dc, size):
-        """!Draw record symbol"""
-        dc.SetBrush(wx.Brush(wx.Color(255, 0, 0)))
-        dc.DrawCircle(size[0]/2, size[1] / 2, size[0] / 2)
-        
-    def DrawStop(self, dc, size):
-        """!Draw stop symbol"""
-        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
-        dc.DrawRectangle(0, 0, size[0], size[1])
-        
-    def DrawPlay(self, dc, size):
-        """!Draw play symbol"""
-        dc.SetBrush(wx.Brush(wx.Color(0, 255, 0)))
-        points = (wx.Point(0, 0), wx.Point(0, size[1]), wx.Point(size[0], size[1] / 2))
-        dc.DrawPolygon(points)
-        
-    def DrawPause(self, dc, size):
-        """!Draw pause symbol"""
-        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
-        dc.DrawRectangle(0, 0, 2 * size[0] / 5, size[1])
-        dc.DrawRectangle(3 * size[0] / 5, 0, 2 * size[0] / 5, size[1])
-        
-        
 class NvizToolWindow(FN.FlatNotebook):
     """!Nviz (3D view) tools panel
     """
@@ -551,13 +383,13 @@ class NvizToolWindow(FN.FlatNotebook):
         # animation controls
         hSizer = wx.BoxSizer(wx.HORIZONTAL)
         record = SymbolButton(parent = panel, id = wx.ID_ANY,
-                                    usage = "record", label = _("Record"))
+                              usage = "record", label = _("Record"))
         play = SymbolButton(parent = panel, id = wx.ID_ANY,
-                                  usage = "play", label = _("Play"))
+                            usage = "play", label = _("Play"))
         pause = SymbolButton(parent = panel, id = wx.ID_ANY,
-                                   usage = "pause", label = _("Pause"))
+                             usage = "pause", label = _("Pause"))
         stop = SymbolButton(parent = panel, id = wx.ID_ANY,
-                                  usage = "stop", label = _("Stop"))
+                            usage = "stop", label = _("Stop"))
         
         self.win['anim']['record'] = record.GetId()
         self.win['anim']['play'] = play.GetId()
@@ -676,9 +508,6 @@ class NvizToolWindow(FN.FlatNotebook):
 
         self.mainPanelData = ScrolledPanel(parent = self)
         self.mainPanelData.SetupScrolling(scroll_x = False)
-##        style = fpb.CaptionBarStyle()
-##        style.SetCaptionStyle(fpb.CAPTIONBAR_FILLED_RECTANGLE)
-##        style.SetFirstColour(wx.Color(250,250,250))
         try:# wxpython <= 2.8.10
             self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,
                                                   style = fpb.FPB_DEFAULT_STYLE,
@@ -805,8 +634,8 @@ class NvizToolWindow(FN.FlatNotebook):
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Raster map")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        rmaps = gselect.Select(parent = panel, type = 'raster',
-                               onPopup = self.GselectOnPopup)
+        rmaps = Select(parent = panel, type = 'raster',
+                       onPopup = self.GselectOnPopup)
         rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster)
         self.win['surface']['map'] = rmaps.GetId()
         desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
@@ -963,10 +792,10 @@ class NvizToolWindow(FN.FlatNotebook):
             gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
                           pos = (row, 1))
             
-            map = gselect.Select(parent = panel, id = wx.ID_ANY,
-                                 # size = globalvar.DIALOG_GSELECT_SIZE,
-                                 size = (-1, -1),
-                                 type = "raster")
+            map = Select(parent = panel, id = wx.ID_ANY,
+                         # size = globalvar.DIALOG_GSELECT_SIZE,
+                         size = (-1, -1),
+                         type = "raster")
             self.win['surface'][code]['map'] = map.GetId() - 1 # FIXME
             map.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
             gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
@@ -1326,8 +1155,8 @@ class NvizToolWindow(FN.FlatNotebook):
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Vector map")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        vmaps = gselect.Select(parent = panel, type = 'vector',
-                               onPopup = self.GselectOnPopup)
+        vmaps = Select(parent = panel, type = 'vector',
+                       onPopup = self.GselectOnPopup)
         vmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetVector)
         self.win['vector']['map'] = vmaps.GetId()
         desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
@@ -1667,8 +1496,8 @@ class NvizToolWindow(FN.FlatNotebook):
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("3D raster map")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        rmaps = gselect.Select(parent = panel, type = '3d-raster',
-                               onPopup = self.GselectOnPopup)
+        rmaps = Select(parent = panel, type = '3d-raster',
+                       onPopup = self.GselectOnPopup)
         rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster3D)
         self.win['volume']['map'] = rmaps.GetId()
         desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
@@ -1986,8 +1815,8 @@ class NvizToolWindow(FN.FlatNotebook):
         rbox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                              label = " %s " % (_("Surface")))
         rboxSizer = wx.StaticBoxSizer(rbox, wx.VERTICAL)
-        rmaps = gselect.Select(parent = panel, type = 'raster',
-                               onPopup = self.GselectOnPopup)
+        rmaps = Select(parent = panel, type = 'raster',
+                       onPopup = self.GselectOnPopup)
         rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetSurface)
         self.win['fringe']['map'] = rmaps.GetId()
         rboxSizer.Add(item = rmaps, proportion = 0,
@@ -2340,11 +2169,11 @@ class NvizToolWindow(FN.FlatNotebook):
         dir = self.FindWindowById(self.win['anim']['save']['image']['dir']).GetValue()
         
         if not prefix:
-            gcmd.GMessage(parent = self,
+            GMessage(parent = self,
                           message = _("No file prefix given."))
             return
         elif not os.path.exists(dir):
-            gcmd.GMessage(parent = self,
+            GMessage(parent = self,
                           message = _("Directory %s does not exist.") % dir)
             return
             
@@ -2520,10 +2349,10 @@ class NvizToolWindow(FN.FlatNotebook):
                               pos = (row, 1))
                     
             if code != 'topo':
-                map = gselect.Select(parent = panel, id = wx.ID_ANY,
-                                     # size = globalvar.DIALOG_GSELECT_SIZE,
-                                     size = (200, -1),
-                                     type = "grid3")
+                map = Select(parent = panel, id = wx.ID_ANY,
+                             # size = globalvar.DIALOG_GSELECT_SIZE,
+                             size = (200, -1),
+                             type = "grid3")
                 self.win['volume'][code]['map'] = map.GetId() - 1 # FIXME
                 map.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
                 gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
@@ -3331,7 +3160,7 @@ class NvizToolWindow(FN.FlatNotebook):
     
     def _get3dRange(self, name):
         """!Gelper func for getting range of 3d map"""
-        ret = gcmd.RunCommand('r3.info', read = True, flags = 'r', map = name)
+        ret = RunCommand('r3.info', read = True, flags = 'r', map = name)
         if ret:
             range = []
             for value in ret.strip('\n').split('\n'):
@@ -3759,7 +3588,7 @@ class NvizToolWindow(FN.FlatNotebook):
             attrType = 'size'
         else:
             attrType = 'width'
-        ctable = colorrules.ThematicVectorTable(self, vtype, attributeType = attrType)
+        ctable = ThematicVectorTable(self, vtype, attributeType = attrType)
         ctable.CentreOnScreen()
         ctable.Show()
         

+ 342 - 0
gui/wxpython/nviz/workspace.py

@@ -0,0 +1,342 @@
+"""!
+@package nviz.settings
+
+@brief wxNviz settings
+
+Classes:
+ - NvizSettings
+
+(C) 2007-2011 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.
+
+@author Anna Kratochvilova <kratochanna gmail.com> (wxNviz / Google SoC 2011)
+"""
+
+import copy
+
+from core.settings import UserSettings
+
+try:
+    import nviz.wxcore as wxnviz
+except ImportError:
+    wxnviz = None
+
+class NvizSettings(object):
+    def __init__(self):
+        """Default 3D settings"""
+        UserSettings.Reset('nviz')
+        UserSettings.ReadSettingsFile()
+        
+    def SetConstantDefaultProp(self):
+        """Set default constant data properties"""
+        data = dict()
+        for key, value in UserSettings.Get(group='nviz', key='constant').iteritems():
+            data[key] = value
+        color = str(data['color'][0]) + ':' + str(data['color'][1]) + ':' + str(data['color'][2])
+        data['color'] = color
+
+        return data
+    
+    def SetSurfaceDefaultProp(self, data = None):
+        """Set default surface data properties"""
+        if not data:
+            data = dict()
+        for sec in ('attribute', 'draw', 'mask', 'position'):
+            data[sec] = {}
+        
+        #
+        # attributes
+        #
+        for attrb in ('shine', ):
+            data['attribute'][attrb] = {}
+            for key, value in UserSettings.Get(group='nviz', key='surface',
+                                               subkey=attrb).iteritems():
+                data['attribute'][attrb][key] = value
+            data['attribute'][attrb]['update'] = None
+        
+        #
+        # draw
+        #
+        data['draw']['all'] = False # apply only for current surface
+        for control, value in UserSettings.Get(group='nviz', key='surface', subkey='draw').iteritems():
+            if control[:3] == 'res':
+                if 'resolution' not in data['draw']:
+                    data['draw']['resolution'] = {}
+                if 'update' not in data['draw']['resolution']:
+                    data['draw']['resolution']['update'] = None
+                data['draw']['resolution'][control[4:]] = value
+                continue
+            
+            if control == 'wire-color':
+                value = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+            elif control in ('mode', 'style', 'shading'):
+                if 'mode' not in data['draw']:
+                    data['draw']['mode'] = {}
+                continue
+
+            data['draw'][control] = { 'value' : value }
+            data['draw'][control]['update'] = None
+        
+        value, desc = self.GetDrawMode(UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'mode']),
+                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'style']),
+                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'shading']))
+    
+        data['draw']['mode'] = { 'value' : value,
+                                 'desc' : desc, 
+                                 'update': None }
+        # position
+        for coord in ('x', 'y', 'z'):
+            data['position'][coord] = UserSettings.Get(group='nviz', key='surface', subkey=['position', coord])
+        data['position']['update'] = None
+            
+        return data
+    
+    def SetVolumeDefaultProp(self):
+        """Set default volume data properties"""
+        data = dict()
+        for sec in ('attribute', 'draw', 'position'):
+            data[sec] = dict()
+            for sec in ('isosurface', 'slice'):
+                    data[sec] = list()
+        
+        #
+        # draw
+        #
+        for control, value in UserSettings.Get(group='nviz', key='volume', subkey='draw').iteritems():
+            if control == 'shading':
+                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'shading'])
+                value, desc = self.GetDrawMode(shade=sel, string=False)
+                
+                data['draw']['shading'] = {}
+                data['draw']['shading']['isosurface'] = { 'value' : value,
+                                                          'desc' : desc['shading'] }
+                data['draw']['shading']['slice'] = { 'value' : value,
+                                                     'desc' : desc['shading'] }
+            elif control == 'mode':
+                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'mode'])
+                if sel == 0:
+                    desc = 'isosurface'
+                else:
+                    desc = 'slice'
+                data['draw']['mode'] = { 'value' : sel,
+                                         'desc' : desc, }
+            else:
+                data['draw'][control] = {}
+                data['draw'][control]['isosurface'] = { 'value' : value }
+                data['draw'][control]['slice'] = { 'value' : value }
+
+            if 'update' not in data['draw'][control]:
+                data['draw'][control]['update'] = None
+        
+        #
+        # isosurface attributes
+        #
+        for attrb in ('shine', ):
+            data['attribute'][attrb] = {}
+            for key, value in UserSettings.Get(group='nviz', key='volume',
+                                               subkey=attrb).iteritems():
+                data['attribute'][attrb][key] = value
+        
+        return data
+    
+    def SetIsosurfaceDefaultProp(self):
+        """!Set default isosurface properties"""
+        data = dict()
+        for attr in ('shine', 'topo', 'transp', 'color'):
+            data[attr] = {}
+            for key, value in UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = attr).iteritems():
+                data[attr][key] = value
+            data[attr]['update'] = None
+        return data
+    
+    def SetSliceDefaultProp(self):
+        """!Set default slice properties"""
+        data = dict()
+        data['position'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'slice_position'))
+        data['position']['update'] = None
+        
+        data['transp'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'transp'))
+        return data
+    
+    def SetVectorDefaultProp(self, data = None):
+        """Set default vector data properties"""
+        if not data:
+            data = dict()
+        for sec in ('lines', 'points'):
+            data[sec] = {}
+        
+        self.SetVectorLinesDefaultProp(data['lines'])
+        self.SetVectorPointsDefaultProp(data['points'])
+
+        return data
+    
+    def SetVectorLinesDefaultProp(self, data):
+        """Set default vector properties -- lines"""
+        # width
+        data['width'] = {'value' : UserSettings.Get(group='nviz', key='vector',
+                                                    subkey=['lines', 'width']) }
+        
+        # color
+        value = UserSettings.Get(group='nviz', key='vector',
+                                 subkey=['lines', 'color'])
+        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+        data['color'] = { 'value' : color }
+
+        # mode
+        if UserSettings.Get(group='nviz', key='vector',
+                            subkey=['lines', 'flat']):
+            type = 'flat'
+            
+        else:
+            type = 'surface'
+            
+        data['mode'] = {}
+        data['mode']['type'] = type
+        data['mode']['update'] = None
+    
+        # height
+        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['lines', 'height']) }
+        # thematic
+        data['thematic'] = {'rgbcolumn' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['lines', 'rgbcolumn']),
+                            'sizecolumn' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['lines', 'sizecolumn']),
+                            'layer': 1,
+                            'usecolor' : False,
+                            'usewidth' : False}
+        if 'object' in data:
+            for attrb in ('color', 'width', 'mode', 'height', 'thematic'):
+                data[attrb]['update'] = None
+        
+    def SetVectorPointsDefaultProp(self, data):
+        """Set default vector properties -- points"""
+        # size
+        data['size'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                    subkey=['points', 'size']) }
+
+        # width
+        data['width'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                     subkey=['points', 'width']) }
+
+        # marker
+        data['marker'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'marker']) }
+
+        # color
+        value = UserSettings.Get(group='nviz', key='vector',
+                                 subkey=['points', 'color'])
+        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+        data['color'] = { 'value' : color }
+
+        # mode
+        data['mode'] = { 'type' : 'surface'}
+##                         'surface' : '', }
+        
+        # height
+        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'height']) }
+        
+        data['thematic'] = {'rgbcolumn' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'rgbcolumn']),
+                            'sizecolumn' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'sizecolumn']),
+                            'layer': 1,
+                            'usecolor' : False,
+                            'usesize' : False}
+        if 'object' in data:
+            for attrb in ('size', 'width', 'marker',
+                          'color', 'height', 'thematic'):
+                data[attrb]['update'] = None
+        
+    def GetDrawMode(self, mode=None, style=None, shade=None, string=False):
+        """Get surface draw mode (value) from description/selection
+
+        @param mode,style,shade modes
+        @param string if True input parameters are strings otherwise
+        selections
+        """
+        if not wxnviz:
+            return None
+        
+        value = 0
+        desc = {}
+
+        if string:
+            if mode is not None:
+                if mode == 'coarse':
+                    value |= wxnviz.DM_WIRE
+                elif mode == 'fine':
+                    value |= wxnviz.DM_POLY
+                else: # both
+                    value |= wxnviz.DM_WIRE_POLY
+
+            if style is not None:
+                if style == 'wire':
+                    value |= wxnviz.DM_GRID_WIRE
+                else: # surface
+                    value |= wxnviz.DM_GRID_SURF
+                    
+            if shade is not None:
+                if shade == 'flat':
+                    value |= wxnviz.DM_FLAT
+                else: # surface
+                    value |= wxnviz.DM_GOURAUD
+
+            return value
+
+        # -> string is False
+        if mode is not None:
+            if mode == 0: # coarse
+                value |= wxnviz.DM_WIRE
+                desc['mode'] = 'coarse'
+            elif mode == 1: # fine
+                value |= wxnviz.DM_POLY
+                desc['mode'] = 'fine'
+            else: # both
+                value |= wxnviz.DM_WIRE_POLY
+                desc['mode'] = 'both'
+
+        if style is not None:
+            if style == 0: # wire
+                value |= wxnviz.DM_GRID_WIRE
+                desc['style'] = 'wire'
+            else: # surface
+                value |= wxnviz.DM_GRID_SURF
+                desc['style'] = 'surface'
+
+        if shade is not None:
+            if shade == 0:
+                value |= wxnviz.DM_FLAT
+                desc['shading'] = 'flat'
+            else: # surface
+                value |= wxnviz.DM_GOURAUD
+                desc['shading'] = 'gouraud'
+        
+        return (value, desc)
+    
+    def SetDecorDefaultProp(self, type):
+        """!Set default arrow properties
+        """
+        data = {}
+        
+        # arrow
+        if type == 'arrow':
+            data['arrow'] = UserSettings.Get(group = 'nviz', key = 'arrow')
+            data['arrow']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'arrow', subkey = 'color')[:3])
+            data['arrow'].update(UserSettings.Get(group = 'nviz', key = 'arrow', internal = True))
+            data['arrow']['show'] = False
+        
+        # arrow
+        if type == 'scalebar':
+            data['scalebar'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar'))
+            data['scalebar']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'scalebar', subkey = 'color')[:3])
+            data['scalebar'].update(copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar', internal = True)))
+            data['scalebar']['id'] = 0
+        return data

+ 17 - 7
gui/wxpython/gui_modules/wxnviz.py

@@ -1,7 +1,7 @@
 """!
-@package wxnviz.py
+@package nviz.wxnviz
 
-@brief wxGUI 3D view mode
+@brief wxGUI 3D view mode (ctypes-based classes)
 
 This module implements 3D visualization mode for map display (ctypes
 required).
@@ -9,6 +9,8 @@ required).
 List of classes:
  - Nviz
  - Texture
+ - ImageTexture
+ - TextTexture
 
 (C) 2008-2011 by the GRASS Development Team
 
@@ -21,13 +23,21 @@ for details.
 @author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
 """
 
-import wx
 import sys
 import locale
 import struct
-from threading import Thread
-from math import sqrt
-from numpy import matrix
+from math  import sqrt
+try:
+    from numpy import matrix
+except ImportError:
+    msg = _("This module requires the NumPy module, which could not be "
+            "imported. It probably is not installed (it's not part of the "
+            "standard Python distribution). See the Numeric Python site "
+            "(http://numpy.scipy.org) for information on downloading source or "
+            "binaries.")
+    print >> sys.stderr, "wxnviz.py: " + msg
+
+import wx
 
 from ctypes import *
 try:
@@ -40,7 +50,7 @@ try:
 except ImportError, e:
     sys.stderr.write(_("3D view mode: %s\n") % e)
     
-from debug import Debug
+from core.debug import Debug
 import grass.script as grass
 
 log      = None

+ 17 - 22
gui/wxpython/gui_modules/psmap_dialogs.py

@@ -1,7 +1,7 @@
 """!
-@package psmap_dialogs.py
+@package psmap.dialogs
 
-@brief Map feature objects and dialogs for ps.map
+@brief Map feature objects and dialogs for wxPsMap
 
 Classes:
  - UnitConversion
@@ -47,7 +47,6 @@ This program is free software under the GNU General Public License
 @author Martin Landa <landa.martin gmail.com> (mentor)
 """
 
-
 import os
 import sys
 import string
@@ -60,33 +59,29 @@ try:
 except ImportError:
     havePILImage = False
 
-import grass.script as grass
-if int(grass.version()['version'].split('.')[0]) > 6:
-    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython',
-                                 'gui_modules'))
-else:
-    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython',
-                                 'gui_modules'))
-import globalvar
-import dbm_base
-from   utils      import CmdToTuple, GetCmdString
-from   gselect    import Select
-from   gcmd       import RunCommand, GError, GMessage, GWarning
-
 import wx
-import wx.lib.scrolledpanel as scrolled
-import  wx.lib.filebrowsebutton as filebrowse
+import wx.lib.scrolledpanel    as scrolled
+import wx.lib.filebrowsebutton as filebrowse
 from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
-from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+from wx.lib.expando         import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
 try:
     import wx.lib.agw.floatspin as fs
 except ImportError:
     fs = None
 
-grass.set_raise_on_error(True)
+import grass.script as grass
+
+from core             import globalvar
+from dbm.vinfo        import VectorDBInfo
+from core.utils       import CmdToTuple, GetCmdString
+from gui_core.gselect import Select
+from core.gcmd        import RunCommand, GError, GMessage, GWarning
+
+# grass.set_raise_on_error(True)
 
 PSMAP_COLORS = ['aqua', 'black', 'blue', 'brown', 'cyan', 'gray', 'grey', 'green', 'indigo',
                 'magenta','orange', 'purple', 'red', 'violet', 'white', 'yellow']
+
 class UnitConversion:
     """! Class for converting units"""
     def __init__(self, parent = None):
@@ -1698,7 +1693,7 @@ class VProperties(InstructionObject):
         instr['name'] = info['fullname']
         #connection
         instr['connection'] = True
-        self.mapDBInfo = dbm_base.VectorDBInfo(instr['name'])
+        self.mapDBInfo = VectorDBInfo(instr['name'])
         self.layers = self.mapDBInfo.layers.keys()
         if not self.layers:
             instr['connection'] = False
@@ -3213,7 +3208,7 @@ class VPropertiesDialog(PsmapDialog):
         #vector map info
         self.connection = True
         try:
-            self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
+            self.mapDBInfo = VectorDBInfo(self.vectorName)
             self.layers = self.mapDBInfo.layers.keys()
         except grass.ScriptError:
             self.connection = False

+ 15 - 21
gui/wxpython/gui_modules/psmap.py

@@ -1,5 +1,5 @@
 """!
-@package psmap.py
+@package psmap.frame
 
 @brief GUI for ps.map
 
@@ -19,29 +19,12 @@ import os
 import sys
 import textwrap
 import Queue
+from math import sin, cos, pi
 try:
     import Image as PILImage
     havePILImage = True
 except ImportError:
     havePILImage = False
-from math import sin, cos, pi
-
-import grass.script as grass
-if int(grass.version()['version'].split('.')[0]) > 6:
-    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython',
-                                 'gui_modules'))
-else:
-    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython',
-                                 'gui_modules'))
-import globalvar
-import menu
-from   goutput    import CmdThread, EVT_CMD_DONE
-from   menudata   import PsMapData
-from   toolbars   import PsMapToolbar
-from   icon       import Icons, MetaIcon, iconSet
-from   gcmd       import RunCommand, GError, GMessage
-from   menuform   import GUI
-from psmap_dialogs import *
 
 import wx
 
@@ -49,7 +32,18 @@ try:
     import wx.lib.agw.flatnotebook as fnb
 except ImportError:
     import wx.lib.flatnotebook as fnb
-    
+
+import grass.script as grass
+
+from core             import globalvar
+from gui_core.menu    import Menu
+from gui_core.goutput import CmdThread, EVT_CMD_DONE
+from psmap.toolbars   import PsMapToolbar
+from icon             import Icons, MetaIcon, iconSet
+from core.gcmd        import RunCommand, GError, GMessage
+from core.forms       import GUI
+from psmap.dialogs    import *
+
 class PsMapFrame(wx.Frame):
     def __init__(self, parent = None, id = wx.ID_ANY,
                  title = _("GRASS GIS Cartographic Composer"), **kwargs):
@@ -66,7 +60,7 @@ class PsMapFrame(wx.Frame):
         wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs)
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         #menubar
-        self.menubar = menu.Menu(parent = self, data = PsMapData())
+        self.menubar = Menu(parent = self, data = PsMapData())
         self.SetMenuBar(self.menubar)
         #toolbar
 

+ 39 - 0
gui/wxpython/psmap/menudata.py

@@ -0,0 +1,39 @@
+"""!
+@package core.menudata
+
+@brief Complex list for menu entries for wxGUI
+
+Classes:
+ - MenuData
+
+Usage:
+@code
+python menudata.py [action] [manager|modeler]
+@endcode
+
+where <i>action</i>:
+ - strings (default)
+ - tree
+ - commands
+ - dump
+
+(C) 2007-2011 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.
+
+@author Michael Barton (Arizona State University)
+@author Yann Chemin <yann.chemin gmail.com>
+@author Martin Landa <landa.martin gmail.com>
+@author Glynn Clements
+@author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+import pprint
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+import wx

+ 93 - 0
gui/wxpython/psmap/toolbars.py

@@ -0,0 +1,93 @@
+"""!
+@package psmap.toolbars
+
+@brief wxPsMap toolbars classes
+
+Classes:
+ - PsMapToolbar
+
+(C) 2007-2011 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.
+
+@author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+import wx
+
+from core              import globalvar
+from gui_core.toolbars import BaseToolbar
+
+sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+from icon              import Icons
+
+class PsMapToolbar(BaseToolbar):
+    def __init__(self, parent):
+        """!Toolbar Cartographic Composer (psmap.py)
+        
+        @param parent parent window
+        """
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        self.Realize()
+        
+        self.action = { 'id' : self.pointer }
+        self.defaultAction = { 'id' : self.pointer,
+                               'bind' : self.parent.OnPointer }
+        self.OnTool(None)
+        
+        from psmap import havePILImage
+        if not havePILImage:
+            self.EnableTool(self.preview, False)
+        
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = Icons['psMap']
+        return self._getToolbarData((('loadFile', icons['scriptLoad'],
+                                      self.parent.OnLoadFile),                                    
+                                     ('instructionFile', icons['scriptSave'],
+                                      self.parent.OnInstructionFile),
+                                     (None, ),
+                                     ('pagesetup', icons['pageSetup'],
+                                      self.parent.OnPageSetup),
+                                     (None, ),
+                                     ("pointer", Icons["displayWindow"]["pointer"],
+                                      self.parent.OnPointer, wx.ITEM_CHECK),
+                                     ('pan', Icons["displayWindow"]['pan'],
+                                      self.parent.OnPan, wx.ITEM_CHECK),
+                                     ("zoomin", Icons["displayWindow"]["zoomIn"],
+                                      self.parent.OnZoomIn, wx.ITEM_CHECK),
+                                     ("zoomout", Icons["displayWindow"]["zoomOut"],
+                                      self.parent.OnZoomOut, wx.ITEM_CHECK),
+                                     ('zoomAll', icons['fullExtent'],
+                                      self.parent.OnZoomAll),
+                                     (None, ),
+                                     ('addMap', icons['addMap'],
+                                      self.parent.OnAddMap, wx.ITEM_CHECK),
+                                     ('addRaster', icons['addRast'],
+                                      self.parent.OnAddRaster),
+                                     ('addVector', icons['addVect'],
+                                      self.parent.OnAddVect),
+                                     ("dec", Icons["displayWindow"]["overlay"],
+                                      self.parent.OnDecoration),
+                                     ("delete", icons["deleteObj"],
+                                      self.parent.OnDelete),
+                                     (None, ),
+                                     ("preview", icons["preview"],
+                                      self.parent.OnPreview),
+                                     ('generatePS', icons['psExport'],
+                                      self.parent.OnPSFile),
+                                     ('generatePDF', icons['pdfExport'],
+                                      self.parent.OnPDFFile),
+                                     (None, ),
+                                     ("help", Icons['misc']['help'],
+                                      self.parent.OnHelp),
+                                     ('quit', icons['quit'],
+                                      self.parent.OnCloseWindow))
+                                    )

+ 3 - 3
gui/wxpython/scripts/r.li.setup.py

@@ -25,7 +25,7 @@ import platform
 GUIModulesPath = os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython", "gui_modules")
 sys.path.append(GUIModulesPath)
 
-import globalvar
+from core import globalvar
 
 import wx
 import wx.lib.mixins.listctrl as listmix
@@ -33,8 +33,8 @@ import wx.wizard as wiz
 import wx.lib.scrolledpanel as scrolled
 import time
 
-import gcmd
-import utils
+from core import cmd as gcmd
+from core import utils
 from grass.script import core as grass
 
 #@TODO create wizard instead of progressively increasing window

+ 4 - 4
gui/wxpython/scripts/vkrige.py

@@ -36,11 +36,11 @@ except ImportError:
 GUIModulesPath = os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython", "gui_modules")
 sys.path.append(GUIModulesPath)
 
-import globalvar
-import gselect
-import goutput
+from core import globalvar
+import gui_core.gselect
+import gui_core.goutput
 import menuform
-from preferences import globalSettings as UserSettings
+from core.settings import UserSettings
 #import help
 
 import wx

gui/wxpython/gui_modules/states.txt → gui/wxpython/states.txt


+ 1 - 1
gui/wxpython/tools/update_menudata.py

@@ -174,6 +174,6 @@ if __name__ == '__main__':
     sys.path.append(os.path.join(os.getenv("GISBASE"), 'etc', 'wxpython', 'gui_modules'))
     import menudata
     import menuform
-    import globalvar
+    from core import globalvar
     
     sys.exit(main())

+ 753 - 0
gui/wxpython/vdigit/dialogs.py

@@ -0,0 +1,753 @@
+"""!
+@package vdigit.dialogs
+
+@brief wxGUI vector digitizer dialogs
+
+Classes:
+ - VDigitCategoryDialog
+ - CategoryListCtrl
+ - VDigitZBulkDialog
+ - VDigitDuplicatesDialog
+ - CheckListFeature
+
+(C) 2007-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+import sys
+import copy
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+
+from core.gcmd        import RunCommand, GError
+from core.debug       import Debug
+from core.settings    import UserSettings
+
+class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
+    def __init__(self, parent, title,
+                 vectorName, query = None, cats = None,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Dialog used to display/modify categories of vector objects
+        
+        @param parent
+        @param title dialog title
+        @param query {coordinates, qdist} - used by v.edit/v.what
+        @param cats  directory of lines (layer/categories) - used by vdigit
+        @param style dialog style
+        """
+        self.parent = parent       # mapdisplay.BufferedWindow class instance
+        self.digit = parent.digit
+        
+        # map name
+        self.vectorName = vectorName
+        
+        # line : {layer: [categories]}
+        self.cats = {}
+        
+        # do not display dialog if no line is found (-> self.cats)
+        if cats is None:
+            if self._getCategories(query[0], query[1]) == 0 or not self.line:
+                Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
+        else:
+            self.cats = cats
+            for line in cats.keys():
+                for layer in cats[line].keys():
+                    self.cats[line][layer] = list(cats[line][layer])
+            
+            layers = []
+            for layer in self.digit.GetLayers():
+                layers.append(str(layer))
+        
+        # make copy of cats (used for 'reload')
+        self.cats_orig = copy.deepcopy(self.cats)
+        
+        wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY, title = title,
+                           style = style, **kwargs)
+        
+        # list of categories
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("List of categories - right-click to delete"))
+        listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        self.list = CategoryListCtrl(parent = self, id = wx.ID_ANY,
+                                     style = wx.LC_REPORT |
+                                     wx.BORDER_NONE |
+                                     wx.LC_SORT_ASCENDING |
+                                     wx.LC_HRULES |
+                                     wx.LC_VRULES)
+        # sorter
+        self.fid = self.cats.keys()[0]
+        self.itemDataMap = self.list.Populate(self.cats[self.fid])
+        listmix.ColumnSorterMixin.__init__(self, 2)
+        self.fidMulti = wx.Choice(parent = self, id = wx.ID_ANY,
+                                  size = (150, -1))
+        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
+        self.fidText = wx.StaticText(parent = self, id = wx.ID_ANY)
+        if len(self.cats.keys()) == 1:
+            self.fidMulti.Show(False)
+            self.fidText.SetLabel(str(self.fid))
+        else:
+            self.fidText.Show(False)
+            choices = []
+            for fid in self.cats.keys():
+                choices.append(str(fid))
+            self.fidMulti.SetItems(choices)
+            self.fidMulti.SetSelection(0)
+        
+        listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
+
+        # add new category
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Add new category"))
+        addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 5, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(3)
+
+        layerNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                 label = "%s:" % _("Layer"))
+        self.layerNew = wx.Choice(parent = self, id = wx.ID_ANY, size = (75, -1),
+                                  choices = layers)
+        if len(layers) > 0:
+            self.layerNew.SetSelection(0)
+        
+        catNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
+                               label = "%s:" % _("Category"))
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+        self.catNew = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (75, -1),
+                                  initial = newCat, min = 0, max = 1e9)
+        btnAddCat = wx.Button(self, wx.ID_ADD)
+        flexSizer.Add(item = layerNewTxt, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.layerNew, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = catNewTxt, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                      border = 10)
+        flexSizer.Add(item = self.catNew, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = btnAddCat, proportion = 0,
+                      flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+        addSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        # buttons
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnApply.SetToolTipString(_("Apply changes"))
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetToolTipString(_("Apply changes and close dialog"))
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        #btnSizer.AddButton(btnReload)
+        #btnSizer.SetNegativeButton(btnReload)
+        btnSizer.AddButton(btnApply)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = listSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        mainSizer.Add(item = addSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
+        fidSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                        label = _("Feature id:")),
+                     proportion = 0, border = 5,
+                     flag = wx.ALIGN_CENTER_VERTICAL)
+        fidSizer.Add(item = self.fidMulti, proportion = 0,
+                     flag = wx.EXPAND | wx.ALL,  border = 5)
+        fidSizer.Add(item = self.fidText, proportion = 0,
+                     flag = wx.EXPAND | wx.ALL,  border = 5)
+        mainSizer.Add(item = fidSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        self.SetAutoLayout(True)
+
+        # set min size for dialog
+        self.SetMinSize(self.GetBestSize())
+
+        # bindings
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
+        btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+                                     
+        # list
+        self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
+        self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
+        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
+        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
+
+    def GetListCtrl(self):
+        """!Used by ColumnSorterMixin
+        """
+        return self.list
+
+    def OnColClick(self, event):
+        """!Click on column header (order by)
+        """
+        event.Skip()
+        
+    def OnBeginEdit(self, event):
+        """!Editing of item started
+        """
+        event.Allow()
+
+    def OnEndEdit(self, event):
+        """!Finish editing of item
+        """
+        itemIndex = event.GetIndex()
+        layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
+        catOld = int (self.list.GetItem(itemIndex, 1).GetText())
+
+        if event.GetColumn() == 0:
+            layerNew = int(event.GetLabel())
+            catNew = catOld
+        else:
+            layerNew = layerOld
+            catNew = int(event.GetLabel())
+        
+        try:
+            if layerNew not in self.cats[self.fid].keys():
+                self.cats[self.fid][layerNew] = []
+            self.cats[self.fid][layerNew].append(catNew)
+            self.cats[self.fid][layerOld].remove(catOld)
+        except:
+            event.Veto()
+            self.list.SetStringItem(itemIndex, 0, str(layerNew))
+            self.list.SetStringItem(itemIndex, 1, str(catNew))
+            dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
+                                           "Layer and category number must be integer.\n"
+                                           "Layer number must be greater then zero.") %
+                                   { 'layer': self.layerNew.GetStringSelection(),
+                                     'category' : str(self.catNew.GetValue()) },
+                                   _("Error"), wx.OK | wx.ICON_ERROR)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return False
+
+    def OnRightDown(self, event):
+        """!Mouse right button down
+        """
+        x = event.GetX()
+        y = event.GetY()
+        item, flags = self.list.HitTest((x, y))
+
+        if item !=  wx.NOT_FOUND and \
+                flags & wx.LIST_HITTEST_ONITEM:
+            self.list.Select(item)
+
+        event.Skip()
+
+    def OnRightUp(self, event):
+        """!Mouse right button up
+        """
+        if not hasattr(self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnItemDelete,    id = self.popupID1)
+            self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id = self.popupID2)
+            self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
+
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupID1, _("Delete selected"))
+        if self.list.GetFirstSelected() == -1:
+            menu.Enable(self.popupID1, False)
+
+        menu.Append(self.popupID2, _("Delete all"))
+        menu.AppendSeparator()
+        menu.Append(self.popupID3, _("Reload"))
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnItemSelected(self, event):
+        """!Item selected
+        """
+        event.Skip()
+
+    def OnItemDelete(self, event):
+        """!Delete selected item(s) from the list (layer/category pair)
+        """
+        item = self.list.GetFirstSelected()
+        while item != -1:
+            layer = int (self.list.GetItem(item, 0).GetText())
+            cat = int (self.list.GetItem(item, 1).GetText())
+            self.list.DeleteItem(item)
+            self.cats[self.fid][layer].remove(cat)
+
+            item = self.list.GetFirstSelected()
+            
+        event.Skip()
+        
+    def OnItemDeleteAll(self, event):
+        """!Delete all items from the list
+        """
+        self.list.DeleteAllItems()
+        self.cats[self.fid] = {}
+
+        event.Skip()
+
+    def OnFeature(self, event):
+        """!Feature id changed (on duplicates)
+        """
+        self.fid = int(event.GetString())
+        
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+            
+        self.catNew.SetValue(newCat)
+        
+        event.Skip()
+        
+    def _getCategories(self, coords, qdist):
+        """!Get layer/category pairs for all available
+        layers
+
+        Return True line found or False if not found
+        """
+        ret = RunCommand('v.what',
+                         parent = self,
+                         quiet = True,
+                         map = self.vectorName,
+                         east_north = '%f,%f' % \
+                             (float(coords[0]), float(coords[1])),
+                         distance = qdist)
+
+        if not ret:
+            return False
+
+        for item in ret.splitlines():
+            litem = item.lower()
+            if "id:" in litem: # get line id
+                self.line = int(item.split(':')[1].strip())
+            elif "layer:" in litem: # add layer
+                layer = int(item.split(':')[1].strip())
+                if layer not in self.cats.keys():
+                    self.cats[layer] = []
+            elif "category:" in litem: # add category
+                self.cats[layer].append(int(item.split(':')[1].strip()))
+
+        return True
+
+    def OnReload(self, event):
+        """!Reload button pressed
+        """
+        # restore original list
+        self.cats = copy.deepcopy(self.cats_orig)
+
+        # polulate list
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        event.Skip()
+
+    def OnCancel(self, event):
+        """!Cancel button pressed
+        """
+        self.parent.parent.dialogs['category'] = None
+        if self.digit:
+            self.digit.GetDisplay().SetSelected([])
+            self.parent.UpdateMap(render = False)
+        else:
+            self.parent.parent.OnRender(None)
+            
+        self.Close()
+
+    def OnApply(self, event):
+        """!Apply button pressed
+        """
+        for fid in self.cats.keys():
+            newfid = self.ApplyChanges(fid)
+            if fid == self.fid and newfid > 0:
+                self.fid = newfid
+            
+    def ApplyChanges(self, fid):
+        """!Apply changes 
+
+        @param fid feature id
+        """
+        cats = self.cats[fid]
+        cats_orig = self.cats_orig[fid]
+
+        # action : (catsFrom, catsTo)
+        check = {'catadd': (cats,      cats_orig),
+                 'catdel': (cats_orig, cats)}
+
+        newfid = -1
+        
+        # add/delete new category
+        for action, catsCurr in check.iteritems():
+            for layer in catsCurr[0].keys():
+                catList = []
+                for cat in catsCurr[0][layer]:
+                    if layer not in catsCurr[1].keys() or \
+                            cat not in catsCurr[1][layer]:
+                        catList.append(cat)
+                if catList != []:
+                    if action == 'catadd':
+                        add = True
+                    else:
+                        add = False
+                        
+                    newfid = self.digit.SetLineCats(fid, layer,
+                                                    catList, add)
+                    if len(self.cats.keys()) == 1:
+                        self.fidText.SetLabel("%d" % newfid)
+                    else:
+                        choices = self.fidMulti.GetItems()
+                        choices[choices.index(str(fid))] = str(newfid)
+                        self.fidMulti.SetItems(choices)
+                        self.fidMulti.SetStringSelection(str(newfid))
+                    
+                    self.cats[newfid] = self.cats[fid]
+                    del self.cats[fid]
+                    
+                    fid = newfid
+                    if self.fid < 0:
+                        wx.MessageBox(parent = self, message = _("Unable to update vector map."),
+                                      caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
+        
+        self.cats_orig[fid] = copy.deepcopy(cats)
+        
+        return newfid
+
+    def OnOK(self, event):
+        """!OK button pressed
+        """
+        self.OnApply(event)
+        self.OnCancel(event)
+
+    def OnAddCat(self, event):
+        """!Button 'Add' new category pressed
+        """
+        try:
+            layer = int(self.layerNew.GetStringSelection())
+            cat   = int(self.catNew.GetValue())
+            if layer <= 0:
+                raise ValueError
+        except ValueError:
+            GError(parent = self,
+                   message = _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
+                               "Layer and category number must be integer.\n"
+                               "Layer number must be greater then zero.") %
+                   {'layer' : str(self.layerNew.GetValue()),
+                    'category' : str(self.catNew.GetValue())})
+            return False
+        
+        if layer not in self.cats[self.fid].keys():
+            self.cats[self.fid][layer] = []
+        
+        self.cats[self.fid][layer].append(cat)
+        
+        # reload list
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+        
+        # update category number for add
+        self.catNew.SetValue(cat + 1)
+        
+        event.Skip()
+
+        return True
+
+    def GetLine(self):
+        """!Get id of selected line of 'None' if no line is selected
+        """
+        return self.cats.keys()
+
+    def UpdateDialog(self, query = None, cats = None):
+        """!Update dialog
+        
+        @param query {coordinates, distance} - v.what
+        @param cats  directory layer/cats    - vdigit
+        Return True if updated otherwise False
+        """
+        # line: {layer: [categories]}
+        self.cats = {}
+        # do not display dialog if no line is found (-> self.cats)
+        if cats is None:
+            ret = self._getCategories(query[0], query[1])
+        else:
+            self.cats = cats
+            for line in cats.keys():
+                for layer in cats[line].keys():
+                    self.cats[line][layer] = list(cats[line][layer])
+            ret = 1
+        if ret == 0 or len(self.cats.keys()) < 1:
+            Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
+            return False
+        
+        # make copy of cats (used for 'reload')
+        self.cats_orig = copy.deepcopy(self.cats)
+
+        # polulate list
+        self.fid = self.cats.keys()[0]
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+        self.catNew.SetValue(newCat)
+        
+        if len(self.cats.keys()) == 1:
+            self.fidText.Show(True)
+            self.fidMulti.Show(False)
+            self.fidText.SetLabel("%d" % self.fid)
+        else:
+            self.fidText.Show(False)
+            self.fidMulti.Show(True)
+            choices = []
+            for fid in self.cats.keys():
+                choices.append(str(fid))
+            self.fidMulti.SetItems(choices)
+            self.fidMulti.SetSelection(0)
+
+        self.Layout()
+        
+        return True
+
+class CategoryListCtrl(wx.ListCtrl,
+                       listmix.ListCtrlAutoWidthMixin,
+                       listmix.TextEditMixin):
+    def __init__(self, parent, id, pos = wx.DefaultPosition,
+                 size = wx.DefaultSize, style = 0):
+        """!List of layers/categories"""
+        self.parent = parent
+        
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.TextEditMixin.__init__(self)
+
+    def Populate(self, cats, update = False):
+        """!Populate the list
+        """
+        itemData = {} # requested by sorter
+
+        if not update:
+            self.InsertColumn(0, _("Layer"))
+            self.InsertColumn(1, _("Category"))
+        else:
+            self.DeleteAllItems()
+
+        i = 1
+        for layer in cats.keys():
+            catsList = cats[layer]
+            for cat in catsList:
+                index = self.InsertStringItem(sys.maxint, str(catsList[0]))
+                self.SetStringItem(index, 0, str(layer))
+                self.SetStringItem(index, 1, str(cat))
+                self.SetItemData(index, i)
+                itemData[i] = (str(layer), str(cat))
+                i = i + 1
+
+        if not update:
+            self.SetColumnWidth(0, 100)
+            self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
+
+        self.currentItem = 0
+
+        return itemData
+
+class VDigitZBulkDialog(wx.Dialog):
+    def __init__(self, parent, title, nselected, style = wx.DEFAULT_DIALOG_STYLE):
+        """!Dialog used for Z bulk-labeling tool
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
+
+        self.parent = parent # mapdisplay.BufferedWindow class instance
+
+        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        txt = wx.StaticText(parent = self,
+                            label = _("%d lines selected for z bulk-labeling") % nselected);
+        border.Add(item = txt, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Set value"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+
+        # starting value
+        txt = wx.StaticText(parent = self,
+                            label = _("Starting value"));
+        self.value = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
+                                 initial = 0,
+                                 min = -1e6, max = 1e6)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.value, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+
+        # step
+        txt = wx.StaticText(parent = self,
+                            label = _("Step"))
+        self.step  = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
+                                 initial = 0,
+                                 min = 0, max = 1e6)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.step, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 0)
+
+        # buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = border, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+class VDigitDuplicatesDialog(wx.Dialog):
+    def __init__(self, parent, data, title = _("List of duplicates"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 pos = wx.DefaultPosition):
+        """!Show duplicated feature ids
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
+                           pos = pos)
+        
+        self.parent = parent # BufferedWindow
+        self.data = data
+        self.winList = []
+
+        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        # notebook
+        self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+
+        id = 1
+        for key in self.data.keys():
+            panel = wx.Panel(parent = self.notebook, id = wx.ID_ANY)
+            self.notebook.AddPage(page = panel, text = " %d " % (id))
+            
+            # notebook body
+            border = wx.BoxSizer(wx.VERTICAL)
+
+            win = CheckListFeature(parent = panel, data = list(self.data[key]))
+            self.winList.append(win.GetId())
+
+            border.Add(item = win, proportion = 1,
+                       flag = wx.ALL | wx.EXPAND, border = 5)
+
+            panel.SetSizer(border)
+
+            id += 1
+
+        # buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        self.SetAutoLayout(True)
+
+        # set min size for dialog
+        self.SetMinSize((250, 180))
+
+    def GetUnSelected(self):
+        """!Get unselected items (feature id)
+
+        @return list of ids
+        """
+        ids = []
+        for id in self.winList:
+            wlist = self.FindWindowById(id)
+
+            for item in range(wlist.GetItemCount()):
+                if not wlist.IsChecked(item):
+                    ids.append(int(wlist.GetItem(item, 0).GetText()))
+                    
+        return ids
+
+class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
+    def __init__(self, parent, data,
+                 pos = wx.DefaultPosition, log = None):
+        """!List of mapset/owner/group
+        """
+        self.parent = parent
+        self.data = data
+
+        wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
+                             style = wx.LC_REPORT)
+
+        listmix.CheckListCtrlMixin.__init__(self)
+
+        self.log = log
+
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        self.LoadData(self.data)
+
+    def LoadData(self, data):
+        """!Load data into list
+        """
+        self.InsertColumn(0, _('Feature id'))
+        self.InsertColumn(1, _('Layer (Categories)'))
+
+        for item in data:
+            index = self.InsertStringItem(sys.maxint, str(item[0]))
+            self.SetStringItem(index, 1, str(item[1]))
+
+        # enable all items by default
+        for item in range(self.GetItemCount()):
+            self.CheckItem(item, True)
+
+        self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
+        self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE_USEHEADER)
+                
+    def OnCheckItem(self, index, flag):
+        """!Mapset checked/unchecked
+        """
+        pass

+ 34 - 0
gui/wxpython/vdigit/main.py

@@ -0,0 +1,34 @@
+"""!
+@package vdigit.main
+
+@brief wxGUI vector digitizer
+
+Classes:
+ - VDigit
+
+(C) 2007-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+try:
+    from vdigit.wxdigit import IVDigit, GV_LINES
+    haveVDigit = True
+    errorMsg   = ''
+except ImportError, err:
+    haveVDigit = False
+    errorMsg   = err
+    GV_LINES   = -1
+    class IVDigit:
+        def __init__(self):
+            pass
+
+class VDigit(IVDigit):
+    def __init__(self, mapwindow):
+        """!Base class of vector digitizer
+        
+        @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
+        """
+        IVDigit.__init__(self, mapwindow)

+ 39 - 45
gui/wxpython/gui_modules/mapdisp_vdigit.py

@@ -1,34 +1,28 @@
 """!
-@package mapdisp_vdigit.py
+@package vdigit.mapwindow
 
-@brief Map display canvas extended for vector digitizer
-
-See also vdigit.py, wxvdriver.py and wxvdigit.py
+@brief Map display canvas for wxGUI vector digitizer
 
 Classes:
  - VDigitWindow
 
 (C) 2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """
 
 import wx
 
-import dbm_dialogs
-
-import gcmd
-from debug          import Debug
-from mapdisp_window import BufferedWindow
-from preferences    import globalSettings as UserSettings
-from utils          import ListOfCatsToRange
-from globalvar      import QUERYLAYER
-from vdigit import VDigitCategoryDialog
-from vdigit import VDigitZBulkDialog
-from vdigit import VDigitDuplicatesDialog
+from dbm.dialogs    import DisplayAttributesDialog
+from core.gcmd      import RunCommand, GMessage, GError
+from core.debug     import Debug
+from mapdisp.window import BufferedWindow
+from core.settings  import UserSettings
+from core.utils     import ListOfCatsToRange
+from core.globalvar import QUERYLAYER
+from vdigit.dialogs import VDigitCategoryDialog, DigitZBulkDialog, VDigitDuplicatesDialog
 
 class VDigitWindow(BufferedWindow):
     """!A Buffered window extended for vector digitizer.
@@ -160,10 +154,10 @@ class VDigitWindow(BufferedWindow):
                 posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
                                                  self.mouse['end'][1] + self.dialogOffset))
                 
-                addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapLayer,
-                                                                   cats = cats,
-                                                                   pos = posWindow,
-                                                                   action = "add", ignoreError = True)
+                addRecordDlg = DisplayAttributesDialog(parent = self, map = mapLayer,
+                                                       cats = cats,
+                                                       pos = posWindow,
+                                                       action = "add", ignoreError = True)
                 
                 if self.toolbar.GetAction('type') == 'centroid':
                     for fid in fids:
@@ -177,10 +171,10 @@ class VDigitWindow(BufferedWindow):
                         sqlfile.file.write(sql + ";\n")
                     sqlfile.file.flush()
                     
-                    gcmd.RunCommand('db.execute',
-                                    parent = self,
-                                    quiet = True, 
-                                    input = sqlfile.name)
+                    RunCommand('db.execute',
+                               parent = self,
+                               quiet = True, 
+                               input = sqlfile.name)
                 
                 if addRecordDlg.mapDBInfo:
                     self._updateATM()
@@ -266,10 +260,10 @@ class VDigitWindow(BufferedWindow):
                                            dbInfo.GetKeyColumn(layer), cat))
             
             sqlfile.file.flush()
-            gcmd.RunCommand('db.execute',
-                            parent = True,
-                            quiet = True,
-                            input = sqlfile.name)
+            RunCommand('db.execute',
+                       parent = True,
+                       quiet = True,
+                       input = sqlfile.name)
             
     def _updateATM(self):
         """!Update open Attribute Table Manager
@@ -354,9 +348,9 @@ class VDigitWindow(BufferedWindow):
             # select attributes based on coordinates (all layers)
             if self.parent.dialogs['attributes'] is None:
                 self.parent.dialogs['attributes'] = \
-                    dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapLayer,
-                                                        cats = cats,
-                                                        action = "update")
+                    DisplayAttributesDialog(parent = self, map = mapLayer,
+                                            cats = cats,
+                                            action = "update")
             else:
                 # upgrade dialog
                 self.parent.dialogs['attributes'].UpdateDialog(cats = cats)
@@ -501,8 +495,8 @@ class VDigitWindow(BufferedWindow):
         try:
             mapLayer = self.toolbar.GetLayer().GetName()
         except:
-            gcmd.GMessage(parent = self,
-                          message = _("No vector map selected for editing."))
+            GMessage(parent = self,
+                     message = _("No vector map selected for editing."))
             event.Skip()
             return
         
@@ -857,8 +851,8 @@ class VDigitWindow(BufferedWindow):
                 mapName = self.toolbar.GetLayer().GetName()
             except:
                 mapName = None
-                gcmd.GError(parent = self,
-                            message = _("No vector map selected for editing."))
+                GError(parent = self,
+                       message = _("No vector map selected for editing."))
                     
             if mapName:
                 if self.toolbar.GetAction('type') == 'line':
@@ -892,10 +886,10 @@ class VDigitWindow(BufferedWindow):
                                 (UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'), )
                             }}
                     
-                    addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapName,
-                                                                       cats = cats,
-                                                                       pos = posWindow,
-                                                                       action = "add", ignoreError = True)
+                    addRecordDlg = DisplayAttributesDialog(parent = self, map = mapName,
+                                                           cats = cats,
+                                                           pos = posWindow,
+                                                           action = "add", ignoreError = True)
                     
                     for fid in fids:
                         self._geomAttrb(fid, addRecordDlg, 'length')
@@ -910,10 +904,10 @@ class VDigitWindow(BufferedWindow):
                         for sql in addRecordDlg.GetSQLString():
                             sqlfile.file.write(sql + ";\n")
                         sqlfile.file.flush()
-                        gcmd.RunCommand('db.execute',
-                                        parent = True,
-                                        quiet = True,
-                                        input = sqlfile.name)
+                        RunCommand('db.execute',
+                                   parent = True,
+                                   quiet = True,
+                                   input = sqlfile.name)
                         
                     if addRecordDlg.mapDBInfo:
                         self._updateATM()

+ 10 - 770
gui/wxpython/gui_modules/vdigit.py

@@ -1,16 +1,10 @@
 """!
-@package vdigit
+@package vdigit.preferences
 
-@brief Dialogs for wxGUI vector digitizer
+@brief wxGUI vector digitizer preferences dialogs
 
 Classes:
- - VDigit
  - VDigitSettingsDialog
- - VDigitCategoryDialog
- - CategoryListCtrl
- - VDigitZBulkDialog
- - VDigitDuplicatesDialog
- - CheckListFeature
 
 (C) 2007-2011 by the GRASS Development Team
 This program is free software under the GNU General Public License
@@ -19,46 +13,17 @@ This program is free software under the GNU General Public License
 @author Martin Landa <landa.martin gmail.com>
 """
 
-import os
-import sys
-import string
-import copy
 import textwrap
-import traceback
-
-from threading import Thread
 
 import wx
 import wx.lib.colourselect as csel
-import wx.lib.mixins.listctrl as listmix
-
-import gcmd
-import dbm
-from debug import Debug as Debug
-import gselect
-import globalvar
-from units import Units
-from preferences import globalSettings as UserSettings
-try:
-    from wxvdigit  import IVDigit, GV_LINES
-    haveVDigit = True
-    errorMsg = ''
-except ImportError, err:
-    haveVDigit = False
-    errorMsg = err
-    GV_LINES = -1
-    class IVDigit:
-        def __init__(self):
-            pass
-
-class VDigit(IVDigit):
-    def __init__(self, mapwindow):
-        """!Base class of vector digitizer
-        
-        @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
-        """
-        IVDigit.__init__(self, mapwindow)
-        
+
+from core             import globalvar
+from core.debug       import Debug
+from gui_core.gselect import ColumnSelect
+from core.units       import Units
+from core.settings    import UserSettings
+
 class VDigitSettingsDialog(wx.Dialog):
     def __init__(self, parent, title, style = wx.DEFAULT_DIALOG_STYLE):
         """!Standard settings dialog for digitization purposes
@@ -467,7 +432,7 @@ class VDigitSettingsDialog(wx.Dialog):
             ### self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
             check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
             # column (only numeric)
-            column = gselect.ColumnSelect(parent = panel, size = (200, -1))
+            column = ColumnSelect(parent = panel, size = (200, -1))
             column.InsertColumns(vector = vectorName,
                                  layer = layer, excludeKey = True,
                                  type = ['integer', 'double precision'])
@@ -814,728 +779,3 @@ class VDigitSettingsDialog(wx.Dialog):
         # redraw map if auto-rendering is enabled
         if self.parent.IsAutoRendered(): 
             self.parent.OnRender(None)
-
-class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
-    def __init__(self, parent, title,
-                 vectorName, query = None, cats = None,
-                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
-        """!Dialog used to display/modify categories of vector objects
-        
-        @param parent
-        @param title dialog title
-        @param query {coordinates, qdist} - used by v.edit/v.what
-        @param cats  directory of lines (layer/categories) - used by vdigit
-        @param style dialog style
-        """
-        self.parent = parent       # mapdisplay.BufferedWindow class instance
-        self.digit = parent.digit
-        
-        # map name
-        self.vectorName = vectorName
-        
-        # line : {layer: [categories]}
-        self.cats = {}
-        
-        # do not display dialog if no line is found (-> self.cats)
-        if cats is None:
-            if self._getCategories(query[0], query[1]) == 0 or not self.line:
-                Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
-        else:
-            self.cats = cats
-            for line in cats.keys():
-                for layer in cats[line].keys():
-                    self.cats[line][layer] = list(cats[line][layer])
-            
-            layers = []
-            for layer in self.digit.GetLayers():
-                layers.append(str(layer))
-        
-        # make copy of cats (used for 'reload')
-        self.cats_orig = copy.deepcopy(self.cats)
-        
-        wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY, title = title,
-                           style = style, **kwargs)
-        
-        # list of categories
-        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
-                           label = " %s " % _("List of categories - right-click to delete"))
-        listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        self.list = CategoryListCtrl(parent = self, id = wx.ID_ANY,
-                                     style = wx.LC_REPORT |
-                                     wx.BORDER_NONE |
-                                     wx.LC_SORT_ASCENDING |
-                                     wx.LC_HRULES |
-                                     wx.LC_VRULES)
-        # sorter
-        self.fid = self.cats.keys()[0]
-        self.itemDataMap = self.list.Populate(self.cats[self.fid])
-        listmix.ColumnSorterMixin.__init__(self, 2)
-        self.fidMulti = wx.Choice(parent = self, id = wx.ID_ANY,
-                                  size = (150, -1))
-        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
-        self.fidText = wx.StaticText(parent = self, id = wx.ID_ANY)
-        if len(self.cats.keys()) == 1:
-            self.fidMulti.Show(False)
-            self.fidText.SetLabel(str(self.fid))
-        else:
-            self.fidText.Show(False)
-            choices = []
-            for fid in self.cats.keys():
-                choices.append(str(fid))
-            self.fidMulti.SetItems(choices)
-            self.fidMulti.SetSelection(0)
-        
-        listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
-
-        # add new category
-        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
-                           label = " %s " % _("Add new category"))
-        addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        flexSizer = wx.FlexGridSizer (cols = 5, hgap = 5, vgap = 5)
-        flexSizer.AddGrowableCol(3)
-
-        layerNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                 label = "%s:" % _("Layer"))
-        self.layerNew = wx.Choice(parent = self, id = wx.ID_ANY, size = (75, -1),
-                                  choices = layers)
-        if len(layers) > 0:
-            self.layerNew.SetSelection(0)
-        
-        catNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
-                               label = "%s:" % _("Category"))
-
-        try:
-            newCat = max(self.cats[self.fid][1]) + 1
-        except KeyError:
-            newCat = 1
-        self.catNew = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (75, -1),
-                                  initial = newCat, min = 0, max = 1e9)
-        btnAddCat = wx.Button(self, wx.ID_ADD)
-        flexSizer.Add(item = layerNewTxt, proportion = 0,
-                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-        flexSizer.Add(item = self.layerNew, proportion = 0,
-                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-        flexSizer.Add(item = catNewTxt, proportion = 0,
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
-                      border = 10)
-        flexSizer.Add(item = self.catNew, proportion = 0,
-                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-        flexSizer.Add(item = btnAddCat, proportion = 0,
-                      flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
-        addSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
-
-        # buttons
-        btnApply = wx.Button(self, wx.ID_APPLY)
-        btnApply.SetToolTipString(_("Apply changes"))
-        btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
-        btnOk = wx.Button(self, wx.ID_OK)
-        btnOk.SetToolTipString(_("Apply changes and close dialog"))
-        btnOk.SetDefault()
-
-        # sizers
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(btnCancel)
-        #btnSizer.AddButton(btnReload)
-        #btnSizer.SetNegativeButton(btnReload)
-        btnSizer.AddButton(btnApply)
-        btnSizer.AddButton(btnOk)
-        btnSizer.Realize()
-        
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(item = listSizer, proportion = 1,
-                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-        mainSizer.Add(item = addSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALIGN_CENTER |
-                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
-        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
-        fidSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
-                                        label = _("Feature id:")),
-                     proportion = 0, border = 5,
-                     flag = wx.ALIGN_CENTER_VERTICAL)
-        fidSizer.Add(item = self.fidMulti, proportion = 0,
-                     flag = wx.EXPAND | wx.ALL,  border = 5)
-        fidSizer.Add(item = self.fidText, proportion = 0,
-                     flag = wx.EXPAND | wx.ALL,  border = 5)
-        mainSizer.Add(item = fidSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL, border = 5)
-        mainSizer.Add(item = btnSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-        
-        self.SetSizer(mainSizer)
-        mainSizer.Fit(self)
-        self.SetAutoLayout(True)
-
-        # set min size for dialog
-        self.SetMinSize(self.GetBestSize())
-
-        # bindings
-        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
-        btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
-        btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
-        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
-                                     
-        # list
-        self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
-        self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
-        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
-        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
-        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
-
-    def GetListCtrl(self):
-        """!Used by ColumnSorterMixin
-        """
-        return self.list
-
-    def OnColClick(self, event):
-        """!Click on column header (order by)
-        """
-        event.Skip()
-        
-    def OnBeginEdit(self, event):
-        """!Editing of item started
-        """
-        event.Allow()
-
-    def OnEndEdit(self, event):
-        """!Finish editing of item
-        """
-        itemIndex = event.GetIndex()
-        layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
-        catOld = int (self.list.GetItem(itemIndex, 1).GetText())
-
-        if event.GetColumn() == 0:
-            layerNew = int(event.GetLabel())
-            catNew = catOld
-        else:
-            layerNew = layerOld
-            catNew = int(event.GetLabel())
-        
-        try:
-            if layerNew not in self.cats[self.fid].keys():
-                self.cats[self.fid][layerNew] = []
-            self.cats[self.fid][layerNew].append(catNew)
-            self.cats[self.fid][layerOld].remove(catOld)
-        except:
-            event.Veto()
-            self.list.SetStringItem(itemIndex, 0, str(layerNew))
-            self.list.SetStringItem(itemIndex, 1, str(catNew))
-            dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
-                                           "Layer and category number must be integer.\n"
-                                           "Layer number must be greater then zero.") %
-                                   { 'layer': self.layerNew.GetStringSelection(),
-                                     'category' : str(self.catNew.GetValue()) },
-                                   _("Error"), wx.OK | wx.ICON_ERROR)
-            dlg.ShowModal()
-            dlg.Destroy()
-            return False
-
-    def OnRightDown(self, event):
-        """!Mouse right button down
-        """
-        x = event.GetX()
-        y = event.GetY()
-        item, flags = self.list.HitTest((x, y))
-
-        if item !=  wx.NOT_FOUND and \
-                flags & wx.LIST_HITTEST_ONITEM:
-            self.list.Select(item)
-
-        event.Skip()
-
-    def OnRightUp(self, event):
-        """!Mouse right button up
-        """
-        if not hasattr(self, "popupID1"):
-            self.popupID1 = wx.NewId()
-            self.popupID2 = wx.NewId()
-            self.popupID3 = wx.NewId()
-            self.Bind(wx.EVT_MENU, self.OnItemDelete,    id = self.popupID1)
-            self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id = self.popupID2)
-            self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
-
-        # generate popup-menu
-        menu = wx.Menu()
-        menu.Append(self.popupID1, _("Delete selected"))
-        if self.list.GetFirstSelected() == -1:
-            menu.Enable(self.popupID1, False)
-
-        menu.Append(self.popupID2, _("Delete all"))
-        menu.AppendSeparator()
-        menu.Append(self.popupID3, _("Reload"))
-
-        self.PopupMenu(menu)
-        menu.Destroy()
-
-    def OnItemSelected(self, event):
-        """!Item selected
-        """
-        event.Skip()
-
-    def OnItemDelete(self, event):
-        """!Delete selected item(s) from the list (layer/category pair)
-        """
-        item = self.list.GetFirstSelected()
-        while item != -1:
-            layer = int (self.list.GetItem(item, 0).GetText())
-            cat = int (self.list.GetItem(item, 1).GetText())
-            self.list.DeleteItem(item)
-            self.cats[self.fid][layer].remove(cat)
-
-            item = self.list.GetFirstSelected()
-            
-        event.Skip()
-        
-    def OnItemDeleteAll(self, event):
-        """!Delete all items from the list
-        """
-        self.list.DeleteAllItems()
-        self.cats[self.fid] = {}
-
-        event.Skip()
-
-    def OnFeature(self, event):
-        """!Feature id changed (on duplicates)
-        """
-        self.fid = int(event.GetString())
-        
-        self.itemDataMap = self.list.Populate(self.cats[self.fid],
-                                              update = True)
-
-        try:
-            newCat = max(self.cats[self.fid][1]) + 1
-        except KeyError:
-            newCat = 1
-            
-        self.catNew.SetValue(newCat)
-        
-        event.Skip()
-        
-    def _getCategories(self, coords, qdist):
-        """!Get layer/category pairs for all available
-        layers
-
-        Return True line found or False if not found
-        """
-        ret = gcmd.RunCommand('v.what',
-                              parent = self,
-                              quiet = True,
-                              map = self.vectorName,
-                              east_north = '%f,%f' % \
-                                  (float(coords[0]), float(coords[1])),
-                              distance = qdist)
-
-        if not ret:
-            return False
-
-        for item in ret.splitlines():
-            litem = item.lower()
-            if "id:" in litem: # get line id
-                self.line = int(item.split(':')[1].strip())
-            elif "layer:" in litem: # add layer
-                layer = int(item.split(':')[1].strip())
-                if layer not in self.cats.keys():
-                    self.cats[layer] = []
-            elif "category:" in litem: # add category
-                self.cats[layer].append(int(item.split(':')[1].strip()))
-
-        return True
-
-    def OnReload(self, event):
-        """!Reload button pressed
-        """
-        # restore original list
-        self.cats = copy.deepcopy(self.cats_orig)
-
-        # polulate list
-        self.itemDataMap = self.list.Populate(self.cats[self.fid],
-                                              update = True)
-
-        event.Skip()
-
-    def OnCancel(self, event):
-        """!Cancel button pressed
-        """
-        self.parent.parent.dialogs['category'] = None
-        if self.digit:
-            self.digit.GetDisplay().SetSelected([])
-            self.parent.UpdateMap(render = False)
-        else:
-            self.parent.parent.OnRender(None)
-            
-        self.Close()
-
-    def OnApply(self, event):
-        """!Apply button pressed
-        """
-        for fid in self.cats.keys():
-            newfid = self.ApplyChanges(fid)
-            if fid == self.fid and newfid > 0:
-                self.fid = newfid
-            
-    def ApplyChanges(self, fid):
-        """!Apply changes 
-
-        @param fid feature id
-        """
-        cats = self.cats[fid]
-        cats_orig = self.cats_orig[fid]
-
-        # action : (catsFrom, catsTo)
-        check = {'catadd': (cats,      cats_orig),
-                 'catdel': (cats_orig, cats)}
-
-        newfid = -1
-        
-        # add/delete new category
-        for action, catsCurr in check.iteritems():
-            for layer in catsCurr[0].keys():
-                catList = []
-                for cat in catsCurr[0][layer]:
-                    if layer not in catsCurr[1].keys() or \
-                            cat not in catsCurr[1][layer]:
-                        catList.append(cat)
-                if catList != []:
-                    if action == 'catadd':
-                        add = True
-                    else:
-                        add = False
-                        
-                    newfid = self.digit.SetLineCats(fid, layer,
-                                                    catList, add)
-                    if len(self.cats.keys()) == 1:
-                        self.fidText.SetLabel("%d" % newfid)
-                    else:
-                        choices = self.fidMulti.GetItems()
-                        choices[choices.index(str(fid))] = str(newfid)
-                        self.fidMulti.SetItems(choices)
-                        self.fidMulti.SetStringSelection(str(newfid))
-                    
-                    self.cats[newfid] = self.cats[fid]
-                    del self.cats[fid]
-                    
-                    fid = newfid
-                    if self.fid < 0:
-                        wx.MessageBox(parent = self, message = _("Unable to update vector map."),
-                                      caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
-        
-        self.cats_orig[fid] = copy.deepcopy(cats)
-        
-        return newfid
-
-    def OnOK(self, event):
-        """!OK button pressed
-        """
-        self.OnApply(event)
-        self.OnCancel(event)
-
-    def OnAddCat(self, event):
-        """!Button 'Add' new category pressed
-        """
-        try:
-            layer = int(self.layerNew.GetStringSelection())
-            cat   = int(self.catNew.GetValue())
-            if layer <= 0:
-                raise ValueError
-        except ValueError:
-            gcmd.GError(parent = self,
-                        message = _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
-                                    "Layer and category number must be integer.\n"
-                                    "Layer number must be greater then zero.") %
-                        {'layer' : str(self.layerNew.GetValue()),
-                         'category' : str(self.catNew.GetValue())})
-            return False
-        
-        if layer not in self.cats[self.fid].keys():
-            self.cats[self.fid][layer] = []
-        
-        self.cats[self.fid][layer].append(cat)
-        
-        # reload list
-        self.itemDataMap = self.list.Populate(self.cats[self.fid],
-                                              update = True)
-        
-        # update category number for add
-        self.catNew.SetValue(cat + 1)
-        
-        event.Skip()
-
-        return True
-
-    def GetLine(self):
-        """!Get id of selected line of 'None' if no line is selected
-        """
-        return self.cats.keys()
-
-    def UpdateDialog(self, query = None, cats = None):
-        """!Update dialog
-        
-        @param query {coordinates, distance} - v.what
-        @param cats  directory layer/cats    - vdigit
-        Return True if updated otherwise False
-        """
-        # line: {layer: [categories]}
-        self.cats = {}
-        # do not display dialog if no line is found (-> self.cats)
-        if cats is None:
-            ret = self._getCategories(query[0], query[1])
-        else:
-            self.cats = cats
-            for line in cats.keys():
-                for layer in cats[line].keys():
-                    self.cats[line][layer] = list(cats[line][layer])
-            ret = 1
-        if ret == 0 or len(self.cats.keys()) < 1:
-            Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
-            return False
-        
-        # make copy of cats (used for 'reload')
-        self.cats_orig = copy.deepcopy(self.cats)
-
-        # polulate list
-        self.fid = self.cats.keys()[0]
-        self.itemDataMap = self.list.Populate(self.cats[self.fid],
-                                              update = True)
-
-        try:
-            newCat = max(self.cats[self.fid][1]) + 1
-        except KeyError:
-            newCat = 1
-        self.catNew.SetValue(newCat)
-        
-        if len(self.cats.keys()) == 1:
-            self.fidText.Show(True)
-            self.fidMulti.Show(False)
-            self.fidText.SetLabel("%d" % self.fid)
-        else:
-            self.fidText.Show(False)
-            self.fidMulti.Show(True)
-            choices = []
-            for fid in self.cats.keys():
-                choices.append(str(fid))
-            self.fidMulti.SetItems(choices)
-            self.fidMulti.SetSelection(0)
-
-        self.Layout()
-        
-        return True
-
-class CategoryListCtrl(wx.ListCtrl,
-                       listmix.ListCtrlAutoWidthMixin,
-                       listmix.TextEditMixin):
-    def __init__(self, parent, id, pos = wx.DefaultPosition,
-                 size = wx.DefaultSize, style = 0):
-        """!List of layers/categories"""
-        self.parent = parent
-        
-        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
-
-        listmix.ListCtrlAutoWidthMixin.__init__(self)
-        listmix.TextEditMixin.__init__(self)
-
-    def Populate(self, cats, update = False):
-        """!Populate the list
-        """
-        itemData = {} # requested by sorter
-
-        if not update:
-            self.InsertColumn(0, _("Layer"))
-            self.InsertColumn(1, _("Category"))
-        else:
-            self.DeleteAllItems()
-
-        i = 1
-        for layer in cats.keys():
-            catsList = cats[layer]
-            for cat in catsList:
-                index = self.InsertStringItem(sys.maxint, str(catsList[0]))
-                self.SetStringItem(index, 0, str(layer))
-                self.SetStringItem(index, 1, str(cat))
-                self.SetItemData(index, i)
-                itemData[i] = (str(layer), str(cat))
-                i = i + 1
-
-        if not update:
-            self.SetColumnWidth(0, 100)
-            self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
-
-        self.currentItem = 0
-
-        return itemData
-
-class VDigitZBulkDialog(wx.Dialog):
-    def __init__(self, parent, title, nselected, style = wx.DEFAULT_DIALOG_STYLE):
-        """!Dialog used for Z bulk-labeling tool
-        """
-        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
-
-        self.parent = parent # mapdisplay.BufferedWindow class instance
-
-        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
-
-        border = wx.BoxSizer(wx.VERTICAL)
-        
-        txt = wx.StaticText(parent = self,
-                            label = _("%d lines selected for z bulk-labeling") % nselected);
-        border.Add(item = txt, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
-
-        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Set value"))
-        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
-        flexSizer.AddGrowableCol(0)
-
-        # starting value
-        txt = wx.StaticText(parent = self,
-                            label = _("Starting value"));
-        self.value = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
-                                 initial = 0,
-                                 min = -1e6, max = 1e6)
-        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
-        flexSizer.Add(self.value, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
-
-        # step
-        txt = wx.StaticText(parent = self,
-                            label = _("Step"))
-        self.step  = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
-                                 initial = 0,
-                                 min = 0, max = 1e6)
-        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
-        flexSizer.Add(self.step, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
-
-        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
-        border.Add(item = sizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 0)
-
-        # buttons
-        btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnOk = wx.Button(self, wx.ID_OK)
-        btnOk.SetDefault()
-
-        # sizers
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(btnCancel)
-        btnSizer.AddButton(btnOk)
-        btnSizer.Realize()
-        
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(item = border, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
-        mainSizer.Add(item = btnSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-
-        self.SetSizer(mainSizer)
-        mainSizer.Fit(self)
-
-class VDigitDuplicatesDialog(wx.Dialog):
-    def __init__(self, parent, data, title = _("List of duplicates"),
-                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                 pos = wx.DefaultPosition):
-        """!Show duplicated feature ids
-        """
-        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
-                           pos = pos)
-        
-        self.parent = parent # BufferedWindow
-        self.data = data
-        self.winList = []
-
-        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
-
-        # notebook
-        self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
-
-        id = 1
-        for key in self.data.keys():
-            panel = wx.Panel(parent = self.notebook, id = wx.ID_ANY)
-            self.notebook.AddPage(page = panel, text = " %d " % (id))
-            
-            # notebook body
-            border = wx.BoxSizer(wx.VERTICAL)
-
-            win = CheckListFeature(parent = panel, data = list(self.data[key]))
-            self.winList.append(win.GetId())
-
-            border.Add(item = win, proportion = 1,
-                       flag = wx.ALL | wx.EXPAND, border = 5)
-
-            panel.SetSizer(border)
-
-            id += 1
-
-        # buttons
-        btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnOk = wx.Button(self, wx.ID_OK)
-        btnOk.SetDefault()
-
-        # sizers
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(btnCancel)
-        btnSizer.AddButton(btnOk)
-        btnSizer.Realize()
-        
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
-        mainSizer.Add(item = btnSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
-
-        self.SetSizer(mainSizer)
-        mainSizer.Fit(self)
-        self.SetAutoLayout(True)
-
-        # set min size for dialog
-        self.SetMinSize((250, 180))
-
-    def GetUnSelected(self):
-        """!Get unselected items (feature id)
-
-        @return list of ids
-        """
-        ids = []
-        for id in self.winList:
-            wlist = self.FindWindowById(id)
-
-            for item in range(wlist.GetItemCount()):
-                if not wlist.IsChecked(item):
-                    ids.append(int(wlist.GetItem(item, 0).GetText()))
-                    
-        return ids
-
-class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
-    def __init__(self, parent, data,
-                 pos = wx.DefaultPosition, log = None):
-        """!List of mapset/owner/group
-        """
-        self.parent = parent
-        self.data = data
-
-        wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
-                             style = wx.LC_REPORT)
-
-        listmix.CheckListCtrlMixin.__init__(self)
-
-        self.log = log
-
-        # setup mixins
-        listmix.ListCtrlAutoWidthMixin.__init__(self)
-
-        self.LoadData(self.data)
-
-    def LoadData(self, data):
-        """!Load data into list
-        """
-        self.InsertColumn(0, _('Feature id'))
-        self.InsertColumn(1, _('Layer (Categories)'))
-
-        for item in data:
-            index = self.InsertStringItem(sys.maxint, str(item[0]))
-            self.SetStringItem(index, 1, str(item[1]))
-
-        # enable all items by default
-        for item in range(self.GetItemCount()):
-            self.CheckItem(item, True)
-
-        self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
-        self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE_USEHEADER)
-                
-    def OnCheckItem(self, index, flag):
-        """!Mapset checked/unchecked
-        """
-        pass

+ 815 - 0
gui/wxpython/vdigit/toolbars.py

@@ -0,0 +1,815 @@
+"""!
+@package vdigit.toolbars
+
+@brief wxGUI vector digitizer toolbars
+
+List of classes:
+ - VDigitToolbar
+
+(C) 2007-2011 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.
+
+@author Martin Landa <landa.martin gmail.com>
+"""
+
+from grass.script import core as grass
+
+from gui_core.toolbars  import BaseToolbar
+from gui_core.dialogs   import CreateNewVector
+from vdigit.preferences import VDigitSettingsDialog
+from vdigit.main        import VDigit
+from core.debug         import Debug
+from core.settings      import UserSettings
+from core.gcmd          import GError
+
+class VDigitToolbar(BaseToolbar):
+    """!Toolbar for digitization
+    """
+    def __init__(self, parent, MapWindow, tools = [], layerTree = None, log = None):
+        self.MapWindow     = MapWindow
+        self.Map           = MapWindow.GetMap() # Map class instance
+        self.layerTree     = layerTree  # reference to layer tree associated to map display
+        self.log           = log        # log area
+        self.tools         = tools
+        BaseToolbar.__init__(self, parent)
+        self.digit         = None
+        
+        # currently selected map layer for editing (reference to MapLayer instance)
+        self.mapLayer = None
+        # list of vector layers from Layer Manager (only in the current mapset)
+        self.layers   = [] 
+        
+        self.comboid  = self.combo = None
+        self.undo     = -1
+        
+        # only one dialog can be open
+        self.settingsDialog   = None
+        
+        # create toolbars (two rows optionally)
+        self.InitToolbar(self._toolbarData())
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
+        # default action (digitize new point, line, etc.)
+        self.action = { 'desc' : '',
+                        'type' : '',
+                        'id'   : -1 }
+        
+        # list of available vector maps
+        self.UpdateListOfLayers(updateTool = True)
+        
+        # realize toolbar
+        self.Realize()
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        if self.combo:
+            self.combo.Hide()
+            self.combo.Show()
+        
+        # disable undo/redo
+        if self.undo:
+            self.EnableTool(self.undo, False)
+        
+        # toogle to pointer by default
+        self.OnTool(None)
+        
+        self.FixSize(width = 105)
+        
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        data = []
+        icons = Icons['vdigit']
+        if not self.tools or 'selector' in self.tools:
+            data.append((None, ))
+        if not self.tools or 'addPoint' in self.tools:
+            data.append(("addPoint", icons["addPoint"],
+                         self.OnAddPoint,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'addLine' in self.tools:
+            data.append(("addLine", icons["addLine"],
+                        self.OnAddLine,
+                        wx.ITEM_CHECK))
+        if not self.tools or 'addBoundary' in self.tools:
+            data.append(("addBoundary", icons["addBoundary"],
+                         self.OnAddBoundary,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'addCentroid' in self.tools:
+            data.append(("addCentroid", icons["addCentroid"],
+                         self.OnAddCentroid,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'addArea' in self.tools:
+            data.append(("addArea", icons["addArea"],
+                         self.OnAddArea,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'moveVertex' in self.tools:            
+            data.append(("moveVertex", icons["moveVertex"],
+                         self.OnMoveVertex,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'addVertex' in self.tools:
+            data.append(("addVertex", icons["addVertex"],
+                         self.OnAddVertex,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'removeVertex' in self.tools:
+            data.append(("removeVertex", icons["removeVertex"],
+                         self.OnRemoveVertex,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'editLine' in self.tools:            
+            data.append(("editLine", icons["editLine"],
+                         self.OnEditLine,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'moveLine' in self.tools:
+            data.append(("moveLine", icons["moveLine"],
+                         self.OnMoveLine,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'deleteLine' in self.tools:
+            data.append(("deleteLine", icons["deleteLine"],
+                         self.OnDeleteLine,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'displayCats' in self.tools:
+            data.append(("displayCats", icons["displayCats"],
+                         self.OnDisplayCats,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'displayAttr' in self.tools:
+            data.append(("displayAttr", icons["displayAttr"],
+                         self.OnDisplayAttr,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'additionalSelf.Tools' in self.tools:
+            data.append(("additionalTools", icons["additionalTools"],
+                         self.OnAdditionalToolMenu,
+                         wx.ITEM_CHECK))
+        if not self.tools or 'undo' in self.tools or \
+                'settings' in self.tools or \
+                'quit' in self.tools:
+            data.append((None, ))
+        if not self.tools or 'undo' in self.tools:
+            data.append(("undo", icons["undo"],
+                         self.OnUndo))
+        if not self.tools or 'settings' in self.tools:
+            data.append(("settings", icons["settings"],
+                         self.OnSettings))
+        if not self.tools or 'quit' in self.tools:
+            data.append(("quit", icons["quit"],
+                         self.OnExit))
+        
+        return self._getToolbarData(data)
+    
+    def OnTool(self, event):
+        """!Tool selected -> disable selected tool in map toolbar"""
+        if self.parent.GetName() == 'IClassWindow':
+            toolbarName = 'iClassMap'
+        else:
+            toolbarName = 'map'
+        aId = self.parent.toolbars[toolbarName].GetAction(type = 'id')
+        self.parent.toolbars[toolbarName].ToggleTool(aId, False)
+                
+        # set cursor
+        cursor = self.parent.cursors["cross"]
+        self.MapWindow.SetCursor(cursor)
+        
+        # pointer
+        self.parent.OnPointer(None)
+                
+        if event:
+            # deselect previously selected tool
+            aId = self.action.get('id', -1)
+            if aId != event.GetId() and \
+                    self.action['id'] != -1:
+                self.ToggleTool(self.action['id'], False)
+            else:
+                self.ToggleTool(self.action['id'], True)
+            
+            self.action['id'] = event.GetId()
+            
+            event.Skip()
+        
+        if self.action['id'] != -1:
+            self.ToggleTool(self.action['id'], True)
+        
+        # clear tmp canvas
+        if self.action['id'] != aId:
+            self.MapWindow.ClearLines(pdc = self.MapWindow.pdcTmp)
+            if self.digit and \
+                    len(self.MapWindow.digit.GetDisplay().GetSelected()) > 0:
+                # cancel action
+                self.MapWindow.OnMiddleDown(None)
+        
+        # set focus
+        self.MapWindow.SetFocus()
+        
+    def OnAddPoint(self, event):
+        """!Add point to the vector map Laier"""
+        Debug.msg (2, "VDigitToolbar.OnAddPoint()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "point",
+                        'id'   : self.addPoint }
+        self.MapWindow.mouse['box'] = 'point'
+        
+    def OnAddLine(self, event):
+        """!Add line to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddLine()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "line",
+                        'id'   : self.addLine }
+        self.MapWindow.mouse['box'] = 'line'
+        ### self.MapWindow.polycoords = [] # reset temp line
+                
+    def OnAddBoundary(self, event):
+        """!Add boundary to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddBoundary()")
+        if self.action['desc'] != 'addLine' or \
+                self.action['type'] != 'boundary':
+            self.MapWindow.polycoords = [] # reset temp line
+        self.action = { 'desc' : "addLine",
+                        'type' : "boundary",
+                        'id'   : self.addBoundary }
+        self.MapWindow.mouse['box'] = 'line'
+        
+    def OnAddCentroid(self, event):
+        """!Add centroid to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddCentroid()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "centroid",
+                        'id'   : self.addCentroid }
+        self.MapWindow.mouse['box'] = 'point'
+
+    def OnAddArea(self, event):
+        """!Add area to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddCentroid()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "area",
+                        'id'   : self.addArea }
+        self.MapWindow.mouse['box'] = 'line'
+
+    def OnExit (self, event=None):
+        """!Quit digitization tool"""
+        # stop editing of the currently selected map layer
+        if self.mapLayer:
+            self.StopEditing()
+        
+        # close dialogs if still open
+        if self.settingsDialog:
+            self.settingsDialog.OnCancel(None)
+            
+        # set default mouse settings
+        self.MapWindow.mouse['use'] = "pointer"
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.polycoords = []
+        
+        # disable the toolbar
+        self.parent.RemoveToolbar("vdigit")
+        
+    def OnMoveVertex(self, event):
+        """!Move line vertex"""
+        Debug.msg(2, "Digittoolbar.OnMoveVertex():")
+        self.action = { 'desc' : "moveVertex",
+                        'id'   : self.moveVertex }
+        self.MapWindow.mouse['box'] = 'point'
+
+    def OnAddVertex(self, event):
+        """!Add line vertex"""
+        Debug.msg(2, "Digittoolbar.OnAddVertex():")
+        self.action = { 'desc' : "addVertex",
+                        'id'   : self.addVertex }
+        self.MapWindow.mouse['box'] = 'point'
+        
+    def OnRemoveVertex(self, event):
+        """!Remove line vertex"""
+        Debug.msg(2, "Digittoolbar.OnRemoveVertex():")
+        self.action = { 'desc' : "removeVertex",
+                        'id'   : self.removeVertex }
+        self.MapWindow.mouse['box'] = 'point'
+
+    def OnEditLine(self, event):
+        """!Edit line"""
+        Debug.msg(2, "Digittoolbar.OnEditLine():")
+        self.action = { 'desc' : "editLine",
+                        'id'   : self.editLine }
+        self.MapWindow.mouse['box'] = 'line'
+
+    def OnMoveLine(self, event):
+        """!Move line"""
+        Debug.msg(2, "Digittoolbar.OnMoveLine():")
+        self.action = { 'desc' : "moveLine",
+                        'id'   : self.moveLine }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnDeleteLine(self, event):
+        """!Delete line"""
+        Debug.msg(2, "Digittoolbar.OnDeleteLine():")
+        self.action = { 'desc' : "deleteLine",
+                        'id'   : self.deleteLine }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnDisplayCats(self, event):
+        """!Display/update categories"""
+        Debug.msg(2, "Digittoolbar.OnDisplayCats():")
+        self.action = { 'desc' : "displayCats",
+                        'id'   : self.displayCats }
+        self.MapWindow.mouse['box'] = 'point'
+
+    def OnDisplayAttr(self, event):
+        """!Display/update attributes"""
+        Debug.msg(2, "Digittoolbar.OnDisplayAttr():")
+        self.action = { 'desc' : "displayAttrs",
+                        'id'   : self.displayAttr }
+        self.MapWindow.mouse['box'] = 'point'
+        
+    def OnUndo(self, event):
+        """!Undo previous changes"""
+        self.digit.Undo()
+        
+        event.Skip()
+
+    def EnableUndo(self, enable = True):
+        """!Enable 'Undo' in toolbar
+        
+        @param enable False for disable
+        """
+        if enable:
+            if self.GetToolEnabled(self.undo) is False:
+                self.EnableTool(self.undo, True)
+        else:
+            if self.GetToolEnabled(self.undo) is True:
+                self.EnableTool(self.undo, False)
+        
+    def OnSettings(self, event):
+        """!Show settings dialog"""
+        if self.digit is None:
+            try:
+                self.digit = self.MapWindow.digit = VDigit(mapwindow = self.MapWindow)
+            except SystemExit:
+                self.digit = self.MapWindow.digit = None
+        
+        if not self.settingsDialog:
+            self.settingsDialog = VDigitSettingsDialog(parent = self.parent, title = _("Digitization settings"),
+                                                       style = wx.DEFAULT_DIALOG_STYLE)
+            self.settingsDialog.Show()
+
+    def OnAdditionalToolMenu(self, event):
+        """!Menu for additional tools"""
+        point = wx.GetMousePosition()
+        toolMenu = wx.Menu()
+        
+        for label, itype, handler, desc in (
+            (_('Break selected lines/boundaries at intersection'),
+             wx.ITEM_CHECK, self.OnBreak, "breakLine"),
+            (_('Connect selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnConnect, "connectLine"),
+            (_('Copy categories'),
+             wx.ITEM_CHECK, self.OnCopyCats, "copyCats"),
+            (_('Copy features from (background) vector map'),
+             wx.ITEM_CHECK, self.OnCopy, "copyLine"),
+            (_('Copy attributes'),
+             wx.ITEM_CHECK, self.OnCopyAttrb, "copyAttrs"),
+            (_('Feature type conversion'),
+             wx.ITEM_CHECK, self.OnTypeConversion, "typeConv"),
+            (_('Flip selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnFlip, "flipLine"),
+            (_('Merge selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnMerge, "mergeLine"),
+            (_('Snap selected lines/boundaries (only to nodes)'),
+             wx.ITEM_CHECK, self.OnSnap, "snapLine"),
+            (_('Split line/boundary'),
+             wx.ITEM_CHECK, self.OnSplitLine, "splitLine"),
+            (_('Query features'),
+             wx.ITEM_CHECK, self.OnQuery, "queryLine"),
+            (_('Z bulk-labeling of 3D lines'),
+             wx.ITEM_CHECK, self.OnZBulk, "zbulkLine")):
+            # Add items to the menu
+            item = wx.MenuItem(parentMenu = toolMenu, id = wx.ID_ANY,
+                               text = label,
+                               kind = itype)
+            toolMenu.AppendItem(item)
+            self.MapWindow.Bind(wx.EVT_MENU, handler, item)
+            if self.action['desc'] == desc:
+                item.Check(True)
+        
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.MapWindow.PopupMenu(toolMenu)
+        toolMenu.Destroy()
+        
+        if self.action['desc'] == 'addPoint':
+            self.ToggleTool(self.additionalTools, False)
+        
+    def OnCopy(self, event):
+        """!Copy selected features from (background) vector map"""
+        if self.action['desc'] == 'copyLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopy():")
+        self.action = { 'desc' : "copyLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnSplitLine(self, event):
+        """!Split line"""
+        if self.action['desc'] == 'splitLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnSplitLine():")
+        self.action = { 'desc' : "splitLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'point'
+
+
+    def OnCopyCats(self, event):
+        """!Copy categories"""
+        if self.action['desc'] == 'copyCats': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopyCats():")
+        self.action = { 'desc' : "copyCats",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'point'
+
+    def OnCopyAttrb(self, event):
+        """!Copy attributes"""
+        if self.action['desc'] == 'copyAttrs': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopyAttrb():")
+        self.action = { 'desc' : "copyAttrs",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'point'
+        
+
+    def OnFlip(self, event):
+        """!Flip selected lines/boundaries"""
+        if self.action['desc'] == 'flipLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnFlip():")
+        self.action = { 'desc' : "flipLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnMerge(self, event):
+        """!Merge selected lines/boundaries"""
+        if self.action['desc'] == 'mergeLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnMerge():")
+        self.action = { 'desc' : "mergeLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnBreak(self, event):
+        """!Break selected lines/boundaries"""
+        if self.action['desc'] == 'breakLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnBreak():")
+        self.action = { 'desc' : "breakLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnSnap(self, event):
+        """!Snap selected features"""
+        if self.action['desc'] == 'snapLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnSnap():")
+        self.action = { 'desc' : "snapLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnConnect(self, event):
+        """!Connect selected lines/boundaries"""
+        if self.action['desc'] == 'connectLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnConnect():")
+        self.action = { 'desc' : "connectLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnQuery(self, event):
+        """!Query selected lines/boundaries"""
+        if self.action['desc'] == 'queryLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnQuery(): %s" % \
+                      UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection'))
+        self.action = { 'desc' : "queryLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnZBulk(self, event):
+        """!Z bulk-labeling selected lines/boundaries"""
+        if not self.digit.IsVector3D():
+            GError(parent = self.parent,
+                   message = _("Vector map is not 3D. Operation canceled."))
+            return
+        
+        if self.action['desc'] == 'zbulkLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnZBulk():")
+        self.action = { 'desc' : "zbulkLine",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'line'
+
+    def OnTypeConversion(self, event):
+        """!Feature type conversion
+
+        Supported conversions:
+         - point <-> centroid
+         - line <-> boundary
+        """
+        if self.action['desc'] == 'typeConv': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnTypeConversion():")
+        self.action = { 'desc' : "typeConv",
+                        'id'   : self.additionalTools }
+        self.MapWindow.mouse['box'] = 'box'
+
+    def OnSelectMap (self, event):
+        """!Select vector map layer for editing
+
+        If there is a vector map layer already edited, this action is
+        firstly terminated. The map layer is closed. After this the
+        selected map layer activated for editing.
+        """
+        if event.GetSelection() == 0: # create new vector map layer
+            if self.mapLayer:
+                openVectorMap = self.mapLayer.GetName(fullyQualified = False)['name']
+            else:
+                openVectorMap = None
+            dlg = CreateNewVector(self.parent,
+                                  exceptMap = openVectorMap, log = self.log,
+                                  cmd = (('v.edit',
+                                          { 'flags' : 'b', 'tool' : 'create' },
+                                          'map')),
+                                  disableAdd = True)
+            
+            if dlg and dlg.GetName():
+                # add layer to map layer tree
+                if self.layerTree:
+                    mapName = dlg.GetName() + '@' + grass.gisenv()['MAPSET']
+                    self.layerTree.AddLayer(ltype = 'vector',
+                                            lname = mapName,
+                                            lcmd = ['d.vect', 'map=%s' % mapName])
+                    
+                    vectLayers = self.UpdateListOfLayers(updateTool = True)
+                    selection = vectLayers.index(mapName)
+                
+                # create table ?
+                if dlg.IsChecked('table'):
+                    lmgr = self.parent.GetLayerManager()
+                    if lmgr:
+                        lmgr.OnShowAttributeTable(None, selection = 'table')
+                dlg.Destroy()
+            else:
+                self.combo.SetValue(_('Select vector map'))
+                if dlg:
+                    dlg.Destroy()
+                return
+        else:
+            selection = event.GetSelection() - 1 # first option is 'New vector map'
+        
+        # skip currently selected map
+        if self.layers[selection] == self.mapLayer:
+            return
+        
+        if self.mapLayer:
+            # deactive map layer for editing
+            self.StopEditing()
+        
+        # select the given map layer for editing
+        self.StartEditing(self.layers[selection])
+        
+        event.Skip()
+        
+    def StartEditing (self, mapLayer):
+        """!Start editing selected vector map layer.
+
+        @param mapLayer MapLayer to be edited
+        """
+        # deactive layer
+        self.Map.ChangeLayerActive(mapLayer, False)
+        
+        # clean map canvas
+        self.MapWindow.EraseMap()
+        
+        # unset background map if needed
+        if mapLayer:
+            if UserSettings.Get(group = 'vdigit', key = 'bgmap',
+                                subkey = 'value', internal = True) == mapLayer.GetName():
+                UserSettings.Set(group = 'vdigit', key = 'bgmap',
+                                 subkey = 'value', value = '', internal = True)
+            
+            self.parent.SetStatusText(_("Please wait, "
+                                        "opening vector map <%s> for editing...") % mapLayer.GetName(),
+                                        0)
+        
+        self.MapWindow.pdcVector = wx.PseudoDC()
+        self.digit = self.MapWindow.digit = VDigit(mapwindow = self.MapWindow)
+        
+        self.mapLayer = mapLayer
+        # open vector map
+        if self.digit.OpenMap(mapLayer.GetName()) is None:
+            self.mapLayer = None
+            self.StopEditing()
+            return False
+        
+        # check feature type (only for OGR layers)
+        fType = self.digit.GetFeatureType()
+        self.EnableAll()
+        self.EnableUndo(False)
+        
+        if fType == 'Point':
+            for tool in (self.addLine, self.addBoundary, self.addCentroid,
+                         self.addArea, self.moveVertex, self.addVertex,
+                         self.removeVertex, self.editLine):
+                self.EnableTool(tool, False)
+        elif fType == 'Line String':
+            for tool in (self.addPoint, self.addBoundary, self.addCentroid,
+                         self.addArea):
+                self.EnableTool(tool, False)
+        elif fType == 'Polygon':
+            for tool in (self.addPoint, self.addLine, self.addBoundary, self.addCentroid):
+                self.EnableTool(tool, False)
+        elif fType:
+            GError(parent = self,
+                   message = _("Unsupported feature type '%s'. Unable to edit "
+                               "OGR layer <%s>.") % (fType, mapLayer.GetName()))
+            self.digit.CloseMap()
+            self.mapLayer = None
+            self.StopEditing()
+            return False
+        
+        # update toolbar
+        if self.combo:
+            self.combo.SetValue(mapLayer.GetName())
+        if 'map' in self.parent.toolbars:
+            self.parent.toolbars['map'].combo.SetValue (_('Digitize'))
+        
+        if self.parent.GetName() != "IClassWindow":
+            lmgr = self.parent.GetLayerManager()
+            if lmgr:
+                lmgr.toolbars['tools'].Enable('vdigit', enable = False)
+        
+        Debug.msg (4, "VDigitToolbar.StartEditing(): layer=%s" % mapLayer.GetName())
+        
+        # change cursor
+        if self.MapWindow.mouse['use'] == 'pointer':
+            self.MapWindow.SetCursor(self.parent.cursors["cross"])
+        
+        if not self.MapWindow.resize:
+            self.MapWindow.UpdateMap(render = True)
+        
+        # respect opacity
+        opacity = mapLayer.GetOpacity(float = True)
+        
+        if opacity < 1.0:
+            alpha = int(opacity * 255)
+            self.digit.GetDisplay().UpdateSettings(alpha = alpha)
+        
+        return True
+
+    def StopEditing(self):
+        """!Stop editing of selected vector map layer.
+
+        @return True on success
+        @return False on failure
+        """
+        if self.combo:
+            self.combo.SetValue (_('Select vector map'))
+        
+        # save changes
+        if self.mapLayer:
+            Debug.msg (4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName())
+            if UserSettings.Get(group = 'vdigit', key = 'saveOnExit', subkey = 'enabled') is False:
+                if self.digit.GetUndoLevel() > -1:
+                    dlg = wx.MessageDialog(parent = self.parent,
+                                           message = _("Do you want to save changes "
+                                                     "in vector map <%s>?") % self.mapLayer.GetName(),
+                                           caption = _("Save changes?"),
+                                           style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+                    if dlg.ShowModal() == wx.ID_NO:
+                        # revert changes
+                        self.digit.Undo(0)
+                    dlg.Destroy()
+            
+            self.parent.SetStatusText(_("Please wait, "
+                                        "closing and rebuilding topology of "
+                                        "vector map <%s>...") % self.mapLayer.GetName(),
+                                      0)
+            self.digit.CloseMap()
+            
+            lmgr = self.parent.GetLayerManager()
+            if lmgr:
+                lmgr.toolbars['tools'].Enable('vdigit', enable = True)
+                lmgr.GetLogWindow().GetProgressBar().SetValue(0)
+                lmgr.GetLogWindow().WriteCmdLog(_("Editing of vector map <%s> successfully finished") % \
+                                                    self.mapLayer.GetName(),
+                                                switchPage = False)
+            # re-active layer 
+            item = self.parent.tree.FindItemByData('maplayer', self.mapLayer)
+            if item and self.parent.tree.IsItemChecked(item):
+                self.Map.ChangeLayerActive(self.mapLayer, True)
+        
+        # change cursor
+        self.MapWindow.SetCursor(self.parent.cursors["default"])
+        self.MapWindow.pdcVector = None
+        
+        # close dialogs
+        for dialog in ('attributes', 'category'):
+            if self.parent.dialogs[dialog]:
+                self.parent.dialogs[dialog].Close()
+                self.parent.dialogs[dialog] = None
+        
+        del self.digit
+        del self.MapWindow.digit
+        
+        self.mapLayer = None
+        
+        self.MapWindow.redrawAll = True
+        
+        return True
+    
+    def UpdateListOfLayers (self, updateTool = False):
+        """!Update list of available vector map layers.
+        This list consists only editable layers (in the current mapset)
+
+        @param updateTool True to update also toolbar
+        """
+        Debug.msg (4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % \
+                   updateTool)
+        
+        layerNameSelected = None
+         # name of currently selected layer
+        if self.mapLayer:
+            layerNameSelected = self.mapLayer.GetName()
+        
+        # select vector map layer in the current mapset
+        layerNameList = []
+        self.layers = self.Map.GetListOfLayers(l_type = "vector",
+                                                      l_mapset = grass.gisenv()['MAPSET'])
+        
+        for layer in self.layers:
+            if not layer.name in layerNameList: # do not duplicate layer
+                layerNameList.append (layer.GetName())
+        
+        if updateTool: # update toolbar
+            if not self.mapLayer:
+                value = _('Select vector map')
+            else:
+                value = layerNameSelected
+            
+            if not self.comboid:
+                if not self.tools or 'selector' in self.tools:
+                    self.combo = wx.ComboBox(self, id = wx.ID_ANY, value = value,
+                                             choices = [_('New vector map'), ] + layerNameList, size = (80, -1),
+                                             style = wx.CB_READONLY)
+                    self.comboid = self.InsertControl(0, self.combo)
+                    self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid)
+            else:
+                self.combo.SetItems([_('New vector map'), ] + layerNameList)
+            
+            self.Realize()
+        
+        return layerNameList
+
+    def GetLayer(self):
+        """!Get selected layer for editing -- MapLayer instance"""
+        return self.mapLayer
+

+ 14 - 12
gui/wxpython/gui_modules/wxvdigit.py

@@ -1,5 +1,5 @@
 """!
-@package wxvdigit.py
+@package vdigit.wxdigit
 
 @brief wxGUI vector digitizer (base class)
 
@@ -25,19 +25,21 @@ This program is free software under the GNU General Public License
 @author Martin Landa <landa.martin gmail.com>
 """
 
-from gcmd        import GError
-from debug       import Debug
-from preferences import globalSettings as UserSettings
-
-from wxvdriver   import DisplayDriver, GetLastError
-
-from grass.lib.gis    import *
-from grass.lib.vector import *
-from grass.lib.vedit  import *
-from grass.lib.dbmi   import *
-
 import grass.script.core as grass
 
+from core.cmd         import GError
+from core.debug       import Debug
+from core.settings    import UserSettings
+from vdigit.wxdisplay import DisplayDriver, GetLastError
+
+try:
+    from grass.lib.gis    import *
+    from grass.lib.vector import *
+    from grass.lib.vedit  import *
+    from grass.lib.dbmi   import *
+except ImportError:
+    pass
+
 class VDigitError:
     def __init__(self, parent):
         """!Class for managing error messages of vector digitizer

+ 9 - 8
gui/wxpython/gui_modules/wxvdriver.py

@@ -1,5 +1,5 @@
 """!
-@package wxvdriver.py
+@package vdigit.wxdisplay
 
 @brief wxGUI vector digitizer (display driver)
 
@@ -10,24 +10,25 @@ List of classes:
  - DisplayDriver
 
 (C) 2007-2011 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.
 
 @author Martin Landa <landa.martin gmail.com>
 """
 
-import math
 import locale
 
 import wx
 
-from debug import Debug
-from preferences import globalSettings as UserSettings
+from core.debug    import Debug
+from core.settings import UserSettings
 
-from grass.lib.gis    import *
-from grass.lib.vector import *
-from grass.lib.vedit  import *
+try:
+    from grass.lib.gis    import *
+    from grass.lib.vector import *
+    from grass.lib.vedit  import *
+except ImportError:
+    pass
 
 log       = None
 progress  = None

+ 132 - 143
gui/wxpython/wxgui.py

@@ -1,18 +1,19 @@
 """!
-@package wxgui.py
+@package wxgui
 
-@brief Main Python app for GRASS wxPython GUI. Main menu, layer management
-toolbar, notebook control for display management and access to
-command console.
+@brief Main Python application for GRASS wxPython GUI
+
+Layer Manager - main menu, layer management toolbar, notebook control
+for display management and access to command console.
 
 Classes:
  - GMFrame
  - GMApp
+ - Usage
 
 (C) 2006-2011 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.
 
 @author Michael Barton (Arizona State University)
 @author Jachym Cepicky (Mendel University of Agriculture)
@@ -22,67 +23,56 @@ for details.
 
 import sys
 import os
-import time
-import string
 import getopt
-import platform
-import signal
 import tempfile
 try:
     import xml.etree.ElementTree as etree
 except ImportError:
     import elementtree.ElementTree as etree # Python <= 2.4
 
-from gui_modules import globalvar
+from core import globalvar
 import wx
 import wx.aui
-import wx.combo
-import wx.html
-import wx.stc
 try:
-    import wx.lib.agw.customtreectrl as CT
     import wx.lib.agw.flatnotebook   as FN
 except ImportError:
-    import wx.lib.customtreectrl as CT
     import wx.lib.flatnotebook   as FN
-
 try:
     import wx.lib.agw.advancedsplash as SC
 except ImportError:
     SC = None
 
 sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
-from grass.script import core as grass
-
-from gui_modules import utils
-from gui_modules import preferences
-from gui_modules import layertree
-from gui_modules import mapdisp
-from gui_modules import menudata
-from gui_modules import menuform
-from gui_modules import histogram
-from gui_modules import mcalc_builder as mapcalculator
-from gui_modules import gcmd
-from gui_modules import dbm
-from gui_modules import workspace
-from gui_modules import goutput
-from gui_modules import gdialogs
-from gui_modules import colorrules
-from gui_modules import ogc_services
-from gui_modules import prompt
-from gui_modules import menu
-from gui_modules import gmodeler
-from gui_modules import vclean
-from gui_modules import nviz_tools
-from gui_modules import psmap
-from gui_modules.debug    import Debug
-from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow
-from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar,\
-                                 LMMiscToolbar, LMVectorToolbar, LMNvizToolbar
-from gui_modules.gpyshell import PyShellWindow
-from icons.icon           import Icons
-
-UserSettings = preferences.globalSettings
+from grass.script          import core as grass
+
+from core                  import utils
+from core.gcmd             import RunCommand, GError, GMessage
+from core.settings         import UserSettings
+from icons.icon            import Icons
+from gui_core.preferences  import MapsetAccess, PreferencesDialog, EVT_SETTINGS_CHANGED
+from lmgr.layertree        import LayerTree
+from lmgr.menudata         import ManagerData
+from gui_core.widgets      import GNotebook
+from modules.histogram     import HistFrame
+from modules.mcalc_builder import MapCalcFrame
+from dbm.manager           import AttributeManager
+from core.workspace        import ProcessWorkspaceFile, ProcessGrcFile, WriteWorkspaceFile
+from gui_core.goutput      import GMConsole
+from gui_core.dialogs      import GdalOutputDialog, DxfImportDialog, GdalImportDialog, MapLayersDialog
+from gui_core.dialogs      import LocationDialog, MapsetDialog, CreateNewVector, GroupDialog
+from modules.ogc_services  import WMSDialog
+from modules.colorrules    import RasterColorTable, VectorColorTable
+from gui_core.menu         import Menu
+from gmodeler.model        import Model
+from gmodeler.frame        import ModelFrame
+from modules.vclean        import VectorCleaningFrame
+from nviz.tools            import NvizToolWindow
+from psmap.frame           import PsMapFrame
+from core.debug            import Debug
+from gui_core.ghelp        import MenuTreeWindow, AboutWindow, InstallExtensionWindow
+from lmgr.toolbars         import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar
+from lmgr.toolbars         import LMMiscToolbar, LMVectorToolbar, LMNvizToolbar
+from lmgr.pyshell          import PyShellWindow
 
 class GMFrame(wx.Frame):
     """!Layer Manager frame with notebook widget for controlling GRASS
@@ -229,7 +219,7 @@ class GMFrame(wx.Frame):
         
     def _createMenuBar(self):
         """!Creates menu bar"""
-        self.menubar = menu.Menu(parent = self, data = menudata.ManagerData())
+        self.menubar = Menu(parent = self, data = ManagerData())
         self.SetMenuBar(self.menubar)
         self.menucmd = self.menubar.GetCmd()
         
@@ -245,7 +235,7 @@ class GMFrame(wx.Frame):
     
     def _createNoteBook(self):
         """!Creates notebook widgets"""
-        self.notebook = menuform.GNotebook(parent = self, style = globalvar.FNPageDStyle)
+        self.notebook = GNotebook(parent = self, style = globalvar.FNPageDStyle)
         # create displays notebook widget and add it to main notebook page
         cbStyle = globalvar.FNPageStyle
         if globalvar.hasAgw:
@@ -256,7 +246,7 @@ class GMFrame(wx.Frame):
         self.notebook.AddPage(page = self.gm_cb, text = _("Map layers"), name = 'layers')
         
         # create 'command output' text area
-        self.goutput = goutput.GMConsole(self)
+        self.goutput = GMConsole(self)
         self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
         self._setCopyingOfSelectedText()
         
@@ -292,8 +282,8 @@ class GMFrame(wx.Frame):
         self._auimgr.Update()
         
         # create nviz tools tab
-        self.nviz = nviz_tools.NvizToolWindow(parent = self,
-                                              display = self.curr_page.maptree.GetMapDisplay())
+        self.nviz = NvizToolWindow(parent = self,
+                                   display = self.curr_page.maptree.GetMapDisplay())
         idx = self.notebook.GetPageIndexByName('layers')
         self.notebook.InsertPage(indx = idx + 1, page = self.nviz, text = _("3D view"), name = 'nviz')
         self.notebook.SetSelectionByName('nviz')
@@ -338,10 +328,10 @@ class GMFrame(wx.Frame):
             
             ret = dlg.ShowModal()
             if ret == wx.ID_YES:
-                gcmd.RunCommand("g.gisenv",
-                                set = "LOCATION_NAME=%s" % gWizard.location)
-                gcmd.RunCommand("g.gisenv",
-                                set = "MAPSET=PERMANENT")
+                RunCommand("g.gisenv",
+                           set = "LOCATION_NAME=%s" % gWizard.location)
+                RunCommand("g.gisenv",
+                           set = "MAPSET=PERMANENT")
             
             dlg.Destroy()
             
@@ -368,7 +358,7 @@ class GMFrame(wx.Frame):
     def OnPsMap(self, event):
         """!Launch Cartographic Composer
         """
-        win = psmap.PsMapFrame(parent = self)
+        win = PsMapFrame(parent = self)
         win.CentreOnScreen()
         
         win.Show()
@@ -379,9 +369,9 @@ class GMFrame(wx.Frame):
         try:
             from gui_modules import rstream
         except:
-            gcmd.GError(parent = self.parent,
-                        message = _("RStream Utility is not available. You can install it by %s") % \
-                            'g.extension -s extension=wx.stream')
+            GError(parent = self.parent,
+                   message = _("RStream Utility is not available. You can install it by %s") % \
+                       'g.extension -s extension=wx.stream')
             return
         
         win = rstream.RStreamFrame(parent = self)
@@ -418,14 +408,14 @@ class GMFrame(wx.Frame):
     def OnMapsets(self, event):
         """!Launch mapset access dialog
         """
-        dlg = preferences.MapsetAccess(parent = self, id = wx.ID_ANY)
+        dlg = MapsetAccess(parent = self, id = wx.ID_ANY)
         dlg.CenterOnScreen()
         
         if dlg.ShowModal() == wx.ID_OK:
             ms = dlg.GetMapsets()
-            gcmd.RunCommand('g.mapsets',
-                            parent = self,
-                            mapset = '%s' % ','.join(ms))
+            RunCommand('g.mapsets',
+                       parent = self,
+                       mapset = '%s' % ','.join(ms))
         
     def OnCBPageChanged(self, event):
         """!Page in notebook (display) changed"""
@@ -575,14 +565,14 @@ class GMFrame(wx.Frame):
             mapLayer = None
         
         if not mapLayer or mapLayer.GetType() != 'vector':
-            gcmd.GMessage(parent = self,
-                          message = _("Selected map layer is not vector."))
+            GMessage(parent = self,
+                     message = _("Selected map layer is not vector."))
             return
         
         if mapLayer.GetMapset() != grass.gisenv()['MAPSET']:
-            gcmd.GMessage(parent = self,
-                          message = _("Editing is allowed only for vector maps from the "
-                                      "current mapset."))
+            GMessage(parent = self,
+                     message = _("Editing is allowed only for vector maps from the "
+                                 "current mapset."))
             return
         
         if not tree.GetPyData(layer)[0]:
@@ -608,9 +598,9 @@ class GMFrame(wx.Frame):
             return False
 
         if not os.path.exists(filename):
-            gcmd.GError(parent = self,
-                        message = _("Script file '%s' doesn't exist. "
-                                    "Operation cancelled.") % filename)
+            GError(parent = self,
+                   message = _("Script file '%s' doesn't exist. "
+                               "Operation cancelled.") % filename)
             return
         
         self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
@@ -618,14 +608,14 @@ class GMFrame(wx.Frame):
         
     def OnChangeLocation(self, event):
         """Change current location"""
-        dlg = gdialogs.LocationDialog(parent = self)
+        dlg = LocationDialog(parent = self)
         if dlg.ShowModal() == wx.ID_OK:
             location, mapset = dlg.GetValues()
             if location and mapset:
-                ret = gcmd.RunCommand("g.gisenv",
-                                      set = "LOCATION_NAME=%s" % location)
-                ret += gcmd.RunCommand("g.gisenv",
-                                       set = "MAPSET=%s" % mapset)
+                ret = RunCommand("g.gisenv",
+                                 set = "LOCATION_NAME=%s" % location)
+                ret += RunCommand("g.gisenv",
+                                  set = "MAPSET=%s" % mapset)
                 if ret > 0:
                     wx.MessageBox(parent = self,
                                   message = _("Unable to switch to location <%(loc)s> mapset <%(mapset)s>.") % \
@@ -650,43 +640,43 @@ class GMFrame(wx.Frame):
         if dlg.ShowModal() ==  wx.ID_OK:
             mapset = dlg.GetValue()
             if not mapset:
-                gcmd.GError(parent = self,
-                            message = _("No mapset provided. Operation canceled."))
+                GError(parent = self,
+                       message = _("No mapset provided. Operation canceled."))
                 return
             
-            ret = gcmd.RunCommand('g.mapset',
-                                  parent = self,
-                                  flags = 'c',
-                                  mapset = mapset)
+            ret = RunCommand('g.mapset',
+                             parent = self,
+                             flags = 'c',
+                             mapset = mapset)
             if ret == 0:
-                gcmd.GMessage(parent = self,
-                              message = _("Current mapset is <%s>.") % mapset)
-            
+                GMessage(parent = self,
+                         message = _("Current mapset is <%s>.") % mapset)
+                
     def OnChangeMapset(self, event):
         """Change current mapset"""
-        dlg = gdialogs.MapsetDialog(parent = self)
+        dlg = MapsetDialog(parent = self)
         
         if dlg.ShowModal() == wx.ID_OK:
             mapset = dlg.GetMapset()
             if not mapset:
-                gcmd.GError(parent = self,
-                            message = _("No mapset provided. Operation canceled."))
+                GError(parent = self,
+                       message = _("No mapset provided. Operation canceled."))
                 return
             
-            ret = gcmd.RunCommand('g.mapset',
-                                  parent = self,
-                                  mapset = mapset)
+            ret = RunCommand('g.mapset',
+                             parent = self,
+                             mapset = mapset)
             
             if ret == 0:
-                gcmd.GMessage(parent = self,
-                              message = _("Current mapset is <%s>.") % mapset)
+                GMessage(parent = self,
+                         message = _("Current mapset is <%s>.") % mapset)
         
     def OnNewVector(self, event):
         """!Create new vector map layer"""
-        dlg = gdialogs.CreateNewVector(self, log = self.goutput,
-                                       cmd = (('v.edit',
-                                               { 'tool' : 'create' },
-                                               'map')))
+        dlg = CreateNewVector(self, log = self.goutput,
+                              cmd = (('v.edit',
+                                      { 'tool' : 'create' },
+                                      'map')))
         
         if not dlg:
             return
@@ -814,11 +804,11 @@ class GMFrame(wx.Frame):
         
         # parse workspace file
         try:
-            gxwXml = workspace.ProcessWorkspaceFile(etree.parse(filename))
+            gxwXml = ProcessWorkspaceFile(etree.parse(filename))
         except Exception, e:
-            gcmd.GError(parent = self,
-                        message = _("Reading workspace file <%s> failed.\n"
-                                    "Invalid file, unable to parse XML document.") % filename)
+            GError(parent = self,
+                   message = _("Reading workspace file <%s> failed.\n"
+                               "Invalid file, unable to parse XML document.") % filename)
             return
         
         busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
@@ -958,7 +948,7 @@ class GMFrame(wx.Frame):
         wx.Yield()
 
         maptree = None
-        for layer in workspace.ProcessGrcFile(filename).read(self):
+        for layer in ProcessGrcFile(filename).read(self):
             maptree = self.gm_cb.GetPage(layer['display']).maptree
             newItem = maptree.AddLayer(ltype = layer['type'],
                                        lname = layer['name'],
@@ -1027,11 +1017,11 @@ class GMFrame(wx.Frame):
         """
         tmpfile = tempfile.TemporaryFile(mode = 'w+b')
         try:
-            workspace.WriteWorkspaceFile(lmgr = self, file = tmpfile)
+            WriteWorkspaceFile(lmgr = self, file = tmpfile)
         except StandardError, e:
-            gcmd.GError(parent = self,
-                        message = _("Writing current settings to workspace file "
-                                    "failed."))
+            GError(parent = self,
+                   message = _("Writing current settings to workspace file "
+                               "failed."))
             return False
         
         try:
@@ -1040,8 +1030,8 @@ class GMFrame(wx.Frame):
             for line in tmpfile.readlines():
                 mfile.write(line)
         except IOError:
-            gcmd.GError(parent = self,
-                        message = _("Unable to open file <%s> for writing.") % filename)
+            GError(parent = self,
+                   message = _("Unable to open file <%s> for writing.") % filename)
             return False
         
         mfile.close()
@@ -1094,7 +1084,7 @@ class GMFrame(wx.Frame):
     def OnEditImageryGroups(self, event, cmd = None):
         """!Show dialog for creating and editing groups.
         """
-        dlg = gdialogs.GroupDialog(self)
+        dlg = GroupDialog(self)
         dlg.CentreOnScreen()
         dlg.Show()
         
@@ -1108,11 +1098,11 @@ class GMFrame(wx.Frame):
         """!General GUI preferences/settings
         """
         if not self.dialogs['preferences']:
-            dlg = preferences.PreferencesDialog(parent = self)
+            dlg = PreferencesDialog(parent = self)
             self.dialogs['preferences'] = dlg
             self.dialogs['preferences'].CenterOnScreen()
             
-            dlg.Bind(preferences.EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
+            dlg.Bind(EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
         
         self.dialogs['preferences'].ShowModal()
         
@@ -1125,9 +1115,8 @@ class GMFrame(wx.Frame):
         """
         Init histogram display canvas and tools
         """
-        self.histogram = histogram.HistFrame(self,
-                                             id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
-                                             style = wx.DEFAULT_FRAME_STYLE)
+        self.histogram = HistFrame(self, id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
+                                   style = wx.DEFAULT_FRAME_STYLE)
 
         #show new display
         self.histogram.Show()
@@ -1154,8 +1143,8 @@ class GMFrame(wx.Frame):
             except KeyError:
                 cmd = ['r.mapcalc']
         
-        win = mapcalculator.MapCalcFrame(parent = self,
-                                         cmd = cmd[0])
+        win = MapCalcFrame(parent = self,
+                           cmd = cmd[0])
         win.CentreOnScreen()
         win.Show()
     
@@ -1166,7 +1155,7 @@ class GMFrame(wx.Frame):
         if event:
             cmd = self.GetMenuCmd(event)
 
-        win = vclean.VectorCleaningFrame(parent = self, cmd = cmd[0])
+        win = VectorCleaningFrame(parent = self, cmd = cmd[0])
         win.CentreOnScreen()
         win.Show()
 
@@ -1176,43 +1165,43 @@ class GMFrame(wx.Frame):
 
     def OnVectorOutputFormat(self, event):
         """!Set vector output format handler"""
-        dlg = gdialogs.GdalOutputDialog(parent = self, ogr = True)
+        dlg = GdalOutputDialog(parent = self, ogr = True)
         dlg.CentreOnScreen()
         dlg.Show()
     
     def OnImportDxfFile(self, event, cmd = None):
         """!Convert multiple DXF layers to GRASS vector map layers"""
-        dlg = gdialogs.DxfImportDialog(parent = self)
+        dlg = DxfImportDialog(parent = self)
         dlg.CentreOnScreen()
         dlg.Show()
 
     def OnImportGdalLayers(self, event, cmd = None):
         """!Convert multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self)
+        dlg = GdalImportDialog(parent = self)
         dlg.CentreOnScreen()
         dlg.Show()
 
     def OnLinkGdalLayers(self, event, cmd = None):
         """!Link multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, link = True)
+        dlg = GdalImportDialog(parent = self, link = True)
         dlg.CentreOnScreen()
         dlg.Show()
         
     def OnImportOgrLayers(self, event, cmd = None):
         """!Convert multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, ogr = True)
+        dlg = GdalImportDialog(parent = self, ogr = True)
         dlg.CentreOnScreen()
         dlg.Show()
         
     def OnLinkOgrLayers(self, event, cmd = None):
         """!Links multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, ogr = True, link = True)
+        dlg = GdalImportDialog(parent = self, ogr = True, link = True)
         dlg.CentreOnScreen()
         dlg.Show()
         
     def OnImportWMS(self, event):
         """!Import data from OGC WMS server"""
-        dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
+        dlg = WMSDialog(parent = self, service = 'wms')
         dlg.CenterOnScreen()
         
         if dlg.ShowModal() == wx.ID_OK: # -> import layers
@@ -1262,8 +1251,8 @@ class GMFrame(wx.Frame):
             maptype = None
         
         if not maptype or maptype != 'vector':
-            gcmd.GMessage(parent = self,
-                          message = _("Selected map layer is not vector."))
+            GMessage(parent = self,
+                     message = _("Selected map layer is not vector."))
             return
         
         if not tree.GetPyData(layer)[0]:
@@ -1276,10 +1265,10 @@ class GMFrame(wx.Frame):
                            parent = self)
         wx.Yield()
         
-        dbmanager = dbm.AttributeManager(parent = self, id = wx.ID_ANY,
-                                         size = wx.Size(500, 300),
-                                         item = layer, log = self.goutput,
-                                         selection = selection)
+        dbmanager = AttributeManager(parent = self, id = wx.ID_ANY,
+                                     size = wx.Size(500, 300),
+                                     item = layer, log = self.goutput,
+                                     selection = selection)
         
         busy.Destroy()
         
@@ -1309,12 +1298,12 @@ class GMFrame(wx.Frame):
         self.curr_page = self.gm_cb.GetCurrentPage()
         
         # create layer tree (tree control for managing GIS layers)  and put on new notebook page
-        self.curr_page.maptree = layertree.LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
-                                                     size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
-                                                     wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
-                                                     wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
-                                                     idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
-                                                     auimgr = self._auimgr, showMapDisplay = show)
+        self.curr_page.maptree = LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                                           size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
+                                           wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
+                                           wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
+                                           idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
+                                           auimgr = self._auimgr, showMapDisplay = show)
         
         # layout for controls
         cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
@@ -1342,7 +1331,7 @@ class GMFrame(wx.Frame):
     
     def OnAddMaps(self, event = None):
         """!Add selected map layers into layer tree"""
-        dialog = gdialogs.MapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
+        dialog = MapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
         
         if dialog.ShowModal() != wx.ID_OK:
             dialog.Destroy()
@@ -1366,8 +1355,8 @@ class GMFrame(wx.Frame):
                 cmd = ['d.vect', 'map=%s' % layerName]
                 wxType = 'vector'
             else:
-                gcmd.GError(parent = self,
-                            message = _("Unsupported map layer type <%s>.") % ltype)
+                GError(parent = self,
+                       message = _("Unsupported map layer type <%s>.") % ltype)
                 return
             
             newItem = maptree.AddLayer(ltype = wxType,
@@ -1739,7 +1728,7 @@ def printHelp():
     print >> sys.stderr, " python wxgui.py [options]"
     print >> sys.stderr, "%sOptions:" % os.linesep
     print >> sys.stderr, " -w\t--workspace file\tWorkspace file to load"
-    sys.exit(0)
+    sys.exit(1)
 
 def process_opt(opts, args):
     """!Process command-line arguments"""

+ 529 - 0
gui/wxpython/wxplot/base.py

@@ -0,0 +1,529 @@
+"""!
+@package wxplot.base
+
+@brief Base classes for iinteractive plotting using PyPlot
+
+Classes:
+ - BasePlotFrame
+
+(C) 2011 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.
+
+@author Michael Barton, Arizona State University
+"""
+
+import os
+import sys
+
+import wx
+import wx.lib.plot as plot
+
+from core.globalvar import ETCICONDIR
+from core.settings  import UserSettings
+from wxplot.dialogs import TextDialog, OptDialog
+
+import grass.script as grass
+
+class BasePlotFrame(wx.Frame):
+    """!Abstract PyPlot display frame class"""
+    def __init__(self, parent = None, id = wx.ID_ANY, size = (700, 300),
+                 style = wx.DEFAULT_FRAME_STYLE, rasterList = [],  **kwargs):
+
+        wx.Frame.__init__(self, parent, id, size = size, style = style, **kwargs)
+        
+        self.parent = parent            # MapFrame
+        self.mapwin = self.parent.MapWindow
+        self.Map    = Map()             # instance of render.Map to be associated with display
+        self.rasterList = rasterList    #list of rasters to plot
+        self.raster = {}    # dictionary of raster maps and their plotting parameters
+        self.plottype = ''
+        
+        self.linestyledict = { 'solid' : wx.SOLID,
+                            'dot' : wx.DOT,
+                            'long-dash' : wx.LONG_DASH,
+                            'short-dash' : wx.SHORT_DASH,
+                            'dot-dash' : wx.DOT_DASH }
+
+        self.ptfilldict = { 'transparent' : wx.TRANSPARENT,
+                            'solid' : wx.SOLID }
+
+        #
+        # Icon
+        #
+        self.SetIcon(wx.Icon(os.path.join(ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+                
+        #
+        # Add statusbar
+        #
+        self.statusbar = self.CreateStatusBar(number = 2, style = 0)
+        self.statusbar.SetStatusWidths([-2, -1])
+
+        #
+        # Define canvas and settings
+        #
+        # 
+        self.client = plot.PlotCanvas(self)
+
+        #define the function for drawing pointLabels
+        self.client.SetPointLabelFunc(self.DrawPointLabel)
+
+        # Create mouse event for showing cursor coords in status bar
+        self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
+
+        # Show closest point when enabled
+        self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
+
+        self.plotlist = []      # list of things to plot
+        self.plot = None        # plot draw object
+        self.ptitle = ""        # title of window
+        self.xlabel = ""        # default X-axis label
+        self.ylabel = ""        # default Y-axis label
+
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        
+        self.CentreOnScreen()
+        
+        self._createColorDict()
+
+
+    def _createColorDict(self):
+        """!Create color dictionary to return wx.Color tuples
+        for assigning colors to images in imagery groups"""
+                
+        self.colorDict = {}
+        for clr in grass.named_colors.iterkeys():
+            if clr == 'white' or clr == 'black': continue
+            r = grass.named_colors[clr][0] * 255
+            g = grass.named_colors[clr][1] * 255
+            b = grass.named_colors[clr][2] * 255
+            self.colorDict[clr] = (r,g,b,255)
+
+    def InitPlotOpts(self, plottype):
+        """!Initialize options for entire plot
+        """
+        
+        self.plottype = plottype                # histogram, profile, or scatter
+
+        self.properties = {}                    # plot properties
+        self.properties['font'] = {}
+        self.properties['font']['prop'] = UserSettings.Get(group = self.plottype, key = 'font')
+        self.properties['font']['wxfont'] = wx.Font(11, wx.FONTFAMILY_SWISS,
+                                                    wx.FONTSTYLE_NORMAL,
+                                                    wx.FONTWEIGHT_NORMAL)
+        
+        if self.plottype == 'profile':
+            self.properties['marker'] = UserSettings.Get(group = self.plottype, key = 'marker')
+            # changing color string to tuple for markers/points
+            colstr = str(self.properties['marker']['color'])
+            self.properties['marker']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
+
+        self.properties['grid'] = UserSettings.Get(group = self.plottype, key = 'grid')        
+        colstr = str(self.properties['grid']['color']) # changing color string to tuple        
+        self.properties['grid']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
+                
+        self.properties['x-axis'] = {}
+        self.properties['x-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'x-axis')
+        self.properties['x-axis']['axis'] = None
+
+        self.properties['y-axis'] = {}
+        self.properties['y-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'y-axis')
+        self.properties['y-axis']['axis'] = None
+        
+        self.properties['legend'] = UserSettings.Get(group = self.plottype, key = 'legend')
+
+        self.zoom = False  # zooming disabled
+        self.drag = False  # draging disabled
+        self.client.SetShowScrollbars(True) # vertical and horizontal scrollbars
+
+        # x and y axis set to normal (non-log)
+        self.client.setLogScale((False, False))
+        if self.properties['x-axis']['prop']['type']:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+        else:
+            self.client.SetXSpec('auto')
+        
+        if self.properties['y-axis']['prop']['type']:
+            self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
+        else:
+            self.client.SetYSpec('auto')
+        
+    def InitRasterOpts(self, rasterList, plottype):
+        """!Initialize or update raster dictionary for plotting
+        """
+
+        rdict = {} # initialize a dictionary
+
+        for r in rasterList:
+            idx = rasterList.index(r)
+            
+            try:
+                ret = grass.raster_info(r)
+            except:
+                continue
+                # if r.info cannot parse map, skip it
+                
+            self.raster[r] = UserSettings.Get(group = plottype, key = 'raster') # some default settings
+            rdict[r] = {} # initialize sub-dictionaries for each raster in the list
+
+            if ret['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
+                rdict[r]['units'] = ''
+            else:
+                self.raster[r]['units'] = ret['units']
+
+            rdict[r]['plegend'] = r.split('@')[0]
+            rdict[r]['datalist'] = [] # list of cell value,frequency pairs for plotting histogram
+            rdict[r]['pline'] = None
+            rdict[r]['datatype'] = ret['datatype']
+            rdict[r]['pwidth'] = 1
+            rdict[r]['pstyle'] = 'solid'
+            
+            if idx <= len(self.colorList):
+                rdict[r]['pcolor'] = self.colorDict[self.colorList[idx]]
+            else:
+                r = randint(0, 255)
+                b = randint(0, 255)
+                g = randint(0, 255)
+                rdict[r]['pcolor'] = ((r,g,b,255))
+ 
+            
+        return rdict
+            
+    def InitRasterPairs(self, rasterList, plottype):
+        """!Initialize or update raster dictionary with raster pairs for
+            bivariate scatterplots
+        """
+        
+        if len(rasterList) == 0: return
+
+        rdict = {} # initialize a dictionary
+        for rpair in rasterList:
+            idx = rasterList.index(rpair)
+            
+            try:
+                ret0 = grass.raster_info(rpair[0])
+                ret1 = grass.raster_info(rpair[1])
+
+            except:
+                continue
+                # if r.info cannot parse map, skip it
+
+            self.raster[rpair] = UserSettings.Get(group = plottype, key = 'rasters') # some default settings
+            rdict[rpair] = {} # initialize sub-dictionaries for each raster in the list
+            rdict[rpair][0] = {}
+            rdict[rpair][1] = {}
+
+            if ret0['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
+                rdict[rpair][0]['units'] = ''
+            else:
+                self.raster[rpair][0]['units'] = ret0['units']
+
+            if ret1['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
+                rdict[rpair][1]['units'] = ''
+            else:
+                self.raster[rpair][1]['units'] = ret1['units']
+
+            rdict[rpair]['plegend'] = rpair[0].split('@')[0] + ' vs ' + rpair[1].split('@')[0]
+            rdict[rpair]['datalist'] = [] # list of cell value,frequency pairs for plotting histogram
+            rdict[rpair]['ptype'] = 'dot'
+            rdict[rpair][0]['datatype'] = ret0['datatype']
+            rdict[rpair][1]['datatype'] = ret1['datatype']
+            rdict[rpair]['psize'] = 1
+            rdict[rpair]['pfill'] = 'solid'
+            
+            if idx <= len(self.colorList):
+                rdict[rpair]['pcolor'] = self.colorDict[self.colorList[idx]]
+            else:
+                r = randint(0, 255)
+                b = randint(0, 255)
+                g = randint(0, 255)
+                rdict[rpair]['pcolor'] = ((r,g,b,255))
+            
+        return rdict
+
+    def SetGraphStyle(self):
+        """!Set plot and text options
+        """
+        self.client.SetFont(self.properties['font']['wxfont'])
+        self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
+        self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
+
+        self.client.SetEnableZoom(self.zoom)
+        self.client.SetEnableDrag(self.drag)
+        
+        #
+        # axis settings
+        #
+        if self.properties['x-axis']['prop']['type'] == 'custom':
+            self.client.SetXSpec('min')
+        else:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+
+        if self.properties['y-axis']['prop']['type'] == 'custom':
+            self.client.SetYSpec('min')
+        else:
+            self.client.SetYSpec(self.properties['y-axis']['prop'])
+
+        if self.properties['x-axis']['prop']['type'] == 'custom' and \
+               self.properties['x-axis']['prop']['min'] < self.properties['x-axis']['prop']['max']:
+            self.properties['x-axis']['axis'] = (self.properties['x-axis']['prop']['min'],
+                                                 self.properties['x-axis']['prop']['max'])
+        else:
+            self.properties['x-axis']['axis'] = None
+
+        if self.properties['y-axis']['prop']['type'] == 'custom' and \
+                self.properties['y-axis']['prop']['min'] < self.properties['y-axis']['prop']['max']:
+            self.properties['y-axis']['axis'] = (self.properties['y-axis']['prop']['min'],
+                                                 self.properties['y-axis']['prop']['max'])
+        else:
+            self.properties['y-axis']['axis'] = None
+
+        self.client.SetEnableGrid(self.properties['grid']['enabled'])
+        
+        self.client.SetGridColour(wx.Color(self.properties['grid']['color'][0],
+                                           self.properties['grid']['color'][1],
+                                           self.properties['grid']['color'][2],
+                                           255))
+
+        self.client.SetFontSizeLegend(self.properties['font']['prop']['legendSize'])
+        self.client.SetEnableLegend(self.properties['legend']['enabled'])
+
+        if self.properties['x-axis']['prop']['log'] == True:
+            self.properties['x-axis']['axis'] = None
+            self.client.SetXSpec('min')
+        if self.properties['y-axis']['prop']['log'] == True:
+            self.properties['y-axis']['axis'] = None
+            self.client.SetYSpec('min')
+            
+        self.client.setLogScale((self.properties['x-axis']['prop']['log'],
+                                 self.properties['y-axis']['prop']['log']))
+
+    def DrawPlot(self, plotlist):
+        """!Draw line and point plot from list plot elements.
+        """
+        self.plot = plot.PlotGraphics(plotlist,
+                                         self.ptitle,
+                                         self.xlabel,
+                                         self.ylabel)
+
+        if self.properties['x-axis']['prop']['type'] == 'custom':
+            self.client.SetXSpec('min')
+        else:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+
+        if self.properties['y-axis']['prop']['type'] == 'custom':
+            self.client.SetYSpec('min')
+        else:
+            self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
+
+        self.client.Draw(self.plot, self.properties['x-axis']['axis'],
+                         self.properties['y-axis']['axis'])
+                
+    def DrawPointLabel(self, dc, mDataDict):
+        """!This is the fuction that defines how the pointLabels are
+            plotted dc - DC that will be passed mDataDict - Dictionary
+            of data that you want to use for the pointLabel
+
+            As an example I have decided I want a box at the curve
+            point with some text information about the curve plotted
+            below.  Any wxDC method can be used.
+        """
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
+
+        sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
+        dc.DrawRectangle( sx-5,sy-5, 10, 10)  #10by10 square centered on point
+        px,py = mDataDict["pointXY"]
+        cNum = mDataDict["curveNum"]
+        pntIn = mDataDict["pIndex"]
+        legend = mDataDict["legend"]
+        #make a string to display
+        s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
+        dc.DrawText(s, sx , sy+1)
+
+    def OnZoom(self, event):
+        """!Enable zooming and disable dragging
+        """
+        self.zoom = True
+        self.drag = False
+        self.client.SetEnableZoom(self.zoom)
+        self.client.SetEnableDrag(self.drag)
+
+    def OnDrag(self, event):
+        """!Enable dragging and disable zooming
+        """
+        self.zoom = False
+        self.drag = True
+        self.client.SetEnableDrag(self.drag)
+        self.client.SetEnableZoom(self.zoom)
+
+    def OnRedraw(self, event):
+        """!Redraw the plot window. Unzoom to original size
+        """
+        self.client.Reset()
+        self.client.Redraw()
+       
+    def OnErase(self, event):
+        """!Erase the plot window
+        """
+        self.client.Clear()
+        self.mapwin.ClearLines(self.mapwin.pdc)
+        self.mapwin.ClearLines(self.mapwin.pdcTmp)
+        self.mapwin.polycoords = []
+        self.mapwin.Refresh()
+
+    def SaveToFile(self, event):
+        """!Save plot to graphics file
+        """
+        self.client.SaveFile()
+
+    def OnMouseLeftDown(self,event):
+        self.SetStatusText(_("Left Mouse Down at Point: (%.4f, %.4f)") % \
+                               self.client._getXY(event))
+        event.Skip() # allows plotCanvas OnMouseLeftDown to be called
+
+    def OnMotion(self, event):
+        """!Indicate when mouse is outside the plot area
+        """
+        if self.client.OnLeave(event): print 'out of area'
+        #show closest point (when enbled)
+        if self.client.GetEnablePointLabel() == True:
+            #make up dict with info for the pointLabel
+            #I've decided to mark the closest point on the closest curve
+            dlst =  self.client.GetClosetPoint( self.client._getXY(event), pointScaled =  True)
+            if dlst != []:      #returns [] if none
+                curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
+                #make up dictionary to pass to my user function (see DrawPointLabel)
+                mDataDict =  {"curveNum":curveNum, "legend":legend, "pIndex":pIndex,\
+                    "pointXY":pointXY, "scaledXY":scaledXY}
+                #pass dict to update the pointLabel
+                self.client.UpdatePointLabel(mDataDict)
+        event.Skip()           #go to next handler        
+ 
+ 
+    def PlotOptionsMenu(self, event):
+        """!Popup menu for plot and text options
+        """
+        point = wx.GetMousePosition()
+        popt = wx.Menu()
+        # Add items to the menu
+        settext = wx.MenuItem(popt, wx.ID_ANY, _('Text settings'))
+        popt.AppendItem(settext)
+        self.Bind(wx.EVT_MENU, self.PlotText, settext)
+
+        setgrid = wx.MenuItem(popt, wx.ID_ANY, _('Plot settings'))
+        popt.AppendItem(setgrid)
+        self.Bind(wx.EVT_MENU, self.PlotOptions, setgrid)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(popt)
+        popt.Destroy()
+
+    def NotFunctional(self):
+        """!Creates a 'not functional' message dialog
+        """
+        dlg = wx.MessageDialog(parent = self,
+                               message = _('This feature is not yet functional'),
+                               caption = _('Under Construction'),
+                               style = wx.OK | wx.ICON_INFORMATION)
+        dlg.ShowModal()
+        dlg.Destroy()
+
+    def OnPlotText(self, dlg):
+        """!Custom text settings for histogram plot.
+        """
+        self.ptitle = dlg.ptitle
+        self.xlabel = dlg.xlabel
+        self.ylabel = dlg.ylabel
+        dlg.UpdateSettings()
+
+        self.client.SetFont(self.properties['font']['wxfont'])
+        self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
+        self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
+
+        if self.plot:
+            self.plot.setTitle(dlg.ptitle)
+            self.plot.setXLabel(dlg.xlabel)
+            self.plot.setYLabel(dlg.ylabel)
+        
+        self.OnRedraw(event = None)
+    
+    def PlotText(self, event):
+        """!Set custom text values for profile title and axis labels.
+        """
+        dlg = TextDialog(parent = self, id = wx.ID_ANY, 
+                                 plottype = self.plottype, 
+                                 title = _('Histogram text settings'))
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self.OnPlotText(dlg)
+
+        dlg.Destroy()
+
+    def PlotOptions(self, event):
+        """!Set various profile options, including: line width, color,
+        style; marker size, color, fill, and style; grid and legend
+        options.  Calls OptDialog class.
+        """
+        dlg = OptDialog(parent = self, id = wx.ID_ANY, 
+                        plottype = self.plottype, 
+                        title = _('Plot settings'))
+        btnval = dlg.ShowModal()
+
+        if btnval == wx.ID_SAVE:
+            dlg.UpdateSettings()            
+            self.SetGraphStyle()            
+            dlg.Destroy()            
+        elif btnval == wx.ID_CANCEL:
+            dlg.Destroy()
+
+    def PrintMenu(self, event):
+        """!Print options and output menu
+        """
+        point = wx.GetMousePosition()
+        printmenu = wx.Menu()
+        for title, handler in ((_("Page setup"), self.OnPageSetup),
+                               (_("Print preview"), self.OnPrintPreview),
+                               (_("Print display"), self.OnDoPrint)):
+            item = wx.MenuItem(printmenu, wx.ID_ANY, title)
+            printmenu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, handler, item)
+        
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(printmenu)
+        printmenu.Destroy()
+
+    def OnPageSetup(self, event):
+        self.client.PageSetup()
+
+    def OnPrintPreview(self, event):
+        self.client.PrintPreview()
+
+    def OnDoPrint(self, event):
+        self.client.Printout()
+
+    def OnQuit(self, event):
+        self.Close(True)
+
+    def OnCloseWindow(self, event):
+        """!Close plot window and clean up
+        """
+        try:
+            self.mapwin.ClearLines()
+            self.mapwin.mouse['begin'] = self.mapwin.mouse['end'] = (0.0, 0.0)
+            self.mapwin.mouse['use'] = 'pointer'
+            self.mapwin.mouse['box'] = 'point'
+            self.mapwin.polycoords = []
+            self.mapwin.UpdateMap(render = False, renderVector = False)
+        except:
+            pass
+        
+        self.mapwin.SetCursor(self.Parent.cursors["default"])
+        self.Destroy()
+        

+ 8 - 13
gui/wxpython/gui_modules/wxplot_dialogs.py

@@ -1,11 +1,12 @@
 """!
-@package wxplot_dialogs.py
+@package wxplot.dialogs
 
-Iinteractive plotting using PyPlot (wx.lib.plot.py). Dialogs for
-different plotting routines.
+@brief Dialogs for different plotting routines
 
 Classes:
  - ProfileRasterDialog
+ - ScatterRasterDialog
+ - PlotStatsFrame
  - HistRasterDialog
  - TextDialog
  - OptDialog
@@ -18,20 +19,14 @@ This program is free software under the GNU General Public License
 @author Michael Barton, Arizona State University
 """
 
-import os
-import sys
-from types import *
-
 import wx
-import wx.lib.colourselect as  csel
+import wx.lib.colourselect  as csel
 import wx.lib.scrolledpanel as scrolled
 
-import globalvar
-import gcmd
-from gselect     import Select
-from preferences import globalSettings as UserSettings
+from core          import globalvar
+from core.settings import UserSettings
 
-from grass.script import core   as grass
+from grass.script import core  as grass
 
 class ProfileRasterDialog(wx.Dialog):
     def __init__(self, parent, id = wx.ID_ANY, 

+ 260 - 0
gui/wxpython/wxplot/histogram.py

@@ -0,0 +1,260 @@
+"""!
+@package wxplot.histogram
+
+@brief Histogramming using PyPlot
+
+Classes:
+ - Histogram2Frame
+ - Histogram2Toolbar
+
+(C) 2011 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.
+
+@author Michael Barton, Arizona State University
+"""
+
+import sys
+
+import wx
+import wx.lib.plot as plot
+
+import grass.script as grass
+
+from gui_core.toolbars import BaseToolbar
+from wxplot.base       import BasePlotFrame
+
+class Histogram2Frame(BasePlotFrame):
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
+        """!Mainframe for displaying histogram of raster map. Uses wx.lib.plot.
+        """
+        BasePlotFrame.__init__(self, parent)
+        
+        self.toolbar = Histogram2Toolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Histogramming Tool"))
+
+        #
+        # Init variables
+        #
+        self.rasterList = rasterList
+        self.plottype = 'histogram'
+        self.group = '' 
+        self.ptitle = _('Histogram of')         # title of window
+        self.xlabel = _("Raster cell values")   # default X-axis label
+        self.ylabel = _("Cell counts")          # default Y-axis label
+        self.maptype = 'raster'                 # default type of histogram to plot
+        self.histtype = 'count' 
+        self.bins = 255
+        self.colorList = ["blue", "green", "red", "yellow", "magenta", "cyan", \
+                    "aqua", "black", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+        
+        if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
+            self.InitRasterOpts(self.rasterList, self.plottype)
+
+        self._initOpts()
+
+    def _initOpts(self):
+        """!Initialize plot options
+        """
+        self.InitPlotOpts('histogram')            
+
+    def OnCreateHist(self, event):
+        """!Main routine for creating a histogram. Uses r.stats to
+        create a list of cell value and count/percent/area pairs. This is passed to
+        plot to create a line graph of the histogram.
+        """
+        self.SetCursor(self.parent.cursors["default"])
+        self.SetGraphStyle()
+        self.SetupHistogram()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+    def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
+        """
+        dlg = dialogs.HistRasterDialog(parent = self)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self.rasterList = dlg.rasterList
+            self.group = dlg.group
+            self.bins = dlg.bins
+            self.histtype = dlg.histtype
+            self.maptype = dlg.maptype
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
+
+            # plot histogram
+            if len(self.rasterList) > 0:
+                self.OnCreateHist(event = None)
+
+        dlg.Destroy()
+
+    def SetupHistogram(self):
+        """!Build data list for ploting each raster
+        """
+
+        #
+        # populate raster dictionary
+        #
+        if len(self.rasterList) == 0: return  # nothing selected
+        
+        for r in self.rasterList:
+            self.raster[r]['datalist'] = self.CreateDatalist(r)
+            
+        #
+        # update title
+        #
+        if self.maptype == 'group':
+            self.ptitle = _('Histogram of %s') % self.group.split('@')[0] 
+        else: 
+            self.ptitle = _('Histogram of %s') % self.rasterList[0].split('@')[0] 
+
+        
+        #
+        # set xlabel based on first raster map in list to be histogrammed
+        #
+        units = self.raster[self.rasterList[0]]['units']
+        if units != '' and units != '(none)' and units != None:
+            self.xlabel = _('Raster cell values %s') % units
+        else:
+            self.xlabel = _('Raster cell values') 
+
+        #
+        # set ylabel from self.histtype
+        #
+        if self.histtype == 'count': self.ylabel = _('Cell counts')
+        if self.histtype == 'percent': self.ylabel = _('Percent of total cells')
+        if self.histtype == 'area': self.ylabel = _('Area')
+
+    def CreateDatalist(self, raster):
+        """!Build a list of cell value, frequency pairs for histogram
+            frequency can be in cell counts, percents, or area
+        """
+        datalist = []
+        
+        if self.histtype == 'count': freqflag = 'cn'
+        if self.histtype == 'percent': freqflag = 'pn'
+        if self.histtype == 'area': freqflag = 'an'
+                
+        try:
+            ret = gcmd.RunCommand("r.stats",
+                                  parent = self,
+                                  input = raster,
+                                  flags = freqflag,
+                                  nsteps = self.bins,
+                                  fs = ',',
+                                  quiet = True,
+                                  read = True)
+            
+            if not ret:
+                return datalist
+            
+            for line in ret.splitlines():
+                cellval, histval = line.strip().split(',')
+                histval = histval.strip()
+                if self.raster[raster]['datatype'] != 'CELL':
+                    cellval = cellval.split('-')[0]
+                if self.histtype == 'percent':
+                    histval = histval.rstrip('%')
+                    
+                datalist.append((cellval,histval))
+
+            return datalist
+        except gcmd.GException, e:
+            gcmd.GError(parent = self,
+                        message = e.value)
+            return None
+        
+    def CreatePlotList(self):
+        """!Make list of elements to plot
+        """
+        
+        # graph the cell value, frequency pairs for the histogram
+        self.plotlist = []
+
+        for r in self.rasterList:
+            if len(self.raster[r]['datalist']) > 0:
+                col = wx.Color(self.raster[r]['pcolor'][0],
+                               self.raster[r]['pcolor'][1],
+                               self.raster[r]['pcolor'][2],
+                               255)
+                self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
+                                                        colour = col,
+                                                        width = self.raster[r]['pwidth'],
+                                                        style = self.linestyledict[self.raster[r]['pstyle']],
+                                                        legend = self.raster[r]['plegend'])
+
+                self.plotlist.append(self.raster[r]['pline'])
+          
+        if len(self.plotlist) > 0:        
+            return self.plotlist
+        else:
+            return None
+
+    def Update(self):
+        """!Update histogram after changing options
+        """
+        self.SetGraphStyle()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+ 
+    def OnStats(self, event):
+        """!Displays regression information in messagebox
+        """
+        message = []
+        title = _('Statistics for Map(s) Histogrammed')
+
+        for r in self.rasterList:
+            rast = r.split('@')[0] 
+            ret = grass.read_command('r.univar', map = r, flags = 'e', quiet = True)
+            stats = _('Statistics for %s\n\n%s\n') % (rast, ret)
+            message.append(stats)
+            
+        stats = dialogs.PlotStatsFrame(self, id = wx.ID_ANY, message = message, 
+                                      title = title)
+
+        if stats.Show() == wx.ID_CLOSE:
+            stats.Destroy()       
+
+class Histogram2Toolbar(BaseToolbar):
+    """!Toolbar for histogramming raster map
+    """ 
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['plot']
+        return self._getToolbarData((('addraster', Icons['layerManager']["addRast"],
+                                      self.parent.OnSelectRaster),
+                                     (None, ),
+                                     ('draw', icons["draw"],
+                                      self.parent.OnCreateHist),
+                                     ('erase', Icons['displayWindow']["erase"],
+                                      self.parent.OnErase),
+                                     ('drag', Icons['displayWindow']['pan'],
+                                      self.parent.OnDrag),
+                                     ('zoom', Icons['displayWindow']['zoomIn'],
+                                      self.parent.OnZoom),
+                                     ('unzoom', Icons['displayWindow']['zoomBack'],
+                                      self.parent.OnRedraw),
+                                     (None, ),
+                                     ('statistics', icons['statistics'],
+                                      self.parent.OnStats),
+                                     ('image', Icons['displayWindow']["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('print', Icons['displayWindow']["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('settings', icons["options"],
+                                      self.parent.PlotOptionsMenu),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit),
+                                     )
+)

+ 427 - 0
gui/wxpython/wxplot/profile.py

@@ -0,0 +1,427 @@
+"""!
+@package wxplot.profile
+
+@brief Profiling using PyPlot
+
+Classes:
+ - ProfileFrame
+ - ProfileToolbar
+
+(C) 2011 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.
+
+@author Michael Barton, Arizona State University
+"""
+
+import os
+import sys
+import math
+
+import wx
+import wx.lib.plot as plot
+
+import grass.script as grass
+
+try:
+    import numpy
+except ImportError:
+    msg = _("This module requires the NumPy module, which could not be "
+            "imported. It probably is not installed (it's not part of the "
+            "standard Python distribution). See the Numeric Python site "
+            "(http://numpy.scipy.org) for information on downloading source or "
+            "binaries.")
+    print >> sys.stderr, "wxplot.py: " + msg
+
+from wxplot.base       import BasePlotFrame
+from gui_core.toolbars import BaseToolbar
+
+class ProfileFrame(BasePlotFrame):
+    """!Mainframe for displaying profile of one or more raster maps. Uses wx.lib.plot.
+    """
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
+
+        BasePlotFrame.__init__(self, parent)
+
+        self.toolbar = ProfileToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Profile Analysis Tool"))
+
+        #
+        # Init variables
+        #
+        self.rasterList = rasterList
+        self.plottype = 'profile'
+        self.coordstr = ''              # string of coordinates for r.profile
+        self.seglist = []               # segment endpoint list
+        self.ppoints = ''               # segment endpoints data         
+        self.transect_length = 0.0      # total transect length        
+        self.ptitle = _('Profile of')   # title of window
+        self.raster = {}
+
+        self.colorList =  ["blue", "red", "green", "yellow", "magenta", "cyan", \
+                    "aqua", "black", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+
+        if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
+            self.InitRasterOpts(self.rasterList, self.plottype)
+            
+        
+        self._initOpts()
+
+        # determine units (axis labels)
+        if self.parent.Map.projinfo['units'] != '':
+            self.xlabel = _('Distance (%s)') % self.parent.Map.projinfo['units']
+        else:
+            self.xlabel = _("Distance along transect")
+        self.ylabel = _("Cell values")
+        
+    def _initOpts(self):
+        """!Initialize plot options
+        """
+        self.InitPlotOpts('profile')
+
+    def OnDrawTransect(self, event):
+        """!Draws transect to profile in map display
+        """
+        self.mapwin.polycoords = []
+        self.seglist = []
+        self.mapwin.ClearLines(self.mapwin.pdc)
+        self.ppoints = ''
+
+        self.parent.SetFocus()
+        self.parent.Raise()
+        
+        self.mapwin.mouse['use'] = 'profile'
+        self.mapwin.mouse['box'] = 'line'
+        self.mapwin.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
+        self.mapwin.polypen = wx.Pen(colour = 'dark green', width = 2, style = wx.SHORT_DASH)
+        self.mapwin.SetCursor(self.Parent.cursors["cross"])
+
+    def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
+        """
+        dlg = dialogs.ProfileRasterDialog(parent = self)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self.rasterList = dlg.rasterList
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
+            
+            # plot profile
+            if len(self.mapwin.polycoords) > 0 and len(self.rasterList) > 0:
+                self.OnCreateProfile(event = None)
+
+        dlg.Destroy()
+
+    def SetupProfile(self):
+        """!Create coordinate string for profiling. Create segment list for
+           transect segment markers.
+        """
+
+        #
+        # create list of coordinate points for r.profile
+        #                
+        dist = 0
+        cumdist = 0
+        self.coordstr = ''
+        lasteast = lastnorth = None
+        
+        if len(self.mapwin.polycoords) > 0:
+            for point in self.mapwin.polycoords:
+                # build string of coordinate points for r.profile
+                if self.coordstr == '':
+                    self.coordstr = '%d,%d' % (point[0], point[1])
+                else:
+                    self.coordstr = '%s,%d,%d' % (self.coordstr, point[0], point[1])
+
+        if len(self.rasterList) == 0:
+            return
+
+        # title of window
+        self.ptitle = _('Profile of')
+
+        #
+        # create list of coordinates for transect segment markers
+        #
+        if len(self.mapwin.polycoords) > 0:
+            self.seglist = []
+            for point in self.mapwin.polycoords:
+                # get value of raster cell at coordinate point
+                ret = gcmd.RunCommand('r.what',
+                                      parent = self,
+                                      read = True,
+                                      input = self.rasterList[0],
+                                      east_north = '%d,%d' % (point[0],point[1]))
+                
+                val = ret.splitlines()[0].split('|')[3]
+                if val == None or val == '*': continue
+                val = float(val)
+                
+                # calculate distance between coordinate points
+                if lasteast and lastnorth:
+                    dist = math.sqrt(math.pow((lasteast-point[0]),2) + math.pow((lastnorth-point[1]),2))
+                cumdist += dist
+                
+                #store total transect length
+                self.transect_length = cumdist
+
+                # build a list of distance,value pairs for each segment of transect
+                self.seglist.append((cumdist,val))
+                lasteast = point[0]
+                lastnorth = point[1]
+
+            # delete extra first segment point
+            try:
+                self.seglist.pop(0)
+            except:
+                pass
+
+        #
+        # create datalist of dist/value pairs and y labels for each raster map
+        #    
+        self.ylabel = ''
+        i = 0
+
+        for r in self.raster.iterkeys():
+            self.raster[r]['datalist'] = []
+            datalist = self.CreateDatalist(r, self.coordstr)
+            if len(datalist) > 0:   
+                self.raster[r]['datalist'] = datalist
+
+                # update ylabel to match units if they exist           
+                if self.raster[r]['units'] != '':
+                    self.ylabel += '%s (%d),' % (r['units'], i)
+                i += 1
+
+                # update title
+                self.ptitle += ' %s ,' % r.split('@')[0]
+
+        self.ptitle = self.ptitle.rstrip(',')
+            
+        if self.ylabel == '':
+            self.ylabel = _('Raster values')
+        else:
+            self.ylabel = self.ylabel.rstrip(',')
+
+    def CreateDatalist(self, raster, coords):
+        """!Build a list of distance, value pairs for points along transect using r.profile
+        """
+        datalist = []
+        
+        # keep total number of transect points to 500 or less to avoid 
+        # freezing with large, high resolution maps
+        region = grass.region()
+        curr_res = min(float(region['nsres']),float(region['ewres']))
+        transect_rec = 0
+        if self.transect_length / curr_res > 500:
+            transect_res = self.transect_length / 500
+        else: transect_res = curr_res
+        
+        ret = gcmd.RunCommand("r.profile",
+                              parent = self,
+                              input = raster,
+                              profile = coords,
+                              res = transect_res,
+                              null = "nan",
+                              quiet = True,
+                              read = True)
+        
+        if not ret:
+            return []
+            
+        for line in ret.splitlines():
+            dist, elev = line.strip().split(' ')
+            if dist == None or dist == '' or dist == 'nan' or \
+                elev == None or elev == '' or elev == 'nan':
+                continue
+            dist = float(dist)
+            elev = float(elev)
+            datalist.append((dist,elev))
+
+        return datalist
+
+    def OnCreateProfile(self, event):
+        """!Main routine for creating a profile. Uses r.profile to
+        create a list of distance,cell value pairs. This is passed to
+        plot to create a line graph of the profile. If the profile
+        transect is in multiple segments, these are drawn as
+        points. Profile transect is drawn, using methods in mapdisp.py
+        """
+            
+        if len(self.mapwin.polycoords) == 0 or len(self.rasterList) == 0:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('You must draw a transect to profile in the map display window.'),
+                                   caption = _('Nothing to profile'),
+                                   style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+
+        self.mapwin.SetCursor(self.parent.cursors["default"])
+        self.SetCursor(self.parent.cursors["default"])
+        self.SetGraphStyle()
+        self.SetupProfile()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+        # reset transect
+        self.mapwin.mouse['begin'] = self.mapwin.mouse['end'] = (0.0,0.0)
+        self.mapwin.mouse['use'] = 'pointer'
+        self.mapwin.mouse['box'] = 'point'
+
+    def CreatePlotList(self):
+        """!Create a plot data list from transect datalist and
+            transect segment endpoint coordinates.
+        """
+        # graph the distance, value pairs for the transect
+        self.plotlist = []
+
+        # Add segment marker points to plot data list
+        if len(self.seglist) > 0 :
+            self.ppoints = plot.PolyMarker(self.seglist,
+                                           legend = ' ' + self.properties['marker']['legend'],
+                                           colour = wx.Color(self.properties['marker']['color'][0],
+                                                           self.properties['marker']['color'][1],
+                                                           self.properties['marker']['color'][2],
+                                                           255),
+                                           size = self.properties['marker']['size'],
+                                           fillstyle = self.ptfilldict[self.properties['marker']['fill']],
+                                           marker = self.properties['marker']['type'])
+            self.plotlist.append(self.ppoints)
+
+        # Add profile distance/elevation pairs to plot data list for each raster profiled
+        for r in self.rasterList:
+            col = wx.Color(self.raster[r]['pcolor'][0],
+                           self.raster[r]['pcolor'][1],
+                           self.raster[r]['pcolor'][2],
+                           255)
+            self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
+                                                    colour = col,
+                                                    width = self.raster[r]['pwidth'],
+                                                    style = self.linestyledict[self.raster[r]['pstyle']],
+                                                    legend = self.raster[r]['plegend'])
+
+            self.plotlist.append(self.raster[r]['pline'])
+
+        if len(self.plotlist) > 0:        
+            return self.plotlist
+        else:
+            return None
+
+    def Update(self):
+        """!Update profile after changing options
+        """
+        self.SetGraphStyle()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+    def SaveProfileToFile(self, event):
+        """!Save r.profile data to a csv file
+        """    
+        wildcard = _("Comma separated value (*.csv)|*.csv")
+        
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Path and prefix (for raster name) to save profile values..."),
+                            defaultDir = os.getcwd(), 
+                            defaultFile = "",  wildcard = wildcard, style = wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            
+            for r in self.rasterList:
+                pfile = path+'_'+str(r['name'])+'.csv'
+                try:
+                    file = open(pfile, "w")
+                except IOError:
+                    wx.MessageBox(parent = self,
+                                  message = _("Unable to open file <%s> for writing.") % pfile,
+                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                    return False
+                for datapair in self.raster[r]['datalist']:
+                    file.write('%d,%d\n' % (float(datapair[0]),float(datapair[1])))
+                                        
+                file.close()
+
+        dlg.Destroy()
+        
+    def OnStats(self, event):
+        """!Displays regression information in messagebox
+        """
+        message = []
+        title = _('Statistics for Profile(s)')
+
+        for r in self.raster.iterkeys():
+            try:
+                rast = r.split('@')[0] 
+                statstr = 'Profile of %s\n\n' % rast
+
+                iterable = (i[1] for i in self.raster[r]['datalist'])            
+                a = numpy.fromiter(iterable, numpy.float)
+                
+                statstr += 'n: %f\n' % a.size
+                statstr += 'minimum: %f\n' % numpy.amin(a)
+                statstr += 'maximum: %f\n' % numpy.amax(a)
+                statstr += 'range: %f\n' % numpy.ptp(a)
+                statstr += 'mean: %f\n' % numpy.mean(a)
+                statstr += 'standard deviation: %f\n' % numpy.std(a)
+                statstr += 'variance: %f\n' % numpy.var(a)
+                cv = numpy.std(a)/numpy.mean(a)
+                statstr += 'coefficient of variation: %f\n' % cv
+                statstr += 'sum: %f\n' % numpy.sum(a)
+                statstr += 'median: %f\n' % numpy.median(a)
+                statstr += 'distance along transect: %f\n\n' % self.transect_length       
+                message.append(statstr)
+            except:
+                pass
+            
+        stats = dialogs.PlotStatsFrame(self, id = wx.ID_ANY, message = message, 
+                                      title = title)
+
+        if stats.Show() == wx.ID_CLOSE:
+            stats.Destroy()       
+
+    
+class ProfileToolbar(BaseToolbar):
+    """!Toolbar for profiling raster map
+    """ 
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['plot']
+        return self._getToolbarData((('addraster', Icons['layerManager']["addRast"],
+                                      self.parent.OnSelectRaster),
+                                     ('transect', icons["transect"],
+                                      self.parent.OnDrawTransect),
+                                     (None, ),
+                                     ('draw', icons["draw"],
+                                      self.parent.OnCreateProfile),
+                                     ('erase', Icons['displayWindow']["erase"],
+                                      self.parent.OnErase),
+                                     ('drag', Icons['displayWindow']['pan'],
+                                      self.parent.OnDrag),
+                                     ('zoom', Icons['displayWindow']['zoomIn'],
+                                      self.parent.OnZoom),
+                                     ('unzoom', Icons['displayWindow']['zoomBack'],
+                                      self.parent.OnRedraw),
+                                     (None, ),
+                                     ('statistics', icons['statistics'],
+                                      self.parent.OnStats),
+                                     ('datasave', icons["save"],
+                                      self.parent.SaveProfileToFile),
+                                     ('image', Icons['displayWindow']["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('print', Icons['displayWindow']["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('settings', icons["options"],
+                                      self.parent.PlotOptionsMenu),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit),
+                                     ))

+ 299 - 0
gui/wxpython/wxplot/scatter.py

@@ -0,0 +1,299 @@
+"""!
+@package wxplot.scatter
+
+@brief Scatter plotting using PyPlot
+
+Classes:
+ - ScatterFrame
+ - ScatterToolbar
+
+(C) 2011 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.
+
+@author Michael Barton, Arizona State University
+"""
+
+import sys
+
+import wx
+import wx.lib.plot as plot
+
+import grass.script as grass
+
+from wxplot.base       import BasePlotFrame
+from gui_core.toolbars import BaseToolbar
+
+class ScatterFrame(BasePlotFrame):
+    """!Mainframe for displaying bivariate scatter plot of two raster maps. Uses wx.lib.plot.
+    """
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
+
+        BasePlotFrame.__init__(self, parent)
+        
+        self.toolbar = ScatterToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Bivariate Scatterplot Tool"))
+
+        #
+        # Init variables
+        #
+        self.rasterList = rasterList
+        self.plottype = 'scatter'
+        self.ptitle = _('Bivariate Scatterplot')     # title of window
+        self.xlabel = _("Raster cell values")           # default X-axis label
+        self.ylabel = _("Raster cell values")           # default Y-axis label
+        self.maptype = 'raster'                         # default type of scatterplot
+        self.scattertype = 'normal' 
+        self.bins = 255
+        self.colorList = ["blue", "red", "black", "green", "yellow", "magenta", "cyan", \
+                    "aqua", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+        
+        if len(self.rasterList) > 1: # set raster name(s) from layer manager if a map is selected
+            self.InitRasterOpts(self.rasterList, 'scatter')
+
+        self._initOpts()
+
+    def _initOpts(self):
+        """!Initialize plot options
+        """
+        self.InitPlotOpts('scatter')            
+
+    def OnCreateScatter(self, event):
+        """!Main routine for creating a scatterplot. Uses r.stats to
+        create a list of cell value pairs. This is passed to
+        plot to create a scatterplot.
+        """
+        self.SetCursor(self.parent.cursors["default"])
+        self.SetGraphStyle()
+        self.SetupScatterplot()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+    def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
+        """
+        dlg = dialogs.ScatterRasterDialog(parent = self)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            rlist = dlg.rasterList
+            if rlist < 2: 
+                dlg.Destroy()
+                return                        # need at least 2 rasters for scatterplot
+
+            self.bins = dlg.bins                        # bins for r.stats with float and dcell maps
+            self.scattertype = dlg.scattertype          # scatterplot or bubbleplot
+            self.rasterList = self.CreatePairs(rlist)   # list of raster pairs (tuples)
+            self.raster = self.InitRasterPairs(self.rasterList, 'scatter') # dictionary of raster pairs
+
+            # plot histogram
+            if len(self.rasterList) > 0:
+                self.OnCreateScatter(event = None)
+
+        dlg.Destroy()
+        
+    def CreatePairs(self, rlist):
+        """!Transforms list of rasters into tuples of raster pairs
+        """
+        rasterList = []
+        next = 'first'
+        for r in rlist:
+            if next == 'first':
+                first = r
+                next = 'second'
+            else:
+                second = r
+                t = (first, second)
+                rasterList.append(t)
+                next = 'first'
+                first = second = ''
+                
+        return rasterList
+
+    def SetupScatterplot(self):
+        """!Build data list for ploting each raster
+        """
+
+        #
+        # initialize title string
+        #
+        self.ptitle = _('Bivariate Scatterplot of ')        
+
+        #
+        # create a datalist for plotting for each raster pair
+        #
+        if len(self.rasterList) == 0: return  # at least 1 pair of maps needed to plot        
+        
+        for rpair in self.rasterList:
+            self.raster[rpair]['datalist'] = self.CreateDatalist(rpair)
+            
+            # update title
+            self.ptitle += '%s vs %s, ' % (rpair[0].split('@')[0], rpair[1].split('@')[0])
+
+        self.ptitle = self.ptitle.strip(', ')
+        
+        #
+        # set xlabel & ylabel based on raster maps of first pair to be plotted
+        #
+        units = self.raster[self.rasterList[0]][0]['units']
+        if units != '' and units != '(none)' and units != None:
+            self.xlabel = _('Raster cell values %s') % units
+        else:
+            self.xlabel = _('Raster cell values') 
+
+        units = self.raster[self.rasterList[0]][1]['units']
+        if units != '' and units != '(none)' and units != None:
+            self.ylabel = _('Raster cell values %s') % units
+        else:
+            self.ylabel = _('Raster cell values') 
+
+    def CreateDatalist(self, rpair):
+        """!Build a list of cell value, frequency pairs for histogram
+            frequency can be in cell counts, percents, or area
+        """
+        datalist = []
+        
+        if self.scattertype == 'bubble': 
+            freqflag = 'cn'
+        else:
+            freqflag = 'n'
+                
+        try:
+            ret = gcmd.RunCommand("r.stats",
+                                  parent = self,
+                                  input = '%s,%s' % rpair,
+                                  flags = freqflag,
+                                  nsteps = self.bins,
+                                  fs = ',',
+                                  quiet = True,
+                                  read = True)
+            
+            if not ret:
+                return datalist
+            
+            for line in ret.splitlines():
+                rast1, rast2 = line.strip().split(',')
+                rast1 = rast1.strip()
+                if '-' in rast1: rast1 = rast1.split('-')[0]
+                rast2 = rast2.strip()
+                if '-' in rast2: rast2 = rast2.split('-')[0]
+                
+                rast1 = rast1.encode('ascii', 'ignore')
+                rast2 = rast2.encode('ascii', 'ignore')
+                    
+                datalist.append((rast1,rast2))
+
+            return datalist
+        except gcmd.GException, e:
+            gcmd.GError(parent = self,
+                        message = e.value)
+            return None
+        
+    def CreatePlotList(self):
+        """!Make list of elements to plot
+        """
+        # graph the cell value, frequency pairs for the histogram
+        self.plotlist = []
+
+        for rpair in self.rasterList:
+            if 'datalist' not in self.raster[rpair] or \
+                self.raster[rpair]['datalist'] == None: return
+            
+            if len(self.raster[rpair]['datalist']) > 0:
+                col = wx.Color(self.raster[rpair]['pcolor'][0],
+                               self.raster[rpair]['pcolor'][1],
+                               self.raster[rpair]['pcolor'][2],
+                               255)
+                scatterpoints = plot.PolyMarker(self.raster[rpair]['datalist'],
+                                                legend = ' ' + self.raster[rpair]['plegend'],
+                                                colour = col,size = self.raster[rpair]['psize'],
+                                                fillstyle = self.ptfilldict[self.raster[rpair]['pfill']],
+                                                marker = self.raster[rpair]['ptype'])
+
+                self.plotlist.append(scatterpoints)
+          
+        if len(self.plotlist) > 0:        
+            return self.plotlist
+        else:
+            return None
+
+    def Update(self):
+        """!Update histogram after changing options
+        """
+        self.SetGraphStyle()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+    
+    def OnRegression(self, event):
+        """!Displays regression information in messagebox
+        """
+        message = []
+        title = _('Regression Statistics for Scatterplot(s)')
+
+        for rpair in self.rasterList:
+            if isinstance(rpair, tuple) == False: continue
+            rast1, rast2 = rpair
+            rast1 = rast1.split('@')[0] 
+            rast2 = rast2.split('@')[0] 
+            ret = grass.parse_command('r.regression.line', 
+                                      map1 = rast1, 
+                                      map2 = rast2, 
+                                      flags = 'g', quiet = True,
+                                      parse = (grass.parse_key_val, { 'sep' : '=' }))
+            eqtitle = _('Regression equation for %s vs. %s:\n\n')  % (rast1, rast2)
+            eq = _('   %s = %s + %s(%s)\n\n') % (rast2, ret['a'], ret['b'], rast1)
+            num = _('N = %s\n') % ret['N']
+            rval = _('R = %s\n') % ret['R']
+            rsq = _('R-squared = %f\n') % pow(float(ret['R']), 2)
+            ftest = _('F = %s\n') % ret['F']
+            str = eqtitle + eq + num + rval + rsq + ftest
+            message.append(str)
+            
+        stats = dialogs.PlotStatsFrame(self, id = wx.ID_ANY, message = message, 
+                                      title = title)
+
+        if stats.Show() == wx.ID_CLOSE:
+            stats.Destroy()       
+
+class ScatterplotToolbar(BaseToolbar):
+    """!Toolbar for bivariate scatterplots of raster map pairs
+    """ 
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['plot']
+#        icons2 = Icons['modeler']
+        return self._getToolbarData((('addraster', Icons['layerManager']["addRast"],
+                                      self.parent.OnSelectRaster),
+                                     (None, ),
+                                     ('draw', icons["draw"],
+                                      self.parent.OnCreateScatter),
+                                     ('erase', Icons['displayWindow']["erase"],
+                                      self.parent.OnErase),
+                                     ('drag', Icons['displayWindow']['pan'],
+                                      self.parent.OnDrag),
+                                     ('zoom', Icons['displayWindow']['zoomIn'],
+                                      self.parent.OnZoom),
+                                     ('unzoom', Icons['displayWindow']['zoomBack'],
+                                      self.parent.OnRedraw),
+                                     (None, ),
+                                     ('statistics', icons['statistics'],
+                                      self.parent.OnRegression),
+                                     ('image', Icons['displayWindow']["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('print', Icons['displayWindow']["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('settings', icons["options"],
+                                      self.parent.PlotOptionsMenu),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit),
+                                     ))