##// END OF EJS Templates
clarify close dialog on qtconsole...
MinRK -
Show More
@@ -1,847 +1,851 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 init_magic_menu(self):
547 def init_magic_menu(self):
548 self.magic_menu = self.menuBar().addMenu("&Magic")
548 self.magic_menu = self.menuBar().addMenu("&Magic")
549 self.all_magic_menu = self.magic_menu.addMenu("&All Magics")
549 self.all_magic_menu = self.magic_menu.addMenu("&All Magics")
550
550
551 self.reset_action = QtGui.QAction("&Reset",
551 self.reset_action = QtGui.QAction("&Reset",
552 self,
552 self,
553 statusTip="Clear all varible from workspace",
553 statusTip="Clear all varible from workspace",
554 triggered=self.reset_magic_active_frontend)
554 triggered=self.reset_magic_active_frontend)
555 self.add_menu_action(self.magic_menu, self.reset_action)
555 self.add_menu_action(self.magic_menu, self.reset_action)
556
556
557 self.history_action = QtGui.QAction("&History",
557 self.history_action = QtGui.QAction("&History",
558 self,
558 self,
559 statusTip="show command history",
559 statusTip="show command history",
560 triggered=self.history_magic_active_frontend)
560 triggered=self.history_magic_active_frontend)
561 self.add_menu_action(self.magic_menu, self.history_action)
561 self.add_menu_action(self.magic_menu, self.history_action)
562
562
563 self.save_action = QtGui.QAction("E&xport History ",
563 self.save_action = QtGui.QAction("E&xport History ",
564 self,
564 self,
565 statusTip="Export History as Python File",
565 statusTip="Export History as Python File",
566 triggered=self.save_magic_active_frontend)
566 triggered=self.save_magic_active_frontend)
567 self.add_menu_action(self.magic_menu, self.save_action)
567 self.add_menu_action(self.magic_menu, self.save_action)
568
568
569 self.who_action = QtGui.QAction("&Who",
569 self.who_action = QtGui.QAction("&Who",
570 self,
570 self,
571 statusTip="List interactive variable",
571 statusTip="List interactive variable",
572 triggered=self.who_magic_active_frontend)
572 triggered=self.who_magic_active_frontend)
573 self.add_menu_action(self.magic_menu, self.who_action)
573 self.add_menu_action(self.magic_menu, self.who_action)
574
574
575 self.who_ls_action = QtGui.QAction("Wh&o ls",
575 self.who_ls_action = QtGui.QAction("Wh&o ls",
576 self,
576 self,
577 statusTip="Return a list of interactive variable",
577 statusTip="Return a list of interactive variable",
578 triggered=self.who_ls_magic_active_frontend)
578 triggered=self.who_ls_magic_active_frontend)
579 self.add_menu_action(self.magic_menu, self.who_ls_action)
579 self.add_menu_action(self.magic_menu, self.who_ls_action)
580
580
581 self.whos_action = QtGui.QAction("Who&s",
581 self.whos_action = QtGui.QAction("Who&s",
582 self,
582 self,
583 statusTip="List interactive variable with detail",
583 statusTip="List interactive variable with detail",
584 triggered=self.whos_magic_active_frontend)
584 triggered=self.whos_magic_active_frontend)
585 self.add_menu_action(self.magic_menu, self.whos_action)
585 self.add_menu_action(self.magic_menu, self.whos_action)
586
586
587 # allmagics submenu:
587 # allmagics submenu:
588
588
589 #for now this is just a copy and paste, but we should get this dynamically
589 #for now this is just a copy and paste, but we should get this dynamically
590 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
590 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
591 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
591 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
592 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
592 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
593 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
593 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
594 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
594 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
595 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
595 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
596 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
596 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
597 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
597 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
598 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
598 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
599 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
599 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
600
600
601 def make_dynamic_magic(i):
601 def make_dynamic_magic(i):
602 def inner_dynamic_magic():
602 def inner_dynamic_magic():
603 self.active_frontend.execute(i)
603 self.active_frontend.execute(i)
604 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
604 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
605 return inner_dynamic_magic
605 return inner_dynamic_magic
606
606
607 for magic in magiclist:
607 for magic in magiclist:
608 xaction = QtGui.QAction(magic,
608 xaction = QtGui.QAction(magic,
609 self,
609 self,
610 triggered=make_dynamic_magic(magic)
610 triggered=make_dynamic_magic(magic)
611 )
611 )
612 self.all_magic_menu.addAction(xaction)
612 self.all_magic_menu.addAction(xaction)
613
613
614 def init_window_menu(self):
614 def init_window_menu(self):
615 self.window_menu = self.menuBar().addMenu("&Window")
615 self.window_menu = self.menuBar().addMenu("&Window")
616 if sys.platform == 'darwin':
616 if sys.platform == 'darwin':
617 # add min/maximize actions to OSX, which lacks default bindings.
617 # add min/maximize actions to OSX, which lacks default bindings.
618 self.minimizeAct = QtGui.QAction("Mini&mize",
618 self.minimizeAct = QtGui.QAction("Mini&mize",
619 self,
619 self,
620 shortcut="Ctrl+m",
620 shortcut="Ctrl+m",
621 statusTip="Minimize the window/Restore Normal Size",
621 statusTip="Minimize the window/Restore Normal Size",
622 triggered=self.toggleMinimized)
622 triggered=self.toggleMinimized)
623 # maximize is called 'Zoom' on OSX for some reason
623 # maximize is called 'Zoom' on OSX for some reason
624 self.maximizeAct = QtGui.QAction("&Zoom",
624 self.maximizeAct = QtGui.QAction("&Zoom",
625 self,
625 self,
626 shortcut="Ctrl+Shift+M",
626 shortcut="Ctrl+Shift+M",
627 statusTip="Maximize the window/Restore Normal Size",
627 statusTip="Maximize the window/Restore Normal Size",
628 triggered=self.toggleMaximized)
628 triggered=self.toggleMaximized)
629
629
630 self.add_menu_action(self.window_menu, self.minimizeAct)
630 self.add_menu_action(self.window_menu, self.minimizeAct)
631 self.add_menu_action(self.window_menu, self.maximizeAct)
631 self.add_menu_action(self.window_menu, self.maximizeAct)
632 self.window_menu.addSeparator()
632 self.window_menu.addSeparator()
633
633
634 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
634 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
635 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
635 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
636 self,
636 self,
637 shortcut=prev_key,
637 shortcut=prev_key,
638 statusTip="Select previous tab",
638 statusTip="Select previous tab",
639 triggered=self.prev_tab)
639 triggered=self.prev_tab)
640 self.add_menu_action(self.window_menu, self.prev_tab_act)
640 self.add_menu_action(self.window_menu, self.prev_tab_act)
641
641
642 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
642 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
643 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
643 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
644 self,
644 self,
645 shortcut=next_key,
645 shortcut=next_key,
646 statusTip="Select next tab",
646 statusTip="Select next tab",
647 triggered=self.next_tab)
647 triggered=self.next_tab)
648 self.add_menu_action(self.window_menu, self.next_tab_act)
648 self.add_menu_action(self.window_menu, self.next_tab_act)
649
649
650 def init_help_menu(self):
650 def init_help_menu(self):
651 # please keep the Help menu in Mac Os even if empty. It will
651 # please keep the Help menu in Mac Os even if empty. It will
652 # automatically contain a search field to search inside menus and
652 # automatically contain a search field to search inside menus and
653 # please keep it spelled in English, as long as Qt Doesn't support
653 # please keep it spelled in English, as long as Qt Doesn't support
654 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
654 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
655 # this search field fonctionality
655 # this search field fonctionality
656
656
657 self.help_menu = self.menuBar().addMenu("&Help")
657 self.help_menu = self.menuBar().addMenu("&Help")
658
658
659
659
660 # Help Menu
660 # Help Menu
661
661
662 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
662 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
663 self,
663 self,
664 triggered=self.intro_active_frontend
664 triggered=self.intro_active_frontend
665 )
665 )
666 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
666 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
667
667
668 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
668 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
669 self,
669 self,
670 triggered=self.quickref_active_frontend
670 triggered=self.quickref_active_frontend
671 )
671 )
672 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
672 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
673
673
674 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
674 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
675 self,
675 self,
676 triggered=self.guiref_active_frontend
676 triggered=self.guiref_active_frontend
677 )
677 )
678 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
678 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
679
679
680 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
680 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
681 self,
681 self,
682 triggered=self._open_online_help)
682 triggered=self._open_online_help)
683 self.add_menu_action(self.help_menu, self.onlineHelpAct)
683 self.add_menu_action(self.help_menu, self.onlineHelpAct)
684
684
685 # minimize/maximize/fullscreen actions:
685 # minimize/maximize/fullscreen actions:
686
686
687 def toggle_menu_bar(self):
687 def toggle_menu_bar(self):
688 menu_bar = self.menuBar()
688 menu_bar = self.menuBar()
689 if menu_bar.isVisible():
689 if menu_bar.isVisible():
690 menu_bar.setVisible(False)
690 menu_bar.setVisible(False)
691 else:
691 else:
692 menu_bar.setVisible(True)
692 menu_bar.setVisible(True)
693
693
694 def toggleMinimized(self):
694 def toggleMinimized(self):
695 if not self.isMinimized():
695 if not self.isMinimized():
696 self.showMinimized()
696 self.showMinimized()
697 else:
697 else:
698 self.showNormal()
698 self.showNormal()
699
699
700 def _open_online_help(self):
700 def _open_online_help(self):
701 filename="http://ipython.org/ipython-doc/stable/index.html"
701 filename="http://ipython.org/ipython-doc/stable/index.html"
702 webbrowser.open(filename, new=1, autoraise=True)
702 webbrowser.open(filename, new=1, autoraise=True)
703
703
704 def toggleMaximized(self):
704 def toggleMaximized(self):
705 if not self.isMaximized():
705 if not self.isMaximized():
706 self.showMaximized()
706 self.showMaximized()
707 else:
707 else:
708 self.showNormal()
708 self.showNormal()
709
709
710 # Min/Max imizing while in full screen give a bug
710 # Min/Max imizing while in full screen give a bug
711 # when going out of full screen, at least on OSX
711 # when going out of full screen, at least on OSX
712 def toggleFullScreen(self):
712 def toggleFullScreen(self):
713 if not self.isFullScreen():
713 if not self.isFullScreen():
714 self.showFullScreen()
714 self.showFullScreen()
715 if sys.platform == 'darwin':
715 if sys.platform == 'darwin':
716 self.maximizeAct.setEnabled(False)
716 self.maximizeAct.setEnabled(False)
717 self.minimizeAct.setEnabled(False)
717 self.minimizeAct.setEnabled(False)
718 else:
718 else:
719 self.showNormal()
719 self.showNormal()
720 if sys.platform == 'darwin':
720 if sys.platform == 'darwin':
721 self.maximizeAct.setEnabled(True)
721 self.maximizeAct.setEnabled(True)
722 self.minimizeAct.setEnabled(True)
722 self.minimizeAct.setEnabled(True)
723
723
724 def close_active_frontend(self):
724 def close_active_frontend(self):
725 self.close_tab(self.active_frontend)
725 self.close_tab(self.active_frontend)
726
726
727 def restart_kernel_active_frontend(self):
727 def restart_kernel_active_frontend(self):
728 self.active_frontend.request_restart_kernel()
728 self.active_frontend.request_restart_kernel()
729
729
730 def interrupt_kernel_active_frontend(self):
730 def interrupt_kernel_active_frontend(self):
731 self.active_frontend.request_interrupt_kernel()
731 self.active_frontend.request_interrupt_kernel()
732
732
733 def cut_active_frontend(self):
733 def cut_active_frontend(self):
734 widget = self.active_frontend
734 widget = self.active_frontend
735 if widget.can_cut():
735 if widget.can_cut():
736 widget.cut()
736 widget.cut()
737
737
738 def copy_active_frontend(self):
738 def copy_active_frontend(self):
739 widget = self.active_frontend
739 widget = self.active_frontend
740 if widget.can_copy():
740 if widget.can_copy():
741 widget.copy()
741 widget.copy()
742
742
743 def copy_raw_active_frontend(self):
743 def copy_raw_active_frontend(self):
744 self.active_frontend._copy_raw_action.trigger()
744 self.active_frontend._copy_raw_action.trigger()
745
745
746 def paste_active_frontend(self):
746 def paste_active_frontend(self):
747 widget = self.active_frontend
747 widget = self.active_frontend
748 if widget.can_paste():
748 if widget.can_paste():
749 widget.paste()
749 widget.paste()
750
750
751 def undo_active_frontend(self):
751 def undo_active_frontend(self):
752 self.active_frontend.undo()
752 self.active_frontend.undo()
753
753
754 def redo_active_frontend(self):
754 def redo_active_frontend(self):
755 self.active_frontend.redo()
755 self.active_frontend.redo()
756
756
757 def reset_magic_active_frontend(self):
757 def reset_magic_active_frontend(self):
758 self.active_frontend.execute("%reset")
758 self.active_frontend.execute("%reset")
759
759
760 def history_magic_active_frontend(self):
760 def history_magic_active_frontend(self):
761 self.active_frontend.execute("%history")
761 self.active_frontend.execute("%history")
762
762
763 def save_magic_active_frontend(self):
763 def save_magic_active_frontend(self):
764 self.active_frontend.save_magic()
764 self.active_frontend.save_magic()
765
765
766 def clear_magic_active_frontend(self):
766 def clear_magic_active_frontend(self):
767 self.active_frontend.execute("%clear")
767 self.active_frontend.execute("%clear")
768
768
769 def who_magic_active_frontend(self):
769 def who_magic_active_frontend(self):
770 self.active_frontend.execute("%who")
770 self.active_frontend.execute("%who")
771
771
772 def who_ls_magic_active_frontend(self):
772 def who_ls_magic_active_frontend(self):
773 self.active_frontend.execute("%who_ls")
773 self.active_frontend.execute("%who_ls")
774
774
775 def whos_magic_active_frontend(self):
775 def whos_magic_active_frontend(self):
776 self.active_frontend.execute("%whos")
776 self.active_frontend.execute("%whos")
777
777
778 def print_action_active_frontend(self):
778 def print_action_active_frontend(self):
779 self.active_frontend.print_action.trigger()
779 self.active_frontend.print_action.trigger()
780
780
781 def export_action_active_frontend(self):
781 def export_action_active_frontend(self):
782 self.active_frontend.export_action.trigger()
782 self.active_frontend.export_action.trigger()
783
783
784 def select_all_active_frontend(self):
784 def select_all_active_frontend(self):
785 self.active_frontend.select_all_action.trigger()
785 self.active_frontend.select_all_action.trigger()
786
786
787 def increase_font_size_active_frontend(self):
787 def increase_font_size_active_frontend(self):
788 self.active_frontend.increase_font_size.trigger()
788 self.active_frontend.increase_font_size.trigger()
789
789
790 def decrease_font_size_active_frontend(self):
790 def decrease_font_size_active_frontend(self):
791 self.active_frontend.decrease_font_size.trigger()
791 self.active_frontend.decrease_font_size.trigger()
792
792
793 def reset_font_size_active_frontend(self):
793 def reset_font_size_active_frontend(self):
794 self.active_frontend.reset_font_size.trigger()
794 self.active_frontend.reset_font_size.trigger()
795
795
796 def guiref_active_frontend(self):
796 def guiref_active_frontend(self):
797 self.active_frontend.execute("%guiref")
797 self.active_frontend.execute("%guiref")
798
798
799 def intro_active_frontend(self):
799 def intro_active_frontend(self):
800 self.active_frontend.execute("?")
800 self.active_frontend.execute("?")
801
801
802 def quickref_active_frontend(self):
802 def quickref_active_frontend(self):
803 self.active_frontend.execute("%quickref")
803 self.active_frontend.execute("%quickref")
804 #---------------------------------------------------------------------------
804 #---------------------------------------------------------------------------
805 # QWidget interface
805 # QWidget interface
806 #---------------------------------------------------------------------------
806 #---------------------------------------------------------------------------
807
807
808 def closeEvent(self, event):
808 def closeEvent(self, event):
809 """ Forward the close event to every tabs contained by the windows
809 """ Forward the close event to every tabs contained by the windows
810 """
810 """
811 if self.tab_widget.count() == 0:
811 if self.tab_widget.count() == 0:
812 # no tabs, just close
812 # no tabs, just close
813 event.accept()
813 event.accept()
814 return
814 return
815 # Do Not loop on the widget count as it change while closing
815 # Do Not loop on the widget count as it change while closing
816 title = self.window().windowTitle()
816 title = self.window().windowTitle()
817 cancel = QtGui.QMessageBox.Cancel
817 cancel = QtGui.QMessageBox.Cancel
818 okay = QtGui.QMessageBox.Ok
818 okay = QtGui.QMessageBox.Ok
819
819
820 if self.confirm_exit:
820 if self.confirm_exit:
821 msg = "Close all tabs, stop all kernels, and Quit?"
821 if self.tab_widget.count() > 1:
822 msg = "Close all tabs, stop all kernels, and Quit?"
823 else:
824 msg = "Close console, stop kernel, and Quit?"
825 info = "Kernels not started here (e.g. notebooks) will be left alone."
822 closeall = QtGui.QPushButton("&Yes, quit everything", self)
826 closeall = QtGui.QPushButton("&Yes, quit everything", self)
823 closeall.setShortcut('Y')
827 closeall.setShortcut('Y')
824 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
828 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
825 title, msg)
829 title, msg)
826 # box.setInformativeText(info)
830 box.setInformativeText(info)
827 box.addButton(cancel)
831 box.addButton(cancel)
828 box.addButton(closeall, QtGui.QMessageBox.YesRole)
832 box.addButton(closeall, QtGui.QMessageBox.YesRole)
829 box.setDefaultButton(closeall)
833 box.setDefaultButton(closeall)
830 box.setEscapeButton(cancel)
834 box.setEscapeButton(cancel)
831 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
835 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
832 box.setIconPixmap(pixmap)
836 box.setIconPixmap(pixmap)
833 reply = box.exec_()
837 reply = box.exec_()
834 else:
838 else:
835 reply = okay
839 reply = okay
836
840
837 if reply == cancel:
841 if reply == cancel:
838 event.ignore()
842 event.ignore()
839 return
843 return
840 if reply == okay:
844 if reply == okay:
841 while self.tab_widget.count() >= 1:
845 while self.tab_widget.count() >= 1:
842 # prevent further confirmations:
846 # prevent further confirmations:
843 widget = self.active_frontend
847 widget = self.active_frontend
844 widget._confirm_exit = False
848 widget._confirm_exit = False
845 self.close_tab(widget)
849 self.close_tab(widget)
846 event.accept()
850 event.accept()
847
851
General Comments 0
You need to be logged in to leave comments. Login now