##// END OF EJS Templates
Using igrid with wxPyhon 2.6 and -wthread should work now....
walter.doerwald -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,805 +1,825 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 import ipipe, os, webbrowser, urllib
4 4 import wx
5 5 import wx.grid, wx.html
6 6
7 7 try:
8 8 sorted
9 9 except NameError:
10 10 from ipipe import sorted
11 11
12 12
13 13 __all__ = ["igrid"]
14 14
15 15
16 16 class IGridRenderer(wx.grid.PyGridCellRenderer):
17 17 """
18 18 This is a custom renderer for our IGridGrid
19 19 """
20 20 def __init__(self, table):
21 21 self.maxchars = 200
22 22 self.table = table
23 23 self.colormap = (
24 24 ( 0, 0, 0),
25 25 (174, 0, 0),
26 26 ( 0, 174, 0),
27 27 (174, 174, 0),
28 28 ( 0, 0, 174),
29 29 (174, 0, 174),
30 30 ( 0, 174, 174),
31 31 ( 64, 64, 64)
32 32 )
33 33
34 34 wx.grid.PyGridCellRenderer.__init__(self)
35 35
36 36 def _getvalue(self, row, col):
37 37 try:
38 38 value = self.table._displayattrs[col].value(self.table.items[row])
39 39 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
40 40 except Exception, exc:
41 41 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
42 42 return (align, text)
43 43
44 44 def GetBestSize(self, grid, attr, dc, row, col):
45 45 text = grid.GetCellValue(row, col)
46 46 (align, text) = self._getvalue(row, col)
47 47 dc.SetFont(attr.GetFont())
48 48 (w, h) = dc.GetTextExtent(str(text))
49 49 return wx.Size(min(w+2, 600), h+2) # add border
50 50
51 51 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
52 52 """
53 53 Takes care of drawing everything in the cell; aligns the text
54 54 """
55 55 text = grid.GetCellValue(row, col)
56 56 (align, text) = self._getvalue(row, col)
57 57 if isSelected:
58 58 bg = grid.GetSelectionBackground()
59 59 else:
60 60 bg = ["white", (240, 240, 240)][row%2]
61 61 dc.SetTextBackground(bg)
62 62 dc.SetBrush(wx.Brush(bg, wx.SOLID))
63 63 dc.SetPen(wx.TRANSPARENT_PEN)
64 64 dc.SetFont(attr.GetFont())
65 65 dc.DrawRectangleRect(rect)
66 66 dc.SetClippingRect(rect)
67 67 # Format the text
68 68 if align == -1: # left alignment
69 69 (width, height) = dc.GetTextExtent(str(text))
70 70 x = rect[0]+1
71 71 y = rect[1]+0.5*(rect[3]-height)
72 72
73 73 for (style, part) in text:
74 74 if isSelected:
75 75 fg = grid.GetSelectionForeground()
76 76 else:
77 77 fg = self.colormap[style.fg]
78 78 dc.SetTextForeground(fg)
79 79 (w, h) = dc.GetTextExtent(part)
80 80 dc.DrawText(part, x, y)
81 81 x += w
82 82 elif align == 0: # center alignment
83 83 (width, height) = dc.GetTextExtent(str(text))
84 84 x = rect[0]+0.5*(rect[2]-width)
85 85 y = rect[1]+0.5*(rect[3]-height)
86 86 for (style, part) in text:
87 87 if isSelected:
88 88 fg = grid.GetSelectionForeground()
89 89 else:
90 90 fg = self.colormap[style.fg]
91 91 dc.SetTextForeground(fg)
92 92 (w, h) = dc.GetTextExtent(part)
93 93 dc.DrawText(part, x, y)
94 94 x += w
95 95 else: # right alignment
96 96 (width, height) = dc.GetTextExtent(str(text))
97 97 x = rect[0]+rect[2]-1
98 98 y = rect[1]+0.5*(rect[3]-height)
99 99 for (style, part) in reversed(text):
100 100 (w, h) = dc.GetTextExtent(part)
101 101 x -= w
102 102 if isSelected:
103 103 fg = grid.GetSelectionForeground()
104 104 else:
105 105 fg = self.colormap[style.fg]
106 106 dc.SetTextForeground(fg)
107 107 dc.DrawText(part, x, y)
108 108 dc.DestroyClippingRegion()
109 109
110 110 def Clone(self):
111 111 return IGridRenderer(self.table)
112 112
113 113
114 114 class IGridTable(wx.grid.PyGridTableBase):
115 115 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
116 116 # ``GetValue()`` does not get any values (or at least it does not return
117 117 # anything, accessing the values is done by the renderer)
118 118 # but rather tries to fetch the objects which were requested into the table.
119 119 # General behaviour is: Fetch the first X objects. If the user scrolls down
120 120 # to the last object another bunch of X objects is fetched (if possible)
121 121 def __init__(self, input, fontsize, *attrs):
122 122 wx.grid.PyGridTableBase.__init__(self)
123 123 self.input = input
124 124 self.iterator = ipipe.xiter(input)
125 125 self.items = []
126 126 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
127 127 self._displayattrs = self.attrs[:]
128 128 self._displayattrset = set(self.attrs)
129 129 self._sizing = False
130 130 self.fontsize = fontsize
131 131 self._fetch(1)
132 132
133 133 def GetAttr(self, *args):
134 134 attr = wx.grid.GridCellAttr()
135 135 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
136 136 return attr
137 137
138 138 def GetNumberRows(self):
139 139 return len(self.items)
140 140
141 141 def GetNumberCols(self):
142 142 return len(self._displayattrs)
143 143
144 144 def GetColLabelValue(self, col):
145 145 if col < len(self._displayattrs):
146 146 return self._displayattrs[col].name()
147 147 else:
148 148 return ""
149 149
150 150 def GetRowLabelValue(self, row):
151 151 return str(row)
152 152
153 153 def IsEmptyCell(self, row, col):
154 154 return False
155 155
156 156 def _append(self, item):
157 157 self.items.append(item)
158 158 # Nothing to do if the set of attributes has been fixed by the user
159 159 if not self.attrs:
160 160 for attr in ipipe.xattrs(item):
161 161 attr = ipipe.upgradexattr(attr)
162 162 if attr not in self._displayattrset:
163 163 self._displayattrs.append(attr)
164 164 self._displayattrset.add(attr)
165 165
166 166 def _fetch(self, count):
167 167 # Try to fill ``self.items`` with at least ``count`` objects.
168 168 have = len(self.items)
169 169 while self.iterator is not None and have < count:
170 170 try:
171 171 item = self.iterator.next()
172 172 except StopIteration:
173 173 self.iterator = None
174 174 break
175 175 except (KeyboardInterrupt, SystemExit):
176 176 raise
177 177 except Exception, exc:
178 178 have += 1
179 179 self._append(item)
180 180 self.iterator = None
181 181 break
182 182 else:
183 183 have += 1
184 184 self._append(item)
185 185
186 186 def GetValue(self, row, col):
187 187 # some kind of dummy-function: does not return anything but "";
188 188 # (The value isn't use anyway)
189 189 # its main task is to trigger the fetch of new objects
190 190 had_cols = self._displayattrs[:]
191 191 had_rows = len(self.items)
192 192 if row == had_rows - 1 and self.iterator is not None and not self._sizing:
193 193 self._fetch(row + 20)
194 194 have_rows = len(self.items)
195 195 have_cols = len(self._displayattrs)
196 196 if have_rows > had_rows:
197 197 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
198 198 self.GetView().ProcessTableMessage(msg)
199 199 self._sizing = True
200 200 self.GetView().AutoSizeColumns(False)
201 201 self._sizing = False
202 202 if row >= have_rows:
203 203 return ""
204 204 if self._displayattrs != had_cols:
205 205 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - len(had_cols))
206 206 self.GetView().ProcessTableMessage(msg)
207 207 return ""
208 208
209 209 def SetValue(self, row, col, value):
210 210 pass
211 211
212 212
213 213 class IGridGrid(wx.grid.Grid):
214 214 # The actual grid
215 215 # all methods for selecting/sorting/picking/... data are implemented here
216 216 def __init__(self, panel, input, *attrs):
217 217 wx.grid.Grid.__init__(self, panel)
218 218 fontsize = 9
219 219 self.input = input
220 220 self.table = IGridTable(self.input, fontsize, *attrs)
221 221 self.SetTable(self.table, True)
222 222 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
223 223 self.SetDefaultRenderer(IGridRenderer(self.table))
224 224 self.EnableEditing(False)
225 225 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
226 226 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
227 227 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
228 228 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
229 229 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
230 230 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
231 231 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
232 232 self.current_selection = set()
233 233 self.maxchars = 200
234 234
235 235 def on_label_leftclick(self, event):
236 236 event.Skip()
237 237
238 238 def error_output(self, text):
239 239 wx.Bell()
240 240 frame = self.GetParent().GetParent().GetParent()
241 241 frame.SetStatusText(text)
242 242
243 243 def _on_selected_range(self, event):
244 244 # Internal update to the selection tracking lists
245 245 if event.Selecting():
246 246 # adding to the list...
247 247 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
248 248 else:
249 249 # removal from list
250 250 for index in xrange( event.GetTopRow(), event.GetBottomRow()+1):
251 251 self.current_selection.discard(index)
252 252 event.Skip()
253 253
254 254 def _on_selected_cell(self, event):
255 255 # Internal update to the selection tracking list
256 256 self.current_selection = set([event.GetRow()])
257 257 event.Skip()
258 258
259 259 def sort(self, key, reverse=False):
260 260 """
261 261 Sort the current list of items using the key function ``key``. If
262 262 ``reverse`` is true the sort order is reversed.
263 263 """
264 264 row = self.GetGridCursorRow()
265 265 col = self.GetGridCursorCol()
266 266 curitem = self.table.items[row] # Remember where the cursor is now
267 267 # Sort items
268 268 def realkey(item):
269 269 try:
270 270 return key(item)
271 271 except (KeyboardInterrupt, SystemExit):
272 272 raise
273 273 except Exception:
274 274 return None
275 275 try:
276 276 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
277 277 except TypeError, exc:
278 278 self.error_output("Exception encountered: %s" % exc)
279 279 return
280 280 # Find out where the object under the cursor went
281 281 for (i, item) in enumerate(self.table.items):
282 282 if item is curitem:
283 283 self.SetGridCursor(i,col)
284 284 self.MakeCellVisible(i,col)
285 285 self.Refresh()
286 286
287 287 def sortattrasc(self):
288 288 """
289 289 Sort in ascending order; sorting criteria is the current attribute
290 290 """
291 291 col = self.GetGridCursorCol()
292 292 attr = self.table._displayattrs[col]
293 293 frame = self.GetParent().GetParent().GetParent()
294 294 if attr is ipipe.noitem:
295 295 self.error_output("no column under cursor")
296 296 return
297 297 frame.SetStatusText("sort by %s (ascending)" % attr.name())
298 298 def key(item):
299 299 try:
300 300 return attr.value(item)
301 301 except (KeyboardInterrupt, SystemExit):
302 302 raise
303 303 except Exception:
304 304 return None
305 305 self.sort(key)
306 306
307 307 def sortattrdesc(self):
308 308 """
309 309 Sort in descending order; sorting criteria is the current attribute
310 310 """
311 311 col = self.GetGridCursorCol()
312 312 attr = self.table._displayattrs[col]
313 313 frame = self.GetParent().GetParent().GetParent()
314 314 if attr is ipipe.noitem:
315 315 self.error_output("no column under cursor")
316 316 return
317 317 frame.SetStatusText("sort by %s (descending)" % attr.name())
318 318 def key(item):
319 319 try:
320 320 return attr.value(item)
321 321 except (KeyboardInterrupt, SystemExit):
322 322 raise
323 323 except Exception:
324 324 return None
325 325 self.sort(key, reverse=True)
326 326
327 327 def label_doubleclicked(self, event):
328 328 row = event.GetRow()
329 329 col = event.GetCol()
330 330 if col == -1:
331 331 self.enter(row)
332 332
333 333 def _getvalue(self, row, col):
334 334 """
335 335 Gets the text which is displayed at ``(row, col)``
336 336 """
337 337 try:
338 338 value = self.table._displayattrs[col].value(self.table.items[row])
339 339 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
340 340 except IndexError:
341 341 raise IndexError
342 342 except Exception, exc:
343 343 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
344 344 return text
345 345
346 346 def search(self, searchtext, startrow=0, startcol=0, search_forward=True):
347 347 """
348 348 search for ``searchtext``, starting in ``(startrow, startcol)``;
349 349 if ``search_forward`` is true the direction is "forward"
350 350 """
351 351 row = startrow
352 352 searchtext = searchtext.lower()
353 353 if search_forward:
354 354 while True:
355 355 for col in xrange(startcol, self.table.GetNumberCols()):
356 356 try:
357 357 foo = self.table.GetValue(row, col)
358 358 text = self._getvalue(row, col)
359 359 if searchtext in text.string().lower():
360 360 self.SetGridCursor(row, col)
361 361 self.MakeCellVisible(row, col)
362 362 return
363 363 except IndexError:
364 364 return
365 365 startcol = 0
366 366 row += 1
367 367 else:
368 368 while True:
369 369 for col in xrange(startcol, -1, -1):
370 370 try:
371 371 foo = self.table.GetValue(row, col)
372 372 text = self._getvalue(row, col)
373 373 if searchtext in text.string().lower():
374 374 self.SetGridCursor(row, col)
375 375 self.MakeCellVisible(row, col)
376 376 return
377 377 except IndexError:
378 378 return
379 379 startcol = self.table.GetNumberCols()-1
380 380 row -= 1
381 381
382 382 def key_pressed(self, event):
383 383 """
384 384 Maps pressed keys to functions
385 385 """
386 386 frame = self.GetParent().GetParent().GetParent()
387 387 frame.SetStatusText("")
388 388 sh = event.ShiftDown()
389 389 ctrl = event.ControlDown()
390 390
391 391 keycode = event.GetKeyCode()
392 392 if keycode == ord("P"):
393 393 row = self.GetGridCursorRow()
394 394 if event.ShiftDown():
395 395 col = self.GetGridCursorCol()
396 396 self.pickattr(row, col)
397 397 else:
398 398 self.pick(row)
399 399 elif keycode == ord("M"):
400 400 if ctrl:
401 401 col = self.GetGridCursorCol()
402 402 self.pickrowsattr(sorted(self.current_selection), col)
403 403 else:
404 404 self.pickrows(sorted(self.current_selection))
405 405 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
406 406 self.delete_current_notebook()
407 407 elif keycode == ord("E") and not (ctrl or sh):
408 408 row = self.GetGridCursorRow()
409 409 self.enter(row)
410 410 elif keycode == ord("E") and sh and not ctrl:
411 411 row = self.GetGridCursorRow()
412 412 col = self.GetGridCursorCol()
413 413 self.enterattr(row, col)
414 414 elif keycode == ord("E") and ctrl:
415 415 row = self.GetGridCursorRow()
416 416 self.SetGridCursor(row, self.GetNumberCols()-1)
417 417 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
418 418 row = self.GetGridCursorRow()
419 419 self.SetGridCursor(row, 0)
420 420 elif keycode == ord("C") and sh:
421 421 col = self.GetGridCursorCol()
422 422 attr = self.table._displayattrs[col]
423 423 result = []
424 424 for i in xrange(self.GetNumberRows()):
425 425 result.append(self.table._displayattrs[col].value(self.table.items[i]))
426 426 self.quit(result)
427 427 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
428 428 self.quit()
429 429 elif keycode == ord("<"):
430 430 row = self.GetGridCursorRow()
431 431 col = self.GetGridCursorCol()
432 432 if not event.ShiftDown():
433 433 newcol = col - 1
434 434 if newcol >= 0:
435 435 self.SetGridCursor(row, col - 1)
436 436 else:
437 437 newcol = col + 1
438 438 if newcol < self.GetNumberCols():
439 439 self.SetGridCursor(row, col + 1)
440 440 elif keycode == ord("D"):
441 441 col = self.GetGridCursorCol()
442 442 row = self.GetGridCursorRow()
443 443 if not sh:
444 444 self.detail(row, col)
445 445 else:
446 446 self.detail_attr(row, col)
447 447 elif keycode == ord("F") and ctrl:
448 448 frame.enter_searchtext(event)
449 449 elif keycode == wx.WXK_F3:
450 450 if sh:
451 451 frame.find_previous(event)
452 452 else:
453 453 frame.find_next(event)
454 454 elif keycode == ord("V"):
455 455 if sh:
456 456 self.sortattrdesc()
457 457 else:
458 458 self.sortattrasc()
459 459 elif keycode == wx.WXK_DOWN:
460 460 row = self.GetGridCursorRow()
461 461 try:
462 462 item = self.table.items[row+1]
463 463 except IndexError:
464 464 item = self.table.items[row]
465 465 self.set_footer(item)
466 466 event.Skip()
467 467 elif keycode == wx.WXK_UP:
468 468 row = self.GetGridCursorRow()
469 469 if row >= 1:
470 470 item = self.table.items[row-1]
471 471 else:
472 472 item = self.table.items[row]
473 473 self.set_footer(item)
474 474 event.Skip()
475 475 elif keycode == wx.WXK_RIGHT:
476 476 row = self.GetGridCursorRow()
477 477 item = self.table.items[row]
478 478 self.set_footer(item)
479 479 event.Skip()
480 480 elif keycode == wx.WXK_LEFT:
481 481 row = self.GetGridCursorRow()
482 482 item = self.table.items[row]
483 483 self.set_footer(item)
484 484 event.Skip()
485 485 else:
486 486 event.Skip()
487 487
488 488 def delete_current_notebook(self):
489 489 """
490 490 deletes the current notebook tab
491 491 """
492 492 panel = self.GetParent()
493 493 nb = panel.GetParent()
494 494 current = nb.GetSelection()
495 495 count = nb.GetPageCount()
496 496 if count > 1:
497 497 for i in xrange(count-1, current-1, -1):
498 498 nb.DeletePage(i)
499 499 nb.GetCurrentPage().grid.SetFocus()
500 500 else:
501 501 frame = nb.GetParent()
502 502 frame.SetStatusText("This is the last level!")
503 503
504 504 def _doenter(self, value, *attrs):
505 505 """
506 506 "enter" a special item resulting in a new notebook tab
507 507 """
508 508 panel = self.GetParent()
509 509 nb = panel.GetParent()
510 510 frame = nb.GetParent()
511 511 current = nb.GetSelection()
512 512 count = nb.GetPageCount()
513 513 try: # if we want to enter something non-iterable, e.g. a function
514 514 if current + 1 == count and value is not self.input: # we have an event in the last tab
515 515 frame._add_notebook(value, *attrs)
516 516 elif value != self.input: # we have to delete all tabs newer than [panel] first
517 517 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
518 518 nb.DeletePage(i)
519 519 frame._add_notebook(value)
520 520 except TypeError, exc:
521 521 if exc.__class__.__module__ == "exceptions":
522 522 msg = "%s: %s" % (exc.__class__.__name__, exc)
523 523 else:
524 524 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
525 525 frame.SetStatusText(msg)
526 526
527 527 def enterattr(self, row, col):
528 528 try:
529 529 attr = self.table._displayattrs[col]
530 530 value = attr.value(self.table.items[row])
531 531 except Exception, exc:
532 532 self.error_output(str(exc))
533 533 else:
534 534 self._doenter(value)
535 535
536 536 def set_footer(self, item):
537 537 frame = self.GetParent().GetParent().GetParent()
538 538 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]))
539 539
540 540 def enter(self, row):
541 541 try:
542 542 value = self.table.items[row]
543 543 except Exception, exc:
544 544 self.error_output(str(exc))
545 545 else:
546 546 self._doenter(value)
547 547
548 548 def detail(self, row, col):
549 549 """
550 550 shows a detail-view of the current cell
551 551 """
552 552 try:
553 553 attr = self.table._displayattrs[col]
554 554 item = self.table.items[row]
555 555 except Exception, exc:
556 556 self.error_output(str(exc))
557 557 else:
558 558 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
559 559 self._doenter(attrs)
560 560
561 561 def detail_attr(self, row, col):
562 562 try:
563 563 attr = self.table._displayattrs[col]
564 564 item = attr.value(self.table.items[row])
565 565 except Exception, exc:
566 566 self.error_output(str(exc))
567 567 else:
568 568 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
569 569 self._doenter(attrs)
570 570
571 571 def quit(self, result=None):
572 572 """
573 573 quit
574 574 """
575 575 frame = self.GetParent().GetParent().GetParent()
576 576 if frame.helpdialog:
577 577 frame.helpdialog.Destroy()
578 frame.parent.result = result
578 app = frame.parent
579 if app is not None:
580 app.result = result
579 581 frame.Close()
580 582 frame.Destroy()
581 583
582 584 def cell_doubleclicked(self, event):
583 585 self.enterattr(event.GetRow(), event.GetCol())
584 586 event.Skip()
585 587
586 588 def cell_leftclicked(self, event):
587 589 row = event.GetRow()
588 590 item = self.table.items[row]
589 591 self.set_footer(item)
590 592 event.Skip()
591 593
592 594 def pick(self, row):
593 595 """
594 596 pick a single row and return to the IPython prompt
595 597 """
596 598 try:
597 599 value = self.table.items[row]
598 600 except Exception, exc:
599 601 self.error_output(str(exc))
600 602 else:
601 603 self.quit(value)
602 604
603 605 def pickrows(self, rows):
604 606 """
605 607 pick multiple rows and return to the IPython prompt
606 608 """
607 609 try:
608 610 value = [self.table.items[row] for row in rows]
609 611 except Exception, exc:
610 612 self.error_output(str(exc))
611 613 else:
612 614 self.quit(value)
613 615
614 616 def pickrowsattr(self, rows, col):
615 617 """"
616 618 pick one column from multiple rows
617 619 """
618 620 values = []
619 621 try:
620 622 attr = self.table._displayattrs[col]
621 623 for row in rows:
622 624 try:
623 625 values.append(attr.value(self.table.items[row]))
624 626 except (SystemExit, KeyboardInterrupt):
625 627 raise
626 628 except Exception:
627 629 raise #pass
628 630 except Exception, exc:
629 631 self.error_output(str(exc))
630 632 else:
631 633 self.quit(values)
632 634
633 635 def pickattr(self, row, col):
634 636 try:
635 637 attr = self.table._displayattrs[col]
636 638 value = attr.value(self.table.items[row])
637 639 except Exception, exc:
638 640 self.error_output(str(exc))
639 641 else:
640 642 self.quit(value)
641 643
642 644
643 645 class IGridPanel(wx.Panel):
644 646 # Each IGridPanel contains an IGridGrid
645 647 def __init__(self, parent, input, *attrs):
646 648 wx.Panel.__init__(self, parent, -1)
647 649 self.grid = IGridGrid(self, input, *attrs)
648 650 sizer = wx.BoxSizer(wx.VERTICAL)
649 651 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
650 652 self.SetSizer(sizer)
651 653 sizer.Fit(self)
652 654 sizer.SetSizeHints(self)
653 655
654 656
655 657 class IGridHTMLHelp(wx.Frame):
656 658 def __init__(self, parent, title, filename, size):
657 659 wx.Frame.__init__(self, parent, -1, title, size=size)
658 660 html = wx.html.HtmlWindow(self)
659 661 if "gtk2" in wx.PlatformInfo:
660 662 html.SetStandardFonts()
661 663 html.LoadFile(filename)
662 664
663 665
664 666 class IGridFrame(wx.Frame):
665 667 maxtitlelen = 30
666 668
667 669 def __init__(self, parent, input):
668 670 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
669 671 wx.Frame.__init__(self, None, title=title, size=(640, 480))
670 672 self.menubar = wx.MenuBar()
671 673 self.menucounter = 100
672 674 self.m_help = wx.Menu()
673 675 self.m_search = wx.Menu()
674 676 self.m_sort = wx.Menu()
675 677 self.notebook = wx.Notebook(self, -1, style=0)
676 678 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
677 679 self.parent = parent
678 680 self._add_notebook(input)
679 681 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
680 682 self.makemenu(self.m_sort, "&Sort (asc)", "Sort ascending", self.sortasc)
681 683 self.makemenu(self.m_sort, "Sort (&desc)", "Sort descending", self.sortdesc)
682 684 self.makemenu(self.m_help, "&Help", "Help", self.display_help)
683 685 self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
684 686 self.makemenu(self.m_search, "&Find text", "Find text", self.enter_searchtext)
685 687 self.makemenu(self.m_search, "Find by &expression", "Find by expression", self.enter_searchexpression)
686 688 self.makemenu(self.m_search, "Find &next", "Find next", self.find_next)
687 689 self.makemenu(self.m_search, "Find &previous", "Find previous", self.find_previous)
688 690 self.menubar.Append(self.m_search, "&Find")
689 691 self.menubar.Append(self.m_sort, "&Sort")
690 692 self.menubar.Append(self.m_help, "&Help")
691 693 self.SetMenuBar(self.menubar)
692 694 self.searchtext = ""
693 695 self.helpdialog = None
694 696
695 697 def sortasc(self, event):
696 698 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
697 699 grid.sortattrasc()
698 700
699 701 def sortdesc(self, event):
700 702 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
701 703 grid.sortattrdesc()
702 704
703 705 def find_previous(self, event):
704 706 """
705 707 find previous occurrences
706 708 """
707 709 if self.searchtext:
708 710 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
709 711 row = grid.GetGridCursorRow()
710 712 col = grid.GetGridCursorCol()
711 713 if col-1 >= 0:
712 714 grid.search(self.searchtext, row, col-1, False)
713 715 else:
714 716 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
715 717 else:
716 718 self.enter_searchtext(event)
717 719
718 720 def find_next(self, event):
719 721 """
720 722 find the next occurrence
721 723 """
722 724 if self.searchtext:
723 725 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
724 726 row = grid.GetGridCursorRow()
725 727 col = grid.GetGridCursorCol()
726 728 if col+1 < grid.table.GetNumberCols():
727 729 grid.search(self.searchtext, row, col+1)
728 730 else:
729 731 grid.search(self.searchtext, row+1, 0)
730 732 else:
731 733 self.enter_searchtext(event)
732 734
733 735 def display_help(self, event):
734 736 """
735 737 Display a help dialog
736 738 """
737 739 if self.helpdialog:
738 740 self.helpdialog.Destroy()
739 741 filename = os.path.join(os.path.dirname(__file__), "igrid_help.html")
740 742 self.helpdialog = IGridHTMLHelp(None, title="Help", filename=filename, size=wx.Size(600,400))
741 743 self.helpdialog.Show()
742 744
743 745 def display_help_in_browser(self, event):
744 746 """
745 747 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
746 748 CSS this looks better)
747 749 """
748 750 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
749 751 if not filename.startswith("file"):
750 752 filename = "file:" + filename
751 753 webbrowser.open(filename, new=1, autoraise=True)
752 754
753 755 def enter_searchexpression(self, event):
754 756 pass
755 757
756 758 def makemenu(self, menu, label, help, cmd):
757 759 menu.Append(self.menucounter, label, help)
758 760 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
759 761 self.menucounter += 1
760 762
761 763 def _add_notebook(self, input, *attrs):
762 764 # Adds another notebook which has the starting object ``input``
763 765 panel = IGridPanel(self.notebook, input, *attrs)
764 766 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
765 767 if len(text) >= self.maxtitlelen:
766 768 text = text[:self.maxtitlelen].rstrip(".") + "..."
767 769 self.notebook.AddPage(panel, text, True)
768 770 panel.grid.SetFocus()
769 771 self.Layout()
770 772
771 773 def OnCloseWindow(self, event):
772 774 self.Destroy()
773 775
774 776 def enter_searchtext(self, event):
775 777 # Displays a dialog asking for the searchtext
776 778 dlg = wx.TextEntryDialog(self, "Find:", "Find in list")
777 779 if dlg.ShowModal() == wx.ID_OK:
778 780 self.searchtext = dlg.GetValue()
779 781 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext, 0, 0)
780 782 dlg.Destroy()
781 783
782 784
783 785 class App(wx.App):
784 786 def __init__(self, input):
785 787 self.input = input
786 788 self.result = None # Result to be returned to IPython. Set by quit().
787 789 wx.App.__init__(self)
788 790
789 791 def OnInit(self):
790 792 frame = IGridFrame(self, self.input)
791 793 frame.Show()
792 794 self.SetTopWindow(frame)
793 795 frame.Raise()
794 796 return True
795 797
796 798
797 799 class igrid(ipipe.Display):
798 800 """
799 801 This is a wx-based display object that can be used instead of ``ibrowse``
800 802 (which is curses-based) or ``idump`` (which simply does a print).
801 803 """
804 if wx.VERSION < (2, 7):
802 805 def display(self):
806 try:
807 # Try to create a "standalone" from. If this works we're probably
808 # running with -wthread.
809 # Note that this sets the parent of the frame to None, but we can't
810 # pass a result object back to the shell anyway.
811 frame = IGridFrame(None, self.input)
812 frame.Show()
813 frame.Raise()
814 except wx.PyNoAppError:
815 # There's no wx application yet => create one.
803 816 app = App(self.input)
804 817 app.MainLoop()
805 818 return app.result
819 else:
820 # With wx 2.7 it gets simpler.
821 def display(self):
822 app = App(self.input)
823 app.MainLoop()
824 return app.result
825
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now