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