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

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

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

revision 808 by bh, Mon May 5 10:33:47 2003 UTC revision 2032 by bh, Mon Dec 22 14:54:47 2003 UTC
# Line 7  Line 7 
7    
8  __version__ = "$Revision$"  __version__ = "$Revision$"
9    
10    import os.path
11    
12    from Thuban import _
13    
14  from wxPython.wx import *  from wxPython.wx import *
15  from wxPython.grid import *  from wxPython.grid import *
16    
17  from Thuban.Lib.connector import Publisher  from Thuban.Lib.connector import Publisher
18  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
19       FIELDTYPE_STRING       FIELDTYPE_STRING, table_to_dbf, table_to_csv
20  import view  from dialogs import ThubanFrame
21  from dialogs import NonModalDialog  
22  from messages import SHAPES_SELECTED  from messages import SHAPES_SELECTED, SESSION_REPLACED
23    from Thuban.Model.messages import TABLE_REMOVED, MAP_LAYERS_REMOVED
24    from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor
25    
26  wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,  wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,
27                       FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,                       FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,
# Line 23  wx_value_type_map = {FIELDTYPE_INT: wxGR Line 29  wx_value_type_map = {FIELDTYPE_INT: wxGR
29    
30  ROW_SELECTED = "ROW_SELECTED"  ROW_SELECTED = "ROW_SELECTED"
31    
32    QUERY_KEY = 'S'
33    
34  class DataTable(wxPyGridTableBase):  class DataTable(wxPyGridTableBase):
35    
# Line 38  class DataTable(wxPyGridTableBase): Line 45  class DataTable(wxPyGridTableBase):
45    
46      def SetTable(self, table):      def SetTable(self, table):
47          self.table = table          self.table = table
48          self.num_cols = table.field_count()          self.num_cols = table.NumColumns()
49          self.num_rows = table.record_count()          self.num_rows = table.NumRows()
50    
51          self.columns = []          self.columns = []
52          for i in range(self.num_cols):          for i in range(self.num_cols):
53              type, name, len, decc = table.field_info(i)              col = table.Column(i)
54              self.columns.append((name, wx_value_type_map[type], len, decc))              self.columns.append((col.name, wx_value_type_map[col.type]))
55    
56      #      #
57      # required methods for the wxPyGridTableBase interface      # required methods for the wxPyGridTableBase interface
# Line 64  class DataTable(wxPyGridTableBase): Line 71  class DataTable(wxPyGridTableBase):
71      # Renderer understands the type too,) not just strings as in the      # Renderer understands the type too,) not just strings as in the
72      # C++ version.      # C++ version.
73      def GetValue(self, row, col):      def GetValue(self, row, col):
74          record = self.table.read_record(row)          record = self.table.ReadRowAsDict(row, row_is_ordinal = 1)
75          return record[self.columns[col][0]]          return record[self.columns[col][0]]
76    
77      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
# Line 95  class DataTable(wxPyGridTableBase): Line 102  class DataTable(wxPyGridTableBase):
102          return self.CanGetValueAs(row, col, typeName)          return self.CanGetValueAs(row, col, typeName)
103    
104    
105        #
106        def RowIdToOrdinal(self, rowid):
107            """Return the ordinal of the row given by its id"""
108            return self.table.RowIdToOrdinal(rowid)
109    
110        def RowOrdinalToId(self, ordinal):
111            """Return the id of the row given by its ordinal"""
112            return self.table.RowOrdinalToId(ordinal)
113    
114    
115    class NullRenderer(wxPyGridCellRenderer):
116    
117        """Renderer that draws NULL as a gray rectangle
118    
119        Other values are delegated to a normal renderer which is given as
120        the parameter to the constructor.
121        """
122    
123        def __init__(self, non_null_renderer):
124            wxPyGridCellRenderer.__init__(self)
125            self.non_null_renderer = non_null_renderer
126    
127        def Draw(self, grid, attr, dc, rect, row, col, isSelected):
128            value = grid.table.GetValue(row, col)
129            if value is None:
130                dc.SetBackgroundMode(wxSOLID)
131                dc.SetBrush(wxBrush(wxColour(192, 192, 192), wxSOLID))
132                dc.SetPen(wxTRANSPARENT_PEN)
133                dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
134            else:
135                self.non_null_renderer.Draw(grid, attr, dc, rect, row, col,
136                                            isSelected)
137    
138        def GetBestSize(self, grid, attr, dc, row, col):
139            self.non_null_renderer.GetBestSize(grid, attr, dc, row, col)
140    
141        def Clone(self):
142            return NullRenderer(self.non_null_renderer)
143    
144    
145  class TableGrid(wxGrid, Publisher):  class TableGrid(wxGrid, Publisher):
146    
147      """A grid view for a Thuban table      """A grid view for a Thuban table
# Line 112  class TableGrid(wxGrid, Publisher): Line 159  class TableGrid(wxGrid, Publisher):
159    
160          self.allow_messages_count = 0          self.allow_messages_count = 0
161    
162            # keep track of which rows are selected.
163            self.rows = {}
164    
165          self.table = DataTable(table)          self.table = DataTable(table)
166    
167          # The second parameter means that the grid is to take ownership          # The second parameter means that the grid is to take ownership
168          # of the table and will destroy it when done. Otherwise you          # of the table and will destroy it when done. Otherwise you
169          # would need to keep a reference to it and call its Destroy          # would need to keep a reference to it and call its Destroy
170          # method later.          # method later.
171          self.SetTable(self.table, true)          self.SetTable(self.table, True)
172    
173          #self.SetMargins(0,0)          #self.SetMargins(0,0)
174    
# Line 126  class TableGrid(wxGrid, Publisher): Line 176  class TableGrid(wxGrid, Publisher):
176          # column widths automatically but it would cause a traversal of          # column widths automatically but it would cause a traversal of
177          # the entire table which for large .dbf files can take a very          # the entire table which for large .dbf files can take a very
178          # long time.          # long time.
179          #self.AutoSizeColumns(false)          #self.AutoSizeColumns(False)
180    
181          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
182    
183            self.ToggleEventListeners(True)
184          EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)          EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
185          EVT_GRID_SELECT_CELL(self, self.OnSelectCell)          EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
186    
187            # Replace the normal renderers with our own versions which
188            # render NULL/None values specially
189            self.RegisterDataType(wxGRID_VALUE_STRING,
190                                  NullRenderer(wxGridCellStringRenderer()), None)
191            self.RegisterDataType(wxGRID_VALUE_NUMBER,
192                                  NullRenderer(wxGridCellNumberRenderer()), None)
193            self.RegisterDataType(wxGRID_VALUE_FLOAT,
194                                  NullRenderer(wxGridCellFloatRenderer()), None)
195    
196            EVT_WINDOW_DESTROY(self, self.OnDestroy)
197    
198        def OnDestroy(self, event):
199            Publisher.Destroy(self)
200    
201      def SetTableObject(self, table):      def SetTableObject(self, table):
202          self.table.SetTable(table)          self.table.SetTable(table)
203    
204      def OnRangeSelect(self, event):      def OnRangeSelect(self, event):
205          if event.Selecting():          to_id = self.table.RowOrdinalToId
206              self.issue(ROW_SELECTED, event.GetTopLeftCoords().GetRow())          if self.handleSelectEvents:
207                self.rows = dict([(to_id(i), 0) for i in self.GetSelectedRows()])
208    
209                # if we're selecting we need to include the selected range and
210                # make sure that the current row is also included, which may
211                # not be the case if you just click on a single row!
212                if event.Selecting():
213                    for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
214                        self.rows[to_id(i)] = 0
215                    self.rows[to_id(event.GetTopLeftCoords().GetRow())] = 0
216    
217                self.issue(ROW_SELECTED, self.rows.keys())
218    
219            event.Skip()
220    
221      def OnSelectCell(self, event):      def OnSelectCell(self, event):
222          self.issue(ROW_SELECTED, event.GetRow())          to_id = self.table.RowOrdinalToId
223            if self.handleSelectEvents:
224                self.issue(ROW_SELECTED,
225                           [to_id(i) for i in self.GetSelectedRows()])
226            event.Skip()
227    
228        def ToggleEventListeners(self, on):
229            self.handleSelectEvents = on
230    
231        def GetNumberSelected(self):
232            return len(self.rows)
233    
234      def disallow_messages(self):      def disallow_messages(self):
235          """Disallow messages to be send.          """Disallow messages to be send.
# Line 169  class TableGrid(wxGrid, Publisher): Line 257  class TableGrid(wxGrid, Publisher):
257          if self.allow_messages_count == 0:          if self.allow_messages_count == 0:
258              Publisher.issue(self, *args)              Publisher.issue(self, *args)
259    
260        def SelectRowById(self, rowid, do_select):
261            """Select row with the id rowid"""
262            self.SelectRow(self.table.RowIdToOrdinal(rowid), do_select)
263    
264    
265  class LayerTableGrid(TableGrid):  class LayerTableGrid(TableGrid):
266    
# Line 178  class LayerTableGrid(TableGrid): Line 270  class LayerTableGrid(TableGrid):
270      selection is usually coupled to the selected object in the map.      selection is usually coupled to the selected object in the map.
271      """      """
272    
273      def select_shape(self, layer, shape):      def select_shapes(self, layer, shapes):
274          """Select the row corresponding to the specified shape and layer          """Select the row corresponding to the specified shape and layer
275    
276          If layer is not the layer the table is associated with do          If layer is not the layer the table is associated with do
277          nothing. If shape or layer is None also do nothing.          nothing. If shape or layer is None also do nothing.
278          """          """
279          if layer is not None and layer.table is self.table.table \          if layer is not None \
280             and shape is not None:              and layer.ShapeStore().Table() is self.table.table:
281    
282              self.disallow_messages()              self.disallow_messages()
283              try:              try:
284                  self.SelectRow(shape)                  self.ClearSelection()
285                  self.SetGridCursor(shape, 0)                  if len(shapes) > 0:
286                  self.MakeCellVisible(shape, 0)                      # keep track of the lowest id so we can make it the
287                        # first visible item
288                        first = -1
289    
290                        to_ordinal = self.table.RowIdToOrdinal
291                        for shape in shapes:
292                            row = to_ordinal(shape)
293                            self.SelectRow(row, True)
294                            if row < first:
295                                first = row
296    
297                        self.SetGridCursor(first, 0)
298                        self.MakeCellVisible(first, 0)
299              finally:              finally:
300                  self.allow_messages()                  self.allow_messages()
301    
302    
303  class TableFrame(NonModalDialog):  class TableFrame(ThubanFrame):
304    
305      """Frame that displays a Thuban table in a grid view"""      """Frame that displays a Thuban table in a grid view"""
306    
307      def __init__(self, parent, name, title, table):      def __init__(self, parent, name, title, table):
308          NonModalDialog.__init__(self, parent, name, title)          ThubanFrame.__init__(self, parent, name, title)
309            self.panel = wxPanel(self, -1)
310    
311          self.table = table          self.table = table
312          self.grid = self.make_grid(self.table)          self.grid = self.make_grid(self.table)
313            self.app = self.parent.application
314            self.app.Subscribe(SESSION_REPLACED, self.close_on_session_replaced)
315            self.session = self.app.Session()
316            self.session.Subscribe(TABLE_REMOVED, self.close_on_table_removed)
317    
318        def OnDestroy(self, event):
319            """Extend inherited method to unsubscribe messages"""
320            self.app.Unsubscribe(SESSION_REPLACED, self.close_on_session_replaced)
321            self.session.Unsubscribe(TABLE_REMOVED, self.close_on_table_removed)
322            ThubanFrame.OnDestroy(self, event)
323    
324      def make_grid(self, table):      def make_grid(self, table):
325          """Return the table grid to use in the frame.          """Return the table grid to use in the frame.
# Line 212  class TableFrame(NonModalDialog): Line 329  class TableFrame(NonModalDialog):
329          """          """
330          return TableGrid(self, table)          return TableGrid(self, table)
331    
332        def close_on_session_replaced(self, *args):
333            """Subscriber for the SESSION_REPLACED messages.
334    
335            The table frame is tied to a session so close the window when
336            the session changes.
337            """
338            self.Close()
339    
340        def close_on_table_removed(self, table):
341            """Subscriber for the TABLE_REMOVED messages.
342    
343            The table frame is tied to a particular table so close the
344            window when the table is removed.
345            """
346            if table is self.table:
347                self.Close()
348    
349  class LayerTableFrame(TableFrame):  
350    ID_QUERY = 4001
351    ID_EXPORT = 4002
352    ID_COMBOVALUE = 4003
353    ID_EXPORTSEL = 4004
354    
355    class QueryTableFrame(TableFrame):
356    
357        """Frame that displays a table in a grid view and offers user actions
358        selection and export
359    
360        A QueryTableFrame is TableFrame whose selection is connected to the
361        selected object in a map.
362        """
363    
364        def __init__(self, parent, name, title, table):
365            TableFrame.__init__(self, parent, name, title, table)
366    
367            self.combo_fields = wxComboBox(self.panel, -1, style=wxCB_READONLY)
368            self.choice_comp = wxChoice(self.panel, -1,
369                                  choices=["<", "<=", "==", "!=", ">=", ">"])
370            self.combo_value = wxComboBox(self.panel, ID_COMBOVALUE)
371            self.choice_action = wxChoice(self.panel, -1,
372                                    choices=[_("Replace Selection"),
373                                            _("Refine Selection"),
374                                            _("Add to Selection")])
375    
376            button_query = wxButton(self.panel, ID_QUERY, _("Query"))
377            button_export = wxButton(self.panel, ID_EXPORT, _("Export"))
378            button_exportSel = wxButton(self.panel, ID_EXPORTSEL, _("Export Selection"))
379            button_close = wxButton(self.panel, wxID_CLOSE, _("Close"))
380    
381            self.CreateStatusBar()
382    
383            self.grid.SetSize((400, 200))
384    
385            self.combo_value.Append("")
386            for i in range(table.NumColumns()):
387                name = table.Column(i).name
388                self.combo_fields.Append(name)
389                self.combo_value.Append(name)
390    
391            # assume at least one field?
392            self.combo_fields.SetSelection(0)
393            self.combo_value.SetSelection(0)
394            self.choice_action.SetSelection(0)
395            self.choice_comp.SetSelection(0)
396    
397            self.grid.Reparent(self.panel)
398    
399            self.UpdateStatusText()
400    
401            topBox = wxBoxSizer(wxVERTICAL)
402    
403            sizer = wxStaticBoxSizer(wxStaticBox(self.panel, -1,
404                                      _("Selection")),
405                                      wxHORIZONTAL)
406            sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
407            sizer.Add(self.choice_comp, 0, wxALL, 4)
408            sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
409            sizer.Add(self.choice_action, 0, wxALL, 4)
410            sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
411            sizer.Add(40, 20, 0, wxALL, 4)
412    
413            topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
414            topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
415    
416            sizer = wxBoxSizer(wxHORIZONTAL)
417            sizer.Add(button_export, 0, wxALL, 4)
418            sizer.Add(button_exportSel, 0, wxALL, 4)
419            sizer.Add(60, 20, 1, wxALL|wxEXPAND, 4)
420            sizer.Add(button_close, 0, wxALL|wxALIGN_RIGHT, 4)
421            topBox.Add(sizer, 0, wxALL | wxEXPAND, 4)
422    
423            self.panel.SetAutoLayout(True)
424            self.panel.SetSizer(topBox)
425            topBox.Fit(self.panel)
426            topBox.SetSizeHints(self.panel)
427    
428            panelSizer = wxBoxSizer(wxVERTICAL)
429            panelSizer.Add(self.panel, 1, wxEXPAND, 0)
430            self.SetAutoLayout(True)
431            self.SetSizer(panelSizer)
432            panelSizer.Fit(self)
433            panelSizer.SetSizeHints(self)
434    
435            self.grid.SetFocus()
436    
437            EVT_BUTTON(self, ID_QUERY, self.OnQuery)
438            EVT_BUTTON(self, ID_EXPORT, self.OnExport)
439            EVT_BUTTON(self, ID_EXPORTSEL, self.OnExportSel)
440            EVT_BUTTON(self, wxID_CLOSE, self.OnClose)
441            EVT_KEY_DOWN(self.grid, self.OnKeyDown)
442    
443            self.grid.Subscribe(ROW_SELECTED, self.UpdateStatusText)
444    
445        def UpdateStatusText(self, rows=None):
446            self.SetStatusText(_("%i rows (%i selected), %i columns")
447                % (self.grid.GetNumberRows(),
448                   self.grid.GetNumberSelected(),
449                   self.grid.GetNumberCols()))
450    
451        def OnKeyDown(self, event):
452            """Catch query key from grid"""
453            if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
454                self.combo_fields.SetFocus()
455                self.combo_fields.refocus = True
456            else:
457                event.Skip()
458    
459        def OnQuery(self, event):
460            ThubanBeginBusyCursor()
461            try:
462    
463                text = self.combo_value.GetValue()
464                if self.combo_value.GetSelection() < 1 \
465                    or self.combo_value.FindString(text) == -1:
466                    value = text
467                else:
468                    value = self.table.Column(text)
469    
470                ids = self.table.SimpleQuery(
471                        self.table.Column(self.combo_fields.GetStringSelection()),
472                        self.choice_comp.GetStringSelection(),
473                        value)
474    
475                choice = self.choice_action.GetSelection()
476                
477                #
478                # what used to be nice code got became a bit ugly because
479                # each time we select a row a message is sent to the grid
480                # which we are listening for and then we send further
481                # messages.
482                #
483                # now, we disable those listeners select everything but
484                # the first item, reenable the listeners, and select
485                # the first element, which causes everything to be
486                # updated properly.
487                #
488                if ids:
489                    self.grid.ToggleEventListeners(False)
490    
491                if choice == 0:
492                    # Replace Selection
493                    self.grid.ClearSelection()
494                elif choice == 1:
495                    # Refine Selection
496                    sel = self.get_selected()
497                    self.grid.ClearSelection()
498                    ids = filter(sel.has_key, ids)
499                elif choice == 2:
500                    # Add to Selection
501                    pass
502    
503                #
504                # select the rows (all but the first)
505                #
506                firsttime = True
507                for id in ids:
508                    if firsttime:
509                        firsttime = False
510                    else:
511                        self.grid.SelectRowById(id, True)
512    
513                self.grid.ToggleEventListeners(True)
514    
515                #
516                # select the first row
517                #
518                if ids:
519                    self.grid.SelectRowById(ids[0], True)
520    
521            finally:
522                ThubanEndBusyCursor()
523            
524        def doExport(self, onlySelected):
525            
526            dlg = wxFileDialog(self, _("Export Table To"), ".", "",
527                               _("DBF Files (*.dbf)|*.dbf|") +
528                               _("CSV Files (*.csv)|*.csv|") +
529                               _("All Files (*.*)|*.*"),
530                               wxSAVE|wxOVERWRITE_PROMPT)
531            if dlg.ShowModal() == wxID_OK:
532                filename = dlg.GetPath()
533                type = os.path.basename(filename).split('.')[-1:][0]
534                dlg.Destroy()
535    
536                if onlySelected:
537                    records = self.grid.GetSelectedRows()
538                else:
539                    records = None
540    
541                if type.upper() == "DBF":
542                    table_to_dbf(self.table, filename, records)
543                elif type.upper() == 'CSV':
544                    table_to_csv(self.table, filename, records)
545                else:
546                    dlg = wxMessageDialog(None, "Unsupported format: %s" % type,
547                                          "Table Export", wxOK|wxICON_WARNING)
548                    dlg.ShowModal()
549                    dlg.Destroy()
550            else:
551                dlg.Destroy()
552    
553        def OnExport(self, event):
554            self.doExport(False)
555    
556        def OnExportSel(self, event):
557            self.doExport(True)
558    
559        def OnClose(self, event):
560            TableFrame.OnClose(self, event)
561    
562        def get_selected(self):
563            """Return a dictionary of the selected rows.
564    
565            The dictionary has the indexes as keys."""
566            to_id = self.table.RowOrdinalToId
567            return dict([(to_id(i), 0) for i in self.grid.GetSelectedRows()])
568    
569    
570    class LayerTableFrame(QueryTableFrame):
571    
572      """Frame that displays a layer table in a grid view      """Frame that displays a layer table in a grid view
573    
574      A LayerTableFrame is TableFrame whose selection is connected to the      A LayerTableFrame is a QueryTableFrame whose selection is connected to the
575      selected object in a map.      selected object in a map.
576      """      """
577    
578      def __init__(self, parent, name, title, layer, table):      def __init__(self, parent, name, title, layer, table):
579          TableFrame.__init__(self, parent, name, title, table)          QueryTableFrame.__init__(self, parent, name, title, table)
580          self.layer = layer          self.layer = layer
581          self.grid.Subscribe(ROW_SELECTED, self.row_selected)          self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
582          self.parent.Subscribe(SHAPES_SELECTED, self.select_shape)          self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
583            self.map = self.parent.Map()
584            self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
585    
586            # if there is already a selection present, update the grid
587            # accordingly
588            sel = self.get_selected().keys()
589            for i in sel:
590                self.grid.SelectRowById(i, True)
591    
592        def OnDestroy(self, event):
593            """Extend inherited method to unsubscribe messages"""
594            # There's no need to unsubscribe from self.grid's messages
595            # because it will get a DESTROY event too (since destroying the
596            # frame basically means that all child windows are also
597            # destroyed) and this it will clear all subscriptions
598            # automatically.  It may even have been destroyed already (this
599            # does happen on w2000 for instance) so calling any of its
600            # methods here would be an error.
601            self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
602            self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
603            QueryTableFrame.OnDestroy(self, event)
604    
605      def make_grid(self, table):      def make_grid(self, table):
606          """Override the derived method to return a LayerTableGrid.          """Override the derived method to return a LayerTableGrid.
607          """          """
608          return LayerTableGrid(self, table)          return LayerTableGrid(self, table)
609    
610      def OnClose(self, event):      def get_selected(self):
611          self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shape)          """Override the derived method to return a dictionary of the selected
612          TableFrame.OnClose(self, event)          rows.
613            """
614            return dict([(i, 0) for i in self.parent.SelectedShapes()])
615    
616      def select_shape(self, layer, shapes):      def select_shapes(self, layer, shapes):
617          """Subscribed to the SHAPES_SELECTED message.          """Subscribed to the SHAPES_SELECTED message.
618    
619          If shapes contains exactly one shape id, select that shape in          If shapes contains exactly one shape id, select that shape in
620          the grid. Otherwise deselect all.          the grid. Otherwise deselect all.
621          """          """
622          if len(shapes):          self.grid.select_shapes(layer, shapes)
             shape = shapes[0]  
         else:  
             shape = None  
         self.grid.select_shape(layer, shape)  
623    
624      def row_selected(self, row):      def rows_selected(self, rows):
625            """Return the selected rows of the layer as they are returned
626            by Layer.SelectShapes().
627            """
628          if self.layer is not None:          if self.layer is not None:
629              self.parent.SelectShapes(self.layer, [row])              self.parent.SelectShapes(self.layer, rows)
630    
631        def map_layers_removed(self, *args):
632            """Receiver for the map's MAP_LAYERS_REMOVED message
633    
634            Close the dialog if the layer whose table we're showing is not
635            in the map anymore.
636            """
637            if self.layer not in self.map.Layers():
638                self.Close()
639    

Legend:
Removed from v.808  
changed lines
  Added in v.2032

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26