##// END OF EJS Templates
Remove "all magics" menu stuff.
Dimitry Kloper -
Show More
@@ -1,1151 +1,1034 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 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import json
23 import json
24 import re
24 import re
25 import sys
25 import sys
26 import webbrowser
26 import webbrowser
27 from threading import Thread
27 from threading import Thread
28
28
29 # System library imports
29 # System library imports
30 from IPython.external.qt import QtGui,QtCore
30 from IPython.external.qt import QtGui,QtCore
31
31
32 from IPython.core.magic import magic_escapes
32 from IPython.core.magic import magic_escapes
33
33
34 def background(f):
34 def background(f):
35 """call a function in a simple thread, to prevent blocking"""
35 """call a function in a simple thread, to prevent blocking"""
36 t = Thread(target=f)
36 t = Thread(target=f)
37 t.start()
37 t.start()
38 return t
38 return t
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Classes
41 # Classes
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 class MainWindow(QtGui.QMainWindow):
44 class MainWindow(QtGui.QMainWindow):
45
45
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47 # 'object' interface
47 # 'object' interface
48 #---------------------------------------------------------------------------
48 #---------------------------------------------------------------------------
49
49
50 _magic_menu_dict = {}
51
52 def __init__(self, app,
50 def __init__(self, app,
53 confirm_exit=True,
51 confirm_exit=True,
54 new_frontend_factory=None, slave_frontend_factory=None,
52 new_frontend_factory=None, slave_frontend_factory=None,
55 ):
53 ):
56 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
57
55
58 Parameters
56 Parameters
59 ----------
57 ----------
60
58
61 app : reference to QApplication parent
59 app : reference to QApplication parent
62 confirm_exit : bool, optional
60 confirm_exit : bool, optional
63 Whether we should prompt on close of tabs
61 Whether we should prompt on close of tabs
64 new_frontend_factory : callable
62 new_frontend_factory : callable
65 A callable that returns a new IPythonWidget instance, attached to
63 A callable that returns a new IPythonWidget instance, attached to
66 its own running kernel.
64 its own running kernel.
67 slave_frontend_factory : callable
65 slave_frontend_factory : callable
68 A callable that takes an existing IPythonWidget, and returns a new
66 A callable that takes an existing IPythonWidget, and returns a new
69 IPythonWidget instance, attached to the same kernel.
67 IPythonWidget instance, attached to the same kernel.
70 """
68 """
71
69
72 super(MainWindow, self).__init__()
70 super(MainWindow, self).__init__()
73 self._kernel_counter = 0
71 self._kernel_counter = 0
74 self._app = app
72 self._app = app
75 self.confirm_exit = confirm_exit
73 self.confirm_exit = confirm_exit
76 self.new_frontend_factory = new_frontend_factory
74 self.new_frontend_factory = new_frontend_factory
77 self.slave_frontend_factory = slave_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
78
76
79 self.tab_widget = QtGui.QTabWidget(self)
77 self.tab_widget = QtGui.QTabWidget(self)
80 self.tab_widget.setDocumentMode(True)
78 self.tab_widget.setDocumentMode(True)
81 self.tab_widget.setTabsClosable(True)
79 self.tab_widget.setTabsClosable(True)
82 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
83
81
84 self.setCentralWidget(self.tab_widget)
82 self.setCentralWidget(self.tab_widget)
85 # hide tab bar at first, since we have no tabs:
83 # hide tab bar at first, since we have no tabs:
86 self.tab_widget.tabBar().setVisible(False)
84 self.tab_widget.tabBar().setVisible(False)
87 # prevent focus in tab bar
85 # prevent focus in tab bar
88 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
89
87
90 def update_tab_bar_visibility(self):
88 def update_tab_bar_visibility(self):
91 """ update visibility of the tabBar depending of the number of tab
89 """ update visibility of the tabBar depending of the number of tab
92
90
93 0 or 1 tab, tabBar hidden
91 0 or 1 tab, tabBar hidden
94 2+ tabs, tabBar visible
92 2+ tabs, tabBar visible
95
93
96 send a self.close if number of tab ==0
94 send a self.close if number of tab ==0
97
95
98 need to be called explicitly, or be connected to tabInserted/tabRemoved
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
99 """
97 """
100 if self.tab_widget.count() <= 1:
98 if self.tab_widget.count() <= 1:
101 self.tab_widget.tabBar().setVisible(False)
99 self.tab_widget.tabBar().setVisible(False)
102 else:
100 else:
103 self.tab_widget.tabBar().setVisible(True)
101 self.tab_widget.tabBar().setVisible(True)
104 if self.tab_widget.count()==0 :
102 if self.tab_widget.count()==0 :
105 self.close()
103 self.close()
106
104
107 @property
105 @property
108 def next_kernel_id(self):
106 def next_kernel_id(self):
109 """constantly increasing counter for kernel IDs"""
107 """constantly increasing counter for kernel IDs"""
110 c = self._kernel_counter
108 c = self._kernel_counter
111 self._kernel_counter += 1
109 self._kernel_counter += 1
112 return c
110 return c
113
111
114 @property
112 @property
115 def active_frontend(self):
113 def active_frontend(self):
116 return self.tab_widget.currentWidget()
114 return self.tab_widget.currentWidget()
117
115
118 def create_tab_with_new_frontend(self):
116 def create_tab_with_new_frontend(self):
119 """create a new frontend and attach it to a new tab"""
117 """create a new frontend and attach it to a new tab"""
120 widget = self.new_frontend_factory()
118 widget = self.new_frontend_factory()
121 self.add_tab_with_frontend(widget)
119 self.add_tab_with_frontend(widget)
122
120
123 def create_tab_with_current_kernel(self):
121 def create_tab_with_current_kernel(self):
124 """create a new frontend attached to the same kernel as the current tab"""
122 """create a new frontend attached to the same kernel as the current tab"""
125 current_widget = self.tab_widget.currentWidget()
123 current_widget = self.tab_widget.currentWidget()
126 current_widget_index = self.tab_widget.indexOf(current_widget)
124 current_widget_index = self.tab_widget.indexOf(current_widget)
127 current_widget_name = self.tab_widget.tabText(current_widget_index)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
128 widget = self.slave_frontend_factory(current_widget)
126 widget = self.slave_frontend_factory(current_widget)
129 if 'slave' in current_widget_name:
127 if 'slave' in current_widget_name:
130 # don't keep stacking slaves
128 # don't keep stacking slaves
131 name = current_widget_name
129 name = current_widget_name
132 else:
130 else:
133 name = '(%s) slave' % current_widget_name
131 name = '(%s) slave' % current_widget_name
134 self.add_tab_with_frontend(widget,name=name)
132 self.add_tab_with_frontend(widget,name=name)
135
133
136 def close_tab(self,current_tab):
134 def close_tab(self,current_tab):
137 """ Called when you need to try to close a tab.
135 """ Called when you need to try to close a tab.
138
136
139 It takes the number of the tab to be closed as argument, or a reference
137 It takes the number of the tab to be closed as argument, or a reference
140 to the widget inside this tab
138 to the widget inside this tab
141 """
139 """
142
140
143 # let's be sure "tab" and "closing widget" are respectively the index
141 # let's be sure "tab" and "closing widget" are respectively the index
144 # of the tab to close and a reference to the frontend to close
142 # of the tab to close and a reference to the frontend to close
145 if type(current_tab) is not int :
143 if type(current_tab) is not int :
146 current_tab = self.tab_widget.indexOf(current_tab)
144 current_tab = self.tab_widget.indexOf(current_tab)
147 closing_widget=self.tab_widget.widget(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
148
146
149
147
150 # when trying to be closed, widget might re-send a request to be
148 # when trying to be closed, widget might re-send a request to be
151 # closed again, but will be deleted when event will be processed. So
149 # closed again, but will be deleted when event will be processed. So
152 # need to check that widget still exists and skip if not. One example
150 # need to check that widget still exists and skip if not. One example
153 # of this is when 'exit' is sent in a slave tab. 'exit' will be
151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
154 # re-sent by this function on the master widget, which ask all slave
152 # re-sent by this function on the master widget, which ask all slave
155 # widgets to exit
153 # widgets to exit
156 if closing_widget==None:
154 if closing_widget==None:
157 return
155 return
158
156
159 #get a list of all slave widgets on the same kernel.
157 #get a list of all slave widgets on the same kernel.
160 slave_tabs = self.find_slave_widgets(closing_widget)
158 slave_tabs = self.find_slave_widgets(closing_widget)
161
159
162 keepkernel = None #Use the prompt by default
160 keepkernel = None #Use the prompt by default
163 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
164 keepkernel = closing_widget._keep_kernel_on_exit
162 keepkernel = closing_widget._keep_kernel_on_exit
165 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
166 # we set local slave tabs._hidden to True to avoid prompting for kernel
164 # we set local slave tabs._hidden to True to avoid prompting for kernel
167 # restart when they get the signal. and then "forward" the 'exit'
165 # restart when they get the signal. and then "forward" the 'exit'
168 # to the main window
166 # to the main window
169 if keepkernel is not None:
167 if keepkernel is not None:
170 for tab in slave_tabs:
168 for tab in slave_tabs:
171 tab._hidden = True
169 tab._hidden = True
172 if closing_widget in slave_tabs:
170 if closing_widget in slave_tabs:
173 try :
171 try :
174 self.find_master_tab(closing_widget).execute('exit')
172 self.find_master_tab(closing_widget).execute('exit')
175 except AttributeError:
173 except AttributeError:
176 self.log.info("Master already closed or not local, closing only current tab")
174 self.log.info("Master already closed or not local, closing only current tab")
177 self.tab_widget.removeTab(current_tab)
175 self.tab_widget.removeTab(current_tab)
178 self.update_tab_bar_visibility()
176 self.update_tab_bar_visibility()
179 return
177 return
180
178
181 kernel_client = closing_widget.kernel_client
179 kernel_client = closing_widget.kernel_client
182 kernel_manager = closing_widget.kernel_manager
180 kernel_manager = closing_widget.kernel_manager
183
181
184 if keepkernel is None and not closing_widget._confirm_exit:
182 if keepkernel is None and not closing_widget._confirm_exit:
185 # don't prompt, just terminate the kernel if we own it
183 # don't prompt, just terminate the kernel if we own it
186 # or leave it alone if we don't
184 # or leave it alone if we don't
187 keepkernel = closing_widget._existing
185 keepkernel = closing_widget._existing
188 if keepkernel is None: #show prompt
186 if keepkernel is None: #show prompt
189 if kernel_client and kernel_client.channels_running:
187 if kernel_client and kernel_client.channels_running:
190 title = self.window().windowTitle()
188 title = self.window().windowTitle()
191 cancel = QtGui.QMessageBox.Cancel
189 cancel = QtGui.QMessageBox.Cancel
192 okay = QtGui.QMessageBox.Ok
190 okay = QtGui.QMessageBox.Ok
193 if closing_widget._may_close:
191 if closing_widget._may_close:
194 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
192 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
195 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
193 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
196 justthis = QtGui.QPushButton("&No, just this Tab", self)
194 justthis = QtGui.QPushButton("&No, just this Tab", self)
197 justthis.setShortcut('N')
195 justthis.setShortcut('N')
198 closeall = QtGui.QPushButton("&Yes, close all", self)
196 closeall = QtGui.QPushButton("&Yes, close all", self)
199 closeall.setShortcut('Y')
197 closeall.setShortcut('Y')
200 # allow ctrl-d ctrl-d exit, like in terminal
198 # allow ctrl-d ctrl-d exit, like in terminal
201 closeall.setShortcut('Ctrl+D')
199 closeall.setShortcut('Ctrl+D')
202 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
200 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
203 title, msg)
201 title, msg)
204 box.setInformativeText(info)
202 box.setInformativeText(info)
205 box.addButton(cancel)
203 box.addButton(cancel)
206 box.addButton(justthis, QtGui.QMessageBox.NoRole)
204 box.addButton(justthis, QtGui.QMessageBox.NoRole)
207 box.addButton(closeall, QtGui.QMessageBox.YesRole)
205 box.addButton(closeall, QtGui.QMessageBox.YesRole)
208 box.setDefaultButton(closeall)
206 box.setDefaultButton(closeall)
209 box.setEscapeButton(cancel)
207 box.setEscapeButton(cancel)
210 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
208 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
211 box.setIconPixmap(pixmap)
209 box.setIconPixmap(pixmap)
212 reply = box.exec_()
210 reply = box.exec_()
213 if reply == 1: # close All
211 if reply == 1: # close All
214 for slave in slave_tabs:
212 for slave in slave_tabs:
215 background(slave.kernel_client.stop_channels)
213 background(slave.kernel_client.stop_channels)
216 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
214 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
217 closing_widget.execute("exit")
215 closing_widget.execute("exit")
218 self.tab_widget.removeTab(current_tab)
216 self.tab_widget.removeTab(current_tab)
219 background(kernel_client.stop_channels)
217 background(kernel_client.stop_channels)
220 elif reply == 0: # close Console
218 elif reply == 0: # close Console
221 if not closing_widget._existing:
219 if not closing_widget._existing:
222 # Have kernel: don't quit, just close the tab
220 # Have kernel: don't quit, just close the tab
223 closing_widget.execute("exit True")
221 closing_widget.execute("exit True")
224 self.tab_widget.removeTab(current_tab)
222 self.tab_widget.removeTab(current_tab)
225 background(kernel_client.stop_channels)
223 background(kernel_client.stop_channels)
226 else:
224 else:
227 reply = QtGui.QMessageBox.question(self, title,
225 reply = QtGui.QMessageBox.question(self, title,
228 "Are you sure you want to close this Console?"+
226 "Are you sure you want to close this Console?"+
229 "\nThe Kernel and other Consoles will remain active.",
227 "\nThe Kernel and other Consoles will remain active.",
230 okay|cancel,
228 okay|cancel,
231 defaultButton=okay
229 defaultButton=okay
232 )
230 )
233 if reply == okay:
231 if reply == okay:
234 self.tab_widget.removeTab(current_tab)
232 self.tab_widget.removeTab(current_tab)
235 elif keepkernel: #close console but leave kernel running (no prompt)
233 elif keepkernel: #close console but leave kernel running (no prompt)
236 self.tab_widget.removeTab(current_tab)
234 self.tab_widget.removeTab(current_tab)
237 background(kernel_client.stop_channels)
235 background(kernel_client.stop_channels)
238 else: #close console and kernel (no prompt)
236 else: #close console and kernel (no prompt)
239 self.tab_widget.removeTab(current_tab)
237 self.tab_widget.removeTab(current_tab)
240 if kernel_client and kernel_client.channels_running:
238 if kernel_client and kernel_client.channels_running:
241 for slave in slave_tabs:
239 for slave in slave_tabs:
242 background(slave.kernel_client.stop_channels)
240 background(slave.kernel_client.stop_channels)
243 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
241 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
244 if kernel_manager:
242 if kernel_manager:
245 kernel_manager.shutdown_kernel()
243 kernel_manager.shutdown_kernel()
246 background(kernel_client.stop_channels)
244 background(kernel_client.stop_channels)
247
245
248 self.update_tab_bar_visibility()
246 self.update_tab_bar_visibility()
249
247
250 def add_tab_with_frontend(self,frontend,name=None):
248 def add_tab_with_frontend(self,frontend,name=None):
251 """ insert a tab with a given frontend in the tab bar, and give it a name
249 """ insert a tab with a given frontend in the tab bar, and give it a name
252
250
253 """
251 """
254 if not name:
252 if not name:
255 name = 'kernel %i' % self.next_kernel_id
253 name = 'kernel %i' % self.next_kernel_id
256 self.tab_widget.addTab(frontend,name)
254 self.tab_widget.addTab(frontend,name)
257 self.update_tab_bar_visibility()
255 self.update_tab_bar_visibility()
258 self.make_frontend_visible(frontend)
256 self.make_frontend_visible(frontend)
259 frontend.exit_requested.connect(self.close_tab)
257 frontend.exit_requested.connect(self.close_tab)
260
258
261 def next_tab(self):
259 def next_tab(self):
262 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
260 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
263
261
264 def prev_tab(self):
262 def prev_tab(self):
265 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
263 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
266
264
267 def make_frontend_visible(self,frontend):
265 def make_frontend_visible(self,frontend):
268 widget_index=self.tab_widget.indexOf(frontend)
266 widget_index=self.tab_widget.indexOf(frontend)
269 if widget_index > 0 :
267 if widget_index > 0 :
270 self.tab_widget.setCurrentIndex(widget_index)
268 self.tab_widget.setCurrentIndex(widget_index)
271
269
272 def find_master_tab(self,tab,as_list=False):
270 def find_master_tab(self,tab,as_list=False):
273 """
271 """
274 Try to return the frontend that owns the kernel attached to the given widget/tab.
272 Try to return the frontend that owns the kernel attached to the given widget/tab.
275
273
276 Only finds frontend owned by the current application. Selection
274 Only finds frontend owned by the current application. Selection
277 based on port of the kernel might be inaccurate if several kernel
275 based on port of the kernel might be inaccurate if several kernel
278 on different ip use same port number.
276 on different ip use same port number.
279
277
280 This function does the conversion tabNumber/widget if needed.
278 This function does the conversion tabNumber/widget if needed.
281 Might return None if no master widget (non local kernel)
279 Might return None if no master widget (non local kernel)
282 Will crash IPython if more than 1 masterWidget
280 Will crash IPython if more than 1 masterWidget
283
281
284 When asList set to True, always return a list of widget(s) owning
282 When asList set to True, always return a list of widget(s) owning
285 the kernel. The list might be empty or containing several Widget.
283 the kernel. The list might be empty or containing several Widget.
286 """
284 """
287
285
288 #convert from/to int/richIpythonWidget if needed
286 #convert from/to int/richIpythonWidget if needed
289 if isinstance(tab, int):
287 if isinstance(tab, int):
290 tab = self.tab_widget.widget(tab)
288 tab = self.tab_widget.widget(tab)
291 km=tab.kernel_client
289 km=tab.kernel_client
292
290
293 #build list of all widgets
291 #build list of all widgets
294 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
292 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
295
293
296 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
294 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
297 # And should have a _may_close attribute
295 # And should have a _may_close attribute
298 filtered_widget_list = [ widget for widget in widget_list if
296 filtered_widget_list = [ widget for widget in widget_list if
299 widget.kernel_client.connection_file == km.connection_file and
297 widget.kernel_client.connection_file == km.connection_file and
300 hasattr(widget,'_may_close') ]
298 hasattr(widget,'_may_close') ]
301 # the master widget is the one that may close the kernel
299 # the master widget is the one that may close the kernel
302 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
300 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
303 if as_list:
301 if as_list:
304 return master_widget
302 return master_widget
305 assert(len(master_widget)<=1 )
303 assert(len(master_widget)<=1 )
306 if len(master_widget)==0:
304 if len(master_widget)==0:
307 return None
305 return None
308
306
309 return master_widget[0]
307 return master_widget[0]
310
308
311 def find_slave_widgets(self,tab):
309 def find_slave_widgets(self,tab):
312 """return all the frontends that do not own the kernel attached to the given widget/tab.
310 """return all the frontends that do not own the kernel attached to the given widget/tab.
313
311
314 Only find frontends owned by the current application. Selection
312 Only find frontends owned by the current application. Selection
315 based on connection file of the kernel.
313 based on connection file of the kernel.
316
314
317 This function does the conversion tabNumber/widget if needed.
315 This function does the conversion tabNumber/widget if needed.
318 """
316 """
319 #convert from/to int/richIpythonWidget if needed
317 #convert from/to int/richIpythonWidget if needed
320 if isinstance(tab, int):
318 if isinstance(tab, int):
321 tab = self.tab_widget.widget(tab)
319 tab = self.tab_widget.widget(tab)
322 km=tab.kernel_client
320 km=tab.kernel_client
323
321
324 #build list of all widgets
322 #build list of all widgets
325 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
323 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
326
324
327 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
325 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
328 filtered_widget_list = ( widget for widget in widget_list if
326 filtered_widget_list = ( widget for widget in widget_list if
329 widget.kernel_client.connection_file == km.connection_file)
327 widget.kernel_client.connection_file == km.connection_file)
330 # Get a list of all widget owning the same kernel and removed it from
328 # Get a list of all widget owning the same kernel and removed it from
331 # the previous cadidate. (better using sets ?)
329 # the previous cadidate. (better using sets ?)
332 master_widget_list = self.find_master_tab(tab, as_list=True)
330 master_widget_list = self.find_master_tab(tab, as_list=True)
333 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
331 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
334
332
335 return slave_list
333 return slave_list
336
334
337 # Populate the menu bar with common actions and shortcuts
335 # Populate the menu bar with common actions and shortcuts
338 def add_menu_action(self, menu, action, defer_shortcut=False):
336 def add_menu_action(self, menu, action, defer_shortcut=False):
339 """Add action to menu as well as self
337 """Add action to menu as well as self
340
338
341 So that when the menu bar is invisible, its actions are still available.
339 So that when the menu bar is invisible, its actions are still available.
342
340
343 If defer_shortcut is True, set the shortcut context to widget-only,
341 If defer_shortcut is True, set the shortcut context to widget-only,
344 where it will avoid conflict with shortcuts already bound to the
342 where it will avoid conflict with shortcuts already bound to the
345 widgets themselves.
343 widgets themselves.
346 """
344 """
347 menu.addAction(action)
345 menu.addAction(action)
348 self.addAction(action)
346 self.addAction(action)
349
347
350 if defer_shortcut:
348 if defer_shortcut:
351 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
349 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
352
350
353 def init_menu_bar(self):
351 def init_menu_bar(self):
354 #create menu in the order they should appear in the menu bar
352 #create menu in the order they should appear in the menu bar
355 self.init_file_menu()
353 self.init_file_menu()
356 self.init_edit_menu()
354 self.init_edit_menu()
357 self.init_view_menu()
355 self.init_view_menu()
358 self.init_kernel_menu()
356 self.init_kernel_menu()
359 self.init_magic_menu()
357 self.init_magic_menu()
360 self.init_window_menu()
358 self.init_window_menu()
361 self.init_help_menu()
359 self.init_help_menu()
362
360
363 def init_file_menu(self):
361 def init_file_menu(self):
364 self.file_menu = self.menuBar().addMenu("&File")
362 self.file_menu = self.menuBar().addMenu("&File")
365
363
366 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
364 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
367 self,
365 self,
368 shortcut="Ctrl+T",
366 shortcut="Ctrl+T",
369 triggered=self.create_tab_with_new_frontend)
367 triggered=self.create_tab_with_new_frontend)
370 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
368 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
371
369
372 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
370 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
373 self,
371 self,
374 shortcut="Ctrl+Shift+T",
372 shortcut="Ctrl+Shift+T",
375 triggered=self.create_tab_with_current_kernel)
373 triggered=self.create_tab_with_current_kernel)
376 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
374 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
377
375
378 self.file_menu.addSeparator()
376 self.file_menu.addSeparator()
379
377
380 self.close_action=QtGui.QAction("&Close Tab",
378 self.close_action=QtGui.QAction("&Close Tab",
381 self,
379 self,
382 shortcut=QtGui.QKeySequence.Close,
380 shortcut=QtGui.QKeySequence.Close,
383 triggered=self.close_active_frontend
381 triggered=self.close_active_frontend
384 )
382 )
385 self.add_menu_action(self.file_menu, self.close_action)
383 self.add_menu_action(self.file_menu, self.close_action)
386
384
387 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
385 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
388 self,
386 self,
389 shortcut=QtGui.QKeySequence.Save,
387 shortcut=QtGui.QKeySequence.Save,
390 triggered=self.export_action_active_frontend
388 triggered=self.export_action_active_frontend
391 )
389 )
392 self.add_menu_action(self.file_menu, self.export_action, True)
390 self.add_menu_action(self.file_menu, self.export_action, True)
393
391
394 self.file_menu.addSeparator()
392 self.file_menu.addSeparator()
395
393
396 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
394 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
397 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
395 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
398 # Only override the default if there is a collision.
396 # Only override the default if there is a collision.
399 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
397 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
400 printkey = "Ctrl+Shift+P"
398 printkey = "Ctrl+Shift+P"
401 self.print_action = QtGui.QAction("&Print",
399 self.print_action = QtGui.QAction("&Print",
402 self,
400 self,
403 shortcut=printkey,
401 shortcut=printkey,
404 triggered=self.print_action_active_frontend)
402 triggered=self.print_action_active_frontend)
405 self.add_menu_action(self.file_menu, self.print_action, True)
403 self.add_menu_action(self.file_menu, self.print_action, True)
406
404
407 if sys.platform != 'darwin':
405 if sys.platform != 'darwin':
408 # OSX always has Quit in the Application menu, only add it
406 # OSX always has Quit in the Application menu, only add it
409 # to the File menu elsewhere.
407 # to the File menu elsewhere.
410
408
411 self.file_menu.addSeparator()
409 self.file_menu.addSeparator()
412
410
413 self.quit_action = QtGui.QAction("&Quit",
411 self.quit_action = QtGui.QAction("&Quit",
414 self,
412 self,
415 shortcut=QtGui.QKeySequence.Quit,
413 shortcut=QtGui.QKeySequence.Quit,
416 triggered=self.close,
414 triggered=self.close,
417 )
415 )
418 self.add_menu_action(self.file_menu, self.quit_action)
416 self.add_menu_action(self.file_menu, self.quit_action)
419
417
420
418
421 def init_edit_menu(self):
419 def init_edit_menu(self):
422 self.edit_menu = self.menuBar().addMenu("&Edit")
420 self.edit_menu = self.menuBar().addMenu("&Edit")
423
421
424 self.undo_action = QtGui.QAction("&Undo",
422 self.undo_action = QtGui.QAction("&Undo",
425 self,
423 self,
426 shortcut=QtGui.QKeySequence.Undo,
424 shortcut=QtGui.QKeySequence.Undo,
427 statusTip="Undo last action if possible",
425 statusTip="Undo last action if possible",
428 triggered=self.undo_active_frontend
426 triggered=self.undo_active_frontend
429 )
427 )
430 self.add_menu_action(self.edit_menu, self.undo_action)
428 self.add_menu_action(self.edit_menu, self.undo_action)
431
429
432 self.redo_action = QtGui.QAction("&Redo",
430 self.redo_action = QtGui.QAction("&Redo",
433 self,
431 self,
434 shortcut=QtGui.QKeySequence.Redo,
432 shortcut=QtGui.QKeySequence.Redo,
435 statusTip="Redo last action if possible",
433 statusTip="Redo last action if possible",
436 triggered=self.redo_active_frontend)
434 triggered=self.redo_active_frontend)
437 self.add_menu_action(self.edit_menu, self.redo_action)
435 self.add_menu_action(self.edit_menu, self.redo_action)
438
436
439 self.edit_menu.addSeparator()
437 self.edit_menu.addSeparator()
440
438
441 self.cut_action = QtGui.QAction("&Cut",
439 self.cut_action = QtGui.QAction("&Cut",
442 self,
440 self,
443 shortcut=QtGui.QKeySequence.Cut,
441 shortcut=QtGui.QKeySequence.Cut,
444 triggered=self.cut_active_frontend
442 triggered=self.cut_active_frontend
445 )
443 )
446 self.add_menu_action(self.edit_menu, self.cut_action, True)
444 self.add_menu_action(self.edit_menu, self.cut_action, True)
447
445
448 self.copy_action = QtGui.QAction("&Copy",
446 self.copy_action = QtGui.QAction("&Copy",
449 self,
447 self,
450 shortcut=QtGui.QKeySequence.Copy,
448 shortcut=QtGui.QKeySequence.Copy,
451 triggered=self.copy_active_frontend
449 triggered=self.copy_active_frontend
452 )
450 )
453 self.add_menu_action(self.edit_menu, self.copy_action, True)
451 self.add_menu_action(self.edit_menu, self.copy_action, True)
454
452
455 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
453 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
456 self,
454 self,
457 shortcut="Ctrl+Shift+C",
455 shortcut="Ctrl+Shift+C",
458 triggered=self.copy_raw_active_frontend
456 triggered=self.copy_raw_active_frontend
459 )
457 )
460 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
458 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
461
459
462 self.paste_action = QtGui.QAction("&Paste",
460 self.paste_action = QtGui.QAction("&Paste",
463 self,
461 self,
464 shortcut=QtGui.QKeySequence.Paste,
462 shortcut=QtGui.QKeySequence.Paste,
465 triggered=self.paste_active_frontend
463 triggered=self.paste_active_frontend
466 )
464 )
467 self.add_menu_action(self.edit_menu, self.paste_action, True)
465 self.add_menu_action(self.edit_menu, self.paste_action, True)
468
466
469 self.edit_menu.addSeparator()
467 self.edit_menu.addSeparator()
470
468
471 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
469 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
472 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
470 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
473 # Only override the default if there is a collision.
471 # Only override the default if there is a collision.
474 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
472 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
475 selectall = "Ctrl+Shift+A"
473 selectall = "Ctrl+Shift+A"
476 self.select_all_action = QtGui.QAction("Select &All",
474 self.select_all_action = QtGui.QAction("Select &All",
477 self,
475 self,
478 shortcut=selectall,
476 shortcut=selectall,
479 triggered=self.select_all_active_frontend
477 triggered=self.select_all_active_frontend
480 )
478 )
481 self.add_menu_action(self.edit_menu, self.select_all_action, True)
479 self.add_menu_action(self.edit_menu, self.select_all_action, True)
482
480
483
481
484 def init_view_menu(self):
482 def init_view_menu(self):
485 self.view_menu = self.menuBar().addMenu("&View")
483 self.view_menu = self.menuBar().addMenu("&View")
486
484
487 if sys.platform != 'darwin':
485 if sys.platform != 'darwin':
488 # disable on OSX, where there is always a menu bar
486 # disable on OSX, where there is always a menu bar
489 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
487 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
490 self,
488 self,
491 shortcut="Ctrl+Shift+M",
489 shortcut="Ctrl+Shift+M",
492 statusTip="Toggle visibility of menubar",
490 statusTip="Toggle visibility of menubar",
493 triggered=self.toggle_menu_bar)
491 triggered=self.toggle_menu_bar)
494 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
492 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
495
493
496 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
494 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
497 self.full_screen_act = QtGui.QAction("&Full Screen",
495 self.full_screen_act = QtGui.QAction("&Full Screen",
498 self,
496 self,
499 shortcut=fs_key,
497 shortcut=fs_key,
500 statusTip="Toggle between Fullscreen and Normal Size",
498 statusTip="Toggle between Fullscreen and Normal Size",
501 triggered=self.toggleFullScreen)
499 triggered=self.toggleFullScreen)
502 self.add_menu_action(self.view_menu, self.full_screen_act)
500 self.add_menu_action(self.view_menu, self.full_screen_act)
503
501
504 self.view_menu.addSeparator()
502 self.view_menu.addSeparator()
505
503
506 self.increase_font_size = QtGui.QAction("Zoom &In",
504 self.increase_font_size = QtGui.QAction("Zoom &In",
507 self,
505 self,
508 shortcut=QtGui.QKeySequence.ZoomIn,
506 shortcut=QtGui.QKeySequence.ZoomIn,
509 triggered=self.increase_font_size_active_frontend
507 triggered=self.increase_font_size_active_frontend
510 )
508 )
511 self.add_menu_action(self.view_menu, self.increase_font_size, True)
509 self.add_menu_action(self.view_menu, self.increase_font_size, True)
512
510
513 self.decrease_font_size = QtGui.QAction("Zoom &Out",
511 self.decrease_font_size = QtGui.QAction("Zoom &Out",
514 self,
512 self,
515 shortcut=QtGui.QKeySequence.ZoomOut,
513 shortcut=QtGui.QKeySequence.ZoomOut,
516 triggered=self.decrease_font_size_active_frontend
514 triggered=self.decrease_font_size_active_frontend
517 )
515 )
518 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
516 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
519
517
520 self.reset_font_size = QtGui.QAction("Zoom &Reset",
518 self.reset_font_size = QtGui.QAction("Zoom &Reset",
521 self,
519 self,
522 shortcut="Ctrl+0",
520 shortcut="Ctrl+0",
523 triggered=self.reset_font_size_active_frontend
521 triggered=self.reset_font_size_active_frontend
524 )
522 )
525 self.add_menu_action(self.view_menu, self.reset_font_size, True)
523 self.add_menu_action(self.view_menu, self.reset_font_size, True)
526
524
527 self.view_menu.addSeparator()
525 self.view_menu.addSeparator()
528
526
529 self.clear_action = QtGui.QAction("&Clear Screen",
527 self.clear_action = QtGui.QAction("&Clear Screen",
530 self,
528 self,
531 shortcut='Ctrl+L',
529 shortcut='Ctrl+L',
532 statusTip="Clear the console",
530 statusTip="Clear the console",
533 triggered=self.clear_magic_active_frontend)
531 triggered=self.clear_magic_active_frontend)
534 self.add_menu_action(self.view_menu, self.clear_action)
532 self.add_menu_action(self.view_menu, self.clear_action)
535
533
536 self.pager_menu = self.view_menu.addMenu("&Pager")
534 self.pager_menu = self.view_menu.addMenu("&Pager")
537
535
538 hsplit_action = QtGui.QAction(".. &Horizontal Split",
536 hsplit_action = QtGui.QAction(".. &Horizontal Split",
539 self,
537 self,
540 triggered=lambda: self.set_paging_active_frontend('hsplit'))
538 triggered=lambda: self.set_paging_active_frontend('hsplit'))
541
539
542 vsplit_action = QtGui.QAction(" : &Vertical Split",
540 vsplit_action = QtGui.QAction(" : &Vertical Split",
543 self,
541 self,
544 triggered=lambda: self.set_paging_active_frontend('vsplit'))
542 triggered=lambda: self.set_paging_active_frontend('vsplit'))
545
543
546 inside_action = QtGui.QAction(" &Inside Pager",
544 inside_action = QtGui.QAction(" &Inside Pager",
547 self,
545 self,
548 triggered=lambda: self.set_paging_active_frontend('inside'))
546 triggered=lambda: self.set_paging_active_frontend('inside'))
549
547
550 self.pager_menu.addAction(hsplit_action)
548 self.pager_menu.addAction(hsplit_action)
551 self.pager_menu.addAction(vsplit_action)
549 self.pager_menu.addAction(vsplit_action)
552 self.pager_menu.addAction(inside_action)
550 self.pager_menu.addAction(inside_action)
553
551
554 def init_kernel_menu(self):
552 def init_kernel_menu(self):
555 self.kernel_menu = self.menuBar().addMenu("&Kernel")
553 self.kernel_menu = self.menuBar().addMenu("&Kernel")
556 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
554 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
557 # keep the signal shortcuts to ctrl, rather than
555 # keep the signal shortcuts to ctrl, rather than
558 # platform-default like we do elsewhere.
556 # platform-default like we do elsewhere.
559
557
560 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
558 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
561
559
562 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
560 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
563 self,
561 self,
564 triggered=self.interrupt_kernel_active_frontend,
562 triggered=self.interrupt_kernel_active_frontend,
565 shortcut=ctrl+"+C",
563 shortcut=ctrl+"+C",
566 )
564 )
567 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
565 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
568
566
569 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
567 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
570 self,
568 self,
571 triggered=self.restart_kernel_active_frontend,
569 triggered=self.restart_kernel_active_frontend,
572 shortcut=ctrl+"+.",
570 shortcut=ctrl+"+.",
573 )
571 )
574 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
572 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
575
573
576 self.kernel_menu.addSeparator()
574 self.kernel_menu.addSeparator()
577
575
578 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
576 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
579 self,
577 self,
580 checkable=True,
578 checkable=True,
581 checked=self.active_frontend.confirm_restart,
579 checked=self.active_frontend.confirm_restart,
582 triggered=self.toggle_confirm_restart_active_frontend
580 triggered=self.toggle_confirm_restart_active_frontend
583 )
581 )
584
582
585 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
583 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
586 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
584 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
587
588 def _make_dynamic_magic(self,magic):
589 """Return a function `fun` that will execute `magic` on active frontend.
590
591 Parameters
592 ----------
593 magic : string
594 string that will be executed as is when the returned function is called
595
596 Returns
597 -------
598 fun : function
599 function with no parameters, when called will execute `magic` on the
600 current active frontend at call time
601
602 See Also
603 --------
604 populate_all_magic_menu : generate the "All Magics..." menu
605
606 Notes
607 -----
608 `fun` executes `magic` in active frontend at the moment it is triggered,
609 not the active frontend at the moment it was created.
610
611 This function is mostly used to create the "All Magics..." Menu at run time.
612 """
613 # need two level nested function to be sure to pass magic
614 # to active frontend **at run time**.
615 def inner_dynamic_magic():
616 self.active_frontend.execute(magic)
617 inner_dynamic_magic.__name__ = "dynamics_magic_s"
618 return inner_dynamic_magic
619
620 def populate_all_magic_menu(self, display_data=None):
621 """Clean "All Magics..." menu and repopulate it with `display_data`
622
623 Parameters
624 ----------
625 display_data : dict,
626 dict of display_data for the magics dict of a MagicsManager.
627 Expects json data, as the result of %lsmagic
628
629 """
630 for k,v in self._magic_menu_dict.items():
631 v.clear()
632 self.all_magic_menu.clear()
633
634 if not display_data:
635 return
636
637 if display_data['status'] != 'ok':
638 self.log.warn("%%lsmagic user-expression failed: %s" % display_data)
639 return
640
641 mdict = json.loads(display_data['data'].get('application/json', {}))
642
643 for mtype in sorted(mdict):
644 subdict = mdict[mtype]
645 prefix = magic_escapes[mtype]
646 for name in sorted(subdict):
647 mclass = subdict[name]
648 magic_menu = self._get_magic_menu(mclass)
649 pmagic = prefix + name
650
651 # Adding seperate QActions is needed for some window managers
652 xaction = QtGui.QAction(pmagic,
653 self,
654 triggered=self._make_dynamic_magic(pmagic)
655 )
656 magic_menu.addAction(xaction)
657 self.all_magic_menu.addAction(xaction)
658
659 def update_all_magic_menu(self):
660 """ Update the list of magics in the "All Magics..." Menu
661
662 Request the kernel with the list of available magics and populate the
663 menu with the list received back
664
665 """
666 self.active_frontend._silent_exec_callback('get_ipython().magic("lsmagic")',
667 self.populate_all_magic_menu)
668
669 def _get_magic_menu(self,menuidentifier, menulabel=None):
670 """return a submagic menu by name, and create it if needed
671
672 Parameters
673 ----------
674
675 menulabel : str
676 Label for the menu
677
678 Will infere the menu name from the identifier at creation if menulabel not given.
679 To do so you have too give menuidentifier as a CamelCassedString
680 """
681 menu = self._magic_menu_dict.get(menuidentifier,None)
682 if not menu :
683 if not menulabel:
684 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
685 menu = QtGui.QMenu(menulabel,self.magic_menu)
686 self._magic_menu_dict[menuidentifier]=menu
687 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
688 return menu
689
690
691
585
692 def init_magic_menu(self):
586 def init_magic_menu(self):
693 self.magic_menu = self.menuBar().addMenu("&Magic")
587 self.magic_menu = self.menuBar().addMenu("&Magic")
588
589 self.add_menu_action(self.magic_menu,
590 self.magic_helper.toggleViewAction())
591
694 self.magic_menu_separator = self.magic_menu.addSeparator()
592 self.magic_menu_separator = self.magic_menu.addSeparator()
695
593
696 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
697
698 # This action should usually not appear as it will be cleared when menu
699 # is updated at first kernel response. Though, it is necessary when
700 # connecting through X-forwarding, as in this case, the menu is not
701 # auto updated, SO DO NOT DELETE.
702 self.pop = QtGui.QAction("&Update All Magic Menu ",
703 self, triggered=self.update_all_magic_menu)
704 self.add_menu_action(self.all_magic_menu, self.pop)
705 # we need to populate the 'Magic Menu' once the kernel has answer at
706 # least once let's do it immediately, but it's assured to works
707 self.pop.trigger()
708
709 self.reset_action = QtGui.QAction("&Reset",
594 self.reset_action = QtGui.QAction("&Reset",
710 self,
595 self,
711 statusTip="Clear all variables from workspace",
596 statusTip="Clear all variables from workspace",
712 triggered=self.reset_magic_active_frontend)
597 triggered=self.reset_magic_active_frontend)
713 self.add_menu_action(self.magic_menu, self.reset_action)
598 self.add_menu_action(self.magic_menu, self.reset_action)
714
599
715 self.history_action = QtGui.QAction("&History",
600 self.history_action = QtGui.QAction("&History",
716 self,
601 self,
717 statusTip="show command history",
602 statusTip="show command history",
718 triggered=self.history_magic_active_frontend)
603 triggered=self.history_magic_active_frontend)
719 self.add_menu_action(self.magic_menu, self.history_action)
604 self.add_menu_action(self.magic_menu, self.history_action)
720
605
721 self.save_action = QtGui.QAction("E&xport History ",
606 self.save_action = QtGui.QAction("E&xport History ",
722 self,
607 self,
723 statusTip="Export History as Python File",
608 statusTip="Export History as Python File",
724 triggered=self.save_magic_active_frontend)
609 triggered=self.save_magic_active_frontend)
725 self.add_menu_action(self.magic_menu, self.save_action)
610 self.add_menu_action(self.magic_menu, self.save_action)
726
611
727 self.who_action = QtGui.QAction("&Who",
612 self.who_action = QtGui.QAction("&Who",
728 self,
613 self,
729 statusTip="List interactive variables",
614 statusTip="List interactive variables",
730 triggered=self.who_magic_active_frontend)
615 triggered=self.who_magic_active_frontend)
731 self.add_menu_action(self.magic_menu, self.who_action)
616 self.add_menu_action(self.magic_menu, self.who_action)
732
617
733 self.who_ls_action = QtGui.QAction("Wh&o ls",
618 self.who_ls_action = QtGui.QAction("Wh&o ls",
734 self,
619 self,
735 statusTip="Return a list of interactive variables",
620 statusTip="Return a list of interactive variables",
736 triggered=self.who_ls_magic_active_frontend)
621 triggered=self.who_ls_magic_active_frontend)
737 self.add_menu_action(self.magic_menu, self.who_ls_action)
622 self.add_menu_action(self.magic_menu, self.who_ls_action)
738
623
739 self.whos_action = QtGui.QAction("Who&s",
624 self.whos_action = QtGui.QAction("Who&s",
740 self,
625 self,
741 statusTip="List interactive variables with details",
626 statusTip="List interactive variables with details",
742 triggered=self.whos_magic_active_frontend)
627 triggered=self.whos_magic_active_frontend)
743 self.add_menu_action(self.magic_menu, self.whos_action)
628 self.add_menu_action(self.magic_menu, self.whos_action)
744
629
745 def init_window_menu(self):
630 def init_window_menu(self):
746 self.window_menu = self.menuBar().addMenu("&Window")
631 self.window_menu = self.menuBar().addMenu("&Window")
747 if sys.platform == 'darwin':
632 if sys.platform == 'darwin':
748 # add min/maximize actions to OSX, which lacks default bindings.
633 # add min/maximize actions to OSX, which lacks default bindings.
749 self.minimizeAct = QtGui.QAction("Mini&mize",
634 self.minimizeAct = QtGui.QAction("Mini&mize",
750 self,
635 self,
751 shortcut="Ctrl+m",
636 shortcut="Ctrl+m",
752 statusTip="Minimize the window/Restore Normal Size",
637 statusTip="Minimize the window/Restore Normal Size",
753 triggered=self.toggleMinimized)
638 triggered=self.toggleMinimized)
754 # maximize is called 'Zoom' on OSX for some reason
639 # maximize is called 'Zoom' on OSX for some reason
755 self.maximizeAct = QtGui.QAction("&Zoom",
640 self.maximizeAct = QtGui.QAction("&Zoom",
756 self,
641 self,
757 shortcut="Ctrl+Shift+M",
642 shortcut="Ctrl+Shift+M",
758 statusTip="Maximize the window/Restore Normal Size",
643 statusTip="Maximize the window/Restore Normal Size",
759 triggered=self.toggleMaximized)
644 triggered=self.toggleMaximized)
760
645
761 self.add_menu_action(self.window_menu, self.minimizeAct)
646 self.add_menu_action(self.window_menu, self.minimizeAct)
762 self.add_menu_action(self.window_menu, self.maximizeAct)
647 self.add_menu_action(self.window_menu, self.maximizeAct)
763 self.window_menu.addSeparator()
648 self.window_menu.addSeparator()
764
649
765 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
650 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
766 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
651 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
767 self,
652 self,
768 shortcut=prev_key,
653 shortcut=prev_key,
769 statusTip="Select previous tab",
654 statusTip="Select previous tab",
770 triggered=self.prev_tab)
655 triggered=self.prev_tab)
771 self.add_menu_action(self.window_menu, self.prev_tab_act)
656 self.add_menu_action(self.window_menu, self.prev_tab_act)
772
657
773 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
658 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
774 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
659 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
775 self,
660 self,
776 shortcut=next_key,
661 shortcut=next_key,
777 statusTip="Select next tab",
662 statusTip="Select next tab",
778 triggered=self.next_tab)
663 triggered=self.next_tab)
779 self.add_menu_action(self.window_menu, self.next_tab_act)
664 self.add_menu_action(self.window_menu, self.next_tab_act)
780
665
781 def init_help_menu(self):
666 def init_help_menu(self):
782 # please keep the Help menu in Mac Os even if empty. It will
667 # please keep the Help menu in Mac Os even if empty. It will
783 # automatically contain a search field to search inside menus and
668 # automatically contain a search field to search inside menus and
784 # please keep it spelled in English, as long as Qt Doesn't support
669 # please keep it spelled in English, as long as Qt Doesn't support
785 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
670 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
786 # this search field functionality
671 # this search field functionality
787
672
788 self.help_menu = self.menuBar().addMenu("&Help")
673 self.help_menu = self.menuBar().addMenu("&Help")
789
674
790
675
791 # Help Menu
676 # Help Menu
792
677
793 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
678 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
794 self,
679 self,
795 triggered=self.intro_active_frontend
680 triggered=self.intro_active_frontend
796 )
681 )
797 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
682 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
798
683
799 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
684 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
800 self,
685 self,
801 triggered=self.quickref_active_frontend
686 triggered=self.quickref_active_frontend
802 )
687 )
803 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
688 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
804
689
805 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
690 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
806 self,
691 self,
807 triggered=self.guiref_active_frontend
692 triggered=self.guiref_active_frontend
808 )
693 )
809 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
694 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
810
695
811 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
696 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
812 self,
697 self,
813 triggered=self._open_online_help)
698 triggered=self._open_online_help)
814 self.add_menu_action(self.help_menu, self.onlineHelpAct)
699 self.add_menu_action(self.help_menu, self.onlineHelpAct)
815
700
816 def init_magic_helper(self):
701 def init_magic_helper(self):
817 self.magic_helper_data = None
702 self.magic_helper_data = None
818 self.magic_helper = QtGui.QDockWidget("Magics", self)
703 self.magic_helper = QtGui.QDockWidget("Show Magics", self)
819 self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea |
704 self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea |
820 QtCore.Qt.RightDockWidgetArea)
705 QtCore.Qt.RightDockWidgetArea)
821 self.magic_helper.setVisible(False)
706 self.magic_helper.setVisible(False)
822
707
823 class MinListWidget(QtGui.QListWidget):
708 class MinListWidget(QtGui.QListWidget):
824 def sizeHint(self):
709 def sizeHint(self):
825 s = QtCore.QSize()
710 s = QtCore.QSize()
826 s.setHeight(super(MinListWidget,self).sizeHint().height())
711 s.setHeight(super(MinListWidget,self).sizeHint().height())
827 s.setWidth(self.sizeHintForColumn(0))
712 s.setWidth(self.sizeHintForColumn(0))
828 return s
713 return s
829
714
830 self.magic_helper_frame = QtGui.QFrame()
715 self.magic_helper_frame = QtGui.QFrame()
831 self.magic_helper_searchl = QtGui.QLabel("Search:")
716 self.magic_helper_searchl = QtGui.QLabel("Search:")
832 self.magic_helper_search = QtGui.QLineEdit()
717 self.magic_helper_search = QtGui.QLineEdit()
833 self.magic_helper_class = QtGui.QComboBox()
718 self.magic_helper_class = QtGui.QComboBox()
834 self.magic_helper_list = MinListWidget()
719 self.magic_helper_list = MinListWidget()
835 self.magic_helper_paste = QtGui.QPushButton("Paste")
720 self.magic_helper_paste = QtGui.QPushButton("Paste")
836 self.magic_helper_run = QtGui.QPushButton("Run")
721 self.magic_helper_run = QtGui.QPushButton("Run")
837
722
838 main_layout = QtGui.QVBoxLayout()
723 main_layout = QtGui.QVBoxLayout()
839 search_layout = QtGui.QHBoxLayout()
724 search_layout = QtGui.QHBoxLayout()
840 search_layout.addWidget(self.magic_helper_searchl)
725 search_layout.addWidget(self.magic_helper_searchl)
841 search_layout.addWidget(self.magic_helper_search, 10)
726 search_layout.addWidget(self.magic_helper_search, 10)
842 main_layout.addLayout(search_layout)
727 main_layout.addLayout(search_layout)
843 main_layout.addWidget(self.magic_helper_class)
728 main_layout.addWidget(self.magic_helper_class)
844 main_layout.addWidget(self.magic_helper_list, 10)
729 main_layout.addWidget(self.magic_helper_list, 10)
845 action_layout = QtGui.QHBoxLayout()
730 action_layout = QtGui.QHBoxLayout()
846 action_layout.addWidget(self.magic_helper_paste)
731 action_layout.addWidget(self.magic_helper_paste)
847 action_layout.addWidget(self.magic_helper_run)
732 action_layout.addWidget(self.magic_helper_run)
848 main_layout.addLayout(action_layout)
733 main_layout.addLayout(action_layout)
849
734
850 self.magic_helper_frame.setLayout(main_layout)
735 self.magic_helper_frame.setLayout(main_layout)
851 self.magic_helper.setWidget(self.magic_helper_frame)
736 self.magic_helper.setWidget(self.magic_helper_frame)
852
737
853 self.magic_helper.visibilityChanged[bool].connect(
738 self.magic_helper.visibilityChanged[bool].connect(
854 self.update_magic_helper
739 self.update_magic_helper
855 )
740 )
856 self.magic_helper_class.activated[int].connect(
741 self.magic_helper_class.activated[int].connect(
857 self.magic_helper_class_selected
742 self.magic_helper_class_selected
858 )
743 )
859 self.magic_helper_search.textChanged[str].connect(
744 self.magic_helper_search.textChanged[str].connect(
860 self.magic_helper_search_changed
745 self.magic_helper_search_changed
861 )
746 )
862 self.magic_helper_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
747 self.magic_helper_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
863 self.magic_helper_paste_requested
748 self.magic_helper_paste_requested
864 )
749 )
865 self.magic_helper_paste.clicked[bool].connect(
750 self.magic_helper_paste.clicked[bool].connect(
866 self.magic_helper_paste_requested
751 self.magic_helper_paste_requested
867 )
752 )
868 self.magic_helper_run.clicked[bool].connect(
753 self.magic_helper_run.clicked[bool].connect(
869 self.magic_helper_run_requested
754 self.magic_helper_run_requested
870 )
755 )
871
756
872 self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper)
757 self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper)
873 self.add_menu_action(self.magic_menu,
874 self.magic_helper.toggleViewAction())
875
758
876 def update_magic_helper(self, visible):
759 def update_magic_helper(self, visible):
877 if not visible or self.magic_helper_data != None:
760 if not visible or self.magic_helper_data != None:
878 return
761 return
879 self.magic_helper_data = {}
762 self.magic_helper_data = {}
880 self.magic_helper_class.clear()
763 self.magic_helper_class.clear()
881 self.magic_helper_class.addItem("Populating...")
764 self.magic_helper_class.addItem("Populating...")
882 self.active_frontend._silent_exec_callback(
765 self.active_frontend._silent_exec_callback(
883 'get_ipython().magic("lsmagic")',
766 'get_ipython().magic("lsmagic")',
884 self.populate_magic_helper
767 self.populate_magic_helper
885 )
768 )
886
769
887 def populate_magic_helper(self, data):
770 def populate_magic_helper(self, data):
888 if not data:
771 if not data:
889 return
772 return
890
773
891 if data['status'] != 'ok':
774 if data['status'] != 'ok':
892 self.log.warn("%%lsmagic user-expression failed: {}".format(data))
775 self.log.warn("%%lsmagic user-expression failed: {}".format(data))
893 return
776 return
894
777
895 self.magic_helper_class.clear()
778 self.magic_helper_class.clear()
896 self.magic_helper_list.clear()
779 self.magic_helper_list.clear()
897
780
898 self.magic_helper_data = json.loads(
781 self.magic_helper_data = json.loads(
899 data['data'].get('application/json', {})
782 data['data'].get('application/json', {})
900 )
783 )
901
784
902 self.magic_helper_class.addItem('All Magics', 'any')
785 self.magic_helper_class.addItem('All Magics', 'any')
903 classes = set()
786 classes = set()
904
787
905 for mtype in sorted(self.magic_helper_data):
788 for mtype in sorted(self.magic_helper_data):
906 subdict = self.magic_helper_data[mtype]
789 subdict = self.magic_helper_data[mtype]
907 for name in sorted(subdict):
790 for name in sorted(subdict):
908 classes.add(subdict[name])
791 classes.add(subdict[name])
909
792
910 for cls in sorted(classes):
793 for cls in sorted(classes):
911 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
794 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
912 self.magic_helper_class.addItem(label, cls)
795 self.magic_helper_class.addItem(label, cls)
913
796
914 self.filter_magic_helper('.', 'any')
797 self.filter_magic_helper('.', 'any')
915
798
916 def magic_helper_class_selected(self, index):
799 def magic_helper_class_selected(self, index):
917 item = self.magic_helper_class.itemData(index)
800 item = self.magic_helper_class.itemData(index)
918 regex = self.magic_helper_search.text()
801 regex = self.magic_helper_search.text()
919 self.filter_magic_helper(regex = regex, cls = item)
802 self.filter_magic_helper(regex = regex, cls = item)
920
803
921 def magic_helper_search_changed(self, search_string):
804 def magic_helper_search_changed(self, search_string):
922 item = self.magic_helper_class.itemData(
805 item = self.magic_helper_class.itemData(
923 self.magic_helper_class.currentIndex()
806 self.magic_helper_class.currentIndex()
924 )
807 )
925 self.filter_magic_helper(regex = search_string, cls = item)
808 self.filter_magic_helper(regex = search_string, cls = item)
926
809
927 def _magic_helper_get_current(self, item = None):
810 def _magic_helper_get_current(self, item = None):
928 text = None
811 text = None
929 if not isinstance(item, QtGui.QListWidgetItem):
812 if not isinstance(item, QtGui.QListWidgetItem):
930 item = self.magic_helper_list.currentItem()
813 item = self.magic_helper_list.currentItem()
931 text = item.text()
814 text = item.text()
932 return text
815 return text
933
816
934 def _set_active_frontend_focus(self):
817 def _set_active_frontend_focus(self):
935 # this is a hack, self.active_frontend._control seems to be
818 # this is a hack, self.active_frontend._control seems to be
936 # a private member. Unfortunately this is the only method
819 # a private member. Unfortunately this is the only method
937 # to set focus reliably
820 # to set focus reliably
938 QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus)
821 QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus)
939
822
940 def magic_helper_paste_requested(self, item = None):
823 def magic_helper_paste_requested(self, item = None):
941 text = self._magic_helper_get_current(item)
824 text = self._magic_helper_get_current(item)
942 if text != None:
825 if text != None:
943 self.active_frontend.input_buffer = text
826 self.active_frontend.input_buffer = text
944 self._set_active_frontend_focus()
827 self._set_active_frontend_focus()
945
828
946 def magic_helper_run_requested(self, item = None):
829 def magic_helper_run_requested(self, item = None):
947 text = self._magic_helper_get_current(item)
830 text = self._magic_helper_get_current(item)
948 if text != None:
831 if text != None:
949 self.active_frontend.execute(text)
832 self.active_frontend.execute(text)
950 self._set_active_frontend_focus()
833 self._set_active_frontend_focus()
951
834
952 def filter_magic_helper(self, regex, cls):
835 def filter_magic_helper(self, regex, cls):
953 if regex == "" or regex == None:
836 if regex == "" or regex == None:
954 regex = '.'
837 regex = '.'
955 if cls == None:
838 if cls == None:
956 cls = 'any'
839 cls = 'any'
957
840
958 self.magic_helper_list.clear()
841 self.magic_helper_list.clear()
959 for mtype in sorted(self.magic_helper_data):
842 for mtype in sorted(self.magic_helper_data):
960 subdict = self.magic_helper_data[mtype]
843 subdict = self.magic_helper_data[mtype]
961 prefix = magic_escapes[mtype]
844 prefix = magic_escapes[mtype]
962
845
963 for name in sorted(subdict):
846 for name in sorted(subdict):
964 mclass = subdict[name]
847 mclass = subdict[name]
965 pmagic = prefix + name
848 pmagic = prefix + name
966
849
967 if (re.match(regex, name) or re.match(regex, pmagic)) and \
850 if (re.match(regex, name) or re.match(regex, pmagic)) and \
968 (cls == 'any' or cls == mclass):
851 (cls == 'any' or cls == mclass):
969 self.magic_helper_list.addItem(pmagic)
852 self.magic_helper_list.addItem(pmagic)
970
853
971
854
972 # minimize/maximize/fullscreen actions:
855 # minimize/maximize/fullscreen actions:
973
856
974 def toggle_menu_bar(self):
857 def toggle_menu_bar(self):
975 menu_bar = self.menuBar()
858 menu_bar = self.menuBar()
976 if menu_bar.isVisible():
859 if menu_bar.isVisible():
977 menu_bar.setVisible(False)
860 menu_bar.setVisible(False)
978 else:
861 else:
979 menu_bar.setVisible(True)
862 menu_bar.setVisible(True)
980
863
981 def toggleMinimized(self):
864 def toggleMinimized(self):
982 if not self.isMinimized():
865 if not self.isMinimized():
983 self.showMinimized()
866 self.showMinimized()
984 else:
867 else:
985 self.showNormal()
868 self.showNormal()
986
869
987 def _open_online_help(self):
870 def _open_online_help(self):
988 filename="http://ipython.org/ipython-doc/stable/index.html"
871 filename="http://ipython.org/ipython-doc/stable/index.html"
989 webbrowser.open(filename, new=1, autoraise=True)
872 webbrowser.open(filename, new=1, autoraise=True)
990
873
991 def toggleMaximized(self):
874 def toggleMaximized(self):
992 if not self.isMaximized():
875 if not self.isMaximized():
993 self.showMaximized()
876 self.showMaximized()
994 else:
877 else:
995 self.showNormal()
878 self.showNormal()
996
879
997 # Min/Max imizing while in full screen give a bug
880 # Min/Max imizing while in full screen give a bug
998 # when going out of full screen, at least on OSX
881 # when going out of full screen, at least on OSX
999 def toggleFullScreen(self):
882 def toggleFullScreen(self):
1000 if not self.isFullScreen():
883 if not self.isFullScreen():
1001 self.showFullScreen()
884 self.showFullScreen()
1002 if sys.platform == 'darwin':
885 if sys.platform == 'darwin':
1003 self.maximizeAct.setEnabled(False)
886 self.maximizeAct.setEnabled(False)
1004 self.minimizeAct.setEnabled(False)
887 self.minimizeAct.setEnabled(False)
1005 else:
888 else:
1006 self.showNormal()
889 self.showNormal()
1007 if sys.platform == 'darwin':
890 if sys.platform == 'darwin':
1008 self.maximizeAct.setEnabled(True)
891 self.maximizeAct.setEnabled(True)
1009 self.minimizeAct.setEnabled(True)
892 self.minimizeAct.setEnabled(True)
1010
893
1011 def set_paging_active_frontend(self, paging):
894 def set_paging_active_frontend(self, paging):
1012 self.active_frontend._set_paging(paging)
895 self.active_frontend._set_paging(paging)
1013
896
1014 def close_active_frontend(self):
897 def close_active_frontend(self):
1015 self.close_tab(self.active_frontend)
898 self.close_tab(self.active_frontend)
1016
899
1017 def restart_kernel_active_frontend(self):
900 def restart_kernel_active_frontend(self):
1018 self.active_frontend.request_restart_kernel()
901 self.active_frontend.request_restart_kernel()
1019
902
1020 def interrupt_kernel_active_frontend(self):
903 def interrupt_kernel_active_frontend(self):
1021 self.active_frontend.request_interrupt_kernel()
904 self.active_frontend.request_interrupt_kernel()
1022
905
1023 def toggle_confirm_restart_active_frontend(self):
906 def toggle_confirm_restart_active_frontend(self):
1024 widget = self.active_frontend
907 widget = self.active_frontend
1025 widget.confirm_restart = not widget.confirm_restart
908 widget.confirm_restart = not widget.confirm_restart
1026 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
909 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
1027
910
1028 def update_restart_checkbox(self):
911 def update_restart_checkbox(self):
1029 if self.active_frontend is None:
912 if self.active_frontend is None:
1030 return
913 return
1031 widget = self.active_frontend
914 widget = self.active_frontend
1032 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
915 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
1033
916
1034 def cut_active_frontend(self):
917 def cut_active_frontend(self):
1035 widget = self.active_frontend
918 widget = self.active_frontend
1036 if widget.can_cut():
919 if widget.can_cut():
1037 widget.cut()
920 widget.cut()
1038
921
1039 def copy_active_frontend(self):
922 def copy_active_frontend(self):
1040 widget = self.active_frontend
923 widget = self.active_frontend
1041 widget.copy()
924 widget.copy()
1042
925
1043 def copy_raw_active_frontend(self):
926 def copy_raw_active_frontend(self):
1044 self.active_frontend._copy_raw_action.trigger()
927 self.active_frontend._copy_raw_action.trigger()
1045
928
1046 def paste_active_frontend(self):
929 def paste_active_frontend(self):
1047 widget = self.active_frontend
930 widget = self.active_frontend
1048 if widget.can_paste():
931 if widget.can_paste():
1049 widget.paste()
932 widget.paste()
1050
933
1051 def undo_active_frontend(self):
934 def undo_active_frontend(self):
1052 self.active_frontend.undo()
935 self.active_frontend.undo()
1053
936
1054 def redo_active_frontend(self):
937 def redo_active_frontend(self):
1055 self.active_frontend.redo()
938 self.active_frontend.redo()
1056
939
1057 def reset_magic_active_frontend(self):
940 def reset_magic_active_frontend(self):
1058 self.active_frontend.execute("%reset")
941 self.active_frontend.execute("%reset")
1059
942
1060 def history_magic_active_frontend(self):
943 def history_magic_active_frontend(self):
1061 self.active_frontend.execute("%history")
944 self.active_frontend.execute("%history")
1062
945
1063 def save_magic_active_frontend(self):
946 def save_magic_active_frontend(self):
1064 self.active_frontend.save_magic()
947 self.active_frontend.save_magic()
1065
948
1066 def clear_magic_active_frontend(self):
949 def clear_magic_active_frontend(self):
1067 self.active_frontend.execute("%clear")
950 self.active_frontend.execute("%clear")
1068
951
1069 def who_magic_active_frontend(self):
952 def who_magic_active_frontend(self):
1070 self.active_frontend.execute("%who")
953 self.active_frontend.execute("%who")
1071
954
1072 def who_ls_magic_active_frontend(self):
955 def who_ls_magic_active_frontend(self):
1073 self.active_frontend.execute("%who_ls")
956 self.active_frontend.execute("%who_ls")
1074
957
1075 def whos_magic_active_frontend(self):
958 def whos_magic_active_frontend(self):
1076 self.active_frontend.execute("%whos")
959 self.active_frontend.execute("%whos")
1077
960
1078 def print_action_active_frontend(self):
961 def print_action_active_frontend(self):
1079 self.active_frontend.print_action.trigger()
962 self.active_frontend.print_action.trigger()
1080
963
1081 def export_action_active_frontend(self):
964 def export_action_active_frontend(self):
1082 self.active_frontend.export_action.trigger()
965 self.active_frontend.export_action.trigger()
1083
966
1084 def select_all_active_frontend(self):
967 def select_all_active_frontend(self):
1085 self.active_frontend.select_all_action.trigger()
968 self.active_frontend.select_all_action.trigger()
1086
969
1087 def increase_font_size_active_frontend(self):
970 def increase_font_size_active_frontend(self):
1088 self.active_frontend.increase_font_size.trigger()
971 self.active_frontend.increase_font_size.trigger()
1089
972
1090 def decrease_font_size_active_frontend(self):
973 def decrease_font_size_active_frontend(self):
1091 self.active_frontend.decrease_font_size.trigger()
974 self.active_frontend.decrease_font_size.trigger()
1092
975
1093 def reset_font_size_active_frontend(self):
976 def reset_font_size_active_frontend(self):
1094 self.active_frontend.reset_font_size.trigger()
977 self.active_frontend.reset_font_size.trigger()
1095
978
1096 def guiref_active_frontend(self):
979 def guiref_active_frontend(self):
1097 self.active_frontend.execute("%guiref")
980 self.active_frontend.execute("%guiref")
1098
981
1099 def intro_active_frontend(self):
982 def intro_active_frontend(self):
1100 self.active_frontend.execute("?")
983 self.active_frontend.execute("?")
1101
984
1102 def quickref_active_frontend(self):
985 def quickref_active_frontend(self):
1103 self.active_frontend.execute("%quickref")
986 self.active_frontend.execute("%quickref")
1104 #---------------------------------------------------------------------------
987 #---------------------------------------------------------------------------
1105 # QWidget interface
988 # QWidget interface
1106 #---------------------------------------------------------------------------
989 #---------------------------------------------------------------------------
1107
990
1108 def closeEvent(self, event):
991 def closeEvent(self, event):
1109 """ Forward the close event to every tabs contained by the windows
992 """ Forward the close event to every tabs contained by the windows
1110 """
993 """
1111 if self.tab_widget.count() == 0:
994 if self.tab_widget.count() == 0:
1112 # no tabs, just close
995 # no tabs, just close
1113 event.accept()
996 event.accept()
1114 return
997 return
1115 # Do Not loop on the widget count as it change while closing
998 # Do Not loop on the widget count as it change while closing
1116 title = self.window().windowTitle()
999 title = self.window().windowTitle()
1117 cancel = QtGui.QMessageBox.Cancel
1000 cancel = QtGui.QMessageBox.Cancel
1118 okay = QtGui.QMessageBox.Ok
1001 okay = QtGui.QMessageBox.Ok
1119
1002
1120 if self.confirm_exit:
1003 if self.confirm_exit:
1121 if self.tab_widget.count() > 1:
1004 if self.tab_widget.count() > 1:
1122 msg = "Close all tabs, stop all kernels, and Quit?"
1005 msg = "Close all tabs, stop all kernels, and Quit?"
1123 else:
1006 else:
1124 msg = "Close console, stop kernel, and Quit?"
1007 msg = "Close console, stop kernel, and Quit?"
1125 info = "Kernels not started here (e.g. notebooks) will be left alone."
1008 info = "Kernels not started here (e.g. notebooks) will be left alone."
1126 closeall = QtGui.QPushButton("&Quit", self)
1009 closeall = QtGui.QPushButton("&Quit", self)
1127 closeall.setShortcut('Q')
1010 closeall.setShortcut('Q')
1128 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
1011 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
1129 title, msg)
1012 title, msg)
1130 box.setInformativeText(info)
1013 box.setInformativeText(info)
1131 box.addButton(cancel)
1014 box.addButton(cancel)
1132 box.addButton(closeall, QtGui.QMessageBox.YesRole)
1015 box.addButton(closeall, QtGui.QMessageBox.YesRole)
1133 box.setDefaultButton(closeall)
1016 box.setDefaultButton(closeall)
1134 box.setEscapeButton(cancel)
1017 box.setEscapeButton(cancel)
1135 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
1018 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
1136 box.setIconPixmap(pixmap)
1019 box.setIconPixmap(pixmap)
1137 reply = box.exec_()
1020 reply = box.exec_()
1138 else:
1021 else:
1139 reply = okay
1022 reply = okay
1140
1023
1141 if reply == cancel:
1024 if reply == cancel:
1142 event.ignore()
1025 event.ignore()
1143 return
1026 return
1144 if reply == okay:
1027 if reply == okay:
1145 while self.tab_widget.count() >= 1:
1028 while self.tab_widget.count() >= 1:
1146 # prevent further confirmations:
1029 # prevent further confirmations:
1147 widget = self.active_frontend
1030 widget = self.active_frontend
1148 widget._confirm_exit = False
1031 widget._confirm_exit = False
1149 self.close_tab(widget)
1032 self.close_tab(widget)
1150 event.accept()
1033 event.accept()
1151
1034
@@ -1,379 +1,379 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
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 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import os
23 import os
24 import signal
24 import signal
25 import sys
25 import sys
26
26
27 # If run on Windows, install an exception hook which pops up a
27 # If run on Windows, install an exception hook which pops up a
28 # message box. Pythonw.exe hides the console, so without this
28 # message box. Pythonw.exe hides the console, so without this
29 # the application silently fails to load.
29 # the application silently fails to load.
30 #
30 #
31 # We always install this handler, because the expectation is for
31 # We always install this handler, because the expectation is for
32 # qtconsole to bring up a GUI even if called from the console.
32 # qtconsole to bring up a GUI even if called from the console.
33 # The old handler is called, so the exception is printed as well.
33 # The old handler is called, so the exception is printed as well.
34 # If desired, check for pythonw with an additional condition
34 # If desired, check for pythonw with an additional condition
35 # (sys.executable.lower().find('pythonw.exe') >= 0).
35 # (sys.executable.lower().find('pythonw.exe') >= 0).
36 if os.name == 'nt':
36 if os.name == 'nt':
37 old_excepthook = sys.excepthook
37 old_excepthook = sys.excepthook
38
38
39 # Exclude this from our autogenerated API docs.
39 # Exclude this from our autogenerated API docs.
40 undoc = lambda func: func
40 undoc = lambda func: func
41
41
42 @undoc
42 @undoc
43 def gui_excepthook(exctype, value, tb):
43 def gui_excepthook(exctype, value, tb):
44 try:
44 try:
45 import ctypes, traceback
45 import ctypes, traceback
46 MB_ICONERROR = 0x00000010
46 MB_ICONERROR = 0x00000010
47 title = u'Error starting IPython QtConsole'
47 title = u'Error starting IPython QtConsole'
48 msg = u''.join(traceback.format_exception(exctype, value, tb))
48 msg = u''.join(traceback.format_exception(exctype, value, tb))
49 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
49 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
50 finally:
50 finally:
51 # Also call the old exception hook to let it do
51 # Also call the old exception hook to let it do
52 # its thing too.
52 # its thing too.
53 old_excepthook(exctype, value, tb)
53 old_excepthook(exctype, value, tb)
54
54
55 sys.excepthook = gui_excepthook
55 sys.excepthook = gui_excepthook
56
56
57 # System library imports
57 # System library imports
58 from IPython.external.qt import QtCore, QtGui
58 from IPython.external.qt import QtCore, QtGui
59
59
60 # Local imports
60 # Local imports
61 from IPython.config.application import catch_config_error
61 from IPython.config.application import catch_config_error
62 from IPython.core.application import BaseIPythonApplication
62 from IPython.core.application import BaseIPythonApplication
63 from IPython.qt.console.ipython_widget import IPythonWidget
63 from IPython.qt.console.ipython_widget import IPythonWidget
64 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
64 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
65 from IPython.qt.console import styles
65 from IPython.qt.console import styles
66 from IPython.qt.console.mainwindow import MainWindow
66 from IPython.qt.console.mainwindow import MainWindow
67 from IPython.qt.client import QtKernelClient
67 from IPython.qt.client import QtKernelClient
68 from IPython.qt.manager import QtKernelManager
68 from IPython.qt.manager import QtKernelManager
69 from IPython.utils.traitlets import (
69 from IPython.utils.traitlets import (
70 Dict, Unicode, CBool, Any
70 Dict, Unicode, CBool, Any
71 )
71 )
72
72
73 from IPython.consoleapp import (
73 from IPython.consoleapp import (
74 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
74 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
75 )
75 )
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Network Constants
78 # Network Constants
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81 from IPython.utils.localinterfaces import is_local_ip
81 from IPython.utils.localinterfaces import is_local_ip
82
82
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84 # Globals
84 # Globals
85 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
86
86
87 _examples = """
87 _examples = """
88 ipython qtconsole # start the qtconsole
88 ipython qtconsole # start the qtconsole
89 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
89 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
90 """
90 """
91
91
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93 # Aliases and Flags
93 # Aliases and Flags
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95
95
96 # start with copy of flags
96 # start with copy of flags
97 flags = dict(flags)
97 flags = dict(flags)
98 qt_flags = {
98 qt_flags = {
99 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
99 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
100 "Disable rich text support."),
100 "Disable rich text support."),
101 }
101 }
102
102
103 # and app_flags from the Console Mixin
103 # and app_flags from the Console Mixin
104 qt_flags.update(app_flags)
104 qt_flags.update(app_flags)
105 # add frontend flags to the full set
105 # add frontend flags to the full set
106 flags.update(qt_flags)
106 flags.update(qt_flags)
107
107
108 # start with copy of front&backend aliases list
108 # start with copy of front&backend aliases list
109 aliases = dict(aliases)
109 aliases = dict(aliases)
110 qt_aliases = dict(
110 qt_aliases = dict(
111 style = 'IPythonWidget.syntax_style',
111 style = 'IPythonWidget.syntax_style',
112 stylesheet = 'IPythonQtConsoleApp.stylesheet',
112 stylesheet = 'IPythonQtConsoleApp.stylesheet',
113 colors = 'ZMQInteractiveShell.colors',
113 colors = 'ZMQInteractiveShell.colors',
114
114
115 editor = 'IPythonWidget.editor',
115 editor = 'IPythonWidget.editor',
116 paging = 'ConsoleWidget.paging',
116 paging = 'ConsoleWidget.paging',
117 )
117 )
118 # and app_aliases from the Console Mixin
118 # and app_aliases from the Console Mixin
119 qt_aliases.update(app_aliases)
119 qt_aliases.update(app_aliases)
120 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
120 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
121 # add frontend aliases to the full set
121 # add frontend aliases to the full set
122 aliases.update(qt_aliases)
122 aliases.update(qt_aliases)
123
123
124 # get flags&aliases into sets, and remove a couple that
124 # get flags&aliases into sets, and remove a couple that
125 # shouldn't be scrubbed from backend flags:
125 # shouldn't be scrubbed from backend flags:
126 qt_aliases = set(qt_aliases.keys())
126 qt_aliases = set(qt_aliases.keys())
127 qt_aliases.remove('colors')
127 qt_aliases.remove('colors')
128 qt_flags = set(qt_flags.keys())
128 qt_flags = set(qt_flags.keys())
129
129
130 #-----------------------------------------------------------------------------
130 #-----------------------------------------------------------------------------
131 # Classes
131 # Classes
132 #-----------------------------------------------------------------------------
132 #-----------------------------------------------------------------------------
133
133
134 #-----------------------------------------------------------------------------
134 #-----------------------------------------------------------------------------
135 # IPythonQtConsole
135 # IPythonQtConsole
136 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137
137
138
138
139 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
139 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
140 name = 'ipython-qtconsole'
140 name = 'ipython-qtconsole'
141
141
142 description = """
142 description = """
143 The IPython QtConsole.
143 The IPython QtConsole.
144
144
145 This launches a Console-style application using Qt. It is not a full
145 This launches a Console-style application using Qt. It is not a full
146 console, in that launched terminal subprocesses will not be able to accept
146 console, in that launched terminal subprocesses will not be able to accept
147 input.
147 input.
148
148
149 The QtConsole supports various extra features beyond the Terminal IPython
149 The QtConsole supports various extra features beyond the Terminal IPython
150 shell, such as inline plotting with matplotlib, via:
150 shell, such as inline plotting with matplotlib, via:
151
151
152 ipython qtconsole --matplotlib=inline
152 ipython qtconsole --matplotlib=inline
153
153
154 as well as saving your session as HTML, and printing the output.
154 as well as saving your session as HTML, and printing the output.
155
155
156 """
156 """
157 examples = _examples
157 examples = _examples
158
158
159 classes = [IPythonWidget] + IPythonConsoleApp.classes
159 classes = [IPythonWidget] + IPythonConsoleApp.classes
160 flags = Dict(flags)
160 flags = Dict(flags)
161 aliases = Dict(aliases)
161 aliases = Dict(aliases)
162 frontend_flags = Any(qt_flags)
162 frontend_flags = Any(qt_flags)
163 frontend_aliases = Any(qt_aliases)
163 frontend_aliases = Any(qt_aliases)
164 kernel_client_class = QtKernelClient
164 kernel_client_class = QtKernelClient
165 kernel_manager_class = QtKernelManager
165 kernel_manager_class = QtKernelManager
166
166
167 stylesheet = Unicode('', config=True,
167 stylesheet = Unicode('', config=True,
168 help="path to a custom CSS stylesheet")
168 help="path to a custom CSS stylesheet")
169
169
170 hide_menubar = CBool(False, config=True,
170 hide_menubar = CBool(False, config=True,
171 help="Start the console window with the menu bar hidden.")
171 help="Start the console window with the menu bar hidden.")
172
172
173 maximize = CBool(False, config=True,
173 maximize = CBool(False, config=True,
174 help="Start the console window maximized.")
174 help="Start the console window maximized.")
175
175
176 plain = CBool(False, config=True,
176 plain = CBool(False, config=True,
177 help="Use a plaintext widget instead of rich text (plain can't print/save).")
177 help="Use a plaintext widget instead of rich text (plain can't print/save).")
178
178
179 def _plain_changed(self, name, old, new):
179 def _plain_changed(self, name, old, new):
180 kind = 'plain' if new else 'rich'
180 kind = 'plain' if new else 'rich'
181 self.config.ConsoleWidget.kind = kind
181 self.config.ConsoleWidget.kind = kind
182 if new:
182 if new:
183 self.widget_factory = IPythonWidget
183 self.widget_factory = IPythonWidget
184 else:
184 else:
185 self.widget_factory = RichIPythonWidget
185 self.widget_factory = RichIPythonWidget
186
186
187 # the factory for creating a widget
187 # the factory for creating a widget
188 widget_factory = Any(RichIPythonWidget)
188 widget_factory = Any(RichIPythonWidget)
189
189
190 def parse_command_line(self, argv=None):
190 def parse_command_line(self, argv=None):
191 super(IPythonQtConsoleApp, self).parse_command_line(argv)
191 super(IPythonQtConsoleApp, self).parse_command_line(argv)
192 self.build_kernel_argv(argv)
192 self.build_kernel_argv(argv)
193
193
194
194
195 def new_frontend_master(self):
195 def new_frontend_master(self):
196 """ Create and return new frontend attached to new kernel, launched on localhost.
196 """ Create and return new frontend attached to new kernel, launched on localhost.
197 """
197 """
198 kernel_manager = self.kernel_manager_class(
198 kernel_manager = self.kernel_manager_class(
199 connection_file=self._new_connection_file(),
199 connection_file=self._new_connection_file(),
200 parent=self,
200 parent=self,
201 autorestart=True,
201 autorestart=True,
202 )
202 )
203 # start the kernel
203 # start the kernel
204 kwargs = dict()
204 kwargs = dict()
205 kwargs['extra_arguments'] = self.kernel_argv
205 kwargs['extra_arguments'] = self.kernel_argv
206 kernel_manager.start_kernel(**kwargs)
206 kernel_manager.start_kernel(**kwargs)
207 kernel_manager.client_factory = self.kernel_client_class
207 kernel_manager.client_factory = self.kernel_client_class
208 kernel_client = kernel_manager.client()
208 kernel_client = kernel_manager.client()
209 kernel_client.start_channels(shell=True, iopub=True)
209 kernel_client.start_channels(shell=True, iopub=True)
210 widget = self.widget_factory(config=self.config,
210 widget = self.widget_factory(config=self.config,
211 local_kernel=True)
211 local_kernel=True)
212 self.init_colors(widget)
212 self.init_colors(widget)
213 widget.kernel_manager = kernel_manager
213 widget.kernel_manager = kernel_manager
214 widget.kernel_client = kernel_client
214 widget.kernel_client = kernel_client
215 widget._existing = False
215 widget._existing = False
216 widget._may_close = True
216 widget._may_close = True
217 widget._confirm_exit = self.confirm_exit
217 widget._confirm_exit = self.confirm_exit
218 return widget
218 return widget
219
219
220 def new_frontend_slave(self, current_widget):
220 def new_frontend_slave(self, current_widget):
221 """Create and return a new frontend attached to an existing kernel.
221 """Create and return a new frontend attached to an existing kernel.
222
222
223 Parameters
223 Parameters
224 ----------
224 ----------
225 current_widget : IPythonWidget
225 current_widget : IPythonWidget
226 The IPythonWidget whose kernel this frontend is to share
226 The IPythonWidget whose kernel this frontend is to share
227 """
227 """
228 kernel_client = self.kernel_client_class(
228 kernel_client = self.kernel_client_class(
229 connection_file=current_widget.kernel_client.connection_file,
229 connection_file=current_widget.kernel_client.connection_file,
230 config = self.config,
230 config = self.config,
231 )
231 )
232 kernel_client.load_connection_file()
232 kernel_client.load_connection_file()
233 kernel_client.start_channels()
233 kernel_client.start_channels()
234 widget = self.widget_factory(config=self.config,
234 widget = self.widget_factory(config=self.config,
235 local_kernel=False)
235 local_kernel=False)
236 self.init_colors(widget)
236 self.init_colors(widget)
237 widget._existing = True
237 widget._existing = True
238 widget._may_close = False
238 widget._may_close = False
239 widget._confirm_exit = False
239 widget._confirm_exit = False
240 widget.kernel_client = kernel_client
240 widget.kernel_client = kernel_client
241 widget.kernel_manager = current_widget.kernel_manager
241 widget.kernel_manager = current_widget.kernel_manager
242 return widget
242 return widget
243
243
244 def init_qt_app(self):
244 def init_qt_app(self):
245 # separate from qt_elements, because it must run first
245 # separate from qt_elements, because it must run first
246 self.app = QtGui.QApplication([])
246 self.app = QtGui.QApplication([])
247
247
248 def init_qt_elements(self):
248 def init_qt_elements(self):
249 # Create the widget.
249 # Create the widget.
250
250
251 base_path = os.path.abspath(os.path.dirname(__file__))
251 base_path = os.path.abspath(os.path.dirname(__file__))
252 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
252 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
253 self.app.icon = QtGui.QIcon(icon_path)
253 self.app.icon = QtGui.QIcon(icon_path)
254 QtGui.QApplication.setWindowIcon(self.app.icon)
254 QtGui.QApplication.setWindowIcon(self.app.icon)
255
255
256 ip = self.ip
256 ip = self.ip
257 local_kernel = (not self.existing) or is_local_ip(ip)
257 local_kernel = (not self.existing) or is_local_ip(ip)
258 self.widget = self.widget_factory(config=self.config,
258 self.widget = self.widget_factory(config=self.config,
259 local_kernel=local_kernel)
259 local_kernel=local_kernel)
260 self.init_colors(self.widget)
260 self.init_colors(self.widget)
261 self.widget._existing = self.existing
261 self.widget._existing = self.existing
262 self.widget._may_close = not self.existing
262 self.widget._may_close = not self.existing
263 self.widget._confirm_exit = self.confirm_exit
263 self.widget._confirm_exit = self.confirm_exit
264
264
265 self.widget.kernel_manager = self.kernel_manager
265 self.widget.kernel_manager = self.kernel_manager
266 self.widget.kernel_client = self.kernel_client
266 self.widget.kernel_client = self.kernel_client
267 self.window = MainWindow(self.app,
267 self.window = MainWindow(self.app,
268 confirm_exit=self.confirm_exit,
268 confirm_exit=self.confirm_exit,
269 new_frontend_factory=self.new_frontend_master,
269 new_frontend_factory=self.new_frontend_master,
270 slave_frontend_factory=self.new_frontend_slave,
270 slave_frontend_factory=self.new_frontend_slave,
271 )
271 )
272 self.window.log = self.log
272 self.window.log = self.log
273 self.window.add_tab_with_frontend(self.widget)
273 self.window.add_tab_with_frontend(self.widget)
274 self.window.init_menu_bar()
275 self.window.init_magic_helper()
274 self.window.init_magic_helper()
275 self.window.init_menu_bar()
276
276
277 # Ignore on OSX, where there is always a menu bar
277 # Ignore on OSX, where there is always a menu bar
278 if sys.platform != 'darwin' and self.hide_menubar:
278 if sys.platform != 'darwin' and self.hide_menubar:
279 self.window.menuBar().setVisible(False)
279 self.window.menuBar().setVisible(False)
280
280
281 self.window.setWindowTitle('IPython')
281 self.window.setWindowTitle('IPython')
282
282
283 def init_colors(self, widget):
283 def init_colors(self, widget):
284 """Configure the coloring of the widget"""
284 """Configure the coloring of the widget"""
285 # Note: This will be dramatically simplified when colors
285 # Note: This will be dramatically simplified when colors
286 # are removed from the backend.
286 # are removed from the backend.
287
287
288 # parse the colors arg down to current known labels
288 # parse the colors arg down to current known labels
289 cfg = self.config
289 cfg = self.config
290 colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
290 colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
291 style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None
291 style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None
292 sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None
292 sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None
293
293
294 # find the value for colors:
294 # find the value for colors:
295 if colors:
295 if colors:
296 colors=colors.lower()
296 colors=colors.lower()
297 if colors in ('lightbg', 'light'):
297 if colors in ('lightbg', 'light'):
298 colors='lightbg'
298 colors='lightbg'
299 elif colors in ('dark', 'linux'):
299 elif colors in ('dark', 'linux'):
300 colors='linux'
300 colors='linux'
301 else:
301 else:
302 colors='nocolor'
302 colors='nocolor'
303 elif style:
303 elif style:
304 if style=='bw':
304 if style=='bw':
305 colors='nocolor'
305 colors='nocolor'
306 elif styles.dark_style(style):
306 elif styles.dark_style(style):
307 colors='linux'
307 colors='linux'
308 else:
308 else:
309 colors='lightbg'
309 colors='lightbg'
310 else:
310 else:
311 colors=None
311 colors=None
312
312
313 # Configure the style
313 # Configure the style
314 if style:
314 if style:
315 widget.style_sheet = styles.sheet_from_template(style, colors)
315 widget.style_sheet = styles.sheet_from_template(style, colors)
316 widget.syntax_style = style
316 widget.syntax_style = style
317 widget._syntax_style_changed()
317 widget._syntax_style_changed()
318 widget._style_sheet_changed()
318 widget._style_sheet_changed()
319 elif colors:
319 elif colors:
320 # use a default dark/light/bw style
320 # use a default dark/light/bw style
321 widget.set_default_style(colors=colors)
321 widget.set_default_style(colors=colors)
322
322
323 if self.stylesheet:
323 if self.stylesheet:
324 # we got an explicit stylesheet
324 # we got an explicit stylesheet
325 if os.path.isfile(self.stylesheet):
325 if os.path.isfile(self.stylesheet):
326 with open(self.stylesheet) as f:
326 with open(self.stylesheet) as f:
327 sheet = f.read()
327 sheet = f.read()
328 else:
328 else:
329 raise IOError("Stylesheet %r not found." % self.stylesheet)
329 raise IOError("Stylesheet %r not found." % self.stylesheet)
330 if sheet:
330 if sheet:
331 widget.style_sheet = sheet
331 widget.style_sheet = sheet
332 widget._style_sheet_changed()
332 widget._style_sheet_changed()
333
333
334
334
335 def init_signal(self):
335 def init_signal(self):
336 """allow clean shutdown on sigint"""
336 """allow clean shutdown on sigint"""
337 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
337 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
338 # need a timer, so that QApplication doesn't block until a real
338 # need a timer, so that QApplication doesn't block until a real
339 # Qt event fires (can require mouse movement)
339 # Qt event fires (can require mouse movement)
340 # timer trick from http://stackoverflow.com/q/4938723/938949
340 # timer trick from http://stackoverflow.com/q/4938723/938949
341 timer = QtCore.QTimer()
341 timer = QtCore.QTimer()
342 # Let the interpreter run each 200 ms:
342 # Let the interpreter run each 200 ms:
343 timer.timeout.connect(lambda: None)
343 timer.timeout.connect(lambda: None)
344 timer.start(200)
344 timer.start(200)
345 # hold onto ref, so the timer doesn't get cleaned up
345 # hold onto ref, so the timer doesn't get cleaned up
346 self._sigint_timer = timer
346 self._sigint_timer = timer
347
347
348 @catch_config_error
348 @catch_config_error
349 def initialize(self, argv=None):
349 def initialize(self, argv=None):
350 self.init_qt_app()
350 self.init_qt_app()
351 super(IPythonQtConsoleApp, self).initialize(argv)
351 super(IPythonQtConsoleApp, self).initialize(argv)
352 IPythonConsoleApp.initialize(self,argv)
352 IPythonConsoleApp.initialize(self,argv)
353 self.init_qt_elements()
353 self.init_qt_elements()
354 self.init_signal()
354 self.init_signal()
355
355
356 def start(self):
356 def start(self):
357
357
358 # draw the window
358 # draw the window
359 if self.maximize:
359 if self.maximize:
360 self.window.showMaximized()
360 self.window.showMaximized()
361 else:
361 else:
362 self.window.show()
362 self.window.show()
363 self.window.raise_()
363 self.window.raise_()
364
364
365 # Start the application main loop.
365 # Start the application main loop.
366 self.app.exec_()
366 self.app.exec_()
367
367
368 #-----------------------------------------------------------------------------
368 #-----------------------------------------------------------------------------
369 # Main entry point
369 # Main entry point
370 #-----------------------------------------------------------------------------
370 #-----------------------------------------------------------------------------
371
371
372 def main():
372 def main():
373 app = IPythonQtConsoleApp()
373 app = IPythonQtConsoleApp()
374 app.initialize()
374 app.initialize()
375 app.start()
375 app.start()
376
376
377
377
378 if __name__ == '__main__':
378 if __name__ == '__main__':
379 main()
379 main()
General Comments 0
You need to be logged in to leave comments. Login now