/[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 535 by bh, Fri Mar 14 20:42:18 2003 UTC revision 1892 by bh, Thu Oct 30 18:16:53 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
148    
149        When rows are selected by the user the table issues ROW_SELECTED
150        messages. wx sends selection events even when the selection is
151        manipulated by code (instead of by the user) which usually lead to
152        ROW_SELECTED messages being sent in turn. Therefore sending messages
153        can be switched on and off with the allow_messages and
154        disallow_messages methods.
155        """
156    
157      def __init__(self, parent, table = None):      def __init__(self, parent, table = None):
158          wxGrid.__init__(self, parent, -1)          wxGrid.__init__(self, parent, -1)
159    
160            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 116  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      def SetTableObject(self, table):      def SetTableObject(self, table):
197          self.table.SetTable(table)          self.table.SetTable(table)
198    
199      def OnRangeSelect(self, event):      def OnRangeSelect(self, event):
200          if event.Selecting():          to_id = self.table.RowOrdinalToId
201              self.issue(ROW_SELECTED, event.GetTopLeftCoords().GetRow())          if self.handleSelectEvents:
202                self.rows = dict([(to_id(i), 0) for i in self.GetSelectedRows()])
203    
204                # if we're selecting we need to include the selected range and
205                # make sure that the current row is also included, which may
206                # not be the case if you just click on a single row!
207                if event.Selecting():
208                    for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
209                        self.rows[to_id(i)] = 0
210                    self.rows[to_id(event.GetTopLeftCoords().GetRow())] = 0
211    
212                self.issue(ROW_SELECTED, self.rows.keys())
213    
214            event.Skip()
215    
216      def OnSelectCell(self, event):      def OnSelectCell(self, event):
217          self.issue(ROW_SELECTED, event.GetRow())          to_id = self.table.RowOrdinalToId
218            if self.handleSelectEvents:
219                self.issue(ROW_SELECTED,
220                           [to_id(i) for i in self.GetSelectedRows()])
221            event.Skip()
222    
223        def ToggleEventListeners(self, on):
224            self.handleSelectEvents = on
225    
226        def GetNumberSelected(self):
227            return len(self.rows)
228    
229        def disallow_messages(self):
230            """Disallow messages to be send.
231    
232            This method only increases a counter so that calls to
233            disallow_messages and allow_messages can be nested. Only the
234            outermost calls will actually switch message sending on and off.
235            """
236            self.allow_messages_count += 1
237    
238        def allow_messages(self):
239            """Allow messages to be send.
240    
241            This method only decreases a counter so that calls to
242            disallow_messages and allow_messages can be nested. Only the
243            outermost calls will actually switch message sending on and off.
244            """
245            self.allow_messages_count -= 1
246    
247        def issue(self, *args):
248            """Issue a message unless disallowed.
249    
250            See the allow_messages and disallow_messages methods.
251            """
252            if self.allow_messages_count == 0:
253                Publisher.issue(self, *args)
254    
255        def SelectRowById(self, rowid, do_select):
256            """Select row with the id rowid"""
257            self.SelectRow(self.table.RowIdToOrdinal(rowid), do_select)
258    
259    
260  class LayerTableGrid(TableGrid):  class LayerTableGrid(TableGrid):
# Line 142  class LayerTableGrid(TableGrid): Line 265  class LayerTableGrid(TableGrid):
265      selection is usually coupled to the selected object in the map.      selection is usually coupled to the selected object in the map.
266      """      """
267    
268      def select_shape(self, layer, shape):      def select_shapes(self, layer, shapes):
269          """Select the row corresponding to the specified shape and layer          """Select the row corresponding to the specified shape and layer
270    
271          If layer is not the layer the table is associated with do          If layer is not the layer the table is associated with do
272          nothing. If shape or layer is None also do nothing.          nothing. If shape or layer is None also do nothing.
273          """          """
274          if layer is not None and layer.table is self.table.table \          if layer is not None \
275             and shape is not None:              and layer.ShapeStore().Table() is self.table.table:
             self.SelectRow(shape)  
             self.SetGridCursor(shape, 0)  
             self.MakeCellVisible(shape, 0)  
276    
277                self.disallow_messages()
278                try:
279                    self.ClearSelection()
280                    if len(shapes) > 0:
281                        # keep track of the lowest id so we can make it the
282                        # first visible item
283                        first = -1
284    
285                        to_ordinal = self.table.RowIdToOrdinal
286                        for shape in shapes:
287                            row = to_ordinal(shape)
288                            self.SelectRow(row, True)
289                            if row < first:
290                                first = row
291    
292                        self.SetGridCursor(first, 0)
293                        self.MakeCellVisible(first, 0)
294                finally:
295                    self.allow_messages()
296    
297  class TableFrame(NonModalDialog):  
298    class TableFrame(ThubanFrame):
299    
300      """Frame that displays a Thuban table in a grid view"""      """Frame that displays a Thuban table in a grid view"""
301    
302      def __init__(self, parent, name, title, table):      def __init__(self, parent, name, title, table):
303          NonModalDialog.__init__(self, parent, name, title)          ThubanFrame.__init__(self, parent, name, title)
304            self.panel = wxPanel(self, -1)
305    
306          self.table = table          self.table = table
307          self.grid = self.make_grid(self.table)          self.grid = self.make_grid(self.table)
308            self.app = self.parent.application
309            self.app.Subscribe(SESSION_REPLACED, self.close_on_session_replaced)
310            self.session = self.app.Session()
311            self.session.Subscribe(TABLE_REMOVED, self.close_on_table_removed)
312    
313        def OnDestroy(self, event):
314            """Extend inherited method to unsubscribe messages"""
315            self.app.Unsubscribe(SESSION_REPLACED, self.close_on_session_replaced)
316            self.session.Unsubscribe(TABLE_REMOVED, self.close_on_table_removed)
317            ThubanFrame.OnDestroy(self, event)
318    
319      def make_grid(self, table):      def make_grid(self, table):
320          """Return the table grid to use in the frame.          """Return the table grid to use in the frame.
# Line 172  class TableFrame(NonModalDialog): Line 324  class TableFrame(NonModalDialog):
324          """          """
325          return TableGrid(self, table)          return TableGrid(self, table)
326    
327        def close_on_session_replaced(self, *args):
328            """Subscriber for the SESSION_REPLACED messages.
329    
330            The table frame is tied to a session so close the window when
331            the session changes.
332            """
333            self.Close()
334    
335        def close_on_table_removed(self, table):
336            """Subscriber for the TABLE_REMOVED messages.
337    
338            The table frame is tied to a particular table so close the
339            window when the table is removed.
340            """
341            if table is self.table:
342                self.Close()
343    
344    
345    ID_QUERY = 4001
346    ID_EXPORT = 4002
347    ID_COMBOVALUE = 4003
348    ID_EXPORTSEL = 4004
349    
350    class QueryTableFrame(TableFrame):
351    
352        """Frame that displays a table in a grid view and offers user actions
353        selection and export
354    
355        A QueryTableFrame is TableFrame whose selection is connected to the
356        selected object in a map.
357        """
358    
359        def __init__(self, parent, name, title, table):
360            TableFrame.__init__(self, parent, name, title, table)
361    
362            self.combo_fields = wxComboBox(self.panel, -1, style=wxCB_READONLY)
363            self.choice_comp = wxChoice(self.panel, -1,
364                                  choices=["<", "<=", "==", "!=", ">=", ">"])
365            self.combo_value = wxComboBox(self.panel, ID_COMBOVALUE)
366            self.choice_action = wxChoice(self.panel, -1,
367                                    choices=[_("Replace Selection"),
368                                            _("Refine Selection"),
369                                            _("Add to Selection")])
370    
371            button_query = wxButton(self.panel, ID_QUERY, _("Query"))
372            button_export = wxButton(self.panel, ID_EXPORT, _("Export"))
373            button_exportSel = wxButton(self.panel, ID_EXPORTSEL, _("Export Selection"))
374            button_close = wxButton(self.panel, wxID_CLOSE, _("Close"))
375    
376            self.CreateStatusBar()
377    
378            self.grid.SetSize((400, 200))
379    
380            self.combo_value.Append("")
381            for i in range(table.NumColumns()):
382                name = table.Column(i).name
383                self.combo_fields.Append(name)
384                self.combo_value.Append(name)
385    
386            # assume at least one field?
387            self.combo_fields.SetSelection(0)
388            self.combo_value.SetSelection(0)
389            self.choice_action.SetSelection(0)
390            self.choice_comp.SetSelection(0)
391    
392            self.grid.Reparent(self.panel)
393    
394            self.UpdateStatusText()
395    
396            topBox = wxBoxSizer(wxVERTICAL)
397    
398            sizer = wxStaticBoxSizer(wxStaticBox(self.panel, -1,
399                                      _("Selection")),
400                                      wxHORIZONTAL)
401            sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
402            sizer.Add(self.choice_comp, 0, wxALL, 4)
403            sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
404            sizer.Add(self.choice_action, 0, wxALL, 4)
405            sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
406            sizer.Add(40, 20, 0, wxALL, 4)
407    
408            topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
409            topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
410    
411            sizer = wxBoxSizer(wxHORIZONTAL)
412            sizer.Add(button_export, 0, wxALL, 4)
413            sizer.Add(button_exportSel, 0, wxALL, 4)
414            sizer.Add(60, 20, 1, wxALL|wxEXPAND, 4)
415            sizer.Add(button_close, 0, wxALL|wxALIGN_RIGHT, 4)
416            topBox.Add(sizer, 0, wxALL | wxEXPAND, 4)
417    
418            self.panel.SetAutoLayout(True)
419            self.panel.SetSizer(topBox)
420            topBox.Fit(self.panel)
421            topBox.SetSizeHints(self.panel)
422    
423            panelSizer = wxBoxSizer(wxVERTICAL)
424            panelSizer.Add(self.panel, 1, wxEXPAND, 0)
425            self.SetAutoLayout(True)
426            self.SetSizer(panelSizer)
427            panelSizer.Fit(self)
428            panelSizer.SetSizeHints(self)
429    
430            self.grid.SetFocus()
431    
432            EVT_BUTTON(self, ID_QUERY, self.OnQuery)
433            EVT_BUTTON(self, ID_EXPORT, self.OnExport)
434            EVT_BUTTON(self, ID_EXPORTSEL, self.OnExportSel)
435            EVT_BUTTON(self, wxID_CLOSE, self.OnClose)
436            EVT_KEY_DOWN(self.grid, self.OnKeyDown)
437    
438            self.grid.Subscribe(ROW_SELECTED, self.UpdateStatusText)
439    
440        def UpdateStatusText(self, rows=None):
441            self.SetStatusText(_("%i rows (%i selected), %i columns")
442                % (self.grid.GetNumberRows(),
443                   self.grid.GetNumberSelected(),
444                   self.grid.GetNumberCols()))
445    
446        def OnKeyDown(self, event):
447            """Catch query key from grid"""
448            if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
449                self.combo_fields.SetFocus()
450                self.combo_fields.refocus = True
451            else:
452                event.Skip()
453    
454        def OnQuery(self, event):
455            ThubanBeginBusyCursor()
456            try:
457    
458                text = self.combo_value.GetValue()
459                if self.combo_value.GetSelection() < 1 \
460                    or self.combo_value.FindString(text) == -1:
461                    value = text
462                else:
463                    value = self.table.Column(text)
464    
465                ids = self.table.SimpleQuery(
466                        self.table.Column(self.combo_fields.GetStringSelection()),
467                        self.choice_comp.GetStringSelection(),
468                        value)
469    
470                choice = self.choice_action.GetSelection()
471                
472                #
473                # what used to be nice code got became a bit ugly because
474                # each time we select a row a message is sent to the grid
475                # which we are listening for and then we send further
476                # messages.
477                #
478                # now, we disable those listeners select everything but
479                # the first item, reenable the listeners, and select
480                # the first element, which causes everything to be
481                # updated properly.
482                #
483                if ids:
484                    self.grid.ToggleEventListeners(False)
485    
486                if choice == 0:
487                    # Replace Selection
488                    self.grid.ClearSelection()
489                elif choice == 1:
490                    # Refine Selection
491                    sel = self.get_selected()
492                    self.grid.ClearSelection()
493                    ids = filter(sel.has_key, ids)
494                elif choice == 2:
495                    # Add to Selection
496                    pass
497    
498                #
499                # select the rows (all but the first)
500                #
501                firsttime = True
502                for id in ids:
503                    if firsttime:
504                        firsttime = False
505                    else:
506                        self.grid.SelectRowById(id, True)
507    
508                self.grid.ToggleEventListeners(True)
509    
510                #
511                # select the first row
512                #
513                if ids:
514                    self.grid.SelectRowById(ids[0], True)
515    
516            finally:
517                ThubanEndBusyCursor()
518            
519        def doExport(self, onlySelected):
520            
521            dlg = wxFileDialog(self, _("Export Table To"), ".", "",
522                               _("DBF Files (*.dbf)|*.dbf|") +
523                               _("CSV Files (*.csv)|*.csv|") +
524                               _("All Files (*.*)|*.*"),
525                               wxSAVE|wxOVERWRITE_PROMPT)
526            if dlg.ShowModal() == wxID_OK:
527                filename = dlg.GetPath()
528                type = os.path.basename(filename).split('.')[-1:][0]
529                dlg.Destroy()
530    
531                if onlySelected:
532                    records = self.grid.GetSelectedRows()
533                else:
534                    records = None
535    
536                if type.upper() == "DBF":
537                    table_to_dbf(self.table, filename, records)
538                elif type.upper() == 'CSV':
539                    table_to_csv(self.table, filename, records)
540                else:
541                    dlg = wxMessageDialog(None, "Unsupported format: %s" % type,
542                                          "Table Export", wxOK|wxICON_WARNING)
543                    dlg.ShowModal()
544                    dlg.Destroy()
545            else:
546                dlg.Destroy()
547    
548        def OnExport(self, event):
549            self.doExport(False)
550    
551  class LayerTableFrame(TableFrame):      def OnExportSel(self, event):
552            self.doExport(True)
553    
554        def OnClose(self, event):
555            TableFrame.OnClose(self, event)
556    
557        def get_selected(self):
558            """Return a dictionary of the selected rows.
559    
560            The dictionary has the indexes as keys."""
561            to_id = self.table.RowOrdinalToId
562            return dict([(to_id(i), 0) for i in self.grid.GetSelectedRows()])
563    
564    
565    class LayerTableFrame(QueryTableFrame):
566    
567      """Frame that displays a layer table in a grid view      """Frame that displays a layer table in a grid view
568    
569      A LayerTableFrame is TableFrame whose selection is connected to the      A LayerTableFrame is a QueryTableFrame whose selection is connected to the
570      selected object in a map.      selected object in a map.
571      """      """
572    
573      def __init__(self, parent, name, title, layer, table):      def __init__(self, parent, name, title, layer, table):
574          TableFrame.__init__(self, parent, name, title, table)          QueryTableFrame.__init__(self, parent, name, title, table)
575          self.layer = layer          self.layer = layer
576          self.grid.Subscribe(ROW_SELECTED, self.row_selected)          self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
577          self.parent.Subscribe(SHAPES_SELECTED, self.select_shape)          self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
578            self.map = self.parent.Map()
579            self.map.Subscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
580    
581            # if there is already a selection present, update the grid
582            # accordingly
583            sel = self.get_selected().keys()
584            for i in sel:
585                self.grid.SelectRowById(i, True)
586    
587        def OnDestroy(self, event):
588            """Extend inherited method to unsubscribe messages"""
589            self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
590            self.grid.Unsubscribe(ROW_SELECTED, self.rows_selected)
591            self.map.Unsubscribe(MAP_LAYERS_REMOVED, self.map_layers_removed)
592            QueryTableFrame.OnDestroy(self, event)
593    
594      def make_grid(self, table):      def make_grid(self, table):
595          """Override the derived method to return a LayerTableGrid.          """Override the derived method to return a LayerTableGrid.
596          """          """
597          return LayerTableGrid(self, table)          return LayerTableGrid(self, table)
598    
599      def OnClose(self, event):      def get_selected(self):
600          self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shape)          """Override the derived method to return a dictionary of the selected
601          TableFrame.OnClose(self, event)          rows.
602            """
603            return dict([(i, 0) for i in self.parent.SelectedShapes()])
604    
605      def select_shape(self, layer, shapes):      def select_shapes(self, layer, shapes):
606          """Subscribed to the SHAPES_SELECTED message.          """Subscribed to the SHAPES_SELECTED message.
607    
608          If shapes contains exactly one shape id, select that shape in          If shapes contains exactly one shape id, select that shape in
609          the grid. Otherwise deselect all.          the grid. Otherwise deselect all.
610          """          """
611          if len(shapes):          self.grid.select_shapes(layer, shapes)
             shape = shapes[0]  
         else:  
             shape = None  
         self.grid.select_shape(layer, shape)  
612    
613      def row_selected(self, row):      def rows_selected(self, rows):
614            """Return the selected rows of the layer as they are returned
615            by Layer.SelectShapes().
616            """
617          if self.layer is not None:          if self.layer is not None:
618              self.parent.SelectShapes(self.layer, [row])              self.parent.SelectShapes(self.layer, rows)
619    
620        def map_layers_removed(self, *args):
621            """Receiver for the map's MAP_LAYERS_REMOVED message
622    
623            Close the dialog if the layer whose table we're showing is not
624            in the map anymore.
625            """
626            if self.layer not in self.map.Layers():
627                self.Close()
628    

Legend:
Removed from v.535  
changed lines
  Added in v.1892

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26