/[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 1013 - (hide annotations)
Fri May 23 09:18:28 2003 UTC (21 years, 9 months ago) by jan
Original Path: trunk/thuban/Thuban/UI/tableview.py
File MIME type: text/x-python
File size: 14816 byte(s)
(LayerTableFrame, QueryTableFrame): Split the class LayerTableFrame into
two classes, LayerTableFrame and QueryTableFrame.
The latter implements the selection GUI without dependency on a layer.
LayerTableFrame now is derived from QueryTableFrame and connects
to a layer.

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 frank 979 QUERY_KEY = 'S'
29 bh 34
30 bh 6 class DataTable(wxPyGridTableBase):
31    
32 bh 34 """Wrapper around a Thuban table object suitable for a wxGrid"""
33    
34 bh 6 def __init__(self, table = None):
35     wxPyGridTableBase.__init__(self)
36     self.num_cols = 0
37     self.num_rows = 0
38     self.columns = []
39     self.table = None
40     self.SetTable(table)
41    
42     def SetTable(self, table):
43     self.table = table
44 bh 838 self.num_cols = table.NumColumns()
45     self.num_rows = table.NumRows()
46 bh 6
47     self.columns = []
48     for i in range(self.num_cols):
49 bh 838 col = table.Column(i)
50     self.columns.append((col.name, wx_value_type_map[col.type]))
51 bh 6
52     #
53     # required methods for the wxPyGridTableBase interface
54     #
55    
56     def GetNumberRows(self):
57     return self.num_rows
58    
59     def GetNumberCols(self):
60     return self.num_cols
61    
62     def IsEmptyCell(self, row, col):
63     return 0
64    
65     # Get/Set values in the table. The Python version of these
66     # methods can handle any data-type, (as long as the Editor and
67     # Renderer understands the type too,) not just strings as in the
68     # C++ version.
69     def GetValue(self, row, col):
70 bh 838 record = self.table.ReadRowAsDict(row)
71 bh 6 return record[self.columns[col][0]]
72    
73     def SetValue(self, row, col, value):
74     pass
75    
76     #
77     # Some optional methods
78     #
79    
80     # Called when the grid needs to display labels
81     def GetColLabelValue(self, col):
82     return self.columns[col][0]
83    
84     # Called to determine the kind of editor/renderer to use by
85     # default, doesn't necessarily have to be the same type used
86     # nativly by the editor/renderer if they know how to convert.
87     def GetTypeName(self, row, col):
88     return self.columns[col][1]
89    
90     # Called to determine how the data can be fetched and stored by the
91     # editor and renderer. This allows you to enforce some type-safety
92     # in the grid.
93     def CanGetValueAs(self, row, col, typeName):
94     # perhaps we should allow conversion int->double?
95     return self.GetTypeName(row, col) == typeName
96    
97     def CanSetValueAs(self, row, col, typeName):
98     return self.CanGetValueAs(row, col, typeName)
99    
100    
101 bh 34 class TableGrid(wxGrid, Publisher):
102 bh 6
103 bh 808 """A grid view for a Thuban table
104 bh 6
105 bh 808 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 bh 6 def __init__(self, parent, table = None):
114     wxGrid.__init__(self, parent, -1)
115    
116 bh 808 self.allow_messages_count = 0
117    
118 bh 6 self.table = DataTable(table)
119    
120     # The second parameter means that the grid is to take ownership
121     # of the table and will destroy it when done. Otherwise you
122 bh 77 # would need to keep a reference to it and call its Destroy
123 bh 6 # method later.
124     self.SetTable(self.table, true)
125    
126     #self.SetMargins(0,0)
127    
128 bh 173 # 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 bh 6 self.SetSelectionMode(wxGrid.wxGridSelectRows)
135 bh 278
136 jonathan 966 #EVT_GRID_RANGE_SELECT(self, None)
137     #EVT_GRID_SELECT_CELL(self, None)
138     #EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
139     #EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
140 bh 6
141 jonathan 966 self.ToggleEventListeners(True)
142    
143 bh 6 def SetTableObject(self, table):
144     self.table.SetTable(table)
145    
146     def OnRangeSelect(self, event):
147 jonathan 881 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 bh 6 if event.Selecting():
153 jonathan 881 for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
154     rows[i] = 0
155     rows[event.GetTopLeftCoords().GetRow()] = 0
156 bh 6
157 jonathan 881 self.issue(ROW_SELECTED, rows.keys())
158     event.Skip()
159    
160 bh 6 def OnSelectCell(self, event):
161 jonathan 881 self.issue(ROW_SELECTED, self.GetSelectedRows())
162     event.Skip()
163 bh 6
164 jonathan 966 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 bh 808 def disallow_messages(self):
173     """Disallow messages to be send.
174 bh 278
175 bh 808 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 bh 278 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 jonathan 881 def select_shapes(self, layer, shapes):
208 bh 278 """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 jonathan 881 if layer is not None \
214     and layer.table is self.table.table:
215    
216 bh 808 self.disallow_messages()
217     try:
218 jonathan 881 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 bh 808 finally:
234     self.allow_messages()
235 bh 6
236    
237 bh 30 class TableFrame(NonModalDialog):
238 bh 6
239 bh 34 """Frame that displays a Thuban table in a grid view"""
240    
241 bh 535 def __init__(self, parent, name, title, table):
242     NonModalDialog.__init__(self, parent, name, title)
243 bh 278 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 jonathan 881 ID_QUERY = 4001
256 jan 1013 ID_EXPORT = 4002
257 jonathan 881
258 jan 1013 class QueryTableFrame(TableFrame):
259 bh 278
260 jan 1013 """Frame that displays a table in a grid view and offers user actions
261     selection and export
262 bh 278
263     A LayerTableFrame is TableFrame whose selection is connected to the
264     selected object in a map.
265     """
266    
267 jan 1013 def __init__(self, parent, name, title, table):
268 bh 535 TableFrame.__init__(self, parent, name, title, table)
269 bh 30
270 jonathan 881 self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)
271     self.choice_comp = wxChoice(self, -1,
272 jonathan 939 choices=["<", "<=", "==", "!=", ">=", ">"])
273 jonathan 881 self.combo_value = wxComboBox(self, -1)
274     self.choice_action = wxChoice(self, -1,
275     choices=[_("Replace Selection"),
276     _("Refine Selection"),
277     _("Add to Selection")])
278    
279     button_query = wxButton(self, ID_QUERY, _("Query"))
280 jan 1013 button_saveas = wxButton(self, ID_EXPORT, _("Export"))
281 jonathan 881
282     self.grid.SetSize((400, 200))
283    
284     self.combo_value.Append("")
285 jonathan 939 for i in range(table.NumColumns()):
286     name = table.Column(i).name
287 jonathan 881 self.combo_fields.Append(name)
288     self.combo_value.Append(name)
289 jonathan 939
290 jonathan 881 # assume at least one field?
291     self.combo_fields.SetSelection(0)
292     self.combo_value.SetSelection(0)
293    
294     topBox = wxBoxSizer(wxVERTICAL)
295    
296 jan 1013 sizer = wxStaticBoxSizer(wxStaticBox(self, -1, _("Selection")),
297 jonathan 881 wxHORIZONTAL)
298     sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
299     sizer.Add(self.choice_comp, 0, wxALL, 4)
300     sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
301     sizer.Add(self.choice_action, 0, wxALL, 4)
302 frank 979 sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
303 jonathan 881 sizer.Add(40, 20, 0, wxALL, 4)
304 frank 979 sizer.Add(button_saveas, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
305 jonathan 881
306     topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
307     topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
308    
309     self.SetAutoLayout(True)
310     self.SetSizer(topBox)
311     topBox.Fit(self)
312     topBox.SetSizeHints(self)
313    
314 frank 979 self.grid.SetFocus()
315 jonathan 881 EVT_BUTTON(self, ID_QUERY, self.OnQuery)
316 jan 1013 EVT_BUTTON(self, ID_EXPORT, self.OnSaveAs)
317 frank 979 EVT_KEY_DOWN(self.grid, self.OnKeyDown)
318 jonathan 881
319 frank 979 def OnKeyDown(self, event):
320     """Catch query key from grid"""
321     if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
322     self.combo_fields.SetFocus()
323     self.combo_fields.refocus = True
324     else:
325     event.Skip()
326    
327    
328 jonathan 881 def OnQuery(self, event):
329     wxBeginBusyCursor()
330    
331     if self.combo_value.GetSelection() < 1:
332     value = self.combo_value.GetValue()
333     else:
334     value = self.table.Column(self.combo_value.GetValue())
335    
336 jonathan 939 ids = self.table.SimpleQuery(
337     self.table.Column(self.combo_fields.GetStringSelection()),
338     self.choice_comp.GetStringSelection(),
339     value)
340 jonathan 881
341     choice = self.choice_action.GetSelection()
342    
343 jonathan 966 #
344     # what used to be nice code got became a bit ugly because
345     # each time we select a row a message is sent to the grid
346     # which we are listening for and then we send further
347     # messages.
348     #
349     # now, we disable those listeners select everything but
350     # the first item, reenable the listeners, and select
351     # the first element, which causes everything to be
352     # updated properly.
353     #
354     self.grid.ToggleEventListeners(False)
355    
356 jonathan 881 if choice == 0:
357     # Replace Selection
358     self.grid.ClearSelection()
359     elif choice == 1:
360     # Refine Selection
361 jan 1013 sel = self.get_selected()
362 jonathan 881 self.grid.ClearSelection()
363 jonathan 966 ids = filter(sel.has_key, ids)
364 jonathan 881 elif choice == 2:
365     # Add to Selection
366 jonathan 966 pass
367    
368     #
369     # select the rows (all but the first)
370     #
371     firsttime = True
372     for id in ids:
373     if firsttime:
374     firsttime = False
375     else:
376 jonathan 881 self.grid.SelectRow(id, True)
377    
378 jonathan 966 self.grid.ToggleEventListeners(True)
379    
380     #
381     # select the first row
382     #
383     if ids:
384     self.grid.SelectRow(ids[0], True)
385    
386 jonathan 881 wxEndBusyCursor()
387    
388     def OnSaveAs(self, event):
389 jan 1013 dlg = wxFileDialog(self, _("Export Table To"), ".", "",
390     _("DBF Files (*.dbf)|*.dbf|") +
391     _("CSV Files (*.csv)|*.csv|") +
392     _("All Files (*.*)|*.*"),
393 jonathan 881 wxSAVE|wxOVERWRITE_PROMPT)
394     if dlg.ShowModal() == wxID_OK:
395     pass
396    
397     dlg.Destroy()
398    
399 jan 1013 def OnClose(self, event):
400     TableFrame.OnClose(self, event)
401    
402     def get_selected(self):
403     """Return a dictionary of the selected rows.
404    
405     The dictionary has sthe indexes as keys."""
406     return dict([(i, 0) for i in self.grid.GetSelectedRows()])
407    
408     class LayerTableFrame(QueryTableFrame):
409    
410     """Frame that displays a layer table in a grid view
411    
412     A LayerTableFrame is a QueryTableFrame whose selection is connected to the
413     selected object in a map.
414     """
415    
416     def __init__(self, parent, name, title, layer, table):
417     QueryTableFrame.__init__(self, parent, name, title, table)
418     self.layer = layer
419     self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
420     self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
421    
422 bh 278 def make_grid(self, table):
423     """Override the derived method to return a LayerTableGrid.
424     """
425     return LayerTableGrid(self, table)
426    
427 jan 1013 def get_selected(self):
428     """Override the derived method to return a dictionary of the selected
429     rows.
430     """
431     return dict([(i, 0) for i in self.parent.SelectedShapes()])
432    
433 bh 30 def OnClose(self, event):
434 jan 1013 """Override the derived method to first unsubscribed."""
435 jonathan 881 self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
436 jan 1013 QueryTableFrame.OnClose(self, event)
437 bh 30
438 jonathan 881 def select_shapes(self, layer, shapes):
439 bh 535 """Subscribed to the SHAPES_SELECTED message.
440    
441     If shapes contains exactly one shape id, select that shape in
442     the grid. Otherwise deselect all.
443     """
444 jonathan 881 self.grid.select_shapes(layer, shapes)
445 bh 34
446 jonathan 881 def rows_selected(self, rows):
447 jan 1013 """Return the selected rows of the layer as they are returned
448     by Layer.SelectShapes().
449     """
450 bh 34 if self.layer is not None:
451 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