浏览代码

wxGUI: wx.vnet moved from addons to trunk

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@54515 15284696-431f-4ddb-bdfa-cd5b030d7da7
Martin Landa 12 年之前
父节点
当前提交
186e0732a8

+ 3 - 3
gui/wxpython/Makefile

@@ -11,12 +11,12 @@ ETCDIR = $(ETC)/gui/wxpython
 
 
 SRCFILES := $(wildcard icons/*.* scripts/* xml/*) \
 SRCFILES := $(wildcard icons/*.* scripts/* xml/*) \
 	$(wildcard animation/* core/* dbmgr/* gcp/* gmodeler/* gui_core/* iclass/* lmgr/* location_wizard/* \
 	$(wildcard animation/* core/* dbmgr/* gcp/* gmodeler/* gui_core/* iclass/* lmgr/* location_wizard/* \
-	mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* ogc_services/* rlisetup/*) \
+	mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* ogc_services/* rlisetup/* vnet/*) \
 	gis_set.py gis_set_error.py wxgui.py README
 	gis_set.py gis_set_error.py wxgui.py README
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
 
 PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler gui_core iclass lmgr location_wizard \
 PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler gui_core iclass lmgr location_wizard \
-	mapdisp modules nviz psmap mapswipe vdigit wxplot ogc_services rlisetup)
+	mapdisp modules nviz psmap mapswipe vdigit wxplot ogc_services rlisetup vnet)
 DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
 DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
 
 
 default: $(DSTFILES)
 default: $(DSTFILES)
@@ -41,4 +41,4 @@ $(ETCDIR):
 	$(MKDIR) $@
 	$(MKDIR) $@
 
 
 #doxygen:
 #doxygen:
-DOXNAME=wxpython
+DOXNAME = wxpython

+ 100 - 0
gui/wxpython/docs/wxGUI.vnet.html

@@ -0,0 +1,100 @@
+<!-- meta page description: wxGUI Vector Network Analysis Tool -->
+<!-- meta page index: wxGUI -->
+
+<h2>DESCRIPTION</h2>
+
+<em>Vector Network Analysis Tool</em> is graphical front-end
+for <tt>v.net*</tt> modules. It allows perform network analysis
+directly in <em><a href="wxGUI.html">wxGUI</a></em> without need to
+use command line. The tool is available from GRASS AddOns repository
+and needs to be installed to GRASS GIS by command:
+
+<div class="code"><pre>
+g.extension -s extension=wx.vnet
+</pre></div>
+
+The tool can be launched from Map Display toolbar submenu
+of <em>Analyze Map</em> button where is <em>Vector network analysis
+(experimental, GSoC 2012)</em> item.
+
+<p>
+<em>Vector Network Analysis Tool</em> currently allows to:
+
+<ul>
+  <li> perform these network analyses:
+  <ul>
+    <li> Shortest path
+    (<em><a href="v.net.path.html">v.net.path</a></em>)</li>
+    <li> Salesman
+    (<em><a href="v.net.salesman.html">v.net.salesman</a></em>)</li>
+    <li> Flow (<em><a href="v.net.flow.html">v.net.flow</a></em>)</li>
+    <li> Allocate subnets for nearest centers
+    (<em><a href="v.net.alloc.html">v.net.alloc</a></em>)</li>
+    <li> Steiner tree for the network and given terminals
+    (<em><a href="v.net.distance.html">v.net.distance</a></em>)</li>
+    <li> Splits net by cost isolines
+    (<em><a href="v.net.iso.html">v.net.iso</a></em>)</li>
+  </ul>
+  </li>
+  <li>show and set all data needed for the analysis (points, attribute
+  tables, compute costs)</li>
+  <li>show analysis results (maps and it's attribute tables)</li>
+  <li>snapping to nodes</li>
+  <li>browse previous analysis results</li>
+</ul>
+
+<h2>NOTES</h2>
+
+The tool is split into tabs. Every tab represents some functionality:
+
+<ul>
+  <li> <tt>Parameters</tt> tab - It is used for setting vector map and
+  it's layer on which analysis will be done. Also it is possible to
+  set columns with cost values from attribute table connected 
+  to particular layer.</li>
+  <li> <tt>Points</tt> tab - It manages points, which are used for
+  analysis.</li>
+  <li> <tt>Output</tt> tab - There is a output console, which shows
+  information about running analysis. </li>
+  <li> <tt>Input tables</tt> tab - When existing vector map and it's
+  existing layers are set in <tt>Parameters</tt> tab, this tab is
+  dynamically added. It shows attribute tables of node and arc layers,
+  which were chosen for analysis. It is also possible to compute cost
+  values in this tab. This can be done by right mouse button click on
+  column label. Then from pop-up menu choose <tt>Add column</tt>,
+  where new column for cost values can be created. After that by right
+  mouse button click on the added column label can be chosen
+  item <tt>Field calculator</tt>. This tool allows to compute cost
+  values.</li>
+  <li> <tt>Result tables</tt> tab - Result of vector network analysis
+  is always vector map. Some vector network analysis results can also
+  include attribute tables. If such a table is connected to result
+  map, this tab is showed and allows to browse these data.</li>
+</ul>
+
+<h2>KNOWN ISSUES</h2>
+
+When some change is done in layer tree of Map Display, temporary
+vector map representing result of analysis is not rendered 
+(use <tt>Show result</tt> button in toolbar to render it again).
+
+<h2>SEE ALSO</h2>
+
+<em>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
+</em>
+
+<p>
+See also the
+user <a href="http://grass.osgeo.org/wiki/WxGUI_Vector_Network_Analysis_Tool">wiki</a>
+page.
+
+<h2>AUTHOR</h2>
+
+Stepan
+Turek, <a href="http://grass.osgeo.org/wiki/GRASS_GSoC_2012_WxGUI_front_end_for_vector_analysis_modules">Google
+Summer of Code 2012</a> (mentor: Martin Landa)
+
+<p>
+<i>$Date$</i>

+ 7 - 0
gui/wxpython/lmgr/frame.py

@@ -725,6 +725,13 @@ class GMFrame(wx.Frame):
             cmd = self.GetMenuCmd(event)
             cmd = self.GetMenuCmd(event)
         GUI(parent = self).ParseCommand(cmd)
         GUI(parent = self).ParseCommand(cmd)
         
         
+    def OnVNet(self, event):
+        """Vector network analysis tool"""
+        if self.GetMapDisplay():
+            self.GetMapDisplay().OnVNet(event)
+        else:
+            self.NewDisplay(show = True).OnVNet(event)
+        
     def OnVDigit(self, event):
     def OnVDigit(self, event):
         """!Start vector digitizer
         """!Start vector digitizer
         """
         """

+ 6 - 13
gui/wxpython/mapdisp/frame.py

@@ -1335,23 +1335,16 @@ class MapFrame(SingleMapFrame):
         """!Returns toolbar with zooming tools"""
         """!Returns toolbar with zooming tools"""
         return self.toolbars['map']
         return self.toolbars['map']
 
 
-    def OnVnet(self, event):
+    def OnVNet(self, event):
         """!Dialog for v.net* modules 
         """!Dialog for v.net* modules 
         """
         """
         if self.dialogs['vnet']:
         if self.dialogs['vnet']:
             return
             return
-        else:
-            try:
-                from vnet.dialogs import VNETDialog
-            except ImportError:
-                GError(_("Extension <%s> not available, run '%s' to install it.") % \
-                           ('wx.vnet', 'g.extension -s extension=wx.vnet'),
-                       parent = self, showTraceback = False)
-                return
-            
-            self.dialogs['vnet'] = VNETDialog(parent = self)
-            self.dialogs['vnet'].CenterOnScreen()
-            self.dialogs['vnet'].Show()
+        
+        from vnet.dialogs import VNETDialog
+        self.dialogs['vnet'] = VNETDialog(parent = self)
+        self.dialogs['vnet'].CenterOnScreen()
+        self.dialogs['vnet'].Show()
             
             
     def SwitchTool(self, toolbar, event):
     def SwitchTool(self, toolbar, event):
         """!Calls UpdateTools to manage connected toolbars"""
         """!Calls UpdateTools to manage connected toolbars"""

+ 6 - 6
gui/wxpython/mapdisp/toolbars.py

@@ -47,7 +47,7 @@ MapIcons =  {
     'histogram'  : MetaIcon(img = 'layer-raster-histogram',
     'histogram'  : MetaIcon(img = 'layer-raster-histogram',
                             label = _('Create histogram of raster map')),
                             label = _('Create histogram of raster map')),
     'vnet'       : MetaIcon(img = 'line-split',
     'vnet'       : MetaIcon(img = 'line-split',
-                            label = _('Vector network analysis (experimental, GSoC 2012)')),
+                            label = _('Vector network analysis tool')),
     }
     }
 
 
 NvizIcons = {
 NvizIcons = {
@@ -239,12 +239,12 @@ class MapToolbar(BaseToolbar):
     def OnAnalyze(self, event):
     def OnAnalyze(self, event):
         """!Analysis tools menu
         """!Analysis tools menu
         """
         """
-        self._onMenu(((MapIcons["measure"],    self.parent.OnMeasure),
-                      (MapIcons["profile"],    self.parent.OnProfile),
-                      (MapIcons["scatter"],    self.parent.OnScatterplot),
-                      (MapIcons["histogram"],  self.parent.OnHistogramPyPlot),
+        self._onMenu(((MapIcons["measure"],     self.parent.OnMeasure),
+                      (MapIcons["profile"],     self.parent.OnProfile),
+                      (MapIcons["scatter"],     self.parent.OnScatterplot),
+                      (MapIcons["histogram"],   self.parent.OnHistogramPyPlot),
                       (BaseIcons["histogramD"], self.parent.OnHistogram),
                       (BaseIcons["histogramD"], self.parent.OnHistogram),
-                      (MapIcons["vnet"],        self.parent.OnVnet)))
+                      (MapIcons["vnet"],        self.parent.OnVNet)))
         
         
     def OnDecoration(self, event):
     def OnDecoration(self, event):
         """!Decorations overlay menu
         """!Decorations overlay menu

文件差异内容过多而无法显示
+ 2827 - 0
gui/wxpython/vnet/dialogs.py


+ 176 - 0
gui/wxpython/vnet/toolbars.py

@@ -0,0 +1,176 @@
+"""!
+@package vnet.toolbars
+
+@brief Vector network analysis dialog - toolbars
+
+Classes:
+ - toolbars::PointListToolbar
+ - toolbars::MainToolbar
+
+(C) 2012 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.
+
+@author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
+"""
+
+import wx
+
+from icon              import MetaIcon
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from core.gcmd         import RunCommand
+class PointListToolbar(BaseToolbar):
+    """!Toolbar for managing list of points
+
+    @param parent reference to VNETDialog
+    """
+    def __init__(self, parent, list):
+        BaseToolbar.__init__(self, parent)
+        self.list = list
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+
+    def _toolbarData(self):
+
+        icons = {
+            'insertPoint'  : MetaIcon(img = 'pointer',
+                                    label = _('Insert points from Map Display')),
+            'snapping'  : MetaIcon(img = 'move',
+                                    label = _('Activate snapping to nodes')),
+            'pointAdd'     : MetaIcon(img = 'point-create',
+                                    label = _('Add new point')),
+            'pointDelete'  : MetaIcon(img = 'gcp-delete',
+                                    label = _('Delete selected point'))
+            }
+
+        return  self._getToolbarData((('insertPoint', icons['insertPoint'],
+                                      self.list.dialog.OnInsertPoint,#TODO self.list.dialog
+                                      wx.ITEM_CHECK),
+                                      ('snapping', icons['snapping'],
+                                      self.list.dialog.OnSnapping,
+                                      wx.ITEM_CHECK),
+                                      (None, ),
+                                     ('pointAdd', icons["pointAdd"],
+                                        self.list.AddItem),
+                                     ('pointDelete', icons["pointDelete"],
+                                        self.list.DeleteItem)))
+                                    
+    def GetToolId(self, toolName): #TODO can be useful in base
+
+        return vars(self)[toolName]
+
+class MainToolbar(BaseToolbar):
+    """!Main toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+
+        choices = []
+
+        for moduleName in self.parent.vnetModulesOrder:
+            choices.append(self.parent.vnetParams[moduleName]['label'])
+
+        self.UpdateUndoRedo()
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+
+        icons = {
+                 'run' : MetaIcon(img = 'execute',
+                                  label = _('Execute analysis')),
+                 'undo' : MetaIcon(img = 'undo',
+                                  label = _('Go to previous analysis result')),
+                 'redo' : MetaIcon(img = 'redo',
+                                  label = _('Go to next analysis result')),
+                 'showResult'   : MetaIcon(img = 'layer-add',
+                                    label = _("Show analysis result")),
+                 'saveTempLayer' : MetaIcon(img = 'map-export',
+                                             label = _('Save temporary result')),
+                 'settings' : BaseIcons['settings'].SetLabel( _('Vector network analysis settings')),
+                 'help'       : MetaIcon(img = 'help',
+                                         label = _('Show manual'))
+                }
+
+        return self._getToolbarData((
+                                     ("run", icons['run'],
+                                      self.parent.OnAnalyze),
+                                     (None, ),     
+                                     ("undo", icons['undo'], 
+                                      self.parent.OnUndo), 
+                                     ("redo", icons['redo'], 
+                                      self.parent.OnRedo),
+                                     (None, ),
+                                     ("showResult", icons['showResult'], 
+                                      self.parent.OnShowResult, wx.ITEM_CHECK), 
+                                     ("saveTempLayer", icons['saveTempLayer'],
+                                      self.parent.OnSaveTmpLayer),
+                                     (None, ),
+                                     ('settings', icons["settings"],
+                                      self.parent.OnSettings),  
+                                     ('help', icons["help"],
+                                      self.OnHelp),                                     
+                                     ("quit", BaseIcons['quit'],
+                                      self.parent.OnCloseDialog)
+                                    ))
+
+
+    def UpdateUndoRedo(self):
+
+        id = vars(self)['showResult']
+        self.ToggleTool(id =id,
+                        toggle = True)
+
+        if self.parent.history.GetCurrHistStep() >= self.parent.history.GetStepsNum():
+           self.Enable("undo", False)
+        else:
+           self.Enable("undo", True)
+
+        if self.parent.history.GetCurrHistStep() <= 0:
+           self.Enable("redo", False)
+        else:
+           self.Enable("redo", True)  
+
+    def OnHelp(self, event) :
+            RunCommand('g.manual',
+                       entry = 'wxGUI.VNet')
+
+class AnalysisToolbar(BaseToolbar):
+    """!Main toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+
+        choices = []
+
+        for moduleName in self.parent.vnetModulesOrder:
+            choices.append(self.parent.vnetParams[moduleName]['label'])
+
+        self.anChoice = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                    choices = choices,
+                                    style = wx.CB_READONLY, size = (350, 30))#FIXME
+        self.anChoice.SetToolTipString(_('Availiable analyses'))
+        self.anChoice.SetSelection(0)
+               
+        self.anChoiceId = self.AddControl(self.anChoice)
+        self.parent.Bind(wx.EVT_COMBOBOX, self.parent.OnAnalysisChanged, self.anChoiceId)
+                
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.anChoice.Hide()
+        self.anChoice.Show()
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+
+        icons = {}
+
+        return self._getToolbarData(())

+ 638 - 0
gui/wxpython/vnet/widgets.py

@@ -0,0 +1,638 @@
+"""!
+@package vnet.widgets
+
+@brief Base class for list of points. 
+
+Classes:
+ - widgets::PointsList
+ - widgets::EditItem
+
+(C) 2012 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.
+
+@author Original author Michael Barton
+@author Original version improved by Martin Landa <landa.martin gmail.com>
+@author Rewritten by Markus Metz redesign georectfier -> GCP Manage
+@author Stepan Turek <stepan.turek seznam.cz> (Created PointsList from GCPList) (GSoC 2012, mentor: Martin Landa)
+"""
+
+import os
+import wx
+from copy import copy, deepcopy
+
+import wx
+from wx.lib.mixins.listctrl import CheckListCtrlMixin, ColumnSorterMixin, ListCtrlAutoWidthMixin, TextEditMixin
+
+from core import globalvar
+
+
+class PointsList(wx.ListCtrl,
+                 CheckListCtrlMixin,
+                 ListCtrlAutoWidthMixin,
+                 ColumnSorterMixin):
+
+    def __init__(self, parent, cols, id=wx.ID_ANY,
+                 pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
+                 wx.LC_SINGLE_SEL):
+        """!Creates list for points. 
+
+        PointsList class was created from GCPList class in GCP manager. It is possible 
+        to be shared by GCP and VNET front end.
+
+        Important parameters:
+        @param cols is list containing list items. which represents columns.
+                This columns will be added in order as they are in list. 
+                Class will add as first column "use" with number of point and checkbox.
+                Structure of list item must be this:
+               -1. item: column name 
+               -2. item: column label                
+               -3. item: If column is editable by user, it must contain convert function to convert
+                         inserted string to it's type for sorting. Use None for not editable 
+                         columns. Values for insertion can be in list. This allows insert
+                         just values in the list. 
+               -4. item: Default value for column cell. Value should be given in it's  type 
+                         in order to sorting would work properly. If 3. item is list, it must be index
+                         of some item in the list.
+  
+        Example of cols parameter:
+                 column name, column label, convert function, default val
+        @code
+         cols =   [
+                   ['E', _('source E'), float, 0.0],
+                   ['N', _('source N'), float, 0.0],
+                   ['E', _('target E'), float, 0.0],
+                   ['N', _('target N'), float, 0.0],
+                   ['F_Err', _('Forward error'), None, 0],
+                   ['B_Err', _(Backward error'), None, 0]
+                   ['type', _('type'), [_(""), _("Start point"), _("End point")], 0] # Select from 3 choices ("Start point", "End point"), 
+                                                                                     # Choice with index 0 ("") is default.
+                  ]
+        @endcode 
+        """
+
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+        # Mixin settings
+        CheckListCtrlMixin.__init__(self)
+        ListCtrlAutoWidthMixin.__init__(self)
+        # TextEditMixin.__init__(self)
+
+        # inserts first column with points numbers and checkboxes
+        cols.insert(0, ['use', _('use'), False, 0])
+
+        self.colsData = cols
+        self.dataTypes = {"colName" : 0,
+                          "colLabel" : 1,
+                          "colEditable" : 2,
+                          "itemDefaultValue" : 3} # just for better understanding
+
+        # information whether list items are checked or not
+        self.CheckList = [] 
+
+        self._createCols()
+        self.hiddenCols = {}
+
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+
+        self.selected = wx.NOT_FOUND
+        self.selectedkey = -1
+
+
+        # CheckListCtrlMixin must set an ImageList first
+        self.il = self.GetImageList(wx.IMAGE_LIST_SMALL)
+
+        # images for column sorting
+        SmallUpArrow = wx.BitmapFromImage(self.getSmallUpArrowImage())            
+        SmallDnArrow = wx.BitmapFromImage(self.getSmallDnArrowImage())            
+        self.sm_dn = self.il.Add(SmallDnArrow)
+        self.sm_up = self.il.Add(SmallUpArrow)
+
+        # initialize column sorter
+        self.itemDataMap = [] 
+        ncols = self.GetColumnCount()
+        ColumnSorterMixin.__init__(self, ncols)
+
+        # init to ascending sort on first click
+        self._colSortFlag = [1] * ncols
+
+        # same structure as itemDataMap, information about choice index selected
+        # if cell is in column without values to choose then is -1 
+        self.selIdxs = []
+
+        self.ResizeColumns()
+        self.SetColumnWidth(0, 50)
+
+    def _createCols(self):
+        """!Creates columns in list"""
+        if 0:
+            # normal, simple columns
+            for col in enumerate(self.colsData):
+                iLabel = self.dataTypes["colLabel"]
+                self.InsertColumn(col[0], col[1][iLabel])
+        else:
+            # the hard way: we want images on the column header
+            info = wx.ListItem()
+            info.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT)
+            info.SetImage(-1)
+            info.m_format = wx.LIST_FORMAT_LEFT
+
+            for col in enumerate(self.colsData):
+                iLabel = self.dataTypes["colLabel"]
+                info.SetText(col[1][iLabel]) 
+                self.InsertColumnInfo(col[0], info)
+
+    def AddItem(self, event):
+        """!Appends an item to list with default values"""
+        iDefVal = self.dataTypes["itemDefaultValue"]
+        iColEd = self.dataTypes["colEditable"]
+        itemData = []
+        itemIndexes = []
+        for col in self.colsData:
+            if type(col[iColEd]).__name__ == "list":
+                itemData.append(col[iColEd][col[iDefVal]])
+                itemIndexes.append(col[iDefVal])
+            else:
+                itemData.append(col[iDefVal])
+                itemIndexes.append(-1)# not a choise column 
+
+                
+        self.selIdxs.append(itemIndexes) 
+
+        for hCol in self.hiddenCols.itervalues():
+            defVal = hCol['colsData'][iDefVal]
+            if type(hCol['colsData'][iColEd]).__name__ == "list":
+                hCol['itemDataMap'].append(hCol['colsData'][iColEd][defVal])
+                hCol['selIdxs'].append(defVal)
+            else:
+                hCol['itemDataMap'].append(defVal)
+                hCol['selIdxs'].append(-1)
+
+        self.selectedkey = self.GetItemCount()
+
+        itemData[0] = self.selectedkey + 1
+        self.itemDataMap.append(copy(itemData))
+
+        self.Append(map(str, itemData))             
+
+        self.selected = self.GetItemCount() - 1
+        self.SetItemData(self.selected, self.selectedkey)
+
+        self.SetItemState(self.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+        self.ResizeColumns()
+
+        return self.selected
+
+    def GetCellValue(self, key, colName):
+        """!Get value in cell of list using key (same regardless of sorting)"""
+        colNum = self._getColumnNum(colName)
+        iColEd = self.dataTypes["colEditable"]      
+        return self.itemDataMap[key][colNum]
+
+    def GetCellSelIdx(self, key, colName):
+        """!Get selected index in cell of list using key (same regardless of sorting)
+
+            @return number of chosen value, if column has values to choose
+            @return -1 if column does not has values to choose
+        """
+        colNum = self._getColumnNum(colName)
+        iColEd = self.dataTypes["colEditable"]
+        return self.selIdxs[key][colNum]
+
+    def EditCellIndex(self, index, colName, cellData):
+        """!Changes value in list using key (same regardless of sorting)"""
+        colNum = self._getColumnNum(colName) 
+        key = self.GetItemData(index)
+
+        iColEd = self.dataTypes["colEditable"]
+        if type(self.colsData[colNum][iColEd]).__name__ == "list":
+            cellVal = self.colsData[colNum][iColEd][cellData]
+            self.selIdxs[key][colNum] = cellData
+        else:
+            cellVal = cellData  
+            self.selIdxs[key][colNum] = -1
+
+        self.itemDataMap[key][colNum] = cellVal
+        self.SetStringItem(index, colNum, str(cellVal))
+
+    def EditCellKey(self, key, colName, cellData):
+        """!Changes value in list using index (changes during sorting)"""
+        colNum = self._getColumnNum(colName)   
+
+        iColEd = self.dataTypes["colEditable"]
+        if type(self.colsData[colNum][iColEd]).__name__ == "list":
+            cellVal = self.colsData[colNum][iColEd][cellData]
+            self.selIdxs[key][colNum] = cellData
+        else:
+            cellVal = cellData  
+            self.selIdxs[key][colNum] = -1
+
+        self.itemDataMap[key][colNum] = cellVal
+        index = self._findIndex(key)
+
+        self.SetStringItem(index, colNum, str(cellVal))
+
+    def _findIndex(self, key):
+        """!Find index for key"""
+        index = -1
+        while True:
+            index = self.GetNextItem(index,
+                                     wx.LIST_NEXT_BELOW)
+            if key == self.GetItemData(index):
+                return index
+            if index == -1:
+                break
+        return -1
+
+    def ChangeColEditable(self, colName, colType):
+        """!Change 3. item in constructor parameter cols (see the class constructor hint)"""     
+        colNum = self._getColumnNum(colName)
+        iColEd = self.dataTypes["colEditable"]
+        self.colsData[colNum][iColEd] = colType
+
+    def DeleteItem(self, event = None):
+        """!Delete selected item in list"""
+        if self.selected == wx.NOT_FOUND:
+            return
+
+        key = self.GetItemData(self.selected)
+        wx.ListCtrl.DeleteItem(self, self.selected)
+
+        del self.itemDataMap[key]
+        self.selIdxs.pop(key)
+
+        # update hidden columns
+        for hCol in self.hiddenCols.itervalues():    
+            hCol['itemDataMap'].pop(key) 
+            hCol['selIdxs'].pop(key)
+
+        # update key and point number
+        for newkey in range(key, len(self.itemDataMap)):
+            index = self.FindItemData(-1, newkey + 1)
+            self.itemDataMap[newkey][0] = newkey
+            self.SetStringItem(index, 0, str(newkey + 1))
+            self.SetItemData(index, newkey)
+
+        # update selected
+        if self.GetItemCount() > 0:
+            if self.selected < self.GetItemCount():
+                self.selectedkey = self.GetItemData(self.selected)
+            else:
+                self.selected = self.GetItemCount() - 1
+                self.selectedkey = self.GetItemData(self.selected)
+                
+            self.SetItemState(self.selected,
+                              wx.LIST_STATE_SELECTED,
+                              wx.LIST_STATE_SELECTED)
+        else:
+            self.selected = wx.NOT_FOUND
+            self.selectedkey = -1
+
+    def ClearItem(self, event):
+        """"!Set all values to default in selected item of points list and uncheck it."""
+        if self.selected == wx.NOT_FOUND:
+            return
+        index = self.selected
+
+        iDefVal = self.dataTypes["itemDefaultValue"]
+        iColEd = self.dataTypes["colEditable"]
+
+        i = 0
+        for col in self.colsData:
+            if i == 0:
+                i  += 1
+                continue
+            if type(col[iColEd]).__name__ == "list":
+                self.EditCell(index, i, col[iColEd][col[iDefVal]])
+            else:
+                self.EditCell(index, i, col[iDefVal])
+            i  += 1
+        self.CheckItem(index, False)
+
+    def ResizeColumns(self, minWidth = [90, 120]):
+        """!Resize columns"""
+        for i in range(self.GetColumnCount()):
+            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
+            # first column is checkbox, don't set to minWidth
+            if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]:
+                self.SetColumnWidth(i, minWidth[i > 4])
+
+        self.SendSizeEvent()
+
+    def GetSelected(self):
+        """!Get index of selected item."""
+        return self.selected
+
+    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+    def GetSortImages(self):
+        return (self.sm_dn, self.sm_up)
+
+    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+    def GetListCtrl(self):
+        return self
+
+    def OnItemActivated(self, event):
+        """!When item is double clicked, open editor to edit editable columns."""
+        data = []
+
+        index = event.GetIndex()
+        key = self.GetItemData(index)       
+        changed = False
+        iColEd = self.dataTypes["colEditable"]
+
+        for col in  enumerate(self.colsData):
+            if col[1][iColEd]:
+                data.append([col[0], #culumn number
+                             self.GetItem(index, col[0]).GetText(), #cell value 
+                             col[1][iColEd]]) #convert function for type check
+
+        if not data:
+            return
+        dlg = self.CreateEditDialog(data = data, pointNo = key)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            editedData = dlg.GetValues() # string
+            
+            if len(editedData) == 0:
+                GError(parent = self,
+                       message=_("Invalid value inserted. Operation canceled."))
+            else:
+                i = 0
+                for editedCell in editedData:
+                    if editedCell[1] != data[i][1]:
+                        self.SetStringItem(index, editedCell[0], str(editedCell[1]))
+                        self.itemDataMap[key][editedCell[0]] = editedCell[1]
+                        changed = True
+                    i += 1 
+
+                self.selIdxs[key] = dlg.GetSelectionIndexes()
+        dlg.Destroy()
+        return changed
+        
+    def CreateEditDialog(self, data, pointNo):
+        """!Helper function
+        It is possible to define in child derived class
+        and adapt created dialog (e. g. it's title...) 
+        """
+
+        return  EditItem(parent=self, id=wx.ID_ANY, data = data, pointNo=pointNo)
+
+    def OnColClick(self, event):
+        """!ListCtrl forgets selected item..."""
+        self.selected = self.FindItemData(-1, self.selectedkey)
+        self.SetItemState(self.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+        event.Skip()
+
+    def OnItemSelected(self, event):
+        """!Updates class attributes holding information about selected item"""
+        if self.selected != event.GetIndex():
+            self.selected = event.GetIndex()
+            self.selectedkey = self.GetItemData(self.selected)
+
+        event.Skip()
+
+    def getSmallUpArrowImage(self):
+        """!Get arrow up symbol for indication of sorting"""
+        stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_up_arrow.png'), 'rb')
+        try:
+            img = wx.ImageFromStream(stream)
+        finally:
+            stream.close()
+        return img
+
+    def getSmallDnArrowImage(self):
+        """!Get arrow down symbol for indication of sorting"""
+        stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_down_arrow.png'), 'rb')
+        try:
+            img = wx.ImageFromStream(stream)
+        finally:
+            stream.close()
+        return img
+
+    def _getColumnNum(self, colName):
+        """!Get position of column among showed columns 
+
+        @param colName - name of column
+
+        @return index of columns or -1 if col was not found
+        """
+
+        for iCol, col in enumerate(self.colsData):
+             if colName == col[0]:
+                 return iCol
+ 
+        return -1
+
+    def HideColumn(self, colName):
+        """!Hide column (hidden columns are not editable)
+
+        @param colName - name of column
+
+        @return True - if column was hidden
+        @return False - if position is not valid or column is not showed
+        """
+        colNum = self._getColumnNum(colName)
+        if colNum == -1:
+            return False
+
+        hiddenCol = self.GetColumn(colNum)
+        self.DeleteColumn(colNum) 
+
+        self.hiddenCols[colName] = {}
+        self.hiddenCols[colName]['wxCol'] = hiddenCol
+        hiddenMaps = []
+        hiddenSelIdxs = []
+        for item in self.itemDataMap:
+            hiddenMaps.append(item.pop(colNum))
+        for item in self.selIdxs:
+            hiddenSelIdxs.append(item.pop(colNum))
+
+        self.hiddenCols[colName]['itemDataMap'] = hiddenMaps
+        self.hiddenCols[colName]['selIdxs'] = hiddenSelIdxs
+        self.hiddenCols[colName]['colsData'] = self.colsData.pop(colNum)
+        self.ResizeColumns()
+
+        return True
+
+    def ShowColumn(self, colName, pos):
+        """!Show column
+
+        @param colName - name of column
+        @param pos - zero based index of position among showed columns (including added 'use' column)
+
+        @return True - if column was shown
+        @return False - if position is not valid or column is not hidden
+        """
+        if pos < 0 and pos >= self.self.GetColumnCount():
+            return False
+        if self.hiddenCols.has_key(colName):
+            col = self.hiddenCols[colName]
+
+            for item in enumerate(self.itemDataMap):
+                item[1].insert(pos, col['itemDataMap'][item[0]])
+            for item in enumerate(self.selIdxs):
+                item[1].insert(pos, col['selIdxs'][item[0]])
+
+            self.colsData.insert(pos, col['colsData'])
+
+            self.InsertColumnItem(pos, col['wxCol'])
+            self.ResizeColumns()
+            del self.hiddenCols[colName]  
+            return True
+
+        return False
+
+    def IsShown(self, colName):
+        """!Is column shown
+
+        @param colName - name of column
+
+        @return True - if is shown
+        @return False - if is not shown
+        """
+
+        if self._getColumnNum(colName) == -1:
+            return False
+        else:
+            return True
+
+class EditItem(wx.Dialog):
+    
+    def __init__(self, parent, data, pointNo, itemCap = "Point No." ,id=wx.ID_ANY,
+                 title =_("Edit point"), style=wx.DEFAULT_DIALOG_STYLE):
+        """!Dialog for editing item cells in list"""
+
+        wx.Dialog.__init__(self, parent, id, title=_(title), style=style)
+
+        self.parent = parent
+        panel = wx.Panel(parent=self)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
+                            label=" %s %s " % (_(itemCap), str(pointNo + 1)))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        # source coordinates
+        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+       
+        self.fields = [] 
+        self.data = deepcopy(data)
+
+
+        col = 0
+        row = 0
+        iField = 0
+        for cell in self.data:
+
+            # Select
+            if type(cell[2]).__name__ == "list":
+                self.fields.append(wx.ComboBox(parent = panel, id = wx.ID_ANY,
+                                               choices = cell[2],
+                                               style = wx.CB_READONLY, 
+                                               size = (110, -1)))
+            # Text field
+            else:
+                if cell[2] == float:
+                    validator = FloatValidator()
+                elif cell[2] == int:
+                    validator = IntegerValidator()
+                else:
+                    validator = None
+
+                if validator:
+                    self.fields.append(wx.TextCtrl(parent=panel, id=wx.ID_ANY, 
+                                                   validator = validator, size=(150, -1)))
+                else:
+                    self.fields.append(wx.TextCtrl(parent=panel, id=wx.ID_ANY, 
+                                                   size=(150, -1)))
+                    self.fields[iField].SetValue(str(cell[1]))
+
+            label = wx.StaticText(parent = panel, id=wx.ID_ANY,
+                                  label = _(parent.GetColumn(cell[0]).GetText()) + ":") # name of column)
+
+            gridSizer.Add(item=label,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos=(row, col))
+
+            col += 1
+
+            gridSizer.Add(item=self.fields[iField],
+                          pos=(row, col))
+
+
+            if col%3 == 0:
+                col = 0
+                row += 1
+            else:
+                col += 1
+
+            iField += 1
+
+        boxSizer.Add(item=gridSizer, proportion=1,
+                  flag=wx.EXPAND | wx.ALL, border=5)
+
+        sizer.Add(item=boxSizer, proportion=1,
+                  flag=wx.EXPAND | wx.ALL, border=5)
+
+        #
+        # buttons
+        #
+        self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
+        self.btnOk = wx.Button(panel, wx.ID_OK)
+        self.btnOk.SetDefault()
+
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item=btnSizer, proportion=0,
+                  flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+        panel.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def GetValues(self):
+        """!Return list of values (as strings).
+        """
+
+        iField = 0
+        for cell in self.data:
+            value = self.fields[iField].GetValue()
+            
+            if type(cell[2]).__name__ == "list":
+                    cell[1] = value
+            else:
+                try:
+                    cell[1] = cell[2](value)
+                except ValueError:
+                    return []
+            iField += 1
+
+        return self.data
+
+    def GetSelectionIndexes(self):
+        """!Return indexes of selected values (works just for choice columns).
+        """
+        iField = 0
+        itemIndexes = []
+        for cell in self.parent.colsData:            
+            if type(cell[2]).__name__ == "list":
+                itemIndexes.append(self.fields[iField].GetSelection())
+            else:
+                itemIndexes.append(-1) # not a choise column 
+            if cell[2]:   
+                iField += 1
+
+        return itemIndexes
+
+
+

+ 7 - 0
gui/wxpython/xml/menudata.xml

@@ -2314,6 +2314,13 @@
         <menu>
         <menu>
           <label>Network analysis</label>
           <label>Network analysis</label>
           <items>`
           <items>`
+           <menuitem>
+              <label>Vector network analysis tool</label>
+              <help>Tool for interactive vector network analysis.</help>
+              <keywords>gui,vector,network</keywords>
+              <handler>OnVNet</handler>
+            </menuitem>
+            <separator />
             <menuitem>
             <menuitem>
               <label>Network preparation</label>
               <label>Network preparation</label>
               <help>Performs network maintenance.</help>
               <help>Performs network maintenance.</help>