GRASS Programmer's Manual  6.4.2(2012)
wxgui.py
Go to the documentation of this file.
00001 """!
00002 @package wxgui.py
00003 
00004 @brief Main Python app for GRASS wxPython GUI. Main menu, layer management
00005 toolbar, notebook control for display management and access to
00006 command console.
00007 
00008 Classes:
00009  - GMFrame
00010  - GMApp
00011 
00012 (C) 2006-2011 by the GRASS Development Team
00013 This program is free software under the GNU General Public
00014 License (>=v2). Read the file COPYING that comes with GRASS
00015 for details.
00016 
00017 @author Michael Barton (Arizona State University)
00018 @author Jachym Cepicky (Mendel University of Agriculture)
00019 @author Martin Landa <landa.martin gmail.com>
00020 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
00021 """
00022 
00023 import sys
00024 import os
00025 import time
00026 import string
00027 import getopt
00028 import platform
00029 import signal
00030 import tempfile
00031 
00032 ### XML
00033 try:
00034     import xml.etree.ElementTree as etree
00035 except ImportError:
00036     import elementtree.ElementTree as etree # Python <= 2.4
00037 
00038 ### i18N
00039 import gettext
00040 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
00041 
00042 from gui_modules import globalvar
00043 import wx
00044 import wx.aui
00045 import wx.combo
00046 import wx.html
00047 import wx.stc
00048 try:
00049     import wx.lib.agw.customtreectrl as CT
00050     import wx.lib.agw.flatnotebook   as FN
00051 except ImportError:
00052     import wx.lib.customtreectrl as CT
00053     import wx.lib.flatnotebook   as FN
00054 
00055 try:
00056     import wx.lib.agw.advancedsplash as SC
00057 except ImportError:
00058     SC = None
00059 
00060 sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
00061 from grass.script import core as grass
00062 
00063 from gui_modules import utils
00064 from gui_modules import preferences
00065 from gui_modules import layertree
00066 from gui_modules import mapdisp
00067 from gui_modules import menudata
00068 from gui_modules import menuform
00069 from gui_modules import histogram
00070 from gui_modules import profile
00071 from gui_modules import mcalc_builder as mapcalculator
00072 from gui_modules import gcmd
00073 from gui_modules import dbm
00074 from gui_modules import workspace
00075 from gui_modules import goutput
00076 from gui_modules import gdialogs
00077 from gui_modules import colorrules
00078 from gui_modules import ogc_services
00079 from gui_modules import prompt
00080 from gui_modules import menu
00081 from gui_modules import gmodeler
00082 from gui_modules import vclean
00083 from gui_modules import nviz_tools
00084 from gui_modules.debug    import Debug
00085 from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow, UninstallExtensionWindow
00086 from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar, LMMiscToolbar, LMVectorToolbar
00087 from gui_modules.gpyshell import PyShellWindow
00088 from icons.icon           import Icons
00089 
00090 UserSettings = preferences.globalSettings
00091 
00092 class GMFrame(wx.Frame):
00093     """!Layer Manager frame with notebook widget for controlling GRASS
00094     GIS. Includes command console page for typing GRASS (and other)
00095     commands, tree widget page for managing map layers.
00096     """
00097     def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS Layer Manager"),
00098                  workspace = None,
00099                  size = globalvar.GM_WINDOW_SIZE, style = wx.DEFAULT_FRAME_STYLE, **kwargs):
00100         self.parent    = parent
00101         self.baseTitle = title
00102         self.iconsize  = (16, 16)
00103         
00104         wx.Frame.__init__(self, parent = parent, id = id, size = size,
00105                           style = style, **kwargs)
00106                           
00107         self.SetTitle(self.baseTitle)
00108         self.SetName("LayerManager")
00109 
00110         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00111 
00112         self._auimgr = wx.aui.AuiManager(self)
00113 
00114         # initialize variables
00115         self.disp_idx      = 0            # index value for map displays and layer trees
00116         self.curr_page     = None         # currently selected page for layer tree notebook
00117         self.curr_pagenum  = None         # currently selected page number for layer tree notebook
00118         self.workspaceFile = workspace    # workspace file
00119         self.workspaceChanged = False     # track changes in workspace
00120         self.georectifying = None         # reference to GCP class or None
00121         self.gcpmanagement = None         # reference to GCP class or None
00122         # list of open dialogs
00123         self.dialogs        = dict()
00124         self.dialogs['preferences'] = None
00125         self.dialogs['atm'] = list()
00126         
00127         # creating widgets
00128         self._createMenuBar()
00129         self.statusbar = self.CreateStatusBar(number = 1)
00130         self.notebook  = self._createNoteBook()
00131         self.toolbars  = { 'workspace' : LMWorkspaceToolbar(parent = self),
00132                            'data'      : LMDataToolbar(parent = self),
00133                            'tools'     : LMToolsToolbar(parent = self),
00134                            'misc'      : LMMiscToolbar(parent = self),
00135                            'vector'    : LMVectorToolbar(parent = self) }
00136         
00137         self._toolbarsData = { 'workspace' : ("toolbarWorkspace",     # name
00138                                               _("Workspace Toolbar"), # caption
00139                                               1),                     # row
00140                                'data'      : ("toolbarData",
00141                                               _("Data Toolbar"),
00142                                               1),
00143                                'misc'      : ("toolbarMisc",
00144                                               _("Misc Toolbar"),
00145                                               2),
00146                                'tools'     : ("toolbarTools",
00147                                               _("Tools Toolbar"),
00148                                               2),
00149                                'vector'    : ("toolbarVector",
00150                                               _("Vector Toolbar"),
00151                                               2),
00152                                }
00153         if sys.platform == 'win32':
00154             self._toolbarsList = ('workspace', 'data',
00155                                   'vector', 'tools', 'misc')
00156         else:
00157             self._toolbarsList = ('data', 'workspace',
00158                                   'misc', 'tools', 'vector')
00159         for toolbar in self._toolbarsList:
00160             name, caption, row = self._toolbarsData[toolbar]
00161             self._auimgr.AddPane(self.toolbars[toolbar],
00162                                  wx.aui.AuiPaneInfo().
00163                                  Name(name).Caption(caption).
00164                                  ToolbarPane().Top().Row(row).
00165                                  LeftDockable(False).RightDockable(False).
00166                                  BottomDockable(False).TopDockable(True).
00167                                  CloseButton(False).Layer(2).
00168                                  BestSize((self.toolbars[toolbar].GetBestSize())))
00169         
00170         # bindings
00171         self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
00172         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
00173 
00174         # minimal frame size
00175         self.SetMinSize((500, 400))
00176 
00177         # AUI stuff
00178         self._auimgr.AddPane(self.notebook, wx.aui.AuiPaneInfo().
00179                              Left().CentrePane().BestSize((-1,-1)).Dockable(False).
00180                              CloseButton(False).DestroyOnClose(True).Row(1).Layer(0))
00181 
00182         self._auimgr.Update()
00183 
00184         wx.CallAfter(self.notebook.SetSelectionByName, 'layers')
00185         
00186         # use default window layout ?
00187         if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
00188             dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
00189             try:
00190                x, y = map(int, dim.split(',')[0:2])
00191                w, h = map(int, dim.split(',')[2:4])
00192                self.SetPosition((x, y))
00193                self.SetSize((w, h))
00194             except:
00195                 pass
00196         else:
00197             self.Centre()
00198         
00199         self.Layout()
00200         self.Show()
00201         
00202         # load workspace file if requested
00203         if self.workspaceFile:
00204             # load given workspace file
00205             if self.LoadWorkspaceFile(self.workspaceFile):
00206                 self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
00207             else:
00208                 self.workspaceFile = None
00209         else:
00210             # start default initial display
00211             self.NewDisplay(show = False)
00212 
00213         # show map display widnow
00214         # -> OnSize() -> UpdateMap()
00215         if self.curr_page and not self.curr_page.maptree.mapdisplay.IsShown():
00216             self.curr_page.maptree.mapdisplay.Show()
00217         
00218         # redirect stderr to log area    
00219         self.goutput.Redirect()
00220         # fix goutput's pane size
00221         self.goutput.SetSashPosition(int(self.GetSize()[1] * .60))
00222 
00223         self.workspaceChanged = False
00224         
00225         # start with layer manager on top
00226         if self.curr_page:
00227             self.curr_page.maptree.mapdisplay.Raise()
00228         wx.CallAfter(self.Raise)
00229         
00230     def _createMenuBar(self):
00231         """!Creates menu bar"""
00232         self.menubar = menu.Menu(parent = self, data = menudata.ManagerData())
00233         self.SetMenuBar(self.menubar)
00234         self.menucmd = self.menubar.GetCmd()
00235         
00236     def _setCopyingOfSelectedText(self):
00237         copy = UserSettings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled')
00238         self.goutput.SetCopyingOfSelectedText(copy)
00239         
00240     def _createNoteBook(self):
00241         """!Creates notebook widgets"""
00242         self.notebook = menuform.GNotebook(parent = self, style = globalvar.FNPageDStyle)
00243         # create displays notebook widget and add it to main notebook page
00244         cbStyle = globalvar.FNPageStyle
00245         if globalvar.hasAgw:
00246             self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, agwStyle = cbStyle)
00247         else:
00248             self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, style = cbStyle)
00249         self.gm_cb.SetTabAreaColour(globalvar.FNPageColor)
00250         self.notebook.AddPage(page = self.gm_cb, text = _("Map layers"), name = 'layers')
00251         
00252         # create 'command output' text area
00253         self.goutput = goutput.GMConsole(self)
00254         self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
00255         self._setCopyingOfSelectedText()
00256         
00257         # create 'search module' notebook page
00258         if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'):
00259             self.search = MenuTreeWindow(parent = self)
00260             self.notebook.AddPage(page = self.search, text = _("Search module"), name = 'search')
00261         else:
00262             self.search = None
00263         
00264         # create 'python shell' notebook page
00265         if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'):
00266             self.pyshell = PyShellWindow(parent = self)
00267             self.notebook.AddPage(page = self.pyshell, text = _("Python shell"), name = 'pyshell')
00268         else:
00269             self.pyshell = None
00270         
00271         # bindings
00272         self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,    self.OnCBPageChanged)
00273         self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
00274         self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING,    self.OnCBPageClosed)
00275         
00276         return self.notebook
00277             
00278     def AddNviz(self):
00279         """!Add nviz notebook page"""
00280         self.nviz = nviz_tools.NvizToolWindow(parent = self,
00281                                               display = self.curr_page.maptree.GetMapDisplay()) 
00282         self.notebook.AddPage(page = self.nviz, text = _("3D view"), name = 'nviz')
00283         self.notebook.SetSelectionByName('nviz')
00284         
00285     def RemoveNviz(self):
00286         """!Remove nviz notebook page"""
00287         # print self.notebook.GetPage(1)
00288         self.notebook.RemovePage(self.notebook.GetPageIndexByName('nviz'))
00289         del self.nviz
00290         self.notebook.SetSelectionByName('layers')
00291         
00292     def WorkspaceChanged(self):
00293         """!Update window title"""
00294         if not self.workspaceChanged:
00295             self.workspaceChanged = True
00296         
00297         if self.workspaceFile:
00298             self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile) + '*')
00299         
00300     def OnSettingsChanged(self, event):
00301         """!Here can be functions which have to be called after EVT_SETTINGS_CHANGED. 
00302         Now only set copying of selected text to clipboard (in goutput).
00303         """
00304         ### self._createMenuBar() # bug when menu is re-created on the fly
00305         self._setCopyingOfSelectedText()
00306         
00307     def OnGCPManager(self, event):
00308         """!Launch georectifier module
00309         """
00310         from gui_modules import gcpmanager
00311         gcpmanager.GCPWizard(self)
00312 
00313     def OnGModeler(self, event):
00314         """!Launch Graphical Modeler"""
00315         win = gmodeler.ModelFrame(parent = self)
00316         win.CentreOnScreen()
00317         
00318         win.Show()
00319         
00320     def OnPsMap(self, event):
00321         """!Launch Cartographic Composer
00322         """
00323         try:
00324             from gui_modules import psmap
00325         except:
00326             gcmd.GError(parent = self.parent,
00327                         message = _("Hardcopy Map Output Utility is not available. You can install it by %s") % \
00328                             'g.extension.py -s svnurl=https://svn.osgeo.org/grass/grass-addons extension=wx.psmap')
00329             return
00330         
00331         win = psmap.PsMapFrame(parent = self)
00332         win.CentreOnScreen()
00333         
00334         win.Show()
00335         
00336     def OnDone(self, cmd, returncode):
00337         """Command execution finised"""
00338         if hasattr(self, "model"):
00339             self.model.DeleteIntermediateData(log = self.goutput)
00340             del self.model
00341         self.SetStatusText('')
00342         
00343     def OnRunModel(self, event):
00344         """!Run model"""
00345         filename = ''
00346         dlg = wx.FileDialog(parent = self, message =_("Choose model to run"),
00347                             defaultDir = os.getcwd(),
00348                             wildcard = _("GRASS Model File (*.gxm)|*.gxm"))
00349         if dlg.ShowModal() == wx.ID_OK:
00350             filename = dlg.GetPath()
00351         
00352         if not filename:
00353             dlg.Destroy()
00354             return
00355         
00356         self.model = gmodeler.Model()
00357         self.model.LoadModel(filename)
00358         self.model.Run(log = self.goutput, onDone = self.OnDone, parent = self)
00359         
00360         dlg.Destroy()
00361         
00362     def OnMapsets(self, event):
00363         """!Launch mapset access dialog
00364         """
00365         dlg = preferences.MapsetAccess(parent = self, id = wx.ID_ANY)
00366         dlg.CenterOnScreen()
00367         
00368         if dlg.ShowModal() == wx.ID_OK:
00369             ms = dlg.GetMapsets()
00370             gcmd.RunCommand('g.mapsets',
00371                             parent = self,
00372                             mapset = '%s' % ','.join(ms))
00373         
00374     def OnCBPageChanged(self, event):
00375         """!Page in notebook (display) changed"""
00376         old_pgnum = event.GetOldSelection()
00377         new_pgnum = event.GetSelection()
00378         
00379         self.curr_page   = self.gm_cb.GetCurrentPage()
00380         self.curr_pagenum = self.gm_cb.GetSelection()
00381         try:
00382             self.curr_page.maptree.mapdisplay.SetFocus()
00383             self.curr_page.maptree.mapdisplay.Raise()
00384         except:
00385             pass
00386         
00387         event.Skip()
00388 
00389     def OnPageChanged(self, event):
00390         """!Page in notebook changed"""
00391         page = event.GetSelection()
00392         if page == self.notebook.GetPageIndexByName('output'):
00393             # remove '(...)'
00394             self.notebook.SetPageText(page, _("Command console"))
00395             wx.CallAfter(self.goutput.cmd_prompt.SetFocus)
00396         self.SetStatusText('', 0)
00397         
00398         event.Skip()
00399 
00400     def OnCBPageClosed(self, event):
00401         """!Page of notebook closed
00402         Also close associated map display
00403         """
00404         if UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
00405             maptree = self.curr_page.maptree
00406             
00407             if self.workspaceFile:
00408                 message = _("Do you want to save changes in the workspace?")
00409             else:
00410                 message = _("Do you want to store current settings "
00411                             "to workspace file?")
00412             
00413             # ask user to save current settings
00414             if maptree.GetCount() > 0:
00415                 dlg = wx.MessageDialog(self,
00416                                        message = message,
00417                                        caption = _("Close Map Display %d") % (self.curr_pagenum + 1),
00418                                        style = wx.YES_NO | wx.YES_DEFAULT |
00419                                        wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
00420                 ret = dlg.ShowModal()
00421                 if ret == wx.ID_YES:
00422                     if not self.workspaceFile:
00423                         self.OnWorkspaceSaveAs()
00424                     else:
00425                         self.SaveToWorkspaceFile(self.workspaceFile)
00426                 elif ret == wx.ID_CANCEL:
00427                     event.Veto()
00428                     dlg.Destroy()
00429                     return
00430                 dlg.Destroy()
00431 
00432         self.gm_cb.GetPage(event.GetSelection()).maptree.Map.Clean()
00433         self.gm_cb.GetPage(event.GetSelection()).maptree.Close(True)
00434 
00435         self.curr_page = None
00436 
00437         event.Skip()
00438 
00439     def GetLayerTree(self):
00440         """!Get current layer tree"""
00441         return self.curr_page.maptree
00442     
00443     def GetLogWindow(self):
00444         """!Get widget for command output"""
00445         return self.goutput
00446     
00447     def GetMenuCmd(self, event):
00448         """!Get GRASS command from menu item
00449 
00450         Return command as a list"""
00451         layer = None
00452         
00453         if event:
00454             cmd = self.menucmd[event.GetId()]
00455         
00456         try:
00457             cmdlist = cmd.split(' ')
00458         except: # already list?
00459             cmdlist = cmd
00460         
00461         # check list of dummy commands for GUI modules that do not have GRASS
00462         # bin modules or scripts. 
00463         if cmd in ['vcolors', 'r.mapcalc', 'r3.mapcalc']:
00464             return cmdlist
00465 
00466         try:
00467             layer = self.curr_page.maptree.layer_selected
00468             name = self.curr_page.maptree.GetPyData(layer)[0]['maplayer'].name
00469             type = self.curr_page.maptree.GetPyData(layer)[0]['type']
00470         except:
00471             layer = None
00472 
00473         if layer and len(cmdlist) == 1: # only if no paramaters given
00474             if (type == 'raster' and cmdlist[0][0] == 'r' and cmdlist[0][1] != '3') or \
00475                     (type == 'vector' and cmdlist[0][0] == 'v'):
00476                 input = menuform.GUI().GetCommandInputMapParamKey(cmdlist[0])
00477                 if input:
00478                     cmdlist.append("%s=%s" % (input, name))
00479         
00480         return cmdlist
00481 
00482     def RunMenuCmd(self, event = None, cmd = []):
00483         """!Run command selected from menu"""
00484         if event:
00485             cmd = self.GetMenuCmd(event)
00486         self.goutput.RunCmd(cmd, switchPage = False)
00487 
00488     def OnMenuCmd(self, event = None, cmd = []):
00489         """!Parse command selected from menu"""
00490         if event:
00491             cmd = self.GetMenuCmd(event)
00492         menuform.GUI(parent = self).ParseCommand(cmd)
00493         
00494     def OnVDigit(self, event):
00495         """!Start vector digitizer
00496         """
00497         if not self.curr_page:
00498             self.MsgNoLayerSelected()
00499             return
00500         
00501         tree = self.GetLayerTree()
00502         layer = tree.layer_selected
00503         # no map layer selected
00504         if not layer:
00505             self.MsgNoLayerSelected()
00506             return
00507         
00508         # available only for vector map layers
00509         try:
00510             mapLayer = tree.GetPyData(layer)[0]['maplayer']
00511         except:
00512             mapLayer = None
00513         
00514         if not mapLayer or mapLayer.GetType() != 'vector':
00515             gcmd.GMessage(parent = self,
00516                           message = _("Selected map layer is not vector."))
00517             return
00518         
00519         if mapLayer.GetMapset() != grass.gisenv()['MAPSET']:
00520             gcmd.GMessage(parent = self,
00521                           message = _("Editing is allowed only for vector maps from the "
00522                                       "current mapset."))
00523             return
00524         
00525         if not tree.GetPyData(layer)[0]:
00526             return
00527         dcmd = tree.GetPyData(layer)[0]['cmd']
00528         if not dcmd:
00529             return
00530         
00531         tree.OnStartEditing(None)
00532         
00533     def OnRunScript(self, event):
00534         """!Run script"""
00535         # open dialog and choose script file
00536         dlg = wx.FileDialog(parent = self, message = _("Choose script file to run"),
00537                             defaultDir = os.getcwd(),
00538                             wildcard = _("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"))
00539         
00540         filename = None
00541         if dlg.ShowModal() == wx.ID_OK:
00542             filename = dlg.GetPath()
00543         
00544         if not filename:
00545             return False
00546 
00547         if not os.path.exists(filename):
00548             gcmd.GError(parent = self,
00549                         message = _("Script file '%s' doesn't exist. "
00550                                     "Operation cancelled.") % filename)
00551             return
00552         
00553         self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
00554         self.goutput.RunCmd([filename], switchPage = True)
00555         
00556     def OnChangeLocation(self, event):
00557         """Change current location"""
00558         dlg = gdialogs.LocationDialog(parent = self)
00559         if dlg.ShowModal() == wx.ID_OK:
00560             location, mapset = dlg.GetValues()
00561             if location and mapset:
00562                 ret = gcmd.RunCommand("g.gisenv",
00563                                       set = "LOCATION_NAME=%s" % location)
00564                 ret += gcmd.RunCommand("g.gisenv",
00565                                        set = "MAPSET=%s" % mapset)
00566                 if ret > 0:
00567                     wx.MessageBox(parent = self,
00568                                   message = _("Unable to switch to location <%(loc)s> mapset <%(mapset)s>.") % \
00569                                       { 'loc' : location, 'mapset' : mapset },
00570                                   caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00571                 else:
00572                     # close workspace
00573                     self.OnWorkspaceClose()
00574                     self.OnWorkspaceNew()
00575                     wx.MessageBox(parent = self,
00576                                   message = _("Current location is <%(loc)s>.\n"
00577                                               "Current mapset is <%(mapset)s>.") % \
00578                                       { 'loc' : location, 'mapset' : mapset },
00579                                   caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
00580                     
00581     def OnChangeMapset(self, event):
00582         """Change current mapset"""
00583         dlg = gdialogs.MapsetDialog(parent = self)
00584         if dlg.ShowModal() == wx.ID_OK:
00585             mapset = dlg.GetMapset()
00586             if mapset:
00587                 if gcmd.RunCommand("g.gisenv",
00588                                    set = "MAPSET=%s" % mapset) != 0:
00589                     wx.MessageBox(parent = self,
00590                                   message = _("Unable to switch to mapset <%s>.") % mapset,
00591                                   caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00592                 else:
00593                     wx.MessageBox(parent = self,
00594                                   message = _("Current mapset is <%s>.") % mapset,
00595                                   caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
00596         
00597     def OnNewVector(self, event):
00598         """!Create new vector map layer"""
00599         dlg = gdialogs.CreateNewVector(self, log = self.goutput,
00600                                        cmd = (('v.edit',
00601                                                { 'tool' : 'create' },
00602                                                'map')))
00603         
00604         if not dlg:
00605             return
00606         
00607         name = dlg.GetName(full = True)
00608         if name and dlg.IsChecked('add'):
00609             # add layer to map layer tree
00610             self.curr_page.maptree.AddLayer(ltype = 'vector',
00611                                             lname = name,
00612                                             lcmd = ['d.vect', 'map=%s' % name])
00613         
00614         # create table ?
00615         if dlg.IsChecked('table'):
00616             self.OnShowAttributeTable(None, selection = 1)
00617         
00618         dlg.Destroy()
00619         
00620     def OnAboutGRASS(self, event):
00621         """!Display 'About GRASS' dialog"""
00622         win = AboutWindow(self)
00623         win.CentreOnScreen()
00624         win.Show(True)  
00625         
00626     def _popupMenu(self, data):
00627         """!Create popup menu
00628         """
00629         point = wx.GetMousePosition()
00630         menu = wx.Menu()
00631         
00632         for key, handler in data:
00633             if key is None:
00634                 menu.AppendSeparator()
00635                 continue
00636             item = wx.MenuItem(menu, wx.ID_ANY, Icons['layerManager'][key].GetLabel())
00637             item.SetBitmap(Icons['layerManager'][key].GetBitmap(self.iconsize))
00638             menu.AppendItem(item)
00639             self.Bind(wx.EVT_MENU, handler, item)
00640         
00641         # create menu
00642         self.PopupMenu(menu)
00643         menu.Destroy()
00644 
00645     def OnImportMenu(self, event):
00646         """!Import maps menu (import, link)
00647         """
00648         self._popupMenu((('rastImport',    self.OnImportGdalLayers),
00649                          ('vectImport',    self.OnImportOgrLayers)))
00650         
00651     def OnWorkspaceNew(self, event = None):
00652         """!Create new workspace file
00653 
00654         Erase current workspace settings first
00655         """
00656         Debug.msg(4, "GMFrame.OnWorkspaceNew():")
00657         
00658         # start new map display if no display is available
00659         if not self.curr_page:
00660             self.NewDisplay()
00661         
00662         maptree = self.curr_page.maptree
00663         
00664         # ask user to save current settings
00665         if self.workspaceFile and self.workspaceChanged:
00666             self.OnWorkspaceSave()
00667         elif self.workspaceFile is None and maptree.GetCount() > 0:
00668              dlg = wx.MessageDialog(self, message = _("Current workspace is not empty. "
00669                                                     "Do you want to store current settings "
00670                                                     "to workspace file?"),
00671                                     caption = _("Create new workspace?"),
00672                                     style = wx.YES_NO | wx.YES_DEFAULT | \
00673                                         wx.CANCEL | wx.ICON_QUESTION)
00674              ret = dlg.ShowModal()
00675              if ret == wx.ID_YES:
00676                  self.OnWorkspaceSaveAs()
00677              elif ret == wx.ID_CANCEL:
00678                  dlg.Destroy()
00679                  return
00680              
00681              dlg.Destroy()
00682         
00683         # delete all items
00684         maptree.DeleteAllItems()
00685         
00686         # add new root element
00687         maptree.root = maptree.AddRoot("Map Layers")
00688         self.curr_page.maptree.SetPyData(maptree.root, (None,None))
00689         
00690         # no workspace file loaded
00691         self.workspaceFile = None
00692         self.workspaceChanged = False
00693         self.SetTitle(self.baseTitle)
00694         
00695     def OnWorkspaceOpen(self, event = None):
00696         """!Open file with workspace definition"""
00697         dlg = wx.FileDialog(parent = self, message = _("Choose workspace file"),
00698                             defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"))
00699 
00700         filename = ''
00701         if dlg.ShowModal() == wx.ID_OK:
00702             filename = dlg.GetPath()
00703 
00704         if filename == '':
00705             return
00706 
00707         Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
00708 
00709         # delete current layer tree content
00710         self.OnWorkspaceClose()
00711         
00712         self.LoadWorkspaceFile(filename)
00713 
00714         self.workspaceFile = filename
00715         self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
00716 
00717     def LoadWorkspaceFile(self, filename):
00718         """!Load layer tree definition stored in GRASS Workspace XML file (gxw)
00719 
00720         @todo Validate against DTD
00721         
00722         @return True on success
00723         @return False on error
00724         """
00725         # dtd
00726         dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxw.dtd")
00727         
00728         # parse workspace file
00729         try:
00730             gxwXml = workspace.ProcessWorkspaceFile(etree.parse(filename))
00731         except Exception, e:
00732             gcmd.GError(parent = self,
00733                         message = _("Reading workspace file <%s> failed.\n"
00734                                     "Invalid file, unable to parse XML document.") % filename)
00735             return
00736         
00737         busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
00738                            parent = self)
00739         wx.Yield()
00740 
00741         #
00742         # load layer manager window properties
00743         #
00744         if UserSettings.Get(group = 'workspace', key = 'posManager', subkey = 'enabled') is False:
00745             if gxwXml.layerManager['pos']:
00746                 self.SetPosition(gxwXml.layerManager['pos'])
00747             if gxwXml.layerManager['size']:
00748                 self.SetSize(gxwXml.layerManager['size'])
00749         
00750         #
00751         # start map displays first (list of layers can be empty)
00752         #
00753         displayId = 0
00754         mapdisplay = list()
00755         for display in gxwXml.displays:
00756             mapdisp = self.NewDisplay(show = False)
00757             mapdisplay.append(mapdisp)
00758             maptree = self.gm_cb.GetPage(displayId).maptree
00759             
00760             # set windows properties
00761             mapdisp.SetProperties(render = display['render'],
00762                                   mode = display['mode'],
00763                                   showCompExtent = display['showCompExtent'],
00764                                   constrainRes = display['constrainRes'],
00765                                   projection = display['projection']['enabled'])
00766 
00767             if display['projection']['enabled']:
00768                 if display['projection']['epsg']:
00769                     UserSettings.Set(group = 'display', key = 'projection', subkey = 'epsg',
00770                                      value = display['projection']['epsg'])
00771                     if display['projection']['proj']:
00772                         UserSettings.Set(group = 'display', key = 'projection', subkey = 'proj4',
00773                                          value = display['projection']['proj'])
00774             
00775             # set position and size of map display
00776             if UserSettings.Get(group = 'workspace', key = 'posDisplay', subkey = 'enabled') is False:
00777                 if display['pos']:
00778                     mapdisp.SetPosition(display['pos'])
00779                 if display['size']:
00780                     mapdisp.SetSize(display['size'])
00781                     
00782             # set extent if defined
00783             if display['extent']:
00784                 w, s, e, n = display['extent']
00785                 region = maptree.Map.region = maptree.Map.GetRegion(w = w, s = s, e = e, n = n)
00786                 mapdisp.GetWindow().ResetZoomHistory()
00787                 mapdisp.GetWindow().ZoomHistory(region['n'],
00788                                                 region['s'],
00789                                                 region['e'],
00790                                                 region['w'])
00791                 
00792             mapdisp.Show()
00793             
00794             displayId += 1
00795     
00796         maptree = None 
00797         selected = [] # list of selected layers
00798         # 
00799         # load list of map layers
00800         #
00801         for layer in gxwXml.layers:
00802             display = layer['display']
00803             maptree = self.gm_cb.GetPage(display).maptree
00804             
00805             newItem = maptree.AddLayer(ltype = layer['type'],
00806                                        lname = layer['name'],
00807                                        lchecked = layer['checked'],
00808                                        lopacity = layer['opacity'],
00809                                        lcmd = layer['cmd'],
00810                                        lgroup = layer['group'],
00811                                        lnviz = layer['nviz'],
00812                                        lvdigit = layer['vdigit'])
00813             
00814             if layer.has_key('selected'):
00815                 if layer['selected']:
00816                     selected.append((maptree, newItem))
00817                 else:
00818                     maptree.SelectItem(newItem, select = False)
00819             
00820         for maptree, layer in selected:
00821             if not maptree.IsSelected(layer):
00822                 maptree.SelectItem(layer, select = True)
00823                 maptree.layer_selected = layer
00824                 
00825         busy.Destroy()
00826             
00827         for mdisp in mapdisplay:
00828             mdisp.MapWindow2D.UpdateMap()
00829 
00830         return True
00831     
00832     def OnWorkspaceLoadGrcFile(self, event):
00833         """!Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
00834         dlg = wx.FileDialog(parent = self, message = _("Choose GRC file to load"),
00835                             defaultDir = os.getcwd(), wildcard = _("Old GRASS Workspace File (*.grc)|*.grc"))
00836 
00837         filename = ''
00838         if dlg.ShowModal() == wx.ID_OK:
00839             filename = dlg.GetPath()
00840 
00841         if filename == '':
00842             return
00843 
00844         Debug.msg(4, "GMFrame.OnWorkspaceLoadGrcFile(): filename=%s" % filename)
00845 
00846         # start new map display if no display is available
00847         if not self.curr_page:
00848             self.NewDisplay()
00849 
00850         busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
00851                            parent = self)
00852         wx.Yield()
00853 
00854         maptree = None
00855         for layer in workspace.ProcessGrcFile(filename).read(self):
00856             maptree = self.gm_cb.GetPage(layer['display']).maptree
00857             newItem = maptree.AddLayer(ltype = layer['type'],
00858                                        lname = layer['name'],
00859                                        lchecked = layer['checked'],
00860                                        lopacity = layer['opacity'],
00861                                        lcmd = layer['cmd'],
00862                                        lgroup = layer['group'])
00863 
00864             busy.Destroy()
00865             
00866         if maptree:
00867             # reverse list of map layers
00868             maptree.Map.ReverseListOfLayers()
00869 
00870     def OnWorkspaceSaveAs(self, event = None):
00871         """!Save workspace definition to selected file"""
00872         dlg = wx.FileDialog(parent = self, message = _("Choose file to save current workspace"),
00873                             defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"), style = wx.FD_SAVE)
00874 
00875         filename = ''
00876         if dlg.ShowModal() == wx.ID_OK:
00877             filename = dlg.GetPath()
00878 
00879         if filename == '':
00880             return False
00881 
00882         # check for extension
00883         if filename[-4:] != ".gxw":
00884             filename += ".gxw"
00885 
00886         if os.path.exists(filename):
00887             dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
00888                                                      "Do you want to overwrite this file?") % filename,
00889                                    caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
00890             if dlg.ShowModal() != wx.ID_YES:
00891                 dlg.Destroy()
00892                 return False
00893 
00894         Debug.msg(4, "GMFrame.OnWorkspaceSaveAs(): filename=%s" % filename)
00895 
00896         self.SaveToWorkspaceFile(filename)
00897         self.workspaceFile = filename
00898         self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
00899 
00900     def OnWorkspaceSave(self, event = None):
00901         """!Save file with workspace definition"""
00902         if self.workspaceFile:
00903             dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
00904                                                    "Do you want to overwrite this file?") % \
00905                                        self.workspaceFile,
00906                                    caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
00907             if dlg.ShowModal() == wx.ID_NO:
00908                 dlg.Destroy()
00909             else:
00910                 Debug.msg(4, "GMFrame.OnWorkspaceSave(): filename=%s" % self.workspaceFile)
00911                 self.SaveToWorkspaceFile(self.workspaceFile)
00912                 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
00913                 self.workspaceChanged = False
00914         else:
00915             self.OnWorkspaceSaveAs()
00916 
00917     def SaveToWorkspaceFile(self, filename):
00918         """!Save layer tree layout to workspace file
00919         
00920         Return True on success, False on error
00921         """
00922         tmpfile = tempfile.TemporaryFile(mode = 'w+b')
00923         try:
00924             workspace.WriteWorkspaceFile(lmgr = self, file = tmpfile)
00925         except StandardError, e:
00926             gcmd.GError(parent = self,
00927                         message = _("Writing current settings to workspace file "
00928                                     "failed."))
00929             return False
00930         
00931         try:
00932             mfile = open(filename, "w")
00933             tmpfile.seek(0)
00934             for line in tmpfile.readlines():
00935                 mfile.write(line)
00936         except IOError:
00937             gcmd.GError(parent = self,
00938                         message = _("Unable to open file <%s> for writing.") % filename)
00939             return False
00940         
00941         mfile.close()
00942         
00943         return True
00944     
00945     def OnWorkspaceClose(self, event = None):
00946         """!Close file with workspace definition
00947         
00948         If workspace has been modified ask user to save the changes.
00949         """
00950         Debug.msg(4, "GMFrame.OnWorkspaceClose(): file=%s" % self.workspaceFile)
00951         
00952         self.OnDisplayCloseAll()
00953         self.workspaceFile = None
00954         self.workspaceChanged = False
00955         self.SetTitle(self.baseTitle)
00956         self.disp_idx = 0
00957         self.curr_page = None
00958         
00959     def OnDisplayClose(self, event = None):
00960         """!Close current map display window
00961         """
00962         if self.curr_page and self.curr_page.maptree.mapdisplay:
00963             self.curr_page.maptree.mapdisplay.OnCloseWindow(event)
00964         
00965     def OnDisplayCloseAll(self, event = None):
00966         """!Close all open map display windows
00967         """
00968         displays = list()
00969         for page in range(0, self.gm_cb.GetPageCount()):
00970             displays.append(self.gm_cb.GetPage(page).maptree.mapdisplay)
00971         
00972         for display in displays:
00973             display.OnCloseWindow(event)
00974         
00975     def RulesCmd(self, event):
00976         """!Launches dialog for commands that need rules input and
00977         processes rules
00978         """
00979         cmd = self.GetMenuCmd(event)
00980         
00981         if cmd[0] == 'r.colors':
00982             ctable = colorrules.ColorTable(self, raster = True)
00983         else:
00984             ctable = colorrules.ColorTable(self, raster = False)
00985         ctable.CentreOnScreen()
00986         ctable.Show()
00987         
00988     def OnXTermNoXMon(self, event):
00989         """!
00990         Run commands that need xterm
00991         """
00992         self.OnXTerm(event, need_xmon = False)
00993         
00994     def OnXTerm(self, event, need_xmon = True):
00995         """!
00996         Run commands that need interactive xmon
00997 
00998         @param need_xmon True to start X monitor
00999         """
01000         # unset display mode
01001         del os.environ['GRASS_RENDER_IMMEDIATE']
01002         
01003         if need_xmon:
01004             # open next available xmon
01005             xmonlist = []
01006             
01007             # make list of xmons that are not running
01008             ret = gcmd.RunCommand('d.mon',
01009                                   flags = 'L',
01010                                   read = True)
01011             
01012             for line in ret.split('\n'):               
01013                 line = line.strip()
01014                 if line.startswith('x') and 'not running' in line:
01015                     xmonlist.append(line[0:2])
01016             
01017             # find available xmon
01018             xmon = xmonlist[0]
01019             
01020             # bring up the xmon
01021             cmdlist = ['d.mon', xmon]
01022             p = gcmd.Command(cmdlist, wait=False)
01023         
01024         # run the command        
01025         command = self.GetMenuCmd(event)
01026         command = ' '.join(command)
01027         
01028         gisbase = os.environ['GISBASE']
01029         
01030         if sys.platform == "win32":
01031             runbat = os.path.join(gisbase,'etc','grass-run.bat')
01032             cmdlist = ["start", runbat, runbat, command]
01033         else:
01034             if sys.platform == "darwin":
01035                 xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-mac')
01036             else:
01037                 xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-wrapper')
01038             
01039             grassrun = os.path.join(gisbase,'etc','grass-run.sh')
01040             cmdlist = [xtermwrapper, '-e', grassrun, command]
01041         
01042         p = gcmd.Command(cmdlist, wait=False)
01043         
01044         # reset display mode
01045         os.environ['GRASS_RENDER_IMMEDIATE'] = 'TRUE'
01046         
01047     def OnInstallExtension(self, event):
01048         """!Install extension from GRASS Addons SVN repository"""
01049         win = InstallExtensionWindow(self, size = (650, 550))
01050         win.CentreOnScreen()
01051         win.Show()
01052 
01053     def OnUninstallExtension(self, event):
01054         """!Uninstall extension"""
01055         win = UninstallExtensionWindow(self, size = (650, 300))
01056         win.CentreOnScreen()
01057         win.Show()
01058 
01059     def OnPreferences(self, event):
01060         """!General GUI preferences/settings
01061         """
01062         if not self.dialogs['preferences']:
01063             dlg = preferences.PreferencesDialog(parent = self)
01064             self.dialogs['preferences'] = dlg
01065             self.dialogs['preferences'].CenterOnScreen()
01066             
01067             dlg.Bind(preferences.EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
01068         
01069         self.dialogs['preferences'].ShowModal()
01070         
01071     def OnHelp(self, event):
01072         """!Show help
01073         """
01074         self.goutput.RunCmd(['g.manual','-i'])
01075         
01076     def DispHistogram(self, event):
01077         """
01078         Init histogram display canvas and tools
01079         """
01080         self.histogram = histogram.HistFrame(self,
01081                                              id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
01082                                              style = wx.DEFAULT_FRAME_STYLE)
01083 
01084         #show new display
01085         self.histogram.Show()
01086         self.histogram.Refresh()
01087         self.histogram.Update()
01088 
01089     def DispProfile(self, event):
01090         """
01091         Init profile canvas and tools
01092         """
01093         self.profile = profile.ProfileFrame(self,
01094                                            id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
01095                                            style = wx.DEFAULT_FRAME_STYLE)
01096         self.profile.Show()
01097         self.profile.Refresh()
01098         self.profile.Update()
01099         
01100     def OnMapCalculator(self, event, cmd = ''):
01101         """!Init map calculator for interactive creation of mapcalc statements
01102         """
01103         if event:
01104             try:
01105                 cmd = self.GetMenuCmd(event)
01106             except KeyError:
01107                 cmd = ['r.mapcalc']
01108         
01109         win = mapcalculator.MapCalcFrame(parent = self,
01110                                          cmd = cmd[0])
01111         win.CentreOnScreen()
01112         win.Show()
01113         
01114     def OnVectorCleaning(self, event, cmd = ''):
01115         """!Init interactive vector cleaning
01116         """
01117         
01118         if event:
01119             cmd = self.GetMenuCmd(event)
01120 
01121         win = vclean.VectorCleaningFrame(parent = self, cmd = cmd[0])
01122         win.CentreOnScreen()
01123         win.Show()
01124         
01125     def OnImportDxfFile(self, event, cmd = None):
01126         """!Convert multiple DXF layers to GRASS vector map layers"""
01127         dlg = gdialogs.DxfImportDialog(parent = self)
01128         dlg.CentreOnScreen()
01129         dlg.Show()
01130 
01131     def OnImportGdalLayers(self, event, cmd = None):
01132         """!Convert multiple GDAL layers to GRASS raster map layers"""
01133         dlg = gdialogs.GdalImportDialog(parent = self)
01134         dlg.CentreOnScreen()
01135         dlg.Show()
01136 
01137     def OnLinkGdalLayers(self, event, cmd = None):
01138         """!Link multiple GDAL layers to GRASS raster map layers"""
01139         dlg = gdialogs.GdalImportDialog(parent = self, link = True)
01140         dlg.CentreOnScreen()
01141         dlg.Show()
01142         
01143     def OnImportOgrLayers(self, event, cmd = None):
01144         """!Convert multiple OGR layers to GRASS vector map layers"""
01145         dlg = gdialogs.GdalImportDialog(parent = self, ogr = True)
01146         dlg.CentreOnScreen()
01147         dlg.Show()
01148         
01149     def OnLinkOgrLayers(self, event, cmd = None):
01150         """!Links multiple OGR layers to GRASS vector map layers"""
01151         dlg = gdialogs.GdalImportDialog(parent = self, ogr = True, link = True)
01152         dlg.CentreOnScreen()
01153         dlg.Show()
01154         
01155     def OnImportWMS(self, event):
01156         """!Import data from OGC WMS server"""
01157         dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
01158         dlg.CenterOnScreen()
01159         
01160         if dlg.ShowModal() == wx.ID_OK: # -> import layers
01161             layers = dlg.GetLayers()
01162             
01163             if len(layers.keys()) > 0:
01164                 for layer in layers.keys():
01165                     cmd = ['r.in.wms',
01166                            'mapserver=%s' % dlg.GetSettings()['server'],
01167                            'layers=%s' % layer,
01168                            'output=%s' % layer,
01169                            'format=png',
01170                            '--overwrite']
01171                     styles = ','.join(layers[layer])
01172                     if styles:
01173                         cmd.append('styles=%s' % styles)
01174                     self.goutput.RunCmd(cmd, switchPage = True)
01175 
01176                     self.curr_page.maptree.AddLayer(ltype = 'raster',
01177                                                     lname = layer,
01178                                                     lcmd = ['d.rast', 'map=%s' % layer],
01179                                                     multiple = False)
01180             else:
01181                 self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
01182                 
01183                 
01184         dlg.Destroy()
01185         
01186     def OnShowAttributeTable(self, event, selection = 0):
01187         """!Show attribute table of the given vector map layer
01188         """
01189         if not self.curr_page:
01190             self.MsgNoLayerSelected()
01191             return
01192         
01193         tree = self.GetLayerTree()
01194         layer = tree.layer_selected
01195         # no map layer selected
01196         if not layer:
01197             self.MsgNoLayerSelected()
01198             return
01199         
01200         # available only for vector map layers
01201         try:
01202             maptype = tree.GetPyData(layer)[0]['maplayer'].type
01203         except:
01204             maptype = None
01205         
01206         if not maptype or maptype != 'vector':
01207             gcmd.GMessage(parent = self,
01208                           message = _("Selected map layer is not vector."))
01209             return
01210         
01211         if not tree.GetPyData(layer)[0]:
01212             return
01213         dcmd = tree.GetPyData(layer)[0]['cmd']
01214         if not dcmd:
01215             return
01216         
01217         busy = wx.BusyInfo(message = _("Please wait, loading attribute data..."),
01218                            parent = self)
01219         wx.Yield()
01220         
01221         dbmanager = dbm.AttributeManager(parent = self, id = wx.ID_ANY,
01222                                          size = wx.Size(500, 300),
01223                                          item = layer, log = self.goutput,
01224                                          selection = selection)
01225         
01226         busy.Destroy()
01227         
01228         # register ATM dialog
01229         self.dialogs['atm'].append(dbmanager)
01230         
01231         # show ATM window
01232         dbmanager.Show()
01233         
01234     def OnNewDisplay(self, event = None):
01235         """!Create new layer tree and map display instance"""
01236         self.NewDisplay()
01237 
01238     def NewDisplay(self, show = True):
01239         """!Create new layer tree, which will
01240         create an associated map display frame
01241 
01242         @param show show map display window if True
01243 
01244         @return reference to mapdisplay intance
01245         """
01246         Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
01247         
01248         # make a new page in the bookcontrol for the layer tree (on page 0 of the notebook)
01249         self.pg_panel = wx.Panel(self.gm_cb, id = wx.ID_ANY, style = wx.EXPAND)
01250         self.gm_cb.AddPage(self.pg_panel, text = "Display "+ str(self.disp_idx + 1), select = True)
01251         self.curr_page = self.gm_cb.GetCurrentPage()
01252         
01253         # create layer tree (tree control for managing GIS layers)  and put on new notebook page
01254         self.curr_page.maptree = layertree.LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
01255                                                      size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
01256                                                      wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
01257                                                      wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
01258                                                      idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
01259                                                      auimgr = self._auimgr, showMapDisplay = show)
01260         
01261         # layout for controls
01262         cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
01263         cb_boxsizer.Add(self.curr_page.maptree, proportion = 1, flag = wx.EXPAND, border = 1)
01264         self.curr_page.SetSizer(cb_boxsizer)
01265         cb_boxsizer.Fit(self.curr_page.maptree)
01266         self.curr_page.Layout()
01267         self.curr_page.maptree.Layout()
01268         
01269         # use default window layout
01270         if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
01271             dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
01272             idx = 4 + self.disp_idx * 4
01273             try:
01274                 x, y = map(int, dim.split(',')[idx:idx + 2])
01275                 w, h = map(int, dim.split(',')[idx + 2:idx + 4])
01276                 self.curr_page.maptree.mapdisplay.SetPosition((x, y))
01277                 self.curr_page.maptree.mapdisplay.SetSize((w, h))
01278             except:
01279                 pass
01280         
01281         self.disp_idx += 1
01282         
01283         return self.curr_page.maptree.mapdisplay
01284     
01285     def OnAddMaps(self, event = None):
01286         """!Add selected map layers into layer tree"""
01287         dialog = gdialogs.AddMapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
01288 
01289         if dialog.ShowModal() == wx.ID_OK:
01290             # start new map display if no display is available
01291             if not self.curr_page:
01292                 self.NewDisplay()
01293 
01294             maptree = self.curr_page.maptree
01295             busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
01296                                parent = self)
01297             wx.Yield()
01298             
01299             for layerName in dialog.GetMapLayers():
01300                 if dialog.GetLayerType() == 'raster':
01301                     cmd = ['d.rast', 'map=%s' % layerName]
01302                 elif dialog.GetLayerType() == 'vector':
01303                     cmd = ['d.vect', 'map=%s' % layerName]
01304                 newItem = maptree.AddLayer(ltype = dialog.GetLayerType(),
01305                                            lname = layerName,
01306                                            lchecked = False,
01307                                            lopacity = 1.0,
01308                                            lcmd = cmd,
01309                                            lgroup = None)
01310 
01311             busy.Destroy()
01312     
01313     def OnAddRaster(self, event):
01314         """!Add raster map layer"""
01315         # start new map display if no display is available
01316         if not self.curr_page:
01317             self.NewDisplay(show = True)
01318         
01319         self.notebook.SetSelectionByName('layers')
01320         self.curr_page.maptree.AddLayer('raster')
01321         
01322     def OnAddRaster3D(self, event):
01323         """!Add 3D raster map layer"""
01324         # start new map display if no display is available
01325         if not self.curr_page:
01326             self.NewDisplay(show = True)
01327         
01328         self.AddRaster3D(event)
01329         
01330     def OnAddRasterMisc(self, event):
01331         """!Create misc raster popup-menu"""
01332         # start new map display if no display is available
01333         if not self.curr_page:
01334             self.NewDisplay(show = True)
01335         
01336         self._popupMenu((('addRast3d', self.OnAddRaster3D),
01337                          (None, None),
01338                          ('addRgb',    self.OnAddRasterRGB),
01339                          ('addHis',    self.OnAddRasterHIS),
01340                          (None, None),
01341                          ('addShaded', self.OnAddRasterShaded),
01342                          (None, None),
01343                          ('addRArrow', self.OnAddRasterArrow),
01344                          ('addRNum',   self.OnAddRasterNum)))
01345         
01346         # show map display
01347         self.curr_page.maptree.mapdisplay.Show()
01348         
01349     def OnAddVector(self, event):
01350         """!Add vector map to the current layer tree"""
01351         # start new map display if no display is available
01352         if not self.curr_page:
01353             self.NewDisplay(show = True)
01354         
01355         self.notebook.SetSelectionByName('layers')
01356         self.curr_page.maptree.AddLayer('vector')
01357 
01358     def OnAddVectorMisc(self, event):
01359         """!Create misc vector popup-menu"""
01360         # start new map display if no display is available
01361         if not self.curr_page:
01362             self.NewDisplay(show = True)
01363 
01364         self._popupMenu((('addThematic', self.OnAddVectorTheme),
01365                          ('addChart',    self.OnAddVectorChart)))
01366         
01367         # show map display
01368         self.curr_page.maptree.mapdisplay.Show()
01369 
01370     def OnAddVectorTheme(self, event):
01371         """!Add thematic vector map to the current layer tree"""
01372         self.notebook.SetSelectionByName('layers')
01373         self.curr_page.maptree.AddLayer('thememap')
01374 
01375     def OnAddVectorChart(self, event):
01376         """!Add chart vector map to the current layer tree"""
01377         self.notebook.SetSelectionByName('layers')
01378         self.curr_page.maptree.AddLayer('themechart')
01379 
01380     def OnAddOverlay(self, event):
01381         """!Create decoration overlay menu""" 
01382         # start new map display if no display is available
01383         if not self.curr_page:
01384             self.NewDisplay(show = True)
01385 
01386         self._popupMenu((('addGrid',     self.OnAddGrid),
01387                          ('addLabels',   self.OnAddLabels),
01388                          ('addGeodesic', self.OnAddGeodesic),
01389                          ('addRhumb',    self.OnAddRhumb),
01390                          (None, None),
01391                          ('addCmd',      self.OnAddCommand)))
01392         
01393         # show map display
01394         self.curr_page.maptree.mapdisplay.Show()
01395         
01396     def OnAddRaster3D(self, event):
01397         """!Add 3D raster map to the current layer tree"""
01398         self.notebook.SetSelectionByName('layers')
01399         self.curr_page.maptree.AddLayer('3d-raster')
01400 
01401     def OnAddRasterRGB(self, event):
01402         """!Add RGB raster map to the current layer tree"""
01403         self.notebook.SetSelectionByName('layers')
01404         self.curr_page.maptree.AddLayer('rgb')
01405 
01406     def OnAddRasterHIS(self, event):
01407         """!Add HIS raster map to the current layer tree"""
01408         self.notebook.SetSelectionByName('layers')
01409         self.curr_page.maptree.AddLayer('his')
01410 
01411     def OnAddRasterShaded(self, event):
01412         """!Add shaded relief raster map to the current layer tree"""
01413         self.notebook.SetSelectionByName('layers')
01414         self.curr_page.maptree.AddLayer('shaded')
01415 
01416     def OnAddRasterArrow(self, event):
01417         """!Add flow arrows raster map to the current layer tree"""
01418         self.notebook.SetSelectionByName('layers')
01419         self.curr_page.maptree.AddLayer('rastarrow')
01420 
01421     def OnAddRasterNum(self, event):
01422         """!Add cell number raster map to the current layer tree"""
01423         self.notebook.SetSelectionByName('layers')
01424         self.curr_page.maptree.AddLayer('rastnum')
01425 
01426     def OnAddCommand(self, event):
01427         """!Add command line map layer to the current layer tree"""
01428         # start new map display if no display is available
01429         if not self.curr_page:
01430             self.NewDisplay(show = True)
01431 
01432         self.notebook.SetSelectionByName('layers')
01433         self.curr_page.maptree.AddLayer('command')
01434 
01435         # show map display
01436         self.curr_page.maptree.mapdisplay.Show()
01437 
01438     def OnAddGroup(self, event):
01439         """!Add layer group"""
01440         # start new map display if no display is available
01441         if not self.curr_page:
01442             self.NewDisplay(show = True)
01443 
01444         self.notebook.SetSelectionByName('layers')
01445         self.curr_page.maptree.AddLayer('group')
01446 
01447         # show map display
01448         self.curr_page.maptree.mapdisplay.Show()
01449 
01450     def OnAddGrid(self, event):
01451         """!Add grid map layer to the current layer tree"""
01452         self.notebook.SetSelectionByName('layers')
01453         self.curr_page.maptree.AddLayer('grid')
01454 
01455     def OnAddGeodesic(self, event):
01456         """!Add geodesic line map layer to the current layer tree"""
01457         self.notebook.SetSelectionByName('layers')
01458         self.curr_page.maptree.AddLayer('geodesic')
01459 
01460     def OnAddRhumb(self, event):
01461         """!Add rhumb map layer to the current layer tree"""
01462         self.notebook.SetSelectionByName('layers')
01463         self.curr_page.maptree.AddLayer('rhumb')
01464 
01465     def OnAddLabels(self, event):
01466         """!Add vector labels map layer to the current layer tree"""
01467         # start new map display if no display is available
01468         if not self.curr_page:
01469             self.NewDisplay(show = True)
01470 
01471         self.notebook.SetSelectionByName('layers')
01472         self.curr_page.maptree.AddLayer('labels')
01473 
01474         # show map display
01475         self.curr_page.maptree.mapdisplay.Show()
01476 
01477     def OnDeleteLayer(self, event):
01478         """!Remove selected map layer from the current layer Tree
01479         """
01480         if not self.curr_page or not self.curr_page.maptree.layer_selected:
01481             self.MsgNoLayerSelected()
01482             return
01483 
01484         if UserSettings.Get(group = 'manager', key = 'askOnRemoveLayer', subkey = 'enabled'):
01485             layerName = ''
01486             for item in self.curr_page.maptree.GetSelections():
01487                 name = str(self.curr_page.maptree.GetItemText(item))
01488                 idx = name.find('(opacity')
01489                 if idx > -1:
01490                     layerName += '<' + name[:idx].strip(' ') + '>,\n'
01491                 else:
01492                     layerName += '<' + name + '>,\n'
01493             layerName = layerName.rstrip(',\n')
01494             
01495             if len(layerName) > 2: # <>
01496                 message = _("Do you want to remove map layer(s)\n%s\n"
01497                             "from layer tree?") % layerName
01498             else:
01499                 message = _("Do you want to remove selected map layer(s) "
01500                             "from layer tree?")
01501 
01502             dlg = wx.MessageDialog (parent = self, message = message,
01503                                     caption = _("Remove map layer"),
01504                                     style  =  wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
01505 
01506             if dlg.ShowModal() != wx.ID_YES:
01507                 dlg.Destroy()
01508                 return
01509             
01510             dlg.Destroy()
01511 
01512         for layer in self.curr_page.maptree.GetSelections():
01513             if self.curr_page.maptree.GetPyData(layer)[0]['type'] == 'group':
01514                 self.curr_page.maptree.DeleteChildren(layer)
01515             self.curr_page.maptree.Delete(layer)
01516         
01517     def OnKeyDown(self, event):
01518         """!Key pressed"""
01519         kc = event.GetKeyCode()
01520         
01521         if event.ControlDown():
01522             if kc == wx.WXK_TAB:
01523                 # switch layer list / command output
01524                 if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('layers'):
01525                     self.notebook.SetSelectionByName('output')
01526                 else:
01527                     self.notebook.SetSelectionByName('layers')
01528         
01529         try:
01530             ckc = chr(kc)
01531         except ValueError:
01532             event.Skip()
01533             return
01534         
01535         if event.CtrlDown():
01536             if kc == 'R':
01537                 self.OnAddRaster(None)
01538             elif kc == 'V':
01539                 self.OnAddVector(None)
01540         
01541         event.Skip()
01542 
01543     def OnCloseWindow(self, event):
01544         """!Cleanup when wxGUI is quitted"""
01545         if not self.curr_page:
01546             self._auimgr.UnInit()
01547             self.Destroy()
01548             return
01549         
01550         maptree = self.curr_page.maptree
01551         if self.workspaceChanged and \
01552                 UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
01553             if self.workspaceFile:
01554                 message = _("Do you want to save changes in the workspace?")
01555             else:
01556                 message = _("Do you want to store current settings "
01557                             "to workspace file?")
01558             
01559             # ask user to save current settings
01560             if maptree.GetCount() > 0:
01561                 dlg = wx.MessageDialog(self,
01562                                        message = message,
01563                                        caption = _("Quit GRASS GUI"),
01564                                        style = wx.YES_NO | wx.YES_DEFAULT |
01565                                        wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
01566                 ret = dlg.ShowModal()
01567                 if ret == wx.ID_YES:
01568                     if not self.workspaceFile:
01569                         self.OnWorkspaceSaveAs()
01570                     else:
01571                         self.SaveToWorkspaceFile(self.workspaceFile)
01572                 elif ret == wx.ID_CANCEL:
01573                     event.Veto()
01574                     dlg.Destroy()
01575                     return
01576                 dlg.Destroy()
01577         
01578         # don't ask any more...
01579         UserSettings.Set(group = 'manager', key = 'askOnQuit', subkey = 'enabled',
01580                          value = False)
01581         
01582         self.OnDisplayCloseAll()
01583         
01584         self.gm_cb.DeleteAllPages()
01585         
01586         self._auimgr.UnInit()
01587         self.Destroy()
01588         
01589     def MsgNoLayerSelected(self):
01590         """!Show dialog message 'No layer selected'"""
01591         wx.MessageBox(parent = self,
01592                       message = _("No map layer selected. Operation cancelled."),
01593                       caption = _("Message"),
01594                       style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
01595     
01596 class GMApp(wx.App):
01597     def __init__(self, workspace = None):
01598         """!Main GUI class.
01599 
01600         @param workspace path to the workspace file
01601         """
01602         self.workspaceFile = workspace
01603         
01604         # call parent class initializer
01605         wx.App.__init__(self, False)
01606         
01607         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
01608         
01609     def OnInit(self):
01610         """!Initialize all available image handlers
01611         
01612         @return True
01613         """
01614         wx.InitAllImageHandlers()
01615 
01616         # create splash screen
01617         introImagePath = os.path.join(globalvar.ETCIMGDIR, "silesia_splash.png")
01618         introImage     = wx.Image(introImagePath, wx.BITMAP_TYPE_PNG)
01619         introBmp       = introImage.ConvertToBitmap()
01620         if SC:
01621             splash = SC.AdvancedSplash(bitmap = introBmp, 
01622                                        timeout = 2000, parent = None, id = wx.ID_ANY)
01623             splash.SetText(_('Starting GRASS GUI...'))
01624             splash.SetTextColour(wx.Colour(45, 52, 27))
01625             splash.SetTextFont(wx.Font(pointSize = 15, family = wx.DEFAULT, style = wx.NORMAL,
01626                                        weight = wx.BOLD))
01627             splash.SetTextPosition((150, 430))
01628         else:
01629             wx.SplashScreen (bitmap = introBmp, splashStyle = wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
01630                              milliseconds = 2000, parent = None, id = wx.ID_ANY)
01631         
01632         wx.Yield()
01633         
01634         ### TODO: adjust initial window layout if necessary
01635         w, h = wx.GetDisplaySize()
01636         # only neccessary if one of the windows is falling out of
01637         # the current display size
01638         
01639         # check if settings file exists
01640         # if settings file exists, check if we should use the stored settings
01641         #     if we should use stored settings, use stored settings
01642         #     else use default settings
01643         # else if settings file does not exist, use default settings
01644         # check if any of the windows is falling out of the current display
01645         # if yes, pull it in
01646         #   falling out to the right
01647         #   x pos = display width - window width
01648         #   falling out to the bottom
01649         #   y pos = 0
01650         # update settings
01651         # if settings file exists, update settings but keep settings for
01652         # additional map display windows, or update them too
01653         # do not erase settings for additional map display windows !
01654         
01655         # create and show main frame
01656         mainframe = GMFrame(parent = None, id = wx.ID_ANY,
01657                             workspace = self.workspaceFile)
01658 
01659         mainframe.Show()
01660         self.SetTopWindow(mainframe)
01661 
01662         return True
01663 
01664 class Usage(Exception):
01665     def __init__(self, msg):
01666         self.msg = msg
01667 
01668 def printHelp():
01669     """!Print program help"""
01670     print >> sys.stderr, "Usage:"
01671     print >> sys.stderr, " python wxgui.py [options]"
01672     print >> sys.stderr, "%sOptions:" % os.linesep
01673     print >> sys.stderr, " -w\t--workspace file\tWorkspace file to load"
01674     sys.exit(0)
01675 
01676 def process_opt(opts, args):
01677     """!Process command-line arguments"""
01678     workspaceFile = None
01679     for o, a in opts:
01680         if o in ("-h", "--help"):
01681             printHelp()
01682             
01683         if o in ("-w", "--workspace"):
01684             if a != '':
01685                 workspaceFile = str(a)
01686             else:
01687                 workspaceFile = args.pop(0)
01688 
01689     return (workspaceFile,)
01690 
01691 def main(argv = None):
01692     #
01693     # process command-line arguments
01694     #
01695     if argv is None:
01696         argv = sys.argv
01697     try:
01698         try:
01699             opts, args = getopt.getopt(argv[1:], "hw:",
01700                                        ["help", "workspace"])
01701         except getopt.error, msg:
01702             raise Usage(msg)
01703 
01704     except Usage, err:
01705         print >> sys.stderr, err.msg
01706         print >> sys.stderr, "for help use --help"
01707         printHelp()
01708 
01709     workspaceFile = process_opt(opts, args)[0]
01710 
01711     #
01712     # run application
01713     #
01714     app = GMApp(workspaceFile)
01715     # suppress wxPython logs
01716     q = wx.LogNull()
01717 
01718     app.MainLoop()
01719 
01720 if __name__ == "__main__":
01721     sys.exit(main())
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines