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

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/UI/tableview.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2755 - (hide annotations)
Thu Apr 12 09:21:58 2007 UTC (17 years, 10 months ago) by dpinte
File MIME type: text/x-python
File size: 22367 byte(s)
2007-04-12 Didrik Pinte <dpinte@itae.be>

   * Removed workaround for file encoding in the Thuban code


1 bh 535 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with Thuban for details.
7    
8     __version__ = "$Revision$"
9    
10 frank 1027 import os.path
11    
12 jonathan 881 from Thuban import _
13    
14 dpinte 2700 import wx
15     from wx import grid
16 bh 6
17 bh 34 from Thuban.Lib.connector import Publisher
18 bh 6 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
19 frank 1027 FIELDTYPE_STRING, table_to_dbf, table_to_csv
20 jonathan 1199 from dialogs import ThubanFrame
21 bh 6
22 bh 1068 from messages import SHAPES_SELECTED, SESSION_REPLACED
23 bh 1146 from Thuban.Model.messages import TABLE_REMOVED, MAP_LAYERS_REMOVED
24 jonathan 1276 from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor
25 bh 1068
26 dpinte 2700 wx_value_type_map = {FIELDTYPE_INT: grid.GRID_VALUE_NUMBER,
27     FIELDTYPE_DOUBLE: grid.GRID_VALUE_FLOAT,
28     FIELDTYPE_STRING: grid.GRID_VALUE_STRING}
29 bh 6
30 bh 34 ROW_SELECTED = "ROW_SELECTED"
31    
32 frank 979 QUERY_KEY = 'S'
33 bh 34
34 dpinte 2700 class DataTable(grid.PyGridTableBase):
35 bh 6
36 bh 34 """Wrapper around a Thuban table object suitable for a wxGrid"""
37    
38 bh 6 def __init__(self, table = None):
39 dpinte 2700 grid.PyGridTableBase.__init__(self)
40 bh 6 self.num_cols = 0
41     self.num_rows = 0
42     self.columns = []
43     self.table = None
44     self.SetTable(table)
45    
46     def SetTable(self, table):
47     self.table = table
48 bh 838 self.num_cols = table.NumColumns()
49     self.num_rows = table.NumRows()
50 bh 6
51     self.columns = []
52     for i in range(self.num_cols):
53 bh 838 col = table.Column(i)
54     self.columns.append((col.name, wx_value_type_map[col.type]))
55 bh 6
56     #
57     # required methods for the wxPyGridTableBase interface
58     #
59    
60     def GetNumberRows(self):
61     return self.num_rows
62    
63     def GetNumberCols(self):
64     return self.num_cols
65    
66     def IsEmptyCell(self, row, col):
67     return 0
68    
69     # Get/Set values in the table. The Python version of these
70     # methods can handle any data-type, (as long as the Editor and
71     # Renderer understands the type too,) not just strings as in the
72     # C++ version.
73     def GetValue(self, row, col):
74 dpinte 2718 try:
75     record = self.table.ReadRowAsDict(row, row_is_ordinal = 1)
76     except UnicodeError:
77     record = dict()
78     for (key, val) in self.table.ReadRowAsDict(row, \
79     row_is_ordinal = 1).items():
80 dpinte 2755 record[key] = val
81 bh 6 return record[self.columns[col][0]]
82    
83     def SetValue(self, row, col, value):
84     pass
85    
86     #
87     # Some optional methods
88     #
89    
90     # Called when the grid needs to display labels
91     def GetColLabelValue(self, col):
92     return self.columns[col][0]
93    
94     # Called to determine the kind of editor/renderer to use by
95     # default, doesn't necessarily have to be the same type used
96     # nativly by the editor/renderer if they know how to convert.
97     def GetTypeName(self, row, col):
98     return self.columns[col][1]
99    
100     # Called to determine how the data can be fetched and stored by the
101     # editor and renderer. This allows you to enforce some type-safety
102     # in the grid.
103     def CanGetValueAs(self, row, col, typeName):
104     # perhaps we should allow conversion int->double?
105     return self.GetTypeName(row, col) == typeName
106    
107     def CanSetValueAs(self, row, col, typeName):
108     return self.CanGetValueAs(row, col, typeName)
109    
110    
111 bh 1662 #
112     def RowIdToOrdinal(self, rowid):
113     """Return the ordinal of the row given by its id"""
114     return self.table.RowIdToOrdinal(rowid)
115 bh 1092
116 bh 1662 def RowOrdinalToId(self, ordinal):
117     """Return the id of the row given by its ordinal"""
118     return self.table.RowOrdinalToId(ordinal)
119    
120    
121 dpinte 2700 class NullRenderer(grid.PyGridCellRenderer):
122 bh 1092
123     """Renderer that draws NULL as a gray rectangle
124    
125     Other values are delegated to a normal renderer which is given as
126     the parameter to the constructor.
127     """
128    
129     def __init__(self, non_null_renderer):
130 dpinte 2700 grid.PyGridCellRenderer.__init__(self)
131 bh 1092 self.non_null_renderer = non_null_renderer
132    
133     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
134     value = grid.table.GetValue(row, col)
135     if value is None:
136 dpinte 2700 dc.SetBackgroundMode(wx.SOLID)
137     dc.SetBrush(wx.Brush(wx.Colour(192, 192, 192), wx.SOLID))
138     dc.SetPen(wx.TRANSPARENT_PEN)
139 bh 1092 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
140     else:
141     self.non_null_renderer.Draw(grid, attr, dc, rect, row, col,
142     isSelected)
143    
144     def GetBestSize(self, grid, attr, dc, row, col):
145     self.non_null_renderer.GetBestSize(grid, attr, dc, row, col)
146    
147     def Clone(self):
148     return NullRenderer(self.non_null_renderer)
149    
150    
151 dpinte 2700 class TableGrid(grid.Grid, Publisher):
152 bh 6
153 bh 808 """A grid view for a Thuban table
154 bh 6
155 bh 808 When rows are selected by the user the table issues ROW_SELECTED
156     messages. wx sends selection events even when the selection is
157     manipulated by code (instead of by the user) which usually lead to
158     ROW_SELECTED messages being sent in turn. Therefore sending messages
159     can be switched on and off with the allow_messages and
160     disallow_messages methods.
161     """
162    
163 bh 6 def __init__(self, parent, table = None):
164 dpinte 2700 grid.Grid.__init__(self, parent, -1)
165 bh 6
166 bh 808 self.allow_messages_count = 0
167    
168 jonathan 1199 # keep track of which rows are selected.
169     self.rows = {}
170    
171 bh 6 self.table = DataTable(table)
172    
173     # The second parameter means that the grid is to take ownership
174     # of the table and will destroy it when done. Otherwise you
175 bh 77 # would need to keep a reference to it and call its Destroy
176 bh 6 # method later.
177 jan 1035 self.SetTable(self.table, True)
178 bh 6
179     #self.SetMargins(0,0)
180    
181 bh 173 # AutoSizeColumns would allow us to make the grid have optimal
182     # column widths automatically but it would cause a traversal of
183     # the entire table which for large .dbf files can take a very
184     # long time.
185 jan 1035 #self.AutoSizeColumns(False)
186 bh 173
187 dpinte 2700 self.SetSelectionMode(grid.Grid.wxGridSelectRows)
188 bh 278
189 jonathan 966 self.ToggleEventListeners(True)
190 dpinte 2700 self.Bind(grid.EVT_GRID_RANGE_SELECT, self.OnRangeSelect)
191     self.Bind(grid.EVT_GRID_SELECT_CELL, self.OnSelectCell)
192 jonathan 966
193 bh 1092 # Replace the normal renderers with our own versions which
194     # render NULL/None values specially
195 dpinte 2700 self.RegisterDataType(grid.GRID_VALUE_STRING,
196     NullRenderer(grid.GridCellStringRenderer()), None)
197     self.RegisterDataType(grid.GRID_VALUE_NUMBER,
198     NullRenderer(grid.GridCellNumberRenderer()), None)
199     self.RegisterDataType(grid.GRID_VALUE_FLOAT,
200     NullRenderer(grid.GridCellFloatRenderer()), None)
201 bh 1092
202 dpinte 2700 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
203 bh 2032
204     def OnDestroy(self, event):
205     Publisher.Destroy(self)
206    
207 bh 6 def SetTableObject(self, table):
208     self.table.SetTable(table)
209    
210     def OnRangeSelect(self, event):
211 bh 1662 to_id = self.table.RowOrdinalToId
212 jonathan 1199 if self.handleSelectEvents:
213 bh 1662 self.rows = dict([(to_id(i), 0) for i in self.GetSelectedRows()])
214 jonathan 881
215 jonathan 1199 # if we're selecting we need to include the selected range and
216     # make sure that the current row is also included, which may
217     # not be the case if you just click on a single row!
218     if event.Selecting():
219     for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
220 bh 1662 self.rows[to_id(i)] = 0
221     self.rows[to_id(event.GetTopLeftCoords().GetRow())] = 0
222    
223 jonathan 1199 self.issue(ROW_SELECTED, self.rows.keys())
224 bh 6
225 jonathan 881 event.Skip()
226    
227 bh 6 def OnSelectCell(self, event):
228 bh 1662 to_id = self.table.RowOrdinalToId
229 jonathan 1199 if self.handleSelectEvents:
230 bh 1662 self.issue(ROW_SELECTED,
231     [to_id(i) for i in self.GetSelectedRows()])
232 jonathan 881 event.Skip()
233 bh 6
234 jonathan 966 def ToggleEventListeners(self, on):
235 jonathan 1199 self.handleSelectEvents = on
236 bh 1662
237 jonathan 1199 def GetNumberSelected(self):
238     return len(self.rows)
239    
240 bh 808 def disallow_messages(self):
241     """Disallow messages to be send.
242 bh 278
243 bh 808 This method only increases a counter so that calls to
244     disallow_messages and allow_messages can be nested. Only the
245     outermost calls will actually switch message sending on and off.
246     """
247     self.allow_messages_count += 1
248    
249     def allow_messages(self):
250     """Allow messages to be send.
251    
252     This method only decreases a counter so that calls to
253     disallow_messages and allow_messages can be nested. Only the
254     outermost calls will actually switch message sending on and off.
255     """
256     self.allow_messages_count -= 1
257    
258     def issue(self, *args):
259     """Issue a message unless disallowed.
260    
261     See the allow_messages and disallow_messages methods.
262     """
263     if self.allow_messages_count == 0:
264     Publisher.issue(self, *args)
265    
266 bh 1662 def SelectRowById(self, rowid, do_select):
267     """Select row with the id rowid"""
268     self.SelectRow(self.table.RowIdToOrdinal(rowid), do_select)
269 bh 808
270 bh 1662
271 bh 278 class LayerTableGrid(TableGrid):
272    
273     """Table grid for the layer tables.
274    
275     The LayerTableGrid is basically the same as a TableGrid but it's
276     selection is usually coupled to the selected object in the map.
277     """
278    
279 jonathan 881 def select_shapes(self, layer, shapes):
280 bh 278 """Select the row corresponding to the specified shape and layer
281    
282     If layer is not the layer the table is associated with do
283     nothing. If shape or layer is None also do nothing.
284     """
285 jonathan 881 if layer is not None \
286 bh 1219 and layer.ShapeStore().Table() is self.table.table:
287 jonathan 881
288 bh 808 self.disallow_messages()
289     try:
290 jonathan 881 self.ClearSelection()
291     if len(shapes) > 0:
292 bh 1662 # keep track of the lowest id so we can make it the
293     # first visible item
294     first = -1
295 jonathan 881
296 bh 1662 to_ordinal = self.table.RowIdToOrdinal
297 jonathan 881 for shape in shapes:
298 bh 1662 row = to_ordinal(shape)
299     self.SelectRow(row, True)
300     if row < first:
301     first = row
302 jonathan 881
303     self.SetGridCursor(first, 0)
304     self.MakeCellVisible(first, 0)
305 bh 808 finally:
306     self.allow_messages()
307 bh 6
308    
309 jonathan 1199 class TableFrame(ThubanFrame):
310 bh 6
311 bh 34 """Frame that displays a Thuban table in a grid view"""
312    
313 bh 535 def __init__(self, parent, name, title, table):
314 jonathan 1199 ThubanFrame.__init__(self, parent, name, title)
315 dpinte 2700 self.panel = wx.Panel(self, -1)
316 jonathan 1202
317 bh 278 self.table = table
318     self.grid = self.make_grid(self.table)
319 bh 1068 self.app = self.parent.application
320     self.app.Subscribe(SESSION_REPLACED, self.close_on_session_replaced)
321     self.session = self.app.Session()
322     self.session.Subscribe(TABLE_REMOVED, self.close_on_table_removed)
323 bh 278
324 bh 1892 def OnDestroy(self, event):
325     """Extend inherited method to unsubscribe messages"""
326     self.app.Unsubscribe(SESSION_REPLACED, self.close_on_session_replaced)
327     self.session.Unsubscribe(TABLE_REMOVED, self.close_on_table_removed)
328     ThubanFrame.OnDestroy(self, event)
329 jonathan 1202
330 bh 278 def make_grid(self, table):
331     """Return the table grid to use in the frame.
332    
333     The default implementation returns a TableGrid instance.
334     Override in derived classes to use different grid classes.
335     """
336     return TableGrid(self, table)
337    
338 bh 1068 def close_on_session_replaced(self, *args):
339     """Subscriber for the SESSION_REPLACED messages.
340    
341     The table frame is tied to a session so close the window when
342     the session changes.
343     """
344     self.Close()
345    
346     def close_on_table_removed(self, table):
347     """Subscriber for the TABLE_REMOVED messages.
348    
349     The table frame is tied to a particular table so close the
350     window when the table is removed.
351     """
352     if table is self.table:
353     self.Close()
354    
355    
356 jonathan 881 ID_QUERY = 4001
357 jan 1013 ID_EXPORT = 4002
358 jonathan 1199 ID_COMBOVALUE = 4003
359 jonathan 1394 ID_EXPORTSEL = 4004
360 jonathan 881
361 jan 1013 class QueryTableFrame(TableFrame):
362 bh 278
363 jan 1013 """Frame that displays a table in a grid view and offers user actions
364     selection and export
365 bh 278
366 jan 1096 A QueryTableFrame is TableFrame whose selection is connected to the
367 bh 278 selected object in a map.
368     """
369    
370 jan 1013 def __init__(self, parent, name, title, table):
371 bh 535 TableFrame.__init__(self, parent, name, title, table)
372 bh 30
373 dpinte 2700 self.combo_fields = wx.ComboBox(self.panel, -1, style=wx.CB_READONLY)
374     self.choice_comp = wx.Choice(self.panel, -1,
375 jonathan 939 choices=["<", "<=", "==", "!=", ">=", ">"])
376 dpinte 2700 self.combo_value = wx.ComboBox(self.panel, ID_COMBOVALUE)
377     self.choice_action = wx.Choice(self.panel, -1,
378     choices=[_("Replace Selection"),
379     _("Refine Selection"),
380 jonathan 881 _("Add to Selection")])
381    
382 dpinte 2700 button_query = wx.Button(self.panel, ID_QUERY, _("Query"))
383     button_export = wx.Button(self.panel, ID_EXPORT, _("Export"))
384     button_exportSel = wx.Button(self.panel, ID_EXPORTSEL, _("Export Selection"))
385     button_close = wx.Button(self.panel, wx.ID_CLOSE, _("Close"))
386 jonathan 881
387 jonathan 1199 self.CreateStatusBar()
388    
389 jonathan 881 self.grid.SetSize((400, 200))
390    
391     self.combo_value.Append("")
392 jonathan 939 for i in range(table.NumColumns()):
393     name = table.Column(i).name
394 jonathan 881 self.combo_fields.Append(name)
395     self.combo_value.Append(name)
396 jonathan 939
397 jonathan 881 # assume at least one field?
398     self.combo_fields.SetSelection(0)
399     self.combo_value.SetSelection(0)
400 jonathan 1199 self.choice_action.SetSelection(0)
401     self.choice_comp.SetSelection(0)
402 jonathan 881
403 jonathan 1202 self.grid.Reparent(self.panel)
404    
405     self.UpdateStatusText()
406    
407 dpinte 2700 topBox = wx.BoxSizer(wx.VERTICAL)
408 jonathan 881
409 dpinte 2700 sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, -1,
410 frank 1066 _("Selection")),
411 dpinte 2700 wx.HORIZONTAL)
412     sizer.Add(self.combo_fields, 1, wx.EXPAND|wx.ALL, 4)
413     sizer.Add(self.choice_comp, 0, wx.ALL, 4)
414     sizer.Add(self.combo_value, 1, wx.EXPAND|wx.ALL, 4)
415     sizer.Add(self.choice_action, 0, wx.ALL, 4)
416     sizer.Add(button_query, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4)
417     sizer.Add( (40, 20), 0, wx.ALL, 4)
418 jonathan 881
419 dpinte 2700 topBox.Add(sizer, 0, wx.EXPAND|wx.ALL, 4)
420     topBox.Add(self.grid, 1, wx.EXPAND|wx.ALL, 0)
421 jonathan 881
422 dpinte 2700 sizer = wx.BoxSizer(wx.HORIZONTAL)
423     sizer.Add(button_export, 0, wx.ALL, 4)
424     sizer.Add(button_exportSel, 0, wx.ALL, 4)
425     sizer.Add( (60, 20), 1, wx.ALL|wx.EXPAND, 4)
426     sizer.Add(button_close, 0, wx.ALL|wx.ALIGN_RIGHT, 4)
427     topBox.Add(sizer, 0, wx.ALL | wx.EXPAND, 4)
428 jonathan 1394
429 jonathan 1202 self.panel.SetAutoLayout(True)
430     self.panel.SetSizer(topBox)
431     topBox.Fit(self.panel)
432     topBox.SetSizeHints(self.panel)
433    
434 dpinte 2700 panelSizer = wx.BoxSizer(wx.VERTICAL)
435     panelSizer.Add(self.panel, 1, wx.EXPAND, 0)
436 jonathan 881 self.SetAutoLayout(True)
437 jonathan 1202 self.SetSizer(panelSizer)
438     panelSizer.Fit(self)
439     panelSizer.SetSizeHints(self)
440 jonathan 881
441 frank 979 self.grid.SetFocus()
442 jonathan 1202
443 dpinte 2700 self.Bind(wx.EVT_BUTTON, self.OnQuery, id=ID_QUERY)
444     self.Bind(wx.EVT_BUTTON, self.OnExport, id=ID_EXPORT)
445     self.Bind(wx.EVT_BUTTON, self.OnExportSel, id=ID_EXPORTSEL)
446     self.Bind(wx.EVT_BUTTON, self.OnClose, id=wx.ID_CLOSE)
447     self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown, self.grid)
448 jonathan 881
449 jonathan 1394 self.grid.Subscribe(ROW_SELECTED, self.UpdateStatusText)
450    
451     def UpdateStatusText(self, rows=None):
452 dpinte 2700 self.SetStatusText(_("%i rows (%i selected), %i columns")
453     % (self.grid.GetNumberRows(),
454 jonathan 1199 self.grid.GetNumberSelected(),
455     self.grid.GetNumberCols()))
456    
457 frank 979 def OnKeyDown(self, event):
458     """Catch query key from grid"""
459     if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
460     self.combo_fields.SetFocus()
461     self.combo_fields.refocus = True
462     else:
463     event.Skip()
464    
465 jonathan 881 def OnQuery(self, event):
466 jonathan 1276 ThubanBeginBusyCursor()
467 jonathan 1104 try:
468 jonathan 881
469 jonathan 1199 text = self.combo_value.GetValue()
470     if self.combo_value.GetSelection() < 1 \
471     or self.combo_value.FindString(text) == -1:
472     value = text
473 jonathan 1104 else:
474 jonathan 1199 value = self.table.Column(text)
475 jonathan 881
476 jonathan 1104 ids = self.table.SimpleQuery(
477 dpinte 2700 self.table.Column(self.combo_fields.GetStringSelection()),
478     self.choice_comp.GetStringSelection(),
479 jonathan 1104 value)
480 jonathan 881
481 jonathan 1104 choice = self.choice_action.GetSelection()
482 dpinte 2700
483 jonathan 1104 #
484     # what used to be nice code got became a bit ugly because
485     # each time we select a row a message is sent to the grid
486     # which we are listening for and then we send further
487     # messages.
488     #
489     # now, we disable those listeners select everything but
490     # the first item, reenable the listeners, and select
491     # the first element, which causes everything to be
492     # updated properly.
493     #
494 jonathan 1199 if ids:
495     self.grid.ToggleEventListeners(False)
496 jonathan 966
497 jonathan 1104 if choice == 0:
498     # Replace Selection
499     self.grid.ClearSelection()
500     elif choice == 1:
501     # Refine Selection
502     sel = self.get_selected()
503     self.grid.ClearSelection()
504     ids = filter(sel.has_key, ids)
505     elif choice == 2:
506     # Add to Selection
507     pass
508 jonathan 966
509 jonathan 1104 #
510     # select the rows (all but the first)
511     #
512     firsttime = True
513     for id in ids:
514     if firsttime:
515     firsttime = False
516     else:
517 bh 1662 self.grid.SelectRowById(id, True)
518 jonathan 881
519 jonathan 1104 self.grid.ToggleEventListeners(True)
520 jonathan 966
521 jonathan 1104 #
522     # select the first row
523     #
524     if ids:
525 bh 1662 self.grid.SelectRowById(ids[0], True)
526 jonathan 1199
527 jonathan 1104 finally:
528 jonathan 1276 ThubanEndBusyCursor()
529 dpinte 2700
530 jonathan 1394 def doExport(self, onlySelected):
531 dpinte 2700
532     dlg = wx.FileDialog(self, _("Export Table To"), ".", "",
533 jan 1013 _("DBF Files (*.dbf)|*.dbf|") +
534     _("CSV Files (*.csv)|*.csv|") +
535     _("All Files (*.*)|*.*"),
536 dpinte 2700 wx.SAVE|wx.OVERWRITE_PROMPT)
537     if dlg.ShowModal() == wx.ID_OK:
538 frank 1027 filename = dlg.GetPath()
539     type = os.path.basename(filename).split('.')[-1:][0]
540     dlg.Destroy()
541 jonathan 1394
542     if onlySelected:
543     records = self.grid.GetSelectedRows()
544     else:
545     records = None
546    
547 frank 1027 if type.upper() == "DBF":
548 jonathan 1394 table_to_dbf(self.table, filename, records)
549 frank 1027 elif type.upper() == 'CSV':
550 jonathan 1394 table_to_csv(self.table, filename, records)
551 frank 1027 else:
552 dpinte 2700 dlg = wx.MessageDialog(None, "Unsupported format: %s" % type,
553     "Table Export", wx.OK|wx.ICON_WARNING)
554 frank 1027 dlg.ShowModal()
555     dlg.Destroy()
556     else:
557     dlg.Destroy()
558 jonathan 881
559 jonathan 1394 def OnExport(self, event):
560     self.doExport(False)
561    
562     def OnExportSel(self, event):
563     self.doExport(True)
564    
565 jan 1013 def OnClose(self, event):
566     TableFrame.OnClose(self, event)
567    
568     def get_selected(self):
569     """Return a dictionary of the selected rows.
570 bh 1662
571 jonathan 1394 The dictionary has the indexes as keys."""
572 bh 1662 to_id = self.table.RowOrdinalToId
573     return dict([(to_id(i), 0) for i in self.grid.GetSelectedRows()])
574 jan 1013
575 bh 1662
576 jan 1013 class LayerTableFrame(QueryTableFrame):
577    
578     """Frame that displays a layer table in a grid view
579    
580     A LayerTableFrame is a QueryTableFrame whose selection is connected to the
581     selected object in a map.
582     """
583    
584     def __init__(self, parent, name, title, layer, table):
585     QueryTableFrame.__init__(self, parent, name, title, table)
586     self.layer = layer
587     self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
588     self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
589 bh 1146 self.map = self.parent.Map()
590     self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
591 jan 1013
592 jan 1032 # if there is already a selection present, update the grid
593     # accordingly
594     sel = self.get_selected().keys()
595     for i in sel:
596 bh 1662 self.grid.SelectRowById(i, True)
597 jan 1032
598 bh 1892 def OnDestroy(self, event):
599     """Extend inherited method to unsubscribe messages"""
600 bh 2032 # There's no need to unsubscribe from self.grid's messages
601     # because it will get a DESTROY event too (since destroying the
602     # frame basically means that all child windows are also
603     # destroyed) and this it will clear all subscriptions
604     # automatically. It may even have been destroyed already (this
605     # does happen on w2000 for instance) so calling any of its
606     # methods here would be an error.
607 bh 1892 self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
608     self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
609     QueryTableFrame.OnDestroy(self, event)
610    
611 bh 278 def make_grid(self, table):
612     """Override the derived method to return a LayerTableGrid.
613     """
614     return LayerTableGrid(self, table)
615    
616 jan 1013 def get_selected(self):
617     """Override the derived method to return a dictionary of the selected
618     rows.
619     """
620     return dict([(i, 0) for i in self.parent.SelectedShapes()])
621    
622 jonathan 881 def select_shapes(self, layer, shapes):
623 bh 535 """Subscribed to the SHAPES_SELECTED message.
624    
625     If shapes contains exactly one shape id, select that shape in
626     the grid. Otherwise deselect all.
627     """
628 jonathan 881 self.grid.select_shapes(layer, shapes)
629 bh 34
630 jonathan 881 def rows_selected(self, rows):
631 jan 1013 """Return the selected rows of the layer as they are returned
632     by Layer.SelectShapes().
633     """
634 bh 34 if self.layer is not None:
635 jonathan 881 self.parent.SelectShapes(self.layer, rows)
636 bh 1146
637     def map_layers_removed(self, *args):
638     """Receiver for the map's MAP_LAYERS_REMOVED message
639    
640     Close the dialog if the layer whose table we're showing is not
641     in the map anymore.
642     """
643     if self.layer not in self.map.Layers():
644     self.Close()
645    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26