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