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