/[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 1027 - (hide annotations)
Mon May 26 11:47:17 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: 15320 byte(s)
(QueryTableFrame.OnSaveAs): Call table_to_dbf or table_to_csv depending on
	file selection.

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 frank 1027 import os.path
11    
12 jonathan 881 from Thuban import _
13    
14 bh 6 from wxPython.wx import *
15     from wxPython.grid import *
16    
17 bh 34 from Thuban.Lib.connector import Publisher
18 bh 6 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
19 frank 1027 FIELDTYPE_STRING, table_to_dbf, table_to_csv
20 bh 6 import view
21 bh 30 from dialogs import NonModalDialog
22 bh 535 from messages import SHAPES_SELECTED
23 bh 6
24     wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,
25     FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,
26     FIELDTYPE_STRING: wxGRID_VALUE_STRING}
27    
28 bh 34 ROW_SELECTED = "ROW_SELECTED"
29    
30 frank 979 QUERY_KEY = 'S'
31 bh 34
32 bh 6 class DataTable(wxPyGridTableBase):
33    
34 bh 34 """Wrapper around a Thuban table object suitable for a wxGrid"""
35    
36 bh 6 def __init__(self, table = None):
37     wxPyGridTableBase.__init__(self)
38     self.num_cols = 0
39     self.num_rows = 0
40     self.columns = []
41     self.table = None
42     self.SetTable(table)
43    
44     def SetTable(self, table):
45     self.table = table
46 bh 838 self.num_cols = table.NumColumns()
47     self.num_rows = table.NumRows()
48 bh 6
49     self.columns = []
50     for i in range(self.num_cols):
51 bh 838 col = table.Column(i)
52     self.columns.append((col.name, wx_value_type_map[col.type]))
53 bh 6
54     #
55     # required methods for the wxPyGridTableBase interface
56     #
57    
58     def GetNumberRows(self):
59     return self.num_rows
60    
61     def GetNumberCols(self):
62     return self.num_cols
63    
64     def IsEmptyCell(self, row, col):
65     return 0
66    
67     # Get/Set values in the table. The Python version of these
68     # methods can handle any data-type, (as long as the Editor and
69     # Renderer understands the type too,) not just strings as in the
70     # C++ version.
71     def GetValue(self, row, col):
72 bh 838 record = self.table.ReadRowAsDict(row)
73 bh 6 return record[self.columns[col][0]]
74    
75     def SetValue(self, row, col, value):
76     pass
77    
78     #
79     # Some optional methods
80     #
81    
82     # Called when the grid needs to display labels
83     def GetColLabelValue(self, col):
84     return self.columns[col][0]
85    
86     # Called to determine the kind of editor/renderer to use by
87     # default, doesn't necessarily have to be the same type used
88     # nativly by the editor/renderer if they know how to convert.
89     def GetTypeName(self, row, col):
90     return self.columns[col][1]
91    
92     # Called to determine how the data can be fetched and stored by the
93     # editor and renderer. This allows you to enforce some type-safety
94     # in the grid.
95     def CanGetValueAs(self, row, col, typeName):
96     # perhaps we should allow conversion int->double?
97     return self.GetTypeName(row, col) == typeName
98    
99     def CanSetValueAs(self, row, col, typeName):
100     return self.CanGetValueAs(row, col, typeName)
101    
102    
103 bh 34 class TableGrid(wxGrid, Publisher):
104 bh 6
105 bh 808 """A grid view for a Thuban table
106 bh 6
107 bh 808 When rows are selected by the user the table issues ROW_SELECTED
108     messages. wx sends selection events even when the selection is
109     manipulated by code (instead of by the user) which usually lead to
110     ROW_SELECTED messages being sent in turn. Therefore sending messages
111     can be switched on and off with the allow_messages and
112     disallow_messages methods.
113     """
114    
115 bh 6 def __init__(self, parent, table = None):
116     wxGrid.__init__(self, parent, -1)
117    
118 bh 808 self.allow_messages_count = 0
119    
120 bh 6 self.table = DataTable(table)
121    
122     # The second parameter means that the grid is to take ownership
123     # of the table and will destroy it when done. Otherwise you
124 bh 77 # would need to keep a reference to it and call its Destroy
125 bh 6 # method later.
126     self.SetTable(self.table, true)
127    
128     #self.SetMargins(0,0)
129    
130 bh 173 # AutoSizeColumns would allow us to make the grid have optimal
131     # column widths automatically but it would cause a traversal of
132     # the entire table which for large .dbf files can take a very
133     # long time.
134     #self.AutoSizeColumns(false)
135    
136 bh 6 self.SetSelectionMode(wxGrid.wxGridSelectRows)
137 bh 278
138 jonathan 966 #EVT_GRID_RANGE_SELECT(self, None)
139     #EVT_GRID_SELECT_CELL(self, None)
140     #EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
141     #EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
142 bh 6
143 jonathan 966 self.ToggleEventListeners(True)
144    
145 bh 6 def SetTableObject(self, table):
146     self.table.SetTable(table)
147    
148     def OnRangeSelect(self, event):
149 jonathan 881 rows = dict([(i, 0) for i in self.GetSelectedRows()])
150    
151     # if we're selecting we need to include the selected range and
152     # make sure that the current row is also included, which may
153     # not be the case if you just click on a single row!
154 bh 6 if event.Selecting():
155 jonathan 881 for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
156     rows[i] = 0
157     rows[event.GetTopLeftCoords().GetRow()] = 0
158 bh 6
159 jonathan 881 self.issue(ROW_SELECTED, rows.keys())
160     event.Skip()
161    
162 bh 6 def OnSelectCell(self, event):
163 jonathan 881 self.issue(ROW_SELECTED, self.GetSelectedRows())
164     event.Skip()
165 bh 6
166 jonathan 966 def ToggleEventListeners(self, on):
167     if on:
168     EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
169     EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
170     else:
171     EVT_GRID_RANGE_SELECT(self, None)
172     EVT_GRID_SELECT_CELL(self, None)
173    
174 bh 808 def disallow_messages(self):
175     """Disallow messages to be send.
176 bh 278
177 bh 808 This method only increases a counter so that calls to
178     disallow_messages and allow_messages can be nested. Only the
179     outermost calls will actually switch message sending on and off.
180     """
181     self.allow_messages_count += 1
182    
183     def allow_messages(self):
184     """Allow messages to be send.
185    
186     This method only decreases a counter so that calls to
187     disallow_messages and allow_messages can be nested. Only the
188     outermost calls will actually switch message sending on and off.
189     """
190     self.allow_messages_count -= 1
191    
192     def issue(self, *args):
193     """Issue a message unless disallowed.
194    
195     See the allow_messages and disallow_messages methods.
196     """
197     if self.allow_messages_count == 0:
198     Publisher.issue(self, *args)
199    
200    
201 bh 278 class LayerTableGrid(TableGrid):
202    
203     """Table grid for the layer tables.
204    
205     The LayerTableGrid is basically the same as a TableGrid but it's
206     selection is usually coupled to the selected object in the map.
207     """
208    
209 jonathan 881 def select_shapes(self, layer, shapes):
210 bh 278 """Select the row corresponding to the specified shape and layer
211    
212     If layer is not the layer the table is associated with do
213     nothing. If shape or layer is None also do nothing.
214     """
215 jonathan 881 if layer is not None \
216     and layer.table is self.table.table:
217    
218 bh 808 self.disallow_messages()
219     try:
220 jonathan 881 self.ClearSelection()
221     if len(shapes) > 0:
222     #
223     # keep track of the lowest id so we can make it
224     # the first visible item
225     #
226     first = shapes[0]
227    
228     for shape in shapes:
229     self.SelectRow(shape, True)
230     if shape < first:
231     first = shape
232    
233     self.SetGridCursor(first, 0)
234     self.MakeCellVisible(first, 0)
235 bh 808 finally:
236     self.allow_messages()
237 bh 6
238    
239 bh 30 class TableFrame(NonModalDialog):
240 bh 6
241 bh 34 """Frame that displays a Thuban table in a grid view"""
242    
243 bh 535 def __init__(self, parent, name, title, table):
244     NonModalDialog.__init__(self, parent, name, title)
245 bh 278 self.table = table
246     self.grid = self.make_grid(self.table)
247    
248     def make_grid(self, table):
249     """Return the table grid to use in the frame.
250    
251     The default implementation returns a TableGrid instance.
252     Override in derived classes to use different grid classes.
253     """
254     return TableGrid(self, table)
255    
256    
257 jonathan 881 ID_QUERY = 4001
258 jan 1013 ID_EXPORT = 4002
259 jonathan 881
260 jan 1013 class QueryTableFrame(TableFrame):
261 bh 278
262 jan 1013 """Frame that displays a table in a grid view and offers user actions
263     selection and export
264 bh 278
265     A LayerTableFrame is TableFrame whose selection is connected to the
266     selected object in a map.
267     """
268    
269 jan 1013 def __init__(self, parent, name, title, table):
270 bh 535 TableFrame.__init__(self, parent, name, title, table)
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 jan 1013 button_saveas = wxButton(self, ID_EXPORT, _("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 jan 1013 sizer = wxStaticBoxSizer(wxStaticBox(self, -1, _("Selection")),
299 jonathan 881 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 jan 1013 EVT_BUTTON(self, ID_EXPORT, 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     if event.AltDown() and event.GetKeyCode() == ord(QUERY_KEY):
324     self.combo_fields.SetFocus()
325     self.combo_fields.refocus = True
326     else:
327     event.Skip()
328    
329    
330 jonathan 881 def OnQuery(self, event):
331     wxBeginBusyCursor()
332    
333     if self.combo_value.GetSelection() < 1:
334     value = self.combo_value.GetValue()
335     else:
336     value = self.table.Column(self.combo_value.GetValue())
337    
338 jonathan 939 ids = self.table.SimpleQuery(
339     self.table.Column(self.combo_fields.GetStringSelection()),
340     self.choice_comp.GetStringSelection(),
341     value)
342 jonathan 881
343     choice = self.choice_action.GetSelection()
344    
345 jonathan 966 #
346     # what used to be nice code got became a bit ugly because
347     # each time we select a row a message is sent to the grid
348     # which we are listening for and then we send further
349     # messages.
350     #
351     # now, we disable those listeners select everything but
352     # the first item, reenable the listeners, and select
353     # the first element, which causes everything to be
354     # updated properly.
355     #
356     self.grid.ToggleEventListeners(False)
357    
358 jonathan 881 if choice == 0:
359     # Replace Selection
360     self.grid.ClearSelection()
361     elif choice == 1:
362     # Refine Selection
363 jan 1013 sel = self.get_selected()
364 jonathan 881 self.grid.ClearSelection()
365 jonathan 966 ids = filter(sel.has_key, ids)
366 jonathan 881 elif choice == 2:
367     # Add to Selection
368 jonathan 966 pass
369    
370     #
371     # select the rows (all but the first)
372     #
373     firsttime = True
374     for id in ids:
375     if firsttime:
376     firsttime = False
377     else:
378 jonathan 881 self.grid.SelectRow(id, True)
379    
380 jonathan 966 self.grid.ToggleEventListeners(True)
381    
382     #
383     # select the first row
384     #
385     if ids:
386     self.grid.SelectRow(ids[0], True)
387    
388 jonathan 881 wxEndBusyCursor()
389    
390     def OnSaveAs(self, event):
391 jan 1013 dlg = wxFileDialog(self, _("Export Table To"), ".", "",
392     _("DBF Files (*.dbf)|*.dbf|") +
393     _("CSV Files (*.csv)|*.csv|") +
394     _("All Files (*.*)|*.*"),
395 jonathan 881 wxSAVE|wxOVERWRITE_PROMPT)
396     if dlg.ShowModal() == wxID_OK:
397 frank 1027 filename = dlg.GetPath()
398     type = os.path.basename(filename).split('.')[-1:][0]
399     dlg.Destroy()
400     if type.upper() == "DBF":
401     table_to_dbf(self.table, filename)
402     elif type.upper() == 'CSV':
403     table_to_csv(self.table, filename)
404     else:
405     dlg = wxMessageDialog(None, "Unsupported format: %s" % type,
406     "Table Export", wxOK|wxICON_WARNING)
407     dlg.ShowModal()
408     dlg.Destroy()
409     else:
410     dlg.Destroy()
411 jonathan 881
412 jan 1013 def OnClose(self, event):
413     TableFrame.OnClose(self, event)
414    
415     def get_selected(self):
416     """Return a dictionary of the selected rows.
417    
418     The dictionary has sthe indexes as keys."""
419     return dict([(i, 0) for i in self.grid.GetSelectedRows()])
420    
421     class LayerTableFrame(QueryTableFrame):
422    
423     """Frame that displays a layer table in a grid view
424    
425     A LayerTableFrame is a QueryTableFrame whose selection is connected to the
426     selected object in a map.
427     """
428    
429     def __init__(self, parent, name, title, layer, table):
430     QueryTableFrame.__init__(self, parent, name, title, table)
431     self.layer = layer
432     self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
433     self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
434    
435 bh 278 def make_grid(self, table):
436     """Override the derived method to return a LayerTableGrid.
437     """
438     return LayerTableGrid(self, table)
439    
440 jan 1013 def get_selected(self):
441     """Override the derived method to return a dictionary of the selected
442     rows.
443     """
444     return dict([(i, 0) for i in self.parent.SelectedShapes()])
445    
446 bh 30 def OnClose(self, event):
447 jan 1013 """Override the derived method to first unsubscribed."""
448 jonathan 881 self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
449 jan 1013 QueryTableFrame.OnClose(self, event)
450 bh 30
451 jonathan 881 def select_shapes(self, layer, shapes):
452 bh 535 """Subscribed to the SHAPES_SELECTED message.
453    
454     If shapes contains exactly one shape id, select that shape in
455     the grid. Otherwise deselect all.
456     """
457 jonathan 881 self.grid.select_shapes(layer, shapes)
458 bh 34
459 jonathan 881 def rows_selected(self, rows):
460 jan 1013 """Return the selected rows of the layer as they are returned
461     by Layer.SelectShapes().
462     """
463 bh 34 if self.layer is not None:
464 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