/[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 6 by bh, Tue Aug 28 15:41:52 2001 UTC revision 979 by frank, Thu May 22 11:40:59 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  #  #
# Line 7  Line 7 
7    
8  __version__ = "$Revision$"  __version__ = "$Revision$"
9    
10    from Thuban import _
11    
12  from wxPython.wx import *  from wxPython.wx import *
13  from wxPython.grid import *  from wxPython.grid import *
14    
15    from Thuban.Lib.connector import Publisher
16  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
17       FIELDTYPE_STRING       FIELDTYPE_STRING
18  import view  import view
19  from messages import SELECTED_SHAPE  from dialogs import NonModalDialog
20    from messages import SHAPES_SELECTED
21    
22  wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,  wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,
23                       FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,                       FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,
24                       FIELDTYPE_STRING: wxGRID_VALUE_STRING}                       FIELDTYPE_STRING: wxGRID_VALUE_STRING}
25    
26    ROW_SELECTED = "ROW_SELECTED"
27    
28    QUERY_KEY = 'S'
29    
30  class DataTable(wxPyGridTableBase):  class DataTable(wxPyGridTableBase):
31    
32        """Wrapper around a Thuban table object suitable for a wxGrid"""
33    
34      def __init__(self, table = None):      def __init__(self, table = None):
35          wxPyGridTableBase.__init__(self)          wxPyGridTableBase.__init__(self)
36          self.num_cols = 0          self.num_cols = 0
# Line 31  class DataTable(wxPyGridTableBase): Line 41  class DataTable(wxPyGridTableBase):
41    
42      def SetTable(self, table):      def SetTable(self, table):
43          self.table = table          self.table = table
44          self.num_cols = table.field_count()          self.num_cols = table.NumColumns()
45          self.num_rows = table.record_count()          self.num_rows = table.NumRows()
46    
47          self.columns = []          self.columns = []
48          for i in range(self.num_cols):          for i in range(self.num_cols):
49              type, name, len, decc = table.field_info(i)              col = table.Column(i)
50              self.columns.append((name, wx_value_type_map[type], len, decc))              self.columns.append((col.name, wx_value_type_map[col.type]))
51    
52      #      #
53      # required methods for the wxPyGridTableBase interface      # required methods for the wxPyGridTableBase interface
# Line 57  class DataTable(wxPyGridTableBase): Line 67  class DataTable(wxPyGridTableBase):
67      # Renderer understands the type too,) not just strings as in the      # Renderer understands the type too,) not just strings as in the
68      # C++ version.      # C++ version.
69      def GetValue(self, row, col):      def GetValue(self, row, col):
70          record = self.table.read_record(row)          record = self.table.ReadRowAsDict(row)
71          return record[self.columns[col][0]]          return record[self.columns[col][0]]
72    
73      def SetValue(self, row, col, value):      def SetValue(self, row, col, value):
# Line 88  class DataTable(wxPyGridTableBase): Line 98  class DataTable(wxPyGridTableBase):
98          return self.CanGetValueAs(row, col, typeName)          return self.CanGetValueAs(row, col, typeName)
99    
100    
101      # Thuban stuff  class TableGrid(wxGrid, Publisher):
     def SelectRow(self, row):  
         import main  
         interactor = main.app.interactor  
         return interactor.SelectShape(self.table, row)  
   
   
102    
103        """A grid view for a Thuban table
104    
105        When rows are selected by the user the table issues ROW_SELECTED
106  class TableGrid(wxGrid):      messages. wx sends selection events even when the selection is
107        manipulated by code (instead of by the user) which usually lead to
108        ROW_SELECTED messages being sent in turn. Therefore sending messages
109        can be switched on and off with the allow_messages and
110        disallow_messages methods.
111        """
112    
113      def __init__(self, parent, table = None):      def __init__(self, parent, table = None):
114          wxGrid.__init__(self, parent, -1)          wxGrid.__init__(self, parent, -1)
115    
116            self.allow_messages_count = 0
117    
118          self.table = DataTable(table)          self.table = DataTable(table)
119    
120          # The second parameter means that the grid is to take ownership          # The second parameter means that the grid is to take ownership
121          # of the table and will destroy it when done. Otherwise you          # of the table and will destroy it when done. Otherwise you
122          # would need to keep a reference to it and call it's Destroy          # would need to keep a reference to it and call its Destroy
123          # method later.          # method later.
124          self.SetTable(self.table, true)          self.SetTable(self.table, true)
125    
126          #self.SetMargins(0,0)          #self.SetMargins(0,0)
127          self.AutoSizeColumns(false)  
128            # AutoSizeColumns would allow us to make the grid have optimal
129            # column widths automatically but it would cause a traversal of
130            # the entire table which for large .dbf files can take a very
131            # long time.
132            #self.AutoSizeColumns(false)
133    
134          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
135            
136          EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)          #EVT_GRID_RANGE_SELECT(self, None)
137          EVT_GRID_SELECT_CELL(self, self.OnSelectCell)          #EVT_GRID_SELECT_CELL(self, None)
138          import main          #EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
139          main.app.interactor.Subscribe(SELECTED_SHAPE, self.select_shape)          #EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
140    
141            self.ToggleEventListeners(True)
142    
143      def SetTableObject(self, table):      def SetTableObject(self, table):
144          self.table.SetTable(table)          self.table.SetTable(table)
145    
146      def OnRangeSelect(self, event):      def OnRangeSelect(self, event):
147            rows = dict([(i, 0) for i in self.GetSelectedRows()])
148    
149            # if we're selecting we need to include the selected range and
150            # make sure that the current row is also included, which may
151            # not be the case if you just click on a single row!
152          if event.Selecting():          if event.Selecting():
153              if not self.table.SelectRow(event.GetTopLeftCoords().GetRow()):              for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
154                  event.Skip()                  rows[i] = 0
155                rows[event.GetTopLeftCoords().GetRow()] = 0
156    
157            self.issue(ROW_SELECTED, rows.keys())
158            event.Skip()
159    
160      def OnSelectCell(self, event):      def OnSelectCell(self, event):
161          if not self.table.SelectRow(event.GetRow()):          self.issue(ROW_SELECTED, self.GetSelectedRows())
162            event.Skip()
163    
164        def ToggleEventListeners(self, on):
165            if on:
166                EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
167                EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
168            else:
169                EVT_GRID_RANGE_SELECT(self, None)
170                EVT_GRID_SELECT_CELL(self, None)
171                
172        def disallow_messages(self):
173            """Disallow messages to be send.
174    
175            This method only increases a counter so that calls to
176            disallow_messages and allow_messages can be nested. Only the
177            outermost calls will actually switch message sending on and off.
178            """
179            self.allow_messages_count += 1
180    
181        def allow_messages(self):
182            """Allow messages to be send.
183    
184            This method only decreases a counter so that calls to
185            disallow_messages and allow_messages can be nested. Only the
186            outermost calls will actually switch message sending on and off.
187            """
188            self.allow_messages_count -= 1
189    
190        def issue(self, *args):
191            """Issue a message unless disallowed.
192    
193            See the allow_messages and disallow_messages methods.
194            """
195            if self.allow_messages_count == 0:
196                Publisher.issue(self, *args)
197    
198    
199    class LayerTableGrid(TableGrid):
200    
201        """Table grid for the layer tables.
202    
203        The LayerTableGrid is basically the same as a TableGrid but it's
204        selection is usually coupled to the selected object in the map.
205        """
206    
207        def select_shapes(self, layer, shapes):
208            """Select the row corresponding to the specified shape and layer
209    
210            If layer is not the layer the table is associated with do
211            nothing. If shape or layer is None also do nothing.
212            """
213            if layer is not None \
214                and layer.table is self.table.table:
215    
216                self.disallow_messages()
217                try:
218                    self.ClearSelection()
219                    if len(shapes) > 0:
220                        #
221                        # keep track of the lowest id so we can make it
222                        # the first visible item
223                        #
224                        first = shapes[0]
225    
226                        for shape in shapes:
227                            self.SelectRow(shape, True)
228                            if shape < first:
229                                first = shape
230    
231                        self.SetGridCursor(first, 0)
232                        self.MakeCellVisible(first, 0)
233                finally:
234                    self.allow_messages()
235    
236    
237    class TableFrame(NonModalDialog):
238    
239        """Frame that displays a Thuban table in a grid view"""
240    
241        def __init__(self, parent, name, title, table):
242            NonModalDialog.__init__(self, parent, name, title)
243            self.table = table
244            self.grid = self.make_grid(self.table)
245    
246        def make_grid(self, table):
247            """Return the table grid to use in the frame.
248    
249            The default implementation returns a TableGrid instance.
250            Override in derived classes to use different grid classes.
251            """
252            return TableGrid(self, table)
253    
254    
255    ID_QUERY = 4001
256    ID_SAVEAS = 4002
257    
258    class LayerTableFrame(TableFrame):
259    
260        """Frame that displays a layer table in a grid view
261    
262        A LayerTableFrame is TableFrame whose selection is connected to the
263        selected object in a map.
264        """
265    
266        def __init__(self, parent, name, title, layer, table):
267            TableFrame.__init__(self, parent, name, title, table)
268            self.layer = layer
269            self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
270            self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
271    
272            self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)
273            self.choice_comp = wxChoice(self, -1,
274                                  choices=["<", "<=", "==", "!=", ">=", ">"])
275            self.combo_value = wxComboBox(self, -1)
276            self.choice_action = wxChoice(self, -1,
277                                    choices=[_("Replace Selection"),
278                                            _("Refine Selection"),
279                                            _("Add to Selection")])
280    
281            button_query = wxButton(self, ID_QUERY, _("Query"))
282            button_saveas = wxButton(self, ID_SAVEAS, _("Export"))
283    
284            self.grid.SetSize((400, 200))
285    
286            self.combo_value.Append("")
287            for i in range(table.NumColumns()):
288                name = table.Column(i).name
289                self.combo_fields.Append(name)
290                self.combo_value.Append(name)
291    
292            # assume at least one field?
293            self.combo_fields.SetSelection(0)
294            self.combo_value.SetSelection(0)
295    
296            topBox = wxBoxSizer(wxVERTICAL)
297    
298            sizer = wxStaticBoxSizer(wxStaticBox(self, -1, _("Selections")),
299                                      wxHORIZONTAL)
300            sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
301            sizer.Add(self.choice_comp, 0, wxALL, 4)
302            sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
303            sizer.Add(self.choice_action, 0, wxALL, 4)
304            sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
305            sizer.Add(40, 20, 0, wxALL, 4)
306            sizer.Add(button_saveas, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
307    
308            topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
309            topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
310    
311            self.SetAutoLayout(True)
312            self.SetSizer(topBox)
313            topBox.Fit(self)
314            topBox.SetSizeHints(self)
315    
316            self.grid.SetFocus()
317            EVT_BUTTON(self, ID_QUERY, self.OnQuery)
318            EVT_BUTTON(self, ID_SAVEAS, self.OnSaveAs)
319            EVT_KEY_DOWN(self.grid, self.OnKeyDown)
320    
321        def OnKeyDown(self, event):
322            """Catch query key from grid"""
323            print "In OnKeyDown"
324            if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
325                print "Got the Key!"
326                self.combo_fields.SetFocus()
327                self.combo_fields.refocus = True
328            else:
329              event.Skip()              event.Skip()
330    
     def select_shape(self, layer, shape):  
         if layer is not None and layer.table is self.table.table:  
             self.SelectRow(shape)  
             self.SetGridCursor(shape, 0)  
             self.MakeCellVisible(shape, 0)  
331    
332        def OnQuery(self, event):
333            wxBeginBusyCursor()
334    
335  class TableFrame(wxFrame):          if self.combo_value.GetSelection() < 1:
336                value = self.combo_value.GetValue()
337            else:
338                value = self.table.Column(self.combo_value.GetValue())
339    
340            ids = self.table.SimpleQuery(
341                    self.table.Column(self.combo_fields.GetStringSelection()),
342                    self.choice_comp.GetStringSelection(),
343                    value)
344    
345            choice = self.choice_action.GetSelection()
346                
347            #
348            # what used to be nice code got became a bit ugly because
349            # each time we select a row a message is sent to the grid
350            # which we are listening for and then we send further
351            # messages.
352            #
353            # now, we disable those listeners select everything but
354            # the first item, reenable the listeners, and select
355            # the first element, which causes everything to be
356            # updated properly.
357            #
358            self.grid.ToggleEventListeners(False)
359    
360            if choice == 0:
361                # Replace Selection
362                self.grid.ClearSelection()
363            elif choice == 1:
364                # Refine Selection
365                sel = dict([(i, 0) for i in self.parent.SelectedShapes()])
366                self.grid.ClearSelection()
367                ids = filter(sel.has_key, ids)
368            elif choice == 2:
369                # Add to Selection
370                pass
371    
372            #
373            # select the rows (all but the first)
374            #
375            firsttime = True
376            for id in ids:
377                if firsttime:
378                    firsttime = False
379                else:
380                    self.grid.SelectRow(id, True)
381    
382            self.grid.ToggleEventListeners(True)
383    
384            #
385            # select the first row
386            #
387            if ids:
388                self.grid.SelectRow(ids[0], True)
389    
390      def __init__(self, parent, table = None):          wxEndBusyCursor()
391          wxFrame.__init__(self, parent, -1, "Thuban Table")          
392          grid = TableGrid(self, table)      def OnSaveAs(self, event):
393            dlg = wxFileDialog(self, _("Save Table As"), ".", "",
394                               "DBF Files (*.dbf)|*.dbf|" +
395                               "CSV Files (*.csv)|*.csv|" +
396                               "All Files (*.*)|*.*",
397                               wxSAVE|wxOVERWRITE_PROMPT)
398            if dlg.ShowModal() == wxID_OK:
399                pass
400                                                                                    
401            dlg.Destroy()
402    
403        def make_grid(self, table):
404            """Override the derived method to return a LayerTableGrid.
405            """
406            return LayerTableGrid(self, table)
407    
408        def OnClose(self, event):
409            self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
410            TableFrame.OnClose(self, event)
411    
412        def select_shapes(self, layer, shapes):
413            """Subscribed to the SHAPES_SELECTED message.
414    
415            If shapes contains exactly one shape id, select that shape in
416            the grid. Otherwise deselect all.
417            """
418            self.grid.select_shapes(layer, shapes)
419    
420        def rows_selected(self, rows):
421            if self.layer is not None:
422                self.parent.SelectShapes(self.layer, rows)

Legend:
Removed from v.6  
changed lines
  Added in v.979

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26