Skip to content

Commit

Permalink
Added property current_widget and method activate
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Fleming committed Sep 4, 2024
1 parent f6a4637 commit 3c8c263
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 23 deletions.
20 changes: 15 additions & 5 deletions ipylab/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
from __future__ import annotations

import uuid
from typing import Generic, TypeVar
from typing import TYPE_CHECKING, Generic, TypeVar

from ipywidgets import register
from traitlets import Bool, Unicode

from ipylab.asyncwidget import AsyncWidgetBase

if TYPE_CHECKING:
from asyncio import Task

from ipylab._compat.typing import Self

T = TypeVar("T", bound="Connection")


Expand Down Expand Up @@ -57,7 +62,7 @@ class Connection(AsyncWidgetBase, Generic[T]):
_connections: dict[str, T] = {} # noqa RUF012
_model_name = Unicode("ConnectionModel").tag(sync=True)
cid = Unicode(read_only=True, help="connection id").tag(sync=True)
id = Unicode(allow_none=True, read_only=True, help="id of the disposable if it has one")
id = Unicode("", read_only=True, help="id of the object if it has one").tag(sync=True)
_dispose = Bool(read_only=True).tag(sync=True)
_basename = None

Expand All @@ -80,7 +85,7 @@ def __init__(self, *, cid: str, model_id=None, id: str | None = None, **kwgs):
if self._async_widget_base_init_complete:
return
self.set_trait("cid", cid)
self.set_trait("id", id)
self.set_trait("id", id or "")
super().__init__(model_id=model_id, **kwgs)

def __str__(self):
Expand Down Expand Up @@ -130,6 +135,11 @@ def get_existing_connection(cls, *name_or_id: str, quiet=False):
class MainAreaConnection(Connection):
CID_PREFIX = "ipylab MainArea"

def activate(self):
def activate(self) -> Task[Self]:
self._check_closed()
return self.app.shell.execute_method("activateById", self.id)

async def activate_():
self.app.shell.execute_method("activateById", self.id)
return self

return self.to_task(activate_())
7 changes: 7 additions & 0 deletions ipylab/jupyterfrontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from traitlets import Dict, Instance, Tuple, Unicode

from ipylab import MainAreaConnection
from ipylab.asyncwidget import AsyncWidgetBase, Transform, pack, pack_code, register, widget_serialization
from ipylab.commands import CommandRegistry
from ipylab.dialog import Dialog, FileDialog
Expand Down Expand Up @@ -38,6 +39,12 @@ class JupyterFrontEnd(AsyncWidgetBase):
shell = Instance(Shell, (), read_only=True)
session_manager = Instance(SessionManager, (), read_only=True)

@property
def current_widget(self):
"""A connection to the current widget in the shell."""
id_ = self.current_widget_id
return MainAreaConnection(cid=MainAreaConnection.to_cid(id_), id=id_)

def _init_python_backend(self):
"Run by the Ipylab python backend."
# This is called in a separate kernel started by the JavaScript frontend
Expand Down
42 changes: 29 additions & 13 deletions src/widgets/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,44 @@ import { ObjectHash } from 'backbone';
import { IpylabModel } from './ipylab';

/**
* Maintain a connection to any object by using a cid.
* Provides a connection from any object reachable here to one or more Python backends.
*
* Typically the object is registered first via the method `registerConnection` with a cid
* In Python The `cid` is passed when creating a new Connection.
*
* The object is set as the base. If the object is disposable, the ConnectionModel will
* also close when the object is disposed.
*/
export class ConnectionModel extends IpylabModel {
async initialize(attributes: ObjectHash, options: any): Promise<void> {
super.initialize(attributes, {
...options,
base: this.getConnection(this.get('cid'))
});
this.base.disposed.connect(() => {
if (!this.get('_dispose')) {
this.close();
}
});
this.listenTo(this, 'change:_dispose', () => this.base.dispose());
let base;
const cid = this.get('cid');
const id = this.get('id');
try {
base = this.getConnection(cid, id);
} catch {}
super.initialize(attributes, { ...options, base });
if (base) {
this.base.disposed.connect(() => this.close());
this.on('change:_dispose', this.dispose, this);
} else {
console.log(
`Failed to get connection for cid='${cid}' id='${id}' so closing...`
);
this.close();
}
}

close(comm_closed?: boolean): Promise<void> {
if (this.base?.ipylabDisposeOnClose) {
delete this.base.ipylabDisposeOnClose;
this.base.dispose();
this.dispose();
}
return super.close(comm_closed);
return super.close((comm_closed || this.get('_dispose')) ?? false);
}

dispose() {
this.base?.dispose();
}

/**
Expand Down
9 changes: 4 additions & 5 deletions src/widgets/ipylab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@ export class IpylabModel extends WidgetModel {
initialize(attributes: ObjectHash, options: any): void {
super.initialize(attributes, options);
this.set('kernelId', this.kernelId);
this.on('msg:custom', this._onCustomMessage.bind(this));
this.on('msg:custom', this._onCustomMessage, this);
this.save_changes();
const msg = `ipylab ${this.get('_model_name')} ready for operations`;
this.send({ init: msg });
onKernelLost(this.kernel, this.close, this);
if (typeof options.base === 'object') {
this._base = options.base;
delete options.base;
} else {
const basename = this.get('_basename');
this._base = basename
Expand Down Expand Up @@ -240,7 +239,7 @@ export class IpylabModel extends WidgetModel {
if (value.slice(0, 10) === 'IPY_MODEL_') {
const model = await unpack_models(value, this.widget_manager);
if (model.model_name === IpylabModel.connection_model_name) {
const widget = this.getConnection(model.cid);
const widget = this.getConnection(model.get('cid'), model.get('id'));
if (!(widget instanceof Widget)) {
throw new Error(`Failed to get a lumio widget for: ${value}`);
}
Expand Down Expand Up @@ -390,11 +389,11 @@ export class IpylabModel extends WidgetModel {
* @param cid Get an object that has been registered as a connection.
* @returns
*/
getConnection(cid: string): any {
getConnection(cid: string, id: string | null = null): any {
if (Private.connection.has(cid)) {
return Private.connection.get(cid);
}
const obj = this._getLuminoWidgetFromShell(cid);
const obj = this._getLuminoWidgetFromShell(id || cid);
IpylabModel.registerConnection(obj, cid);
return obj;
}
Expand Down

0 comments on commit 3c8c263

Please sign in to comment.