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