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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 966 - (hide annotations)
Wed May 21 17:24:40 2003 UTC (21 years, 9 months ago) by jonathan
Original Path: trunk/thuban/Thuban/UI/tableview.py
File MIME type: text/x-python
File size: 13384 byte(s)
(TableGrid.__init__): Call
        ToggleEventListeners to turn on listening.
(TableGrid.ToggleEventListeners): New. Turns event listening on
        and off so as to prevent excessive messages.
(LayerTableFrame.OnQuery): Use TableGrid.ToggleEventListeners
        to suppress excessive messages when selecting many rows.
        Fixes RTBug #1880.

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26