##// END OF EJS Templates
protect two more magic in menu ("loadpy" and "save")
Matthias BUSSONNIER -
Show More
@@ -1,858 +1,858 b''
1 1 """The Qt MainWindow for the QtConsole
2 2
3 3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
4 4 common actions.
5 5
6 6 Authors:
7 7
8 8 * Evan Patterson
9 9 * Min RK
10 10 * Erik Tollerud
11 11 * Fernando Perez
12 12 * Bussonnier Matthias
13 13 * Thomas Kluyver
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 21 # stdlib imports
22 22 import sys
23 23 import webbrowser
24 24 from threading import Thread
25 25
26 26 # System library imports
27 27 from IPython.external.qt import QtGui,QtCore
28 28
29 29 def background(f):
30 30 """call a function in a simple thread, to prevent blocking"""
31 31 t = Thread(target=f)
32 32 t.start()
33 33 return t
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Classes
37 37 #-----------------------------------------------------------------------------
38 38
39 39 class MainWindow(QtGui.QMainWindow):
40 40
41 41 #---------------------------------------------------------------------------
42 42 # 'object' interface
43 43 #---------------------------------------------------------------------------
44 44
45 45 def __init__(self, app,
46 46 confirm_exit=True,
47 47 new_frontend_factory=None, slave_frontend_factory=None,
48 48 ):
49 49 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
50 50
51 51 Parameters
52 52 ----------
53 53
54 54 app : reference to QApplication parent
55 55 confirm_exit : bool, optional
56 56 Whether we should prompt on close of tabs
57 57 new_frontend_factory : callable
58 58 A callable that returns a new IPythonWidget instance, attached to
59 59 its own running kernel.
60 60 slave_frontend_factory : callable
61 61 A callable that takes an existing IPythonWidget, and returns a new
62 62 IPythonWidget instance, attached to the same kernel.
63 63 """
64 64
65 65 super(MainWindow, self).__init__()
66 66 self._kernel_counter = 0
67 67 self._app = app
68 68 self.confirm_exit = confirm_exit
69 69 self.new_frontend_factory = new_frontend_factory
70 70 self.slave_frontend_factory = slave_frontend_factory
71 71
72 72 self.tab_widget = QtGui.QTabWidget(self)
73 73 self.tab_widget.setDocumentMode(True)
74 74 self.tab_widget.setTabsClosable(True)
75 75 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
76 76
77 77 self.setCentralWidget(self.tab_widget)
78 78 # hide tab bar at first, since we have no tabs:
79 79 self.tab_widget.tabBar().setVisible(False)
80 80 # prevent focus in tab bar
81 81 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
82 82
83 83 def update_tab_bar_visibility(self):
84 84 """ update visibility of the tabBar depending of the number of tab
85 85
86 86 0 or 1 tab, tabBar hidden
87 87 2+ tabs, tabBar visible
88 88
89 89 send a self.close if number of tab ==0
90 90
91 91 need to be called explicitely, or be connected to tabInserted/tabRemoved
92 92 """
93 93 if self.tab_widget.count() <= 1:
94 94 self.tab_widget.tabBar().setVisible(False)
95 95 else:
96 96 self.tab_widget.tabBar().setVisible(True)
97 97 if self.tab_widget.count()==0 :
98 98 self.close()
99 99
100 100 @property
101 101 def next_kernel_id(self):
102 102 """constantly increasing counter for kernel IDs"""
103 103 c = self._kernel_counter
104 104 self._kernel_counter += 1
105 105 return c
106 106
107 107 @property
108 108 def active_frontend(self):
109 109 return self.tab_widget.currentWidget()
110 110
111 111 def create_tab_with_new_frontend(self):
112 112 """create a new frontend and attach it to a new tab"""
113 113 widget = self.new_frontend_factory()
114 114 self.add_tab_with_frontend(widget)
115 115
116 116 def create_tab_with_current_kernel(self):
117 117 """create a new frontend attached to the same kernel as the current tab"""
118 118 current_widget = self.tab_widget.currentWidget()
119 119 current_widget_index = self.tab_widget.indexOf(current_widget)
120 120 current_widget_name = self.tab_widget.tabText(current_widget_index)
121 121 widget = self.slave_frontend_factory(current_widget)
122 122 if 'slave' in current_widget_name:
123 123 # don't keep stacking slaves
124 124 name = current_widget_name
125 125 else:
126 126 name = '(%s) slave' % current_widget_name
127 127 self.add_tab_with_frontend(widget,name=name)
128 128
129 129 def close_tab(self,current_tab):
130 130 """ Called when you need to try to close a tab.
131 131
132 132 It takes the number of the tab to be closed as argument, or a referece
133 133 to the wiget insite this tab
134 134 """
135 135
136 136 # let's be sure "tab" and "closing widget are respectivey the index of the tab to close
137 137 # and a reference to the trontend to close
138 138 if type(current_tab) is not int :
139 139 current_tab = self.tab_widget.indexOf(current_tab)
140 140 closing_widget=self.tab_widget.widget(current_tab)
141 141
142 142
143 143 # when trying to be closed, widget might re-send a request to be closed again, but will
144 144 # be deleted when event will be processed. So need to check that widget still exist and
145 145 # skip if not. One example of this is when 'exit' is send in a slave tab. 'exit' will be
146 146 # re-send by this fonction on the master widget, which ask all slaves widget to exit
147 147 if closing_widget==None:
148 148 return
149 149
150 150 #get a list of all slave widgets on the same kernel.
151 151 slave_tabs = self.find_slave_widgets(closing_widget)
152 152
153 153 keepkernel = None #Use the prompt by default
154 154 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
155 155 keepkernel = closing_widget._keep_kernel_on_exit
156 156 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
157 157 # we set local slave tabs._hidden to True to avoid prompting for kernel
158 158 # restart when they get the signal. and then "forward" the 'exit'
159 159 # to the main window
160 160 if keepkernel is not None:
161 161 for tab in slave_tabs:
162 162 tab._hidden = True
163 163 if closing_widget in slave_tabs:
164 164 try :
165 165 self.find_master_tab(closing_widget).execute('exit')
166 166 except AttributeError:
167 167 self.log.info("Master already closed or not local, closing only current tab")
168 168 self.tab_widget.removeTab(current_tab)
169 169 self.update_tab_bar_visibility()
170 170 return
171 171
172 172 kernel_manager = closing_widget.kernel_manager
173 173
174 174 if keepkernel is None and not closing_widget._confirm_exit:
175 175 # don't prompt, just terminate the kernel if we own it
176 176 # or leave it alone if we don't
177 177 keepkernel = closing_widget._existing
178 178 if keepkernel is None: #show prompt
179 179 if kernel_manager and kernel_manager.channels_running:
180 180 title = self.window().windowTitle()
181 181 cancel = QtGui.QMessageBox.Cancel
182 182 okay = QtGui.QMessageBox.Ok
183 183 if closing_widget._may_close:
184 184 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
185 185 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
186 186 justthis = QtGui.QPushButton("&No, just this Tab", self)
187 187 justthis.setShortcut('N')
188 188 closeall = QtGui.QPushButton("&Yes, close all", self)
189 189 closeall.setShortcut('Y')
190 190 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
191 191 title, msg)
192 192 box.setInformativeText(info)
193 193 box.addButton(cancel)
194 194 box.addButton(justthis, QtGui.QMessageBox.NoRole)
195 195 box.addButton(closeall, QtGui.QMessageBox.YesRole)
196 196 box.setDefaultButton(closeall)
197 197 box.setEscapeButton(cancel)
198 198 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
199 199 box.setIconPixmap(pixmap)
200 200 reply = box.exec_()
201 201 if reply == 1: # close All
202 202 for slave in slave_tabs:
203 203 background(slave.kernel_manager.stop_channels)
204 204 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
205 205 closing_widget.execute("exit")
206 206 self.tab_widget.removeTab(current_tab)
207 207 background(kernel_manager.stop_channels)
208 208 elif reply == 0: # close Console
209 209 if not closing_widget._existing:
210 210 # Have kernel: don't quit, just close the tab
211 211 closing_widget.execute("exit True")
212 212 self.tab_widget.removeTab(current_tab)
213 213 background(kernel_manager.stop_channels)
214 214 else:
215 215 reply = QtGui.QMessageBox.question(self, title,
216 216 "Are you sure you want to close this Console?"+
217 217 "\nThe Kernel and other Consoles will remain active.",
218 218 okay|cancel,
219 219 defaultButton=okay
220 220 )
221 221 if reply == okay:
222 222 self.tab_widget.removeTab(current_tab)
223 223 elif keepkernel: #close console but leave kernel running (no prompt)
224 224 self.tab_widget.removeTab(current_tab)
225 225 background(kernel_manager.stop_channels)
226 226 else: #close console and kernel (no prompt)
227 227 self.tab_widget.removeTab(current_tab)
228 228 if kernel_manager and kernel_manager.channels_running:
229 229 for slave in slave_tabs:
230 230 background(slave.kernel_manager.stop_channels)
231 231 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
232 232 kernel_manager.shutdown_kernel()
233 233 background(kernel_manager.stop_channels)
234 234
235 235 self.update_tab_bar_visibility()
236 236
237 237 def add_tab_with_frontend(self,frontend,name=None):
238 238 """ insert a tab with a given frontend in the tab bar, and give it a name
239 239
240 240 """
241 241 if not name:
242 242 name = 'kernel %i' % self.next_kernel_id
243 243 self.tab_widget.addTab(frontend,name)
244 244 self.update_tab_bar_visibility()
245 245 self.make_frontend_visible(frontend)
246 246 frontend.exit_requested.connect(self.close_tab)
247 247
248 248 def next_tab(self):
249 249 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
250 250
251 251 def prev_tab(self):
252 252 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
253 253
254 254 def make_frontend_visible(self,frontend):
255 255 widget_index=self.tab_widget.indexOf(frontend)
256 256 if widget_index > 0 :
257 257 self.tab_widget.setCurrentIndex(widget_index)
258 258
259 259 def find_master_tab(self,tab,as_list=False):
260 260 """
261 261 Try to return the frontend that own the kernel attached to the given widget/tab.
262 262
263 263 Only find frontend owed by the current application. Selection
264 264 based on port of the kernel, might be inacurate if several kernel
265 265 on different ip use same port number.
266 266
267 267 This fonction does the conversion tabNumber/widget if needed.
268 268 Might return None if no master widget (non local kernel)
269 269 Will crash IPython if more than 1 masterWidget
270 270
271 271 When asList set to True, always return a list of widget(s) owning
272 272 the kernel. The list might be empty or containing several Widget.
273 273 """
274 274
275 275 #convert from/to int/richIpythonWidget if needed
276 276 if isinstance(tab, int):
277 277 tab = self.tab_widget.widget(tab)
278 278 km=tab.kernel_manager
279 279
280 280 #build list of all widgets
281 281 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
282 282
283 283 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
284 284 # And should have a _may_close attribute
285 285 filtered_widget_list = [ widget for widget in widget_list if
286 286 widget.kernel_manager.connection_file == km.connection_file and
287 287 hasattr(widget,'_may_close') ]
288 288 # the master widget is the one that may close the kernel
289 289 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
290 290 if as_list:
291 291 return master_widget
292 292 assert(len(master_widget)<=1 )
293 293 if len(master_widget)==0:
294 294 return None
295 295
296 296 return master_widget[0]
297 297
298 298 def find_slave_widgets(self,tab):
299 299 """return all the frontends that do not own the kernel attached to the given widget/tab.
300 300
301 301 Only find frontends owned by the current application. Selection
302 302 based on connection file of the kernel.
303 303
304 304 This function does the conversion tabNumber/widget if needed.
305 305 """
306 306 #convert from/to int/richIpythonWidget if needed
307 307 if isinstance(tab, int):
308 308 tab = self.tab_widget.widget(tab)
309 309 km=tab.kernel_manager
310 310
311 311 #build list of all widgets
312 312 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
313 313
314 314 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
315 315 filtered_widget_list = ( widget for widget in widget_list if
316 316 widget.kernel_manager.connection_file == km.connection_file)
317 317 # Get a list of all widget owning the same kernel and removed it from
318 318 # the previous cadidate. (better using sets ?)
319 319 master_widget_list = self.find_master_tab(tab, as_list=True)
320 320 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
321 321
322 322 return slave_list
323 323
324 324 # Populate the menu bar with common actions and shortcuts
325 325 def add_menu_action(self, menu, action, defer_shortcut=False):
326 326 """Add action to menu as well as self
327 327
328 328 So that when the menu bar is invisible, its actions are still available.
329 329
330 330 If defer_shortcut is True, set the shortcut context to widget-only,
331 331 where it will avoid conflict with shortcuts already bound to the
332 332 widgets themselves.
333 333 """
334 334 menu.addAction(action)
335 335 self.addAction(action)
336 336
337 337 if defer_shortcut:
338 338 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
339 339
340 340 def init_menu_bar(self):
341 341 #create menu in the order they should appear in the menu bar
342 342 self.init_file_menu()
343 343 self.init_edit_menu()
344 344 self.init_view_menu()
345 345 self.init_kernel_menu()
346 346 self.init_magic_menu()
347 347 self.init_window_menu()
348 348 self.init_help_menu()
349 349
350 350 def init_file_menu(self):
351 351 self.file_menu = self.menuBar().addMenu("&File")
352 352
353 353 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
354 354 self,
355 355 shortcut="Ctrl+T",
356 356 triggered=self.create_tab_with_new_frontend)
357 357 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
358 358
359 359 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
360 360 self,
361 361 shortcut="Ctrl+Shift+T",
362 362 triggered=self.create_tab_with_current_kernel)
363 363 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
364 364
365 365 self.file_menu.addSeparator()
366 366
367 367 self.close_action=QtGui.QAction("&Close Tab",
368 368 self,
369 369 shortcut=QtGui.QKeySequence.Close,
370 370 triggered=self.close_active_frontend
371 371 )
372 372 self.add_menu_action(self.file_menu, self.close_action)
373 373
374 374 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
375 375 self,
376 376 shortcut=QtGui.QKeySequence.Save,
377 377 triggered=self.export_action_active_frontend
378 378 )
379 379 self.add_menu_action(self.file_menu, self.export_action, True)
380 380
381 381 self.file_menu.addSeparator()
382 382
383 383 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
384 384 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
385 385 # Only override the default if there is a collision.
386 386 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
387 387 printkey = "Ctrl+Shift+P"
388 388 self.print_action = QtGui.QAction("&Print",
389 389 self,
390 390 shortcut=printkey,
391 391 triggered=self.print_action_active_frontend)
392 392 self.add_menu_action(self.file_menu, self.print_action, True)
393 393
394 394 if sys.platform != 'darwin':
395 395 # OSX always has Quit in the Application menu, only add it
396 396 # to the File menu elsewhere.
397 397
398 398 self.file_menu.addSeparator()
399 399
400 400 self.quit_action = QtGui.QAction("&Quit",
401 401 self,
402 402 shortcut=QtGui.QKeySequence.Quit,
403 403 triggered=self.close,
404 404 )
405 405 self.add_menu_action(self.file_menu, self.quit_action)
406 406
407 407
408 408 def init_edit_menu(self):
409 409 self.edit_menu = self.menuBar().addMenu("&Edit")
410 410
411 411 self.undo_action = QtGui.QAction("&Undo",
412 412 self,
413 413 shortcut=QtGui.QKeySequence.Undo,
414 414 statusTip="Undo last action if possible",
415 415 triggered=self.undo_active_frontend
416 416 )
417 417 self.add_menu_action(self.edit_menu, self.undo_action)
418 418
419 419 self.redo_action = QtGui.QAction("&Redo",
420 420 self,
421 421 shortcut=QtGui.QKeySequence.Redo,
422 422 statusTip="Redo last action if possible",
423 423 triggered=self.redo_active_frontend)
424 424 self.add_menu_action(self.edit_menu, self.redo_action)
425 425
426 426 self.edit_menu.addSeparator()
427 427
428 428 self.cut_action = QtGui.QAction("&Cut",
429 429 self,
430 430 shortcut=QtGui.QKeySequence.Cut,
431 431 triggered=self.cut_active_frontend
432 432 )
433 433 self.add_menu_action(self.edit_menu, self.cut_action, True)
434 434
435 435 self.copy_action = QtGui.QAction("&Copy",
436 436 self,
437 437 shortcut=QtGui.QKeySequence.Copy,
438 438 triggered=self.copy_active_frontend
439 439 )
440 440 self.add_menu_action(self.edit_menu, self.copy_action, True)
441 441
442 442 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
443 443 self,
444 444 shortcut="Ctrl+Shift+C",
445 445 triggered=self.copy_raw_active_frontend
446 446 )
447 447 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
448 448
449 449 self.paste_action = QtGui.QAction("&Paste",
450 450 self,
451 451 shortcut=QtGui.QKeySequence.Paste,
452 452 triggered=self.paste_active_frontend
453 453 )
454 454 self.add_menu_action(self.edit_menu, self.paste_action, True)
455 455
456 456 self.edit_menu.addSeparator()
457 457
458 458 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
459 459 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
460 460 # Only override the default if there is a collision.
461 461 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
462 462 selectall = "Ctrl+Shift+A"
463 463 self.select_all_action = QtGui.QAction("Select &All",
464 464 self,
465 465 shortcut=selectall,
466 466 triggered=self.select_all_active_frontend
467 467 )
468 468 self.add_menu_action(self.edit_menu, self.select_all_action, True)
469 469
470 470
471 471 def init_view_menu(self):
472 472 self.view_menu = self.menuBar().addMenu("&View")
473 473
474 474 if sys.platform != 'darwin':
475 475 # disable on OSX, where there is always a menu bar
476 476 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
477 477 self,
478 478 shortcut="Ctrl+Shift+M",
479 479 statusTip="Toggle visibility of menubar",
480 480 triggered=self.toggle_menu_bar)
481 481 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
482 482
483 483 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
484 484 self.full_screen_act = QtGui.QAction("&Full Screen",
485 485 self,
486 486 shortcut=fs_key,
487 487 statusTip="Toggle between Fullscreen and Normal Size",
488 488 triggered=self.toggleFullScreen)
489 489 self.add_menu_action(self.view_menu, self.full_screen_act)
490 490
491 491 self.view_menu.addSeparator()
492 492
493 493 self.increase_font_size = QtGui.QAction("Zoom &In",
494 494 self,
495 495 shortcut=QtGui.QKeySequence.ZoomIn,
496 496 triggered=self.increase_font_size_active_frontend
497 497 )
498 498 self.add_menu_action(self.view_menu, self.increase_font_size, True)
499 499
500 500 self.decrease_font_size = QtGui.QAction("Zoom &Out",
501 501 self,
502 502 shortcut=QtGui.QKeySequence.ZoomOut,
503 503 triggered=self.decrease_font_size_active_frontend
504 504 )
505 505 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
506 506
507 507 self.reset_font_size = QtGui.QAction("Zoom &Reset",
508 508 self,
509 509 shortcut="Ctrl+0",
510 510 triggered=self.reset_font_size_active_frontend
511 511 )
512 512 self.add_menu_action(self.view_menu, self.reset_font_size, True)
513 513
514 514 self.view_menu.addSeparator()
515 515
516 516 self.clear_action = QtGui.QAction("&Clear Screen",
517 517 self,
518 518 shortcut='Ctrl+L',
519 519 statusTip="Clear the console",
520 520 triggered=self.clear_magic_active_frontend)
521 521 self.add_menu_action(self.view_menu, self.clear_action)
522 522
523 523 def init_kernel_menu(self):
524 524 self.kernel_menu = self.menuBar().addMenu("&Kernel")
525 525 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
526 526 # keep the signal shortcuts to ctrl, rather than
527 527 # platform-default like we do elsewhere.
528 528
529 529 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
530 530
531 531 self.interrupt_kernel_action = QtGui.QAction("Interrupt current Kernel",
532 532 self,
533 533 triggered=self.interrupt_kernel_active_frontend,
534 534 shortcut=ctrl+"+C",
535 535 )
536 536 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
537 537
538 538 self.restart_kernel_action = QtGui.QAction("Restart current Kernel",
539 539 self,
540 540 triggered=self.restart_kernel_active_frontend,
541 541 shortcut=ctrl+"+.",
542 542 )
543 543 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
544 544
545 545 self.kernel_menu.addSeparator()
546 546
547 547 def update_all_magic_menu(self):
548 548 # first define a callback which will get the list of all magic and put it in the menu.
549 549 def populate_all_magic_menu(val=None):
550 550 alm_magic_menu = self.all_magic_menu
551 551 alm_magic_menu.clear()
552 552 def make_dynamic_magic(i):
553 553 def inner_dynamic_magic():
554 554 self.active_frontend.execute(i)
555 555 inner_dynamic_magic.__name__ = "dynamics_magic_s"
556 556 return inner_dynamic_magic
557 557
558 558 # list of protected magic that don't like to be called without argument
559 559 # append '?' to the end to print the docstring when called from the menu
560 protected_magic= ["more","less","load_ext","pycat"]
560 protected_magic= ["more","less","load_ext","pycat","loadpy","save"]
561 561
562 562 for magic in eval(val):
563 563 if magic in protected_magic:
564 564 pmagic = '%s%s%s'%('%',magic,'?')
565 565 else:
566 566 pmagic = '%s%s'%('%',magic)
567 567 xaction = QtGui.QAction(pmagic,
568 568 self,
569 569 triggered=make_dynamic_magic(pmagic)
570 570 )
571 571 alm_magic_menu.addAction(xaction)
572 572 self.active_frontend._silent_exec_callback('get_ipython().lsmagic()',populate_all_magic_menu)
573 573
574 574 def init_magic_menu(self):
575 575 self.magic_menu = self.menuBar().addMenu("&Magic")
576 576 self.all_magic_menu = self.magic_menu.addMenu("&All Magics")
577 577
578 578 # this action should not appear as it will be cleard when menu
579 579 # will be updated at first kernel response.
580 580 self.pop = QtGui.QAction("&Update All Magic Menu ",
581 581 self,
582 582 triggered=self.update_all_magic_menu)
583 583 self.add_menu_action(self.all_magic_menu, self.pop)
584 584
585 585 self.reset_action = QtGui.QAction("&Reset",
586 586 self,
587 587 statusTip="Clear all varible from workspace",
588 588 triggered=self.reset_magic_active_frontend)
589 589 self.add_menu_action(self.magic_menu, self.reset_action)
590 590
591 591 self.history_action = QtGui.QAction("&History",
592 592 self,
593 593 statusTip="show command history",
594 594 triggered=self.history_magic_active_frontend)
595 595 self.add_menu_action(self.magic_menu, self.history_action)
596 596
597 597 self.save_action = QtGui.QAction("E&xport History ",
598 598 self,
599 599 statusTip="Export History as Python File",
600 600 triggered=self.save_magic_active_frontend)
601 601 self.add_menu_action(self.magic_menu, self.save_action)
602 602
603 603 self.who_action = QtGui.QAction("&Who",
604 604 self,
605 605 statusTip="List interactive variable",
606 606 triggered=self.who_magic_active_frontend)
607 607 self.add_menu_action(self.magic_menu, self.who_action)
608 608
609 609 self.who_ls_action = QtGui.QAction("Wh&o ls",
610 610 self,
611 611 statusTip="Return a list of interactive variable",
612 612 triggered=self.who_ls_magic_active_frontend)
613 613 self.add_menu_action(self.magic_menu, self.who_ls_action)
614 614
615 615 self.whos_action = QtGui.QAction("Who&s",
616 616 self,
617 617 statusTip="List interactive variable with detail",
618 618 triggered=self.whos_magic_active_frontend)
619 619 self.add_menu_action(self.magic_menu, self.whos_action)
620 620
621 621 def init_window_menu(self):
622 622 self.window_menu = self.menuBar().addMenu("&Window")
623 623 if sys.platform == 'darwin':
624 624 # add min/maximize actions to OSX, which lacks default bindings.
625 625 self.minimizeAct = QtGui.QAction("Mini&mize",
626 626 self,
627 627 shortcut="Ctrl+m",
628 628 statusTip="Minimize the window/Restore Normal Size",
629 629 triggered=self.toggleMinimized)
630 630 # maximize is called 'Zoom' on OSX for some reason
631 631 self.maximizeAct = QtGui.QAction("&Zoom",
632 632 self,
633 633 shortcut="Ctrl+Shift+M",
634 634 statusTip="Maximize the window/Restore Normal Size",
635 635 triggered=self.toggleMaximized)
636 636
637 637 self.add_menu_action(self.window_menu, self.minimizeAct)
638 638 self.add_menu_action(self.window_menu, self.maximizeAct)
639 639 self.window_menu.addSeparator()
640 640
641 641 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
642 642 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
643 643 self,
644 644 shortcut=prev_key,
645 645 statusTip="Select previous tab",
646 646 triggered=self.prev_tab)
647 647 self.add_menu_action(self.window_menu, self.prev_tab_act)
648 648
649 649 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
650 650 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
651 651 self,
652 652 shortcut=next_key,
653 653 statusTip="Select next tab",
654 654 triggered=self.next_tab)
655 655 self.add_menu_action(self.window_menu, self.next_tab_act)
656 656
657 657 def init_help_menu(self):
658 658 # please keep the Help menu in Mac Os even if empty. It will
659 659 # automatically contain a search field to search inside menus and
660 660 # please keep it spelled in English, as long as Qt Doesn't support
661 661 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
662 662 # this search field fonctionality
663 663
664 664 self.help_menu = self.menuBar().addMenu("&Help")
665 665
666 666
667 667 # Help Menu
668 668
669 669 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
670 670 self,
671 671 triggered=self.intro_active_frontend
672 672 )
673 673 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
674 674
675 675 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
676 676 self,
677 677 triggered=self.quickref_active_frontend
678 678 )
679 679 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
680 680
681 681 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
682 682 self,
683 683 triggered=self.guiref_active_frontend
684 684 )
685 685 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
686 686
687 687 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
688 688 self,
689 689 triggered=self._open_online_help)
690 690 self.add_menu_action(self.help_menu, self.onlineHelpAct)
691 691
692 692 # minimize/maximize/fullscreen actions:
693 693
694 694 def toggle_menu_bar(self):
695 695 menu_bar = self.menuBar()
696 696 if menu_bar.isVisible():
697 697 menu_bar.setVisible(False)
698 698 else:
699 699 menu_bar.setVisible(True)
700 700
701 701 def toggleMinimized(self):
702 702 if not self.isMinimized():
703 703 self.showMinimized()
704 704 else:
705 705 self.showNormal()
706 706
707 707 def _open_online_help(self):
708 708 filename="http://ipython.org/ipython-doc/stable/index.html"
709 709 webbrowser.open(filename, new=1, autoraise=True)
710 710
711 711 def toggleMaximized(self):
712 712 if not self.isMaximized():
713 713 self.showMaximized()
714 714 else:
715 715 self.showNormal()
716 716
717 717 # Min/Max imizing while in full screen give a bug
718 718 # when going out of full screen, at least on OSX
719 719 def toggleFullScreen(self):
720 720 if not self.isFullScreen():
721 721 self.showFullScreen()
722 722 if sys.platform == 'darwin':
723 723 self.maximizeAct.setEnabled(False)
724 724 self.minimizeAct.setEnabled(False)
725 725 else:
726 726 self.showNormal()
727 727 if sys.platform == 'darwin':
728 728 self.maximizeAct.setEnabled(True)
729 729 self.minimizeAct.setEnabled(True)
730 730
731 731 def close_active_frontend(self):
732 732 self.close_tab(self.active_frontend)
733 733
734 734 def restart_kernel_active_frontend(self):
735 735 self.active_frontend.request_restart_kernel()
736 736
737 737 def interrupt_kernel_active_frontend(self):
738 738 self.active_frontend.request_interrupt_kernel()
739 739
740 740 def cut_active_frontend(self):
741 741 widget = self.active_frontend
742 742 if widget.can_cut():
743 743 widget.cut()
744 744
745 745 def copy_active_frontend(self):
746 746 widget = self.active_frontend
747 747 if widget.can_copy():
748 748 widget.copy()
749 749
750 750 def copy_raw_active_frontend(self):
751 751 self.active_frontend._copy_raw_action.trigger()
752 752
753 753 def paste_active_frontend(self):
754 754 widget = self.active_frontend
755 755 if widget.can_paste():
756 756 widget.paste()
757 757
758 758 def undo_active_frontend(self):
759 759 self.active_frontend.undo()
760 760
761 761 def redo_active_frontend(self):
762 762 self.active_frontend.redo()
763 763
764 764 def reset_magic_active_frontend(self):
765 765 self.active_frontend.execute("%reset")
766 766
767 767 def history_magic_active_frontend(self):
768 768 self.active_frontend.execute("%history")
769 769
770 770 def save_magic_active_frontend(self):
771 771 self.active_frontend.save_magic()
772 772
773 773 def clear_magic_active_frontend(self):
774 774 self.active_frontend.execute("%clear")
775 775
776 776 def who_magic_active_frontend(self):
777 777 self.active_frontend.execute("%who")
778 778
779 779 def who_ls_magic_active_frontend(self):
780 780 self.active_frontend.execute("%who_ls")
781 781
782 782 def whos_magic_active_frontend(self):
783 783 self.active_frontend.execute("%whos")
784 784
785 785 def print_action_active_frontend(self):
786 786 self.active_frontend.print_action.trigger()
787 787
788 788 def export_action_active_frontend(self):
789 789 self.active_frontend.export_action.trigger()
790 790
791 791 def select_all_active_frontend(self):
792 792 self.active_frontend.select_all_action.trigger()
793 793
794 794 def increase_font_size_active_frontend(self):
795 795 self.active_frontend.increase_font_size.trigger()
796 796
797 797 def decrease_font_size_active_frontend(self):
798 798 self.active_frontend.decrease_font_size.trigger()
799 799
800 800 def reset_font_size_active_frontend(self):
801 801 self.active_frontend.reset_font_size.trigger()
802 802
803 803 def guiref_active_frontend(self):
804 804 self.active_frontend.execute("%guiref")
805 805
806 806 def intro_active_frontend(self):
807 807 self.active_frontend.execute("?")
808 808
809 809 def quickref_active_frontend(self):
810 810 self.active_frontend.execute("%quickref")
811 811 #---------------------------------------------------------------------------
812 812 # QWidget interface
813 813 #---------------------------------------------------------------------------
814 814
815 815 def closeEvent(self, event):
816 816 """ Forward the close event to every tabs contained by the windows
817 817 """
818 818 if self.tab_widget.count() == 0:
819 819 # no tabs, just close
820 820 event.accept()
821 821 return
822 822 # Do Not loop on the widget count as it change while closing
823 823 title = self.window().windowTitle()
824 824 cancel = QtGui.QMessageBox.Cancel
825 825 okay = QtGui.QMessageBox.Ok
826 826
827 827 if self.confirm_exit:
828 828 if self.tab_widget.count() > 1:
829 829 msg = "Close all tabs, stop all kernels, and Quit?"
830 830 else:
831 831 msg = "Close console, stop kernel, and Quit?"
832 832 info = "Kernels not started here (e.g. notebooks) will be left alone."
833 833 closeall = QtGui.QPushButton("&Yes, quit everything", self)
834 834 closeall.setShortcut('Y')
835 835 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
836 836 title, msg)
837 837 box.setInformativeText(info)
838 838 box.addButton(cancel)
839 839 box.addButton(closeall, QtGui.QMessageBox.YesRole)
840 840 box.setDefaultButton(closeall)
841 841 box.setEscapeButton(cancel)
842 842 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
843 843 box.setIconPixmap(pixmap)
844 844 reply = box.exec_()
845 845 else:
846 846 reply = okay
847 847
848 848 if reply == cancel:
849 849 event.ignore()
850 850 return
851 851 if reply == okay:
852 852 while self.tab_widget.count() >= 1:
853 853 # prevent further confirmations:
854 854 widget = self.active_frontend
855 855 widget._confirm_exit = False
856 856 self.close_tab(widget)
857 857 event.accept()
858 858
General Comments 0
You need to be logged in to leave comments. Login now