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