6 Initialization module for wxPython GRASS GUI.
7 Location/mapset management (selection, creation, etc.).
14 (C) 2006-2011 by the GRASS Development Team
16 This program is free software under the GNU General Public License
17 (>=v2). Read the file COPYING that comes with GRASS for details.
19 @author Michael Barton and Jachym Cepicky (original author)
20 @author Martin Landa <landa.martin gmail.com> (various updates)
33 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
35 from gui_modules
import globalvar
38 import wx.lib.rcsizer
as rcs
39 import wx.lib.filebrowsebutton
as filebrowse
40 import wx.lib.mixins.listctrl
as listmix
41 import wx.lib.scrolledpanel
as scrolled
43 from gui_modules
import goutput
47 sys.stderr = codecs.getwriter(
'utf8')(sys.stderr)
50 """!GRASS start-up screen"""
51 def __init__(self, parent = None, id = wx.ID_ANY, style = wx.DEFAULT_FRAME_STYLE):
67 wx.Frame.__init__(self, parent = parent, id = id, style = style)
69 self.
locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
71 self.
panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
75 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
82 name = os.path.join(globalvar.ETCIMGDIR,
"startup_banner.gif")
84 wx.Bitmap(name = name,
85 type = wx.BITMAP_TYPE_GIF))
87 self.
hbitmap = wx.StaticBitmap(self.
panel, wx.ID_ANY, wx.EmptyBitmap(530,150))
91 versionFile = open(os.path.join(globalvar.ETCDIR,
"VERSIONNUMBER"))
92 grassVersion = versionFile.readline().
split(
' ')[0].rstrip(
'\n')
96 label =
" %s " % _(
"Choose project location and mapset"))
99 label =
" %s " % _(
"Manage"))
101 label = _(
"Welcome to GRASS GIS %s\n"
102 "The world's leading open source GIS") % grassVersion,
103 style = wx.ALIGN_CENTRE)
105 label = _(
"Select an existing project location and mapset\n"
106 "or define a new location"),
107 style = wx.ALIGN_CENTRE)
109 label = _(
"GIS Data Directory:"))
111 label = _(
"Project location\n(projection/coordinate system)"),
112 style = wx.ALIGN_CENTRE)
114 label = _(
"Accessible mapsets\n(directories of GIS files)"),
115 style = wx.ALIGN_CENTRE)
117 label = _(
"Create new mapset\nin selected location"),
118 style = wx.ALIGN_CENTRE)
120 label = _(
"Define new location"),
121 style = wx.ALIGN_CENTRE)
123 label = _(
"Rename/delete selected\nmapset or location"),
124 style = wx.ALIGN_CENTRE)
128 label = _(
"Start &GRASS"))
129 self.bstart.SetDefault()
131 self.bstart.SetMinSize((180, self.bexit.GetSize()[1]))
134 label = _(
"&Browse"))
136 label = _(
"&Create mapset"))
138 label = _(
"&Location wizard"))
140 choices = [_(
'Rename mapset'), _(
'Rename location'),
141 _(
'Delete mapset'), _(
'Delete location')])
142 self.manageloc.SetSelection(0)
145 self.
tgisdbase = wx.TextCtrl(parent = self.
panel, id = wx.ID_ANY, value =
"", size = (300, -1),
146 style = wx.TE_PROCESS_ENTER)
151 id = wx.ID_ANY, size = (180, 200),
154 self.lblocations.SetColumnWidth(0, 180)
160 id = wx.ID_ANY, size = (180, 200),
163 self.lbmapsets.SetColumnWidth(0, 180)
170 self.bbrowse.Bind(wx.EVT_BUTTON, self.
OnBrowse)
171 self.bstart.Bind(wx.EVT_BUTTON, self.
OnStart)
172 self.bexit.Bind(wx.EVT_BUTTON, self.
OnExit)
173 self.bhelp.Bind(wx.EVT_BUTTON, self.
OnHelp)
175 self.bwizard.Bind(wx.EVT_BUTTON, self.
OnWizard)
176 self.manageloc.Bind(wx.EVT_CHOICE, self.
OnManageLoc)
178 self.lbmapsets.Bind(wx.EVT_LIST_ITEM_SELECTED, self.
OnSelectMapset)
179 self.lbmapsets.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.
OnStart)
183 def _set_properties(self):
184 """!Set frame properties"""
185 self.SetTitle(_(
"Welcome to GRASS GIS"))
186 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR,
"grass.ico"),
189 self.lwelcome.SetForegroundColour(wx.Colour(35, 142, 35))
190 self.lwelcome.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0,
""))
192 self.bstart.SetForegroundColour(wx.Colour(35, 142, 35))
193 self.bstart.SetToolTipString(_(
"Enter GRASS session"))
194 self.bstart.Enable(
False)
195 self.bmapset.Enable(
False)
196 self.manageloc.Enable(
False)
201 if os.path.isdir(os.getenv(
"HOME")):
206 self.tgisdbase.SetValue(self.
gisdbase)
207 except UnicodeDecodeError:
208 wx.MessageBox(parent = self, caption = _(
"Error"),
209 message = _(
"Unable to set GRASS database. "
210 "Check your locale settings."),
211 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
215 if location ==
"<UNKNOWN>" or \
216 not os.path.isdir(os.path.join(self.
gisdbase, location)):
223 self.lblocations.SetSelection(self.listOfLocations.index(location),
225 self.lblocations.EnsureVisible(self.listOfLocations.index(location))
227 print >> sys.stderr, _(
"ERROR: Location <%s> not found") % \
228 (utils.UnicodeString(location))
235 self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset),
237 self.lbmapsets.EnsureVisible(self.listOfMapsets.index(mapset))
239 self.lbmapsets.Clear()
240 print >> sys.stderr, _(
"ERROR: Mapset <%s> not found") % \
241 (utils.UnicodeString(mapset))
243 def _do_layout(self):
244 label_style = wx.ADJUST_MINSIZE | wx.ALIGN_CENTER_HORIZONTAL
246 sizer = wx.BoxSizer(wx.VERTICAL)
247 dbase_sizer = wx.BoxSizer(wx.HORIZONTAL)
248 location_sizer = wx.FlexGridSizer(rows = 1, cols = 2, vgap = 4, hgap = 4)
249 select_boxsizer = wx.StaticBoxSizer(self.
select_box, wx.VERTICAL)
250 select_sizer = wx.FlexGridSizer(rows = 2, cols = 2, vgap = 4, hgap = 4)
251 manage_boxsizer = wx.StaticBoxSizer(self.
manage_box, wx.VERTICAL)
252 manage_sizer = wx.BoxSizer(wx.VERTICAL)
253 btns_sizer = wx.BoxSizer(wx.HORIZONTAL)
256 dbase_sizer.Add(item = self.
ldbase, proportion = 0,
257 flag = wx.ALIGN_CENTER_VERTICAL |
258 wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
260 dbase_sizer.Add(item = self.
tgisdbase, proportion = 0,
261 flag = wx.ALIGN_CENTER_VERTICAL |
262 wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
264 dbase_sizer.Add(item = self.
bbrowse, proportion = 0,
265 flag = wx.ALIGN_CENTER_VERTICAL |
266 wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
270 select_sizer.Add(item = self.
llocation, proportion = 0,
271 flag = label_style | wx.ALL,
273 select_sizer.Add(item = self.
lmapset, proportion = 0,
274 flag = label_style | wx.ALL,
276 select_sizer.Add(item = self.
lpanel, proportion = 0,
277 flag = wx.ADJUST_MINSIZE |
278 wx.ALIGN_CENTER_VERTICAL |
279 wx.ALIGN_CENTER_HORIZONTAL)
280 select_sizer.Add(item = self.
mpanel, proportion = 0,
281 flag = wx.ADJUST_MINSIZE |
282 wx.ALIGN_CENTER_VERTICAL |
283 wx.ALIGN_CENTER_HORIZONTAL)
285 select_boxsizer.Add(item = select_sizer, proportion = 0)
288 manage_sizer.Add(item = self.
ldefine, proportion = 0,
289 flag = label_style | wx.ALL,
291 manage_sizer.Add(item = self.
bwizard, proportion = 0,
292 flag = label_style | wx.BOTTOM,
294 manage_sizer.Add(item = self.
lcreate, proportion = 0,
295 flag = label_style | wx.ALL,
297 manage_sizer.Add(item = self.
bmapset, proportion = 0,
298 flag = label_style | wx.BOTTOM,
300 manage_sizer.Add(item = self.
lmanageloc, proportion = 0,
301 flag = label_style | wx.ALL,
303 manage_sizer.Add(item = self.
manageloc, proportion = 0,
304 flag = label_style | wx.BOTTOM,
307 manage_boxsizer.Add(item = manage_sizer, proportion = 0)
310 location_sizer.Add(item = select_boxsizer, proportion = 0,
311 flag = wx.ADJUST_MINSIZE |
312 wx.ALIGN_CENTER_VERTICAL |
313 wx.ALIGN_CENTER_HORIZONTAL |
314 wx.RIGHT | wx.LEFT | wx.EXPAND,
316 location_sizer.Add(item = manage_boxsizer, proportion = 0,
317 flag = wx.ADJUST_MINSIZE |
319 wx.ALIGN_CENTER_HORIZONTAL |
320 wx.RIGHT | wx.EXPAND,
324 btns_sizer.Add(item = self.
bstart, proportion = 0,
325 flag = wx.ALIGN_CENTER_HORIZONTAL |
326 wx.ALIGN_CENTER_VERTICAL |
329 btns_sizer.Add(item = self.
bexit, proportion = 0,
330 flag = wx.ALIGN_CENTER_HORIZONTAL |
331 wx.ALIGN_CENTER_VERTICAL |
334 btns_sizer.Add(item = self.
bhelp, proportion = 0,
335 flag = wx.ALIGN_CENTER_HORIZONTAL |
336 wx.ALIGN_CENTER_VERTICAL |
343 flag = wx.ALIGN_CENTER_VERTICAL |
344 wx.ALIGN_CENTER_HORIZONTAL |
349 flag = wx.ALIGN_CENTER_VERTICAL |
350 wx.ALIGN_CENTER_HORIZONTAL |
353 sizer.Add(item = self.
ltitle,
355 flag = wx.ALIGN_CENTER_VERTICAL |
356 wx.ALIGN_CENTER_HORIZONTAL)
357 sizer.Add(item = dbase_sizer, proportion = 0,
358 flag = wx.ALIGN_CENTER_HORIZONTAL |
361 sizer.Add(item = location_sizer, proportion = 1,
362 flag = wx.ALIGN_CENTER_VERTICAL |
363 wx.ALIGN_CENTER_HORIZONTAL |
366 sizer.Add(item = btns_sizer, proportion = 0,
367 flag = wx.ALIGN_CENTER_VERTICAL |
368 wx.ALIGN_CENTER_HORIZONTAL |
372 self.panel.SetAutoLayout(
True)
373 self.panel.SetSizer(sizer)
374 sizer.Fit(self.
panel)
375 sizer.SetSizeHints(self)
379 def _readGisRC(self):
381 Read variables from $HOME/.grassrc6 file
386 gisrc = os.getenv(
"GISRC")
388 if gisrc
and os.path.isfile(gisrc):
390 rc = open(gisrc,
"r")
391 for line
in rc.readlines():
392 key, val = line.split(
":", 1)
393 grassrc[key.strip()] = utils.DecodeString(val.strip())
400 """!Return GRASS variable (read from GISRC)
402 if self.grassrc.has_key(value):
408 """!Location wizard started"""
409 from gui_modules
import location_wizard
410 gWizard = location_wizard.LocationWizard(parent = self,
411 grassdatabase = self.tgisdbase.GetValue())
412 if gWizard.location !=
None:
415 self.lblocations.SetSelection(self.listOfLocations.index(gWizard.location))
416 self.lbmapsets.SetSelection(0)
419 """!Location management choice control handler
421 sel = event.GetSelection()
434 """!Rename selected mapset
436 location = utils.UnicodeString(self.
listOfLocations[self.lblocations.GetSelection()])
437 mapset = utils.UnicodeString(self.
listOfMapsets[self.lbmapsets.GetSelection()])
438 if mapset ==
'PERMANENT':
440 message = _(
'Mapset <PERMANENT> is required for valid GRASS location.\n\n'
441 'This mapset cannot be renamed.'))
444 dlg = wx.TextEntryDialog(parent = self,
445 message = _(
'Current name: %s\n\nEnter new name:') % mapset,
446 caption = _(
'Rename selected mapset'))
448 if dlg.ShowModal() == wx.ID_OK:
449 newmapset = dlg.GetValue()
450 if newmapset == mapset:
455 wx.MessageBox(parent = self,
456 caption = _(
'Message'),
457 message = _(
'Unable to rename mapset.\n\n'
458 'Mapset <%s> already exists in location.') % newmapset,
459 style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
462 os.rename(os.path.join(self.
gisdbase, location, mapset),
463 os.path.join(self.
gisdbase, location, newmapset))
465 self.lbmapsets.SetSelection(self.listOfMapsets.index(newmapset))
466 except StandardError, e:
467 wx.MessageBox(parent = self,
468 caption = _(
'Error'),
469 message = _(
'Unable to rename mapset.\n\n%s') % e,
470 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
475 """!Rename selected location
477 location = utils.UnicodeString(self.
listOfLocations[self.lblocations.GetSelection()])
479 dlg = wx.TextEntryDialog(parent = self,
480 message = _(
'Current name: %s\n\nEnter new name:') % location,
481 caption = _(
'Rename selected location'))
483 if dlg.ShowModal() == wx.ID_OK:
484 newlocation = dlg.GetValue()
485 if newlocation == location:
490 wx.MessageBox(parent = self,
491 caption = _(
'Message'),
492 message = _(
'Unable to rename location.\n\n'
493 'Location <%s> already exists in GRASS database.') % newlocation,
494 style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
497 os.rename(os.path.join(self.
gisdbase, location),
498 os.path.join(self.
gisdbase, newlocation))
500 self.lblocations.SetSelection(self.listOfLocations.index(newlocation))
502 except StandardError, e:
503 wx.MessageBox(parent = self,
504 caption = _(
'Error'),
505 message = _(
'Unable to rename location.\n\n%s') % e,
506 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
511 """!Delete selected mapset
515 if mapset ==
'PERMANENT':
517 message = _(
'Mapset <PERMANENT> is required for valid GRASS location.\n\n'
518 'This mapset cannot be deleted.'))
521 dlg = wx.MessageDialog(parent = self, message = _(
"Do you want to continue with deleting mapset <%(mapset)s> "
522 "from location <%(location)s>?\n\n"
523 "ALL MAPS included in this mapset will be "
524 "PERMANENTLY DELETED!") % {
'mapset' : mapset,
525 'location' : location},
526 caption = _(
"Delete selected mapset"),
527 style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
529 if dlg.ShowModal() == wx.ID_YES:
531 shutil.rmtree(os.path.join(self.
gisdbase, location, mapset))
533 self.lbmapsets.SetSelection(0)
535 wx.MessageBox(message = _(
'Unable to delete mapset'))
541 Delete selected location
546 dlg = wx.MessageDialog(parent = self, message = _(
"Do you want to continue with deleting "
548 "ALL MAPS included in this location will be "
549 "PERMANENTLY DELETED!") % (location),
550 caption = _(
"Delete selected location"),
551 style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
553 if dlg.ShowModal() == wx.ID_YES:
555 shutil.rmtree(os.path.join(self.
gisdbase, location))
557 self.lblocations.SetSelection(0)
559 self.lbmapsets.SetSelection(0)
561 wx.MessageBox(message = _(
'Unable to delete location'))
566 """!Update list of locations"""
569 except UnicodeEncodeError:
570 wx.MessageBox(parent = self, caption = _(
"Error"),
571 message = _(
"Unable to set GRASS database. "
572 "Check your locale settings."),
573 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
575 self.lblocations.Clear()
579 self.lblocations.SetSelection(0)
581 self.lblocations.SetSelection(wx.NOT_FOUND)
586 """!Update list of mapsets"""
592 self.lbmapsets.Clear()
595 locationName = os.path.basename(location)
597 ret = gcmd.RunCommand(
'g.mapset',
600 location = locationName,
604 for line
in ret.splitlines():
607 gcmd.RunCommand(
"g.gisenv",
608 set =
"GISDBASE=%s" % self.
gisdbase)
609 gcmd.RunCommand(
"g.gisenv",
610 set =
"LOCATION_NAME=%s" % locationName)
611 gcmd.RunCommand(
"g.gisenv",
612 set =
"MAPSET=PERMANENT")
620 os.path.isfile(os.path.join(self.
gisdbase,
622 mapset,
".gislock")):
626 self.lbmapsets.InsertItems(self.
listOfMapsets, 0, disabled = disabled)
631 """!Location selected"""
633 self.lblocations.SetSelection(event.GetIndex())
635 if self.lblocations.GetSelection() != wx.NOT_FOUND:
650 os.path.isfile(os.path.join(self.
gisdbase,
652 mapset,
".gislock")):
656 self.lbmapsets.Clear()
657 self.lbmapsets.InsertItems(self.
listOfMapsets, 0, disabled = disabled)
660 self.lbmapsets.SetSelection(0)
664 self.bmapset.Enable()
665 self.manageloc.Enable()
667 self.lbmapsets.SetSelection(wx.NOT_FOUND)
668 self.bstart.Enable(
False)
669 self.bmapset.Enable(
False)
670 self.manageloc.Enable(
False)
673 """!Mapset selected"""
674 self.lbmapsets.SetSelection(event.GetIndex())
684 self.
gisdbase = self.tgisdbase.GetValue()
691 """'Browse' button clicked"""
694 dlg = wx.DirDialog(self, _(
"Choose GIS Data Directory:"),
695 style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
696 if dlg.ShowModal() == wx.ID_OK:
698 self.tgisdbase.SetValue(self.
gisdbase)
704 """!Create new mapset"""
705 self.
gisdbase = self.tgisdbase.GetValue()
708 dlg = wx.TextEntryDialog(parent = self,
709 message = _(
'Enter name for new mapset:'),
710 caption = _(
'Create new mapset'))
712 if dlg.ShowModal() == wx.ID_OK:
713 mapset = dlg.GetValue()
715 os.mkdir(os.path.join(self.
gisdbase, location, mapset))
717 shutil.copy(os.path.join(self.
gisdbase, location,
'PERMANENT',
'WIND'),
718 os.path.join(self.
gisdbase, location, mapset))
721 self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
722 except StandardError, e:
723 dlg = wx.MessageDialog(parent = self, message = _(
"Unable to create new mapset: %s") % e,
724 caption = _(
"Error"), style = wx.OK | wx.ICON_ERROR)
729 self.bstart.SetFocus()
734 """'Start GRASS' button clicked"""
735 dbase = self.tgisdbase.GetValue()
739 lockfile = os.path.join(dbase, location, mapset,
'.gislock')
740 if os.path.isfile(lockfile):
741 dlg = wx.MessageDialog(parent = self,
742 message = _(
"GRASS is already running in selected mapset <%(mapset)s>\n"
743 "(file %(lock)s found).\n\n"
744 "Concurrent use not allowed.\n\n"
745 "Do you want to try to remove .gislock (note that you "
746 "need permission for this operation) and continue?") %
747 {
'mapset' : mapset,
'lock' : lockfile },
748 caption = _(
"Lock file found"),
749 style = wx.YES_NO | wx.NO_DEFAULT |
750 wx.ICON_QUESTION | wx.CENTRE)
752 ret = dlg.ShowModal()
755 dlg1 = wx.MessageDialog(parent = self,
756 message = _(
"ARE YOU REALLY SURE?\n\n"
757 "If you really are running another GRASS session doing this "
758 "could corrupt your data. Have another look in the processor "
759 "manager just to be sure..."),
760 caption = _(
"Lock file found"),
761 style = wx.YES_NO | wx.NO_DEFAULT |
762 wx.ICON_QUESTION | wx.CENTRE)
764 ret = dlg1.ShowModal()
771 GError(_(
"Unable to remove '%(lock)s'.\n\n"
772 "Details: %(reason)s") % {
'lock' : lockfile,
'reason' : e})
778 gcmd.RunCommand(
"g.gisenv",
779 set =
"GISDBASE=%s" % dbase)
780 gcmd.RunCommand(
"g.gisenv",
781 set =
"LOCATION_NAME=%s" % location)
782 gcmd.RunCommand(
"g.gisenv",
783 set =
"MAPSET=%s" % mapset)
789 """'Exit' button clicked"""
794 """'Help' button clicked"""
796 file = os.path.join(self.
gisbase,
"docs",
"html",
"helptext.html")
798 helpFrame =
HelpFrame(parent = self, id = wx.ID_ANY,
799 title = _(
"GRASS Quickstart"),
807 """!Close window event"""
811 class GListBox(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
812 """!Use wx.ListCtrl instead of wx.ListBox, different style for
813 non-selectable items (e.g. mapsets with denied permission)"""
814 def __init__(self, parent, id, size,
815 choices, disabled = []):
816 wx.ListCtrl.__init__(self, parent, id, size = size,
817 style = wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL |
820 listmix.ListCtrlAutoWidthMixin.__init__(self)
822 self.InsertColumn(0,
'')
828 def _LoadData(self, choices, disabled = []):
829 """!Load data into list
831 @param choices list of item
832 @param disabled list of indeces of non-selectable items
836 index = self.InsertStringItem(sys.maxint, item)
837 self.SetStringItem(index, 0, item)
840 self.SetItemTextColour(idx, wx.Colour(150, 150, 150))
844 self.DeleteAllItems()
850 if item != wx.NOT_FOUND
and \
851 (platform.system() !=
'Windows' or force):
853 self.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
861 """!Start-up application"""
864 wx.InitAllImageHandlers()
866 StartUp.CenterOnScreen()
867 self.SetTopWindow(StartUp)
870 if StartUp.GetRCValue(
"LOCATION_NAME") ==
"<UNKNOWN>":
871 wx.MessageBox(parent = StartUp,
872 caption = _(
'Starting GRASS for the first time'),
873 message = _(
'GRASS needs a directory in which to store its data. '
874 'Create one now if you have not already done so. '
875 'A popular choice is "grassdata", located in '
876 'your home directory.'),
877 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
879 StartUp.OnBrowse(
None)
883 if __name__ ==
"__main__":
885 if os.getenv(
"GISBASE")
is None:
886 print >> sys.stderr,
"Failed to start GUI, GRASS GIS is not running."
889 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
895 GRASSStartUp.MainLoop()