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