[GRASS-SVN] r65205 - in grass/trunk/gui/wxpython: core lmgr mapdisp mapwin

svn_grass at osgeo.org svn_grass at osgeo.org
Sun May 10 02:01:39 PDT 2015


Author: martinl
Date: 2015-05-10 02:01:38 -0700 (Sun, 10 May 2015)
New Revision: 65205

Modified:
   grass/trunk/gui/wxpython/core/render.py
   grass/trunk/gui/wxpython/core/ws.py
   grass/trunk/gui/wxpython/lmgr/layertree.py
   grass/trunk/gui/wxpython/mapdisp/frame.py
   grass/trunk/gui/wxpython/mapdisp/main.py
   grass/trunk/gui/wxpython/mapwin/buffered.py
Log:
wxGUI: implement RenderLayerMgr and RenderMapMgr to render map layers in threads


Modified: grass/trunk/gui/wxpython/core/render.py
===================================================================
--- grass/trunk/gui/wxpython/core/render.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/core/render.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -3,19 +3,13 @@
 
 @brief Rendering map layers and overlays into map composition image.
 
-.. todo::
-    Implement RenderManager also for other layers (see WMS
-    implementation for details)
-
-.. todo::
-    Render classes should not care about updating statusbar (change
-    emiting events).
-
 Classes:
  - render::Layer
  - render::MapLayer
  - render::Overlay
  - render::Map
+ - render::RenderLayerMgr
+ - render::RenderMapMgr
 
 (C) 2006-2015 by the GRASS Development Team
 
@@ -40,7 +34,7 @@
 
 from grass.script import core as grass
 from grass.script.utils import try_remove
-from grass.script.task import cmdlist_to_tuple
+from grass.script.task import cmdlist_to_tuple, cmdtuple_to_list
 from grass.pydispatch.signal import Signal
 
 from core          import utils
@@ -49,6 +43,7 @@
 from core.gcmd     import GException, GError, RunCommand
 from core.debug    import Debug
 from core.settings import UserSettings
+from core.gconsole import CmdThread, GStderr, EVT_CMD_DONE
 
 class Layer(object):
     """Virtual class which stores information about layers (map layers and
@@ -122,7 +117,7 @@
                    (self.name, self.GetCmd(string = True)))
 
     def __str__(self):
-        return self.name
+        return self.GetCmd(string=True)
 
     def __repr__(self):
         return self.__str__()
@@ -150,55 +145,31 @@
             raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
                                  {'type' : self.type, 'name' : self.name})
 
+        # render layers
         env.update(self.render_env)
-        # execute command
         try:
             if self.type == 'command':
-                read = False
+                first = True
                 for c in self.cmd:
-                    ret, msg = self._runCommand(c, env)
-                    if ret != 0:
-                        break
-                    if not read:
+                    self.renderMgr.Render(c, env)
+                    if first:
                         env["GRASS_RENDER_FILE_READ"] = "TRUE"
-
-                env["GRASS_RENDER_FILE_READ"] = "FALSE"
+                        first = False
             else:
-                ret, msg = self._runCommand(self.cmd, env)
-            if ret != 0:
-                sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
-                if msg:
-                    sys.stderr.write(_("Details: %s\n") % msg)
-                raise GException()
-
+                self.renderMgr.Render(self.cmd, env)
         except GException:
+            sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
+            sys.stderr.write(_("Details: %s\n") % e)
+            
             # clean up after problems
             for f in [self.mapfile, self.maskfile]:
                 if not f:
                     continue
                 try_remove(f)
                 f = None
-
-        self.forceRender = False
-
+        
         return self.mapfile
-
-    def _runCommand(self, cmd, env):
-        """Run command to render data
-        """
-        if self.type == 'wms':
-            ret = 0
-            msg = ''
-            self.renderMgr.Render(cmd, env=env)
-        else:
-            ret, msg = RunCommand(cmd[0],
-                                  getErrorMsg = True,
-                                  quiet = True,
-                                  env=env,
-                                  **cmd[1])
-
-        return ret, msg
-
+    
     def GetCmd(self, string = False):
         """Get GRASS command as list of string.
 
@@ -275,13 +246,13 @@
         if ltype not in utils.command2ltype.values() + ['overlay', 'command']:
             raise GException(_("Unsupported map layer type '%s'") % ltype)
 
-        if ltype == 'wms' and not isinstance(self.renderMgr, RenderWMSMgr):
-            self.renderMgr = RenderWMSMgr(layer=self,
-                                          mapfile=self.mapfile,
-                                          maskfile=self.maskfile)
-        elif self.type == 'wms' and ltype != 'wms':
-            self.renderMgr = None
-
+        if not self.renderMgr:
+            if ltype == 'wms':
+                renderMgr = RenderWMSMgr
+            else:
+                renderMgr = RenderLayerMgr
+            self.renderMgr = renderMgr(self)
+        
         self.type = ltype
 
     def SetName(self, name):
@@ -320,17 +291,11 @@
 
     def IsDownloading(self):
         """Is data downloading from web server e. g. wms"""
-        if self.renderMgr is None:
-            return False
-        else:
-            return self.renderMgr.IsDownloading()
+        return self.renderMgr.IsDownloading()
 
     def AbortThread(self):
         """Abort running thread e. g. downloading data"""
-        if self.renderMgr is None:
-            return
-        else:
-            self.renderMgr.Abort()
+        self.renderMgr.Abort()
 
     def GetRenderMgr(self):
         """Get render manager """
@@ -367,13 +332,272 @@
         self.render_env["GRASS_RENDER_FILE_READ"] = "FALSE"
         self.render_env["GRASS_RENDER_TRANSPARENT"] = "TRUE"
         
+class RenderLayerMgr(wx.EvtHandler):
+    def __init__(self, layer):
+        """Render layer into image
+
+        :param layer: Layer to be rendered
+        """
+        self.layer = layer
+         
+        wx.EvtHandler.__init__(self)
+        self.thread = CmdThread(self)
+        self.cmdStdErr = GStderr(self)
+        
+        self.updateProgress = Signal('RenderLayerMgr.updateProgress')
+        self.Bind(EVT_CMD_DONE, self.OnRenderDone)
+        
+        self._startTime = None
+         
+    def Render(self, cmd, env):
+        """Render layer
+
+        :param cmd: display command given as tuple
+        :param env: environmental variables used for rendering
+        """
+        Debug.msg(1, "RenderLayerMgr.Render(%s): force=%d img=%s" % \
+                  (self.layer, self.layer.forceRender, self.layer.mapfile))
+        
+        env_cmd = env.copy()
+        env_cmd['GRASS_RENDER_FILE'] = self.layer.mapfile
+        
+        cmd[1]['quiet'] = True # be quiet
+        cmd_tuple = cmdtuple_to_list(cmd)
+        
+        self._startTime = time.time()
+        self.thread.RunCmd(cmd_tuple, env=env_cmd, stderr=self.cmdStdErr)
+        self.layer.forceRender = False
+        
+    def Abort(self):
+        """Abort rendering process"""
+        self.thread.abort(abortall = True)        
+
+    def IsDownloading(self):
+        """Is downloading
+
+        :return: always False
+        """
+        return False
+    
+    def OnRenderDone(self, event):
+        """Rendering done
+
+        Emits updateProcess
+        """
+        stop = time.time()
+        Debug.msg(1, "RenderLayerMgr.OnRenderDone(%s): ret=%d time=%f" % \
+                      (self.layer, event.returncode, stop - self._startTime))
+        self.updateProgress.emit(layer=self.layer)
+        
+class RenderMapMgr(wx.EvtHandler):
+    def __init__(self, Map):
+        """Render map layers as image composition
+
+        :param Map: Map object to be rendered
+        """
+        wx.EvtHandler.__init__(self)
+
+        self.Map = Map
+        self.thread = CmdThread(self)
+        self.cmdStdErr = GStderr(self)
+        
+        self.updateMap = Signal('RenderMapMgr.updateMap')
+        self.updateProgress = Signal('RenderMapMgr.updateProgress')
+        self.renderDone = Signal('RenderMapMgr.renderDone')
+        self.renderDone.connect(self.OnRenderDone)
+        
+        # GRASS environment variable (for rendering)
+        self._render_env = {"GRASS_RENDER_BACKGROUNDCOLOR" : "000000",
+                            "GRASS_RENDER_FILE_COMPRESSION" : "0",
+                            "GRASS_RENDER_TRUECOLOR"       : "TRUE",
+                            "GRASS_RENDER_TRANSPARENT"     : "TRUE" }
+        
+        self._init()
+        self._rendering = False
+        
+    def _init(self, env=None):
+        """Init render manager
+
+        :param env: environmental variables or None
+        """
+        self._startTime = time.time()
+        self.progressInfo = None
+        self._env = env
+        self.layers = []
+        
+        # re-render from scratch
+        if os.path.exists(self.Map.mapfile):
+            os.remove(self.Map.mapfile)
+        
+    def _renderLayers(self, env, force = False, overlaysOnly = False):
+        """Render all map layers into files
+
+        :param dict env: environmental variables to be used for rendering process
+        :param bool force: True to force rendering
+        :param bool overlaysOnly: True to render only overlays
+
+        :return: number of layers to be rendered
+        """
+        if overlaysOnly:
+            self.layers = self.Map.GetListOfLayers(ltype='overlay', active=True)
+        else:
+            self.layers = self.Map.GetListOfLayers(active=True)
+        
+        # reset progress
+        self.ReportProgress()
+
+        # render map layers if forced
+        nlayers = 0
+        for layer in self.layers:
+            if force or layer.forceRender:
+                nlayers += 1
+                layer.Render(env)
+            else:
+                layer.GetRenderMgr().updateProgress.emit(layer=layer)
+        
+        Debug.msg(1, "RenderMapMgr.Render(): %d layers to be rendered "
+                  "(force=%d, all active layers -> %d)" % (nlayers, force,
+                                                           len(self.layers)))
+        
+        return nlayers
+    
+    def Render(self, force = False, windres = False):
+        """Render map composition
+
+        :param bool force: force rendering all map layers in the composition
+        :param windres: True for region resolution instead for map resolution
+        """
+        if self._rendering:
+            Debug.msg(1, "RenderMapMgr().Render(): cancelled (already rendering)")
+            return
+        
+        wx.BeginBusyCursor()
+        self._rendering = True
+        
+        env = os.environ.copy()
+        env.update(self._render_env)
+        # use external gisrc if defined
+        if self.Map.gisrc:
+            env['GISRC'] = self.Map.gisrc
+        env['GRASS_REGION'] = self.Map.SetRegion(windres)
+        env['GRASS_RENDER_WIDTH'] = str(self.Map.width)
+        env['GRASS_RENDER_HEIGHT'] = str(self.Map.height)
+        driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
+        if driver == 'png':
+            env['GRASS_RENDER_IMMEDIATE'] = 'png'
+        else:
+            env['GRASS_RENDER_IMMEDIATE'] = 'cairo'
+        
+        self._init(env)
+        if self._renderLayers(env, force, windres) == 0:
+            self.renderDone.emit()
+        
+    def OnRenderDone(self):
+        """Rendering process done
+
+        Make image composiotion, emits updateMap event.
+        """
+        stopTime = time.time()
+        
+        maps = list()
+        masks = list()
+        opacities = list()
+        
+        for layer in self.layers:
+            maps.append(layer.mapfile)
+            masks.append(layer.maskfile)
+            opacities.append(str(layer.opacity))
+        
+        # run g.pngcomp to get composite image
+        bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
+                                                     subkey = 'color')))
+        startCompTime = time.time()
+        if maps:
+            ret, msg = RunCommand('g.pnmcomp',
+                                  getErrorMsg = True,
+                                  overwrite = True,
+                                  input = '%s' % ",".join(maps),
+                                  mask = '%s' % ",".join(masks),
+                                  opacity = '%s' % ",".join(opacities),
+                                  bgcolor = bgcolor,
+                                  width = self.Map.width,
+                                  height = self.Map.height,
+                                  output = self.Map.mapfile,
+                                  env=self._env)
+            if ret != 0:
+                raise GException(_("Rendering failed: %s" % msg))
+        
+        stop = time.time()
+        Debug.msg (1, "RenderMapMgr.OnRenderDone() time=%f sec (comp: %f)" % \
+                   (stop - self._startTime, stop - startCompTime))
+        
+        self._rendering = False
+        wx.EndBusyCursor()
+        
+        self.updateMap.emit()
+
+    def Abort(self):
+        """Abort all rendering processes"""
+        for layer in self.layers:
+            layer.GetRenderMgr().Abort()
+        
+    def ReportProgress(self, layer=None):
+        """Calculates progress in rendering/downloading
+        and emits signal to inform progress bar about progress.
+
+        Emits renderDone event when progressVal is equal to range.
+        
+        :param layer: Layer to be processed or None to reset
+        """
+        if self.progressInfo is None or layer is None:
+            self.progressInfo = {'progresVal' : 0,   # current progress value
+                                 'downloading' : [], # layers, which are downloading data
+                                 'rendered' : [],    # already rendered layers
+                                 'range' : len(self.layers)}
+        else:
+            if layer not in self.progressInfo['rendered']:
+                self.progressInfo['rendered'].append(layer)
+            if layer.IsDownloading() and \
+                    layer not in self.progressInfo['downloading']:
+                self.progressInfo['downloading'].append(layer)
+            else:
+                self.progressInfo['progresVal'] += 1
+                if layer in self.progressInfo['downloading']:
+                    self.progressInfo['downloading'].remove(layer)
+
+        # for updating statusbar text
+        stText = ''
+        first = True
+        for layer in self.progressInfo['downloading']:
+            if first:
+                stText += _("Downloading data ")
+                first = False
+            else:
+                stText += ', '
+            stText += '<%s>' % layer.GetName()
+        if stText:
+            stText += '...'
+
+        if  self.progressInfo['range'] != len(self.progressInfo['rendered']):
+            if stText:
+                stText = _('Rendering & ') + stText
+            else:
+                stText = _('Rendering...')
+        
+        self.updateProgress.emit(range=self.progressInfo['range'],
+                                 value=self.progressInfo['progresVal'],
+                                 text=stText)
+        
+        if layer and self.progressInfo['progresVal'] == self.progressInfo['range']:
+            self.renderDone.emit()
+
 class Map(object):
     def __init__(self, gisrc = None):
         """Map composition (stack of map layers and overlays)
 
         :param gisrc: alternative gisrc (used eg. by georectifier)
         """
-        Debug.msg (1, "Map.__init__(): %s" % gisrc)
+        Debug.msg (1, "Map.__init__(): gisrc=%s" % gisrc)
         # region/extent settigns
         self.wind      = dict() # WIND settings (wind file)
         self.region    = dict() # region settings (g.region)
@@ -388,7 +612,7 @@
 
         # path to external gisrc
         self.gisrc = gisrc
-
+        
         # generated file for g.pnmcomp output for rendering the map
         self.mapfile = grass.tempfile(create = False) + '.ppm'
         
@@ -397,27 +621,19 @@
             sys.stderr.write(_("Trying to recover from default region..."))
             RunCommand('g.region', flags='d')
 
-        # info to report progress
-        self.progressInfo = None
-
-        # GRASS environment variable (for rendering)
-        self.render_env = {"GRASS_RENDER_BACKGROUNDCOLOR" : "000000",
-                            "GRASS_RENDER_FILE_COMPRESSION" : "0",
-                            "GRASS_RENDER_TRUECOLOR"       : "TRUE",
-                            "GRASS_RENDER_TRANSPARENT"     : "TRUE",
-                            }
-
         # projection info
         self.projinfo = self._projInfo()
 
-        # is some layer being downloaded?
-        self.downloading = False
-
         self.layerChanged = Signal('Map.layerChanged')
-        self.updateProgress = Signal('Map.updateProgress')
         self.layerRemoved = Signal('Map:layerRemoved')
         self.layerAdded = Signal('Map:layerAdded')
+        
+        self.renderMgr = RenderMapMgr(self)
 
+    def GetRenderMgr(self):
+        """Get render manager """
+        return self.renderMgr
+        
     def GetProjInfo(self):
         """Get projection info"""
         return self.projinfo
@@ -844,62 +1060,6 @@
 
         return selected
 
-    def _renderLayers(self, env, force = False, overlaysOnly = False):
-        """Render all map layers into files
-
-        :param bool force: True to force rendering
-        :param bool overlaysOnly: True to render only overlays
-
-        :return: list of maps, masks and opacities
-        """
-        maps = list()
-        masks = list()
-        opacities = list()
-        # render map layers
-        if overlaysOnly:
-            layers = self.overlays
-        else:
-            layers = self.layers + self.overlays
-
-        self.downloading = False
-        
-        self.ReportProgress(layer=None)
-
-        for layer in layers:
-            # skip non-active map layers
-            if not layer or not layer.active:
-                continue
-
-            # render
-            if force or layer.forceRender:
-                if not layer.Render(env):
-                    continue
-
-            if layer.IsDownloading():
-                self.downloading = True
-
-            self.ReportProgress(layer=layer)
-
-            # skip map layers when rendering fails
-            if not os.path.exists(layer.mapfile):
-                continue
-
-            # add image to compositing list
-            if layer.type != "overlay":
-                maps.append(layer.mapfile)
-                masks.append(layer.maskfile)
-                opacities.append(str(layer.opacity))
-        
-        return maps, masks, opacities
-
-    def GetMapsMasksAndOpacities(self, force, windres, env):
-        """
-        Used by Render function.
-
-        :return: maps, masks, opacities
-        """
-        return self._renderLayers(force=force, env=env)
-
     def Render(self, force = False, windres = False):
         """Creates final image composite
 
@@ -909,73 +1069,9 @@
         :param force: force rendering
         :param windres: use region resolution (True) otherwise display
                         resolution
-
-        :return: name of file with rendered image or None
         """
-        wx.BeginBusyCursor()
-        env = os.environ.copy()
-        env.update(self.render_env)
-        # use external gisrc if defined
-        if self.gisrc:
-            env['GISRC'] = self.gisrc
-        env['GRASS_REGION'] = self.SetRegion(windres)
-        env['GRASS_RENDER_WIDTH'] = str(self.width)
-        env['GRASS_RENDER_HEIGHT'] = str(self.height)
-        driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
-        if driver == 'png':
-            env['GRASS_RENDER_IMMEDIATE'] = 'png'
-        else:
-            env['GRASS_RENDER_IMMEDIATE'] = 'cairo'
+        self.renderMgr.Render(force, windres)
         
-        start = time.time()
-        maps, masks, opacities = self.GetMapsMasksAndOpacities(force, windres, env)
-
-        # ugly hack for MSYS
-        if sys.platform != 'win32':
-            mapstr = ",".join(maps)
-            maskstr = ",".join(masks)
-        else:
-            mapstr = ""
-            for item in maps:
-                mapstr += item.replace('\\', '/')
-            mapstr = mapstr.rstrip(',')
-            maskstr = ""
-            for item in masks:
-                maskstr += item.replace('\\', '/')
-            maskstr = maskstr.rstrip(',')
-
-        # run g.pngcomp to get composite image
-        bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
-                                                     subkey = 'color')))
-        startComp = time.time()
-        if maps:
-            ret, msg = RunCommand('g.pnmcomp',
-                                  getErrorMsg = True,
-                                  overwrite = True,
-                                  input = '%s' % ",".join(maps),
-                                  mask = '%s' % ",".join(masks),
-                                  opacity = '%s' % ",".join(opacities),
-                                  bgcolor = bgcolor,
-                                  width = self.width,
-                                  height = self.height,
-                                  output = self.mapfile,
-                                  env=env)
-
-            if ret != 0:
-                print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
-                wx.EndBusyCursor()
-                return None
-
-        stop = time.time()
-        Debug.msg (1, "Map.Render() force=%s -> time=%f (comp: %f)" % \
-                   (force, stop - start, stop - startComp))
-        
-        wx.EndBusyCursor()
-        if not maps:
-            return None
-
-        return self.mapfile
-
     def _addLayer(self, layer, render=False, pos=-1):
         if layer.type == 'overlay':
             llist = self.overlays
@@ -1023,8 +1119,9 @@
         
         renderMgr = layer.GetRenderMgr()
         if renderMgr:
-            renderMgr.dataFetched.connect(self.layerChanged)
-            renderMgr.updateProgress.connect(self.ReportProgress)
+            if layer.type == 'wms':
+                renderMgr.dataFetched.connect(self.layerChanged)
+            renderMgr.updateProgress.connect(self.renderMgr.ReportProgress)
         
         self.layerAdded.emit(layer=layer)
         
@@ -1107,11 +1204,13 @@
         if 'opacity' in kargs:
             layer.SetOpacity(kargs['opacity'])
 
+
         if render and not layer.Render():
             raise GException(_("Unable to render map layer <%s>.") %
                              layer.GetName())
-
+        
         self.layerChanged(layer=layer)
+        
         return layer
 
     def ChangeOpacity(self, layer, opacity):
@@ -1321,48 +1420,3 @@
         """Abort all layers threads e. g. donwloading data"""
         for l in self.layers + self.overlays:
             l.AbortThread()
-
-    def ReportProgress(self, layer):
-        """Calculates progress in rendering/downloading
-        and emits signal to inform progress bar about progress.
-        """
-        if self.progressInfo is None or layer is None:
-            self.progressInfo = {'progresVal' : 0, # current progress value
-                                 'downloading' : [], # layers, which are downloading data
-                                 'rendered' : [], # already rendered layers
-                                 'range' : len(self.GetListOfLayers(active = True)) +
-                                           len(self.GetListOfLayers(active = True, ltype = 'overlay')) -
-                                           len(self.GetListOfLayers(active = True, ltype = '3d-raster'))}
-        else:
-            if layer not in self.progressInfo['rendered']:
-                self.progressInfo['rendered'].append(layer)
-            if layer.IsDownloading() and \
-                    layer not in self.progressInfo['downloading']:
-                self.progressInfo['downloading'].append(layer)
-            else:
-                self.progressInfo['progresVal'] += 1
-                if layer in self.progressInfo['downloading']:
-                    self.progressInfo['downloading'].remove(layer)
-
-        # for updating statusbar text
-        stText = ''
-        first = True
-        for layer in self.progressInfo['downloading']:
-            if first:
-                stText += _("Downloading data ")
-                first = False
-            else:
-                stText += ', '
-            stText += '<%s>' % layer.GetName()
-        if stText:
-            stText += '...'
-
-        if  self.progressInfo['range'] != len(self.progressInfo['rendered']):
-            if stText:
-                stText = _('Rendering & ') + stText
-            else:
-                stText = _('Rendering...')
-
-        self.updateProgress.emit(range=self.progressInfo['range'],
-                                 value=self.progressInfo['progresVal'],
-                                 text=stText)

Modified: grass/trunk/gui/wxpython/core/ws.py
===================================================================
--- grass/trunk/gui/wxpython/core/ws.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/core/ws.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -45,13 +45,13 @@
 class RenderWMSMgr(wx.EvtHandler):
     """Fetch and prepare WMS data for rendering.
     """
-    def __init__(self, layer, mapfile, maskfile):
+    def __init__(self, layer):
         if not haveGdal:
             sys.stderr.write(_("Unable to load GDAL Python bindings.\n"\
                                "WMS layers can not be displayed without the bindings.\n"))
 
         self.layer = layer
-
+        
         wx.EvtHandler.__init__(self)
 
         # thread for d.wms commands
@@ -65,8 +65,6 @@
 
         self.cmdStdErr = GStderr(self)
 
-        self.mapfile = mapfile
-        self.maskfile = maskfile
         self.tempMap = grass.tempfile()
         self.dstSize = {}
  
@@ -119,7 +117,7 @@
             self.fetched_data_cmd = None
             self.renderedRegion = region
 
-            try_remove(self.mapfile)
+            try_remove(self.layer.mapfile)
             try_remove(self.tempMap)
 
             self.currentPid = self.thread.GetId()
@@ -159,12 +157,13 @@
             self.fetched_data_cmd = None
             return
 
-        self.mapMerger = GDALRasterMerger(targetFile = self.mapfile, region = self.renderedRegion,
+        self.mapMerger = GDALRasterMerger(targetFile = self.layer.mapfile,
+                                          region = self.renderedRegion,
                                           bandsNum = 3, gdalDriver = 'PNM', fillValue = 0)
         self.mapMerger.AddRasterBands(self.tempMap, {1 : 1, 2 : 2, 3 : 3})
         del self.mapMerger
 
-        self.maskMerger = GDALRasterMerger(targetFile = self.maskfile, region = self.renderedRegion,
+        self.maskMerger = GDALRasterMerger(targetFile = self.layer.maskfile, region = self.renderedRegion,
                                            bandsNum = 1, gdalDriver = 'PNM', fillValue = 0)
         #{4 : 1} alpha channel (4) to first and only channel (1) in mask
         self.maskMerger.AddRasterBands(self.tempMap, {4 : 1}) 

Modified: grass/trunk/gui/wxpython/lmgr/layertree.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/layertree.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/lmgr/layertree.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -370,7 +370,7 @@
             # can cause another idle event
             self.rerender = False
             if self.mapdisplay.IsAutoRendered():
-                self.mapdisplay.GetMapWindow().UpdateMap(render=True)
+                self.mapdisplay.GetMapWindow().UpdateMap(render=False)
         
         event.Skip()
         
@@ -1103,7 +1103,7 @@
         if lchecked is not None:
             checked = lchecked
         else:
-            checked = True
+            checked = False
         
         self.forceCheck = True
         
@@ -1143,7 +1143,8 @@
             else:
                 ctrlId = None
             
-            # add a data object to hold the layer's command (does not apply to generic command layers)
+            # add a data object to hold the layer's command (does not
+            # apply to generic command layers)
             self.SetPyData(layer, ({'cmd'      : cmd,
                                     'type'     : ltype,
                                     'ctrl'     : ctrlId,
@@ -1153,6 +1154,7 @@
                                     'nviz'     : lnviz,
                                     'propwin'  : None}, 
                                    None))
+            
             # must be after SetPyData because it calls OnLayerChecked
             # which calls GetVisibleLayers which requires already set PyData
             self.CheckItem(layer, checked=checked)
@@ -1183,7 +1185,6 @@
                 self.PropertiesDialog(layer, show = True)
             else:
                 self.first = False
-        
         else: # group
             self.SetPyData(layer, ({'cmd'      : None,
                                     'type'     : ltype,
@@ -1209,7 +1210,6 @@
             if ltype == 'group':
                 self.OnRenameLayer(None)
 
-        
         return layer
 
     def PropertiesDialog(self, layer, show = True):
@@ -1223,37 +1223,32 @@
                 win.SetFocus()
             else:
                 win.Show()
-            
             return
         
         params = self.GetLayerParams(layer)
                 
         Debug.msg (3, "LayerTree.PropertiesDialog(): ltype=%s" % \
                    ltype)
-
+        
         cmd = None
         if self.GetLayerInfo(layer, key = 'cmd'):
-
             module = GUI(parent = self, show = show, centreOnParent = False)
             module.ParseCommand(self.GetLayerInfo(layer, key = 'cmd'),
-                                completed = (self.GetOptData,layer,params))
-            
+                                completed = (self.GetOptData, layer, params))
             self.SetLayerInfo(layer, key = 'cmd', value = module.GetCmd())
         elif self.GetLayerInfo(layer, key = 'type') != 'command':
             cmd = [ltype2command[ltype]]
-            if ltype == 'raster':
+            if ltype in ('raster', 'rgb'):
                 if UserSettings.Get(group = 'rasterLayer', key = 'opaque', subkey = 'enabled'):
                     cmd.append('-n')
-            elif ltype == 'rgb':
-                if UserSettings.Get(group = 'rasterLayer', key = 'opaque', subkey = 'enabled'):
-                    cmd.append('-n')
             elif ltype == 'vector':
                 cmd += GetDisplayVectSettings()
+        
+        if cmd:
+            module = GUI(parent = self, centreOnParent = False)
+            module.ParseCommand(cmd,
+                                completed = (self.GetOptData,layer,params))
             
-        if cmd:
-            GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
-                                                                    completed = (self.GetOptData,layer,params))
-        
     def OnActivateLayer(self, event):
         """Double click on the layer item.
         Launch property dialog, or expand/collapse group of items, etc.
@@ -1666,25 +1661,32 @@
                 GWarning(parent = self,
                          message = _("Map <%s> not found.") % mapName)
                 return
-        
+
+            if not mapLayer.IsActive():
+                self.forceCheck = True
+                self.CheckItem(layer, True)
+                mapLayer.SetActive(True)
+                
         # update layer data
         if params:
             self.SetPyData(layer, (self.GetLayerInfo(layer), params))
         self.SetLayerInfo(layer, key = 'propwin', value = propwin)
         
         # change parameters for item in layers list in render.Map
-        self.ChangeLayer(layer)
-        
+        if params:
+            self.ChangeLayer(layer)
+
         # set region if auto-zooming is enabled or layer tree contains
         # only one map layer
         if dcmd:
-            if not self.mapdisplay.IsPaneShown('3d') and (self.first or
-                    UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled')):
+            if not self.mapdisplay.IsPaneShown('3d') and \
+               (self.first or \
+                UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled')):
                 mapLayer = self.GetLayerInfo(layer, key = 'maplayer')
                 if mapLayer.GetType() in ('raster', 'vector'):
                     self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
                                                         render = False)
-            
+                    
             self.first = False # first layer has been already added to
                                # the layer tree
         
@@ -1716,7 +1718,7 @@
                                                          active = True))
                 if nlayers < 2:
                     mapWin.ResetView()
-
+        
     def GetVisibleLayers(self, skipDigitized=False):
         # make a list of visible layers
         layers = []
@@ -1775,18 +1777,19 @@
                 layerName, found = GetLayerNameFromCmd(cmdlist, fullyQualified = True)
                 if not found:
                     layerName = self.GetItemText(item)
+
+        maplayer = self.Map.ChangeLayer(layer = self.GetLayerInfo(item, key = 'maplayer'),
+                                        type = type, command = cmdlist, name = layerName,
+                                        active = chk, hidden = hidden, opacity = opac)
         
-        maplayer = self.Map.ChangeLayer(layer = self.GetLayerInfo(item, key = 'maplayer'), type = type,
-                                        command = cmdlist, name = layerName,
-                                        active = chk, hidden = hidden, opacity = opac, render = False)
-        
         self.SetLayerInfo(item, key = 'maplayer', value = maplayer)
         
         # if digitization tool enabled -> update list of available vector map layers
         if self.mapdisplay.GetToolbar('vdigit'):
             self.mapdisplay.GetToolbar('vdigit').UpdateListOfLayers(updateTool = True)
-
+        
         self.Map.SetLayers(self.GetVisibleLayers())
+        
         # redraw map if auto-rendering is enabled
         self.rerender = True
 

Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -247,7 +247,7 @@
         self.statusbarManager.Update()
         
         #
-        self.Map.updateProgress.connect(self.statusbarManager.SetProgress)
+        self.Map.GetRenderMgr().updateProgress.connect(self.statusbarManager.SetProgress)
         
     def GetMapWindow(self):
         return self.MapWindow

Modified: grass/trunk/gui/wxpython/mapdisp/main.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/main.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/mapdisp/main.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -5,7 +5,6 @@
 
 Classes:
  - mapdisp::DMonMap
- - mapdisp::DMonMapDirect
  - mapdisp::Layer
  - mapdisp::LayerList
  - mapdisp::DMonGrassInterface
@@ -38,7 +37,7 @@
 from core          import utils
 from core.giface   import StandaloneGrassInterface
 from core.gcmd     import RunCommand
-from core.render   import Map, MapLayer, Overlay
+from core.render   import Map, MapLayer, Overlay, RenderMapMgr
 from core.utils import _
 from mapdisp.frame import MapFrame
 from core.debug    import Debug
@@ -66,7 +65,7 @@
         :param mapfile: full path to the map file (defined by d.mon)
         """
         Map.__init__(self)
-
+        
         self._giface = giface
 
         # environment settings
@@ -93,7 +92,9 @@
         # signal sent when d.what.rast/vect appears in cmd file, attribute is cmd
         self.query = Signal('DMonMap.query')
 
-    def GetLayersFromCmdFile(self, mapfile=None):
+        self.renderMgr = RenderMapMgr(self)
+        
+    def GetLayersFromCmdFile(self):
         """Get list of map layers from cmdfile
         """
         if not self.cmdfile:
@@ -155,12 +156,12 @@
                     args['id'] = 1
                 else:
                     classLayer = MapLayer
-                    args['mapfile'] = mapfile
                     args['ltype'] = ltype
                 
                 mapLayer = classLayer(name = name, cmd = cmd, Map = None,
                                       hidden = True, **args)
-                
+                mapLayer.GetRenderMgr().updateProgress.connect(self.GetRenderMgr().ReportProgress)
+
                 exists = False
                 for i, layer in enumerate(existingLayers):
                     if layer.GetCmd(string=True) == mapLayer.GetCmd(string=True):
@@ -245,50 +246,6 @@
 
         #return layer
 
-class DMonMapDirect(DMonMap):
-    def __init__(self, giface, cmdfile=None, mapfile=None):
-        """Map composition (stack of map layers and overlays)
-
-        :param cmdline: full path to the cmd file (defined by d.mon)
-        :param mapfile: full path to the map file (defined by d.mon)
-        """
-        DMonMap.__init__(self, giface, cmdfile, mapfile)
-        
-        self.render_env['GRASS_RENDER_BACKGROUNDCOLOR'] = 'FFFFFF'
-        self.render_env['GRASS_RENDER_TRANSPARENT'] = 'FALSE'
-        self.render_env['GRASS_RENDER_FILE_READ'] = 'TRUE'
-        
-    def Render(self, *args, **kwargs):
-        """Render layer to image.
-
-        For input params and returned data see overridden method in Map class.
-        """
-        wx.BeginBusyCursor()
-        env = os.environ.copy()
-        env.update(self.render_env)
-        # use external gisrc if defined
-        if self.gisrc:
-            env['GISRC'] = self.gisrc
-        env['GRASS_REGION'] = self.SetRegion(kwargs.get('windres', False))
-        env['GRASS_RENDER_WIDTH'] = str(self.width)
-        env['GRASS_RENDER_HEIGHT'] = str(self.height)
-        driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
-        if driver == 'png':
-            env['GRASS_RENDER_IMMEDIATE'] = 'png'
-        else:
-            env['GRASS_RENDER_IMMEDIATE'] = 'cairo'
-
-        if os.path.exists(self.mapfile):
-            os.remove(self.mapfile)
-        
-        self.GetMapsMasksAndOpacities(kwargs['force'], kwargs.get('windres', False), env)
-        wx.EndBusyCursor()
-
-        return self.mapfile
-
-    def GetLayersFromCmdFile(self):
-        super(self.__class__, self).GetLayersFromCmdFile(self.mapfile)
-        
 class Layer(object):
     """@implements core::giface::Layer"""
     def __init__(self, maplayer):
@@ -444,11 +401,8 @@
             toolbars.append('map')
 
         if __name__ == "__main__":
-            dmonMap = DMonMapDirect
-            # if decorations:
-            #    dmonMap = DMonMap
             self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
-            self.Map = dmonMap(giface=self._giface, cmdfile=monFile['cmd'],
+            self.Map = DMonMap(giface=self._giface, cmdfile=monFile['cmd'],
                                mapfile = monFile['map'])
             
             self.timer = wx.PyTimer(self.watcher)

Modified: grass/trunk/gui/wxpython/mapwin/buffered.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/buffered.py	2015-05-09 20:47:32 UTC (rev 65204)
+++ grass/trunk/gui/wxpython/mapwin/buffered.py	2015-05-10 09:01:38 UTC (rev 65205)
@@ -149,7 +149,6 @@
         self.processMouse = True
 
         # render output objects
-        self.mapfile = None   # image file to be rendered
         self.img     = None   # wx.Image object (self.mapfile)
         # decoration overlays
         self.overlays = overlays
@@ -181,8 +180,9 @@
         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
 
         # rerender when Map reports change
-        self.Map.layerChanged.connect(self.OnUpdateMap)
-
+        ### self.Map.layerChanged.connect(self.OnUpdateMap)
+        self.Map.GetRenderMgr().renderDone.connect(self._updateMFinished)
+        
         # vars for handling mouse clicks
         self.dragid   = -1
         self.lastpos  = (0, 0)
@@ -704,7 +704,7 @@
         :return: wx.Image instance (map composition)
         """
         imgId = 99
-        if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
+        if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
                 os.path.getsize(self.Map.mapfile):
             img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
         else:
@@ -808,43 +808,29 @@
         """
         :func:`UpdateMap` for arguments description.
         """
+        Debug.msg (1, "BufferedWindow.UpdateMap(): started "
+                   "(render=%s, renderVector=%s)" % (render, renderVector))
+
         self.resize = False
 
         # was if self.Map.cmdfile and ...
         if self.IsAlwaysRenderEnabled() and self.img is None:
             render = True
-
-
-        #
-        # render background image if needed
-        #
-
-        # here was the change of the layertree rerender variable
-        # but it is fully the problem of layertree
-        # and so it is handled there
-        # remove this comment when it is old enough
-
+        
         try:
             if render:
                 # update display size
                 self.Map.ChangeMapSize(self.GetClientSize())
-                if self._properties.resolution:
-                    # use computation region resolution for rendering
-                    windres = True
-                else:
-                    windres = False
-
-                self.mapfile = self.Map.Render(force = True,
-                                               windres = windres)
-            else:
-                self.mapfile = self.Map.Render(force = False)
-
+                
+            self.Map.Render(force=render,
+                            windres=self._properties.resolution)
         except GException as e:
-            GError(message = e.value)
-            self.mapfile = None
+                GError(message=e.value)
 
+    def _updateMFinished(self, renderVector=True):
+        Debug.msg (1, "BufferedWindow.UpdateMap(): finished")
         self.img = self.GetImage() # id=99
-
+        
         #
         # clear pseudoDcs
         #
@@ -853,7 +839,7 @@
                     self.pdcTmp):
             pdc.Clear()
             pdc.RemoveAll()
-
+        
         #
         # draw background map image to PseudoDC
         #
@@ -862,9 +848,9 @@
         else:
             try:
                 id = self.imagedict[self.img]['id']
-            except:
+            except Exception as e:
+                Debug.mgs(1, "UpdateMap() failed: %s", e)
                 return False
-
             self.Draw(self.pdc, self.img, drawid = id)
 
         #
@@ -872,6 +858,7 @@
         #
         if renderVector and hasattr(self, "digit"):
             self._updateMap()
+        
         #
         # render overlays
         #
@@ -880,19 +867,20 @@
             if self.imagedict[img]['layer'].IsActive():
                 id = self.imagedict[img]['id']
                 self.Draw(self.pdc, img = img, drawid = id,
-                          pdctype = self.overlays[id].pdcType, coords = self.overlays[id].coords)
-
+                          pdctype = self.overlays[id].pdcType,
+                          coords = self.overlays[id].coords)
+        
         for id in self.textdict.keys():
             self.Draw(self.pdc, img = self.textdict[id], drawid = id,
                       pdctype = 'text', coords = [10, 10, 10, 10])
-
+        
         # optionally draw computational extent box
         self.DrawCompRegionExtent()
-
+        
         #
         # redraw pdcTmp if needed
         #
-
+        
         # draw registered graphics
         if  len(self.graphicsSetList) > 0:
             penOrig = self.pen
@@ -912,12 +900,9 @@
 
         if len(self.polycoords) > 0:
             self.DrawLines(self.pdcTmp)
-        
-        Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s" % \
-                   (render, renderVector))
-        
-        return True
 
+        return True
+    
     def DrawCompRegionExtent(self):
         """Draw computational region extent in the display
 



More information about the grass-commit mailing list