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