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