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

Contents of /branches/WIP-pyshapelib-bramz/Thuban/UI/tree.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2700 - (show annotations)
Mon Sep 18 14:27:02 2006 UTC (18 years, 5 months ago) by dpinte
Original Path: trunk/thuban/Thuban/UI/tree.py
File MIME type: text/x-python
File size: 8809 byte(s)
2006-09-18 Didrik Pinte <dpinte@itae.be>
    
        * wxPython 2.6 update : wx 2.4 syntax has been updated to 2.6


1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Jan-Oliver Wagner <[email protected]>
4 # Bernhard Herzog <[email protected]>
5 #
6 # This program is free software under the GPL (>=v2)
7 # Read the file COPYING coming with Thuban for details.
8
9 __version__ = "$Revision$"
10
11 from types import StringType, UnicodeType
12
13 import wx
14
15 from Thuban import _
16 from Thuban.UI.common import Color2wxColour
17
18 from Thuban.Model.color import Color
19
20 from Thuban.Model.messages import CHANGED
21 from Thuban.Model.layer import Layer
22 from Thuban.Model.map import Map
23
24 from dialogs import NonModalNonParentDialog
25 from messages import SESSION_REPLACED, LAYER_SELECTED
26
27 BMP_SIZE = 15
28
29 class SessionTreeCtrl(wx.TreeCtrl):
30
31 """Widget to display a tree view of the session.
32
33 The tree view is created recursively from the session object. The
34 tree view calls the session's TreeInfo method which should return a
35 pair (<title>, <item>) where <title> ist the title of the session
36 item in the tree view and <items> is a list of objects to use as the
37 children of the session in the tree view.
38
39 The items list can contain three types of items:
40
41 1. a string. The string is used as the title for a leaf item in
42 the tree view.
43
44 2. an object with a TreeInfo method. This method is called and
45 should return a pair just like the session's TreeInfo method.
46
47 3. a pair (<title>, <item>) which is treated like the return
48 value of TreeInfo.
49 """
50
51 def __init__(self, parent, ID, mainwindow, app):
52
53 # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
54 wx.TreeCtrl.__init__(self, parent, ID)
55
56 self.mainwindow = mainwindow
57 self.app = app
58 # boolean to indicate that we manipulate the selection ourselves
59 # so that we can ignore the selection events generated
60 self.changing_selection = 0
61
62 # Dictionary mapping layer id's to tree items
63 self.layer_to_item = {}
64
65 self.app.Subscribe(SESSION_REPLACED, self.session_changed)
66 self.mainwindow.Subscribe(LAYER_SELECTED, self.layer_selected)
67
68 # the session currently displayed in the tree
69 self.session = None
70
71
72 # pretend the session has changed to build the initial tree
73 self.session_changed()
74
75 self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=self.GetId())
76
77 def unsubscribe_all(self):
78 if self.session is not None:
79 self.session.Unsubscribe(CHANGED, self.update_tree)
80 self.session = None
81 self.app.Unsubscribe(SESSION_REPLACED, self.session_changed)
82 self.mainwindow.Unsubscribe(LAYER_SELECTED, self.layer_selected)
83
84 def update_tree(self, *args):
85 """Clear and rebuild the tree"""
86 self.DeleteAllItems()
87 self.layer_to_item.clear()
88 self.image_list = wx.ImageList(BMP_SIZE, BMP_SIZE, False, 0)
89
90 bmp = wx.EmptyBitmap(BMP_SIZE, BMP_SIZE)
91 dc = wx.MemoryDC()
92 dc.SelectObject(bmp)
93 dc.SetBrush(wx.BLACK_BRUSH)
94 dc.Clear()
95 dc.SelectObject(wx.NullBitmap)
96
97 self.emptyImageIndex = \
98 self.image_list.AddWithColourMask(bmp, wx.Colour(0, 0, 0))
99
100 self.AssignImageList(self.image_list)
101
102 session = self.app.session
103 info = session.TreeInfo()
104 root = self.AddRoot(info[0], -1, -1, None)
105 self.SetItemImage(root, self.emptyImageIndex)
106 self.add_items(root, info[1])
107 self.Expand(root)
108 # select the selected layer
109 selected_layer = self.mainwindow.current_layer()
110 if selected_layer is not None:
111 # One would expect that the selected_layer's id is in
112 # layer_to_item at this point as we've just rebuilt that
113 # mapping completely. However, when a new session is loaded
114 # for instance, it can happen that the tree view is updated
115 # before the canvas's selection in which case selected_layer
116 # may be a layer of the old session.
117 item = self.layer_to_item.get(id(selected_layer))
118 if item is not None:
119 self.SelectItem(item)
120
121 def add_items(self, parent, items):
122
123 if items is None: return
124
125 for item in items:
126 if hasattr(item, "TreeInfo"):
127 # Supports the TreeInfo protocol
128 info = item.TreeInfo()
129 treeitem = self.AppendItem(parent, info[0], -1, -1, None)
130 self.SetItemImage(treeitem, self.emptyImageIndex)
131 self.SetPyData(treeitem, item)
132 self.add_items(treeitem, info[1])
133 self.Expand(treeitem)
134 if isinstance(item, Layer):
135 self.layer_to_item[id(item)] = treeitem
136 elif isinstance(item, StringType) or \
137 isinstance(item, UnicodeType):
138 # it's a string
139 treeitem = self.AppendItem(parent, item, -1, -1, None)
140 self.SetItemImage(treeitem, self.emptyImageIndex)
141 else:
142 # assume its a sequence (title, items)
143 if isinstance(item[1], Color):
144
145 treeitem = self.AppendItem(parent, "(%s)" % item[0])
146
147 bmp = wx.EmptyBitmap(BMP_SIZE, BMP_SIZE)
148 brush = wx.Brush(Color2wxColour(item[1]), wx.SOLID)
149 dc = wx.MemoryDC()
150 dc.SelectObject(bmp)
151 dc.SetBrush(brush)
152 dc.Clear()
153 dc.DrawRoundedRectangle(0, 0,
154 bmp.GetWidth(), bmp.GetHeight(),
155 4)
156 dc.SelectObject(wx.NullBitmap)
157
158 i = self.image_list.Add(bmp)
159 self.SetItemImage(treeitem, i)
160 else:
161 treeitem = self.AppendItem(parent, item[0], -1, -1, None)
162 self.SetItemImage(treeitem, self.emptyImageIndex)
163 self.add_items(treeitem, item[1])
164 self.Expand(treeitem)
165
166 def session_changed(self, *args):
167 new_session = self.app.session
168 # if the session has changed subscribe/unsubscribe
169 if self.session is not new_session:
170 if self.session is not None:
171 self.session.Unsubscribe(CHANGED, self.update_tree)
172 if new_session is not None:
173 new_session.Subscribe(CHANGED, self.update_tree)
174 self.session = new_session
175 self.update_tree()
176
177 def normalize_selection(self):
178 """Select the layer or map containing currently selected item"""
179 item = self.GetSelection()
180 while item.IsOk():
181 object = self.GetPyData(item)
182 if isinstance(object, Layer) or isinstance(object, Map):
183 break
184 item = self.GetItemParent(item)
185 else:
186 # No layer or map was found in the chain of parents, so
187 # there's nothing we can do.
188 return
189
190 self.changing_selection = 1
191 try:
192 self.SelectItem(item)
193 finally:
194 self.changing_selection = 0
195
196 def SelectedLayer(self):
197 """Return the layer object currently selected in the tree.
198 Return None if no layer is selected"""
199 layer = self.GetPyData(self.GetSelection())
200 if isinstance(layer, Layer):
201 return layer
202 return None
203
204 def OnSelChanged(self, event):
205 if self.changing_selection:
206 # we're changing the selection ourselves (probably through
207 # self.normalize_selection(). ignore the event.
208 return
209 self.normalize_selection()
210 # SelectedLayer returns None if no layer is selected. Since
211 # passing None to SelectLayer deselects the layer we can simply
212 # pass the result of SelectedLayer on in all cases
213 self.mainwindow.SelectLayer(self.SelectedLayer())
214
215 def layer_selected(self, layer):
216 item = self.layer_to_item.get(id(layer))
217 if item is not None and item != self.GetSelection():
218 self.SelectItem(item)
219
220
221 class SessionTreeView(NonModalNonParentDialog):
222
223 """Non modal dialog showing the session as a tree"""
224
225 def __init__(self, parent, app, name):
226 NonModalNonParentDialog.__init__(self, parent, name, _("Session"))
227 self.tree = SessionTreeCtrl(self, -1, parent, app)
228
229 def OnClose(self, event):
230 NonModalNonParentDialog.OnClose(self, event)
231
232 # if there were a way to get notified when the tree control
233 # itself is destroyed we could use that to unsubscribe instead
234 # of doing it here. (EVT_WINDOW_DESTROY doesn't seem to sent at
235 # all)
236 self.tree.unsubscribe_all()
237

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26