/[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 979 - (hide annotations)
Thu May 22 11:40:59 2003 UTC (21 years, 9 months ago) by frank
Original Path: trunk/thuban/Thuban/UI/tableview.py
File MIME type: text/x-python
File size: 13855 byte(s)
(LayerTableFrame.__init__): Rename 'Save As'
	Button to 'Export'. Center Buttons in Selection Box, set Focus to
	Grid.
(LayerTableFrame.OnKeyDown()): New, bound to the grid with EVT_KEY_DOWN,
	changes focus to the Selection when pressing "Alt-S".

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     ID_SAVEAS = 4002
257    
258 bh 278 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 bh 535 def __init__(self, parent, name, title, layer, table):
267     TableFrame.__init__(self, parent, name, title, table)
268 bh 34 self.layer = layer
269 jonathan 881 self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
270     self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
271 bh 30
272 jonathan 881 self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)
273     self.choice_comp = wxChoice(self, -1,
274 jonathan 939 choices=["<", "<=", "==", "!=", ">=", ">"])
275 jonathan 881 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 frank 979 button_saveas = wxButton(self, ID_SAVEAS, _("Export"))
283 jonathan 881
284     self.grid.SetSize((400, 200))
285    
286     self.combo_value.Append("")
287 jonathan 939 for i in range(table.NumColumns()):
288     name = table.Column(i).name
289 jonathan 881 self.combo_fields.Append(name)
290     self.combo_value.Append(name)
291 jonathan 939
292 jonathan 881 # 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 frank 979 sizer.Add(button_query, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
305 jonathan 881 sizer.Add(40, 20, 0, wxALL, 4)
306 frank 979 sizer.Add(button_saveas, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
307 jonathan 881
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 frank 979 self.grid.SetFocus()
317 jonathan 881 EVT_BUTTON(self, ID_QUERY, self.OnQuery)
318     EVT_BUTTON(self, ID_SAVEAS, self.OnSaveAs)
319 frank 979 EVT_KEY_DOWN(self.grid, self.OnKeyDown)
320 jonathan 881
321 frank 979 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 jonathan 881 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 jonathan 939 ids = self.table.SimpleQuery(
341     self.table.Column(self.combo_fields.GetStringSelection()),
342     self.choice_comp.GetStringSelection(),
343     value)
344 jonathan 881
345     choice = self.choice_action.GetSelection()
346    
347 jonathan 966 #
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 jonathan 881 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 jonathan 966 ids = filter(sel.has_key, ids)
368 jonathan 881 elif choice == 2:
369     # Add to Selection
370 jonathan 966 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 jonathan 881 self.grid.SelectRow(id, True)
381    
382 jonathan 966 self.grid.ToggleEventListeners(True)
383    
384     #
385     # select the first row
386     #
387     if ids:
388     self.grid.SelectRow(ids[0], True)
389    
390 jonathan 881 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 bh 278 def make_grid(self, table):
404     """Override the derived method to return a LayerTableGrid.
405     """
406     return LayerTableGrid(self, table)
407    
408 bh 30 def OnClose(self, event):
409 jonathan 881 self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
410 bh 278 TableFrame.OnClose(self, event)
411 bh 30
412 jonathan 881 def select_shapes(self, layer, shapes):
413 bh 535 """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 jonathan 881 self.grid.select_shapes(layer, shapes)
419 bh 34
420 jonathan 881 def rows_selected(self, rows):
421 bh 34 if self.layer is not None:
422 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