/[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 1394 - (hide annotations)
Thu Jul 10 14:54:58 2003 UTC (21 years, 8 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/tableview.py
File MIME type: text/x-python
File size: 20726 byte(s)
(QueryTableFrame.__init__): Add an
        Export Selection button and move the export buttons underneath
        the table.
(QueryTableFrame.UpdateStatusText): Added event argument so
        that it can respond to grid selection events. The status text
        is now updated even when the table is not associated with a
        layer as was previously assumed.
(QueryTableFrame.OnGridSelectRange, OnGridSelectCell): Removed.
        UpdateStatusText responds to these events.
(QueryTableFrame.OnSaveAs): Renamed to doExport.
(QueryTableFrame.doExport): Helper function that saves the
        entire table, or selected rows, to a file.
(QueryTableFrame.OnExport, QueryTableFrame.OnExportSel): New.
        Respond to export button events and call doExport.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26