/[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 2718 - (hide annotations)
Fri Jan 5 23:43:49 2007 UTC (18 years, 2 months ago) by dpinte
Original Path: trunk/thuban/Thuban/UI/tableview.py
File MIME type: text/x-python
File size: 22493 byte(s)
2007-01-06 Didrik Pinte <dpinte@itae.be>

       UTF-8 locales support reading non utf-8 files. This is a workaround and
       not a real bugfix. See
       http://wald.intevation.org/tracker/index.php?func=detail&aid=118 for
       more details

       * Thuban/UI/
       tableview.py, controls.py, baserenderer.py, view.py: decode text from
       iso-8859-1 encoding.



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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26