GRASS Programmer's Manual  6.4.2(2012)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
wxgui.py
Go to the documentation of this file.
1 """!
2 @package wxgui.py
3 
4 @brief Main Python app for GRASS wxPython GUI. Main menu, layer management
5 toolbar, notebook control for display management and access to
6 command console.
7 
8 Classes:
9  - GMFrame
10  - GMApp
11 
12 (C) 2006-2011 by the GRASS Development Team
13 This program is free software under the GNU General Public
14 License (>=v2). Read the file COPYING that comes with GRASS
15 for details.
16 
17 @author Michael Barton (Arizona State University)
18 @author Jachym Cepicky (Mendel University of Agriculture)
19 @author Martin Landa <landa.martin gmail.com>
20 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
21 """
22 
23 import sys
24 import os
25 import time
26 import string
27 import getopt
28 import platform
29 import signal
30 import tempfile
31 
32 ### XML
33 try:
34  import xml.etree.ElementTree as etree
35 except ImportError:
36  import elementtree.ElementTree as etree # Python <= 2.4
37 
38 ### i18N
39 import gettext
40 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
41 
42 from gui_modules import globalvar
43 import wx
44 import wx.aui
45 import wx.combo
46 import wx.html
47 import wx.stc
48 try:
49  import wx.lib.agw.customtreectrl as CT
50  import wx.lib.agw.flatnotebook as FN
51 except ImportError:
52  import wx.lib.customtreectrl as CT
53  import wx.lib.flatnotebook as FN
54 
55 try:
56  import wx.lib.agw.advancedsplash as SC
57 except ImportError:
58  SC = None
59 
60 sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
61 from grass.script import core as grass
62 
63 from gui_modules import utils
64 from gui_modules import preferences
65 from gui_modules import layertree
66 from gui_modules import mapdisp
67 from gui_modules import menudata
68 from gui_modules import menuform
69 from gui_modules import histogram
70 from gui_modules import profile
71 from gui_modules import mcalc_builder as mapcalculator
72 from gui_modules import gcmd
73 from gui_modules import dbm
74 from gui_modules import workspace
75 from gui_modules import goutput
76 from gui_modules import gdialogs
77 from gui_modules import colorrules
78 from gui_modules import ogc_services
79 from gui_modules import prompt
80 from gui_modules import menu
81 from gui_modules import gmodeler
82 from gui_modules import vclean
83 from gui_modules import nviz_tools
84 from gui_modules.debug import Debug
85 from gui_modules.ghelp import MenuTreeWindow, AboutWindow, InstallExtensionWindow, UninstallExtensionWindow
86 from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar, LMMiscToolbar, LMVectorToolbar
87 from gui_modules.gpyshell import PyShellWindow
88 from icons.icon import Icons
89 
90 UserSettings = preferences.globalSettings
91 
92 class GMFrame(wx.Frame):
93  """!Layer Manager frame with notebook widget for controlling GRASS
94  GIS. Includes command console page for typing GRASS (and other)
95  commands, tree widget page for managing map layers.
96  """
97  def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS Layer Manager"),
98  workspace = None,
99  size = globalvar.GM_WINDOW_SIZE, style = wx.DEFAULT_FRAME_STYLE, **kwargs):
100  self.parent = parent
101  self.baseTitle = title
102  self.iconsize = (16, 16)
103 
104  wx.Frame.__init__(self, parent = parent, id = id, size = size,
105  style = style, **kwargs)
106 
107  self.SetTitle(self.baseTitle)
108  self.SetName("LayerManager")
109 
110  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
111 
112  self._auimgr = wx.aui.AuiManager(self)
113 
114  # initialize variables
115  self.disp_idx = 0 # index value for map displays and layer trees
116  self.curr_page = None # currently selected page for layer tree notebook
117  self.curr_pagenum = None # currently selected page number for layer tree notebook
118  self.workspaceFile = workspace # workspace file
119  self.workspaceChanged = False # track changes in workspace
120  self.georectifying = None # reference to GCP class or None
121  self.gcpmanagement = None # reference to GCP class or None
122  # list of open dialogs
123  self.dialogs = dict()
124  self.dialogs['preferences'] = None
125  self.dialogs['atm'] = list()
126 
127  # creating widgets
128  self._createMenuBar()
129  self.statusbar = self.CreateStatusBar(number = 1)
130  self.notebook = self._createNoteBook()
131  self.toolbars = { 'workspace' : LMWorkspaceToolbar(parent = self),
132  'data' : LMDataToolbar(parent = self),
133  'tools' : LMToolsToolbar(parent = self),
134  'misc' : LMMiscToolbar(parent = self),
135  'vector' : LMVectorToolbar(parent = self) }
136 
137  self._toolbarsData = { 'workspace' : ("toolbarWorkspace", # name
138  _("Workspace Toolbar"), # caption
139  1), # row
140  'data' : ("toolbarData",
141  _("Data Toolbar"),
142  1),
143  'misc' : ("toolbarMisc",
144  _("Misc Toolbar"),
145  2),
146  'tools' : ("toolbarTools",
147  _("Tools Toolbar"),
148  2),
149  'vector' : ("toolbarVector",
150  _("Vector Toolbar"),
151  2),
152  }
153  if sys.platform == 'win32':
154  self._toolbarsList = ('workspace', 'data',
155  'vector', 'tools', 'misc')
156  else:
157  self._toolbarsList = ('data', 'workspace',
158  'misc', 'tools', 'vector')
159  for toolbar in self._toolbarsList:
160  name, caption, row = self._toolbarsData[toolbar]
161  self._auimgr.AddPane(self.toolbars[toolbar],
162  wx.aui.AuiPaneInfo().
163  Name(name).Caption(caption).
164  ToolbarPane().Top().Row(row).
165  LeftDockable(False).RightDockable(False).
166  BottomDockable(False).TopDockable(True).
167  CloseButton(False).Layer(2).
168  BestSize((self.toolbars[toolbar].GetBestSize())))
169 
170  # bindings
171  self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
172  self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
173 
174  # minimal frame size
175  self.SetMinSize((500, 400))
176 
177  # AUI stuff
178  self._auimgr.AddPane(self.notebook, wx.aui.AuiPaneInfo().
179  Left().CentrePane().BestSize((-1,-1)).Dockable(False).
180  CloseButton(False).DestroyOnClose(True).Row(1).Layer(0))
181 
182  self._auimgr.Update()
183 
184  wx.CallAfter(self.notebook.SetSelectionByName, 'layers')
185 
186  # use default window layout ?
187  if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
188  dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
189  try:
190  x, y = map(int, dim.split(',')[0:2])
191  w, h = map(int, dim.split(',')[2:4])
192  self.SetPosition((x, y))
193  self.SetSize((w, h))
194  except:
195  pass
196  else:
197  self.Centre()
198 
199  self.Layout()
200  self.Show()
201 
202  # load workspace file if requested
203  if self.workspaceFile:
204  # load given workspace file
205  if self.LoadWorkspaceFile(self.workspaceFile):
206  self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
207  else:
208  self.workspaceFile = None
209  else:
210  # start default initial display
211  self.NewDisplay(show = False)
212 
213  # show map display widnow
214  # -> OnSize() -> UpdateMap()
215  if self.curr_page and not self.curr_page.maptree.mapdisplay.IsShown():
216  self.curr_page.maptree.mapdisplay.Show()
217 
218  # redirect stderr to log area
219  self.goutput.Redirect()
220  # fix goutput's pane size
221  self.goutput.SetSashPosition(int(self.GetSize()[1] * .60))
222 
223  self.workspaceChanged = False
224 
225  # start with layer manager on top
226  if self.curr_page:
227  self.curr_page.maptree.mapdisplay.Raise()
228  wx.CallAfter(self.Raise)
229 
230  def _createMenuBar(self):
231  """!Creates menu bar"""
232  self.menubar = menu.Menu(parent = self, data = menudata.ManagerData())
233  self.SetMenuBar(self.menubar)
234  self.menucmd = self.menubar.GetCmd()
235 
236  def _setCopyingOfSelectedText(self):
237  copy = UserSettings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled')
238  self.goutput.SetCopyingOfSelectedText(copy)
239 
240  def _createNoteBook(self):
241  """!Creates notebook widgets"""
242  self.notebook = menuform.GNotebook(parent = self, style = globalvar.FNPageDStyle)
243  # create displays notebook widget and add it to main notebook page
244  cbStyle = globalvar.FNPageStyle
245  if globalvar.hasAgw:
246  self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, agwStyle = cbStyle)
247  else:
248  self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, style = cbStyle)
249  self.gm_cb.SetTabAreaColour(globalvar.FNPageColor)
250  self.notebook.AddPage(page = self.gm_cb, text = _("Map layers"), name = 'layers')
251 
252  # create 'command output' text area
253  self.goutput = goutput.GMConsole(self)
254  self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
256 
257  # create 'search module' notebook page
258  if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'):
259  self.search = MenuTreeWindow(parent = self)
260  self.notebook.AddPage(page = self.search, text = _("Search module"), name = 'search')
261  else:
262  self.search = None
263 
264  # create 'python shell' notebook page
265  if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'):
266  self.pyshell = PyShellWindow(parent = self)
267  self.notebook.AddPage(page = self.pyshell, text = _("Python shell"), name = 'pyshell')
268  else:
269  self.pyshell = None
270 
271  # bindings
272  self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnCBPageChanged)
273  self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
274  self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnCBPageClosed)
275 
276  return self.notebook
277 
278  def AddNviz(self):
279  """!Add nviz notebook page"""
280  self.nviz = nviz_tools.NvizToolWindow(parent = self,
281  display = self.curr_page.maptree.GetMapDisplay())
282  self.notebook.AddPage(page = self.nviz, text = _("3D view"), name = 'nviz')
283  self.notebook.SetSelectionByName('nviz')
284 
285  def RemoveNviz(self):
286  """!Remove nviz notebook page"""
287  # print self.notebook.GetPage(1)
288  self.notebook.RemovePage(self.notebook.GetPageIndexByName('nviz'))
289  del self.nviz
290  self.notebook.SetSelectionByName('layers')
291 
292  def WorkspaceChanged(self):
293  """!Update window title"""
294  if not self.workspaceChanged:
295  self.workspaceChanged = True
296 
297  if self.workspaceFile:
298  self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile) + '*')
299 
300  def OnSettingsChanged(self, event):
301  """!Here can be functions which have to be called after EVT_SETTINGS_CHANGED.
302  Now only set copying of selected text to clipboard (in goutput).
303  """
304  ### self._createMenuBar() # bug when menu is re-created on the fly
306 
307  def OnGCPManager(self, event):
308  """!Launch georectifier module
309  """
310  from gui_modules import gcpmanager
311  gcpmanager.GCPWizard(self)
312 
313  def OnGModeler(self, event):
314  """!Launch Graphical Modeler"""
315  win = gmodeler.ModelFrame(parent = self)
316  win.CentreOnScreen()
317 
318  win.Show()
319 
320  def OnPsMap(self, event):
321  """!Launch Cartographic Composer
322  """
323  try:
324  from gui_modules import psmap
325  except:
326  gcmd.GError(parent = self.parent,
327  message = _("Hardcopy Map Output Utility is not available. You can install it by %s") % \
328  'g.extension.py -s svnurl=https://svn.osgeo.org/grass/grass-addons extension=wx.psmap')
329  return
330 
331  win = psmap.PsMapFrame(parent = self)
332  win.CentreOnScreen()
333 
334  win.Show()
335 
336  def OnDone(self, cmd, returncode):
337  """Command execution finised"""
338  if hasattr(self, "model"):
339  self.model.DeleteIntermediateData(log = self.goutput)
340  del self.model
341  self.SetStatusText('')
342 
343  def OnRunModel(self, event):
344  """!Run model"""
345  filename = ''
346  dlg = wx.FileDialog(parent = self, message =_("Choose model to run"),
347  defaultDir = os.getcwd(),
348  wildcard = _("GRASS Model File (*.gxm)|*.gxm"))
349  if dlg.ShowModal() == wx.ID_OK:
350  filename = dlg.GetPath()
351 
352  if not filename:
353  dlg.Destroy()
354  return
355 
356  self.model = gmodeler.Model()
357  self.model.LoadModel(filename)
358  self.model.Run(log = self.goutput, onDone = self.OnDone, parent = self)
359 
360  dlg.Destroy()
361 
362  def OnMapsets(self, event):
363  """!Launch mapset access dialog
364  """
365  dlg = preferences.MapsetAccess(parent = self, id = wx.ID_ANY)
366  dlg.CenterOnScreen()
367 
368  if dlg.ShowModal() == wx.ID_OK:
369  ms = dlg.GetMapsets()
370  gcmd.RunCommand('g.mapsets',
371  parent = self,
372  mapset = '%s' % ','.join(ms))
373 
374  def OnCBPageChanged(self, event):
375  """!Page in notebook (display) changed"""
376  old_pgnum = event.GetOldSelection()
377  new_pgnum = event.GetSelection()
378 
379  self.curr_page = self.gm_cb.GetCurrentPage()
380  self.curr_pagenum = self.gm_cb.GetSelection()
381  try:
382  self.curr_page.maptree.mapdisplay.SetFocus()
383  self.curr_page.maptree.mapdisplay.Raise()
384  except:
385  pass
386 
387  event.Skip()
388 
389  def OnPageChanged(self, event):
390  """!Page in notebook changed"""
391  page = event.GetSelection()
392  if page == self.notebook.GetPageIndexByName('output'):
393  # remove '(...)'
394  self.notebook.SetPageText(page, _("Command console"))
395  wx.CallAfter(self.goutput.cmd_prompt.SetFocus)
396  self.SetStatusText('', 0)
397 
398  event.Skip()
399 
400  def OnCBPageClosed(self, event):
401  """!Page of notebook closed
402  Also close associated map display
403  """
404  if UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
405  maptree = self.curr_page.maptree
406 
407  if self.workspaceFile:
408  message = _("Do you want to save changes in the workspace?")
409  else:
410  message = _("Do you want to store current settings "
411  "to workspace file?")
412 
413  # ask user to save current settings
414  if maptree.GetCount() > 0:
415  dlg = wx.MessageDialog(self,
416  message = message,
417  caption = _("Close Map Display %d") % (self.curr_pagenum + 1),
418  style = wx.YES_NO | wx.YES_DEFAULT |
419  wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
420  ret = dlg.ShowModal()
421  if ret == wx.ID_YES:
422  if not self.workspaceFile:
423  self.OnWorkspaceSaveAs()
424  else:
426  elif ret == wx.ID_CANCEL:
427  event.Veto()
428  dlg.Destroy()
429  return
430  dlg.Destroy()
431 
432  self.gm_cb.GetPage(event.GetSelection()).maptree.Map.Clean()
433  self.gm_cb.GetPage(event.GetSelection()).maptree.Close(True)
434 
435  self.curr_page = None
436 
437  event.Skip()
438 
439  def GetLayerTree(self):
440  """!Get current layer tree"""
441  return self.curr_page.maptree
442 
443  def GetLogWindow(self):
444  """!Get widget for command output"""
445  return self.goutput
446 
447  def GetMenuCmd(self, event):
448  """!Get GRASS command from menu item
449 
450  Return command as a list"""
451  layer = None
452 
453  if event:
454  cmd = self.menucmd[event.GetId()]
455 
456  try:
457  cmdlist = cmd.split(' ')
458  except: # already list?
459  cmdlist = cmd
460 
461  # check list of dummy commands for GUI modules that do not have GRASS
462  # bin modules or scripts.
463  if cmd in ['vcolors', 'r.mapcalc', 'r3.mapcalc']:
464  return cmdlist
465 
466  try:
467  layer = self.curr_page.maptree.layer_selected
468  name = self.curr_page.maptree.GetPyData(layer)[0]['maplayer'].name
469  type = self.curr_page.maptree.GetPyData(layer)[0]['type']
470  except:
471  layer = None
472 
473  if layer and len(cmdlist) == 1: # only if no paramaters given
474  if (type == 'raster' and cmdlist[0][0] == 'r' and cmdlist[0][1] != '3') or \
475  (type == 'vector' and cmdlist[0][0] == 'v'):
476  input = menuform.GUI().GetCommandInputMapParamKey(cmdlist[0])
477  if input:
478  cmdlist.append("%s=%s" % (input, name))
479 
480  return cmdlist
481 
482  def RunMenuCmd(self, event = None, cmd = []):
483  """!Run command selected from menu"""
484  if event:
485  cmd = self.GetMenuCmd(event)
486  self.goutput.RunCmd(cmd, switchPage = False)
487 
488  def OnMenuCmd(self, event = None, cmd = []):
489  """!Parse command selected from menu"""
490  if event:
491  cmd = self.GetMenuCmd(event)
492  menuform.GUI(parent = self).ParseCommand(cmd)
493 
494  def OnVDigit(self, event):
495  """!Start vector digitizer
496  """
497  if not self.curr_page:
498  self.MsgNoLayerSelected()
499  return
500 
501  tree = self.GetLayerTree()
502  layer = tree.layer_selected
503  # no map layer selected
504  if not layer:
505  self.MsgNoLayerSelected()
506  return
507 
508  # available only for vector map layers
509  try:
510  mapLayer = tree.GetPyData(layer)[0]['maplayer']
511  except:
512  mapLayer = None
513 
514  if not mapLayer or mapLayer.GetType() != 'vector':
515  gcmd.GMessage(parent = self,
516  message = _("Selected map layer is not vector."))
517  return
518 
519  if mapLayer.GetMapset() != grass.gisenv()['MAPSET']:
520  gcmd.GMessage(parent = self,
521  message = _("Editing is allowed only for vector maps from the "
522  "current mapset."))
523  return
524 
525  if not tree.GetPyData(layer)[0]:
526  return
527  dcmd = tree.GetPyData(layer)[0]['cmd']
528  if not dcmd:
529  return
530 
531  tree.OnStartEditing(None)
532 
533  def OnRunScript(self, event):
534  """!Run script"""
535  # open dialog and choose script file
536  dlg = wx.FileDialog(parent = self, message = _("Choose script file to run"),
537  defaultDir = os.getcwd(),
538  wildcard = _("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"))
539 
540  filename = None
541  if dlg.ShowModal() == wx.ID_OK:
542  filename = dlg.GetPath()
543 
544  if not filename:
545  return False
546 
547  if not os.path.exists(filename):
548  gcmd.GError(parent = self,
549  message = _("Script file '%s' doesn't exist. "
550  "Operation cancelled.") % filename)
551  return
552 
553  self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
554  self.goutput.RunCmd([filename], switchPage = True)
555 
556  def OnChangeLocation(self, event):
557  """Change current location"""
558  dlg = gdialogs.LocationDialog(parent = self)
559  if dlg.ShowModal() == wx.ID_OK:
560  location, mapset = dlg.GetValues()
561  if location and mapset:
562  ret = gcmd.RunCommand("g.gisenv",
563  set = "LOCATION_NAME=%s" % location)
564  ret += gcmd.RunCommand("g.gisenv",
565  set = "MAPSET=%s" % mapset)
566  if ret > 0:
567  wx.MessageBox(parent = self,
568  message = _("Unable to switch to location <%(loc)s> mapset <%(mapset)s>.") % \
569  { 'loc' : location, 'mapset' : mapset },
570  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
571  else:
572  # close workspace
573  self.OnWorkspaceClose()
574  self.OnWorkspaceNew()
575  wx.MessageBox(parent = self,
576  message = _("Current location is <%(loc)s>.\n"
577  "Current mapset is <%(mapset)s>.") % \
578  { 'loc' : location, 'mapset' : mapset },
579  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
580 
581  def OnChangeMapset(self, event):
582  """Change current mapset"""
583  dlg = gdialogs.MapsetDialog(parent = self)
584  if dlg.ShowModal() == wx.ID_OK:
585  mapset = dlg.GetMapset()
586  if mapset:
587  if gcmd.RunCommand("g.gisenv",
588  set = "MAPSET=%s" % mapset) != 0:
589  wx.MessageBox(parent = self,
590  message = _("Unable to switch to mapset <%s>.") % mapset,
591  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
592  else:
593  wx.MessageBox(parent = self,
594  message = _("Current mapset is <%s>.") % mapset,
595  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
596 
597  def OnNewVector(self, event):
598  """!Create new vector map layer"""
599  dlg = gdialogs.CreateNewVector(self, log = self.goutput,
600  cmd = (('v.edit',
601  { 'tool' : 'create' },
602  'map')))
603 
604  if not dlg:
605  return
606 
607  name = dlg.GetName(full = True)
608  if name and dlg.IsChecked('add'):
609  # add layer to map layer tree
610  self.curr_page.maptree.AddLayer(ltype = 'vector',
611  lname = name,
612  lcmd = ['d.vect', 'map=%s' % name])
613 
614  # create table ?
615  if dlg.IsChecked('table'):
616  self.OnShowAttributeTable(None, selection = 1)
617 
618  dlg.Destroy()
619 
620  def OnAboutGRASS(self, event):
621  """!Display 'About GRASS' dialog"""
622  win = AboutWindow(self)
623  win.CentreOnScreen()
624  win.Show(True)
625 
626  def _popupMenu(self, data):
627  """!Create popup menu
628  """
629  point = wx.GetMousePosition()
630  menu = wx.Menu()
631 
632  for key, handler in data:
633  if key is None:
634  menu.AppendSeparator()
635  continue
636  item = wx.MenuItem(menu, wx.ID_ANY, Icons['layerManager'][key].GetLabel())
637  item.SetBitmap(Icons['layerManager'][key].GetBitmap(self.iconsize))
638  menu.AppendItem(item)
639  self.Bind(wx.EVT_MENU, handler, item)
640 
641  # create menu
642  self.PopupMenu(menu)
643  menu.Destroy()
644 
645  def OnImportMenu(self, event):
646  """!Import maps menu (import, link)
647  """
648  self._popupMenu((('rastImport', self.OnImportGdalLayers),
649  ('vectImport', self.OnImportOgrLayers)))
650 
651  def OnWorkspaceNew(self, event = None):
652  """!Create new workspace file
653 
654  Erase current workspace settings first
655  """
656  Debug.msg(4, "GMFrame.OnWorkspaceNew():")
657 
658  # start new map display if no display is available
659  if not self.curr_page:
660  self.NewDisplay()
661 
662  maptree = self.curr_page.maptree
663 
664  # ask user to save current settings
665  if self.workspaceFile and self.workspaceChanged:
666  self.OnWorkspaceSave()
667  elif self.workspaceFile is None and maptree.GetCount() > 0:
668  dlg = wx.MessageDialog(self, message = _("Current workspace is not empty. "
669  "Do you want to store current settings "
670  "to workspace file?"),
671  caption = _("Create new workspace?"),
672  style = wx.YES_NO | wx.YES_DEFAULT | \
673  wx.CANCEL | wx.ICON_QUESTION)
674  ret = dlg.ShowModal()
675  if ret == wx.ID_YES:
676  self.OnWorkspaceSaveAs()
677  elif ret == wx.ID_CANCEL:
678  dlg.Destroy()
679  return
680 
681  dlg.Destroy()
682 
683  # delete all items
684  maptree.DeleteAllItems()
685 
686  # add new root element
687  maptree.root = maptree.AddRoot("Map Layers")
688  self.curr_page.maptree.SetPyData(maptree.root, (None,None))
689 
690  # no workspace file loaded
691  self.workspaceFile = None
692  self.workspaceChanged = False
693  self.SetTitle(self.baseTitle)
694 
695  def OnWorkspaceOpen(self, event = None):
696  """!Open file with workspace definition"""
697  dlg = wx.FileDialog(parent = self, message = _("Choose workspace file"),
698  defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"))
699 
700  filename = ''
701  if dlg.ShowModal() == wx.ID_OK:
702  filename = dlg.GetPath()
703 
704  if filename == '':
705  return
706 
707  Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
708 
709  # delete current layer tree content
710  self.OnWorkspaceClose()
711 
712  self.LoadWorkspaceFile(filename)
713 
714  self.workspaceFile = filename
715  self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
716 
717  def LoadWorkspaceFile(self, filename):
718  """!Load layer tree definition stored in GRASS Workspace XML file (gxw)
719 
720  @todo Validate against DTD
721 
722  @return True on success
723  @return False on error
724  """
725  # dtd
726  dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxw.dtd")
727 
728  # parse workspace file
729  try:
730  gxwXml = workspace.ProcessWorkspaceFile(etree.parse(filename))
731  except Exception, e:
732  gcmd.GError(parent = self,
733  message = _("Reading workspace file <%s> failed.\n"
734  "Invalid file, unable to parse XML document.") % filename)
735  return
736 
737  busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
738  parent = self)
739  wx.Yield()
740 
741  #
742  # load layer manager window properties
743  #
744  if UserSettings.Get(group = 'workspace', key = 'posManager', subkey = 'enabled') is False:
745  if gxwXml.layerManager['pos']:
746  self.SetPosition(gxwXml.layerManager['pos'])
747  if gxwXml.layerManager['size']:
748  self.SetSize(gxwXml.layerManager['size'])
749 
750  #
751  # start map displays first (list of layers can be empty)
752  #
753  displayId = 0
754  mapdisplay = list()
755  for display in gxwXml.displays:
756  mapdisp = self.NewDisplay(show = False)
757  mapdisplay.append(mapdisp)
758  maptree = self.gm_cb.GetPage(displayId).maptree
759 
760  # set windows properties
761  mapdisp.SetProperties(render = display['render'],
762  mode = display['mode'],
763  showCompExtent = display['showCompExtent'],
764  constrainRes = display['constrainRes'],
765  projection = display['projection']['enabled'])
766 
767  if display['projection']['enabled']:
768  if display['projection']['epsg']:
769  UserSettings.Set(group = 'display', key = 'projection', subkey = 'epsg',
770  value = display['projection']['epsg'])
771  if display['projection']['proj']:
772  UserSettings.Set(group = 'display', key = 'projection', subkey = 'proj4',
773  value = display['projection']['proj'])
774 
775  # set position and size of map display
776  if UserSettings.Get(group = 'workspace', key = 'posDisplay', subkey = 'enabled') is False:
777  if display['pos']:
778  mapdisp.SetPosition(display['pos'])
779  if display['size']:
780  mapdisp.SetSize(display['size'])
781 
782  # set extent if defined
783  if display['extent']:
784  w, s, e, n = display['extent']
785  region = maptree.Map.region = maptree.Map.GetRegion(w = w, s = s, e = e, n = n)
786  mapdisp.GetWindow().ResetZoomHistory()
787  mapdisp.GetWindow().ZoomHistory(region['n'],
788  region['s'],
789  region['e'],
790  region['w'])
791 
792  mapdisp.Show()
793 
794  displayId += 1
795 
796  maptree = None
797  selected = [] # list of selected layers
798  #
799  # load list of map layers
800  #
801  for layer in gxwXml.layers:
802  display = layer['display']
803  maptree = self.gm_cb.GetPage(display).maptree
804 
805  newItem = maptree.AddLayer(ltype = layer['type'],
806  lname = layer['name'],
807  lchecked = layer['checked'],
808  lopacity = layer['opacity'],
809  lcmd = layer['cmd'],
810  lgroup = layer['group'],
811  lnviz = layer['nviz'],
812  lvdigit = layer['vdigit'])
813 
814  if layer.has_key('selected'):
815  if layer['selected']:
816  selected.append((maptree, newItem))
817  else:
818  maptree.SelectItem(newItem, select = False)
819 
820  for maptree, layer in selected:
821  if not maptree.IsSelected(layer):
822  maptree.SelectItem(layer, select = True)
823  maptree.layer_selected = layer
824 
825  busy.Destroy()
826 
827  for mdisp in mapdisplay:
828  mdisp.MapWindow2D.UpdateMap()
829 
830  return True
831 
832  def OnWorkspaceLoadGrcFile(self, event):
833  """!Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
834  dlg = wx.FileDialog(parent = self, message = _("Choose GRC file to load"),
835  defaultDir = os.getcwd(), wildcard = _("Old GRASS Workspace File (*.grc)|*.grc"))
836 
837  filename = ''
838  if dlg.ShowModal() == wx.ID_OK:
839  filename = dlg.GetPath()
840 
841  if filename == '':
842  return
843 
844  Debug.msg(4, "GMFrame.OnWorkspaceLoadGrcFile(): filename=%s" % filename)
845 
846  # start new map display if no display is available
847  if not self.curr_page:
848  self.NewDisplay()
849 
850  busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
851  parent = self)
852  wx.Yield()
853 
854  maptree = None
855  for layer in workspace.ProcessGrcFile(filename).read(self):
856  maptree = self.gm_cb.GetPage(layer['display']).maptree
857  newItem = maptree.AddLayer(ltype = layer['type'],
858  lname = layer['name'],
859  lchecked = layer['checked'],
860  lopacity = layer['opacity'],
861  lcmd = layer['cmd'],
862  lgroup = layer['group'])
863 
864  busy.Destroy()
865 
866  if maptree:
867  # reverse list of map layers
868  maptree.Map.ReverseListOfLayers()
869 
870  def OnWorkspaceSaveAs(self, event = None):
871  """!Save workspace definition to selected file"""
872  dlg = wx.FileDialog(parent = self, message = _("Choose file to save current workspace"),
873  defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"), style = wx.FD_SAVE)
874 
875  filename = ''
876  if dlg.ShowModal() == wx.ID_OK:
877  filename = dlg.GetPath()
878 
879  if filename == '':
880  return False
881 
882  # check for extension
883  if filename[-4:] != ".gxw":
884  filename += ".gxw"
885 
886  if os.path.exists(filename):
887  dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
888  "Do you want to overwrite this file?") % filename,
889  caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
890  if dlg.ShowModal() != wx.ID_YES:
891  dlg.Destroy()
892  return False
893 
894  Debug.msg(4, "GMFrame.OnWorkspaceSaveAs(): filename=%s" % filename)
895 
896  self.SaveToWorkspaceFile(filename)
897  self.workspaceFile = filename
898  self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
899 
900  def OnWorkspaceSave(self, event = None):
901  """!Save file with workspace definition"""
902  if self.workspaceFile:
903  dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
904  "Do you want to overwrite this file?") % \
905  self.workspaceFile,
906  caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
907  if dlg.ShowModal() == wx.ID_NO:
908  dlg.Destroy()
909  else:
910  Debug.msg(4, "GMFrame.OnWorkspaceSave(): filename=%s" % self.workspaceFile)
912  self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
913  self.workspaceChanged = False
914  else:
915  self.OnWorkspaceSaveAs()
916 
917  def SaveToWorkspaceFile(self, filename):
918  """!Save layer tree layout to workspace file
919 
920  Return True on success, False on error
921  """
922  tmpfile = tempfile.TemporaryFile(mode = 'w+b')
923  try:
924  workspace.WriteWorkspaceFile(lmgr = self, file = tmpfile)
925  except StandardError, e:
926  gcmd.GError(parent = self,
927  message = _("Writing current settings to workspace file "
928  "failed."))
929  return False
930 
931  try:
932  mfile = open(filename, "w")
933  tmpfile.seek(0)
934  for line in tmpfile.readlines():
935  mfile.write(line)
936  except IOError:
937  gcmd.GError(parent = self,
938  message = _("Unable to open file <%s> for writing.") % filename)
939  return False
940 
941  mfile.close()
942 
943  return True
944 
945  def OnWorkspaceClose(self, event = None):
946  """!Close file with workspace definition
947 
948  If workspace has been modified ask user to save the changes.
949  """
950  Debug.msg(4, "GMFrame.OnWorkspaceClose(): file=%s" % self.workspaceFile)
951 
952  self.OnDisplayCloseAll()
953  self.workspaceFile = None
954  self.workspaceChanged = False
955  self.SetTitle(self.baseTitle)
956  self.disp_idx = 0
957  self.curr_page = None
958 
959  def OnDisplayClose(self, event = None):
960  """!Close current map display window
961  """
962  if self.curr_page and self.curr_page.maptree.mapdisplay:
963  self.curr_page.maptree.mapdisplay.OnCloseWindow(event)
964 
965  def OnDisplayCloseAll(self, event = None):
966  """!Close all open map display windows
967  """
968  displays = list()
969  for page in range(0, self.gm_cb.GetPageCount()):
970  displays.append(self.gm_cb.GetPage(page).maptree.mapdisplay)
971 
972  for display in displays:
973  display.OnCloseWindow(event)
974 
975  def RulesCmd(self, event):
976  """!Launches dialog for commands that need rules input and
977  processes rules
978  """
979  cmd = self.GetMenuCmd(event)
980 
981  if cmd[0] == 'r.colors':
982  ctable = colorrules.ColorTable(self, raster = True)
983  else:
984  ctable = colorrules.ColorTable(self, raster = False)
985  ctable.CentreOnScreen()
986  ctable.Show()
987 
988  def OnXTermNoXMon(self, event):
989  """!
990  Run commands that need xterm
991  """
992  self.OnXTerm(event, need_xmon = False)
993 
994  def OnXTerm(self, event, need_xmon = True):
995  """!
996  Run commands that need interactive xmon
997 
998  @param need_xmon True to start X monitor
999  """
1000  # unset display mode
1001  del os.environ['GRASS_RENDER_IMMEDIATE']
1002 
1003  if need_xmon:
1004  # open next available xmon
1005  xmonlist = []
1006 
1007  # make list of xmons that are not running
1008  ret = gcmd.RunCommand('d.mon',
1009  flags = 'L',
1010  read = True)
1011 
1012  for line in ret.split('\n'):
1013  line = line.strip()
1014  if line.startswith('x') and 'not running' in line:
1015  xmonlist.append(line[0:2])
1016 
1017  # find available xmon
1018  xmon = xmonlist[0]
1019 
1020  # bring up the xmon
1021  cmdlist = ['d.mon', xmon]
1022  p = gcmd.Command(cmdlist, wait=False)
1023 
1024  # run the command
1025  command = self.GetMenuCmd(event)
1026  command = ' '.join(command)
1027 
1028  gisbase = os.environ['GISBASE']
1029 
1030  if sys.platform == "win32":
1031  runbat = os.path.join(gisbase,'etc','grass-run.bat')
1032  cmdlist = ["start", runbat, runbat, command]
1033  else:
1034  if sys.platform == "darwin":
1035  xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-mac')
1036  else:
1037  xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-wrapper')
1038 
1039  grassrun = os.path.join(gisbase,'etc','grass-run.sh')
1040  cmdlist = [xtermwrapper, '-e', grassrun, command]
1041 
1042  p = gcmd.Command(cmdlist, wait=False)
1043 
1044  # reset display mode
1045  os.environ['GRASS_RENDER_IMMEDIATE'] = 'TRUE'
1046 
1047  def OnInstallExtension(self, event):
1048  """!Install extension from GRASS Addons SVN repository"""
1049  win = InstallExtensionWindow(self, size = (650, 550))
1050  win.CentreOnScreen()
1051  win.Show()
1052 
1053  def OnUninstallExtension(self, event):
1054  """!Uninstall extension"""
1055  win = UninstallExtensionWindow(self, size = (650, 300))
1056  win.CentreOnScreen()
1057  win.Show()
1058 
1059  def OnPreferences(self, event):
1060  """!General GUI preferences/settings
1061  """
1062  if not self.dialogs['preferences']:
1063  dlg = preferences.PreferencesDialog(parent = self)
1064  self.dialogs['preferences'] = dlg
1065  self.dialogs['preferences'].CenterOnScreen()
1066 
1067  dlg.Bind(preferences.EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
1068 
1069  self.dialogs['preferences'].ShowModal()
1070 
1071  def OnHelp(self, event):
1072  """!Show help
1073  """
1074  self.goutput.RunCmd(['g.manual','-i'])
1075 
1076  def DispHistogram(self, event):
1077  """
1078  Init histogram display canvas and tools
1079  """
1080  self.histogram = histogram.HistFrame(self,
1081  id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
1082  style = wx.DEFAULT_FRAME_STYLE)
1083 
1084  #show new display
1085  self.histogram.Show()
1086  self.histogram.Refresh()
1087  self.histogram.Update()
1088 
1089  def DispProfile(self, event):
1090  """
1091  Init profile canvas and tools
1092  """
1093  self.profile = profile.ProfileFrame(self,
1094  id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
1095  style = wx.DEFAULT_FRAME_STYLE)
1096  self.profile.Show()
1097  self.profile.Refresh()
1098  self.profile.Update()
1099 
1100  def OnMapCalculator(self, event, cmd = ''):
1101  """!Init map calculator for interactive creation of mapcalc statements
1102  """
1103  if event:
1104  try:
1105  cmd = self.GetMenuCmd(event)
1106  except KeyError:
1107  cmd = ['r.mapcalc']
1108 
1109  win = mapcalculator.MapCalcFrame(parent = self,
1110  cmd = cmd[0])
1111  win.CentreOnScreen()
1112  win.Show()
1113 
1114  def OnVectorCleaning(self, event, cmd = ''):
1115  """!Init interactive vector cleaning
1116  """
1117 
1118  if event:
1119  cmd = self.GetMenuCmd(event)
1120 
1121  win = vclean.VectorCleaningFrame(parent = self, cmd = cmd[0])
1122  win.CentreOnScreen()
1123  win.Show()
1124 
1125  def OnImportDxfFile(self, event, cmd = None):
1126  """!Convert multiple DXF layers to GRASS vector map layers"""
1127  dlg = gdialogs.DxfImportDialog(parent = self)
1128  dlg.CentreOnScreen()
1129  dlg.Show()
1130 
1131  def OnImportGdalLayers(self, event, cmd = None):
1132  """!Convert multiple GDAL layers to GRASS raster map layers"""
1133  dlg = gdialogs.GdalImportDialog(parent = self)
1134  dlg.CentreOnScreen()
1135  dlg.Show()
1136 
1137  def OnLinkGdalLayers(self, event, cmd = None):
1138  """!Link multiple GDAL layers to GRASS raster map layers"""
1139  dlg = gdialogs.GdalImportDialog(parent = self, link = True)
1140  dlg.CentreOnScreen()
1141  dlg.Show()
1142 
1143  def OnImportOgrLayers(self, event, cmd = None):
1144  """!Convert multiple OGR layers to GRASS vector map layers"""
1145  dlg = gdialogs.GdalImportDialog(parent = self, ogr = True)
1146  dlg.CentreOnScreen()
1147  dlg.Show()
1148 
1149  def OnLinkOgrLayers(self, event, cmd = None):
1150  """!Links multiple OGR layers to GRASS vector map layers"""
1151  dlg = gdialogs.GdalImportDialog(parent = self, ogr = True, link = True)
1152  dlg.CentreOnScreen()
1153  dlg.Show()
1154 
1155  def OnImportWMS(self, event):
1156  """!Import data from OGC WMS server"""
1157  dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
1158  dlg.CenterOnScreen()
1159 
1160  if dlg.ShowModal() == wx.ID_OK: # -> import layers
1161  layers = dlg.GetLayers()
1162 
1163  if len(layers.keys()) > 0:
1164  for layer in layers.keys():
1165  cmd = ['r.in.wms',
1166  'mapserver=%s' % dlg.GetSettings()['server'],
1167  'layers=%s' % layer,
1168  'output=%s' % layer,
1169  'format=png',
1170  '--overwrite']
1171  styles = ','.join(layers[layer])
1172  if styles:
1173  cmd.append('styles=%s' % styles)
1174  self.goutput.RunCmd(cmd, switchPage = True)
1175 
1176  self.curr_page.maptree.AddLayer(ltype = 'raster',
1177  lname = layer,
1178  lcmd = ['d.rast', 'map=%s' % layer],
1179  multiple = False)
1180  else:
1181  self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
1182 
1183 
1184  dlg.Destroy()
1185 
1186  def OnShowAttributeTable(self, event, selection = 0):
1187  """!Show attribute table of the given vector map layer
1188  """
1189  if not self.curr_page:
1190  self.MsgNoLayerSelected()
1191  return
1192 
1193  tree = self.GetLayerTree()
1194  layer = tree.layer_selected
1195  # no map layer selected
1196  if not layer:
1197  self.MsgNoLayerSelected()
1198  return
1199 
1200  # available only for vector map layers
1201  try:
1202  maptype = tree.GetPyData(layer)[0]['maplayer'].type
1203  except:
1204  maptype = None
1205 
1206  if not maptype or maptype != 'vector':
1207  gcmd.GMessage(parent = self,
1208  message = _("Selected map layer is not vector."))
1209  return
1210 
1211  if not tree.GetPyData(layer)[0]:
1212  return
1213  dcmd = tree.GetPyData(layer)[0]['cmd']
1214  if not dcmd:
1215  return
1216 
1217  busy = wx.BusyInfo(message = _("Please wait, loading attribute data..."),
1218  parent = self)
1219  wx.Yield()
1220 
1221  dbmanager = dbm.AttributeManager(parent = self, id = wx.ID_ANY,
1222  size = wx.Size(500, 300),
1223  item = layer, log = self.goutput,
1224  selection = selection)
1225 
1226  busy.Destroy()
1227 
1228  # register ATM dialog
1229  self.dialogs['atm'].append(dbmanager)
1230 
1231  # show ATM window
1232  dbmanager.Show()
1233 
1234  def OnNewDisplay(self, event = None):
1235  """!Create new layer tree and map display instance"""
1236  self.NewDisplay()
1237 
1238  def NewDisplay(self, show = True):
1239  """!Create new layer tree, which will
1240  create an associated map display frame
1241 
1242  @param show show map display window if True
1243 
1244  @return reference to mapdisplay intance
1245  """
1246  Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
1247 
1248  # make a new page in the bookcontrol for the layer tree (on page 0 of the notebook)
1249  self.pg_panel = wx.Panel(self.gm_cb, id = wx.ID_ANY, style = wx.EXPAND)
1250  self.gm_cb.AddPage(self.pg_panel, text = "Display "+ str(self.disp_idx + 1), select = True)
1251  self.curr_page = self.gm_cb.GetCurrentPage()
1252 
1253  # create layer tree (tree control for managing GIS layers) and put on new notebook page
1254  self.curr_page.maptree = layertree.LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
1255  size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
1256  wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
1257  wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
1258  idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
1259  auimgr = self._auimgr, showMapDisplay = show)
1260 
1261  # layout for controls
1262  cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
1263  cb_boxsizer.Add(self.curr_page.maptree, proportion = 1, flag = wx.EXPAND, border = 1)
1264  self.curr_page.SetSizer(cb_boxsizer)
1265  cb_boxsizer.Fit(self.curr_page.maptree)
1266  self.curr_page.Layout()
1267  self.curr_page.maptree.Layout()
1268 
1269  # use default window layout
1270  if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
1271  dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
1272  idx = 4 + self.disp_idx * 4
1273  try:
1274  x, y = map(int, dim.split(',')[idx:idx + 2])
1275  w, h = map(int, dim.split(',')[idx + 2:idx + 4])
1276  self.curr_page.maptree.mapdisplay.SetPosition((x, y))
1277  self.curr_page.maptree.mapdisplay.SetSize((w, h))
1278  except:
1279  pass
1280 
1281  self.disp_idx += 1
1282 
1283  return self.curr_page.maptree.mapdisplay
1284 
1285  def OnAddMaps(self, event = None):
1286  """!Add selected map layers into layer tree"""
1287  dialog = gdialogs.AddMapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
1288 
1289  if dialog.ShowModal() == wx.ID_OK:
1290  # start new map display if no display is available
1291  if not self.curr_page:
1292  self.NewDisplay()
1293 
1294  maptree = self.curr_page.maptree
1295  busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
1296  parent = self)
1297  wx.Yield()
1298 
1299  for layerName in dialog.GetMapLayers():
1300  if dialog.GetLayerType() == 'raster':
1301  cmd = ['d.rast', 'map=%s' % layerName]
1302  elif dialog.GetLayerType() == 'vector':
1303  cmd = ['d.vect', 'map=%s' % layerName]
1304  newItem = maptree.AddLayer(ltype = dialog.GetLayerType(),
1305  lname = layerName,
1306  lchecked = False,
1307  lopacity = 1.0,
1308  lcmd = cmd,
1309  lgroup = None)
1310 
1311  busy.Destroy()
1312 
1313  def OnAddRaster(self, event):
1314  """!Add raster map layer"""
1315  # start new map display if no display is available
1316  if not self.curr_page:
1317  self.NewDisplay(show = True)
1318 
1319  self.notebook.SetSelectionByName('layers')
1320  self.curr_page.maptree.AddLayer('raster')
1321 
1322  def OnAddRaster3D(self, event):
1323  """!Add 3D raster map layer"""
1324  # start new map display if no display is available
1325  if not self.curr_page:
1326  self.NewDisplay(show = True)
1327 
1328  self.AddRaster3D(event)
1329 
1330  def OnAddRasterMisc(self, event):
1331  """!Create misc raster popup-menu"""
1332  # start new map display if no display is available
1333  if not self.curr_page:
1334  self.NewDisplay(show = True)
1335 
1336  self._popupMenu((('addRast3d', self.OnAddRaster3D),
1337  (None, None),
1338  ('addRgb', self.OnAddRasterRGB),
1339  ('addHis', self.OnAddRasterHIS),
1340  (None, None),
1341  ('addShaded', self.OnAddRasterShaded),
1342  (None, None),
1343  ('addRArrow', self.OnAddRasterArrow),
1344  ('addRNum', self.OnAddRasterNum)))
1345 
1346  # show map display
1347  self.curr_page.maptree.mapdisplay.Show()
1348 
1349  def OnAddVector(self, event):
1350  """!Add vector map to the current layer tree"""
1351  # start new map display if no display is available
1352  if not self.curr_page:
1353  self.NewDisplay(show = True)
1354 
1355  self.notebook.SetSelectionByName('layers')
1356  self.curr_page.maptree.AddLayer('vector')
1357 
1358  def OnAddVectorMisc(self, event):
1359  """!Create misc vector popup-menu"""
1360  # start new map display if no display is available
1361  if not self.curr_page:
1362  self.NewDisplay(show = True)
1363 
1364  self._popupMenu((('addThematic', self.OnAddVectorTheme),
1365  ('addChart', self.OnAddVectorChart)))
1366 
1367  # show map display
1368  self.curr_page.maptree.mapdisplay.Show()
1369 
1370  def OnAddVectorTheme(self, event):
1371  """!Add thematic vector map to the current layer tree"""
1372  self.notebook.SetSelectionByName('layers')
1373  self.curr_page.maptree.AddLayer('thememap')
1374 
1375  def OnAddVectorChart(self, event):
1376  """!Add chart vector map to the current layer tree"""
1377  self.notebook.SetSelectionByName('layers')
1378  self.curr_page.maptree.AddLayer('themechart')
1379 
1380  def OnAddOverlay(self, event):
1381  """!Create decoration overlay menu"""
1382  # start new map display if no display is available
1383  if not self.curr_page:
1384  self.NewDisplay(show = True)
1385 
1386  self._popupMenu((('addGrid', self.OnAddGrid),
1387  ('addLabels', self.OnAddLabels),
1388  ('addGeodesic', self.OnAddGeodesic),
1389  ('addRhumb', self.OnAddRhumb),
1390  (None, None),
1391  ('addCmd', self.OnAddCommand)))
1392 
1393  # show map display
1394  self.curr_page.maptree.mapdisplay.Show()
1395 
1396  def OnAddRaster3D(self, event):
1397  """!Add 3D raster map to the current layer tree"""
1398  self.notebook.SetSelectionByName('layers')
1399  self.curr_page.maptree.AddLayer('3d-raster')
1400 
1401  def OnAddRasterRGB(self, event):
1402  """!Add RGB raster map to the current layer tree"""
1403  self.notebook.SetSelectionByName('layers')
1404  self.curr_page.maptree.AddLayer('rgb')
1405 
1406  def OnAddRasterHIS(self, event):
1407  """!Add HIS raster map to the current layer tree"""
1408  self.notebook.SetSelectionByName('layers')
1409  self.curr_page.maptree.AddLayer('his')
1410 
1411  def OnAddRasterShaded(self, event):
1412  """!Add shaded relief raster map to the current layer tree"""
1413  self.notebook.SetSelectionByName('layers')
1414  self.curr_page.maptree.AddLayer('shaded')
1415 
1416  def OnAddRasterArrow(self, event):
1417  """!Add flow arrows raster map to the current layer tree"""
1418  self.notebook.SetSelectionByName('layers')
1419  self.curr_page.maptree.AddLayer('rastarrow')
1420 
1421  def OnAddRasterNum(self, event):
1422  """!Add cell number raster map to the current layer tree"""
1423  self.notebook.SetSelectionByName('layers')
1424  self.curr_page.maptree.AddLayer('rastnum')
1425 
1426  def OnAddCommand(self, event):
1427  """!Add command line map layer to the current layer tree"""
1428  # start new map display if no display is available
1429  if not self.curr_page:
1430  self.NewDisplay(show = True)
1431 
1432  self.notebook.SetSelectionByName('layers')
1433  self.curr_page.maptree.AddLayer('command')
1434 
1435  # show map display
1436  self.curr_page.maptree.mapdisplay.Show()
1437 
1438  def OnAddGroup(self, event):
1439  """!Add layer group"""
1440  # start new map display if no display is available
1441  if not self.curr_page:
1442  self.NewDisplay(show = True)
1443 
1444  self.notebook.SetSelectionByName('layers')
1445  self.curr_page.maptree.AddLayer('group')
1446 
1447  # show map display
1448  self.curr_page.maptree.mapdisplay.Show()
1449 
1450  def OnAddGrid(self, event):
1451  """!Add grid map layer to the current layer tree"""
1452  self.notebook.SetSelectionByName('layers')
1453  self.curr_page.maptree.AddLayer('grid')
1454 
1455  def OnAddGeodesic(self, event):
1456  """!Add geodesic line map layer to the current layer tree"""
1457  self.notebook.SetSelectionByName('layers')
1458  self.curr_page.maptree.AddLayer('geodesic')
1459 
1460  def OnAddRhumb(self, event):
1461  """!Add rhumb map layer to the current layer tree"""
1462  self.notebook.SetSelectionByName('layers')
1463  self.curr_page.maptree.AddLayer('rhumb')
1464 
1465  def OnAddLabels(self, event):
1466  """!Add vector labels map layer to the current layer tree"""
1467  # start new map display if no display is available
1468  if not self.curr_page:
1469  self.NewDisplay(show = True)
1470 
1471  self.notebook.SetSelectionByName('layers')
1472  self.curr_page.maptree.AddLayer('labels')
1473 
1474  # show map display
1475  self.curr_page.maptree.mapdisplay.Show()
1476 
1477  def OnDeleteLayer(self, event):
1478  """!Remove selected map layer from the current layer Tree
1479  """
1480  if not self.curr_page or not self.curr_page.maptree.layer_selected:
1481  self.MsgNoLayerSelected()
1482  return
1483 
1484  if UserSettings.Get(group = 'manager', key = 'askOnRemoveLayer', subkey = 'enabled'):
1485  layerName = ''
1486  for item in self.curr_page.maptree.GetSelections():
1487  name = str(self.curr_page.maptree.GetItemText(item))
1488  idx = name.find('(opacity')
1489  if idx > -1:
1490  layerName += '<' + name[:idx].strip(' ') + '>,\n'
1491  else:
1492  layerName += '<' + name + '>,\n'
1493  layerName = layerName.rstrip(',\n')
1494 
1495  if len(layerName) > 2: # <>
1496  message = _("Do you want to remove map layer(s)\n%s\n"
1497  "from layer tree?") % layerName
1498  else:
1499  message = _("Do you want to remove selected map layer(s) "
1500  "from layer tree?")
1501 
1502  dlg = wx.MessageDialog (parent = self, message = message,
1503  caption = _("Remove map layer"),
1504  style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
1505 
1506  if dlg.ShowModal() != wx.ID_YES:
1507  dlg.Destroy()
1508  return
1509 
1510  dlg.Destroy()
1511 
1512  for layer in self.curr_page.maptree.GetSelections():
1513  if self.curr_page.maptree.GetPyData(layer)[0]['type'] == 'group':
1514  self.curr_page.maptree.DeleteChildren(layer)
1515  self.curr_page.maptree.Delete(layer)
1516 
1517  def OnKeyDown(self, event):
1518  """!Key pressed"""
1519  kc = event.GetKeyCode()
1520 
1521  if event.ControlDown():
1522  if kc == wx.WXK_TAB:
1523  # switch layer list / command output
1524  if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('layers'):
1525  self.notebook.SetSelectionByName('output')
1526  else:
1527  self.notebook.SetSelectionByName('layers')
1528 
1529  try:
1530  ckc = chr(kc)
1531  except ValueError:
1532  event.Skip()
1533  return
1534 
1535  if event.CtrlDown():
1536  if kc == 'R':
1537  self.OnAddRaster(None)
1538  elif kc == 'V':
1539  self.OnAddVector(None)
1540 
1541  event.Skip()
1542 
1543  def OnCloseWindow(self, event):
1544  """!Cleanup when wxGUI is quitted"""
1545  if not self.curr_page:
1546  self._auimgr.UnInit()
1547  self.Destroy()
1548  return
1549 
1550  maptree = self.curr_page.maptree
1551  if self.workspaceChanged and \
1552  UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
1553  if self.workspaceFile:
1554  message = _("Do you want to save changes in the workspace?")
1555  else:
1556  message = _("Do you want to store current settings "
1557  "to workspace file?")
1558 
1559  # ask user to save current settings
1560  if maptree.GetCount() > 0:
1561  dlg = wx.MessageDialog(self,
1562  message = message,
1563  caption = _("Quit GRASS GUI"),
1564  style = wx.YES_NO | wx.YES_DEFAULT |
1565  wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
1566  ret = dlg.ShowModal()
1567  if ret == wx.ID_YES:
1568  if not self.workspaceFile:
1569  self.OnWorkspaceSaveAs()
1570  else:
1572  elif ret == wx.ID_CANCEL:
1573  event.Veto()
1574  dlg.Destroy()
1575  return
1576  dlg.Destroy()
1577 
1578  # don't ask any more...
1579  UserSettings.Set(group = 'manager', key = 'askOnQuit', subkey = 'enabled',
1580  value = False)
1581 
1582  self.OnDisplayCloseAll()
1583 
1584  self.gm_cb.DeleteAllPages()
1585 
1586  self._auimgr.UnInit()
1587  self.Destroy()
1588 
1590  """!Show dialog message 'No layer selected'"""
1591  wx.MessageBox(parent = self,
1592  message = _("No map layer selected. Operation cancelled."),
1593  caption = _("Message"),
1594  style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
1595 
1596 class GMApp(wx.App):
1597  def __init__(self, workspace = None):
1598  """!Main GUI class.
1599 
1600  @param workspace path to the workspace file
1601  """
1602  self.workspaceFile = workspace
1603 
1604  # call parent class initializer
1605  wx.App.__init__(self, False)
1606 
1607  self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
1608 
1609  def OnInit(self):
1610  """!Initialize all available image handlers
1611 
1612  @return True
1613  """
1614  wx.InitAllImageHandlers()
1615 
1616  # create splash screen
1617  introImagePath = os.path.join(globalvar.ETCIMGDIR, "silesia_splash.png")
1618  introImage = wx.Image(introImagePath, wx.BITMAP_TYPE_PNG)
1619  introBmp = introImage.ConvertToBitmap()
1620  if SC:
1621  splash = SC.AdvancedSplash(bitmap = introBmp,
1622  timeout = 2000, parent = None, id = wx.ID_ANY)
1623  splash.SetText(_('Starting GRASS GUI...'))
1624  splash.SetTextColour(wx.Colour(45, 52, 27))
1625  splash.SetTextFont(wx.Font(pointSize = 15, family = wx.DEFAULT, style = wx.NORMAL,
1626  weight = wx.BOLD))
1627  splash.SetTextPosition((150, 430))
1628  else:
1629  wx.SplashScreen (bitmap = introBmp, splashStyle = wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1630  milliseconds = 2000, parent = None, id = wx.ID_ANY)
1631 
1632  wx.Yield()
1633 
1634  ### TODO: adjust initial window layout if necessary
1635  w, h = wx.GetDisplaySize()
1636  # only neccessary if one of the windows is falling out of
1637  # the current display size
1638 
1639  # check if settings file exists
1640  # if settings file exists, check if we should use the stored settings
1641  # if we should use stored settings, use stored settings
1642  # else use default settings
1643  # else if settings file does not exist, use default settings
1644  # check if any of the windows is falling out of the current display
1645  # if yes, pull it in
1646  # falling out to the right
1647  # x pos = display width - window width
1648  # falling out to the bottom
1649  # y pos = 0
1650  # update settings
1651  # if settings file exists, update settings but keep settings for
1652  # additional map display windows, or update them too
1653  # do not erase settings for additional map display windows !
1654 
1655  # create and show main frame
1656  mainframe = GMFrame(parent = None, id = wx.ID_ANY,
1657  workspace = self.workspaceFile)
1658 
1659  mainframe.Show()
1660  self.SetTopWindow(mainframe)
1661 
1662  return True
1663 
1664 class Usage(Exception):
1665  def __init__(self, msg):
1666  self.msg = msg
1667 
1669  """!Print program help"""
1670  print >> sys.stderr, "Usage:"
1671  print >> sys.stderr, " python wxgui.py [options]"
1672  print >> sys.stderr, "%sOptions:" % os.linesep
1673  print >> sys.stderr, " -w\t--workspace file\tWorkspace file to load"
1674  sys.exit(0)
1675 
1676 def process_opt(opts, args):
1677  """!Process command-line arguments"""
1678  workspaceFile = None
1679  for o, a in opts:
1680  if o in ("-h", "--help"):
1681  printHelp()
1682 
1683  if o in ("-w", "--workspace"):
1684  if a != '':
1685  workspaceFile = str(a)
1686  else:
1687  workspaceFile = args.pop(0)
1688 
1689  return (workspaceFile,)
1690 
1691 def main(argv = None):
1692  #
1693  # process command-line arguments
1694  #
1695  if argv is None:
1696  argv = sys.argv
1697  try:
1698  try:
1699  opts, args = getopt.getopt(argv[1:], "hw:",
1700  ["help", "workspace"])
1701  except getopt.error, msg:
1702  raise Usage(msg)
1703 
1704  except Usage, err:
1705  print >> sys.stderr, err.msg
1706  print >> sys.stderr, "for help use --help"
1707  printHelp()
1708 
1709  workspaceFile = process_opt(opts, args)[0]
1710 
1711  #
1712  # run application
1713  #
1714  app = GMApp(workspaceFile)
1715  # suppress wxPython logs
1716  q = wx.LogNull()
1717 
1718  app.MainLoop()
1719 
1720 if __name__ == "__main__":
1721  sys.exit(main())