/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/legend.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/legend.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1050 by frank, Tue May 27 09:31:13 2003 UTC revision 2187 by jan, Sun Apr 18 20:37:45 2004 UTC
# Line 8  Line 8 
8    
9  __version__ = "$Revision$"  __version__ = "$Revision$"
10    
11    from  math import fabs, cos, pi
12    
13  from Thuban import _  from Thuban import _
14    
15  import resource  import resource
# Line 17  from wxPython.wx import * Line 19  from wxPython.wx import *
19  from Thuban.Model.layer import BaseLayer  from Thuban.Model.layer import BaseLayer
20  from Thuban.Model.map import Map  from Thuban.Model.map import Map
21  from Thuban.Model.classification import ClassGroup  from Thuban.Model.classification import ClassGroup
22    from Thuban.Model.proj import PROJ_UNITS_DEGREES
23    
24  from Thuban.Model.messages import \  from Thuban.Model.messages import \
25      MAP_STACKING_CHANGED, MAP_LAYERS_ADDED, MAP_LAYERS_REMOVED, LAYER_CHANGED,\      MAP_STACKING_CHANGED, MAP_LAYERS_ADDED, MAP_LAYERS_REMOVED, LAYER_CHANGED,\
# Line 28  from Thuban.UI.classifier import ClassDa Line 31  from Thuban.UI.classifier import ClassDa
31  from Thuban.UI.dock import DockPanel  from Thuban.UI.dock import DockPanel
32  from Thuban.UI.scalebar import ScaleBar  from Thuban.UI.scalebar import ScaleBar
33    
34    from Thuban.UI.menu import Menu
35    
36  from Thuban.Lib.connector import ConnectorError  from Thuban.Lib.connector import ConnectorError
37    
38  ID_LEGEND_TOP = 4001  ID_LEGEND_TOP = 4001
# Line 50  SHOW_BMP  = "show_layer" Line 55  SHOW_BMP  = "show_layer"
55  HIDE_BMP  = "hide_layer"  HIDE_BMP  = "hide_layer"
56  PROPS_BMP = "layer_properties"  PROPS_BMP = "layer_properties"
57    
   
58  class LegendPanel(DockPanel):  class LegendPanel(DockPanel):
59    
60      def __init__(self, parent, map, mainWindow):      def __init__(self, parent, map, mainWindow):
# Line 179  class LegendPanel(DockPanel): Line 183  class LegendPanel(DockPanel):
183          self.tree.DoOnHideLayer()          self.tree.DoOnHideLayer()
184          pass          pass
185    
186        def _OnToggleVisibility(self, event):
187            self.tree.ToggleVisibility()
188    
189        def _OnProjection(self, event):
190            self.tree.LayerProjection()
191    
192        def _OnRemoveLayer(self, event):
193            self.mainWindow.RemoveLayer()
194    
195        def _OnShowTable(self, event):
196            self.mainWindow.LayerShowTable()
197    
198      def __EnableButtons(self, on):      def __EnableButtons(self, on):
199          self.toolBar.EnableTool(ID_LEGEND_TOP, on)          self.toolBar.EnableTool(ID_LEGEND_TOP, on)
200          self.toolBar.EnableTool(ID_LEGEND_RAISE, on)          self.toolBar.EnableTool(ID_LEGEND_RAISE, on)
# Line 201  class LegendTree(wxTreeCtrl): Line 217  class LegendTree(wxTreeCtrl):
217          self.mainWindow = mainWindow          self.mainWindow = mainWindow
218          self.map = None          self.map = None
219          self.parent = parent          self.parent = parent
220          self.layer2id = {}          self.changing_selection = 0
221    
222            #
223            # The image list used by the wxTreeCtrl causes problems when
224            # we remove layers and/or change a classification because it
225            # changes the image indices if you remove images from the list.
226            # Rather than removing unused images we use this list to keep
227            # track of which indices are available in the image list
228            # (because of a previous removal) and then  replace those indices
229            # with new images rather than appending to the end of the image
230            # list (assuming there are any that are available).
231            #
232            self.availImgListIndices = []
233    
234          self.image_list = None          self.image_list = None
235          self.emptyImageIndex = 0          self.emptyImageIndex = 0
236    
237          self.previewer = ClassDataPreviewer()          self.previewer = ClassDataPreviewer()
238    
239            self.preventExpandCollapse = False
240            self.raiseProperties = False
241    
242          EVT_TREE_ITEM_ACTIVATED(self, ID_LEGEND_TREE, self._OnItemActivated)          EVT_TREE_ITEM_ACTIVATED(self, ID_LEGEND_TREE, self._OnItemActivated)
243          EVT_TREE_SEL_CHANGED(self, ID_LEGEND_TREE, self._OnSelChanged)          EVT_TREE_SEL_CHANGED(self, ID_LEGEND_TREE, self._OnSelChanged)
244            EVT_TREE_ITEM_EXPANDING(self, ID_LEGEND_TREE, self.OnItemExpandCollapse)
245            EVT_TREE_ITEM_COLLAPSING(self, ID_LEGEND_TREE, self.OnItemExpandCollapse)
246            EVT_TREE_ITEM_RIGHT_CLICK(self, ID_LEGEND_TREE, self._OnRightClick)
247    
248          EVT_CLOSE(self, self._OnClose)          EVT_CLOSE(self, self._OnClose)
249    
250          self.SetMap(map)          self.SetMap(map)
251    
252        def _OnRightClick(self, event):
253            """Select item and pop up a context menu"""
254    
255            # The pop up menu is related to the legend tree, so we have direct
256            # access on the tree items. The events issued by the menu are handled
257            # by the legend panel, since most of the handlers are already
258            # implemented there.
259    
260            # Update item selection to the right click
261            item = event.GetItem()
262            self.SelectItem(item)
263    
264            # Define the menu
265            popup_menu = Menu("PopUp", "",
266                              [ "layer_visibility",
267                                None,
268                                "layer_properties",
269                                "layer_projection",
270                                "layer_remove",
271                                "layer_show_table",
272                                None,
273                                "layer_to_top",
274                                "layer_raise",
275                                "layer_lower",
276                                "layer_to_bottom"
277                                ])
278    
279            # Display the menu
280            pos = event.GetPoint()
281            shift = self.ClientToScreen((0,0))
282            self.PopupMenu(self.mainWindow.build_menu(popup_menu), pos)
283    
284        def find_layer(self, layer):
285            """Return the tree item for the layer"""
286            root = self.GetRootItem()
287            id, cookie = self.GetFirstChild(root, 0)
288            while id.IsOk():
289                if self.GetPyData(id) is layer:
290                    return id
291                id, cookie = self.GetNextChild(root, cookie)
292            return None
293    
294      def _OnClose(self, event):      def _OnClose(self, event):
295          self.SetMap(None)          self.SetMap(None)
296    
# Line 224  class LegendTree(wxTreeCtrl): Line 300  class LegendTree(wxTreeCtrl):
300      def SetMap(self, map):      def SetMap(self, map):
301    
302          sub_list = [(MAP_STACKING_CHANGED, self._OnMsgMapStackingChanged),          sub_list = [(MAP_STACKING_CHANGED, self._OnMsgMapStackingChanged),
303                      (MAP_LAYERS_ADDED, self._OnMsgMapLayersAddedRemoved),                      (MAP_LAYERS_ADDED, self._OnMsgMapLayersAdded),
304                      (MAP_LAYERS_REMOVED, self._OnMsgMapLayersAddedRemoved)]                      (MAP_LAYERS_REMOVED, self._OnMsgMapLayersRemoved)]
305    
306          if self.map is not None:          if self.map is not None:
307              for msg, func in sub_list: self.map.Unsubscribe(msg, func)              for msg, func in sub_list: self.map.Unsubscribe(msg, func)
# Line 236  class LegendTree(wxTreeCtrl): Line 312  class LegendTree(wxTreeCtrl):
312                      #self._OnMsgMapsChanged)                      #self._OnMsgMapsChanged)
313              #except ConnectorError:              #except ConnectorError:
314                  #pass                  #pass
315              self.__DeleteAllItems()              self.DeleteAllItems()
316                    
317          self.map = map          self.map = map
318    
# Line 252  class LegendTree(wxTreeCtrl): Line 328  class LegendTree(wxTreeCtrl):
328          layer, group = self.GetSelectedHierarchy()          layer, group = self.GetSelectedHierarchy()
329    
330          if layer is not None:          if layer is not None:
331              self.map.TopLayer(layer)              self.map.MoveLayerToTop(layer)
332          else:          else:
333              assert False, "Shouldn't be allowed."              assert False, "Shouldn't be allowed."
334              pass              pass
# Line 279  class LegendTree(wxTreeCtrl): Line 355  class LegendTree(wxTreeCtrl):
355          layer, group = self.GetSelectedHierarchy()          layer, group = self.GetSelectedHierarchy()
356    
357          if layer is not None:          if layer is not None:
358              self.map.BottomLayer(layer)              self.map.MoveLayerToBottom(layer)
359          else:          else:
360              assert False, "Shouldn't be allowed."              assert False, "Shouldn't be allowed."
361              pass              pass
# Line 296  class LegendTree(wxTreeCtrl): Line 372  class LegendTree(wxTreeCtrl):
372              return wxTreeCtrl.OnCompareItems(self, item1, item2)              return wxTreeCtrl.OnCompareItems(self, item1, item2)
373    
374      def DoOnShowLayer(self):      def DoOnShowLayer(self):
         #self.__ShowHideLayer(True)  
375          layer, group = self.GetSelectedHierarchy()          layer, group = self.GetSelectedHierarchy()
376          layer.SetVisible(True)          layer.SetVisible(True)
377    
378      def DoOnHideLayer(self):      def DoOnHideLayer(self):
         #self.__ShowHideLayer(False)  
379          layer, group = self.GetSelectedHierarchy()          layer, group = self.GetSelectedHierarchy()
380          layer.SetVisible(False)          layer.SetVisible(False)
381    
382        def ToggleVisibility(self):
383            layer, group = self.GetSelectedHierarchy()
384    
385            layer.SetVisible(not layer.Visible())
386    
387        def LayerProjection(self):
388            self.parent.mainWindow.LayerProjection()
389    
390      def Sort(self):      def Sort(self):
391          self.SortChildren(self.GetRootItem())          self.SortChildren(self.GetRootItem())
392    
# Line 330  class LegendTree(wxTreeCtrl): Line 412  class LegendTree(wxTreeCtrl):
412          self.SetMap(self.mainWindow.Map())          self.SetMap(self.mainWindow.Map())
413                    
414      def _OnSelChanged(self, event):      def _OnSelChanged(self, event):
415            # If we change the selection from normalize_selection do nothing.
416            if self.changing_selection:
417                return
418    
419            self.normalize_selection()
420          self.__UpdateSelection()          self.__UpdateSelection()
421    
422        def normalize_selection(self):
423            """Select the layer containing currently selected item"""
424            # This is really a workaround for a bug in wx where deleting a
425            # subtree with DeleteChildren does not update the selection
426            # properly and can lead to segfaults later because the return
427            # value of GetSelection points to invalid data.
428            item = self.GetSelection()
429            while item.IsOk():
430                object = self.GetPyData(item)
431                if isinstance(object, BaseLayer):
432                    break
433                item = self.GetItemParent(item)
434            else:
435                # No layer was found in the chain of parents, so there's
436                # nothing we can do.
437                return
438    
439            self.changing_selection = 1
440            try:
441                self.SelectItem(item)
442            finally:
443                self.changing_selection = 0
444    
445    
446        def OnItemExpandCollapse(self, event):
447            if self.preventExpandCollapse:
448                event.Veto()
449                self.preventExpandCollapse = False
450    
451      def _OnItemActivated(self, event):      def _OnItemActivated(self, event):
452          self.parent.DoOnProperties()          # The following looks strange but is need under Windows to
453            # raise the Properties on double-click: The tree control
454            # always gets an Expanded / Collapsed event after the ItemActivated
455            # on double click, which raises the main window again. We add a second
456            # ItemActivated event to the queue, which simply raises the already
457            # displayed window.
458            if self.raiseProperties:
459                self.parent.DoOnProperties()
460                self.raiseProperties = False
461            else:
462                self.raiseProperties = True
463                self.preventExpandCollapse = True
464                self.parent.DoOnProperties()
465                self.AddPendingEvent(event)
466    
467      def _OnMsgLayerChanged(self, layer):      def _OnMsgLayerChanged(self, layer):
468          assert isinstance(layer, BaseLayer)          assert isinstance(layer, BaseLayer)
469    
470          id = self.layer2id[layer]          id = self.find_layer(layer)
471          assert id.IsOk()          assert id is not None
472    
473          # XXX: yikes! this is so bad, we should be doing what is          self.__FillTreeLayer(id)
         #      commented out, but there is a problem with keeping  
         #      track of the images in the image list when we replace  
         #      a layer. it ends up causing a seg fault.  
         self.__FillTree(self.map)  
474          self.__UpdateSelection()          self.__UpdateSelection()
         #self.__FillTreeLayer(id)  
475    
476      def _OnMsgMapStackingChanged(self, *args):      def _OnMsgMapStackingChanged(self, *args):
477          self.Sort()          self.Sort()
# Line 357  class LegendTree(wxTreeCtrl): Line 481  class LegendTree(wxTreeCtrl):
481              self.EnsureVisible(id)              self.EnsureVisible(id)
482          self.__UpdateSelection()          self.__UpdateSelection()
483    
484      def _OnMsgMapLayersAddedRemoved(self, map):      def _OnMsgMapLayersAdded(self, map):
485          assert map is self.map          assert map is self.map
486    
487          self.__FillTree(self.map)          # Build a dict with all layers known by the the tree as keys
488            layers = {}
489            root = self.GetRootItem()
490            id, cookie = self.GetFirstChild(root, 0)
491            while id.IsOk():
492                layers[self.GetPyData(id)] = 1
493                id, cookie = self.GetNextChild(root, cookie)
494    
495            # Add layers in the map but not in the dict
496            i = 0
497            for l in map.Layers():
498                if not l in layers:
499                    self.__AddLayer(i, l)
500    
501            self.__UpdateSelection()
502    
503        def _OnMsgMapLayersRemoved(self, map):
504            assert map is self.map
505    
506            layers = map.Layers()
507    
508            root = self.GetRootItem()
509            id, cookie = self.GetFirstChild(root, 0)
510            while id.IsOk():
511                if self.GetPyData(id) not in layers:
512                    self.__RemoveLayer(id)
513                id, cookie = self.GetNextChild(root, cookie)
514    
515    
516          self.__UpdateSelection()          self.__UpdateSelection()
517    
518      def _OnMsgLayerVisibilityChanged(self, layer):      def _OnMsgLayerVisibilityChanged(self, layer):
# Line 371  class LegendTree(wxTreeCtrl): Line 523  class LegendTree(wxTreeCtrl):
523    
524      def _OnMsgLayerTitleChanged(self, layer):      def _OnMsgLayerTitleChanged(self, layer):
525    
526          id = self.layer2id[layer]          id = self.find_layer(layer)
527          if id.IsOk():          if id.IsOk():
528              self.SetItemText(id, layer.Title())              self.SetItemText(id, layer.Title())
529          self.__UpdateSelection()          self.__UpdateSelection()
# Line 384  class LegendTree(wxTreeCtrl): Line 536  class LegendTree(wxTreeCtrl):
536    
537          self.Freeze()          self.Freeze()
538    
539          self.__DeleteAllItems()          self.DeleteAllItems()
540    
541          if map.HasLayers():          if map.HasLayers():
542                root = self.GetRootItem()
             self.image_list = wxImageList(BMP_SIZE_W, BMP_SIZE_H, False, 0)  
                                                                                   
             bmp = wxEmptyBitmap(BMP_SIZE_W, BMP_SIZE_H)  
             dc = wxMemoryDC()  
             dc.SelectObject(bmp)  
             dc.SetBrush(wxBLACK_BRUSH)  
             dc.Clear()  
             dc.SelectObject(wxNullBitmap)  
                                                                                   
             self.emptyImageIndex = \  
                 self.image_list.AddWithColourMask(bmp, wxColour(0, 0, 0))  
                                                                                   
             bmp = resource.GetBitmapResource("legend_icon_map",  
                                               wxBITMAP_TYPE_XPM)  
             self.mapImageIndex = \  
                 self.image_list.Add(bmp)  
   
             self.AssignImageList(self.image_list)  
   
             root = self.AddRoot("")  
   
543              for l in map.Layers():              for l in map.Layers():
544                  id = self.PrependItem(root, l.Title())                  self.__AddLayer(0, l)
                 self.SetItemImage(id, self.mapImageIndex)  
                 self.SetItemSelectedImage(id, self.mapImageIndex)  
   
                 l.Subscribe(LAYER_CHANGED, self._OnMsgLayerChanged)  
                 l.Subscribe(LAYER_VISIBILITY_CHANGED,  
                             self._OnMsgLayerVisibilityChanged)  
                 l.Subscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)  
                 self.SetPyData(id, l)  
                 self.__SetVisibilityStyle(l.Visible(), id)  
   
                 self.layer2id[l] = id  
   
                 self.__FillTreeLayer(id)  
                 self.Expand(id)  
545    
546          self.Thaw()          self.Thaw()
547    
# Line 454  class LegendTree(wxTreeCtrl): Line 571  class LegendTree(wxTreeCtrl):
571                          self.SetItemImage(id, -1)                          self.SetItemImage(id, -1)
572                          self.SetItemSelectedImage(id, -1)                          self.SetItemSelectedImage(id, -1)
573                      else:                      else:
574                          i = self.image_list.Add(bmp)                          if self.availImgListIndices:
575                                i = self.availImgListIndices.pop(0)
576                                self.image_list.Replace(i, bmp)
577                            else:
578                                i = self.image_list.Add(bmp)
579    
580                          self.SetItemImage(id, i)                          self.SetItemImage(id, i)
581                          self.SetItemSelectedImage(id, i)                          self.SetItemSelectedImage(id, i)
582    
# Line 472  class LegendTree(wxTreeCtrl): Line 594  class LegendTree(wxTreeCtrl):
594    
595          return bmp          return bmp
596    
597      def __DeleteAllItems(self):      def DeleteAllItems(self):
598    
599          while len(self.layer2id) > 0:          pid = self.GetRootItem()
             layer, id = self.layer2id.popitem()  
             layer.Unsubscribe(LAYER_CHANGED,  
                               self._OnMsgLayerChanged)  
             layer.Unsubscribe(LAYER_VISIBILITY_CHANGED,  
                               self._OnMsgLayerVisibilityChanged)  
             layer.Unsubscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)  
600    
601          self.DeleteAllItems()          id, cookie = self.GetFirstChild(pid, 123)
602            while id.IsOk():
603                self.__RemoveLayer(id)
604                id, cookie = self.GetNextChild(pid, cookie)
605    
606            wxTreeCtrl.DeleteAllItems(self)
607    
608        def __AddLayer(self, before, l):
609            root = self.GetRootItem()
610            id = self.InsertItemBefore(root, before,
611                                l.Title(),
612                                self.mapImageIndex,
613                                self.mapImageIndex)
614    
615            self.SetPyData(id, l)
616            self.__SetVisibilityStyle(l.Visible(), id)
617    
618            self.__FillTreeLayer(id)
619            self.Expand(id)
620    
621            l.Subscribe(LAYER_CHANGED, self._OnMsgLayerChanged)
622            l.Subscribe(LAYER_VISIBILITY_CHANGED,
623                        self._OnMsgLayerVisibilityChanged)
624            l.Subscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)
625    
626        def __RemoveLayer(self, id):
627            self.DeleteChildren(id)
628    
629            layer = self.GetPyData(id)
630            layer.Unsubscribe(LAYER_CHANGED,
631                              self._OnMsgLayerChanged)
632            layer.Unsubscribe(LAYER_VISIBILITY_CHANGED,
633                              self._OnMsgLayerVisibilityChanged)
634            layer.Unsubscribe(TITLE_CHANGED, self._OnMsgLayerTitleChanged)
635    
636            self.Delete(id)
637    
638        def DeleteChildren(self, pid):
639            id, cookie = self.GetFirstChild(pid, 123)
640            while id.IsOk():
641                self.availImgListIndices.append(self.GetItemImage(id))
642                id, cookie = self.GetNextChild(pid, cookie)
643            wxTreeCtrl.DeleteChildren(self, pid)
644    
645        def GetRootItem(self):
646            root = wxTreeCtrl.GetRootItem(self)
647    
648            if not root.IsOk():
649                self.image_list = wxImageList(BMP_SIZE_W, BMP_SIZE_H, False, 0)
650    
651                bmp = wxEmptyBitmap(BMP_SIZE_W, BMP_SIZE_H)
652                dc = wxMemoryDC()
653                dc.SelectObject(bmp)
654                dc.SetBrush(wxBLACK_BRUSH)
655                dc.Clear()
656                dc.SelectObject(wxNullBitmap)
657    
658                self.emptyImageIndex = \
659                    self.image_list.AddWithColourMask(bmp, wxColour(0, 0, 0))
660    
661                bmp = resource.GetBitmapResource("legend_icon_layer",
662                                                  wxBITMAP_TYPE_XPM)
663                self.mapImageIndex = \
664                    self.image_list.Add(bmp)
665    
666                self.AssignImageList(self.image_list)
667                self.availImgListIndices = []
668    
669                root = self.AddRoot("")
670    
671            return root
672    
673      def __SetVisibilityStyle(self, visible, id):      def __SetVisibilityStyle(self, visible, id):
674          font = self.GetItemFont(id)          font = self.GetItemFont(id)
# Line 499  class LegendTree(wxTreeCtrl): Line 685  class LegendTree(wxTreeCtrl):
685          self.SetItemFont(id, font)          self.SetItemFont(id, font)
686                    
687      def __ShowHideLayer(self, layer):      def __ShowHideLayer(self, layer):
688          parent = self.layer2id[layer]          parent = self.find_layer(layer)
689          assert parent.IsOk()          assert parent.IsOk()
690    
691          visible = layer.Visible()          visible = layer.Visible()
# Line 553  class ScaleBarBitmap(wxBoxSizer): Line 739  class ScaleBarBitmap(wxBoxSizer):
739          dc.SelectObject(bmp)          dc.SelectObject(bmp)
740          dc.Clear()          dc.Clear()
741    
742          self.scalebar.DrawScaleBar(scale, dc, (0,0), dc.GetSizeTuple())          if self.canvas.map is not None \
743                and self.canvas.map.projection is not None:
744    
745                # if we are using a projection with geographics coordinates
746                # we need to change the scale value based on where we are
747                # on the globe.
748                if self.canvas.map.projection.GetProjectedUnits() \
749                    == PROJ_UNITS_DEGREES:
750    
751                    width, height = self.canvas.GetSizeTuple()
752                    long, lat = self.canvas.win_to_proj(width/2, height/2)
753    
754                    # slightly inaccurate, but if we are looking at
755                    # the north/south pole we could end up dividing by zero
756                    #
757                    # it shouldn't matter for our purposes that we ignore
758                    # the original sign of lat.
759                    if fabs(lat) > 89.9: lat = 89.9
760    
761                    #
762                    # one degree is about 111,133m at the equator
763                    # we need to adjust that for latitude as
764                    # we move north/south. use the center of the map
765                    # as the point to scale the length to.
766                    #
767                    scale = scale / (111133.0 * fabs(cos(lat * pi/180)))
768    
769                self.scalebar.DrawScaleBar(scale, dc, (0,0), dc.GetSizeTuple())
770    
771          self.scalebarBitmap.SetBitmap(bmp)          self.scalebarBitmap.SetBitmap(bmp)
772    

Legend:
Removed from v.1050  
changed lines
  Added in v.2187

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26