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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 939 - (show annotations)
Tue May 20 15:25:22 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: 12240 byte(s)
(LayerTableFrame.__init__): Change the
        Choices symbols to match those used in the table query method.
        Replace deprecated method calls on table with new method names.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # 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 from Thuban import _
11
12 from wxPython.wx import *
13 from wxPython.grid import *
14
15 from Thuban.Lib.connector import Publisher
16 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
17 FIELDTYPE_STRING
18 import view
19 from dialogs import NonModalDialog
20 from messages import SHAPES_SELECTED
21
22 wx_value_type_map = {FIELDTYPE_INT: wxGRID_VALUE_NUMBER,
23 FIELDTYPE_DOUBLE: wxGRID_VALUE_FLOAT,
24 FIELDTYPE_STRING: wxGRID_VALUE_STRING}
25
26 ROW_SELECTED = "ROW_SELECTED"
27
28
29 class DataTable(wxPyGridTableBase):
30
31 """Wrapper around a Thuban table object suitable for a wxGrid"""
32
33 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 self.num_cols = table.NumColumns()
44 self.num_rows = table.NumRows()
45
46 self.columns = []
47 for i in range(self.num_cols):
48 col = table.Column(i)
49 self.columns.append((col.name, wx_value_type_map[col.type]))
50
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 record = self.table.ReadRowAsDict(row)
70 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 class TableGrid(wxGrid, Publisher):
101
102 """A grid view for a Thuban table
103
104 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 def __init__(self, parent, table = None):
113 wxGrid.__init__(self, parent, -1)
114
115 self.allow_messages_count = 0
116
117 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 # would need to keep a reference to it and call its Destroy
122 # method later.
123 self.SetTable(self.table, true)
124
125 #self.SetMargins(0,0)
126
127 # 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 self.SetSelectionMode(wxGrid.wxGridSelectRows)
134
135 EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect)
136 EVT_GRID_SELECT_CELL(self, self.OnSelectCell)
137
138 def SetTableObject(self, table):
139 self.table.SetTable(table)
140
141 def OnRangeSelect(self, event):
142 rows = dict([(i, 0) for i in self.GetSelectedRows()])
143
144 # if we're selecting we need to include the selected range and
145 # make sure that the current row is also included, which may
146 # not be the case if you just click on a single row!
147 if event.Selecting():
148 for i in range(event.GetTopRow(), event.GetBottomRow() + 1):
149 rows[i] = 0
150 rows[event.GetTopLeftCoords().GetRow()] = 0
151
152 self.issue(ROW_SELECTED, rows.keys())
153 event.Skip()
154
155 def OnSelectCell(self, event):
156 self.issue(ROW_SELECTED, self.GetSelectedRows())
157 event.Skip()
158
159 def disallow_messages(self):
160 """Disallow messages to be send.
161
162 This method only increases a counter so that calls to
163 disallow_messages and allow_messages can be nested. Only the
164 outermost calls will actually switch message sending on and off.
165 """
166 self.allow_messages_count += 1
167
168 def allow_messages(self):
169 """Allow messages to be send.
170
171 This method only decreases a counter so that calls to
172 disallow_messages and allow_messages can be nested. Only the
173 outermost calls will actually switch message sending on and off.
174 """
175 self.allow_messages_count -= 1
176
177 def issue(self, *args):
178 """Issue a message unless disallowed.
179
180 See the allow_messages and disallow_messages methods.
181 """
182 if self.allow_messages_count == 0:
183 Publisher.issue(self, *args)
184
185
186 class LayerTableGrid(TableGrid):
187
188 """Table grid for the layer tables.
189
190 The LayerTableGrid is basically the same as a TableGrid but it's
191 selection is usually coupled to the selected object in the map.
192 """
193
194 def select_shapes(self, layer, shapes):
195 """Select the row corresponding to the specified shape and layer
196
197 If layer is not the layer the table is associated with do
198 nothing. If shape or layer is None also do nothing.
199 """
200 if layer is not None \
201 and layer.table is self.table.table:
202
203 self.disallow_messages()
204 try:
205 self.ClearSelection()
206 if len(shapes) > 0:
207 #
208 # keep track of the lowest id so we can make it
209 # the first visible item
210 #
211 first = shapes[0]
212
213 for shape in shapes:
214 self.SelectRow(shape, True)
215 if shape < first:
216 first = shape
217
218 self.SetGridCursor(first, 0)
219 self.MakeCellVisible(first, 0)
220 finally:
221 self.allow_messages()
222
223
224 class TableFrame(NonModalDialog):
225
226 """Frame that displays a Thuban table in a grid view"""
227
228 def __init__(self, parent, name, title, table):
229 NonModalDialog.__init__(self, parent, name, title)
230 self.table = table
231 self.grid = self.make_grid(self.table)
232
233 def make_grid(self, table):
234 """Return the table grid to use in the frame.
235
236 The default implementation returns a TableGrid instance.
237 Override in derived classes to use different grid classes.
238 """
239 return TableGrid(self, table)
240
241
242 ID_QUERY = 4001
243 ID_SAVEAS = 4002
244
245 class LayerTableFrame(TableFrame):
246
247 """Frame that displays a layer table in a grid view
248
249 A LayerTableFrame is TableFrame whose selection is connected to the
250 selected object in a map.
251 """
252
253 def __init__(self, parent, name, title, layer, table):
254 TableFrame.__init__(self, parent, name, title, table)
255 self.layer = layer
256 self.grid.Subscribe(ROW_SELECTED, self.rows_selected)
257 self.parent.Subscribe(SHAPES_SELECTED, self.select_shapes)
258
259 self.combo_fields = wxComboBox(self, -1, style=wxCB_READONLY)
260 self.choice_comp = wxChoice(self, -1,
261 choices=["<", "<=", "==", "!=", ">=", ">"])
262 self.combo_value = wxComboBox(self, -1)
263 self.choice_action = wxChoice(self, -1,
264 choices=[_("Replace Selection"),
265 _("Refine Selection"),
266 _("Add to Selection")])
267
268 button_query = wxButton(self, ID_QUERY, _("Query"))
269 button_saveas = wxButton(self, ID_SAVEAS, _("Save As..."))
270
271 self.grid.SetSize((400, 200))
272
273 self.combo_value.Append("")
274 for i in range(table.NumColumns()):
275 name = table.Column(i).name
276 self.combo_fields.Append(name)
277 self.combo_value.Append(name)
278
279 # assume at least one field?
280 self.combo_fields.SetSelection(0)
281 self.combo_value.SetSelection(0)
282
283 topBox = wxBoxSizer(wxVERTICAL)
284
285 sizer = wxStaticBoxSizer(wxStaticBox(self, -1, _("Selections")),
286 wxHORIZONTAL)
287 sizer.Add(self.combo_fields, 1, wxEXPAND|wxALL, 4)
288 sizer.Add(self.choice_comp, 0, wxALL, 4)
289 sizer.Add(self.combo_value, 1, wxEXPAND|wxALL, 4)
290 sizer.Add(self.choice_action, 0, wxALL, 4)
291 sizer.Add(button_query, 0, wxALL, 4)
292 sizer.Add(40, 20, 0, wxALL, 4)
293 sizer.Add(button_saveas, 0, wxALL, 4)
294
295 topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)
296 topBox.Add(self.grid, 1, wxEXPAND|wxALL, 0)
297
298 self.SetAutoLayout(True)
299 self.SetSizer(topBox)
300 topBox.Fit(self)
301 topBox.SetSizeHints(self)
302
303 EVT_BUTTON(self, ID_QUERY, self.OnQuery)
304 EVT_BUTTON(self, ID_SAVEAS, self.OnSaveAs)
305
306 def OnQuery(self, event):
307 wxBeginBusyCursor()
308
309 if self.combo_value.GetSelection() < 1:
310 value = self.combo_value.GetValue()
311 else:
312 value = self.table.Column(self.combo_value.GetValue())
313
314 ids = self.table.SimpleQuery(
315 self.table.Column(self.combo_fields.GetStringSelection()),
316 self.choice_comp.GetStringSelection(),
317 value)
318
319 choice = self.choice_action.GetSelection()
320
321 if choice == 0:
322 # Replace Selection
323 self.grid.ClearSelection()
324 for id in ids:
325 self.grid.SelectRow(id, True)
326 elif choice == 1:
327 # Refine Selection
328 sel = dict([(i, 0) for i in self.parent.SelectedShapes()])
329 self.grid.ClearSelection()
330 for id in filter(sel.has_key, ids):
331 self.grid.SelectRow(id, True)
332 elif choice == 2:
333 # Add to Selection
334 for id in ids:
335 self.grid.SelectRow(id, True)
336
337 wxEndBusyCursor()
338
339 def OnSaveAs(self, event):
340 dlg = wxFileDialog(self, _("Save Table As"), ".", "",
341 "DBF Files (*.dbf)|*.dbf|" +
342 "CSV Files (*.csv)|*.csv|" +
343 "All Files (*.*)|*.*",
344 wxSAVE|wxOVERWRITE_PROMPT)
345 if dlg.ShowModal() == wxID_OK:
346 pass
347
348 dlg.Destroy()
349
350 def make_grid(self, table):
351 """Override the derived method to return a LayerTableGrid.
352 """
353 return LayerTableGrid(self, table)
354
355 def OnClose(self, event):
356 self.parent.Unsubscribe(SHAPES_SELECTED, self.select_shapes)
357 TableFrame.OnClose(self, event)
358
359 def select_shapes(self, layer, shapes):
360 """Subscribed to the SHAPES_SELECTED message.
361
362 If shapes contains exactly one shape id, select that shape in
363 the grid. Otherwise deselect all.
364 """
365 self.grid.select_shapes(layer, shapes)
366
367 def rows_selected(self, rows):
368 if self.layer is not None:
369 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