[GRASS-SVN] r58197 - in grass/trunk/gui: icons/grass wxpython/core wxpython/gui_core wxpython/mapswipe

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Nov 11 14:39:36 PST 2013


Author: annakrat
Date: 2013-11-11 14:39:35 -0800 (Mon, 11 Nov 2013)
New Revision: 58197

Added:
   grass/trunk/gui/icons/grass/layer-down.png
   grass/trunk/gui/icons/grass/layer-edit.png
   grass/trunk/gui/icons/grass/layer-up.png
   grass/trunk/gui/wxpython/core/layerlist.py
   grass/trunk/gui/wxpython/gui_core/simplelmgr.py
Modified:
   grass/trunk/gui/wxpython/gui_core/forms.py
   grass/trunk/gui/wxpython/mapswipe/dialogs.py
   grass/trunk/gui/wxpython/mapswipe/frame.py
   grass/trunk/gui/wxpython/mapswipe/toolbars.py
Log:
wxGUI/mapswipe: added possibility to display multiple raster/vector maps; reusable SimpleLayerManager created

Added: grass/trunk/gui/icons/grass/layer-down.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/layer-down.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/trunk/gui/icons/grass/layer-edit.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/layer-edit.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/trunk/gui/icons/grass/layer-up.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/layer-up.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/trunk/gui/wxpython/core/layerlist.py
===================================================================
--- grass/trunk/gui/wxpython/core/layerlist.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/core/layerlist.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -0,0 +1,371 @@
+# -*- coding: utf-8 -*-
+"""!
+ at package core.layerlist
+
+ at brief Non GUI classes for layer management (so far used for class simplelmgr only)
+
+Classes:
+ - layerlist::LayerList
+ - layerlist::Layer
+ - layerlist::LayerListToRendererConverter
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Petrasova (kratochanna gmail.com)
+"""
+
+from grass.script import core as gcore
+
+
+class LayerList(object):
+    """!Non GUI class managing list of layers.
+
+    It provides API for handling layers. In the future,
+    a non GUI class (e.g. named LayerTree) which includes this API,
+    should be used for Layer Manager.
+    """
+    def __init__(self):
+        self._list = []
+
+    def GetSelectedLayers(self, activeOnly=True):
+        """!Returns list of selected layers.
+
+        @param activeOnly return only active layers
+        """
+        layers = []
+        for layer in self._list:
+            if layer.IsSelected():
+                if activeOnly and layer.IsActive():
+                    layers.append(layer)
+                else:
+                    layers.append(layer)
+        return layers
+
+    def GetSelectedLayer(self, activeOnly=False):
+        """!Returns selected layer or None when there is no selected layer.
+
+        @param activeOnly return only active layers
+        """
+        layers = self.GetSelectedLayers(activeOnly)
+        if layers:
+            return layers[0]
+        return None
+
+    def GetActiveLayers(self):
+        """!Returns list of active layers."""
+        return [layer for layer in self._list if layer.IsActive()]
+
+    def GetLayersByTypes(self, mapTypes):
+        """!Returns layers by types.
+
+        @param mapTypes list of types
+        """
+        layers = []
+        for layer in self._list:
+            if layer.mapType in mapTypes:
+                layers.append(layer)
+        return layers
+
+    def AddNewLayer(self, name, mapType, cmd, active=True, hidden=False,
+                    opacity=100, label=None, pos=0):
+        """!Creates new layer and adds it to the list (insert to the first position).
+
+        @param ltype layer type (raster, vector, 3d-raster, ...)
+        @param cmd command (given as a list)
+        @param active if True layer is active
+        @param hidden if True layer is hidden
+        @param opacity layer opacity level (0 - 100)
+        @param name layer name (set automatically from cmd)
+        @param label layer label (set automatically from name)
+        @param pos add layer to position
+        """
+        layer = Layer()
+        layer.hidden = hidden
+        layer.mapType = mapType
+        layer.cmd = cmd
+        layer.active = active
+        layer.opacity = opacity
+        layer.name = name
+        if label:
+            layer.label = label
+
+        self._list.insert(pos, layer)
+        return layer
+
+    def AddLayer(self, layer):
+        """!Adds a layer to the layer list.
+        """
+        self._list.insert(0, layer)
+
+    def InsertLayer(self, index, layer):
+        """!Adds a layer to the layer list.
+        """
+        self._list.insert(index, layer)
+
+    def RemoveLayer(self, layer):
+        """!Removes layer."""
+        self._list.remove(layer)
+
+    def GetLayerByData(self, key, value):
+        """!Returns layer with specified.
+
+        @note Returns only one layer. This might change.
+
+        @warning Avoid using this method, it might be removed in the future.
+        """
+        raise NotImplementedError()
+
+    def GetLayerIndex(self, layer):
+        """!Get index of layer."""
+        return self._list.index(layer)
+
+    def MoveLayerUp(self, layer):
+        """!Moves layer up (1 step)."""
+        idx = self._list.index(layer)
+        if idx > 0:
+            lr = self._list.pop(idx)
+            self._list.insert(idx - 1, lr)
+
+    def MoveLayerDown(self, layer):
+        """!Moves layer down (1 step)."""
+        idx = self._list.index(layer)
+        if idx < len(self._list) - 1:
+            lr = self._list.pop(idx)
+            self._list.insert(idx + 1, lr)
+
+    def __iter__(self):
+        for layer in self._list:
+            yield layer
+
+    def __getitem__(self, index):
+        return self._list[index]
+
+    def __len__(self):
+        return len(self._list)
+
+    def __str__(self):
+        text = ''
+        for layer in self._list:
+            text += str(layer.name) + '\n'
+        return text
+
+
+class Layer(object):
+    """!Object representing layer.
+
+    Properties of the object are checked during setting.
+    Map types can be extended if needed.
+
+    >>> layer = Layer()
+    >>> layer.selected = True
+    >>> layer.IsSelected()
+    True
+    >>> layer.opacity = 0.1
+    Traceback (most recent call last):
+    ...
+    ValueError: Opacity must be an integer between 0 and 100, not 0.1.
+    >>> layer.name = 'blablabla'
+    Traceback (most recent call last):
+    ...
+    ValueError: To set layer name, the type of layer must be specified.
+    >>> layer.mapType = 'rast'
+    >>> layer.name = 'blablabla'
+    Traceback (most recent call last):
+    ...
+    ValueError: Map <blablabla> not found.
+    """
+    def __init__(self):
+        self._mapType = None
+        self._name = None
+        self._label = None
+        self._cmd = None
+        self._opacity = 1
+
+        self._selected = False
+        self._active = True
+        self._hidden = False
+        self._initialized = False
+
+        self._mapTypes = ['rast', 'vect', 'rast3d']
+        self._internalTypes = {'rast': 'cell',
+                               'vect': 'vect',
+                               'rast3d': 'grid3'}
+
+    def GetName(self):
+        return self._name
+
+    def SetName(self, name):
+        """!Sets name of the layer.
+
+        It checks the name of the layer by g.findfile
+        (raises ValueError if map does not exist).
+        Therefore map type has to be set first.
+        """
+        if not self.hidden:
+            fullName = name.split('@')
+            if len(fullName) == 1:
+                if self._mapType is None:
+                    raise ValueError("To set layer name, the type of layer must be specified.")
+
+                res = gcore.find_file(name=fullName,
+                                      element=self._internalTypes[self._mapType])
+                if not res['mapset']:
+                    raise ValueError("Map <{name}> not found.".format(name=name))
+                self._name = name + '@' + res['mapset']
+            else:
+                self._name = name
+        self.label = name
+
+    name = property(fget=GetName, fset=SetName)
+
+    def GetLabel(self):
+        return self._label
+
+    def SetLabel(self, label):
+        self._label = label
+
+    label = property(fget=GetLabel, fset=SetLabel)
+
+    def GetCmd(self):
+        return self._cmd
+
+    def SetCmd(self, cmd):
+        self._cmd = cmd
+
+    cmd = property(fget=GetCmd, fset=SetCmd)
+
+    def GetMapType(self):
+        return self._mapType
+
+    def SetMapType(self, mapType):
+        """!Sets map type of the layer.
+
+        @param mapType can be 'rast', 'vect', 'rast3'
+        """
+        if mapType not in self._mapTypes:
+            raise ValueError("Wrong map type used: {mtype}".format(mtype=mapType))
+
+        self._mapType = mapType
+
+    mapType = property(fget=GetMapType, fset=SetMapType)
+
+    def GetOpacity(self):
+        """!Returns opacity value.
+
+        @return opacity as integer between 0 and 100
+        """
+        return int(self._opacity * 100)
+
+    def SetOpacity(self, opacity):
+        """!Sets opacity of the layer.
+
+        @param opacity integer between 0 and 100
+        """
+        if not (0 <= opacity <= 100) or opacity != int(opacity):
+            raise ValueError("Opacity must be an integer between 0 and 100, not {op}.".format(op=opacity))
+        self._opacity = opacity / 100.
+
+    opacity = property(fget=GetOpacity, fset=SetOpacity)
+
+    def Select(self, select=True):
+        self._selected = select
+
+    def IsSelected(self):
+        return self._selected
+
+    selected = property(fget=IsSelected, fset=Select)
+
+    def IsActive(self):
+        return self._active
+
+    def Activate(self, active=True):
+        """!Sets if layer is active (checked)."""
+        self._active = active
+
+    active = property(fget=IsActive, fset=Activate)
+
+    def IsHidden(self):
+        return self._hidden
+
+    def Hide(self, hide=True):
+        """!Sets if layer is hidden."""
+        self._hidden = hide
+
+    hidden = property(fget=IsHidden, fset=Hide)
+
+
+class LayerListToRendererConverter:
+    """!Help class for converting LayerList layers into renderer list (Map)"""
+    def __init__(self, renderer):
+        """!
+
+        @param layerList instance of LayerList
+        @param renderer instance of Map
+        """
+        self._renderer = renderer
+
+    def _getRendererLayer(self, index):
+        """!Returns corresponding layer of renderer."""
+        rLayers = self._renderer.GetListOfLayers()
+        index = len(rLayers) - index - 1
+        return rLayers[index]
+
+    def ConvertAll(self, layerList):
+        """!Removes all layers in Map and adds new layers form layerList.
+        It's not meant for continuous update because everything is rerendered.
+        """
+        self._renderer.DeleteAllLayers()
+        for layer in reversed(layerList):
+            self.AddLayer(index=-1, layer=layer)
+
+    def ChangeLayerOpacity(self, index, layer):
+        """!Changes layer opacity in renderer."""
+        rLayer = self._getRendererLayer(index)
+        self._renderer.ChangeLayer(rLayer, opacity=layer.opacity / 100.)
+
+    def ChangeLayerCmd(self, index, layer):
+        """!Changes layer cmd in renderer."""
+        rLayer = self._getRendererLayer(index)
+        self._renderer.ChangeLayer(rLayer, command=layer.cmd)
+
+    def ChangeLayerActive(self, index, layer):
+        """!Changes layer active state in renderer."""
+        rLayer = self._getRendererLayer(index)
+        self._renderer.ChangeLayer(rLayer, active=layer.active)
+
+    def MoveLayerUp(self, index):
+        """!Moves layer up in renderer."""
+        rLayers = self._renderer.GetListOfLayers()
+        index = len(rLayers) - index - 1
+        rLayer = rLayers.pop(index)
+        rLayers.insert(index + 1, rLayer)
+        self._renderer.SetLayers(rLayers)
+
+    def MoveLayerDown(self, index):
+        """!Moves layer down in renderer."""
+        rLayers = self._renderer.GetListOfLayers()
+        index = len(rLayers) - index - 1
+        rLayer = rLayers.pop(index)
+        rLayers.insert(index - 1, rLayer)
+        self._renderer.SetLayers(rLayers)
+
+    def AddLayer(self, index, layer):
+        """!Adds layer to renderer (prepends)."""
+        mapType = None
+        if layer.mapType == 'rast':
+            mapType = 'raster'
+        elif layer.mapType == 'vect':
+            mapType = 'vector'
+        elif layer.mapType == 'rast3d':
+            mapType = '3d-raster'
+        self._renderer.AddLayer(ltype=mapType, command=layer.cmd,
+                                name=layer.name, active=layer.active,
+                                hidden=False, opacity=layer.opacity / 100.,
+                                render=True, pos=-1)
+
+    def RemoveLayer(self, index):
+        """!Removes layer from renderer."""
+        self._renderer.DeleteLayer(self._getRendererLayer(index))


Property changes on: grass/trunk/gui/wxpython/core/layerlist.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/trunk/gui/wxpython/gui_core/forms.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/forms.py	2013-11-11 22:23:51 UTC (rev 58196)
+++ grass/trunk/gui/wxpython/gui_core/forms.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -751,7 +751,7 @@
         self.dialogClosing.emit()
         if self.get_dcmd and \
                 self.parent and \
-                self.parent.GetName() in ('LayerTree',
+                self.parent.GetName() in ('LayerTree', 'SimpleLayerManager',
                                           'MapWindow'):
             # display decorations and 
             # pressing OK or cancel after setting layer properties

Added: grass/trunk/gui/wxpython/gui_core/simplelmgr.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/simplelmgr.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/gui_core/simplelmgr.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -0,0 +1,422 @@
+# -*- coding: utf-8 -*-
+"""!
+ at package gui_core.simplelmgr
+
+ at brief GUI class for simple layer management.
+
+Classes:
+ - simplelmgr::SimpleLayerManager
+ - simplelmgr::SimpleLmgrToolbar
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Petrasova (kratochanna gmail.com)
+"""
+import os
+import sys
+
+# adding a path to wxGUI modules
+if __name__ == '__main__':
+    WXGUIBASE = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
+    if WXGUIBASE not in sys.path:
+        sys.path.append(WXGUIBASE)
+import wx
+import wx.aui
+
+from grass.pydispatch.signal import Signal
+
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon import MetaIcon
+from gui_core.forms import GUI
+from gui_core.dialogs import SetOpacityDialog
+from core.utils import GetLayerNameFromCmd
+from core.gcmd import GError
+from core.layerlist import LayerList
+
+SIMPLE_LMGR_RASTER = 1
+SIMPLE_LMGR_VECTOR = 2
+SIMPLE_LMGR_RASTER3D = 4
+SIMPLE_LMGR_TB_TOP = 8
+SIMPLE_LMGR_TB_BOTTOM = 16
+SIMPLE_LMGR_TB_LEFT = 32
+SIMPLE_LMGR_TB_RIGHT = 64
+
+
+class SimpleLayerManager(wx.Panel):
+    """!Simple layer manager class provides similar functionality to
+    Layertree, but it's just list, not tree."""
+    def __init__(self, parent, layerList,
+                 lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT):
+        wx.Panel.__init__(self, parent=parent, name='SimpleLayerManager')
+
+        self._style = lmgrStyle
+        self._layerList = layerList
+        self._checkList = wx.CheckListBox(self, style=wx.LB_EXTENDED)
+        # dialog windows held separately
+        self._dialogs = {}
+        self._toolbar = SimpleLmgrToolbar(self, lmgrStyle=self._style)
+
+        self._auimgr = wx.aui.AuiManager(self)
+
+        # needed in order not to change selection when moving layers
+        self._blockSelectionChanged = False
+
+        self._checkList.Bind(wx.EVT_LISTBOX, lambda evt: self._selectionChanged())
+        self._checkList.Bind(wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties)
+        self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked)
+
+        # signal emitted when somethin in layer list changes
+        self.opacityChanged = Signal('SimpleLayerManager.opacityChanged')
+        self.cmdChanged = Signal('SimpleLayerManager.cmdChanged')
+        self.layerAdded = Signal('SimpleLayerManager.layerAdded')
+        self.layerRemoved = Signal('SimpleLayerManager.layerRemoved')
+        self.layerActivated = Signal('SimpleLayerManager.layerActivated')
+        self.layerMovedUp = Signal('SimpleLayerManager.layerMovedUp')
+        self.layerMovedDown = Signal('SimpleLayerManager.layerMovedDown')
+        # emitted by any change (e.g. for rerendering)
+        self.anyChange = Signal('SimpleLayerManager.layerChange')
+
+        self._layout()
+        self.SetMinSize((200, -1))
+
+    def _layout(self):
+        self._auimgr.AddPane(self._checkList,
+                             wx.aui.AuiPaneInfo().
+                             Name("checklist").
+                             CenterPane().
+                             CloseButton(False).
+                             BestSize((self._checkList.GetBestSize())))
+        paneInfo = wx.aui.AuiPaneInfo(). \
+                   Name("toolbar").Caption(_("Toolbar")).ToolbarPane(). \
+                   CloseButton(False).Layer(1).Gripper(False). \
+                   BestSize((self._toolbar.GetBestSize()))
+        if self._style & SIMPLE_LMGR_TB_LEFT:
+            paneInfo.Left()
+        elif self._style & SIMPLE_LMGR_TB_RIGHT:
+            paneInfo.Right()
+        elif self._style & SIMPLE_LMGR_TB_TOP:
+            paneInfo.Top()
+        else:
+            paneInfo.Bottom()
+
+        self._auimgr.AddPane(self._toolbar, paneInfo)
+        self._auimgr.Update()
+
+    def _selectionChanged(self):
+        """!Selection was changed externally,
+        updates selection info in layers."""
+        if self._blockSelectionChanged:
+            return
+        selected = self._checkList.GetSelections()
+        for i, layer in enumerate(self._layerList):
+            layer.Select(i in selected)
+
+    def OnLayerChecked(self, event):
+        """!Layer was (un)checked, update layer's info."""
+        checkedIdxs = self._checkList.GetChecked()
+        for i, layer in enumerate(self._layerList):
+            if i in checkedIdxs and not layer.IsActive():
+                layer.Activate()
+                self.layerActivated.emit(index=i, layer=layer)
+            elif i not in checkedIdxs and layer.IsActive():
+                layer.Activate(False)
+                self.layerActivated.emit(index=i, layer=layer)
+        self.anyChange.emit()
+        event.Skip()
+
+    def OnAddRaster(self, event):
+        """!Opens d.rast dialog and adds layer.
+        Dummy layer is added first."""
+        cmd = ['d.rast']
+        layer = self.AddRaster(name='', cmd=cmd, hidden=True, dialog=None)
+        GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
+                                                   completed=(self.GetOptData, layer, ''))
+        event.Skip()
+
+    def OnAddVector(self, event):
+        """!Opens d.vect dialog and adds layer.
+        Dummy layer is added first."""
+        cmd = ['d.vect']
+        layer = self.AddVector(name='', cmd=cmd, hidden=True, dialog=None)
+        GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
+                                                   completed=(self.GetOptData, layer, ''))
+        event.Skip()
+
+    def OnAddRast3d(self, event):
+        """!Opens d.rast3d dialog and adds layer.
+        Dummy layer is added first."""
+        cmd = ['d.rast3d']
+        layer = self.AddRast3d(name='', cmd=cmd, hidden=True, dialog=None)
+        GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
+                                                   completed=(self.GetOptData, layer, ''))
+        event.Skip()
+
+    def OnRemove(self, event):
+        """!Removes selected layers from list."""
+        layers = self._layerList.GetSelectedLayers(activeOnly=False)
+        for layer in layers:
+            self.layerRemoved.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
+            self._layerList.RemoveLayer(layer)
+            if self._dialogs[layer]:
+                self._dialogs[layer].Destroy()
+        self._update()
+        self.anyChange.emit()
+        event.Skip()
+
+    def OnLayerUp(self, event):
+        """!Moves selected layers one step up.
+
+        Note: not completely correct for multiple layers."""
+        layers = self._layerList.GetSelectedLayers()
+        self._blockSelectionChanged = True
+        for layer in layers:
+            idx = self._layerList.GetLayerIndex(layer)
+            if idx > 0:
+                self.layerMovedUp.emit(index=idx, layer=layer)
+                self._layerList.MoveLayerUp(layer)
+        self._update()
+        self._blockSelectionChanged = False
+        self.anyChange.emit()
+        event.Skip()
+
+    def OnLayerDown(self, event):
+        """!Moves selected layers one step down.
+
+        Note: not completely correct for multiple layers."""
+        layers = self._layerList.GetSelectedLayers()
+        self._blockSelectionChanged = True
+        for layer in layers:
+            idx = self._layerList.GetLayerIndex(layer)
+            if idx < len(self._layerList) - 1:
+                self.layerMovedDown.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
+                self._layerList.MoveLayerDown(layer)
+        self._update()
+        self._blockSelectionChanged = False
+        self.anyChange.emit()
+        event.Skip()
+
+    def OnLayerChangeProperties(self, event):
+        """Opens module dialog to edit layer command."""
+        layers = self._layerList.GetSelectedLayers()
+        if not layers or len(layers) > 1:
+            return
+        self._layerChangeProperties(layers[0])
+        event.Skip()
+
+    def _layerChangeProperties(self, layer):
+        """!Opens new module dialog or recycles it."""
+        dlg = self._dialogs[layer]
+        if dlg is not None:
+            if dlg.IsShown():
+                dlg.Raise()
+                dlg.SetFocus()
+            else:
+                dlg.Show()
+        else:
+            GUI(parent=self, giface=None,
+                modal=False).ParseCommand(cmd=layer.cmd,
+                                          completed=(self.GetOptData, layer, ''))
+
+    def OnLayerChangeOpacity(self, event):
+        """!Opacity of a layer is changing."""
+        layers = self._layerList.GetSelectedLayers()
+        if not layers or len(layers) > 1:
+            return
+        layer = layers[0]
+        dlg = SetOpacityDialog(self, opacity=layer.opacity,
+                               title=_("Set opacity of <%s>") % layer.name)
+        dlg.applyOpacity.connect(lambda value:
+                                 self._setLayerOpacity(layer, value))
+        dlg.CentreOnParent()
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self._setLayerOpacity(layer, dlg.GetOpacity())
+        dlg.Destroy()
+        event.Skip()
+
+    def _setLayerOpacity(self, layer, value):
+        """!Sets layer's opacity.'"""
+        # * 100 to be compatible, should be changes everywhere to 0-100 range
+        layer.opacity = int(value * 100)
+        self._update()
+        self.opacityChanged.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
+        self.anyChange.emit()
+
+    def _update(self):
+        """!Updates checklistbox according to layerList structure."""
+        items = []
+        active = []
+        selected = []
+        for layer in self._layerList:
+            if layer.opacity < 100:
+                items.append("{name} (opacity {opacity}%)".format(name=layer.name,
+                                                                  opacity=layer.opacity))
+            else:
+                items.append(layer.name)
+            active.append(layer.IsActive())
+            selected.append(layer.IsSelected())
+
+        self._checkList.SetItems(items)
+        for i, check in enumerate(active):
+            self._checkList.Check(i, check)
+
+        for i, layer in enumerate(self._layerList):
+            if selected[i]:
+                self._checkList.Select(i)
+            else:
+                self._checkList.Deselect(i)
+
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Handler for module dialogs."""
+        if dcmd:
+            layer.cmd = dcmd
+            self._dialogs[layer] = propwin
+            layer.selected = True
+            mapName, found = GetLayerNameFromCmd(dcmd)
+            if found:
+                try:
+                    if layer.hidden:
+                        layer.hidden = False
+                        signal = self.layerAdded
+                    else:
+                        signal = self.cmdChanged
+
+                    layer.name = mapName
+                    signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
+                except ValueError, e:
+                    self._layerList.RemoveLayer(layer)
+                    GError(parent=self,
+                           message=str(e),
+                           showTraceback=False)
+
+            self._update()
+            self.anyChange.emit()
+
+    def AddRaster(self, name, cmd, hidden, dialog):
+        """!Ads new raster layer."""
+        layer = self._layerList.AddNewLayer(name=name, mapType='rast',
+                                            active=True,
+                                            cmd=cmd, hidden=hidden)
+        self._dialogs[layer] = dialog
+        return layer
+
+    def AddRast3d(self, name, cmd, hidden, dialog):
+        """!Ads new raster3d layer."""
+        layer = self._layerList.AddNewLayer(name=name, mapType='rast3d',
+                                            active=True,
+                                            cmd=cmd, hidden=hidden)
+        self._dialogs[layer] = dialog
+        return layer
+
+    def AddVector(self, name, cmd, hidden, dialog):
+        """!Ads new vector layer."""
+        layer = self._layerList.AddNewLayer(name=name, mapType='vect',
+                                            active=True,
+                                            cmd=cmd, hidden=hidden)
+        self._dialogs[layer] = dialog
+        return layer
+
+    def GetLayerInfo(self, layer, key):
+        """!Just for compatibility, should be removed in the future"""
+        value = getattr(layer, key)
+        # hack to return empty list, required in OnCancel in forms
+        # not sure why it should be empty
+        if key == 'cmd' and len(value) == 1:
+            return []
+        return value
+
+    def Delete(self, layer):
+        """!Just for compatibility, should be removed in the future"""
+        self._layerList.RemoveLayer(layer)
+        if self._dialogs[layer]:
+            self._dialogs[layer].Destroy()
+
+
+class SimpleLmgrToolbar(BaseToolbar):
+    """!Toolbar of simple layer manager.
+
+    Style of the toolbar can be changed (horizontal,
+    vertical, which map types to include).
+    """
+    def __init__(self, parent, lmgrStyle):
+        """!Toolbar constructor
+        """
+        self._style = lmgrStyle
+        if lmgrStyle & (SIMPLE_LMGR_TB_LEFT | SIMPLE_LMGR_TB_RIGHT):
+            direction = wx.TB_VERTICAL
+        else:
+            direction = wx.TB_HORIZONTAL
+        BaseToolbar.__init__(self, parent, style=wx.NO_BORDER | direction)
+
+        self.InitToolbar(self._toolbarData())
+
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data"""
+        data = [('edit', icons['edit'],
+                 self.parent.OnLayerChangeProperties),
+                 ('remove', icons['remove'],
+                 self.parent.OnRemove),
+                 (None, ),
+                 ('up', icons['up'],
+                 self.parent.OnLayerUp),
+                 ('down', icons['down'],
+                 self.parent.OnLayerDown),
+                 (None, ),
+                 ('opacity', icons['opacity'],
+                 self.parent.OnLayerChangeOpacity),
+                 ]
+        if self._style & SIMPLE_LMGR_RASTER3D:
+            data.insert(0, ('addRaster3d', icons['addRast3d'],
+                            self.parent.OnAddRast3d))
+        if self._style & SIMPLE_LMGR_VECTOR:
+            data.insert(0, ('addVector', BaseIcons['addVect'],
+                            self.parent.OnAddVector))
+        if self._style & SIMPLE_LMGR_RASTER:
+            data.insert(0, ('addRaster', BaseIcons['addRast'],
+                            self.parent.OnAddRaster))
+
+        return self._getToolbarData(data)
+
+
+icons = {
+    'remove': MetaIcon(img='layer-remove',
+                       label=_("Remove"),
+                       desc=_("Remove selected map(s) from list")),
+    'up': MetaIcon(img='layer-up',
+                   label=_("Layer up"),
+                   desc=_("Move selected layer(s) up")),
+    'down': MetaIcon(img='layer-down',
+                     label=_("Layer down"),
+                     desc=_("Move selected layer(s) down")),
+    'edit': MetaIcon(img='layer-edit',
+                     label=_("Edit layer properties"),
+                     desc=_("Edit layer properties")),
+    'opacity': MetaIcon(img='layer-opacity',
+                        label=_("Change opacity"),
+                        desc=_("Change layer opacity")),
+    'addRast3d': MetaIcon(img='layer-raster3d-add',
+                          label=_("Add 3D raster map layer"),
+                          desc=_("Add 3D raster map layer")),
+    }
+
+
+class TestFrame(wx.Frame):
+    def __init__(self, parent):
+        wx.Frame.__init__(self, parent=parent)
+        SimpleLayerManager(parent=self, layerList=LayerList())
+
+
+def test():
+    app = wx.App()
+    frame = TestFrame(None)
+    frame.Show()
+    app.MainLoop()
+
+if __name__ == '__main__':
+    test()


Property changes on: grass/trunk/gui/wxpython/gui_core/simplelmgr.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/trunk/gui/wxpython/mapswipe/dialogs.py
===================================================================
--- grass/trunk/gui/wxpython/mapswipe/dialogs.py	2013-11-11 22:23:51 UTC (rev 58196)
+++ grass/trunk/gui/wxpython/mapswipe/dialogs.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -6,70 +6,204 @@
 Classes:
  - dialogs::SwipeMapDialog
 
-(C) 2012 by the GRASS Development Team
+(C) 2013 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
 
- at author Anna Kratochvilova <kratochanna gmail.com>
+ at author Anna Petrasova <kratochanna gmail.com>
 """
 
+import copy
 import wx
 
 from core               import globalvar
 from core.utils import _
-from gui_core.dialogs   import SimpleDialog
 from gui_core           import gselect
 from gui_core.widgets   import SimpleValidator
 from core.gcmd          import GMessage
+from core.layerlist import LayerList
+from gui_core.simplelmgr import SimpleLayerManager, SIMPLE_LMGR_RASTER, \
+   SIMPLE_LMGR_VECTOR, SIMPLE_LMGR_TB_LEFT, SIMPLE_LMGR_TB_RIGHT
 
-class SwipeMapDialog(SimpleDialog):
-    """!Dialog used to select raster maps"""
-    def __init__(self, parent, title = _("Select raster maps"), first = None, second = None):
-        SimpleDialog.__init__(self, parent, title)
+from grass.pydispatch.signal import Signal
 
-        self.element1 = gselect.Select(parent = self.panel, type = 'raster',
-                                      size = globalvar.DIALOG_GSELECT_SIZE,
-                                      validator = SimpleValidator(callback = self.ValidatorCallback))
-        
-        self.element2 = gselect.Select(parent = self.panel, type = 'raster',
-                                       size = globalvar.DIALOG_GSELECT_SIZE,
-                                       validator = SimpleValidator(callback = self.ValidatorCallback))
-        
-        self.element1.SetFocus()
+
+class SwipeMapDialog(wx.Dialog):
+    """!Dialog used to select maps.
+
+    There are two modes - simple (only two raster maps),
+    or two layer lists.
+    """
+    def __init__(self, parent, title=_("Select raster maps"),
+                 first=None, second=None,
+                 firstLayerList=None, secondLayerList=None):
+
+        wx.Dialog.__init__(self, parent=parent, title=title,
+                           style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE)
+
+        if firstLayerList is None:
+            self._firstLayerList = LayerList()
+        else:
+            self._firstLayerList = copy.deepcopy(firstLayerList)
+        if secondLayerList is None:
+            self._secondLayerList = LayerList()
+        else:
+            self._secondLayerList = copy.deepcopy(secondLayerList)
+
+        self._firstPanel = self._createSimplePanel()
+        self._secondPanel = self._createAdvancedPanel()
+
+        self.btnSwitch = wx.Button(self)
+        self.btnCancel = wx.Button(self, id=wx.ID_CANCEL)
+        self.btnApply = wx.Button(self, id=wx.ID_APPLY)
+        self.btnOK = wx.Button(self, id=wx.ID_OK)
+        self.btnOK.SetDefault()
+
+        self.btnSwitch.Bind(wx.EVT_BUTTON, self.OnSwitchMode)
+        self.btnApply.Bind(wx.EVT_BUTTON, lambda evt: self._apply())
+        self.btnOK.Bind(wx.EVT_BUTTON, lambda evt: self._ok())
+        self.btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
+        self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide())
+
+        self.applyChanges = Signal('SwipeMapDialog.applyChanges')
+
         if first:
-            self.element1.SetValue(first)
+            self._firstRaster.SetValue(first)
         if second:
-            self.element2.SetValue(second)
-            
+            self._secondRaster.SetValue(second)
+
         self._layout()
-        self.SetMinSize(self.GetSize())
 
     def _layout(self):
         """!Do layout"""
-        self.dataSizer.Add(wx.StaticText(self.panel, id = wx.ID_ANY,
-                                         label = _("Name of top/left raster map:")),
-                           proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
-        self.dataSizer.Add(self.element1, proportion = 0,
-                           flag = wx.EXPAND | wx.ALL, border = 1)
- 
-        self.dataSizer.Add(wx.StaticText(parent = self.panel, id = wx.ID_ANY,
-                                         label = _("Name of bottom/right raster map:")), proportion = 0,
-                           flag = wx.EXPAND | wx.ALL, border = 1)
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        self._switchSizer = wx.BoxSizer()
+        self._switchSizer.Add(self._firstPanel, proportion=1,
+                              flag=wx.EXPAND | wx.ALL, border=5)
+        self._switchSizer.Add(self._secondPanel, proportion=1,
+                              flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(self._switchSizer, proportion=1,
+                      flag=wx.EXPAND | wx.ALL)
 
-        self.dataSizer.Add(self.element2, proportion = 0,
-                           flag = wx.EXPAND | wx.ALL, border = 1)
-       
-        self.panel.SetSizer(self.sizer)
-        self.sizer.Fit(self)
+        self.btnSizer = wx.StdDialogButtonSizer()
+        self.btnSizer.AddButton(self.btnCancel)
+        self.btnSizer.AddButton(self.btnOK)
+        self.btnSizer.AddButton(self.btnApply)
+        self.btnSizer.Realize()
 
+        mainSizer.Add(item=self.btnSwitch, proportion=0,
+                      flag=wx.ALL | wx.ALIGN_LEFT, border=5)
+        mainSizer.Add(item=self.btnSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        self.mainSizer = mainSizer
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        self._switchMode(simple=True)
+
+    def _createSimplePanel(self):
+        panel = wx.Panel(self)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        self._firstRaster = gselect.Select(parent=panel, type='raster',
+                                           size=globalvar.DIALOG_GSELECT_SIZE,
+                                           validator=SimpleValidator(callback=self.ValidatorCallback))
+
+        self._secondRaster = gselect.Select(parent=panel, type='raster',
+                                            size=globalvar.DIALOG_GSELECT_SIZE,
+                                            validator=SimpleValidator(callback=self.ValidatorCallback))
+        sizer.Add(wx.StaticText(panel, label=_("Name of top/left raster map:")),
+                  proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(self._firstRaster, proportion=0,
+                  flag=wx.EXPAND | wx.ALL, border=1)
+        sizer.Add(wx.StaticText(panel, label=_("Name of bottom/right raster map:")),
+                  proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
+        sizer.Add(self._secondRaster, proportion=0,
+                  flag=wx.EXPAND | wx.ALL, border=1)
+
+        self._firstRaster.SetFocus()
+
+        panel.SetSizer(sizer)
+        sizer.Fit(panel)
+
+        return panel
+
+    def _createAdvancedPanel(self):
+        panel = wx.Panel(self)
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        self._firstLmgr = SimpleLayerManager(parent=panel, layerList=self._firstLayerList,
+                                             lmgrStyle=SIMPLE_LMGR_RASTER |
+                                             SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT)
+        self._secondLmgr = SimpleLayerManager(parent=panel, layerList=self._secondLayerList,
+                                              lmgrStyle=SIMPLE_LMGR_RASTER |
+                                              SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_RIGHT)
+        sizer.Add(self._firstLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+        sizer.Add(self._secondLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+        panel.SetSizer(sizer)
+        sizer.Fit(panel)
+
+        return panel
+
+    def _switchMode(self, simple):
+        if simple:
+            self._switchSizer.Show(self._firstPanel, show=True, recursive=True)
+            self._switchSizer.Show(self._secondPanel, show=False, recursive=True)
+            self.btnSwitch.SetLabel(_("Switch to advanced mode"))
+            self.btnCancel.SetLabel(_("Cancel"))
+        else:
+            self._switchSizer.Show(self._firstPanel, show=False, recursive=True)
+            self._switchSizer.Show(self._secondPanel, show=True, recursive=True)
+            self.btnSwitch.SetLabel(_("Switch to simple mode"))
+            self.btnCancel.SetLabel(_("Close"))
+
+        self.Freeze()  # doesn't do anything (at least on Ubuntu)
+        self.btnSizer.Show(self.btnApply, simple)
+        self.btnSizer.Show(self.btnOK, simple)
+        self.btnSizer.Layout()
+        self._switchSizer.Layout()
+        self.Fit()
+        self.Thaw()
+
+        self.applyChanges.emit()
+
+    def OnSwitchMode(self, event):
+        if self._switchSizer.IsShown(self._secondPanel):
+            self._switchMode(simple=True)
+        else:
+            self._switchMode(simple=False)
+
     def ValidatorCallback(self, win):
-        if win == self.element1.GetTextCtrl():
-            GMessage(parent = self, message = _("Name of the first map is missing."))
+        if self._switchSizer.IsShown(self._secondPanel):
+            return
+
+        if win == self._firstRaster.GetTextCtrl():
+            GMessage(parent=self, message=_("Name of the first map is missing."))
         else:
-            GMessage(parent = self, message = _("Name of the second map is missing."))
+            GMessage(parent=self, message=_("Name of the second map is missing."))
 
+    def _ok(self):
+        self._apply()
+        self.Close()
+
+    def _apply(self):
+        # TODO check if not empty
+        self.applyChanges.emit()
+
     def GetValues(self):
         """!Get raster maps"""
-        return (self.element1.GetValue(), self.element2.GetValue())
+        if self.IsSimpleMode():
+            return (self._firstRaster.GetValue(), self._secondRaster.GetValue())
+        else:
+            return (self._firstLayerList, self._secondLayerList)
 
+    def IsSimpleMode(self):
+        if self._switchSizer.IsShown(self._firstPanel):
+            return True
+        return False
+
+    def GetFirstSimpleLmgr(self):
+        return self._firstLmgr
+
+    def GetSecondSimpleLmgr(self):
+        return self._secondLmgr

Modified: grass/trunk/gui/wxpython/mapswipe/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapswipe/frame.py	2013-11-11 22:23:51 UTC (rev 58196)
+++ grass/trunk/gui/wxpython/mapswipe/frame.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -29,6 +29,7 @@
 from core.debug         import Debug
 from core.gcmd          import GError, GMessage
 from core.utils import _
+from core.layerlist import LayerListToRendererConverter
 
 from mapswipe.toolbars  import SwipeMapToolbar, SwipeMainToolbar, SwipeMiscToolbar
 from mapswipe.mapwindow import SwipeBufferedWindow
@@ -97,6 +98,8 @@
 
         self.rasters = {'first': None, 'second': None}
 
+        self._inputDialog = None
+
         # default action in map toolbar
         self.GetMapToolbar().SelectDefault()
 
@@ -143,7 +146,7 @@
         self.splitter.SplitVertically(self.firstMapWindow, self.secondMapWindow, 0)
         self.splitter.Init()
         if not (self.rasters['first'] and self.rasters['second']):
-            self.OnSelectRasters(event = None)
+            self.OnSelectLayers(event=None)
         
     def InitStatusbar(self):
         """!Init statusbar (default items)."""
@@ -336,28 +339,66 @@
         self.secondMap.region = self.firstMap.region
         self.Render(self.GetSecondWindow())
 
-    def OnSelectRasters(self, event):
-        """!Choose raster maps and rerender."""
-        dlg = SwipeMapDialog(self, first = self.rasters['first'], second = self.rasters['second'])
-        dlg.CentreOnParent()
-        if dlg.ShowModal() == wx.ID_OK:
-            maps = dlg.GetValues()
-            res1 = self.SetFirstRaster(name = maps[0])
-            res2 = self.SetSecondRaster(name = maps[1])
+    def OnSelectLayers(self, event):
+        if self._inputDialog is None:
+            dlg = SwipeMapDialog(self, first=self.rasters['first'],
+                                 second=self.rasters['second'],
+                                 firstLayerList=None, secondLayerList=None)
+            dlg.applyChanges.connect(self.OnApplyInputChanges)
+            # connect to convertor object to convert to Map
+            # store reference to convertor is needed otherwise it would be discarded
+            self._firstConverter = self._connectSimpleLmgr(dlg.GetFirstSimpleLmgr(),
+                                                           self.GetFirstMap())
+            self._secondConverter = self._connectSimpleLmgr(dlg.GetSecondSimpleLmgr(),
+                                                            self.GetSecondMap())
+            self._inputDialog = dlg
+            dlg.CentreOnParent()
+            dlg.Show()
+        else:
+            if self._inputDialog.IsShown():
+                self._inputDialog.Raise()
+                self._inputDialog.SetFocus()
+            else:
+                self._inputDialog.Show()
 
-            if not (res1 and res2):
+    def _connectSimpleLmgr(self, lmgr, renderer):
+        converter = LayerListToRendererConverter(renderer)
+        lmgr.opacityChanged.connect(converter.ChangeLayerOpacity)
+        lmgr.cmdChanged.connect(converter.ChangeLayerCmd)
+        lmgr.layerAdded.connect(converter.AddLayer)
+        lmgr.layerRemoved.connect(converter.RemoveLayer)
+        lmgr.layerActivated.connect(converter.ChangeLayerActive)
+        lmgr.layerMovedUp.connect(converter.MoveLayerUp)
+        lmgr.layerMovedDown.connect(converter.MoveLayerDown)
+        lmgr.anyChange.connect(self._simpleLmgrChanged)
+        return converter
+
+    def _simpleLmgrChanged(self):
+        if self.IsAutoRendered():
+            self.OnRender(event=None)
+
+    def OnApplyInputChanges(self):
+        first, second = self._inputDialog.GetValues()
+        if self._inputDialog.IsSimpleMode():
+            self.rasters['first'], self.rasters['second'] = first, second
+            res1 = self.SetFirstRaster(name=self.rasters['first'])
+            res2 = self.SetSecondRaster(name=self.rasters['second'])
+            if not (res1 and res2) and first and second:
                 message = ''
                 if not res1:
-                    message += _("Map <%s> not found. ") % maps[0]
+                    message += _("Map <%s> not found. ") % self.rasters['first']
                 if not res2:
-                    message += _("Map <%s> not found.") % maps[1]
-                GError(parent = self, message = message)
-                dlg.Destroy()
-            self.SetRasterNames()
+                    message += _("Map <%s> not found.") % self.rasters['second']
+                    GError(parent = self, message = message)
+                    return
             self.ZoomToMap()
+        else:
+            LayerListToRendererConverter(self.GetFirstMap()).ConvertAll(first)
+            LayerListToRendererConverter(self.GetSecondMap()).ConvertAll(second)
 
-        dlg.Destroy()
-        self.OnRender(event = None)
+        self.SetRasterNames()
+        if self.IsAutoRendered():
+            self.OnRender(event=None)
 
     def SetFirstRaster(self, name):
         """!Set raster map to first Map"""
@@ -551,10 +592,14 @@
         self.SendSizeEvent()
 
     def SetRasterNames(self):
-        if self.rasters['first']:
-            self.GetFirstWindow().SetRasterNameText(self.rasters['first'], 101)
-        if self.rasters['second']:
-            self.GetSecondWindow().SetRasterNameText(self.rasters['second'], 102)
+        if self._inputDialog.IsSimpleMode():
+            if self.rasters['first']:
+                self.GetFirstWindow().SetRasterNameText(self.rasters['first'], 101)
+            if self.rasters['second']:
+                self.GetSecondWindow().SetRasterNameText(self.rasters['second'], 102)
+        else:
+            self.GetFirstWindow().SetRasterNameText('', 101)
+            self.GetSecondWindow().SetRasterNameText('', 102)
 
     def GetMapToolbar(self):
         """!Returns toolbar with zooming tools"""

Modified: grass/trunk/gui/wxpython/mapswipe/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/mapswipe/toolbars.py	2013-11-11 22:23:51 UTC (rev 58196)
+++ grass/trunk/gui/wxpython/mapswipe/toolbars.py	2013-11-11 22:39:35 UTC (rev 58197)
@@ -118,7 +118,7 @@
     def _toolbarData(self):
         """!Toolbar data"""
         return self._getToolbarData((("addRaster", swipeIcons['addRast'],
-                                      self.parent.OnSelectRasters),
+                                      self.parent.OnSelectLayers),
                                      (None, ),
                                      ("tools", swipeIcons['tools'],
                                       self.OnToolMenu)



More information about the grass-commit mailing list