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