/[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 77 by bh, Mon Feb 4 19:27:13 2002 UTC revision 979 by frank, Thu May 22 11:40:59 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 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    
# Line 15  from Thuban.Model.table import FIELDTYPE Line 17  from Thuban.Model.table import FIELDTYPE
17       FIELDTYPE_STRING       FIELDTYPE_STRING
18  import view  import view
19  from dialogs import NonModalDialog  from dialogs import NonModalDialog
20  from messages import SELECTED_SHAPE  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,
# Line 23  wx_value_type_map = {FIELDTYPE_INT: wxGR Line 25  wx_value_type_map = {FIELDTYPE_INT: wxGR
25    
26  ROW_SELECTED = "ROW_SELECTED"  ROW_SELECTED = "ROW_SELECTED"
27    
28    QUERY_KEY = 'S'
29    
30  class DataTable(wxPyGridTableBase):  class DataTable(wxPyGridTableBase):
31    
# Line 38  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 64  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 97  class DataTable(wxPyGridTableBase): Line 100  class DataTable(wxPyGridTableBase):
100    
101  class TableGrid(wxGrid, Publisher):  class TableGrid(wxGrid, Publisher):
102    
103      """A grid view for a Thuban table"""      """A grid view for a Thuban table
104    
105        When rows are selected by the user the table issues ROW_SELECTED
106        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
# Line 111  class TableGrid(wxGrid, Publisher): Line 124  class TableGrid(wxGrid, Publisher):
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            #EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
139            #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              self.issue(ROW_SELECTED, event.GetTopLeftCoords().GetRow())              for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
154                    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          self.issue(ROW_SELECTED, event.GetRow())          self.issue(ROW_SELECTED, self.GetSelectedRows())
162            event.Skip()
163    
164      def select_shape(self, layer, shape):      def ToggleEventListeners(self, on):
165          if layer is not None and layer.table is self.table.table \          if on:
166             and shape is not None:              EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
167              self.SelectRow(shape)              EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
168              self.SetGridCursor(shape, 0)          else:
169              self.MakeCellVisible(shape, 0)              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):  class TableFrame(NonModalDialog):
238    
239      """Frame that displays a Thuban table in a grid view"""      """Frame that displays a Thuban table in a grid view"""
240    
241      def __init__(self, parent, interactor, name, title, layer = None,      def __init__(self, parent, name, title, table):
242                   table = None):          NonModalDialog.__init__(self, parent, name, title)
         NonModalDialog.__init__(self, parent, interactor, name, title)  
         self.layer = layer  
243          self.table = table          self.table = table
244          self.grid = TableGrid(self, table)          self.grid = self.make_grid(self.table)
245          self.grid.Subscribe(ROW_SELECTED, self.row_selected)  
246          self.interactor.Subscribe(SELECTED_SHAPE, self.select_shape)      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()
330    
331    
332        def OnQuery(self, event):
333            wxBeginBusyCursor()
334    
335            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            wxEndBusyCursor()
391            
392        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):      def OnClose(self, event):
409          self.interactor.Unsubscribe(SELECTED_SHAPE, self.select_shape)          self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
410          NonModalDialog.OnClose(self, event)          TableFrame.OnClose(self, event)
411    
412        def select_shapes(self, layer, shapes):
413            """Subscribed to the SHAPES_SELECTED message.
414    
415      def select_shape(self, layer, shape):          If shapes contains exactly one shape id, select that shape in
416          self.grid.select_shape(layer, shape)          the grid. Otherwise deselect all.
417            """
418            self.grid.select_shapes(layer, shapes)
419    
420      def row_selected(self, row):      def rows_selected(self, rows):
421          if self.layer is not None:          if self.layer is not None:
422              self.interactor.SelectLayerAndShape(self.layer, row)              self.parent.SelectShapes(self.layer, rows)

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26