##// END OF EJS Templates
Add new commands refresh and refresh_timer (mapped to "R"/"F5"...
walter.doerwald -
Show More
@@ -1,914 +1,1121 b''
1 # -*- coding: iso-8859-1 -*-
1 # -*- coding: iso-8859-1 -*-
2
2
3 import ipipe, os, webbrowser, urllib
3 import ipipe, os, webbrowser, urllib
4 from IPython import ipapi
4 import wx
5 import wx
5 import wx.grid, wx.html
6 import wx.grid, wx.html
6
7
7 try:
8 try:
8 sorted
9 sorted
9 except NameError:
10 except NameError:
10 from ipipe import sorted
11 from ipipe import sorted
11
12
12
13
13 __all__ = ["igrid"]
14 __all__ = ["igrid"]
14
15
15
16
16 help = """
17 help = """
17 <?xml version='1.0' encoding='iso-8859-1'?>
18 <?xml version='1.0' encoding='iso-8859-1'?>
18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
19 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
19 <html>
20 <html>
20 <head>
21 <head>
21 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
22 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
22 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
23 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
23 <title>igrid help</title>
24 <title>igrid help</title>
24 </head>
25 </head>
25 <body>
26 <body>
26 <h1>igrid help</h1>
27 <h1>igrid help</h1>
27
28
28
29
29 <h2>Commands</h2>
30 <h2>Commands</h2>
30
31
31
32
32 <h3>pick (P)</h3>
33 <h3>pick (P)</h3>
33 <p>Pick the whole row (object is available as "_")</p>
34 <p>Pick the whole row (object is available as "_")</p>
34
35
35 <h3>pickattr (Shift-P)</h3>
36 <h3>pickattr (Shift-P)</h3>
36 <p>Pick the attribute under the cursor</p>
37 <p>Pick the attribute under the cursor</p>
37
38
38 <h3>pickallattrs (Shift-C)</h3>
39 <h3>pickallattrs (Shift-C)</h3>
39 <p>Pick' the complete column under the cursor (i.e. the attribute under the
40 <p>Pick the complete column under the cursor (i.e. the attribute under the
40 cursor) from all currently fetched objects. These attributes will be returned
41 cursor) from all currently fetched objects. These attributes will be returned
41 as a list.</p>
42 as a list.</p>
42
43
44 <h3>pickinput (I)</h3>
45 <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p>
46
47 <h3>pickinputattr (Shift-I)</h3>
48 <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p>
49
43 <h3>enter (E)</h3>
50 <h3>enter (E)</h3>
44 <p>Enter the object under the cursor. (what this mean depends on the object
51 <p>Enter the object under the cursor. (what this mean depends on the object
45 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
52 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
46
53
47 <h3>enterattr (Shift-E)</h3>
54 <h3>enterattr (Shift-E)</h3>
48 <p>Enter the attribute under the cursor.</p>
55 <p>Enter the attribute under the cursor.</p>
49
56
50 <h3>detail (D)</h3>
57 <h3>detail (D)</h3>
51 <p>Show a detail view of the object under the cursor. This shows the name,
58 <p>Show a detail view of the object under the cursor. This shows the name,
52 type, doc string and value of the object attributes (and it might show more
59 type, doc string and value of the object attributes (and it might show more
53 attributes than in the list view, depending on the object).</p>
60 attributes than in the list view, depending on the object).</p>
54
61
55 <h3>detailattr (Shift-D)</h3>
62 <h3>detailattr (Shift-D)</h3>
56 <p>Show a detail view of the attribute under the cursor.</p>
63 <p>Show a detail view of the attribute under the cursor.</p>
57
64
58 <h3>pickrows (M)</h3>
65 <h3>pickrows (M)</h3>
59 <p>Pick multiple selected rows (M)</p>
66 <p>Pick multiple selected rows (M)</p>
60
67
61 <h3>pickrowsattr (CTRL-M)</h3>
68 <h3>pickrowsattr (CTRL-M)</h3>
62 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
69 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
63
70
64 <h3>find (CTRL-F)</h3>
71 <h3>find (CTRL-F)</h3>
65 <p>Find text</p>
72 <p>Find text</p>
66
73
74 <h3>find_expression (CTRL-Shift-F)</h3>
75 <p>Find entries matching an expression</p>
76
67 <h3>find_next (F3)</h3>
77 <h3>find_next (F3)</h3>
68 <p>Find next occurrence of the searchtext</p>
78 <p>Find next occurrence</p>
69
79
70 <h3>find_previous (Shift-F3)</h3>
80 <h3>find_previous (Shift-F3)</h3>
71 <p>Find previous occurrence of the searchtext </p>
81 <p>Find previous occurrence</p>
72
82
73 <h3>sortattrasc (V)</h3>
83 <h3>sortattrasc (V)</h3>
74 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
84 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
75
85
76 <h3>sortattrdesc (Shift-V)</h3>
86 <h3>sortattrdesc (Shift-V)</h3>
77 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
87 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
78
88
89 <h3>refresh_once (R, F5)</h3>
90 <p>Refreshes the display by restarting the iterator</p>
91
92 <h3>refresh_every_second</h3>
93 <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p>
94
95 <h3>refresh_interval</h3>
96 <p>Refreshes the display by restarting the iterator every X ms (X is a custom interval set by the user) until stopped by stop_refresh.</p>
97
98 <h3>stop_refresh</h3>
99 <p>Stops all refresh timers.</p>
100
79 <h3>leave (Backspace, DEL, X)</h3>
101 <h3>leave (Backspace, DEL, X)</h3>
80 <p>Close current tab (and all the tabs to the right of the current one).</h3>
102 <p>Close current tab (and all the tabs to the right of the current one).</h3>
81
103
82 <h3>quit (ESC,Q)</h3>
104 <h3>quit (ESC,Q)</h3>
83 <p>Quit igrid and return to the IPython prompt.</p>
105 <p>Quit igrid and return to the IPython prompt.</p>
84
106
85
107
86 <h2>Navigation</h2>
108 <h2>Navigation</h2>
87
109
88
110
89 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
111 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
90
112
91 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
113 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
92
114
93 <h3>Move the cursor one column to the left (&lt;)</h3>
115 <h3>Move the cursor one column to the left (&lt;)</h3>
94
116
95 <h3>Move the cursor one column to the right (&gt;)</h3>
117 <h3>Move the cursor one column to the right (&gt;)</h3>
96
118
97 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
119 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
98
120
99 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
121 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
100
122
101 </body>
123 </body>
102 </html>
124 </html>
125
103 """
126 """
104
127
105
128
106 class IGridRenderer(wx.grid.PyGridCellRenderer):
129 class IGridRenderer(wx.grid.PyGridCellRenderer):
107 """
130 """
108 This is a custom renderer for our IGridGrid
131 This is a custom renderer for our IGridGrid
109 """
132 """
110 def __init__(self, table):
133 def __init__(self, table):
111 self.maxchars = 200
134 self.maxchars = 200
112 self.table = table
135 self.table = table
113 self.colormap = (
136 self.colormap = (
114 ( 0, 0, 0),
137 ( 0, 0, 0),
115 (174, 0, 0),
138 (174, 0, 0),
116 ( 0, 174, 0),
139 ( 0, 174, 0),
117 (174, 174, 0),
140 (174, 174, 0),
118 ( 0, 0, 174),
141 ( 0, 0, 174),
119 (174, 0, 174),
142 (174, 0, 174),
120 ( 0, 174, 174),
143 ( 0, 174, 174),
121 ( 64, 64, 64)
144 ( 64, 64, 64)
122 )
145 )
123
146
124 wx.grid.PyGridCellRenderer.__init__(self)
147 wx.grid.PyGridCellRenderer.__init__(self)
125
148
126 def _getvalue(self, row, col):
149 def _getvalue(self, row, col):
127 try:
150 try:
128 value = self.table._displayattrs[col].value(self.table.items[row])
151 value = self.table._displayattrs[col].value(self.table.items[row])
129 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
152 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
130 except Exception, exc:
153 except Exception, exc:
131 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
154 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
132 return (align, text)
155 return (align, text)
133
156
134 def GetBestSize(self, grid, attr, dc, row, col):
157 def GetBestSize(self, grid, attr, dc, row, col):
135 text = grid.GetCellValue(row, col)
158 text = grid.GetCellValue(row, col)
136 (align, text) = self._getvalue(row, col)
159 (align, text) = self._getvalue(row, col)
137 dc.SetFont(attr.GetFont())
160 dc.SetFont(attr.GetFont())
138 (w, h) = dc.GetTextExtent(str(text))
161 (w, h) = dc.GetTextExtent(str(text))
139 return wx.Size(min(w+2, 600), h+2) # add border
162 return wx.Size(min(w+2, 600), h+2) # add border
140
163
141 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
164 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
142 """
165 """
143 Takes care of drawing everything in the cell; aligns the text
166 Takes care of drawing everything in the cell; aligns the text
144 """
167 """
145 text = grid.GetCellValue(row, col)
168 text = grid.GetCellValue(row, col)
146 (align, text) = self._getvalue(row, col)
169 (align, text) = self._getvalue(row, col)
147 if isSelected:
170 if isSelected:
148 bg = grid.GetSelectionBackground()
171 bg = grid.GetSelectionBackground()
149 else:
172 else:
150 bg = ["white", (240, 240, 240)][row%2]
173 bg = ["white", (240, 240, 240)][row%2]
151 dc.SetTextBackground(bg)
174 dc.SetTextBackground(bg)
152 dc.SetBrush(wx.Brush(bg, wx.SOLID))
175 dc.SetBrush(wx.Brush(bg, wx.SOLID))
153 dc.SetPen(wx.TRANSPARENT_PEN)
176 dc.SetPen(wx.TRANSPARENT_PEN)
154 dc.SetFont(attr.GetFont())
177 dc.SetFont(attr.GetFont())
155 dc.DrawRectangleRect(rect)
178 dc.DrawRectangleRect(rect)
156 dc.SetClippingRect(rect)
179 dc.SetClippingRect(rect)
157 # Format the text
180 # Format the text
158 if align == -1: # left alignment
181 if align == -1: # left alignment
159 (width, height) = dc.GetTextExtent(str(text))
182 (width, height) = dc.GetTextExtent(str(text))
160 x = rect[0]+1
183 x = rect[0]+1
161 y = rect[1]+0.5*(rect[3]-height)
184 y = rect[1]+0.5*(rect[3]-height)
162
185
163 for (style, part) in text:
186 for (style, part) in text:
164 if isSelected:
187 if isSelected:
165 fg = grid.GetSelectionForeground()
188 fg = grid.GetSelectionForeground()
166 else:
189 else:
167 fg = self.colormap[style.fg]
190 fg = self.colormap[style.fg]
168 dc.SetTextForeground(fg)
191 dc.SetTextForeground(fg)
169 (w, h) = dc.GetTextExtent(part)
192 (w, h) = dc.GetTextExtent(part)
170 dc.DrawText(part, x, y)
193 dc.DrawText(part, x, y)
171 x += w
194 x += w
172 elif align == 0: # center alignment
195 elif align == 0: # center alignment
173 (width, height) = dc.GetTextExtent(str(text))
196 (width, height) = dc.GetTextExtent(str(text))
174 x = rect[0]+0.5*(rect[2]-width)
197 x = rect[0]+0.5*(rect[2]-width)
175 y = rect[1]+0.5*(rect[3]-height)
198 y = rect[1]+0.5*(rect[3]-height)
176 for (style, part) in text:
199 for (style, part) in text:
177 if isSelected:
200 if isSelected:
178 fg = grid.GetSelectionForeground()
201 fg = grid.GetSelectionForeground()
179 else:
202 else:
180 fg = self.colormap[style.fg]
203 fg = self.colormap[style.fg]
181 dc.SetTextForeground(fg)
204 dc.SetTextForeground(fg)
182 (w, h) = dc.GetTextExtent(part)
205 (w, h) = dc.GetTextExtent(part)
183 dc.DrawText(part, x, y)
206 dc.DrawText(part, x, y)
184 x += w
207 x += w
185 else: # right alignment
208 else: # right alignment
186 (width, height) = dc.GetTextExtent(str(text))
209 (width, height) = dc.GetTextExtent(str(text))
187 x = rect[0]+rect[2]-1
210 x = rect[0]+rect[2]-1
188 y = rect[1]+0.5*(rect[3]-height)
211 y = rect[1]+0.5*(rect[3]-height)
189 for (style, part) in reversed(text):
212 for (style, part) in reversed(text):
190 (w, h) = dc.GetTextExtent(part)
213 (w, h) = dc.GetTextExtent(part)
191 x -= w
214 x -= w
192 if isSelected:
215 if isSelected:
193 fg = grid.GetSelectionForeground()
216 fg = grid.GetSelectionForeground()
194 else:
217 else:
195 fg = self.colormap[style.fg]
218 fg = self.colormap[style.fg]
196 dc.SetTextForeground(fg)
219 dc.SetTextForeground(fg)
197 dc.DrawText(part, x, y)
220 dc.DrawText(part, x, y)
198 dc.DestroyClippingRegion()
221 dc.DestroyClippingRegion()
199
222
200 def Clone(self):
223 def Clone(self):
201 return IGridRenderer(self.table)
224 return IGridRenderer(self.table)
202
225
203
226
204 class IGridTable(wx.grid.PyGridTableBase):
227 class IGridTable(wx.grid.PyGridTableBase):
205 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
228 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
206 # ``GetValue()`` does not get any values (or at least it does not return
229 # ``GetValue()`` does not get any values (or at least it does not return
207 # anything, accessing the values is done by the renderer)
230 # anything, accessing the values is done by the renderer)
208 # but rather tries to fetch the objects which were requested into the table.
231 # but rather tries to fetch the objects which were requested into the table.
209 # General behaviour is: Fetch the first X objects. If the user scrolls down
232 # General behaviour is: Fetch the first X objects. If the user scrolls down
210 # to the last object another bunch of X objects is fetched (if possible)
233 # to the last object another bunch of X objects is fetched (if possible)
211 def __init__(self, input, fontsize, *attrs):
234 def __init__(self, input, fontsize, *attrs):
212 wx.grid.PyGridTableBase.__init__(self)
235 wx.grid.PyGridTableBase.__init__(self)
213 self.input = input
236 self.input = input
214 self.iterator = ipipe.xiter(input)
237 self.iterator = ipipe.xiter(input)
215 self.items = []
238 self.items = []
216 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
239 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
217 self._displayattrs = self.attrs[:]
240 self._displayattrs = self.attrs[:]
218 self._displayattrset = set(self.attrs)
241 self._displayattrset = set(self.attrs)
219 self._sizing = False
220 self.fontsize = fontsize
242 self.fontsize = fontsize
221 self._fetch(1)
243 self._fetch(1)
244 self.timer = wx.Timer()
245 self.timer.Bind(wx.EVT_TIMER, self.refresh_content)
222
246
223 def GetAttr(self, *args):
247 def GetAttr(self, *args):
224 attr = wx.grid.GridCellAttr()
248 attr = wx.grid.GridCellAttr()
225 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
249 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
226 return attr
250 return attr
227
251
228 def GetNumberRows(self):
252 def GetNumberRows(self):
229 return len(self.items)
253 return len(self.items)
230
254
231 def GetNumberCols(self):
255 def GetNumberCols(self):
232 return len(self._displayattrs)
256 return len(self._displayattrs)
233
257
234 def GetColLabelValue(self, col):
258 def GetColLabelValue(self, col):
235 if col < len(self._displayattrs):
259 if col < len(self._displayattrs):
236 return self._displayattrs[col].name()
260 return self._displayattrs[col].name()
237 else:
261 else:
238 return ""
262 return ""
239
263
240 def GetRowLabelValue(self, row):
264 def GetRowLabelValue(self, row):
241 return str(row)
265 return str(row)
242
266
243 def IsEmptyCell(self, row, col):
267 def IsEmptyCell(self, row, col):
244 return False
268 return False
245
269
246 def _append(self, item):
270 def _append(self, item):
247 self.items.append(item)
271 self.items.append(item)
248 # Nothing to do if the set of attributes has been fixed by the user
272 # Nothing to do if the set of attributes has been fixed by the user
249 if not self.attrs:
273 if not self.attrs:
250 for attr in ipipe.xattrs(item):
274 for attr in ipipe.xattrs(item):
251 attr = ipipe.upgradexattr(attr)
275 attr = ipipe.upgradexattr(attr)
252 if attr not in self._displayattrset:
276 if attr not in self._displayattrset:
253 self._displayattrs.append(attr)
277 self._displayattrs.append(attr)
254 self._displayattrset.add(attr)
278 self._displayattrset.add(attr)
255
279
256 def _fetch(self, count):
280 def _fetch(self, count):
257 # Try to fill ``self.items`` with at least ``count`` objects.
281 # Try to fill ``self.items`` with at least ``count`` objects.
258 have = len(self.items)
282 have = len(self.items)
259 while self.iterator is not None and have < count:
283 while self.iterator is not None and have < count:
260 try:
284 try:
261 item = self.iterator.next()
285 item = self.iterator.next()
262 except StopIteration:
286 except StopIteration:
263 self.iterator = None
287 self.iterator = None
264 break
288 break
265 except (KeyboardInterrupt, SystemExit):
289 except (KeyboardInterrupt, SystemExit):
266 raise
290 raise
267 except Exception, exc:
291 except Exception, exc:
268 have += 1
292 have += 1
269 self._append(exc)
293 self._append(exc)
270 self.iterator = None
294 self.iterator = None
271 break
295 break
272 else:
296 else:
273 have += 1
297 have += 1
274 self._append(item)
298 self._append(item)
275
299
276 def GetValue(self, row, col):
300 def GetValue(self, row, col):
277 # some kind of dummy-function: does not return anything but "";
301 # some kind of dummy-function: does not return anything but "";
278 # (The value isn't use anyway)
302 # (The value isn't use anyway)
279 # its main task is to trigger the fetch of new objects
303 # its main task is to trigger the fetch of new objects
280 had_cols = self._displayattrs[:]
304 sizing_needed = False
305 had_cols = len(self._displayattrs)
281 had_rows = len(self.items)
306 had_rows = len(self.items)
282 if row == had_rows - 1 and self.iterator is not None and not self._sizing:
307 if row == had_rows - 1 and self.iterator is not None:
283 self._fetch(row + 20)
308 self._fetch(row + 20)
309 sizing_needed = True
284 have_rows = len(self.items)
310 have_rows = len(self.items)
285 have_cols = len(self._displayattrs)
311 have_cols = len(self._displayattrs)
286 if have_rows > had_rows:
312 if have_rows > had_rows:
287 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
313 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
288 self.GetView().ProcessTableMessage(msg)
314 self.GetView().ProcessTableMessage(msg)
289 self._sizing = True
315 sizing_needed = True
290 self.GetView().AutoSizeColumns(False)
291 self._sizing = False
292 if row >= have_rows:
316 if row >= have_rows:
293 return ""
317 return ""
294 if self._displayattrs != had_cols:
318 if have_cols != had_cols:
295 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - len(had_cols))
319 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - had_cols)
296 self.GetView().ProcessTableMessage(msg)
320 self.GetView().ProcessTableMessage(msg)
321 sizing_needed = True
322 if sizing_needed:
323 self.GetView().AutoSizeColumns(False)
297 return ""
324 return ""
298
325
299 def SetValue(self, row, col, value):
326 def SetValue(self, row, col, value):
300 pass
327 pass
301
328
329 def refresh_content(self, event):
330 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
331 self.GetView().ProcessTableMessage(msg)
332 self.iterator = ipipe.xiter(self.input)
333 self.items = []
334 self.attrs = [] # _append will calculate new displayattrs
335 self._fetch(1) # fetch one...
336 if self.items:
337 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
338 self.GetView().ProcessTableMessage(msg)
339 self.GetValue(0, 0) # and trigger "fetch next 20"
340 item = self.items[0]
341 self.GetView().AutoSizeColumns(False)
342 panel = self.GetView().GetParent()
343 nb = panel.GetParent()
344 current = nb.GetSelection()
345 if nb.GetPage(current) == panel:
346 self.GetView().set_footer(item)
302
347
303 class IGridGrid(wx.grid.Grid):
348 class IGridGrid(wx.grid.Grid):
304 # The actual grid
349 # The actual grid
305 # all methods for selecting/sorting/picking/... data are implemented here
350 # all methods for selecting/sorting/picking/... data are implemented here
306 def __init__(self, panel, input, *attrs):
351 def __init__(self, panel, input, *attrs):
307 wx.grid.Grid.__init__(self, panel)
352 wx.grid.Grid.__init__(self, panel)
308 fontsize = 9
353 fontsize = 9
309 self.input = input
354 self.input = input
310 self.table = IGridTable(self.input, fontsize, *attrs)
355 self.table = IGridTable(self.input, fontsize, *attrs)
311 self.SetTable(self.table, True)
356 self.SetTable(self.table, True)
312 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
357 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
313 self.SetDefaultRenderer(IGridRenderer(self.table))
358 self.SetDefaultRenderer(IGridRenderer(self.table))
314 self.EnableEditing(False)
359 self.EnableEditing(False)
315 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
360 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
316 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
361 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
317 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
362 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
318 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
363 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
319 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
364 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
320 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
365 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
321 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
366 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
322 self.current_selection = set()
367 self.current_selection = set()
323 self.maxchars = 200
368 self.maxchars = 200
324
369
325 def on_label_leftclick(self, event):
370 def on_label_leftclick(self, event):
326 event.Skip()
371 event.Skip()
327
372
328 def error_output(self, text):
373 def error_output(self, text):
329 wx.Bell()
374 wx.Bell()
330 frame = self.GetParent().GetParent().GetParent()
375 frame = self.GetParent().GetParent().GetParent()
331 frame.SetStatusText(text)
376 frame.SetStatusText(str(text))
332
377
333 def _on_selected_range(self, event):
378 def _on_selected_range(self, event):
334 # Internal update to the selection tracking lists
379 # Internal update to the selection tracking lists
335 if event.Selecting():
380 if event.Selecting():
336 # adding to the list...
381 # adding to the list...
337 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
382 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
338 else:
383 else:
339 # removal from list
384 # removal from list
340 for index in xrange( event.GetTopRow(), event.GetBottomRow()+1):
385 for index in xrange(event.GetTopRow(), event.GetBottomRow()+1):
341 self.current_selection.discard(index)
386 self.current_selection.discard(index)
342 event.Skip()
387 event.Skip()
343
388
344 def _on_selected_cell(self, event):
389 def _on_selected_cell(self, event):
345 # Internal update to the selection tracking list
390 # Internal update to the selection tracking list
346 self.current_selection = set([event.GetRow()])
391 self.current_selection = set([event.GetRow()])
347 event.Skip()
392 event.Skip()
348
393
349 def sort(self, key, reverse=False):
394 def sort(self, key, reverse=False):
350 """
395 """
351 Sort the current list of items using the key function ``key``. If
396 Sort the current list of items using the key function ``key``. If
352 ``reverse`` is true the sort order is reversed.
397 ``reverse`` is true the sort order is reversed.
353 """
398 """
354 row = self.GetGridCursorRow()
399 row = self.GetGridCursorRow()
355 col = self.GetGridCursorCol()
400 col = self.GetGridCursorCol()
356 curitem = self.table.items[row] # Remember where the cursor is now
401 curitem = self.table.items[row] # Remember where the cursor is now
357 # Sort items
402 # Sort items
358 def realkey(item):
403 def realkey(item):
359 try:
404 try:
360 return key(item)
405 return key(item)
361 except (KeyboardInterrupt, SystemExit):
406 except (KeyboardInterrupt, SystemExit):
362 raise
407 raise
363 except Exception:
408 except Exception:
364 return None
409 return None
365 try:
410 try:
366 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
411 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
367 except TypeError, exc:
412 except TypeError, exc:
368 self.error_output("Exception encountered: %s" % exc)
413 self.error_output("Exception encountered: %s" % exc)
369 return
414 return
370 # Find out where the object under the cursor went
415 # Find out where the object under the cursor went
371 for (i, item) in enumerate(self.table.items):
416 for (i, item) in enumerate(self.table.items):
372 if item is curitem:
417 if item is curitem:
373 self.SetGridCursor(i,col)
418 self.SetGridCursor(i,col)
374 self.MakeCellVisible(i,col)
419 self.MakeCellVisible(i,col)
375 self.Refresh()
420 self.Refresh()
376
421
377 def sortattrasc(self):
422 def sortattrasc(self):
378 """
423 """
379 Sort in ascending order; sorting criteria is the current attribute
424 Sort in ascending order; sorting criteria is the current attribute
380 """
425 """
381 col = self.GetGridCursorCol()
426 col = self.GetGridCursorCol()
382 attr = self.table._displayattrs[col]
427 attr = self.table._displayattrs[col]
383 frame = self.GetParent().GetParent().GetParent()
428 frame = self.GetParent().GetParent().GetParent()
384 if attr is ipipe.noitem:
429 if attr is ipipe.noitem:
385 self.error_output("no column under cursor")
430 self.error_output("no column under cursor")
386 return
431 return
387 frame.SetStatusText("sort by %s (ascending)" % attr.name())
432 frame.SetStatusText("sort by %s (ascending)" % attr.name())
388 def key(item):
433 def key(item):
389 try:
434 try:
390 return attr.value(item)
435 return attr.value(item)
391 except (KeyboardInterrupt, SystemExit):
436 except (KeyboardInterrupt, SystemExit):
392 raise
437 raise
393 except Exception:
438 except Exception:
394 return None
439 return None
395 self.sort(key)
440 self.sort(key)
396
441
397 def sortattrdesc(self):
442 def sortattrdesc(self):
398 """
443 """
399 Sort in descending order; sorting criteria is the current attribute
444 Sort in descending order; sorting criteria is the current attribute
400 """
445 """
401 col = self.GetGridCursorCol()
446 col = self.GetGridCursorCol()
402 attr = self.table._displayattrs[col]
447 attr = self.table._displayattrs[col]
403 frame = self.GetParent().GetParent().GetParent()
448 frame = self.GetParent().GetParent().GetParent()
404 if attr is ipipe.noitem:
449 if attr is ipipe.noitem:
405 self.error_output("no column under cursor")
450 self.error_output("no column under cursor")
406 return
451 return
407 frame.SetStatusText("sort by %s (descending)" % attr.name())
452 frame.SetStatusText("sort by %s (descending)" % attr.name())
408 def key(item):
453 def key(item):
409 try:
454 try:
410 return attr.value(item)
455 return attr.value(item)
411 except (KeyboardInterrupt, SystemExit):
456 except (KeyboardInterrupt, SystemExit):
412 raise
457 raise
413 except Exception:
458 except Exception:
414 return None
459 return None
415 self.sort(key, reverse=True)
460 self.sort(key, reverse=True)
416
461
417 def label_doubleclicked(self, event):
462 def label_doubleclicked(self, event):
418 row = event.GetRow()
463 row = event.GetRow()
419 col = event.GetCol()
464 col = event.GetCol()
420 if col == -1:
465 if col == -1:
421 self.enter(row)
466 self.enter(row)
422
467
423 def _getvalue(self, row, col):
468 def _getvalue(self, row, col):
424 """
469 """
425 Gets the text which is displayed at ``(row, col)``
470 Gets the text which is displayed at ``(row, col)``
426 """
471 """
427 try:
472 try:
428 value = self.table._displayattrs[col].value(self.table.items[row])
473 value = self.table._displayattrs[col].value(self.table.items[row])
429 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
474 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
430 except IndexError:
475 except IndexError:
431 raise IndexError
476 raise IndexError
432 except Exception, exc:
477 except Exception, exc:
433 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
478 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
434 return text
479 return text
435
480
436 def search(self, searchtext, startrow=0, startcol=0, search_forward=True):
481 def searchexpression(self, searchexp, startrow=None, search_forward=True ):
482 """
483 Find by expression
484 """
485 frame = self.GetParent().GetParent().GetParent()
486 if searchexp:
487 if search_forward:
488 if not startrow:
489 row = self.GetGridCursorRow()+1
490 else:
491 row = startrow + 1
492 while True:
493 try:
494 foo = self.table.GetValue(row, 0)
495 item = self.table.items[row]
496 try:
497 globals = ipipe.getglobals(None)
498 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
499 self.SetGridCursor(row, 0) # found something
500 self.MakeCellVisible(row, 0)
501 break
502 except (KeyboardInterrupt, SystemExit):
503 raise
504 except Exception, exc:
505 frame.SetStatusText(str(exc))
506 wx.Bell()
507 break # break on error
508 except IndexError:
509 return
510 row += 1
511 else:
512 if not startrow:
513 row = self.GetGridCursorRow() - 1
514 else:
515 row = startrow - 1
516 while True:
517 try:
518 foo = self.table.GetValue(row, 0)
519 item = self.table.items[row]
520 try:
521 globals = ipipe.getglobals(None)
522 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
523 self.SetGridCursor(row, 0) # found something
524 self.MakeCellVisible(row, 0)
525 break
526 except (KeyboardInterrupt, SystemExit):
527 raise
528 except Exception, exc:
529 frame.SetStatusText(str(exc))
530 wx.Bell()
531 break # break on error
532 except IndexError:
533 return
534 row -= 1
535
536
537 def search(self, searchtext, startrow=None, startcol=None, search_forward=True):
437 """
538 """
438 search for ``searchtext``, starting in ``(startrow, startcol)``;
539 search for ``searchtext``, starting in ``(startrow, startcol)``;
439 if ``search_forward`` is true the direction is "forward"
540 if ``search_forward`` is true the direction is "forward"
440 """
541 """
441 row = startrow
442 searchtext = searchtext.lower()
542 searchtext = searchtext.lower()
443 if search_forward:
543 if search_forward:
544 if startrow is not None and startcol is not None:
545 row = startrow
546 else:
547 startcol = self.GetGridCursorCol() + 1
548 row = self.GetGridCursorRow()
549 if startcol >= self.GetNumberCols():
550 startcol = 0
551 row += 1
444 while True:
552 while True:
445 for col in xrange(startcol, self.table.GetNumberCols()):
553 for col in xrange(startcol, self.table.GetNumberCols()):
446 try:
554 try:
447 foo = self.table.GetValue(row, col)
555 foo = self.table.GetValue(row, col)
448 text = self._getvalue(row, col)
556 text = self._getvalue(row, col)
449 if searchtext in text.string().lower():
557 if searchtext in text.string().lower():
450 self.SetGridCursor(row, col)
558 self.SetGridCursor(row, col)
451 self.MakeCellVisible(row, col)
559 self.MakeCellVisible(row, col)
452 return
560 return
453 except IndexError:
561 except IndexError:
454 return
562 return
455 startcol = 0
563 startcol = 0
456 row += 1
564 row += 1
457 else:
565 else:
566 if startrow is not None and startcol is not None:
567 row = startrow
568 else:
569 startcol = self.GetGridCursorCol() - 1
570 row = self.GetGridCursorRow()
571 if startcol < 0:
572 startcol = self.GetNumberCols() - 1
573 row -= 1
458 while True:
574 while True:
459 for col in xrange(startcol, -1, -1):
575 for col in xrange(startcol, -1, -1):
460 try:
576 try:
461 foo = self.table.GetValue(row, col)
577 foo = self.table.GetValue(row, col)
462 text = self._getvalue(row, col)
578 text = self._getvalue(row, col)
463 if searchtext in text.string().lower():
579 if searchtext in text.string().lower():
464 self.SetGridCursor(row, col)
580 self.SetGridCursor(row, col)
465 self.MakeCellVisible(row, col)
581 self.MakeCellVisible(row, col)
466 return
582 return
467 except IndexError:
583 except IndexError:
468 return
584 return
469 startcol = self.table.GetNumberCols()-1
585 startcol = self.table.GetNumberCols()-1
470 row -= 1
586 row -= 1
471
587
472 def key_pressed(self, event):
588 def key_pressed(self, event):
473 """
589 """
474 Maps pressed keys to functions
590 Maps pressed keys to functions
475 """
591 """
476 frame = self.GetParent().GetParent().GetParent()
592 frame = self.GetParent().GetParent().GetParent()
477 frame.SetStatusText("")
593 frame.SetStatusText("")
478 sh = event.ShiftDown()
594 sh = event.ShiftDown()
479 ctrl = event.ControlDown()
595 ctrl = event.ControlDown()
480
596
481 keycode = event.GetKeyCode()
597 keycode = event.GetKeyCode()
482 if keycode == ord("P"):
598 if keycode == ord("P"):
483 row = self.GetGridCursorRow()
599 row = self.GetGridCursorRow()
484 if sh:
600 if sh:
485 col = self.GetGridCursorCol()
601 col = self.GetGridCursorCol()
486 self.pickattr(row, col)
602 self.pickattr(row, col)
487 else:
603 else:
488 self.pick(row)
604 self.pick(row)
489 elif keycode == ord("M"):
605 elif keycode == ord("M"):
490 if ctrl:
606 if ctrl:
491 col = self.GetGridCursorCol()
607 col = self.GetGridCursorCol()
492 self.pickrowsattr(sorted(self.current_selection), col)
608 self.pickrowsattr(sorted(self.current_selection), col)
493 else:
609 else:
494 self.pickrows(sorted(self.current_selection))
610 self.pickrows(sorted(self.current_selection))
495 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
611 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
496 self.delete_current_notebook()
612 self.delete_current_notebook()
497 elif keycode in (ord("E"), ord("\r")):
613 elif keycode in (ord("E"), ord("\r")):
498 row = self.GetGridCursorRow()
614 row = self.GetGridCursorRow()
499 if sh:
615 if sh:
500 col = self.GetGridCursorCol()
616 col = self.GetGridCursorCol()
501 self.enterattr(row, col)
617 self.enterattr(row, col)
502 else:
618 else:
503 self.enter(row)
619 self.enter(row)
504 elif keycode == ord("E") and ctrl:
620 elif keycode == ord("E") and ctrl:
505 row = self.GetGridCursorRow()
621 row = self.GetGridCursorRow()
506 self.SetGridCursor(row, self.GetNumberCols()-1)
622 self.SetGridCursor(row, self.GetNumberCols()-1)
507 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
623 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
508 row = self.GetGridCursorRow()
624 row = self.GetGridCursorRow()
509 self.SetGridCursor(row, 0)
625 self.SetGridCursor(row, 0)
510 elif keycode == ord("C") and sh:
626 elif keycode == ord("C") and sh:
511 col = self.GetGridCursorCol()
627 col = self.GetGridCursorCol()
512 attr = self.table._displayattrs[col]
628 attr = self.table._displayattrs[col]
513 result = []
629 result = []
514 for i in xrange(self.GetNumberRows()):
630 for i in xrange(self.GetNumberRows()):
515 result.append(self.table._displayattrs[col].value(self.table.items[i]))
631 result.append(self.table._displayattrs[col].value(self.table.items[i]))
516 self.quit(result)
632 self.quit(result)
517 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
633 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
518 self.quit()
634 self.quit()
519 elif keycode == ord("<"):
635 elif keycode == ord("<"):
520 row = self.GetGridCursorRow()
636 row = self.GetGridCursorRow()
521 col = self.GetGridCursorCol()
637 col = self.GetGridCursorCol()
522 if not event.ShiftDown():
638 if not event.ShiftDown():
523 newcol = col - 1
639 newcol = col - 1
524 if newcol >= 0:
640 if newcol >= 0:
525 self.SetGridCursor(row, col - 1)
641 self.SetGridCursor(row, col - 1)
526 else:
642 else:
527 newcol = col + 1
643 newcol = col + 1
528 if newcol < self.GetNumberCols():
644 if newcol < self.GetNumberCols():
529 self.SetGridCursor(row, col + 1)
645 self.SetGridCursor(row, col + 1)
530 elif keycode == ord("D"):
646 elif keycode == ord("D"):
531 col = self.GetGridCursorCol()
647 col = self.GetGridCursorCol()
532 row = self.GetGridCursorRow()
648 row = self.GetGridCursorRow()
533 if not sh:
649 if not sh:
534 self.detail(row, col)
650 self.detail(row, col)
535 else:
651 else:
536 self.detail_attr(row, col)
652 self.detail_attr(row, col)
537 elif keycode == ord("F") and ctrl:
653 elif keycode == ord("F") and ctrl:
538 frame.enter_searchtext(event)
654 if sh:
655 frame.enter_searchexpression(event)
656 else:
657 frame.enter_searchtext(event)
539 elif keycode == wx.WXK_F3:
658 elif keycode == wx.WXK_F3:
540 if sh:
659 if sh:
541 frame.find_previous(event)
660 frame.find_previous(event)
542 else:
661 else:
543 frame.find_next(event)
662 frame.find_next(event)
544 elif keycode == ord("V"):
663 elif keycode == ord("V"):
545 if sh:
664 if sh:
546 self.sortattrdesc()
665 self.sortattrdesc()
547 else:
666 else:
548 self.sortattrasc()
667 self.sortattrasc()
549 elif keycode == wx.WXK_DOWN:
668 elif keycode == wx.WXK_DOWN:
550 row = self.GetGridCursorRow()
669 row = self.GetGridCursorRow()
551 try:
670 try:
552 item = self.table.items[row+1]
671 item = self.table.items[row+1]
553 except IndexError:
672 except IndexError:
554 item = self.table.items[row]
673 item = self.table.items[row]
555 self.set_footer(item)
674 self.set_footer(item)
556 event.Skip()
675 event.Skip()
557 elif keycode == wx.WXK_UP:
676 elif keycode == wx.WXK_UP:
558 row = self.GetGridCursorRow()
677 row = self.GetGridCursorRow()
559 if row >= 1:
678 if row >= 1:
560 item = self.table.items[row-1]
679 item = self.table.items[row-1]
561 else:
680 else:
562 item = self.table.items[row]
681 item = self.table.items[row]
563 self.set_footer(item)
682 self.set_footer(item)
564 event.Skip()
683 event.Skip()
565 elif keycode == wx.WXK_RIGHT:
684 elif keycode == wx.WXK_RIGHT:
566 row = self.GetGridCursorRow()
685 row = self.GetGridCursorRow()
567 item = self.table.items[row]
686 item = self.table.items[row]
568 self.set_footer(item)
687 self.set_footer(item)
569 event.Skip()
688 event.Skip()
570 elif keycode == wx.WXK_LEFT:
689 elif keycode == wx.WXK_LEFT:
571 row = self.GetGridCursorRow()
690 row = self.GetGridCursorRow()
572 item = self.table.items[row]
691 item = self.table.items[row]
573 self.set_footer(item)
692 self.set_footer(item)
574 event.Skip()
693 event.Skip()
694 elif keycode == ord("R") or keycode == wx.WXK_F5:
695 self.table.refresh_content(event)
696 elif keycode == ord("I"):
697 row = self.GetGridCursorRow()
698 if not sh:
699 self.pickinput(row)
700 else:
701 col = self.GetGridCursorCol()
702 self.pickinputattr(row, col)
575 else:
703 else:
576 event.Skip()
704 event.Skip()
577
705
578 def delete_current_notebook(self):
706 def delete_current_notebook(self):
579 """
707 """
580 deletes the current notebook tab
708 deletes the current notebook tab
581 """
709 """
582 panel = self.GetParent()
710 panel = self.GetParent()
583 nb = panel.GetParent()
711 nb = panel.GetParent()
584 current = nb.GetSelection()
712 current = nb.GetSelection()
585 count = nb.GetPageCount()
713 count = nb.GetPageCount()
586 if count > 1:
714 if count > 1:
587 for i in xrange(count-1, current-1, -1):
715 for i in xrange(count-1, current-1, -1):
588 nb.DeletePage(i)
716 nb.DeletePage(i)
589 nb.GetCurrentPage().grid.SetFocus()
717 nb.GetCurrentPage().grid.SetFocus()
590 else:
718 else:
591 frame = nb.GetParent()
719 frame = nb.GetParent()
592 frame.SetStatusText("This is the last level!")
720 frame.SetStatusText("This is the last level!")
593
721
594 def _doenter(self, value, *attrs):
722 def _doenter(self, value, *attrs):
595 """
723 """
596 "enter" a special item resulting in a new notebook tab
724 "enter" a special item resulting in a new notebook tab
597 """
725 """
598 panel = self.GetParent()
726 panel = self.GetParent()
599 nb = panel.GetParent()
727 nb = panel.GetParent()
600 frame = nb.GetParent()
728 frame = nb.GetParent()
601 current = nb.GetSelection()
729 current = nb.GetSelection()
602 count = nb.GetPageCount()
730 count = nb.GetPageCount()
603 try: # if we want to enter something non-iterable, e.g. a function
731 try: # if we want to enter something non-iterable, e.g. a function
604 if current + 1 == count and value is not self.input: # we have an event in the last tab
732 if current + 1 == count and value is not self.input: # we have an event in the last tab
605 frame._add_notebook(value, *attrs)
733 frame._add_notebook(value, *attrs)
606 elif value != self.input: # we have to delete all tabs newer than [panel] first
734 elif value != self.input: # we have to delete all tabs newer than [panel] first
607 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
735 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
608 nb.DeletePage(i)
736 nb.DeletePage(i)
609 frame._add_notebook(value)
737 frame._add_notebook(value)
610 except TypeError, exc:
738 except TypeError, exc:
611 if exc.__class__.__module__ == "exceptions":
739 if exc.__class__.__module__ == "exceptions":
612 msg = "%s: %s" % (exc.__class__.__name__, exc)
740 msg = "%s: %s" % (exc.__class__.__name__, exc)
613 else:
741 else:
614 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
742 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
615 frame.SetStatusText(msg)
743 frame.SetStatusText(msg)
616
744
617 def enterattr(self, row, col):
745 def enterattr(self, row, col):
618 try:
746 try:
619 attr = self.table._displayattrs[col]
747 attr = self.table._displayattrs[col]
620 value = attr.value(self.table.items[row])
748 value = attr.value(self.table.items[row])
621 except Exception, exc:
749 except Exception, exc:
622 self.error_output(str(exc))
750 self.error_output(str(exc))
623 else:
751 else:
624 self._doenter(value)
752 self._doenter(value)
625
753
626 def set_footer(self, item):
754 def set_footer(self, item):
627 frame = self.GetParent().GetParent().GetParent()
755 frame = self.GetParent().GetParent().GetParent()
628 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]))
756 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]), 0)
629
757
630 def enter(self, row):
758 def enter(self, row):
631 try:
759 try:
632 value = self.table.items[row]
760 value = self.table.items[row]
633 except Exception, exc:
761 except Exception, exc:
634 self.error_output(str(exc))
762 self.error_output(str(exc))
635 else:
763 else:
636 self._doenter(value)
764 self._doenter(value)
637
765
638 def detail(self, row, col):
766 def detail(self, row, col):
639 """
767 """
640 shows a detail-view of the current cell
768 shows a detail-view of the current cell
641 """
769 """
642 try:
770 try:
643 attr = self.table._displayattrs[col]
771 attr = self.table._displayattrs[col]
644 item = self.table.items[row]
772 item = self.table.items[row]
645 except Exception, exc:
773 except Exception, exc:
646 self.error_output(str(exc))
774 self.error_output(str(exc))
647 else:
775 else:
648 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
776 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
649 self._doenter(attrs)
777 self._doenter(attrs)
650
778
651 def detail_attr(self, row, col):
779 def detail_attr(self, row, col):
652 try:
780 try:
653 attr = self.table._displayattrs[col]
781 attr = self.table._displayattrs[col]
654 item = attr.value(self.table.items[row])
782 item = attr.value(self.table.items[row])
655 except Exception, exc:
783 except Exception, exc:
656 self.error_output(str(exc))
784 self.error_output(str(exc))
657 else:
785 else:
658 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
786 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
659 self._doenter(attrs)
787 self._doenter(attrs)
660
788
661 def quit(self, result=None):
789 def quit(self, result=None):
662 """
790 """
663 quit
791 quit
664 """
792 """
665 frame = self.GetParent().GetParent().GetParent()
793 frame = self.GetParent().GetParent().GetParent()
666 if frame.helpdialog:
794 if frame.helpdialog:
667 frame.helpdialog.Destroy()
795 frame.helpdialog.Destroy()
668 app = frame.parent
796 app = frame.parent
669 if app is not None:
797 if app is not None:
670 app.result = result
798 app.result = result
671 frame.Close()
799 frame.Close()
672 frame.Destroy()
800 frame.Destroy()
673
801
674 def cell_doubleclicked(self, event):
802 def cell_doubleclicked(self, event):
675 self.enterattr(event.GetRow(), event.GetCol())
803 self.enterattr(event.GetRow(), event.GetCol())
676 event.Skip()
804 event.Skip()
677
805
678 def cell_leftclicked(self, event):
806 def cell_leftclicked(self, event):
679 row = event.GetRow()
807 row = event.GetRow()
680 item = self.table.items[row]
808 item = self.table.items[row]
681 self.set_footer(item)
809 self.set_footer(item)
682 event.Skip()
810 event.Skip()
683
811
684 def pick(self, row):
812 def pick(self, row):
685 """
813 """
686 pick a single row and return to the IPython prompt
814 pick a single row and return to the IPython prompt
687 """
815 """
688 try:
816 try:
689 value = self.table.items[row]
817 value = self.table.items[row]
690 except Exception, exc:
818 except Exception, exc:
691 self.error_output(str(exc))
819 self.error_output(str(exc))
692 else:
820 else:
693 self.quit(value)
821 self.quit(value)
694
822
823 def pickinput(self, row):
824 try:
825 value = self.table.items[row]
826 except Exception, exc:
827 self.error_output(str(exc))
828 else:
829 api = ipapi.get()
830 api.set_next_input(str(value))
831 self.quit(value)
832
833 def pickinputattr(self, row, col):
834 try:
835 attr = self.table._displayattrs[col]
836 value = attr.value(self.table.items[row])
837 except Exception, exc:
838 self.error_output(str(exc))
839 else:
840 api = ipapi.get()
841 api.set_next_input(str(value))
842 self.quit(value)
843
695 def pickrows(self, rows):
844 def pickrows(self, rows):
696 """
845 """
697 pick multiple rows and return to the IPython prompt
846 pick multiple rows and return to the IPython prompt
698 """
847 """
699 try:
848 try:
700 value = [self.table.items[row] for row in rows]
849 value = [self.table.items[row] for row in rows]
701 except Exception, exc:
850 except Exception, exc:
702 self.error_output(str(exc))
851 self.error_output(str(exc))
703 else:
852 else:
704 self.quit(value)
853 self.quit(value)
705
854
706 def pickrowsattr(self, rows, col):
855 def pickrowsattr(self, rows, col):
707 """"
856 """"
708 pick one column from multiple rows
857 pick one column from multiple rows
709 """
858 """
710 values = []
859 values = []
711 try:
860 try:
712 attr = self.table._displayattrs[col]
861 attr = self.table._displayattrs[col]
713 for row in rows:
862 for row in rows:
714 try:
863 try:
715 values.append(attr.value(self.table.items[row]))
864 values.append(attr.value(self.table.items[row]))
716 except (SystemExit, KeyboardInterrupt):
865 except (SystemExit, KeyboardInterrupt):
717 raise
866 raise
718 except Exception:
867 except Exception:
719 raise #pass
868 raise #pass
720 except Exception, exc:
869 except Exception, exc:
721 self.error_output(str(exc))
870 self.error_output(str(exc))
722 else:
871 else:
723 self.quit(values)
872 self.quit(values)
724
873
725 def pickattr(self, row, col):
874 def pickattr(self, row, col):
726 try:
875 try:
727 attr = self.table._displayattrs[col]
876 attr = self.table._displayattrs[col]
728 value = attr.value(self.table.items[row])
877 value = attr.value(self.table.items[row])
729 except Exception, exc:
878 except Exception, exc:
730 self.error_output(str(exc))
879 self.error_output(str(exc))
731 else:
880 else:
732 self.quit(value)
881 self.quit(value)
733
882
734
883
735 class IGridPanel(wx.Panel):
884 class IGridPanel(wx.Panel):
736 # Each IGridPanel contains an IGridGrid
885 # Each IGridPanel contains an IGridGrid
737 def __init__(self, parent, input, *attrs):
886 def __init__(self, parent, input, *attrs):
738 wx.Panel.__init__(self, parent, -1)
887 wx.Panel.__init__(self, parent, -1)
739 self.grid = IGridGrid(self, input, *attrs)
888 self.grid = IGridGrid(self, input, *attrs)
889 self.grid.FitInside()
740 sizer = wx.BoxSizer(wx.VERTICAL)
890 sizer = wx.BoxSizer(wx.VERTICAL)
741 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
891 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
742 self.SetSizer(sizer)
892 self.SetSizer(sizer)
743 sizer.Fit(self)
893 sizer.Fit(self)
744 sizer.SetSizeHints(self)
894 sizer.SetSizeHints(self)
745
895
746
896
747 class IGridHTMLHelp(wx.Frame):
897 class IGridHTMLHelp(wx.Frame):
748 def __init__(self, parent, title, size):
898 def __init__(self, parent, title, size):
749 wx.Frame.__init__(self, parent, -1, title, size=size)
899 wx.Frame.__init__(self, parent, -1, title, size=size)
750 html = wx.html.HtmlWindow(self)
900 html = wx.html.HtmlWindow(self)
751 if "gtk2" in wx.PlatformInfo:
901 if "gtk2" in wx.PlatformInfo:
752 html.SetStandardFonts()
902 html.SetStandardFonts()
753 html.SetPage(help)
903 html.SetPage(help)
754
904
755
905
756 class IGridFrame(wx.Frame):
906 class IGridFrame(wx.Frame):
757 maxtitlelen = 30
907 maxtitlelen = 30
758
908
759 def __init__(self, parent, input):
909 def __init__(self, parent, input):
760 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
910 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
761 wx.Frame.__init__(self, None, title=title, size=(640, 480))
911 wx.Frame.__init__(self, None, title=title, size=(640, 480))
762 self.menubar = wx.MenuBar()
912 self.menubar = wx.MenuBar()
763 self.menucounter = 100
913 self.menucounter = 100
764 self.m_help = wx.Menu()
914 self.m_help = wx.Menu()
765 self.m_search = wx.Menu()
915 self.m_search = wx.Menu()
766 self.m_sort = wx.Menu()
916 self.m_sort = wx.Menu()
917 self.m_refresh = wx.Menu()
767 self.notebook = wx.Notebook(self, -1, style=0)
918 self.notebook = wx.Notebook(self, -1, style=0)
768 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
919 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
920 self.statusbar.SetFieldsCount(2)
921 self.SetStatusWidths([-1, 200])
769 self.parent = parent
922 self.parent = parent
770 self._add_notebook(input)
923 self._add_notebook(input)
771 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
924 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
772 self.makemenu(self.m_sort, "&Sort (asc)", "Sort ascending", self.sortasc)
925 self.makemenu(self.m_sort, "&Sort (asc)\tV", "Sort ascending", self.sortasc)
773 self.makemenu(self.m_sort, "Sort (&desc)", "Sort descending", self.sortdesc)
926 self.makemenu(self.m_sort, "Sort (&desc)\tShift-V", "Sort descending", self.sortdesc)
774 self.makemenu(self.m_help, "&Help", "Help", self.display_help)
927 self.makemenu(self.m_help, "&Help\tF1", "Help", self.display_help)
775 self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
928 # self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
776 self.makemenu(self.m_search, "&Find text", "Find text", self.enter_searchtext)
929 self.makemenu(self.m_search, "&Find text\tCTRL-F", "Find text", self.enter_searchtext)
777 self.makemenu(self.m_search, "Find by &expression", "Find by expression", self.enter_searchexpression)
930 self.makemenu(self.m_search, "Find by &expression\tCTRL-Shift-F", "Find by expression", self.enter_searchexpression)
778 self.makemenu(self.m_search, "Find &next", "Find next", self.find_next)
931 self.makemenu(self.m_search, "Find &next\tF3", "Find next", self.find_next)
779 self.makemenu(self.m_search, "Find &previous", "Find previous", self.find_previous)
932 self.makemenu(self.m_search, "Find &previous\tShift-F3", "Find previous", self.find_previous)
933 self.makemenu(self.m_refresh, "&Refresh once \tF5", "Refresh once", self.refresh_once)
934 self.makemenu(self.m_refresh, "Refresh every &1s", "Refresh every second", self.refresh_every_second)
935 self.makemenu(self.m_refresh, "Refresh every &X seconds", "Refresh every X seconds", self.refresh_interval)
936 self.makemenu(self.m_refresh, "&Stop all refresh timers", "Stop refresh timers", self.stop_refresh)
780 self.menubar.Append(self.m_search, "&Find")
937 self.menubar.Append(self.m_search, "&Find")
781 self.menubar.Append(self.m_sort, "&Sort")
938 self.menubar.Append(self.m_sort, "&Sort")
939 self.menubar.Append(self.m_refresh, "&Refresh")
782 self.menubar.Append(self.m_help, "&Help")
940 self.menubar.Append(self.m_help, "&Help")
783 self.SetMenuBar(self.menubar)
941 self.SetMenuBar(self.menubar)
784 self.searchtext = ""
942 self.searchtext = ""
943 self.searchexpression = ""
785 self.helpdialog = None
944 self.helpdialog = None
945 self.refresh_interval = 1000
946 self.SetStatusText("Refreshing inactive", 1)
947
948 def refresh_once(self, event):
949 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
950 table.refresh_content(event)
951
952 def refresh_interval(self, event):
953 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
954 dlg = wx.TextEntryDialog(self, "Enter refresh interval (milliseconds):", "Refresh timer:", defaultValue=str(self.refresh_interval))
955 if dlg.ShowModal() == wx.ID_OK:
956 try:
957 milliseconds = int(dlg.GetValue())
958 except ValueError, exc:
959 self.SetStatusText(str(exc))
960 else:
961 table.timer.Start(milliseconds=milliseconds, oneShot=False)
962 self.SetStatusText("Refresh timer set to %s ms" % milliseconds)
963 self.SetStatusText("Refresh interval: %s ms" % milliseconds, 1)
964 self.refresh_interval = milliseconds
965 dlg.Destroy()
966
967 def stop_refresh(self, event):
968 for i in xrange(self.notebook.GetPageCount()):
969 nb = self.notebook.GetPage(i)
970 nb.grid.table.timer.Stop()
971 self.SetStatusText("Refreshing inactive", 1)
972
973 def refresh_every_second(self, event):
974 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
975 table.timer.Start(milliseconds=1000, oneShot=False)
976 self.SetStatusText("Refresh interval: 1000 ms", 1)
786
977
787 def sortasc(self, event):
978 def sortasc(self, event):
788 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
979 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
789 grid.sortattrasc()
980 grid.sortattrasc()
790
981
791 def sortdesc(self, event):
982 def sortdesc(self, event):
792 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
983 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
793 grid.sortattrdesc()
984 grid.sortattrdesc()
794
985
795 def find_previous(self, event):
986 def find_previous(self, event):
796 """
987 """
797 find previous occurrences
988 find previous occurrences
798 """
989 """
990 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
799 if self.searchtext:
991 if self.searchtext:
800 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
801 row = grid.GetGridCursorRow()
992 row = grid.GetGridCursorRow()
802 col = grid.GetGridCursorCol()
993 col = grid.GetGridCursorCol()
994 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
803 if col-1 >= 0:
995 if col-1 >= 0:
804 grid.search(self.searchtext, row, col-1, False)
996 grid.search(self.searchtext, row, col-1, False)
805 else:
997 else:
806 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
998 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
999 elif self.searchexpression:
1000 self.SetStatusText("Search mode: expression; looking for %s" % repr(self.searchexpression)[2:-1])
1001 grid.searchexpression(searchexp=self.searchexpression, search_forward=False)
807 else:
1002 else:
808 self.enter_searchtext(event)
1003 self.SetStatusText("No search yet: please enter search-text or -expression")
809
1004
810 def find_next(self, event):
1005 def find_next(self, event):
811 """
1006 """
812 find the next occurrence
1007 find the next occurrence
813 """
1008 """
814 if self.searchtext:
1009 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
815 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
1010 if self.searchtext != "":
816 row = grid.GetGridCursorRow()
1011 row = grid.GetGridCursorRow()
817 col = grid.GetGridCursorCol()
1012 col = grid.GetGridCursorCol()
1013 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
818 if col+1 < grid.table.GetNumberCols():
1014 if col+1 < grid.table.GetNumberCols():
819 grid.search(self.searchtext, row, col+1)
1015 grid.search(self.searchtext, row, col+1)
820 else:
1016 else:
821 grid.search(self.searchtext, row+1, 0)
1017 grid.search(self.searchtext, row+1, 0)
1018 elif self.searchexpression != "":
1019 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1020 grid.searchexpression(searchexp=self.searchexpression)
822 else:
1021 else:
823 self.enter_searchtext(event)
1022 self.SetStatusText("No search yet: please enter search-text or -expression")
824
1023
825 def display_help(self, event):
1024 def display_help(self, event):
826 """
1025 """
827 Display a help dialog
1026 Display a help dialog
828 """
1027 """
829 if self.helpdialog:
1028 if self.helpdialog:
830 self.helpdialog.Destroy()
1029 self.helpdialog.Destroy()
831 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
1030 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
832 self.helpdialog.Show()
1031 self.helpdialog.Show()
833
1032
834 def display_help_in_browser(self, event):
1033 def display_help_in_browser(self, event):
835 """
1034 """
836 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
1035 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
837 CSS this looks better)
1036 CSS this looks better)
838 """
1037 """
839 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
1038 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
840 if not filename.startswith("file"):
1039 if not filename.startswith("file"):
841 filename = "file:" + filename
1040 filename = "file:" + filename
842 webbrowser.open(filename, new=1, autoraise=True)
1041 webbrowser.open(filename, new=1, autoraise=True)
843
1042
844 def enter_searchexpression(self, event):
1043 def enter_searchexpression(self, event):
845 pass
1044 dlg = wx.TextEntryDialog(self, "Find:", "Find matching expression:", defaultValue=self.searchexpression)
1045 if dlg.ShowModal() == wx.ID_OK:
1046 self.searchexpression = dlg.GetValue()
1047 self.searchtext = ""
1048 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1049 self.notebook.GetPage(self.notebook.GetSelection()).grid.searchexpression(self.searchexpression)
1050 dlg.Destroy()
846
1051
847 def makemenu(self, menu, label, help, cmd):
1052 def makemenu(self, menu, label, help, cmd):
848 menu.Append(self.menucounter, label, help)
1053 menu.Append(self.menucounter, label, help)
849 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
1054 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
850 self.menucounter += 1
1055 self.menucounter += 1
851
1056
852 def _add_notebook(self, input, *attrs):
1057 def _add_notebook(self, input, *attrs):
853 # Adds another notebook which has the starting object ``input``
1058 # Adds another notebook which has the starting object ``input``
854 panel = IGridPanel(self.notebook, input, *attrs)
1059 panel = IGridPanel(self.notebook, input, *attrs)
855 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
1060 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
856 if len(text) >= self.maxtitlelen:
1061 if len(text) >= self.maxtitlelen:
857 text = text[:self.maxtitlelen].rstrip(".") + "..."
1062 text = text[:self.maxtitlelen].rstrip(".") + "..."
858 self.notebook.AddPage(panel, text, True)
1063 self.notebook.AddPage(panel, text, True)
859 panel.grid.SetFocus()
1064 panel.grid.SetFocus()
860 self.Layout()
1065 self.Layout()
861
1066
862 def OnCloseWindow(self, event):
1067 def OnCloseWindow(self, event):
863 self.Destroy()
1068 self.Destroy()
864
1069
865 def enter_searchtext(self, event):
1070 def enter_searchtext(self, event):
866 # Displays a dialog asking for the searchtext
1071 # Displays a dialog asking for the searchtext
867 dlg = wx.TextEntryDialog(self, "Find:", "Find in list")
1072 dlg = wx.TextEntryDialog(self, "Find:", "Find in list", defaultValue=self.searchtext)
868 if dlg.ShowModal() == wx.ID_OK:
1073 if dlg.ShowModal() == wx.ID_OK:
869 self.searchtext = dlg.GetValue()
1074 self.searchtext = dlg.GetValue()
870 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext, 0, 0)
1075 self.searchexpression = ""
1076 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1077 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext)
871 dlg.Destroy()
1078 dlg.Destroy()
872
1079
873
1080
874 class App(wx.App):
1081 class App(wx.App):
875 def __init__(self, input):
1082 def __init__(self, input):
876 self.input = input
1083 self.input = input
877 self.result = None # Result to be returned to IPython. Set by quit().
1084 self.result = None # Result to be returned to IPython. Set by quit().
878 wx.App.__init__(self)
1085 wx.App.__init__(self)
879
1086
880 def OnInit(self):
1087 def OnInit(self):
881 frame = IGridFrame(self, self.input)
1088 frame = IGridFrame(self, self.input)
882 frame.Show()
1089 frame.Show()
883 self.SetTopWindow(frame)
1090 self.SetTopWindow(frame)
884 frame.Raise()
1091 frame.Raise()
885 return True
1092 return True
886
1093
887
1094
888 class igrid(ipipe.Display):
1095 class igrid(ipipe.Display):
889 """
1096 """
890 This is a wx-based display object that can be used instead of ``ibrowse``
1097 This is a wx-based display object that can be used instead of ``ibrowse``
891 (which is curses-based) or ``idump`` (which simply does a print).
1098 (which is curses-based) or ``idump`` (which simply does a print).
892 """
1099 """
893 if wx.VERSION < (2, 7):
1100 if wx.VERSION < (2, 7):
894 def display(self):
1101 def display(self):
895 try:
1102 try:
896 # Try to create a "standalone" from. If this works we're probably
1103 # Try to create a "standalone" from. If this works we're probably
897 # running with -wthread.
1104 # running with -wthread.
898 # Note that this sets the parent of the frame to None, but we can't
1105 # Note that this sets the parent of the frame to None, but we can't
899 # pass a result object back to the shell anyway.
1106 # pass a result object back to the shell anyway.
900 frame = IGridFrame(None, self.input)
1107 frame = IGridFrame(None, self.input)
901 frame.Show()
1108 frame.Show()
902 frame.Raise()
1109 frame.Raise()
903 except wx.PyNoAppError:
1110 except wx.PyNoAppError:
904 # There's no wx application yet => create one.
1111 # There's no wx application yet => create one.
905 app = App(self.input)
1112 app = App(self.input)
906 app.MainLoop()
1113 app.MainLoop()
907 return app.result
1114 return app.result
908 else:
1115 else:
909 # With wx 2.7 it gets simpler.
1116 # With wx 2.7 it gets simpler.
910 def display(self):
1117 def display(self):
911 app = App(self.input)
1118 app = App(self.input)
912 app.MainLoop()
1119 app.MainLoop()
913 return app.result
1120 return app.result
914
1121
General Comments 0
You need to be logged in to leave comments. Login now