[GRASS-SVN] r49347 - in grass/trunk/gui/wxpython: . core dbm gcp
gmodeler gui_core gui_modules icons lmgr location_wizard
mapdisp modules nviz psmap scripts tools vdigit wxplot
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Nov 24 06:46:55 EST 2011
Author: martinl
Date: 2011-11-24 03:46:55 -0800 (Thu, 24 Nov 2011)
New Revision: 49347
Added:
grass/trunk/gui/wxpython/core/
grass/trunk/gui/wxpython/core/debug.py
grass/trunk/gui/wxpython/core/gcmd.py
grass/trunk/gui/wxpython/core/globalvar.py
grass/trunk/gui/wxpython/core/menudata.py
grass/trunk/gui/wxpython/core/render.py
grass/trunk/gui/wxpython/core/settings.py
grass/trunk/gui/wxpython/core/units.py
grass/trunk/gui/wxpython/core/utils.py
grass/trunk/gui/wxpython/core/workspace.py
grass/trunk/gui/wxpython/create__init__.py
grass/trunk/gui/wxpython/dbm/
grass/trunk/gui/wxpython/dbm/dialogs.py
grass/trunk/gui/wxpython/dbm/manager.py
grass/trunk/gui/wxpython/dbm/sqlbuilder.py
grass/trunk/gui/wxpython/dbm/vinfo.py
grass/trunk/gui/wxpython/gcp/
grass/trunk/gui/wxpython/gcp/manager.py
grass/trunk/gui/wxpython/gcp/mapdisp.py
grass/trunk/gui/wxpython/gcp/toolbars.py
grass/trunk/gui/wxpython/gmodeler/
grass/trunk/gui/wxpython/gmodeler/dialogs.py
grass/trunk/gui/wxpython/gmodeler/frame.py
grass/trunk/gui/wxpython/gmodeler/model.py
grass/trunk/gui/wxpython/gmodeler/model_file.py
grass/trunk/gui/wxpython/gmodeler/preferences.py
grass/trunk/gui/wxpython/gui_core/
grass/trunk/gui/wxpython/gui_core/dialogs.py
grass/trunk/gui/wxpython/gui_core/forms.py
grass/trunk/gui/wxpython/gui_core/ghelp.py
grass/trunk/gui/wxpython/gui_core/goutput.py
grass/trunk/gui/wxpython/gui_core/gselect.py
grass/trunk/gui/wxpython/gui_core/mapdisp.py
grass/trunk/gui/wxpython/gui_core/mapwindow.py
grass/trunk/gui/wxpython/gui_core/menu.py
grass/trunk/gui/wxpython/gui_core/preferences.py
grass/trunk/gui/wxpython/gui_core/prompt.py
grass/trunk/gui/wxpython/gui_core/toolbars.py
grass/trunk/gui/wxpython/gui_core/widgets.py
grass/trunk/gui/wxpython/lmgr/
grass/trunk/gui/wxpython/lmgr/layertree.py
grass/trunk/gui/wxpython/lmgr/menudata.py
grass/trunk/gui/wxpython/lmgr/pyshell.py
grass/trunk/gui/wxpython/lmgr/toolbars.py
grass/trunk/gui/wxpython/location_wizard/
grass/trunk/gui/wxpython/location_wizard/base.py
grass/trunk/gui/wxpython/location_wizard/dialogs.py
grass/trunk/gui/wxpython/location_wizard/wizard.py
grass/trunk/gui/wxpython/mapdisp/
grass/trunk/gui/wxpython/mapdisp/frame.py
grass/trunk/gui/wxpython/mapdisp/gprint.py
grass/trunk/gui/wxpython/mapdisp/mapwindow.py
grass/trunk/gui/wxpython/mapdisp/statusbar.py
grass/trunk/gui/wxpython/mapdisp/toolbars.py
grass/trunk/gui/wxpython/modules/
grass/trunk/gui/wxpython/modules/colorrules.py
grass/trunk/gui/wxpython/modules/histogram.py
grass/trunk/gui/wxpython/modules/mcalc_builder.py
grass/trunk/gui/wxpython/modules/ogc_services.py
grass/trunk/gui/wxpython/modules/vclean.py
grass/trunk/gui/wxpython/nviz/
grass/trunk/gui/wxpython/nviz/animation.py
grass/trunk/gui/wxpython/nviz/main.py
grass/trunk/gui/wxpython/nviz/mapwindow.py
grass/trunk/gui/wxpython/nviz/preferences.py
grass/trunk/gui/wxpython/nviz/tools.py
grass/trunk/gui/wxpython/nviz/workspace.py
grass/trunk/gui/wxpython/nviz/wxnviz.py
grass/trunk/gui/wxpython/psmap/
grass/trunk/gui/wxpython/psmap/dialogs.py
grass/trunk/gui/wxpython/psmap/frame.py
grass/trunk/gui/wxpython/psmap/menudata.py
grass/trunk/gui/wxpython/psmap/toolbars.py
grass/trunk/gui/wxpython/states.txt
grass/trunk/gui/wxpython/vdigit/
grass/trunk/gui/wxpython/vdigit/dialogs.py
grass/trunk/gui/wxpython/vdigit/main.py
grass/trunk/gui/wxpython/vdigit/mapwindow.py
grass/trunk/gui/wxpython/vdigit/preferences.py
grass/trunk/gui/wxpython/vdigit/toolbars.py
grass/trunk/gui/wxpython/vdigit/wxdigit.py
grass/trunk/gui/wxpython/vdigit/wxdisplay.py
grass/trunk/gui/wxpython/wxplot/
grass/trunk/gui/wxpython/wxplot/base.py
grass/trunk/gui/wxpython/wxplot/dialogs.py
grass/trunk/gui/wxpython/wxplot/histogram.py
grass/trunk/gui/wxpython/wxplot/profile.py
grass/trunk/gui/wxpython/wxplot/scatter.py
Removed:
grass/trunk/gui/wxpython/gui_modules/__init__.py
grass/trunk/gui/wxpython/gui_modules/colorrules.py
grass/trunk/gui/wxpython/gui_modules/dbm.py
grass/trunk/gui/wxpython/gui_modules/dbm_base.py
grass/trunk/gui/wxpython/gui_modules/dbm_dialogs.py
grass/trunk/gui/wxpython/gui_modules/debug.py
grass/trunk/gui/wxpython/gui_modules/disp_print.py
grass/trunk/gui/wxpython/gui_modules/gcmd.py
grass/trunk/gui/wxpython/gui_modules/gcpmanager.py
grass/trunk/gui/wxpython/gui_modules/gcpmapdisp.py
grass/trunk/gui/wxpython/gui_modules/gdialogs.py
grass/trunk/gui/wxpython/gui_modules/ghelp.py
grass/trunk/gui/wxpython/gui_modules/globalvar.py
grass/trunk/gui/wxpython/gui_modules/gmodeler.py
grass/trunk/gui/wxpython/gui_modules/goutput.py
grass/trunk/gui/wxpython/gui_modules/gpyshell.py
grass/trunk/gui/wxpython/gui_modules/gselect.py
grass/trunk/gui/wxpython/gui_modules/histogram.py
grass/trunk/gui/wxpython/gui_modules/layertree.py
grass/trunk/gui/wxpython/gui_modules/location_wizard.py
grass/trunk/gui/wxpython/gui_modules/mapdisp.py
grass/trunk/gui/wxpython/gui_modules/mapdisp_statusbar.py
grass/trunk/gui/wxpython/gui_modules/mapdisp_vdigit.py
grass/trunk/gui/wxpython/gui_modules/mapdisp_window.py
grass/trunk/gui/wxpython/gui_modules/mcalc_builder.py
grass/trunk/gui/wxpython/gui_modules/menu.py
grass/trunk/gui/wxpython/gui_modules/menudata.py
grass/trunk/gui/wxpython/gui_modules/menuform.py
grass/trunk/gui/wxpython/gui_modules/nviz.py
grass/trunk/gui/wxpython/gui_modules/nviz_animation.py
grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py
grass/trunk/gui/wxpython/gui_modules/nviz_preferences.py
grass/trunk/gui/wxpython/gui_modules/nviz_tools.py
grass/trunk/gui/wxpython/gui_modules/ogc_services.py
grass/trunk/gui/wxpython/gui_modules/preferences.py
grass/trunk/gui/wxpython/gui_modules/prompt.py
grass/trunk/gui/wxpython/gui_modules/psmap.py
grass/trunk/gui/wxpython/gui_modules/psmap_dialogs.py
grass/trunk/gui/wxpython/gui_modules/render.py
grass/trunk/gui/wxpython/gui_modules/sqlbuilder.py
grass/trunk/gui/wxpython/gui_modules/states.txt
grass/trunk/gui/wxpython/gui_modules/toolbars.py
grass/trunk/gui/wxpython/gui_modules/units.py
grass/trunk/gui/wxpython/gui_modules/utils.py
grass/trunk/gui/wxpython/gui_modules/vclean.py
grass/trunk/gui/wxpython/gui_modules/vdigit.py
grass/trunk/gui/wxpython/gui_modules/workspace.py
grass/trunk/gui/wxpython/gui_modules/wxnviz.py
grass/trunk/gui/wxpython/gui_modules/wxplot.py
grass/trunk/gui/wxpython/gui_modules/wxplot_dialogs.py
grass/trunk/gui/wxpython/gui_modules/wxvdigit.py
grass/trunk/gui/wxpython/gui_modules/wxvdriver.py
Modified:
grass/trunk/gui/wxpython/Makefile
grass/trunk/gui/wxpython/icons/grass_icons.py
grass/trunk/gui/wxpython/icons/icon.py
grass/trunk/gui/wxpython/scripts/r.li.setup.py
grass/trunk/gui/wxpython/scripts/vkrige.py
grass/trunk/gui/wxpython/tools/update_menudata.py
grass/trunk/gui/wxpython/wxgui.py
Log:
wxGUI major code reorganization
Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile 2011-11-24 09:13:37 UTC (rev 49346)
+++ grass/trunk/gui/wxpython/Makefile 2011-11-24 11:46:55 UTC (rev 49347)
@@ -9,21 +9,30 @@
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) $@
Copied: grass/trunk/gui/wxpython/core/debug.py (from rev 49282, grass/trunk/gui/wxpython/gui_modules/debug.py)
===================================================================
--- grass/trunk/gui/wxpython/core/debug.py (rev 0)
+++ grass/trunk/gui/wxpython/core/debug.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,75 @@
+"""!
+ at package core.debug
+
+ at brief wxGUI debugging
+
+Classes:
+ - DebugMsg
+
+ at code
+from core.debug import Debug
+Debug.msg (3, 'debug message')
+ at 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+
+import grass.script as grass
+
+class DebugMsg:
+ """!wxGUI debugging
+
+ @code
+ g.gisenv set=WX_DEBUG=[0-5]
+ @endcode
+ """
+ def __init__(self):
+ # default level
+ self.debuglevel = 0
+
+ self.SetLevel()
+
+ def SetLevel(self):
+ """!Initialize gui debug level
+ """
+ self.debuglevel = int(grass.gisenv().get('WX_DEBUG', 0))
+
+ def msg(self, level, message, *args):
+ """!Print debug message
+
+ @param level debug level (0-5)
+ @param message message to be printed
+ @param *args formatting params
+ """
+ # self.SetLevel()
+ if self.debuglevel > 0 and level > 0 and level <= self.debuglevel:
+ if args:
+ sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) + \
+ message % args + os.linesep)
+ else:
+ sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) + \
+ message + os.linesep)
+ sys.stderr.flush() # force flush (required for MS Windows)
+
+ def GetLevel(self):
+ """!Return current GUI debug level"""
+ return self.debuglevel
+
+# Debug instance
+Debug = DebugMsg()
+
+# testing
+if __name__ == "__main__":
+ from core import cmd as gcmd
+ gcmd.RunCommand('g.gisenv',
+ set = 'DEBUG=3')
+
+ for level in range (4):
+ Debug.msg (level, "message level=%d" % level)
Copied: grass/trunk/gui/wxpython/core/gcmd.py (from rev 49282, grass/trunk/gui/wxpython/gui_modules/gcmd.py)
===================================================================
--- grass/trunk/gui/wxpython/core/gcmd.py (rev 0)
+++ grass/trunk/gui/wxpython/core/gcmd.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,673 @@
+"""!
+ at package core.gcmd
+
+ at brief wxGUI command interface
+
+Classes:
+ - GError
+ - GWarning
+ - GMessage
+ - GException
+ - Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
+ - Command
+ - CommandThread
+
+Functions:
+ - RunCommand
+
+(C) 2007-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.
+
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import time
+import errno
+import signal
+import locale
+import traceback
+
+import wx
+
+try:
+ import subprocess
+except:
+ compatPath = os.path.join(globalvar.ETCWXDIR, "compat")
+ sys.path.append(compatPath)
+ import subprocess
+if subprocess.mswindows:
+ from win32file import ReadFile, WriteFile
+ from win32pipe import PeekNamedPipe
+ import msvcrt
+else:
+ import select
+ import fcntl
+from threading import Thread
+
+from grass.script import core as grass
+
+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):
+ if not caption:
+ caption = _('Error')
+ style = wx.OK | wx.ICON_ERROR | wx.CENTRE
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ if exc_traceback:
+ exception = traceback.format_exc()
+ reason = exception.splitlines()[-1].split(':', 1)[-1].strip()
+
+ if Debug.GetLevel() > 0 and exc_traceback:
+ sys.stderr.write(exception)
+
+ if showTraceback and exc_traceback:
+ wx.MessageBox(parent = parent,
+ message = message + '\n\n%s: %s\n\n%s' % \
+ (_('Reason'),
+ reason, exception),
+ caption = caption,
+ style = style)
+ else:
+ wx.MessageBox(parent = parent,
+ message = message,
+ caption = caption,
+ style = style)
+
+class GWarning:
+ def __init__(self, message, parent = None):
+ caption = _('Warning')
+ style = wx.OK | wx.ICON_WARNING | wx.CENTRE
+ wx.MessageBox(parent = parent,
+ message = message,
+ caption = caption,
+ style = style)
+
+class GMessage:
+ def __init__(self, message, parent = None):
+ caption = _('Message')
+ style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
+ wx.MessageBox(parent = parent,
+ message = message,
+ caption = caption,
+ style = style)
+
+class GException(Exception):
+ def __init__(self, value = ''):
+ self.value = value
+
+ def __str__(self):
+ return str(self.value)
+
+class Popen(subprocess.Popen):
+ """!Subclass subprocess.Popen"""
+ def __init__(self, args, **kwargs):
+ if subprocess.mswindows:
+ args = map(EncodeString, args)
+
+ subprocess.Popen.__init__(self, args, **kwargs)
+
+ def recv(self, maxsize = None):
+ return self._recv('stdout', maxsize)
+
+ def recv_err(self, maxsize = None):
+ return self._recv('stderr', maxsize)
+
+ def send_recv(self, input = '', maxsize = None):
+ return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+
+ def get_conn_maxsize(self, which, maxsize):
+ if maxsize is None:
+ maxsize = 1024
+ elif maxsize < 1:
+ maxsize = 1
+ return getattr(self, which), maxsize
+
+ def _close(self, which):
+ getattr(self, which).close()
+ setattr(self, which, None)
+
+ def kill(self):
+ """!Try to kill running process"""
+ if subprocess.mswindows:
+ import win32api
+ handle = win32api.OpenProcess(1, 0, self.pid)
+ return (0 != win32api.TerminateProcess(handle, 0))
+ else:
+ try:
+ os.kill(-self.pid, signal.SIGTERM) # kill whole group
+ except OSError:
+ pass
+
+ if subprocess.mswindows:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(self.stdin.fileno())
+ (errCode, written) = WriteFile(x, input)
+ except ValueError:
+ return self._close('stdin')
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(conn.fileno())
+ (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+ if maxsize < nAvail:
+ nAvail = maxsize
+ if nAvail > 0:
+ (errCode, read) = ReadFile(x, nAvail, None)
+ except ValueError:
+ return self._close(which)
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close(which)
+ raise
+
+ if self.universal_newlines:
+ read = self._translate_newlines(read)
+ return read
+
+ else:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ if not select.select([], [self.stdin], [], 0)[1]:
+ return 0
+
+ try:
+ written = os.write(self.stdin.fileno(), input)
+ except OSError, why:
+ if why[0] == errno.EPIPE: #broken pipe
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+ if not conn.closed:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+ try:
+ if not select.select([conn], [], [], 0)[0]:
+ return ''
+
+ r = conn.read(maxsize)
+
+ if not r:
+ return self._close(which)
+
+ if self.universal_newlines:
+ r = self._translate_newlines(r)
+ return r
+ finally:
+ if not conn.closed:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+message = "Other end disconnected!"
+
+def recv_some(p, t = .1, e = 1, tr = 5, stderr = 0):
+ if tr < 1:
+ tr = 1
+ x = time.time()+t
+ y = []
+ r = ''
+ pr = p.recv
+ if stderr:
+ pr = p.recv_err
+ while time.time() < x or r:
+ r = pr()
+ if r is None:
+ if e:
+ raise Exception(message)
+ else:
+ break
+ elif r:
+ y.append(r)
+ else:
+ time.sleep(max((x-time.time())/tr, 0))
+ return ''.join(y)
+
+def send_all(p, data):
+ while len(data):
+ sent = p.send(data)
+ if sent is None:
+ raise Exception(message)
+ data = buffer(data, sent)
+
+class Command:
+ """!Run command in separate thread. Used for commands launched
+ on the background.
+
+ If stdout/err is redirected, write() method is required for the
+ given classes.
+
+ @code
+ cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
+
+ if cmd.returncode == None:
+ print 'RUNNING?'
+ elif cmd.returncode == 0:
+ print 'SUCCESS'
+ else:
+ print 'FAILURE (%d)' % cmd.returncode
+ @endcode
+ """
+ def __init__ (self, cmd, stdin = None,
+ verbose = None, wait = True, rerr = False,
+ stdout = None, stderr = None):
+ """
+ @param cmd command given as list
+ @param stdin standard input stream
+ @param verbose verbose level [0, 3] (--q, --v)
+ @param wait wait for child execution terminated
+ @param rerr error handling (when GException raised).
+ True for redirection to stderr, False for GUI dialog,
+ None for no operation (quiet mode)
+ @param stdout redirect standard output or None
+ @param stderr redirect standard error output or None
+ """
+ Debug.msg(1, "gcmd.Command(): %s" % ' '.join(cmd))
+ self.cmd = cmd
+ self.stderr = stderr
+
+ #
+ # set verbosity level
+ #
+ verbose_orig = None
+ if ('--q' not in self.cmd and '--quiet' not in self.cmd) and \
+ ('--v' not in self.cmd and '--verbose' not in self.cmd):
+ if verbose is not None:
+ if verbose == 0:
+ self.cmd.append('--quiet')
+ elif verbose == 3:
+ self.cmd.append('--verbose')
+ else:
+ verbose_orig = os.getenv("GRASS_VERBOSE")
+ os.environ["GRASS_VERBOSE"] = str(verbose)
+
+ #
+ # create command thread
+ #
+ self.cmdThread = CommandThread(cmd, stdin,
+ stdout, stderr)
+ self.cmdThread.start()
+
+ if wait:
+ self.cmdThread.join()
+ if self.cmdThread.module:
+ self.cmdThread.module.wait()
+ self.returncode = self.cmdThread.module.returncode
+ else:
+ self.returncode = 1
+ else:
+ self.cmdThread.join(0.5)
+ self.returncode = None
+
+ if self.returncode is not None:
+ Debug.msg (3, "Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" % \
+ (' '.join(cmd), wait, self.returncode, self.cmdThread.isAlive()))
+ if rerr is not None and self.returncode != 0:
+ if rerr is False: # GUI dialog
+ raise GException("%s '%s'%s%s%s %s%s" % \
+ (_("Execution failed:"),
+ ' '.join(self.cmd),
+ os.linesep, os.linesep,
+ _("Details:"),
+ os.linesep,
+ _("Error: ") + self.__GetError()))
+ elif rerr == sys.stderr: # redirect message to sys
+ stderr.write("Execution failed: '%s'" % (' '.join(self.cmd)))
+ stderr.write("%sDetails:%s%s" % (os.linesep,
+ _("Error: ") + self.__GetError(),
+ os.linesep))
+ else:
+ pass # nop
+ else:
+ Debug.msg (3, "Command(): cmd='%s', wait=%s, returncode=?, alive=%s" % \
+ (' '.join(cmd), wait, self.cmdThread.isAlive()))
+
+ if verbose_orig:
+ os.environ["GRASS_VERBOSE"] = verbose_orig
+ elif "GRASS_VERBOSE" in os.environ:
+ del os.environ["GRASS_VERBOSE"]
+
+ def __ReadOutput(self, stream):
+ """!Read stream and return list of lines
+
+ @param stream stream to be read
+ """
+ lineList = []
+
+ if stream is None:
+ return lineList
+
+ while True:
+ line = stream.readline()
+ if not line:
+ break
+ line = line.replace('%s' % os.linesep, '').strip()
+ lineList.append(line)
+
+ return lineList
+
+ def __ReadErrOutput(self):
+ """!Read standard error output and return list of lines"""
+ return self.__ReadOutput(self.cmdThread.module.stderr)
+
+ def __ProcessStdErr(self):
+ """
+ Read messages/warnings/errors from stderr
+
+ @return list of (type, message)
+ """
+ if self.stderr is None:
+ lines = self.__ReadErrOutput()
+ else:
+ lines = self.cmdThread.error.strip('%s' % os.linesep). \
+ split('%s' % os.linesep)
+
+ msg = []
+
+ type = None
+ content = ""
+ for line in lines:
+ if len(line) == 0:
+ continue
+ if 'GRASS_' in line: # error or warning
+ if 'GRASS_INFO_WARNING' in line: # warning
+ type = "WARNING"
+ elif 'GRASS_INFO_ERROR' in line: # error
+ type = "ERROR"
+ elif 'GRASS_INFO_END': # end of message
+ msg.append((type, content))
+ type = None
+ content = ""
+
+ if type:
+ content += line.split(':', 1)[1].strip()
+ else: # stderr
+ msg.append((None, line.strip()))
+
+ return msg
+
+ def __GetError(self):
+ """!Get error message or ''"""
+ if not self.cmdThread.module:
+ return _("Unable to exectute command: '%s'") % ' '.join(self.cmd)
+
+ for type, msg in self.__ProcessStdErr():
+ if type == 'ERROR':
+ enc = locale.getdefaultlocale()[1]
+ if enc:
+ return unicode(msg, enc)
+ else:
+ return msg
+
+ return ''
+
+class CommandThread(Thread):
+ """!Create separate thread for command. Used for commands launched
+ on the background."""
+ def __init__ (self, cmd, stdin = None,
+ stdout = sys.stdout, stderr = sys.stderr):
+ """
+ @param cmd command (given as list)
+ @param stdin standard input stream
+ @param stdout redirect standard output or None
+ @param stderr redirect standard error output or None
+ """
+ Thread.__init__(self)
+
+ self.cmd = cmd
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+
+ self.module = None
+ self.error = ''
+
+ self._want_abort = False
+ self.aborted = False
+
+ self.setDaemon(True)
+
+ # set message formatting
+ self.message_format = os.getenv("GRASS_MESSAGE_FORMAT")
+ os.environ["GRASS_MESSAGE_FORMAT"] = "gui"
+
+ def __del__(self):
+ if self.message_format:
+ os.environ["GRASS_MESSAGE_FORMAT"] = self.message_format
+ else:
+ del os.environ["GRASS_MESSAGE_FORMAT"]
+
+ def run(self):
+ """!Run command"""
+ if len(self.cmd) == 0:
+ return
+
+ Debug.msg(1, "gcmd.CommandThread(): %s" % ' '.join(self.cmd))
+
+ self.startTime = time.time()
+ try:
+ self.module = Popen(self.cmd,
+ stdin = subprocess.PIPE,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ shell = sys.platform == "win32")
+ except OSError, e:
+ self.error = str(e)
+ return 1
+
+ if self.stdin: # read stdin if requested ...
+ self.module.stdin.write(self.stdin)
+ self.module.stdin.close()
+
+ # redirect standard outputs...
+ self._redirect_stream()
+
+ def _redirect_stream(self):
+ """!Redirect stream"""
+ if self.stdout:
+ # make module stdout/stderr non-blocking
+ out_fileno = self.module.stdout.fileno()
+ if not subprocess.mswindows:
+ flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
+ fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+ if self.stderr:
+ # make module stdout/stderr non-blocking
+ out_fileno = self.module.stderr.fileno()
+ if not subprocess.mswindows:
+ flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
+ fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+ # wait for the process to end, sucking in stuff until it does end
+ while self.module.poll() is None:
+ if self._want_abort: # abort running process
+ self.module.kill()
+ self.aborted = True
+ return
+ if self.stdout:
+ line = recv_some(self.module, e = 0, stderr = 0)
+ self.stdout.write(line)
+ if self.stderr:
+ line = recv_some(self.module, e = 0, stderr = 1)
+ self.stderr.write(line)
+ if len(line) > 0:
+ self.error = line
+
+ # get the last output
+ if self.stdout:
+ line = recv_some(self.module, e = 0, stderr = 0)
+ self.stdout.write(line)
+ if self.stderr:
+ line = recv_some(self.module, e = 0, stderr = 1)
+ self.stderr.write(line)
+ if len(line) > 0:
+ self.error = line
+
+ def abort(self):
+ """!Abort running process, used by main thread to signal an abort"""
+ self._want_abort = True
+
+def _formatMsg(text):
+ """!Format error messages for dialogs
+ """
+ message = ''
+ for line in text.splitlines():
+ if len(line) == 0:
+ continue
+ elif 'GRASS_INFO_MESSAGE' in line:
+ message += line.split(':', 1)[1].strip() + '\n'
+ elif 'GRASS_INFO_WARNING' in line:
+ message += line.split(':', 1)[1].strip() + '\n'
+ elif 'GRASS_INFO_ERROR' in line:
+ message += line.split(':', 1)[1].strip() + '\n'
+ elif 'GRASS_INFO_END' in line:
+ return message
+ else:
+ message += line.strip() + '\n'
+
+ return message
+
+def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
+ parent = None, read = False, parse = None, stdin = None, getErrorMsg = False, **kwargs):
+ """!Run GRASS command
+
+ @param prog program to run
+ @param flags flags given as a string
+ @param overwrite, quiet, verbose flags
+ @param parent parent window for error messages
+ @param read fetch stdout
+ @param parse fn to parse stdout (e.g. grass.parse_key_val) or None
+ @param stdin stdin or None
+ @param getErrorMsg get error messages on failure
+ @param kwargs program parameters
+
+ @return returncode (read == False and getErrorMsg == False)
+ @return returncode, messages (read == False and getErrorMsg == True)
+ @return stdout (read == True and getErrorMsg == False)
+ @return returncode, stdout, messages (read == True and getErrorMsg == True)
+ @return stdout, stderr
+ """
+ cmdString = ' '.join(grass.make_command(prog, flags, overwrite,
+ quiet, verbose, **kwargs))
+
+ Debug.msg(1, "gcmd.RunCommand(): %s" % cmdString)
+
+ kwargs['stderr'] = subprocess.PIPE
+
+ if read:
+ kwargs['stdout'] = subprocess.PIPE
+
+ if stdin:
+ kwargs['stdin'] = subprocess.PIPE
+
+ Debug.msg(2, "gcmd.RunCommand(): command started")
+ start = time.time()
+
+ ps = grass.start_command(prog, flags, overwrite, quiet, verbose, **kwargs)
+
+ if stdin:
+ ps.stdin.write(stdin)
+ ps.stdin.close()
+ ps.stdin = None
+
+ Debug.msg(3, "gcmd.RunCommand(): decoding string")
+ stdout, stderr = map(DecodeString, ps.communicate())
+
+ ret = ps.returncode
+ Debug.msg(1, "gcmd.RunCommand(): get return code %d (%.6f sec)" % \
+ (ret, (time.time() - start)))
+
+ Debug.msg(3, "gcmd.RunCommand(): print error")
+ if ret != 0 and parent:
+ Debug.msg(2, "gcmd.RunCommand(): error %s" % stderr)
+ if (stderr == None):
+ Debug.msg(2, "gcmd.RunCommand(): nothing to print ???")
+ else:
+ GError(parent = parent,
+ message = stderr)
+
+ Debug.msg(3, "gcmd.RunCommand(): print read error")
+ if not read:
+ if not getErrorMsg:
+ return ret
+ else:
+ return ret, _formatMsg(stderr)
+
+ if stdout:
+ Debug.msg(2, "gcmd.RunCommand(): return stdout\n'%s'" % stdout)
+ else:
+ Debug.msg(2, "gcmd.RunCommand(): return stdout = None")
+
+ if parse:
+ stdout = parse(stdout)
+
+ if not getErrorMsg:
+ return stdout
+
+ Debug.msg(2, "gcmd.RunCommand(): return ret, stdout")
+ if read and getErrorMsg:
+ return ret, stdout, _formatMsg(stderr)
+
+ Debug.msg(2, "gcmd.RunCommand(): return result")
+ return stdout, _formatMsg(stderr)
Copied: grass/trunk/gui/wxpython/core/globalvar.py (from rev 49282, grass/trunk/gui/wxpython/gui_modules/globalvar.py)
===================================================================
--- grass/trunk/gui/wxpython/core/globalvar.py (rev 0)
+++ grass/trunk/gui/wxpython/core/globalvar.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,177 @@
+"""!
+ at package core.globalvar
+
+ at 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import locale
+
+if not os.getenv("GISBASE"):
+ sys.exit("GRASS is not running. Exiting...")
+
+# path to python scripts
+ETCDIR = os.path.join(os.getenv("GISBASE"), "etc")
+ETCICONDIR = os.path.join(os.getenv("GISBASE"), "etc", "gui", "icons")
+ETCWXDIR = os.path.join(ETCDIR, "gui", "wxpython")
+ETCIMGDIR = os.path.join(ETCDIR, "gui", "images")
+
+sys.path.append(os.path.join(ETCDIR, "python"))
+import grass.script as grass
+
+def CheckWxVersion(version = [2, 8, 11, 0]):
+ """!Check wx version"""
+ ver = wx.version().split(' ')[0]
+ if map(int, ver.split('.')) < version:
+ return False
+
+ return True
+
+def CheckForWx():
+ """!Try to import wx module and check its version"""
+ if 'wx' in sys.modules.keys():
+ return
+
+ minVersion = [2, 8, 1, 1]
+ try:
+ try:
+ import wxversion
+ except ImportError, e:
+ raise ImportError(e)
+ # wxversion.select(str(minVersion[0]) + '.' + str(minVersion[1]))
+ wxversion.ensureMinimal(str(minVersion[0]) + '.' + str(minVersion[1]))
+ import wx
+ version = wx.version().split(' ')[0]
+
+ if map(int, version.split('.')) < minVersion:
+ raise ValueError('Your wxPython version is %s.%s.%s.%s' % tuple(version.split('.')))
+
+ except ImportError, e:
+ print >> sys.stderr, 'ERROR: wxGUI requires wxPython. %s' % str(e)
+ sys.exit(1)
+ except (ValueError, wxversion.VersionError), e:
+ print >> sys.stderr, 'ERROR: wxGUI requires wxPython >= %d.%d.%d.%d. ' % tuple(minVersion) + \
+ '%s.' % (str(e))
+ sys.exit(1)
+ except locale.Error, e:
+ print >> sys.stderr, "Unable to set locale:", e
+ os.environ['LC_ALL'] = ''
+
+if not os.getenv("GRASS_WXBUNDLED"):
+ CheckForWx()
+import wx
+import wx.lib.flatnotebook as FN
+
+"""
+Query layer (generated for example by selecting item in the Attribute Table Manager)
+Deleted automatically on re-render action
+"""
+# temporal query layer (removed on re-render action)
+QUERYLAYER = 'qlayer'
+
+"""!Style definition for FlatNotebook pages"""
+FNPageStyle = FN.FNB_VC8 | \
+ FN.FNB_BACKGROUND_GRADIENT | \
+ FN.FNB_NODRAG | \
+ FN.FNB_TABS_BORDER_SIMPLE
+
+FNPageDStyle = FN.FNB_FANCY_TABS | \
+ FN.FNB_BOTTOM | \
+ FN.FNB_NO_NAV_BUTTONS | \
+ FN.FNB_NO_X_BUTTON
+
+FNPageColor = wx.Colour(125,200,175)
+
+"""!Dialog widget dimension"""
+DIALOG_SPIN_SIZE = (150, -1)
+DIALOG_COMBOBOX_SIZE = (300, -1)
+DIALOG_GSELECT_SIZE = (400, -1)
+DIALOG_TEXTCTRL_SIZE = (400, -1)
+DIALOG_LAYER_SIZE = (100, -1)
+DIALOG_COLOR_SIZE = (30, 30)
+
+MAP_WINDOW_SIZE = (725, 600)
+HIST_WINDOW_SIZE = (500, 350)
+GM_WINDOW_SIZE = (500, 600)
+
+
+"""!File name extension binaries/scripts"""
+if sys.platform == 'win32':
+ EXT_BIN = '.exe'
+ EXT_SCT = '.py'
+else:
+ EXT_BIN = ''
+ EXT_SCT = ''
+
+def GetGRASSCmds(bin = True, scripts = True, gui_scripts = True, addons = True):
+ """!Create list of available GRASS commands to use when parsing
+ string from the command line
+
+ @param bin True to include executable into list
+ @param scripts True to include scripts into list
+ @param gui_scripts True to include GUI scripts into list
+ """
+ gisbase = os.environ['GISBASE']
+ cmd = list()
+
+ if bin:
+ for executable in os.listdir(os.path.join(gisbase, 'bin')):
+ ext = os.path.splitext(executable)[1]
+ if not EXT_BIN or \
+ ext in (EXT_BIN, EXT_SCT):
+ cmd.append(executable)
+
+ # add special call for setting vector colors
+ cmd.append('vcolors')
+
+ if scripts:
+ cmd += os.listdir(os.path.join(gisbase, 'scripts'))
+
+ if gui_scripts:
+ os.environ["PATH"] = os.getenv("PATH") + os.pathsep + os.path.join(gisbase, 'etc', 'gui', 'scripts')
+ cmd = cmd + os.listdir(os.path.join(gisbase, 'etc', 'gui', 'scripts'))
+
+ if addons and os.getenv('GRASS_ADDON_PATH'):
+ path = os.getenv('GRASS_ADDON_PATH')
+ bpath = os.path.join(path, 'bin')
+ spath = os.path.join(path, 'scripts')
+ if os.path.exists(bpath) and os.path.isdir(bpath):
+ for executable in os.listdir(bpath):
+ ext = os.path.splitext(executable)[1]
+ if not EXT_BIN or \
+ ext in (EXT_BIN, EXT_SCT):
+ cmd.append(executable)
+ if os.path.exists(spath) and os.path.isdir(spath):
+ cmd += os.listdir(spath)
+
+ if sys.platform == 'win32':
+ for idx in range(len(cmd)):
+ name, ext = os.path.splitext(cmd[idx])
+ if ext in (EXT_BIN, EXT_SCT):
+ cmd[idx] = name
+
+ return cmd
+
+"""@brief Collected GRASS-relared binaries/scripts"""
+grassCmd = {}
+grassCmd['all'] = GetGRASSCmds()
+grassCmd['script'] = GetGRASSCmds(bin = False, gui_scripts = False)
+
+"""@Toolbar icon size"""
+toolbarSize = (24, 24)
+
+"""@Is g.mlist available?"""
+if 'g.mlist' in grassCmd['all']:
+ have_mlist = True
+else:
+ have_mlist = False
+
+"""@Check version of wxPython, use agwStyle for 2.8.11+"""
+hasAgw = CheckWxVersion()
Copied: grass/trunk/gui/wxpython/core/menudata.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/menudata.py)
===================================================================
--- grass/trunk/gui/wxpython/core/menudata.py (rev 0)
+++ grass/trunk/gui/wxpython/core/menudata.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,231 @@
+"""!
+ at package core.menudata
+
+ at brief Complex list for menu entries for wxGUI
+
+Classes:
+ - MenuData
+
+Usage:
+ at code
+python menudata.py [action] [manager|modeler]
+ at 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.
+
+ at author Michael Barton (Arizona State University)
+ at author Yann Chemin <yann.chemin gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
+ at author Glynn Clements
+"""
+
+import os
+import sys
+import pprint
+try:
+ import xml.etree.ElementTree as etree
+except ImportError:
+ import elementtree.ElementTree as etree # Python <= 2.4
+
+from wx import ID_ANY
+
+if not os.getenv("GISBASE"):
+ sys.exit("GRASS is not running. Exiting...")
+
+class MenuData:
+ """!Abstract menu data class"""
+ def __init__(self, filename):
+ self.tree = etree.parse(filename)
+
+ def _getMenuItem(self, mi):
+ """!Get menu item
+
+ @param mi menu item instance
+ """
+ if mi.tag == 'separator':
+ return ('', '', '', '', '')
+ elif mi.tag == 'menuitem':
+ label = _(mi.find('label').text)
+ help = _(mi.find('help').text)
+ handler = mi.find('handler').text
+ gcmd = mi.find('command') # optional
+ keywords = mi.find('keywords') # optional
+ shortcut = mi.find('shortcut') # optional
+ wxId = mi.find('id') # optional
+ if gcmd != None:
+ gcmd = gcmd.text
+ else:
+ gcmd = ""
+ if keywords != None:
+ keywords = keywords.text
+ else:
+ keywords = ""
+ if shortcut != None:
+ shortcut = shortcut.text
+ else:
+ shortcut = ""
+ if wxId != None:
+ wxId = eval('wx.' + wxId.text)
+ else:
+ wxId = ID_ANY
+ return (label, help, handler, gcmd, keywords, shortcut, wxId)
+ elif mi.tag == 'menu':
+ return self._getMenu(mi)
+ else:
+ raise Exception(_("Unknow tag"))
+
+ def _getMenu(self, m):
+ """!Get menu
+
+ @param m menu
+
+ @return label, menu items
+ """
+ label = _(m.find('label').text)
+ items = m.find('items')
+ return (label, tuple(map(self._getMenuItem, items)))
+
+ def _getMenuBar(self, mb):
+ """!Get menu bar
+
+ @param mb menu bar instance
+
+ @return menu items
+ """
+ return tuple(map(self._getMenu, mb.findall('menu')))
+
+ def _getMenuData(self, md):
+ """!Get menu data
+
+ @param md menu data instace
+
+ @return menu data
+ """
+ return list(map(self._getMenuBar, md.findall('menubar')))
+
+ def GetMenu(self):
+ """!Get menu
+
+ @return menu data
+ """
+ return self._getMenuData(self.tree.getroot())
+
+ def PrintStrings(self, fh):
+ """!Print menu strings to file (used for localization)
+
+ @param fh file descriptor"""
+ className = str(self.__class__).split('.', 1)[1]
+ fh.write('menustrings_%s = [\n' % className)
+ for node in self.tree.getiterator():
+ if node.tag in ['label', 'help']:
+ fh.write(' _(%r),\n' % node.text)
+ fh.write(' \'\']\n')
+
+ def PrintTree(self, fh):
+ """!Print menu tree to file
+
+ @param fh file descriptor"""
+ level = 0
+ for eachMenuData in self.GetMenu():
+ for label, items in eachMenuData:
+ fh.write('- %s\n' % label.replace('&', ''))
+ self._PrintTreeItems(fh, level + 1, items)
+
+ def _PrintTreeItems(self, fh, level, menuData):
+ """!Print menu tree items to file (used by PrintTree)
+
+ @param fh file descriptor
+ @param level menu level
+ @param menuData menu data to print out"""
+ for eachItem in menuData:
+ if len(eachItem) == 2:
+ if eachItem[0]:
+ fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+ self._PrintTreeItems(fh, level + 1, eachItem[1])
+ else:
+ if eachItem[0]:
+ fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+
+ def PrintCommands(self, fh, itemSep = ' | ', menuSep = ' > '):
+ """!Print commands list (command | menu item > menu item)
+
+ @param fh file descriptor
+ """
+ level = 0
+ for eachMenuData in self.GetMenu():
+ for label, items in eachMenuData:
+ menuItems = [label, ]
+ self._PrintCommandsItems(fh, level + 1, items,
+ menuItems, itemSep, menuSep)
+
+ def _PrintCommandsItems(self, fh, level, menuData,
+ menuItems, itemSep, menuSep):
+ """!Print commands item (used by PrintCommands)
+
+ @param fh file descriptor
+ @param menuItems list of menu items
+ """
+ for eachItem in menuData:
+ if len(eachItem) == 2:
+ if eachItem[0]:
+ try:
+ menuItems[level] = eachItem[0]
+ except IndexError:
+ menuItems.append(eachItem[0])
+ self._PrintCommandsItems(fh, level + 1, eachItem[1],
+ menuItems, itemSep, menuSep)
+ else:
+ try:
+ del menuItems[level]
+ except IndexError:
+ pass
+
+ if eachItem[3]:
+ fh.write('%s%s' % (eachItem[3], itemSep))
+ fh.write(menuSep.join(map(lambda x: x.replace('&', ''), menuItems)))
+ fh.write('%s%s' % (menuSep, eachItem[0]))
+ fh.write('\n')
+
+if __name__ == "__main__":
+ import os
+ import sys
+
+ # i18N
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+
+ action = 'strings'
+ menu = 'manager'
+
+ for arg in sys.argv:
+ if arg in ('strings', 'tree', 'commands', 'dump'):
+ action = arg
+ 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':
+ data.PrintStrings(sys.stdout)
+ elif action == 'tree':
+ data.PrintTree(sys.stdout)
+ elif action == 'commands':
+ data.PrintCommands(sys.stdout)
+ elif action == 'dump':
+ pprint.pprint(data.GetMenu(), stream = sys.stdout, indent = 2)
+
+ sys.exit(0)
Copied: grass/trunk/gui/wxpython/core/render.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/render.py)
===================================================================
--- grass/trunk/gui/wxpython/core/render.py (rev 0)
+++ grass/trunk/gui/wxpython/core/render.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,1423 @@
+"""!
+ at package core.render
+
+ at brief Rendering map layers and overlays into map composition image.
+
+Classes:
+ - Layer
+ - MapLayer
+ - Overlay
+ - Map
+
+(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.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import glob
+import math
+import copy
+import tempfile
+import types
+
+import wx
+from wx.lib.newevent import NewEvent
+
+from grass.script import core as grass
+
+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()
+
+USE_GPNMCOMP = True
+
+class Layer(object):
+ """!Virtual class which stores information about layers (map layers and
+ overlays) of the map composition.
+
+ - For map layer use MapLayer class.
+ - For overlays use Overlay class.
+ """
+ def __init__(self, type, cmd, name = None,
+ active = True, hidden = False, opacity = 1.0):
+ """!Create new instance
+
+ @todo pass cmd as tuple instead of list
+
+ @param type layer type ('raster', 'vector', 'overlay', 'command', etc.)
+ @param cmd GRASS command to render layer,
+ given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+ @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree)
+ @param active layer is active, will be rendered only if True
+ @param hidden layer is hidden, won't be listed in Layer Manager if True
+ @param opacity layer opacity <0;1>
+ """
+ self.type = type
+ self.name = name
+
+ if self.type == 'command':
+ self.cmd = list()
+ for c in cmd:
+ self.cmd.append(utils.CmdToTuple(c))
+ else:
+ self.cmd = utils.CmdToTuple(cmd)
+
+ self.active = active
+ self.hidden = hidden
+ self.opacity = opacity
+
+ self.force_render = True
+
+ Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \
+ "active=%d, opacity=%d, hidden=%d" % \
+ (self.type, self.GetCmd(string = True), self.name, self.active,
+ self.opacity, self.hidden))
+
+ # generated file for each layer
+ if USE_GPNMCOMP or self.type == 'overlay':
+ tmpfile = tempfile.mkstemp()[1]
+ self.maskfile = tmpfile + '.pgm'
+ if self.type == 'overlay':
+ self.mapfile = tmpfile + '.png'
+ else:
+ self.mapfile = tmpfile + '.ppm'
+ grass.try_remove(tmpfile)
+ else:
+ self.mapfile = self.maskfile = None
+
+ def __del__(self):
+ Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
+ (self.name, self.GetCmd(string = True)))
+
+ def Render(self):
+ """!Render layer to image
+
+ @return rendered image filename
+ @return None on error or if cmdfile is defined
+ """
+ if not self.cmd:
+ return None
+
+ # ignore in 2D
+ if self.type == '3d-raster':
+ return None
+
+ Debug.msg (3, "Layer.Render(): type=%s, name=%s" % \
+ (self.type, self.name))
+
+ # prepare command for each layer
+ layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
+ 'vector','thememap','themechart',
+ 'grid', 'geodesic', 'rhumb', 'labels',
+ 'command', 'rastleg','maplegend',
+ 'overlay')
+
+ if self.type not in layertypes:
+ raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
+ {'type' : self.type, 'name' : self.name})
+
+ # start monitor
+ if self.mapfile:
+ os.environ["GRASS_PNGFILE"] = self.mapfile
+
+ # execute command
+ try:
+ if self.type == 'command':
+ read = False
+ for c in self.cmd:
+ ret, msg = RunCommand(c[0],
+ getErrorMsg = True,
+ quiet = True,
+ **c[1])
+ if ret != 0:
+ break
+ if not read:
+ os.environ["GRASS_PNG_READ"] = "TRUE"
+
+ os.environ["GRASS_PNG_READ"] = "FALSE"
+ else:
+ 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 GException()
+
+ except GException:
+ # clean up after problems
+ for f in [self.mapfile, self.maskfile]:
+ if not f:
+ continue
+ grass.try_remove(f)
+ f = None
+
+ # stop monitor
+ if self.mapfile and "GRASS_PNGFILE" in os.environ:
+ del os.environ["GRASS_PNGFILE"]
+
+ self.force_render = False
+
+ return self.mapfile
+
+ def GetCmd(self, string = False):
+ """!Get GRASS command as list of string.
+
+ @param string get command as string if True otherwise as list
+
+ @return command list/string
+ """
+ if string:
+ if self.type == 'command':
+ scmd = []
+ for c in self.cmd:
+ scmd.append(utils.GetCmdString(c))
+
+ return ';'.join(scmd)
+ else:
+ return utils.GetCmdString(self.cmd)
+ else:
+ return self.cmd
+
+ def GetType(self):
+ """!Get map layer type"""
+ return self.type
+
+ def GetElement(self):
+ """!Get map element type"""
+ if self.type == 'raster':
+ return 'cell'
+ return self.type
+
+ def GetOpacity(self, float = False):
+ """
+ Get layer opacity level
+
+ @param float get opacity level in <0,1> otherwise <0,100>
+
+ @return opacity level
+ """
+ if float:
+ return self.opacity
+
+ return int (self.opacity * 100)
+
+ def GetName(self, fullyQualified = True):
+ """!Get map layer name
+
+ @param fullyQualified True to return fully qualified name as a
+ string 'name at mapset' otherwise directory { 'name', 'mapset' }
+ is returned
+
+ @return string / directory
+ """
+ if fullyQualified:
+ return self.name
+ else:
+ if '@' in self.name:
+ return { 'name' : self.name.split('@')[0],
+ 'mapset' : self.name.split('@')[1] }
+ else:
+ return { 'name' : self.name,
+ 'mapset' : '' }
+
+ def IsActive(self):
+ """!Check if layer is activated for rendering"""
+ return self.active
+
+ def SetType(self, type):
+ """!Set layer type"""
+ if type not in ('raster', '3d-raster', 'vector',
+ 'overlay', 'command',
+ 'shaded', 'rgb', 'his', 'rastarrow', 'rastnum','maplegend',
+ 'thememap', 'themechart', 'grid', 'labels',
+ 'geodesic','rhumb'):
+ raise GException(_("Unsupported map layer type '%s'") % type)
+
+ self.type = type
+
+ def SetName(self, name):
+ """!Set layer name"""
+ self.name = name
+
+ def SetActive(self, enable = True):
+ """!Active or deactive layer"""
+ self.active = bool(enable)
+
+ def SetHidden(self, enable = False):
+ """!Hide or show map layer in Layer Manager"""
+ self.hidden = bool(enable)
+
+ def SetOpacity(self, value):
+ """!Set opacity value"""
+ if value < 0:
+ value = 0.
+ elif value > 1:
+ value = 1.
+
+ self.opacity = float(value)
+
+ def SetCmd(self, cmd):
+ """!Set new command for layer"""
+ if self.type == 'command':
+ self.cmd = []
+ for c in cmd:
+ self.cmd.append(utils.CmdToTuple(c))
+ else:
+ self.cmd = utils.CmdToTuple(cmd)
+ Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string = True))
+
+ # for re-rendering
+ self.force_render = True
+
+class MapLayer(Layer):
+ def __init__(self, type, cmd, name = None,
+ active = True, hidden = False, opacity = 1.0):
+ """!Represents map layer in the map canvas
+
+ @param type layer type ('raster', 'vector', 'command', etc.)
+ @param cmd GRASS command to render layer,
+ given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+ @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree) or None
+ @param active layer is active, will be rendered only if True
+ @param hidden layer is hidden, won't be listed in Layer Manager if True
+ @param opacity layer opacity <0;1>
+ """
+ Layer.__init__(self, type, cmd, name,
+ active, hidden, opacity)
+
+ def GetMapset(self):
+ """!Get mapset of map layer
+
+ @return mapset name
+ @return '' on error (no name given)
+ """
+ if not self.name:
+ return ''
+
+ try:
+ return self.name.split('@')[1]
+ except IndexError:
+ return self.name
+
+class Overlay(Layer):
+ def __init__(self, id, type, cmd,
+ active = True, hidden = True, opacity = 1.0):
+ """!Represents overlay displayed in map canvas
+
+ @param id overlay id (for PseudoDC)
+ @param type overlay type ('barscale', 'legend', etc.)
+ @param cmd GRASS command to render overlay,
+ given as list, e.g. ['d.legend', 'map=elevation at PERMANENT']
+ @param active layer is active, will be rendered only if True
+ @param hidden layer is hidden, won't be listed in Layer Manager if True
+ @param opacity layer opacity <0;1>
+ """
+ Layer.__init__(self, 'overlay', cmd, type,
+ active, hidden, opacity)
+
+ self.id = id
+
+class Map(object):
+ def __init__(self, gisrc = None, cmdfile = None, mapfile = None, envfile = None, monitor = None):
+ """!Map composition (stack of map layers and overlays)
+
+ @param gisrc alternative gisrc (used eg. by georectifier)
+ @param cmdline full path to the cmd file (defined by d.mon)
+ @param mapfile full path to the map file (defined by d.mon)
+ @param envfile full path to the env file (defined by d.mon)
+ @param monitor name of monitor (defined by d.mon)
+ """
+ # region/extent settigns
+ self.wind = dict() # WIND settings (wind file)
+ self.region = dict() # region settings (g.region)
+ self.width = 640 # map width
+ self.height = 480 # map height
+
+ # list of layers
+ self.layers = list() # stack of available GRASS layer
+
+ self.overlays = list() # stack of available overlays
+ self.ovlookup = dict() # lookup dictionary for overlay items and overlays
+
+ # environment settings
+ self.env = dict()
+ # path to external gisrc
+ self.gisrc = gisrc
+
+ self.cmdfile = cmdfile
+ self.envfile = envfile
+ self.monitor = monitor
+
+ if mapfile:
+ self.mapfileCmd = mapfile
+ self.maskfileCmd = os.path.splitext(mapfile)[0] + '.pgm'
+
+ # generated file for g.pnmcomp output for rendering the map
+ self.mapfile = grass.tempfile(create = False) + '.ppm'
+
+ # setting some initial env. variables
+ self._initGisEnv() # g.gisenv
+ self.GetWindow()
+ # GRASS environment variable (for rendering)
+ env = {"GRASS_BACKGROUNDCOLOR" : "FFFFFF",
+ "GRASS_COMPRESSION" : "0",
+ "GRASS_TRUECOLOR" : "TRUE",
+ "GRASS_TRANSPARENT" : "TRUE",
+ "GRASS_PNG_READ" : "FALSE",
+ }
+
+ self._writeEnvFile(env)
+ self._writeEnvFile({"GRASS_PNG_READ" : "TRUE"})
+ for k, v in env.iteritems():
+ os.environ[k] = v
+
+ # projection info
+ self.projinfo = self._projInfo()
+
+ def _runCommand(self, cmd, **kwargs):
+ """!Run command in environment defined by self.gisrc if
+ defined"""
+ # use external gisrc if defined
+ gisrc_orig = os.getenv("GISRC")
+ if self.gisrc:
+ os.environ["GISRC"] = self.gisrc
+
+ ret = cmd(**kwargs)
+
+ # back to original gisrc
+ if self.gisrc:
+ os.environ["GISRC"] = gisrc_orig
+
+ return ret
+
+ def _initGisEnv(self):
+ """!Stores GRASS variables (g.gisenv) to self.env variable
+ """
+ if not os.getenv("GISBASE"):
+ sys.exit(_("GISBASE not set. You must be in GRASS GIS to run this program."))
+
+ self.env = self._runCommand(grass.gisenv)
+
+ def GetProjInfo(self):
+ """!Get projection info"""
+ return self.projinfo
+
+ def _projInfo(self):
+ """!Return region projection and map units information
+ """
+ projinfo = dict()
+ if not grass.find_program('g.proj', ['--help']):
+ sys.exit(_("GRASS module '%s' not found. Unable to start map "
+ "display window.") % 'g.proj')
+
+ ret = self._runCommand(RunCommand, prog = 'g.proj',
+ read = True, flags = 'p')
+
+ if not ret:
+ return projinfo
+
+ for line in ret.splitlines():
+ if ':' in line:
+ key, val = map(lambda x: x.strip(), line.split(':'))
+ if key in ['units']:
+ val = val.lower()
+ projinfo[key] = val
+ elif "XY location (unprojected)" in line:
+ projinfo['proj'] = 'xy'
+ projinfo['units'] = ''
+ break
+
+ return projinfo
+
+ def GetWindow(self):
+ """!Read WIND file and set up self.wind dictionary"""
+ # FIXME: duplicated region WIND == g.region (at least some values)
+ filename = os.path.join (self.env['GISDBASE'],
+ self.env['LOCATION_NAME'],
+ self.env['MAPSET'],
+ "WIND")
+ try:
+ windfile = open (filename, "r")
+ except IOError, e:
+ sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \
+ { 'file' : filename, 'ret' : e})
+
+ for line in windfile.readlines():
+ line = line.strip()
+ key, value = line.split(":", 1)
+ self.wind[key.strip()] = value.strip()
+
+ windfile.close()
+
+ return self.wind
+
+ def AdjustRegion(self):
+ """!Adjusts display resolution to match monitor size in
+ pixels. Maintains constant display resolution, not related to
+ computational region. Do NOT use the display resolution to set
+ computational resolution. Set computational resolution through
+ g.region.
+ """
+ mapwidth = abs(self.region["e"] - self.region["w"])
+ mapheight = abs(self.region['n'] - self.region['s'])
+
+ self.region["nsres"] = mapheight / self.height
+ self.region["ewres"] = mapwidth / self.width
+ self.region['rows'] = round(mapheight / self.region["nsres"])
+ self.region['cols'] = round(mapwidth / self.region["ewres"])
+ self.region['cells'] = self.region['rows'] * self.region['cols']
+
+ Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)
+
+ return self.region
+
+ def AlignResolution(self):
+ """!Sets display extents to even multiple of current
+ resolution defined in WIND file from SW corner. This must be
+ done manually as using the -a flag can produce incorrect
+ extents.
+ """
+ # new values to use for saving to region file
+ new = {}
+ n = s = e = w = 0.0
+ nwres = ewres = 0.0
+
+ # Get current values for region and display
+ reg = self.GetRegion()
+ nsres = reg['nsres']
+ ewres = reg['ewres']
+
+ n = float(self.region['n'])
+ s = float(self.region['s'])
+ e = float(self.region['e'])
+ w = float(self.region['w'])
+
+ # Calculate rows, columns, and extents
+ new['rows'] = math.fabs(round((n-s)/nsres))
+ new['cols'] = math.fabs(round((e-w)/ewres))
+
+ # Calculate new extents
+ new['s'] = nsres * round(s / nsres)
+ new['w'] = ewres * round(w / ewres)
+ new['n'] = new['s'] + (new['rows'] * nsres)
+ new['e'] = new['w'] + (new['cols'] * ewres)
+
+ return new
+
+ def AlignExtentFromDisplay(self):
+ """!Align region extent based on display size from center
+ point"""
+ # calculate new bounding box based on center of display
+ if self.region["ewres"] > self.region["nsres"]:
+ res = self.region["ewres"]
+ else:
+ res = self.region["nsres"]
+
+ Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
+ (self.width, self.height, res, self.region['center_easting'],
+ self.region['center_northing']))
+
+ ew = (self.width / 2) * res
+ ns = (self.height / 2) * res
+
+ self.region['n'] = self.region['center_northing'] + ns
+ self.region['s'] = self.region['center_northing'] - ns
+ self.region['e'] = self.region['center_easting'] + ew
+ self.region['w'] = self.region['center_easting'] - ew
+
+ # LL locations
+ if self.projinfo['proj'] == 'll':
+ self.region['n'] = min(self.region['n'], 90.0)
+ self.region['s'] = max(self.region['s'], -90.0)
+
+ def _writeEnvFile(self, data):
+ """!Write display-related variable to the file (used for
+ standalone app)
+ """
+ if not self.envfile:
+ return
+
+ try:
+ fd = open(self.envfile, "r")
+ for line in fd.readlines():
+ key, value = line.split('=')
+ if key not in data.keys():
+ data[key] = value
+ fd.close()
+
+ fd = open(self.envfile, "w")
+ for k, v in data.iteritems():
+ fd.write('%s=%s\n' % (k.strip(), str(v).strip()))
+ except IOError, e:
+ grass.warning(_("Unable to open file '%(file)s' for writting. Details: %(det)s") % \
+ { 'cmd' : self.envfile, 'det' : e })
+ return
+
+ fd.close()
+
+ def ChangeMapSize(self, (width, height)):
+ """!Change size of rendered map.
+
+ @param width,height map size
+ """
+ try:
+ self.width = int(width)
+ self.height = int(height)
+ except:
+ self.width = 640
+ self.height = 480
+
+ Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
+ (self.width, self.height))
+ self._writeEnvFile({'GRASS_WIDTH' : self.width,
+ 'GRASS_HEIGHT' : self.height})
+
+ def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
+ n = None, s = None, e = None, w = None, default = False,
+ update = False):
+ """!Get region settings (g.region -upgc)
+
+ Optionally extent, raster or vector map layer can be given.
+
+ @param rast list of raster maps
+ @param zoom zoom to raster map (ignore NULLs)
+ @param vect list of vector maps
+ @param regionName named region or None
+ @param n,s,e,w force extent
+ @param default force default region settings
+ @param update if True update current display region settings
+
+ @return region settings as directory, e.g. {
+ 'n':'4928010', 's':'4913700', 'w':'589980',...}
+
+ @see GetCurrentRegion()
+ """
+ region = {}
+
+ tmpreg = os.getenv("GRASS_REGION")
+ if tmpreg:
+ del os.environ["GRASS_REGION"]
+
+ # use external gisrc if defined
+ gisrc_orig = os.getenv("GISRC")
+ if self.gisrc:
+ os.environ["GISRC"] = self.gisrc
+
+ # do not update & shell style output
+ cmd = {}
+ cmd['flags'] = 'ugpc'
+
+ if default:
+ cmd['flags'] += 'd'
+
+ if regionName:
+ cmd['region'] = regionName
+
+ if n:
+ cmd['n'] = n
+ if s:
+ cmd['s'] = s
+ if e:
+ cmd['e'] = e
+ if w:
+ cmd['w'] = w
+
+ if rast:
+ if zoom:
+ cmd['zoom'] = rast[0]
+ else:
+ cmd['rast'] = ','.join(rast)
+
+ if vect:
+ cmd['vect'] = ','.join(vect)
+
+ ret, reg, msg = RunCommand('g.region',
+ read = True,
+ getErrorMsg = True,
+ **cmd)
+
+ if ret != 0:
+ if rast:
+ message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
+ "\n\n" + _("Details:") + " %s" % msg
+ elif vect:
+ message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
+ "\n\n" + _("Details:") + " %s" % msg
+ else:
+ message = _("Unable to get current geographic extent. "
+ "Force quiting wxGUI. Please manually run g.region to "
+ "fix the problem.")
+ GError(message)
+ return self.region
+
+ for r in reg.splitlines():
+ key, val = r.split("=", 1)
+ try:
+ region[key] = float(val)
+ except ValueError:
+ region[key] = val
+
+ # back to original gisrc
+ if self.gisrc:
+ os.environ["GISRC"] = gisrc_orig
+
+ # restore region
+ if tmpreg:
+ os.environ["GRASS_REGION"] = tmpreg
+
+ Debug.msg (3, "Map.GetRegion(): %s" % region)
+
+ if update:
+ self.region = region
+
+ return region
+
+ def GetCurrentRegion(self):
+ """!Get current display region settings
+
+ @see GetRegion()
+ """
+ return self.region
+
+ def SetRegion(self, windres = False):
+ """!Render string for GRASS_REGION env. variable, so that the
+ images will be rendered from desired zoom level.
+
+ @param windres uses resolution from WIND file rather than
+ display (for modules that require set resolution like
+ d.rast.num)
+
+ @return String usable for GRASS_REGION variable or None
+ """
+ grass_region = ""
+
+ if windres:
+ compRegion = self.GetRegion()
+ region = copy.copy(self.region)
+ for key in ('nsres', 'ewres', 'cells'):
+ region[key] = compRegion[key]
+ else:
+ # adjust region settings to match monitor
+ region = self.AdjustRegion()
+
+ # read values from wind file
+ try:
+ for key in self.wind.keys():
+ if key == 'north':
+ grass_region += "north: %s; " % \
+ (region['n'])
+ continue
+ elif key == "south":
+ grass_region += "south: %s; " % \
+ (region['s'])
+ continue
+ elif key == "east":
+ grass_region += "east: %s; " % \
+ (region['e'])
+ continue
+ elif key == "west":
+ grass_region += "west: %s; " % \
+ (region['w'])
+ continue
+ elif key == "e-w resol":
+ grass_region += "e-w resol: %f; " % \
+ (region['ewres'])
+ continue
+ elif key == "n-s resol":
+ grass_region += "n-s resol: %f; " % \
+ (region['nsres'])
+ continue
+ elif key == "cols":
+ if windres:
+ continue
+ grass_region += 'cols: %d; ' % \
+ region['cols']
+ continue
+ elif key == "rows":
+ if windres:
+ continue
+ grass_region += 'rows: %d; ' % \
+ region['rows']
+ continue
+ else:
+ grass_region += key + ": " + self.wind[key] + "; "
+
+ Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
+
+ return grass_region
+
+ except:
+ return None
+
+ def GetListOfLayers(self, l_type = None, l_mapset = None, l_name = None,
+ l_active = None, l_hidden = None):
+ """!Returns list of layers of selected properties or list of
+ all layers.
+
+ @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
+ @param l_mapset all layers from given mapset (only for maplayers)
+ @param l_name all layers with given name
+ @param l_active only layers with 'active' attribute set to True or False
+ @param l_hidden only layers with 'hidden' attribute set to True or False
+
+ @return list of selected layers
+ """
+ selected = []
+
+ if type(l_type) == types.StringType:
+ one_type = True
+ else:
+ one_type = False
+
+ if one_type and l_type == 'overlay':
+ llist = self.overlays
+ else:
+ llist = self.layers
+
+ # ["raster", "vector", "wms", ... ]
+ for layer in llist:
+ # specified type only
+ if l_type != None:
+ if one_type and layer.type != l_type:
+ continue
+ elif not one_type and layer.type not in l_type:
+ continue
+
+ # mapset
+ if (l_mapset != None and l_type != 'overlay') and \
+ layer.GetMapset() != l_mapset:
+ continue
+
+ # name
+ if l_name != None and layer.name != l_name:
+ continue
+
+ # hidden and active layers
+ if l_active != None and \
+ l_hidden != None:
+ if layer.active == l_active and \
+ layer.hidden == l_hidden:
+ selected.append(layer)
+
+ # active layers
+ elif l_active != None:
+ if layer.active == l_active:
+ selected.append(layer)
+
+ # hidden layers
+ elif l_hidden != None:
+ if layer.hidden == l_hidden:
+ selected.append(layer)
+
+ # all layers
+ else:
+ selected.append(layer)
+
+ Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
+
+ return selected
+
+ def _renderLayers(self, force = False, mapWindow = None, overlaysOnly = False):
+ maps = list()
+ masks = list()
+ opacities = list()
+ # render map layers
+ ilayer = 1
+ if overlaysOnly:
+ layers = self.overlays
+ else:
+ layers = self.layers + self.overlays
+
+ for layer in layers:
+ # skip non-active map layers
+ if not layer or not layer.active:
+ continue
+
+ # render
+ if force or layer.force_render:
+ if not layer.Render():
+ continue
+
+ if mapWindow:
+ # update progress bar
+ ### wx.SafeYield(mapWindow)
+ event = wxUpdateProgressBar(value = ilayer)
+ wx.PostEvent(mapWindow, event)
+
+ # add image to compositing list
+ if layer.type != "overlay":
+ maps.append(layer.mapfile)
+ masks.append(layer.maskfile)
+ opacities.append(str(layer.opacity))
+
+ Debug.msg(3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
+ ilayer += 1
+
+ return maps, masks, opacities
+
+ def GetLayersFromCmdFile(self):
+ """!Get list of map layers from cmdfile
+ """
+ if not self.cmdfile:
+ return
+
+ nlayers = 0
+ try:
+ fd = open(self.cmdfile, 'r')
+ for line in fd.readlines():
+ cmd = utils.split(line.strip())
+ ltype = None
+ if cmd[0] == 'd.rast':
+ ltype = 'raster'
+ elif cmd[0] == 'd.vect':
+ ltype = 'vector'
+
+ name = utils.GetLayerNameFromCmd(cmd, fullyQualified = True,
+ layerType = ltype)[0]
+
+ self.AddLayer(type = ltype, command = cmd, l_active = False, name = name)
+ nlayers += 1
+ except IOError, e:
+ grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
+ { 'cmd' : self.cmdfile, 'det' : e })
+ return
+
+ fd.close()
+
+ Debug.msg(1, "Map.GetLayersFromCmdFile(): cmdfile=%s" % self.cmdfile)
+ Debug.msg(1, " nlayers=%d" % nlayers)
+
+ def _parseCmdFile(self):
+ """!Parse cmd file for standalone application
+ """
+ nlayers = 0
+ try:
+ fd = open(self.cmdfile, 'r')
+ grass.try_remove(self.mapfile)
+ cmdLines = fd.readlines()
+ RunCommand('g.gisenv',
+ set = 'MONITOR_%s_CMDFILE=' % self.monitor)
+
+ for cmd in cmdLines:
+ cmdStr = utils.split(cmd.strip())
+ cmd = utils.CmdToTuple(cmdStr)
+ RunCommand(cmd[0], **cmd[1])
+ nlayers += 1
+
+ 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 })
+ return
+
+ fd.close()
+
+ Debug.msg(1, "Map.__parseCmdFile(): cmdfile=%s" % self.cmdfile)
+ Debug.msg(1, " nlayers=%d" % nlayers)
+
+ return nlayers
+
+ def _renderCmdFile(self, force, windres):
+ if not force:
+ return ([self.mapfileCmd],
+ [self.maskfileCmd],
+ ['1.0'])
+
+ region = os.environ["GRASS_REGION"] = self.SetRegion(windres)
+ self._writeEnvFile({'GRASS_REGION' : region})
+ currMon = grass.gisenv()['MONITOR']
+ if currMon != 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:
+ 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"
+ else:
+ os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
+ self._renderLayers(overlaysOnly = True)
+ del os.environ["GRASS_RENDER_IMMEDIATE"]
+ RunCommand('g.gisenv',
+ set = 'MONITOR=%s' % currMon)
+
+ if currMon != self.monitor:
+ RunCommand('g.gisenv',
+ set = 'MONITOR=%s' % currMon)
+
+ if nlayers > 0:
+ return ([self.mapfileCmd],
+ [self.maskfileCmd],
+ ['1.0'])
+ else:
+ return ([], [], [])
+
+ def Render(self, force = False, mapWindow = None, windres = False):
+ """!Creates final image composite
+
+ This function can conditionaly use high-level tools, which
+ should be avaliable in wxPython library
+
+ @param force force rendering
+ @param reference for MapFrame instance (for progress bar)
+ @param windres use region resolution (True) otherwise display resolution
+
+ @return name of file with rendered image or None
+ """
+ wx.BeginBusyCursor()
+ # use external gisrc if defined
+ gisrc_orig = os.getenv("GISRC")
+ if self.gisrc:
+ os.environ["GISRC"] = self.gisrc
+
+ tmp_region = os.getenv("GRASS_REGION")
+ os.environ["GRASS_REGION"] = self.SetRegion(windres)
+ os.environ["GRASS_WIDTH"] = str(self.width)
+ os.environ["GRASS_HEIGHT"] = str(self.height)
+ driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
+ if driver == 'png':
+ os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
+ else:
+ os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
+
+ if self.cmdfile:
+ maps, masks, opacities = self._renderCmdFile(force, windres)
+ else:
+ maps, masks, opacities = self._renderLayers(force, mapWindow)
+
+ # ugly hack for MSYS
+ if sys.platform != 'win32':
+ mapstr = ",".join(maps)
+ maskstr = ",".join(masks)
+ mapoutstr = self.mapfile
+ else:
+ mapstr = ""
+ for item in maps:
+ mapstr += item.replace('\\', '/')
+ mapstr = mapstr.rstrip(',')
+ maskstr = ""
+ for item in masks:
+ maskstr += item.replace('\\', '/')
+ maskstr = maskstr.rstrip(',')
+ mapoutstr = self.mapfile.replace('\\', '/')
+
+ # run g.pngcomp to get composite image
+ bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
+ subkey = 'color')))
+
+ if maps:
+ 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
+ wx.EndBusyCursor()
+ return None
+
+ Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))
+
+ # back to original region
+ if tmp_region:
+ os.environ["GRASS_REGION"] = tmp_region
+ else:
+ del os.environ["GRASS_REGION"]
+
+ # back to original gisrc
+ if self.gisrc:
+ os.environ["GISRC"] = gisrc_orig
+
+ wx.EndBusyCursor()
+ if not maps:
+ return None
+
+ return self.mapfile
+
+ def AddLayer(self, type, command, name = None,
+ l_active = True, l_hidden = False, l_opacity = 1.0, l_render = False,
+ pos = -1):
+ """!Adds generic map layer to list of layers
+
+ @param type layer type ('raster', 'vector', etc.)
+ @param command GRASS command given as list
+ @param name layer name
+ @param l_active layer render only if True
+ @param l_hidden layer not displayed in layer tree if True
+ @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
+ @param l_render render an image if True
+ @param pos position in layer list (-1 for append)
+
+ @return new layer on success
+ @return None on failure
+ """
+ wx.BeginBusyCursor()
+ # l_opacity must be <0;1>
+ if l_opacity < 0:
+ l_opacity = 0
+ elif l_opacity > 1:
+ l_opacity = 1
+ layer = MapLayer(type = type, name = name, cmd = command,
+ active = l_active, hidden = l_hidden, opacity = l_opacity)
+
+ # add maplayer to the list of layers
+ if pos > -1:
+ self.layers.insert(pos, layer)
+ else:
+ self.layers.append(layer)
+
+ Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
+ if l_render:
+ if not layer.Render():
+ raise GException(_("Unable to render map layer <%s>.") % name)
+
+ wx.EndBusyCursor()
+
+ return layer
+
+ def DeleteAllLayers(self, overlay = False):
+ """!Delete all layers
+
+ @param overlay True to delete also overlayes
+ """
+ self.layers = []
+ if overlay:
+ self.overlays = []
+
+ def DeleteLayer(self, layer, overlay = False):
+ """!Removes layer from list of layers
+
+ @param layer layer instance in layer tree
+ @param overlay delete overlay (use self.DeleteOverlay() instead)
+
+ @return removed layer on success or None
+ """
+ Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
+
+ if overlay:
+ list = self.overlays
+ else:
+ list = self.layers
+
+ if layer in list:
+ if layer.mapfile:
+ base = os.path.split(layer.mapfile)[0]
+ mapfile = os.path.split(layer.mapfile)[1]
+ tempbase = mapfile.split('.')[0]
+ if base == '' or tempbase == '':
+ return None
+ basefile = os.path.join(base, tempbase) + r'.*'
+ for f in glob.glob(basefile):
+ os.remove(f)
+ list.remove(layer)
+
+ return layer
+
+ return None
+
+ def ReorderLayers(self, layerList):
+ """!Reorder list to match layer tree
+
+ @param layerList list of layers
+ """
+ self.layers = layerList
+
+ layerNameList = ""
+ for layer in self.layers:
+ if layer.name:
+ layerNameList += layer.name + ','
+ Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
+ (layerNameList))
+
+ def ChangeLayer(self, layer, render = False, **kargs):
+ """!Change map layer properties
+
+ @param layer map layer instance
+ @param type layer type ('raster', 'vector', etc.)
+ @param command GRASS command given as list
+ @param name layer name
+ @param active layer render only if True
+ @param hidden layer not displayed in layer tree if True
+ @param opacity opacity level range from 0(transparent) - 1(not transparent)
+ @param render render an image if True
+ """
+ Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
+
+ if 'type' in kargs:
+ layer.SetType(kargs['type']) # check type
+
+ if 'command' in kargs:
+ layer.SetCmd(kargs['command'])
+
+ if 'name' in kargs:
+ layer.SetName(kargs['name'])
+
+ if 'active' in kargs:
+ layer.SetActive(kargs['active'])
+
+ if 'hidden' in kargs:
+ layer.SetHidden(kargs['hidden'])
+
+ if 'opacity' in kargs:
+ layer.SetOpacity(kargs['opacity'])
+
+ if render and not layer.Render():
+ raise GException(_("Unable to render map layer <%s>.") %
+ name)
+
+ return layer
+
+ def ChangeOpacity(self, layer, l_opacity):
+ """!Changes opacity value of map layer
+
+ @param layer layer instance in layer tree
+ @param l_opacity opacity level <0;1>
+ """
+ # l_opacity must be <0;1>
+ if l_opacity < 0: l_opacity = 0
+ elif l_opacity > 1: l_opacity = 1
+
+ layer.opacity = l_opacity
+ Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
+ (layer.name, layer.opacity))
+
+ def ChangeLayerActive(self, layer, active):
+ """!Enable or disable map layer
+
+ @param layer layer instance in layer tree
+ @param active to be rendered (True)
+ """
+ layer.active = active
+
+ Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
+ (layer.name, layer.active))
+
+ def ChangeLayerName (self, layer, name):
+ """!Change name of the layer
+
+ @param layer layer instance in layer tree
+ @param name layer name to set up
+ """
+ Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
+ (layer.name, name))
+ layer.name = name
+
+ def RemoveLayer(self, name = None, id = None):
+ """!Removes layer from layer list
+
+ Layer is defined by name at mapset or id.
+
+ @param name layer name (must be unique)
+ @param id layer index in layer list
+
+ @return removed layer on success
+ @return None on failure
+ """
+ # delete by name
+ if name:
+ retlayer = None
+ for layer in self.layers:
+ if layer.name == name:
+ retlayer = layer
+ os.remove(layer.mapfile)
+ os.remove(layer.maskfile)
+ self.layers.remove(layer)
+ return layer
+ # del by id
+ elif id != None:
+ return self.layers.pop(id)
+
+ return None
+
+ def GetLayerIndex(self, layer, overlay = False):
+ """!Get index of layer in layer list.
+
+ @param layer layer instace in layer tree
+ @param overlay use list of overlays instead
+
+ @return layer index
+ @return -1 if layer not found
+ """
+ if overlay:
+ list = self.overlay
+ else:
+ list = self.layers
+
+ if layer in list:
+ return list.index(layer)
+
+ return -1
+
+ def AddOverlay(self, id, type, command,
+ l_active = True, l_hidden = True, l_opacity = 1.0, l_render = False):
+ """!Adds overlay (grid, barscale, legend, etc.) to list of
+ overlays
+
+ @param id overlay id (PseudoDC)
+ @param type overlay type (barscale, legend)
+ @param command GRASS command to render overlay
+ @param l_active overlay activated (True) or disabled (False)
+ @param l_hidden overlay is not shown in layer tree (if True)
+ @param l_render render an image (if True)
+
+ @return new layer on success
+ @retutn None on failure
+ """
+ Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
+ overlay = Overlay(id = id, type = type, cmd = command,
+ active = l_active, hidden = l_hidden, opacity = l_opacity)
+
+ # add maplayer to the list of layers
+ self.overlays.append(overlay)
+
+ if l_render and command != '' and not overlay.Render():
+ raise GException(_("Unable render overlay <%s>.") %
+ name)
+
+ return self.overlays[-1]
+
+ def ChangeOverlay(self, id, render = False, **kargs):
+ """!Change overlay properities
+
+ Add new overlay if overlay with 'id' doesn't exist.
+
+ @param id overlay id (PseudoDC)
+ @param type overlay type (barscale, legend)
+ @param command GRASS command to render overlay
+ @param l_active overlay activated (True) or disabled (False)
+ @param l_hidden overlay is not shown in layer tree (if True)
+ @param l_render render an image (if True)
+
+ @return new layer on success
+ """
+ overlay = self.GetOverlay(id, list = False)
+ if overlay is None:
+ overlay = Overlay(id, type = None, cmd = None)
+
+ if 'type' in kargs:
+ overlay.SetName(kargs['type']) # type -> overlay
+
+ if 'command' in kargs:
+ overlay.SetCmd(kargs['command'])
+
+ if 'active' in kargs:
+ overlay.SetActive(kargs['active'])
+
+ if 'hidden' in kargs:
+ overlay.SetHidden(kargs['hidden'])
+
+ if 'opacity' in kargs:
+ overlay.SetOpacity(kargs['opacity'])
+
+ if render and overlay.GetCmd() != [] and not overlay.Render():
+ raise GException(_("Unable render overlay <%s>") %
+ name)
+
+ return overlay
+
+ def GetOverlay(self, id, list = False):
+ """!Return overlay(s) with 'id'
+
+ @param id overlay id
+ @param list return list of overlays of True
+ otherwise suppose 'id' to be unique
+
+ @return list of overlays (list=True)
+ @return overlay (list=False)
+ @retur None (list=False) if no overlay or more overlays found
+ """
+ ovl = []
+ for overlay in self.overlays:
+ if overlay.id == id:
+ ovl.append(overlay)
+
+ if not list:
+ if len(ovl) != 1:
+ return None
+ else:
+ return ovl[0]
+
+ return ovl
+
+ def DeleteOverlay(self, overlay):
+ """!Delete overlay
+
+ @param overlay overlay layer
+
+ @return removed overlay on success or None
+ """
+ return self.DeleteLayer(overlay, overlay = True)
+
+ def _clean(self, llist):
+ for layer in llist:
+ if layer.maskfile:
+ grass.try_remove(layer.maskfile)
+ if layer.mapfile:
+ grass.try_remove(layer.mapfile)
+ llist.remove(layer)
+
+ def Clean(self):
+ """!Clean layer stack - go trough all layers and remove them
+ from layer list.
+
+ Removes also mapfile and maskfile.
+ """
+ self._clean(self.layers)
+ self._clean(self.overlays)
+
+ def ReverseListOfLayers(self):
+ """!Reverse list of layers"""
+ return self.layers.reverse()
+
+ def RenderOverlays(self, force):
+ """!Render overlays only (for nviz)"""
+ for layer in self.overlays:
+ if force or layer.force_render:
+ layer.Render()
+
+if __name__ == "__main__":
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ Map = Map()
+ Map.GetRegion(update = True)
+
+ Map.AddLayer(type = "raster",
+ name = "elevation",
+ command = ["d.rast", "map=elevation at PERMANENT"],
+ l_opacity = .7)
+
+ Map.AddLayer(type = "vector",
+ name = "roadsmajor",
+ command = ["d.vect", "map=roadsmajor at PERMANENT", "color=red", "width=3", "type=line"])
+
+ image = Map.Render(force = True)
+
+ if image:
+ grass.call(["display", image])
Added: grass/trunk/gui/wxpython/core/settings.py
===================================================================
--- grass/trunk/gui/wxpython/core/settings.py (rev 0)
+++ grass/trunk/gui/wxpython/core/settings.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,1046 @@
+"""!
+ at package core.settings
+
+ at brief Default GUI settings
+
+List of classes:
+ - Settings
+
+Usage:
+ at code
+from core.settings import UserSettings
+ at endcode
+
+(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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import copy
+import types
+
+from core import globalvar
+from core.gcmd import GException, GError
+from core.utils import GetSettingsPath, PathJoin
+
+class Settings:
+ """!Generic class where to store settings"""
+ def __init__(self):
+ # settings file
+ self.filePath = os.path.join(GetSettingsPath(), 'wx')
+
+ # key/value separator
+ self.sep = ';'
+
+ try:
+ projFile = PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
+ except KeyError:
+ projFile = ''
+
+ #
+ # default settings
+ #
+ self.defaultSettings = {
+ #
+ # general
+ #
+ 'general': {
+ # use default window layout (layer manager, displays, ...)
+ 'defWindowPos' : {
+ 'enabled' : True,
+ 'dim' : '0,0,%d,%d,%d,0,%d,%d' % \
+ (globalvar.GM_WINDOW_SIZE[0],
+ globalvar.GM_WINDOW_SIZE[1],
+ globalvar.GM_WINDOW_SIZE[0],
+ globalvar.MAP_WINDOW_SIZE[0],
+ globalvar.MAP_WINDOW_SIZE[1])
+ },
+ # workspace
+ 'workspace' : {
+ 'posDisplay' : {
+ 'enabled' : False
+ },
+ 'posManager' : {
+ 'enabled' : False
+ },
+ },
+ },
+ 'manager' : {
+ # show opacity level widget
+ 'changeOpacityLevel' : {
+ 'enabled' : False
+ },
+ # ask when removing layer from layer tree
+ 'askOnRemoveLayer' : {
+ 'enabled' : True
+ },
+ # ask when quiting wxGUI or closing display
+ 'askOnQuit' : {
+ 'enabled' : True
+ },
+ # hide tabs
+ 'hideTabs' : {
+ 'search' : False,
+ 'pyshell' : False,
+ },
+ 'copySelectedTextToClipboard' : {
+ 'enabled' : False
+ },
+ },
+ #
+ # appearance
+ #
+ 'appearance': {
+ 'outputfont' : {
+ 'type' : 'Courier New',
+ 'size': '10',
+ },
+ # expand/collapse element list
+ 'elementListExpand' : {
+ 'selection' : 0
+ },
+ 'menustyle' : {
+ 'selection' : 1
+ },
+ 'gSelectPopupHeight' : {
+ 'value' : 200
+ },
+ 'iconTheme' : {
+ 'type' : 'grass'
+ },
+ },
+ #
+ # display
+ #
+ 'display': {
+ 'font' : {
+ 'type' : '',
+ 'encoding': 'ISO-8859-1',
+ },
+ 'driver': {
+ 'type': 'cairo'
+ },
+ 'alignExtent' : {
+ 'enabled' : True
+ },
+ 'compResolution' : {
+ 'enabled' : False
+ },
+ 'autoRendering': {
+ 'enabled' : True
+ },
+ 'autoZooming' : {
+ 'enabled' : False
+ },
+ 'statusbarMode': {
+ 'selection' : 0
+ },
+ 'bgcolor': {
+ 'color' : (255, 255, 255, 255),
+ },
+ },
+ #
+ # projection
+ #
+ 'projection' : {
+ 'statusbar' : {
+ 'proj4' : '',
+ 'epsg' : '',
+ 'projFile' : projFile,
+ },
+ 'format' : {
+ 'll' : 'DMS',
+ 'precision' : 2,
+ },
+ },
+ #
+ # Attribute Table Manager
+ #
+ 'atm' : {
+ 'highlight' : {
+ 'color' : (255, 255, 0, 255),
+ 'width' : 2
+ },
+ 'leftDbClick' : {
+ 'selection' : 1 # draw selected
+ },
+ 'askOnDeleteRec' : {
+ 'enabled' : True
+ },
+ 'keycolumn' : {
+ 'value' : 'cat'
+ },
+ 'encoding' : {
+ 'value' : '',
+ }
+ },
+ #
+ # Command
+ #
+ 'cmd': {
+ 'overwrite' : {
+ 'enabled' : False
+ },
+ 'closeDlg' : {
+ 'enabled' : False
+ },
+ 'verbosity' : {
+ 'selection' : 'grassenv'
+ },
+ # d.rast
+ 'rasterOpaque' : {
+ 'enabled' : False
+ },
+ 'rasterColorTable' : {
+ 'enabled' : False,
+ 'selection' : 'rainbow',
+ },
+ # d.vect
+ 'showType': {
+ 'point' : {
+ 'enabled' : True
+ },
+ 'line' : {
+ 'enabled' : True
+ },
+ 'centroid' : {
+ 'enabled' : True
+ },
+ 'boundary' : {
+ 'enabled' : True
+ },
+ 'area' : {
+ 'enabled' : True
+ },
+ 'face' : {
+ 'enabled' : True
+ },
+ },
+ 'addNewLayer' : {
+ 'enabled' : True,
+ },
+ 'interactiveInput' : {
+ 'enabled' : True,
+ },
+ },
+ #
+ # vdigit
+ #
+ 'vdigit' : {
+ # symbology
+ 'symbol' : {
+ 'highlight' : {
+ 'enabled' : None,
+ 'color' : (255, 255, 0, 255)
+ }, # yellow
+ 'highlightDupl' : {
+ 'enabled' : None,
+ 'color' : (255, 72, 0, 255)
+ }, # red
+ 'point' : {
+ 'enabled' : True,
+ 'color' : (0, 0, 0, 255)
+ }, # black
+ 'line' : {
+ 'enabled' : True,
+ 'color' : (0, 0, 0, 255)
+ }, # black
+ 'boundaryNo' : {
+ 'enabled' : True,
+ 'color' : (126, 126, 126, 255)
+ }, # grey
+ 'boundaryOne' : {
+ 'enabled' : True,
+ 'color' : (0, 255, 0, 255)
+ }, # green
+ 'boundaryTwo' : {
+ 'enabled' : True,
+ 'color' : (255, 135, 0, 255)
+ }, # orange
+ 'centroidIn' : {
+ 'enabled' : True,
+ 'color' : (0, 0, 255, 255)
+ }, # blue
+ 'centroidOut' : {
+ 'enabled' : True,
+ 'color' : (165, 42, 42, 255)
+ }, # brown
+ 'centroidDup' : {
+ 'enabled' : True,
+ 'color' : (156, 62, 206, 255)
+ }, # violet
+ 'nodeOne' : {
+ 'enabled' : True,
+ 'color' : (255, 0, 0, 255)
+ }, # red
+ 'nodeTwo' : {
+ 'enabled' : True,
+ 'color' : (0, 86, 45, 255)
+ }, # dark green
+ 'vertex' : {
+ 'enabled' : False,
+ 'color' : (255, 20, 147, 255)
+ }, # deep pink
+ 'area' : {
+ 'enabled' : False,
+ 'color' : (217, 255, 217, 255)
+ }, # green
+ 'direction' : {
+ 'enabled' : False,
+ 'color' : (255, 0, 0, 255)
+ }, # red
+ },
+ # display
+ 'lineWidth' : {
+ 'value' : 2,
+ 'units' : 'screen pixels'
+ },
+ # snapping
+ 'snapping' : {
+ 'value' : 10,
+ 'units' : 'screen pixels'
+ },
+ 'snapToVertex' : {
+ 'enabled' : False
+ },
+ # digitize new record
+ 'addRecord' : {
+ 'enabled' : True
+ },
+ 'layer' :{
+ 'value' : 1
+ },
+ 'category' : {
+ 'value' : 1
+ },
+ 'categoryMode' : {
+ 'selection' : 0
+ },
+ # delete existing feature(s)
+ 'delRecord' : {
+ 'enabled' : True
+ },
+ # query tool
+ 'query' : {
+ 'selection' : 0,
+ 'box' : True
+ },
+ 'queryLength' : {
+ 'than-selection' : 0,
+ 'thresh' : 0
+ },
+ 'queryDangle' : {
+ 'than-selection' : 0,
+ 'thresh' : 0
+ },
+ # select feature (point, line, centroid, boundary)
+ 'selectType': {
+ 'point' : {
+ 'enabled' : True
+ },
+ 'line' : {
+ 'enabled' : True
+ },
+ 'centroid' : {
+ 'enabled' : True
+ },
+ 'boundary' : {
+ 'enabled' : True
+ },
+ },
+ 'selectThresh' : {
+ 'value' : 10,
+ 'units' : 'screen pixels'
+ },
+ 'checkForDupl' : {
+ 'enabled' : False
+ },
+ 'selectInside' : {
+ 'enabled' : False
+ },
+ # exit
+ 'saveOnExit' : {
+ 'enabled' : False,
+ },
+ # break lines on intersection
+ 'breakLines' : {
+ 'enabled' : False,
+ },
+ },
+ #
+ # plots for profiles, histograms, and scatterplots
+ #
+ 'profile': {
+ 'raster' : {
+ 'pcolor' : (0, 0, 255, 255), # line color
+ 'pwidth' : 1, # line width
+ 'pstyle' : 'solid', # line pen style
+ 'datatype' : 'cell', # raster type
+ },
+ 'font' : {
+ 'titleSize' : 12,
+ 'axisSize' : 11,
+ 'legendSize' : 10,
+ },
+ 'marker' : {
+ 'color' : (0, 0, 0, 255),
+ 'fill' : 'transparent',
+ 'size' : 2,
+ 'type' : 'triangle',
+ 'legend' : _('Segment break'),
+ },
+ 'grid' : {
+ 'color' : (200, 200, 200, 255),
+ 'enabled' : True,
+ },
+ 'x-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max': 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'y-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max': 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'legend' : {
+ 'enabled' : True
+ },
+ },
+ 'histogram': {
+ 'raster' : {
+ 'pcolor' : (0, 0, 255, 255), # line color
+ 'pwidth' : 1, # line width
+ 'pstyle' : 'solid', # line pen style
+ 'datatype' : 'cell', # raster type
+ },
+ 'font' : {
+ 'titleSize' : 12,
+ 'axisSize' : 11,
+ 'legendSize' : 10,
+ },
+ 'grid' : {
+ 'color' : (200, 200, 200, 255),
+ 'enabled' : True,
+ },
+ 'x-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max' : 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'y-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max' : 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'legend' : {
+ 'enabled' : True
+ },
+ },
+ 'scatter': {
+ 'rasters' : {
+ 'pcolor' : (0, 0, 255, 255),
+ 'pfill' : 'solid',
+ 'psize' : 1,
+ 'ptype' : 'dot',
+ 'plegend' : _('Data point'),
+ 0 : {'datatype' : 'CELL'},
+ 1 : {'datatype' : 'CELL'},
+ },
+ 'font' : {
+ 'titleSize' : 12,
+ 'axisSize' : 11,
+ 'legendSize' : 10,
+ },
+ 'grid' : {
+ 'color' : (200, 200, 200, 255),
+ 'enabled' : True,
+ },
+ 'x-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max' : 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'y-axis' : {
+ 'type' : 'auto', # axis format
+ 'min' : 0, # axis min for custom axis range
+ 'max' : 0, # axis max for custom axis range
+ 'log' : False,
+ },
+ 'legend' : {
+ 'enabled' : True
+ },
+ },
+ 'gcpman' : {
+ 'rms' : {
+ 'highestonly' : True,
+ 'sdfactor' : 1,
+ },
+ 'symbol' : {
+ 'color' : (0, 0, 255, 255),
+ 'hcolor' : (255, 0, 0, 255),
+ 'scolor' : (0, 255, 0, 255),
+ 'ucolor' : (255, 165, 0, 255),
+ 'unused' : True,
+ 'size' : 8,
+ 'width' : 2,
+ },
+ },
+ 'nviz' : {
+ 'view' : {
+ 'persp' : {
+ 'value' : 20,
+ 'step' : 2,
+ },
+ 'position' : {
+ 'x' : 0.84,
+ 'y' : 0.16,
+ },
+ 'twist' : {
+ 'value' : 0,
+ },
+ 'z-exag' : {
+ 'min' : 0,
+ 'max' : 10,
+ 'value': 1,
+ },
+ 'background' : {
+ 'color' : (255, 255, 255, 255), # white
+ },
+ },
+ 'fly' : {
+ 'exag' : {
+ 'move' : 5,
+ 'turn' : 5,
+ }
+ },
+ 'animation' : {
+ 'fps' : 24,
+ 'prefix' : _("animation")
+ },
+ 'surface' : {
+ 'shine': {
+ 'map' : False,
+ 'value' : 60.0,
+ },
+ 'color' : {
+ 'map' : True,
+ 'value' : (100, 100, 100, 255), # constant: grey
+ },
+ 'draw' : {
+ 'wire-color' : (136, 136, 136, 255),
+ 'mode' : 1, # fine
+ 'style' : 1, # surface
+ 'shading' : 1, # gouraud
+ 'res-fine' : 6,
+ 'res-coarse' : 9,
+ },
+ 'position' : {
+ 'x' : 0,
+ 'y' : 0,
+ 'z' : 0,
+ },
+ },
+ 'constant' : {
+ 'color' : (100, 100, 100, 255),
+ 'value' : 0.0,
+ 'transp' : 0,
+ 'resolution': 6
+ },
+ 'vector' : {
+ 'lines' : {
+ 'show' : False,
+ 'width' : 2,
+ 'color' : (0, 0, 255, 255), # blue
+ 'flat' : False,
+ 'height' : 0,
+ 'rgbcolumn': None,
+ 'sizecolumn': None,
+ },
+ 'points' : {
+ 'show' : False,
+ 'size' : 100,
+ 'width' : 2,
+ 'marker' : 2,
+ 'color' : (0, 0, 255, 255), # blue
+ 'height' : 0,
+ 'rgbcolumn': None,
+ 'sizecolumn': None,
+ }
+ },
+ 'volume' : {
+ 'color' : {
+ 'map' : True,
+ 'value' : (100, 100, 100, 255), # constant: grey
+ },
+ 'draw' : {
+ 'mode' : 0, # isosurfaces
+ 'shading' : 1, # gouraud
+ 'resolution' : 3, # polygon resolution
+ },
+ 'shine': {
+ 'map' : False,
+ 'value' : 60,
+ },
+ 'topo': {
+ 'map' : None,
+ 'value' : 0.0
+ },
+ 'transp': {
+ 'map' : None,
+ 'value': 0
+ },
+ 'mask': {
+ 'map' : None,
+ 'value': ''
+ },
+ 'slice_position': {
+ 'x1' : 0,
+ 'x2' : 1,
+ 'y1' : 0,
+ 'y2' : 1,
+ 'z1' : 0,
+ 'z2' : 1,
+ 'axis' : 0,
+ }
+ },
+ 'cplane' : {
+ 'shading': 4,
+ 'rotation':{
+ 'rot': 0,
+ 'tilt': 0
+ },
+ 'position':{
+ 'x' : 0,
+ 'y' : 0,
+ 'z' : 0
+ }
+ },
+ 'light' : {
+ 'position' : {
+ 'x' : 0.68,
+ 'y' : -0.68,
+ 'z' : 80,
+ },
+ 'bright' : 80,
+ 'color' : (255, 255, 255, 255), # white
+ 'ambient' : 20,
+ },
+ 'fringe' : {
+ 'elev' : 55,
+ 'color' : (128, 128, 128, 255), # grey
+ },
+ 'arrow': {
+ 'color': (0, 0, 0),
+ },
+ 'scalebar': {
+ 'color': (0, 0, 0),
+ }
+ },
+ 'modeler' : {
+ 'disabled': {
+ 'color': (211, 211, 211, 255), # light grey
+ },
+ 'action' : {
+ 'color' : {
+ 'valid' : (180, 234, 154, 255), # light green
+ 'invalid' : (255, 255, 255, 255), # white
+ 'running' : (255, 0, 0, 255), # red
+ },
+ 'size' : {
+ 'width' : 100,
+ 'height' : 50,
+ },
+ 'width': {
+ 'parameterized' : 2,
+ 'default' : 1,
+ },
+ },
+ 'data' : {
+ 'color': {
+ 'raster' : (215, 215, 248, 255), # light blue
+ 'raster3d' : (215, 248, 215, 255), # light green
+ 'vector' : (248, 215, 215, 255), # light red
+ },
+ 'size' : {
+ 'width' : 175,
+ 'height' : 50,
+ },
+ },
+ 'loop' : {
+ 'color' : {
+ 'valid' : (234, 226, 154, 255), # light yellow
+ },
+ 'size' : {
+ 'width' : 175,
+ 'height' : 40,
+ },
+ },
+ 'if-else' : {
+ 'size' : {
+ 'width' : 150,
+ 'height' : 40,
+ },
+ },
+ },
+ }
+
+ # quick fix, http://trac.osgeo.org/grass/ticket/1233
+ # TODO
+ if sys.platform == 'darwin':
+ self.defaultSettings['general']['defWindowPos']['enabled'] = False
+
+ #
+ # user settings
+ #
+ self.userSettings = copy.deepcopy(self.defaultSettings)
+ try:
+ self.ReadSettingsFile()
+ except GException, e:
+ print >> sys.stderr, e.value
+
+ #
+ # internal settings (based on user settings)
+ #
+ self.internalSettings = {}
+ for group in self.userSettings.keys():
+ self.internalSettings[group] = {}
+ for key in self.userSettings[group].keys():
+ self.internalSettings[group][key] = {}
+
+ # self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath()
+ self.internalSettings['appearance']['elementListExpand']['choices'] = \
+ (_("Collapse all except PERMANENT and current"),
+ _("Collapse all except PERMANENT"),
+ _("Collapse all except current"),
+ _("Collapse all"),
+ _("Expand all"))
+ self.internalSettings['atm']['leftDbClick']['choices'] = (_('Edit selected record'),
+ _('Display selected'))
+
+ self.internalSettings['cmd']['verbosity']['choices'] = ('grassenv',
+ 'verbose',
+ 'quiet')
+
+ self.internalSettings['appearance']['iconTheme']['choices'] = ('grass',)
+ self.internalSettings['appearance']['menustyle']['choices'] = \
+ (_("Classic (labels only)"),
+ _("Combined (labels and module names)"),
+ _("Professional (module names only)"))
+ self.internalSettings['appearance']['gSelectPopupHeight']['min'] = 50
+ # there is also maxHeight given to TreeCtrlComboPopup.GetAdjustedSize
+ self.internalSettings['appearance']['gSelectPopupHeight']['max'] = 1000
+
+ self.internalSettings['display']['driver']['choices'] = ['cairo', 'png']
+ self.internalSettings['display']['statusbarMode']['choices'] = None # set during MapFrame init
+
+ self.internalSettings['nviz']['view'] = {}
+ self.internalSettings['nviz']['view']['twist'] = {}
+ self.internalSettings['nviz']['view']['twist']['min'] = -180
+ self.internalSettings['nviz']['view']['twist']['max'] = 180
+ self.internalSettings['nviz']['view']['persp'] = {}
+ self.internalSettings['nviz']['view']['persp']['min'] = 1
+ self.internalSettings['nviz']['view']['persp']['max'] = 100
+ self.internalSettings['nviz']['view']['height'] = {}
+ self.internalSettings['nviz']['view']['height']['value'] = -1
+ self.internalSettings['nviz']['view']['z-exag'] = {}
+ self.internalSettings['nviz']['view']['z-exag']['original'] = 1
+ self.internalSettings['nviz']['view']['rotation'] = None
+ self.internalSettings['nviz']['view']['focus'] = {}
+ self.internalSettings['nviz']['view']['focus']['x'] = -1
+ self.internalSettings['nviz']['view']['focus']['y'] = -1
+ self.internalSettings['nviz']['view']['focus']['z'] = -1
+ self.internalSettings['nviz']['view']['dir'] = {}
+ self.internalSettings['nviz']['view']['dir']['x'] = -1
+ self.internalSettings['nviz']['view']['dir']['y'] = -1
+ self.internalSettings['nviz']['view']['dir']['z'] = -1
+ self.internalSettings['nviz']['view']['dir']['use'] = False
+
+ for decor in ('arrow', 'scalebar'):
+ self.internalSettings['nviz'][decor] = {}
+ self.internalSettings['nviz'][decor]['position'] = {}
+ self.internalSettings['nviz'][decor]['position']['x'] = 0
+ self.internalSettings['nviz'][decor]['position']['y'] = 0
+ self.internalSettings['nviz'][decor]['size'] = 100
+ self.internalSettings['nviz']['vector'] = {}
+ self.internalSettings['nviz']['vector']['points'] = {}
+ self.internalSettings['nviz']['vector']['points']['marker'] = ("x",
+ _("box"),
+ _("sphere"),
+ _("cube"),
+ _("diamond"),
+ _("dtree"),
+ _("ctree"),
+ _("aster"),
+ _("gyro"),
+ _("histogram"))
+ self.internalSettings['vdigit']['bgmap'] = {}
+ self.internalSettings['vdigit']['bgmap']['value'] = ''
+
+ def ReadSettingsFile(self, settings = None):
+ """!Reads settings file (mapset, location, gisdbase)"""
+ if settings is None:
+ settings = self.userSettings
+
+ self._readFile(self.filePath, settings)
+
+ # set environment variables
+ font = self.Get(group = 'display', key = 'font', subkey = 'type')
+ enc = self.Get(group = 'display', key = 'font', subkey = 'encoding')
+ if font:
+ os.environ["GRASS_FONT"] = font
+ if enc:
+ os.environ["GRASS_ENCODING"] = enc
+
+ def _readFile(self, filename, settings = None):
+ """!Read settings from file to dict
+
+ @param filename settings file path
+ @param settings dict where to store settings (None for self.userSettings)
+ """
+ if settings is None:
+ settings = self.userSettings
+
+ if not os.path.exists(filename):
+ return
+
+ try:
+ fd = open(filename, "r")
+ except IOError:
+ sys.stderr.write(_("Unable to read settings file <%s>\n") % filename)
+ return
+
+ try:
+ line = ''
+ for line in fd.readlines():
+ line = line.rstrip('%s' % os.linesep)
+ group, key = line.split(self.sep)[0:2]
+ kv = line.split(self.sep)[2:]
+ subkeyMaster = None
+ if len(kv) % 2 != 0: # multiple (e.g. nviz)
+ subkeyMaster = kv[0]
+ del kv[0]
+ idx = 0
+ while idx < len(kv):
+ if subkeyMaster:
+ subkey = [subkeyMaster, kv[idx]]
+ else:
+ subkey = kv[idx]
+ value = kv[idx+1]
+ value = self._parseValue(value, read = True)
+ self.Append(settings, group, key, subkey, value)
+ idx += 2
+ except ValueError, e:
+ print >> sys.stderr, _("Error: Reading settings from file <%(file)s> failed.\n"
+ "\t\tDetails: %(detail)s\n"
+ "\t\tLine: '%(line)s'\n") % { 'file' : filename,
+ 'detail' : e,
+ 'line' : line }
+ fd.close()
+
+ fd.close()
+
+ def SaveToFile(self, settings = None):
+ """!Save settings to the file"""
+ if settings is None:
+ settings = self.userSettings
+
+ dirPath = GetSettingsPath()
+ if not os.path.exists(dirPath):
+ try:
+ os.mkdir(dirPath)
+ except:
+ GError(_('Unable to create settings directory'))
+ return
+
+ try:
+ file = open(self.filePath, "w")
+ for group in settings.keys():
+ for key in settings[group].keys():
+ subkeys = settings[group][key].keys()
+ file.write('%s%s%s%s' % (group, self.sep, key, self.sep))
+ for idx in range(len(subkeys)):
+ value = settings[group][key][subkeys[idx]]
+ if type(value) == types.DictType:
+ if idx > 0:
+ file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
+ file.write('%s%s' % (subkeys[idx], self.sep))
+ kvalues = settings[group][key][subkeys[idx]].keys()
+ srange = range(len(kvalues))
+ for sidx in srange:
+ svalue = self._parseValue(settings[group][key][subkeys[idx]][kvalues[sidx]])
+ file.write('%s%s%s' % (kvalues[sidx], self.sep,
+ svalue))
+ if sidx < len(kvalues) - 1:
+ file.write('%s' % self.sep)
+ else:
+ if idx > 0 and \
+ type(settings[group][key][subkeys[idx - 1]]) == types.DictType:
+ file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
+ value = self._parseValue(settings[group][key][subkeys[idx]])
+ file.write('%s%s%s' % (subkeys[idx], self.sep, value))
+ if idx < len(subkeys) - 1 and \
+ type(settings[group][key][subkeys[idx + 1]]) != types.DictType:
+ file.write('%s' % self.sep)
+ file.write(os.linesep)
+ except IOError, e:
+ raise GException(e)
+ except StandardError, e:
+ raise GException(_('Writing settings to file <%(file)s> failed.'
+ '\n\nDetails: %(detail)s') % { 'file' : self.filePath,
+ 'detail' : e })
+
+ file.close()
+
+ def _parseValue(self, value, read = False):
+ """!Parse value to be store in settings file"""
+ if read: # -> read settings (cast values)
+ if value == 'True':
+ value = True
+ elif value == 'False':
+ value = False
+ elif value == 'None':
+ value = None
+ elif ':' in value: # -> color
+ try:
+ value = tuple(map(int, value.split(':')))
+ except ValueError: # -> string
+ pass
+ else:
+ try:
+ value = int(value)
+ except ValueError:
+ try:
+ value = float(value)
+ except ValueError:
+ pass
+ else: # -> write settings
+ if type(value) == type(()): # -> color
+ value = str(value[0]) + ':' +\
+ str(value[1]) + ':' + \
+ str(value[2])
+
+ return value
+
+ def Get(self, group, key = None, subkey = None, internal = False):
+ """!Get value by key/subkey
+
+ Raise KeyError if key is not found
+
+ @param group settings group
+ @param key (value, None)
+ @param subkey (value, list or None)
+ @param internal use internal settings instead
+
+ @return value
+ """
+ if internal is True:
+ settings = self.internalSettings
+ else:
+ settings = self.userSettings
+
+ try:
+ if subkey is None:
+ if key is None:
+ return settings[group]
+ else:
+ return settings[group][key]
+ else:
+ if type(subkey) == type(tuple()) or \
+ type(subkey) == type(list()):
+ return settings[group][key][subkey[0]][subkey[1]]
+ else:
+ return settings[group][key][subkey]
+
+ except KeyError:
+ print >> sys.stderr, "Settings: unable to get value '%s:%s:%s'\n" % \
+ (group, key, subkey)
+
+ def Set(self, group, value, key = None, subkey = None, internal = False):
+ """!Set value of key/subkey
+
+ Raise KeyError if group/key is not found
+
+ @param group settings group
+ @param key key (value, None)
+ @param subkey subkey (value, list or None)
+ @param value value
+ @param internal use internal settings instead
+ """
+ if internal is True:
+ settings = self.internalSettings
+ else:
+ settings = self.userSettings
+
+ try:
+ if subkey is None:
+ if key is None:
+ settings[group] = value
+ else:
+ settings[group][key] = value
+ else:
+ if type(subkey) == type(tuple()) or \
+ type(subkey) == type(list()):
+ settings[group][key][subkey[0]][subkey[1]] = value
+ else:
+ settings[group][key][subkey] = value
+ except KeyError:
+ raise GException("%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey))
+
+ def Append(self, dict, group, key, subkey, value):
+ """!Set value of key/subkey
+
+ Create group/key/subkey if not exists
+
+ @param dict settings dictionary to use
+ @param group settings group
+ @param key key
+ @param subkey subkey (value or list)
+ @param value value
+ """
+ if group not in dict:
+ dict[group] = {}
+
+ if key not in dict[group]:
+ dict[group][key] = {}
+
+ if type(subkey) == types.ListType:
+ # TODO: len(subkey) > 2
+ if subkey[0] not in dict[group][key]:
+ dict[group][key][subkey[0]] = {}
+ try:
+ dict[group][key][subkey[0]][subkey[1]] = value
+ except TypeError:
+ print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+ ' (' + group + ':' + key + ':' + subkey[0] + ':' + subkey[1] + ')'
+ else:
+ try:
+ dict[group][key][subkey] = value
+ except TypeError:
+ print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+ ' (' + group + ':' + key + ':' + subkey + ')'
+
+ def GetDefaultSettings(self):
+ """!Get default user settings"""
+ return self.defaultSettings
+
+ def Reset(self, key = None):
+ """!Reset to default settings
+
+ @key key in settings dict (None for all keys)
+ """
+ if not key:
+ self.userSettings = copy.deepcopy(self.defaultSettings)
+ else:
+ self.userSettings[key] = copy.deepcopy(self.defaultSettings[key])
+
+UserSettings = Settings()
Property changes on: grass/trunk/gui/wxpython/core/settings.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Copied: grass/trunk/gui/wxpython/core/units.py (from rev 49282, grass/trunk/gui/wxpython/gui_modules/units.py)
===================================================================
--- grass/trunk/gui/wxpython/core/units.py (rev 0)
+++ grass/trunk/gui/wxpython/core/units.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,116 @@
+"""!
+ at package core.units
+
+ at brief Units management
+
+ at todo Probably will be replaced by Python ctypes fns in the near
+future(?)
+
+Usage:
+ at code
+from core.units import Units
+ at endcode
+
+Classes:
+ - BaseUnits
+
+(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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+class BaseUnits:
+ def __init__(self):
+ self._units = dict()
+ self._units['length'] = { 0 : { 'key' : 'mu', 'label' : _('map units') },
+ 1 : { 'key' : 'me', 'label' : _('meters') },
+ 2 : { 'key' : 'km', 'label' : _('kilometers') },
+ 3 : { 'key' : 'mi', 'label' : _('miles') },
+ 4 : { 'key' : 'ft', 'label' : _('feet') } }
+
+ self._units['area'] = { 0 : { 'key' : 'mu', 'label' : _('sq map units') },
+ 1 : { 'key' : 'me', 'label' : _('sq meters') },
+ 2 : { 'key' : 'km', 'label' : _('sq kilometers') },
+ 3 : { 'key' : 'ar', 'label' : _('acres') },
+ 4 : { 'key' : 'ht', 'label' : _('hectares') } }
+
+ def GetUnitsList(self, type):
+ """!Get list of units (their labels)
+
+ @param type units type ('length' or 'area')
+
+ @return list of units labels
+ """
+ result = list()
+ try:
+ keys = self._units[type].keys()
+ keys.sort()
+ for idx in keys:
+ result.append(self._units[type][idx]['label'])
+ except KeyError:
+ pass
+
+ return result
+
+ def GetUnitsKey(self, type, index):
+ """!Get units key based on index
+
+ @param type units type ('length' or 'area')
+ @param index units index
+ """
+ return self._units[type][index]['key']
+
+ def GetUnitsIndex(self, type, key):
+ """!Get units index based on key
+
+ @param type units type ('length' or 'area')
+ @param key units key, e.g. 'me' for meters
+
+ @return index
+ """
+ for k, u in self._units[type].iteritems():
+ if u['key'] == key:
+ return k
+ return 0
+
+Units = BaseUnits()
+
+def ConvertValue(value, type, units):
+ """!Convert value from map units to given units
+
+ Inspired by vector/v.to.db/units.c
+
+ @param value value to be converted
+ @param type units type ('length', 'area')
+ @param unit destination units
+ """
+ # get map units
+ # TODO
+
+ f = 1
+ if type == 'length':
+ if units == 'me':
+ f = 1.0
+ elif units == 'km':
+ f = 1.0e-3
+ elif units == 'mi':
+ f = 6.21371192237334e-4
+ elif units == 'ft':
+ f = 3.28083989501312
+ else: # -> area
+ if units == 'me':
+ f = 1.0
+ elif units == 'km':
+ f = 1.0e-6
+ elif units == 'mi':
+ f = 3.86102158542446e-7
+ elif units == 'ft':
+ f = 10.7639104167097
+ elif units == 'ar':
+ f = 2.47105381467165e-4
+ elif units == 'ht':
+ f = 1.0e-4
+
+ return f * value
Copied: grass/trunk/gui/wxpython/core/utils.py (from rev 49282, grass/trunk/gui/wxpython/gui_modules/utils.py)
===================================================================
--- grass/trunk/gui/wxpython/core/utils.py (rev 0)
+++ grass/trunk/gui/wxpython/core/utils.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,745 @@
+"""!
+ at package core.utils
+
+ at 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Jachym Cepicky
+"""
+
+import os
+import sys
+import platform
+import string
+import glob
+import shlex
+import re
+
+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
+
+from core.gcmd import RunCommand
+from core.debug import Debug
+
+def normalize_whitespace(text):
+ """!Remove redundant whitespace from a string"""
+ return string.join(string.split(text), ' ')
+
+def split(s):
+ """!Platform spefic shlex.split"""
+ if sys.version_info >= (2, 6):
+ return shlex.split(s, posix = (sys.platform != "win32"))
+ elif sys.platform == "win32":
+ return shlex.split(s.replace('\\', r'\\'))
+ else:
+ return shlex.split(s)
+
+def GetTempfile(pref=None):
+ """!Creates GRASS temporary file using defined prefix.
+
+ @todo Fix path on MS Windows/MSYS
+
+ @param pref prefer the given path
+
+ @return Path to file name (string) or None
+ """
+ from core import cmd as gcmd
+
+ ret = RunCommand('g.tempfile',
+ read = True,
+ pid = os.getpid())
+
+ tempfile = ret.splitlines()[0].strip()
+
+ # FIXME
+ # ugly hack for MSYS (MS Windows)
+ if platform.system() == 'Windows':
+ tempfile = tempfile.replace("/", "\\")
+ try:
+ path, file = os.path.split(tempfile)
+ if pref:
+ return os.path.join(pref, file)
+ else:
+ return tempfile
+ except:
+ return None
+
+def GetLayerNameFromCmd(dcmd, fullyQualified = False, param = None,
+ layerType = None):
+ """!Get map name from GRASS command
+
+ Parameter dcmd can be modified when first parameter is not
+ defined.
+
+ @param dcmd GRASS command (given as list)
+ @param fullyQualified change map name to be fully qualified
+ @param param params directory
+ @param layerType check also layer type ('raster', 'vector', '3d-raster', ...)
+
+ @return tuple (name, found)
+ """
+ mapname = ''
+ found = True
+
+ if len(dcmd) < 1:
+ return mapname, False
+
+ if 'd.grid' == dcmd[0]:
+ mapname = 'grid'
+ elif 'd.geodesic' in dcmd[0]:
+ mapname = 'geodesic'
+ elif 'd.rhumbline' in dcmd[0]:
+ mapname = 'rhumb'
+ else:
+ params = list()
+ for idx in range(len(dcmd)):
+ try:
+ p, v = dcmd[idx].split('=', 1)
+ except ValueError:
+ continue
+
+ if p == param:
+ params = [(idx, p, v)]
+ break
+
+ if p in ('map', 'input', 'layer',
+ 'red', 'blue', 'green',
+ 'h_map', 's_map', 'i_map',
+ 'reliefmap', 'labels'):
+ params.append((idx, p, v))
+
+ if len(params) < 1:
+ if len(dcmd) > 1 and '=' not in dcmd[1]:
+ task = gtask.parse_interface(dcmd[0])
+ p = task.get_options()['params'][0].get('name', '')
+ params.append((1, p, dcmd[1]))
+ else:
+ return mapname, False
+
+ mapname = params[0][2]
+ mapset = ''
+ if fullyQualified and '@' not in mapname:
+ if layerType in ('raster', 'vector', '3d-raster', 'rgb', 'his'):
+ try:
+ if layerType in ('raster', 'rgb', 'his'):
+ findType = 'cell'
+ else:
+ findType = layerType
+ mapset = grass.find_file(mapname, element = findType)['mapset']
+ except AttributeError, e: # not found
+ return '', False
+ if not mapset:
+ found = False
+ else:
+ mapset = grass.gisenv()['MAPSET']
+
+ # update dcmd
+ for i, p, v in params:
+ if p == 'layer':
+ continue
+ dcmd[i] = p + '=' + v
+ if mapset:
+ dcmd[i] += '@' + mapset
+
+ maps = list()
+ ogr = False
+ for i, p, v in params:
+ if v.lower().rfind('@ogr') > -1:
+ ogr = True
+ if p == 'layer' and not ogr:
+ continue
+ maps.append(dcmd[i].split('=', 1)[1])
+
+ mapname = '\n'.join(maps)
+
+ return mapname, found
+
+def GetValidLayerName(name):
+ """!Make layer name SQL compliant, based on G_str_to_sql()
+
+ @todo: Better use directly GRASS Python SWIG...
+ """
+ retName = str(name).strip()
+
+ # check if name is fully qualified
+ if '@' in retName:
+ retName, mapset = retName.split('@')
+ else:
+ mapset = None
+
+ cIdx = 0
+ retNameList = list(retName)
+ for c in retNameList:
+ if not (c >= 'A' and c <= 'Z') and \
+ not (c >= 'a' and c <= 'z') and \
+ not (c >= '0' and c <= '9'):
+ retNameList[cIdx] = '_'
+ cIdx += 1
+ retName = ''.join(retNameList)
+
+ if not (retName[0] >= 'A' and retName[0] <= 'Z') and \
+ not (retName[0] >= 'a' and retName[0] <= 'z'):
+ retName = 'x' + retName[1:]
+
+ if mapset:
+ retName = retName + '@' + mapset
+
+ return retName
+
+def ListOfCatsToRange(cats):
+ """!Convert list of category number to range(s)
+
+ Used for example for d.vect cats=[range]
+
+ @param cats category list
+
+ @return category range string
+ @return '' on error
+ """
+
+ catstr = ''
+
+ try:
+ cats = map(int, cats)
+ except:
+ return catstr
+
+ i = 0
+ while i < len(cats):
+ next = 0
+ j = i + 1
+ while j < len(cats):
+ if cats[i + next] == cats[j] - 1:
+ next += 1
+ else:
+ break
+ j += 1
+
+ if next > 1:
+ catstr += '%d-%d,' % (cats[i], cats[i + next])
+ i += next + 1
+ else:
+ catstr += '%d,' % (cats[i])
+ i += 1
+
+ return catstr.strip(',')
+
+def ListOfMapsets(get = 'ordered'):
+ """!Get list of available/accessible mapsets
+
+ @param get method ('all', 'accessible', 'ordered')
+
+ @return list of mapsets
+ @return None on error
+ """
+ mapsets = []
+
+ if get == 'all' or get == 'ordered':
+ ret = RunCommand('g.mapsets',
+ read = True,
+ quiet = True,
+ flags = 'l',
+ fs = 'newline')
+
+ if ret:
+ mapsets = ret.splitlines()
+ ListSortLower(mapsets)
+ else:
+ return None
+
+ if get == 'accessible' or get == 'ordered':
+ ret = RunCommand('g.mapsets',
+ read = True,
+ quiet = True,
+ flags = 'p',
+ fs = 'newline')
+ if ret:
+ if get == 'accessible':
+ mapsets = ret.splitlines()
+ else:
+ mapsets_accessible = ret.splitlines()
+ for mapset in mapsets_accessible:
+ mapsets.remove(mapset)
+ mapsets = mapsets_accessible + mapsets
+ else:
+ return None
+
+ return mapsets
+
+def ListSortLower(list):
+ """!Sort list items (not case-sensitive)"""
+ list.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
+
+def GetVectorNumberOfLayers(parent, vector):
+ """!Get list of vector layers"""
+ layers = list()
+ if not vector:
+ return layers
+
+ fullname = grass.find_file(name = vector, element = 'vector')['fullname']
+ if not fullname:
+ Debug.msg(5, "utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
+ return layers
+
+ 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
+ else:
+ Debug.msg(1, "GetVectorNumberOfLayers(): ret %s" % ret)
+
+ for line in ret.splitlines():
+ try:
+ layer = line.split(';')[0]
+ if '/' in layer:
+ layer = layer.split('/')[0]
+ layers.append(layer)
+ except IndexError:
+ pass
+
+ Debug.msg(3, "utils.GetVectorNumberOfLayers(): vector=%s -> %s" % \
+ (fullname, ','.join(layers)))
+
+ return layers
+
+def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
+ """!Convert deg value to dms string
+
+ @param lon longitude (x)
+ @param lat latitude (y)
+ @param string True to return string otherwise tuple
+ @param hemisphere print hemisphere
+ @param precision seconds precision
+
+ @return DMS string or tuple of values
+ @return empty string on error
+ """
+ try:
+ flat = float(lat)
+ flon = float(lon)
+ except ValueError:
+ if string:
+ return ''
+ else:
+ return None
+
+ # fix longitude
+ while flon > 180.0:
+ flon -= 360.0
+ while flon < -180.0:
+ flon += 360.0
+
+ # hemisphere
+ if hemisphere:
+ if flat < 0.0:
+ flat = abs(flat)
+ hlat = 'S'
+ else:
+ hlat = 'N'
+
+ if flon < 0.0:
+ hlon = 'W'
+ flon = abs(flon)
+ else:
+ hlon = 'E'
+ else:
+ flat = abs(flat)
+ flon = abs(flon)
+ hlon = ''
+ hlat = ''
+
+ slat = __ll_parts(flat, precision = precision)
+ slon = __ll_parts(flon, precision = precision)
+
+ if string:
+ return slon + hlon + '; ' + slat + hlat
+
+ return (slon + hlon, slat + hlat)
+
+def DMS2Deg(lon, lat):
+ """!Convert dms value to deg
+
+ @param lon longitude (x)
+ @param lat latitude (y)
+
+ @return tuple of converted values
+ @return ValueError on error
+ """
+ x = __ll_parts(lon, reverse = True)
+ y = __ll_parts(lat, reverse = True)
+
+ return (x, y)
+
+def __ll_parts(value, reverse = False, precision = 3):
+ """!Converts deg to d:m:s string
+
+ @param value value to be converted
+ @param reverse True to convert from d:m:s to deg
+ @param precision seconds precision (ignored if reverse is True)
+
+ @return converted value (string/float)
+ @return ValueError on error (reverse == True)
+ """
+ if not reverse:
+ if value == 0.0:
+ return '%s%.*f' % ('00:00:0', precision, 0.0)
+
+ d = int(int(value))
+ m = int((value - d) * 60)
+ s = ((value - d) * 60 - m) * 60
+ if m < 0:
+ m = '00'
+ elif m < 10:
+ m = '0' + str(m)
+ else:
+ m = str(m)
+ if s < 0:
+ s = '00.0000'
+ elif s < 10.0:
+ s = '0%.*f' % (precision, s)
+ else:
+ s = '%.*f' % (precision, s)
+
+ return str(d) + ':' + m + ':' + s
+ else: # -> reverse
+ try:
+ d, m, s = value.split(':')
+ hs = s[-1]
+ s = s[:-1]
+ except ValueError:
+ try:
+ d, m = value.split(':')
+ hs = m[-1]
+ m = m[:-1]
+ s = '0.0'
+ except ValueError:
+ try:
+ d = value
+ hs = d[-1]
+ d = d[:-1]
+ m = '0'
+ s = '0.0'
+ except ValueError:
+ raise ValueError
+
+ if hs not in ('N', 'S', 'E', 'W'):
+ raise ValueError
+
+ coef = 1.0
+ if hs in ('S', 'W'):
+ coef = -1.0
+
+ fm = int(m) / 60.0
+ fs = float(s) / (60 * 60)
+
+ return coef * (float(d) + fm + fs)
+
+def GetCmdString(cmd):
+ """
+ Get GRASS command as string.
+
+ @param cmd GRASS command given as dictionary
+
+ @return command string
+ """
+ scmd = ''
+ if not cmd:
+ return scmd
+
+ scmd = cmd[0]
+
+ if 'flags' in cmd[1]:
+ for flag in cmd[1]['flags']:
+ scmd += ' -' + flag
+ for flag in ('verbose', 'quiet', 'overwrite'):
+ if flag in cmd[1] and cmd[1][flag] is True:
+ scmd += ' --' + flag
+
+ for k, v in cmd[1].iteritems():
+ if k in ('flags', 'verbose', 'quiet', 'overwrite'):
+ continue
+ scmd += ' %s=%s' % (k, v)
+
+ return scmd
+
+def CmdToTuple(cmd):
+ """!Convert command list to tuple for gcmd.RunCommand()"""
+ if len(cmd) < 1:
+ return None
+
+ dcmd = {}
+ for item in cmd[1:]:
+ if '=' in item: # params
+ key, value = item.split('=', 1)
+ dcmd[str(key)] = str(value).replace('"', '')
+ elif item[:2] == '--': # long flags
+ flag = item[2:]
+ if flag in ('verbose', 'quiet', 'overwrite'):
+ dcmd[str(flag)] = True
+ else: # -> flags
+ if 'flags' not in dcmd:
+ dcmd['flags'] = ''
+ dcmd['flags'] += item.replace('-', '')
+
+ return (cmd[0],
+ dcmd)
+
+def PathJoin(*args):
+ """!Check path created by os.path.join"""
+ path = os.path.join(*args)
+ if platform.system() == 'Windows' and \
+ '/' in path:
+ return path[1].upper() + ':\\' + path[3:].replace('/', '\\')
+
+ return path
+
+def ReadEpsgCodes(path):
+ """!Read EPSG code from the file
+
+ @param path full path to the file with EPSG codes
+
+ @return dictionary of EPSG code
+ @return string on error
+ """
+ epsgCodeDict = dict()
+ try:
+ try:
+ f = open(path, "r")
+ except IOError:
+ return _("failed to open '%s'" % path)
+
+ i = 0
+ code = None
+ for line in f.readlines():
+ line = line.strip()
+ if len(line) < 1:
+ continue
+
+ if line[0] == '#':
+ descr = line[1:].strip()
+ elif line[0] == '<':
+ code, params = line.split(" ", 1)
+ try:
+ code = int(code.replace('<', '').replace('>', ''))
+ except ValueError:
+ return e
+
+ if code is not None:
+ epsgCodeDict[code] = (descr, params)
+ code = None
+ i += 1
+
+ f.close()
+ except StandardError, e:
+ return e
+
+ return epsgCodeDict
+
+def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
+ """!Reproject coordinates
+
+ @param coord coordinates given as tuple
+ @param projOut output projection
+ @param projIn input projection (use location projection settings)
+
+ @return reprojected coordinates (returned as tuple)
+ """
+ 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]
+ n = coors[1]
+ try:
+ proj = projOut.split(' ')[0].split('=')[1]
+ except IndexError:
+ proj = ''
+ if proj in ('ll', 'latlong', 'longlat') and 'd' not in flags:
+ return (proj, (e, n))
+ else:
+ try:
+ return (proj, (float(e), float(n)))
+ except ValueError:
+ return (None, None)
+
+ return (None, None)
+
+def GetListOfLocations(dbase):
+ """!Get list of GRASS locations in given dbase
+
+ @param dbase GRASS database path
+
+ @return list of locations (sorted)
+ """
+ listOfLocations = list()
+
+ try:
+ for location in glob.glob(os.path.join(dbase, "*")):
+ try:
+ if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
+ listOfLocations.append(os.path.basename(location))
+ except:
+ pass
+ except UnicodeEncodeError, e:
+ raise e
+
+ ListSortLower(listOfLocations)
+
+ return listOfLocations
+
+def GetListOfMapsets(dbase, location, selectable = False):
+ """!Get list of mapsets in given GRASS location
+
+ @param dbase GRASS database path
+ @param location GRASS location
+ @param selectable True to get list of selectable mapsets, otherwise all
+
+ @return list of mapsets - sorted (PERMANENT first)
+ """
+ listOfMapsets = list()
+
+ if selectable:
+ ret = RunCommand('g.mapset',
+ read = True,
+ flags = 'l',
+ location = location,
+ gisdbase = dbase)
+
+ if not ret:
+ return listOfMapsets
+
+ for line in ret.rstrip().splitlines():
+ listOfMapsets += line.split(' ')
+ else:
+ for mapset in glob.glob(os.path.join(dbase, location, "*")):
+ if os.path.isdir(mapset) and \
+ os.path.isfile(os.path.join(dbase, location, mapset, "WIND")):
+ listOfMapsets.append(os.path.basename(mapset))
+
+ ListSortLower(listOfMapsets)
+ return listOfMapsets
+
+def GetColorTables():
+ """!Get list of color tables"""
+ ret = RunCommand('r.colors',
+ read = True,
+ flags = 'l')
+ if not ret:
+ return list()
+
+ return ret.splitlines()
+
+def _getGDALFormats():
+ """!Get dictionary of avaialble GDAL drivers"""
+ ret = grass.read_command('r.in.gdal',
+ quiet = True,
+ flags = 'f')
+
+ return _parseFormats(ret), _parseFormats(ret, writableOnly = True)
+
+def _getOGRFormats():
+ """!Get dictionary of avaialble OGR drivers"""
+ ret = grass.read_command('v.in.ogr',
+ quiet = True,
+ flags = 'f')
+
+ return _parseFormats(ret), _parseFormats(ret, writableOnly = True)
+
+def _parseFormats(output, writableOnly = False):
+ """!Parse r.in.gdal/v.in.ogr -f output"""
+ formats = { 'file' : list(),
+ 'database' : list(),
+ 'protocol' : list()
+ }
+
+ if not output:
+ return formats
+
+ patt = None
+ if writableOnly:
+ patt = re.compile('\(rw\+?\)$', re.IGNORECASE)
+
+ for line in output.splitlines():
+ key, name = map(lambda x: x.strip(), line.strip().rsplit(':', -1))
+
+ if writableOnly and not patt.search(key):
+ continue
+
+ if name in ('Memory', 'Virtual Raster', 'In Memory Raster'):
+ continue
+ if name in ('PostgreSQL', 'SQLite',
+ 'ODBC', 'ESRI Personal GeoDatabase',
+ 'Rasterlite',
+ 'PostGIS WKT Raster driver'):
+ formats['database'].append(name)
+ elif name in ('GeoJSON',
+ 'OGC Web Coverage Service',
+ 'OGC Web Map Service',
+ 'HTTP Fetching Wrapper'):
+ formats['protocol'].append(name)
+ else:
+ formats['file'].append(name)
+
+ for items in formats.itervalues():
+ items.sort()
+
+ return formats
+
+formats = None
+
+def GetFormats(writableOnly = False):
+ """!Get GDAL/OGR formats"""
+ global formats
+ if not formats:
+ gdalAll, gdalWritable = _getGDALFormats()
+ ogrAll, ogrWritable = _getOGRFormats()
+ formats = {
+ 'all' : {
+ 'gdal' : gdalAll,
+ 'ogr' : ogrAll,
+ },
+ 'writable' : {
+ 'gdal' : gdalWritable,
+ 'ogr' : ogrWritable,
+ },
+ }
+
+ if writableOnly:
+ return formats['writable']
+
+ return formats['all']
+
+def GetSettingsPath():
+ """!Get full path to the settings directory
+ """
+ try:
+ 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)
+
+ verFd.close()
+
+ # keep location of settings files rc and wx in sync with lib/init/grass.py
+ if sys.platform == 'win32':
+ return os.path.join(os.getenv('APPDATA'), 'grass%d' % version)
+
+ return os.path.join(os.getenv('HOME'), '.grass%d' % version)
Copied: grass/trunk/gui/wxpython/core/workspace.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/workspace.py)
===================================================================
--- grass/trunk/gui/wxpython/core/workspace.py (rev 0)
+++ grass/trunk/gui/wxpython/core/workspace.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,1296 @@
+"""!
+ at package core.workspace
+
+ at brief Open/save workspace definition file
+
+Classes:
+ - ProcessWorkspaceFile
+ - 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+import wx
+
+from core.utils import normalize_whitespace
+from core.settings import UserSettings
+
+class ProcessWorkspaceFile:
+ def __init__(self, tree):
+ """!A ElementTree handler for the GXW XML file, as defined in
+ grass-gxw.dtd.
+ """
+ self.tree = tree
+ self.root = self.tree.getroot()
+
+ #
+ # layer manager properties
+ #
+ self.layerManager = {}
+ self.layerManager['pos'] = None # window position
+ self.layerManager['size'] = None # window size
+
+ #
+ # list of mapdisplays
+ #
+ self.displays = []
+ #
+ # list of map layers
+ #
+ self.layers = []
+ #
+ # nviz state
+ #
+ self.nviz_state = {}
+
+ self.displayIndex = -1 # first display has index '0'
+
+ self.__processFile()
+
+ self.nvizDefault = Nviz()
+
+ def __filterValue(self, value):
+ """!Filter value
+
+ @param value
+ """
+ value = value.replace('<', '<')
+ value = value.replace('>', '>')
+
+ return value
+
+ def __getNodeText(self, node, tag, default = ''):
+ """!Get node text"""
+ p = node.find(tag)
+ if p is not None:
+ return normalize_whitespace(p.text)
+
+ return default
+
+ def __processFile(self):
+ """!Process workspace file"""
+ #
+ # layer manager
+ #
+ node_lm = self.root.find('layer_manager')
+ if node_lm is not None:
+ posAttr = node_lm.get('dim', '')
+ if posAttr:
+ posVal = map(int, posAttr.split(','))
+ try:
+ self.layerManager['pos'] = (posVal[0], posVal[1])
+ self.layerManager['size'] = (posVal[2], posVal[3])
+ except:
+ pass
+
+ #
+ # displays
+ #
+ for display in self.root.findall('display'):
+ self.displayIndex += 1
+
+ # window position and size
+ posAttr = display.get('dim', '')
+ if posAttr:
+ posVal = map(int, posAttr.split(','))
+ try:
+ pos = (posVal[0], posVal[1])
+ size = (posVal[2], posVal[3])
+ except:
+ pos = None
+ size = None
+ else:
+ pos = None
+ size = None
+
+ extentAttr = display.get('extent', '')
+ if extentAttr:
+ # w, s, e, n
+ extent = map(float, extentAttr.split(','))
+ else:
+ extent = None
+
+ # projection
+ node_projection = display.find('projection')
+ if node_projection is not None:
+ projection = { 'enabled' : True,
+ 'epsg' : node_projection.get('epsg', ''),
+ 'proj' : self.__getNodeText(node_projection, 'value') }
+ else:
+ projection = { 'enabled' : False }
+
+ self.displays.append( {
+ "render" : bool(int(display.get('render', "0"))),
+ "mode" : int(display.get('mode', 0)),
+ "showCompExtent" : bool(int(display.get('showCompExtent', "0"))),
+ "pos" : pos,
+ "size" : size,
+ "extent" : extent,
+ "alignExtent" : bool(int(display.get('alignExtent', "0"))),
+ "constrainRes" : bool(int(display.get('constrainRes', "0"))),
+ "projection" : projection,
+ "viewMode" : display.get('viewMode', '2d')} )
+
+ # process all layers/groups in the display
+ self.__processLayers(display)
+ # process nviz_state
+ self.__processNvizState(display)
+
+ def __processLayers(self, node, inGroup = -1):
+ """!Process layers/groups of selected display
+
+ @param node display tree node
+ @param inGroup in group -> index of group item otherwise -1
+ """
+ for item in node.getchildren():
+ if item.tag == 'group':
+ # -> group
+ self.layers.append( {
+ "type" : 'group',
+ "name" : item.get('name', ''),
+ "checked" : bool(int(item.get('checked', "0"))),
+ "opacity" : None,
+ "cmd" : None,
+ "group" : inGroup,
+ "display" : self.displayIndex,
+ "vdigit" : None,
+ "nviz" : None})
+
+ self.__processLayers(item, inGroup = len(self.layers) - 1) # process items in group
+
+ elif item.tag == 'layer':
+ cmd, selected, vdigit, nviz = self.__processLayer(item)
+
+ self.layers.append( {
+ "type" : item.get('type', None),
+ "name" : item.get('name', None),
+ "checked" : bool(int(item.get('checked', "0"))),
+ "opacity" : float(item.get('opacity', '1.0')),
+ "cmd" : cmd,
+ "group" : inGroup,
+ "display" : self.displayIndex,
+ "selected" : selected,
+ "vdigit" : vdigit,
+ "nviz" : nviz } )
+
+ def __processLayer(self, layer):
+ """!Process layer item
+
+ @param layer tree node
+ """
+ cmd = list()
+
+ #
+ # layer attributes (task) - 2D settings
+ #
+ node_task = layer.find('task')
+ cmd.append(node_task.get('name', "unknown"))
+
+ # flags
+ for p in node_task.findall('flag'):
+ flag = p.get('name', '')
+ if len(flag) > 1:
+ cmd.append('--' + flag)
+ else:
+ cmd.append('-' + flag)
+
+ # parameters
+ for p in node_task.findall('parameter'):
+ cmd.append('%s=%s' % (p.get('name', ''),
+ self.__filterValue(self.__getNodeText(p, 'value'))))
+
+ if layer.find('selected') is not None:
+ selected = True
+ else:
+ selected = False
+
+ #
+ # Vector digitizer settings
+ #
+ node_vdigit = layer.find('vdigit')
+ if node_vdigit is not None:
+ vdigit = self.__processLayerVdigit(node_vdigit)
+ else:
+ vdigit = None
+
+ #
+ # Nviz (3D settings)
+ #
+ node_nviz = layer.find('nviz')
+ if node_nviz is not None:
+ nviz = self.__processLayerNviz(node_nviz)
+ else:
+ nviz = None
+
+ return (cmd, selected, vdigit, nviz)
+
+ def __processLayerVdigit(self, node_vdigit):
+ """!Process vector digitizer layer settings
+
+ @param node_vdigit vdigit node
+ """
+ # init nviz layer properties
+ vdigit = dict()
+ for node in node_vdigit.findall('geometryAttribute'):
+ if 'geomAttr' not in vdigit:
+ vdigit['geomAttr'] = dict()
+ type = node.get('type')
+ vdigit['geomAttr'][type] = dict()
+ vdigit['geomAttr'][type]['column'] = node.get('column') # required
+ # default map units
+ vdigit['geomAttr'][type]['units'] = node.get('units', 'mu')
+
+ return vdigit
+
+ def __processLayerNviz(self, node_nviz):
+ """!Process 3D layer settings
+
+ @param node_nviz nviz node
+ """
+ # init nviz layer properties
+ nviz = {}
+ if node_nviz.find('surface') is not None: # -> raster
+ nviz['surface'] = {}
+ for sec in ('attribute', 'draw', 'mask', 'position'):
+ nviz['surface'][sec] = {}
+ elif node_nviz.find('vlines') is not None or \
+ node_nviz.find('vpoints') is not None: # -> vector
+ nviz['vector'] = {}
+ for sec in ('lines', 'points'):
+ nviz['vector'][sec] = {}
+
+ if 'surface' in nviz:
+ node_surface = node_nviz.find('surface')
+ # attributes
+ for attrb in node_surface.findall('attribute'):
+ tagName = str(attrb.tag)
+ attrbName = attrb.get('name', '')
+ dc = nviz['surface'][tagName][attrbName] = {}
+ if attrb.get('map', '0') == '0':
+ dc['map'] = False
+ else:
+ dc['map'] = True
+ value = self.__getNodeText(attrb, 'value')
+ try:
+ dc['value'] = int(value)
+ except ValueError:
+ try:
+ dc['value'] = float(value)
+ except ValueError:
+ dc['value'] = str(value)
+
+ # draw
+ node_draw = node_surface.find('draw')
+ if node_draw is not None:
+ tagName = str(node_draw.tag)
+ nviz['surface'][tagName]['all'] = False
+ nviz['surface'][tagName]['mode'] = {}
+ nviz['surface'][tagName]['mode']['value'] = -1 # to be calculated
+ nviz['surface'][tagName]['mode']['desc'] = {}
+ nviz['surface'][tagName]['mode']['desc']['shading'] = \
+ str(node_draw.get('shading', ''))
+ nviz['surface'][tagName]['mode']['desc']['style'] = \
+ str(node_draw.get('style', ''))
+ nviz['surface'][tagName]['mode']['desc']['mode'] = \
+ str(node_draw.get('mode', ''))
+
+ # resolution
+ for node_res in node_draw.findall('resolution'):
+ resType = str(node_res.get('type', ''))
+ if 'resolution' not in nviz['surface']['draw']:
+ nviz['surface']['draw']['resolution'] = {}
+ value = int(self.__getNodeText(node_res, 'value'))
+ nviz['surface']['draw']['resolution'][resType] = value
+
+ # wire-color
+ node_wire_color = node_draw.find('wire_color')
+ if node_wire_color is not None:
+ nviz['surface']['draw']['wire-color'] = {}
+ value = str(self.__getNodeText(node_wire_color, 'value'))
+ nviz['surface']['draw']['wire-color']['value'] = value
+
+ # position
+ node_pos = node_surface.find('position')
+ if node_pos is not None:
+ dc = nviz['surface']['position'] = {}
+ for coor in ['x', 'y', 'z']:
+ node = node_pos.find(coor)
+ if node is None:
+ continue
+ value = int(self.__getNodeText(node_pos, coor))
+ dc[coor] = value
+
+ elif 'vector' in nviz:
+ # vpoints
+ node_vpoints = node_nviz.find('vpoints')
+ if node_vpoints is not None:
+ marker = str(node_vpoints.get('marker', ''))
+ markerId = list(UserSettings.Get(group='nviz', key='vector',
+ subkey=['points', 'marker'], internal=True)).index(marker)
+ nviz['vector']['points']['marker'] = { 'value' : markerId }
+
+ node_mode = node_vpoints.find('mode')
+ if node_mode is not None:
+ nviz['vector']['points']['mode'] = {}
+ nviz['vector']['points']['mode']['type'] = str(node_mode.get('type', 'surface'))
+ nviz['vector']['points']['mode']['surface'] = {}
+ nviz['vector']['points']['mode']['surface']['value'] = []
+ nviz['vector']['points']['mode']['surface']['show'] = []
+
+ # map
+ for node_map in node_mode.findall('map'):
+ nviz['vector']['points']['mode']['surface']['value'].append(
+ self.__processLayerNvizNode(node_map, 'name', str))
+ nviz['vector']['points']['mode']['surface']['show'].append(bool(
+ self.__processLayerNvizNode(node_map, 'checked', int)))
+
+ # color
+ self.__processLayerNvizNode(node_vpoints, 'color', str,
+ nviz['vector']['points'])
+
+ # width
+ self.__processLayerNvizNode(node_vpoints, 'width', int,
+ nviz['vector']['points'])
+
+ # height
+ self.__processLayerNvizNode(node_vpoints, 'height', int,
+ nviz['vector']['points'])
+
+ # height
+ self.__processLayerNvizNode(node_vpoints, 'size', int,
+ nviz['vector']['points'])
+
+ # thematic
+ node_thematic = node_vpoints.find('thematic')
+ thematic = nviz['vector']['points']['thematic'] = {}
+ thematic['rgbcolumn'] = self.__processLayerNvizNode(node_thematic, 'rgbcolumn', str)
+ thematic['sizecolumn'] = self.__processLayerNvizNode(node_thematic, 'sizecolumn', str)
+ for col in ('rgbcolumn', 'sizecolumn'):
+ if thematic[col] == 'None':
+ thematic[col] = None
+ thematic['layer'] = self.__processLayerNvizNode(node_thematic, 'layer', int)
+ for use in ('usecolor', 'usesize', 'usewidth'):
+ if node_thematic.get(use, ''):
+ thematic[use] = int(node_thematic.get(use, '0'))
+
+ # vlines
+ node_vlines = node_nviz.find('vlines')
+ if node_vlines is not None:
+ node_mode = node_vlines.find('mode')
+ if node_mode is not None:
+ nviz['vector']['lines']['mode'] = {}
+ nviz['vector']['lines']['mode']['type'] = str(node_mode.get('type', ''))
+ nviz['vector']['lines']['mode']['surface'] = {}
+ nviz['vector']['lines']['mode']['surface']['value'] = []
+ nviz['vector']['lines']['mode']['surface']['show'] = []
+
+ # map
+ for node_map in node_mode.findall('map'):
+ nviz['vector']['lines']['mode']['surface']['value'].append(
+ self.__processLayerNvizNode(node_map, 'name', str))
+ nviz['vector']['lines']['mode']['surface']['show'].append(bool(
+ self.__processLayerNvizNode(node_map, 'checked', int)))
+
+ # color
+ self.__processLayerNvizNode(node_vlines, 'color', str,
+ nviz['vector']['lines'])
+
+ # width
+ self.__processLayerNvizNode(node_vlines, 'width', int,
+ nviz['vector']['lines'])
+
+ # height
+ self.__processLayerNvizNode(node_vlines, 'height', int,
+ nviz['vector']['lines'])
+
+ # thematic
+ node_thematic = node_vlines.find('thematic')
+ thematic = nviz['vector']['lines']['thematic'] = {}
+ thematic['rgbcolumn'] = self.__processLayerNvizNode(node_thematic, 'rgbcolumn', str)
+ thematic['sizecolumn'] = self.__processLayerNvizNode(node_thematic, 'sizecolumn', str)
+ for col in ('rgbcolumn', 'sizecolumn'):
+ if thematic[col] == 'None':
+ thematic[col] = None
+ thematic['layer'] = self.__processLayerNvizNode(node_thematic, 'layer', int)
+ for use in ('usecolor', 'usesize', 'usewidth'):
+ if node_thematic.get(use, ''):
+ thematic[use] = int(node_thematic.get(use, '0'))
+
+ return nviz
+
+ def __processLayerNvizNode(self, node, tag, cast, dc = None):
+ """!Process given tag nviz/vector"""
+ node_tag = node.find(tag)
+ if node_tag is not None:
+ if node_tag.find('value') is not None:
+ value = cast(self.__getNodeText(node_tag, 'value'))
+ else:
+ try:
+ value = cast(node_tag.text)
+ except ValueError:
+ if cast == str:
+ value = ''
+ else:
+ value = None
+ if dc:
+ dc[tag] = dict()
+ dc[tag]['value'] = value
+ else:
+ return value
+
+ def __processNvizState(self, node):
+ """!Process tag nviz_state"""
+ node_state = node.find('nviz_state')
+ if node_state is None:
+ return
+ self.nviz_state['display'] = self.displayIndex
+ #
+ # view
+ #
+ node_view = node_state.find('view')
+ view = {}
+ iview = {}
+
+ node_position = node_view.find('v_position')
+ view['position'] = {}
+ view['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+ view['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+ node_persp = node_view.find('persp')
+ view['persp'] = {}
+ iview['persp'] = {}
+ view['persp']['value'] = self.__processLayerNvizNode(node_persp, 'value', int)
+ view['persp']['step'] = self.__processLayerNvizNode(node_persp, 'step', int)
+ iview['persp']['min'] = self.__processLayerNvizNode(node_persp, 'min', int)
+ iview['persp']['max'] = self.__processLayerNvizNode(node_persp, 'max', int)
+ node_height = node_view.find('v_height')
+ iview['height'] = {}
+ iview['height']['value'] = self.__processLayerNvizNode(node_height, 'value', int)
+ iview['height']['min'] = self.__processLayerNvizNode(node_height, 'min', int)
+ iview['height']['max'] = self.__processLayerNvizNode(node_height, 'max', int)
+ node_twist = node_view.find('twist')
+ view['twist'] = {}
+ iview['twist'] = {}
+ view['twist']['value'] = self.__processLayerNvizNode(node_twist, 'value', int)
+ iview['twist']['min'] = self.__processLayerNvizNode(node_twist, 'min', int)
+ iview['twist']['max'] = self.__processLayerNvizNode(node_twist, 'max', int)
+ node_zexag = node_view.find('z-exag')
+ view['z-exag'] = {}
+ iview['z-exag'] = {}
+ view['z-exag']['value'] = self.__processLayerNvizNode(node_zexag, 'value', int)
+ view['z-exag']['min'] = self.__processLayerNvizNode(node_zexag, 'min', int)
+ view['z-exag']['max'] = self.__processLayerNvizNode(node_zexag, 'max', int)
+ iview['z-exag']['original'] = self.__processLayerNvizNode(node_zexag, 'original', float)
+ node_focus = node_view.find('focus')
+ iview['focus'] = {}
+ iview['focus']['x'] = self.__processLayerNvizNode(node_focus, 'x', int)
+ iview['focus']['y'] = self.__processLayerNvizNode(node_focus, 'y', int)
+ iview['focus']['z'] = self.__processLayerNvizNode(node_focus, 'z', int)
+ node_dir = node_view.find('dir')
+ if node_dir:
+ iview['dir'] = {}
+ iview['dir']['x'] = self.__processLayerNvizNode(node_dir, 'x', int)
+ iview['dir']['y'] = self.__processLayerNvizNode(node_dir, 'y', int)
+ iview['dir']['z'] = self.__processLayerNvizNode(node_dir, 'z', int)
+ iview['dir']['use'] = True
+ else:
+ iview['dir'] = {}
+ iview['dir']['x'] = -1
+ iview['dir']['y'] = -1
+ iview['dir']['z'] = -1
+ iview['dir']['use'] = False
+
+ view['background'] = {}
+ color = self.__processLayerNvizNode(node_view, 'background_color', str)
+ view['background']['color'] = tuple(map(int, color.split(':')))
+
+ self.nviz_state['view'] = view
+ self.nviz_state['iview'] = iview
+ #
+ # light
+ #
+ node_light = node_state.find('light')
+ light = {}
+
+ node_position = node_light.find('l_position')
+ light['position'] = {}
+ light['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+ light['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+ light['position']['z'] = self.__processLayerNvizNode(node_position, 'z', int)
+
+ light['bright'] = self.__processLayerNvizNode(node_light, 'bright', int)
+ light['ambient'] = self.__processLayerNvizNode(node_light, 'ambient', int)
+ color = self.__processLayerNvizNode(node_light, 'color', str)
+ light['color'] = tuple(map(int, color.split(':')))
+
+ self.nviz_state['light'] = light
+
+ node_constants = node_state.find('constant_planes')
+ constants = []
+ if node_constants:
+ for i, node_plane in enumerate(node_constants.findall('plane')):
+ plane = {}
+ plane['color'] = self.__processLayerNvizNode(node_plane, 'color', str)
+ plane['resolution'] = self.__processLayerNvizNode(node_plane, 'fine_resolution', int)
+ plane['value'] = self.__processLayerNvizNode(node_plane, 'height', int)
+ plane['object'] = {}
+ constants.append({'constant': plane})
+ self.nviz_state['constants'] = constants
+
+class WriteWorkspaceFile(object):
+ """!Generic class for writing workspace file"""
+ def __init__(self, lmgr, file):
+ self.file = file
+ self.lmgr = lmgr
+ self.indent = 0
+
+ # write header
+ self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+ self.file.write('<!DOCTYPE gxw SYSTEM "grass-gxw.dtd">\n')
+ self.file.write('%s<gxw>\n' % (' ' * self.indent))
+
+ self.indent =+ 4
+
+ # layer manager
+ windowPos = self.lmgr.GetPosition()
+ windowSize = self.lmgr.GetSize()
+ file.write('%s<layer_manager dim="%d,%d,%d,%d">\n' % (' ' * self.indent,
+ windowPos[0],
+ windowPos[1],
+ windowSize[0],
+ windowSize[1]
+ ))
+
+ file.write('%s</layer_manager>\n' % (' ' * self.indent))
+
+ # list of displays
+ for page in range(0, self.lmgr.gm_cb.GetPageCount()):
+ mapTree = self.lmgr.gm_cb.GetPage(page).maptree
+ region = mapTree.Map.region
+
+ displayPos = mapTree.mapdisplay.GetPosition()
+ displaySize = mapTree.mapdisplay.GetSize()
+ if mapTree.mapdisplay.toolbars['map'].combo.GetSelection() == 1:
+ viewmode = '3d'
+ else:
+ viewmode = '2d'
+
+ file.write('%s<display render="%d" '
+ 'mode="%d" showCompExtent="%d" '
+ 'alignExtent="%d" '
+ 'constrainRes="%d" '
+ 'dim="%d,%d,%d,%d" '
+ 'extent="%f,%f,%f,%f" '
+ 'viewMode="%s" >\n' % (' ' * self.indent,
+ int(mapTree.mapdisplay.GetProperty('render')),
+ mapTree.mapdisplay.statusbarManager.GetMode(),
+ int(mapTree.mapdisplay.GetProperty('region')),
+ int(mapTree.mapdisplay.GetProperty('alignExtent')),
+ int(mapTree.mapdisplay.GetProperty('resolution')),
+ displayPos[0],
+ displayPos[1],
+ displaySize[0],
+ displaySize[1],
+ region['w'],
+ region['s'],
+ region['e'],
+ region['n'],
+ viewmode
+ ))
+ # projection statusbar info
+ if mapTree.mapdisplay.GetProperty('projection') and \
+ UserSettings.Get(group='display', key='projection', subkey='proj4'):
+ self.indent += 4
+ file.write('%s<projection' % (' ' * self.indent))
+ epsg = UserSettings.Get(group='display', key='projection', subkey='epsg')
+ if epsg:
+ file.write(' epsg="%s"' % epsg)
+ file.write('>\n')
+ proj = UserSettings.Get(group='display', key='projection', subkey='proj4')
+ self.indent += 4
+ file.write('%s<value>%s</value>\n' % (' ' * self.indent, proj))
+ self.indent -= 4
+ file.write('%s</projection>\n' % (' ' * self.indent))
+ self.indent -= 4
+
+ # list of layers
+ item = mapTree.GetFirstChild(mapTree.root)[0]
+ self.__writeLayer(mapTree, item)
+
+ if mapTree.mapdisplay.MapWindow3D is not None:
+ nvizDisp = mapTree.mapdisplay.MapWindow3D
+ self.__writeNvizState(view = nvizDisp.view, iview = nvizDisp.iview,
+ light = nvizDisp.light, constants = nvizDisp.constants)
+
+ file.write('%s</display>\n' % (' ' * self.indent))
+
+ self.indent =- 4
+ file.write('%s</gxw>\n' % (' ' * self.indent))
+
+ def __filterValue(self, value):
+ """!Make value XML-valid"""
+ value = value.replace('<', '<')
+ value = value.replace('>', '>')
+
+ return value
+
+ def __writeLayer(self, mapTree, item):
+ """!Write bunch of layers to GRASS Workspace XML file"""
+ self.indent += 4
+ itemSelected = mapTree.GetSelections()
+ while item and item.IsOk():
+ type = mapTree.GetPyData(item)[0]['type']
+ if type != 'group':
+ maplayer = mapTree.GetPyData(item)[0]['maplayer']
+ else:
+ maplayer = None
+
+ checked = int(item.IsChecked())
+ if type == 'command':
+ cmd = mapTree.GetPyData(item)[0]['maplayer'].GetCmd(string=True)
+ self.file.write('%s<layer type="%s" name="%s" checked="%d">\n' % \
+ (' ' * self.indent, type, cmd, checked));
+ self.file.write('%s</layer>\n' % (' ' * self.indent));
+ elif type == 'group':
+ name = mapTree.GetItemText(item)
+ self.file.write('%s<group name="%s" checked="%d">\n' % \
+ (' ' * self.indent, name.encode('utf8'), checked));
+ self.indent += 4
+ subItem = mapTree.GetFirstChild(item)[0]
+ self.__writeLayer(mapTree, subItem)
+ self.indent -= 4
+ self.file.write('%s</group>\n' % (' ' * self.indent));
+ else:
+ cmd = mapTree.GetPyData(item)[0]['maplayer'].GetCmd(string=False)
+ name = mapTree.GetItemText(item)
+ opacity = maplayer.GetOpacity(float = True)
+ # remove 'opacity' part
+ if opacity < 1:
+ name = name.split('(', -1)[0].strip()
+ self.file.write('%s<layer type="%s" name="%s" checked="%d" opacity="%f">\n' % \
+ (' ' * self.indent, type, name.encode('utf8'), checked, opacity));
+
+ self.indent += 4
+ # selected ?
+ if item in itemSelected:
+ self.file.write('%s<selected />\n' % (' ' * self.indent))
+ # layer properties
+ self.file.write('%s<task name="%s">\n' % (' ' * self.indent, cmd[0]))
+ self.indent += 4
+ for key, val in cmd[1].iteritems():
+ if key == 'flags':
+ for f in val:
+ self.file.write('%s<flag name="%s" />\n' %
+ (' ' * self.indent, f))
+ elif val in (True, False):
+ self.file.write('%s<flag name="%s" />\n' %
+ (' ' * self.indent, key))
+ else: # parameter
+ self.file.write('%s<parameter name="%s">\n' %
+ (' ' * self.indent, key))
+ self.indent += 4
+ self.file.write('%s<value>%s</value>\n' %
+ (' ' * self.indent, self.__filterValue(val)))
+ self.indent -= 4
+ self.file.write('%s</parameter>\n' % (' ' * self.indent));
+ self.indent -= 4
+ self.file.write('%s</task>\n' % (' ' * self.indent));
+ # vector digitizer
+ vdigit = mapTree.GetPyData(item)[0]['vdigit']
+ if vdigit:
+ self.file.write('%s<vdigit>\n' % (' ' * self.indent))
+ if 'geomAttr' in vdigit:
+ self.indent += 4
+ for type, val in vdigit['geomAttr'].iteritems():
+ units = ''
+ if val['units'] != 'mu':
+ units = ' units="%s"' % val['units']
+ self.file.write('%s<geometryAttribute type="%s" column="%s"%s />\n' % \
+ (' ' * self.indent, type, val['column'], units))
+ self.indent -= 4
+ self.file.write('%s</vdigit>\n' % (' ' * self.indent))
+ # nviz
+ nviz = mapTree.GetPyData(item)[0]['nviz']
+ if nviz:
+ self.file.write('%s<nviz>\n' % (' ' * self.indent))
+ if maplayer.type == 'raster':
+ self.__writeNvizSurface(nviz['surface'])
+ elif maplayer.type == 'vector':
+ self.__writeNvizVector(nviz['vector'])
+ self.file.write('%s</nviz>\n' % (' ' * self.indent))
+ self.indent -= 4
+ self.file.write('%s</layer>\n' % (' ' * self.indent))
+ item = mapTree.GetNextSibling(item)
+ self.indent -= 4
+
+ def __writeNvizSurface(self, data):
+ """!Save Nviz raster layer properties to workspace
+
+ @param data Nviz layer properties
+ """
+ if 'object' not in data: # skip disabled
+ return
+ self.indent += 4
+ self.file.write('%s<surface>\n' % (' ' * self.indent))
+ self.indent += 4
+ for attrb in data.iterkeys():
+ if len(data[attrb]) < 1: # skip empty attributes
+ continue
+ if attrb == 'object':
+ continue
+
+ for name in data[attrb].iterkeys():
+ # surface attribute
+ if attrb == 'attribute':
+ self.file.write('%s<%s name="%s" map="%d">\n' % \
+ (' ' * self.indent, attrb, name, data[attrb][name]['map']))
+ self.indent += 4
+ self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]['value']))
+ self.indent -= 4
+ # end tag
+ self.file.write('%s</%s>\n' % (' ' * self.indent, attrb))
+
+ # draw mode
+ if attrb == 'draw':
+ self.file.write('%s<%s' %(' ' * self.indent, attrb))
+ if 'mode' in data[attrb]:
+ for tag, value in data[attrb]['mode']['desc'].iteritems():
+ self.file.write(' %s="%s"' % (tag, value))
+ self.file.write('>\n') # <draw ...>
+
+ if 'resolution' in data[attrb]:
+ self.indent += 4
+ for type in ('coarse', 'fine'):
+ self.file.write('%s<resolution type="%s">\n' % (' ' * self.indent, type))
+ self.indent += 4
+ self.file.write('%s<value>%d</value>\n' % (' ' * self.indent,
+ data[attrb]['resolution'][type]))
+ self.indent -= 4
+ self.file.write('%s</resolution>\n' % (' ' * self.indent))
+
+ if 'wire-color' in data[attrb]:
+ self.file.write('%s<wire_color>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<value>%s</value>\n' % (' ' * self.indent,
+ data[attrb]['wire-color']['value']))
+ self.indent -= 4
+ self.file.write('%s</wire_color>\n' % (' ' * self.indent))
+ self.indent -= 4
+
+ # position
+ elif attrb == 'position':
+ self.file.write('%s<%s>\n' %(' ' * self.indent, attrb))
+ i = 0
+ for tag in ('x', 'y', 'z'):
+ self.indent += 4
+ self.file.write('%s<%s>%d</%s>\n' % (' ' * self.indent, tag,
+ data[attrb][tag], tag))
+ i += 1
+ self.indent -= 4
+
+ if attrb != 'attribute':
+ # end tag
+ self.file.write('%s</%s>\n' % (' ' * self.indent, attrb))
+
+ self.indent -= 4
+ self.file.write('%s</surface>\n' % (' ' * self.indent))
+ self.indent -= 4
+
+ def __writeNvizVector(self, data):
+ """!Save Nviz vector layer properties (lines/points) to workspace
+
+ @param data Nviz layer properties
+ """
+ self.indent += 4
+ for attrb in data.iterkeys():
+ if len(data[attrb]) < 1: # skip empty attributes
+ continue
+
+ if 'object' not in data[attrb]: # skip disabled
+ continue
+ if attrb == 'lines':
+ self.file.write('%s<v%s>\n' % (' ' * self.indent, attrb))
+ elif attrb == 'points':
+ markerId = data[attrb]['marker']['value']
+ marker = UserSettings.Get(group = 'nviz', key = 'vector',
+ subkey = ['points', 'marker'], internal = True)[markerId]
+ self.file.write('%s<v%s marker="%s">\n' % (' ' * self.indent,
+ attrb,
+ marker))
+ self.indent += 4
+ for name in data[attrb].iterkeys():
+ if name in ('object', 'marker'):
+ continue
+ if name == 'mode':
+ self.file.write('%s<%s type="%s">\n' % (' ' * self.indent, name,
+ data[attrb][name]['type']))
+ if data[attrb][name]['type'] == 'surface':
+ self.indent += 4
+ for idx, surface in enumerate(data[attrb][name]['surface']['value']):
+ checked = data[attrb][name]['surface']['show'][idx]
+ self.file.write('%s<map>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<name>%s</name>\n' % (' ' * self.indent, surface))
+ self.file.write('%s<checked>%s</checked>\n' % (' ' * self.indent, int(checked)))
+ self.indent -= 4
+ self.file.write('%s</map>\n' % (' ' * self.indent))
+ self.indent -= 4
+ self.file.write('%s</%s>\n' % ((' ' * self.indent, name)))
+ elif name == 'thematic':
+ self.file.write('%s<%s ' % (' ' * self.indent, name))
+ for key in data[attrb][name].iterkeys():
+ if key.startswith('use'):
+ self.file.write('%s="%s" ' % (key, int(data[attrb][name][key])))
+ self.file.write('>\n')
+ self.indent += 4
+ for key, value in data[attrb][name].iteritems():
+ if key.startswith('use'):
+ continue
+ if value is None:
+ value = ''
+ self.file.write('%s<%s>%s</%s>\n' % (' ' * self.indent, key, value, key))
+ self.indent -= 4
+ self.file.write('%s</%s>\n' % (' ' * self.indent, name))
+ else:
+ self.file.write('%s<%s>\n' % (' ' * self.indent, name))
+ self.indent += 4
+ self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]['value']))
+ self.indent -= 4
+ self.file.write('%s</%s>\n' % (' ' * self.indent, name))
+ self.indent -= 4
+ self.file.write('%s</v%s>\n' % (' ' * self.indent, attrb))
+
+ self.indent -= 4
+
+ def __writeNvizState(self, view, iview, light, constants):
+ """"!Save Nviz properties (view, light) to workspace
+
+ @param view Nviz view properties
+ @param iview Nviz internal view properties
+ @param light Nviz light properties
+ """
+ self.indent += 4
+ self.file.write('%s<nviz_state>\n' % (' ' * self.indent))
+ #
+ # view
+ #
+ self.indent += 4
+ self.file.write('%s<view>\n' % (' ' * self.indent))
+ self.indent += 4
+ # position
+ self.file.write('%s<v_position>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, view['position']['x']))
+ self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, view['position']['y']))
+ self.indent -= 4
+ self.file.write('%s</v_position>\n' % (' ' * self.indent))
+ # perspective
+ self.file.write('%s<persp>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['persp']['value']))
+ self.file.write('%s<step>%d</step>\n' % (' ' * self.indent, view['persp']['step']))
+ self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['persp']['min']))
+ self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['persp']['max']))
+ self.indent -= 4
+ self.file.write('%s</persp>\n' % (' ' * self.indent))
+ # height
+ self.file.write('%s<v_height>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, iview['height']['value']))
+ self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['height']['min']))
+ self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['height']['max']))
+ self.indent -= 4
+ self.file.write('%s</v_height>\n' % (' ' * self.indent))
+ # twist
+ self.file.write('%s<twist>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['twist']['value']))
+ self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['twist']['min']))
+ self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['twist']['max']))
+ self.indent -= 4
+ self.file.write('%s</twist>\n' % (' ' * self.indent))
+ # z-exag
+ self.file.write('%s<z-exag>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['z-exag']['value']))
+ self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, view['z-exag']['min']))
+ self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, view['z-exag']['max']))
+ self.file.write('%s<original>%d</original>\n' % (' ' * self.indent, iview['z-exag']['original']))
+ self.indent -= 4
+ self.file.write('%s</z-exag>\n' % (' ' * self.indent))
+ # focus (look here)
+ self.file.write('%s<focus>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<x>%d</x>\n' % (' ' * self.indent, iview['focus']['x']))
+ self.file.write('%s<y>%d</y>\n' % (' ' * self.indent, iview['focus']['y']))
+ self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, iview['focus']['z']))
+ self.indent -= 4
+ self.file.write('%s</focus>\n' % (' ' * self.indent))
+ # background
+ self.__writeTagWithValue('background_color', view['background']['color'][:3], format = 'd:%d:%d')
+
+ self.indent -= 4
+ self.file.write('%s</view>\n' % (' ' * self.indent))
+ #
+ # light
+ #
+ self.file.write('%s<light>\n' % (' ' * self.indent))
+ self.indent += 4
+ # position
+ self.file.write('%s<l_position>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, light['position']['x']))
+ self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, light['position']['y']))
+ self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, light['position']['z']))
+ self.indent -= 4
+ self.file.write('%s</l_position>\n' % (' ' * self.indent))
+ # bright
+ self.__writeTagWithValue('bright', light['bright'])
+ # ambient
+ self.__writeTagWithValue('ambient', light['ambient'])
+ # color
+ self.__writeTagWithValue('color', light['color'][:3], format = 'd:%d:%d')
+
+ self.indent -= 4
+ self.file.write('%s</light>\n' % (' ' * self.indent))
+ #
+ # constant planes
+ #
+ if constants:
+ self.file.write('%s<constant_planes>\n' % (' ' * self.indent))
+ self.indent += 4
+ for idx, plane in enumerate(constants):
+ self.file.write('%s<plane>\n' % (' ' * self.indent))
+ self.indent += 4
+ self.__writeTagWithValue('height', constants[idx]['constant']['value'])
+ self.__writeTagWithValue('fine_resolution', constants[idx]['constant']['resolution'])
+ self.__writeTagWithValue('color', constants[idx]['constant']['color'], format = 's')
+ self.indent -= 4
+ self.file.write('%s</plane>\n' % (' ' * self.indent))
+ self.indent -= 4
+ self.file.write('%s</constant_planes>\n' % (' ' * self.indent))
+ self.indent -= 4
+
+ self.file.write('%s</nviz_state>\n' % (' ' * self.indent))
+ self.indent -= 4
+
+ def __writeTagWithValue(self, tag, data, format = 'd'):
+ """!Helper function for writing pair tag
+
+ @param tag written tag
+ @param data written data
+ @param format conversion type
+ """
+ self.file.write('%s<%s>\n' % (' ' * self.indent, tag))
+ self.indent += 4
+ self.file.write('%s' % (' ' * self.indent))
+ self.file.write(('<value>%' + format + '</value>\n') % data)
+ self.indent -= 4
+ self.file.write('%s</%s>\n' % (' ' * self.indent, tag))
+
+class ProcessGrcFile(object):
+ def __init__(self, filename):
+ """!Process GRC file"""
+ self.filename = filename
+
+ # elements
+ self.inGroup = False
+ self.inRaster = False
+ self.inVector = False
+
+ # list of layers
+ self.layers = []
+
+ # error message
+ self.error = ''
+ self.num_error = 0
+
+ def read(self, parent):
+ """!Read GRC file
+
+ @param parent parent window
+
+ @return list of map layers
+ """
+ try:
+ file = open(self.filename, "r")
+ except IOError:
+ wx.MessageBox(parent=parent,
+ message=_("Unable to open file <%s> for reading.") % self.filename,
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
+ return []
+
+ line_id = 1
+ for line in file.readlines():
+ self.process_line(line.rstrip('\n'), line_id)
+ line_id +=1
+
+ file.close()
+
+ if self.num_error > 0:
+ wx.MessageBox(parent=parent,
+ message=_("Some lines were skipped when reading settings "
+ "from file <%(file)s>.\nSee 'Command output' window for details.\n\n"
+ "Number of skipped lines: %(line)d") % \
+ { 'file' : self.filename, 'line' : self.num_error },
+ caption=_("Warning"), style=wx.OK | wx.ICON_EXCLAMATION)
+ parent.goutput.WriteLog('Map layers loaded from GRC file <%s>' % self.filename)
+ parent.goutput.WriteLog('Skipped lines:\n%s' % self.error)
+
+ return self.layers
+
+ def process_line(self, line, line_id):
+ """!Process line definition"""
+ element = self._get_element(line)
+ if element == 'Group':
+ self.groupName = self._get_value(line)
+ self.layers.append({
+ "type" : 'group',
+ "name" : self.groupName,
+ "checked" : None,
+ "opacity" : None,
+ "cmd" : None,
+ "group" : self.inGroup,
+ "display" : 0 })
+ self.inGroup = True
+
+ elif element == '_check':
+ if int(self._get_value(line)) == 1:
+ self.layers[-1]['checked'] = True
+ else:
+ self.layers[-1]['checked'] = False
+
+ elif element == 'End':
+ if self.inRaster:
+ self.inRaster = False
+ elif self.inVector:
+ self.inVector = False
+ elif self.inGroup:
+ self.inGroup = False
+ elif self.inGridline:
+ self.inGridline = False
+
+ elif element == 'opacity':
+ self.layers[-1]['opacity'] = float(self._get_value(line))
+
+ # raster
+ elif element == 'Raster':
+ self.inRaster = True
+ self.layers.append({
+ "type" : 'raster',
+ "name" : self._get_value(line),
+ "checked" : None,
+ "opacity" : None,
+ "cmd" : ['d.rast'],
+ "group" : self.inGroup,
+ "display" : 0})
+
+ elif element == 'map' and self.inRaster:
+ self.layers[-1]['cmd'].append('map=%s' % self._get_value(line))
+
+ elif element == 'overlay' and self.inRaster:
+ if int(self._get_value(line)) == 1:
+ self.layers[-1]['cmd'].append('-o')
+
+ elif element == 'rastquery' and self.inRaster:
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('catlist=%s' % value)
+
+ elif element == 'bkcolor' and self.inRaster:
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('bg=%s' % value)
+
+ # vector
+ elif element == 'Vector':
+ self.inVector = True
+ self.layers.append({
+ "type" : 'vector',
+ "name" : self._get_value(line),
+ "checked" : None,
+ "opacity" : None,
+ "cmd" : ['d.vect'],
+ "group" : self.inGroup,
+ "display" : 0})
+
+ elif element == 'vect' and self.inVector:
+ self.layers[-1]['cmd'].append('map=%s' % self._get_value(line))
+
+ elif element in ('display_shape',
+ 'display_cat',
+ 'display_topo',
+ 'display_dir',
+ 'display_attr',
+ 'type_point',
+ 'type_line',
+ 'type_boundary',
+ 'type_centroid',
+ 'type_area',
+ 'type_face') and self.inVector:
+
+ if int(self._get_value(line)) == 1:
+ name = element.split('_')[0]
+ type = element.split('_')[1]
+ paramId = self._get_cmd_param_index(self.layers[-1]['cmd'], name)
+ if paramId == -1:
+ self.layers[-1]['cmd'].append('%s=%s' % (name, type))
+ else:
+ self.layers[-1]['cmd'][paramId] += ',%s' % type
+
+ elif element in ('color',
+ 'fcolor',
+ 'lcolor') and self.inVector:
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('%s=%s' % (element,
+ self._color_name_to_rgb(value)))
+
+ elif element == 'rdmcolor' and self.inVector:
+ if int(self._get_value(line)) == 1:
+ self.layers[-1]['cmd'].append('-c')
+
+ elif element == 'sqlcolor' and self.inVector:
+ if int(self._get_value(line)) == 1:
+ self.layers[-1]['cmd'].append('-a')
+
+ elif element in ('icon',
+ 'size',
+ 'layer',
+ 'xref',
+ 'yref',
+ 'lsize',
+ 'where',
+ 'minreg',
+ 'maxreg') and self.inVector:
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('%s=%s' % (element,
+ value))
+
+ elif element == 'lwidth':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('width=%s' % value)
+
+ elif element == 'lfield':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('llayer=%s' % value)
+
+ elif element == 'attribute':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('attrcol=%s' % value)
+
+ elif element == 'cat':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('cats=%s' % value)
+
+ # gridline
+ elif element == 'gridline':
+ self.inGridline = True
+ self.layers.append({
+ "type" : 'grid',
+ "name" : self._get_value(line),
+ "checked" : None,
+ "opacity" : None,
+ "cmd" : ['d.grid'],
+ "group" : self.inGroup,
+ "display" : 0})
+
+ elif element == 'gridcolor':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('color=%s' % self._color_name_to_rgb(value))
+
+ elif element == 'gridborder':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('bordercolor=%s' % self._color_name_to_rgb(value))
+
+ elif element == 'textcolor':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('textcolor=%s' % self._color_name_to_rgb(value))
+
+ elif element in ('gridsize',
+ 'gridorigin'):
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('%s=%s' % (element[4:], value))
+
+ elif element in 'fontsize':
+ value = self._get_value(line)
+ if value != '':
+ self.layers[-1]['cmd'].append('%s=%s' % (element, value))
+
+ elif element == 'griddraw':
+ value = self._get_value(line)
+ if value == '0':
+ self.layers[-1]['cmd'].append('-n')
+
+ elif element == 'gridgeo':
+ value = self._get_value(line)
+ if value == '1':
+ self.layers[-1]['cmd'].append('-g')
+
+ elif element == 'borderdraw':
+ value = self._get_value(line)
+ if value == '0':
+ self.layers[-1]['cmd'].append('-b')
+
+ elif element == 'textdraw':
+ value = self._get_value(line)
+ if value == '0':
+ self.layers[-1]['cmd'].append('-t')
+
+ else:
+ self.error += _(' row %d:') % line_id + line + os.linesep
+ self.num_error += 1
+
+ def _get_value(self, line):
+ """!Get value of element"""
+ try:
+ return line.strip(' ').split(' ')[1].strip(' ')
+ except:
+ return ''
+
+ def _get_element(self, line):
+ """!Get element tag"""
+ return line.strip(' ').split(' ')[0].strip(' ')
+
+ def _get_cmd_param_index(self, cmd, name):
+ """!Get index of parameter in cmd list
+
+ @param cmd cmd list
+ @param name parameter name
+
+ @return index
+ @return -1 if not found
+ """
+ i = 0
+ for param in cmd:
+ if '=' not in param:
+ i += 1
+ continue
+ if param.split('=')[0] == name:
+ return i
+
+ i += 1
+
+ return -1
+
+ def _color_name_to_rgb(self, value):
+ """!Convert color name (#) to rgb values"""
+ col = wx.NamedColour(value)
+ return str(col.Red()) + ':' + \
+ str(col.Green()) + ':' + \
+ str(col.Blue())
Added: grass/trunk/gui/wxpython/create__init__.py
===================================================================
--- grass/trunk/gui/wxpython/create__init__.py (rev 0)
+++ grass/trunk/gui/wxpython/create__init__.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -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]))
Property changes on: grass/trunk/gui/wxpython/create__init__.py
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Copied: grass/trunk/gui/wxpython/dbm/dialogs.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/dbm_dialogs.py)
===================================================================
--- grass/trunk/gui/wxpython/dbm/dialogs.py (rev 0)
+++ grass/trunk/gui/wxpython/dbm/dialogs.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,668 @@
+"""!
+ at package dbm.dialogs
+
+ at brief DBM-related dialogs
+
+List of classes:
+ - DisplayAttributesDialog
+ - 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+from core import globalvar
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+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,
+ query = None, cats = None, line = None,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+ pos = wx.DefaultPosition,
+ action = "add", ignoreError = False):
+ """!Standard dialog used to add/update/display attributes linked
+ to the vector map.
+
+ Attribute data can be selected based on layer and category number
+ or coordinates.
+
+ @param parent
+ @param map vector map
+ @param query query coordinates and distance (used for v.edit)
+ @param cats {layer: cats}
+ @param line feature id (requested for cats)
+ @param style
+ @param pos
+ @param action (add, update, display)
+ @param ignoreError True to ignore errors
+ """
+ self.parent = parent # mapdisplay.BufferedWindow
+ self.map = map
+ self.action = action
+
+ # ids/cats of selected features
+ # fid : {layer : cats}
+ self.cats = {}
+ self.fid = -1 # feature id
+
+ # get layer/table/column information
+ self.mapDBInfo = VectorDBInfo(self.map)
+
+ layers = self.mapDBInfo.layers.keys() # get available layers
+
+ # check if db connection / layer exists
+ if len(layers) <= 0:
+ if not ignoreError:
+ dlg = wx.MessageDialog(parent = self.parent,
+ message = _("No attribute table found.\n\n"
+ "Do you want to create a new attribute table "
+ "and defined a link to vector map <%s>?") % self.map,
+ caption = _("Create table?"),
+ style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+ if dlg.ShowModal() == wx.ID_YES:
+ lmgr = self.parent.lmgr
+ lmgr.OnShowAttributeTable(event = None, selection = 'layers')
+
+ dlg.Destroy()
+
+ self.mapDBInfo = None
+
+ wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY,
+ title = "", style = style, pos = pos)
+
+ # dialog body
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+ # notebook
+ self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+
+ self.closeDialog = wx.CheckBox(parent = self, id = wx.ID_ANY,
+ label = _("Close dialog on submit"))
+ self.closeDialog.SetValue(True)
+ if self.action == 'display':
+ self.closeDialog.Enable(False)
+
+ # feature id (text/choice for duplicates)
+ self.fidMulti = wx.Choice(parent = self, id = wx.ID_ANY,
+ size = (150, -1))
+ self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
+ self.fidText = wx.StaticText(parent = self, id = wx.ID_ANY)
+
+ self.noFoundMsg = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = _("No attributes found"))
+
+ self.UpdateDialog(query = query, cats = cats)
+
+ # set title
+ if self.action == "update":
+ self.SetTitle(_("Update attributes"))
+ elif self.action == "add":
+ self.SetTitle(_("Define attributes"))
+ else:
+ self.SetTitle(_("Display attributes"))
+
+ # buttons
+ btnCancel = wx.Button(self, wx.ID_CANCEL)
+ btnReset = wx.Button(self, wx.ID_UNDO, _("&Reload"))
+ btnSubmit = wx.Button(self, wx.ID_OK, _("&Submit"))
+ if self.action == 'display':
+ btnSubmit.Enable(False)
+
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(btnCancel)
+ btnSizer.AddButton(btnReset)
+ btnSizer.SetNegativeButton(btnReset)
+ btnSubmit.SetDefault()
+ btnSizer.AddButton(btnSubmit)
+ btnSizer.Realize()
+
+ mainSizer.Add(item = self.noFoundMsg, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+ mainSizer.Add(item = self.notebook, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, 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.LEFT | wx.RIGHT, border = 5)
+ mainSizer.Add(item = self.closeDialog, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
+ border = 5)
+ mainSizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ # bindigs
+ btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
+ btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
+ btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ # set min size for dialog
+ w, h = self.GetBestSize()
+ if h < 200:
+ self.SetMinSize((w, 200))
+ else:
+ self.SetMinSize(self.GetBestSize())
+
+ if self.notebook.GetPageCount() == 0:
+ Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
+ ### self.mapDBInfo = None
+
+ def OnSQLStatement(self, event):
+ """!Update SQL statement"""
+ pass
+
+ def IsFound(self):
+ """!Check for status
+
+ @return True on attributes found
+ @return False attributes not found
+ """
+ return bool(self.mapDBInfo and self.notebook.GetPageCount() > 0)
+
+ def GetSQLString(self, updateValues = False):
+ """!Create SQL statement string based on self.sqlStatement
+
+ If updateValues is True, update dataFrame according to values
+ in textfields.
+ """
+ sqlCommands = []
+ # find updated values for each layer/category
+ for layer in self.mapDBInfo.layers.keys(): # for each layer
+ table = self.mapDBInfo.GetTable(layer)
+ key = self.mapDBInfo.GetKeyColumn(layer)
+ columns = self.mapDBInfo.GetTableDesc(table)
+ for idx in range(len(columns[key]['values'])): # for each category
+ updatedColumns = []
+ updatedValues = []
+ for name in columns.keys():
+ if name == key:
+ cat = columns[name]['values'][idx]
+ continue
+ type = columns[name]['type']
+ value = columns[name]['values'][idx]
+ id = columns[name]['ids'][idx]
+ try:
+ newvalue = self.FindWindowById(id).GetValue()
+ except:
+ newvalue = self.FindWindowById(id).GetLabel()
+
+ if newvalue == '':
+ newvalue = None
+
+ if newvalue != value:
+ updatedColumns.append(name)
+ if newvalue is None:
+ updatedValues.append('NULL')
+ else:
+ if type != 'character':
+ updatedValues.append(newvalue)
+ else:
+ updatedValues.append("'" + newvalue + "'")
+ columns[name]['values'][idx] = newvalue
+
+ if self.action != "add" and len(updatedValues) == 0:
+ continue
+
+ if self.action == "add":
+ sqlString = "INSERT INTO %s (%s," % (table, key)
+ else:
+ sqlString = "UPDATE %s SET " % table
+
+ for idx in range(len(updatedColumns)):
+ name = updatedColumns[idx]
+ if self.action == "add":
+ sqlString += name + ","
+ else:
+ sqlString += name + "=" + updatedValues[idx] + ","
+
+ sqlString = sqlString[:-1] # remove last comma
+
+ if self.action == "add":
+ sqlString += ") VALUES (%s," % cat
+ for value in updatedValues:
+ sqlString += str(value) + ","
+ sqlString = sqlString[:-1] # remove last comma
+ sqlString += ")"
+ else:
+ sqlString += " WHERE %s=%s" % (key, cat)
+ sqlCommands.append(sqlString)
+ # for each category
+ # for each layer END
+
+ Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)
+
+ return sqlCommands
+
+ def OnReset(self, event = None):
+ """!Reset form"""
+ for layer in self.mapDBInfo.layers.keys():
+ table = self.mapDBInfo.layers[layer]["table"]
+ key = self.mapDBInfo.layers[layer]["key"]
+ columns = self.mapDBInfo.tables[table]
+ for idx in range(len(columns[key]['values'])):
+ for name in columns.keys():
+ type = columns[name]['type']
+ value = columns[name]['values'][idx]
+ if value is None:
+ value = ''
+ try:
+ id = columns[name]['ids'][idx]
+ except IndexError:
+ id = wx.NOT_FOUND
+
+ if name != key and id != wx.NOT_FOUND:
+ self.FindWindowById(id).SetValue(str(value))
+
+ def OnCancel(self, event):
+ """!Cancel button pressed
+ """
+ self.parent.parent.dialogs['attributes'] = None
+
+ if hasattr(self, "digit"):
+ self.parent.digit.GetDisplay().SetSelected([])
+ self.parent.UpdateMap(render = False)
+ else:
+ self.parent.parent.OnRender(None)
+
+ self.Close()
+
+ def OnSubmit(self, event):
+ """!Submit records"""
+ layer = 1
+ for sql in self.GetSQLString(updateValues = True):
+ enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+ if not enc and 'GRASS_DB_ENCODING' in os.environ:
+ enc = os.environ['GRASS_DB_ENCODING']
+ if enc:
+ sql = sql.encode(enc)
+
+ driver, database = self.mapDBInfo.GetDbSettings(layer)
+ Debug.msg(1, "SQL: %s" % sql)
+ RunCommand('db.execute',
+ parent = self,
+ quiet = True,
+ input = '-',
+ stdin = sql,
+ driver = driver,
+ database = database)
+
+ layer += 1
+
+ if self.closeDialog.IsChecked():
+ self.OnCancel(event)
+
+ def OnFeature(self, event):
+ self.fid = int(event.GetString())
+ self.UpdateDialog(cats = self.cats, fid = self.fid)
+
+ def GetCats(self):
+ """!Get id of selected vector object or 'None' if nothing selected
+
+ @param id if true return ids otherwise cats
+ """
+ if self.fid < 0:
+ return None
+
+ return self.cats[self.fid]
+
+ def GetFid(self):
+ """!Get selected feature id"""
+ return self.fid
+
+ def UpdateDialog(self, map = None, query = None, cats = None, fid = -1,
+ action = None):
+ """!Update dialog
+
+ @param map name of vector map
+ @param query
+ @param cats
+ @param fid feature id
+ @param action add, update, display or None
+
+ @return True if updated
+ @return False
+ """
+ if action:
+ self.action = action
+ if action == 'display':
+ enabled = False
+ else:
+ enabled = True
+ self.closeDialog.Enable(enabled)
+ self.FindWindowById(wx.ID_OK).Enable(enabled)
+
+ if map:
+ self.map = map
+ # get layer/table/column information
+ self.mapDBInfo = VectorDBInfo(self.map)
+
+ if not self.mapDBInfo:
+ return False
+
+ self.mapDBInfo.Reset()
+
+ layers = self.mapDBInfo.layers.keys() # get available layers
+
+ # id of selected line
+ if query: # select by position
+ data = self.mapDBInfo.SelectByPoint(query[0],
+ query[1])
+ self.cats = {}
+ if data and 'Layer' in data:
+ idx = 0
+ for layer in data['Layer']:
+ layer = int(layer)
+ if 'Id' in data:
+ tfid = int(data['Id'][idx])
+ else:
+ tfid = 0 # Area / Volume
+ if not tfid in self.cats:
+ self.cats[tfid] = {}
+ if not layer in self.cats[tfid]:
+ self.cats[tfid][layer] = []
+ cat = int(data['Category'][idx])
+ self.cats[tfid][layer].append(cat)
+ idx += 1
+ else:
+ self.cats = cats
+
+ if fid > 0:
+ self.fid = fid
+ elif len(self.cats.keys()) > 0:
+ self.fid = self.cats.keys()[0]
+ else:
+ self.fid = -1
+
+ if len(self.cats.keys()) == 1:
+ self.fidMulti.Show(False)
+ self.fidText.Show(True)
+ if self.fid > 0:
+ self.fidText.SetLabel("%d" % self.fid)
+ else:
+ self.fidText.SetLabel(_("Unknown"))
+ else:
+ self.fidMulti.Show(True)
+ self.fidText.Show(False)
+ choices = []
+ for tfid in self.cats.keys():
+ choices.append(str(tfid))
+ self.fidMulti.SetItems(choices)
+ self.fidMulti.SetStringSelection(str(self.fid))
+
+ # reset notebook
+ self.notebook.DeleteAllPages()
+
+ for layer in layers: # for each layer
+ if not query: # select by layer/cat
+ if self.fid > 0 and layer in self.cats[self.fid]:
+ for cat in self.cats[self.fid][layer]:
+ nselected = self.mapDBInfo.SelectFromTable(layer,
+ where = "%s=%d" % \
+ (self.mapDBInfo.layers[layer]['key'],
+ cat))
+ else:
+ nselected = 0
+
+ # if nselected <= 0 and self.action != "add":
+ # continue # nothing selected ...
+
+ if self.action == "add":
+ if nselected <= 0:
+ if layer in self.cats[self.fid]:
+ table = self.mapDBInfo.layers[layer]["table"]
+ key = self.mapDBInfo.layers[layer]["key"]
+ columns = self.mapDBInfo.tables[table]
+ for name in columns.keys():
+ if name == key:
+ for cat in self.cats[self.fid][layer]:
+ self.mapDBInfo.tables[table][name]['values'].append(cat)
+ else:
+ self.mapDBInfo.tables[table][name]['values'].append(None)
+ else: # change status 'add' -> 'update'
+ self.action = "update"
+
+ table = self.mapDBInfo.layers[layer]["table"]
+ key = self.mapDBInfo.layers[layer]["key"]
+ columns = self.mapDBInfo.tables[table]
+
+ for idx in range(len(columns[key]['values'])):
+ for name in columns.keys():
+ if name == key:
+ cat = int(columns[name]['values'][idx])
+ break
+
+ # use scrolled panel instead (and fix initial max height of the window to 480px)
+ panel = scrolled.ScrolledPanel(parent = self.notebook, id = wx.ID_ANY,
+ size = (-1, 150))
+ panel.SetupScrolling(scroll_x = False)
+
+ self.notebook.AddPage(page = panel, text = " %s %d / %s %d" % (_("Layer"), layer,
+ _("Category"), cat))
+
+ # notebook body
+ border = wx.BoxSizer(wx.VERTICAL)
+
+ flexSizer = wx.FlexGridSizer (cols = 4, hgap = 3, vgap = 3)
+ flexSizer.AddGrowableCol(3)
+ # columns (sorted by index)
+ names = [''] * len(columns.keys())
+ for name in columns.keys():
+ names[columns[name]['index']] = name
+
+ for name in names:
+ if name == key: # skip key column (category)
+ continue
+
+ vtype = columns[name]['type']
+
+ if columns[name]['values'][idx] is not None:
+ if columns[name]['ctype'] != type(''):
+ value = str(columns[name]['values'][idx])
+ else:
+ value = columns[name]['values'][idx]
+ else:
+ value = ''
+
+ colName = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = name)
+ colType = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = "[" + vtype.lower() + "]")
+ delimiter = wx.StaticText(parent = panel, id = wx.ID_ANY, label = ":")
+
+ colValue = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = value)
+ colValue.SetName(name)
+ self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
+ if self.action == 'display':
+ colValue.SetWindowStyle(wx.TE_READONLY)
+
+ flexSizer.Add(colName, proportion = 0,
+ flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+ flexSizer.Add(colType, proportion = 0,
+ flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+ flexSizer.Add(delimiter, proportion = 0,
+ flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+ flexSizer.Add(colValue, proportion = 1,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+ # add widget reference to self.columns
+ columns[name]['ids'].append(colValue.GetId()) # name, type, values, id
+ # for each attribute (including category) END
+ border.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+ panel.SetSizer(border)
+ # for each category END
+ # for each layer END
+
+ if self.notebook.GetPageCount() == 0:
+ self.noFoundMsg.Show(True)
+ else:
+ self.noFoundMsg.Show(False)
+
+ self.Layout()
+
+ return True
+
+ def SetColumnValue(self, layer, column, value):
+ """!Set attrbute value
+
+ @param column column name
+ @param value value
+ """
+ table = self.mapDBInfo.GetTable(layer)
+ columns = self.mapDBInfo.GetTableDesc(table)
+
+ for key, col in columns.iteritems():
+ if key == column:
+ col['values'] = [col['ctype'](value),]
+ break
+
+class ModifyTableRecord(wx.Dialog):
+ def __init__(self, parent, title, data, keyEditable = (-1, True),
+ id = wx.ID_ANY, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+ """!Dialog for inserting/updating table record
+
+ @param data a list: [(column, value)]
+ @param KeyEditable (id, editable?) indicates if textarea for key column
+ is editable(True) or not
+ """
+ # parent -> VDigitWindow
+ wx.Dialog.__init__(self, parent, id, title, style = style)
+
+ self.CenterOnParent()
+
+ self.keyId = keyEditable[0]
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ box = wx.StaticBox(parent = self.panel, id = wx.ID_ANY)
+ box.Hide()
+ self.dataPanel = scrolled.ScrolledPanel(parent = self.panel, id = wx.ID_ANY,
+ style = wx.TAB_TRAVERSAL)
+ self.dataPanel.SetupScrolling(scroll_x = False)
+
+ # buttons
+ self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+ self.btnSubmit = wx.Button(self.panel, wx.ID_OK, _("&Submit"))
+ self.btnSubmit.SetDefault()
+
+ # data area
+ self.widgets = []
+ cId = 0
+ self.usebox = False
+ self.cat = None
+ winFocus = False
+ for column, value in data:
+ if self.keyId == cId:
+ self.cat = int(value)
+ if not keyEditable[1]:
+ self.usebox = True
+ box.SetLabel(" %s %d " % (_("Category"), self.cat))
+ box.Show()
+ self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ cId += 1
+ continue
+ else:
+ valueWin = wx.SpinCtrl(parent = self.dataPanel, id = wx.ID_ANY,
+ value = value, min = -1e9, max = 1e9, size = (250, -1))
+ else:
+ valueWin = wx.TextCtrl(parent = self.dataPanel, id = wx.ID_ANY,
+ value = value, size = (250, -1))
+ if not winFocus:
+ wx.CallAfter(valueWin.SetFocus)
+ winFocus = True
+
+ label = wx.StaticText(parent = self.dataPanel, id = wx.ID_ANY,
+ label = column + ":")
+
+ self.widgets.append((label.GetId(), valueWin.GetId()))
+
+ cId += 1
+
+ self._layout()
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # data area
+ dataSizer = wx.FlexGridSizer (cols = 2, hgap = 3, vgap = 3)
+ dataSizer.AddGrowableCol(1)
+
+ for labelId, valueId in self.widgets:
+ label = self.FindWindowById(labelId)
+ value = self.FindWindowById(valueId)
+
+ dataSizer.Add(label, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ dataSizer.Add(value, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+
+ self.dataPanel.SetAutoLayout(True)
+ self.dataPanel.SetSizer(dataSizer)
+ dataSizer.Fit(self.dataPanel)
+
+ if self.usebox:
+ self.boxSizer.Add(item = self.dataPanel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ # buttons
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnSubmit)
+ btnSizer.Realize()
+
+ if not self.usebox:
+ sizer.Add(item = self.dataPanel, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+ else:
+ sizer.Add(item = self.boxSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ framewidth = self.GetSize()[0]
+ self.SetMinSize((framewidth,150))
+ self.SetMaxSize((framewidth,300))
+
+ # sizer.SetSizeHints(self.panel)
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizer(sizer)
+ sizer.Fit(self.panel)
+
+ self.Layout()
+
+ def GetValues(self, columns = None):
+ """!Return list of values (casted to string).
+
+ If columns is given (list), return only values of given columns.
+ """
+ valueList = []
+ for labelId, valueId in self.widgets:
+ column = self.FindWindowById(labelId).GetLabel().replace(':', '')
+ if columns is None or column in columns:
+ value = str(self.FindWindowById(valueId).GetValue())
+ valueList.append(value)
+
+ # add key value
+ if self.usebox:
+ valueList.insert(self.keyId, str(self.cat))
+
+ return valueList
Copied: grass/trunk/gui/wxpython/dbm/manager.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/dbm.py)
===================================================================
--- grass/trunk/gui/wxpython/dbm/manager.py (rev 0)
+++ grass/trunk/gui/wxpython/dbm/manager.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,3092 @@
+"""!
+ at package dbm.manager
+
+ at brief GRASS Attribute Table Manager
+
+This program is based on FileHunter, published in 'The wxPython Linux
+Tutorial' on wxPython WIKI pages.
+
+It also uses some functions at
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407
+
+ at code
+python dbm.py vector at mapset
+ at endcode
+
+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.
+
+ at author Jachym Cepicky <jachym.cepicky gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import sys
+import os
+import locale
+import tempfile
+import copy
+import types
+
+from core import globalvar
+import wx
+import wx.lib.mixins.listctrl as listmix
+import wx.lib.flatnotebook as FN
+
+import grass.script as grass
+
+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.
+ """
+ def __init__(self, parent):
+ self.parent = parent
+
+ def write(self, text_string):
+ """!Update status bar"""
+ self.parent.SetStatusText(text_string.strip())
+
+
+class VirtualAttributeList(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin,
+ listmix.ColumnSorterMixin):
+ """
+ Support virtual list class
+ """
+ def __init__(self, parent, log, mapDBInfo, layer):
+ #
+ # initialize variables
+ #
+ self.parent = parent
+ self.log = log
+ self.mapDBInfo = mapDBInfo
+ self.layer = layer
+
+ self.columns = {} # <- LoadData()
+
+ wx.ListCtrl.__init__(self, parent=parent, id=wx.ID_ANY,
+ style=wx.LC_REPORT | wx.LC_HRULES |
+ wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING)
+
+ try:
+ keyColumn = self.LoadData(layer)
+ except GException, e:
+ GError(parent = self,
+ message = e.value)
+ return
+
+ #
+ # add some attributes (colourful background for each item rows)
+ #
+ self.attr1 = wx.ListItemAttr()
+ self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
+ self.attr2 = wx.ListItemAttr()
+ self.attr2.SetBackgroundColour("white")
+ self.il = wx.ImageList(16, 16)
+ self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR,
+ (16,16)))
+ self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
+ (16,16)))
+ self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
+
+ # setup mixins
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+ listmix.ColumnSorterMixin.__init__(self, len(self.columns))
+
+ # sort item by category (id)
+ if keyColumn > -1:
+ self.SortListItems(col=keyColumn, ascending=True)
+ else:
+ self.SortListItems(col=0, ascending=True)
+
+ # events
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+ self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
+ self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumnSort)
+ self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)
+
+ def Update(self, mapDBInfo):
+ """!Update list according new mapDBInfo description"""
+ self.mapDBInfo = mapDBInfo
+ self.LoadData(self.layer)
+
+ def LoadData(self, layer, columns=None, where=None, sql=None):
+ """!Load data into list
+
+ @param layer layer number
+ @param columns list of columns for output (-> v.db.select)
+ @param where where statement (-> v.db.select)
+ @param sql full sql statement (-> db.select)
+
+ @return id of key column
+ @return -1 if key column is not displayed
+ """
+ self.log.write(_("Loading data..."))
+
+ tableName = self.mapDBInfo.layers[layer]['table']
+ keyColumn = self.mapDBInfo.layers[layer]['key']
+ try:
+ self.columns = self.mapDBInfo.tables[tableName]
+ except KeyError:
+ raise GException(_("Attribute table <%s> not found. "
+ "For creating the table switch to "
+ "'Manage layers' tab.") % tableName)
+
+ if not columns:
+ columns = self.mapDBInfo.GetColumns(tableName)
+ else:
+ all = self.mapDBInfo.GetColumns(tableName)
+ for col in columns:
+ if col not in all:
+ wx.MessageBox(parent=self,
+ message=_("Column <%(column)s> not found in "
+ "in the table <%(table)s>.") % \
+ { 'column' : col, 'table' : tableName },
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ try:
+ # for maps connected via v.external
+ keyId = columns.index(keyColumn)
+ except:
+ keyId = -1
+
+ #
+ # read data
+ #
+ # FIXME: Max. number of rows, while the GUI is still usable
+
+ # stdout can be very large, do not use PIPE, redirect to temp file
+ # TODO: more effective way should be implemented...
+ outFile = tempfile.NamedTemporaryFile(mode='w+b')
+
+ if sql:
+ ret = RunCommand('db.select',
+ quiet = True,
+ parent = self,
+ flags = 'c',
+ sql = sql,
+ output = outFile.name)
+ else:
+ if columns:
+ 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 = 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
+ self.itemDataMap = {}
+ self.itemIndexMap = []
+ self.itemCatsMap = {}
+
+ self.DeleteAllItems()
+
+ # self.ClearAll()
+ for i in range(self.GetColumnCount()):
+ self.DeleteColumn(0)
+
+ i = 0
+ info = wx.ListItem()
+ info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
+ info.m_image = -1
+ info.m_format = 0
+ for column in columns:
+ info.m_text = column
+ self.InsertColumnInfo(i, info)
+ i += 1
+
+ if i >= 256:
+ self.log.write(_("Can display only 256 columns."))
+
+ i = 0
+ outFile.seek(0)
+
+ while True:
+ # os.linesep doesn't work here (MSYS)
+ record = outFile.readline().replace('\n', '')
+
+ if not record:
+ break
+
+ self.AddDataRow(i, record, columns, keyId)
+
+ i += 1
+ if i >= 100000:
+ self.log.write(_("Limit 100000 records."))
+ break
+
+ self.SetItemCount(i)
+
+ i = 0
+ for col in columns:
+ width = self.columns[col]['length'] * 6 # FIXME
+ if width < 60:
+ width = 60
+ if width > 300:
+ width = 300
+ self.SetColumnWidth(col=i, width=width)
+ i += 1
+
+ self.SendSizeEvent()
+
+ self.log.write(_("Number of loaded records: %d") % \
+ self.GetItemCount())
+
+ return keyId
+
+ def AddDataRow(self, i, record, columns, keyId):
+ """!Add row to the data list"""
+ self.itemDataMap[i] = []
+ keyColumn = self.mapDBInfo.layers[self.layer]['key']
+ j = 0
+ cat = None
+
+ if keyColumn == 'OGC_FID':
+ self.itemDataMap[i].append(i+1)
+ j += 1
+ cat = i + 1
+
+ for value in record.split('|'):
+ if self.columns[columns[j]]['ctype'] != types.StringType:
+ try:
+ ### casting disabled (2009/03)
+ ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
+ self.itemDataMap[i].append(value)
+ except ValueError:
+ self.itemDataMap[i].append(_('Unknown value'))
+ else:
+ # encode string values
+ try:
+ self.itemDataMap[i].append(unicodeValue(value))
+ except UnicodeDecodeError:
+ self.itemDataMap[i].append(_("Unable to decode value. "
+ "Set encoding in GUI preferences ('Attributes')."))
+
+ if not cat and keyId > -1 and keyId == j:
+ try:
+ cat = self.columns[columns[j]]['ctype'] (value)
+ except ValueError, e:
+ cat = -1
+ 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)
+ if keyId > -1: # load cats only when LoadData() is called first time
+ self.itemCatsMap[i] = cat
+
+ def OnItemSelected(self, event):
+ """!Item selected. Add item to selected cats..."""
+ # cat = int(self.GetItemText(event.m_itemIndex))
+ # if cat not in self.selectedCats:
+ # self.selectedCats.append(cat)
+ # self.selectedCats.sort()
+
+ event.Skip()
+
+ def OnItemDeselected(self, event):
+ """!Item deselected. Remove item from selected cats..."""
+ # cat = int(self.GetItemText(event.m_itemIndex))
+ # if cat in self.selectedCats:
+ # self.selectedCats.remove(cat)
+ # self.selectedCats.sort()
+
+ event.Skip()
+
+ def GetSelectedItems(self):
+ """!Return list of selected items (category numbers)"""
+ cats = []
+ item = self.GetFirstSelected()
+ while item != -1:
+ cats.append(self.GetItemText(item))
+ item = self.GetNextSelected(item)
+
+ return cats
+
+ def GetColumnText(self, index, col):
+ """!Return column text"""
+ item = self.GetItem(index, col)
+ return item.GetText()
+
+ def GetListCtrl(self):
+ """!Returt list"""
+ return self
+
+ def OnGetItemText(self, item, col):
+ """!Get item text"""
+ index = self.itemIndexMap[item]
+ s = self.itemDataMap[index][col]
+ return s
+
+ def OnGetItemAttr(self, item):
+ """!Get item attributes"""
+ if ( item % 2) == 0:
+ return self.attr2
+ else:
+ return self.attr1
+
+ def OnColumnMenu(self, event):
+ """!Column heading right mouse button -> pop-up menu"""
+ self._col = event.GetColumn()
+
+ popupMenu = wx.Menu()
+
+ if not hasattr (self, "popupID1"):
+ self.popupID1 = wx.NewId()
+ self.popupID2 = wx.NewId()
+ self.popupID3 = wx.NewId()
+ self.popupID4 = wx.NewId()
+ self.popupID5 = wx.NewId()
+ self.popupID6 = wx.NewId()
+ self.popupID7 = wx.NewId()
+ self.popupID8 = wx.NewId()
+ self.popupID9 = wx.NewId()
+ self.popupID10 = wx.NewId()
+ self.popupID11 = wx.NewId()
+ self.popupID12 = wx.NewId()
+
+ popupMenu.Append(self.popupID1, text=_("Sort ascending"))
+ popupMenu.Append(self.popupID2, text=_("Sort descending"))
+ popupMenu.AppendSeparator()
+ subMenu = wx.Menu()
+ popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"),
+ subMenu)
+ if not self.log.parent.editable or \
+ self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType):
+ popupMenu.Enable(self.popupID3, False)
+
+ subMenu.Append(self.popupID4, text=_("Area size"))
+ subMenu.Append(self.popupID5, text=_("Line length"))
+ subMenu.Append(self.popupID6, text=_("Compactness of an area"))
+ subMenu.Append(self.popupID7, text=_("Fractal dimension of boundary defining a polygon"))
+ subMenu.Append(self.popupID8, text=_("Perimeter length of an area"))
+ subMenu.Append(self.popupID9, text=_("Number of features for each category"))
+ subMenu.Append(self.popupID10, text=_("Slope steepness of 3D line"))
+ subMenu.Append(self.popupID11, text=_("Line sinuousity"))
+ subMenu.Append(self.popupID12, text=_("Line azimuth"))
+
+ self.Bind (wx.EVT_MENU, self.OnColumnSortAsc, id=self.popupID1)
+ self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id=self.popupID2)
+ for id in (self.popupID4, self.popupID5, self.popupID6,
+ self.popupID7, self.popupID8, self.popupID9,
+ self.popupID10, self.popupID11, self.popupID12):
+ self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id)
+
+ self.PopupMenu(popupMenu)
+ popupMenu.Destroy()
+
+ def OnColumnSort(self, event):
+ """!Column heading left mouse button -> sorting"""
+ self._col = event.GetColumn()
+
+ self.ColumnSort()
+
+ event.Skip()
+
+ def OnColumnSortAsc(self, event):
+ """!Sort values of selected column (ascending)"""
+ self.SortListItems(col = self._col, ascending = True)
+ event.Skip()
+
+ def OnColumnSortDesc(self, event):
+ """!Sort values of selected column (descending)"""
+ self.SortListItems(col = self._col, ascending = False)
+ event.Skip()
+
+ def OnColumnCompute(self, event):
+ """!Compute values of selected column"""
+ id = event.GetId()
+
+ option = None
+ if id == self.popupID4:
+ option = 'area'
+ elif id == self.popupID5:
+ option = 'length'
+ elif id == self.popupID6:
+ option = 'compact'
+ elif id == self.popupID7:
+ option = 'fd'
+ elif id == self.popupID8:
+ option = 'perimeter'
+ elif id == self.popupID9:
+ option = 'count'
+ elif id == self.popupID10:
+ option = 'slope'
+ elif id == self.popupID11:
+ option = 'sinuous'
+ elif id == self.popupID12:
+ option = 'azimuth'
+
+ if not option:
+ return
+
+ 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)
+
+ def ColumnSort(self):
+ """!Sort values of selected column (self._col)"""
+ # remove duplicated arrow symbol from column header
+ # FIXME: should be done automatically
+ info = wx.ListItem()
+ info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
+ info.m_image = -1
+ for column in range(self.GetColumnCount()):
+ info.m_text = self.GetColumn(column).GetText()
+ self.SetColumn(column, info)
+
+ def SortItems(self, sorter=cmp):
+ """!Sort items"""
+ items = list(self.itemDataMap.keys())
+ items.sort(self.Sorter)
+ self.itemIndexMap = items
+
+ # redraw the list
+ self.Refresh()
+
+ def Sorter(self, key1, key2):
+ colName = self.GetColumn(self._col).GetText()
+ ascending = self._colSortFlag[self._col]
+ try:
+ item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col])
+ item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col])
+ except ValueError:
+ item1 = self.itemDataMap[key1][self._col]
+ item2 = self.itemDataMap[key2][self._col]
+
+ if type(item1) == type('') or type(item2) == type(''):
+ cmpVal = locale.strcoll(str(item1), str(item2))
+ else:
+ cmpVal = cmp(item1, item2)
+
+
+ # If the items are equal then pick something else to make the sort value unique
+ if cmpVal == 0:
+ cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
+
+ if ascending:
+ return cmpVal
+ else:
+ return -cmpVal
+
+ def GetSortImages(self):
+ """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
+ return (self.sm_dn, self.sm_up)
+
+ def IsEmpty(self):
+ """!Check if list if empty"""
+ if self.columns:
+ return False
+
+ return True
+
+class AttributeManager(wx.Frame):
+ def __init__(self, parent, id = wx.ID_ANY,
+ title = None, vectorName = None, item = None, log = None,
+ selection = None, **kwargs):
+ """!GRASS Attribute Table Manager window
+
+ @param parent parent window
+ @parem id window id
+ @param title window title or None for default title
+ @param vetorName name of vector map
+ @param item item from Layer Tree
+ @param log log window
+ @param selection name of page to be selected
+ @param kwagrs other wx.Frame's arguments
+ """
+ self.vectorName = vectorName
+ self.parent = parent # GMFrame
+ self.treeItem = item # item in layer tree
+ if self.parent and self.parent.GetName() == "LayerManager" and \
+ self.treeItem and not self.vectorName:
+ maptree = self.parent.curr_page.maptree
+ name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName()
+ self.vectorName = name
+
+ # vector attributes can be changed only if vector map is in
+ # the current mapset
+ if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
+ self.editable = True
+ else:
+ self.editable = False
+
+ self.cmdLog = log # self.parent.goutput
+
+ wx.Frame.__init__(self, parent, id, *kwargs)
+
+ # title
+ if not title:
+ self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
+ self.vectorName))
+ else:
+ self.SetTitle(title)
+
+ # icon
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
+
+ self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
+
+ try:
+ self.map = self.parent.curr_page.maptree.Map
+ self.mapdisplay = self.parent.curr_page.maptree.mapdisplay
+ except:
+ self.map = self.mapdisplay = None
+
+ # status bar log class
+ self.log = Log(self) # -> statusbar
+
+ # query map layer (if parent (GMFrame) is given)
+ self.qlayer = None
+
+ # -> layers / tables description
+ self.mapDBInfo = VectorDBInfo(self.vectorName)
+
+ # sqlbuilder
+ self.builder = None
+
+ if len(self.mapDBInfo.layers.keys()) == 0:
+ wx.MessageBox(parent=self.parent,
+ message=_("Database connection for vector map <%s> "
+ "is not defined in DB file. "
+ "You can define new connection in "
+ "'Manage layers' tab.") % self.vectorName,
+ caption=_("Attribute Table Manager"),
+ style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+
+ #
+ # list of command/SQL statements to be performed
+ #
+ self.listOfCommands = []
+ self.listOfSQLStatements = []
+
+ self.CreateStatusBar(number=1)
+
+ # set up virtual lists (each layer)
+ ### {layer: list, widgets...}
+ self.layerPage = {}
+
+ self.notebook = GNotebook(self.panel, style = globalvar.FNPageDStyle)
+
+ if globalvar.hasAgw:
+ dbmStyle = { 'agwStyle' : globalvar.FNPageStyle }
+ else:
+ dbmStyle = { 'style' : globalvar.FNPageStyle }
+
+ self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+ **dbmStyle)
+ self.notebook.AddPage(page = self.browsePage, text = _("Browse data"),
+ name = 'browse')
+ self.browsePage.SetTabAreaColour(globalvar.FNPageColor)
+
+ self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+ **dbmStyle)
+ self.notebook.AddPage(page = self.manageTablePage, text = _("Manage tables"),
+ name = 'table')
+ if not self.editable:
+ self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
+ self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor)
+
+ self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+ **dbmStyle)
+ self.notebook.AddPage(page = self.manageLayerPage, text = _("Manage layers"),
+ name = 'layers')
+ self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor)
+ if not self.editable:
+ self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
+
+ self._createBrowsePage()
+ self._createManageTablePage()
+ self._createManageLayerPage()
+
+ if selection:
+ wx.CallAfter(self.notebook.SetSelectionByName, selection) # select browse tab
+
+ # buttons
+ self.btnQuit = wx.Button(parent=self.panel, id=wx.ID_EXIT)
+ self.btnQuit.SetToolTipString(_("Close Attribute Table Manager"))
+ self.btnReload = wx.Button(parent=self.panel, id=wx.ID_REFRESH)
+ self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)"))
+
+ # events
+ self.btnQuit.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+ self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload)
+ self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+ self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage)
+ self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage)
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+ # do layout
+ self._layout()
+
+ # self.SetMinSize(self.GetBestSize())
+ self.SetSize((680, 550)) # FIXME hard-coded size
+ self.SetMinSize(self.GetSize())
+
+ def _createBrowsePage(self, onlyLayer=-1):
+ """!Create browse tab page"""
+ for layer in self.mapDBInfo.layers.keys():
+ if onlyLayer > 0 and layer != onlyLayer:
+ continue
+
+ panel = wx.Panel(parent=self.browsePage, id=wx.ID_ANY)
+
+ #IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the
+ # controls that are placed IN the wx.StaticBox, or it will freeze
+ # on the Mac
+
+ listBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Attribute data - right-click to edit/manage records"))
+ listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
+
+ win = VirtualAttributeList(panel, self.log,
+ self.mapDBInfo, layer)
+ if win.IsEmpty():
+ del panel
+ continue
+
+ win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated)
+
+ self.layerPage[layer] = {'browsePage': panel.GetId()}
+
+ label = _("Table")
+ if not self.editable:
+ label += _(" (readonly)")
+ self.browsePage.AddPage(page=panel, text=" %d / %s %s" % \
+ (layer, label, self.mapDBInfo.layers[layer]['table']))
+
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+ # attribute data
+ sqlBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("SQL Query"))
+
+ sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
+
+ win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW
+ win.Bind(wx.EVT_RIGHT_UP, self.OnDataRightUp) #wxGTK
+ if UserSettings.Get(group='atm', key='leftDbClick', subkey='selection') == 0:
+ win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit)
+ win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit)
+ else:
+ win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected)
+ win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected)
+
+
+ listSizer.Add(item=win, proportion=1,
+ flag=wx.EXPAND | wx.ALL,
+ border=3)
+
+ # sql statement box
+ btnApply = wx.Button(parent=panel, id=wx.ID_APPLY)
+ btnApply.SetToolTipString(_("Apply SELECT statement and reload data records"))
+ btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement)
+ btnSqlBuilder = wx.Button(parent=panel, id=wx.ID_ANY, label=_("SQL Builder"))
+ btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder)
+
+ sqlSimple = wx.RadioButton(parent=panel, id=wx.ID_ANY,
+ label=_("Simple"))
+ sqlSimple.SetValue(True)
+ sqlAdvanced = wx.RadioButton(parent=panel, id=wx.ID_ANY,
+ label=_("Advanced"))
+ sqlSimple.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
+ sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
+
+ sqlWhereColumn = wx.Choice(parent=panel, id=wx.ID_ANY,
+ size=(100,-1),
+ choices=self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table']))
+ sqlWhereValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value="",
+ style=wx.TE_PROCESS_ENTER)
+ sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10")
+
+ sqlStatement = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+ value="SELECT * FROM %s" % \
+ self.mapDBInfo.layers[layer]['table'],
+ style=wx.TE_PROCESS_ENTER)
+ sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10")
+ sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
+ sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
+
+ sqlLabel = wx.StaticText(parent=panel, id=wx.ID_ANY,
+ label="SELECT * FROM %s WHERE " % \
+ self.mapDBInfo.layers[layer]['table'])
+ label_query = wx.StaticText(parent=panel, id=wx.ID_ANY,
+ label="")
+
+ sqlFlexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
+ sqlFlexSizer.AddGrowableCol(1)
+
+ sqlFlexSizer.Add(item=sqlSimple,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL)
+ sqlSimpleSizer.Add(item=sqlLabel,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ sqlSimpleSizer.Add(item=sqlWhereColumn,
+ flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+ sqlSimpleSizer.Add(item=sqlWhereValue, proportion=1,
+ flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+ sqlFlexSizer.Add(item=sqlSimpleSizer,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+ sqlFlexSizer.Add(item=btnApply,
+ flag=wx.ALIGN_RIGHT)
+ sqlFlexSizer.Add(item=sqlAdvanced,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ sqlFlexSizer.Add(item=sqlStatement,
+ flag=wx.EXPAND)
+ sqlFlexSizer.Add(item=btnSqlBuilder,
+ flag=wx.ALIGN_RIGHT)
+
+ sqlSizer.Add(item=sqlFlexSizer,
+ flag=wx.ALL | wx.EXPAND,
+ border=3)
+
+ pageSizer.Add(item=listSizer,
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ pageSizer.Add(item=sqlSizer,
+ proportion=0,
+ flag=wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
+ border=5)
+
+ panel.SetSizer(pageSizer)
+
+ self.layerPage[layer]['data'] = win.GetId()
+ self.layerPage[layer]['simple'] = sqlSimple.GetId()
+ self.layerPage[layer]['advanced'] = sqlAdvanced.GetId()
+ self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId()
+ self.layerPage[layer]['where'] = sqlWhereValue.GetId()
+ self.layerPage[layer]['builder'] = btnSqlBuilder.GetId()
+ self.layerPage[layer]['statement'] = sqlStatement.GetId()
+
+
+ self.browsePage.SetSelection(0) # select first layer
+ try:
+ self.layer = self.mapDBInfo.layers.keys()[0]
+ self.OnChangeSql(None)
+ self.log.write(_("Number of loaded records: %d") % \
+ self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
+ except (IndexError, KeyError):
+ self.layer = None
+
+ def _createManageTablePage(self, onlyLayer=-1):
+ """!Create manage page (create/link and alter tables)"""
+ for layer in self.mapDBInfo.layers.keys():
+ if onlyLayer > 0 and layer != onlyLayer:
+ continue
+
+ if not layer in self.layerPage:
+ continue
+
+ panel = wx.Panel(parent=self.manageTablePage, id=wx.ID_ANY)
+ self.layerPage[layer]['tablePage'] = panel.GetId()
+ label = _("Table")
+ if not self.editable:
+ label += _(" (readonly)")
+ self.manageTablePage.AddPage(page=panel,
+ text=" %d / %s %s" % (layer, label,
+ self.mapDBInfo.layers[layer]['table']))
+
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+ #
+ # dbInfo
+ #
+ dbBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Database connection"))
+ dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
+ dbSizer.Add(item=createDbInfoDesc(panel, self.mapDBInfo, layer),
+ proportion=1,
+ flag=wx.EXPAND | wx.ALL,
+ border=3)
+
+ #
+ # table description
+ #
+ table = self.mapDBInfo.layers[layer]['table']
+ tableBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Table <%s> - right-click to delete column(s)") % table)
+
+ tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
+
+ list = self._createTableDesc(panel, table)
+ list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW
+ list.Bind(wx.EVT_RIGHT_UP, self.OnTableRightUp) #wxGTK
+ self.layerPage[layer]['tableData'] = list.GetId()
+
+ #
+ # add column
+ #
+ columnBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Manage columns"))
+
+ columnSizer = wx.StaticBoxSizer(columnBox, wx.VERTICAL)
+
+ addSizer = wx.FlexGridSizer (cols=5, hgap=3, vgap=3)
+ addSizer.AddGrowableCol(3)
+
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Column name"))
+ column = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value='',
+ size=(150, -1), style=wx.TE_PROCESS_ENTER)
+ column.Bind(wx.EVT_TEXT, self.OnTableAddColumnName)
+ column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd)
+ self.layerPage[layer]['addColName'] = column.GetId()
+ addSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ addSizer.Add(item=column,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ # data type
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Data type"))
+ addSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+
+ subSizer = wx.BoxSizer(wx.HORIZONTAL)
+ type = wx.Choice (parent=panel, id=wx.ID_ANY,
+ choices = ["integer",
+ "double",
+ "varchar",
+ "date"]) # FIXME
+ type.SetSelection(0)
+ type.Bind(wx.EVT_CHOICE, self.OnTableChangeType)
+ self.layerPage[layer]['addColType'] = type.GetId()
+ subSizer.Add(item=type,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+ border=3)
+ # length
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Data length"))
+ length = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
+ initial=250,
+ min=1, max=1e6)
+ length.Enable(False)
+ self.layerPage[layer]['addColLength'] = length.GetId()
+ subSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+ border=3)
+ subSizer.Add(item=length,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+ border=3)
+
+ addSizer.Add(item=subSizer,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+ border=3)
+
+ btnAddCol = wx.Button(parent=panel, id=wx.ID_ANY, label=_("Add"))
+ btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd)
+ btnAddCol.Enable(False)
+ self.layerPage[layer]['addColButton'] = btnAddCol.GetId()
+ addSizer.Add(item=btnAddCol,
+ proportion=0,
+ flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE |
+ wx.ALIGN_CENTER_VERTICAL )
+
+ # rename col
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Rename column"))
+ column = wx.ComboBox(parent=panel, id=wx.ID_ANY, size=(150, -1),
+ style=wx.CB_SIMPLE | wx.CB_READONLY,
+ choices=self.mapDBInfo.GetColumns(table))
+ column.SetSelection(0)
+ self.layerPage[layer]['renameCol'] = column.GetId()
+ addSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ addSizer.Add(item=column,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("To"))
+ columnTo = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value='',
+ size=(150, -1), style=wx.TE_PROCESS_ENTER)
+ columnTo.Bind(wx.EVT_TEXT, self.OnTableRenameColumnName)
+ columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange)
+ self.layerPage[layer]['renameColTo'] = columnTo.GetId()
+ addSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER)
+ addSizer.Add(item=columnTo,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ btnRenameCol = wx.Button(parent=panel, id=wx.ID_ANY, label=_("&Rename"))
+ btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange)
+ btnRenameCol.Enable(False)
+ self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId()
+
+ addSizer.Add(item=btnRenameCol,
+ proportion=0,
+ flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE |
+ wx.ALIGN_CENTER_VERTICAL)
+
+ columnSizer.Add(item=addSizer, proportion=1,
+ flag=wx.ALL | wx.EXPAND, border=3)
+
+ tableSizer.Add(item=list,
+ flag=wx.ALL | wx.EXPAND,
+ proportion=1,
+ border=3)
+
+ pageSizer.Add(item=dbSizer,
+ flag=wx.ALL | wx.EXPAND,
+ proportion=0,
+ border=3)
+
+ pageSizer.Add(item=tableSizer,
+ flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+ proportion=1,
+ border=3)
+
+ pageSizer.Add(item=columnSizer,
+ flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+ proportion=0,
+ border=3)
+
+ panel.SetSizer(pageSizer)
+
+ self.manageTablePage.SetSelection(0) # select first layer
+ try:
+ self.layer = self.mapDBInfo.layers.keys()[0]
+ except IndexError:
+ self.layer = None
+
+ def _createTableDesc(self, parent, table):
+ """!Create list with table description"""
+ list = TableListCtrl(parent=parent, id=wx.ID_ANY,
+ table=self.mapDBInfo.tables[table],
+ columns=self.mapDBInfo.GetColumns(table))
+ list.Populate()
+ # sorter
+ # itemDataMap = list.Populate()
+ # listmix.ColumnSorterMixin.__init__(self, 2)
+
+ return list
+
+ def _createManageLayerPage(self):
+ """!Create manage page"""
+ splitterWin = wx.SplitterWindow(parent=self.manageLayerPage, id=wx.ID_ANY)
+ splitterWin.SetMinimumPaneSize(100)
+
+ label = _("Layers of vector map")
+ if not self.editable:
+ label += _(" (readonly)")
+ self.manageLayerPage.AddPage(page=splitterWin,
+ text=label) # dummy page
+
+ #
+ # list of layers
+ #
+ panelList = wx.Panel(parent=splitterWin, id=wx.ID_ANY)
+
+ panelListSizer = wx.BoxSizer(wx.VERTICAL)
+ layerBox = wx.StaticBox(parent=panelList, id=wx.ID_ANY,
+ label=" %s " % _("List of layers"))
+ layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
+
+ self.layerList = self._createLayerDesc(panelList)
+ self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW
+ self.layerList.Bind(wx.EVT_RIGHT_UP, self.OnLayerRightUp) #wxGTK
+
+ layerSizer.Add(item=self.layerList,
+ flag=wx.ALL | wx.EXPAND,
+ proportion=1,
+ border=3)
+
+ panelListSizer.Add(item=layerSizer,
+ flag=wx.ALL | wx.EXPAND,
+ proportion=1,
+ border=3)
+
+ panelList.SetSizer(panelListSizer)
+
+ #
+ # manage part
+ #
+ panelManage = wx.Panel(parent=splitterWin, id=wx.ID_ANY)
+
+ manageSizer = wx.BoxSizer(wx.VERTICAL)
+
+ self.manageLayerBook = LayerBook(parent=panelManage, id=wx.ID_ANY,
+ parentDialog=self)
+
+ manageSizer.Add(item=self.manageLayerBook,
+ proportion=1,
+ flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+ border=5)
+
+ panelManage.SetSizer(manageSizer)
+ splitterWin.SplitHorizontally(panelList, panelManage, 100)
+ splitterWin.Fit()
+
+ def _createLayerDesc(self, parent):
+ """!Create list of linked layers"""
+ list = LayerListCtrl(parent=parent, id=wx.ID_ANY,
+ layers=self.mapDBInfo.layers)
+
+ list.Populate()
+ # sorter
+ # itemDataMap = list.Populate()
+ # listmix.ColumnSorterMixin.__init__(self, 2)
+
+ return list
+
+ def _layout(self):
+ """!Do layout"""
+ # frame body
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+ # buttons
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item=self.btnReload, proportion=1,
+ flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+ btnSizer.Add(item=self.btnQuit, proportion=1,
+ flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+
+ mainSizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND)
+ mainSizer.Add(item=btnSizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizer(mainSizer)
+ mainSizer.Fit(self.panel)
+ self.Layout()
+
+ def OnDataRightUp(self, event):
+ """!Table description area, context menu"""
+ if not hasattr(self, "popupDataID1"):
+ self.popupDataID1 = wx.NewId()
+ self.popupDataID2 = wx.NewId()
+ self.popupDataID3 = wx.NewId()
+ self.popupDataID4 = wx.NewId()
+ self.popupDataID5 = wx.NewId()
+ self.popupDataID6 = wx.NewId()
+ self.popupDataID7 = wx.NewId()
+ self.popupDataID8 = wx.NewId()
+ self.popupDataID9 = wx.NewId()
+ self.popupDataID10 = wx.NewId()
+ self.popupDataID11 = wx.NewId()
+
+ self.Bind(wx.EVT_MENU, self.OnDataItemEdit, id=self.popupDataID1)
+ self.Bind(wx.EVT_MENU, self.OnDataItemAdd, id=self.popupDataID2)
+ self.Bind(wx.EVT_MENU, self.OnDataItemDelete, id=self.popupDataID3)
+ self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll, id=self.popupDataID4)
+ self.Bind(wx.EVT_MENU, self.OnDataSelectAll, id=self.popupDataID5)
+ self.Bind(wx.EVT_MENU, self.OnDataSelectNone, id=self.popupDataID6)
+ self.Bind(wx.EVT_MENU, self.OnDataDrawSelected, id=self.popupDataID7)
+ self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id=self.popupDataID8)
+ self.Bind(wx.EVT_MENU, self.OnExtractSelected, id=self.popupDataID9)
+ self.Bind(wx.EVT_MENU, self.OnDeleteSelected, id=self.popupDataID11)
+ self.Bind(wx.EVT_MENU, self.OnDataReload, id=self.popupDataID10)
+
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupDataID1, _("Edit selected record"))
+ selected = list.GetFirstSelected()
+ if not self.editable or selected == -1 or list.GetNextSelected(selected) != -1:
+ menu.Enable(self.popupDataID1, False)
+ menu.Append(self.popupDataID2, _("Insert new record"))
+ menu.Append(self.popupDataID3, _("Delete selected record(s)"))
+ menu.Append(self.popupDataID4, _("Delete all records"))
+ if not self.editable:
+ menu.Enable(self.popupDataID2, False)
+ menu.Enable(self.popupDataID3, False)
+ menu.Enable(self.popupDataID4, False)
+ menu.AppendSeparator()
+ menu.Append(self.popupDataID5, _("Select all"))
+ menu.Append(self.popupDataID6, _("Deselect all"))
+ menu.AppendSeparator()
+ menu.Append(self.popupDataID7, _("Highlight selected features"))
+ menu.Append(self.popupDataID8, _("Highlight selected features and zoom"))
+ if not self.map or len(list.GetSelectedItems()) == 0:
+ menu.Enable(self.popupDataID7, False)
+ menu.Enable(self.popupDataID8, False)
+ menu.Append(self.popupDataID9, _("Extract selected features"))
+ menu.Append(self.popupDataID11, _("Delete selected features"))
+ if not self.editable:
+ menu.Enable(self.popupDataID11, False)
+ if list.GetFirstSelected() == -1:
+ menu.Enable(self.popupDataID3, False)
+ menu.Enable(self.popupDataID9, False)
+ menu.Enable(self.popupDataID11, False)
+ menu.AppendSeparator()
+ menu.Append(self.popupDataID10, _("Reload"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ # update statusbar
+ self.log.write(_("Number of loaded records: %d") % \
+ list.GetItemCount())
+
+ def OnDataItemDelete(self, event):
+ """!Delete selected item(s) from the list (layer/category pair)"""
+ dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+ item = dlist.GetFirstSelected()
+
+ table = self.mapDBInfo.layers[self.layer]["table"]
+ key = self.mapDBInfo.layers[self.layer]["key"]
+
+ indeces = []
+ # collect SQL statements
+ while item != -1:
+ index = dlist.itemIndexMap[item]
+ indeces.append(index)
+
+ cat = dlist.itemCatsMap[index]
+
+ self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \
+ (table, key, cat))
+
+ item = dlist.GetNextSelected(item)
+
+ if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
+ deleteDialog = wx.MessageBox(parent=self,
+ message=_("Selected data records (%d) will permanently deleted "
+ "from table. Do you want to delete them?") % \
+ (len(self.listOfSQLStatements)),
+ caption=_("Delete records"),
+ style=wx.YES_NO | wx.CENTRE)
+ if deleteDialog != wx.YES:
+ self.listOfSQLStatements = []
+ return False
+
+ # restore maps
+ i = 0
+ indexTemp = copy.copy(dlist.itemIndexMap)
+ dlist.itemIndexMap = []
+ dataTemp = copy.deepcopy(dlist.itemDataMap)
+ dlist.itemDataMap = {}
+ catsTemp = copy.deepcopy(dlist.itemCatsMap)
+ dlist.itemCatsMap = {}
+
+ i = 0
+ for index in indexTemp:
+ if index in indeces:
+ continue
+ dlist.itemIndexMap.append(i)
+ dlist.itemDataMap[i] = dataTemp[index]
+ dlist.itemCatsMap[i] = catsTemp[index]
+
+ i += 1
+
+ dlist.SetItemCount(len(dlist.itemIndexMap))
+
+ # deselect items
+ item = dlist.GetFirstSelected()
+ while item != -1:
+ dlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
+ item = dlist.GetNextSelected(item)
+
+ # submit SQL statements
+ self.ApplyCommands()
+
+ return True
+
+ def OnDataItemDeleteAll(self, event):
+ """!Delete all items from the list"""
+ dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+ if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
+ deleteDialog = wx.MessageBox(parent=self,
+ message=_("All data records (%d) will permanently deleted "
+ "from table. Do you want to delete them?") % \
+ (len(dlist.itemIndexMap)),
+ caption=_("Delete records"),
+ style=wx.YES_NO | wx.CENTRE)
+ if deleteDialog != wx.YES:
+ return
+
+ dlist.DeleteAllItems()
+ dlist.itemDataMap = {}
+ dlist.itemIndexMap = []
+ dlist.SetItemCount(0)
+
+ table = self.mapDBInfo.layers[self.layer]["table"]
+ self.listOfSQLStatements.append('DELETE FROM %s' % table)
+
+ self.ApplyCommands()
+
+ event.Skip()
+
+ def _drawSelected(self, zoom):
+ """!Highlight selected features"""
+ if not self.map or not self.mapdisplay:
+ return
+
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ cats = map(int, list.GetSelectedItems())
+
+ digitToolbar = None
+ if 'vdigit' in self.mapdisplay.toolbars:
+ digitToolbar = self.mapdisplay.toolbars['vdigit']
+ if digitToolbar and digitToolbar.GetLayer() and \
+ digitToolbar.GetLayer().GetName() == self.vectorName:
+
+ self.mapdisplay.digit.driver.SetSelected(cats, field=self.layer)
+ if zoom:
+ n, s, w, e = self.mapdisplay.digit.driver.GetRegionSelected()
+ self.mapdisplay.Map.GetRegion(n=n, s=s, w=w, e=e,
+ update=True)
+ else:
+ # add map layer with higlighted vector features
+ self.AddQueryMapLayer() # -> self.qlayer
+
+ # set opacity based on queried layer
+ if self.parent and self.parent.GetName() == "LayerManager" and \
+ self.treeItem:
+ maptree = self.parent.curr_page.maptree
+ opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float=True)
+ self.qlayer.SetOpacity(opacity)
+ if zoom:
+ keyColumn = self.mapDBInfo.layers[self.layer]['key']
+ where = ''
+ for range in ListOfCatsToRange(cats).split(','):
+ if '-' in range:
+ min, max = range.split('-')
+ where += '%s >= %d and %s <= %d or ' % \
+ (keyColumn, int(min),
+ keyColumn, int(max))
+ else:
+ where += '%s = %d or ' % (keyColumn, int(range))
+ where = where.rstrip('or ')
+
+ 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():
+ key, value = line.split('=')
+ region[key.strip()] = float(value.strip())
+
+ self.mapdisplay.Map.GetRegion(n=region['n'], s=region['s'],
+ w=region['w'], e=region['e'],
+ update=True)
+
+ if zoom:
+ self.mapdisplay.Map.AdjustRegion() # adjust resolution
+ self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent
+ self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
+ else:
+ self.mapdisplay.MapWindow.UpdateMap(render=False, renderVector=True)
+
+ def OnDataDrawSelected(self, event):
+ """!Reload table description"""
+ self._drawSelected(zoom=False)
+ event.Skip()
+
+ def OnDataDrawSelectedZoom(self, event):
+ self._drawSelected(zoom=True)
+ event.Skip()
+
+ def OnDataItemAdd(self, event):
+ """!Add new record to the attribute table"""
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ table = self.mapDBInfo.layers[self.layer]['table']
+ keyColumn = self.mapDBInfo.layers[self.layer]['key']
+
+ # (column name, value)
+ data = []
+
+ # collect names of all visible columns
+ columnName = []
+ for i in range(list.GetColumnCount()):
+ columnName.append(list.GetColumn(i).GetText())
+
+ # maximal category number
+ if len(list.itemCatsMap.values()) > 0:
+ maxCat = max(list.itemCatsMap.values())
+ else:
+ maxCat = 0 # starting category '1'
+
+ # key column must be always presented
+ if keyColumn not in columnName:
+ columnName.insert(0, keyColumn) # insert key column on first position
+ data.append((keyColumn, str(maxCat + 1)))
+ missingKey = True
+ else:
+ missingKey = False
+
+ # add other visible columns
+ colIdx = 0
+ keyId = -1
+ for col in columnName:
+ if col == keyColumn: # key
+ if missingKey is False:
+ data.append((col, str(maxCat + 1)))
+ keyId = colIdx
+ else:
+ data.append((col, ''))
+ colIdx += 1
+
+ dlg = ModifyTableRecord(parent = self,
+ title = _("Insert new record"),
+ data = data, keyEditable = (keyId, True))
+
+ if dlg.ShowModal() == wx.ID_OK:
+ try: # get category number
+ cat = int(dlg.GetValues(columns=[keyColumn])[0])
+ except:
+ cat = -1
+
+ try:
+ if cat in list.itemCatsMap.values():
+ raise ValueError(_("Record with category number %d "
+ "already exists in the table.") % cat)
+
+ values = dlg.GetValues() # values (need to be casted)
+ columnsString = ''
+ valuesString = ''
+
+ for i in range(len(values)):
+ if len(values[i]) == 0: # NULL
+ if columnName[i] == keyColumn:
+ raise ValueError(_("Category number (column %s)"
+ " is missing.") % keyColumn)
+ else:
+ continue
+
+ try:
+ if list.columns[columnName[i]]['ctype'] == int:
+ # values[i] is stored as text.
+ value = float(values[i])
+ else:
+ value = values[i]
+ values[i] = list.columns[columnName[i]]['ctype'] (value)
+
+ except:
+ raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") %
+ {'value' : str(values[i]),
+ 'type' : list.columns[columnName[i]]['type']})
+ columnsString += '%s,' % columnName[i]
+ if list.columns[columnName[i]]['ctype'] == str:
+ valuesString += "'%s'," % values[i]
+ else:
+ valuesString += "%s," % values[i]
+
+ except ValueError, err:
+ wx.MessageBox(parent=self,
+ message="%s%s%s" % (_("Unable to insert new record."),
+ os.linesep, err),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ # remove category if need
+ if missingKey is True:
+ del values[0]
+
+ # add new item to the list
+ if len(list.itemIndexMap) > 0:
+ index = max(list.itemIndexMap) + 1
+ else:
+ index = 0
+
+ list.itemIndexMap.append(index)
+ list.itemDataMap[index] = values
+ list.itemCatsMap[index] = cat
+ list.SetItemCount(list.GetItemCount() + 1)
+
+ self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \
+ (table,
+ columnsString.strip(','),
+ valuesString.strip(',')))
+ self.ApplyCommands()
+
+ def OnDataItemEdit(self, event):
+ """!Edit selected record of the attribute table"""
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ item = list.GetFirstSelected()
+ if item == -1:
+ return
+
+ table = self.mapDBInfo.layers[self.layer]['table']
+ keyColumn = self.mapDBInfo.layers[self.layer]['key']
+ cat = list.itemCatsMap[list.itemIndexMap[item]]
+
+ # (column name, value)
+ data = []
+
+ # collect names of all visible columns
+ columnName = []
+ for i in range(list.GetColumnCount()):
+ columnName.append(list.GetColumn(i).GetText())
+
+
+ # key column must be always presented
+ if keyColumn not in columnName:
+ columnName.insert(0, keyColumn) # insert key column on first position
+ data.append((keyColumn, str(cat)))
+ keyId = 0
+ missingKey = True
+ else:
+ missingKey = False
+
+ # add other visible columns
+ for i in range(len(columnName)):
+ if columnName[i] == keyColumn: # key
+ if missingKey is False:
+ data.append((columnName[i], str(cat)))
+ keyId = i
+ else:
+ if missingKey is True:
+ value = list.GetItem(item, i-1).GetText()
+ else:
+ value = list.GetItem(item, i).GetText()
+ data.append((columnName[i], value))
+
+ dlg = ModifyTableRecord(parent = self,
+ title = _("Update existing record"),
+ data = data, keyEditable = (keyId, False))
+
+ if dlg.ShowModal() == wx.ID_OK:
+ values = dlg.GetValues() # string
+ updateString = ''
+ try:
+ for i in range(len(values)):
+ if i == keyId: # skip key column
+ continue
+ if list.GetItem(item, i).GetText() != values[i]:
+ if len(values[i]) > 0:
+ try:
+ if missingKey is True:
+ idx = i - 1
+ else:
+ idx = i
+ if list.columns[columnName[i]]['ctype'] != type(''):
+ if list.columns[columnName[i]]['ctype'] == int:
+ value = float(values[i])
+ else:
+ value = values[i]
+ list.itemDataMap[item][idx] = \
+ list.columns[columnName[i]]['ctype'] (value)
+ else:
+ list.itemDataMap[item][idx] = values[i]
+ except:
+ raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \
+ {'value' : str(values[i]),
+ 'type' : list.columns[columnName[i]]['type']})
+
+ if list.columns[columnName[i]]['ctype'] == str:
+ updateString += "%s='%s'," % (columnName[i], values[i])
+ else:
+ updateString += "%s=%s," % (columnName[i], values[i])
+ else: # NULL
+ updateString += "%s=NULL," % (columnName[i])
+
+ except ValueError, err:
+ wx.MessageBox(parent=self,
+ message="%s%s%s" % (_("Unable to update existing record."),
+ os.linesep, err),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ if len(updateString) > 0:
+ self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \
+ (table, updateString.strip(','),
+ keyColumn, cat))
+ self.ApplyCommands()
+
+ list.Update(self.mapDBInfo)
+
+ def OnDataReload(self, event):
+ """!Reload list of records"""
+ self.OnApplySqlStatement(None)
+ self.listOfSQLStatements = []
+
+ def OnDataSelectAll(self, event):
+ """!Select all items"""
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ item = -1
+
+ while True:
+ item = list.GetNextItem(item)
+ if item == -1:
+ break
+ list.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
+
+ event.Skip()
+
+ def OnDataSelectNone(self, event):
+ """!Deselect items"""
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ item = -1
+
+ while True:
+ item = list.GetNextItem(item, wx.LIST_STATE_SELECTED)
+ if item == -1:
+ break
+ list.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
+
+ event.Skip()
+
+
+ def OnTableChangeType(self, event):
+ """!Data type for new column changed. Enable or disable
+ data length widget"""
+ win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
+ if event.GetString() == "varchar":
+ win.Enable(True)
+ else:
+ win.Enable(False)
+
+ def OnTableRenameColumnName(self, event):
+ """!Editing column name to be added to the table"""
+ btn = self.FindWindowById(self.layerPage[self.layer]['renameColButton'])
+ col = self.FindWindowById(self.layerPage[self.layer]['renameCol'])
+ colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo'])
+ if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0:
+ btn.Enable(True)
+ else:
+ btn.Enable(False)
+
+ event.Skip()
+
+ def OnTableAddColumnName(self, event):
+ """!Editing column name to be added to the table"""
+ btn = self.FindWindowById(self.layerPage[self.layer]['addColButton'])
+ if len(event.GetString()) > 0:
+ btn.Enable(True)
+ else:
+ btn.Enable(False)
+
+ event.Skip()
+
+ def OnTableItemChange(self, event):
+ """!Rename column in the table"""
+ list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+ name = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue()
+ nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue()
+
+ table = self.mapDBInfo.layers[self.layer]["table"]
+
+ if not name or not nameTo:
+ wx.MessageBox(self=self,
+ message=_("Unable to rename column. "
+ "No column name defined."),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+ else:
+ item = list.FindItem(start=-1, str=name)
+ if item > -1:
+ if list.FindItem(start=-1, str=nameTo) > -1:
+ wx.MessageBox(parent=self,
+ message=_("Unable to rename column <%(column)s> to "
+ "<%(columnTo)s>. Column already exists "
+ "in the table <%(table)s>.") % \
+ {'column' : name, 'columnTo' : nameTo,
+ 'table' : table},
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+ else:
+ list.SetItemText(item, nameTo)
+
+ self.listOfCommands.append(('v.db.renamecolumn',
+ { 'map' : self.vectorName,
+ 'layer' : self.layer,
+ 'column' : '%s,%s' % (name, nameTo) }
+ ))
+ else:
+ wx.MessageBox(parent=self,
+ message=_("Unable to rename column. "
+ "Column <%(column)s> doesn't exist in the table <%(table)s>.") %
+ {'column' : name, 'table' : table},
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ # apply changes
+ self.ApplyCommands()
+
+ # update widgets
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+ self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('')
+
+ event.Skip()
+
+ def OnTableRightUp(self, event):
+ """!Table description area, context menu"""
+ if not hasattr(self, "popupTableID"):
+ self.popupTableID1 = wx.NewId()
+ self.popupTableID2 = wx.NewId()
+ self.popupTableID3 = wx.NewId()
+ self.Bind(wx.EVT_MENU, self.OnTableItemDelete, id=self.popupTableID1)
+ self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id=self.popupTableID2)
+ self.Bind(wx.EVT_MENU, self.OnTableReload, id=self.popupTableID3)
+
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupTableID1, _("Drop selected column"))
+ if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1:
+ menu.Enable(self.popupTableID1, False)
+ menu.Append(self.popupTableID2, _("Drop all columns"))
+ menu.AppendSeparator()
+ menu.Append(self.popupTableID3, _("Reload"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def OnTableItemDelete(self, event):
+ """!Delete selected item(s) from the list"""
+ list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+
+ item = list.GetFirstSelected()
+
+ if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
+ deleteDialog = wx.MessageBox(parent=self,
+ message=_("Selected column '%s' will PERMANENTLY removed "
+ "from table. Do you want to drop the column?") % \
+ (list.GetItemText(item)),
+ caption=_("Drop column(s)"),
+ style=wx.YES_NO | wx.CENTRE)
+ if deleteDialog != wx.YES:
+ return False
+
+ while item != -1:
+ self.listOfCommands.append(('v.db.dropcolumn',
+ { 'map' : self.vectorName,
+ 'layer' : self.layer,
+ 'column' : list.GetItemText(item) }
+ ))
+ list.DeleteItem(item)
+ item = list.GetFirstSelected()
+
+ # apply changes
+ self.ApplyCommands()
+
+ # update widgets
+ table = self.mapDBInfo.layers[self.layer]['table']
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+
+ event.Skip()
+
+ def OnTableItemDeleteAll(self, event):
+ """!Delete all items from the list"""
+ table = self.mapDBInfo.layers[self.layer]['table']
+ cols = self.mapDBInfo.GetColumns(table)
+ keyColumn = self.mapDBInfo.layers[self.layer]['key']
+ if keyColumn in cols:
+ cols.remove(keyColumn)
+
+ if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
+ deleteDialog = wx.MessageBox(parent=self,
+ message=_("Selected columns\n%s\nwill PERMANENTLY removed "
+ "from table. Do you want to drop the columns?") % \
+ ('\n'.join(cols)),
+ caption=_("Drop column(s)"),
+ style=wx.YES_NO | wx.CENTRE)
+ if deleteDialog != wx.YES:
+ return False
+
+ for col in cols:
+ self.listOfCommands.append(('v.db.dropcolumn',
+ { 'map' : self.vectorName,
+ 'layer' : self.layer,
+ 'column' : col }
+ ))
+ self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
+
+ # apply changes
+ self.ApplyCommands()
+
+ # update widgets
+ table = self.mapDBInfo.layers[self.layer]['table']
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+
+ event.Skip()
+
+ def OnTableReload(self, event=None):
+ """!Reload table description"""
+ self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update=True)
+ self.listOfCommands = []
+
+ def OnTableItemAdd(self, event):
+ """!Add new column to the table"""
+ table = self.mapDBInfo.layers[self.layer]['table']
+ name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
+
+ if not name:
+ GError(parent = self,
+ message = _("Unable to add column to the table. "
+ "No column name defined."))
+ return
+
+ ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
+ GetStringSelection()
+
+ # cast type if needed
+ if ctype == 'double':
+ ctype = 'double precision'
+ if ctype == 'varchar':
+ length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \
+ GetValue())
+ else:
+ length = '' # FIXME
+
+ # add item to the list of table columns
+ tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+ # check for duplicate items
+ if tlist.FindItem(start=-1, str=name) > -1:
+ 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))
+ tlist.SetStringItem(index, 1, str(ctype))
+ tlist.SetStringItem(index, 2, str(length))
+
+ # add v.db.addcolumn command to the list
+ if ctype == 'varchar':
+ ctype += ' (%d)' % length
+ self.listOfCommands.append(('v.db.addcolumn',
+ { 'map' : self.vectorName,
+ 'layer' : self.layer,
+ 'columns' : '%s %s' % (name, ctype) }
+ ))
+ # apply changes
+ self.ApplyCommands()
+
+ # update widgets
+ self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('')
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+ self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+
+ event.Skip()
+
+ def OnLayerPageChanged(self, event):
+ """!Layer tab changed"""
+ pageNum = event.GetSelection()
+ self.layer = self.mapDBInfo.layers.keys()[pageNum]
+
+ try:
+ idCol = self.layerPage[self.layer]['whereColumn']
+ except KeyError:
+ idCol = None
+
+ try:
+ self.OnChangeSql(None)
+ # update statusbar
+ self.log.write(_("Number of loaded records: %d") % \
+ self.FindWindowById(self.layerPage[self.layer]['data']).\
+ GetItemCount())
+ except:
+ pass
+
+ if idCol:
+ winCol = self.FindWindowById(idCol)
+ table = self.mapDBInfo.layers[self.layer]["table"]
+ self.mapDBInfo.GetColumns(table)
+
+ event.Skip()
+
+ def OnPageChanged(self, event):
+ try:
+ id = self.layerPage[self.layer]['data']
+ except KeyError:
+ id = None
+
+ if event.GetSelection() == 0 and id:
+ win = self.FindWindowById(id)
+ if win:
+ self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
+ else:
+ self.log.write("")
+ self.btnReload.Enable()
+ else:
+ self.log.write("")
+ self.btnReload.Enable(False)
+
+ event.Skip()
+
+ def OnLayerRightUp(self, event):
+ """!Layer description area, context menu"""
+ pass
+
+ def OnChangeSql(self, event):
+ """!Switch simple/advanced sql statement"""
+ if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
+ self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True)
+ self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False)
+ self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False)
+ else:
+ self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False)
+ self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True)
+ self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
+
+ def ApplyCommands(self):
+ """!Apply changes"""
+ # perform GRASS commands (e.g. v.db.addcolumn)
+ wx.BeginBusyCursor()
+
+ if len(self.listOfCommands) > 0:
+ for cmd in self.listOfCommands:
+ RunCommand(prog = cmd[0],
+ quiet = True,
+ parent = self,
+ **cmd[1])
+
+ self.mapDBInfo = VectorDBInfo(self.vectorName)
+ table = self.mapDBInfo.layers[self.layer]['table']
+
+ # update table description
+ list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+ list.Update(table=self.mapDBInfo.tables[table],
+ columns=self.mapDBInfo.GetColumns(table))
+ self.OnTableReload(None)
+
+ # update data list
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ list.Update(self.mapDBInfo)
+
+ # reset list of commands
+ self.listOfCommands = []
+
+ # perform SQL non-select statements (e.g. 'delete from table where cat=1')
+ if len(self.listOfSQLStatements) > 0:
+ sqlFile = tempfile.NamedTemporaryFile(mode="wt")
+ for sql in self.listOfSQLStatements:
+ enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
+ if not enc and 'GRASS_DB_ENCODING' in os.environ:
+ enc = os.environ['GRASS_DB_ENCODING']
+ if enc:
+ sqlFile.file.write(sql.encode(enc) + ';')
+ else:
+ sqlFile.file.write(sql + ';')
+ sqlFile.file.write(os.linesep)
+ sqlFile.file.flush()
+
+ driver = self.mapDBInfo.layers[self.layer]["driver"]
+ database = self.mapDBInfo.layers[self.layer]["database"]
+
+ Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
+ ';'.join(["%s" % s for s in self.listOfSQLStatements]))
+
+ RunCommand('db.execute',
+ parent = self,
+ input = sqlFile.name,
+ driver = driver,
+ database = database)
+
+ # reset list of statements
+ self.listOfSQLStatements = []
+
+ wx.EndBusyCursor()
+
+ def OnApplySqlStatement(self, event):
+ """!Apply simple/advanced sql statement"""
+ keyColumn = -1 # index of key column
+ listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
+ sql = None
+
+ wx.BeginBusyCursor()
+
+ if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
+ # simple sql statement
+ whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
+ whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip()
+ try:
+ if len(whereVal) > 0:
+ keyColumn = listWin.LoadData(self.layer, where=whereCol + whereVal)
+ else:
+ keyColumn = listWin.LoadData(self.layer)
+ 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
+ win = self.FindWindowById(self.layerPage[self.layer]['statement'])
+ try:
+ cols, where = self.ValidateSelectStatement(win.GetValue())
+ if cols is None and where is None:
+ sql = win.GetValue()
+ except TypeError:
+ wx.MessageBox(parent=self,
+ message=_("Loading attribute data failed.\n"
+ "Invalid SQL select statement.\n\n%s") % win.GetValue(),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
+ cols = None
+ where = None
+
+ if cols or where or sql:
+ try:
+ keyColumn = listWin.LoadData(self.layer, columns=cols,
+ where=where, sql=sql)
+ 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
+ if sql and 'order by' in sql.lower():
+ pass # don't order by key column
+ else:
+ if keyColumn > -1:
+ listWin.SortListItems(col=keyColumn, ascending=True)
+ else:
+ listWin.SortListItems(col=0, ascending=True)
+
+ wx.EndBusyCursor()
+
+ # update statusbar
+ self.log.write(_("Number of loaded records: %d") % \
+ self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
+
+ def ValidateSelectStatement(self, statement):
+ """!Validate SQL select statement
+
+ @return (columns, where)
+ @return None on error
+ """
+ if statement[0:7].lower() != 'select ':
+ return None
+
+ cols = ''
+ index = 7
+ for c in statement[index:]:
+ if c == ' ':
+ break
+ cols += c
+ index += 1
+ if cols == '*':
+ cols = None
+ else:
+ cols = cols.split(',')
+
+ tablelen = len(self.mapDBInfo.layers[self.layer]['table'])
+
+ if statement[index+1:index+6].lower() != 'from ' or \
+ statement[index+6:index+6+tablelen] != '%s' % \
+ (self.mapDBInfo.layers[self.layer]['table']):
+ return None
+
+ if len(statement[index+7+tablelen:]) > 0:
+ index = statement.lower().find('where ')
+ if index > -1:
+ where = statement[index+6:]
+ else:
+ where = None
+ else:
+ where = None
+
+ return (cols, where)
+
+ def OnCloseWindow(self, event):
+ """!Cancel button pressed"""
+ if self.parent and self.parent.GetName() == 'LayerManager':
+ # deregister ATM
+ self.parent.dialogs['atm'].remove(self)
+
+ if not isinstance(event, wx.CloseEvent):
+ self.Destroy()
+
+ event.Skip()
+
+ def OnBuilder(self,event):
+ """!SQL Builder button pressed -> show the SQLBuilder dialog"""
+ if not self.builder:
+ 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()
+
+ def OnBuilderEvt(self, event):
+ if event == 'apply':
+ sqlstr = self.builder.GetSQLStatement()
+ self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr)
+ if self.builder.CloseOnApply():
+ self.builder = None
+ elif event == 'close':
+ self.builder = None
+
+ def OnTextEnter(self, event):
+ pass
+
+ def OnDataItemActivated(self, event):
+ """!Item activated, highlight selected item"""
+ self.OnDataDrawSelected(event)
+
+ event.Skip()
+
+ def OnExtractSelected(self, event):
+ """!Extract vector objects selected in attribute browse window
+ to new vector map
+ """
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ # cats = list.selectedCats[:]
+ cats = list.GetSelectedItems()
+ if len(cats) == 0:
+ wx.MessageBox(parent=self,
+ message=_('Nothing to extract.'),
+ caption=_('Message'), style=wx.CENTRE)
+ return
+ else:
+ # dialog to get file name
+ 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
+
+ name = dlg.GetName(full = True)
+ if name and dlg.IsChecked('add'):
+ # add layer to map layer tree
+ self.parent.curr_page.maptree.AddLayer(ltype = 'vector',
+ lname = name,
+ lcmd = ['d.vect', 'map=%s' % name])
+ dlg.Destroy()
+
+ def OnDeleteSelected(self, event):
+ """!Delete vector objects selected in attribute browse window
+ (attribures and geometry)
+ """
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ cats = list.GetSelectedItems()
+ if len(cats) == 0:
+ wx.MessageBox(parent=self,
+ message=_('Nothing to delete.'),
+ caption=_('Message'), style=wx.CENTRE)
+
+ if self.OnDataItemDelete(None):
+ digitToolbar = self.mapdisplay.toolbars['vdigit']
+ if digitToolbar and digitToolbar.GetLayer() and \
+ digitToolbar.GetLayer().GetName() == self.vectorName:
+ self.mapdisplay.digit.driver.SetSelected(map(int, cats), field=self.layer)
+ self.mapdisplay.digit.DeleteSelectedLines()
+ else:
+ RunCommand('v.edit',
+ parent = self,
+ quiet = True,
+ map = self.vectorName,
+ tool = 'delete',
+ cats = ListOfCatsToRange(cats))
+
+ self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
+
+ def AddQueryMapLayer(self):
+ """!Redraw a map
+
+ Return True if map has been redrawn, False if no map is given
+ """
+ list = self.FindWindowById(self.layerPage[self.layer]['data'])
+ cats = {
+ self.layer : list.GetSelectedItems()
+ }
+
+ if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0:
+ self.qlayer = None
+
+ if self.qlayer:
+ self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer=False))
+ else:
+ self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
+
+ return self.qlayer
+
+ def UpdateDialog(self, layer):
+ """!Updates dialog layout for given layer"""
+ # delete page
+ if layer in self.mapDBInfo.layers.keys():
+ # delete page
+ # draging pages disallowed
+ # if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer):
+ # self.browsePage.DeletePage(page)
+ # break
+ self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
+ self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
+ # set current page selection
+ self.notebook.SetSelectionByName('layers')
+
+ # fetch fresh db info
+ self.mapDBInfo = VectorDBInfo(self.vectorName)
+
+ #
+ # add new page
+ #
+ if layer in self.mapDBInfo.layers.keys():
+ # 'browse data' page
+ self._createBrowsePage(layer)
+ # 'manage tables' page
+ self._createManageTablePage(layer)
+ # set current page selection
+ self.notebook.SetSelectionByName('layers')
+
+ #
+ # 'manage layers' page
+ #
+ # update list of layers
+ self.layerList.Update(self.mapDBInfo.layers)
+ self.layerList.Populate(update=True)
+ # update selected widgets
+ listOfLayers = map(str, self.mapDBInfo.layers.keys())
+ ### delete layer page
+ self.manageLayerBook.deleteLayer.SetItems(listOfLayers)
+ if len(listOfLayers) > 0:
+ self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0])
+ tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table']
+ maxLayer = max(self.mapDBInfo.layers.keys())
+ else:
+ tableName = ''
+ maxLayer = 0
+ self.manageLayerBook.deleteTable.SetLabel( \
+ _('Drop also linked attribute table (%s)') % \
+ tableName)
+ ### add layer page
+ self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\
+ maxLayer+1)
+ ### modify layer
+ self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers)
+ self.manageLayerBook.OnChangeLayer(event=None)
+
+ def GetVectorName(self):
+ """!Get vector name"""
+ return self.vectorName
+
+ def LoadData(self, layer, columns=None, where=None, sql=None):
+ """!Load data into list
+
+ @param layer layer number
+ @param columns list of columns for output
+ @param where where statement
+ @param sql full sql statement
+
+ @return id of key column
+ @return -1 if key column is not displayed
+ """
+ listWin = self.FindWindowById(self.layerPage[layer]['data'])
+ return listWin.LoadData(layer, columns, where, sql)
+
+class TableListCtrl(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin):
+ # listmix.TextEditMixin):
+ """!Table description list"""
+
+ def __init__(self, parent, id, table, columns, pos=wx.DefaultPosition,
+ size=wx.DefaultSize):
+
+ self.parent = parent
+ self.table = table
+ self.columns = columns
+ wx.ListCtrl.__init__(self, parent, id, pos, size,
+ style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
+ wx.BORDER_NONE)
+
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+ # listmix.TextEditMixin.__init__(self)
+
+ def Update(self, table, columns):
+ """!Update column description"""
+ self.table = table
+ self.columns = columns
+
+ def Populate(self, update=False):
+ """!Populate the list"""
+ itemData = {} # requested by sorter
+
+ if not update:
+ headings = [_("Column name"), _("Data type"), _("Data length")]
+ i = 0
+ for h in headings:
+ self.InsertColumn(col=i, heading=h)
+ self.SetColumnWidth(col=i, width=150)
+ i += 1
+ else:
+ self.DeleteAllItems()
+
+ i = 0
+ for column in self.columns:
+ index = self.InsertStringItem(sys.maxint, str(column))
+ self.SetStringItem(index, 0, str(column))
+ self.SetStringItem(index, 1, str(self.table[column]['type']))
+ self.SetStringItem(index, 2, str(self.table[column]['length']))
+ self.SetItemData(index, i)
+ itemData[i] = (str(column),
+ str(self.table[column]['type']),
+ int(self.table[column]['length']))
+ i = i + 1
+
+ self.SendSizeEvent()
+
+ return itemData
+
+class LayerListCtrl(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin):
+ # listmix.ColumnSorterMixin):
+ # listmix.TextEditMixin):
+ """!Layer description list"""
+
+ def __init__(self, parent, id, layers,
+ pos=wx.DefaultPosition,
+ size=wx.DefaultSize):
+
+ self.parent = parent
+ self.layers = layers
+ wx.ListCtrl.__init__(self, parent, id, pos, size,
+ style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
+ wx.BORDER_NONE)
+
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+ # listmix.TextEditMixin.__init__(self)
+
+ def Update(self, layers):
+ """!Update description"""
+ self.layers = layers
+
+ def Populate(self, update=False):
+ """!Populate the list"""
+ itemData = {} # requested by sorter
+
+ if not update:
+ headings = [_("Layer"), _("Driver"), _("Database"), _("Table"), _("Key")]
+ i = 0
+ for h in headings:
+ self.InsertColumn(col=i, heading=h)
+ i += 1
+ else:
+ self.DeleteAllItems()
+
+ i = 0
+ for layer in self.layers.keys():
+ index = self.InsertStringItem(sys.maxint, str(layer))
+ self.SetStringItem(index, 0, str(layer))
+ database = str(self.layers[layer]['database'])
+ driver = str(self.layers[layer]['driver'])
+ table = str(self.layers[layer]['table'])
+ key = str(self.layers[layer]['key'])
+ self.SetStringItem(index, 1, driver)
+ self.SetStringItem(index, 2, database)
+ self.SetStringItem(index, 3, table)
+ self.SetStringItem(index, 4, key)
+ self.SetItemData(index, i)
+ itemData[i] = (str(layer),
+ driver,
+ database,
+ table,
+ key)
+ i += 1
+
+ for i in range(self.GetColumnCount()):
+ self.SetColumnWidth(col=i, width=wx.LIST_AUTOSIZE)
+ if self.GetColumnWidth(col=i) < 60:
+ self.SetColumnWidth(col=i, width=60)
+
+ self.SendSizeEvent()
+
+ return itemData
+
+class LayerBook(wx.Notebook):
+ """!Manage layers (add, delete, modify)"""
+ def __init__(self, parent, id,
+ parentDialog,
+ style=wx.BK_DEFAULT):
+ wx.Notebook.__init__(self, parent, id, style=style)
+
+ self.parent = parent
+ self.parentDialog = parentDialog
+ self.mapDBInfo = self.parentDialog.mapDBInfo
+
+ #
+ # drivers
+ #
+ drivers = RunCommand('db.drivers',
+ quiet = True,
+ read = True,
+ flags = 'p')
+
+ self.listOfDrivers = []
+ for drv in drivers.splitlines():
+ self.listOfDrivers.append(drv.strip())
+
+ #
+ # get default values
+ #
+ self.defaultConnect = {}
+ connect = RunCommand('db.connect',
+ flags = 'p',
+ read = True,
+ quiet = True)
+
+ for line in connect.splitlines():
+ item, value = line.split(':', 1)
+ self.defaultConnect[item.strip()] = value.strip()
+
+ if len(self.defaultConnect['driver']) == 0 or \
+ len(self.defaultConnect['database']) == 0:
+ wx.MessageBox(parent=self.parent,
+ message=_("Unknown default DB connection. "
+ "Please define DB connection using db.connect module."),
+ caption=_("Warning"),
+ style=wx.OK | wx.ICON_WARNING | wx.CENTRE)
+
+ self.defaultTables = self._getTables(self.defaultConnect['driver'],
+ self.defaultConnect['database'])
+ try:
+ self.defaultColumns = self._getColumns(self.defaultConnect['driver'],
+ self.defaultConnect['database'],
+ self.defaultTables[0])
+ except IndexError:
+ self.defaultColumns = []
+
+ self._createAddPage()
+ self._createDeletePage()
+ self._createModifyPage()
+
+ def _createAddPage(self):
+ """!Add new layer"""
+ self.addPanel = wx.Panel(parent=self, id=wx.ID_ANY)
+ self.AddPage(page=self.addPanel, text=_("Add layer"))
+
+ try:
+ maxLayer = max(self.mapDBInfo.layers.keys())
+ except ValueError:
+ maxLayer = 0
+
+ # layer description
+
+ layerBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY,
+ label=" %s " % (_("Layer description")))
+ layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
+
+ #
+ # list of layer widgets (label, value)
+ #
+ self.addLayerWidgets = {'layer':
+ (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Layer")),
+ wx.SpinCtrl(parent=self.addPanel, id=wx.ID_ANY, size=(65, -1),
+ initial=maxLayer+1,
+ min=1, max=1e6)),
+ 'driver':
+ (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Driver")),
+ wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
+ choices=self.listOfDrivers)),
+ 'database':
+ (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Database")),
+ wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
+ value='',
+ style=wx.TE_PROCESS_ENTER)),
+ 'table':
+ (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Table")),
+ wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
+ choices=self.defaultTables)),
+ 'key':
+ (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Key column")),
+ wx.Choice(parent=self.addPanel, id=wx.ID_ANY, size=(200, -1),
+ choices=self.defaultColumns)),
+ 'addCat':
+ (wx.CheckBox(parent=self.addPanel, id=wx.ID_ANY,
+ label=_("Insert record for each category into table")),
+ None),
+ }
+
+ # set default values for widgets
+ self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver'])
+ self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database'])
+ self.addLayerWidgets['table'][1].SetSelection(0)
+ self.addLayerWidgets['key'][1].SetSelection(0)
+ # events
+ self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
+ self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
+ self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
+
+ # tooltips
+ self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories "
+ "by v.category module."))
+ #
+ # list of table widgets
+ #
+ keyCol = UserSettings.Get(group='atm', key='keycolumn', subkey='value')
+ self.tableWidgets = {'table': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Table name")),
+ wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
+ value='',
+ style=wx.TE_PROCESS_ENTER)),
+ 'key': (wx.StaticText(parent=self.addPanel, id=wx.ID_ANY,
+ label='%s:' % _("Key column")),
+ wx.TextCtrl(parent=self.addPanel, id=wx.ID_ANY,
+ value=keyCol,
+ style=wx.TE_PROCESS_ENTER))}
+ # events
+ self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
+ self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
+
+ btnTable = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"),
+ size=(125,-1))
+ btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable)
+
+ btnLayer = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"),
+ size=(125,-1))
+ btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
+
+ btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"),
+ size=(125,-1))
+ btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault)
+
+ # do layout
+
+ pageSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ # data area
+ dataSizer = wx.GridBagSizer(hgap=5, vgap=5)
+ dataSizer.AddGrowableCol(1)
+ row = 0
+ for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'):
+ label, value = self.addLayerWidgets[key]
+ if not value:
+ span = (1, 2)
+ else:
+ span = (1, 1)
+ dataSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0),
+ span=span)
+
+ if not value:
+ row += 1
+ continue
+
+ if label.GetLabel() == "Layer:":
+ style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT
+ else:
+ style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
+
+ dataSizer.Add(item=value,
+ flag=style, pos=(row, 1))
+
+ row += 1
+
+ layerSizer.Add(item=dataSizer,
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item=btnDefault,
+ proportion=0,
+ flag=wx.ALL | wx.ALIGN_LEFT,
+ border=5)
+
+ btnSizer.Add(item=(5, 5),
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ btnSizer.Add(item=btnLayer,
+ proportion=0,
+ flag=wx.ALL | wx.ALIGN_RIGHT,
+ border=5)
+
+ layerSizer.Add(item=btnSizer,
+ proportion=0,
+ flag=wx.ALL | wx.EXPAND,
+ border=0)
+
+ # table description
+ tableBox = wx.StaticBox (parent=self.addPanel, id=wx.ID_ANY,
+ label=" %s " % (_("Table description")))
+ tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
+
+ # data area
+ dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
+ dataSizer.AddGrowableCol(1)
+ for key in ['table', 'key']:
+ label, value = self.tableWidgets[key]
+ dataSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ dataSizer.Add(item=value,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+
+ tableSizer.Add(item=dataSizer,
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ tableSizer.Add(item=btnTable,
+ proportion=0,
+ flag=wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT,
+ border=5)
+
+ pageSizer.Add(item=layerSizer,
+ proportion=3,
+ flag=wx.ALL | wx.EXPAND,
+ border=3)
+
+ pageSizer.Add(item=tableSizer,
+ proportion=2,
+ flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
+ border=3)
+
+ self.addPanel.SetAutoLayout(True)
+ self.addPanel.SetSizer(pageSizer)
+ pageSizer.Fit(self.addPanel)
+
+ def _createDeletePage(self):
+ """!Delete layer"""
+ self.deletePanel = wx.Panel(parent=self, id=wx.ID_ANY)
+ self.AddPage(page=self.deletePanel, text=_("Remove layer"))
+
+ label = wx.StaticText(parent=self.deletePanel, id=wx.ID_ANY,
+ label='%s:' % _("Layer to remove"))
+
+ self.deleteLayer = wx.ComboBox(parent=self.deletePanel, id=wx.ID_ANY, size=(100, -1),
+ style=wx.CB_SIMPLE | wx.CB_READONLY,
+ choices=map(str, self.mapDBInfo.layers.keys()))
+ self.deleteLayer.SetSelection(0)
+ self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
+
+ try:
+ tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table']
+ except ValueError:
+ tableName = ''
+
+ self.deleteTable = wx.CheckBox(parent=self.deletePanel, id=wx.ID_ANY,
+ label=_('Drop also linked attribute table (%s)') % \
+ tableName)
+
+ if tableName == '':
+ self.deleteLayer.Enable(False)
+ self.deleteTable.Enable(False)
+
+ btnDelete = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"),
+ size=(125,-1))
+ btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer)
+
+ #
+ # do layout
+ #
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+ dataSizer = wx.BoxSizer(wx.VERTICAL)
+
+ flexSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
+ flexSizer.AddGrowableCol(2)
+
+ flexSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ flexSizer.Add(item=self.deleteLayer,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+
+ dataSizer.Add(item=flexSizer,
+ proportion=0,
+ flag=wx.ALL | wx.EXPAND,
+ border=1)
+
+ dataSizer.Add(item=self.deleteTable,
+ proportion=0,
+ flag=wx.ALL | wx.EXPAND,
+ border=1)
+
+ pageSizer.Add(item=dataSizer,
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ pageSizer.Add(item=btnDelete,
+ proportion=0,
+ flag=wx.ALL | wx.ALIGN_RIGHT,
+ border=5)
+
+ self.deletePanel.SetSizer(pageSizer)
+
+ def _createModifyPage(self):
+ """!Modify layer"""
+ self.modifyPanel = wx.Panel(parent=self, id=wx.ID_ANY)
+ self.AddPage(page=self.modifyPanel, text=_("Modify layer"))
+
+ #
+ # list of layer widgets (label, value)
+ #
+ self.modifyLayerWidgets = {'layer':
+ (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
+ label='%s:' % _("Layer")),
+ wx.ComboBox(parent=self.modifyPanel, id=wx.ID_ANY,
+ size=(100, -1),
+ style=wx.CB_SIMPLE | wx.CB_READONLY,
+ choices=map(str,
+ self.mapDBInfo.layers.keys()))),
+ 'driver':
+ (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
+ label='%s:' % _("Driver")),
+ wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
+ size=(200, -1),
+ choices=self.listOfDrivers)),
+ 'database':
+ (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
+ label='%s:' % _("Database")),
+ wx.TextCtrl(parent=self.modifyPanel, id=wx.ID_ANY,
+ value='', size=(350, -1),
+ style=wx.TE_PROCESS_ENTER)),
+ 'table':
+ (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
+ label='%s:' % _("Table")),
+ wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
+ size=(200, -1),
+ choices=self.defaultTables)),
+ 'key':
+ (wx.StaticText(parent=self.modifyPanel, id=wx.ID_ANY,
+ label='%s:' % _("Key column")),
+ wx.Choice(parent=self.modifyPanel, id=wx.ID_ANY,
+ size=(200, -1),
+ choices=self.defaultColumns))}
+
+ # set default values for widgets
+ self.modifyLayerWidgets['layer'][1].SetSelection(0)
+ try:
+ layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
+ except ValueError:
+ layer = None
+ for label in self.modifyLayerWidgets.keys():
+ self.modifyLayerWidgets[label][1].Enable(False)
+
+ if layer:
+ driver = self.mapDBInfo.layers[layer]['driver']
+ database = self.mapDBInfo.layers[layer]['database']
+ table = self.mapDBInfo.layers[layer]['table']
+
+ listOfColumns = self._getColumns(driver, database, table)
+ self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
+ self.modifyLayerWidgets['database'][1].SetValue(database)
+ if table in self.modifyLayerWidgets['table'][1].GetItems():
+ self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+ else:
+ if self.defaultConnect['schema'] != '':
+ table = self.defaultConnect['schema'] + table # try with default schema
+ else:
+ table = 'public.' + table # try with 'public' schema
+ self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+ self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
+ self.modifyLayerWidgets['key'][1].SetSelection(0)
+
+ # events
+ self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
+ # self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
+ # self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
+ # self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
+
+ btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"),
+ size=(125,-1))
+ btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer)
+
+ #
+ # do layout
+ #
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+ # data area
+ dataSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
+ dataSizer.AddGrowableCol(1)
+ for key in ('layer', 'driver', 'database', 'table', 'key'):
+ label, value = self.modifyLayerWidgets[key]
+ dataSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+ if label.GetLabel() == "Layer:":
+ dataSizer.Add(item=value,
+ flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ else:
+ dataSizer.Add(item=value,
+ flag=wx.ALIGN_CENTER_VERTICAL)
+
+ pageSizer.Add(item=dataSizer,
+ proportion=1,
+ flag=wx.ALL | wx.EXPAND,
+ border=5)
+
+ pageSizer.Add(item=btnModify,
+ proportion=0,
+ flag=wx.ALL | wx.ALIGN_RIGHT,
+ border=5)
+
+ self.modifyPanel.SetSizer(pageSizer)
+
+ def _getTables(self, driver, database):
+ """!Get list of tables for given driver and database"""
+ tables = []
+
+ ret = RunCommand('db.tables',
+ parent = self,
+ read = True,
+ flags = 'p',
+ driver = driver,
+ database = database)
+
+ if ret is None:
+ wx.MessageBox(parent=self,
+ message=_("Unable to get list of tables.\n"
+ "Please use db.connect to set database parameters."),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+
+ return tables
+
+ for table in ret.splitlines():
+ tables.append(table)
+
+ return tables
+
+ def _getColumns(self, driver, database, table):
+ """!Get list of column of given table"""
+ columns = []
+
+ ret = RunCommand('db.columns',
+ parent = self,
+ quiet = True,
+ read = True,
+ driver = driver,
+ database = database,
+ table = table)
+
+ if ret == None:
+ return columns
+
+ for column in ret.splitlines():
+ columns.append(column)
+
+ return columns
+
+ def OnDriverChanged(self, event):
+ """!Driver selection changed, update list of tables"""
+ driver = event.GetString()
+ database = self.addLayerWidgets['database'][1].GetValue()
+
+ winTable = self.addLayerWidgets['table'][1]
+ winKey = self.addLayerWidgets['key'][1]
+ tables = self._getTables(driver, database)
+
+ winTable.SetItems(tables)
+ winTable.SetSelection(0)
+
+ if len(tables) == 0:
+ winKey.SetItems([])
+
+ event.Skip()
+
+ def OnDatabaseChanged(self, event):
+ """!Database selection changed, update list of tables"""
+ event.Skip()
+
+ def OnTableChanged(self, event):
+ """!Table name changed, update list of columns"""
+ driver = self.addLayerWidgets['driver'][1].GetStringSelection()
+ database = self.addLayerWidgets['database'][1].GetValue()
+ table = event.GetString()
+
+ win = self.addLayerWidgets['key'][1]
+ cols = self._getColumns(driver, database, table)
+ win.SetItems(cols)
+ win.SetSelection(0)
+
+ event.Skip()
+
+ def OnSetDefault(self, event):
+ """!Set default values"""
+ driver = self.addLayerWidgets['driver'][1]
+ database = self.addLayerWidgets['database'][1]
+ table = self.addLayerWidgets['table'][1]
+ key = self.addLayerWidgets['key'][1]
+
+ driver.SetStringSelection(self.defaultConnect['driver'])
+ database.SetValue(self.defaultConnect['database'])
+ tables = self._getTables(self.defaultConnect['driver'],
+ self.defaultConnect['database'])
+ table.SetItems(tables)
+ table.SetSelection(0)
+ if len(tables) == 0:
+ key.SetItems([])
+ else:
+ cols = self._getColumns(self.defaultConnect['driver'],
+ self.defaultConnect['database'],
+ tables[0])
+ key.SetItems(cols)
+ key.SetSelection(0)
+
+ event.Skip()
+
+ def OnCreateTable(self, event):
+ """!Create new table (name and key column given)"""
+ driver = self.addLayerWidgets['driver'][1].GetStringSelection()
+ database = self.addLayerWidgets['database'][1].GetValue()
+ table = self.tableWidgets['table'][1].GetValue()
+ key = self.tableWidgets['key'][1].GetValue()
+
+ if not table or not key:
+ wx.MessageBox(parent=self,
+ message=_("Unable to create new table. "
+ "Table name or key column name is missing."),
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ if table in self.addLayerWidgets['table'][1].GetItems():
+ wx.MessageBox(parent=self,
+ message=_("Unable to create new table. "
+ "Table <%s> already exists in the database.") % table,
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ # create table
+ sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
+
+ RunCommand('db.execute',
+ quiet = True,
+ parent = self,
+ stdin = sql,
+ input = '-',
+ driver = driver,
+ database = database)
+
+ # update list of tables
+ tableList = self.addLayerWidgets['table'][1]
+ tableList.SetItems(self._getTables(driver, database))
+ tableList.SetStringSelection(table)
+
+ # update key column selection
+ keyList = self.addLayerWidgets['key'][1]
+ keyList.SetItems(self._getColumns(driver, database, table))
+ keyList.SetStringSelection(key)
+
+ event.Skip()
+
+ def OnAddLayer(self, event):
+ """!Add new layer to vector map"""
+ layer = int(self.addLayerWidgets['layer'][1].GetValue())
+ layerWin = self.addLayerWidgets['layer'][1]
+ driver = self.addLayerWidgets['driver'][1].GetStringSelection()
+ database = self.addLayerWidgets['database'][1].GetValue()
+ table = self.addLayerWidgets['table'][1].GetStringSelection()
+ key = self.addLayerWidgets['key'][1].GetStringSelection()
+
+ if layer in self.mapDBInfo.layers.keys():
+ wx.MessageBox(parent=self,
+ message=_("Unable to add new layer to vector map <%(vector)s>. "
+ "Layer %(layer)d already exists.") %
+ {'vector' : self.mapDBInfo.map, 'layer' : layer},
+ caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return
+
+ # add new 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():
+ 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)
+ self.parentDialog.UpdateDialog(layer=layer)
+ # update db info
+ self.mapDBInfo = self.parentDialog.mapDBInfo
+ # increase layer number
+ layerWin.SetValue(layer+1)
+
+ if len(self.mapDBInfo.layers.keys()) == 1:
+ # first layer add --- enable previously disabled widgets
+ self.deleteLayer.Enable()
+ self.deleteTable.Enable()
+ for label in self.modifyLayerWidgets.keys():
+ self.modifyLayerWidgets[label][1].Enable()
+
+ def OnDeleteLayer(self, event):
+ """!Delete layer"""
+ try:
+ layer = int(self.deleteLayer.GetValue())
+ except:
+ return
+
+ 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():
+ driver = self.addLayerWidgets['driver'][1].GetStringSelection()
+ database = self.addLayerWidgets['database'][1].GetValue()
+ table = self.mapDBInfo.layers[layer]['table']
+ sql = 'DROP TABLE %s' % (table)
+
+ RunCommand('db.execute',
+ parent = self,
+ stdin = sql,
+ quiet = True,
+ driver = driver,
+ database = database)
+
+ # update list of tables
+ tableList = self.addLayerWidgets['table'][1]
+ tableList.SetItems(self._getTables(driver, database))
+ tableList.SetStringSelection(table)
+
+ # update dialog
+ self.parentDialog.UpdateDialog(layer=layer)
+ # update db info
+ self.mapDBInfo = self.parentDialog.mapDBInfo
+
+ if len(self.mapDBInfo.layers.keys()) == 0:
+ # disable selected widgets
+ self.deleteLayer.Enable(False)
+ self.deleteTable.Enable(False)
+ for label in self.modifyLayerWidgets.keys():
+ self.modifyLayerWidgets[label][1].Enable(False)
+
+ event.Skip()
+
+ def OnChangeLayer(self, event):
+ """!Layer number of layer to be deleted is changed"""
+ try:
+ layer = int(event.GetString())
+ except:
+ try:
+ layer = self.mapDBInfo.layers.keys()[0]
+ except:
+ return
+
+ if self.GetCurrentPage() == self.modifyPanel:
+ driver = self.mapDBInfo.layers[layer]['driver']
+ database = self.mapDBInfo.layers[layer]['database']
+ table = self.mapDBInfo.layers[layer]['table']
+ listOfColumns = self._getColumns(driver, database, table)
+ self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
+ self.modifyLayerWidgets['database'][1].SetValue(database)
+ self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+ self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
+ self.modifyLayerWidgets['key'][1].SetSelection(0)
+ else:
+ self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \
+ self.mapDBInfo.layers[layer]['table'])
+ if event:
+ event.Skip()
+
+ def OnModifyLayer(self, event):
+ """!Modify layer connection settings"""
+
+ layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
+
+ modify = False
+ if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \
+ self.mapDBInfo.layers[layer]['driver'] or \
+ self.modifyLayerWidgets['database'][1].GetStringSelection() != \
+ self.mapDBInfo.layers[layer]['database'] or \
+ self.modifyLayerWidgets['table'][1].GetStringSelection() != \
+ self.mapDBInfo.layers[layer]['table'] or \
+ self.modifyLayerWidgets['key'][1].GetStringSelection() != \
+ self.mapDBInfo.layers[layer]['key']:
+ modify = True
+
+ if modify:
+ # delete layer
+ RunCommand('v.db.connect',
+ parent = self,
+ quiet = True,
+ flag = 'd',
+ map = self.mapDBInfo.map,
+ layer = layer)
+
+ # add modified 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)
+ # update db info
+ self.mapDBInfo = self.parentDialog.mapDBInfo
+
+ event.Skip()
+
+def main(argv = None):
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+
+ if argv is None:
+ argv = sys.argv
+
+ if len(argv) != 2:
+ print >> sys.stderr, __doc__
+ sys.exit()
+
+ #some applications might require image handlers
+ wx.InitAllImageHandlers()
+
+ app = wx.PySimpleApp()
+ f = AttributeManager(parent=None, id=wx.ID_ANY,
+ title="%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
+ argv[1]),
+ size=(900,600), vectorName=argv[1])
+ f.Show()
+
+ app.MainLoop()
+
+if __name__ == '__main__':
+ main()
Copied: grass/trunk/gui/wxpython/dbm/sqlbuilder.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/sqlbuilder.py)
===================================================================
--- grass/trunk/gui/wxpython/dbm/sqlbuilder.py (rev 0)
+++ grass/trunk/gui/wxpython/dbm/sqlbuilder.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,458 @@
+"""!
+ at package dbm.sqlbuilder
+
+ at brief GRASS SQL Builder
+
+Classes:
+ - SQLFrame
+
+Usage:
+ at code
+python sqlbuilder.py vector_map
+ at 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.
+
+ at author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Hamish Bowman <hamish_b yahoo com>
+"""
+
+import os
+import sys
+
+from core import globalvar
+import wx
+
+import grass.script as grass
+
+from core.gcmd import RunCommand, GError
+from dbm.vinfo import createDbInfoDesc, VectorDBInfo
+
+class SQLFrame(wx.Frame):
+ """!SQL Frame class"""
+ def __init__(self, parent, title, vectmap, id = wx.ID_ANY,
+ layer = 1, qtype = "select", evtheader = None):
+
+ wx.Frame.__init__(self, parent, id, title)
+
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'),
+ wx.BITMAP_TYPE_ICO))
+
+ self.parent = parent
+ self.evtHeader = evtheader
+
+ #
+ # variables
+ #
+ self.vectmap = vectmap # fullname
+ if not "@" in self.vectmap:
+ self.vectmap = grass.find_file(self.vectmap, element = 'vector')['fullname']
+ self.mapname, self.mapset = self.vectmap.split("@", 1)
+
+ # db info
+ self.layer = layer
+ self.dbInfo = VectorDBInfo(self.vectmap)
+ self.tablename = self.dbInfo.GetTable(self.layer)
+ self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
+
+ self.qtype = qtype # type of query: SELECT, UPDATE, DELETE, ...
+ self.colvalues = [] # array with unique values in selected column
+
+ # set dialog title
+ self.SetTitle(_("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
+ { 'type' : self.qtype.upper(), 'map' : self.vectmap })
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ # statusbar
+ self.statusbar = self.CreateStatusBar(number=1)
+ self.statusbar.SetStatusText(_("SQL statement not verified"), 0)
+
+ self._doLayout()
+
+ def _doLayout(self):
+ """!Do dialog layout"""
+
+ pagesizer = wx.BoxSizer(wx.VERTICAL)
+
+
+ # dbInfo
+ databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Database connection"))
+ databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
+ databaseboxsizer.Add(item=createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
+ proportion=1,
+ flag=wx.EXPAND | wx.ALL,
+ border=3)
+
+ #
+ # text areas
+ #
+ # sql box
+ sqlbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Query"))
+ sqlboxsizer = wx.StaticBoxSizer(sqlbox, wx.VERTICAL)
+
+ self.text_sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+ value = '', size = (-1, 50),
+ style=wx.TE_MULTILINE)
+ if self.qtype.lower() == "select":
+ self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
+ self.text_sql.SetInsertionPointEnd()
+ self.text_sql.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")
+ wx.CallAfter(self.text_sql.SetFocus)
+
+ sqlboxsizer.Add(item = self.text_sql, flag = wx.EXPAND)
+
+ #
+ # buttons
+ #
+ self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
+ self.btn_clear.SetToolTipString(_("Set SQL statement to default"))
+ self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Verify"))
+ self.btn_verify.SetToolTipString(_("Verify SQL statement"))
+ self.btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
+ self.btn_apply.SetToolTipString(_("Apply SQL statement and close the dialog"))
+ self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+ self.btn_close.SetToolTipString(_("Close the dialog"))
+
+ self.btn_lv = { 'is' : ['=', ],
+ 'isnot' : ['!=', ],
+ 'like' : ['LIKE', ],
+ 'gt' : ['>', ],
+ 'ge' : ['>=', ],
+ 'lt' : ['<', ],
+ 'le' : ['<=', ],
+ 'or' : ['OR', ],
+ 'not' : ['NOT', ],
+ 'and' : ['AND', ],
+ 'brac' : ['()', ],
+ 'prc' : ['%', ] }
+
+ for key, value in self.btn_lv.iteritems():
+ btn = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = value[0])
+ self.btn_lv[key].append(btn.GetId())
+
+ buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
+ buttonsizer.Add(item = self.btn_clear)
+ buttonsizer.Add(item = self.btn_verify)
+ buttonsizer.Add(item = self.btn_apply)
+ buttonsizer.Add(item = self.btn_close)
+
+ buttonsizer2 = wx.GridBagSizer(5, 5)
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['is'][1]), pos = (0,0))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['isnot'][1]), pos = (1,0))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['like'][1]), pos = (2, 0))
+
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['gt'][1]), pos = (0, 1))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['ge'][1]), pos = (1, 1))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['or'][1]), pos = (2, 1))
+
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['lt'][1]), pos = (0, 2))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['le'][1]), pos = (1, 2))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['not'][1]), pos = (2, 2))
+
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['brac'][1]), pos = (0, 3))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['prc'][1]), pos = (1, 3))
+ buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['and'][1]), pos = (2, 3))
+
+ #
+ # list boxes (columns, values)
+ #
+ hsizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ columnsbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Columns"))
+ columnsizer = wx.StaticBoxSizer(columnsbox, wx.VERTICAL)
+ self.list_columns = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
+ choices = self.dbInfo.GetColumns(self.tablename),
+ style = wx.LB_MULTIPLE)
+ columnsizer.Add(item = self.list_columns, proportion = 1,
+ flag = wx.EXPAND)
+
+ radiosizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.radio_cv = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Add on double-click"),
+ choices = [_("columns"), _("values")])
+ self.radio_cv.SetSelection(1) # default 'values'
+ radiosizer.Add(item = self.radio_cv, proportion = 1,
+ flag = wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND, border = 5)
+
+ columnsizer.Add(item = radiosizer, proportion = 0,
+ flag = wx.TOP | wx.EXPAND, border = 5)
+ # self.list_columns.SetMinSize((-1,130))
+ # self.list_values.SetMinSize((-1,100))
+
+ valuesbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Values"))
+ valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
+ self.list_values = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
+ choices = self.colvalues,
+ style = wx.LB_MULTIPLE)
+ valuesizer.Add(item = self.list_values, proportion = 1,
+ flag = wx.EXPAND)
+
+ self.btn_unique = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Get all values"))
+ self.btn_unique.Enable(False)
+ self.btn_uniquesample = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Get sample"))
+ self.btn_uniquesample.Enable(False)
+
+ buttonsizer3 = wx.BoxSizer(wx.HORIZONTAL)
+ buttonsizer3.Add(item = self.btn_uniquesample, proportion = 0,
+ flag = wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border = 5)
+ buttonsizer3.Add(item = self.btn_unique, proportion = 0,
+ flag = wx.ALIGN_CENTER_HORIZONTAL)
+
+ valuesizer.Add(item = buttonsizer3, proportion = 0,
+ flag = wx.TOP, border = 5)
+
+ # hsizer1.Add(wx.StaticText(self.panel,-1, "Unique values: "), border=0, proportion=1)
+
+ hsizer.Add(item = columnsizer, proportion = 1,
+ flag = wx.EXPAND)
+ hsizer.Add(item = valuesizer, proportion = 1,
+ flag = wx.EXPAND)
+
+ self.close_onapply = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = _("Close dialog on apply"))
+ self.close_onapply.SetValue(True)
+
+ pagesizer.Add(item = databaseboxsizer,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+ pagesizer.Add(item = hsizer, proportion = 1,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+ # pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
+ # pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
+ pagesizer.Add(item = buttonsizer2, proportion = 0,
+ flag = wx.ALIGN_CENTER_HORIZONTAL)
+ pagesizer.Add(item = sqlboxsizer, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+ pagesizer.Add(item = buttonsizer, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+ pagesizer.Add(item = self.close_onapply, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+ #
+ # bindings
+ #
+ self.btn_unique.Bind(wx.EVT_BUTTON, self.OnUniqueValues)
+ self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.OnSampleValues)
+
+ for key, value in self.btn_lv.iteritems():
+ self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)
+
+ self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+ self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
+ self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
+ self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
+
+ self.list_columns.Bind(wx.EVT_LISTBOX, self.OnAddColumn)
+ self.list_values.Bind(wx.EVT_LISTBOX, self.OnAddValue)
+
+ self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
+
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizer(pagesizer)
+ pagesizer.Fit(self.panel)
+
+ self.Layout()
+ self.SetMinSize((660, 525))
+ self.SetClientSize(self.panel.GetSize())
+ self.CenterOnParent()
+
+ def OnUniqueValues(self, event, justsample = False):
+ """!Get unique values"""
+ vals = []
+ try:
+ idx = self.list_columns.GetSelections()[0]
+ column = self.list_columns.GetString(idx)
+ except:
+ self.list_values.Clear()
+ return
+
+ self.list_values.Clear()
+
+ querystring = "SELECT %s FROM %s" % (column, self.tablename)
+
+ data = grass.db_select(table = self.tablename,
+ sql = querystring,
+ database = self.database,
+ driver = self.driver)
+ if not data:
+ return
+
+ desc = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]
+
+ i = 0
+ for item in sorted(map(desc['ctype'], data)):
+ if justsample and i < 256 or \
+ not justsample:
+ if desc['type'] != 'character':
+ item = str(item)
+ self.list_values.Append(item)
+ else:
+ break
+ i += 1
+
+ def OnSampleValues(self, event):
+ """!Get sample values"""
+ self.OnUniqueValues(None, True)
+
+ def OnAddColumn(self, event):
+ """!Add column name to the query"""
+ idx = self.list_columns.GetSelections()
+ for i in idx:
+ column = self.list_columns.GetString(i)
+ self._add(element = 'column', value = column)
+
+ if not self.btn_uniquesample.IsEnabled():
+ self.btn_uniquesample.Enable(True)
+ self.btn_unique.Enable(True)
+
+ def OnAddValue(self, event):
+ """!Add value"""
+ selection = self.list_values.GetSelections()
+ if not selection:
+ event.Skip()
+ return
+
+ idx = selection[0]
+ value = self.list_values.GetString(idx)
+ idx = self.list_columns.GetSelections()[0]
+ column = self.list_columns.GetString(idx)
+
+ ctype = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]['type']
+
+ if ctype == 'character':
+ value = "'%s'" % value
+
+ self._add(element = 'value', value = value)
+
+ def OnAddMark(self, event):
+ """!Add mark"""
+ mark = None
+ for key, value in self.btn_lv.iteritems():
+ if event.GetId() == value[1]:
+ mark = value[0]
+ break
+
+ self._add(element = 'mark', value = mark)
+
+ def _add(self, element, value):
+ """!Add element to the query
+
+ @param element element to add (column, value)
+ """
+ sqlstr = self.text_sql.GetValue()
+ newsqlstr = ''
+ if element == 'column':
+ if self.radio_cv.GetSelection() == 0: # -> column
+ idx1 = len('select')
+ idx2 = sqlstr.lower().find('from')
+ colstr = sqlstr[idx1:idx2].strip()
+ if colstr == '*':
+ cols = []
+ else:
+ cols = colstr.split(',')
+ if value in cols:
+ cols.remove(value)
+ else:
+ cols.append(value)
+
+ if len(cols) < 1:
+ cols = ['*',]
+
+ newsqlstr = 'SELECT ' + ','.join(cols) + ' ' + sqlstr[idx2:]
+ else: # -> where
+ newsqlstr = sqlstr
+ if sqlstr.lower().find('where') < 0:
+ newsqlstr += ' WHERE'
+
+ newsqlstr += ' ' + value
+
+ elif element == 'value':
+ newsqlstr = sqlstr + ' ' + value
+ elif element == 'mark':
+ newsqlstr = sqlstr + ' ' + value
+
+ if newsqlstr:
+ self.text_sql.SetValue(newsqlstr)
+
+ def GetSQLStatement(self):
+ """!Return SQL statement"""
+ return self.text_sql.GetValue().strip().replace("\n"," ")
+
+ def CloseOnApply(self):
+ """!Return True if the dialog will be close on apply"""
+ return self.close_onapply.IsChecked()
+
+ def OnText(self, event):
+ """Query string changed"""
+ if len(self.text_sql.GetValue()) > 0:
+ self.btn_verify.Enable(True)
+ else:
+ self.btn_verify.Enable(False)
+
+ def OnApply(self, event):
+ """Apply button pressed"""
+ if self.evtHeader:
+ self.evtHeader(event = 'apply')
+
+ if self.close_onapply.IsChecked():
+ self.Destroy()
+
+ event.Skip()
+
+ def OnVerify(self, event):
+ """!Verify button pressed"""
+ 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)
+ GError(parent = self,
+ message = _("SQL statement is not valid.\n\n%s") % msg)
+ else:
+ self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
+
+ def OnClear(self, event):
+ """!Clear button pressed"""
+ if self.qtype.lower() == "select":
+ self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
+ else:
+ self.text_sql.SetValue("")
+
+ def OnClose(self, event):
+ """!Close button pressed"""
+ if self.evtHeader:
+ self.evtHeader(event = 'close')
+
+ self.Destroy()
+
+ event.Skip()
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print >>sys.stderr, __doc__
+ sys.exit()
+
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+
+ app = wx.App(0)
+ sqlb = SQLFrame(parent = None, title = _('SQL Builder'), vectmap = sys.argv[1])
+ sqlb.Show()
+
+ app.MainLoop()
Copied: grass/trunk/gui/wxpython/dbm/vinfo.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/dbm_base.py)
===================================================================
--- grass/trunk/gui/wxpython/dbm/vinfo.py (rev 0)
+++ grass/trunk/gui/wxpython/dbm/vinfo.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,163 @@
+"""
+ at package dbm.vinfo
+
+ at 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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import types
+
+import wx
+
+from gui_core.gselect import VectorDBInfo
+from core.gcmd import RunCommand
+from core.settings import UserSettings
+
+import grass.script as grass
+
+def unicodeValue(value):
+ """!Encode value"""
+ if type(value) == types.UnicodeType:
+ return value
+
+ enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+ if enc:
+ value = unicode(value, enc)
+ elif 'GRASS_DB_ENCODING' in os.environ:
+ value = unicode(value, os.environ['GRASS_DB_ENCODING'])
+ else:
+ try:
+ value = unicode(value, 'ascii')
+ except UnicodeDecodeError:
+ value = _("Unable to decode value. Set encoding in GUI preferences ('Attributes').")
+
+ return value
+
+def createDbInfoDesc(panel, mapDBInfo, layer):
+ """!Create database connection information content"""
+ infoFlexSizer = wx.FlexGridSizer (cols = 2, hgap = 1, vgap = 1)
+ infoFlexSizer.AddGrowableCol(1)
+
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = "Driver:"))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = mapDBInfo.layers[layer]['driver']))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = "Database:"))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = mapDBInfo.layers[layer]['database']))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = "Table:"))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = mapDBInfo.layers[layer]['table']))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = "Key:"))
+ infoFlexSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = mapDBInfo.layers[layer]['key']))
+
+ return infoFlexSizer
+
+class VectorDBInfo(VectorDBInfo):
+ """!Class providing information about attribute tables
+ linked to the vector map"""
+ def __init__(self, map):
+ VectorDBInfo.__init__(self, map)
+
+ def GetColumns(self, table):
+ """!Return list of columns names (based on their index)"""
+ try:
+ names = [''] * len(self.tables[table].keys())
+ except KeyError:
+ return []
+
+ for name, desc in self.tables[table].iteritems():
+ names[desc['index']] = name
+
+ return names
+
+ def SelectByPoint(self, queryCoords, qdist):
+ """!Get attributes by coordinates (all available layers)
+
+ Return line id or None if no line is found"""
+ line = None
+ nselected = 0
+
+ data = grass.vector_what(map = self.map,
+ coord = (float(queryCoords[0]), float(queryCoords[1])),
+ distance = float(qdist))
+
+ if len(data) < 1 or 'Table' not in data[0]:
+ return None
+
+ # process attributes
+ table = data[0]['Table']
+ for key, value in data[0]['Attributes'].iteritems():
+ if len(value) < 1:
+ value = None
+ else:
+ if self.tables[table][key]['ctype'] != types.StringType:
+ value = self.tables[table][key]['ctype'] (value)
+ else:
+ value = unicodeValue(value)
+ self.tables[table][key]['values'].append(value)
+
+ ret = dict()
+ for key, value in data[0].iteritems():
+ if key == 'Attributes':
+ continue
+ ret[key] = list()
+ ret[key].append(value)
+
+ return ret
+
+ def SelectFromTable(self, layer, cols = '*', where = None):
+ """!Select records from the table
+
+ Return number of selected records, -1 on error
+ """
+ if layer <= 0:
+ return -1
+
+ nselected = 0
+
+ table = self.layers[layer]["table"] # get table desc
+ # select values (only one record)
+ if where is None or where is '':
+ sql = "SELECT %s FROM %s" % (cols, table)
+ else:
+ sql = "SELECT %s FROM %s WHERE %s" % (cols, table, where)
+
+ 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:
+ for line in ret.splitlines():
+ name, value = line.split('|')
+ # casting ...
+ if value:
+ if self.tables[table][name]['ctype'] != type(''):
+ value = self.tables[table][name]['ctype'] (value)
+ else:
+ value = unicodeValue(value)
+ else:
+ value = None
+ self.tables[table][name]['values'].append(value)
+ nselected = 1
+
+ return nselected
Copied: grass/trunk/gui/wxpython/gcp/manager.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/gcpmanager.py)
===================================================================
--- grass/trunk/gui/wxpython/gcp/manager.py (rev 0)
+++ grass/trunk/gui/wxpython/gcp/manager.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,2781 @@
+"""!
+ at package gcp.manager
+
+ at brief Georectification module for GRASS GIS. Includes ground control
+point management and interactive point and click GCP creation
+
+Classes:
+ - GCPWizard
+ - LocationPage
+ - GroupPage
+ - DispMapPage
+ - GCP
+ - GCPList
+ - VectGroup
+ - EditGCP
+ - GrSettingsDialog
+
+(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.
+
+ at author Michael Barton
+ at author Updated by Martin Landa <landa.martin gmail.com>
+ at author Markus Metz redesign georectfier -> GCP Manager
+"""
+
+import os
+import sys
+import shutil
+
+import wx
+from wx.lib.mixins.listctrl import CheckListCtrlMixin, ColumnSorterMixin, ListCtrlAutoWidthMixin
+import wx.lib.colourselect as csel
+import wx.wizard as wiz
+
+import grass.script as grass
+
+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
+#
+global src_map
+global tgt_map
+global maptype
+
+src_map = ''
+tgt_map = ''
+maptype = 'cell'
+
+def getSmallUpArrowImage():
+ stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_up_arrow.png'), 'rb')
+ try:
+ img = wx.ImageFromStream(stream)
+ finally:
+ stream.close()
+ return img
+
+def getSmallDnArrowImage():
+ stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_down_arrow.png'), 'rb')
+ try:
+ img = wx.ImageFromStream(stream)
+ finally:
+ stream.close()
+ stream.close()
+ return img
+
+class GCPWizard(object):
+ """
+ Start wizard here and finish wizard here
+ """
+
+ def __init__(self, parent):
+ self.parent = parent # GMFrame
+
+ #
+ # get environmental variables
+ #
+ self.grassdatabase = grass.gisenv()['GISDBASE']
+
+ #
+ # read original environment settings
+ #
+ self.target_gisrc = os.environ['GISRC']
+ self.gisrc_dict = {}
+ try:
+ f = open(self.target_gisrc, 'r')
+ for line in f.readlines():
+ line = line.replace('\n', '').strip()
+ if len(line) < 1:
+ continue
+ key, value = line.split(':', 1)
+ self.gisrc_dict[key.strip()] = value.strip()
+ finally:
+ f.close()
+
+ self.currentlocation = self.gisrc_dict['LOCATION_NAME']
+ self.currentmapset = self.gisrc_dict['MAPSET']
+ # location for xy map to georectify
+ self.newlocation = ''
+ # mapset for xy map to georectify
+ self.newmapset = ''
+
+ global maptype
+ global src_map
+ global tgt_map
+
+ src_map = ''
+ tgt_map = ''
+ maptype = 'cell'
+
+ # GISRC file for source location/mapset of map(s) to georectify
+ self.source_gisrc = ''
+ self.src_maps = []
+
+ #
+ # define wizard pages
+ #
+ self.wizard = wiz.Wizard(parent=parent, id=wx.ID_ANY, title=_("Setup for georectification"))
+ self.startpage = LocationPage(self.wizard, self)
+ self.grouppage = GroupPage(self.wizard, self)
+ self.mappage = DispMapPage(self.wizard, self)
+
+ #
+ # set the initial order of the pages
+ #
+ self.startpage.SetNext(self.grouppage)
+ self.grouppage.SetPrev(self.startpage)
+ self.grouppage.SetNext(self.mappage)
+ self.mappage.SetPrev(self.grouppage)
+
+ #
+ # do pages layout
+ #
+ self.startpage.DoLayout()
+ self.grouppage.DoLayout()
+ self.mappage.DoLayout()
+ self.wizard.FitToPage(self.startpage)
+
+ # self.Bind(wx.EVT_CLOSE, self.Cleanup)
+ # self.parent.Bind(wx.EVT_ACTIVATE, self.OnGLMFocus)
+
+ success = False
+
+ #
+ # run wizard
+ #
+ if self.wizard.RunWizard(self.startpage):
+ success = self.OnWizFinished()
+ if success == False:
+ GMessage(parent = self.parent,
+ message = _("Georectifying setup canceled."))
+ self.Cleanup()
+ else:
+ GMessage(parent = self.parent,
+ message = _("Georectifying setup canceled."))
+ self.Cleanup()
+
+ #
+ # start GCP display
+ #
+ if success != False:
+ # instance of render.Map to be associated with display
+ self.SwitchEnv('source')
+ self.SrcMap = Map(gisrc=self.source_gisrc)
+ self.SwitchEnv('target')
+ self.TgtMap = Map(gisrc=self.target_gisrc)
+ self.Map = self.SrcMap
+
+ #
+ # add layer to source map
+ #
+ if maptype == 'cell':
+ rendertype = 'raster'
+ cmdlist = ['d.rast', 'map=%s' % src_map]
+ else: # -> vector layer
+ rendertype = 'vector'
+ cmdlist = ['d.vect', 'map=%s' % src_map]
+
+ self.SwitchEnv('source')
+ name, found = utils.GetLayerNameFromCmd(cmdlist)
+ self.SrcMap.AddLayer(type=rendertype, command=cmdlist, l_active=True,
+ name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+ if tgt_map:
+ #
+ # add layer to target map
+ #
+ if maptype == 'cell':
+ rendertype = 'raster'
+ cmdlist = ['d.rast', 'map=%s' % tgt_map]
+ else: # -> vector layer
+ rendertype = 'vector'
+ cmdlist = ['d.vect', 'map=%s' % tgt_map]
+
+ self.SwitchEnv('target')
+ name, found = utils.GetLayerNameFromCmd(cmdlist)
+ self.TgtMap.AddLayer(type=rendertype, command=cmdlist, l_active=True,
+ name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+ #
+ # start GCP Manager
+ #
+ self.gcpmgr = GCP(self.parent, grwiz=self, size=globalvar.MAP_WINDOW_SIZE,
+ toolbars=["gcpdisp"],
+ Map=self.SrcMap, lmgr=self.parent)
+
+ # load GCPs
+ self.gcpmgr.InitMapDisplay()
+ self.gcpmgr.CenterOnScreen()
+ self.gcpmgr.Show()
+ # need to update AUI here for wingrass
+ self.gcpmgr._mgr.Update()
+ else:
+ self.Cleanup()
+
+ def SetSrcEnv(self, location, mapset):
+ """!Create environment to use for location and mapset
+ that are the source of the file(s) to georectify
+
+ @param location source location
+ @param mapset source mapset
+
+ @return False on error
+ @return True on success
+ """
+
+ self.newlocation = location
+ self.newmapset = mapset
+
+ # check to see if we are georectifying map in current working location/mapset
+ if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+ return False
+
+ self.gisrc_dict['LOCATION_NAME'] = location
+ self.gisrc_dict['MAPSET'] = mapset
+
+ self.source_gisrc = utils.GetTempfile()
+
+ try:
+ f = open(self.source_gisrc, mode='w')
+ for line in self.gisrc_dict.items():
+ f.write(line[0] + ": " + line[1] + "\n")
+ finally:
+ f.close()
+
+ return True
+
+ def SwitchEnv(self, grc):
+ """
+ Switches between original working location/mapset and
+ location/mapset that is source of file(s) to georectify
+ """
+ # check to see if we are georectifying map in current working location/mapset
+ if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+ return False
+
+ if grc == 'target':
+ os.environ['GISRC'] = str(self.target_gisrc)
+ elif grc == 'source':
+ os.environ['GISRC'] = str(self.source_gisrc)
+
+ return True
+
+ def OnWizFinished(self):
+ # self.Cleanup()
+
+ return True
+
+ def OnGLMFocus(self, event):
+ """!Layer Manager focus"""
+ # self.SwitchEnv('target')
+
+ event.Skip()
+
+ def Cleanup(self):
+ """!Return to current location and mapset"""
+ self.SwitchEnv('target')
+ self.parent.gcpmanagement = None
+
+ self.wizard.Destroy()
+
+class LocationPage(TitledPage):
+ """
+ Set map type (raster or vector) to georectify and
+ select location/mapset of map(s) to georectify.
+ """
+ def __init__(self, wizard, parent):
+ TitledPage.__init__(self, wizard, _("Select map type and location/mapset"))
+
+ self.parent = parent
+ self.grassdatabase = self.parent.grassdatabase
+
+ self.xylocation = ''
+ self.xymapset = ''
+
+ #
+ # layout
+ #
+ self.sizer.AddGrowableCol(2)
+ # map type
+ self.rb_maptype = wx.RadioBox(parent=self, id=wx.ID_ANY,
+ label=' %s ' % _("Map type to georectify"),
+ choices=[_('raster'), _('vector')],
+ majorDimension=wx.RA_SPECIFY_COLS)
+ self.sizer.Add(item=self.rb_maptype,
+ flag=wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, border=5,
+ pos=(1, 1), span=(1, 2))
+
+ # location
+ 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 = 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))
+
+ # mapset
+ 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 = 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,
+ pos=(3,2))
+
+ #
+ # bindings
+ #
+ self.Bind(wx.EVT_RADIOBOX, self.OnMaptype, self.rb_maptype)
+ self.Bind(wx.EVT_COMBOBOX, self.OnLocation, self.cb_location)
+ self.Bind(wx.EVT_COMBOBOX, self.OnMapset, self.cb_mapset)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+ # self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
+
+ def OnMaptype(self,event):
+ """!Change map type"""
+ global maptype
+
+ if event.GetInt() == 0:
+ maptype = 'cell'
+ else:
+ maptype = 'vector'
+
+ def OnLocation(self, event):
+ """!Sets source location for map(s) to georectify"""
+ self.xylocation = event.GetString()
+
+ #create a list of valid mapsets
+ tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation))
+ self.mapsetList = []
+ for item in tmplist:
+ if os.path.isdir(os.path.join(self.grassdatabase, self.xylocation, item)) and \
+ os.path.exists(os.path.join(self.grassdatabase, self.xylocation, item, 'WIND')):
+ if item != 'PERMANENT':
+ self.mapsetList.append(item)
+
+ self.xymapset = 'PERMANENT'
+ utils.ListSortLower(self.mapsetList)
+ self.mapsetList.insert(0, 'PERMANENT')
+ self.cb_mapset.SetItems(self.mapsetList)
+ self.cb_mapset.SetStringSelection(self.xymapset)
+
+ if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+ def OnMapset(self, event):
+ """!Sets source mapset for map(s) to georectify"""
+ if self.xylocation == '':
+ GMessage(_('You must select a valid location '
+ 'before selecting a mapset'),
+ parent = self)
+ return
+
+ self.xymapset = event.GetString()
+
+ if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+ def OnPageChanging(self, event=None):
+ if event.GetDirection() and \
+ (self.xylocation == '' or self.xymapset == ''):
+ GMessage(_('You must select a valid location '
+ 'and mapset in order to continue'),
+ parent = self)
+ event.Veto()
+ return
+
+ self.parent.SetSrcEnv(self.xylocation, self.xymapset)
+
+ def OnEnterPage(self, event=None):
+ if self.xylocation == '' or self.xymapset == '':
+ wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+ else:
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+class GroupPage(TitledPage):
+ """
+ Set group to georectify. Create group if desired.
+ """
+ def __init__(self, wizard, parent):
+ TitledPage.__init__(self, wizard, _("Select image/map group to georectify"))
+
+ self.parent = parent
+
+ self.grassdatabase = self.parent.grassdatabase
+ self.groupList = []
+
+ self.xylocation = ''
+ self.xymapset = ''
+ self.xygroup = ''
+
+ # default extension
+ self.extension = '.georect' + str(os.getpid())
+
+ #
+ # layout
+ #
+ self.sizer.AddGrowableCol(2)
+ # group
+ self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select group:')),
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(1, 1))
+ self.cb_group = wx.ComboBox(parent=self, id=wx.ID_ANY,
+ choices=self.groupList, size=(350, -1),
+ style=wx.CB_DROPDOWN | wx.CB_READONLY)
+ self.sizer.Add(item=self.cb_group,
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(1, 2))
+
+ # create group
+ self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Create group if none exists')),
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(2, 1))
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.btn_mkgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Create/edit group..."))
+ self.btn_vgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Add vector map to group..."))
+ btnSizer.Add(item=self.btn_mkgroup,
+ flag=wx.RIGHT, border=5)
+
+ btnSizer.Add(item=self.btn_vgroup,
+ flag=wx.LEFT, border=5)
+
+ self.sizer.Add(item=btnSizer,
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(2, 2))
+
+ # extension
+ self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Extension for output maps:')),
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(3, 1))
+ self.ext_txt = wx.TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(350,-1))
+ self.ext_txt.SetValue(self.extension)
+ self.sizer.Add(item=self.ext_txt,
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(3, 2))
+
+ #
+ # bindings
+ #
+ self.Bind(wx.EVT_COMBOBOX, self.OnGroup, self.cb_group)
+ self.Bind(wx.EVT_TEXT, self.OnExtension, self.ext_txt)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+ self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
+
+ # hide vector group button by default
+ self.btn_vgroup.Hide()
+
+ def OnGroup(self, event):
+ self.xygroup = event.GetString()
+
+ def OnMkGroup(self, event):
+ """!Create new group in source location/mapset"""
+ dlg = GroupDialog(parent = self, defaultGroup = self.xygroup)
+
+ dlg.ShowModal()
+ gr = dlg.GetSelectedGroup()
+ if gr in dlg.GetExistGroups():
+ self.xygroup = gr
+ else:
+ gr = ''
+ dlg.Destroy()
+
+ self.OnEnterPage()
+ self.Update()
+
+ def OnVGroup(self, event):
+ """!Add vector maps to group"""
+ dlg = VectGroup(parent = self,
+ id = wx.ID_ANY,
+ grassdb = self.grassdatabase,
+ location = self.xylocation,
+ mapset = self.xymapset,
+ group = self.xygroup)
+
+ if dlg.ShowModal() != wx.ID_OK:
+ return
+
+ dlg.MakeVGroup()
+ self.OnEnterPage()
+
+ def OnExtension(self, event):
+ self.extension = event.GetString()
+
+ def OnPageChanging(self, event=None):
+ if event.GetDirection() and self.xygroup == '':
+ GMessage(_('You must select a valid image/map '
+ 'group in order to continue'),
+ parent = self)
+ event.Veto()
+ return
+
+ if event.GetDirection() and self.extension == '':
+ GMessage(_('You must enter an map name '
+ 'extension in order to continue'),
+ parent = self)
+ event.Veto()
+ return
+
+ def OnEnterPage(self, event=None):
+ global maptype
+
+ self.groupList = []
+
+ self.xylocation = self.parent.gisrc_dict['LOCATION_NAME']
+ self.xymapset = self.parent.gisrc_dict['MAPSET']
+
+ # create a list of groups in selected mapset
+ if os.path.isdir(os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group')):
+ tmplist = os.listdir(os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group'))
+ for item in tmplist:
+ if os.path.isdir(os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ item)):
+ self.groupList.append(item)
+
+ if maptype == 'cell':
+ self.btn_vgroup.Hide()
+ self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
+
+ elif maptype == 'vector':
+ self.btn_vgroup.Show()
+ self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
+ self.Bind(wx.EVT_BUTTON, self.OnVGroup, self.btn_vgroup)
+
+ utils.ListSortLower(self.groupList)
+ self.cb_group.SetItems(self.groupList)
+
+ if len(self.groupList) > 0:
+ if self.xygroup and self.xygroup in self.groupList:
+ self.cb_group.SetStringSelection(self.xygroup)
+ else:
+ self.cb_group.SetSelection(0)
+ self.xygroup = self.groupList[0]
+
+ if self.xygroup == '' or \
+ self.extension == '':
+ wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+ else:
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+ # switch to source
+ self.parent.SwitchEnv('source')
+
+class DispMapPage(TitledPage):
+ """
+ Select ungeoreferenced map to display for interactively
+ setting ground control points (GCPs).
+ """
+ def __init__(self, wizard, parent):
+ TitledPage.__init__(self, wizard,
+ _("Select maps to display for ground control point (GCP) creation"))
+
+ self.parent = parent
+ global maptype
+
+ #
+ # layout
+ #
+ self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source map to display:')),
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(1, 1))
+
+ 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,
+ pos=(1, 2))
+
+ self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select target map to display:')),
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+ pos=(2, 1))
+
+ 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,
+ pos=(2, 2))
+
+ #
+ # bindings
+ #
+ self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection)
+ self.tgtselection.Bind(wx.EVT_TEXT, self.OnTgtSelection)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+ self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+ self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
+
+ def OnSrcSelection(self,event):
+ """!Source map to display selected"""
+ global src_map
+ global maptype
+
+ src_map = event.GetString()
+
+ if src_map == '':
+ wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+ else:
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+ try:
+ # set computational region to match selected map and zoom display to region
+ if maptype == 'cell':
+ p = RunCommand('g.region', 'rast=src_map')
+ elif maptype == 'vector':
+ p = RunCommand('g.region', 'vect=src_map')
+
+ if p.returncode == 0:
+ print 'returncode = ', str(p.returncode)
+ self.parent.Map.region = self.parent.Map.GetRegion()
+ except:
+ pass
+
+ def OnTgtSelection(self,event):
+ """!Source map to display selected"""
+ global tgt_map
+
+ tgt_map = event.GetString()
+
+ def OnPageChanging(self, event=None):
+ global src_map
+ global tgt_map
+
+ if event.GetDirection() and (src_map == ''):
+ GMessage(_('You must select a source map '
+ 'in order to continue'),
+ parent = self)
+ event.Veto()
+ return
+
+ self.parent.SwitchEnv('target')
+
+ def OnEnterPage(self, event=None):
+ global maptype
+ global src_map
+ global tgt_map
+
+ self.srcselection.SetElementList(maptype)
+ ret = RunCommand('i.group',
+ parent = self,
+ read = True,
+ group = self.parent.grouppage.xygroup,
+ flags = 'g')
+
+ if ret:
+ self.parent.src_maps = ret.splitlines()
+ else:
+ 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
+ self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
+ src_map = self.parent.src_maps[0]
+ self.srcselection.SetValue(src_map)
+
+ self.parent.SwitchEnv('target')
+ self.tgtselection.SetElementList(maptype)
+ self.tgtselection.GetElementList()
+ self.parent.SwitchEnv('source')
+
+ if src_map == '':
+ wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+ else:
+ wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+class GCP(MapFrame, ColumnSorterMixin):
+ """!
+ Manages ground control points for georectifying. Calculates RMS statics.
+ Calls i.rectify or v.transform to georectify map.
+ """
+ def __init__(self, parent, grwiz = None, id = wx.ID_ANY,
+ title = _("Manage Ground Control Points"),
+ size = (700, 300), toolbars = ["gcpdisp"], Map = None, lmgr = None):
+
+ self.grwiz = grwiz # GR Wizard
+
+ if tgt_map == '':
+ self.show_target = False
+ else:
+ self.show_target = True
+
+ #wx.Frame.__init__(self, parent, id, title, size = size, name = "GCPFrame")
+ MapFrame.__init__(self, parent = parent, title = title, size = size,
+ Map = Map, toolbars = toolbars, lmgr = lmgr, name = 'GCPMapWindow')
+
+ #
+ # init variables
+ #
+ self.parent = parent # GMFrame
+ self.parent.gcpmanagement = self
+
+ self.grassdatabase = self.grwiz.grassdatabase
+
+ self.currentlocation = self.grwiz.currentlocation
+ self.currentmapset = self.grwiz.currentmapset
+
+ self.newlocation = self.grwiz.newlocation
+ self.newmapset = self.grwiz.newmapset
+
+ self.xylocation = self.grwiz.gisrc_dict['LOCATION_NAME']
+ self.xymapset = self.grwiz.gisrc_dict['MAPSET']
+ self.xygroup = self.grwiz.grouppage.xygroup
+ self.src_maps = self.grwiz.src_maps
+ self.extension = self.grwiz.grouppage.extension
+ self.outname = ''
+ self.VectGRList = []
+
+ self.file = {
+ 'points' : os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'POINTS'),
+ 'points_bak' : os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'POINTS_BAK'),
+ 'rgrp' : os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'REF'),
+ 'vgrp' : os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'VREF'),
+ 'target' : os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'TARGET'),
+ }
+
+ # make a backup of the current points file
+ if os.path.exists(self.file['points']):
+ shutil.copy(self.file['points'], self.file['points_bak'])
+
+ # polynomial order transformation for georectification
+ self.gr_order = 1
+ # interpolation method for georectification
+ self.gr_method = 'nearest'
+ # region clipping for georectified map
+ self.clip_to_region = False
+ # number of GCPs selected to be used for georectification (checked)
+ self.GCPcount = 0
+ # forward RMS error
+ self.fwd_rmserror = 0.0
+ # backward RMS error
+ self.bkw_rmserror = 0.0
+ # list map coords and ID of map display they came from
+ self.mapcoordlist = []
+ self.mapcoordlist.append([ 0, # GCP number
+ 0.0, # source east
+ 0.0, # source north
+ 0.0, # target east
+ 0.0, # target north
+ 0.0, # forward error
+ 0.0 ] ) # backward error
+
+ # init vars to highlight high RMS errors
+ self.highest_only = True
+ self.show_unused = True
+ self.highest_key = -1
+ self.rmsthresh = 0
+ self.rmsmean = 0
+ self.rmssd = 0
+
+ self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset)
+
+ self.itemDataMap = None
+
+ # images for column sorting
+ # CheckListCtrlMixin must set an ImageList first
+ self.il = self.list.GetImageList(wx.IMAGE_LIST_SMALL)
+
+ SmallUpArrow = wx.BitmapFromImage(getSmallUpArrowImage())
+ SmallDnArrow = wx.BitmapFromImage(getSmallDnArrowImage())
+ self.sm_dn = self.il.Add(SmallDnArrow)
+ self.sm_up = self.il.Add(SmallUpArrow)
+
+ # set mouse characteristics
+ self.mapwin = self.SrcMapWindow
+ self.mapwin.mouse['box'] = 'point'
+ self.mapwin.mouse["use"] == "pointer"
+ self.mapwin.zoomtype = 0
+ self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
+ self.mapwin.SetCursor(self.cursors["cross"])
+
+ self.mapwin = self.TgtMapWindow
+
+ # set mouse characteristics
+ self.mapwin.mouse['box'] = 'point'
+ self.mapwin.mouse["use"] == "pointer"
+ self.mapwin.zoomtype = 0
+ self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
+ self.mapwin.SetCursor(self.cursors["cross"])
+
+ #
+ # show new display & draw map
+ #
+ if self.show_target:
+ self.MapWindow = self.TgtMapWindow
+ self.Map = self.TgtMap
+ self.OnZoomToMap(None)
+
+ self.MapWindow = self.SrcMapWindow
+ self.Map = self.SrcMap
+ self.OnZoomToMap(None)
+
+ #
+ # bindings
+ #
+ self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+ self.Bind(wx.EVT_CLOSE, self.OnQuit)
+
+ def __del__(self):
+ """!Disable GCP manager mode"""
+ self.parent.gcpmanagement = None
+
+ def CreateGCPList(self):
+ """!Create GCP List Control"""
+
+ return GCPList(parent=self, gcp=self)
+
+ # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+ def GetListCtrl(self):
+ return self.list
+
+ def GetMapCoordList(self):
+ return self.mapcoordlist
+
+ # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+ def GetSortImages(self):
+ return (self.sm_dn, self.sm_up)
+
+ def GetFwdError(self):
+ return self.fwd_rmserror
+
+ def GetBkwError(self):
+ return self.bkw_rmserror
+
+ def InitMapDisplay(self):
+ self.list.LoadData()
+
+ # initialize column sorter
+ self.itemDataMap = self.mapcoordlist
+ ncols = self.list.GetColumnCount()
+ ColumnSorterMixin.__init__(self, ncols)
+ # init to ascending sort on first click
+ self._colSortFlag = [1] * ncols
+
+ def SetTarget(self, tgroup, tlocation, tmapset):
+ """
+ Sets rectification target to current location and mapset
+ """
+ # check to see if we are georectifying map in current working location/mapset
+ if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+ RunCommand('i.target',
+ parent = self,
+ flags = 'c',
+ group = tgroup)
+ else:
+ self.grwiz.SwitchEnv('source')
+ RunCommand('i.target',
+ parent = self,
+ group = tgroup,
+ location = tlocation,
+ mapset = tmapset)
+ self.grwiz.SwitchEnv('target')
+
+ def AddGCP(self, event):
+ """
+ Appends an item to GCP list
+ """
+ keyval = self.list.AddGCPItem() + 1
+ # source east, source north, target east, target north, forward error, backward error
+ self.mapcoordlist.append([ keyval, # GCP number
+ 0.0, # source east
+ 0.0, # source north
+ 0.0, # target east
+ 0.0, # target north
+ 0.0, # forward error
+ 0.0 ] ) # backward error
+
+ if self.statusbarManager.GetMode() == 8: # go to
+ self.StatusbarUpdate()
+
+ def DeleteGCP(self, event):
+ """
+ Deletes selected item in GCP list
+ """
+ minNumOfItems = self.OnGROrder(None)
+
+ if self.list.GetItemCount() <= minNumOfItems:
+ GMessage(parent = self,
+ message=_("At least %d GCPs required. Operation cancelled.") % minNumOfItems)
+ return
+
+ key = self.list.DeleteGCPItem()
+ del self.mapcoordlist[key]
+
+ # update key and GCP number
+ for newkey in range(key, len(self.mapcoordlist)):
+ index = self.list.FindItemData(-1, newkey + 1)
+ self.mapcoordlist[newkey][0] = newkey
+ self.list.SetStringItem(index, 0, str(newkey))
+ self.list.SetItemData(index, newkey)
+
+ # update selected
+ if self.list.GetItemCount() > 0:
+ if self.list.selected < self.list.GetItemCount():
+ self.list.selectedkey = self.list.GetItemData(self.list.selected)
+ else:
+ self.list.selected = self.list.GetItemCount() - 1
+ self.list.selectedkey = self.list.GetItemData(self.list.selected)
+
+ self.list.SetItemState(self.list.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+ else:
+ self.list.selected = wx.NOT_FOUND
+ self.list.selectedkey = -1
+
+ self.UpdateColours()
+
+ if self.statusbarManager.GetMode() == 8: # go to
+ self.StatusbarUpdate()
+ if self.list.selectedkey > 0:
+ self.statusbarManager.SetProperty('gotoGCP', self.list.selectedkey)
+
+ def ClearGCP(self, event):
+ """
+ Clears all values in selected item of GCP list and unchecks it
+ """
+ index = self.list.GetSelected()
+
+ for i in range(4):
+ self.list.SetStringItem(index, i, '0.0')
+ self.list.SetStringItem(index, 4, '')
+ self.list.SetStringItem(index, 5, '')
+ self.list.CheckItem(index, False)
+ key = self.list.GetItemData(index)
+
+ # GCP number, source E, source N, target E, target N, fwd error, bkwd error
+ self.mapcoordlist[key] = [key, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+
+ def DrawGCP(self, coordtype):
+ """
+ Updates GCP and map coord maps and redraws
+ active (checked) GCP markers
+ """
+ self.highest_only = UserSettings.Get(group='gcpman', key='rms', subkey='highestonly')
+
+ self.show_unused = UserSettings.Get(group='gcpman', key='symbol', subkey='unused')
+ col = UserSettings.Get(group='gcpman', key='symbol', subkey='color')
+ wxLowCol = wx.Colour(col[0], col[1], col[2], 255)
+ col = UserSettings.Get(group='gcpman', key='symbol', subkey='hcolor')
+ wxHiCol = wx.Colour(col[0], col[1], col[2], 255)
+ col = UserSettings.Get(group='gcpman', key='symbol', subkey='scolor')
+ wxSelCol = wx.Colour(col[0], col[1], col[2], 255)
+ col = UserSettings.Get(group='gcpman', key='symbol', subkey='ucolor')
+ wxUnCol = wx.Colour(col[0], col[1], col[2], 255)
+ spx = UserSettings.Get(group='gcpman', key='symbol', subkey='size')
+ wpx = UserSettings.Get(group='gcpman', key='symbol', subkey='width')
+ font = self.GetFont()
+ font.SetPointSize(int(spx) + 2)
+
+ penOrig = polypenOrig = None
+
+ mapWin = None
+
+ if coordtype == 'source':
+ mapWin = self.SrcMapWindow
+ e_idx = 1
+ n_idx = 2
+ elif coordtype == 'target':
+ mapWin = self.TgtMapWindow
+ e_idx = 3
+ n_idx = 4
+
+ if not mapWin:
+ GError(parent = self,
+ message="%s%s." % (_("mapwin not defined for "),
+ str(idx)))
+ return
+
+ #for gcp in self.mapcoordlist:
+ for idx in range(self.list.GetItemCount()):
+
+ key = self.list.GetItemData(idx)
+ gcp = self.mapcoordlist[key]
+
+ if not self.list.IsChecked(idx):
+ if self.show_unused:
+ wxCol = wxUnCol
+ else:
+ continue
+ else:
+ if self.highest_only == True:
+ if key == self.highest_key:
+ wxCol = wxHiCol
+ else:
+ wxCol = wxLowCol
+ elif self.rmsthresh > 0:
+ if (gcp[5] > self.rmsthresh):
+ wxCol = wxHiCol
+ else:
+ wxCol = wxLowCol
+
+ if idx == self.list.selected:
+ wxCol = wxSelCol
+
+ if not penOrig:
+ penOrig = mapWin.pen
+ polypenOrig = mapWin.polypen
+ mapWin.pen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID)
+ mapWin.polypen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID) # ?
+
+ mapWin.pen.SetColour(wxCol)
+ mapWin.polypen.SetColour(wxCol)
+
+ coord = mapWin.Cell2Pixel((gcp[e_idx], gcp[n_idx]))
+ mapWin.DrawCross(pdc=mapWin.pdcTmp, coords=coord,
+ size=spx, text={ 'text' : '%s' % str(gcp[0]),
+ 'active' : True,
+ 'font' : font,
+ 'color': wxCol,
+ 'coords': [coord[0] + 5,
+ coord[1] + 5,
+ 5,
+ 5]})
+
+ if penOrig:
+ mapWin.pen = penOrig
+ mapWin.polypen = polypenOrig
+
+ def SetGCPData(self, coordtype, coord, mapdisp=None, confirm=False):
+ """
+ Inserts coordinates from file, mouse click on map, or after editing
+ into selected item of GCP list and checks it for use
+ """
+
+ index = self.list.GetSelected()
+ if index == wx.NOT_FOUND:
+ return
+
+ coord0 = coord[0]
+ coord1 = coord[1]
+
+ key = self.list.GetItemData(index)
+ if confirm:
+ if self.MapWindow == self.SrcMapWindow:
+ currloc = _("source")
+ else:
+ currloc = _("target")
+ ret = wx.MessageBox(parent=self,
+ caption=_("Set GCP coordinates"),
+ message=_('Set %(coor)s coordinates for GCP No. %(key)s? \n\n'
+ 'East: %(coor0)s \n'
+ 'North: %(coor1)s') % \
+ { 'coor' : currloc,
+ 'key' : str(key),
+ 'coor0' : str(coord0),
+ 'coor1' : str(coord1) },
+ style=wx.ICON_QUESTION | wx.YES_NO | wx.CENTRE)
+
+ # for wingrass
+ if os.name == 'nt':
+ self.MapWindow.SetFocus()
+ if ret == wx.NO:
+ return
+
+ if coordtype == 'source':
+ self.list.SetStringItem(index, 1, str(coord0))
+ self.list.SetStringItem(index, 2, str(coord1))
+ self.mapcoordlist[key][1] = coord[0]
+ self.mapcoordlist[key][2] = coord[1]
+ elif coordtype == 'target':
+ self.list.SetStringItem(index, 3, str(coord0))
+ self.list.SetStringItem(index, 4, str(coord1))
+ self.mapcoordlist[key][3] = coord[0]
+ self.mapcoordlist[key][4] = coord[1]
+
+ self.list.SetStringItem(index, 5, '0')
+ self.list.SetStringItem(index, 6, '0')
+ self.mapcoordlist[key][5] = 0.0
+ self.mapcoordlist[key][6] = 0.0
+
+ # self.list.ResizeColumns()
+
+ def SaveGCPs(self, event):
+ """
+ Make a POINTS file or save GCP coordinates to existing POINTS file
+ """
+
+ self.GCPcount = 0
+ try:
+ f = open(self.file['points'], mode='w')
+ # use os.linesep or '\n' here ???
+ f.write('# Ground Control Points File\n')
+ f.write("# \n")
+ f.write("# target location: " + self.currentlocation + '\n')
+ f.write("# target mapset: " + self.currentmapset + '\n')
+ f.write("#\tsource\t\ttarget\t\tstatus\n")
+ f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n")
+ f.write("#----------------------- ----------------------- ---------------\n")
+
+ for index in range(self.list.GetItemCount()):
+ if self.list.IsChecked(index) == True:
+ check = "1"
+ self.GCPcount += 1
+ else:
+ check = "0"
+ coord0 = self.list.GetItem(index, 1).GetText()
+ coord1 = self.list.GetItem(index, 2).GetText()
+ coord2 = self.list.GetItem(index, 3).GetText()
+ coord3 = self.list.GetItem(index, 4).GetText()
+ f.write(coord0 + ' ' + coord1 + ' ' + coord2 + ' ' + coord3 + ' ' + check + '\n')
+
+ except IOError, err:
+ GError(parent = self,
+ message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
+ self.file['points'], os.linesep, err))
+ return
+
+ f.close()
+
+ # if event != None save also to backup file
+ if event:
+ shutil.copy(self.file['points'], self.file['points_bak'])
+ self.parent.goutput.WriteLog(_('POINTS file saved for group <%s>') % self.xygroup)
+ #self.SetStatusText(_('POINTS file saved'))
+
+ def ReadGCPs(self):
+ """
+ Reads GCPs and georectified coordinates from POINTS file
+ """
+
+ self.GCPcount = 0
+
+ sourceMapWin = self.SrcMapWindow
+ targetMapWin = self.TgtMapWindow
+ #targetMapWin = self.parent.curr_page.maptree.mapdisplay.MapWindow
+
+ if not sourceMapWin:
+ GError(parent = self,
+ message = "%s. %s%s" % (_("source mapwin not defined"),
+ os.linesep, err))
+
+ if not targetMapWin:
+ GError(parent = self,
+ message="%s. %s%s" % (_("target mapwin not defined"),
+ os.linesep, err))
+
+ try:
+ f = open(self.file['points'], 'r')
+ GCPcnt = 0
+
+ for line in f.readlines():
+ if line[0] == '#' or line =='':
+ continue
+ line = line.replace('\n', '').strip()
+ coords = map(float, line.split())
+ if coords[4] == 1:
+ check = True
+ self.GCPcount +=1
+ else:
+ check = False
+
+ self.AddGCP(event=None)
+ self.SetGCPData('source', (coords[0], coords[1]), sourceMapWin)
+ self.SetGCPData('target', (coords[2], coords[3]), targetMapWin)
+ index = self.list.GetSelected()
+ if index != wx.NOT_FOUND:
+ self.list.CheckItem(index, check)
+ GCPcnt += 1
+
+ except IOError, err:
+ GError(parent = self,
+ message = "%s <%s>. %s%s" % (_("Reading POINTS file failed"),
+ self.file['points'], os.linesep, err))
+ return
+
+ f.close()
+
+ if GCPcnt == 0:
+ # 3 gcp is minimum
+ for i in range(3):
+ self.AddGCP(None)
+
+ if self.CheckGCPcount():
+ # calculate RMS
+ self.RMSError(self.xygroup, self.gr_order)
+
+ def ReloadGCPs(self, event):
+ """!Reload data from file"""
+
+ # use backup
+ shutil.copy(self.file['points_bak'], self.file['points'])
+
+ # delete all items in mapcoordlist
+ self.mapcoordlist = []
+ self.mapcoordlist.append([ 0, # GCP number
+ 0.0, # source east
+ 0.0, # source north
+ 0.0, # target east
+ 0.0, # target north
+ 0.0, # forward error
+ 0.0 ] ) # backward error
+
+ self.list.LoadData()
+ self.itemDataMap = self.mapcoordlist
+
+ if self._col != -1:
+ self.list.ClearColumnImage(self._col)
+ self._colSortFlag = [1] * self.list.GetColumnCount()
+
+ # draw GCPs (source and target)
+ sourceMapWin = self.SrcMapWindow
+ sourceMapWin.UpdateMap(render=False, renderVector=False)
+ if self.show_target:
+ targetMapWin = self.TgtMapWindow
+ targetMapWin.UpdateMap(render=False, renderVector=False)
+
+ def OnFocus(self, event):
+ # self.grwiz.SwitchEnv('source')
+ pass
+
+ def OnRMS(self, event):
+ """
+ RMS button handler
+ """
+ self.RMSError(self.xygroup,self.gr_order)
+
+ sourceMapWin = self.SrcMapWindow
+ sourceMapWin.UpdateMap(render=False, renderVector=False)
+ if self.show_target:
+ targetMapWin = self.TgtMapWindow
+ targetMapWin.UpdateMap(render=False, renderVector=False)
+
+ def CheckGCPcount(self, msg=False):
+ """
+ Checks to make sure that the minimum number of GCPs have been defined and
+ are active for the selected transformation order
+ """
+ if (self.GCPcount < 3 and self.gr_order == 1) or \
+ (self.GCPcount < 6 and self.gr_order == 2) or \
+ (self.GCPcount < 10 and self.gr_order == 3):
+ if msg:
+ 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
+
+ def OnGeorect(self, event):
+ """
+ Georectifies map(s) in group using i.rectify or v.transform
+ """
+ global maptype
+ self.SaveGCPs(None)
+
+ if self.CheckGCPcount(msg=True) == False:
+ return
+
+ if maptype == 'cell':
+ self.grwiz.SwitchEnv('source')
+
+ if self.clip_to_region:
+ flags = "ac"
+ else:
+ flags = "a"
+
+ busy = wx.BusyInfo(message=_("Rectifying images, please wait..."),
+ parent=self)
+ wx.Yield()
+
+ ret, msg = RunCommand('i.rectify',
+ parent = self,
+ getErrorMsg = True,
+ quiet = True,
+ group = self.xygroup,
+ extension = self.extension,
+ order = self.gr_order,
+ method=self.gr_method,
+ flags = flags)
+
+ busy.Destroy()
+
+ # provide feedback on failure
+ if ret != 0:
+ print >> sys.stderr, msg
+
+ elif maptype == 'vector':
+ outmsg = ''
+ # loop through all vectors in VREF
+ # and move resulting vector to target location
+
+ # make sure current mapset has a vector folder
+ if not os.path.isdir(os.path.join(self.grassdatabase,
+ self.currentlocation,
+ self.currentmapset,
+ 'vector')):
+ os.mkdir(os.path.join(self.grassdatabase,
+ self.currentlocation,
+ self.currentmapset,
+ 'vector'))
+
+ self.grwiz.SwitchEnv('source')
+
+ # make list of vectors to georectify from VREF
+ f = open(self.file['vgrp'])
+ vectlist = []
+ try:
+ for vect in f.readlines():
+ vect = vect.strip('\n')
+ if len(vect) < 1:
+ continue
+ vectlist.append(vect)
+ finally:
+ f.close()
+
+ # georectify each vector in VREF using v.transform
+ for vect in vectlist:
+ self.outname = vect + '_' + self.extension
+ self.parent.goutput.WriteLog(text = _('Transforming <%s>...') % vect,
+ switchPage = True)
+ msg = err = ''
+
+ 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)
+ # note: WriteLog doesn't handle GRASS_INFO_PERCENT well, so using a print here
+ # self.parent.goutput.WriteLog(text = _(err), switchPage = True)
+ self.parent.goutput.WriteLog(text = out, switchPage = True)
+ else:
+ self.parent.goutput.WriteError(_('Georectification of vector map <%s> failed') %
+ self.outname)
+ self.parent.goutput.WriteError(err)
+
+ # FIXME
+ # Copying database information not working.
+ # Does not copy from xy location to current location
+ # TODO: replace $GISDBASE etc with real paths
+ # xyLayer = []
+ # for layer in grass.vector_db(map = vect).itervalues():
+ # xyLayer.append((layer['driver'],
+ # layer['database'],
+ # layer['table']))
+
+
+ # dbConnect = grass.db_connection()
+ # print 'db connection =', dbConnect
+ # for layer in xyLayer:
+ # self.parent.goutput.RunCmd(['db.copy',
+ # '--q',
+ # '--o',
+ # 'from_driver=%s' % layer[0],
+ # 'from_database=%s' % layer[1],
+ # 'from_table=%s' % layer[2],
+ # 'to_driver=%s' % dbConnect['driver'],
+ # 'to_database=%s' % dbConnect['database'],
+ # 'to_table=%s' % layer[2] + '_' + self.extension])
+
+ # copy all georectified vectors from source location to current location
+ for name in self.VectGRList:
+ xyvpath = os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'vector',
+ name)
+ vpath = os.path.join(self.grassdatabase,
+ self.currentlocation,
+ self.currentmapset,
+ 'vector',
+ name)
+
+ if os.path.isdir(vpath):
+ self.parent.goutput.WriteWarning(_('Vector map <%s> already exists. '
+ 'Change extension name and '
+ 'georectify again.') % self.outname)
+ break
+ else:
+ # use shutil.copytree() because shutil.move() deletes src dir
+ shutil.copytree(xyvpath, vpath)
+
+ # TODO: connect vectors to copied tables with v.db.connect
+
+ 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')
+
+ def OnGeorectDone(self, **kargs):
+ """!Print final message"""
+ global maptype
+ if maptype == 'cell':
+ return
+
+ returncode = kargs['returncode']
+
+ if returncode == 0:
+ self.VectGRList.append(self.outname)
+ print '*****vector list = ' + str(self.VectGRList)
+ else:
+ self.parent.goutput.WriteError(_('Georectification of vector map <%s> failed') %
+ self.outname)
+
+
+ def OnSettings(self, event):
+ """!GCP Manager settings"""
+ dlg = GrSettingsDialog(parent=self, id=wx.ID_ANY, title=_('GCP Manager settings'))
+
+ if dlg.ShowModal() == wx.ID_OK:
+ pass
+
+ dlg.Destroy()
+
+ def UpdateColours(self, srcrender=False, srcrenderVector=False,
+ tgtrender=False, tgtrenderVector=False):
+ """!update colours"""
+ highest_fwd_err = 0.0
+ self.highest_key = 0
+ highest_idx = 0
+
+ for index in range(self.list.GetItemCount()):
+ if self.list.IsChecked(index):
+ key = self.list.GetItemData(index)
+ fwd_err = self.mapcoordlist[key][5]
+
+ if self.highest_only == True:
+ self.list.SetItemTextColour(index, wx.BLACK)
+ if highest_fwd_err < fwd_err:
+ highest_fwd_err = fwd_err
+ self.highest_key = key
+ highest_idx = index
+ elif self.rmsthresh > 0:
+ if (fwd_err > self.rmsthresh):
+ self.list.SetItemTextColour(index, wx.RED)
+ else:
+ self.list.SetItemTextColour(index, wx.BLACK)
+ else:
+ self.list.SetItemTextColour(index, wx.BLACK)
+
+ if self.highest_only and highest_fwd_err > 0.0:
+ self.list.SetItemTextColour(highest_idx, wx.RED)
+
+ sourceMapWin = self.SrcMapWindow
+ sourceMapWin.UpdateMap(render=srcrender, renderVector=srcrenderVector)
+ if self.show_target:
+ targetMapWin = self.TgtMapWindow
+ targetMapWin.UpdateMap(render=tgtrender, renderVector=tgtrenderVector)
+
+ def OnQuit(self, event):
+ """!Quit georectifier"""
+ ret = wx.MessageBox(parent=self,
+ caption=_("Quit GCP Manager"),
+ message=_('Save ground control points?'),
+ style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.CENTRE)
+
+ if ret != wx.CANCEL:
+ if ret == wx.YES:
+ self.SaveGCPs(None)
+ elif ret == wx.NO:
+ # restore POINTS file from backup
+ if os.path.exists(self.file['points_bak']):
+ shutil.copy(self.file['points_bak'], self.file['points'])
+
+ if os.path.exists(self.file['points_bak']):
+ os.unlink(self.file['points_bak'])
+
+ self.SrcMap.Clean()
+ self.TgtMap.Clean()
+
+ self.grwiz.Cleanup()
+
+ self.Destroy()
+
+ #event.Skip()
+
+ def OnGROrder(self, event):
+ """
+ sets transformation order for georectifying
+ """
+ if event:
+ self.gr_order = event.GetInt() + 1
+
+ numOfItems = self.list.GetItemCount()
+ minNumOfItems = numOfItems
+
+ if self.gr_order == 1:
+ minNumOfItems = 3
+ # self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order'))
+
+ elif self.gr_order == 2:
+ minNumOfItems = 6
+ diff = 6 - numOfItems
+ # self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order'))
+
+ elif self.gr_order == 3:
+ minNumOfItems = 10
+ # self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order'))
+
+ for i in range(minNumOfItems - numOfItems):
+ self.AddGCP(None)
+
+ return minNumOfItems
+
+ def RMSError(self, xygroup, order):
+ """
+ Uses m.transform to calculate forward and backward error for each used GCP
+ in POINTS file and insert error values into GCP list.
+ Calculates total forward and backward RMS error for all used points
+ """
+ # save GCPs to points file to make sure that all checked GCPs are used
+ self.SaveGCPs(None)
+ #self.SetStatusText('')
+
+ if self.CheckGCPcount(msg=True) == False:
+ return
+
+ # get list of forward and reverse rms error values for each point
+ self.grwiz.SwitchEnv('source')
+
+ ret = RunCommand('m.transform',
+ parent = self,
+ read = True,
+ group = xygroup,
+ order = order)
+
+ self.grwiz.SwitchEnv('target')
+
+ if ret:
+ errlist = ret.splitlines()
+ else:
+ 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
+ sdfactor = float(UserSettings.Get(group='gcpman', key='rms', subkey='sdfactor'))
+ GCPcount = 0
+ sumsq_fwd_err = 0.0
+ sumsq_bkw_err = 0.0
+ sum_fwd_err = 0.0
+ highest_fwd_err = 0.0
+ self.highest_key = 0
+ highest_idx = 0
+
+ for index in range(self.list.GetItemCount()):
+ key = self.list.GetItemData(index)
+ if self.list.IsChecked(index):
+ fwd_err, bkw_err = errlist[GCPcount].split()
+ self.list.SetStringItem(index, 5, fwd_err)
+ self.list.SetStringItem(index, 6, bkw_err)
+ self.mapcoordlist[key][5] = float(fwd_err)
+ self.mapcoordlist[key][6] = float(bkw_err)
+ self.list.SetItemTextColour(index, wx.BLACK)
+ if self.highest_only:
+ if highest_fwd_err < float(fwd_err):
+ highest_fwd_err = float(fwd_err)
+ self.highest_key = key
+ highest_idx = index
+
+ sumsq_fwd_err += float(fwd_err)**2
+ sumsq_bkw_err += float(bkw_err)**2
+ sum_fwd_err += float(fwd_err)
+ GCPcount += 1
+ else:
+ self.list.SetStringItem(index, 5, '')
+ self.list.SetStringItem(index, 6, '')
+ self.mapcoordlist[key][5] = 0.0
+ self.mapcoordlist[key][6] = 0.0
+ self.list.SetItemTextColour(index, wx.BLACK)
+
+ # SD
+ if GCPcount > 0:
+ sum_fwd_err /= GCPcount
+ self.rmsmean = sum_fwd_err /GCPcount
+ self.rmssd = (((sumsq_fwd_err/GCPcount) - self.rmsmean**2)**0.5)
+ self.rmsthresh = self.rmsmean + sdfactor * self.rmssd
+ else:
+ self.rmsthresh = 0
+ self.rmsmean = 0
+ self.rmssd = 0
+
+ if self.highest_only and highest_fwd_err > 0.0:
+ self.list.SetItemTextColour(highest_idx, wx.RED)
+ elif GCPcount > 0 and self.rmsthresh > 0 and not self.highest_only:
+ for index in range(self.list.GetItemCount()):
+ if self.list.IsChecked(index):
+ key = self.list.GetItemData(index)
+ if (self.mapcoordlist[key][5] > self.rmsthresh):
+ self.list.SetItemTextColour(index, wx.RED)
+
+ # calculate global RMS error (geometric mean)
+ self.fwd_rmserror = round((sumsq_fwd_err/GCPcount)**0.5,4)
+ self.bkw_rmserror = round((sumsq_bkw_err/GCPcount)**0.5,4)
+ self.list.ResizeColumns()
+
+ def GetNewExtent(self, region, map = None):
+
+ coord_file = utils.GetTempfile()
+ newreg = { 'n' : 0.0, 's' : 0.0, 'e' : 0.0, 'w' : 0.0,}
+
+ try:
+ f = open(coord_file, mode='w')
+ # NW corner
+ f.write(str(region['e']) + " " + str(region['n']) + "\n")
+ # NE corner
+ f.write(str(region['e']) + " " + str(region['s']) + "\n")
+ # SW corner
+ f.write(str(region['w']) + " " + str(region['n']) + "\n")
+ # SE corner
+ f.write(str(region['w']) + " " + str(region['s']) + "\n")
+ finally:
+ f.close()
+
+ # save GCPs to points file to make sure that all checked GCPs are used
+ self.SaveGCPs(None)
+
+ order = self.gr_order
+ self.gr_order = 1
+
+ if self.CheckGCPcount(msg=True) == False:
+ self.gr_order = order
+ return
+
+ self.gr_order = order
+
+ # get list of forward and reverse rms error values for each point
+ self.grwiz.SwitchEnv('source')
+
+ if map == 'source':
+ ret = RunCommand('m.transform',
+ parent = self,
+ read = True,
+ group = self.xygroup,
+ order = 1,
+ format = 'dst',
+ coords = coord_file)
+
+ elif map == 'target':
+ ret = RunCommand('m.transform',
+ parent = self,
+ read = True,
+ group = self.xygroup,
+ order = 1,
+ flags = 'r',
+ format = 'src',
+ coords = coord_file)
+
+ os.unlink(coord_file)
+
+ self.grwiz.SwitchEnv('target')
+
+ if ret:
+ errlist = ret.splitlines()
+ else:
+ GError(parent = self,
+ message=_('Could not calculate new extends.\n'
+ 'Possible error with m.transform.'))
+ return
+
+ # fist corner
+ e, n = errlist[0].split()
+ fe = float(e)
+ fn = float(n)
+ newreg['n'] = fn
+ newreg['s'] = fn
+ newreg['e'] = fe
+ newreg['w'] = fe
+ # other three corners
+ for i in range(1, 4):
+ e, n = errlist[i].split()
+ fe = float(e)
+ fn = float(n)
+ if fe < newreg['w']:
+ newreg['w'] = fe
+ if fe > newreg['e']:
+ newreg['e'] = fe
+ if fn < newreg['s']:
+ newreg['s'] = fn
+ if fn > newreg['n']:
+ newreg['n'] = fn
+
+ return newreg
+
+ def OnHelp(self, event):
+ """!Show GCP Manager manual page"""
+ cmdlist = ['g.manual', 'entry=wxGUI.GCP_Manager']
+ self.parent.goutput.RunCmd(cmdlist, compReg=False,
+ switchPage=False)
+
+ def OnUpdateActive(self, event):
+
+ if self.activemap.GetSelection() == 0:
+ self.MapWindow = self.SrcMapWindow
+ self.Map = self.SrcMap
+ else:
+ self.MapWindow = self.TgtMapWindow
+ self.Map = self.TgtMap
+
+ self.UpdateActive(self.MapWindow)
+ # for wingrass
+ if os.name == 'nt':
+ self.MapWindow.SetFocus()
+
+ def UpdateActive(self, win):
+
+ # optionally disable tool zoomback tool
+ self.GetMapToolbar().Enable('zoomback', enable = (len(self.MapWindow.zoomhistory) > 1))
+
+ if self.activemap.GetSelection() != (win == self.TgtMapWindow):
+ self.activemap.SetSelection(win == self.TgtMapWindow)
+ self.StatusbarUpdate()
+
+ def AdjustMap(self, newreg):
+ """!Adjust map window to new extents
+ """
+
+ # adjust map window
+ self.Map.region['n'] = newreg['n']
+ self.Map.region['s'] = newreg['s']
+ self.Map.region['e'] = newreg['e']
+ self.Map.region['w'] = newreg['w']
+
+ self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ # LL locations
+ if self.Map.projinfo['proj'] == 'll':
+ if newreg['n'] > 90.0:
+ newreg['n'] = 90.0
+ if newreg['s'] < -90.0:
+ newreg['s'] = -90.0
+
+ ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
+ cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
+
+ # calculate new center point and display resolution
+ self.Map.region['center_easting'] = ce
+ self.Map.region['center_northing'] = cn
+ self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
+ self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
+ self.Map.AlignExtentFromDisplay()
+
+ self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+ self.Map.region['e'], self.Map.region['w'])
+
+ if self.MapWindow.redrawAll is False:
+ self.MapWindow.redrawAll = True
+
+ self.MapWindow.UpdateMap()
+ self.StatusbarUpdate()
+
+ def OnZoomToSource(self, event):
+ """!Set target map window to match extents of source map window
+ """
+
+ if not self.MapWindow == self.TgtMapWindow:
+ self.MapWindow = self.TgtMapWindow
+ self.Map = self.TgtMap
+ self.UpdateActive(self.TgtMapWindow)
+
+ # get new N, S, E, W for target
+ newreg = self.GetNewExtent(self.SrcMap.region, 'source')
+ if newreg:
+ self.AdjustMap(newreg)
+
+ def OnZoomToTarget(self, event):
+ """!Set source map window to match extents of target map window
+ """
+
+ if not self.MapWindow == self.SrcMapWindow:
+ self.MapWindow = self.SrcMapWindow
+ self.Map = self.SrcMap
+ self.UpdateActive(self.SrcMapWindow)
+
+ # get new N, S, E, W for target
+ newreg = self.GetNewExtent(self.TgtMap.region, 'target')
+ if newreg:
+ self.AdjustMap(newreg)
+
+ def OnZoomMenuGCP(self, event):
+ """!Popup Zoom menu
+ """
+ point = wx.GetMousePosition()
+ zoommenu = wx.Menu()
+ # Add items to the menu
+
+ zoomsource = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust source display to target display'))
+ zoommenu.AppendItem(zoomsource)
+ self.Bind(wx.EVT_MENU, self.OnZoomToTarget, zoomsource)
+
+ zoomtarget = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust target display to source display'))
+ zoommenu.AppendItem(zoomtarget)
+ self.Bind(wx.EVT_MENU, self.OnZoomToSource, zoomtarget)
+
+ # Popup the menu. If an item is selected then its handler
+ # will be called before PopupMenu returns.
+ self.PopupMenu(zoommenu)
+ zoommenu.Destroy()
+
+ def OnDispResize(self, event):
+ """!GCP Map Display resized, adjust Map Windows
+ """
+ if self.GetMapToolbar():
+ srcwidth, srcheight = self.SrcMapWindow.GetSize()
+ tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
+ srcwidth = (srcwidth + tgtwidth) / 2
+ self._mgr.GetPane("target").Hide()
+ self._mgr.Update()
+ self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
+ self._mgr.GetPane("target").BestSize((srcwidth, tgtheight))
+ if self.show_target:
+ self._mgr.GetPane("target").Show()
+ self._mgr.Update()
+ pass
+
+class GCPList(wx.ListCtrl,
+ CheckListCtrlMixin,
+ ListCtrlAutoWidthMixin):
+
+ def __init__(self, parent, gcp, id=wx.ID_ANY,
+ pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
+ wx.LC_SINGLE_SEL):
+
+ wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+ self.gcp = gcp # GCP class
+ self.render = True
+
+ # Mixin settings
+ CheckListCtrlMixin.__init__(self)
+ ListCtrlAutoWidthMixin.__init__(self)
+ # TextEditMixin.__init__(self)
+
+ # tracks whether list items are checked or not
+ self.CheckList = []
+
+ self._Create()
+
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+ self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
+ self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+
+ self.selected = wx.NOT_FOUND
+ self.selectedkey = -1
+
+ def _Create(self):
+
+ if 0:
+ # normal, simple columns
+ idx_col = 0
+ for col in (_('use'),
+ _('source E'),
+ _('source N'),
+ _('target E'),
+ _('target N'),
+ _('Forward error'),
+ _('Backward error')):
+ self.InsertColumn(idx_col, col)
+ idx_col += 1
+ else:
+ # the hard way: we want images on the column header
+ info = wx.ListItem()
+ info.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT)
+ info.SetImage(-1)
+ info.m_format = wx.LIST_FORMAT_LEFT
+
+ idx_col = 0
+ for lbl in (_('use'),
+ _('source E'),
+ _('source N'),
+ _('target E'),
+ _('target N'),
+ _('Forward error'),
+ _('Backward error')):
+ info.SetText(lbl)
+ self.InsertColumnInfo(idx_col, info)
+ idx_col += 1
+
+ def LoadData(self):
+ """!Load data into list"""
+ self.DeleteAllItems()
+
+ self.render = False
+ if os.path.isfile(self.gcp.file['points']):
+ self.gcp.ReadGCPs()
+ else:
+ # 3 gcp is minimum
+ for i in range(3):
+ self.gcp.AddGCP(None)
+
+ # select first point by default
+ self.selected = 0
+ self.selectedkey = self.GetItemData(self.selected)
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+
+ self.ResizeColumns()
+ self.render = True
+
+ def OnCheckItem(self, index, flag):
+ """!Item is checked/unchecked"""
+
+ if self.render:
+ # redraw points
+ sourceMapWin = self.gcp.SrcMapWindow
+ sourceMapWin.UpdateMap(render=False, renderVector=False)
+ if self.gcp.show_target:
+ targetMapWin = self.gcp.TgtMapWindow
+ targetMapWin.UpdateMap(render=False, renderVector=False)
+
+ pass
+
+ def AddGCPItem(self):
+ """
+ Appends an item to GCP list
+ """
+ self.selectedkey = self.GetItemCount() + 1
+
+ self.Append([str(self.selectedkey), # GCP number
+ '0.0', # source E
+ '0.0', # source N
+ '0.0', # target E
+ '0.0', # target N
+ '', # forward error
+ '']) # backward error
+
+ self.selected = self.GetItemCount() - 1
+ self.SetItemData(self.selected, self.selectedkey)
+
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+
+ self.ResizeColumns()
+
+ return self.selected
+
+ def DeleteGCPItem(self):
+ """
+ Deletes selected item in GCP list
+ """
+ if self.selected == wx.NOT_FOUND:
+ return
+
+ key = self.GetItemData(self.selected)
+ self.DeleteItem(self.selected)
+
+ return key
+
+ def ResizeColumns(self):
+ """!Resize columns"""
+ minWidth = [90, 120]
+ for i in range(self.GetColumnCount()):
+ self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
+ # first column is checkbox, don't set to minWidth
+ if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]:
+ self.SetColumnWidth(i, minWidth[i > 4])
+
+ self.SendSizeEvent()
+
+ def GetSelected(self):
+ """!Get index of selected item"""
+ return self.selected
+
+ def OnItemSelected(self, event):
+ """
+ Item selected
+ """
+
+ if self.render and self.selected != event.GetIndex():
+ self.selected = event.GetIndex()
+ self.selectedkey = self.GetItemData(self.selected)
+ sourceMapWin = self.gcp.SrcMapWindow
+ sourceMapWin.UpdateMap(render=False, renderVector=False)
+ if self.gcp.show_target:
+ targetMapWin = self.gcp.TgtMapWindow
+ targetMapWin.UpdateMap(render=False, renderVector=False)
+
+ event.Skip()
+
+ def OnItemActivated(self, event):
+ """
+ When item double clicked, open editor to update coordinate values
+ """
+ coords = []
+ index = event.GetIndex()
+ key = self.GetItemData(index)
+ changed = False
+
+ for i in range(1, 5):
+ coords.append(self.GetItem(index, i).GetText())
+
+ dlg = EditGCP(parent=self, id=wx.ID_ANY, data=coords, gcpno=key)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ values = dlg.GetValues() # string
+
+ if len(values) == 0:
+ GError(parent = self,
+ message=_("Invalid coordinate value. Operation cancelled."))
+ else:
+ for i in range(len(values)):
+ if values[i] != coords[i]:
+ self.SetStringItem(index, i + 1, values[i])
+ changed = True
+
+ if changed:
+ # reset RMS and update mapcoordlist
+ self.SetStringItem(index, 5, '')
+ self.SetStringItem(index, 6, '')
+ key = self.GetItemData(index)
+ self.gcp.mapcoordlist[key] = [key,
+ float(values[0]),
+ float(values[1]),
+ float(values[2]),
+ float(values[3]),
+ 0.0,
+ 0.0]
+ self.gcp.UpdateColours()
+
+ def OnColClick(self, event):
+ """!ListCtrl forgets selected item..."""
+ self.selected = self.FindItemData(-1, self.selectedkey)
+ self.SetItemState(self.selected,
+ wx.LIST_STATE_SELECTED,
+ wx.LIST_STATE_SELECTED)
+ event.Skip()
+
+class VectGroup(wx.Dialog):
+ """
+ Dialog to create a vector group (VREF file) for georectifying
+
+ @todo Replace by g.group
+ """
+ def __init__(self, parent, id, grassdb, location, mapset, group,
+ style=wx.DEFAULT_DIALOG_STYLE):
+
+ wx.Dialog.__init__(self, parent, id, style=style,
+ title = _("Create vector map group"))
+
+ self.grassdatabase = grassdb
+ self.xylocation = location
+ self.xymapset = mapset
+ self.xygroup = group
+
+ #
+ # get list of valid vector directories
+ #
+ vectlist = os.listdir(os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'vector'))
+ for dir in vectlist:
+ if not os.path.isfile(os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'vector',
+ dir,
+ 'coor')):
+ vectlist.remove(dir)
+
+ utils.ListSortLower(vectlist)
+
+ # path to vref file
+ self.vgrpfile = os.path.join(self.grassdatabase,
+ self.xylocation,
+ self.xymapset,
+ 'group',
+ self.xygroup,
+ 'VREF')
+
+ #
+ # buttons
+ #
+ self.btnCancel = wx.Button(parent = self,
+ id = wx.ID_CANCEL)
+ self.btnOK = wx.Button(parent = self,
+ id = wx.ID_OK)
+ self.btnOK.SetDefault()
+
+
+ #
+ # list of vector maps
+ #
+ self.listMap = wx.CheckListBox(parent = self, id = wx.ID_ANY,
+ choices = vectlist)
+
+ if os.path.isfile(self.vgrpfile):
+ f = open(self.vgrpfile)
+ try:
+ checked = []
+ for line in f.readlines():
+ line = line.replace('\n', '')
+ if len(line) < 1:
+ continue
+ checked.append(line)
+ self.listMap.SetCheckedStrings(checked)
+ finally:
+ f.close()
+
+ line = wx.StaticLine(parent = self,
+ id = wx.ID_ANY, size = (20, -1),
+ style = wx.LI_HORIZONTAL)
+
+ #
+ # layout
+ #
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ box.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = _('Select vector map(s) to add to group:')),
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+ border = 5)
+
+ box.Add(item = self.listMap,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+ border = 5)
+
+
+ sizer.Add(box, flag = wx.ALIGN_RIGHT | wx.ALL,
+ border = 3)
+
+ sizer.Add(item = line, proportion = 0,
+ flag = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+ border = 5)
+
+ # buttons
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOK)
+ btnSizer.Realize()
+
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
+ border = 5)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+ self.Layout()
+
+ def MakeVGroup(self):
+ """!Create VREF file"""
+ vgrouplist = []
+ for item in range(self.listMap.GetCount()):
+ if not self.listMap.IsChecked(item):
+ continue
+ vgrouplist.append(self.listMap.GetString(item))
+
+ f = open(self.vgrpfile, mode='w')
+ try:
+ for vect in vgrouplist:
+ f.write(vect + '\n')
+ finally:
+ f.close()
+
+class EditGCP(wx.Dialog):
+ def __init__(self, parent, data, gcpno, id=wx.ID_ANY,
+ title=_("Edit GCP"),
+ style=wx.DEFAULT_DIALOG_STYLE):
+ """!Dialog for editing GPC and map coordinates in list control"""
+
+ wx.Dialog.__init__(self, parent, id, title=title, style=style)
+
+ panel = wx.Panel(parent=self)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
+ label=" %s %s " % (_("Ground Control Point No."), str(gcpno)))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+ # source coordinates
+ gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+
+ self.xcoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+ self.ycoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+ self.ecoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+ self.ncoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+
+ # swap source N, target E
+ tmp_coord = data[1]
+ data[1] = data[2]
+ data[2] = tmp_coord
+
+ row = 0
+ col = 0
+ idx = 0
+ for label, win in ((_("source E:"), self.xcoord),
+ (_("target E:"), self.ecoord),
+ (_("source N:"), self.ycoord),
+ (_("target N:"), self.ncoord)):
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+ label=label)
+ gridSizer.Add(item=label,
+ flag=wx.ALIGN_CENTER_VERTICAL,
+ pos=(row, col))
+
+ col += 1
+ win.SetValue(str(data[idx]))
+
+ gridSizer.Add(item=win,
+ pos=(row, col))
+
+ col += 1
+ idx += 1
+
+ if col > 3:
+ row += 1
+ col = 0
+
+ boxSizer.Add(item=gridSizer, proportion=1,
+ flag=wx.EXPAND | wx.ALL, border=5)
+
+ sizer.Add(item=boxSizer, proportion=1,
+ flag=wx.EXPAND | wx.ALL, border=5)
+
+ #
+ # buttons
+ #
+ self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
+ self.btnOk = wx.Button(panel, wx.ID_OK)
+ self.btnOk.SetDefault()
+
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOk)
+ btnSizer.Realize()
+
+ sizer.Add(item=btnSizer, proportion=0,
+ flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+ panel.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def GetValues(self, columns=None):
+ """!Return list of values (as strings).
+ """
+ valuelist = []
+ try:
+ float(self.xcoord.GetValue())
+ float(self.ycoord.GetValue())
+ float(self.ecoord.GetValue())
+ float(self.ncoord.GetValue())
+ except ValueError:
+ return valuelist
+
+ valuelist.append(self.xcoord.GetValue())
+ valuelist.append(self.ycoord.GetValue())
+ valuelist.append(self.ecoord.GetValue())
+ valuelist.append(self.ncoord.GetValue())
+
+ return valuelist
+
+class GrSettingsDialog(wx.Dialog):
+ def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=wx.DEFAULT_DIALOG_STYLE):
+ wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+ """
+ Dialog to set profile text options: font, title
+ and font size, axis labels and font size
+ """
+ #
+ # initialize variables
+ #
+ self.parent = parent
+ self.new_src_map = src_map
+ self.new_tgt_map = tgt_map
+ self.sdfactor = 0
+
+ self.symbol = {}
+
+ self.methods = ["nearest",
+ "bilinear",
+ "bilinear_f",
+ "cubic",
+ "cubic_f",
+ "lanczos",
+ "lanczos_f"]
+
+ # notebook
+ notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
+ self.__CreateSymbologyPage(notebook)
+ self.__CreateRectificationPage(notebook)
+
+ # buttons
+ btnSave = wx.Button(self, wx.ID_SAVE)
+ btnApply = wx.Button(self, wx.ID_APPLY)
+ btnClose = wx.Button(self, wx.ID_CLOSE)
+ btnApply.SetDefault()
+
+ # bindings
+ btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+ btnApply.SetToolTipString(_("Apply changes for the current session"))
+ btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+ btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+ btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+ btnClose.SetToolTipString(_("Close dialog"))
+
+ # sizers
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
+ btnSizer.Add(btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
+ btnSizer.Add(btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
+
+ # sizers
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ mainSizer.Add(item=notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+ mainSizer.Add(item=btnSizer, proportion=0,
+ flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+ # flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def __CreateSymbologyPage(self, notebook):
+ """!Create notebook page with symbology settings"""
+
+ panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
+ notebook.AddPage(page=panel, text=_("Symbology"))
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ rmsgridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+ rmsgridSizer.AddGrowableCol(1)
+
+ # highlight only highest forward RMS error
+ self.highlighthighest = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+ label=_("Highlight highest RMS error only"))
+ hh = UserSettings.Get(group='gcpman', key='rms', subkey='highestonly')
+ self.highlighthighest.SetValue(hh)
+ rmsgridSizer.Add(item=self.highlighthighest, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 0))
+
+ # RMS forward error threshold
+ rmslabel = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Highlight RMS error > M + SD * factor:"))
+ rmslabel.SetToolTip(wx.ToolTip(_("Highlight GCPs with an RMS error larger than \n"
+ "mean + standard deviation * given factor. \n"
+ "Recommended values for this factor are between 1 and 2.")))
+ rmsgridSizer.Add(item=rmslabel, flag=wx.ALIGN_CENTER_VERTICAL, pos=(1, 0))
+ sdfactor = UserSettings.Get(group='gcpman', key='rms', subkey='sdfactor')
+ self.rmsWin = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+ size=(70,-1), style=wx.TE_NOHIDESEL)
+ self.rmsWin.SetValue("%s" % str(sdfactor))
+ if (self.parent.highest_only == True):
+ self.rmsWin.Disable()
+
+ self.symbol['sdfactor'] = self.rmsWin.GetId()
+ rmsgridSizer.Add(item=self.rmsWin, flag=wx.ALIGN_RIGHT, pos=(1, 1))
+ sizer.Add(item=rmsgridSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+ box = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Symbol settings"))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+ gridSizer.AddGrowableCol(1)
+
+ #
+ # general symbol color
+ #
+ row = 0
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ col = UserSettings.Get(group='gcpman', key='symbol', subkey='color')
+ colWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+ colour=wx.Colour(col[0],
+ col[1],
+ col[2],
+ 255))
+ self.symbol['color'] = colWin.GetId()
+ gridSizer.Add(item=colWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ #
+ # symbol color for high forward RMS error
+ #
+ row += 1
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for high RMS error:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ hcol = UserSettings.Get(group='gcpman', key='symbol', subkey='hcolor')
+ hcolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+ colour=wx.Colour(hcol[0],
+ hcol[1],
+ hcol[2],
+ 255))
+ self.symbol['hcolor'] = hcolWin.GetId()
+ gridSizer.Add(item=hcolWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ #
+ # symbol color for selected GCP
+ #
+ row += 1
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for selected GCP:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ scol = UserSettings.Get(group='gcpman', key='symbol', subkey='scolor')
+ scolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+ colour=wx.Colour(scol[0],
+ scol[1],
+ scol[2],
+ 255))
+ self.symbol['scolor'] = scolWin.GetId()
+ gridSizer.Add(item=scolWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ #
+ # symbol color for unused GCP
+ #
+ row += 1
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for unused GCPs:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ ucol = UserSettings.Get(group='gcpman', key='symbol', subkey='ucolor')
+ ucolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+ colour=wx.Colour(ucol[0],
+ ucol[1],
+ ucol[2],
+ 255))
+ self.symbol['ucolor'] = ucolWin.GetId()
+ gridSizer.Add(item=ucolWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ # show unused GCPs
+ row += 1
+ self.showunused = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+ label=_("Show unused GCPs"))
+ shuu = UserSettings.Get(group='gcpman', key='symbol', subkey='unused')
+ self.showunused.SetValue(shuu)
+ gridSizer.Add(item=self.showunused, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+
+ #
+ # symbol size
+ #
+ row += 1
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Symbol size:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ symsize = int(UserSettings.Get(group='gcpman', key='symbol', subkey='size'))
+ sizeWin = wx.SpinCtrl(parent=panel, id=wx.ID_ANY,
+ min=1, max=20)
+ sizeWin.SetValue(symsize)
+ self.symbol['size'] = sizeWin.GetId()
+ gridSizer.Add(item=sizeWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ #
+ # symbol width
+ #
+ row += 1
+ label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width:"))
+ gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ width = int(UserSettings.Get(group='gcpman', key='symbol', subkey='width'))
+ widWin = wx.SpinCtrl(parent=panel, id=wx.ID_ANY,
+ min=1, max=10)
+ widWin.SetValue(width)
+ self.symbol['width'] = widWin.GetId()
+ gridSizer.Add(item=widWin,
+ flag=wx.ALIGN_RIGHT,
+ pos=(row, 1))
+
+ boxSizer.Add(item=gridSizer, flag=wx.EXPAND)
+ sizer.Add(item=boxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+ #
+ # maps to display
+ #
+ # source map to display
+ 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 = 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()
+
+ sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select source map to display:')),
+ proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ sizer.Add(item=self.srcselection, proportion=0,
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ self.srcselection.SetValue(src_map)
+ sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select target map to display:')),
+ proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ sizer.Add(item=self.tgtselection, proportion=0,
+ flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ self.tgtselection.SetValue(tgt_map)
+
+ # bindings
+ self.highlighthighest.Bind(wx.EVT_CHECKBOX, self.OnHighlight)
+ self.rmsWin.Bind(wx.EVT_TEXT, self.OnSDFactor)
+ self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection)
+ self.tgtselection.Bind(wx.EVT_TEXT, self.OnTgtSelection)
+
+ panel.SetSizer(sizer)
+
+ return panel
+
+ def __CreateRectificationPage(self, notebook):
+ """!Create notebook page with symbology settings"""
+
+ panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
+ notebook.AddPage(page=panel, text=_("Rectification"))
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # transformation order
+ self.rb_grorder = wx.RadioBox(parent=panel, id=wx.ID_ANY,
+ label=" %s " % _("Select rectification order"),
+ choices=[_('1st order'), _('2nd order'), _('3rd order')],
+ majorDimension=wx.RA_SPECIFY_COLS)
+ sizer.Add(item=self.rb_grorder, proportion=0,
+ flag=wx.EXPAND | wx.ALL, border=5)
+ self.rb_grorder.SetSelection(self.parent.gr_order - 1)
+
+ # interpolation method
+ gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+ gridSizer.AddGrowableCol(1)
+ gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select interpolation method:')),
+ pos=(0,0), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ self.grmethod = wx.Choice(parent=panel, id=wx.ID_ANY,
+ choices = self.methods)
+ gridSizer.Add(item=self.grmethod, pos=(0,1),
+ flag=wx.ALIGN_RIGHT, border=5)
+ self.grmethod.SetStringSelection(self.parent.gr_method)
+ sizer.Add(item=gridSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+ # clip to region
+ self.check = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+ label=_("clip to computational region in target location"))
+ sizer.Add(item=self.check, proportion=0,
+ flag=wx.EXPAND | wx.ALL, border=5)
+ self.check.SetValue(self.parent.clip_to_region)
+
+ # extension
+ sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Extension for output maps:')),
+ proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+ self.ext_txt = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value="", size=(350,-1))
+ self.ext_txt.SetValue(self.parent.extension)
+ sizer.Add(item=self.ext_txt,
+ proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+ # bindings
+ self.ext_txt.Bind(wx.EVT_TEXT, self.OnExtension)
+ self.Bind(wx.EVT_RADIOBOX, self.parent.OnGROrder, self.rb_grorder)
+ self.Bind(wx.EVT_CHOICE, self.OnMethod, self.grmethod)
+ self.Bind(wx.EVT_CHECKBOX, self.OnClipRegion, self.check)
+
+ panel.SetSizer(sizer)
+
+ return panel
+
+ def OnHighlight(self, event):
+ """!Checkbox 'highlighthighest' checked/unchecked"""
+ if self.highlighthighest.IsChecked():
+ self.parent.highest_only = True
+ self.rmsWin.Disable()
+ else:
+ self.parent.highest_only = False
+ self.rmsWin.Enable()
+
+ def OnSDFactor(self,event):
+ """!New factor for RMS threshold = M + SD * factor"""
+
+ self.sdfactor = float(event.GetString())
+
+ if self.sdfactor <= 0:
+ GError(parent = self,
+ message=_('RMS threshold factor must be > 0'))
+ elif self.sdfactor < 1:
+ 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"""
+ global src_map
+
+ tmp_map = event.GetString()
+
+ if not tmp_map == '' and not tmp_map == src_map:
+ self.new_src_map = tmp_map
+
+ def OnTgtSelection(self,event):
+ """!Target map to display selected"""
+ global tgt_map
+
+ tmp_map = event.GetString()
+
+ if not tmp_map == tgt_map:
+ self.new_tgt_map = tmp_map
+
+ def OnMethod(self, event):
+ self.parent.gr_method = self.methods[event.GetSelection()]
+
+ def OnClipRegion(self, event):
+ self.parent.clip_to_region = event.IsChecked()
+
+ def OnExtension(self, event):
+ self.parent.extension = event.GetString()
+
+ def UpdateSettings(self):
+ global src_map
+ global tgt_map
+
+ layers = None
+
+ UserSettings.Set(group='gcpman', key='rms', subkey='highestonly',
+ value=self.highlighthighest.GetValue())
+ if self.sdfactor > 0:
+ UserSettings.Set(group='gcpman', key='rms', subkey='sdfactor',
+ value=self.sdfactor)
+
+ self.parent.sdfactor = self.sdfactor
+ if self.parent.rmsthresh > 0:
+ self.parent.rmsthresh = self.parent.mean + self.parent.sdfactor * self.parent.rmssd
+
+ UserSettings.Set(group='gcpman', key='symbol', subkey='color',
+ value=tuple(wx.FindWindowById(self.symbol['color']).GetColour()))
+ UserSettings.Set(group='gcpman', key='symbol', subkey='hcolor',
+ value=tuple(wx.FindWindowById(self.symbol['hcolor']).GetColour()))
+ UserSettings.Set(group='gcpman', key='symbol', subkey='scolor',
+ value=tuple(wx.FindWindowById(self.symbol['scolor']).GetColour()))
+ UserSettings.Set(group='gcpman', key='symbol', subkey='ucolor',
+ value=tuple(wx.FindWindowById(self.symbol['ucolor']).GetColour()))
+ UserSettings.Set(group='gcpman', key='symbol', subkey='unused',
+ value=self.showunused.GetValue())
+ UserSettings.Set(group='gcpman', key='symbol', subkey='size',
+ value=wx.FindWindowById(self.symbol['size']).GetValue())
+ UserSettings.Set(group='gcpman', key='symbol', subkey='width',
+ value=wx.FindWindowById(self.symbol['width']).GetValue())
+
+ srcrender = False
+ srcrenderVector = False
+ tgtrender = False
+ tgtrenderVector = False
+ if self.new_src_map != src_map:
+ # remove old layer
+ layers = self.parent.grwiz.SrcMap.GetListOfLayers()
+ self.parent.grwiz.SrcMap.DeleteLayer(layers[0])
+
+ src_map = self.new_src_map
+ cmdlist = ['d.rast', 'map=%s' % src_map]
+ self.parent.grwiz.SwitchEnv('source')
+ name, found = utils.GetLayerNameFromCmd(cmdlist),
+ self.parent.grwiz.SrcMap.AddLayer(type='raster', command=cmdlist, l_active=True,
+ name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+ self.parent.grwiz.SwitchEnv('target')
+ srcrender = True
+
+ if self.new_tgt_map != tgt_map:
+ # remove old layer
+ layers = self.parent.grwiz.TgtMap.GetListOfLayers()
+ if layers:
+ self.parent.grwiz.TgtMap.DeleteLayer(layers[0])
+ tgt_map = self.new_tgt_map
+
+ if tgt_map != '':
+ cmdlist = ['d.rast', 'map=%s' % tgt_map]
+ name, found = utils.GetLayerNameFromCmd(cmdlist)
+ self.parent.grwiz.TgtMap.AddLayer(type='raster', command=cmdlist, l_active=True,
+ name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+ tgtrender = True
+ if self.parent.show_target == False:
+ self.parent.show_target = True
+ self.parent._mgr.GetPane("target").Show()
+ self.parent._mgr.Update()
+ self.parent.GetMapToolbar().Enable('zoommenu', enable = True)
+ self.parent.activemap.Enable()
+ self.parent.TgtMapWindow.ZoomToMap(layers = self.parent.TgtMap.GetListOfLayers())
+ else: # tgt_map == ''
+ if self.parent.show_target == True:
+ self.parent.show_target = False
+ self.parent._mgr.GetPane("target").Hide()
+ self.parent._mgr.Update()
+ self.parent.activemap.SetSelection(0)
+ self.parent.activemap.Enable(False)
+ self.parent.GetMapToolbar().Enable('zoommenu', enable = False)
+
+ self.parent.UpdateColours(srcrender, srcrenderVector, tgtrender, tgtrenderVector)
+
+ def OnSave(self, event):
+ """!Button 'Save' pressed"""
+ self.UpdateSettings()
+ fileSettings = {}
+ UserSettings.ReadSettingsFile(settings=fileSettings)
+ fileSettings['gcpman'] = UserSettings.Get(group='gcpman')
+ file = UserSettings.SaveToFile(fileSettings)
+ self.parent.parent.goutput.WriteLog(_('GCP Manager settings saved to file \'%s\'.') % file)
+ #self.Close()
+
+ def OnApply(self, event):
+ """!Button 'Apply' pressed"""
+ self.UpdateSettings()
+ #self.Close()
+
+ def OnClose(self, event):
+ """!Button 'Cancel' pressed"""
+ self.Close()
Copied: grass/trunk/gui/wxpython/gcp/mapdisp.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/gcpmapdisp.py)
===================================================================
--- grass/trunk/gui/wxpython/gcp/mapdisp.py (rev 0)
+++ grass/trunk/gui/wxpython/gcp/mapdisp.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,629 @@
+"""!
+ at package gcp.mapdisp
+
+ at brief Display to manage ground control points with two toolbars, one
+for various display management functions, one for manipulating GCPs.
+
+Classes:
+- MapFrame
+
+(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.
+
+ at author Markus Metz
+"""
+
+import os
+import math
+import platform
+
+from core import globalvar
+import wx
+import wx.aui
+
+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
+
+class MapFrame(MapFrameBase):
+ """!Main frame for map display window. Drawing takes place in
+ child double buffered drawing window.
+ """
+ def __init__(self, parent=None, title=_("GRASS GIS Manage Ground Control Points"),
+ toolbars=["gcpdisp"], tree=None, notebook=None, lmgr=None,
+ page=None, Map=None, auimgr=None, name = 'GCPMapWindow', **kwargs):
+ """!Main map display window with toolbars, statusbar and
+ DrawWindow
+
+ @param toolbars array of activated toolbars, e.g. ['map', 'digit']
+ @param tree reference to layer tree
+ @param notebook control book ID in Layer Manager
+ @param lmgr Layer Manager
+ @param page notebook page with layer tree
+ @param Map instance of render.Map
+ @param auimgs AUI manager
+ @param kwargs wx.Frame attribures
+ """
+
+ MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars,
+ Map = Map, auimgr = auimgr, name = name, **kwargs)
+
+ self._layerManager = lmgr # Layer Manager object
+ self.tree = tree # Layer Manager layer tree object
+ self.page = page # Notebook page holding the layer tree
+ self.layerbook = notebook # Layer Manager layer tree notebook
+ #
+ # Add toolbars
+ #
+ for toolb in toolbars:
+ self.AddToolbar(toolb)
+
+ self.activemap = self.toolbars['gcpdisp'].togglemap
+ self.activemap.SetSelection(0)
+
+ self.SrcMap = self.grwiz.SrcMap # instance of render.Map
+ self.TgtMap = self.grwiz.TgtMap # instance of render.Map
+ self._mgr.SetDockSizeConstraint(0.5, 0.5)
+
+ #
+ # Add statusbar
+ #
+
+ # items for choice
+ self.statusbarItems = [sb.SbCoordinates,
+ sb.SbRegionExtent,
+ sb.SbCompRegionExtent,
+ sb.SbShowRegion,
+ sb.SbResolution,
+ sb.SbDisplayGeometry,
+ sb.SbMapScale,
+ sb.SbProjection,
+ sb.SbGoToGCP,
+ sb.SbRMSError]
+
+
+ # create statusbar and its manager
+ statusbar = self.CreateStatusBar(number = 4, style = 0)
+ statusbar.SetStatusWidths([-5, -2, -1, -1])
+ self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
+
+ # fill statusbar manager
+ self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
+ self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
+ self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
+
+ self.statusbarManager.SetMode(8) # goto GCP
+ self.statusbarManager.Update()
+
+
+ #
+ # Init map display (buffered DC & set default cursor)
+ #
+ self.grwiz.SwitchEnv('source')
+ self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY,
+ Map=self.SrcMap, tree=self.tree, lmgr=self._layerManager)
+
+ self.grwiz.SwitchEnv('target')
+ self.TgtMapWindow = BufferedWindow(self, id=wx.ID_ANY,
+ Map=self.TgtMap, tree=self.tree, lmgr=self._layerManager)
+ self.MapWindow = self.SrcMapWindow
+ self.Map = self.SrcMap
+ self.SrcMapWindow.SetCursor(self.cursors["cross"])
+ self.TgtMapWindow.SetCursor(self.cursors["cross"])
+
+ #
+ # initialize region values
+ #
+ self._initMap(map = self.SrcMap)
+ self._initMap(map = self.TgtMap)
+
+ #
+ # Bind various events
+ #
+ self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+ self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+ self.Bind(wx.EVT_SIZE, self.OnDispResize)
+ self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
+
+ #
+ # Update fancy gui style
+ #
+ # AuiManager wants a CentrePane, workaround to get two equally sized windows
+ self.list = self.CreateGCPList()
+
+ #self.SrcMapWindow.SetSize((300, 300))
+ #self.TgtMapWindow.SetSize((300, 300))
+ self.list.SetSize((100, 150))
+ self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
+ Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
+ RightDockable(False).PinButton().FloatingSize((600,200)).
+ CloseButton(False).DestroyOnClose(True).
+ Top().Layer(1).MinSize((200,100)))
+ self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
+ Name("source").Caption(_("Source Display")).Dockable(False).
+ CloseButton(False).DestroyOnClose(True).Floatable(False).
+ Centre())
+ self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
+ Name("target").Caption(_("Target Display")).Dockable(False).
+ CloseButton(False).DestroyOnClose(True).Floatable(False).
+ Right().Layer(0))
+
+ srcwidth, srcheight = self.SrcMapWindow.GetSize()
+ tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
+ srcwidth = (srcwidth + tgtwidth) / 2
+ self._mgr.GetPane("target").Hide()
+ self._mgr.Update()
+ self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
+ self._mgr.GetPane("target").BestSize((srcwidth, srcheight))
+ if self.show_target:
+ self._mgr.GetPane("target").Show()
+ else:
+ self.activemap.Enable(False)
+ # needed by Mac OS, does not harm on Linux, breaks display on Windows
+ if platform.system() != 'Windows':
+ self._mgr.Update()
+
+ #
+ # Init print module and classes
+ #
+ self.printopt = PrintOptions(self, self.MapWindow)
+
+ #
+ # Initialization of digitization tool
+ #
+ self.digit = None
+
+ # set active map
+ self.MapWindow = self.SrcMapWindow
+ self.Map = self.SrcMap
+
+ # do not init zoom history here, that happens when zooming to map(s)
+
+ #
+ # Re-use dialogs
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
+ self.dialogs['category'] = None
+ self.dialogs['barscale'] = None
+ self.dialogs['legend'] = None
+
+ self.decorationDialog = None # decoration/overlays
+
+ def AddToolbar(self, name):
+ """!Add defined toolbar to the window
+
+ Currently known toolbars are:
+ - 'map' - basic map toolbar
+ - 'vdigit' - vector digitizer
+ - 'gcpdisp' - GCP Manager, Display
+ - 'gcpman' - GCP Manager, points management
+ - 'nviz' - 3D view mode
+ """
+ # default toolbar
+ if name == "map":
+ self.toolbars['map'] = MapToolbar(self, self.Map)
+
+ self._mgr.AddPane(self.toolbars['map'],
+ wx.aui.AuiPaneInfo().
+ Name("maptoolbar").Caption(_("Map Toolbar")).
+ ToolbarPane().Top().
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(False).TopDockable(True).
+ CloseButton(False).Layer(2).
+ BestSize((self.toolbars['map'].GetSize())))
+
+ # GCP display
+ elif name == "gcpdisp":
+ self.toolbars['gcpdisp'] = GCPDisplayToolbar(self)
+
+ self._mgr.AddPane(self.toolbars['gcpdisp'],
+ wx.aui.AuiPaneInfo().
+ Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
+ ToolbarPane().Top().
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(False).TopDockable(True).
+ CloseButton(False).Layer(2))
+
+ if self.show_target == False:
+ self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
+
+ self.toolbars['gcpman'] = GCPManToolbar(self)
+
+ self._mgr.AddPane(self.toolbars['gcpman'],
+ wx.aui.AuiPaneInfo().
+ Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
+ ToolbarPane().Top().Row(1).
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(False).TopDockable(True).
+ CloseButton(False).Layer(2))
+
+ self._mgr.Update()
+
+ def OnUpdateProgress(self, event):
+ """
+ Update progress bar info
+ """
+ self.GetProgressBar().SetValue(event.value)
+
+ event.Skip()
+
+ def OnFocus(self, event):
+ """
+ Change choicebook page to match display.
+ Or set display for georectifying
+ """
+ if self._layerManager and \
+ self._layerManager.gcpmanagement:
+ # in GCP Management, set focus to current MapWindow for mouse actions
+ self.OnPointer(event)
+ self.MapWindow.SetFocus()
+ else:
+ # change bookcontrol page to page associated with display
+ # GCP Manager: use bookcontrol?
+ if self.page:
+ pgnum = self.layerbook.GetPageIndex(self.page)
+ if pgnum > -1:
+ self.layerbook.SetSelection(pgnum)
+
+ event.Skip()
+
+ def OnDraw(self, event):
+ """!Re-display current map composition
+ """
+ self.MapWindow.UpdateMap(render = False)
+
+ def OnRender(self, event):
+ """!Re-render map composition (each map layer)
+ """
+ # delete tmp map layers (queries)
+ qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
+ for layer in qlayer:
+ self.Map.DeleteLayer(layer)
+
+ self.SrcMapWindow.UpdateMap(render=True)
+ if self.show_target:
+ self.TgtMapWindow.UpdateMap(render=True)
+
+ # update statusbar
+ self.StatusbarUpdate()
+
+ def OnPointer(self, event):
+ """!Pointer button clicked
+ """
+ # change the cursor
+ self.SrcMapWindow.SetCursor(self.cursors["cross"])
+ self.SrcMapWindow.mouse['use'] = "pointer"
+ self.SrcMapWindow.mouse['box'] = "point"
+ self.TgtMapWindow.SetCursor(self.cursors["cross"])
+ self.TgtMapWindow.mouse['use'] = "pointer"
+ self.TgtMapWindow.mouse['box'] = "point"
+
+ def OnZoomIn(self, event):
+ """
+ Zoom in the map.
+ Set mouse cursor, zoombox attributes, and zoom direction
+ """
+ if self.GetToolbar('map'):
+ self.toolbars['map'].OnTool(event)
+ self.toolbars['map'].action['desc'] = ''
+
+ self.MapWindow.mouse['use'] = "zoom"
+ self.MapWindow.mouse['box'] = "box"
+ self.MapWindow.zoomtype = 1
+ self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+
+ # change the cursor
+ self.MapWindow.SetCursor(self.cursors["cross"])
+
+ if self.MapWindow == self.SrcMapWindow:
+ win = self.TgtMapWindow
+ elif self.MapWindow == self.TgtMapWindow:
+ win = self.SrcMapWindow
+
+ win.mouse['use'] = "zoom"
+ win.mouse['box'] = "box"
+ win.zoomtype = 1
+ win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+
+ # change the cursor
+ win.SetCursor(self.cursors["cross"])
+
+ def OnZoomOut(self, event):
+ """
+ Zoom out the map.
+ Set mouse cursor, zoombox attributes, and zoom direction
+ """
+ if self.GetToolbar('map'):
+ self.toolbars['map'].OnTool(event)
+ self.toolbars['map'].action['desc'] = ''
+
+ self.MapWindow.mouse['use'] = "zoom"
+ self.MapWindow.mouse['box'] = "box"
+ self.MapWindow.zoomtype = -1
+ self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+
+ # change the cursor
+ self.MapWindow.SetCursor(self.cursors["cross"])
+
+ if self.MapWindow == self.SrcMapWindow:
+ win = self.TgtMapWindow
+ elif self.MapWindow == self.TgtMapWindow:
+ win = self.SrcMapWindow
+
+ win.mouse['use'] = "zoom"
+ win.mouse['box'] = "box"
+ win.zoomtype = -1
+ win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+
+ # change the cursor
+ win.SetCursor(self.cursors["cross"])
+
+ def OnPan(self, event):
+ """
+ Panning, set mouse to drag
+ """
+ if self.GetToolbar('map'):
+ self.toolbars['map'].OnTool(event)
+ self.toolbars['map'].action['desc'] = ''
+
+ self.MapWindow.mouse['use'] = "pan"
+ self.MapWindow.mouse['box'] = "pan"
+ self.MapWindow.zoomtype = 0
+
+ # change the cursor
+ self.MapWindow.SetCursor(self.cursors["hand"])
+
+ if self.MapWindow == self.SrcMapWindow:
+ win = self.TgtMapWindow
+ elif self.MapWindow == self.TgtMapWindow:
+ win = self.SrcMapWindow
+
+ win.mouse['use'] = "pan"
+ win.mouse['box'] = "pan"
+ win.zoomtype = 0
+
+ # change the cursor
+ win.SetCursor(self.cursors["hand"])
+
+ def OnErase(self, event):
+ """
+ Erase the canvas
+ """
+ self.MapWindow.EraseMap()
+
+ if self.MapWindow == self.SrcMapWindow:
+ win = self.TgtMapWindow
+ elif self.MapWindow == self.TgtMapWindow:
+ win = self.SrcMapWindow
+
+ win.EraseMap()
+
+ def OnZoomRegion(self, event):
+ """
+ Zoom to region
+ """
+ self.Map.getRegion()
+ self.Map.getResolution()
+ self.UpdateMap()
+ # event.Skip()
+
+ def OnAlignRegion(self, event):
+ """
+ Align region
+ """
+ if not self.Map.alignRegion:
+ self.Map.alignRegion = True
+ else:
+ self.Map.alignRegion = False
+ # event.Skip()
+
+ def SaveToFile(self, event):
+ """!Save map to image
+ """
+ img = self.MapWindow.img
+ if not img:
+ GMessage(parent = self,
+ message = _("Nothing to render (empty map). Operation canceled."))
+ return
+ filetype, ltype = GetImageHandlers(img)
+
+ # get size
+ dlg = ImageSizeDialog(self)
+ dlg.CentreOnParent()
+ if dlg.ShowModal() != wx.ID_OK:
+ dlg.Destroy()
+ return
+ width, height = dlg.GetValues()
+ dlg.Destroy()
+
+ # get filename
+ dlg = wx.FileDialog(parent = self,
+ message = _("Choose a file name to save the image "
+ "(no need to add extension)"),
+ wildcard = filetype,
+ style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ if not path:
+ dlg.Destroy()
+ return
+
+ base, ext = os.path.splitext(path)
+ fileType = ltype[dlg.GetFilterIndex()]['type']
+ extType = ltype[dlg.GetFilterIndex()]['ext']
+ if ext != extType:
+ path = base + '.' + extType
+
+ self.MapWindow.SaveToFile(path, fileType,
+ width, height)
+
+ dlg.Destroy()
+
+ def PrintMenu(self, event):
+ """
+ Print options and output menu for map display
+ """
+ point = wx.GetMousePosition()
+ printmenu = wx.Menu()
+ # Add items to the menu
+ setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+ printmenu.AppendItem(setup)
+ self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
+
+ preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+ printmenu.AppendItem(preview)
+ self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+
+ doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display'))
+ printmenu.AppendItem(doprint)
+ self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
+
+ # Popup the menu. If an item is selected then its handler
+ # will be called before PopupMenu returns.
+ self.PopupMenu(printmenu)
+ printmenu.Destroy()
+
+
+ def FormatDist(self, dist):
+ """!Format length numbers and units in a nice way,
+ as a function of length. From code by Hamish Bowman
+ Grass Development Team 2006"""
+
+ mapunits = self.Map.projinfo['units']
+ if mapunits == 'metres': mapunits = 'meters'
+ outunits = mapunits
+ dist = float(dist)
+ divisor = 1.0
+
+ # figure out which units to use
+ if mapunits == 'meters':
+ if dist > 2500.0:
+ outunits = 'km'
+ divisor = 1000.0
+ else: outunits = 'm'
+ elif mapunits == 'feet':
+ # nano-bug: we match any "feet", but US Survey feet is really
+ # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
+ # miles the tick markers are rounded to the nearest 10th of a
+ # mile (528'), the difference in foot flavours is ignored.
+ if dist > 5280.0:
+ outunits = 'miles'
+ divisor = 5280.0
+ else:
+ outunits = 'ft'
+ elif 'degree' in mapunits:
+ if dist < 1:
+ outunits = 'min'
+ divisor = (1/60.0)
+ else:
+ outunits = 'deg'
+
+ # format numbers in a nice way
+ if (dist/divisor) >= 2500.0:
+ outdist = round(dist/divisor)
+ elif (dist/divisor) >= 1000.0:
+ outdist = round(dist/divisor,1)
+ elif (dist/divisor) > 0.0:
+ outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
+ else:
+ outdist = float(dist/divisor)
+
+ return (outdist, outunits)
+
+ def OnZoomToRaster(self, event):
+ """!
+ Set display extents to match selected raster map (ignore NULLs)
+ """
+ self.MapWindow.ZoomToMap(ignoreNulls = True)
+
+ def OnZoomToSaved(self, event):
+ """!Set display geometry to match extents in
+ saved region file
+ """
+ self.MapWindow.ZoomToSaved()
+
+ def OnDisplayToWind(self, event):
+ """!Set computational region (WIND file) to match display
+ extents
+ """
+ self.MapWindow.DisplayToWind()
+
+ def SaveDisplayRegion(self, event):
+ """!Save display extents to named region file.
+ """
+ self.MapWindow.SaveDisplayRegion()
+
+ def OnZoomMenu(self, event):
+ """!Popup Zoom menu
+ """
+ point = wx.GetMousePosition()
+ zoommenu = wx.Menu()
+ # Add items to the menu
+
+ zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
+ zoommenu.AppendItem(zoomwind)
+ self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
+
+ zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
+ zoommenu.AppendItem(zoomdefault)
+ self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
+
+ zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+ zoommenu.AppendItem(zoomsaved)
+ self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
+
+ savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
+ zoommenu.AppendItem(savewind)
+ self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
+
+ savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
+ zoommenu.AppendItem(savezoom)
+ self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
+
+ # Popup the menu. If an item is selected then its handler
+ # will be called before PopupMenu returns.
+ self.PopupMenu(zoommenu)
+ zoommenu.Destroy()
+
+
+ def IsStandalone(self):
+ """!Check if Map display is standalone"""
+ if self._layerManager:
+ return False
+
+ return True
+
+ def GetLayerManager(self):
+ """!Get reference to Layer Manager
+
+ @return window reference
+ @return None (if standalone)
+ """
+ return self._layerManager
+
+ def GetSrcWindow(self):
+ return self.SrcMapWindow
+
+ def GetTgtWindow(self):
+ return self.TgtMapWindow
+
+ def GetShowTarget(self):
+ return self.show_target
+
+ def GetMapToolbar(self):
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['gcpdisp']
Added: grass/trunk/gui/wxpython/gcp/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/gcp/toolbars.py (rev 0)
+++ grass/trunk/gui/wxpython/gcp/toolbars.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,127 @@
+"""!
+ at package gcp.toolbars
+
+ at 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.
+
+ at 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))
+ )
Property changes on: grass/trunk/gui/wxpython/gcp/toolbars.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/gmodeler/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/gmodeler/dialogs.py (rev 0)
+++ grass/trunk/gui/wxpython/gmodeler/dialogs.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,628 @@
+"""!
+ at package gmodeler.dialogs
+
+ at 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.
+
+ at 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() }
Property changes on: grass/trunk/gui/wxpython/gmodeler/dialogs.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Copied: grass/trunk/gui/wxpython/gmodeler/frame.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/gmodeler.py)
===================================================================
--- grass/trunk/gui/wxpython/gmodeler/frame.py (rev 0)
+++ grass/trunk/gui/wxpython/gmodeler/frame.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,2656 @@
+"""!
+ at package gmodeler.py
+
+ at brief wxGUI Graphical Modeler for creating, editing, and managing models
+
+Classes:
+ - ModelerData
+ - ModelToolbar
+ - ModelFrame
+ - ModelCanvas
+ - ModelObject
+ - ModelAction
+ - ModelData
+ - ModelEvtHandler
+ - ModelRelation
+ - ModelListCtrl
+ - VariablePanel
+ - ValiableListCtrl
+ - ModelItem
+ - ModelLoop
+ - ItemPanel
+ - ItemListCtrl
+ - ItemCheckListCtrl
+ - ModelCondition
+
+(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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import time
+import stat
+import textwrap
+import tempfile
+import copy
+import re
+
+import wx
+import wx.lib.ogl as ogl
+import wx.lib.flatnotebook as FN
+import wx.lib.mixins.listctrl as listmix
+
+from core import globalvar
+from gui_core.widgets import GNotebook
+from gui_core.goutput import GMConsole
+from core.debug import Debug
+from core.gcmd import GMessage, GException, GWarning, GError, RunCommand
+from gui_core.dialogs import GetImageHandlers
+from gui_core.preferences import PreferencesBaseDialog
+from core.settings import UserSettings
+from core.menudata import MenuData
+from gui_core.toolbars import BaseToolbar
+
+from grass.script import core as grass
+
+class ModelerData(MenuData):
+ def __init__(self, filename = None):
+ if not filename:
+ gisbase = os.getenv('GISBASE')
+ global etcwxdir
+ filename = os.path.join(globalvar.ETCWXDIR, 'xml', 'menudata_modeler.xml')
+
+ MenuData.__init__(self, filename)
+
+class ModelToolbar(BaseToolbar):
+ """!Graphical modeler toolbaro (see gmodeler.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['modeler']
+ return self._getToolbarData((('new', icons['new'],
+ self.parent.OnModelNew),
+ ('open', icons['open'],
+ self.parent.OnModelOpen),
+ ('save', icons['save'],
+ self.parent.OnModelSave),
+ ('image', icons['toImage'],
+ self.parent.OnExportImage),
+ ('python', icons['toPython'],
+ self.parent.OnExportPython),
+ (None, ),
+ ('action', icons['actionAdd'],
+ self.parent.OnAddAction),
+ ('data', icons['dataAdd'],
+ self.parent.OnAddData),
+ ('relation', icons['relation'],
+ self.parent.OnDefineRelation),
+ ('loop', icons['loop'],
+ self.parent.OnDefineLoop),
+ (None, ),
+ ('redraw', icons['redraw'],
+ self.parent.OnCanvasRefresh),
+ ('validate', icons['validate'],
+ self.parent.OnValidateModel),
+ ('run', icons['run'],
+ self.parent.OnRunModel),
+ (None, ),
+ ("variables", icons['variables'],
+ self.parent.OnVariables),
+ ("settings", icons['settings'],
+ self.parent.OnPreferences),
+ ("help", Icons['misc']['help'],
+ self.parent.OnHelp),
+ (None, ),
+ ('quit', icons['quit'],
+ self.parent.OnCloseWindow))
+
+)
+
+class ModelFrame(wx.Frame):
+ def __init__(self, parent, id = wx.ID_ANY,
+ title = _("GRASS GIS Graphical Modeler"), **kwargs):
+ """!Graphical modeler main window
+
+ @param parent parent window
+ @param id window id
+ @param title window title
+
+ @param kwargs wx.Frames' arguments
+ """
+ self.parent = parent
+ self.searchDialog = None # module search dialog
+ self.baseTitle = title
+ self.modelFile = None # loaded model
+ self.modelChanged = False
+
+ self.cursors = {
+ "default" : wx.StockCursor(wx.CURSOR_ARROW),
+ "cross" : wx.StockCursor(wx.CURSOR_CROSS),
+ }
+
+ wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+ self.SetName("Modeler")
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+ self.menubar = Menu(parent = self, data = ModelerData())
+
+ self.SetMenuBar(self.menubar)
+
+ self.toolbar = ModelToolbar(parent = self)
+ self.SetToolBar(self.toolbar)
+
+ self.statusbar = self.CreateStatusBar(number = 1)
+
+ self.notebook = GNotebook(parent = self,
+ style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
+ FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON)
+
+ self.canvas = ModelCanvas(self)
+ self.canvas.SetBackgroundColour(wx.WHITE)
+ self.canvas.SetCursor(self.cursors["default"])
+
+ self.model = Model(self.canvas)
+
+ self.variablePanel = VariablePanel(parent = self)
+
+ self.itemPanel = ItemPanel(parent = self)
+
+ self.goutput = GMConsole(parent = self, notebook = self.notebook)
+
+ self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
+ self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')
+ self.notebook.AddPage(page = self.variablePanel, text=_('Variables'), name = 'variables')
+ self.notebook.AddPage(page = self.goutput, text=_('Command output'), name = 'output')
+ wx.CallAfter(self.notebook.SetSelectionByName, 'model')
+ wx.CallAfter(self.ModelChanged, False)
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+
+ self._layout()
+ self.SetMinSize((475, 300))
+ self.SetSize((640, 480))
+
+ # fix goutput's pane size
+ if self.goutput:
+ self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ sizer.Add(item = self.notebook, proportion = 1,
+ flag = wx.EXPAND)
+
+ self.SetAutoLayout(True)
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ self.Layout()
+
+ def _addEvent(self, item):
+ """!Add event to item"""
+ evthandler = ModelEvtHandler(self.statusbar,
+ self)
+ evthandler.SetShape(item)
+ evthandler.SetPreviousHandler(item.GetEventHandler())
+ item.SetEventHandler(evthandler)
+
+ def GetCanvas(self):
+ """!Get canvas"""
+ return self.canvas
+
+ def GetModel(self):
+ """!Get model"""
+ return self.model
+
+ def ModelChanged(self, changed = True):
+ """!Update window title"""
+ self.modelChanged = changed
+
+ if self.modelFile:
+ if self.modelChanged:
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile) + '*')
+ else:
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+ else:
+ self.SetTitle(self.baseTitle)
+
+ def OnVariables(self, event):
+ """!Switch to variables page"""
+ self.notebook.SetSelectionByName('variables')
+
+ def OnRemoveItem(self, event):
+ """!Remove shape
+ """
+ self.GetCanvas().RemoveSelected()
+
+ def OnCanvasRefresh(self, event):
+ """!Refresh canvas"""
+ self.SetStatusText(_("Redrawing model..."), 0)
+ self.GetCanvas().Refresh()
+ self.SetStatusText("", 0)
+
+ def OnCmdRun(self, event):
+ """!Run command"""
+ try:
+ action = self.GetModel().GetItems()[event.pid]
+ if hasattr(action, "task"):
+ action.Update(running = True)
+ except IndexError:
+ pass
+
+ def OnCmdPrepare(self, event):
+ """!Prepare for running command"""
+ event.onPrepare(item = event.userData['item'],
+ params = event.userData['params'])
+
+ def OnCmdDone(self, event):
+ """!Command done (or aborted)"""
+ try:
+ action = self.GetModel().GetItems()[event.pid]
+ if hasattr(action, "task"):
+ action.Update(running = True)
+ except IndexError:
+ pass
+
+ def OnCloseWindow(self, event):
+ """!Close window"""
+ if self.modelChanged and \
+ UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
+ if self.modelFile:
+ message = _("Do you want to save changes in the model?")
+ else:
+ message = _("Do you want to store current model settings "
+ "to model file?")
+
+ # ask user to save current settings
+ dlg = wx.MessageDialog(self,
+ message = message,
+ caption=_("Quit Graphical Modeler"),
+ style = wx.YES_NO | wx.YES_DEFAULT |
+ wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
+ ret = dlg.ShowModal()
+ if ret == wx.ID_YES:
+ if not self.modelFile:
+ self.OnWorkspaceSaveAs()
+ else:
+ self.WriteModelFile(self.modelFile)
+ elif ret == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ dlg.Destroy()
+
+ self.Destroy()
+
+ def OnSize(self, event):
+ """Window resized, save to the model"""
+ self.ModelChanged()
+ event.Skip()
+
+ def OnPreferences(self, event):
+ """!Open preferences dialog"""
+ dlg = PreferencesDialog(parent = self)
+ dlg.CenterOnParent()
+
+ dlg.ShowModal()
+ self.canvas.Refresh()
+
+ def OnHelp(self, event):
+ """!Show help"""
+ if self.parent and self.parent.GetName() == 'LayerManager':
+ log = self.parent.GetLogWindow()
+ log.RunCmd(['g.manual',
+ 'entry=wxGUI.Modeler'])
+ else:
+ RunCommand('g.manual',
+ quiet = True,
+ entry = 'wxGUI.Modeler')
+
+ def OnModelProperties(self, event):
+ """!Model properties dialog"""
+ dlg = PropertiesDialog(parent = self)
+ dlg.CentreOnParent()
+ properties = self.model.GetProperties()
+ dlg.Init(properties)
+ if dlg.ShowModal() == wx.ID_OK:
+ self.ModelChanged()
+ for key, value in dlg.GetValues().iteritems():
+ properties[key] = value
+ for action in self.model.GetItems(objType = ModelAction):
+ action.GetTask().set_flag('overwrite', properties['overwrite'])
+
+ dlg.Destroy()
+
+ def OnDeleteData(self, event):
+ """!Delete intermediate data"""
+ rast, vect, rast3d, msg = self.model.GetIntermediateData()
+
+ if not rast and not vect and not rast3d:
+ GMessage(parent = self,
+ message = _('Nothing to delete.'))
+ return
+
+ dlg = wx.MessageDialog(parent = self,
+ message= _("Do you want to permanently delete data?%s" % msg),
+ caption=_("Delete intermediate data?"),
+ style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+
+ ret = dlg.ShowModal()
+ if ret == wx.ID_YES:
+ dlg.Destroy()
+
+ if rast:
+ self.goutput.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
+ if rast3d:
+ self.goutput.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
+ if vect:
+ self.goutput.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
+
+ self.SetStatusText(_("%d maps deleted from current mapset") % \
+ int(len(rast) + len(rast3d) + len(vect)))
+ return
+
+ dlg.Destroy()
+
+ def OnModelNew(self, event):
+ """!Create new model"""
+ Debug.msg(4, "ModelFrame.OnModelNew():")
+
+ # ask user to save current model
+ if self.modelFile and self.modelChanged:
+ self.OnModelSave()
+ elif self.modelFile is None and \
+ (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0):
+ dlg = wx.MessageDialog(self, message=_("Current model is not empty. "
+ "Do you want to store current settings "
+ "to model file?"),
+ caption=_("Create new model?"),
+ style=wx.YES_NO | wx.YES_DEFAULT |
+ wx.CANCEL | wx.ICON_QUESTION)
+ ret = dlg.ShowModal()
+ if ret == wx.ID_YES:
+ self.OnModelSaveAs()
+ elif ret == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+
+ dlg.Destroy()
+
+ # delete all items
+ self.canvas.GetDiagram().DeleteAllShapes()
+ self.model.Reset()
+ self.canvas.Refresh()
+ self.itemPanel.Update()
+ self.variablePanel.Reset()
+
+ # no model file loaded
+ self.modelFile = None
+ self.modelChanged = False
+ self.SetTitle(self.baseTitle)
+
+ def OnModelOpen(self, event):
+ """!Load model from file"""
+ filename = ''
+ dlg = wx.FileDialog(parent = self, message=_("Choose model file"),
+ defaultDir = os.getcwd(),
+ wildcard=_("GRASS Model File (*.gxm)|*.gxm"))
+ if dlg.ShowModal() == wx.ID_OK:
+ filename = dlg.GetPath()
+
+ if not filename:
+ return
+
+ Debug.msg(4, "ModelFrame.OnModelOpen(): filename=%s" % filename)
+
+ # close current model
+ self.OnModelClose()
+
+ self.LoadModelFile(filename)
+
+ self.modelFile = filename
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+ self.SetStatusText(_('%(items)d items (%(actions)d actions) loaded into model') % \
+ { 'items' : self.model.GetNumItems(),
+ 'actions' : self.model.GetNumItems(actionOnly = True) }, 0)
+
+ def OnModelSave(self, event = None):
+ """!Save model to file"""
+ if self.modelFile and self.modelChanged:
+ dlg = wx.MessageDialog(self, message=_("Model file <%s> already exists. "
+ "Do you want to overwrite this file?") % \
+ self.modelFile,
+ caption=_("Save model"),
+ style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+ if dlg.ShowModal() == wx.ID_NO:
+ dlg.Destroy()
+ else:
+ Debug.msg(4, "ModelFrame.OnModelSave(): filename=%s" % self.modelFile)
+ self.WriteModelFile(self.modelFile)
+ self.SetStatusText(_('File <%s> saved') % self.modelFile, 0)
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+ elif not self.modelFile:
+ self.OnModelSaveAs(None)
+
+ def OnModelSaveAs(self, event):
+ """!Create model to file as"""
+ filename = ''
+ dlg = wx.FileDialog(parent = self,
+ message = _("Choose file to save current model"),
+ defaultDir = os.getcwd(),
+ wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
+ style=wx.FD_SAVE)
+
+
+ if dlg.ShowModal() == wx.ID_OK:
+ filename = dlg.GetPath()
+
+ if not filename:
+ return
+
+ # check for extension
+ if filename[-4:] != ".gxm":
+ filename += ".gxm"
+
+ if os.path.exists(filename):
+ dlg = wx.MessageDialog(parent = self,
+ message=_("Model file <%s> already exists. "
+ "Do you want to overwrite this file?") % filename,
+ caption=_("File already exists"),
+ style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+ if dlg.ShowModal() != wx.ID_YES:
+ dlg.Destroy()
+ return
+
+ Debug.msg(4, "GMFrame.OnModelSaveAs(): filename=%s" % filename)
+
+ self.WriteModelFile(filename)
+ self.modelFile = filename
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+ self.SetStatusText(_('File <%s> saved') % self.modelFile, 0)
+
+ def OnModelClose(self, event = None):
+ """!Close model file"""
+ Debug.msg(4, "ModelFrame.OnModelClose(): file=%s" % self.modelFile)
+ # ask user to save current model
+ if self.modelFile and self.modelChanged:
+ self.OnModelSave()
+ elif self.modelFile is None and \
+ (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0):
+ dlg = wx.MessageDialog(self, message=_("Current model is not empty. "
+ "Do you want to store current settings "
+ "to model file?"),
+ caption=_("Create new model?"),
+ style=wx.YES_NO | wx.YES_DEFAULT |
+ wx.CANCEL | wx.ICON_QUESTION)
+ ret = dlg.ShowModal()
+ if ret == wx.ID_YES:
+ self.OnModelSaveAs()
+ elif ret == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+
+ dlg.Destroy()
+
+ self.modelFile = None
+ self.SetTitle(self.baseTitle)
+
+ self.canvas.GetDiagram().DeleteAllShapes()
+ self.model.Reset()
+
+ self.canvas.Refresh()
+
+ def OnRunModel(self, event):
+ """!Run entire model"""
+ self.model.Run(self.goutput, self.OnDone, parent = self)
+
+ def OnDone(self, cmd, returncode):
+ """!Computation finished"""
+ self.SetStatusText('', 0)
+ # restore original files
+ if hasattr(self.model, "fileInput"):
+ for finput in self.model.fileInput:
+ data = self.model.fileInput[finput]
+ if not data:
+ continue
+
+ fd = open(finput, "w")
+ try:
+ fd.write(data)
+ finally:
+ fd.close()
+ del self.model.fileInput
+
+ def OnValidateModel(self, event, showMsg = True):
+ """!Validate entire model"""
+ if self.model.GetNumItems() < 1:
+ GMessage(parent = self,
+ message = _('Model is empty. Nothing to validate.'))
+ return
+
+
+ self.SetStatusText(_('Validating model...'), 0)
+ errList = self.model.Validate()
+ self.SetStatusText('', 0)
+
+ if errList:
+ GWarning(parent = self,
+ message = _('Model is not valid.\n\n%s') % '\n'.join(errList))
+ else:
+ GMessage(parent = self,
+ message = _('Model is valid.'))
+
+ def OnExportImage(self, event):
+ """!Export model to image (default image)
+ """
+ xminImg = 0
+ xmaxImg = 0
+ yminImg = 0
+ ymaxImg = 0
+ # get current size of canvas
+ for shape in self.canvas.GetDiagram().GetShapeList():
+ w, h = shape.GetBoundingBoxMax()
+ x = shape.GetX()
+ y = shape.GetY()
+ xmin = x - w / 2
+ xmax = x + w / 2
+ ymin = y - h / 2
+ ymax = y + h / 2
+ if xmin < xminImg:
+ xminImg = xmin
+ if xmax > xmaxImg:
+ xmaxImg = xmax
+ if ymin < yminImg:
+ yminImg = ymin
+ if ymax > ymaxImg:
+ ymaxImg = ymax
+ size = wx.Size(int(xmaxImg - xminImg) + 50,
+ int(ymaxImg - yminImg) + 50)
+ bitmap = wx.EmptyBitmap(width = size.width, height = size.height)
+
+ filetype, ltype = GetImageHandlers(wx.ImageFromBitmap(bitmap))
+
+ dlg = wx.FileDialog(parent = self,
+ message = _("Choose a file name to save the image (no need to add extension)"),
+ defaultDir = "",
+ defaultFile = "",
+ wildcard = filetype,
+ style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ if not path:
+ dlg.Destroy()
+ return
+
+ base, ext = os.path.splitext(path)
+ fileType = ltype[dlg.GetFilterIndex()]['type']
+ extType = ltype[dlg.GetFilterIndex()]['ext']
+ if ext != extType:
+ path = base + '.' + extType
+
+ dc = wx.MemoryDC(bitmap)
+ dc.SetBackground(wx.WHITE_BRUSH)
+ dc.SetBackgroundMode(wx.SOLID)
+
+ dc.BeginDrawing()
+ self.canvas.GetDiagram().Clear(dc)
+ self.canvas.GetDiagram().Redraw(dc)
+ dc.EndDrawing()
+
+ bitmap.SaveFile(path, fileType)
+ self.SetStatusText(_("Model exported to <%s>") % path)
+
+ dlg.Destroy()
+
+ def OnExportPython(self, event):
+ """!Export model to Python script"""
+ filename = ''
+ dlg = wx.FileDialog(parent = self,
+ message = _("Choose file to save"),
+ defaultDir = os.getcwd(),
+ wildcard=_("Python script (*.py)|*.py"),
+ style=wx.FD_SAVE)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ filename = dlg.GetPath()
+
+ if not filename:
+ return
+
+ # check for extension
+ if filename[-3:] != ".py":
+ filename += ".py"
+
+ if os.path.exists(filename):
+ dlg = wx.MessageDialog(self, message=_("File <%s> already exists. "
+ "Do you want to overwrite this file?") % filename,
+ caption=_("Save file"),
+ style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+ if dlg.ShowModal() == wx.ID_NO:
+ dlg.Destroy()
+ return
+
+ dlg.Destroy()
+
+ fd = open(filename, "w")
+ try:
+ WritePythonFile(fd, self.model)
+ finally:
+ fd.close()
+
+ # executable file
+ os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
+
+ self.SetStatusText(_("Model exported to <%s>") % filename)
+
+ def OnDefineRelation(self, event):
+ """!Define relation between data and action items"""
+ self.canvas.SetCursor(self.cursors["cross"])
+ self.defineRelation = { 'from' : None,
+ 'to' : None }
+
+ def OnDefineLoop(self, event):
+ """!Define new loop in the model"""
+ self.ModelChanged()
+
+ width, height = self.canvas.GetSize()
+ loop = ModelLoop(self, x = width/2, y = height/2,
+ id = self.model.GetNumItems() + 1)
+ self.canvas.diagram.AddShape(loop)
+ loop.Show(True)
+
+ self._addEvent(loop)
+ self.model.AddItem(loop)
+
+ self.canvas.Refresh()
+
+ def OnDefineCondition(self, event):
+ """!Define new condition in the model"""
+ self.ModelChanged()
+
+ width, height = self.canvas.GetSize()
+ cond = ModelCondition(self, x = width/2, y = height/2,
+ id = self.model.GetNumItems() + 1)
+ self.canvas.diagram.AddShape(cond)
+ cond.Show(True)
+
+ self._addEvent(cond)
+ self.model.AddItem(cond)
+
+ self.canvas.Refresh()
+
+ def OnAddAction(self, event):
+ """!Add action to model"""
+ if self.searchDialog is None:
+ self.searchDialog = ModelSearchDialog(self)
+ self.searchDialog.CentreOnParent()
+ else:
+ self.searchDialog.Reset()
+
+ if self.searchDialog.ShowModal() == wx.ID_CANCEL:
+ self.searchDialog.Hide()
+ return
+
+ cmd = self.searchDialog.GetCmd()
+ self.searchDialog.Hide()
+
+ self.ModelChanged()
+
+ # add action to canvas
+ width, height = self.canvas.GetSize()
+ action = ModelAction(self.model, cmd = cmd, x = width/2, y = height/2,
+ id = self.model.GetNextId())
+ overwrite = self.model.GetProperties().get('overwrite', None)
+ if overwrite is not None:
+ action.GetTask().set_flag('overwrite', overwrite)
+
+ self.canvas.diagram.AddShape(action)
+ action.Show(True)
+
+ self._addEvent(action)
+ self.model.AddItem(action)
+
+ self.itemPanel.Update()
+ self.canvas.Refresh()
+ time.sleep(.1)
+
+ # show properties dialog
+ win = action.GetPropDialog()
+ if not win:
+ if action.IsValid():
+ self.GetOptData(dcmd = action.GetLog(string = False), layer = action,
+ params = action.GetParams(), propwin = None)
+ else:
+ GUI(parent = self, show = True).ParseCommand(action.GetLog(string = False),
+ completed = (self.GetOptData, action, action.GetParams()))
+ elif win and not win.IsShown():
+ win.Show()
+
+ if win:
+ win.Raise()
+
+ def OnAddData(self, event):
+ """!Add data item to model
+ """
+ # add action to canvas
+ width, height = self.canvas.GetSize()
+ data = ModelData(self, x = width/2, y = height/2)
+
+ dlg = ModelDataDialog(parent = self, shape = data)
+ data.SetPropDialog(dlg)
+ dlg.CentreOnParent()
+ ret = dlg.ShowModal()
+ dlg.Destroy()
+ if ret != wx.ID_OK:
+ return
+
+ data.Update()
+ self.canvas.diagram.AddShape(data)
+ data.Show(True)
+
+ self.ModelChanged()
+
+ self._addEvent(data)
+ self.model.AddItem(data)
+
+ self.canvas.Refresh()
+
+
+ def OnHelp(self, event):
+ """!Display manual page"""
+ grass.run_command('g.manual',
+ entry = 'wxGUI.Modeler')
+
+ def OnAbout(self, event):
+ """!Display About window"""
+ info = wx.AboutDialogInfo()
+
+ info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+ info.SetName(_('wxGUI Graphical Modeler'))
+ info.SetWebSite('http://grass.osgeo.org')
+ year = grass.version()['date']
+ info.SetDescription(_('(C) 2010-%s by the GRASS Development Team\n\n') % year +
+ '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
+ '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
+
+ wx.AboutBox(info)
+
+ def GetOptData(self, dcmd, layer, params, propwin):
+ """!Process action data"""
+ if params: # add data items
+ width, height = self.canvas.GetSize()
+ x = [width/2 + 200, width/2 - 200]
+ for p in params['params']:
+ if p.get('prompt', '') in ('raster', 'vector', 'raster3d') and \
+ (p.get('value', None) or \
+ (p.get('age', 'old') != 'old' and p.get('required', 'no') == 'yes')):
+ data = layer.FindData(p.get('name', ''))
+ if data:
+ data.SetValue(p.get('value', ''))
+ data.Update()
+ continue
+
+ data = self.model.FindData(p.get('value', ''),
+ p.get('prompt', ''))
+ if data:
+ if p.get('age', 'old') == 'old':
+ rel = ModelRelation(parent = self, fromShape = data,
+ toShape = layer, param = p.get('name', ''))
+ else:
+ rel = ModelRelation(parent = self, fromShape = layer,
+ toShape = data, param = p.get('name', ''))
+ layer.AddRelation(rel)
+ data.AddRelation(rel)
+ self.AddLine(rel)
+ data.Update()
+ continue
+
+ data = ModelData(self, value = p.get('value', ''),
+ prompt = p.get('prompt', ''),
+ x = x.pop(), y = height/2)
+ self._addEvent(data)
+ self.canvas.diagram.AddShape(data)
+ data.Show(True)
+
+ if p.get('age', 'old') == 'old':
+ rel = ModelRelation(parent = self, fromShape = data,
+ toShape = layer, param = p.get('name', ''))
+ else:
+ rel = ModelRelation(parent = self, fromShape = layer,
+ toShape = data, param = p.get('name', ''))
+ layer.AddRelation(rel)
+ data.AddRelation(rel)
+ self.AddLine(rel)
+ data.Update()
+
+ # valid / parameterized ?
+ layer.SetValid(params)
+
+ self.canvas.Refresh()
+
+ if dcmd:
+ layer.SetProperties(params, propwin)
+
+ self.SetStatusText(layer.GetLog(), 0)
+
+ def AddLine(self, rel):
+ """!Add connection between model objects
+
+ @param rel relation
+ """
+ fromShape = rel.GetFrom()
+ toShape = rel.GetTo()
+
+ rel.SetCanvas(self)
+ rel.SetPen(wx.BLACK_PEN)
+ rel.SetBrush(wx.BLACK_BRUSH)
+ rel.AddArrow(ogl.ARROW_ARROW)
+ points = rel.GetControlPoints()
+ rel.MakeLineControlPoints(2)
+ if points:
+ for x, y in points:
+ rel.InsertLineControlPoint(point = wx.RealPoint(x, y))
+
+ self._addEvent(rel)
+ try:
+ fromShape.AddLine(rel, toShape)
+ except TypeError:
+ pass # bug when connecting ModelCondition and ModelLoop - to be fixed
+
+ self.canvas.diagram.AddShape(rel)
+ rel.Show(True)
+
+ def LoadModelFile(self, filename):
+ """!Load model definition stored in GRASS Model XML file (gxm)
+ """
+ try:
+ self.model.LoadModel(filename)
+ except GException, e:
+ GError(parent = self,
+ message = _("Reading model file <%s> failed.\n"
+ "Invalid file, unable to parse XML document.") % filename)
+
+ self.modelFile = filename
+ self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+
+ self.SetStatusText(_("Please wait, loading model..."), 0)
+
+ # load actions
+ for item in self.model.GetItems(objType = ModelAction):
+ self._addEvent(item)
+ self.canvas.diagram.AddShape(item)
+ item.Show(True)
+ # relations/data
+ for rel in item.GetRelations():
+ if rel.GetFrom() == item:
+ dataItem = rel.GetTo()
+ else:
+ dataItem = rel.GetFrom()
+ self._addEvent(dataItem)
+ self.canvas.diagram.AddShape(dataItem)
+ self.AddLine(rel)
+ dataItem.Show(True)
+
+ # load loops
+ for item in self.model.GetItems(objType = ModelLoop):
+ self._addEvent(item)
+ self.canvas.diagram.AddShape(item)
+ item.Show(True)
+
+ # connect items in the loop
+ self.DefineLoop(item)
+
+ # load conditions
+ for item in self.model.GetItems(objType = ModelCondition):
+ self._addEvent(item)
+ self.canvas.diagram.AddShape(item)
+ item.Show(True)
+
+ # connect items in the condition
+ self.DefineCondition(item)
+
+ # load variables
+ self.variablePanel.Update()
+ self.itemPanel.Update()
+ self.SetStatusText('', 0)
+
+ # final updates
+ for action in self.model.GetItems(objType = ModelAction):
+ action.SetValid(action.GetParams())
+ action.Update()
+
+ self.canvas.Refresh(True)
+
+ def WriteModelFile(self, filename):
+ """!Save model to model file, recover original file on error.
+
+ @return True on success
+ @return False on failure
+ """
+ self.ModelChanged(False)
+ tmpfile = tempfile.TemporaryFile(mode='w+b')
+ try:
+ WriteModelFile(fd = tmpfile, model = self.model)
+ except StandardError:
+ GError(parent = self,
+ message = _("Writing current settings to model file failed."))
+ return False
+
+ try:
+ mfile = open(filename, "w")
+ tmpfile.seek(0)
+ for line in tmpfile.readlines():
+ mfile.write(line)
+ except IOError:
+ wx.MessageBox(parent = self,
+ message = _("Unable to open file <%s> for writing.") % filename,
+ caption = _("Error"),
+ style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ return False
+
+ mfile.close()
+
+ return True
+
+ def DefineLoop(self, loop):
+ """!Define loop with given list of items"""
+ parent = loop
+ items = loop.GetItems()
+ if not items:
+ return
+
+ # remove defined relations first
+ for rel in loop.GetRelations():
+ self.canvas.GetDiagram().RemoveShape(rel)
+ loop.Clear()
+
+ for item in items:
+ rel = ModelRelation(parent = self, fromShape = parent, toShape = item)
+ dx = item.GetX() - parent.GetX()
+ dy = item.GetY() - parent.GetY()
+ loop.AddRelation(rel)
+ if dx != 0:
+ rel.SetControlPoints(((parent.GetX(), parent.GetY() + dy / 2),
+ (parent.GetX() + dx, parent.GetY() + dy / 2)))
+ self.AddLine(rel)
+ parent = item
+
+ # close loop
+ item = loop.GetItems()[-1]
+ rel = ModelRelation(parent = self, fromShape = item, toShape = loop)
+ loop.AddRelation(rel)
+ self.AddLine(rel)
+ dx = (item.GetX() - loop.GetX()) + loop.GetWidth() / 2 + 50
+ dy = item.GetHeight() / 2 + 50
+ rel.MakeLineControlPoints(0)
+ rel.InsertLineControlPoint(point = wx.RealPoint(loop.GetX() - loop.GetWidth() / 2 ,
+ loop.GetY()))
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
+ item.GetY() + item.GetHeight() / 2))
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
+ item.GetY() + dy))
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
+ item.GetY() + dy))
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
+ loop.GetY()))
+
+ self.canvas.Refresh()
+
+ def DefineCondition(self, condition):
+ """!Define if-else statement with given list of items"""
+ parent = condition
+ items = condition.GetItems()
+ if not items['if'] and not items['else']:
+ return
+
+ # remove defined relations first
+ for rel in condition.GetRelations():
+ self.canvas.GetDiagram().RemoveShape(rel)
+ condition.Clear()
+ dxIf = condition.GetX() + condition.GetWidth() / 2
+ dxElse = condition.GetX() - condition.GetWidth() / 2
+ dy = condition.GetY()
+ for branch in items.keys():
+ for item in items[branch]:
+ rel = ModelRelation(parent = self, fromShape = parent,
+ toShape = item)
+ condition.AddRelation(rel)
+ self.AddLine(rel)
+ rel.MakeLineControlPoints(0)
+ if branch == 'if':
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
+ rel.InsertLineControlPoint(point = wx.RealPoint(dxIf, dy))
+ else:
+ rel.InsertLineControlPoint(point = wx.RealPoint(dxElse, dy))
+ rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
+ parent = item
+
+ self.canvas.Refresh()
+
+class ModelCanvas(ogl.ShapeCanvas):
+ """!Canvas where model is drawn"""
+ def __init__(self, parent):
+ self.parent = parent
+ ogl.OGLInitialize()
+ ogl.ShapeCanvas.__init__(self, parent)
+
+ self.diagram = ogl.Diagram()
+ self.SetDiagram(self.diagram)
+ self.diagram.SetCanvas(self)
+
+ self.SetScrollbars(20, 20, 1000/20, 1000/20)
+
+ self.Bind(wx.EVT_CHAR, self.OnChar)
+
+ def OnChar(self, event):
+ """!Key pressed"""
+ kc = event.GetKeyCode()
+ diagram = self.GetDiagram()
+ if kc == wx.WXK_DELETE:
+ self.RemoveSelected()
+
+ def RemoveSelected(self):
+ """!Remove selected shapes"""
+ self.parent.ModelChanged()
+
+ diagram = self.GetDiagram()
+ for shape in diagram.GetShapeList():
+ if not shape.Selected():
+ continue
+ remList, upList = self.parent.GetModel().RemoveItem(shape)
+ shape.Select(False)
+ diagram.RemoveShape(shape)
+ shape.__del__()
+ for item in remList:
+ diagram.RemoveShape(item)
+ item.__del__()
+
+ for item in upList:
+ item.Update()
+
+ self.Refresh()
+
+class ModelObject:
+ def __init__(self, id = -1):
+ self.id = id
+ self.rels = list() # list of ModelRelations
+
+ self.isEnabled = True
+ self.inBlock = list() # list of related loops/conditions
+
+ def __del__(self):
+ pass
+
+ def GetId(self):
+ """!Get id"""
+ return self.id
+
+ def AddRelation(self, rel):
+ """!Record new relation
+ """
+ self.rels.append(rel)
+
+ def GetRelations(self, fdir = None):
+ """!Get list of relations
+
+ @param fdir True for 'from'
+ """
+ if fdir is None:
+ return self.rels
+
+ result = list()
+ for rel in self.rels:
+ if fdir == 'from':
+ if rel.GetFrom() == self:
+ result.append(rel)
+ else:
+ if rel.GetTo() == self:
+ result.append(rel)
+
+ return result
+
+ def IsEnabled(self):
+ """!Get True if action is enabled, otherwise False"""
+ return self.isEnabled
+
+ def Enable(self, enabled = True):
+ """!Enable/disable action"""
+ self.isEnabled = enabled
+ self.Update()
+
+ def Update(self):
+ pass
+
+ def SetBlock(self, item):
+ """!Add object to the block (loop/condition)
+
+ @param item reference to ModelLoop or ModelCondition which
+ defines loops/condition
+ """
+ if item not in self.inBlock:
+ self.inBlock.append(item)
+
+ def UnSetBlock(self, item):
+ """!Remove object from the block (loop/consition)
+
+ @param item reference to ModelLoop or ModelCondition which
+ defines loops/codition
+ """
+ if item in self.inBlock:
+ self.inBlock.remove(item)
+
+ def GetBlock(self):
+ """!Get list of related ModelObject(s) which defines block
+ (loop/condition)
+
+ @return list of ModelObjects
+ """
+ return self.inBlock
+
+ def GetBlockId(self):
+ """!Get list of related ids which defines block
+
+ @return list of ids
+ """
+ ret = list()
+ for mo in self.inBlock:
+ ret.append(mo.GetId())
+
+ return ret
+
+class ModelAction(ModelObject, ogl.RectangleShape):
+ """!Action class (GRASS module)"""
+ def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None):
+ ModelObject.__init__(self, id)
+
+ self.parent = parent
+ self.task = task
+
+ if not width:
+ width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width'))
+ if not height:
+ height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height'))
+
+ if cmd:
+ self.task = GUI(show = None).ParseCommand(cmd = cmd)
+ else:
+ if task:
+ self.task = task
+ else:
+ self.task = None
+
+ self.propWin = None
+
+ self.data = list() # list of connected data items
+
+ self.isValid = False
+ self.isParameterized = False
+
+ if self.parent.GetCanvas():
+ ogl.RectangleShape.__init__(self, width, height)
+
+ self.SetCanvas(self.parent)
+ self.SetX(x)
+ self.SetY(y)
+ self.SetPen(wx.BLACK_PEN)
+ self._setPen()
+ self._setBrush()
+ self.SetId(id)
+
+ if self.task:
+ self.SetValid(self.task.get_options())
+
+ def _setBrush(self, running = False):
+ """!Set brush"""
+ if running:
+ color = UserSettings.Get(group='modeler', key='action',
+ subkey=('color', 'running'))
+ elif not self.isEnabled:
+ color = UserSettings.Get(group='modeler', key='disabled',
+ subkey='color')
+ elif self.isValid:
+ color = UserSettings.Get(group='modeler', key='action',
+ subkey=('color', 'valid'))
+ else:
+ color = UserSettings.Get(group='modeler', key='action',
+ subkey=('color', 'invalid'))
+
+ wxColor = wx.Color(color[0], color[1], color[2])
+ self.SetBrush(wx.Brush(wxColor))
+
+ def _setPen(self):
+ """!Set pen"""
+ if self.isParameterized:
+ width = int(UserSettings.Get(group='modeler', key='action',
+ subkey=('width', 'parameterized')))
+ else:
+ width = int(UserSettings.Get(group='modeler', key='action',
+ subkey=('width', 'default')))
+ pen = self.GetPen()
+ pen.SetWidth(width)
+ self.SetPen(pen)
+
+ def SetId(self, id):
+ """!Set id"""
+ self.id = id
+ cmd = self.task.get_cmd(ignoreErrors = True)
+ if cmd and len(cmd) > 0:
+ self.ClearText()
+ self.AddText('(%d) %s' % (self.id, cmd[0]))
+ else:
+ self.AddText('(%d) <<%s>>' % (self.id, _("unknown")))
+
+ def SetProperties(self, params, propwin):
+ """!Record properties dialog"""
+ self.task.params = params['params']
+ self.task.flags = params['flags']
+ self.propWin = propwin
+
+ def GetPropDialog(self):
+ """!Get properties dialog"""
+ return self.propWin
+
+ def GetLog(self, string = True, substitute = None):
+ """!Get logging info
+
+ @param string True to get cmd as a string otherwise a list
+ @param substitute dictionary of parameter to substitute or None
+ """
+ cmd = self.task.get_cmd(ignoreErrors = True, ignoreRequired = True,
+ ignoreDefault = False)
+
+ # substitute variables
+ if substitute:
+ variables = []
+ if 'variables' in substitute:
+ for p in substitute['variables']['params']:
+ variables.append(p.get('name', ''))
+ else:
+ variables = self.parent.GetVariables()
+ for variable in variables:
+ pattern= re.compile('%' + variable)
+ value = ''
+ if substitute and 'variables' in substitute:
+ for p in substitute['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', '')
+
+ if not value:
+ continue
+
+ for idx in range(len(cmd)):
+ if pattern.search(cmd[idx]):
+ cmd[idx] = pattern.sub(value, cmd[idx])
+ break
+ idx += 1
+
+ if string:
+ if cmd is None:
+ return ''
+ else:
+ return ' '.join(cmd)
+
+ return cmd
+
+ def GetName(self):
+ """!Get name"""
+ cmd = self.task.get_cmd(ignoreErrors = True)
+ if cmd and len(cmd) > 0:
+ return cmd[0]
+
+ return _('unknown')
+
+ def GetParams(self, dcopy = False):
+ """!Get dictionary of parameters"""
+ if dcopy:
+ return copy.deepcopy(self.task.get_options())
+
+ return self.task.get_options()
+
+ def GetTask(self):
+ """!Get grassTask instance"""
+ return self.task
+
+ def SetParams(self, params):
+ """!Set dictionary of parameters"""
+ self.task.params = params['params']
+ self.task.flags = params['flags']
+
+ def MergeParams(self, params):
+ """!Merge dictionary of parameters"""
+ if 'flags' in params:
+ for f in params['flags']:
+ self.task.set_flag(f['name'],
+ f.get('value', False))
+ if 'params' in params:
+ for p in params['params']:
+ self.task.set_param(p['name'],
+ p.get('value', ''))
+
+ def SetValid(self, options):
+ """!Set validity for action
+
+ @param options dictionary with flags and params (gtask)
+ """
+ self.isValid = True
+ self.isParameterized = False
+
+ for f in options['flags']:
+ if f.get('parameterized', False):
+ self.IsParameterized = True
+ break
+
+ for p in options['params']:
+ if self.isValid and p.get('required', False) and \
+ p.get('value', '') == '' and \
+ p.get('default', '') == '':
+ self.isValid = False
+ if not self.isParameterized and p.get('parameterized', False):
+ self.isParameterized = True
+
+ if self.parent.GetCanvas():
+ self._setBrush()
+ self._setPen()
+
+ def IsValid(self):
+ """!Check validity (all required parameters set)"""
+ return self.isValid
+
+ def IsParameterized(self):
+ """!Check if action is parameterized"""
+ return self.isParameterized
+
+ def FindData(self, name):
+ """!Find data item by name"""
+ for rel in self.GetRelations():
+ data = rel.GetData()
+ if name == rel.GetName() and name in data.GetName():
+ return data
+
+ return None
+
+ def Update(self, running = False):
+ """!Update action"""
+ if running:
+ self._setBrush(running = True)
+ else:
+ self._setBrush()
+ self._setPen()
+
+ def OnDraw(self, dc):
+ """!Draw action in canvas"""
+ self._setBrush()
+ self._setPen()
+ ogl.RectangleShape.OnDraw(self, dc)
+
+class ModelData(ModelObject, ogl.EllipseShape):
+ def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None):
+ """Data item class
+
+ @param parent window parent
+ @param x, y position of the shape
+ @param fname, tname list of parameter names from / to
+ @param value value
+ @param prompt type of GIS element
+ @param width,height dimension of the shape
+ """
+ ModelObject.__init__(self)
+
+ self.parent = parent
+ self.value = value
+ self.prompt = prompt
+ self.intermediate = False
+ self.propWin = None
+ if not width:
+ width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width'))
+ if not height:
+ height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height'))
+
+ if self.parent.GetCanvas():
+ ogl.EllipseShape.__init__(self, width, height)
+
+ self.SetCanvas(self.parent)
+ self.SetX(x)
+ self.SetY(y)
+ self.SetPen(wx.BLACK_PEN)
+ self._setBrush()
+
+ self._setText()
+
+ def IsIntermediate(self):
+ """!Checks if data item is intermediate"""
+ return self.intermediate
+
+ def SetIntermediate(self, im):
+ """!Set intermediate flag"""
+ self.intermediate = im
+
+ def OnDraw(self, dc):
+ pen = self.GetPen()
+ pen.SetWidth(1)
+ if self.intermediate:
+ pen.SetStyle(wx.SHORT_DASH)
+ else:
+ pen.SetStyle(wx.SOLID)
+ self.SetPen(pen)
+
+ ogl.EllipseShape.OnDraw(self, dc)
+
+ def GetLog(self, string = True):
+ """!Get logging info"""
+ name = list()
+ for rel in self.GetRelations():
+ name.append(rel.GetName())
+ if name:
+ return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')'
+ else:
+ return self.value + ' (' + self.prompt + ')'
+
+ def GetName(self):
+ """!Get list of names"""
+ name = list()
+ for rel in self.GetRelations():
+ name.append(rel.GetName())
+
+ return name
+
+ def GetPrompt(self):
+ """!Get prompt"""
+ return self.prompt
+
+ def SetPrompt(self, prompt):
+ """!Set prompt
+
+ @param prompt
+ """
+ self.prompt = prompt
+
+ def GetValue(self):
+ """!Get value"""
+ return self.value
+
+ def SetValue(self, value):
+ """!Set value
+
+ @param value
+ """
+ self.value = value
+ self._setText()
+ for direction in ('from', 'to'):
+ for rel in self.GetRelations(direction):
+ if direction == 'from':
+ action = rel.GetTo()
+ else:
+ action = rel.GetFrom()
+
+ task = GUI(show = None).ParseCommand(cmd = action.GetLog(string = False))
+ task.set_param(rel.GetName(), self.value)
+ action.SetParams(params = task.get_options())
+
+ def GetPropDialog(self):
+ """!Get properties dialog"""
+ return self.propWin
+
+ def SetPropDialog(self, win):
+ """!Get properties dialog"""
+ self.propWin = win
+
+ def _setBrush(self):
+ """!Set brush"""
+ if self.prompt == 'raster':
+ color = UserSettings.Get(group = 'modeler', key = 'data',
+ subkey = ('color', 'raster'))
+ elif self.prompt == 'raster3d':
+ color = UserSettings.Get(group = 'modeler', key = 'data',
+ subkey = ('color', 'raster3d'))
+ elif self.prompt == 'vector':
+ color = UserSettings.Get(group = 'modeler', key = 'data',
+ subkey = ('color', 'vector'))
+ else:
+ color = UserSettings.Get(group = 'modeler', key = 'action',
+ subkey = ('color', 'invalid'))
+ wxColor = wx.Color(color[0], color[1], color[2])
+ self.SetBrush(wx.Brush(wxColor))
+
+ def _setPen(self):
+ """!Set pen"""
+ isParameterized = False
+ for rel in self.GetRelations('from'):
+ if rel.GetTo().IsParameterized():
+ isParameterized = True
+ break
+ if not isParameterized:
+ for rel in self.GetRelations('to'):
+ if rel.GetFrom().IsParameterized():
+ isParameterized = True
+ break
+
+ if isParameterized:
+ width = int(UserSettings.Get(group = 'modeler', key = 'action',
+ subkey = ('width', 'parameterized')))
+ else:
+ width = int(UserSettings.Get(group = 'modeler', key = 'action',
+ subkey = ('width', 'default')))
+ pen = self.GetPen()
+ pen.SetWidth(width)
+ self.SetPen(pen)
+
+ def _setText(self):
+ """!Update text"""
+ self.ClearText()
+ name = []
+ for rel in self.GetRelations():
+ name.append(rel.GetName())
+ self.AddText('/'.join(name))
+ if self.value:
+ self.AddText(self.value)
+ else:
+ self.AddText(_('<not defined>'))
+
+ def Update(self):
+ """!Update action"""
+ self._setBrush()
+ self._setPen()
+ self._setText()
+
+class ModelEvtHandler(ogl.ShapeEvtHandler):
+ """!Model event handler class"""
+ def __init__(self, log, frame):
+ ogl.ShapeEvtHandler.__init__(self)
+ self.log = log
+ self.frame = frame
+ self.x = self.y = None
+
+ def OnLeftClick(self, x, y, keys = 0, attachment = 0):
+ """!Left mouse button pressed -> select item & update statusbar"""
+ shape = self.GetShape()
+ canvas = shape.GetCanvas()
+ dc = wx.ClientDC(canvas)
+ canvas.PrepareDC(dc)
+
+ if hasattr(self.frame, 'defineRelation'):
+ drel = self.frame.defineRelation
+ if drel['from'] is None:
+ drel['from'] = shape
+ elif drel['to'] is None:
+ drel['to'] = shape
+ rel = ModelRelation(parent = self.frame, fromShape = drel['from'],
+ toShape = drel['to'])
+ dlg = ModelRelationDialog(parent = self.frame,
+ shape = rel)
+ if dlg.IsValid():
+ ret = dlg.ShowModal()
+ if ret == wx.ID_OK:
+ option = dlg.GetOption()
+ rel.SetName(option)
+ drel['from'].AddRelation(rel)
+ drel['to'].AddRelation(rel)
+ drel['from'].Update()
+ params = { 'params' : [{ 'name' : option,
+ 'value' : drel['from'].GetValue()}] }
+ drel['to'].MergeParams(params)
+ self.frame.AddLine(rel)
+
+ dlg.Destroy()
+ del self.frame.defineRelation
+
+ if shape.Selected():
+ shape.Select(False, dc)
+ else:
+ redraw = False
+ shapeList = canvas.GetDiagram().GetShapeList()
+ toUnselect = list()
+
+ for s in shapeList:
+ if s.Selected():
+ toUnselect.append(s)
+
+ shape.Select(True, dc)
+
+ for s in toUnselect:
+ s.Select(False, dc)
+
+ canvas.Refresh(False)
+
+ if hasattr(shape, "GetLog"):
+ self.log.SetStatusText(shape.GetLog(), 0)
+ else:
+ self.log.SetStatusText('', 0)
+
+ def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
+ """!Left mouse button pressed (double-click) -> show properties"""
+ self.OnProperties()
+
+ def OnProperties(self, event = None):
+ """!Show properties dialog"""
+ self.frame.ModelChanged()
+ shape = self.GetShape()
+ if isinstance(shape, ModelAction):
+ module = GUI(parent = self.frame, show = True).ParseCommand(shape.GetLog(string = False),
+ completed = (self.frame.GetOptData, shape, shape.GetParams()))
+
+ elif isinstance(shape, ModelData):
+ dlg = ModelDataDialog(parent = self.frame, shape = shape)
+ shape.SetPropDialog(dlg)
+ dlg.CentreOnParent()
+ dlg.Show()
+
+ elif isinstance(shape, ModelLoop):
+ dlg = ModelLoopDialog(parent = self.frame, shape = shape)
+ dlg.CentreOnParent()
+ if dlg.ShowModal() == wx.ID_OK:
+ shape.SetText(dlg.GetCondition())
+ alist = list()
+ ids = dlg.GetItems()
+ for aId in ids['unchecked']:
+ action = self.frame.GetModel().GetItem(aId)
+ action.UnSetBlock(shape)
+ for aId in ids['checked']:
+ action = self.frame.GetModel().GetItem(aId)
+ action.SetBlock(shape)
+ if action:
+ alist.append(action)
+ shape.SetItems(alist)
+ self.frame.DefineLoop(shape)
+ self.frame.SetStatusText(shape.GetLog(), 0)
+ self.frame.GetCanvas().Refresh()
+
+ dlg.Destroy()
+
+ elif isinstance(shape, ModelCondition):
+ dlg = ModelConditionDialog(parent = self.frame, shape = shape)
+ dlg.CentreOnParent()
+ if dlg.ShowModal() == wx.ID_OK:
+ shape.SetText(dlg.GetCondition())
+ ids = dlg.GetItems()
+ for b in ids.keys():
+ alist = list()
+ for aId in ids[b]['unchecked']:
+ action = self.frame.GetModel().GetItem(aId)
+ action.UnSetBlock(shape)
+ for aId in ids[b]['checked']:
+ action = self.frame.GetModel().GetItem(aId)
+ action.SetBlock(shape)
+ if action:
+ alist.append(action)
+ shape.SetItems(alist, branch = b)
+ self.frame.DefineCondition(shape)
+ self.frame.GetCanvas().Refresh()
+
+ dlg.Destroy()
+
+ def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+ """!Drag shape (begining)"""
+ self.frame.ModelChanged()
+ if self._previousHandler:
+ self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
+
+ def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+ """!Drag shape (end)"""
+ if self._previousHandler:
+ self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
+
+ shape = self.GetShape()
+ if isinstance(shape, ModelLoop):
+ self.frame.DefineLoop(shape)
+ elif isinstance(shape, ModelCondition):
+ self.frame.DefineCondition(shape)
+
+ for mo in shape.GetBlock():
+ if isinstance(mo, ModelLoop):
+ self.frame.DefineLoop(mo)
+ elif isinstance(mo, ModelCondition):
+ self.frame.DefineCondition(mo)
+
+ def OnEndSize(self, x, y):
+ """!Resize shape"""
+ self.frame.ModelChanged()
+ if self._previousHandler:
+ self._previousHandler.OnEndSize(x, y)
+
+ def OnRightClick(self, x, y, keys = 0, attachment = 0):
+ """!Right click -> pop-up menu"""
+ if not hasattr (self, "popupID"):
+ self.popupID = dict()
+ for key in ('remove', 'enable', 'addPoint',
+ 'delPoint', 'intermediate', 'props', 'id'):
+ self.popupID[key] = wx.NewId()
+
+ # record coordinates
+ self.x = x
+ self.y = y
+
+ shape = self.GetShape()
+ popupMenu = wx.Menu()
+ popupMenu.Append(self.popupID['remove'], text=_('Remove'))
+ self.frame.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID['remove'])
+ if isinstance(shape, ModelAction) or isinstance(shape, ModelLoop):
+ if shape.IsEnabled():
+ popupMenu.Append(self.popupID['enable'], text=_('Disable'))
+ self.frame.Bind(wx.EVT_MENU, self.OnDisable, id = self.popupID['enable'])
+ else:
+ popupMenu.Append(self.popupID['enable'], text=_('Enable'))
+ self.frame.Bind(wx.EVT_MENU, self.OnEnable, id = self.popupID['enable'])
+
+ if isinstance(shape, ModelRelation):
+ popupMenu.AppendSeparator()
+ popupMenu.Append(self.popupID['addPoint'], text=_('Add control point'))
+ self.frame.Bind(wx.EVT_MENU, self.OnAddPoint, id = self.popupID['addPoint'])
+ popupMenu.Append(self.popupID['delPoint'], text=_('Remove control point'))
+ self.frame.Bind(wx.EVT_MENU, self.OnRemovePoint, id = self.popupID['delPoint'])
+ if len(shape.GetLineControlPoints()) == 2:
+ popupMenu.Enable(self.popupID['delPoint'], False)
+
+ if isinstance(shape, ModelData) and '@' not in shape.GetValue():
+ popupMenu.AppendSeparator()
+ popupMenu.Append(self.popupID['intermediate'], text=_('Intermediate'),
+ kind = wx.ITEM_CHECK)
+ if self.GetShape().IsIntermediate():
+ popupMenu.Check(self.popupID['intermediate'], True)
+
+ self.frame.Bind(wx.EVT_MENU, self.OnIntermediate, id = self.popupID['intermediate'])
+
+ if isinstance(shape, ModelData) or \
+ isinstance(shape, ModelAction) or \
+ isinstance(shape, ModelLoop):
+ popupMenu.AppendSeparator()
+ popupMenu.Append(self.popupID['props'], text=_('Properties'))
+ self.frame.Bind(wx.EVT_MENU, self.OnProperties, id = self.popupID['props'])
+
+ self.frame.PopupMenu(popupMenu)
+ popupMenu.Destroy()
+
+ def OnDisable(self, event):
+ """!Disable action"""
+ self._onEnable(False)
+
+ def OnEnable(self, event):
+ """!Disable action"""
+ self._onEnable(True)
+
+ def _onEnable(self, enable):
+ shape = self.GetShape()
+ shape.Enable(enable)
+ self.frame.ModelChanged()
+ self.frame.canvas.Refresh()
+
+ def OnAddPoint(self, event):
+ """!Add control point"""
+ shape = self.GetShape()
+ shape.InsertLineControlPoint(point = wx.RealPoint(self.x, self.y))
+ shape.ResetShapes()
+ shape.Select(True)
+ self.frame.ModelChanged()
+ self.frame.canvas.Refresh()
+
+ def OnRemovePoint(self, event):
+ """!Remove control point"""
+ shape = self.GetShape()
+ shape.DeleteLineControlPoint()
+ shape.Select(False)
+ shape.Select(True)
+ self.frame.ModelChanged()
+ self.frame.canvas.Refresh()
+
+ def OnIntermediate(self, event):
+ """!Mark data as intermediate"""
+ self.frame.ModelChanged()
+ shape = self.GetShape()
+ shape.SetIntermediate(event.IsChecked())
+ self.frame.canvas.Refresh()
+
+ def OnRemove(self, event):
+ """!Remove shape
+ """
+ self.frame.GetCanvas().RemoveSelected()
+ self.frame.itemPanel.Update()
+
+class ModelRelation(ogl.LineShape):
+ """!Data - action relation"""
+ def __init__(self, parent, fromShape, toShape, param = ''):
+ self.fromShape = fromShape
+ self.toShape = toShape
+ self.param = param
+ self.parent = parent
+
+ self._points = None
+
+ if self.parent.GetCanvas():
+ ogl.LineShape.__init__(self)
+
+ def __del__(self):
+ if self in self.fromShape.rels:
+ self.fromShape.rels.remove(self)
+ if self in self.toShape.rels:
+ self.toShape.rels.remove(self)
+
+ def GetFrom(self):
+ """!Get id of 'from' shape"""
+ return self.fromShape
+
+ def GetTo(self):
+ """!Get id of 'to' shape"""
+ return self.toShape
+
+ def GetData(self):
+ """!Get related ModelData instance
+
+ @return ModelData instance
+ @return None if not found
+ """
+ if isinstance(self.fromShape, ModelData):
+ return self.fromShape
+ elif isinstance(self.toShape, ModelData):
+ return self.toShape
+
+ return None
+
+ def GetName(self):
+ """!Get parameter name"""
+ return self.param
+
+ def ResetShapes(self):
+ """!Reset related objects"""
+ self.fromShape.ResetControlPoints()
+ self.toShape.ResetControlPoints()
+ self.ResetControlPoints()
+
+ def SetControlPoints(self, points):
+ """!Set control points"""
+ self._points = points
+
+ def GetControlPoints(self):
+ """!Get list of control points"""
+ return self._points
+
+ def _setPen(self):
+ """!Set pen"""
+ pen = self.GetPen()
+ pen.SetWidth(1)
+ pen.SetStyle(wx.SOLID)
+ self.SetPen(pen)
+
+ def OnDraw(self, dc):
+ """!Draw relation"""
+ self._setPen()
+ ogl.LineShape.OnDraw(self, dc)
+
+ def SetName(self, param):
+ self.param = param
+
+class ModelListCtrl(wx.ListCtrl,
+ listmix.ListCtrlAutoWidthMixin,
+ listmix.TextEditMixin,
+ listmix.ColumnSorterMixin):
+ def __init__(self, parent, columns, id = wx.ID_ANY,
+ style = wx.LC_REPORT | wx.BORDER_NONE |
+ wx.LC_SORT_ASCENDING |wx.LC_HRULES |
+ wx.LC_VRULES, **kwargs):
+ """!List of model variables"""
+ self.parent = parent
+ self.columns = columns
+ self.shape = None
+ try:
+ self.frame = parent.parent
+ except AttributeError:
+ self.frame = None
+
+ wx.ListCtrl.__init__(self, parent, id = id, style = style, **kwargs)
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+ listmix.TextEditMixin.__init__(self)
+ listmix.ColumnSorterMixin.__init__(self, 4)
+
+ i = 0
+ for col in columns:
+ self.InsertColumn(i, col)
+ self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
+ i += 1
+
+ self.itemDataMap = {} # requested by sorter
+ self.itemCount = 0
+
+ self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit)
+ self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit)
+ self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
+ self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
+
+ def OnBeginEdit(self, event):
+ """!Editing of item started"""
+ event.Allow()
+
+ def OnEndEdit(self, event):
+ """!Finish editing of item"""
+ pass
+
+ def OnColClick(self, event):
+ """!Click on column header (order by)"""
+ event.Skip()
+
+class VariablePanel(wx.Panel):
+ def __init__(self, parent, id = wx.ID_ANY,
+ **kwargs):
+ """!Manage model variables panel
+ """
+ self.parent = parent
+
+ wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+
+ self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label=" %s " % _("List of variables - right-click to delete"))
+
+ self.list = VariableListCtrl(parent = self,
+ columns = [_("Name"), _("Data type"),
+ _("Default value"), _("Description")])
+
+ # add new category
+ self.addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label = " %s " % _("Add new variable"))
+ self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+ wx.CallAfter(self.name.SetFocus)
+ self.type = wx.Choice(parent = self, id = wx.ID_ANY,
+ choices = [_("integer"),
+ _("float"),
+ _("string"),
+ _("raster"),
+ _("vector"),
+ _("mapset"),
+ _("file")])
+ self.type.SetSelection(2) # string
+ self.value = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+ self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+
+ # buttons
+ self.btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
+ self.btnAdd.SetToolTipString(_("Add new variable to the model"))
+ self.btnAdd.Enable(False)
+
+ # bindings
+ self.name.Bind(wx.EVT_TEXT, self.OnText)
+ self.value.Bind(wx.EVT_TEXT, self.OnText)
+ self.desc.Bind(wx.EVT_TEXT, self.OnText)
+ self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAdd)
+
+ self._layout()
+
+ def _layout(self):
+ """!Layout dialog"""
+ listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+ listSizer.Add(item = self.list, proportion = 1,
+ flag = wx.EXPAND)
+
+ addSizer = wx.StaticBoxSizer(self.addBox, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+ gridSizer.AddGrowableCol(1)
+ gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = "%s:" % _("Name")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (0, 0))
+ gridSizer.Add(item = self.name,
+ pos = (0, 1),
+ flag = wx.EXPAND)
+ gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = "%s:" % _("Data type")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (0, 2))
+ gridSizer.Add(item = self.type,
+ pos = (0, 3))
+ gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = "%s:" % _("Default value")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (1, 0))
+ gridSizer.Add(item = self.value,
+ pos = (1, 1), span = (1, 3),
+ flag = wx.EXPAND)
+ gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = "%s:" % _("Description")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (2, 0))
+ gridSizer.Add(item = self.desc,
+ pos = (2, 1), span = (1, 3),
+ flag = wx.EXPAND)
+ addSizer.Add(item = gridSizer,
+ flag = wx.EXPAND)
+ addSizer.Add(item = self.btnAdd, proportion = 0,
+ flag = wx.TOP | wx.ALIGN_RIGHT, border = 5)
+
+ 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)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def OnText(self, event):
+ """!Text entered"""
+ if self.name.GetValue():
+ self.btnAdd.Enable()
+ else:
+ self.btnAdd.Enable(False)
+
+ def OnAdd(self, event):
+ """!Add new variable to the list"""
+ msg = self.list.Append(self.name.GetValue(),
+ self.type.GetStringSelection(),
+ self.value.GetValue(),
+ self.desc.GetValue())
+ self.name.SetValue('')
+ self.name.SetFocus()
+
+ if msg:
+ GError(parent = self,
+ message = msg)
+ else:
+ self.type.SetSelection(2) # string
+ self.value.SetValue('')
+ self.desc.SetValue('')
+ self.UpdateModelVariables()
+
+ def UpdateModelVariables(self):
+ """!Update model variables"""
+ variables = dict()
+ for values in self.list.GetData().itervalues():
+ name = values[0]
+ variables[name] = { 'type' : str(values[1]) }
+ if values[2]:
+ variables[name]['value'] = values[2]
+ if values[3]:
+ variables[name]['description'] = values[3]
+
+ self.parent.GetModel().SetVariables(variables)
+ self.parent.ModelChanged()
+
+ def Update(self):
+ """!Reload list of variables"""
+ self.list.OnReload(None)
+
+ def Reset(self):
+ """!Remove all variables"""
+ self.list.DeleteAllItems()
+ self.parent.GetModel().SetVariables([])
+
+class VariableListCtrl(ModelListCtrl):
+ def __init__(self, parent, columns, **kwargs):
+ """!List of model variables"""
+ ModelListCtrl.__init__(self, parent, columns, **kwargs)
+
+ self.SetColumnWidth(2, 200) # default value
+
+ def GetListCtrl(self):
+ """!Used by ColumnSorterMixin"""
+ return self
+
+ def GetData(self):
+ """!Get list data"""
+ return self.itemDataMap
+
+ def Populate(self, data):
+ """!Populate the list"""
+ self.itemDataMap = dict()
+ i = 0
+ for name, values in data.iteritems():
+ self.itemDataMap[i] = [name, values['type'],
+ values.get('value', ''),
+ values.get('description', '')]
+ i += 1
+
+ self.itemCount = len(self.itemDataMap.keys())
+ self.DeleteAllItems()
+ i = 0
+ for name, vtype, value, desc in self.itemDataMap.itervalues():
+ index = self.InsertStringItem(sys.maxint, name)
+ self.SetStringItem(index, 0, name)
+ self.SetStringItem(index, 1, vtype)
+ self.SetStringItem(index, 2, value)
+ self.SetStringItem(index, 3, desc)
+ self.SetItemData(index, i)
+ i += 1
+
+ def Append(self, name, vtype, value, desc):
+ """!Append new item to the list
+
+ @return None on success
+ @return error string
+ """
+ for iname, ivtype, ivalue, idesc in self.itemDataMap.itervalues():
+ if iname == name:
+ return _("Variable <%s> already exists in the model. "
+ "Adding variable failed.") % name
+
+ index = self.InsertStringItem(sys.maxint, name)
+ self.SetStringItem(index, 0, name)
+ self.SetStringItem(index, 1, vtype)
+ self.SetStringItem(index, 2, value)
+ self.SetStringItem(index, 3, desc)
+ self.SetItemData(index, self.itemCount)
+
+ self.itemDataMap[self.itemCount] = [name, vtype, value, desc]
+ self.itemCount += 1
+
+ return None
+
+ def OnRemove(self, event):
+ """!Remove selected variable(s) from the model"""
+ item = self.GetFirstSelected()
+ while item != -1:
+ self.DeleteItem(item)
+ del self.itemDataMap[item]
+ item = self.GetFirstSelected()
+ self.parent.UpdateModelVariables()
+
+ event.Skip()
+
+ def OnRemoveAll(self, event):
+ """!Remove all variable(s) from the model"""
+ dlg = wx.MessageBox(parent=self,
+ message=_("Do you want to delete all variables from "
+ "the model?"),
+ caption=_("Delete variables"),
+ style=wx.YES_NO | wx.CENTRE)
+ if dlg != wx.YES:
+ return
+
+ self.DeleteAllItems()
+ self.itemDataMap = dict()
+
+ self.parent.UpdateModelVariables()
+
+ def OnEndEdit(self, event):
+ """!Finish editing of item"""
+ itemIndex = event.GetIndex()
+ columnIndex = event.GetColumn()
+ nameOld = self.GetItem(itemIndex, 0).GetText()
+
+ if columnIndex == 0: # TODO
+ event.Veto()
+
+ self.itemDataMap[itemIndex][columnIndex] = event.GetText()
+
+ self.parent.UpdateModelVariables()
+
+ def OnReload(self, event):
+ """!Reload list of variables"""
+ self.Populate(self.parent.parent.GetModel().GetVariables())
+
+ 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.OnRemove, id = self.popupID1)
+ self.Bind(wx.EVT_MENU, self.OnRemoveAll, 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"))
+ menu.Append(self.popupID2, _("Delete all"))
+ if self.GetFirstSelected() == -1:
+ menu.Enable(self.popupID1, False)
+ menu.Enable(self.popupID2, False)
+
+ menu.AppendSeparator()
+ menu.Append(self.popupID3, _("Reload"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+class ModelItem(ModelObject):
+ def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
+ """!Abstract class for loops and conditions"""
+ ModelObject.__init__(self, id)
+ self.parent = parent
+ self.text = text
+ self.items = items # list of items in the loop
+
+ def GetText(self):
+ """!Get loop text"""
+ return self.text
+
+ def GetItems(self):
+ """!Get items (id)"""
+ return self.items
+
+ def SetId(self, id):
+ """!Set loop id"""
+ self.id = id
+
+ def SetText(self, cond):
+ """!Set loop text (condition)"""
+ self.text = cond
+ self.ClearText()
+ self.AddText('(' + str(self.id) + ') ' + self.text)
+
+ def GetLog(self):
+ """!Get log info"""
+ if self.text:
+ return _("Condition: ") + self.text
+ else:
+ return _("Condition: not defined")
+
+ def AddRelation(self, rel):
+ """!Record relation"""
+ self.rels.append(rel)
+
+ def Clear(self):
+ """!Clear object, remove rels"""
+ self.rels = list()
+
+class ModelLoop(ModelItem, ogl.RectangleShape):
+ def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
+ """!Defines a loop"""
+ ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
+
+ if not width:
+ width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width'))
+ if not height:
+ height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height'))
+
+ if self.parent.GetCanvas():
+ ogl.RectangleShape.__init__(self, width, height)
+
+ self.SetCanvas(self.parent)
+ self.SetX(x)
+ self.SetY(y)
+ self.SetPen(wx.BLACK_PEN)
+ self.SetCornerRadius(100)
+ if text:
+ self.AddText('(' + str(self.id) + ') ' + text)
+ else:
+ self.AddText('(' + str(self.id) + ')')
+
+ self._setBrush()
+
+ def _setBrush(self):
+ """!Set brush"""
+ if not self.isEnabled:
+ color = UserSettings.Get(group='modeler', key='disabled',
+ subkey='color')
+ else:
+ color = UserSettings.Get(group='modeler', key='loop',
+ subkey=('color', 'valid'))
+
+ wxColor = wx.Color(color[0], color[1], color[2])
+ self.SetBrush(wx.Brush(wxColor))
+
+ def Enable(self, enabled = True):
+ """!Enable/disable action"""
+ for item in self.items:
+ if not isinstance(item, ModelAction):
+ continue
+ item.Enable(enabled)
+
+ ModelObject.Enable(self, enabled)
+
+ def Update(self):
+ self._setBrush()
+
+ def GetName(self):
+ """!Get name"""
+ return _("loop")
+
+ def SetItems(self, items):
+ """!Set items (id)"""
+ self.items = items
+
+class ItemPanel(wx.Panel):
+ def __init__(self, parent, id = wx.ID_ANY,
+ **kwargs):
+ """!Manage model items
+ """
+ self.parent = parent
+
+ wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+
+ self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label=" %s " % _("List of items - right-click to delete"))
+
+ self.list = ItemListCtrl(parent = self,
+ columns = [_("ID"), _("Name"), _("In block"),
+ _("Command / Condition")])
+
+ self._layout()
+
+ def _layout(self):
+ """!Layout dialog"""
+ listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+ listSizer.Add(item = self.list, proportion = 1,
+ flag = wx.EXPAND)
+
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ mainSizer.Add(item = listSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def Update(self):
+ """!Reload list of variables"""
+ self.list.OnReload(None)
+
+class ItemListCtrl(ModelListCtrl):
+ def __init__(self, parent, columns, disablePopup = False, **kwargs):
+ """!List of model actions"""
+ self.disablePopup = disablePopup
+
+ ModelListCtrl.__init__(self, parent, columns, **kwargs)
+ self.SetColumnWidth(1, 100)
+ self.SetColumnWidth(2, 65)
+
+ def GetListCtrl(self):
+ """!Used by ColumnSorterMixin"""
+ return self
+
+ def GetData(self):
+ """!Get list data"""
+ return self.itemDataMap
+
+ def Populate(self, data):
+ """!Populate the list"""
+ self.itemDataMap = dict()
+
+ if self.shape:
+ if isinstance(self.shape, ModelCondition):
+ if self.GetName() == 'ElseBlockList':
+ shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['else'])
+ else:
+ shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['if'])
+ else:
+ shapeItems = map(lambda x: x.GetId(), self.shape.GetItems())
+ else:
+ shapeItems = list()
+
+ i = 0
+ if len(self.columns) == 3: # ItemCheckList
+ checked = list()
+ for action in data:
+ if isinstance(action, ModelData) or \
+ action == self.shape:
+ continue
+
+ if len(self.columns) == 3:
+ self.itemDataMap[i] = [str(action.GetId()),
+ action.GetName(),
+ action.GetLog()]
+ aId = action.GetBlockId()
+ if action.GetId() in shapeItems:
+ checked.append(aId)
+ else:
+ checked.append(None)
+ else:
+ bId = action.GetBlockId()
+ if not bId:
+ bId = ''
+ self.itemDataMap[i] = [str(action.GetId()),
+ action.GetName(),
+ ','.join(map(str, bId)),
+ action.GetLog()]
+
+ i += 1
+
+ self.itemCount = len(self.itemDataMap.keys())
+ self.DeleteAllItems()
+ i = 0
+ if len(self.columns) == 3:
+ for aid, name, desc in self.itemDataMap.itervalues():
+ index = self.InsertStringItem(sys.maxint, aid)
+ self.SetStringItem(index, 0, aid)
+ self.SetStringItem(index, 1, name)
+ self.SetStringItem(index, 2, desc)
+ self.SetItemData(index, i)
+ if checked[i]:
+ self.CheckItem(index, True)
+ i += 1
+ else:
+ for aid, name, inloop, desc in self.itemDataMap.itervalues():
+ index = self.InsertStringItem(sys.maxint, aid)
+ self.SetStringItem(index, 0, aid)
+ self.SetStringItem(index, 1, name)
+ self.SetStringItem(index, 2, inloop)
+ self.SetStringItem(index, 3, desc)
+ self.SetItemData(index, i)
+ i += 1
+
+ def OnRemove(self, event):
+ """!Remove selected action(s) from the model"""
+ model = self.frame.GetModel()
+ canvas = self.frame.GetCanvas()
+
+ item = self.GetFirstSelected()
+ while item != -1:
+ self.DeleteItem(item)
+ del self.itemDataMap[item]
+
+ aId = self.GetItem(item, 0).GetText()
+ action = model.GetItem(int(aId))
+ if not action:
+ item = self.GetFirstSelected()
+ continue
+
+ model.RemoveItem(action)
+ canvas.GetDiagram().RemoveShape(action)
+ self.frame.ModelChanged()
+
+ item = self.GetFirstSelected()
+
+ canvas.Refresh()
+
+ event.Skip()
+
+ def OnRemoveAll(self, event):
+ """!Remove all variable(s) from the model"""
+ deleteDialog = wx.MessageBox(parent=self,
+ message=_("Selected data records (%d) will permanently deleted "
+ "from table. Do you want to delete them?") % \
+ (len(self.listOfSQLStatements)),
+ caption=_("Delete records"),
+ style=wx.YES_NO | wx.CENTRE)
+ if deleteDialog != wx.YES:
+ return False
+
+ self.DeleteAllItems()
+ self.itemDataMap = dict()
+
+ self.parent.UpdateModelVariables()
+
+ def OnEndEdit(self, event):
+ """!Finish editing of item"""
+ itemIndex = event.GetIndex()
+ columnIndex = event.GetColumn()
+
+ self.itemDataMap[itemIndex][columnIndex] = event.GetText()
+
+ aId = int(self.GetItem(itemIndex, 0).GetText())
+ action = self.frame.GetModel().GetItem(aId)
+ if not action:
+ event.Veto()
+ if columnIndex == 0:
+ action.SetId(int(event.GetText()))
+
+ self.frame.ModelChanged()
+
+ def OnReload(self, event = None):
+ """!Reload list of actions"""
+ self.Populate(self.frame.GetModel().GetItems())
+
+ def OnRightUp(self, event):
+ """!Mouse right button up"""
+ if self.disablePopup:
+ return
+
+ if not hasattr(self, "popupID1"):
+ self.popupID1 = wx.NewId()
+ self.popupID2 = wx.NewId()
+ self.popupID3 = wx.NewId()
+ self.popupID4 = wx.NewId()
+ self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1)
+ self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2)
+ self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
+ self.Bind(wx.EVT_MENU, self.OnNormalize, id = self.popupID4)
+
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupID1, _("Delete selected"))
+ menu.Append(self.popupID2, _("Delete all"))
+ if self.GetFirstSelected() == -1:
+ menu.Enable(self.popupID1, False)
+ menu.Enable(self.popupID2, False)
+
+ menu.AppendSeparator()
+ menu.Append(self.popupID4, _("Normalize"))
+ menu.Append(self.popupID3, _("Reload"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def OnNormalize(self, event):
+ """!Update id of actions"""
+ model = self.frame.GetModel()
+
+ aId = 1
+ for item in model.GetItems():
+ item.SetId(aId)
+ aId += 1
+
+ self.OnReload(None)
+ self.frame.GetCanvas().Refresh()
+ self.frame.ModelChanged()
+
+class ItemCheckListCtrl(ItemListCtrl, listmix.CheckListCtrlMixin):
+ def __init__(self, parent, shape, columns, window = None, **kwargs):
+ self.parent = parent
+ self.window = window
+
+ ItemListCtrl.__init__(self, parent, columns, disablePopup = True, **kwargs)
+ listmix.CheckListCtrlMixin.__init__(self)
+ self.SetColumnWidth(0, 50)
+
+ self.shape = shape
+
+ def OnBeginEdit(self, event):
+ """!Disable editing"""
+ event.Veto()
+
+ def OnCheckItem(self, index, flag):
+ """!Item checked/unchecked"""
+ name = self.GetName()
+ if name == 'IfBlockList' and self.window:
+ self.window.OnCheckItemIf(index, flag)
+ elif name == 'ElseBlockList' and self.window:
+ self.window.OnCheckItemElse(index, flag)
+
+ def GetItems(self):
+ """!Get list of selected actions"""
+ ids = { 'checked' : list(),
+ 'unchecked' : list() }
+ for i in range(self.GetItemCount()):
+ iId = int(self.GetItem(i, 0).GetText())
+ if self.IsChecked(i):
+ ids['checked'].append(iId)
+ else:
+ ids['unchecked'].append(iId)
+
+ return ids
+
+ def CheckItemById(self, aId, flag):
+ """!Check/uncheck given item by id"""
+ for i in range(self.GetItemCount()):
+ iId = int(self.GetItem(i, 0).GetText())
+ if iId == aId:
+ self.CheckItem(i, flag)
+ break
+
+class ModelCondition(ModelItem, ogl.PolygonShape):
+ def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '',
+ items = { 'if' : [], 'else' : [] }):
+ """!Defines a if-else condition"""
+ ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
+
+ if not width:
+ self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width'))
+ else:
+ self.width = width
+ if not height:
+ self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height'))
+ else:
+ self.height = height
+
+ if self.parent.GetCanvas():
+ ogl.PolygonShape.__init__(self)
+
+ points = [(0, - self.height / 2),
+ (self.width / 2, 0),
+ (0, self.height / 2),
+ (- self.width / 2, 0)]
+ self.Create(points)
+
+ self.SetCanvas(self.parent)
+ self.SetX(x)
+ self.SetY(y)
+ self.SetPen(wx.BLACK_PEN)
+ if text:
+ self.AddText('(' + str(self.id) + ') ' + text)
+ else:
+ self.AddText('(' + str(self.id) + ')')
+
+ def GetName(self):
+ """!Get name"""
+ return _("if-else")
+
+ def GetWidth(self):
+ """!Get object width"""
+ return self.width
+
+ def GetHeight(self):
+ """!Get object height"""
+ return self.height
+
+ def SetItems(self, items, branch = 'if'):
+ """!Set items (id)
+
+ @param items list of items
+ @param branch 'if' / 'else'
+ """
+ if branch in ['if', 'else']:
+ self.items[branch] = items
+
+def main():
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ app = wx.PySimpleApp()
+ wx.InitAllImageHandlers()
+ frame = ModelFrame(parent = None)
+ if len(sys.argv) > 1:
+ frame.LoadModelFile(sys.argv[1])
+ frame.Show()
+
+ app.MainLoop()
+
+if __name__ == "__main__":
+ main()
Added: grass/trunk/gui/wxpython/gmodeler/model.py
===================================================================
--- grass/trunk/gui/wxpython/gmodeler/model.py (rev 0)
+++ grass/trunk/gui/wxpython/gmodeler/model.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,731 @@
+"""!
+ at package gmodeler.model
+
+ at 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.
+
+ at 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
Property changes on: grass/trunk/gui/wxpython/gmodeler/model.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/gmodeler/model_file.py
===================================================================
--- grass/trunk/gui/wxpython/gmodeler/model_file.py (rev 0)
+++ grass/trunk/gui/wxpython/gmodeler/model_file.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,695 @@
+"""!
+ at package gmodeler.model_file
+
+ at 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.
+
+ at 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('<', '<')
+ value = value.replace('>', '>')
+
+ 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('<', '<')
+ value = value.replace('>', '>')
+
+ 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
Property changes on: grass/trunk/gui/wxpython/gmodeler/model_file.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Added: grass/trunk/gui/wxpython/gmodeler/preferences.py
===================================================================
--- grass/trunk/gui/wxpython/gmodeler/preferences.py (rev 0)
+++ grass/trunk/gui/wxpython/gmodeler/preferences.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,527 @@
+"""!
+ at package gmodeler.preferences
+
+ at 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.
+
+ at 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'])
Property changes on: grass/trunk/gui/wxpython/gmodeler/preferences.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Copied: grass/trunk/gui/wxpython/gui_core/dialogs.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/gdialogs.py)
===================================================================
--- grass/trunk/gui/wxpython/gui_core/dialogs.py (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/dialogs.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,2403 @@
+"""!
+ at package gui_core.gdialogs
+
+ at brief Various dialogs used in wxGUI.
+
+List of classes:
+ - ElementDialog
+ - LocationDialog
+ - MapsetDialog
+ - NewVectorDialog
+ - SavedRegion
+ - DecorationDialog
+ - TextLayerDialog
+ - GroupDialog
+ - MapLayersDialog
+ - ImportDialog
+ - GdalImportDialog
+ - GdalOutputDialog
+ - DxfImportDialog
+ - LayersList (used by MultiImport)
+ - SetOpacityDialog
+ - StaticWrapText
+ - ImageSizeDialog
+ - SqlQueryFrame
+
+(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.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com> (GroupDialog)
+"""
+
+import os
+import sys
+import re
+from bisect import bisect
+
+import wx
+import wx.lib.filebrowsebutton as filebrowse
+import wx.lib.mixins.listctrl as listmix
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+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,
+ etype = False, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+ **kwargs):
+ """!General dialog to choose given element (location, mapset, vector map, etc.)
+
+ @param parent window
+ @param title window title
+ @param label element label
+ @param etype show also ElementSelect
+ """
+ wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+
+ self.etype = etype
+ self.label = label
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ 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()
+ self.btnOK.Enable(False)
+
+ if self.etype:
+ 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
+
+ self.__layout()
+
+ def PostInit(self):
+ self.element.SetFocus()
+ self.element.Bind(wx.EVT_TEXT, self.OnElement)
+
+ def OnType(self, event):
+ """!Select element type"""
+ if not self.etype:
+ return
+ evalue = self.typeSelect.GetValue(event.GetString())
+ self.element.SetType(evalue)
+
+ def OnElement(self, event):
+ """!Name for vector map layer given"""
+ if len(event.GetString()) > 0:
+ self.btnOK.Enable(True)
+ else:
+ self.btnOK.Enable(False)
+
+ def __layout(self):
+ """!Do layout"""
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+
+ self.dataSizer = wx.BoxSizer(wx.VERTICAL)
+
+ if self.etype:
+ self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Type of element:")),
+ proportion = 0, flag = wx.ALL, border = 1)
+ self.dataSizer.Add(item = self.typeSelect,
+ proportion = 0, flag = wx.ALL, border = 1)
+
+ self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = self.label),
+ proportion = 0, flag = wx.ALL, border = 1)
+
+ # buttons
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.AddButton(self.btnOK)
+ btnSizer.Realize()
+
+ self.sizer.Add(item = self.dataSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ self.sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ def GetElement(self):
+ """!Return (mapName, overwrite)"""
+ return self.element.GetValue()
+
+ def GetType(self):
+ """!Get element type"""
+ return self.element.tcp.GetType()
+
+class LocationDialog(ElementDialog):
+ """!Dialog used to select location"""
+ 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 = 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()
+
+ self.__Layout()
+ self.SetMinSize(self.GetSize())
+
+ def __Layout(self):
+ """!Do layout"""
+ self.dataSizer.Add(self.element, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.dataSizer.Add(wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Name of mapset:")), proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.dataSizer.Add(self.element1, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.panel.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+
+ def OnElement(self, event):
+ """!Select mapset given location name"""
+ location = event.GetString()
+
+ if location:
+ dbase = grass.gisenv()['GISDBASE']
+ self.element1.SetItems(GetListOfMapsets(dbase, location, selectable = True))
+ self.element1.SetSelection(0)
+ mapset = self.element1.GetStringSelection()
+
+ if location and mapset:
+ self.btnOK.Enable(True)
+ else:
+ self.btnOK.Enable(False)
+
+ def GetValues(self):
+ """!Get location, mapset"""
+ return (self.GetElement(), self.element1.GetStringSelection())
+
+class MapsetDialog(ElementDialog):
+ """!Dialog used to select mapset"""
+ def __init__(self, parent, title = _("Select mapset in GRASS location"),
+ location = None, id = wx.ID_ANY):
+ ElementDialog.__init__(self, parent, title, label = _("Name of mapset:"))
+ if location:
+ self.SetTitle(self.GetTitle() + ' <%s>' % location)
+ else:
+ self.SetTitle(self.GetTitle() + ' <%s>' % grass.gisenv()['LOCATION_NAME'])
+
+ self.element = gselect.MapsetSelect(parent = self.panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE)
+
+ self.PostInit()
+
+ self.__Layout()
+ self.SetMinSize(self.GetSize())
+
+ 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 GetMapset(self):
+ return self.GetElement()
+
+class NewVectorDialog(ElementDialog):
+ def __init__(self, parent, id = wx.ID_ANY, title = _('Create new vector map'),
+ disableAdd = False, disableTable = False, showType = False,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, *kwargs):
+ """!Dialog for creating new vector map
+
+ @param parent parent window
+ @param id window id
+ @param title window title
+ @param disableAdd disable 'add layer' checkbox
+ @param disableTable disable 'create table' checkbox
+ @param showType True to show feature type selector (used for creating new empty OGR layers)
+ @param style window style
+ @param kwargs other argumentes for ElementDialog
+
+ @return dialog instance
+ """
+ ElementDialog.__init__(self, parent, title, label = _("Name for new vector map:"))
+
+ 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 = OgrTypeSelect(parent = self, panel = self.panel)
+ else:
+ self.ftype = None
+
+ self.table = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = _("Create attribute table"))
+ self.table.SetValue(True)
+ if disableTable:
+ self.table.Enable(False)
+
+ if showType:
+ self.keycol = None
+ else:
+ self.keycol = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_SPIN_SIZE)
+ self.keycol.SetValue(UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value'))
+ if disableTable:
+ self.keycol.Enable(False)
+
+ self.addbox = wx.CheckBox(parent = self.panel,
+ label = _('Add created map into layer tree'), style = wx.NO_BORDER)
+ if disableAdd:
+ self.addbox.SetValue(True)
+ self.addbox.Enable(False)
+ else:
+ self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+
+ self.table.Bind(wx.EVT_CHECKBOX, self.OnTable)
+
+ self.PostInit()
+
+ self._layout()
+ self.SetMinSize(self.GetSize())
+
+ def OnMapName(self, event):
+ """!Name for vector map layer given"""
+ self.OnElement(event)
+
+ def OnTable(self, event):
+ if self.keycol:
+ self.keycol.Enable(event.IsChecked())
+
+ def _layout(self):
+ """!Do layout"""
+ self.dataSizer.Add(item = self.element, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+ if self.ftype:
+ self.dataSizer.AddSpacer(1)
+ self.dataSizer.Add(item = self.ftype, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.dataSizer.Add(item = self.table, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ if self.keycol:
+ keySizer = wx.BoxSizer(wx.HORIZONTAL)
+ keySizer.Add(item = wx.StaticText(parent = self.panel, label = _("Key column:")),
+ proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ keySizer.AddSpacer(10)
+ keySizer.Add(item = self.keycol, proportion = 0,
+ flag = wx.ALIGN_RIGHT)
+ self.dataSizer.Add(item = keySizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.dataSizer.AddSpacer(5)
+
+ self.dataSizer.Add(item = self.addbox, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+
+ self.panel.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+
+ def GetName(self, full = False):
+ """!Get name of vector map to be created
+
+ @param full True to get fully qualified name
+ """
+ name = self.GetElement()
+ if full:
+ if '@' in name:
+ return name
+ else:
+ return name + '@' + grass.gisenv()['MAPSET']
+
+ return name.split('@', 1)[0]
+
+ def GetKey(self):
+ """!Get key column name"""
+ if self.keycol:
+ return self.keycol.GetValue()
+ return UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value')
+
+ def IsChecked(self, key):
+ """!Get dialog properties
+
+ @param key window key ('add', 'table')
+
+ @return True/False
+ @return None on error
+ """
+ if key == 'add':
+ return self.addbox.IsChecked()
+ elif key == 'table':
+ return self.table.IsChecked()
+
+ return None
+
+ def GetFeatureType(self):
+ """!Get feature type for OGR
+
+ @return feature type as string
+ @return None for native format
+ """
+ if self.ftype:
+ return self.ftype.GetType()
+
+ return None
+
+def CreateNewVector(parent, cmd, title = _('Create new vector map'),
+ exceptMap = None, log = None,
+ disableAdd = False, disableTable = False):
+ """!Create new vector map layer
+
+ @param cmd (prog, **kwargs)
+ @param title window title
+ @param exceptMap list of maps to be excepted
+ @param log
+ @param disableAdd disable 'add layer' checkbox
+ @param disableTable disable 'create table' checkbox
+
+ @return dialog instance
+ @return None on error
+ """
+ vExternalOut = grass.parse_command('v.external.out', flags = 'g')
+ isNative = vExternalOut['format'] == 'native'
+ if cmd[0] == 'v.edit' and not isNative:
+ showType = True
+ else:
+ showType = False
+ dlg = NewVectorDialog(parent, title = title,
+ disableAdd = disableAdd, disableTable = disableTable,
+ showType = showType)
+
+ if dlg.ShowModal() != wx.ID_OK:
+ dlg.Destroy()
+ return None
+
+ outmap = dlg.GetName()
+ key = dlg.GetKey()
+ if outmap == exceptMap:
+ GError(parent = parent,
+ message = _("Unable to create vector map <%s>.") % outmap)
+ dlg.Destroy()
+ return None
+ if dlg.table.IsEnabled() and not key:
+ GError(parent = parent,
+ message = _("Invalid or empty key column.\n"
+ "Unable to create vector map <%s>.") % outmap)
+ dlg.Destroy()
+ return
+
+ if outmap == '': # should not happen
+ dlg.Destroy()
+ return None
+
+ # update cmd -> output name defined
+ cmd[1][cmd[2]] = outmap
+ if showType:
+ cmd[1]['type'] = dlg.GetFeatureType()
+
+ if isNative:
+ listOfVectors = grass.list_grouped('vect')[grass.gisenv()['MAPSET']]
+ else:
+ 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 \
+ outmap in listOfVectors:
+ dlgOw = wx.MessageDialog(parent, message = _("Vector map <%s> already exists "
+ "in the current mapset. "
+ "Do you want to overwrite it?") % outmap,
+ caption = _("Overwrite?"),
+ style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+ if dlgOw.ShowModal() == wx.ID_YES:
+ overwrite = True
+ else:
+ dlgOw.Destroy()
+ dlg.Destroy()
+ return None
+
+ if UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'):
+ overwrite = True
+
+ 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
+ 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)
+
+ RunCommand('db.connect',
+ flags = 'c')
+
+ Debug.msg(1, "SQL: %s" % sql)
+ RunCommand('db.execute',
+ quiet = True,
+ parent = parent,
+ input = '-',
+ stdin = sql)
+
+ 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
+ if '@' not in outmap:
+ outmap += '@' + grass.gisenv()['MAPSET']
+
+ if log:
+ log.WriteLog(_("New vector map <%s> created") % outmap)
+
+ return dlg
+
+class SavedRegion(wx.Dialog):
+ def __init__(self, parent, id = wx.ID_ANY, title = "", loadsave = 'load',
+ **kwargs):
+ """!Loading and saving of display extents to saved region file
+
+ @param loadsave load or save region?
+ """
+ wx.Dialog.__init__(self, parent, id, title, **kwargs)
+
+ self.loadsave = loadsave
+ self.wind = ''
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ label = wx.StaticText(parent = self, id = wx.ID_ANY)
+ box.Add(item = label, proportion = 0, flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
+ if loadsave == 'load':
+ label.SetLabel(_("Load region:"))
+ selection = Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+ type = 'windows')
+ elif loadsave == 'save':
+ label.SetLabel(_("Save region:"))
+ 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()
+ selection.Bind(wx.EVT_TEXT, self.OnRegion)
+
+ sizer.Add(item = box, proportion = 0, flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL,
+ border = 5)
+
+ line = wx.StaticLine(parent = self, id = wx.ID_ANY, size = (20, -1), style = wx.LI_HORIZONTAL)
+ sizer.Add(item = line, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border = 5)
+
+ btnsizer = wx.StdDialogButtonSizer()
+
+ btn = wx.Button(parent = self, id = wx.ID_OK)
+ btn.SetDefault()
+ btnsizer.AddButton(btn)
+
+ btn = wx.Button(parent = self, id = wx.ID_CANCEL)
+ btnsizer.AddButton(btn)
+ btnsizer.Realize()
+
+ sizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+ self.Layout()
+
+ def OnRegion(self, event):
+ self.wind = event.GetString()
+
+class DecorationDialog(wx.Dialog):
+ """!Controls setting options and displaying/hiding map overlay
+ decorations
+ """
+ def __init__(self, parent, ovlId, title, cmd, name = None,
+ pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE,
+ checktxt = '', ctrltxt = ''):
+
+ wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+
+ self.ovlId = ovlId # PseudoDC id
+ self.cmd = cmd
+ self.name = name # overlay name
+ self.parent = parent # MapFrame
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY, label = checktxt)
+ if self.parent.Map.GetOverlay(self.ovlId) is None:
+ self.chkbox.SetValue(True)
+ else:
+ self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
+ box.Add(item = self.chkbox, proportion = 0,
+ flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+ sizer.Add(item = box, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ optnbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set options"))
+ box.Add(item = optnbtn, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+ sizer.Add(item = box, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+ if self.name == 'legend':
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ resize = wx.ToggleButton(parent = self, id = wx.ID_ANY, label = _("Set size and position"))
+ resize.SetToolTipString(_("Click and drag on the map display to set legend"
+ " size and position and then press OK"))
+ resize.SetName('resize')
+ if self.parent.IsPaneShown('3d'):
+ resize.Disable()
+ box.Add(item = resize, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+ sizer.Add(item = box, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ label = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = _("Drag %s with mouse in pointer mode to position.\n"
+ "Double-click to change options." % ctrltxt))
+ if self.name == 'legend':
+ label.SetLabel(label.GetLabel() + _('\nDefine raster map name for legend in '
+ 'properties dialog.'))
+ box.Add(item = label, proportion = 0,
+ flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+ sizer.Add(item = box, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+ line = wx.StaticLine(parent = self, id = wx.ID_ANY, size = (20,-1), style = wx.LI_HORIZONTAL)
+ sizer.Add(item = line, proportion = 0,
+ flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+ # buttons
+ btnsizer = wx.StdDialogButtonSizer()
+
+ self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
+ self.btnOK.SetDefault()
+ if self.name == 'legend':
+ self.btnOK.Enable(False)
+ btnsizer.AddButton(self.btnOK)
+
+ btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+ btnsizer.AddButton(btnCancel)
+ btnsizer.Realize()
+
+ sizer.Add(item = btnsizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+ #
+ # bindings
+ #
+ self.Bind(wx.EVT_BUTTON, self.OnOptions, optnbtn)
+ if self.name == 'legend':
+ self.Bind(wx.EVT_TOGGLEBUTTON, self.OnResize, resize)
+ self.Bind(wx.EVT_BUTTON, self.OnCancel, btnCancel)
+ self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ # create overlay if doesn't exist
+ self._createOverlay()
+
+ if len(self.parent.MapWindow.overlays[self.ovlId]['cmd']) > 1:
+ if name == 'legend':
+ mapName, found = GetLayerNameFromCmd(self.parent.MapWindow.overlays[self.ovlId]['cmd'])
+ if found:
+ # enable 'OK' button
+ self.btnOK.Enable()
+ # set title
+ self.SetTitle(_('Legend of raster map <%s>') % \
+ mapName)
+
+
+ def _createOverlay(self):
+ """!Creates overlay"""
+ if not self.parent.GetMap().GetOverlay(self.ovlId):
+ self.newOverlay = self.parent.Map.AddOverlay(id = self.ovlId, type = self.name,
+ command = self.cmd,
+ l_active = False, l_render = False, l_hidden = True)
+ prop = { 'layer' : self.newOverlay,
+ 'params' : None,
+ 'propwin' : None,
+ 'cmd' : self.cmd,
+ 'coords': (0, 0),
+ 'pdcType': 'image' }
+ self.parent.MapWindow2D.overlays[self.ovlId] = prop
+ if self.parent.MapWindow3D:
+ self.parent.MapWindow3D.overlays[self.ovlId] = prop
+
+ else:
+ if self.parent.MapWindow.overlays[self.ovlId]['propwin'] == None:
+ return
+
+ self.parent.MapWindow.overlays[self.ovlId]['propwin'].get_dcmd = self.GetOptData
+
+ def OnOptions(self, event):
+ """!Sets option for decoration map overlays
+ """
+ if self.parent.MapWindow.overlays[self.ovlId]['propwin'] is None:
+ # build properties dialog
+ GUI(parent = self.parent).ParseCommand(cmd = self.cmd,
+ completed = (self.GetOptData, self.name, ''))
+
+ else:
+ if self.parent.MapWindow.overlays[self.ovlId]['propwin'].IsShown():
+ self.parent.MapWindow.overlays[self.ovlId]['propwin'].SetFocus()
+ else:
+ self.parent.MapWindow.overlays[self.ovlId]['propwin'].Show()
+
+ def OnResize(self, event):
+ if self.FindWindowByName('resize').GetValue():
+ self.parent.MapWindow.SetCursor(self.parent.cursors["cross"])
+ self.parent.MapWindow.mouse['use'] = 'legend'
+ self.parent.MapWindow.mouse['box'] = 'box'
+ self.parent.MapWindow.pen = wx.Pen(colour = 'Black', width = 2, style = wx.SHORT_DASH)
+ else:
+ self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
+ self.parent.MapWindow.mouse['use'] = 'pointer'
+
+ def OnCancel(self, event):
+ """!Cancel dialog"""
+ if self.name == 'legend' and self.FindWindowByName('resize').GetValue():
+ self.FindWindowByName('resize').SetValue(False)
+ self.OnResize(None)
+
+ self.parent.dialogs['barscale'] = None
+ if event and hasattr(self, 'newOverlay'):
+ self.parent.Map.DeleteOverlay(self.newOverlay)
+ self.Destroy()
+
+ def OnOK(self, event):
+ """!Button 'OK' pressed"""
+ # enable or disable overlay
+ self.parent.Map.GetOverlay(self.ovlId).SetActive(self.chkbox.IsChecked())
+
+ # update map
+ if self.parent.IsPaneShown('3d'):
+ self.parent.MapWindow.UpdateOverlays()
+
+ self.parent.MapWindow.UpdateMap()
+
+ # close dialog
+ self.OnCancel(None)
+
+ def GetOptData(self, dcmd, layer, params, propwin):
+ """!Process decoration layer data"""
+ # update layer data
+ if params:
+ self.parent.MapWindow.overlays[self.ovlId]['params'] = params
+ if dcmd:
+ self.parent.MapWindow.overlays[self.ovlId]['cmd'] = dcmd
+ self.parent.MapWindow.overlays[self.ovlId]['propwin'] = propwin
+
+ # change parameters for item in layers list in render.Map
+ # "Use mouse..." (-m) flag causes GUI freeze and is pointless here, trac #119
+
+ try:
+ self.parent.MapWindow.overlays[self.ovlId]['cmd'].remove('-m')
+ except ValueError:
+ pass
+
+ self.parent.Map.ChangeOverlay(id = self.ovlId, type = self.name,
+ command = self.parent.MapWindow.overlays[self.ovlId]['cmd'],
+ l_active = self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive(),
+ l_render = False, l_hidden = True)
+ if self.name == 'legend':
+ if params and not self.btnOK.IsEnabled():
+ self.btnOK.Enable()
+
+class TextLayerDialog(wx.Dialog):
+ """
+ Controls setting options and displaying/hiding map overlay decorations
+ """
+
+ def __init__(self, parent, ovlId, title, name = 'text',
+ pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE):
+
+ wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+ from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+
+ self.ovlId = ovlId
+ self.parent = parent
+
+ if self.ovlId in self.parent.MapWindow.textdict.keys():
+ self.currText = self.parent.MapWindow.textdict[self.ovlId]['text']
+ self.currFont = self.parent.MapWindow.textdict[self.ovlId]['font']
+ self.currClr = self.parent.MapWindow.textdict[self.ovlId]['color']
+ self.currRot = self.parent.MapWindow.textdict[self.ovlId]['rotation']
+ self.currCoords = self.parent.MapWindow.textdict[self.ovlId]['coords']
+ self.currBB = self.parent.MapWindow.textdict[self.ovlId]['bbox']
+ else:
+ self.currClr = wx.BLACK
+ self.currText = ''
+ self.currFont = self.GetFont()
+ self.currRot = 0.0
+ self.currCoords = [10, 10]
+ self.currBB = wx.Rect()
+
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ box = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+ # show/hide
+ self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY,
+ label = _('Show text object'))
+ if self.parent.Map.GetOverlay(self.ovlId) is None:
+ self.chkbox.SetValue(True)
+ else:
+ self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
+ box.Add(item = self.chkbox, span = (1,2),
+ flag = wx.ALIGN_LEFT|wx.ALL, border = 5,
+ pos = (0, 0))
+
+ # text entry
+ label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Enter text:"))
+ box.Add(item = label,
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (1, 0))
+
+ self.textentry = ExpandoTextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (300,-1))
+ self.textentry.SetFont(self.currFont)
+ self.textentry.SetForegroundColour(self.currClr)
+ self.textentry.SetValue(self.currText)
+ # get rid of unneeded scrollbar when text box first opened
+ self.textentry.SetClientSize((300,-1))
+
+ box.Add(item = self.textentry,
+ pos = (1, 1))
+
+ # rotation
+ label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Rotation:"))
+ box.Add(item = label,
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (2, 0))
+ self.rotation = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
+ size = (75,-1), style = wx.SP_ARROW_KEYS)
+ self.rotation.SetRange(-360, 360)
+ self.rotation.SetValue(int(self.currRot))
+ box.Add(item = self.rotation,
+ flag = wx.ALIGN_RIGHT,
+ pos = (2, 1))
+
+ # font
+ fontbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set font"))
+ box.Add(item = fontbtn,
+ flag = wx.ALIGN_RIGHT,
+ pos = (3, 1))
+
+ self.sizer.Add(item = box, proportion = 1,
+ flag = wx.ALL, border = 10)
+
+ # note
+ box = wx.BoxSizer(wx.HORIZONTAL)
+ label = wx.StaticText(parent = self, id = wx.ID_ANY,
+ label = _("Drag text with mouse in pointer mode "
+ "to position.\nDouble-click to change options"))
+ box.Add(item = label, proportion = 0,
+ flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
+ self.sizer.Add(item = box, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER | wx.ALL, border = 5)
+
+ line = wx.StaticLine(parent = self, id = wx.ID_ANY,
+ size = (20,-1), style = wx.LI_HORIZONTAL)
+ self.sizer.Add(item = line, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border = 5)
+
+ btnsizer = wx.StdDialogButtonSizer()
+
+ btn = wx.Button(parent = self, id = wx.ID_OK)
+ btn.SetDefault()
+ btnsizer.AddButton(btn)
+
+ btn = wx.Button(parent = self, id = wx.ID_CANCEL)
+ btnsizer.AddButton(btn)
+ btnsizer.Realize()
+
+ self.sizer.Add(item = btnsizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ self.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+
+ # bindings
+ self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnRefit, self.textentry)
+ self.Bind(wx.EVT_BUTTON, self.OnSelectFont, fontbtn)
+ self.Bind(wx.EVT_TEXT, self.OnText, self.textentry)
+ self.Bind(wx.EVT_SPINCTRL, self.OnRotation, self.rotation)
+
+ def OnRefit(self, event):
+ """!Resize text entry to match text"""
+ self.sizer.Fit(self)
+
+ def OnText(self, event):
+ """!Change text string"""
+ self.currText = event.GetString()
+
+ def OnRotation(self, event):
+ """!Change rotation"""
+ self.currRot = event.GetInt()
+
+ event.Skip()
+
+ def OnSelectFont(self, event):
+ """!Change font"""
+ data = wx.FontData()
+ data.EnableEffects(True)
+ data.SetColour(self.currClr) # set colour
+ data.SetInitialFont(self.currFont)
+
+ dlg = wx.FontDialog(self, data)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ data = dlg.GetFontData()
+ self.currFont = data.GetChosenFont()
+ self.currClr = data.GetColour()
+
+ self.textentry.SetFont(self.currFont)
+ self.textentry.SetForegroundColour(self.currClr)
+
+ self.Layout()
+
+ dlg.Destroy()
+
+ def GetValues(self):
+ """!Get text properties"""
+ return { 'text' : self.currText,
+ 'font' : self.currFont,
+ 'color' : self.currClr,
+ 'rotation' : self.currRot,
+ 'coords' : self.currCoords,
+ 'active' : self.chkbox.IsChecked() }
+
+class GroupDialog(wx.Dialog):
+ """!Dialog for creating/editing groups"""
+ def __init__(self, parent = None, defaultGroup = None,
+ title = _("Create or edit imagery groups"),
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+
+ wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+ style = style, **kwargs)
+
+ self.parent = parent
+ self.defaultGroup = defaultGroup
+ self.currentGroup = self.defaultGroup
+ self.groupChanged = False
+
+ self.bodySizer = self._createDialogBody()
+
+ # buttons
+ btnOk = wx.Button(parent = self, id = wx.ID_OK)
+ btnApply = wx.Button(parent = self, id = wx.ID_APPLY)
+ btnClose = wx.Button(parent = self, id = wx.ID_CANCEL)
+
+ btnOk.SetToolTipString(_("Apply changes to selected group and close dialog"))
+ btnApply.SetToolTipString(_("Apply changes to selected group"))
+ btnClose.SetToolTipString(_("Close dialog, changes are not applied"))
+
+ btnOk.SetDefault()
+
+ # sizers & do layout
+ # btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ # btnSizer.Add(item = btnClose, proportion = 0,
+ # flag = wx.RIGHT | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
+ # btnSizer.Add(item = btnApply, proportion = 0,
+ # flag = wx.LEFT, border = 5)
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(btnOk)
+ btnSizer.AddButton(btnApply)
+ btnSizer.AddButton(btnClose)
+ btnSizer.Realize()
+
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ mainSizer.Add(item = self.bodySizer, proportion = 1,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10)
+ mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+ style = wx.LI_HORIZONTAL), proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10)
+
+ mainSizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_RIGHT, border = 10)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+ btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+ btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+
+ # set dialog min size
+ self.SetMinSize(self.GetSize())
+
+ def _createDialogBody(self):
+ bodySizer = wx.BoxSizer(wx.VERTICAL)
+
+ # group selection
+ bodySizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+ 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 = 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)
+
+ bodySizer.AddSpacer(10)
+ # layers in group
+ bodySizer.Add(item = wx.StaticText(parent = self, label = _("Layers in selected group:")),
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, border = 5)
+
+ gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+ gridSizer.AddGrowableCol(0)
+
+ self.layerBox = wx.ListBox(parent = self, id = wx.ID_ANY, size = (-1, 150),
+ style = wx.LB_MULTIPLE | wx.LB_NEEDED_SB)
+
+ gridSizer.Add(item = self.layerBox, pos = (0, 0), span = (2, 1), flag = wx.EXPAND)
+
+ self.addLayer = wx.Button(self, id = wx.ID_ADD)
+ self.addLayer.SetToolTipString(_("Select map layers and add them to the list."))
+ gridSizer.Add(item = self.addLayer, pos = (0, 1))
+
+ self.removeLayer = wx.Button(self, id = wx.ID_REMOVE)
+ self.removeLayer.SetToolTipString(_("Remove selected layer(s) from list."))
+ gridSizer.Add(item = self.removeLayer, pos = (1, 1))
+
+ bodySizer.Add(item = gridSizer, proportion = 1, flag = wx.EXPAND)
+
+ self.infoLabel = wx.StaticText(parent = self, id = wx.ID_ANY)
+ bodySizer.Add(item = self.infoLabel,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.BOTTOM, border = 5)
+
+ # bindings
+ self.groupSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnGroupSelected)
+ self.addLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
+ self.removeLayer.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
+
+ if self.defaultGroup:
+ self.groupSelect.SetValue(self.defaultGroup)
+
+ return bodySizer
+
+ def OnAddLayer(self, event):
+ """!Add new layer to listbox"""
+ dlg = MapLayersDialog(parent = self, title = _("Add selected map layers into group"),
+ mapType = 'raster', selectAll = False,
+ fullyQualified = True, showFullyQualified = False)
+ if dlg.ShowModal() != wx.ID_OK:
+ dlg.Destroy()
+ return
+
+ layers = dlg.GetMapLayers()
+ for layer in layers:
+ if layer not in self.GetLayers():
+ self.layerBox.Append(layer)
+ self.groupChanged = True
+
+
+ def OnRemoveLayer(self, event):
+ """!Remove layer from listbox"""
+ while self.layerBox.GetSelections():
+ sel = self.layerBox.GetSelections()[0]
+ self.layerBox.Delete(sel)
+ self.groupChanged = True
+
+ def GetLayers(self):
+ """!Get layers"""
+ return self.layerBox.GetItems()
+
+ def OnGroupSelected(self, event):
+ """!Text changed in group selector"""
+ # callAfter must be called to close popup before other actions
+ wx.CallAfter(self.GroupSelected)
+
+ def GroupSelected(self):
+ """!Group was selected, check if changes were apllied"""
+ group = self.GetSelectedGroup()
+ if self.groupChanged:
+ dlg = wx.MessageDialog(self, message = _("Group <%s> was changed, "
+ "do you want to apply changes?") % self.currentGroup,
+ caption = _("Unapplied changes"),
+ style = wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT)
+ if dlg.ShowModal() == wx.ID_YES:
+ self.ApplyChanges(showResult = True)
+
+ dlg.Destroy()
+
+
+
+ groups = self.GetExistGroups()
+ if group in groups:
+ self.ShowGroupLayers(self.GetGroupLayers(group))
+
+ self.currentGroup = group
+ self.groupChanged = False
+
+ self.ClearNotification()
+
+ def ShowGroupLayers(self, mapList):
+ """!Show map layers in currently selected group"""
+ self.layerBox.Set(mapList)
+
+
+ def EditGroup(self, group):
+ """!Edit selected group"""
+ layersNew = self.GetLayers()
+ layersOld = self.GetGroupLayers(group)
+
+ add = []
+ remove = []
+ for layerNew in layersNew:
+ if layerNew not in layersOld:
+ add.append(layerNew)
+
+ for layerOld in layersOld:
+ if layerOld not in layersNew:
+ remove.append(layerOld)
+
+ ret = None
+ if remove:
+ ret = RunCommand('i.group',
+ parent = self,
+ group = group,
+ flags = 'r',
+ input = ','.join(remove))
+
+ if 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 = RunCommand('i.group',
+ parent = self,
+ group = group,
+ input = layers)
+ return ret
+
+
+ def GetExistGroups(self):
+ """!Returns existing groups in current mapset"""
+ return grass.list_grouped('group')[grass.gisenv()['MAPSET']]
+
+ def ShowResult(self, group, returnCode, create):
+ """!Show if operation was successfull."""
+ group += '@' + grass.gisenv()['MAPSET']
+ if returnCode is None:
+ label = _("No changes to apply in group <%s>.") % group
+ elif returnCode == 0:
+ if create:
+ label = _("Group <%s> was successfully created.") % group
+ else:
+ label = _("Group <%s> was successfully changed.") % group
+ else:
+ if create:
+ label = _("Creating of new group <%s> failed.") % group
+ else:
+ label = _("Changing of group <%s> failed.") % group
+
+ self.infoLabel.SetLabel(label)
+ wx.FutureCall(4000, self.ClearNotification)
+
+ def GetSelectedGroup(self):
+ """!Return currently selected group (without mapset)"""
+ return self.groupSelect.GetValue().split('@')[0]
+
+ def GetGroupLayers(self, group):
+ """!Get layers in group"""
+ res = RunCommand('i.group',
+ parent = self,
+ flags = 'g',
+ group = group,
+ read = True).strip()
+ if res.split('\n')[0]:
+ return res.split('\n')
+ return []
+
+ def ClearNotification(self):
+ """!Clear notification string"""
+ self.infoLabel.SetLabel("")
+
+ def ApplyChanges(self, showResult):
+ """!Create or edit group"""
+ group = self.currentGroup
+ if not group:
+ GMessage(parent = self,
+ message = _("No group selected."))
+ return False
+
+ groups = self.GetExistGroups()
+ if group in groups:
+ ret = self.EditGroup(group)
+ self.ShowResult(group = group, returnCode = ret, create = False)
+
+ else:
+ ret = self.CreateNewGroup(group)
+ self.ShowResult(group = group, returnCode = ret, create = True)
+
+ self.groupChanged = False
+
+ return True
+
+ def OnApply(self, event):
+ """!Apply changes"""
+ self.ApplyChanges(showResult = True)
+
+ def OnOk(self, event):
+ """!Apply changes and close dialog"""
+ if self.ApplyChanges(showResult = False):
+ self.OnClose(event)
+
+ def OnClose(self, event):
+ """!Close dialog"""
+ if not self.IsModal():
+ self.Destroy()
+ event.Skip()
+
+class MapLayersDialog(wx.Dialog):
+ def __init__(self, parent, title, modeler = False,
+ mapType = None, selectAll = True, fullyQualified = True, showFullyQualified = True,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+ """!Dialog for selecting map layers (raster, vector)
+
+ @param mapType type of map (if None: raster, vector, 3d raster, if one only: selects it and disables selection)
+ @param selectAll all/none maps should be selected by default
+ @param fullyQualified True if dialog should return full map names by default
+ @param showFullyQualified True to show 'fullyQualified' checkbox, otherwise hide it
+ """
+ wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+ style = style, **kwargs)
+
+ self.parent = parent # GMFrame or ?
+ self.mapType = mapType
+ self.selectAll = selectAll
+
+ # dialog body
+ self.bodySizer = self._createDialogBody()
+ # update list of layer to be loaded
+ self.map_layers = [] # list of map layers (full list type/mapset)
+ self.LoadMapLayers(self.GetLayerType(cmd = True),
+ self.mapset.GetStringSelection())
+
+ self.fullyQualified = wx.CheckBox(parent = self, id = wx.ID_ANY,
+ label = _("Use fully-qualified map names"))
+ self.fullyQualified.SetValue(fullyQualified)
+ self.fullyQualified.Show(showFullyQualified)
+
+ self.dseries = None
+ if modeler:
+ self.dseries = wx.CheckBox(parent = self, id = wx.ID_ANY,
+ label = _("Dynamic series (%s)") % 'g.mlist')
+ self.dseries.SetValue(False)
+
+ # buttons
+ btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+ btnOk = wx.Button(parent = self, id = wx.ID_OK)
+ btnOk.SetDefault()
+
+ # sizers & do layout
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(btnCancel)
+ btnSizer.AddButton(btnOk)
+ btnSizer.Realize()
+
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ mainSizer.Add(item = self.bodySizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+ mainSizer.Add(item = self.fullyQualified, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+ if self.dseries:
+ mainSizer.Add(item = self.dseries, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+
+ mainSizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ # set dialog min size
+ self.SetMinSize(self.GetSize())
+
+ def _createDialogBody(self):
+ bodySizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+ bodySizer.AddGrowableCol(1)
+ bodySizer.AddGrowableRow(3)
+
+ # layer type
+ bodySizer.Add(item = wx.StaticText(parent = self, label = _("Map type:")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (0,0))
+
+ self.layerType = wx.Choice(parent = self, id = wx.ID_ANY,
+ choices = [_('raster'), _('3D raster'), _('vector')], size = (100,-1))
+
+ if self.mapType:
+ self.layerType.SetStringSelection(self.mapType)
+ self.layerType.Disable()
+ else:
+ self.layerType.SetSelection(0)
+
+ bodySizer.Add(item = self.layerType,
+ pos = (0,1))
+
+ # select toggle
+ self.toggle = wx.CheckBox(parent = self, id = wx.ID_ANY,
+ label = _("Select toggle"))
+ self.toggle.SetValue(self.selectAll)
+ bodySizer.Add(item = self.toggle,
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (0,2))
+
+ # mapset filter
+ bodySizer.Add(item = wx.StaticText(parent = self, label = _("Mapset:")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (1,0))
+
+ self.mapset = MapsetSelect(parent = self, searchPath = True)
+ self.mapset.SetStringSelection(grass.gisenv()['MAPSET'])
+ bodySizer.Add(item = self.mapset,
+ pos = (1,1), span = (1, 2))
+
+ # map name filter
+ bodySizer.Add(item = wx.StaticText(parent = self, label = _("Pattern:")),
+ flag = wx.ALIGN_CENTER_VERTICAL,
+ pos = (2,0))
+
+ self.filter = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+ value = "",
+ size = (250,-1))
+ bodySizer.Add(item = self.filter,
+ flag = wx.EXPAND,
+ pos = (2,1), span = (1, 2))
+
+ # layer list
+ bodySizer.Add(item = wx.StaticText(parent = self, label = _("List of maps:")),
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_TOP,
+ pos = (3,0))
+ self.layers = wx.CheckListBox(parent = self, id = wx.ID_ANY,
+ size = (250, 100),
+ choices = [])
+ bodySizer.Add(item = self.layers,
+ flag = wx.EXPAND,
+ pos = (3,1), span = (1, 2))
+
+ # bindings
+ self.layerType.Bind(wx.EVT_CHOICE, self.OnChangeParams)
+ self.mapset.Bind(wx.EVT_COMBOBOX, self.OnChangeParams)
+ self.layers.Bind(wx.EVT_RIGHT_DOWN, self.OnMenu)
+ self.filter.Bind(wx.EVT_TEXT, self.OnFilter)
+ self.toggle.Bind(wx.EVT_CHECKBOX, self.OnToggle)
+
+ return bodySizer
+
+ def LoadMapLayers(self, type, mapset):
+ """!Load list of map layers
+
+ @param type layer type ('raster' or 'vector')
+ @param mapset mapset name
+ """
+ self.map_layers = grass.mlist_grouped(type = type)[mapset]
+ self.layers.Set(self.map_layers)
+
+ # check all items by default
+ for item in range(self.layers.GetCount()):
+
+ self.layers.Check(item, check = self.selectAll)
+
+ def OnChangeParams(self, event):
+ """!Filter parameters changed by user"""
+ # update list of layer to be loaded
+ self.LoadMapLayers(self.GetLayerType(cmd = True),
+ self.mapset.GetStringSelection())
+
+ event.Skip()
+
+ def OnMenu(self, event):
+ """!Table description area, context menu"""
+ if not hasattr(self, "popupID1"):
+ self.popupDataID1 = wx.NewId()
+ self.popupDataID2 = wx.NewId()
+ self.popupDataID3 = wx.NewId()
+
+ self.Bind(wx.EVT_MENU, self.OnSelectAll, id = self.popupDataID1)
+ self.Bind(wx.EVT_MENU, self.OnSelectInvert, id = self.popupDataID2)
+ self.Bind(wx.EVT_MENU, self.OnDeselectAll, id = self.popupDataID3)
+
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupDataID1, _("Select all"))
+ menu.Append(self.popupDataID2, _("Invert selection"))
+ menu.Append(self.popupDataID3, _("Deselect all"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def OnSelectAll(self, event):
+ """!Select all map layer from list"""
+ for item in range(self.layers.GetCount()):
+ self.layers.Check(item, True)
+
+ def OnSelectInvert(self, event):
+ """!Invert current selection"""
+ for item in range(self.layers.GetCount()):
+ if self.layers.IsChecked(item):
+ self.layers.Check(item, False)
+ else:
+ self.layers.Check(item, True)
+
+ def OnDeselectAll(self, event):
+ """!Select all map layer from list"""
+ for item in range(self.layers.GetCount()):
+ self.layers.Check(item, False)
+
+ def OnFilter(self, event):
+ """!Apply filter for map names"""
+ if len(event.GetString()) == 0:
+ self.layers.Set(self.map_layers)
+ return
+
+ list = []
+ for layer in self.map_layers:
+ try:
+ if re.compile('^' + event.GetString()).search(layer):
+ list.append(layer)
+ except:
+ pass
+
+ self.layers.Set(list)
+ self.OnSelectAll(None)
+
+ event.Skip()
+
+ def OnToggle(self, event):
+ """!Select toggle (check or uncheck all layers)"""
+ check = event.Checked()
+ for item in range(self.layers.GetCount()):
+ self.layers.Check(item, check)
+
+ event.Skip()
+
+ def GetMapLayers(self):
+ """!Return list of checked map layers"""
+ layerNames = []
+ for indx in self.layers.GetSelections():
+ # layers.append(self.layers.GetStringSelec(indx))
+ pass
+
+ fullyQualified = self.fullyQualified.IsChecked()
+ mapset = self.mapset.GetStringSelection()
+ for item in range(self.layers.GetCount()):
+ if not self.layers.IsChecked(item):
+ continue
+ if fullyQualified:
+ layerNames.append(self.layers.GetString(item) + '@' + mapset)
+ else:
+ layerNames.append(self.layers.GetString(item))
+
+ return layerNames
+
+ def GetLayerType(self, cmd = False):
+ """!Get selected layer type
+
+ @param cmd True for g.mlist
+ """
+ if not cmd:
+ return self.layerType.GetStringSelection()
+
+ sel = self.layerType.GetSelection()
+ if sel == 0:
+ ltype = 'rast'
+ elif sel == 1:
+ ltype = 'rast3d'
+ else:
+ ltype = 'vect'
+
+ return ltype
+
+ def GetDSeries(self):
+ """!Used by modeler only
+
+ @return g.mlist command
+ """
+ if not self.dseries or not self.dseries.IsChecked():
+ return ''
+
+ cond = 'map in `g.mlist type=%s ' % self.GetLayerType(cmd = True)
+ patt = self.filter.GetValue()
+ if patt:
+ cond += 'pattern=%s ' % patt
+ cond += 'mapset=%s`' % self.mapset.GetStringSelection()
+
+ return cond
+
+class ImportDialog(wx.Dialog):
+ """!Dialog for bulk import of various data (base class)"""
+ def __init__(self, parent, itype,
+ id = wx.ID_ANY, title = _("Multiple import"),
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+ self.parent = parent # GMFrame
+ self.importType = itype
+ self.options = dict() # list of options
+
+ self.commandId = -1 # id of running command
+
+ wx.Dialog.__init__(self, parent, id, title, style = style,
+ name = "MultiImportDialog")
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ self.layerBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = _(" List of %s layers ") % self.importType.upper())
+
+ #
+ # list of layers
+ #
+ columns = [_('Layer id'),
+ _('Layer name'),
+ _('Name for GRASS map (editable)')]
+ if itype == 'ogr':
+ columns.insert(2, _('Feature type'))
+ self.list = LayersList(parent = self.panel, columns = columns)
+ self.list.LoadData()
+
+ self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = "%s" % _("Options"))
+
+ cmd = self._getCommand()
+ task = gtask.parse_interface(cmd)
+ for f in task.get_options()['flags']:
+ name = f.get('name', '')
+ desc = f.get('label', '')
+ if not desc:
+ desc = f.get('description', '')
+ if not name and not desc:
+ continue
+ if cmd == 'r.in.gdal' and name not in ('o', 'e', 'l', 'k'):
+ continue
+ elif cmd == 'r.external' and name not in ('o', 'e', 'r', 'h', 'v'):
+ continue
+ elif cmd == 'v.in.ogr' and name not in ('c', 'z', 't', 'o', 'r', 'e', 'w'):
+ continue
+ elif cmd == 'v.external' and name not in ('b'):
+ continue
+ elif cmd == 'v.in.dxf' and name not in ('e', 't', 'b', 'f', 'i'):
+ continue
+ self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = desc)
+
+
+ self.overwrite = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = _("Allow output files to overwrite existing files"))
+ self.overwrite.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
+
+ self.add = wx.CheckBox(parent = self.panel, id = wx.ID_ANY)
+
+ #
+ # buttons
+ #
+ # cancel
+ self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+ self.btn_cancel.SetToolTipString(_("Close dialog"))
+ self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+ # run
+ self.btn_run = wx.Button(parent = self.panel, id = wx.ID_OK, label = _("&Import"))
+ self.btn_run.SetToolTipString(_("Import selected layers"))
+ self.btn_run.SetDefault()
+ self.btn_run.Enable(False)
+ self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
+ # run command dialog
+ self.btn_cmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Command dialog"))
+ self.btn_cmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+
+ def doLayout(self):
+ """!Do layout"""
+ dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+ # dsn input
+ dialogSizer.Add(item = self.dsnInput, proportion = 0,
+ flag = wx.EXPAND)
+
+ #
+ # list of DXF layers
+ #
+ layerSizer = wx.StaticBoxSizer(self.layerBox, wx.HORIZONTAL)
+
+ layerSizer.Add(item = self.list, proportion = 1,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+
+ dialogSizer.Add(item = layerSizer, proportion = 1,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+ # options
+ optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
+ for key in self.options.keys():
+ optionSizer.Add(item = self.options[key], proportion = 0)
+
+ dialogSizer.Add(item = optionSizer, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+ dialogSizer.Add(item = self.overwrite, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+ dialogSizer.Add(item = self.add, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+ #
+ # buttons
+ #
+ btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+
+ btnsizer.Add(item = self.btn_cmd, proportion = 0,
+ flag = wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+
+ btnsizer.Add(item = self.btn_cancel, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+
+ btnsizer.Add(item = self.btn_run, proportion = 0,
+ flag = wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+
+ dialogSizer.Add(item = btnsizer, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.ALIGN_RIGHT,
+ border = 10)
+
+ # dialogSizer.SetSizeHints(self.panel)
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizer(dialogSizer)
+ dialogSizer.Fit(self.panel)
+
+ # auto-layout seems not work here - FIXME
+ size = wx.Size(globalvar.DIALOG_GSELECT_SIZE[0] + 225, 550)
+ self.SetMinSize(size)
+ self.SetSize((size.width, size.height + 100))
+ # width = self.GetSize()[0]
+ # self.list.SetColumnWidth(col = 1, width = width / 2 - 50)
+ self.Layout()
+
+ def _getCommand(self):
+ """!Get command"""
+ return ''
+
+ def OnCancel(self, event = None):
+ """!Close dialog"""
+ self.Close()
+
+ def OnRun(self, event):
+ """!Import/Link data (each layes as separate vector map)"""
+ pass
+
+ def OnCmdDialog(self, event):
+ """!Show command dialog"""
+ pass
+
+ def AddLayers(self, returncode, cmd = None):
+ """!Add imported/linked layers into layer tree"""
+ self.commandId += 1
+
+ if not self.add.IsChecked() or returncode != 0:
+ return
+
+ maptree = self.parent.curr_page.maptree
+
+ layer, output = self.list.GetLayers()[self.commandId]
+
+ if '@' not in output:
+ name = output + '@' + grass.gisenv()['MAPSET']
+ else:
+ name = output
+
+ # add imported layers into layer tree
+ if self.importType == 'gdal':
+ cmd = ['d.rast',
+ 'map=%s' % name]
+ if UserSettings.Get(group = 'cmd', key = 'rasterOverlay', subkey = 'enabled'):
+ cmd.append('-o')
+
+ item = maptree.AddLayer(ltype = 'raster',
+ lname = name, lchecked = False,
+ lcmd = cmd)
+ else:
+ item = maptree.AddLayer(ltype = 'vector',
+ lname = name, lchecked = False,
+ lcmd = ['d.vect',
+ 'map=%s' % name])
+
+ maptree.mapdisplay.MapWindow.ZoomToMap()
+
+ def OnAbort(self, event):
+ """!Abort running import
+
+ @todo not yet implemented
+ """
+ pass
+
+class GdalImportDialog(ImportDialog):
+ def __init__(self, parent, ogr = False, link = False):
+ """!Dialog for bulk import of various raster/vector data
+
+ @param parent parent window
+ @param ogr True for OGR (vector) otherwise GDAL (raster)
+ @param link True for linking data otherwise importing data
+ """
+ self.link = link
+ self.ogr = ogr
+
+ if ogr:
+ ImportDialog.__init__(self, parent, itype = 'ogr')
+ if link:
+ self.SetTitle(_("Link external vector data"))
+ else:
+ self.SetTitle(_("Import vector data"))
+ else:
+ ImportDialog.__init__(self, parent, itype = 'gdal')
+ if link:
+ self.SetTitle(_("Link external raster data"))
+ else:
+ self.SetTitle(_("Import raster data"))
+
+ self.dsnInput = GdalSelect(parent = self, panel = self.panel, ogr = ogr)
+
+ if link:
+ self.add.SetLabel(_("Add linked layers into layer tree"))
+ else:
+ self.add.SetLabel(_("Add imported layers into layer tree"))
+
+ self.add.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+
+ if link:
+ self.btn_run.SetLabel(_("&Link"))
+ self.btn_run.SetToolTipString(_("Link selected layers"))
+ if ogr:
+ self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'v.external')
+ else:
+ self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'r.external')
+ else:
+ self.btn_run.SetLabel(_("&Import"))
+ self.btn_run.SetToolTipString(_("Import selected layers"))
+ if ogr:
+ self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'v.in.ogr')
+ else:
+ self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'r.in.gdal')
+
+ self.doLayout()
+
+ def OnRun(self, event):
+ """!Import/Link data (each layes as separate vector map)"""
+ data = self.list.GetLayers()
+
+ # hide dialog
+ self.Hide()
+
+ dsn = self.dsnInput.GetDsn()
+ ext = self.dsnInput.GetFormatExt()
+
+ for layer, output in data:
+ if self.importType == 'ogr':
+ if ext and layer.rfind(ext) > -1:
+ layer = layer.replace('.' + ext, '')
+ if self.link:
+ cmd = ['v.external',
+ 'dsn=%s' % dsn,
+ 'output=%s' % output,
+ 'layer=%s' % layer]
+ else:
+ cmd = ['v.in.ogr',
+ 'dsn=%s' % dsn,
+ 'layer=%s' % layer,
+ 'output=%s' % output]
+ else: # gdal
+ if self.dsnInput.GetType() == 'dir':
+ idsn = os.path.join(dsn, layer)
+ else:
+ idsn = dsn
+
+ if self.link:
+ cmd = ['r.external',
+ 'input=%s' % idsn,
+ 'output=%s' % output]
+ else:
+ cmd = ['r.in.gdal',
+ 'input=%s' % idsn,
+ 'output=%s' % output]
+
+ if self.overwrite.IsChecked():
+ cmd.append('--overwrite')
+
+ for key in self.options.keys():
+ if self.options[key].IsChecked():
+ cmd.append('-%s' % key)
+
+ if UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'):
+ cmd.append('--overwrite')
+
+ # run in Layer Manager
+ self.parent.goutput.RunCmd(cmd, switchPage = True,
+ onDone = self.AddLayers)
+
+ self.OnCancel()
+
+ def _getCommand(self):
+ """!Get command"""
+ if self.link:
+ if self.ogr:
+ return 'v.external'
+ else:
+ return 'r.external'
+ else:
+ if self.ogr:
+ return 'v.in.ogr'
+ else:
+ return 'r.in.gdal'
+
+ return ''
+
+ def OnCmdDialog(self, event):
+ """!Show command dialog"""
+ name = self._getCommand()
+ GUI(parent = self, modal = False).ParseCommand(cmd = [name])
+
+class GdalOutputDialog(wx.Dialog):
+ def __init__(self, parent, id = wx.ID_ANY, ogr = False,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, *kwargs):
+ """!Dialog for setting output format for rasters/vectors
+
+ @param parent parent window
+ @param id window id
+ @param ogr True for OGR (vector) otherwise GDAL (raster)
+ @param style window style
+ @param *kwargs other wx.Dialog's arguments
+ """
+ self.parent = parent # GMFrame
+ self.ogr = ogr
+ wx.Dialog.__init__(self, parent, id = id, style = style, *kwargs)
+ if self.ogr:
+ self.SetTitle(_("Define output format for vector data"))
+ else:
+ self.SetTitle(_("Define output format for raster data"))
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ # buttons
+ self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Command dialog"))
+ self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+ self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+ self.btnCancel.SetToolTipString(_("Close dialog"))
+ self.btnOk = wx.Button(parent = self.panel, id = wx.ID_OK)
+ self.btnOk.SetToolTipString(_("Set external format and close dialog"))
+ self.btnOk.SetDefault()
+ self.btnOk.Enable(False)
+
+ 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)
+
+ self._layout()
+
+ def _layout(self):
+ dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+ dialogSizer.Add(item = self.dsnInput, proportion = 0,
+ flag = wx.EXPAND)
+
+ btnSizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+ btnSizer.Add(item = self.btnCmd, proportion = 0,
+ flag = wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+ btnSizer.Add(item = self.btnCancel, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+ btnSizer.Add(item = self.btnOk, proportion = 0,
+ flag = wx.RIGHT | wx.ALIGN_CENTER,
+ border = 10)
+
+ dialogSizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP | wx.ALIGN_RIGHT,
+ border = 10)
+
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizer(dialogSizer)
+ dialogSizer.Fit(self.panel)
+
+ size = wx.Size(globalvar.DIALOG_GSELECT_SIZE[0] + 225, self.GetBestSize()[1])
+ self.SetMinSize(size)
+ self.SetSize((size.width, size.height))
+ self.Layout()
+
+ def OnCmdDialog(self, event):
+ 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':
+ RunCommand('v.external.out',
+ parent = self,
+ flags = 'r')
+ else:
+ dsn = self.dsnInput.GetDsn()
+ frmt = self.dsnInput.GetFormat()
+ options = self.dsnInput.GetOptions()
+
+ RunCommand('v.external.out',
+ parent = self,
+ dsn = dsn, format = frmt,
+ options = options)
+ self.Close()
+
+class DxfImportDialog(ImportDialog):
+ """!Dialog for bulk import of DXF layers"""
+ def __init__(self, parent):
+ ImportDialog.__init__(self, parent, itype = 'dxf',
+ title = _("Import DXF layers"))
+
+ self.dsnInput = filebrowse.FileBrowseButton(parent = self.panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+ dialogTitle = _('Choose DXF file to import'),
+ buttonText = _('Browse'),
+ startDirectory = os.getcwd(), fileMode = 0,
+ changeCallback = self.OnSetDsn,
+ fileMask = "DXF File (*.dxf)|*.dxf")
+
+ self.add.SetLabel(_("Add imported layers into layer tree"))
+
+ self.add.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+
+ self.doLayout()
+
+ def _getCommand(self):
+ """!Get command"""
+ return 'v.in.dxf'
+
+ def OnRun(self, event):
+ """!Import/Link data (each layes as separate vector map)"""
+ data = self.list.GetLayers()
+
+ # hide dialog
+ self.Hide()
+
+ inputDxf = self.dsnInput.GetValue()
+
+ for layer, output in data:
+ cmd = ['v.in.dxf',
+ 'input=%s' % inputDxf,
+ 'layers=%s' % layer,
+ 'output=%s' % output]
+
+ for key in self.options.keys():
+ if self.options[key].IsChecked():
+ cmd.append('-%s' % key)
+
+ if self.overwrite.IsChecked() or \
+ UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'):
+ cmd.append('--overwrite')
+
+ # run in Layer Manager
+ self.parent.goutput.RunCmd(cmd, switchPage = True,
+ onDone = self.AddLayers)
+
+ self.OnCancel()
+
+ def OnSetDsn(self, event):
+ """!Input DXF file defined, update list of layer widget"""
+ path = event.GetString()
+ if not path:
+ return
+
+ data = list()
+ 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)
+ return
+
+ for line in ret.splitlines():
+ layerId = line.split(':')[0].split(' ')[1]
+ layerName = line.split(':')[1].strip()
+ grassName = GetValidLayerName(layerName)
+ data.append((layerId, layerName.strip(), grassName.strip()))
+
+ self.list.LoadData(data)
+ if len(data) > 0:
+ self.btn_run.Enable(True)
+ else:
+ self.btn_run.Enable(False)
+
+ def OnCmdDialog(self, event):
+ """!Show command dialog"""
+ GUI(parent = self, modal = True).ParseCommand(cmd = ['v.in.dxf'])
+
+class LayersList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,
+ listmix.CheckListCtrlMixin, listmix.TextEditMixin):
+ """!List of layers to be imported (dxf, shp...)"""
+ def __init__(self, parent, columns, log = None):
+ self.parent = parent
+
+ 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)
+ listmix.TextEditMixin.__init__(self)
+
+ for i in range(len(columns)):
+ self.InsertColumn(i, columns[i])
+
+ if len(columns) == 3:
+ width = (65, 200)
+ else:
+ width = (65, 180, 110)
+
+ for i in range(len(width)):
+ self.SetColumnWidth(col = i, width = width[i])
+
+ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnPopupMenu) #wxMSW
+ self.Bind(wx.EVT_RIGHT_UP, self.OnPopupMenu) #wxGTK
+
+ def LoadData(self, data = None):
+ """!Load data into list"""
+ if data is None:
+ return
+
+ self.DeleteAllItems()
+ for item in data:
+ index = self.InsertStringItem(sys.maxint, str(item[0]))
+ for i in range(1, len(item)):
+ self.SetStringItem(index, i, "%s" % str(item[i]))
+
+ # check by default only on one item
+ if len(data) == 1:
+ self.CheckItem(index, True)
+
+ def OnPopupMenu(self, event):
+ """!Show popup menu"""
+ if self.GetItemCount() < 1:
+ return
+
+ if not hasattr(self, "popupDataID1"):
+ self.popupDataID1 = wx.NewId()
+ self.popupDataID2 = wx.NewId()
+
+ self.Bind(wx.EVT_MENU, self.OnSelectAll, id = self.popupDataID1)
+ self.Bind(wx.EVT_MENU, self.OnSelectNone, id = self.popupDataID2)
+
+ # generate popup-menu
+ menu = wx.Menu()
+ menu.Append(self.popupDataID1, _("Select all"))
+ menu.Append(self.popupDataID2, _("Deselect all"))
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def OnSelectAll(self, event):
+ """!Select all items"""
+ item = -1
+
+ while True:
+ item = self.GetNextItem(item)
+ if item == -1:
+ break
+ self.CheckItem(item, True)
+
+ event.Skip()
+
+ def OnSelectNone(self, event):
+ """!Deselect items"""
+ item = -1
+
+ while True:
+ item = self.GetNextItem(item, wx.LIST_STATE_SELECTED)
+ if item == -1:
+ break
+ self.CheckItem(item, False)
+
+ event.Skip()
+
+ def OnLeftDown(self, event):
+ """!Allow editing only output name
+
+ Code taken from TextEditMixin class.
+ """
+ x, y = event.GetPosition()
+
+ colLocs = [0]
+ loc = 0
+ for n in range(self.GetColumnCount()):
+ loc = loc + self.GetColumnWidth(n)
+ colLocs.append(loc)
+
+ col = bisect(colLocs, x + self.GetScrollPos(wx.HORIZONTAL)) - 1
+
+ if col == self.GetColumnCount() - 1:
+ listmix.TextEditMixin.OnLeftDown(self, event)
+ else:
+ event.Skip()
+
+ def GetLayers(self):
+ """!Get list of layers (layer name, output name)"""
+ data = []
+ item = -1
+ while True:
+ item = self.GetNextItem(item)
+ if item == -1:
+ break
+ if not self.IsChecked(item):
+ continue
+ # layer / output name
+ data.append((self.GetItem(item, 1).GetText(),
+ self.GetItem(item, self.GetColumnCount() - 1).GetText()))
+
+ return data
+
+class SetOpacityDialog(wx.Dialog):
+ """!Set opacity of map layers"""
+ def __init__(self, parent, id = wx.ID_ANY, title = _("Set Map Layer Opacity"),
+ size = wx.DefaultSize, pos = wx.DefaultPosition,
+ style = wx.DEFAULT_DIALOG_STYLE, opacity = 100):
+
+ self.parent = parent # GMFrame
+ self.opacity = opacity # current opacity
+
+ super(SetOpacityDialog, self).__init__(parent, id = id, pos = pos,
+ size = size, style = style, title = title)
+
+ panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ box = wx.GridBagSizer(vgap = 5, hgap = 5)
+ self.value = wx.Slider(panel, id = wx.ID_ANY, value = self.opacity,
+ style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
+ wx.SL_TOP | wx.SL_LABELS,
+ minValue = 0, maxValue = 100,
+ size = (350, -1))
+
+ box.Add(item = self.value,
+ flag = wx.ALIGN_CENTRE, pos = (0, 0), span = (1, 2))
+ box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = _("transparent")),
+ pos = (1, 0))
+ box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = _("opaque")),
+ flag = wx.ALIGN_RIGHT,
+ pos = (1, 1))
+
+ sizer.Add(item = box, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+ line = wx.StaticLine(parent = panel, id = wx.ID_ANY,
+ style = wx.LI_HORIZONTAL)
+ sizer.Add(item = line, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+ # buttons
+ btnsizer = wx.StdDialogButtonSizer()
+
+ btnOK = wx.Button(parent = panel, id = wx.ID_OK)
+ btnOK.SetDefault()
+ btnsizer.AddButton(btnOK)
+
+ btnCancel = wx.Button(parent = panel, id = wx.ID_CANCEL)
+ btnsizer.AddButton(btnCancel)
+ btnsizer.Realize()
+
+ sizer.Add(item = btnsizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+ panel.SetSizer(sizer)
+ sizer.Fit(panel)
+
+ self.SetSize(self.GetBestSize())
+
+ self.Layout()
+
+ def GetOpacity(self):
+ """!Button 'OK' pressed"""
+ # return opacity value
+ opacity = float(self.value.GetValue()) / 100
+ return opacity
+
+def GetImageHandlers(image):
+ """!Get list of supported image handlers"""
+ lext = list()
+ ltype = list()
+ for h in image.GetHandlers():
+ lext.append(h.GetExtension())
+
+ filetype = ''
+ if 'png' in lext:
+ filetype += "PNG file (*.png)|*.png|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_PNG,
+ 'ext' : 'png' })
+ filetype += "BMP file (*.bmp)|*.bmp|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_BMP,
+ 'ext' : 'bmp' })
+ if 'gif' in lext:
+ filetype += "GIF file (*.gif)|*.gif|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_GIF,
+ 'ext' : 'gif' })
+
+ if 'jpg' in lext:
+ filetype += "JPG file (*.jpg)|*.jpg|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_JPEG,
+ 'ext' : 'jpg' })
+
+ if 'pcx' in lext:
+ filetype += "PCX file (*.pcx)|*.pcx|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_PCX,
+ 'ext' : 'pcx' })
+
+ if 'pnm' in lext:
+ filetype += "PNM file (*.pnm)|*.pnm|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_PNM,
+ 'ext' : 'pnm' })
+
+ if 'tif' in lext:
+ filetype += "TIF file (*.tif)|*.tif|"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_TIF,
+ 'ext' : 'tif' })
+
+ if 'xpm' in lext:
+ filetype += "XPM file (*.xpm)|*.xpm"
+ ltype.append({ 'type' : wx.BITMAP_TYPE_XPM,
+ 'ext' : 'xpm' })
+
+ return filetype, ltype
+
+class StaticWrapText(wx.StaticText):
+ """!A Static Text field that wraps its text to fit its width,
+ enlarging its height if necessary.
+ """
+ def __init__(self, parent, id = wx.ID_ANY, label = '', *args, **kwds):
+ self.parent = parent
+ self.originalLabel = label
+
+ wx.StaticText.__init__(self, parent, id, label = '', *args, **kwds)
+
+ self.SetLabel(label)
+ self.Bind(wx.EVT_SIZE, self.OnResize)
+
+ def SetLabel(self, label):
+ self.originalLabel = label
+ self.wrappedSize = None
+ self.OnResize(None)
+
+ def OnResize(self, event):
+ if not getattr(self, "resizing", False):
+ self.resizing = True
+ newSize = wx.Size(self.parent.GetSize().width - 50,
+ self.GetSize().height)
+ if self.wrappedSize != newSize:
+ wx.StaticText.SetLabel(self, self.originalLabel)
+ self.Wrap(newSize.width)
+ self.wrappedSize = newSize
+
+ self.SetSize(self.wrappedSize)
+ del self.resizing
+
+class ImageSizeDialog(wx.Dialog):
+ """!Set size for saved graphic file"""
+ def __init__(self, parent, id = wx.ID_ANY, title = _("Set image size"),
+ style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+ self.parent = parent
+
+ wx.Dialog.__init__(self, parent, id = id, style = style, title = title, **kwargs)
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ self.box = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = ' % s' % _("Image size"))
+
+ size = self.parent.GetWindow().GetClientSize()
+ self.width = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY,
+ style = wx.SP_ARROW_KEYS)
+ self.width.SetRange(20, 1e6)
+ self.width.SetValue(size.width)
+ wx.CallAfter(self.width.SetFocus)
+ self.height = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY,
+ style = wx.SP_ARROW_KEYS)
+ self.height.SetRange(20, 1e6)
+ self.height.SetValue(size.height)
+ self.template = wx.Choice(parent = self.panel, id = wx.ID_ANY,
+ size = (125, -1),
+ choices = [ "",
+ "640x480",
+ "800x600",
+ "1024x768",
+ "1280x960",
+ "1600x1200",
+ "1920x1440" ])
+
+ self.btnOK = wx.Button(parent = self.panel, id = wx.ID_OK)
+ self.btnOK.SetDefault()
+ self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+
+ self.template.Bind(wx.EVT_CHOICE, self.OnTemplate)
+
+ self._layout()
+ self.SetSize(self.GetBestSize())
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # body
+ box = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
+ fbox = wx.FlexGridSizer(cols = 2, vgap = 5, hgap = 5)
+ fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Width:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ fbox.Add(item = self.width)
+ fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Height:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ fbox.Add(item = self.height)
+ fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+ label = _("Template:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ fbox.Add(item = self.template)
+
+ box.Add(item = fbox, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+ sizer.Add(item = box, proportion = 1,
+ flag=wx.EXPAND | wx.ALL, border = 3)
+
+ # buttons
+ btnsizer = wx.StdDialogButtonSizer()
+ btnsizer.AddButton(self.btnOK)
+ btnsizer.AddButton(self.btnCancel)
+ btnsizer.Realize()
+
+ sizer.Add(item = btnsizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+ self.panel.SetSizer(sizer)
+ sizer.Fit(self.panel)
+ self.Layout()
+
+ def GetValues(self):
+ """!Get width/height values"""
+ return self.width.GetValue(), self.height.GetValue()
+
+ def OnTemplate(self, event):
+ """!Template selected"""
+ sel = event.GetString()
+ if not sel:
+ width, height = self.parent.GetWindow().GetClientSize()
+ else:
+ width, height = map(int, sel.split('x'))
+ self.width.SetValue(width)
+ self.height.SetValue(height)
+
+class SqlQueryFrame(wx.Frame):
+ def __init__(self, parent, id = wx.ID_ANY,
+ title = _("GRASS GIS SQL Query Utility"),
+ *kwargs):
+ """!SQL Query Utility window
+ """
+ self.parent = parent
+
+ wx.Frame.__init__(self, parent = parent, id = id, title = title, *kwargs)
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ self.sqlBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = _(" SQL statement "))
+ self.sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+ style = wx.TE_MULTILINE)
+
+ self.btnApply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
+ self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+ self.Bind(wx.EVT_BUTTON, self.OnCloseWindow, self.btnCancel)
+
+ self._layout()
+
+ self.SetMinSize(wx.Size(300, 150))
+ self.SetSize(wx.Size(500, 200))
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ sqlSizer = wx.StaticBoxSizer(self.sqlBox, wx.HORIZONTAL)
+ sqlSizer.Add(item = self.sql, proportion = 1,
+ flag = wx.EXPAND)
+
+ btnSizer = wx.StdDialogButtonSizer()
+ btnSizer.AddButton(self.btnApply)
+ btnSizer.AddButton(self.btnCancel)
+ btnSizer.Realize()
+
+ sizer.Add(item = sqlSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+ self.panel.SetSizer(sizer)
+
+ self.Layout()
+
+ def OnCloseWindow(self, event):
+ """!Close window
+ """
+ self.Close()
Copied: grass/trunk/gui/wxpython/gui_core/forms.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/menuform.py)
===================================================================
--- grass/trunk/gui/wxpython/gui_core/forms.py (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/forms.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,2268 @@
+"""
+ at package gui_core.forms
+
+ at brief Construct simple wxPython GUI from a GRASS command interface
+description.
+
+Classes:
+ - helpPanel
+ - mainFrame
+ - cmdPanel
+ - GrassGUIApp
+ - GUI
+ - FloatValidator
+ - GNotebook
+
+This program is just a coarse approach to automatically build a GUI
+from a xml-based GRASS user interface description.
+
+You need to have Python 2.4, wxPython 2.8 and python-xml.
+
+The XML stream is read from executing the command given in the
+command line, thus you may call it for instance this way:
+
+python <this file.py> r.basins.fill
+
+Or you set an alias or wrap the call up in a nice shell script, GUI
+environment ... please contribute your idea.
+
+Updated to wxPython 2.8 syntax and contrib widgets. Methods added to
+make it callable by gui. Method added to automatically re-run with
+pythonw on a Mac.
+
+ at todo
+ - verify option value types
+
+Copyright(C) 2000-2011 by the GRASS Development Team
+This program is free software under the GPL(>=v2) Read the file
+COPYING coming with GRASS for details.
+
+ at author Jan-Oliver Wagner <jan at intevation.de>
+ at author Bernhard Reiter <bernhard at intevation.de>
+ at author Michael Barton, Arizona State University
+ at author Daniel Calvelo <dca.gis at gmail.com>
+ at author Martin Landa <landa.martin at gmail.com>
+ at author Luca Delucchi <lucadeluge at gmail.com>
+"""
+
+import sys
+import re
+import string
+import textwrap
+import os
+import time
+import copy
+import locale
+from threading import Thread
+import Queue
+
+from core import globalvar
+import wx
+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.filebrowsebutton as filebrowse
+import wx.lib.scrolledpanel as scrolled
+from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+from wx.lib.newevent import NewEvent
+
+try:
+ import xml.etree.ElementTree as etree
+except ImportError:
+ import elementtree.ElementTree as etree # Python <= 2.4
+
+from gui_core.dialogs import StaticWrapText
+from gui_core.ghelp import HelpPanel
+
+gisbase = os.getenv("GISBASE")
+if gisbase is None:
+ print >>sys.stderr, "We don't seem to be properly installed, or we are being run outside GRASS. Expect glitches."
+ gisbase = os.path.join(os.path.dirname(sys.argv[0]), os.path.pardir)
+ wxbase = gisbase
+else:
+ wxbase = os.path.join(globalvar.ETCWXDIR)
+
+sys.path.append(wxbase)
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+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:
+ from compat import subprocess
+
+wxUpdateDialog, EVT_DIALOG_UPDATE = NewEvent()
+
+# From lib/gis/col_str.c, except purple which is mentioned
+# there but not given RGB values
+str2rgb = {'aqua': (100, 128, 255),
+ 'black': (0, 0, 0),
+ 'blue': (0, 0, 255),
+ 'brown': (180, 77, 25),
+ 'cyan': (0, 255, 255),
+ 'gray': (128, 128, 128),
+ 'green': (0, 255, 0),
+ 'grey': (128, 128, 128),
+ 'indigo': (0, 128, 255),
+ 'magenta': (255, 0, 255),
+ 'orange': (255, 128, 0),
+ 'purple': (128, 0, 128),
+ 'red': (255, 0, 0),
+ 'violet': (128, 0, 255),
+ 'white': (255, 255, 255),
+ 'yellow': (255, 255, 0)}
+rgb2str = {}
+for (s,r) in str2rgb.items():
+ rgb2str[ r ] = s
+
+"""!Hide some options in the GUI"""
+#_blackList = { 'enabled' : False,
+# 'items' : { 'r.buffer' : {'params' : ['input', 'output'],
+# 'flags' : ['z', 'overwrite']}}}
+_blackList = { 'enabled' : False,
+ 'items' : {} }
+
+def color_resolve(color):
+ if len(color) > 0 and color[0] in "0123456789":
+ rgb = tuple(map(int, color.split(':')))
+ label = color
+ else:
+ # Convert color names to RGB
+ try:
+ rgb = str2rgb[color]
+ label = color
+ except KeyError:
+ rgb = (200, 200, 200)
+ label = _('Select Color')
+ return (rgb, label)
+
+def text_beautify(someString , width = 70):
+ """!Make really long texts shorter, clean up whitespace and remove
+ trailing punctuation.
+ """
+ if width > 0:
+ return escape_ampersand(string.strip(
+ os.linesep.join(textwrap.wrap(utils.normalize_whitespace(someString), width)),
+ ".,;:"))
+ else:
+ return escape_ampersand(string.strip(utils.normalize_whitespace(someString), ".,;:"))
+
+def escape_ampersand(text):
+ """!Escapes ampersands with additional ampersand for GUI"""
+ return string.replace(text, "&", "&&")
+
+class UpdateThread(Thread):
+ """!Update dialog widgets in the thread"""
+ def __init__(self, parent, event, eventId, task):
+ Thread.__init__(self)
+
+ self.parent = parent
+ self.event = event
+ self.eventId = eventId
+ self.task = task
+ self.setDaemon(True)
+
+ # list of functions which updates the dialog
+ self.data = {}
+
+ def run(self):
+ # get widget id
+ if not self.eventId:
+ for p in self.task.params:
+ if p.get('gisprompt', False) == False:
+ continue
+ prompt = p.get('element', '')
+ if prompt == 'vector':
+ name = p.get('name', '')
+ if name in ('map', 'input'):
+ self.eventId = p['wxId'][0]
+ if self.eventId is None:
+ return
+
+ p = self.task.get_param(self.eventId, element = 'wxId', raiseError = False)
+ if not p or 'wxId-bind' not in p:
+ return
+
+ # get widget prompt
+ pType = p.get('prompt', '')
+ if not pType:
+ return
+
+ # check for map/input parameter
+ pMap = self.task.get_param('map', raiseError = False)
+
+ if not pMap:
+ pMap = self.task.get_param('input', raiseError = False)
+
+ if pMap:
+ map = pMap.get('value', '')
+ else:
+ map = None
+
+ # avoid running db.describe several times
+ cparams = dict()
+ cparams[map] = { 'dbInfo' : None,
+ 'layers' : None, }
+
+ # update reference widgets
+ for uid in p['wxId-bind']:
+ win = self.parent.FindWindowById(uid)
+ if not win:
+ continue
+
+ name = win.GetName()
+
+ map = layer = None
+ driver = db = table = None
+ if name in ('LayerSelect', 'ColumnSelect'):
+ if p.get('element', '') == 'vector': # -> vector
+ # get map name
+ map = p.get('value', '')
+
+ # get layer
+ for bid in p['wxId-bind']:
+ p = self.task.get_param(bid, element = 'wxId', raiseError = False)
+ if not p:
+ continue
+
+ if p.get('element', '') == 'layer':
+ layer = p.get('value', '')
+ if layer != '':
+ layer = p.get('value', '')
+ else:
+ layer = p.get('default', '')
+ break
+
+ elif p.get('element', '') == 'layer': # -> layer
+ # get layer
+ layer = p.get('value', '')
+ if layer != '':
+ layer = p.get('value', '')
+ else:
+ layer = p.get('default', '')
+
+ # get map name
+ pMapL = self.task.get_param(p['wxId'][0], element = 'wxId-bind', raiseError = False)
+ if pMapL:
+ map = pMapL.get('value', '')
+
+ if name == 'TableSelect' or \
+ (name == 'ColumnSelect' and not map):
+ pDriver = self.task.get_param('dbdriver', element = 'prompt', raiseError = False)
+ if pDriver:
+ driver = pDriver.get('value', '')
+ pDb = self.task.get_param('dbname', element = 'prompt', raiseError = False)
+ if pDb:
+ db = pDb.get('value', '')
+ if name == 'ColumnSelect':
+ pTable = self.task.get_param('dbtable', element = 'element', raiseError = False)
+ if pTable:
+ table = pTable.get('value', '')
+
+ if name == 'LayerSelect':
+ # determine format
+ native = True
+
+ for id in pMap['wxId']:
+ winVec = self.parent.FindWindowById(id)
+ if winVec.GetName() == 'VectorFormat' and \
+ winVec.GetSelection() != 0:
+ native = False
+ break
+ # TODO: update only if needed
+ if native:
+ if map:
+ self.data[win.InsertLayers] = { 'vector' : map }
+ else:
+ self.data[win.InsertLayers] = { }
+ else:
+ if map:
+ self.data[win.InsertLayers] = { 'dsn' : map.rstrip('@OGR') }
+ else:
+ self.data[win.InsertLayers] = { }
+
+ elif name == 'TableSelect':
+ self.data[win.InsertTables] = { 'driver' : driver,
+ 'database' : db }
+
+ elif name == 'ColumnSelect':
+ if map:
+ if map in cparams:
+ if not cparams[map]['dbInfo']:
+ cparams[map]['dbInfo'] = gselect.VectorDBInfo(map)
+ self.data[win.InsertColumns] = { 'vector' : map, 'layer' : layer,
+ 'dbInfo' : cparams[map]['dbInfo'] }
+ else: # table
+ if driver and db:
+ self.data[win.InsertTableColumns] = { 'table' : pTable.get('value'),
+ 'driver' : driver,
+ 'database' : db }
+ elif pTable:
+ self.data[win.InsertTableColumns] = { 'table' : pTable.get('value') }
+
+ elif name == 'SubGroupSelect':
+ pGroup = self.task.get_param('group', element = 'element', raiseError = False)
+ if pGroup:
+ self.data[win.Insert] = { 'group' : pGroup.get('value', '')}
+
+ elif name == 'LocationSelect':
+ pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+ if pDbase:
+ self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', '')}
+
+ elif name == 'MapsetSelect':
+ pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+ pLocation = self.task.get_param('location', element = 'element', raiseError = False)
+ if pDbase and pLocation:
+ self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
+ 'location' : pLocation.get('value', '')}
+
+ elif name == 'ProjSelect':
+ pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+ pLocation = self.task.get_param('location', element = 'element', raiseError = False)
+ pMapset = self.task.get_param('mapset', element = 'element', raiseError = False)
+ if pDbase and pLocation and pMapset:
+ self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
+ 'location' : pLocation.get('value', ''),
+ 'mapset' : pMapset.get('value', '')}
+
+def UpdateDialog(parent, event, eventId, task):
+ return UpdateThread(parent, event, eventId, task)
+
+class UpdateQThread(Thread):
+ """!Update dialog widgets in the thread"""
+ requestId = 0
+ def __init__(self, parent, requestQ, resultQ, **kwds):
+ Thread.__init__(self, **kwds)
+
+ self.parent = parent # cmdPanel
+ self.setDaemon(True)
+
+ self.requestQ = requestQ
+ self.resultQ = resultQ
+
+ self.start()
+
+ def Update(self, callable, *args, **kwds):
+ UpdateQThread.requestId += 1
+
+ self.request = None
+ self.requestQ.put((UpdateQThread.requestId, callable, args, kwds))
+
+ return UpdateQThread.requestId
+
+ def run(self):
+ while True:
+ requestId, callable, args, kwds = self.requestQ.get()
+
+ requestTime = time.time()
+
+ self.request = callable(*args, **kwds)
+
+ self.resultQ.put((requestId, self.request.run()))
+
+ if self.request:
+ event = wxUpdateDialog(data = self.request.data)
+ wx.PostEvent(self.parent, event)
+
+class mainFrame(wx.Frame):
+ """!This is the Frame containing the dialog for options input.
+
+ The dialog is organized in a notebook according to the guisections
+ defined by each GRASS command.
+
+ If run with a parent, it may Apply, Ok or Cancel; the latter two
+ close the dialog. The former two trigger a callback.
+
+ If run standalone, it will allow execution of the command.
+
+ The command is checked and sent to the clipboard when clicking
+ 'Copy'.
+ """
+ def __init__(self, parent, ID, task_description,
+ get_dcmd = None, layer = None):
+ self.get_dcmd = get_dcmd
+ self.layer = layer
+ self.task = task_description
+ self.parent = parent # LayerTree | Modeler | None | ...
+ if parent and parent.GetName() == 'Modeler':
+ self.modeler = self.parent
+ else:
+ self.modeler = None
+
+ # module name + keywords
+ if self.task.name.split('.')[-1] in ('py', 'sh'):
+ title = str(self.task.name.rsplit('.',1)[0])
+ else:
+ title = self.task.name
+ try:
+ if self.task.keywords != ['']:
+ title += " [" + ', '.join(self.task.keywords) + "]"
+ except ValueError:
+ pass
+
+ wx.Frame.__init__(self, parent = parent, id = ID, title = title,
+ pos = wx.DefaultPosition, style = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
+ name = "MainFrame")
+
+ self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ # statusbar
+ self.CreateStatusBar()
+
+ # icon
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
+
+ guisizer = wx.BoxSizer(wx.VERTICAL)
+
+ # set apropriate output window
+ if self.parent:
+ self.standalone = False
+ else:
+ self.standalone = True
+
+ # logo + description
+ topsizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ # GRASS logo
+ self.logo = wx.StaticBitmap(parent = self.panel,
+ bitmap = wx.Bitmap(name = os.path.join(globalvar.ETCIMGDIR,
+ 'grass_form.png'),
+ type = wx.BITMAP_TYPE_PNG))
+ topsizer.Add(item = self.logo, proportion = 0, border = 3,
+ flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL)
+
+ # add module description
+ if self.task.label:
+ module_desc = self.task.label + ' ' + self.task.description
+ else:
+ module_desc = self.task.description
+ 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)
+
+ guisizer.Add(item = topsizer, proportion = 0, flag = wx.EXPAND)
+
+ self.panel.SetSizerAndFit(guisizer)
+ self.Layout()
+
+ # notebooks
+ self.notebookpanel = cmdPanel(parent = self.panel, task = self.task,
+ mainFrame = self)
+ self.goutput = self.notebookpanel.goutput
+ self.notebookpanel.OnUpdateValues = self.updateValuesHook
+ guisizer.Add(item = self.notebookpanel, proportion = 1, flag = wx.EXPAND)
+
+ # status bar
+ status_text = _("Enter parameters for '") + self.task.name + "'"
+ try:
+ self.task.get_cmd()
+ self.updateValuesHook()
+ except ValueError:
+ self.SetStatusText(status_text)
+
+ # buttons
+ btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+ # cancel
+ self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+ self.btn_cancel.SetToolTipString(_("Close this window without executing the command (Ctrl+Q)"))
+ btnsizer.Add(item = self.btn_cancel, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
+ self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+
+ if self.get_dcmd is not None: # A callback has been set up
+ btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
+ btn_ok = wx.Button(parent = self.panel, id = wx.ID_OK)
+ btn_ok.SetDefault()
+
+ btnsizer.Add(item = btn_apply, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+ btnsizer.Add(item = btn_ok, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
+ btn_ok.Bind(wx.EVT_BUTTON, self.OnOK)
+ else: # We're standalone
+ # run
+ self.btn_run = wx.Button(parent = self.panel, id = wx.ID_OK, label = _("&Run"))
+ self.btn_run.SetToolTipString(_("Run the command (Ctrl+R)"))
+ self.btn_run.SetDefault()
+ self.btn_run.SetForegroundColour(wx.Colour(35, 142, 35))
+
+ # copy
+ self.btn_clipboard = wx.Button(parent = self.panel, id = wx.ID_COPY)
+ self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
+
+ btnsizer.Add(item = self.btn_run, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ btnsizer.Add(item = self.btn_clipboard, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER,
+ border = 10)
+
+ self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
+ self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
+ # help
+ self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
+ self.btn_help.SetToolTipString(_("Show manual page of the command (Ctrl+H)"))
+ self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
+ if self.notebookpanel.notebook.GetPageIndexByName('manual') < 0:
+ self.btn_help.Hide()
+
+ # add help button
+ btnsizer.Add(item = self.btn_help, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
+
+ guisizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
+ border = 30)
+
+ if self.parent and not self.modeler:
+ addLayer = False
+ for p in self.task.params:
+ if p.get('age', 'old') == 'new' and \
+ p.get('prompt', '') in ('raster', 'vector', '3d-raster'):
+ addLayer = True
+
+ if addLayer:
+ # add newly created map into layer tree
+ self.addbox = wx.CheckBox(parent = self.panel,
+ label = _('Add created map(s) into layer tree'), style = wx.NO_BORDER)
+ self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+ guisizer.Add(item = self.addbox, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+ border = 5)
+
+ hasNew = False
+ for p in self.task.params:
+ if p.get('age', 'old') == 'new':
+ hasNew = True
+ break
+
+ if self.get_dcmd is None and hasNew:
+ # close dialog when command is terminated
+ self.closebox = wx.CheckBox(parent = self.panel,
+ label = _('Close dialog on finish'), style = wx.NO_BORDER)
+ self.closebox.SetValue(UserSettings.Get(group = 'cmd', key = 'closeDlg', subkey = 'enabled'))
+ self.closebox.SetToolTipString(_("Close dialog when command is successfully finished. "
+ "Change this settings in Preferences dialog ('Command' tab)."))
+ guisizer.Add(item = self.closebox, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+ border = 5)
+
+ self.Bind(wx.EVT_CLOSE, self.OnCancel)
+ self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+
+ # do layout
+ # called automatically by SetSizer()
+ self.panel.SetAutoLayout(True)
+ self.panel.SetSizerAndFit(guisizer)
+
+ sizeFrame = self.GetBestSize()
+ self.SetMinSize(sizeFrame)
+
+ if hasattr(self, "closebox"):
+ scale = 0.33
+ else:
+ scale = 0.50
+ self.SetSize(wx.Size(sizeFrame[0], sizeFrame[1] + scale * max(self.notebookpanel.panelMinHeight,
+ self.notebookpanel.constrained_size[1])))
+
+ # thread to update dialog
+ # create queues
+ self.requestQ = Queue.Queue()
+ self.resultQ = Queue.Queue()
+ self.updateThread = UpdateQThread(self.notebookpanel, self.requestQ, self.resultQ)
+
+ self.Layout()
+
+ # keep initial window size limited for small screens
+ width, height = self.GetSizeTuple()
+ self.SetSize(wx.Size(min(width, 650),
+ min(height, 500)))
+
+ # fix goutput's pane size (required for Mac OSX)
+ if self.goutput:
+ self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
+
+ def updateValuesHook(self, event = None):
+ """!Update status bar data"""
+ self.SetStatusText(' '.join(self.notebookpanel.createCmd(ignoreErrors = True)))
+ if event:
+ event.Skip()
+
+ def OnKeyUp(self, event):
+ """!Key released (check hot-keys)"""
+ try:
+ kc = chr(event.GetKeyCode())
+ except ValueError:
+ event.Skip()
+ return
+
+ if not event.ControlDown():
+ event.Skip()
+ return
+
+ if kc == 'Q':
+ self.OnCancel(None)
+ elif kc == 'S':
+ self.OnAbort(None)
+ elif kc == 'H':
+ self.OnHelp(None)
+ elif kc == 'R':
+ self.OnRun(None)
+ elif kc == 'C':
+ self.OnCopy(None)
+
+ event.Skip()
+
+ def OnDone(self, cmd, returncode):
+ """!This function is launched from OnRun() when command is
+ finished
+
+ @param returncode command's return code (0 for success)
+ """
+ if not self.parent or returncode != 0:
+ return
+ if self.parent.GetName() not in ('LayerTree', 'LayerManager'):
+ return
+
+ if self.parent.GetName() == 'LayerTree':
+ display = self.parent.GetMapDisplay()
+ else: # Layer Manager
+ display = self.parent.GetLayerTree().GetMapDisplay()
+
+ if not display or not display.IsAutoRendered():
+ return
+
+ mapLayers = map(lambda x: x.GetName(),
+ display.GetMap().GetListOfLayers(l_type = 'raster') +
+ display.GetMap().GetListOfLayers(l_type = 'vector'))
+
+ task = GUI(show = None).ParseCommand(cmd)
+ for p in task.get_options()['params']:
+ if p.get('prompt', '') not in ('raster', 'vector'):
+ continue
+ mapName = p.get('value', '')
+ if '@' not in mapName:
+ mapName = mapName + '@' + grass.gisenv()['MAPSET']
+ if mapName in mapLayers:
+ display.GetWindow().UpdateMap(render = True)
+ return
+
+ def OnOK(self, event):
+ """!OK button pressed"""
+ cmd = self.OnApply(event)
+ if cmd is not None and self.get_dcmd is not None:
+ self.OnCancel(event)
+
+ def OnApply(self, event):
+ """!Apply the command"""
+ if self.modeler:
+ cmd = self.createCmd(ignoreErrors = True, ignoreRequired = True)
+ else:
+ cmd = self.createCmd()
+
+ if cmd is not None and self.get_dcmd is not None:
+ # return d.* command to layer tree for rendering
+ self.get_dcmd(cmd, self.layer, {"params": self.task.params,
+ "flags" : self.task.flags},
+ self)
+ # echo d.* command to output console
+ # self.parent.writeDCommand(cmd)
+
+ return cmd
+
+ def OnRun(self, event):
+ """!Run the command"""
+ cmd = self.createCmd()
+
+ if not cmd or len(cmd) < 1:
+ return
+
+ ret = 0
+ if self.standalone or cmd[0][0:2] != "d.":
+ # Send any non-display command to parent window (probably wxgui.py)
+ # put to parents switch to 'Command output'
+ self.notebookpanel.notebook.SetSelectionByName('output')
+
+ try:
+ if self.task.path:
+ cmd[0] = self.task.path # full path
+
+ ret = self.goutput.RunCmd(cmd, onDone = self.OnDone)
+ except AttributeError, e:
+ print >> sys.stderr, "%s: Probably not running in wxgui.py session?" % (e)
+ print >> sys.stderr, "parent window is: %s" % (str(self.parent))
+ else:
+ gcmd.Command(cmd)
+
+ if ret != 0:
+ self.notebookpanel.notebook.SetSelection(0)
+ return
+
+ # update buttons status
+ for btn in (self.btn_run,
+ self.btn_cancel,
+ self.btn_clipboard,
+ self.btn_help):
+ btn.Enable(False)
+
+ def OnAbort(self, event):
+ """!Abort running command"""
+ event = goutput.wxCmdAbort(aborted = True)
+ wx.PostEvent(self.goutput, event)
+
+ def OnCopy(self, event):
+ """!Copy the command"""
+ cmddata = wx.TextDataObject()
+ # list -> string
+ cmdstring = ' '.join(self.createCmd(ignoreErrors = True))
+ cmddata.SetText(cmdstring)
+ if wx.TheClipboard.Open():
+# wx.TheClipboard.UsePrimarySelection(True)
+ wx.TheClipboard.SetData(cmddata)
+ wx.TheClipboard.Close()
+ self.SetStatusText(_("'%s' copied to clipboard") % \
+ (cmdstring))
+
+ def OnCancel(self, event):
+ """!Cancel button pressed"""
+ self.MakeModal(False)
+
+ if self.get_dcmd and \
+ self.parent and \
+ self.parent.GetName() in ('LayerTree',
+ 'MapWindow'):
+ # display decorations and
+ # pressing OK or cancel after setting layer properties
+ if self.task.name in ['d.barscale','d.legend','d.histogram'] \
+ or len(self.parent.GetPyData(self.layer)[0]['cmd']) >= 1:
+ self.Hide()
+ # canceled layer with nothing set
+ elif len(self.parent.GetPyData(self.layer)[0]['cmd']) < 1:
+ self.parent.Delete(self.layer)
+ self.Destroy()
+ else:
+ # cancel for non-display commands
+ self.Destroy()
+
+ def OnHelp(self, event):
+ """!Show manual page (switch to the 'Manual' notebook page)"""
+ if self.notebookpanel.notebook.GetPageIndexByName('manual') > -1:
+ self.notebookpanel.notebook.SetSelectionByName('manual')
+ self.notebookpanel.OnPageChange(None)
+
+ if event:
+ event.Skip()
+
+ def createCmd(self, ignoreErrors = False, ignoreRequired = False):
+ """!Create command string (python list)"""
+ return self.notebookpanel.createCmd(ignoreErrors = ignoreErrors,
+ ignoreRequired = ignoreRequired)
+
+class cmdPanel(wx.Panel):
+ """!A panel containing a notebook dividing in tabs the different
+ guisections of the GRASS cmd.
+ """
+ def __init__(self, parent, task, id = wx.ID_ANY, mainFrame = None, *args, **kwargs):
+ if mainFrame:
+ self.parent = mainFrame
+ else:
+ self.parent = parent
+ self.task = task
+
+ wx.Panel.__init__(self, parent, id = id, *args, **kwargs)
+
+ # Determine tab layout
+ sections = []
+ is_section = {}
+ not_hidden = [ p for p in self.task.params + self.task.flags if not p.get('hidden', False) == True ]
+
+ self.label_id = [] # wrap titles on resize
+
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+
+ for task in not_hidden:
+ if task.get('required', False):
+ # All required go into Main, even if they had defined another guisection
+ task['guisection'] = _('Required')
+ if task.get('guisection','') == '':
+ # Undefined guisections end up into Options
+ task['guisection'] = _('Optional')
+ if task['guisection'] not in is_section:
+ # We do it like this to keep the original order, except for Main which goes first
+ is_section[task['guisection']] = 1
+ sections.append(task['guisection'])
+ else:
+ is_section[ task['guisection'] ] += 1
+ del is_section
+
+ # 'Required' tab goes first, 'Optional' as the last one
+ for (newidx,content) in [ (0,_('Required')), (len(sections)-1,_('Optional')) ]:
+ if content in sections:
+ idx = sections.index(content)
+ sections[idx:idx+1] = []
+ sections[newidx:newidx] = [content]
+
+ panelsizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+ # Build notebook
+ self.notebook = GNotebook(self, style = globalvar.FNPageStyle)
+ self.notebook.SetTabAreaColour(globalvar.FNPageColor)
+ self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChange)
+
+ tab = {}
+ tabsizer = {}
+ for section in sections:
+ tab[section] = scrolled.ScrolledPanel(parent = self.notebook)
+ tab[section].SetScrollRate(10, 10)
+ tabsizer[section] = wx.BoxSizer(orient = wx.VERTICAL)
+ self.notebook.AddPage(page = tab[section], text = section)
+
+ # are we running from command line?
+ ### add 'command output' tab regardless standalone dialog
+ if self.parent.GetName() == "MainFrame" and self.parent.get_dcmd is None:
+ self.goutput = goutput.GMConsole(parent = self, margin = False)
+ self.outpage = self.notebook.AddPage(page = self.goutput, text = _("Command output"), name = 'output')
+ else:
+ self.goutput = None
+
+ self.manual_tab = HelpPanel(parent = self, grass_command = self.task.name)
+ if not self.manual_tab.IsFile():
+ self.manual_tab.Hide()
+ else:
+ self.notebook.AddPage(page = self.manual_tab, text = _("Manual"), name = 'manual')
+
+ self.notebook.SetSelection(0)
+
+ panelsizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
+
+ #
+ # flags
+ #
+ text_style = wx.FONTWEIGHT_NORMAL
+ visible_flags = [ f for f in self.task.flags if not f.get('hidden', False) == True ]
+ for f in visible_flags:
+ which_sizer = tabsizer[ f['guisection'] ]
+ which_panel = tab[ f['guisection'] ]
+ # if label is given: description -> tooltip
+ if f.get('label','') != '':
+ title = text_beautify(f['label'])
+ tooltip = text_beautify(f['description'], width = -1)
+ else:
+ title = text_beautify(f['description'])
+ tooltip = None
+ title_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ rtitle_txt = wx.StaticText(parent = which_panel,
+ label = '(' + f['name'] + ')')
+ chk = wx.CheckBox(parent = which_panel, label = title, style = wx.NO_BORDER)
+ self.label_id.append(chk.GetId())
+ if tooltip:
+ chk.SetToolTipString(tooltip)
+ chk.SetValue(f.get('value', False))
+ title_sizer.Add(item = chk, proportion = 1,
+ flag = wx.EXPAND)
+ title_sizer.Add(item = rtitle_txt, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
+ which_sizer.Add(item = title_sizer, proportion = 0,
+ flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+ f['wxId'] = [ chk.GetId(), ]
+ chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+
+ if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+ parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+ label = _("Parameterized in model"))
+ parChk.SetName('ModelParam')
+ parChk.SetValue(f.get('parameterized', False))
+ if 'wxId' in f:
+ f['wxId'].append(parChk.GetId())
+ else:
+ f['wxId'] = [ parChk.GetId() ]
+ parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+ which_sizer.Add(item = parChk, proportion = 0,
+ flag = wx.LEFT, border = 20)
+
+ if f['name'] in ('verbose', 'quiet'):
+ chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
+ vq = UserSettings.Get(group = 'cmd', key = 'verbosity', subkey = 'selection')
+ if f['name'] == vq:
+ chk.SetValue(True)
+ f['value'] = True
+ elif f['name'] == 'overwrite' and 'value' not in f:
+ chk.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
+ f['value'] = UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled')
+
+ #
+ # parameters
+ #
+ visible_params = [ p for p in self.task.params if not p.get('hidden', False) == True ]
+
+ try:
+ first_param = visible_params[0]
+ except IndexError:
+ first_param = None
+
+ for p in visible_params:
+ which_sizer = tabsizer[ p['guisection'] ]
+ which_panel = tab[ p['guisection'] ]
+ # if label is given -> label and description -> tooltip
+ # otherwise description -> lavel
+ if p.get('label','') != '':
+ title = text_beautify(p['label'])
+ tooltip = text_beautify(p['description'], width = -1)
+ else:
+ title = text_beautify(p['description'])
+ tooltip = None
+ txt = None
+
+ # text style (required -> bold)
+ if not p.get('required', False):
+ text_style = wx.FONTWEIGHT_NORMAL
+ else:
+ text_style = wx.FONTWEIGHT_BOLD
+
+ # title sizer (description, name, type)
+ if (len(p.get('values', [])) > 0) and \
+ p.get('multiple', False) and \
+ p.get('gisprompt',False) == False and \
+ p.get('type', '') == 'string':
+ title_txt = wx.StaticBox(parent = which_panel, id = wx.ID_ANY)
+ else:
+ title_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ title_txt = wx.StaticText(parent = which_panel)
+ if p['key_desc']:
+ ltype = ','.join(p['key_desc'])
+ else:
+ ltype = p['type']
+ rtitle_txt = wx.StaticText(parent = which_panel,
+ label = '(' + p['name'] + '=' + ltype + ')')
+ title_sizer.Add(item = title_txt, proportion = 1,
+ flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+ title_sizer.Add(item = rtitle_txt, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, border = 5)
+ which_sizer.Add(item = title_sizer, proportion = 0,
+ flag = wx.EXPAND)
+ self.label_id.append(title_txt.GetId())
+
+ # title expansion
+ if p.get('multiple', False) and len(p.get('values','')) == 0:
+ title = _("[multiple]") + " " + title
+ if p.get('value','') == '' :
+ p['value'] = p.get('default','')
+
+ if (len(p.get('values', [])) > 0):
+ valuelist = map(str, p.get('values',[]))
+ valuelist_desc = map(unicode, p.get('values_desc',[]))
+
+ if p.get('multiple', False) and \
+ p.get('gisprompt',False) == False and \
+ p.get('type', '') == 'string':
+ title_txt.SetLabel(" %s: (%s, %s) " % (title, p['name'], p['type']))
+ if valuelist_desc:
+ hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.VERTICAL)
+ else:
+ hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.HORIZONTAL)
+ isEnabled = {}
+ # copy default values
+ if p['value'] == '':
+ p['value'] = p.get('default', '')
+
+ for defval in p.get('value', '').split(','):
+ isEnabled[ defval ] = 'yes'
+ # for multi checkboxes, this is an array of all wx IDs
+ # for each individual checkbox
+ p[ 'wxId' ] = list()
+ idx = 0
+ for val in valuelist:
+ try:
+ label = valuelist_desc[idx]
+ except IndexError:
+ label = val
+
+ chkbox = wx.CheckBox(parent = which_panel,
+ label = text_beautify(label))
+ p[ 'wxId' ].append(chkbox.GetId())
+ if val in isEnabled:
+ chkbox.SetValue(True)
+ hSizer.Add(item = chkbox, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.ALL, border = 1)
+ chkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBoxMulti)
+ idx += 1
+
+ which_sizer.Add(item = hSizer, proportion = 0,
+ flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border = 5)
+ elif p.get('gisprompt', False) == False:
+ if len(valuelist) == 1: # -> textctrl
+ title_txt.SetLabel("%s (%s %s):" % (title, _('valid range'),
+ str(valuelist[0])))
+
+ if p.get('type', '') == 'integer' and \
+ not p.get('multiple', False):
+
+ # for multiple integers use textctrl instead of spinsctrl
+ try:
+ minValue, maxValue = map(int, valuelist[0].split('-'))
+ except ValueError:
+ minValue = -1e6
+ maxValue = 1e6
+ txt2 = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY, size = globalvar.DIALOG_SPIN_SIZE,
+ min = minValue, max = maxValue)
+ txt2.SetName("SpinCtrl")
+ style = wx.BOTTOM | wx.LEFT
+ else:
+ txt2 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
+ txt2.SetName("TextCtrl")
+ style = wx.EXPAND | wx.BOTTOM | wx.LEFT
+
+ value = self._getValue(p)
+ # parameter previously set
+ if value:
+ if txt2.GetName() == "SpinCtrl":
+ txt2.SetValue(int(value))
+ else:
+ txt2.SetValue(value)
+
+ which_sizer.Add(item = txt2, proportion = 0,
+ flag = style, border = 5)
+
+ p['wxId'] = [ txt2.GetId(), ]
+ txt2.Bind(wx.EVT_TEXT, self.OnSetValue)
+ else:
+ # list of values (combo)
+ title_txt.SetLabel(title + ':')
+ cb = wx.ComboBox(parent = which_panel, id = wx.ID_ANY, value = p.get('default',''),
+ size = globalvar.DIALOG_COMBOBOX_SIZE,
+ choices = valuelist, style = wx.CB_DROPDOWN)
+ value = self._getValue(p)
+ if value:
+ cb.SetValue(value) # parameter previously set
+ which_sizer.Add(item = cb, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+ p['wxId'] = [cb.GetId(),]
+ cb.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ cb.Bind(wx.EVT_TEXT, self.OnSetValue)
+
+ # text entry
+ if (p.get('type','string') in ('string','integer','float')
+ and len(p.get('values',[])) == 0
+ and p.get('gisprompt',False) == False
+ and p.get('prompt','') != 'color'):
+
+ title_txt.SetLabel(title + ':')
+ if p.get('multiple', False) or \
+ p.get('type', 'string') == 'string' or \
+ len(p.get('key_desc', [])) > 1:
+ txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
+
+ value = self._getValue(p)
+ if value:
+ # parameter previously set
+ txt3.SetValue(str(value))
+
+ txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
+ style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
+ else:
+ minValue = -1e9
+ maxValue = 1e9
+ if p.get('type', '') == 'integer':
+ txt3 = wx.SpinCtrl(parent = which_panel, value = p.get('default',''),
+ size = globalvar.DIALOG_SPIN_SIZE,
+ min = minValue, max = maxValue)
+ style = wx.BOTTOM | wx.LEFT | wx.RIGHT
+
+ value = self._getValue(p)
+ if value:
+ txt3.SetValue(int(value)) # parameter previously set
+
+ txt3.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
+ else:
+ txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+ validator = FloatValidator())
+ style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
+
+ value = self._getValue(p)
+ if value:
+ txt3.SetValue(str(value)) # parameter previously set
+
+ txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
+
+ which_sizer.Add(item = txt3, proportion = 0,
+ flag = style, border = 5)
+ p['wxId'] = [ txt3.GetId(), ]
+
+ #
+ # element selection tree combobox (maps, icons, regions, etc.)
+ #
+ if p.get('gisprompt', False) == True:
+ title_txt.SetLabel(title + ':')
+ # GIS element entry
+ if p.get('prompt','') not in ('color',
+ 'color_none',
+ 'subgroup',
+ 'dbdriver',
+ 'dbname',
+ 'dbtable',
+ 'dbcolumn',
+ 'layer',
+ 'layer_all',
+ 'location',
+ 'mapset',
+ 'dbase') and \
+ p.get('element', '') not in ('file', 'dir'):
+ multiple = p.get('multiple', False)
+ if p.get('age', '') == 'new':
+ mapsets = [grass.gisenv()['MAPSET'],]
+ else:
+ mapsets = None
+ if self.task.name in ('r.proj', 'v.proj') \
+ and p.get('name', '') == 'input':
+ if self.task.name == 'r.proj':
+ isRaster = True
+ else:
+ isRaster = False
+ selection = gselect.ProjSelect(parent = which_panel,
+ isRaster = isRaster)
+ p['wxId'] = [ selection.GetId(), ]
+ selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ formatSelector = False
+ selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+ else:
+ selection = gselect.Select(parent = which_panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE,
+ type = p.get('element', ''),
+ multiple = multiple, mapsets = mapsets,
+ fullyQualified = p.get('age', 'old') == 'old')
+
+ value = self._getValue(p)
+ if value:
+ selection.SetValue(value)
+
+ formatSelector = True
+ # A select.Select is a combobox with two children: a textctl and a popupwindow;
+ # we target the textctl here
+ textWin = selection.GetTextCtrl()
+ p['wxId'] = [ textWin.GetId(), ]
+ textWin.Bind(wx.EVT_TEXT, self.OnSetValue)
+
+ if p.get('prompt', '') == 'vector':
+ selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+
+ # if formatSelector and p.get('age', 'old') == 'old':
+ # # OGR supported (read-only)
+ # self.hsizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ # self.hsizer.Add(item = selection,
+ # flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_TOP,
+ # border = 5)
+
+ # # format (native / ogr)
+ # rbox = wx.RadioBox(parent = which_panel, id = wx.ID_ANY,
+ # label = " %s " % _("Format"),
+ # style = wx.RA_SPECIFY_ROWS,
+ # choices = [_("Native / Linked OGR"), _("Direct OGR")])
+ # if p.get('value', '').lower().rfind('@ogr') > -1:
+ # rbox.SetSelection(1)
+ # rbox.SetName('VectorFormat')
+ # rbox.Bind(wx.EVT_RADIOBOX, self.OnVectorFormat)
+
+ # self.hsizer.Add(item = rbox,
+ # flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT |
+ # wx.RIGHT | wx.ALIGN_TOP,
+ # border = 5)
+
+ # ogrSelection = gselect.GdalSelect(parent = self, panel = which_panel, ogr = True,
+ # default = 'dir',
+ # exclude = ['file'])
+ # self.Bind(gselect.EVT_GDALSELECT, self.OnUpdateSelection)
+ # self.Bind(gselect.EVT_GDALSELECT, self.OnSetValue)
+
+ # ogrSelection.SetName('OgrSelect')
+ # ogrSelection.Hide()
+
+ # which_sizer.Add(item = self.hsizer, proportion = 0)
+
+ # p['wxId'].append(rbox.GetId())
+ # p['wxId'].append(ogrSelection.GetId())
+ # for win in ogrSelection.GetDsnWin():
+ # p['wxId'].append(win.GetId())
+ # else:
+ which_sizer.Add(item = selection, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+ elif p.get('prompt', '') == 'group':
+ selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+ which_sizer.Add(item = selection, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+ else:
+ which_sizer.Add(item = selection, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+ # subgroup
+ elif p.get('prompt', '') == 'subgroup':
+ selection = gselect.SubGroupSelect(parent = which_panel)
+ p['wxId'] = [ selection.GetId() ]
+ selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ which_sizer.Add(item = selection, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
+ border = 5)
+
+ # layer, dbdriver, dbname, dbcolumn, dbtable entry
+ elif p.get('prompt', '') in ('dbdriver',
+ 'dbname',
+ 'dbtable',
+ 'dbcolumn',
+ 'layer',
+ 'layer_all',
+ 'location',
+ 'mapset',
+ 'dbase'):
+ if p.get('multiple', 'no') == 'yes':
+ win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+ size = globalvar.DIALOG_TEXTCTRL_SIZE)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+ else:
+ value = self._getValue(p)
+
+ if p.get('prompt', '') in ('layer',
+ 'layer_all'):
+ if p.get('prompt', '') == 'layer_all':
+ all = True
+ else:
+ all = False
+ if p.get('age', 'old') == 'old':
+ win = gselect.LayerSelect(parent = which_panel,
+ all = all,
+ default = p['default'])
+ win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+ win.SetValue(str(value)) # default or previously set value
+ else:
+ win = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY,
+ min = 1, max = 100, initial = int(p['default']))
+ win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
+ win.SetValue(int(value)) # default or previously set value
+
+ p['wxId'] = [ win.GetId() ]
+
+ elif p.get('prompt', '') == 'dbdriver':
+ win = gselect.DriverSelect(parent = which_panel,
+ choices = p.get('values', []),
+ value = value)
+ win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+ win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ elif p.get('prompt', '') == 'dbname':
+ win = gselect.DatabaseSelect(parent = which_panel,
+ value = value)
+ win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+ elif p.get('prompt', '') == 'dbtable':
+ if p.get('age', 'old') == 'old':
+ win = gselect.TableSelect(parent = which_panel)
+ win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+ win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ else:
+ win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+ size = globalvar.DIALOG_TEXTCTRL_SIZE)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+ elif p.get('prompt', '') == 'dbcolumn':
+ win = gselect.ColumnSelect(parent = which_panel,
+ value = value,
+ param = p)
+ win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+
+ elif p.get('prompt', '') == 'location':
+ win = gselect.LocationSelect(parent = which_panel,
+ value = value)
+ win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+ win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+
+ elif p.get('prompt', '') == 'mapset':
+ if p.get('age', 'old') == 'old':
+ new = False
+ else:
+ new = True
+ win = gselect.MapsetSelect(parent = which_panel,
+ value = value, new = new)
+ win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+ win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+ win.Bind(wx.EVT_TEXT, self.OnSetValue)
+
+ elif p.get('prompt', '') == 'dbase':
+ win = gselect.DbaseSelect(parent = which_panel,
+ changeCallback = self.OnSetValue)
+ win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+ p['wxId'] = [ win.GetChildren()[1].GetId() ]
+
+ if 'wxId' not in p:
+ try:
+ p['wxId'] = [ win.GetId(), ]
+ except AttributeError:
+ pass
+
+ which_sizer.Add(item = win, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+ # color entry
+ elif p.get('prompt', '') in ('color',
+ 'color_none'):
+ default_color = (200,200,200)
+ label_color = _("Select Color")
+ if p.get('default','') != '':
+ default_color, label_color = color_resolve(p['default'])
+ if p.get('value','') != '': # parameter previously set
+ default_color, label_color = color_resolve(p['value'])
+ if p.get('prompt', '') == 'color_none':
+ this_sizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+ else:
+ this_sizer = which_sizer
+ btn_colour = csel.ColourSelect(parent = which_panel, id = wx.ID_ANY,
+ label = label_color, colour = default_color,
+ pos = wx.DefaultPosition, size = (150,-1))
+ this_sizer.Add(item = btn_colour, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+ # For color selectors, this is a two-member array, holding the IDs of
+ # the selector proper and either a "transparent" button or None
+ p['wxId'] = [btn_colour.GetId(),]
+ btn_colour.Bind(csel.EVT_COLOURSELECT, self.OnColorChange)
+ if p.get('prompt', '') == 'color_none':
+ none_check = wx.CheckBox(which_panel, wx.ID_ANY, _("Transparent"))
+ if p.get('value','') != '' and p.get('value',[''])[0] == "none":
+ none_check.SetValue(True)
+ else:
+ none_check.SetValue(False)
+ this_sizer.Add(item = none_check, proportion = 0,
+ flag = wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border = 5)
+ which_sizer.Add(this_sizer)
+ none_check.Bind(wx.EVT_CHECKBOX, self.OnColorChange)
+ p['wxId'].append(none_check.GetId())
+ else:
+ p['wxId'].append(None)
+ # file selector
+ elif p.get('prompt','') != 'color' and p.get('element', '') == 'file':
+ if p.get('age', 'new') == 'new':
+ fmode = wx.SAVE
+ else:
+ fmode = wx.OPEN
+ fbb = filebrowse.FileBrowseButton(parent = which_panel, id = wx.ID_ANY, fileMask = '*',
+ size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+ dialogTitle = _('Choose %s') % \
+ p.get('description',_('File')),
+ buttonText = _('Browse'),
+ startDirectory = os.getcwd(), fileMode = fmode,
+ changeCallback = self.OnSetValue)
+ value = self._getValue(p)
+ if value:
+ fbb.SetValue(value) # parameter previously set
+ which_sizer.Add(item = fbb, proportion = 0,
+ flag = wx.EXPAND | wx.RIGHT, border = 5)
+
+ # A file browse button is a combobox with two children:
+ # a textctl and a button;
+ # we have to target the button here
+ p['wxId'] = [ fbb.GetChildren()[1].GetId() ]
+ if p.get('age', 'new') == 'old' and \
+ UserSettings.Get(group = 'cmd', key = 'interactiveInput', subkey = 'enabled'):
+ # widget for interactive input
+ ifbb = wx.TextCtrl(parent = which_panel, id = wx.ID_ANY,
+ style = wx.TE_MULTILINE,
+ size = (-1, 75))
+ if p.get('value', '') and os.path.isfile(p['value']):
+ f = open(p['value'])
+ ifbb.SetValue(''.join(f.readlines()))
+ f.close()
+
+ ifbb.Bind(wx.EVT_TEXT, self.OnFileText)
+
+ btnLoad = wx.Button(parent = which_panel, id = wx.ID_ANY, label = _("&Load"))
+ btnLoad.Bind(wx.EVT_BUTTON, self.OnFileLoad)
+ btnSave = wx.Button(parent = which_panel, id = wx.ID_SAVEAS)
+ btnSave.Bind(wx.EVT_BUTTON, self.OnFileSave)
+
+ which_sizer.Add(item = wx.StaticText(parent = which_panel, id = wx.ID_ANY,
+ label = _('or enter values interactively')),
+ proportion = 0,
+ flag = wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM, border = 5)
+ which_sizer.Add(item = ifbb, proportion = 1,
+ flag = wx.EXPAND | wx.RIGHT | wx.LEFT, border = 5)
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item = btnLoad, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.RIGHT, border = 10)
+ btnSizer.Add(item = btnSave, proportion = 0,
+ flag = wx.ALIGN_RIGHT)
+ which_sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, border = 5)
+
+ p['wxId'].append(ifbb.GetId())
+ p['wxId'].append(btnLoad.GetId())
+ p['wxId'].append(btnSave.GetId())
+
+ # directory selector
+ elif p.get('prompt','') != 'color' and p.get('element', '') == 'dir':
+ fbb = filebrowse.DirBrowseButton(parent = which_panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+ dialogTitle = _('Choose %s') % \
+ p.get('description', _('Directory')),
+ buttonText = _('Browse'),
+ startDirectory = os.getcwd(),
+ changeCallback = self.OnSetValue)
+ value = self._getValue(p)
+ if value:
+ fbb.SetValue(value) # parameter previously set
+ which_sizer.Add(item = fbb, proportion = 0,
+ flag = wx.EXPAND | wx.RIGHT, border = 5)
+
+ # A file browse button is a combobox with two children:
+ # a textctl and a button;
+ # we have to target the button here
+ p['wxId'] = [ fbb.GetChildren()[1].GetId() ]
+
+ if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+ parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+ label = _("Parameterized in model"))
+ parChk.SetName('ModelParam')
+ parChk.SetValue(p.get('parameterized', False))
+ if 'wxId' in p:
+ p['wxId'].append(parChk.GetId())
+ else:
+ p['wxId'] = [ parChk.GetId() ]
+ parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+ which_sizer.Add(item = parChk, proportion = 0,
+ flag = wx.LEFT, border = 20)
+
+ if title_txt is not None:
+ # create tooltip if given
+ if len(p['values_desc']) > 0:
+ if tooltip:
+ tooltip += 2 * os.linesep
+ else:
+ tooltip = ''
+ if len(p['values']) == len(p['values_desc']):
+ for i in range(len(p['values'])):
+ tooltip += p['values'][i] + ': ' + p['values_desc'][i] + os.linesep
+ tooltip.strip(os.linesep)
+ if tooltip:
+ title_txt.SetToolTipString(tooltip)
+
+ if p == first_param:
+ if 'wxId' in p and len(p['wxId']) > 0:
+ win = self.FindWindowById(p['wxId'][0])
+ win.SetFocus()
+
+ #
+ # set widget relations for OnUpdateSelection
+ #
+ pMap = None
+ pLayer = []
+ pDriver = None
+ pDatabase = None
+ pTable = None
+ pColumn = []
+ pGroup = None
+ pSubGroup = None
+ pDbase = None
+ pLocation = None
+ pMapset = None
+ for p in self.task.params:
+ if p.get('gisprompt', False) == False:
+ continue
+
+ guidep = p.get('guidependency', '')
+
+ if guidep:
+ # fixed options dependency defined
+ options = guidep.split(',')
+ for opt in options:
+ pOpt = self.task.get_param(opt, element = 'name', raiseError = False)
+ if id:
+ if 'wxId-bind' not in p:
+ p['wxId-bind'] = list()
+ p['wxId-bind'] += pOpt['wxId']
+ continue
+
+ prompt = p.get('element', '')
+ if prompt in ('cell', 'vector'):
+ name = p.get('name', '')
+ if name in ('map', 'input'):
+ pMap = p
+ elif prompt == 'layer':
+ pLayer.append(p)
+ elif prompt == 'dbcolumn':
+ pColumn.append(p)
+ elif prompt == 'dbdriver':
+ pDriver = p
+ elif prompt == 'dbname':
+ pDatabase = p
+ elif prompt == 'dbtable':
+ pTable = p
+ elif prompt == 'group':
+ pGroup = p
+ elif prompt == 'subgroup':
+ pSubGroup = p
+ elif prompt == 'dbase':
+ pDbase = p
+ elif prompt == 'location':
+ pLocation = p
+ elif prompt == 'mapset':
+ pMapset = p
+
+ # collect ids
+ pColumnIds = []
+ for p in pColumn:
+ pColumnIds += p['wxId']
+ pLayerIds = []
+ for p in pLayer:
+ pLayerIds += p['wxId']
+
+ # set wxId-bindings
+ if pMap:
+ pMap['wxId-bind'] = copy.copy(pColumnIds)
+ if pLayer:
+ pMap['wxId-bind'] += pLayerIds
+ if pLayer:
+ for p in pLayer:
+ p['wxId-bind'] = copy.copy(pColumnIds)
+
+ if pDriver and pTable:
+ pDriver['wxId-bind'] = pTable['wxId']
+
+ if pDatabase and pTable:
+ pDatabase['wxId-bind'] = pTable['wxId']
+
+ if pTable and pColumnIds:
+ pTable['wxId-bind'] = pColumnIds
+
+ if pGroup and pSubGroup:
+ pGroup['wxId-bind'] = pSubGroup['wxId']
+
+ if pDbase and pLocation:
+ pDbase['wxId-bind'] = pLocation['wxId']
+
+ if pLocation and pMapset:
+ pLocation['wxId-bind'] = pMapset['wxId']
+
+ if pLocation and pMapset and pMap:
+ pLocation['wxId-bind'] += pMap['wxId']
+ pMapset['wxId-bind'] = pMap['wxId']
+
+ #
+ # determine panel size
+ #
+ maxsizes = (0, 0)
+ for section in sections:
+ tab[section].SetSizer(tabsizer[section])
+ tabsizer[section].Fit(tab[section])
+ tab[section].Layout()
+ minsecsizes = tabsizer[section].GetSize()
+ maxsizes = map(lambda x: max(maxsizes[x], minsecsizes[x]), (0, 1))
+
+ # TODO: be less arbitrary with these 600
+ self.panelMinHeight = 100
+ self.constrained_size = (min(600, maxsizes[0]) + 25, min(400, maxsizes[1]) + 25)
+ for section in sections:
+ tab[section].SetMinSize((self.constrained_size[0], self.panelMinHeight))
+
+ if self.manual_tab.IsLoaded():
+ self.manual_tab.SetMinSize((self.constrained_size[0], self.panelMinHeight))
+
+ self.SetSizer(panelsizer)
+ panelsizer.Fit(self.notebook)
+
+ self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
+
+ def _getValue(self, p):
+ """!Get value or default value of given parameter
+
+ @param p parameter directory
+ """
+ if p.get('value', '') != '':
+ return p['value']
+ return p.get('default', '')
+
+ def OnFileLoad(self, event):
+ """!Load file to interactive input"""
+ me = event.GetId()
+ win = dict()
+ for p in self.task.params:
+ if 'wxId' in p and me in p['wxId']:
+ win['file'] = self.FindWindowById(p['wxId'][0])
+ win['text'] = self.FindWindowById(p['wxId'][1])
+ break
+
+ if not win:
+ return
+
+ path = win['file'].GetValue()
+ if not path:
+ gcmd.GMessage(parent = self,
+ message = _("Nothing to load."))
+ return
+
+ data = ''
+ f = open(path, "r")
+ try:
+ data = f.read()
+ finally:
+ f.close()
+
+ win['text'].SetValue(data)
+
+ def OnFileSave(self, event):
+ """!Save interactive input to the file"""
+ wId = event.GetId()
+ win = {}
+ for p in self.task.params:
+ if wId in p.get('wxId', []):
+ win['file'] = self.FindWindowById(p['wxId'][0])
+ win['text'] = self.FindWindowById(p['wxId'][1])
+ break
+
+ if not win:
+ return
+
+ text = win['text'].GetValue()
+ if not text:
+ gcmd.GMessage(parent = self,
+ message = _("Nothing to save."))
+ return
+
+ dlg = wx.FileDialog(parent = self,
+ message = _("Save input as..."),
+ defaultDir = os.getcwd(),
+ style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ f = open(path, "w")
+ try:
+ f.write(text + os.linesep)
+ finally:
+ f.close()
+
+ win['file'].SetValue(path)
+
+ dlg.Destroy()
+
+ def OnFileText(self, event):
+ """File input interactively entered"""
+ text = event.GetString()
+ p = self.task.get_param(value = event.GetId(), element = 'wxId', raiseError = False)
+ if not p:
+ return # should not happen
+ win = self.FindWindowById(p['wxId'][0])
+ if text:
+ filename = win.GetValue()
+ if not filename:
+ # outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
+ filename = grass.tempfile()
+ win.SetValue(filename)
+
+ f = open(filename, "w")
+ try:
+ f.write(text)
+ if text[-1] != os.linesep:
+ f.write(os.linesep)
+ finally:
+ f.close()
+ else:
+ win.SetValue('')
+
+ def OnVectorFormat(self, event):
+ """!Change vector format (native / ogr)"""
+ sel = event.GetSelection()
+ idEvent = event.GetId()
+ p = self.task.get_param(value = idEvent, element = 'wxId', raiseError = False)
+ if not p:
+ return # should not happen
+
+ # detect windows
+ winNative = None
+ winOgr = None
+ for id in p['wxId']:
+ if id == idEvent:
+ continue
+ name = self.FindWindowById(id).GetName()
+ if name == 'Select':
+ winNative = self.FindWindowById(id + 1) # fix the mystery (also in nviz_tools.py)
+ elif name == 'OgrSelect':
+ winOgr = self.FindWindowById(id)
+
+ # enable / disable widgets & update values
+ rbox = self.FindWindowByName('VectorFormat')
+ self.hsizer.Remove(rbox)
+ if sel == 0: # -> native
+ winOgr.Hide()
+ self.hsizer.Remove(winOgr)
+
+ self.hsizer.Add(item = winNative,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_TOP,
+ border = 5)
+ winNative.Show()
+ p['value'] = winNative.GetValue()
+
+ elif sel == 1: # -> OGR
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ winNative.Hide()
+ self.hsizer.Remove(winNative)
+
+ sizer.Add(item = winOgr)
+ winOgr.Show()
+ p['value'] = winOgr.GetDsn()
+
+ self.hsizer.Add(item = sizer,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_TOP,
+ border = 5)
+
+ self.hsizer.Add(item = rbox,
+ flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT |
+ wx.RIGHT | wx.ALIGN_TOP,
+ border = 5)
+
+ self.hsizer.Layout()
+ self.Layout()
+ self.OnUpdateValues()
+ self.OnUpdateSelection(event)
+
+ def OnUpdateDialog(self, event):
+ for fn, kwargs in event.data.iteritems():
+ fn(**kwargs)
+
+ self.parent.updateValuesHook()
+
+ def OnVerbosity(self, event):
+ """!Verbosity level changed"""
+ verbose = self.FindWindowById(self.task.get_flag('verbose')['wxId'][0])
+ quiet = self.FindWindowById(self.task.get_flag('quiet')['wxId'][0])
+ if event.IsChecked():
+ if event.GetId() == verbose.GetId():
+ if quiet.IsChecked():
+ quiet.SetValue(False)
+ self.task.get_flag('quiet')['value'] = False
+ else:
+ if verbose.IsChecked():
+ verbose.SetValue(False)
+ self.task.get_flag('verbose')['value'] = False
+
+ event.Skip()
+
+ def OnPageChange(self, event):
+ if not event:
+ sel = self.notebook.GetSelection()
+ else:
+ sel = event.GetSelection()
+
+ idx = self.notebook.GetPageIndexByName('manual')
+ if idx > -1 and sel == idx:
+ # calling LoadPage() is strangely time-consuming (only first call)
+ # FIXME: move to helpPage.__init__()
+ if not self.manual_tab.IsLoaded():
+ wx.Yield()
+ self.manual_tab.LoadPage()
+
+ self.Layout()
+
+ def OnColorChange(self, event):
+ myId = event.GetId()
+ for p in self.task.params:
+ if 'wxId' in p and myId in p['wxId']:
+ has_button = p['wxId'][1] is not None
+ if has_button and wx.FindWindowById(p['wxId'][1]).GetValue() == True:
+ p[ 'value' ] = 'none'
+ else:
+ colorchooser = wx.FindWindowById(p['wxId'][0])
+ new_color = colorchooser.GetValue()[:]
+ # This is weird: new_color is a 4-tuple and new_color[:] is a 3-tuple
+ # under wx2.8.1
+ new_label = rgb2str.get(new_color, ':'.join(map(str,new_color)))
+ colorchooser.SetLabel(new_label)
+ colorchooser.SetColour(new_color)
+ colorchooser.Refresh()
+ p[ 'value' ] = colorchooser.GetLabel()
+ self.OnUpdateValues()
+
+ def OnUpdateValues(self, event = None):
+ """!If we were part of a richer interface, report back the
+ current command being built.
+
+ This method should be set by the parent of this panel if
+ needed. It's a hook, actually. Beware of what is 'self' in
+ the method def, though. It will be called with no arguments.
+ """
+ pass
+
+ def OnCheckBoxMulti(self, event):
+ """!Fill the values as a ','-separated string according to
+ current status of the checkboxes.
+ """
+ me = event.GetId()
+ theParam = None
+ for p in self.task.params:
+ if 'wxId' in p and me in p['wxId']:
+ theParam = p
+ myIndex = p['wxId'].index(me)
+
+ # Unpack current value list
+ currentValues = {}
+ for isThere in theParam.get('value', '').split(','):
+ currentValues[isThere] = 1
+ theValue = theParam['values'][myIndex]
+
+ if event.Checked():
+ currentValues[ theValue ] = 1
+ else:
+ del currentValues[ theValue ]
+
+ # Keep the original order, so that some defaults may be recovered
+ currentValueList = []
+ for v in theParam['values']:
+ if v in currentValues:
+ currentValueList.append(v)
+
+ # Pack it back
+ theParam['value'] = ','.join(currentValueList)
+
+ self.OnUpdateValues()
+
+ def OnSetValue(self, event):
+ """!Retrieve the widget value and set the task value field
+ accordingly.
+
+ Use for widgets that have a proper GetValue() method, i.e. not
+ for selectors.
+ """
+ myId = event.GetId()
+ me = wx.FindWindowById(myId)
+ name = me.GetName()
+
+ found = False
+ for porf in self.task.params + self.task.flags:
+ if 'wxId' not in porf:
+ continue
+ if myId in porf['wxId']:
+ found = True
+ break
+
+ if not found:
+ return
+
+ if name == 'GdalSelect':
+ porf['value'] = event.dsn
+ elif name == 'ModelParam':
+ porf['parameterized'] = me.IsChecked()
+ else:
+ porf['value'] = me.GetValue()
+
+ self.OnUpdateValues(event)
+
+ event.Skip()
+
+ def OnUpdateSelection(self, event):
+ """!Update dialog (layers, tables, columns, etc.)
+ """
+ if not hasattr(self.parent, "updateThread"):
+ if event:
+ event.Skip()
+ return
+ if event:
+ self.parent.updateThread.Update(UpdateDialog,
+ self,
+ event,
+ event.GetId(),
+ self.task)
+ else:
+ self.parent.updateThread.Update(UpdateDialog,
+ self,
+ None,
+ None,
+ self.task)
+
+ def createCmd(self, ignoreErrors = False, ignoreRequired = False):
+ """!Produce a command line string (list) or feeding into GRASS.
+
+ @param ignoreErrors True then it will return whatever has been
+ built so far, even though it would not be a correct command
+ for GRASS
+ """
+ try:
+ cmd = self.task.get_cmd(ignoreErrors = ignoreErrors,
+ ignoreRequired = ignoreRequired)
+ except ValueError, err:
+ dlg = wx.MessageDialog(parent = self,
+ message = unicode(err),
+ caption = _("Error in %s") % self.task.name,
+ style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ dlg.ShowModal()
+ dlg.Destroy()
+ cmd = None
+
+ return cmd
+
+ def OnSize(self, event):
+ width = event.GetSize()[0]
+ fontsize = self.GetFont().GetPointSize()
+ text_width = max(width / (fontsize - 3), 70)
+
+ for id in self.label_id:
+ win = self.FindWindowById(id)
+ label = win.GetLabel()
+ label_new = '\n'.join(textwrap.wrap(label, text_width))
+ win.SetLabel(label_new)
+
+ event.Skip()
+
+class GrassGUIApp(wx.App):
+ """!Stand-alone GRASS command GUI
+ """
+ def __init__(self, grass_task):
+ self.grass_task = grass_task
+ wx.App.__init__(self, False)
+
+ def OnInit(self):
+ msg = self.grass_task.get_error_msg()
+ if msg:
+ gcmd.GError(msg + '\n\nTry to set up GRASS_ADDON_PATH variable.')
+ return True
+
+ self.mf = mainFrame(parent = None, ID = wx.ID_ANY, task_description = self.grass_task)
+ self.mf.CentreOnScreen()
+ self.mf.Show(True)
+ self.SetTopWindow(self.mf)
+
+ return True
+
+class GUI:
+ def __init__(self, parent = None, show = True, modal = False,
+ centreOnParent = False, checkError = False):
+ """!Parses GRASS commands when module is imported and used from
+ Layer Manager.
+ """
+ self.parent = parent
+ self.show = show
+ self.modal = modal
+ self.centreOnParent = centreOnParent
+ self.checkError = checkError
+
+ self.grass_task = None
+ self.cmd = list()
+
+ global _blackList
+ if self.parent:
+ _blackList['enabled'] = True
+ else:
+ _blackList['enabled'] = False
+
+ def GetCmd(self):
+ """!Get validated command"""
+ return self.cmd
+
+ def ParseCommand(self, cmd, gmpath = None, completed = None):
+ """!Parse command
+
+ Note: cmd is given as list
+
+ If command is given with options, return validated cmd list:
+ - add key name for first parameter if not given
+ - change mapname to mapname at mapset
+ """
+ start = time.time()
+ dcmd_params = {}
+ if completed == None:
+ get_dcmd = None
+ layer = None
+ dcmd_params = None
+ else:
+ get_dcmd = completed[0]
+ layer = completed[1]
+ if completed[2]:
+ dcmd_params.update(completed[2])
+
+ # parse the interface decription
+ try:
+ global _blackList
+ self.grass_task = gtask.parse_interface(cmd[0],
+ blackList = _blackList)
+ except ValueError, e: #grass.ScriptError, e:
+ gcmd.GError(e.value)
+ return
+
+ # if layer parameters previously set, re-insert them into dialog
+ if completed is not None:
+ if 'params' in dcmd_params:
+ self.grass_task.params = dcmd_params['params']
+ if 'flags' in dcmd_params:
+ self.grass_task.flags = dcmd_params['flags']
+
+ err = list()
+ # update parameters if needed && validate command
+ if len(cmd) > 1:
+ i = 0
+ cmd_validated = [cmd[0]]
+ for option in cmd[1:]:
+ if option[0] == '-': # flag
+ if option[1] == '-':
+ self.grass_task.set_flag(option[2:], True)
+ else:
+ self.grass_task.set_flag(option[1], True)
+ cmd_validated.append(option)
+ else: # parameter
+ try:
+ key, value = option.split('=', 1)
+ except:
+ if self.grass_task.firstParam:
+ if i == 0: # add key name of first parameter if not given
+ key = self.grass_task.firstParam
+ value = option
+ else:
+ raise gcmd.GException, _("Unable to parse command '%s'") % ' '.join(cmd)
+ else:
+ continue
+
+ element = self.grass_task.get_param(key, raiseError = False)
+ if not element:
+ err.append(_("%(cmd)s: parameter '%(key)s' not available") % \
+ { 'cmd' : cmd[0],
+ 'key' : key })
+ continue
+ element = element['element']
+
+ if element in ['cell', 'vector']:
+ # mapname -> mapname at mapset
+ try:
+ name, mapset = value.split('@')
+ except ValueError:
+ mapset = grass.find_file(value, element)['mapset']
+ curr_mapset = grass.gisenv()['MAPSET']
+ if mapset and mapset != curr_mapset:
+ value = value + '@' + mapset
+
+ self.grass_task.set_param(key, value)
+ cmd_validated.append(key + '=' + value)
+ i += 1
+
+ # update original command list
+ cmd = cmd_validated
+
+ if self.show is not None:
+ self.mf = mainFrame(parent = self.parent, ID = wx.ID_ANY,
+ task_description = self.grass_task,
+ get_dcmd = get_dcmd, layer = layer)
+ else:
+ self.mf = None
+
+ if get_dcmd is not None:
+ # update only propwin reference
+ get_dcmd(dcmd = None, layer = layer, params = None,
+ propwin = self.mf)
+
+ if self.show is not None:
+ self.mf.notebookpanel.OnUpdateSelection(None)
+ if self.show is True:
+ if self.parent and self.centreOnParent:
+ self.mf.CentreOnParent()
+ else:
+ self.mf.CenterOnScreen()
+ self.mf.Show(self.show)
+ self.mf.MakeModal(self.modal)
+ else:
+ self.mf.OnApply(None)
+
+ self.cmd = cmd
+
+ if self.checkError:
+ return self.grass_task, err
+ else:
+ return self.grass_task
+
+ def GetCommandInputMapParamKey(self, cmd):
+ """!Get parameter key for input raster/vector map
+
+ @param cmd module name
+
+ @return parameter key
+ @return None on failure
+ """
+ # parse the interface decription
+ if not self.grass_task:
+ enc = locale.getdefaultlocale()[1]
+ if enc and enc.lower() == "cp932":
+ p = re.compile('encoding="' + enc + '"', re.IGNORECASE)
+ tree = etree.fromstring(p.sub('encoding="utf-8"',
+ gtask.get_interface_description(cmd).decode(enc).encode('utf-8')))
+ else:
+ tree = etree.fromstring(gtask.get_interface_description(cmd))
+ self.grass_task = gtask.processTask(tree).get_task()
+
+ for p in self.grass_task.params:
+ if p.get('name', '') in ('input', 'map'):
+ age = p.get('age', '')
+ prompt = p.get('prompt', '')
+ element = p.get('element', '')
+ if age == 'old' and \
+ element in ('cell', 'grid3', 'vector') and \
+ prompt in ('raster', '3d-raster', 'vector'):
+ return p.get('name', None)
+ return None
+
+class FloatValidator(wx.PyValidator):
+ """!Validator for floating-point input"""
+ def __init__(self):
+ wx.PyValidator.__init__(self)
+
+ self.Bind(wx.EVT_TEXT, self.OnText)
+
+ def Clone(self):
+ """!Clone validator"""
+ return FloatValidator()
+
+ def Validate(self):
+ """Validate input"""
+ textCtrl = self.GetWindow()
+ text = textCtrl.GetValue()
+
+ if text:
+ try:
+ float(text)
+ except ValueError:
+ textCtrl.SetBackgroundColour("grey")
+ textCtrl.SetFocus()
+ textCtrl.Refresh()
+ return False
+
+ sysColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
+ textCtrl.SetBackgroundColour(sysColor)
+
+ textCtrl.Refresh()
+
+ return True
+
+ def OnText(self, event):
+ """!Do validation"""
+ self.Validate()
+
+ event.Skip()
+
+ def TransferToWindow(self):
+ return True # Prevent wxDialog from complaining.
+
+ def TransferFromWindow(self):
+ return True # Prevent wxDialog from complaining.
+
+if __name__ == "__main__":
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ if len(sys.argv) == 1:
+ sys.exit(_("usage: %s <grass command>") % sys.argv[0])
+
+ if sys.argv[1] != 'test':
+ q = wx.LogNull()
+ cmd = utils.split(sys.argv[1])
+ task = gtask.grassTask(cmd[0], blackList = _blackList)
+ task.set_options(cmd[1:])
+ app = GrassGUIApp(task)
+ app.MainLoop()
+ else: #Test
+ # Test grassTask from within a GRASS session
+ if os.getenv("GISBASE") is not None:
+ task = gtask.grassTask("d.vect")
+ task.get_param('map')['value'] = "map_name"
+ task.get_flag('v')['value'] = True
+ task.get_param('layer')['value'] = 1
+ task.get_param('bcolor')['value'] = "red"
+ assert ' '.join(task.get_cmd()) == "d.vect -v map = map_name layer = 1 bcolor = red"
+ # Test interface building with handmade grassTask,
+ # possibly outside of a GRASS session.
+ task = gtask.grassTask()
+ task.name = "TestTask"
+ task.description = "This is an artificial grassTask() object intended for testing purposes."
+ task.keywords = ["grass","test","task"]
+ task.params = [
+ {
+ "name" : "text",
+ "description" : "Descriptions go into tooltips if labels are present, like this one",
+ "label" : "Enter some text",
+ },{
+ "name" : "hidden_text",
+ "description" : "This text should not appear in the form",
+ "hidden" : True
+ },{
+ "name" : "text_default",
+ "description" : "Enter text to override the default",
+ "default" : "default text"
+ },{
+ "name" : "text_prefilled",
+ "description" : "You should see a friendly welcome message here",
+ "value" : "hello, world"
+ },{
+ "name" : "plain_color",
+ "description" : "This is a plain color, and it is a compulsory parameter",
+ "required" : False,
+ "gisprompt" : True,
+ "prompt" : "color"
+ },{
+ "name" : "transparent_color",
+ "description" : "This color becomes transparent when set to none",
+ "guisection" : "tab",
+ "gisprompt" : True,
+ "prompt" : "color"
+ },{
+ "name" : "multi",
+ "description" : "A multiple selection",
+ 'default': u'red,green,blue',
+ 'gisprompt': False,
+ 'guisection': 'tab',
+ 'multiple': u'yes',
+ 'type': u'string',
+ 'value': '',
+ 'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other']
+ },{
+ "name" : "single",
+ "description" : "A single multiple-choice selection",
+ 'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other'],
+ "guisection" : "tab"
+ },{
+ "name" : "large_multi",
+ "description" : "A large multiple selection",
+ "gisprompt" : False,
+ "multiple" : "yes",
+ # values must be an array of strings
+ "values" : str2rgb.keys() + map(str, str2rgb.values())
+ },{
+ "name" : "a_file",
+ "description" : "A file selector",
+ "gisprompt" : True,
+ "element" : "file"
+ }
+ ]
+ task.flags = [
+ {
+ "name" : "a",
+ "description" : "Some flag, will appear in Main since it is required",
+ "required" : True
+ },{
+ "name" : "b",
+ "description" : "pre-filled flag, will appear in options since it is not required",
+ "value" : True
+ },{
+ "name" : "hidden_flag",
+ "description" : "hidden flag, should not be changeable",
+ "hidden" : "yes",
+ "value" : True
+ }
+ ]
+ q = wx.LogNull()
+ GrassGUIApp(task).MainLoop()
+
Copied: grass/trunk/gui/wxpython/gui_core/ghelp.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/ghelp.py)
===================================================================
--- grass/trunk/gui/wxpython/gui_core/ghelp.py (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/ghelp.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,1299 @@
+"""!
+ at package gui_core.ghelp
+
+ at brief Help window
+
+Classes:
+ - SearchModuleWindow
+ - ItemTree
+ - MenuTreeWindow
+ - MenuTree
+ - AboutWindow
+ - InstallExtensionWindow
+ - ExtensionTree
+ - HelpFrame
+ - HelpWindow
+ - HelpPanel
+
+(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.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import codecs
+
+import wx
+try:
+ import wx.lib.agw.customtreectrl as CT
+ from wx.lib.agw.hyperlink import HyperLinkCtrl
+except ImportError:
+ import wx.lib.customtreectrl as CT
+ from wx.lib.hyperlink import HyperLinkCtrl
+import wx.lib.flatnotebook as FN
+import wx.lib.scrolledpanel as scrolled
+
+import grass.script as grass
+from grass.script import task as gtask
+
+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"""
+ def __init__(self, parent, id, title, size, file):
+ wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # text
+ content = HelpPanel(parent = self)
+ content.LoadPage(file)
+
+ sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
+
+ self.SetAutoLayout(True)
+ self.SetSizer(sizer)
+ self.Layout()
+
+class SearchModuleWindow(wx.Panel):
+ """!Search module window (used in MenuTreeWindow)"""
+ def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
+ showChoice = True, showTip = False, **kwargs):
+ self.showTip = showTip
+ self.showChoice = showChoice
+ self.cmdPrompt = cmdPrompt
+
+ wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+
+ self._searchDict = { _('description') : 'description',
+ _('command') : 'command',
+ _('keywords') : 'keywords' }
+
+ self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label = " %s " % _("Find module(s)"))
+
+ self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
+ choices = [_('description'),
+ _('keywords'),
+ _('command')])
+ self.searchBy.SetSelection(0)
+
+ self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+ value = "", size = (-1, 25),
+ style = wx.TE_PROCESS_ENTER)
+ self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
+
+ if self.showTip:
+ 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)
+ if self.cmdPrompt:
+ self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
+ self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
+
+ self._layout()
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
+ gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
+ gridSizer.AddGrowableCol(1)
+
+ gridSizer.Add(item = self.searchBy,
+ flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+ gridSizer.Add(item = self.search,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
+ row = 1
+ if self.showTip:
+ gridSizer.Add(item = self.searchTip,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
+ row += 1
+
+ if self.showChoice:
+ gridSizer.Add(item = self.searchChoice,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
+
+ sizer.Add(item = gridSizer, proportion = 1)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def GetSelection(self):
+ """!Get selected element"""
+ selection = self.searchBy.GetStringSelection()
+
+ return self._searchDict[selection]
+
+ def SetSelection(self, i):
+ """!Set selection element"""
+ self.searchBy.SetSelection(i)
+
+ def OnSearchModule(self, event):
+ """!Search module by keywords or description"""
+ if not self.cmdPrompt:
+ event.Skip()
+ return
+
+ text = event.GetString()
+ if not text:
+ self.cmdPrompt.SetFilter(None)
+ mList = self.cmdPrompt.GetCommandItems()
+ self.searchChoice.SetItems(mList)
+ if self.showTip:
+ self.searchTip.SetLabel(_("%d modules found") % len(mList))
+ event.Skip()
+ return
+
+ modules = dict()
+ iFound = 0
+ for module, data in self.cmdPrompt.moduleDesc.iteritems():
+ found = False
+ sel = self.searchBy.GetSelection()
+ if sel == 0: # -> description
+ if text in data['desc']:
+ found = True
+ elif sel == 1: # keywords
+ if text in ','.join(data['keywords']):
+ found = True
+ else: # command
+ if module[:len(text)] == text:
+ found = True
+
+ if found:
+ iFound += 1
+ try:
+ group, name = module.split('.')
+ except ValueError:
+ continue # TODO
+
+ if group not in modules:
+ modules[group] = list()
+ modules[group].append(name)
+
+ self.cmdPrompt.SetFilter(modules)
+ self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
+ if self.showTip:
+ self.searchTip.SetLabel(_("%d modules found") % iFound)
+
+ event.Skip()
+
+ def OnSelectModule(self, event):
+ """!Module selected from choice, update command prompt"""
+ cmd = event.GetString().split(' ', 1)[0]
+ text = cmd + ' '
+ pos = len(text)
+
+ if self.cmdPrompt:
+ self.cmdPrompt.SetText(text)
+ self.cmdPrompt.SetSelectionStart(pos)
+ self.cmdPrompt.SetCurrentPos(pos)
+ self.cmdPrompt.SetFocus()
+
+ desc = self.cmdPrompt.GetCommandDesc(cmd)
+ if self.showTip:
+ self.searchTip.SetLabel(desc)
+
+ def Reset(self):
+ """!Reset widget"""
+ self.searchBy.SetSelection(0)
+ self.search.SetValue('')
+ if self.showTip:
+ self.searchTip.SetLabel('')
+
+class MenuTreeWindow(wx.Panel):
+ """!Show menu tree"""
+ def __init__(self, parent, id = wx.ID_ANY, **kwargs):
+ self.parent = parent # LayerManager
+
+ wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+
+ 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 = ManagerData())
+ self.tree.Load()
+
+ # search widget
+ self.search = SearchModuleWindow(parent = self, showChoice = False)
+
+ # buttons
+ self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
+ self.btnRun.SetToolTipString(_("Run selected command"))
+ self.btnRun.Enable(False)
+
+ # bindings
+ self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
+ self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
+ self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
+ self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
+ self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
+
+ self._layout()
+
+ self.search.SetFocus()
+
+ def _layout(self):
+ """!Do dialog layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # body
+ dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
+ dataSizer.Add(item = self.tree, proportion =1,
+ flag = wx.EXPAND)
+
+ # buttons
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item = self.btnRun, proportion = 0)
+
+ sizer.Add(item = dataSizer, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ sizer.Add(item = self.search, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
+
+ sizer.Fit(self)
+ sizer.SetSizeHints(self)
+
+ self.SetSizer(sizer)
+
+ self.Fit()
+ self.SetAutoLayout(True)
+ self.Layout()
+
+ def OnCloseWindow(self, event):
+ """!Close window"""
+ self.Destroy()
+
+ def OnRun(self, event):
+ """!Run selected command"""
+ if not self.tree.GetSelected():
+ return # should not happen
+
+ data = self.tree.GetPyData(self.tree.GetSelected())
+ if not data:
+ return
+
+ handler = 'self.parent.' + data['handler'].lstrip('self.')
+ if data['handler'] == 'self.OnXTerm':
+ wx.MessageBox(parent = self,
+ message = _('You must run this command from the menu or command line',
+ 'This command require an XTerm'),
+ caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+ elif data['command']:
+ eval(handler)(event = None, cmd = data['command'].split())
+ else:
+ eval(handler)(None)
+
+ def OnShowItem(self, event):
+ """!Show selected item"""
+ self.tree.OnShowItem(event)
+ if self.tree.GetSelected():
+ self.btnRun.Enable()
+ else:
+ self.btnRun.Enable(False)
+
+ def OnItemActivated(self, event):
+ """!Item activated (double-click)"""
+ item = event.GetItem()
+ if not item or not item.IsOk():
+ return
+
+ data = self.tree.GetPyData(item)
+ if not data or 'command' not in data:
+ return
+
+ self.tree.itemSelected = item
+
+ self.OnRun(None)
+
+ def OnItemSelected(self, event):
+ """!Item selected"""
+ item = event.GetItem()
+ if not item or not item.IsOk():
+ return
+
+ data = self.tree.GetPyData(item)
+ if not data or 'command' not in data:
+ return
+
+ if data['command']:
+ label = data['command'] + ' -- ' + data['description']
+ else:
+ label = data['description']
+
+ self.parent.SetStatusText(label, 0)
+
+ def OnUpdateStatusBar(self, event):
+ """!Update statusbar text"""
+ element = self.search.GetSelection()
+ self.tree.SearchItems(element = element,
+ value = event.GetString())
+
+ nItems = len(self.tree.itemsMarked)
+ if event.GetString():
+ self.parent.SetStatusText(_("%d modules match") % nItems, 0)
+ else:
+ self.parent.SetStatusText("", 0)
+
+ event.Skip()
+
+class ItemTree(CT.CustomTreeCtrl):
+ def __init__(self, parent, id = wx.ID_ANY,
+ ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
+ CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
+ if globalvar.hasAgw:
+ super(ItemTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
+ else:
+ super(ItemTree, self).__init__(parent, id, style = ctstyle, **kwargs)
+
+ self.root = self.AddRoot(_("Menu tree"))
+ self.itemsMarked = [] # list of marked items
+ self.itemSelected = None
+
+ def SearchItems(self, element, value):
+ """!Search item
+
+ @param element element index (see self.searchBy)
+ @param value
+
+ @return list of found tree items
+ """
+ items = list()
+ if not value:
+ return items
+
+ item = self.GetFirstChild(self.root)[0]
+ self._processItem(item, element, value, items)
+
+ self.itemsMarked = items
+ self.itemSelected = None
+
+ return items
+
+ def _processItem(self, item, element, value, listOfItems):
+ """!Search items (used by SearchItems)
+
+ @param item reference item
+ @param listOfItems list of found items
+ """
+ while item and item.IsOk():
+ subItem = self.GetFirstChild(item)[0]
+ if subItem:
+ self._processItem(subItem, element, value, listOfItems)
+ data = self.GetPyData(item)
+
+ if data and element in data and \
+ value.lower() in data[element].lower():
+ listOfItems.append(item)
+
+ item = self.GetNextSibling(item)
+
+ def GetSelected(self):
+ """!Get selected item"""
+ return self.itemSelected
+
+ def OnShowItem(self, event):
+ """!Highlight first found item in menu tree"""
+ if len(self.itemsMarked) > 0:
+ if self.GetSelected():
+ self.ToggleItemSelection(self.GetSelected())
+ idx = self.itemsMarked.index(self.GetSelected()) + 1
+ else:
+ idx = 0
+ try:
+ self.ToggleItemSelection(self.itemsMarked[idx])
+ self.itemSelected = self.itemsMarked[idx]
+ self.EnsureVisible(self.itemsMarked[idx])
+ except IndexError:
+ self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
+ self.EnsureVisible(self.itemsMarked[0])
+ self.itemSelected = self.itemsMarked[0]
+ else:
+ for item in self.root.GetChildren():
+ self.Collapse(item)
+ itemSelected = self.GetSelection()
+ if itemSelected:
+ self.ToggleItemSelection(itemSelected)
+ self.itemSelected = None
+
+class MenuTree(ItemTree):
+ """!Menu tree class"""
+ def __init__(self, parent, data, **kwargs):
+ self.parent = parent
+ self.menudata = data
+
+ super(MenuTree, self).__init__(parent, **kwargs)
+
+ def Load(self, data = None):
+ """!Load menu data tree
+
+ @param data menu data (None to use self.menudata)
+ """
+ if not data:
+ data = self.menudata
+
+ self.itemsMarked = [] # list of marked items
+ for eachMenuData in data.GetMenu():
+ for label, items in eachMenuData:
+ item = self.AppendItem(parentId = self.root,
+ text = label.replace('&', ''))
+ self.__AppendItems(item, items)
+
+ def __AppendItems(self, item, data):
+ """!Append items into tree (used by Load()
+
+ @param item tree item (parent)
+ @parent data menu data"""
+ for eachItem in data:
+ if len(eachItem) == 2:
+ if eachItem[0]:
+ itemSub = self.AppendItem(parentId = item,
+ text = eachItem[0])
+ self.__AppendItems(itemSub, eachItem[1])
+ else:
+ if eachItem[0]:
+ itemNew = self.AppendItem(parentId = item,
+ text = eachItem[0])
+
+ data = { 'item' : eachItem[0],
+ 'description' : eachItem[1],
+ 'handler' : eachItem[2],
+ 'command' : eachItem[3],
+ 'keywords' : eachItem[4] }
+
+ self.SetPyData(itemNew, data)
+
+class AboutWindow(wx.Frame):
+ """!Create custom About Window
+
+ @todo improve styling
+ """
+ def __init__(self, parent, size = (750, 400),
+ title = _('About GRASS GIS'), **kwargs):
+ wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, size = size, **kwargs)
+
+ panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ # icon
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+ # get version and web site
+ vInfo = grass.version()
+
+ infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
+ infoSizer = wx.BoxSizer(wx.VERTICAL)
+ infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+ infoGridSizer.AddGrowableCol(0)
+ infoGridSizer.AddGrowableCol(1)
+ logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
+ logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
+ bitmap = wx.Bitmap(name = logo,
+ type = wx.BITMAP_TYPE_ICO))
+ infoSizer.Add(item = logoBitmap, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
+
+ info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
+ info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
+ infoSizer.Add(item = info, proportion = 0,
+ flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
+
+ row = 0
+ infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = _('Official GRASS site:')),
+ pos = (row, 0),
+ flag = wx.ALIGN_RIGHT)
+
+ infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY,
+ label = 'http://grass.osgeo.org'),
+ pos = (row, 1),
+ flag = wx.ALIGN_LEFT)
+
+ row += 2
+ infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = _('SVN Revision:')),
+ pos = (row, 0),
+ flag = wx.ALIGN_RIGHT)
+
+ infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = vInfo['revision']),
+ pos = (row, 1),
+ flag = wx.ALIGN_LEFT)
+
+ row += 1
+ infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = _('GIS Library Revision:')),
+ pos = (row, 0),
+ flag = wx.ALIGN_RIGHT)
+
+ infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+ label = vInfo['libgis_revision'] + ' (' +
+ vInfo['libgis_date'].split(' ')[0] + ')'),
+ pos = (row, 1),
+ flag = wx.ALIGN_LEFT)
+
+ infoSizer.Add(item = infoGridSizer,
+ proportion = 1,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
+ border = 25)
+
+ # create a flat notebook for displaying information about GRASS
+ aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON)
+ aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
+
+ for title, win in ((_("Info"), infoTxt),
+ (_("Copyright"), self._pageCopyright()),
+ (_("License"), self._pageLicense()),
+ (_("Authors"), self._pageCredit()),
+ (_("Contributors"), self._pageContributors()),
+ (_("Extra contributors"), self._pageContributors(extra = True)),
+ (_("Translators"), self._pageTranslators())):
+ aboutNotebook.AddPage(page = win, text = title)
+ wx.CallAfter(aboutNotebook.SetSelection, 0)
+
+ # buttons
+ btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item = btnClose, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_RIGHT,
+ border = 5)
+ # bindings
+ btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+
+ infoTxt.SetSizer(infoSizer)
+ infoSizer.Fit(infoTxt)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ sizer.Add(item = aboutNotebook, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 1)
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
+ panel.SetSizer(sizer)
+ self.Layout()
+
+ def _pageCopyright(self):
+ """Copyright information"""
+ copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
+ if os.path.exists(copyfile):
+ copyrightFile = open(copyfile, 'r')
+ copytext = copyrightFile.read()
+ copyrightFile.close()
+ else:
+ copytext = _('%s file missing') % 'COPYING'
+
+ # put text into a scrolling panel
+ copyrightwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
+ size = wx.DefaultSize,
+ style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+ copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
+ copyrightwin.SetAutoLayout(True)
+ copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
+ copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+ copyrightwin.SetSizer(copyrightwin.sizer)
+ copyrightwin.Layout()
+ copyrightwin.SetupScrolling()
+
+ return copyrightwin
+
+ def _pageLicense(self):
+ """Licence about"""
+ licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
+ if os.path.exists(licfile):
+ licenceFile = open(licfile, 'r')
+ license = ''.join(licenceFile.readlines())
+ licenceFile.close()
+ else:
+ license = _('%s file missing') % 'GPL.TXT'
+ # put text into a scrolling panel
+ licensewin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
+ style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+ licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
+ licensewin.SetAutoLayout(True)
+ licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
+ licensewin.sizer.Add(item = licensetxt, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+ licensewin.SetSizer(licensewin.sizer)
+ licensewin.Layout()
+ licensewin.SetupScrolling()
+
+ return licensewin
+
+ def _pageCredit(self):
+ """Credit about"""
+ # credits
+ authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
+ if os.path.exists(authfile):
+ authorsFile = open(authfile, 'r')
+ authors = unicode(''.join(authorsFile.readlines()), "utf-8")
+ authorsFile.close()
+ else:
+ authors = _('%s file missing') % 'AUTHORS'
+ authorwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
+ style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
+ authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
+ authorwin.SetAutoLayout(1)
+ authorwin.SetupScrolling()
+ authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
+ authorwin.sizer.Add(item = authortxt, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+ authorwin.SetSizer(authorwin.sizer)
+ authorwin.Layout()
+
+ return authorwin
+
+ def _pageContributors(self, extra = False):
+ """Contributors info"""
+ if extra:
+ contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
+ else:
+ contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
+ if os.path.exists(contribfile):
+ contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
+ contribs = list()
+ errLines = list()
+ for line in contribFile.readlines()[1:]:
+ line = line.rstrip('\n')
+ try:
+ if extra:
+ name, email, rfc2_agreed = line.split(',')
+ else:
+ cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
+ except ValueError:
+ errLines.append(line)
+ continue
+ if extra:
+ contribs.append((name, email))
+ else:
+ contribs.append((name, email, country, osgeo_id))
+
+ contribFile.close()
+
+ if 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
+
+ contribwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
+ style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+ contribwin.SetAutoLayout(True)
+ contribwin.SetupScrolling()
+ contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
+
+ if not contribs:
+ contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
+ label = _('%s file missing') % contribfile)
+ contribwin.sizer.Add(item = contribtxt, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+ else:
+ if extra:
+ items = (_('Name'), _('E-mail'))
+ else:
+ items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
+ contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
+ for item in items:
+ contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
+ label = item))
+ for vals in sorted(contribs, key = lambda x: x[0]):
+ for item in vals:
+ contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
+ label = item))
+ contribwin.sizer.Add(item = contribBox, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+
+ contribwin.SetSizer(contribwin.sizer)
+ contribwin.Layout()
+
+ return contribwin
+
+ def _pageTranslators(self):
+ """Translators info"""
+ translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
+ if os.path.exists(translatorsfile):
+ translatorsFile = open(translatorsfile, 'r')
+ translators = dict()
+ errLines = list()
+ for line in translatorsFile.readlines()[1:]:
+ line = line.rstrip('\n')
+ try:
+ name, email, languages = line.split(',')
+ except ValueError:
+ errLines.append(line)
+ continue
+ for language in languages.split(' '):
+ if language not in translators:
+ translators[language] = list()
+ translators[language].append((name, email))
+ translatorsFile.close()
+
+ if errLines:
+ GError(parent = self,
+ message = _("Error when reading file '%s'.") % translatorsfile + \
+ "\n\n" + _("Lines:") + " %s" % \
+ os.linesep.join(map(utils.DecodeString, errLines)))
+ else:
+ translators = None
+
+ translatorswin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
+ style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
+ translatorswin.SetAutoLayout(1)
+ translatorswin.SetupScrolling()
+ translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
+
+ if not translators:
+ translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
+ label = _('%s file missing') % 'translators.csv')
+ translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+ else:
+ translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
+ languages = translators.keys()
+ languages.sort()
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = _('Name')))
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = _('E-mail')))
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = _('Language')))
+ for lang in languages:
+ for translator in translators[lang]:
+ name, email = translator
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = unicode(name, "utf-8")))
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = email))
+ translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+ label = lang))
+
+ translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
+ flag = wx.EXPAND | wx.ALL, border = 3)
+
+ translatorswin.SetSizer(translatorswin.sizer)
+ translatorswin.Layout()
+
+ return translatorswin
+
+ def OnCloseWindow(self, event):
+ """!Close window"""
+ self.Close()
+
+class InstallExtensionWindow(wx.Frame):
+ def __init__(self, parent, id = wx.ID_ANY,
+ title = _("Fetch & install extension from GRASS Addons"), **kwargs):
+ self.parent = parent
+ self.options = dict() # list of options
+
+ wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+
+ self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+ self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Repository"))
+ self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("List of extensions"))
+
+ self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
+ self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = _("Fetch full info including description and keywords (takes time)"))
+ self.fullDesc.SetValue(True)
+
+ self.search = SearchModuleWindow(parent = self.panel)
+ self.search.SetSelection(0)
+
+ self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
+
+ self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+ label = " %s " % _("Options"))
+ task = gtask.parse_interface('g.extension')
+ for f in task.get_options()['flags']:
+ name = f.get('name', '')
+ desc = f.get('label', '')
+ if not desc:
+ desc = f.get('description', '')
+ if not name and not desc:
+ continue
+ if name in ('l', 'c', 'g', 'quiet', 'verbose'):
+ continue
+ self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+ label = desc)
+ self.repo.SetValue(task.get_param(value = 'svnurl').get('default',
+ 'http://svn.osgeo.org/grass/grass-addons/grass7'))
+
+ self.statusbar = self.CreateStatusBar(number = 1)
+
+ self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("&Fetch"))
+ self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
+ self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+ self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("&Install"))
+ self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
+ self.btnInstall.Enable(False)
+ self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+ label = _("Command dialog"))
+ self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension')
+
+ self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+ self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
+ self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
+ self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+ self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
+ self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
+ self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
+ self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
+
+ self._layout()
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
+ repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
+ repo1Sizer.Add(item = self.repo, proportion = 1,
+ flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
+ repo1Sizer.Add(item = self.btnFetch, proportion = 0,
+ flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
+ repoSizer.Add(item = repo1Sizer,
+ flag = wx.EXPAND)
+ repoSizer.Add(item = self.fullDesc)
+
+ findSizer = wx.BoxSizer(wx.HORIZONTAL)
+ findSizer.Add(item = self.search, proportion = 1)
+
+ treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
+ treeSizer.Add(item = self.tree, proportion = 1,
+ flag = wx.ALL | wx.EXPAND, border = 1)
+
+ # options
+ optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
+ for key in self.options.keys():
+ optionSizer.Add(item = self.options[key], proportion = 0)
+
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+ btnSizer.Add(item = self.btnCmd, proportion = 0,
+ flag = wx.RIGHT, border = 5)
+ btnSizer.AddSpacer(10)
+ btnSizer.Add(item = self.btnClose, proportion = 0,
+ flag = wx.RIGHT, border = 5)
+ btnSizer.Add(item = self.btnInstall, proportion = 0)
+
+ sizer.Add(item = repoSizer, proportion = 0,
+ flag = wx.ALL | wx.EXPAND, border = 3)
+ sizer.Add(item = findSizer, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+ sizer.Add(item = treeSizer, proportion = 1,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+ sizer.Add(item = optionSizer, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+ self.panel.SetSizer(sizer)
+ sizer.Fit(self.panel)
+
+ self.Layout()
+
+ def _getCmd(self):
+ item = self.tree.GetSelected()
+ if not item or not item.IsOk():
+ return ['g.extension']
+
+ name = self.tree.GetItemText(item)
+ if not name:
+ GError(_("Extension not defined"), parent = self)
+ return
+
+ flags = list()
+ for key in self.options.keys():
+ if self.options[key].IsChecked():
+ flags.append('-%s' % key)
+
+ return ['g.extension'] + flags + ['extension=' + name,
+ 'svnurl=' + self.repo.GetValue().strip()]
+
+ def OnUpdateStatusBar(self, event):
+ """!Update statusbar text"""
+ element = self.search.GetSelection()
+ if not self.tree.IsLoaded():
+ self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
+ return
+
+ self.tree.SearchItems(element = element,
+ value = event.GetString())
+
+ nItems = len(self.tree.itemsMarked)
+ if event.GetString():
+ self.SetStatusText(_("%d items match") % nItems, 0)
+ else:
+ self.SetStatusText("", 0)
+
+ event.Skip()
+
+ def OnCloseWindow(self, event):
+ """!Close window"""
+ self.Destroy()
+
+ def OnFetch(self, event):
+ """!Fetch list of available extensions"""
+ wx.BeginBusyCursor()
+ self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
+ self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
+ self.SetStatusText("", 0)
+ wx.EndBusyCursor()
+
+ def OnItemActivated(self, event):
+ item = event.GetItem()
+ data = self.tree.GetPyData(item)
+ if data and 'command' in data:
+ self.OnInstall(event = None)
+
+ def OnInstall(self, event):
+ """!Install selected extension"""
+ log = self.parent.GetLogWindow()
+ log.RunCmd(self._getCmd(), onDone = self.OnDone)
+
+ def OnDone(self, cmd, returncode):
+ item = self.tree.GetSelected()
+ if not item or not item.IsOk() or \
+ returncode != 0 or \
+ not os.getenv('GRASS_ADDON_PATH'):
+ return
+
+ name = self.tree.GetItemText(item)
+ globalvar.grassCmd['all'].append(name)
+
+ def OnItemSelected(self, event):
+ """!Item selected"""
+ item = event.GetItem()
+ self.tree.itemSelected = item
+ data = self.tree.GetPyData(item)
+ if not data:
+ self.SetStatusText('', 0)
+ self.btnInstall.Enable(False)
+ else:
+ self.SetStatusText(data.get('description', ''), 0)
+ self.btnInstall.Enable(True)
+
+ def OnShowItem(self, event):
+ """!Show selected item"""
+ self.tree.OnShowItem(event)
+ if self.tree.GetSelected():
+ self.btnInstall.Enable()
+ else:
+ self.btnInstall.Enable(False)
+
+ def OnCmdDialog(self, event):
+ """!Shows command dialog"""
+ GUI(parent = self).ParseCommand(cmd = self._getCmd())
+
+class ExtensionTree(ItemTree):
+ """!List of available extensions"""
+ def __init__(self, parent, log, id = wx.ID_ANY,
+ ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
+ CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
+ **kwargs):
+ self.parent = parent # GMFrame
+ self.log = log
+
+ super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
+
+ self._initTree()
+
+ def _initTree(self):
+ for prefix in ('display', 'database',
+ 'general', 'imagery',
+ 'misc', 'postscript', 'paint',
+ 'raster', 'raster3D', 'sites', 'vector', 'wxGUI', 'other'):
+ self.AppendItem(parentId = self.root,
+ text = prefix)
+ self._loaded = False
+
+ def _expandPrefix(self, c):
+ name = { 'd' : 'display',
+ 'db' : 'database',
+ 'g' : 'general',
+ 'i' : 'imagery',
+ 'm' : 'misc',
+ 'ps' : 'postscript',
+ 'p' : 'paint',
+ 'r' : 'raster',
+ 'r3' : 'raster3D',
+ 's' : 'sites',
+ 'v' : 'vector',
+ 'wx' : 'wxGUI',
+ 'u' : 'other' }
+
+ if c in name:
+ return name[c]
+
+ return c
+
+ def _findItem(self, text):
+ """!Find item"""
+ item = self.GetFirstChild(self.root)[0]
+ while item and item.IsOk():
+ if text == self.GetItemText(item):
+ return item
+
+ item = self.GetNextSibling(item)
+
+ return None
+
+ def Load(self, url, full = False):
+ """!Load list of extensions"""
+ self.DeleteAllItems()
+ self.root = self.AddRoot(_("Menu tree"))
+ self._initTree()
+
+ if full:
+ flags = 'g'
+ else:
+ flags = 'l'
+ ret = RunCommand('g.extension', read = True,
+ svnurl = url,
+ flags = flags, quiet = True)
+ if not ret:
+ return
+
+ mdict = dict()
+ for line in ret.splitlines():
+ if full:
+ key, value = line.split('=', 1)
+ if key == 'name':
+ try:
+ prefix, name = value.split('.', 1)
+ except ValueError:
+ prefix = 'u'
+ name = value
+ if prefix not in mdict:
+ mdict[prefix] = dict()
+ mdict[prefix][name] = dict()
+ else:
+ mdict[prefix][name][key] = value
+ else:
+ try:
+ prefix, name = line.strip().split('.', 1)
+ except:
+ prefix = 'unknown'
+ name = line.strip()
+
+ if self._expandPrefix(prefix) == prefix:
+ prefix = 'unknown'
+
+ if prefix not in mdict:
+ mdict[prefix] = dict()
+
+ mdict[prefix][name] = { 'command' : prefix + '.' + name }
+
+ for prefix in mdict.keys():
+ prefixName = self._expandPrefix(prefix)
+ item = self._findItem(prefixName)
+ names = mdict[prefix].keys()
+ names.sort()
+ for name in names:
+ new = self.AppendItem(parentId = item,
+ text = prefix + '.' + name)
+ data = dict()
+ for key in mdict[prefix][name].keys():
+ data[key] = mdict[prefix][name][key]
+
+ self.SetPyData(new, data)
+
+ self._loaded = True
+
+ def IsLoaded(self):
+ """Check if items are loaded"""
+ return self._loaded
+
+class HelpWindow(wx.html.HtmlWindow):
+ """!This panel holds the text from GRASS docs.
+
+ GISBASE must be set in the environment to find the html docs dir.
+ The SYNOPSIS section is skipped, since this Panel is supposed to
+ be integrated into the cmdPanel and options are obvious there.
+ """
+ def __init__(self, parent, grass_command, text, skip_description,
+ **kwargs):
+ """!If grass_command is given, the corresponding HTML help
+ file will be presented, with all links pointing to absolute
+ paths of local files.
+
+ If 'skip_description' is True, the HTML corresponding to
+ SYNOPSIS will be skipped, thus only presenting the help file
+ from the DESCRIPTION section onwards.
+
+ If 'text' is given, it must be the HTML text to be presented
+ in the Panel.
+ """
+ self.parent = parent
+ wx.InitAllImageHandlers()
+ wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
+
+ gisbase = os.getenv("GISBASE")
+ self.loaded = False
+ self.history = list()
+ self.historyIdx = 0
+ self.fspath = os.path.join(gisbase, "docs", "html")
+
+ self.SetStandardFonts (size = 10)
+ self.SetBorders(10)
+
+ if text is None:
+ if skip_description:
+ url = os.path.join(self.fspath, grass_command + ".html")
+ self.fillContentsFromFile(url,
+ skip_description = skip_description)
+ self.history.append(url)
+ self.loaded = True
+ else:
+ ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
+ # self.LoadPage(self.fspath + grass_command + ".html")
+ self.loaded = False
+ else:
+ self.SetPage(text)
+ self.loaded = True
+
+ def OnLinkClicked(self, linkinfo):
+ url = linkinfo.GetHref()
+ if url[:4] != 'http':
+ url = os.path.join(self.fspath, url)
+ self.history.append(url)
+ self.historyIdx += 1
+ self.parent.OnHistory()
+
+ super(HelpWindow, self).OnLinkClicked(linkinfo)
+
+ def fillContentsFromFile(self, htmlFile, skip_description = True):
+ """!Load content from file"""
+ aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
+ imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
+ try:
+ contents = []
+ skip = False
+ for l in file(htmlFile, "rb").readlines():
+ if "DESCRIPTION" in l:
+ skip = False
+ if not skip:
+ # do skip the options description if requested
+ if "SYNOPSIS" in l:
+ skip = skip_description
+ else:
+ # FIXME: find only first item
+ findALink = aLink.search(l)
+ if findALink is not None:
+ contents.append(aLink.sub(findALink.group(1)+
+ self.fspath+findALink.group(2),l))
+ findImgLink = imgLink.search(l)
+ if findImgLink is not None:
+ contents.append(imgLink.sub(findImgLink.group(1)+
+ self.fspath+findImgLink.group(2),l))
+
+ if findALink is None and findImgLink is None:
+ contents.append(l)
+ self.SetPage("".join(contents))
+ self.loaded = True
+ except: # The Manual file was not found
+ self.loaded = False
+
+class HelpPanel(wx.Panel):
+ def __init__(self, parent, grass_command = "index", text = None,
+ skip_description = False, **kwargs):
+ self.grass_command = grass_command
+ wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+
+ self.content = HelpWindow(self, grass_command, text,
+ skip_description)
+
+ self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
+ label = _("&Next"))
+ self.btnNext.Enable(False)
+ self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
+ label = _("&Previous"))
+ self.btnPrev.Enable(False)
+
+ self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
+ self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
+
+ self._layout()
+
+ def _layout(self):
+ """!Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ btnSizer.Add(item = self.btnPrev, proportion = 0,
+ flag = wx.ALL, border = 5)
+ btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
+ btnSizer.Add(item = self.btnNext, proportion = 0,
+ flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+ sizer.Add(item = self.content, proportion = 1,
+ flag = wx.EXPAND)
+ sizer.Add(item = btnSizer, proportion = 0,
+ flag = wx.EXPAND)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def LoadPage(self, path = None):
+ """!Load page"""
+ if not path:
+ path = os.path.join(self.content.fspath, self.grass_command + ".html")
+ self.content.history.append(path)
+ self.content.LoadPage(path)
+
+ def IsFile(self):
+ """!Check if file exists"""
+ return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html"))
+
+ def IsLoaded(self):
+ return self.content.loaded
+
+ def OnHistory(self):
+ """!Update buttons"""
+ nH = len(self.content.history)
+ iH = self.content.historyIdx
+ if iH == nH - 1:
+ self.btnNext.Enable(False)
+ elif iH > -1:
+ self.btnNext.Enable(True)
+ if iH < 1:
+ self.btnPrev.Enable(False)
+ else:
+ self.btnPrev.Enable(True)
+
+ def OnNext(self, event):
+ """Load next page"""
+ self.content.historyIdx += 1
+ idx = self.content.historyIdx
+ path = self.content.history[idx]
+ self.content.LoadPage(path)
+ self.OnHistory()
+
+ event.Skip()
+
+ def OnPrev(self, event):
+ """Load previous page"""
+ self.content.historyIdx -= 1
+ idx = self.content.historyIdx
+ path = self.content.history[idx]
+ self.content.LoadPage(path)
+ self.OnHistory()
+
+ event.Skip()
Copied: grass/trunk/gui/wxpython/gui_core/goutput.py (from rev 49324, grass/trunk/gui/wxpython/gui_modules/goutput.py)
===================================================================
--- grass/trunk/gui/wxpython/gui_core/goutput.py (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/goutput.py 2011-11-24 11:46:55 UTC (rev 49347)
@@ -0,0 +1,1154 @@
+"""!
+ at package gui_core.goutput
+
+ at brief Command output widgets
+
+Classes:
+ - CmdThread
+ - GMConsole
+ - GMStc
+ - GMStdout
+ - GMStderr
+
+(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.
+
+ at author Michael Barton (Arizona State University)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (copy&paste customization)
+"""
+
+import os
+import sys
+import textwrap
+import time
+import threading
+import Queue
+import codecs
+import locale
+
+import wx
+import wx.stc
+from wx.lib.newevent import NewEvent
+
+import grass.script as grass
+from grass.script import task as gtask
+
+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()
+wxCmdRun, EVT_CMD_RUN = NewEvent()
+wxCmdDone, EVT_CMD_DONE = NewEvent()
+wxCmdAbort, EVT_CMD_ABORT = NewEvent()
+wxCmdPrepare, EVT_CMD_PREPARE = NewEvent()
+
+def GrassCmd(cmd, stdout = None, stderr = None):
+ """!Return GRASS command thread"""
+ return CommandThread(cmd,
+ stdout = stdout, stderr = stderr)
+
+class CmdThread(threading.Thread):
+ """!Thread for GRASS commands"""
+ requestId = 0