1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
#------------------------------------------------------------------------------
# Copyright (c) 2014-2024,, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
from atom.api import Typed
from enaml.widgets.ipython_console import ProxyIPythonConsole
try:
#: IPython >= 4.0
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
except ImportError as e:
try:
from IPython.qt.console.rich_ipython_widget\
import RichIPythonWidget as RichJupyterWidget
from IPython.qt.inprocess import QtInProcessKernelManager
except ImportError as e2:
msg = ('qtconsole is required to use the IPythonConsole with '
'IPython>=4.0.\nLoading qtconsole failed with: {}\nLoading '
'from IPython < 4.0 failed with: {}')
raise ImportError(msg.format(e, e2))
from .QtWidgets import QFrame, QVBoxLayout
from . import focus_registry
from .q_deferred_caller import deferredCall
from .qt_control import QtControl
class QtIPythonConsole(QtControl, ProxyIPythonConsole):
""" A Qt4 implementation of an Enaml IPythonConsole.
"""
#: The wrapper widget created by the proxy. A wrapper is necessary
#: since the IPython widget overrides critical Qt API methods which
#: renders the widget incompatible with the ancestor hierarchy.
widget = Typed(QFrame)
#: The internal IPython console widget.
ipy_widget = Typed(RichJupyterWidget)
#--------------------------------------------------------------------------
# Lifecycle API
#--------------------------------------------------------------------------
def create_widget(self):
""" Create the underlying widget.
"""
self.widget = QFrame(self.parent_widget())
self.ipy_widget = RichJupyterWidget()
assert self.page_control is not None # always use paging
def init_widget(self):
""" Initialize the underlying widget.
"""
super(QtIPythonConsole, self).init_widget()
self._setup_kernel()
focus_registry.register(self.text_control, self)
focus_registry.register(self.page_control, self)
self.update_ns(self.declaration.initial_ns)
self.ipy_widget.exit_requested.connect(self._on_exit_requested)
def init_layout(self):
""" Initialize the underlying widget layout.
"""
super(QtIPythonConsole, self).init_layout()
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.ipy_widget)
self.widget.setLayout(layout)
def destroy(self):
""" Destroy the underlying widget.
"""
self._teardown_kernel()
focus_registry.unregister(self.text_control)
focus_registry.unregister(self.page_control)
super(QtIPythonConsole, self).destroy()
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _setup_kernel(self):
""" Setup the kernel for the widget.
"""
kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel(show_banner=False)
kernel = kernel_manager.kernel
kernel.gui = 'qt'
kernel_client = kernel_manager.client()
kernel_client.start_channels()
ipy_widget = self.ipy_widget
ipy_widget.kernel_manager = kernel_manager
ipy_widget.kernel_client = kernel_client
def _teardown_kernel(self):
""" Teardown the kernel for the widget.
"""
ipy_widget = self.ipy_widget
ipy_widget.kernel_client.stop_channels()
ipy_widget.kernel_manager.shutdown_kernel()
def _on_exit_requested(self, obj):
""" Handle the 'exit_requested' signal on the widget.
"""
deferredCall(self.declaration.exit_requested)
#--------------------------------------------------------------------------
# Protected API
#--------------------------------------------------------------------------
def focus_target(self):
""" Returns the current focus target for the widget.
"""
page = self.page_control
if page.isVisibleTo(self.widget):
return page
return self.text_control
def hook_focus_events(self):
""" Hook the focus events for the underlyling widget.
"""
text = self.text_control
text.focusInEvent = self.textFocusInEvent
text.focusOutEvent = self.textFocusOutEvent
page = self.page_control
page.focusInEvent = self.pageFocusInEvent
page.focusOutEvent = self.pageFocusOutEvent
def unhook_focus_events(self):
""" Unhook the focus events for the underling widget.
"""
text = self.text_control
del text.focusInEvent
del text.focusOutEvent
page = self.page_control
del page.focusInEvent
del page.focusOutEvent
def textFocusInEvent(self, event):
""" Handle the focusInEvent for the text widget.
"""
self.handleFocusInEvent(self.text_control, event)
def textFocusOutEvent(self, event):
""" Handle the focusOutEvent for the text widget.
"""
self.handleFocusOutEvent(self.text_control, event)
def pageFocusInEvent(self, event):
""" Handle the focusInEvent for the page widget.
"""
self.handleFocusInEvent(self.page_control, event)
def pageFocusOutEvent(self, event):
""" Handle the focusOutEvent for the page widget.
"""
self.handleFocusOutEvent(self.page_control, event)
def handleFocusInEvent(self, widget, event):
""" Handle the focusInEvent for the given widget.
"""
type(widget).focusInEvent(widget, event)
self.declaration.focus_gained()
def handleFocusOutEvent(self, widget, event):
""" Handle the focusOutEvent for the given widget.
"""
type(widget).focusOutEvent(widget, event)
self.declaration.focus_lost()
#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
@property
def text_control(self):
""" Return the text control for the IPython widget.
Returns
-------
result : QTextEdit
The text control for the IPython widget.
"""
return self.ipy_widget._control
@property
def page_control(self):
""" Return the page control for the IPython widget.
Returns
-------
result : QTextEdit
The page control for the IPython widget.
"""
return self.ipy_widget._page_control
#--------------------------------------------------------------------------
# ProxyIPythonConsole API
#--------------------------------------------------------------------------
def get_var(self, name, default):
""" Get a variable from the console namespace.
"""
kernel = self.ipy_widget.kernel_manager.kernel
return kernel.shell.user_ns.get(name, default)
def update_ns(self, ns):
""" Update the namespace of the underlying console.
"""
if len(ns) > 0:
kernel = self.ipy_widget.kernel_manager.kernel
kernel.shell.push(ns)
|