##// END OF EJS Templates
Fix menubar action after @Carreau's suggestion.
Fernando Perez -
Show More
@@ -1,1187 +1,1187 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
12
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib imports
19 # stdlib imports
20 import json
20 import json
21 import os
21 import os
22 import signal
22 import signal
23 import sys
23 import sys
24 import webbrowser
24 import webbrowser
25 import uuid
25 import uuid
26 from getpass import getpass
26 from getpass import getpass
27
27
28 # System library imports
28 # System library imports
29 from IPython.external.qt import QtGui,QtCore
29 from IPython.external.qt import QtGui,QtCore
30 from pygments.styles import get_all_styles
30 from pygments.styles import get_all_styles
31
31
32 # Local imports
32 # Local imports
33 from IPython.config.application import boolean_flag
33 from IPython.config.application import boolean_flag
34 from IPython.core.application import BaseIPythonApplication
34 from IPython.core.application import BaseIPythonApplication
35 from IPython.core.profiledir import ProfileDir
35 from IPython.core.profiledir import ProfileDir
36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 from IPython.frontend.qt.console import styles
40 from IPython.frontend.qt.console import styles
41 from IPython.frontend.qt.kernelmanager import QtKernelManager
41 from IPython.frontend.qt.kernelmanager import QtKernelManager
42 from IPython.utils.path import filefind
42 from IPython.utils.path import filefind
43 from IPython.utils.py3compat import str_to_bytes
43 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.traitlets import (
44 from IPython.utils.traitlets import (
45 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
45 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
46 )
46 )
47 from IPython.zmq.ipkernel import (
47 from IPython.zmq.ipkernel import (
48 flags as ipkernel_flags,
48 flags as ipkernel_flags,
49 aliases as ipkernel_aliases,
49 aliases as ipkernel_aliases,
50 IPKernelApp
50 IPKernelApp
51 )
51 )
52 from IPython.zmq.session import Session, default_secure
52 from IPython.zmq.session import Session, default_secure
53 from IPython.zmq.zmqshell import ZMQInteractiveShell
53 from IPython.zmq.zmqshell import ZMQInteractiveShell
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Network Constants
56 # Network Constants
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
59 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Globals
62 # Globals
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 _examples = """
65 _examples = """
66 ipython qtconsole # start the qtconsole
66 ipython qtconsole # start the qtconsole
67 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
67 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
68 """
68 """
69
69
70 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
71 # Classes
71 # Classes
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73
73
74 class MainWindow(QtGui.QMainWindow):
74 class MainWindow(QtGui.QMainWindow):
75
75
76 #---------------------------------------------------------------------------
76 #---------------------------------------------------------------------------
77 # 'object' interface
77 # 'object' interface
78 #---------------------------------------------------------------------------
78 #---------------------------------------------------------------------------
79
79
80 def __init__(self, app, frontend, existing=False, may_close=True,
80 def __init__(self, app, frontend, existing=False, may_close=True,
81 confirm_exit=True):
81 confirm_exit=True):
82 """ Create a MainWindow for the specified FrontendWidget.
82 """ Create a MainWindow for the specified FrontendWidget.
83
83
84 The app is passed as an argument to allow for different
84 The app is passed as an argument to allow for different
85 closing behavior depending on whether we are the Kernel's parent.
85 closing behavior depending on whether we are the Kernel's parent.
86
86
87 If existing is True, then this Console does not own the Kernel.
87 If existing is True, then this Console does not own the Kernel.
88
88
89 If may_close is True, then this Console is permitted to close the kernel
89 If may_close is True, then this Console is permitted to close the kernel
90 """
90 """
91
91
92 super(MainWindow, self).__init__()
92 super(MainWindow, self).__init__()
93 self._app = app
93 self._app = app
94
94
95 self.tab_widget = QtGui.QTabWidget(self)
95 self.tab_widget = QtGui.QTabWidget(self)
96 self.tab_widget.setDocumentMode(True)
96 self.tab_widget.setDocumentMode(True)
97 self.tab_widget.setTabsClosable(True)
97 self.tab_widget.setTabsClosable(True)
98 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
98 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
99
99
100 self.setCentralWidget(self.tab_widget)
100 self.setCentralWidget(self.tab_widget)
101 self.update_tab_bar_visibility()
101 self.update_tab_bar_visibility()
102
102
103 def update_tab_bar_visibility(self):
103 def update_tab_bar_visibility(self):
104 """ update visibility of the tabBar depending of the number of tab
104 """ update visibility of the tabBar depending of the number of tab
105
105
106 0 or 1 tab, tabBar hidden
106 0 or 1 tab, tabBar hidden
107 2+ tabs, tabBar visible
107 2+ tabs, tabBar visible
108
108
109 send a self.close if number of tab ==0
109 send a self.close if number of tab ==0
110
110
111 need to be called explicitely, or be connected to tabInserted/tabRemoved
111 need to be called explicitely, or be connected to tabInserted/tabRemoved
112 """
112 """
113 if self.tab_widget.count() <= 1:
113 if self.tab_widget.count() <= 1:
114 self.tab_widget.tabBar().setVisible(False)
114 self.tab_widget.tabBar().setVisible(False)
115 else:
115 else:
116 self.tab_widget.tabBar().setVisible(True)
116 self.tab_widget.tabBar().setVisible(True)
117 if self.tab_widget.count()==0 :
117 if self.tab_widget.count()==0 :
118 self.close()
118 self.close()
119
119
120 @property
120 @property
121 def active_frontend(self):
121 def active_frontend(self):
122 return self.tab_widget.currentWidget()
122 return self.tab_widget.currentWidget()
123
123
124 def close_tab(self,current_tab):
124 def close_tab(self,current_tab):
125 """ Called when you need to try to close a tab.
125 """ Called when you need to try to close a tab.
126
126
127 It takes the number of the tab to be closed as argument, or a referece
127 It takes the number of the tab to be closed as argument, or a referece
128 to the wiget insite this tab
128 to the wiget insite this tab
129 """
129 """
130
130
131 # let's be sure "tab" and "closing widget are respectivey the index of the tab to close
131 # let's be sure "tab" and "closing widget are respectivey the index of the tab to close
132 # and a reference to the trontend to close
132 # and a reference to the trontend to close
133 if type(current_tab) is not int :
133 if type(current_tab) is not int :
134 current_tab = self.tab_widget.indexOf(current_tab)
134 current_tab = self.tab_widget.indexOf(current_tab)
135 closing_widget=self.tab_widget.widget(current_tab)
135 closing_widget=self.tab_widget.widget(current_tab)
136
136
137
137
138 # when trying to be closed, widget might re-send a request to be closed again, but will
138 # when trying to be closed, widget might re-send a request to be closed again, but will
139 # be deleted when event will be processed. So need to check that widget still exist and
139 # be deleted when event will be processed. So need to check that widget still exist and
140 # skip if not. One example of this is when 'exit' is send in a slave tab. 'exit' will be
140 # skip if not. One example of this is when 'exit' is send in a slave tab. 'exit' will be
141 # re-send by this fonction on the master widget, which ask all slaves widget to exit
141 # re-send by this fonction on the master widget, which ask all slaves widget to exit
142 if closing_widget==None:
142 if closing_widget==None:
143 return
143 return
144
144
145 #get a list of all wwidget not owning the kernel.
145 #get a list of all wwidget not owning the kernel.
146 slave_tabs=self.find_slaves_tabs(closing_widget)
146 slave_tabs=self.find_slaves_tabs(closing_widget)
147
147
148 keepkernel = None #Use the prompt by default
148 keepkernel = None #Use the prompt by default
149 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
149 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
150 keepkernel = closing_widget._keep_kernel_on_exit
150 keepkernel = closing_widget._keep_kernel_on_exit
151 # If signal sent by exist magic (_keep_kernel_on_exit, exist and not None)
151 # If signal sent by exist magic (_keep_kernel_on_exit, exist and not None)
152 # we set local slave tabs._hidden to True to avoit prompting for kernel
152 # we set local slave tabs._hidden to True to avoit prompting for kernel
153 # restart when they litt get the signal. and the "forward" the 'exit'
153 # restart when they litt get the signal. and the "forward" the 'exit'
154 # to the main win
154 # to the main win
155 if keepkernel is not None:
155 if keepkernel is not None:
156 for tab in slave_tabs:
156 for tab in slave_tabs:
157 tab._hidden = True
157 tab._hidden = True
158 if closing_widget in slave_tabs :
158 if closing_widget in slave_tabs :
159 try :
159 try :
160 self.find_master_tab(closing_widget).execute('exit')
160 self.find_master_tab(closing_widget).execute('exit')
161 except AttributeError:
161 except AttributeError:
162 self.log.info("Master already closed or not local, closing only current tab")
162 self.log.info("Master already closed or not local, closing only current tab")
163 self.tab_widget.removeTab(current_tab)
163 self.tab_widget.removeTab(current_tab)
164 return
164 return
165
165
166 kernel_manager = closing_widget.kernel_manager
166 kernel_manager = closing_widget.kernel_manager
167
167
168 if keepkernel is None and not closing_widget._confirm_exit:
168 if keepkernel is None and not closing_widget._confirm_exit:
169 # don't prompt, just terminate the kernel if we own it
169 # don't prompt, just terminate the kernel if we own it
170 # or leave it alone if we don't
170 # or leave it alone if we don't
171 keepkernel = not closing_widget._existing
171 keepkernel = not closing_widget._existing
172
172
173 if keepkernel is None: #show prompt
173 if keepkernel is None: #show prompt
174 if kernel_manager and kernel_manager.channels_running:
174 if kernel_manager and kernel_manager.channels_running:
175 title = self.window().windowTitle()
175 title = self.window().windowTitle()
176 cancel = QtGui.QMessageBox.Cancel
176 cancel = QtGui.QMessageBox.Cancel
177 okay = QtGui.QMessageBox.Ok
177 okay = QtGui.QMessageBox.Ok
178 if closing_widget._may_close:
178 if closing_widget._may_close:
179 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
179 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
180 info = "Would you like to quit the Kernel and all attached Consoles as well?"
180 info = "Would you like to quit the Kernel and all attached Consoles as well?"
181 justthis = QtGui.QPushButton("&No, just this Console", self)
181 justthis = QtGui.QPushButton("&No, just this Console", self)
182 justthis.setShortcut('N')
182 justthis.setShortcut('N')
183 closeall = QtGui.QPushButton("&Yes, quit everything", self)
183 closeall = QtGui.QPushButton("&Yes, quit everything", self)
184 closeall.setShortcut('Y')
184 closeall.setShortcut('Y')
185 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
185 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
186 title, msg)
186 title, msg)
187 box.setInformativeText(info)
187 box.setInformativeText(info)
188 box.addButton(cancel)
188 box.addButton(cancel)
189 box.addButton(justthis, QtGui.QMessageBox.NoRole)
189 box.addButton(justthis, QtGui.QMessageBox.NoRole)
190 box.addButton(closeall, QtGui.QMessageBox.YesRole)
190 box.addButton(closeall, QtGui.QMessageBox.YesRole)
191 box.setDefaultButton(closeall)
191 box.setDefaultButton(closeall)
192 box.setEscapeButton(cancel)
192 box.setEscapeButton(cancel)
193 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
193 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
194 box.setIconPixmap(pixmap)
194 box.setIconPixmap(pixmap)
195 reply = box.exec_()
195 reply = box.exec_()
196 if reply == 1: # close All
196 if reply == 1: # close All
197 for slave in slave_tabs:
197 for slave in slave_tabs:
198 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
198 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
199 closing_widget.execute("exit")
199 closing_widget.execute("exit")
200 self.tab_widget.removeTab(current_tab)
200 self.tab_widget.removeTab(current_tab)
201 elif reply == 0: # close Console
201 elif reply == 0: # close Console
202 if not closing_widget._existing:
202 if not closing_widget._existing:
203 # Have kernel: don't quit, just close the window
203 # Have kernel: don't quit, just close the window
204 self._app.setQuitOnLastWindowClosed(False)
204 self._app.setQuitOnLastWindowClosed(False)
205 closing_widget.execute("exit True")
205 closing_widget.execute("exit True")
206 else:
206 else:
207 reply = QtGui.QMessageBox.question(self, title,
207 reply = QtGui.QMessageBox.question(self, title,
208 "Are you sure you want to close this Console?"+
208 "Are you sure you want to close this Console?"+
209 "\nThe Kernel and other Consoles will remain active.",
209 "\nThe Kernel and other Consoles will remain active.",
210 okay|cancel,
210 okay|cancel,
211 defaultButton=okay
211 defaultButton=okay
212 )
212 )
213 if reply == okay:
213 if reply == okay:
214 self.tab_widget.removeTab(current_tab)
214 self.tab_widget.removeTab(current_tab)
215 elif keepkernel: #close console but leave kernel running (no prompt)
215 elif keepkernel: #close console but leave kernel running (no prompt)
216 if kernel_manager and kernel_manager.channels_running:
216 if kernel_manager and kernel_manager.channels_running:
217 if not closing_widget._existing:
217 if not closing_widget._existing:
218 # I have the kernel: don't quit, just close the window
218 # I have the kernel: don't quit, just close the window
219 self.tab_widget.removeTab(current_tab)
219 self.tab_widget.removeTab(current_tab)
220 else: #close console and kernel (no prompt)
220 else: #close console and kernel (no prompt)
221 if kernel_manager and kernel_manager.channels_running:
221 if kernel_manager and kernel_manager.channels_running:
222 for slave in slave_tabs:
222 for slave in slave_tabs:
223 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
223 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
224 self.tab_widget.removeTab(current_tab)
224 self.tab_widget.removeTab(current_tab)
225 kernel_manager.shutdown_kernel()
225 kernel_manager.shutdown_kernel()
226 self.update_tab_bar_visibility()
226 self.update_tab_bar_visibility()
227
227
228 def add_tab_with_frontend(self,frontend,name=None):
228 def add_tab_with_frontend(self,frontend,name=None):
229 """ insert a tab with a given frontend in the tab bar, and give it a name
229 """ insert a tab with a given frontend in the tab bar, and give it a name
230
230
231 """
231 """
232 if not name:
232 if not name:
233 name=str('kernel '+str(self.tab_widget.count()))
233 name=str('kernel '+str(self.tab_widget.count()))
234 self.tab_widget.addTab(frontend,name)
234 self.tab_widget.addTab(frontend,name)
235 self.update_tab_bar_visibility()
235 self.update_tab_bar_visibility()
236 self.make_frontend_visible(frontend)
236 self.make_frontend_visible(frontend)
237 frontend.exit_requested.connect(self.close_tab)
237 frontend.exit_requested.connect(self.close_tab)
238
238
239 def next_tab(self):
239 def next_tab(self):
240 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
240 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
241
241
242 def prev_tab(self):
242 def prev_tab(self):
243 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
243 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
244
244
245 def make_frontend_visible(self,frontend):
245 def make_frontend_visible(self,frontend):
246 widget_index=self.tab_widget.indexOf(frontend)
246 widget_index=self.tab_widget.indexOf(frontend)
247 if widget_index > 0 :
247 if widget_index > 0 :
248 self.tab_widget.setCurrentIndex(widget_index)
248 self.tab_widget.setCurrentIndex(widget_index)
249
249
250 def find_master_tab(self,tab,as_list=False):
250 def find_master_tab(self,tab,as_list=False):
251 """
251 """
252 Try to return the frontend that own the kernel attached to the given widget/tab.
252 Try to return the frontend that own the kernel attached to the given widget/tab.
253
253
254 Only find frontend owed by the current application. Selection
254 Only find frontend owed by the current application. Selection
255 based on port of the kernel, might be inacurate if several kernel
255 based on port of the kernel, might be inacurate if several kernel
256 on different ip use same port number.
256 on different ip use same port number.
257
257
258 This fonction does the conversion tabNumber/widget if needed.
258 This fonction does the conversion tabNumber/widget if needed.
259 Might return None if no master widget (non local kernel)
259 Might return None if no master widget (non local kernel)
260 Will crash IPython if more than 1 masterWidget
260 Will crash IPython if more than 1 masterWidget
261
261
262 When asList set to True, always return a list of widget(s) owning
262 When asList set to True, always return a list of widget(s) owning
263 the kernel. The list might be empty or containing several Widget.
263 the kernel. The list might be empty or containing several Widget.
264 """
264 """
265
265
266 #convert from/to int/richIpythonWidget if needed
266 #convert from/to int/richIpythonWidget if needed
267 if type(tab) == int:
267 if type(tab) == int:
268 tab = self.tab_widget.widget(tab)
268 tab = self.tab_widget.widget(tab)
269 km=tab.kernel_manager;
269 km=tab.kernel_manager;
270
270
271 #build list of all widgets
271 #build list of all widgets
272 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
272 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
273
273
274 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
274 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
275 # And should have a _may_close attribute
275 # And should have a _may_close attribute
276 filtred_widget_list = [ widget for widget in widget_list if
276 filtred_widget_list = [ widget for widget in widget_list if
277 widget.kernel_manager.connection_file == km.connection_file and
277 widget.kernel_manager.connection_file == km.connection_file and
278 hasattr(widget,'_may_close') ]
278 hasattr(widget,'_may_close') ]
279 # the master widget is the one that may close the kernel
279 # the master widget is the one that may close the kernel
280 master_widget= [ widget for widget in filtred_widget_list if widget._may_close]
280 master_widget= [ widget for widget in filtred_widget_list if widget._may_close]
281 if as_list:
281 if as_list:
282 return master_widget
282 return master_widget
283 assert(len(master_widget)<=1 )
283 assert(len(master_widget)<=1 )
284 if len(master_widget)==0:
284 if len(master_widget)==0:
285 return None
285 return None
286
286
287 return master_widget[0]
287 return master_widget[0]
288
288
289 def find_slaves_tabs(self,tab):
289 def find_slaves_tabs(self,tab):
290 """
290 """
291 Try to return all the frontend that do not own the kernel attached to the given widget/tab.
291 Try to return all the frontend that do not own the kernel attached to the given widget/tab.
292
292
293 Only find frontend owed by the current application. Selection
293 Only find frontend owed by the current application. Selection
294 based on port of the kernel, might be innacurate if several kernel
294 based on port of the kernel, might be innacurate if several kernel
295 on different ip use same port number.
295 on different ip use same port number.
296
296
297 This fonction does the conversion tabNumber/widget if needed.
297 This fonction does the conversion tabNumber/widget if needed.
298 """
298 """
299 #convert from/to int/richIpythonWidget if needed
299 #convert from/to int/richIpythonWidget if needed
300 if type(tab) == int:
300 if type(tab) == int:
301 tab = self.tab_widget.widget(tab)
301 tab = self.tab_widget.widget(tab)
302 km=tab.kernel_manager;
302 km=tab.kernel_manager;
303
303
304 #build list of all widgets
304 #build list of all widgets
305 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
305 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
306
306
307 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
307 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
308 filtered_widget_list = ( widget for widget in widget_list if
308 filtered_widget_list = ( widget for widget in widget_list if
309 widget.kernel_manager.connection_file == km.connection_file)
309 widget.kernel_manager.connection_file == km.connection_file)
310 # Get a list of all widget owning the same kernel and removed it from
310 # Get a list of all widget owning the same kernel and removed it from
311 # the previous cadidate. (better using sets ?)
311 # the previous cadidate. (better using sets ?)
312 master_widget_list = self.find_master_tab(tab,as_list=True)
312 master_widget_list = self.find_master_tab(tab,as_list=True)
313 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
313 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
314
314
315 return slave_list
315 return slave_list
316
316
317 # MenuBar is always present on Mac Os, so let's populate it with possible
317 # MenuBar is always present on Mac Os, so let's populate it with possible
318 # action, don't do it on other platform as some user might not want the
318 # action, don't do it on other platform as some user might not want the
319 # menu bar, or give them an option to remove it
319 # menu bar, or give them an option to remove it
320 def init_menu_bar(self):
320 def init_menu_bar(self):
321 #create menu in the order they should appear in the menu bar
321 #create menu in the order they should appear in the menu bar
322 self.file_menu = self.menuBar().addMenu("&File")
322 self.file_menu = self.menuBar().addMenu("&File")
323 self.edit_menu = self.menuBar().addMenu("&Edit")
323 self.edit_menu = self.menuBar().addMenu("&Edit")
324 self.kernel_menu = self.menuBar().addMenu("&Kernel")
324 self.kernel_menu = self.menuBar().addMenu("&Kernel")
325 self.window_menu = self.menuBar().addMenu("&Window")
325 self.window_menu = self.menuBar().addMenu("&Window")
326 self.magic_menu = self.menuBar().addMenu("&Magic")
326 self.magic_menu = self.menuBar().addMenu("&Magic")
327 self.all_magic_menu = self.magic_menu.addMenu("&All Magic")
327 self.all_magic_menu = self.magic_menu.addMenu("&All Magic")
328
328
329 # please keep the Help menu in Mac Os even if empty. It will
329 # please keep the Help menu in Mac Os even if empty. It will
330 # automatically contain a search field to search inside menus and
330 # automatically contain a search field to search inside menus and
331 # please keep it spelled in English, as long as Qt Doesn't support
331 # please keep it spelled in English, as long as Qt Doesn't support
332 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
332 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
333 # this search field fonctionnality
333 # this search field fonctionnality
334
334
335 self.help_menu = self.menuBar().addMenu("&Help")
335 self.help_menu = self.menuBar().addMenu("&Help")
336
336
337 self.print_action = QtGui.QAction("&Print",
337 self.print_action = QtGui.QAction("&Print",
338 self,
338 self,
339 shortcut="Ctrl+P",
339 shortcut="Ctrl+P",
340 triggered=self.print_action_active_frontend)
340 triggered=self.print_action_active_frontend)
341 self.file_menu.addAction(self.print_action)
341 self.file_menu.addAction(self.print_action)
342
342
343 self.export_action=QtGui.QAction("E&xport",
343 self.export_action=QtGui.QAction("E&xport",
344 self,
344 self,
345 shortcut="Ctrl+S",
345 shortcut="Ctrl+S",
346 triggered=self.export_action_active_frontend
346 triggered=self.export_action_active_frontend
347 )
347 )
348 self.file_menu.addAction(self.export_action)
348 self.file_menu.addAction(self.export_action)
349
349
350 self.select_all_action = QtGui.QAction("Select &All",
350 self.select_all_action = QtGui.QAction("Select &All",
351 self,
351 self,
352 shortcut="Ctrl+A",
352 shortcut="Ctrl+A",
353 triggered=self.select_all_active_frontend
353 triggered=self.select_all_active_frontend
354 )
354 )
355 self.file_menu.addAction(self.select_all_action)
355 self.file_menu.addAction(self.select_all_action)
356
356
357 self.paste_action = QtGui.QAction("&Paste",
357 self.paste_action = QtGui.QAction("&Paste",
358 self,
358 self,
359 shortcut=QtGui.QKeySequence.Paste,
359 shortcut=QtGui.QKeySequence.Paste,
360 triggered=self.paste_active_frontend
360 triggered=self.paste_active_frontend
361 )
361 )
362 self.edit_menu.addAction(self.paste_action)
362 self.edit_menu.addAction(self.paste_action)
363
363
364 self.copy_action = QtGui.QAction("&Copy",
364 self.copy_action = QtGui.QAction("&Copy",
365 self,
365 self,
366 shortcut=QtGui.QKeySequence.Copy,
366 shortcut=QtGui.QKeySequence.Copy,
367 triggered=self.copy_active_frontend
367 triggered=self.copy_active_frontend
368 )
368 )
369 self.edit_menu.addAction(self.copy_action)
369 self.edit_menu.addAction(self.copy_action)
370
370
371 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
371 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
372 self,
372 self,
373 shortcut="Ctrl+Shift+C",
373 shortcut="Ctrl+Shift+C",
374 triggered=self.copy_raw_active_frontend
374 triggered=self.copy_raw_active_frontend
375 )
375 )
376 self.edit_menu.addAction(self.copy_raw_action)
376 self.edit_menu.addAction(self.copy_raw_action)
377
377
378 self.cut_action = QtGui.QAction("&Cut",
378 self.cut_action = QtGui.QAction("&Cut",
379 self,
379 self,
380 shortcut=QtGui.QKeySequence.Cut,
380 shortcut=QtGui.QKeySequence.Cut,
381 triggered=self.cut_active_frontend
381 triggered=self.cut_active_frontend
382 )
382 )
383 self.edit_menu.addAction(self.cut_action)
383 self.edit_menu.addAction(self.cut_action)
384
384
385 self.edit_menu.addSeparator()
385 self.edit_menu.addSeparator()
386
386
387 self.undo_action = QtGui.QAction("&Undo",
387 self.undo_action = QtGui.QAction("&Undo",
388 self,
388 self,
389 shortcut="Ctrl+Z",
389 shortcut="Ctrl+Z",
390 statusTip="Undo last action if possible",
390 statusTip="Undo last action if possible",
391 triggered=self.undo_active_frontend
391 triggered=self.undo_active_frontend
392 )
392 )
393 self.edit_menu.addAction(self.undo_action)
393 self.edit_menu.addAction(self.undo_action)
394
394
395 self.redo_action = QtGui.QAction("&Redo",
395 self.redo_action = QtGui.QAction("&Redo",
396 self,
396 self,
397 shortcut="Ctrl+Shift+Z",
397 shortcut="Ctrl+Shift+Z",
398 statusTip="Redo last action if possible",
398 statusTip="Redo last action if possible",
399 triggered=self.redo_active_frontend)
399 triggered=self.redo_active_frontend)
400 self.edit_menu.addAction(self.redo_action)
400 self.edit_menu.addAction(self.redo_action)
401
401
402 self.window_menu.addSeparator()
402 self.window_menu.addSeparator()
403
403
404 self.increase_font_size = QtGui.QAction("&Increase Font Size",
404 self.increase_font_size = QtGui.QAction("&Increase Font Size",
405 self,
405 self,
406 shortcut="Ctrl++",
406 shortcut="Ctrl++",
407 triggered=self.increase_font_size_active_frontend
407 triggered=self.increase_font_size_active_frontend
408 )
408 )
409 self.window_menu.addAction(self.increase_font_size)
409 self.window_menu.addAction(self.increase_font_size)
410
410
411 self.decrease_font_size = QtGui.QAction("&Decrease Font Size",
411 self.decrease_font_size = QtGui.QAction("&Decrease Font Size",
412 self,
412 self,
413 shortcut="Ctrl+-",
413 shortcut="Ctrl+-",
414 triggered=self.decrease_font_size_active_frontend
414 triggered=self.decrease_font_size_active_frontend
415 )
415 )
416 self.window_menu.addAction(self.decrease_font_size)
416 self.window_menu.addAction(self.decrease_font_size)
417
417
418 self.reset_font_size = QtGui.QAction("&Reset Font Size",
418 self.reset_font_size = QtGui.QAction("&Reset Font Size",
419 self,
419 self,
420 shortcut="Ctrl+0",
420 shortcut="Ctrl+0",
421 triggered=self.reset_font_size_active_frontend
421 triggered=self.reset_font_size_active_frontend
422 )
422 )
423 self.window_menu.addAction(self.reset_font_size)
423 self.window_menu.addAction(self.reset_font_size)
424
424
425 self.window_menu.addSeparator()
425 self.window_menu.addSeparator()
426
426
427 self.reset_action = QtGui.QAction("&Reset",
427 self.reset_action = QtGui.QAction("&Reset",
428 self,
428 self,
429 statusTip="Clear all varible from workspace",
429 statusTip="Clear all varible from workspace",
430 triggered=self.reset_magic_active_frontend)
430 triggered=self.reset_magic_active_frontend)
431 self.magic_menu.addAction(self.reset_action)
431 self.magic_menu.addAction(self.reset_action)
432
432
433 self.history_action = QtGui.QAction("&History",
433 self.history_action = QtGui.QAction("&History",
434 self,
434 self,
435 statusTip="show command history",
435 statusTip="show command history",
436 triggered=self.history_magic_active_frontend)
436 triggered=self.history_magic_active_frontend)
437 self.magic_menu.addAction(self.history_action)
437 self.magic_menu.addAction(self.history_action)
438
438
439 self.save_action = QtGui.QAction("E&xport History ",
439 self.save_action = QtGui.QAction("E&xport History ",
440 self,
440 self,
441 statusTip="Export History as Python File",
441 statusTip="Export History as Python File",
442 triggered=self.save_magic_active_frontend)
442 triggered=self.save_magic_active_frontend)
443 self.magic_menu.addAction(self.save_action)
443 self.magic_menu.addAction(self.save_action)
444
444
445 self.clear_action = QtGui.QAction("&Clear Screen",
445 self.clear_action = QtGui.QAction("&Clear Screen",
446 self,
446 self,
447 shortcut='Ctrl+L',
447 shortcut='Ctrl+L',
448 statusTip="Clear the console",
448 statusTip="Clear the console",
449 triggered=self.clear_magic_active_frontend)
449 triggered=self.clear_magic_active_frontend)
450 self.window_menu.addAction(self.clear_action)
450 self.window_menu.addAction(self.clear_action)
451
451
452 self.who_action = QtGui.QAction("&Who",
452 self.who_action = QtGui.QAction("&Who",
453 self,
453 self,
454 statusTip="List interactive variable",
454 statusTip="List interactive variable",
455 triggered=self.who_magic_active_frontend)
455 triggered=self.who_magic_active_frontend)
456 self.magic_menu.addAction(self.who_action)
456 self.magic_menu.addAction(self.who_action)
457
457
458 self.who_ls_action = QtGui.QAction("Wh&o ls",
458 self.who_ls_action = QtGui.QAction("Wh&o ls",
459 self,
459 self,
460 statusTip="Return a list of interactive variable",
460 statusTip="Return a list of interactive variable",
461 triggered=self.who_ls_magic_active_frontend)
461 triggered=self.who_ls_magic_active_frontend)
462 self.magic_menu.addAction(self.who_ls_action)
462 self.magic_menu.addAction(self.who_ls_action)
463
463
464 self.whos_action = QtGui.QAction("Who&s",
464 self.whos_action = QtGui.QAction("Who&s",
465 self,
465 self,
466 statusTip="List interactive variable with detail",
466 statusTip="List interactive variable with detail",
467 triggered=self.whos_magic_active_frontend)
467 triggered=self.whos_magic_active_frontend)
468 self.magic_menu.addAction(self.whos_action)
468 self.magic_menu.addAction(self.whos_action)
469
469
470 self.intro_active_frontend_action = QtGui.QAction("Intro",
470 self.intro_active_frontend_action = QtGui.QAction("Intro",
471 self,
471 self,
472 triggered=self.intro_active_frontend
472 triggered=self.intro_active_frontend
473 )
473 )
474 self.help_menu.addAction(self.intro_active_frontend_action)
474 self.help_menu.addAction(self.intro_active_frontend_action)
475
475
476 self.guiref_active_frontend_action = QtGui.QAction("Gui references",
476 self.guiref_active_frontend_action = QtGui.QAction("Gui references",
477 self,
477 self,
478 triggered=self.guiref_active_frontend
478 triggered=self.guiref_active_frontend
479 )
479 )
480 self.help_menu.addAction(self.guiref_active_frontend_action)
480 self.help_menu.addAction(self.guiref_active_frontend_action)
481
481
482 self.quickref_active_frontend_action = QtGui.QAction("Quick references",
482 self.quickref_active_frontend_action = QtGui.QAction("Quick references",
483 self,
483 self,
484 triggered=self.quickref_active_frontend
484 triggered=self.quickref_active_frontend
485 )
485 )
486 self.help_menu.addAction(self.quickref_active_frontend_action)
486 self.help_menu.addAction(self.quickref_active_frontend_action)
487
487
488 self.interrupt_kernel_action = QtGui.QAction("Interrupt current Kernel",
488 self.interrupt_kernel_action = QtGui.QAction("Interrupt current Kernel",
489 self,
489 self,
490 triggered=self.interrupt_kernel_active_frontend
490 triggered=self.interrupt_kernel_active_frontend
491 )
491 )
492 self.kernel_menu.addAction(self.interrupt_kernel_action)
492 self.kernel_menu.addAction(self.interrupt_kernel_action)
493
493
494 self.restart_kernel_action = QtGui.QAction("Restart current Kernel",
494 self.restart_kernel_action = QtGui.QAction("Restart current Kernel",
495 self,
495 self,
496 triggered=self.restart_kernel_active_frontend
496 triggered=self.restart_kernel_active_frontend
497 )
497 )
498 self.kernel_menu.addAction(self.restart_kernel_action)
498 self.kernel_menu.addAction(self.restart_kernel_action)
499 self.kernel_menu.addSeparator()
499 self.kernel_menu.addSeparator()
500
500
501 #for now this is just a copy and paste, but we should get this dynamically
501 #for now this is just a copy and paste, but we should get this dynamically
502 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
502 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
503 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
503 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
504 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
504 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
505 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
505 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
506 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
506 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
507 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
507 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
508 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
508 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
509 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
509 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
510 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
510 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
511 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
511 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
512
512
513 def make_dynamic_magic(i):
513 def make_dynamic_magic(i):
514 def inner_dynamic_magic():
514 def inner_dynamic_magic():
515 self.active_frontend.execute(i)
515 self.active_frontend.execute(i)
516 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
516 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
517 return inner_dynamic_magic
517 return inner_dynamic_magic
518
518
519 for magic in magiclist:
519 for magic in magiclist:
520 xaction = QtGui.QAction(magic,
520 xaction = QtGui.QAction(magic,
521 self,
521 self,
522 triggered=make_dynamic_magic(magic)
522 triggered=make_dynamic_magic(magic)
523 )
523 )
524 self.all_magic_menu.addAction(xaction)
524 self.all_magic_menu.addAction(xaction)
525
525
526 def restart_kernel_active_frontend(self):
526 def restart_kernel_active_frontend(self):
527 self.active_frontend.request_restart_kernel()
527 self.active_frontend.request_restart_kernel()
528
528
529 def interrupt_kernel_active_frontend(self):
529 def interrupt_kernel_active_frontend(self):
530 self.active_frontend.request_interrupt_kernel()
530 self.active_frontend.request_interrupt_kernel()
531
531
532 def cut_active_frontend(self):
532 def cut_active_frontend(self):
533 self.active_frontend.cut_action.trigger()
533 self.active_frontend.cut_action.trigger()
534
534
535 def copy_active_frontend(self):
535 def copy_active_frontend(self):
536 self.active_frontend.copy_action.trigger()
536 self.active_frontend.copy_action.trigger()
537
537
538 def copy_raw_active_frontend(self):
538 def copy_raw_active_frontend(self):
539 self.active_frontend._copy_raw_action.trigger()
539 self.active_frontend._copy_raw_action.trigger()
540
540
541 def paste_active_frontend(self):
541 def paste_active_frontend(self):
542 self.active_frontend.paste_action.trigger()
542 self.active_frontend.paste_action.trigger()
543
543
544 def undo_active_frontend(self):
544 def undo_active_frontend(self):
545 self.active_frontend.undo()
545 self.active_frontend.undo()
546
546
547 def redo_active_frontend(self):
547 def redo_active_frontend(self):
548 self.active_frontend.redo()
548 self.active_frontend.redo()
549
549
550 def reset_magic_active_frontend(self):
550 def reset_magic_active_frontend(self):
551 self.active_frontend.execute("%reset")
551 self.active_frontend.execute("%reset")
552
552
553 def history_magic_active_frontend(self):
553 def history_magic_active_frontend(self):
554 self.active_frontend.execute("%history")
554 self.active_frontend.execute("%history")
555
555
556 def save_magic_active_frontend(self):
556 def save_magic_active_frontend(self):
557 self.active_frontend.save_magic()
557 self.active_frontend.save_magic()
558
558
559 def clear_magic_active_frontend(self):
559 def clear_magic_active_frontend(self):
560 self.active_frontend.execute("%clear")
560 self.active_frontend.execute("%clear")
561
561
562 def who_magic_active_frontend(self):
562 def who_magic_active_frontend(self):
563 self.active_frontend.execute("%who")
563 self.active_frontend.execute("%who")
564
564
565 def who_ls_magic_active_frontend(self):
565 def who_ls_magic_active_frontend(self):
566 self.active_frontend.execute("%who_ls")
566 self.active_frontend.execute("%who_ls")
567
567
568 def whos_magic_active_frontend(self):
568 def whos_magic_active_frontend(self):
569 self.active_frontend.execute("%whos")
569 self.active_frontend.execute("%whos")
570
570
571 def print_action_active_frontend(self):
571 def print_action_active_frontend(self):
572 self.active_frontend.print_action.trigger()
572 self.active_frontend.print_action.trigger()
573
573
574 def export_action_active_frontend(self):
574 def export_action_active_frontend(self):
575 self.active_frontend.export_action.trigger()
575 self.active_frontend.export_action.trigger()
576
576
577 def select_all_active_frontend(self):
577 def select_all_active_frontend(self):
578 self.active_frontend.select_all_action.trigger()
578 self.active_frontend.select_all_action.trigger()
579
579
580 def increase_font_size_active_frontend(self):
580 def increase_font_size_active_frontend(self):
581 self.active_frontend.increase_font_size.trigger()
581 self.active_frontend.increase_font_size.trigger()
582
582
583 def decrease_font_size_active_frontend(self):
583 def decrease_font_size_active_frontend(self):
584 self.active_frontend.decrease_font_size.trigger()
584 self.active_frontend.decrease_font_size.trigger()
585
585
586 def reset_font_size_active_frontend(self):
586 def reset_font_size_active_frontend(self):
587 self.active_frontend.reset_font_size.trigger()
587 self.active_frontend.reset_font_size.trigger()
588
588
589 def guiref_active_frontend(self):
589 def guiref_active_frontend(self):
590 self.active_frontend.execute("%guiref")
590 self.active_frontend.execute("%guiref")
591
591
592 def intro_active_frontend(self):
592 def intro_active_frontend(self):
593 self.active_frontend.execute("?")
593 self.active_frontend.execute("?")
594
594
595 def quickref_active_frontend(self):
595 def quickref_active_frontend(self):
596 self.active_frontend.execute("%quickref")
596 self.active_frontend.execute("%quickref")
597 #---------------------------------------------------------------------------
597 #---------------------------------------------------------------------------
598 # QWidget interface
598 # QWidget interface
599 #---------------------------------------------------------------------------
599 #---------------------------------------------------------------------------
600
600
601 def closeEvent(self, event):
601 def closeEvent(self, event):
602 """ Forward the close event to every tabs contained by the windows
602 """ Forward the close event to every tabs contained by the windows
603 """
603 """
604 # Do Not loop on the widget count as it change while closing
604 # Do Not loop on the widget count as it change while closing
605 widget_list=[ self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
605 widget_list=[ self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
606 for widget in widget_list:
606 for widget in widget_list:
607 self.close_tab(widget)
607 self.close_tab(widget)
608 event.accept()
608 event.accept()
609
609
610 #-----------------------------------------------------------------------------
610 #-----------------------------------------------------------------------------
611 # Aliases and Flags
611 # Aliases and Flags
612 #-----------------------------------------------------------------------------
612 #-----------------------------------------------------------------------------
613
613
614 flags = dict(ipkernel_flags)
614 flags = dict(ipkernel_flags)
615 qt_flags = {
615 qt_flags = {
616 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
616 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
617 "Connect to an existing kernel. If no argument specified, guess most recent"),
617 "Connect to an existing kernel. If no argument specified, guess most recent"),
618 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
618 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
619 "Use a pure Python kernel instead of an IPython kernel."),
619 "Use a pure Python kernel instead of an IPython kernel."),
620 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
620 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
621 "Disable rich text support."),
621 "Disable rich text support."),
622 }
622 }
623 qt_flags.update(boolean_flag(
623 qt_flags.update(boolean_flag(
624 'gui-completion', 'ConsoleWidget.gui_completion',
624 'gui-completion', 'ConsoleWidget.gui_completion',
625 "use a GUI widget for tab completion",
625 "use a GUI widget for tab completion",
626 "use plaintext output for completion"
626 "use plaintext output for completion"
627 ))
627 ))
628 qt_flags.update(boolean_flag(
628 qt_flags.update(boolean_flag(
629 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
629 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
630 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
630 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
631 to force a direct exit without any confirmation.
631 to force a direct exit without any confirmation.
632 """,
632 """,
633 """Don't prompt the user when exiting. This will terminate the kernel
633 """Don't prompt the user when exiting. This will terminate the kernel
634 if it is owned by the frontend, and leave it alive if it is external.
634 if it is owned by the frontend, and leave it alive if it is external.
635 """
635 """
636 ))
636 ))
637 flags.update(qt_flags)
637 flags.update(qt_flags)
638
638
639 aliases = dict(ipkernel_aliases)
639 aliases = dict(ipkernel_aliases)
640
640
641 qt_aliases = dict(
641 qt_aliases = dict(
642 hb = 'IPythonQtConsoleApp.hb_port',
642 hb = 'IPythonQtConsoleApp.hb_port',
643 shell = 'IPythonQtConsoleApp.shell_port',
643 shell = 'IPythonQtConsoleApp.shell_port',
644 iopub = 'IPythonQtConsoleApp.iopub_port',
644 iopub = 'IPythonQtConsoleApp.iopub_port',
645 stdin = 'IPythonQtConsoleApp.stdin_port',
645 stdin = 'IPythonQtConsoleApp.stdin_port',
646 ip = 'IPythonQtConsoleApp.ip',
646 ip = 'IPythonQtConsoleApp.ip',
647 existing = 'IPythonQtConsoleApp.existing',
647 existing = 'IPythonQtConsoleApp.existing',
648 f = 'IPythonQtConsoleApp.connection_file',
648 f = 'IPythonQtConsoleApp.connection_file',
649
649
650 style = 'IPythonWidget.syntax_style',
650 style = 'IPythonWidget.syntax_style',
651 stylesheet = 'IPythonQtConsoleApp.stylesheet',
651 stylesheet = 'IPythonQtConsoleApp.stylesheet',
652 colors = 'ZMQInteractiveShell.colors',
652 colors = 'ZMQInteractiveShell.colors',
653
653
654 editor = 'IPythonWidget.editor',
654 editor = 'IPythonWidget.editor',
655 paging = 'ConsoleWidget.paging',
655 paging = 'ConsoleWidget.paging',
656 ssh = 'IPythonQtConsoleApp.sshserver',
656 ssh = 'IPythonQtConsoleApp.sshserver',
657 )
657 )
658 aliases.update(qt_aliases)
658 aliases.update(qt_aliases)
659
659
660
660
661 #-----------------------------------------------------------------------------
661 #-----------------------------------------------------------------------------
662 # IPythonQtConsole
662 # IPythonQtConsole
663 #-----------------------------------------------------------------------------
663 #-----------------------------------------------------------------------------
664
664
665
665
666 class IPythonQtConsoleApp(BaseIPythonApplication):
666 class IPythonQtConsoleApp(BaseIPythonApplication):
667 name = 'ipython-qtconsole'
667 name = 'ipython-qtconsole'
668 default_config_file_name='ipython_config.py'
668 default_config_file_name='ipython_config.py'
669
669
670 description = """
670 description = """
671 The IPython QtConsole.
671 The IPython QtConsole.
672
672
673 This launches a Console-style application using Qt. It is not a full
673 This launches a Console-style application using Qt. It is not a full
674 console, in that launched terminal subprocesses will not be able to accept
674 console, in that launched terminal subprocesses will not be able to accept
675 input.
675 input.
676
676
677 The QtConsole supports various extra features beyond the Terminal IPython
677 The QtConsole supports various extra features beyond the Terminal IPython
678 shell, such as inline plotting with matplotlib, via:
678 shell, such as inline plotting with matplotlib, via:
679
679
680 ipython qtconsole --pylab=inline
680 ipython qtconsole --pylab=inline
681
681
682 as well as saving your session as HTML, and printing the output.
682 as well as saving your session as HTML, and printing the output.
683
683
684 """
684 """
685 examples = _examples
685 examples = _examples
686
686
687 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
687 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
688 flags = Dict(flags)
688 flags = Dict(flags)
689 aliases = Dict(aliases)
689 aliases = Dict(aliases)
690
690
691 kernel_argv = List(Unicode)
691 kernel_argv = List(Unicode)
692
692
693 # create requested profiles by default, if they don't exist:
693 # create requested profiles by default, if they don't exist:
694 auto_create = CBool(True)
694 auto_create = CBool(True)
695 # connection info:
695 # connection info:
696 ip = Unicode(LOCALHOST, config=True,
696 ip = Unicode(LOCALHOST, config=True,
697 help="""Set the kernel\'s IP address [default localhost].
697 help="""Set the kernel\'s IP address [default localhost].
698 If the IP address is something other than localhost, then
698 If the IP address is something other than localhost, then
699 Consoles on other machines will be able to connect
699 Consoles on other machines will be able to connect
700 to the Kernel, so be careful!"""
700 to the Kernel, so be careful!"""
701 )
701 )
702
702
703 sshserver = Unicode('', config=True,
703 sshserver = Unicode('', config=True,
704 help="""The SSH server to use to connect to the kernel.""")
704 help="""The SSH server to use to connect to the kernel.""")
705 sshkey = Unicode('', config=True,
705 sshkey = Unicode('', config=True,
706 help="""Path to the ssh key to use for logging in to the ssh server.""")
706 help="""Path to the ssh key to use for logging in to the ssh server.""")
707
707
708 hb_port = Int(0, config=True,
708 hb_port = Int(0, config=True,
709 help="set the heartbeat port [default: random]")
709 help="set the heartbeat port [default: random]")
710 shell_port = Int(0, config=True,
710 shell_port = Int(0, config=True,
711 help="set the shell (XREP) port [default: random]")
711 help="set the shell (XREP) port [default: random]")
712 iopub_port = Int(0, config=True,
712 iopub_port = Int(0, config=True,
713 help="set the iopub (PUB) port [default: random]")
713 help="set the iopub (PUB) port [default: random]")
714 stdin_port = Int(0, config=True,
714 stdin_port = Int(0, config=True,
715 help="set the stdin (XREQ) port [default: random]")
715 help="set the stdin (XREQ) port [default: random]")
716 connection_file = Unicode('', config=True,
716 connection_file = Unicode('', config=True,
717 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
717 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
718
718
719 This file will contain the IP, ports, and authentication key needed to connect
719 This file will contain the IP, ports, and authentication key needed to connect
720 clients to this kernel. By default, this file will be created in the security-dir
720 clients to this kernel. By default, this file will be created in the security-dir
721 of the current profile, but can be specified by absolute path.
721 of the current profile, but can be specified by absolute path.
722 """)
722 """)
723 def _connection_file_default(self):
723 def _connection_file_default(self):
724 return 'kernel-%i.json' % os.getpid()
724 return 'kernel-%i.json' % os.getpid()
725
725
726 existing = Unicode('', config=True,
726 existing = Unicode('', config=True,
727 help="""Connect to an already running kernel""")
727 help="""Connect to an already running kernel""")
728
728
729 stylesheet = Unicode('', config=True,
729 stylesheet = Unicode('', config=True,
730 help="path to a custom CSS stylesheet")
730 help="path to a custom CSS stylesheet")
731
731
732 pure = CBool(False, config=True,
732 pure = CBool(False, config=True,
733 help="Use a pure Python kernel instead of an IPython kernel.")
733 help="Use a pure Python kernel instead of an IPython kernel.")
734 plain = CBool(False, config=True,
734 plain = CBool(False, config=True,
735 help="Use a plaintext widget instead of rich text (plain can't print/save).")
735 help="Use a plaintext widget instead of rich text (plain can't print/save).")
736
736
737 def _pure_changed(self, name, old, new):
737 def _pure_changed(self, name, old, new):
738 kind = 'plain' if self.plain else 'rich'
738 kind = 'plain' if self.plain else 'rich'
739 self.config.ConsoleWidget.kind = kind
739 self.config.ConsoleWidget.kind = kind
740 if self.pure:
740 if self.pure:
741 self.widget_factory = FrontendWidget
741 self.widget_factory = FrontendWidget
742 elif self.plain:
742 elif self.plain:
743 self.widget_factory = IPythonWidget
743 self.widget_factory = IPythonWidget
744 else:
744 else:
745 self.widget_factory = RichIPythonWidget
745 self.widget_factory = RichIPythonWidget
746
746
747 _plain_changed = _pure_changed
747 _plain_changed = _pure_changed
748
748
749 confirm_exit = CBool(True, config=True,
749 confirm_exit = CBool(True, config=True,
750 help="""
750 help="""
751 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
751 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
752 to force a direct exit without any confirmation.""",
752 to force a direct exit without any confirmation.""",
753 )
753 )
754
754
755 # the factory for creating a widget
755 # the factory for creating a widget
756 widget_factory = Any(RichIPythonWidget)
756 widget_factory = Any(RichIPythonWidget)
757
757
758 def parse_command_line(self, argv=None):
758 def parse_command_line(self, argv=None):
759 super(IPythonQtConsoleApp, self).parse_command_line(argv)
759 super(IPythonQtConsoleApp, self).parse_command_line(argv)
760 if argv is None:
760 if argv is None:
761 argv = sys.argv[1:]
761 argv = sys.argv[1:]
762
762
763 self.kernel_argv = list(argv) # copy
763 self.kernel_argv = list(argv) # copy
764 # kernel should inherit default config file from frontend
764 # kernel should inherit default config file from frontend
765 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
765 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
766 # Scrub frontend-specific flags
766 # Scrub frontend-specific flags
767 for a in argv:
767 for a in argv:
768 if a.startswith('-') and a.lstrip('-') in qt_flags:
768 if a.startswith('-') and a.lstrip('-') in qt_flags:
769 self.kernel_argv.remove(a)
769 self.kernel_argv.remove(a)
770 swallow_next = False
770 swallow_next = False
771 for a in argv:
771 for a in argv:
772 if swallow_next:
772 if swallow_next:
773 self.kernel_argv.remove(a)
773 self.kernel_argv.remove(a)
774 swallow_next = False
774 swallow_next = False
775 continue
775 continue
776 if a.startswith('-'):
776 if a.startswith('-'):
777 split = a.lstrip('-').split('=')
777 split = a.lstrip('-').split('=')
778 alias = split[0]
778 alias = split[0]
779 if alias in qt_aliases:
779 if alias in qt_aliases:
780 self.kernel_argv.remove(a)
780 self.kernel_argv.remove(a)
781 if len(split) == 1:
781 if len(split) == 1:
782 # alias passed with arg via space
782 # alias passed with arg via space
783 swallow_next = True
783 swallow_next = True
784
784
785 def init_connection_file(self):
785 def init_connection_file(self):
786 """find the connection file, and load the info if found.
786 """find the connection file, and load the info if found.
787
787
788 The current working directory and the current profile's security
788 The current working directory and the current profile's security
789 directory will be searched for the file if it is not given by
789 directory will be searched for the file if it is not given by
790 absolute path.
790 absolute path.
791
791
792 When attempting to connect to an existing kernel and the `--existing`
792 When attempting to connect to an existing kernel and the `--existing`
793 argument does not match an existing file, it will be interpreted as a
793 argument does not match an existing file, it will be interpreted as a
794 fileglob, and the matching file in the current profile's security dir
794 fileglob, and the matching file in the current profile's security dir
795 with the latest access time will be used.
795 with the latest access time will be used.
796 """
796 """
797 if self.existing:
797 if self.existing:
798 try:
798 try:
799 cf = find_connection_file(self.existing)
799 cf = find_connection_file(self.existing)
800 except Exception:
800 except Exception:
801 self.log.critical("Could not find existing kernel connection file %s", self.existing)
801 self.log.critical("Could not find existing kernel connection file %s", self.existing)
802 self.exit(1)
802 self.exit(1)
803 self.log.info("Connecting to existing kernel: %s" % cf)
803 self.log.info("Connecting to existing kernel: %s" % cf)
804 self.connection_file = cf
804 self.connection_file = cf
805 # should load_connection_file only be used for existing?
805 # should load_connection_file only be used for existing?
806 # as it is now, this allows reusing ports if an existing
806 # as it is now, this allows reusing ports if an existing
807 # file is requested
807 # file is requested
808 try:
808 try:
809 self.load_connection_file()
809 self.load_connection_file()
810 except Exception:
810 except Exception:
811 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
811 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
812 self.exit(1)
812 self.exit(1)
813
813
814 def load_connection_file(self):
814 def load_connection_file(self):
815 """load ip/port/hmac config from JSON connection file"""
815 """load ip/port/hmac config from JSON connection file"""
816 # this is identical to KernelApp.load_connection_file
816 # this is identical to KernelApp.load_connection_file
817 # perhaps it can be centralized somewhere?
817 # perhaps it can be centralized somewhere?
818 try:
818 try:
819 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
819 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
820 except IOError:
820 except IOError:
821 self.log.debug("Connection File not found: %s", self.connection_file)
821 self.log.debug("Connection File not found: %s", self.connection_file)
822 return
822 return
823 self.log.debug(u"Loading connection file %s", fname)
823 self.log.debug(u"Loading connection file %s", fname)
824 with open(fname) as f:
824 with open(fname) as f:
825 s = f.read()
825 s = f.read()
826 cfg = json.loads(s)
826 cfg = json.loads(s)
827 if self.ip == LOCALHOST and 'ip' in cfg:
827 if self.ip == LOCALHOST and 'ip' in cfg:
828 # not overridden by config or cl_args
828 # not overridden by config or cl_args
829 self.ip = cfg['ip']
829 self.ip = cfg['ip']
830 for channel in ('hb', 'shell', 'iopub', 'stdin'):
830 for channel in ('hb', 'shell', 'iopub', 'stdin'):
831 name = channel + '_port'
831 name = channel + '_port'
832 if getattr(self, name) == 0 and name in cfg:
832 if getattr(self, name) == 0 and name in cfg:
833 # not overridden by config or cl_args
833 # not overridden by config or cl_args
834 setattr(self, name, cfg[name])
834 setattr(self, name, cfg[name])
835 if 'key' in cfg:
835 if 'key' in cfg:
836 self.config.Session.key = str_to_bytes(cfg['key'])
836 self.config.Session.key = str_to_bytes(cfg['key'])
837
837
838 def init_ssh(self):
838 def init_ssh(self):
839 """set up ssh tunnels, if needed."""
839 """set up ssh tunnels, if needed."""
840 if not self.sshserver and not self.sshkey:
840 if not self.sshserver and not self.sshkey:
841 return
841 return
842
842
843 if self.sshkey and not self.sshserver:
843 if self.sshkey and not self.sshserver:
844 # specifying just the key implies that we are connecting directly
844 # specifying just the key implies that we are connecting directly
845 self.sshserver = self.ip
845 self.sshserver = self.ip
846 self.ip = LOCALHOST
846 self.ip = LOCALHOST
847
847
848 # build connection dict for tunnels:
848 # build connection dict for tunnels:
849 info = dict(ip=self.ip,
849 info = dict(ip=self.ip,
850 shell_port=self.shell_port,
850 shell_port=self.shell_port,
851 iopub_port=self.iopub_port,
851 iopub_port=self.iopub_port,
852 stdin_port=self.stdin_port,
852 stdin_port=self.stdin_port,
853 hb_port=self.hb_port
853 hb_port=self.hb_port
854 )
854 )
855
855
856 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
856 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
857
857
858 # tunnels return a new set of ports, which will be on localhost:
858 # tunnels return a new set of ports, which will be on localhost:
859 self.ip = LOCALHOST
859 self.ip = LOCALHOST
860 try:
860 try:
861 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
861 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
862 except:
862 except:
863 # even catch KeyboardInterrupt
863 # even catch KeyboardInterrupt
864 self.log.error("Could not setup tunnels", exc_info=True)
864 self.log.error("Could not setup tunnels", exc_info=True)
865 self.exit(1)
865 self.exit(1)
866
866
867 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
867 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
868
868
869 cf = self.connection_file
869 cf = self.connection_file
870 base,ext = os.path.splitext(cf)
870 base,ext = os.path.splitext(cf)
871 base = os.path.basename(base)
871 base = os.path.basename(base)
872 self.connection_file = os.path.basename(base)+'-ssh'+ext
872 self.connection_file = os.path.basename(base)+'-ssh'+ext
873 self.log.critical("To connect another client via this tunnel, use:")
873 self.log.critical("To connect another client via this tunnel, use:")
874 self.log.critical("--existing %s" % self.connection_file)
874 self.log.critical("--existing %s" % self.connection_file)
875
875
876 def _new_connection_file(self):
876 def _new_connection_file(self):
877 return os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % uuid.uuid4())
877 return os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % uuid.uuid4())
878
878
879 def init_kernel_manager(self):
879 def init_kernel_manager(self):
880 # Don't let Qt or ZMQ swallow KeyboardInterupts.
880 # Don't let Qt or ZMQ swallow KeyboardInterupts.
881 signal.signal(signal.SIGINT, signal.SIG_DFL)
881 signal.signal(signal.SIGINT, signal.SIG_DFL)
882 sec = self.profile_dir.security_dir
882 sec = self.profile_dir.security_dir
883 try:
883 try:
884 cf = filefind(self.connection_file, ['.', sec])
884 cf = filefind(self.connection_file, ['.', sec])
885 except IOError:
885 except IOError:
886 # file might not exist
886 # file might not exist
887 if self.connection_file == os.path.basename(self.connection_file):
887 if self.connection_file == os.path.basename(self.connection_file):
888 # just shortname, put it in security dir
888 # just shortname, put it in security dir
889 cf = os.path.join(sec, self.connection_file)
889 cf = os.path.join(sec, self.connection_file)
890 else:
890 else:
891 cf = self.connection_file
891 cf = self.connection_file
892
892
893 # Create a KernelManager and start a kernel.
893 # Create a KernelManager and start a kernel.
894 self.kernel_manager = QtKernelManager(
894 self.kernel_manager = QtKernelManager(
895 ip=self.ip,
895 ip=self.ip,
896 shell_port=self.shell_port,
896 shell_port=self.shell_port,
897 iopub_port=self.iopub_port,
897 iopub_port=self.iopub_port,
898 stdin_port=self.stdin_port,
898 stdin_port=self.stdin_port,
899 hb_port=self.hb_port,
899 hb_port=self.hb_port,
900 connection_file=cf,
900 connection_file=cf,
901 config=self.config,
901 config=self.config,
902 )
902 )
903 # start the kernel
903 # start the kernel
904 if not self.existing:
904 if not self.existing:
905 kwargs = dict(ipython=not self.pure)
905 kwargs = dict(ipython=not self.pure)
906 kwargs['extra_arguments'] = self.kernel_argv
906 kwargs['extra_arguments'] = self.kernel_argv
907 self.kernel_manager.start_kernel(**kwargs)
907 self.kernel_manager.start_kernel(**kwargs)
908 elif self.sshserver:
908 elif self.sshserver:
909 # ssh, write new connection file
909 # ssh, write new connection file
910 self.kernel_manager.write_connection_file()
910 self.kernel_manager.write_connection_file()
911 self.kernel_manager.start_channels()
911 self.kernel_manager.start_channels()
912
912
913 def create_tab_with_new_frontend(self):
913 def create_tab_with_new_frontend(self):
914 """ Create new tab attached to new kernel, launched on localhost.
914 """ Create new tab attached to new kernel, launched on localhost.
915 """
915 """
916 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
916 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
917 kernel_manager = QtKernelManager(
917 kernel_manager = QtKernelManager(
918 ip=ip,
918 ip=ip,
919 connection_file=self._new_connection_file(),
919 connection_file=self._new_connection_file(),
920 config=self.config,
920 config=self.config,
921 )
921 )
922 # start the kernel
922 # start the kernel
923 kwargs = dict(ipython=not self.pure)
923 kwargs = dict(ipython=not self.pure)
924 kwargs['extra_arguments'] = self.kernel_argv
924 kwargs['extra_arguments'] = self.kernel_argv
925 kernel_manager.start_kernel(**kwargs)
925 kernel_manager.start_kernel(**kwargs)
926 kernel_manager.start_channels()
926 kernel_manager.start_channels()
927 widget = self.widget_factory(config=self.config,
927 widget = self.widget_factory(config=self.config,
928 local_kernel=True)
928 local_kernel=True)
929 widget.kernel_manager = kernel_manager
929 widget.kernel_manager = kernel_manager
930 widget._existing=False;
930 widget._existing=False;
931 widget._confirm_exit=True;
931 widget._confirm_exit=True;
932 widget._may_close=True;
932 widget._may_close=True;
933 self.window.add_tab_with_frontend(widget)
933 self.window.add_tab_with_frontend(widget)
934
934
935 def create_tab_attached_to_current_tab_kernel(self):
935 def create_tab_attached_to_current_tab_kernel(self):
936 current_widget = self.window.tab_widget.currentWidget()
936 current_widget = self.window.tab_widget.currentWidget()
937 current_widget_index = self.window.tab_widget.indexOf(current_widget)
937 current_widget_index = self.window.tab_widget.indexOf(current_widget)
938 current_widget.kernel_manager = current_widget.kernel_manager;
938 current_widget.kernel_manager = current_widget.kernel_manager;
939 current_widget_name = self.window.tab_widget.tabText(current_widget_index);
939 current_widget_name = self.window.tab_widget.tabText(current_widget_index);
940 kernel_manager = QtKernelManager(
940 kernel_manager = QtKernelManager(
941 connection_file=current_widget.kernel_manager.connection_file,
941 connection_file=current_widget.kernel_manager.connection_file,
942 config = self.config,
942 config = self.config,
943 )
943 )
944 kernel_manager.load_connection_file()
944 kernel_manager.load_connection_file()
945 kernel_manager.start_channels()
945 kernel_manager.start_channels()
946 widget = self.widget_factory(config=self.config,
946 widget = self.widget_factory(config=self.config,
947 local_kernel=False)
947 local_kernel=False)
948 widget._confirm_exit=True;
948 widget._confirm_exit=True;
949 widget._may_close=False;
949 widget._may_close=False;
950 widget.kernel_manager = kernel_manager
950 widget.kernel_manager = kernel_manager
951 self.window.add_tab_with_frontend(widget,name=str('('+current_widget_name+') slave'))
951 self.window.add_tab_with_frontend(widget,name=str('('+current_widget_name+') slave'))
952
952
953 def init_qt_elements(self):
953 def init_qt_elements(self):
954 # Create the widget.
954 # Create the widget.
955 self.app = QtGui.QApplication([])
955 self.app = QtGui.QApplication([])
956
956
957 base_path = os.path.abspath(os.path.dirname(__file__))
957 base_path = os.path.abspath(os.path.dirname(__file__))
958 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
958 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
959 self.app.icon = QtGui.QIcon(icon_path)
959 self.app.icon = QtGui.QIcon(icon_path)
960 QtGui.QApplication.setWindowIcon(self.app.icon)
960 QtGui.QApplication.setWindowIcon(self.app.icon)
961
961
962 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
962 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
963 self.widget = self.widget_factory(config=self.config,
963 self.widget = self.widget_factory(config=self.config,
964 local_kernel=local_kernel)
964 local_kernel=local_kernel)
965 self.widget._existing = self.existing;
965 self.widget._existing = self.existing;
966 self.widget._may_close = not self.existing;
966 self.widget._may_close = not self.existing;
967 self.widget._confirm_exit = not self.existing;
967 self.widget._confirm_exit = not self.existing;
968
968
969 self.widget.kernel_manager = self.kernel_manager
969 self.widget.kernel_manager = self.kernel_manager
970 self.window = MainWindow(self.app, self.widget, self.existing,
970 self.window = MainWindow(self.app, self.widget, self.existing,
971 may_close=local_kernel,
971 may_close=local_kernel,
972 confirm_exit=self.confirm_exit)
972 confirm_exit=self.confirm_exit)
973 self.window.log = self.log
973 self.window.log = self.log
974 self.window.add_tab_with_frontend(self.widget)
974 self.window.add_tab_with_frontend(self.widget)
975 self.window.init_menu_bar()
975 self.window.init_menu_bar()
976 self.window.setWindowTitle('Python' if self.pure else 'IPython')
976 self.window.setWindowTitle('Python' if self.pure else 'IPython')
977
977
978 def init_colors(self):
978 def init_colors(self):
979 """Configure the coloring of the widget"""
979 """Configure the coloring of the widget"""
980 # Note: This will be dramatically simplified when colors
980 # Note: This will be dramatically simplified when colors
981 # are removed from the backend.
981 # are removed from the backend.
982
982
983 if self.pure:
983 if self.pure:
984 # only IPythonWidget supports styling
984 # only IPythonWidget supports styling
985 return
985 return
986
986
987 # parse the colors arg down to current known labels
987 # parse the colors arg down to current known labels
988 try:
988 try:
989 colors = self.config.ZMQInteractiveShell.colors
989 colors = self.config.ZMQInteractiveShell.colors
990 except AttributeError:
990 except AttributeError:
991 colors = None
991 colors = None
992 try:
992 try:
993 style = self.config.IPythonWidget.colors
993 style = self.config.IPythonWidget.colors
994 except AttributeError:
994 except AttributeError:
995 style = None
995 style = None
996
996
997 # find the value for colors:
997 # find the value for colors:
998 if colors:
998 if colors:
999 colors=colors.lower()
999 colors=colors.lower()
1000 if colors in ('lightbg', 'light'):
1000 if colors in ('lightbg', 'light'):
1001 colors='lightbg'
1001 colors='lightbg'
1002 elif colors in ('dark', 'linux'):
1002 elif colors in ('dark', 'linux'):
1003 colors='linux'
1003 colors='linux'
1004 else:
1004 else:
1005 colors='nocolor'
1005 colors='nocolor'
1006 elif style:
1006 elif style:
1007 if style=='bw':
1007 if style=='bw':
1008 colors='nocolor'
1008 colors='nocolor'
1009 elif styles.dark_style(style):
1009 elif styles.dark_style(style):
1010 colors='linux'
1010 colors='linux'
1011 else:
1011 else:
1012 colors='lightbg'
1012 colors='lightbg'
1013 else:
1013 else:
1014 colors=None
1014 colors=None
1015
1015
1016 # Configure the style.
1016 # Configure the style.
1017 widget = self.widget
1017 widget = self.widget
1018 if style:
1018 if style:
1019 widget.style_sheet = styles.sheet_from_template(style, colors)
1019 widget.style_sheet = styles.sheet_from_template(style, colors)
1020 widget.syntax_style = style
1020 widget.syntax_style = style
1021 widget._syntax_style_changed()
1021 widget._syntax_style_changed()
1022 widget._style_sheet_changed()
1022 widget._style_sheet_changed()
1023 elif colors:
1023 elif colors:
1024 # use a default style
1024 # use a default style
1025 widget.set_default_style(colors=colors)
1025 widget.set_default_style(colors=colors)
1026 else:
1026 else:
1027 # this is redundant for now, but allows the widget's
1027 # this is redundant for now, but allows the widget's
1028 # defaults to change
1028 # defaults to change
1029 widget.set_default_style()
1029 widget.set_default_style()
1030
1030
1031 if self.stylesheet:
1031 if self.stylesheet:
1032 # we got an expicit stylesheet
1032 # we got an expicit stylesheet
1033 if os.path.isfile(self.stylesheet):
1033 if os.path.isfile(self.stylesheet):
1034 with open(self.stylesheet) as f:
1034 with open(self.stylesheet) as f:
1035 sheet = f.read()
1035 sheet = f.read()
1036 widget.style_sheet = sheet
1036 widget.style_sheet = sheet
1037 widget._style_sheet_changed()
1037 widget._style_sheet_changed()
1038 else:
1038 else:
1039 raise IOError("Stylesheet %r not found."%self.stylesheet)
1039 raise IOError("Stylesheet %r not found."%self.stylesheet)
1040
1040
1041 def initialize(self, argv=None):
1041 def initialize(self, argv=None):
1042 super(IPythonQtConsoleApp, self).initialize(argv)
1042 super(IPythonQtConsoleApp, self).initialize(argv)
1043 self.init_connection_file()
1043 self.init_connection_file()
1044 default_secure(self.config)
1044 default_secure(self.config)
1045 self.init_ssh()
1045 self.init_ssh()
1046 self.init_kernel_manager()
1046 self.init_kernel_manager()
1047 self.init_qt_elements()
1047 self.init_qt_elements()
1048 self.init_colors()
1048 self.init_colors()
1049 self.init_window_shortcut()
1049 self.init_window_shortcut()
1050
1050
1051 def init_window_shortcut(self):
1051 def init_window_shortcut(self):
1052
1052
1053 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
1053 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
1054 self.window,
1054 self.window,
1055 shortcut="Ctrl+PgDown",
1055 shortcut="Ctrl+PgDown",
1056 statusTip="Cahange to next tab",
1056 statusTip="Cahange to next tab",
1057 triggered=self.window.prev_tab)
1057 triggered=self.window.prev_tab)
1058
1058
1059 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
1059 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
1060 self.window,
1060 self.window,
1061 shortcut="Ctrl+PgUp",
1061 shortcut="Ctrl+PgUp",
1062 statusTip="Cahange to next tab",
1062 statusTip="Cahange to next tab",
1063 triggered=self.window.next_tab)
1063 triggered=self.window.next_tab)
1064
1064
1065 self.fullScreenAct = QtGui.QAction("&Full Screen",
1065 self.fullScreenAct = QtGui.QAction("&Full Screen",
1066 self.window,
1066 self.window,
1067 shortcut="Ctrl+Meta+Space",
1067 shortcut="Ctrl+Meta+Space",
1068 statusTip="Toggle between Fullscreen and Normal Size",
1068 statusTip="Toggle between Fullscreen and Normal Size",
1069 triggered=self.toggleFullScreen)
1069 triggered=self.toggleFullScreen)
1070
1070
1071
1071
1072
1072
1073 self.tabAndNewKernelAct =QtGui.QAction("Tab with &New kernel",
1073 self.tabAndNewKernelAct =QtGui.QAction("Tab with &New kernel",
1074 self.window,
1074 self.window,
1075 shortcut="Ctrl+T",
1075 shortcut="Ctrl+T",
1076 triggered=self.create_tab_with_new_frontend)
1076 triggered=self.create_tab_with_new_frontend)
1077 self.window.kernel_menu.addAction(self.tabAndNewKernelAct)
1077 self.window.kernel_menu.addAction(self.tabAndNewKernelAct)
1078
1078
1079 self.tabSameKernalAct =QtGui.QAction("Tab with Sa&me kernel",
1079 self.tabSameKernalAct =QtGui.QAction("Tab with Sa&me kernel",
1080 self.window,
1080 self.window,
1081 shortcut="Ctrl+Shift+T",
1081 shortcut="Ctrl+Shift+T",
1082 triggered=self.create_tab_attached_to_current_tab_kernel)
1082 triggered=self.create_tab_attached_to_current_tab_kernel)
1083 self.window.kernel_menu.addAction(self.tabSameKernalAct)
1083 self.window.kernel_menu.addAction(self.tabSameKernalAct)
1084 self.window.kernel_menu.addSeparator()
1084 self.window.kernel_menu.addSeparator()
1085
1085
1086 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
1086 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
1087 self.window,
1087 self.window,
1088 triggered=self._open_online_help)
1088 triggered=self._open_online_help)
1089 self.window.help_menu.addAction(self.onlineHelpAct)
1089 self.window.help_menu.addAction(self.onlineHelpAct)
1090 # creating shortcut in menubar only for Mac OS as I don't
1090 # creating shortcut in menubar only for Mac OS as I don't
1091 # know the shortcut or if the windows manager assign it in
1091 # know the shortcut or if the windows manager assign it in
1092 # other platform.
1092 # other platform.
1093 if sys.platform == 'darwin':
1093 if sys.platform == 'darwin':
1094 self.minimizeAct = QtGui.QAction("Mini&mize",
1094 self.minimizeAct = QtGui.QAction("Mini&mize",
1095 self.window,
1095 self.window,
1096 shortcut="Ctrl+m",
1096 shortcut="Ctrl+m",
1097 statusTip="Minimize the window/Restore Normal Size",
1097 statusTip="Minimize the window/Restore Normal Size",
1098 triggered=self.toggleMinimized)
1098 triggered=self.toggleMinimized)
1099 self.maximizeAct = QtGui.QAction("Ma&ximize",
1099 self.maximizeAct = QtGui.QAction("Ma&ximize",
1100 self.window,
1100 self.window,
1101 shortcut="Ctrl+Shift+M",
1101 shortcut="Ctrl+Shift+M",
1102 statusTip="Maximize the window/Restore Normal Size",
1102 statusTip="Maximize the window/Restore Normal Size",
1103 triggered=self.toggleMaximized)
1103 triggered=self.toggleMaximized)
1104
1104
1105
1105
1106 self.window_menu = self.window.window_menu
1106 self.window_menu = self.window.window_menu
1107 self.kernel_menu = self.window.kernel_menu
1107 self.kernel_menu = self.window.kernel_menu
1108
1108
1109 self.kernel_menu.addAction(self.next_tab_act)
1109 self.kernel_menu.addAction(self.next_tab_act)
1110 self.kernel_menu.addAction(self.prev_tab_act)
1110 self.kernel_menu.addAction(self.prev_tab_act)
1111 self.window_menu.addSeparator()
1111 self.window_menu.addSeparator()
1112 self.window_menu.addAction(self.minimizeAct)
1112 self.window_menu.addAction(self.minimizeAct)
1113 self.window_menu.addAction(self.maximizeAct)
1113 self.window_menu.addAction(self.maximizeAct)
1114 self.window_menu.addSeparator()
1114 self.window_menu.addSeparator()
1115 self.window_menu.addAction(self.fullScreenAct)
1115 self.window_menu.addAction(self.fullScreenAct)
1116
1116
1117 else:
1117 else:
1118 # if we don't put it in a menu, we add it to the window so
1118 # if we don't put it in a menu, we add it to the window so
1119 # that it can still be triggerd by shortcut
1119 # that it can still be triggerd by shortcut
1120 self.window.addAction(self.fullScreenAct)
1120 self.window.addAction(self.fullScreenAct)
1121
1121
1122 # Don't activate toggleMenubar on mac, doen't work,
1122 # Don't activate toggleMenubar on mac, doen't work,
1123 # as toolbar always here
1123 # as toolbar always here
1124 self.toggle_menu_bar_act = QtGui.QAction("&Toggle Menu Bar",
1124 self.toggle_menu_bar_act = QtGui.QAction("&Toggle Menu Bar",
1125 self.window,
1125 self.window,
1126 shortcut="Ctrl+Meta+H",
1126 shortcut="Ctrl+Meta+H",
1127 statusTip="Toggle menubar betwin visible and not",
1127 statusTip="Toggle menubar betwin visible and not",
1128 triggered=self.toggle_menu_bar)
1128 triggered=self.toggle_menu_bar)
1129 self.window_menu.addAction(self.toggle_menu_bar_act)
1129 self.window.window_menu.addAction(self.toggle_menu_bar_act)
1130
1130
1131 def toggle_menu_bar(self):
1131 def toggle_menu_bar(self):
1132 menu_bar = self.window.menuBar();
1132 menu_bar = self.window.menuBar();
1133 if not menu_bar.isVisible():
1133 if not menu_bar.isVisible():
1134 menu_bar.setVisible(False)
1134 menu_bar.setVisible(False)
1135 else:
1135 else:
1136 menu_bar.setVisible(True)
1136 menu_bar.setVisible(True)
1137
1137
1138 def toggleMinimized(self):
1138 def toggleMinimized(self):
1139 if not self.window.isMinimized():
1139 if not self.window.isMinimized():
1140 self.window.showMinimized()
1140 self.window.showMinimized()
1141 else:
1141 else:
1142 self.window.showNormal()
1142 self.window.showNormal()
1143
1143
1144 def _open_online_help(self):
1144 def _open_online_help(self):
1145 filename="http://ipython.org/ipython-doc/stable/index.html"
1145 filename="http://ipython.org/ipython-doc/stable/index.html"
1146 webbrowser.open(filename, new=1, autoraise=True)
1146 webbrowser.open(filename, new=1, autoraise=True)
1147
1147
1148 def toggleMaximized(self):
1148 def toggleMaximized(self):
1149 if not self.window.isMaximized():
1149 if not self.window.isMaximized():
1150 self.window.showMaximized()
1150 self.window.showMaximized()
1151 else:
1151 else:
1152 self.window.showNormal()
1152 self.window.showNormal()
1153
1153
1154 # Min/Max imizing while in full screen give a bug
1154 # Min/Max imizing while in full screen give a bug
1155 # when going out of full screen, at least on OSX
1155 # when going out of full screen, at least on OSX
1156 def toggleFullScreen(self):
1156 def toggleFullScreen(self):
1157 if not self.window.isFullScreen():
1157 if not self.window.isFullScreen():
1158 self.window.showFullScreen()
1158 self.window.showFullScreen()
1159 if sys.platform == 'darwin':
1159 if sys.platform == 'darwin':
1160 self.maximizeAct.setEnabled(False)
1160 self.maximizeAct.setEnabled(False)
1161 self.minimizeAct.setEnabled(False)
1161 self.minimizeAct.setEnabled(False)
1162 else:
1162 else:
1163 self.window.showNormal()
1163 self.window.showNormal()
1164 if sys.platform == 'darwin':
1164 if sys.platform == 'darwin':
1165 self.maximizeAct.setEnabled(True)
1165 self.maximizeAct.setEnabled(True)
1166 self.minimizeAct.setEnabled(True)
1166 self.minimizeAct.setEnabled(True)
1167
1167
1168 def start(self):
1168 def start(self):
1169
1169
1170 # draw the window
1170 # draw the window
1171 self.window.show()
1171 self.window.show()
1172
1172
1173 # Start the application main loop.
1173 # Start the application main loop.
1174 self.app.exec_()
1174 self.app.exec_()
1175
1175
1176 #-----------------------------------------------------------------------------
1176 #-----------------------------------------------------------------------------
1177 # Main entry point
1177 # Main entry point
1178 #-----------------------------------------------------------------------------
1178 #-----------------------------------------------------------------------------
1179
1179
1180 def main():
1180 def main():
1181 app = IPythonQtConsoleApp()
1181 app = IPythonQtConsoleApp()
1182 app.initialize()
1182 app.initialize()
1183 app.start()
1183 app.start()
1184
1184
1185
1185
1186 if __name__ == '__main__':
1186 if __name__ == '__main__':
1187 main()
1187 main()
General Comments 0
You need to be logged in to leave comments. Login now