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