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