guiutils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. """
  2. @package startup.guiutils
  3. @brief General GUI-dependent utilities for GUI startup of GRASS GIS
  4. (C) 2018 by Vaclav Petras the GRASS Development Team
  5. This program is free software under the GNU General Public License
  6. (>=v2). Read the file COPYING that comes with GRASS for details.
  7. @author Vaclav Petras <wenzeslaus gmail com>
  8. @author Linda Kladivova <l.kladivova@seznam.cz>
  9. This is for code which depend on something from GUI (wx or wxGUI).
  10. """
  11. import os
  12. import sys
  13. import wx
  14. import grass.script as gs
  15. from core import globalvar
  16. from core.gcmd import GError, GMessage, DecodeString, RunCommand
  17. from gui_core.dialogs import TextEntryDialog
  18. from gui_core.widgets import GenericMultiValidator
  19. from startup.utils import (create_mapset, delete_mapset, delete_location,
  20. rename_mapset, rename_location, mapset_exists,
  21. location_exists, get_default_mapset_name)
  22. def SetSessionMapset(database, location, mapset):
  23. """Sets database, location and mapset for the current session"""
  24. RunCommand("g.gisenv", set="GISDBASE=%s" % database)
  25. RunCommand("g.gisenv", set="LOCATION_NAME=%s" % location)
  26. RunCommand("g.gisenv", set="MAPSET=%s" % mapset)
  27. class MapsetDialog(TextEntryDialog):
  28. def __init__(self, parent=None, default=None, message=None, caption=None,
  29. database=None, location=None):
  30. self.database = database
  31. self.location = location
  32. # list of tuples consisting of conditions and callbacks
  33. checks = [(gs.legal_name, self._nameValidationFailed),
  34. (self._checkMapsetNotExists, self._mapsetAlreadyExists),
  35. (self._checkOGR, self._reservedMapsetName)]
  36. validator = GenericMultiValidator(checks)
  37. TextEntryDialog.__init__(
  38. self, parent=parent,
  39. message=message,
  40. caption=caption,
  41. defaultValue=default,
  42. validator=validator,
  43. )
  44. def _nameValidationFailed(self, ctrl):
  45. message = _(
  46. "Name '{}' is not a valid name for location or mapset. "
  47. "Please use only ASCII characters excluding characters {} "
  48. "and space.").format(ctrl.GetValue(), '/"\'@,=*~')
  49. GError(parent=self, message=message, caption=_("Invalid name"))
  50. def _checkOGR(self, text):
  51. """Check user's input for reserved mapset name."""
  52. if text.lower() == 'ogr':
  53. return False
  54. return True
  55. def _reservedMapsetName(self, ctrl):
  56. message = _(
  57. "Name '{}' is reserved for direct "
  58. "read access to OGR layers. Please use "
  59. "another name for your mapset.").format(ctrl.GetValue())
  60. GError(parent=self, message=message,
  61. caption=_("Reserved mapset name"))
  62. def _checkMapsetNotExists(self, text):
  63. """Check whether user's input mapset exists or not."""
  64. if mapset_exists(self.database, self.location, text):
  65. return False
  66. return True
  67. def _mapsetAlreadyExists(self, ctrl):
  68. message = _(
  69. "Mapset '{}' already exists. Please consider using "
  70. "another name for your mapset.").format(ctrl.GetValue())
  71. GError(parent=self, message=message,
  72. caption=_("Existing mapset path"))
  73. class LocationDialog(TextEntryDialog):
  74. def __init__(self, parent=None, default=None, message=None, caption=None,
  75. database=None):
  76. self.database = database
  77. # list of tuples consisting of conditions and callbacks
  78. checks = [(gs.legal_name, self._nameValidationFailed),
  79. (self._checkLocationNotExists, self._locationAlreadyExists)]
  80. validator = GenericMultiValidator(checks)
  81. TextEntryDialog.__init__(
  82. self, parent=parent,
  83. message=message,
  84. caption=caption,
  85. defaultValue=default,
  86. validator=validator,
  87. )
  88. def _nameValidationFailed(self, ctrl):
  89. message = _(
  90. "Name '{}' is not a valid name for location or mapset. "
  91. "Please use only ASCII characters excluding characters {} "
  92. "and space.").format(ctrl.GetValue(), '/"\'@,=*~')
  93. GError(parent=self, message=message, caption=_("Invalid name"))
  94. def _checkLocationNotExists(self, text):
  95. """Check whether user's input location exists or not."""
  96. if location_exists(self.database, text):
  97. return False
  98. return True
  99. def _locationAlreadyExists(self, ctrl):
  100. message = _(
  101. "Location '{}' already exists. Please consider using "
  102. "another name for your location.").format(ctrl.GetValue())
  103. GError(parent=self, message=message,
  104. caption=_("Existing location path"))
  105. # TODO: similar to (but not the same as) read_gisrc function in grass.py
  106. def read_gisrc():
  107. """Read variables from a current GISRC file
  108. Returns a dictionary representation of the file content.
  109. """
  110. grassrc = {}
  111. gisrc = os.getenv("GISRC")
  112. if gisrc and os.path.isfile(gisrc):
  113. try:
  114. rc = open(gisrc, "r")
  115. for line in rc.readlines():
  116. try:
  117. key, val = line.split(":", 1)
  118. except ValueError as e:
  119. sys.stderr.write(
  120. _('Invalid line in GISRC file (%s):%s\n' % (e, line)))
  121. grassrc[key.strip()] = DecodeString(val.strip())
  122. finally:
  123. rc.close()
  124. return grassrc
  125. def GetVersion():
  126. """Gets version and revision
  127. Returns tuple `(version, revision)`. For standard releases revision
  128. is an empty string.
  129. Revision string is currently wrapped in parentheses with added
  130. leading space. This is an implementation detail and legacy and may
  131. change anytime.
  132. """
  133. versionFile = open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER"))
  134. versionLine = versionFile.readline().rstrip('\n')
  135. versionFile.close()
  136. try:
  137. grassVersion, grassRevision = versionLine.split(' ', 1)
  138. if grassVersion.endswith('dev'):
  139. grassRevisionStr = ' (%s)' % grassRevision
  140. else:
  141. grassRevisionStr = ''
  142. except ValueError:
  143. grassVersion = versionLine
  144. grassRevisionStr = ''
  145. return (grassVersion, grassRevisionStr)
  146. def create_mapset_interactively(guiparent, grassdb, location):
  147. """
  148. Create new mapset
  149. """
  150. dlg = MapsetDialog(
  151. parent=guiparent,
  152. default=get_default_mapset_name(),
  153. message=_("Name for the new mapset:"),
  154. caption=_("Create new mapset"),
  155. database=grassdb,
  156. location=location,
  157. )
  158. if dlg.ShowModal() == wx.ID_OK:
  159. mapset = dlg.GetValue()
  160. try:
  161. create_mapset(grassdb, location, mapset)
  162. except OSError as err:
  163. GError(
  164. parent=guiparent,
  165. message=_("Unable to create new mapset: %s") % err,
  166. showTraceback=False,
  167. )
  168. else:
  169. mapset = None
  170. dlg.Destroy()
  171. return mapset
  172. def rename_mapset_interactively(guiparent, grassdb, location, mapset):
  173. """
  174. Rename selected mapset
  175. """
  176. newmapset = None
  177. if mapset == "PERMANENT":
  178. GMessage(
  179. parent=guiparent,
  180. message=_(
  181. "Mapset <PERMANENT> is required for valid GRASS location.\n\n"
  182. "This mapset cannot be renamed."
  183. ),
  184. )
  185. return newmapset
  186. dlg = MapsetDialog(
  187. parent=guiparent,
  188. default=mapset,
  189. message=_("Current name: %s\n\nEnter new name:") % mapset,
  190. caption=_("Rename selected mapset"),
  191. database=grassdb,
  192. location=location,
  193. )
  194. if dlg.ShowModal() == wx.ID_OK:
  195. newmapset = dlg.GetValue()
  196. try:
  197. rename_mapset(grassdb, location, mapset, newmapset)
  198. except OSError as err:
  199. newmapset = None
  200. wx.MessageBox(
  201. parent=guiparent,
  202. caption=_("Error"),
  203. message=_("Unable to rename mapset.\n\n%s") % err,
  204. style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
  205. )
  206. dlg.Destroy()
  207. return newmapset
  208. def rename_location_interactively(guiparent, grassdb, location):
  209. """
  210. Rename selected location
  211. """
  212. dlg = LocationDialog(
  213. parent=guiparent,
  214. default=location,
  215. message=_("Current name: %s\n\nEnter new name:") % location,
  216. caption=_("Rename selected location"),
  217. database=grassdb,
  218. )
  219. if dlg.ShowModal() == wx.ID_OK:
  220. newlocation = dlg.GetValue()
  221. try:
  222. rename_location(grassdb, location, newlocation)
  223. except OSError as err:
  224. newlocation = None
  225. wx.MessageBox(
  226. parent=guiparent,
  227. caption=_("Error"),
  228. message=_("Unable to rename location.\n\n%s") % err,
  229. style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
  230. )
  231. else:
  232. newlocation = None
  233. dlg.Destroy()
  234. return newlocation
  235. def delete_mapset_interactively(guiparent, grassdb, location, mapset):
  236. """
  237. Delete selected mapset
  238. """
  239. if mapset == "PERMANENT":
  240. GMessage(
  241. parent=guiparent,
  242. message=_(
  243. "Mapset <PERMANENT> is required for valid GRASS location.\n\n"
  244. "This mapset cannot be deleted."
  245. ),
  246. )
  247. return False
  248. dlg = wx.MessageDialog(
  249. parent=guiparent,
  250. message=_(
  251. "Do you want to continue with deleting mapset <%(mapset)s> "
  252. "from location <%(location)s>?\n\n"
  253. "ALL MAPS included in this mapset will be "
  254. "PERMANENTLY DELETED!"
  255. )
  256. % {"mapset": mapset, "location": location},
  257. caption=_("Delete selected mapset"),
  258. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
  259. )
  260. if dlg.ShowModal() == wx.ID_YES:
  261. try:
  262. delete_mapset(grassdb, location, mapset)
  263. dlg.Destroy()
  264. return True
  265. except OSError as err:
  266. wx.MessageBox(
  267. parent=guiparent,
  268. caption=_("Error"),
  269. message=_("Unable to delete mapset.\n\n%s") % err,
  270. style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
  271. )
  272. dlg.Destroy()
  273. return False
  274. def delete_location_interactively(guiparent, grassdb, location):
  275. """
  276. Delete selected location
  277. """
  278. dlg = wx.MessageDialog(
  279. parent=guiparent,
  280. message=_(
  281. "Do you want to continue with deleting "
  282. "location <%s>?\n\n"
  283. "ALL MAPS included in this location will be "
  284. "PERMANENTLY DELETED!"
  285. )
  286. % (location),
  287. caption=_("Delete selected location"),
  288. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
  289. )
  290. if dlg.ShowModal() == wx.ID_YES:
  291. try:
  292. delete_location(grassdb, location)
  293. dlg.Destroy()
  294. return True
  295. except OSError as err:
  296. wx.MessageBox(
  297. parent=guiparent,
  298. caption=_("Error"),
  299. message=_("Unable to delete location.\n\n%s") % err,
  300. style=wx.OK | wx.ICON_ERROR | wx.CENTRE,
  301. )
  302. dlg.Destroy()
  303. return False