main.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. """!
  2. @package mapdisp.main
  3. @brief Start Map Display as standalone application
  4. Classes:
  5. - mapdisp::MapApp
  6. Usage:
  7. python mapdisp/main.py monitor-identifier /path/to/map/file /path/to/command/file /path/to/env/file
  8. (C) 2006-2011 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Michael Barton
  12. @author Jachym Cepicky
  13. @author Martin Landa <landa.martin gmail.com>
  14. @author Vaclav Petras <wenzeslaus gmail.com> (MapFrameBase)
  15. @author Anna Kratochvilova <kratochanna gmail.com> (MapFrameBase)
  16. """
  17. import os
  18. import sys
  19. if __name__ == "__main__":
  20. sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
  21. from core import globalvar
  22. import wx
  23. from core import utils
  24. from core.giface import StandaloneGrassInterface
  25. from core.gcmd import RunCommand
  26. from core.render import Map, MapLayer
  27. from mapdisp.frame import MapFrame
  28. from grass.script import core as grass
  29. from core.debug import Debug
  30. from core.settings import UserSettings
  31. # for standalone app
  32. monFile = { 'cmd' : None,
  33. 'map' : None,
  34. 'env' : None,
  35. }
  36. monName = None
  37. monSize = list(globalvar.MAP_WINDOW_SIZE)
  38. class DMonMap(Map):
  39. def __init__(self, cmdfile=None, mapfile=None, envfile=None, monitor=None):
  40. """!Map composition (stack of map layers and overlays)
  41. @param cmdline full path to the cmd file (defined by d.mon)
  42. @param mapfile full path to the map file (defined by d.mon)
  43. @param envfile full path to the env file (defined by d.mon)
  44. @param monitor name of monitor (defined by d.mon)
  45. """
  46. Map.__init__(self)
  47. # environment settings
  48. self.env = dict()
  49. self.cmdfile = cmdfile
  50. self.envfile = envfile
  51. self.monitor = monitor
  52. if mapfile:
  53. self.mapfileCmd = mapfile
  54. self.maskfileCmd = os.path.splitext(mapfile)[0] + '.pgm'
  55. # generated file for g.pnmcomp output for rendering the map
  56. self.mapfile = grass.tempfile(create = False) + '.ppm'
  57. self._writeEnvFile(self.env) # self.env is expected to be defined in parent class
  58. self._writeEnvFile({"GRASS_PNG_READ" : "TRUE"})
  59. def GetLayersFromCmdFile(self):
  60. """!Get list of map layers from cmdfile
  61. """
  62. if not self.cmdfile:
  63. return
  64. nlayers = 0
  65. try:
  66. fd = open(self.cmdfile, 'r')
  67. existingLayers = self.GetListOfLayers()
  68. for line in fd.readlines():
  69. cmd = utils.split(line.strip())
  70. ltype = None
  71. try:
  72. ltype = utils.command2ltype[cmd[0]]
  73. except KeyError:
  74. grass.warning(_("Unsupported command %s.") % cmd[0])
  75. continue
  76. name = utils.GetLayerNameFromCmd(cmd, fullyQualified = True,
  77. layerType = ltype)[0]
  78. # creating temporary layer object to compare commands
  79. # neccessary to get the same format
  80. # supposing that there are no side effects
  81. tmpMapLayer = MapLayer(ltype = ltype, name = name,
  82. cmd = cmd, Map = None,
  83. active = False, hidden = True,
  84. opacity = 0)
  85. exists = False
  86. for layer in existingLayers:
  87. if layer.GetCmd(string=True) == tmpMapLayer.GetCmd(string=True):
  88. exists = True
  89. break
  90. if exists:
  91. continue
  92. self.AddLayer(ltype = ltype, command = cmd, active = False, name = name)
  93. nlayers += 1
  94. except IOError, e:
  95. grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
  96. { 'cmd' : self.cmdfile, 'det' : e })
  97. return
  98. fd.close()
  99. Debug.msg(1, "Map.GetLayersFromCmdFile(): cmdfile=%s" % self.cmdfile)
  100. Debug.msg(1, " nlayers=%d" % nlayers)
  101. def _parseCmdFile(self):
  102. """!Parse cmd file for standalone application
  103. """
  104. nlayers = 0
  105. try:
  106. fd = open(self.cmdfile, 'r')
  107. grass.try_remove(self.mapfile)
  108. cmdLines = fd.readlines()
  109. RunCommand('g.gisenv',
  110. set = 'MONITOR_%s_CMDFILE=' % self.monitor)
  111. for cmd in cmdLines:
  112. cmdStr = utils.split(cmd.strip())
  113. cmd = utils.CmdToTuple(cmdStr)
  114. RunCommand(cmd[0], **cmd[1])
  115. nlayers += 1
  116. RunCommand('g.gisenv',
  117. set = 'MONITOR_%s_CMDFILE=%s' % (self.monitor, self.cmdfile))
  118. except IOError, e:
  119. grass.warning(_("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % \
  120. { 'cmd' : self.cmdfile, 'det' : e })
  121. return
  122. fd.close()
  123. Debug.msg(1, "Map.__parseCmdFile(): cmdfile=%s" % self.cmdfile)
  124. Debug.msg(1, " nlayers=%d" % nlayers)
  125. return nlayers
  126. def _renderCmdFile(self, force, windres):
  127. if not force:
  128. return ([self.mapfileCmd],
  129. [self.maskfileCmd],
  130. ['1.0'])
  131. region = os.environ["GRASS_REGION"] = self.SetRegion(windres)
  132. self._writeEnvFile({'GRASS_REGION' : region})
  133. currMon = grass.gisenv()['MONITOR']
  134. if currMon != self.monitor:
  135. RunCommand('g.gisenv',
  136. set = 'MONITOR=%s' % self.monitor)
  137. grass.try_remove(self.mapfileCmd) # GRASS_PNG_READ is TRUE
  138. nlayers = self._parseCmdFile()
  139. if self.overlays:
  140. RunCommand('g.gisenv',
  141. unset = 'MONITOR') # GRASS_RENDER_IMMEDIATE doesn't like monitors
  142. driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
  143. if driver == 'png':
  144. os.environ["GRASS_RENDER_IMMEDIATE"] = "png"
  145. else:
  146. os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
  147. self._renderLayers(overlaysOnly = True)
  148. del os.environ["GRASS_RENDER_IMMEDIATE"]
  149. RunCommand('g.gisenv',
  150. set = 'MONITOR=%s' % currMon)
  151. if currMon != self.monitor:
  152. RunCommand('g.gisenv',
  153. set = 'MONITOR=%s' % currMon)
  154. if nlayers > 0:
  155. return ([self.mapfileCmd],
  156. [self.maskfileCmd],
  157. ['1.0'])
  158. else:
  159. return ([], [], [])
  160. def _writeEnvFile(self, data):
  161. """!Write display-related variable to the file (used for
  162. standalone app)
  163. """
  164. if not self.envfile:
  165. return
  166. try:
  167. fd = open(self.envfile, "r")
  168. for line in fd.readlines():
  169. key, value = line.split('=')
  170. if key not in data.keys():
  171. data[key] = value
  172. fd.close()
  173. fd = open(self.envfile, "w")
  174. for k, v in data.iteritems():
  175. fd.write('%s=%s\n' % (k.strip(), str(v).strip()))
  176. except IOError, e:
  177. grass.warning(_("Unable to open file '%(file)s' for writting. Details: %(det)s") % \
  178. { 'cmd' : self.envfile, 'det' : e })
  179. return
  180. fd.close()
  181. def ChangeMapSize(self, (width, height)):
  182. """!Change size of rendered map.
  183. @param width,height map size
  184. """
  185. Map.ChangeMapSize(self, (width, height))
  186. self._writeEnvFile({'GRASS_WIDTH' : self.width,
  187. 'GRASS_HEIGHT' : self.height})
  188. def GetMapsMasksAndOpacities(self, force, windres):
  189. """!
  190. Used by Render function.
  191. @return maps, masks, opacities
  192. """
  193. return self._renderCmdFile(force, windres)
  194. class Layer(object):
  195. def __init__(self, maplayer):
  196. self._maplayer = maplayer
  197. def __getattr__(self, name):
  198. if name == 'cmd':
  199. return utils.CmdTupleToList(self._maplayer.GetCmd())
  200. elif hasattr(self._maplayer, name):
  201. return getattr(self._maplayer, name)
  202. elif name == 'maplayer':
  203. return self._maplayer
  204. elif name == 'type':
  205. return self._maplayer.GetType()
  206. #elif name == 'ctrl':
  207. elif name == 'label':
  208. return self._maplayer.GetName()
  209. #elif name == 'maplayer' : None,
  210. #elif name == 'propwin':
  211. class LayerList(object):
  212. def __init__(self, map):
  213. self._map = map
  214. # def __iter__(self):
  215. # for in :
  216. # yield
  217. def GetSelectedLayers(self, checkedOnly=True):
  218. # hidden and selected vs checked and selected
  219. items = self._map.GetListOfLayers()
  220. layers = []
  221. for item in items:
  222. layer = Layer(item)
  223. layers.append(layer)
  224. return layers
  225. class DMonGrassInterface(StandaloneGrassInterface):
  226. """!@implements GrassInterface"""
  227. def __init__(self, mapframe):
  228. StandaloneGrassInterface.__init__(self)
  229. self._mapframe = mapframe
  230. def GetLayerList(self):
  231. return LayerList(self._mapframe.GetMap())
  232. class DMonFrame(MapFrame):
  233. def OnZoomToMap(self, event):
  234. layers = self.MapWindow.GetMap().GetListOfLayers()
  235. self.MapWindow.ZoomToMap(layers = layers)
  236. class MapApp(wx.App):
  237. def OnInit(self):
  238. if not globalvar.CheckWxVersion([2, 9]):
  239. wx.InitAllImageHandlers()
  240. if __name__ == "__main__":
  241. self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
  242. self.Map = DMonMap(cmdfile = monFile['cmd'], mapfile = monFile['map'],
  243. envfile = monFile['env'], monitor = monName)
  244. else:
  245. self.Map = None
  246. # actual use of StandaloneGrassInterface not yet tested
  247. # needed for adding functionality in future
  248. giface = DMonGrassInterface(None)
  249. self.mapFrm = DMonFrame(parent = None, id = wx.ID_ANY, Map = self.Map,
  250. giface = giface, size = monSize)
  251. # FIXME: hack to solve dependency
  252. giface._mapframe = self.mapFrm
  253. # self.SetTopWindow(Map)
  254. self.mapFrm.GetMapWindow().SetAlwaysRenderEnabled(True)
  255. self.mapFrm.Show()
  256. if __name__ == "__main__":
  257. self.timer = wx.PyTimer(self.watcher)
  258. #check each 0.5s
  259. global mtime
  260. mtime = 500
  261. self.timer.Start(mtime)
  262. return True
  263. def OnExit(self):
  264. if __name__ == "__main__":
  265. # stop the timer
  266. # self.timer.Stop()
  267. # terminate thread
  268. for f in monFile.itervalues():
  269. grass.try_remove(f)
  270. def watcher(self):
  271. """!Redraw, if new layer appears (check's timestamp of
  272. cmdfile)
  273. """
  274. try:
  275. # GISBASE and other sytem enviromental variables can not be used
  276. # since the process inherited them from GRASS
  277. # raises exception when vaiable does not exists
  278. grass.gisenv()['GISDBASE']
  279. except KeyError:
  280. self.timer.Stop()
  281. return
  282. # todo: events
  283. try:
  284. currentCmdFileTime = os.path.getmtime(monFile['cmd'])
  285. if currentCmdFileTime > self.cmdTimeStamp:
  286. self.timer.Stop()
  287. self.cmdTimeStamp = currentCmdFileTime
  288. self.mapFrm.OnDraw(None)
  289. self.mapFrm.GetMap().GetLayersFromCmdFile()
  290. self.timer.Start(mtime)
  291. except OSError, e:
  292. grass.warning("%s" % e)
  293. self.timer.Stop()
  294. def GetMapFrame(self):
  295. """!Get Map Frame instance"""
  296. return self.mapFrm
  297. if __name__ == "__main__":
  298. # set command variable
  299. if len(sys.argv) < 5:
  300. print __doc__
  301. sys.exit(1)
  302. monName = sys.argv[1]
  303. monFile = { 'map' : sys.argv[2],
  304. 'cmd' : sys.argv[3],
  305. 'env' : sys.argv[4],
  306. }
  307. if len(sys.argv) >= 6:
  308. try:
  309. monSize[0] = int(sys.argv[5])
  310. except ValueError:
  311. pass
  312. if len(sys.argv) == 7:
  313. try:
  314. monSize[1] = int(sys.argv[6])
  315. except ValueError:
  316. pass
  317. import gettext
  318. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  319. grass.verbose(_("Starting map display <%s>...") % (monName))
  320. RunCommand('g.gisenv',
  321. set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
  322. gmMap = MapApp(0)
  323. # set title
  324. gmMap.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
  325. monName +
  326. " - Location: " + grass.gisenv()["LOCATION_NAME"]))
  327. gmMap.MainLoop()
  328. grass.verbose(_("Stopping map display <%s>...") % (monName))
  329. # clean up GRASS env variables
  330. env = grass.gisenv()
  331. env_name = 'MONITOR_%s' % monName
  332. for key in env.keys():
  333. if key.find(env_name) == 0:
  334. RunCommand('g.gisenv',
  335. unset = '%s' % key)
  336. if key == 'MONITOR' and env[key] == monName:
  337. RunCommand('g.gisenv',
  338. unset = '%s' % key)
  339. sys.exit(0)