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