/[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 881 by jonathan, Fri May 9 16:34:15 2003 UTC revision 1104 by jonathan, Fri May 30 06:29:54 2003 UTC
# Line 7  Line 7 
7    
8  __version__ = "$Revision$"  __version__ = "$Revision$"
9    
10    import os.path
11    
12  from Thuban import _  from Thuban import _
13    
14  from wxPython.wx import *  from wxPython.wx import *
# Line 14  from wxPython.grid import * Line 16  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  import view
21  from dialogs import NonModalDialog  from dialogs import NonModalNonParentDialog
22  from messages import SHAPES_SELECTED  
23    from messages import SHAPES_SELECTED, SESSION_REPLACED
24    from Thuban.Model.messages import TABLE_REMOVED
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 25  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 97  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    class NullRenderer(wxPyGridCellRenderer):
107    
108        """Renderer that draws NULL as a gray rectangle
109    
110        Other values are delegated to a normal renderer which is given as
111        the parameter to the constructor.
112        """
113    
114        def __init__(self, non_null_renderer):
115            wxPyGridCellRenderer.__init__(self)
116            self.non_null_renderer = non_null_renderer
117    
118        def Draw(self, grid, attr, dc, rect, row, col, isSelected):
119            value = grid.table.GetValue(row, col)
120            if value is None:
121                dc.SetBackgroundMode(wxSOLID)
122                dc.SetBrush(wxBrush(wxColour(192, 192, 192), wxSOLID))
123                dc.SetPen(wxTRANSPARENT_PEN)
124                dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
125            else:
126                self.non_null_renderer.Draw(grid, attr, dc, rect, row, col,
127                                            isSelected)
128    
129        def GetBestSize(self, grid, attr, dc, row, col):
130            self.non_null_renderer.GetBestSize(grid, attr, dc, row, col)
131    
132        def Clone(self):
133            return NullRenderer(self.non_null_renderer)
134    
135    
136  class TableGrid(wxGrid, Publisher):  class TableGrid(wxGrid, Publisher):
137    
138      """A grid view for a Thuban table      """A grid view for a Thuban table
# Line 120  class TableGrid(wxGrid, Publisher): Line 156  class TableGrid(wxGrid, Publisher):
156          # of the table and will destroy it when done. Otherwise you          # of the table and will destroy it when done. Otherwise you
157          # would need to keep a reference to it and call its Destroy          # would need to keep a reference to it and call its Destroy
158          # method later.          # method later.
159          self.SetTable(self.table, true)          self.SetTable(self.table, True)
160    
161          #self.SetMargins(0,0)          #self.SetMargins(0,0)
162    
# Line 128  class TableGrid(wxGrid, Publisher): Line 164  class TableGrid(wxGrid, Publisher):
164          # column widths automatically but it would cause a traversal of          # column widths automatically but it would cause a traversal of
165          # the entire table which for large .dbf files can take a very          # the entire table which for large .dbf files can take a very
166          # long time.          # long time.
167          #self.AutoSizeColumns(false)          #self.AutoSizeColumns(False)
168    
169          self.SetSelectionMode(wxGrid.wxGridSelectRows)          self.SetSelectionMode(wxGrid.wxGridSelectRows)
170    
171          EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)          self.ToggleEventListeners(True)
172          EVT_GRID_SELECT_CELL(self, self.OnSelectCell)  
173            # Replace the normal renderers with our own versions which
174            # render NULL/None values specially
175            self.RegisterDataType(wxGRID_VALUE_STRING,
176                                  NullRenderer(wxGridCellStringRenderer()), None)
177            self.RegisterDataType(wxGRID_VALUE_NUMBER,
178                                  NullRenderer(wxGridCellNumberRenderer()), None)
179            self.RegisterDataType(wxGRID_VALUE_FLOAT,
180                                  NullRenderer(wxGridCellFloatRenderer()), None)
181    
182      def SetTableObject(self, table):      def SetTableObject(self, table):
183          self.table.SetTable(table)          self.table.SetTable(table)
# Line 156  class TableGrid(wxGrid, Publisher): Line 200  class TableGrid(wxGrid, Publisher):
200          self.issue(ROW_SELECTED, self.GetSelectedRows())          self.issue(ROW_SELECTED, self.GetSelectedRows())
201          event.Skip()          event.Skip()
202    
203        def ToggleEventListeners(self, on):
204            if on:
205                EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
206                EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
207            else:
208                EVT_GRID_RANGE_SELECT(self, None)
209                EVT_GRID_SELECT_CELL(self, None)
210                
211      def disallow_messages(self):      def disallow_messages(self):
212          """Disallow messages to be send.          """Disallow messages to be send.
213    
# Line 221  class LayerTableGrid(TableGrid): Line 273  class LayerTableGrid(TableGrid):
273                  self.allow_messages()                  self.allow_messages()
274    
275    
276  class TableFrame(NonModalDialog):  class TableFrame(NonModalNonParentDialog):
277    
278      """Frame that displays a Thuban table in a grid view"""      """Frame that displays a Thuban table in a grid view"""
279    
280      def __init__(self, parent, name, title, table):      def __init__(self, parent, name, title, table):
281          NonModalDialog.__init__(self, parent, name, title)          NonModalNonParentDialog.__init__(self, parent, name, title)
282          self.table = table          self.table = table
283          self.grid = self.make_grid(self.table)          self.grid = self.make_grid(self.table)
284            self.app = self.parent.application
285            self.app.Subscribe(SESSION_REPLACED, self.close_on_session_replaced)
286            self.session = self.app.Session()
287            self.session.Subscribe(TABLE_REMOVED, self.close_on_table_removed)
288    
289      def make_grid(self, table):      def make_grid(self, table):
290          """Return the table grid to use in the frame.          """Return the table grid to use in the frame.
# Line 238  class TableFrame(NonModalDialog): Line 294  class TableFrame(NonModalDialog):
294          """          """
295          return TableGrid(self, table)          return TableGrid(self, table)
296    
297        def OnClose(self, event):
298            self.app.Unsubscribe(SESSION_REPLACED, self.close_on_session_replaced)
299            self.session.Unsubscribe(TABLE_REMOVED, self.close_on_table_removed)
300            NonModalNonParentDialog.OnClose(self, event)
301    
302        def close_on_session_replaced(self, *args):
303            """Subscriber for the SESSION_REPLACED messages.
304    
305            The table frame is tied to a session so close the window when
306            the session changes.
307            """
308            self.Close()
309    
310        def close_on_table_removed(self, table):
311            """Subscriber for the TABLE_REMOVED messages.
312    
313            The table frame is tied to a particular table so close the
314            window when the table is removed.
315            """
316            if table is self.table:
317                self.Close()
318    
319    
320  ID_QUERY = 4001  ID_QUERY = 4001
321  ID_SAVEAS = 4002  ID_EXPORT = 4002
322    
323  class LayerTableFrame(TableFrame):  class QueryTableFrame(TableFrame):
324    
325      """Frame that displays a layer table in a grid view      """Frame that displays a table in a grid view and offers user actions
326        selection and export
327    
328      A LayerTableFrame is TableFrame whose selection is connected to the      A QueryTableFrame is TableFrame whose selection is connected to the
329      selected object in a map.      selected object in a map.
330      """      """
331    
332      def __init__(self, parent, name, title, layer, table):      def __init__(self, parent, name, title, table):
333          TableFrame.__init__(self, parent, name, title, table)          TableFrame.__init__(self, parent, name, title, table)
         self.layer = layer  
         self.grid.Subscribe(ROW_SELECTED, self.rows_selected)  
         self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)  
334    
335          self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)          self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)
336          self.choice_comp = wxChoice(self, -1,          self.choice_comp = wxChoice(self, -1,
337                                choices=["<", "<=", "=", "<>", ">=", ">"])                                choices=["<", "<=", "==", "!=", ">=", ">"])
338          self.combo_value = wxComboBox(self, -1)          self.combo_value = wxComboBox(self, -1)
339          self.choice_action = wxChoice(self, -1,          self.choice_action = wxChoice(self, -1,
340                                  choices=[_("Replace Selection"),                                  choices=[_("Replace Selection"),
# Line 266  class LayerTableFrame(TableFrame): Line 342  class LayerTableFrame(TableFrame):
342                                          _("Add to Selection")])                                          _("Add to Selection")])
343    
344          button_query = wxButton(self, ID_QUERY, _("Query"))          button_query = wxButton(self, ID_QUERY, _("Query"))
345          button_saveas = wxButton(self, ID_SAVEAS, _("Save As..."))          button_saveas = wxButton(self, ID_EXPORT, _("Export"))
346    
347          self.grid.SetSize((400, 200))          self.grid.SetSize((400, 200))
348    
349          self.combo_value.Append("")          self.combo_value.Append("")
350          for i in range(table.field_count()):          for i in range(table.NumColumns()):
351              type, name, len, decc = layer.table.field_info(i)              name = table.Column(i).name
352              self.combo_fields.Append(name)              self.combo_fields.Append(name)
353              self.combo_value.Append(name)              self.combo_value.Append(name)
354                                                                                    
355          # assume at least one field?          # assume at least one field?
356          self.combo_fields.SetSelection(0)          self.combo_fields.SetSelection(0)
357          self.combo_value.SetSelection(0)          self.combo_value.SetSelection(0)
358    
359          topBox = wxBoxSizer(wxVERTICAL)          topBox = wxBoxSizer(wxVERTICAL)
360    
361          sizer = wxStaticBoxSizer(wxStaticBox(self, -1, _("Selections")),          sizer = wxStaticBoxSizer(wxStaticBox(self, -1,
362                                      _("Selection")),
363                                    wxHORIZONTAL)                                    wxHORIZONTAL)
364          sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)          sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
365          sizer.Add(self.choice_comp, 0, wxALL, 4)          sizer.Add(self.choice_comp, 0, wxALL, 4)
366          sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)          sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
367          sizer.Add(self.choice_action, 0, wxALL, 4)          sizer.Add(self.choice_action, 0, wxALL, 4)
368          sizer.Add(button_query, 0, wxALL, 4)          sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
369          sizer.Add(40, 20, 0, wxALL, 4)          sizer.Add(40, 20, 0, wxALL, 4)
370          sizer.Add(button_saveas, 0, wxALL, 4)          sizer.Add(button_saveas, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
371    
372          topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)          topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
373          topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)          topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
# Line 300  class LayerTableFrame(TableFrame): Line 377  class LayerTableFrame(TableFrame):
377          topBox.Fit(self)          topBox.Fit(self)
378          topBox.SetSizeHints(self)          topBox.SetSizeHints(self)
379    
380            self.grid.SetFocus()
381          EVT_BUTTON(self, ID_QUERY, self.OnQuery)          EVT_BUTTON(self, ID_QUERY, self.OnQuery)
382          EVT_BUTTON(self, ID_SAVEAS, self.OnSaveAs)          EVT_BUTTON(self, ID_EXPORT, self.OnSaveAs)
383            EVT_KEY_DOWN(self.grid, self.OnKeyDown)
384    
385        def OnKeyDown(self, event):
386            """Catch query key from grid"""
387            if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
388                self.combo_fields.SetFocus()
389                self.combo_fields.refocus = True
390            else:
391                event.Skip()
392    
393    
394      def OnQuery(self, event):      def OnQuery(self, event):
395          wxBeginBusyCursor()          wxBeginBusyCursor()
396            try:
397    
398          if self.combo_value.GetSelection() < 1:              if self.combo_value.GetSelection() < 1:
399              value = self.combo_value.GetValue()                  value = self.combo_value.GetValue()
400              print value              else:
401          else:                  value = self.table.Column(self.combo_value.GetValue())
402              value = self.table.Column(self.combo_value.GetValue())  
403                ids = self.table.SimpleQuery(
404          #ids = self.table.Query(                      self.table.Column(self.combo_fields.GetStringSelection()),
405                  #self.table.Column(self.combo_fields.GetStringSelection()),                      self.choice_comp.GetStringSelection(),
406                  #self.choice_comp.GetStringSelection(),                      value)
                 #value)  
407    
408          choice = self.choice_action.GetSelection()              choice = self.choice_action.GetSelection()
409                            
410          if choice == 0:              #
411              ids = [1, 2, 3, 4, 5]              # what used to be nice code got became a bit ugly because
412              # Replace Selection              # each time we select a row a message is sent to the grid
413              self.grid.ClearSelection()              # which we are listening for and then we send further
414              for id in ids:              # messages.
415                  self.grid.SelectRow(id, True)              #
416          elif choice == 1:              # now, we disable those listeners select everything but
417              ids = [1, 3, 5]              # the first item, reenable the listeners, and select
418              # Refine Selection              # the first element, which causes everything to be
419              sel = dict([(i, 0) for i in self.parent.SelectedShapes()])              # updated properly.
420              self.grid.ClearSelection()              #
421              for id in filter(sel.has_key, ids):              self.grid.ToggleEventListeners(False)
422                  self.grid.SelectRow(id, True)  
423          elif choice == 2:              if choice == 0:
424              ids = [2, 4]                  # Replace Selection
425              # Add to Selection                  self.grid.ClearSelection()
426                elif choice == 1:
427                    # Refine Selection
428                    sel = self.get_selected()
429                    self.grid.ClearSelection()
430                    ids = filter(sel.has_key, ids)
431                elif choice == 2:
432                    # Add to Selection
433                    pass
434    
435                #
436                # select the rows (all but the first)
437                #
438                firsttime = True
439              for id in ids:              for id in ids:
440                  self.grid.SelectRow(id, True)                  if firsttime:
441                        firsttime = False
442          wxEndBusyCursor()                  else:
443                        self.grid.SelectRow(id, True)
444    
445                self.grid.ToggleEventListeners(True)
446    
447                #
448                # select the first row
449                #
450                if ids:
451                    self.grid.SelectRow(ids[0], True)
452            finally:
453                wxEndBusyCursor()
454                    
455      def OnSaveAs(self, event):      def OnSaveAs(self, event):
456          dlg = wxFileDialog(self, _("Save Table As"), ".", "",          dlg = wxFileDialog(self, _("Export Table To"), ".", "",
457                             "DBF Files (*.dbf)|*.dbf|" +                             _("DBF Files (*.dbf)|*.dbf|") +
458                             "CSV Files (*.csv)|*.csv|" +                             _("CSV Files (*.csv)|*.csv|") +
459                             "All Files (*.*)|*.*",                             _("All Files (*.*)|*.*"),
460                             wxSAVE|wxOVERWRITE_PROMPT)                             wxSAVE|wxOVERWRITE_PROMPT)
461          if dlg.ShowModal() == wxID_OK:          if dlg.ShowModal() == wxID_OK:
462              pass              filename = dlg.GetPath()
463                                                                                                type = os.path.basename(filename).split('.')[-1:][0]
464          dlg.Destroy()              dlg.Destroy()
465                if type.upper() == "DBF":
466                    table_to_dbf(self.table, filename)
467                elif type.upper() == 'CSV':
468                    table_to_csv(self.table, filename)
469                else:
470                    dlg = wxMessageDialog(None, "Unsupported format: %s" % type,
471                                          "Table Export", wxOK|wxICON_WARNING)
472                    dlg.ShowModal()
473                    dlg.Destroy()
474            else:
475                dlg.Destroy()
476    
477        def OnClose(self, event):
478            TableFrame.OnClose(self, event)
479    
480        def get_selected(self):
481            """Return a dictionary of the selected rows.
482            
483            The dictionary has sthe indexes as keys."""
484            return dict([(i, 0) for i in self.grid.GetSelectedRows()])
485    
486    class LayerTableFrame(QueryTableFrame):
487    
488        """Frame that displays a layer table in a grid view
489    
490        A LayerTableFrame is a QueryTableFrame whose selection is connected to the
491        selected object in a map.
492        """
493    
494        def __init__(self, parent, name, title, layer, table):
495            QueryTableFrame.__init__(self, parent, name, title, table)
496            self.layer = layer
497            self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
498            self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
499    
500            # if there is already a selection present, update the grid
501            # accordingly
502            sel = self.get_selected().keys()
503            for i in sel:
504                self.grid.SelectRow(i, True)
505    
506      def make_grid(self, table):      def make_grid(self, table):
507          """Override the derived method to return a LayerTableGrid.          """Override the derived method to return a LayerTableGrid.
508          """          """
509          return LayerTableGrid(self, table)          return LayerTableGrid(self, table)
510    
511        def get_selected(self):
512            """Override the derived method to return a dictionary of the selected
513            rows.
514            """
515            return dict([(i, 0) for i in self.parent.SelectedShapes()])
516    
517      def OnClose(self, event):      def OnClose(self, event):
518            """Override the derived method to first unsubscribed."""
519          self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)          self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
520          TableFrame.OnClose(self, event)          self.grid.Unsubscribe(ROW_SELECTED, self.rows_selected)
521            QueryTableFrame.OnClose(self, event)
522    
523      def select_shapes(self, layer, shapes):      def select_shapes(self, layer, shapes):
524          """Subscribed to the SHAPES_SELECTED message.          """Subscribed to the SHAPES_SELECTED message.
# Line 369  class LayerTableFrame(TableFrame): Line 529  class LayerTableFrame(TableFrame):
529          self.grid.select_shapes(layer, shapes)          self.grid.select_shapes(layer, shapes)
530    
531      def rows_selected(self, rows):      def rows_selected(self, rows):
532            """Return the selected rows of the layer as they are returned
533            by Layer.SelectShapes().
534            """
535          if self.layer is not None:          if self.layer is not None:
536              self.parent.SelectShapes(self.layer, rows)              self.parent.SelectShapes(self.layer, rows)

Legend:
Removed from v.881  
changed lines
  Added in v.1104

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26