Spaces:
Running
Running
Merge pull request #135 from biggraph/darabos-monaco
Browse files
examples/matplotlib_example.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# From https://matplotlib.org/stable/gallery/images_contours_and_fields/contour_corner_mask.html
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
import numpy as np
|
| 4 |
+
from lynxkite.core.ops import op
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@op("LynxKite Graph Analytics", "Matplotlib example", view="matplotlib")
|
| 8 |
+
def example():
|
| 9 |
+
# Data to plot.
|
| 10 |
+
x, y = np.meshgrid(np.arange(7), np.arange(10))
|
| 11 |
+
z = np.sin(0.5 * x) * np.cos(0.52 * y)
|
| 12 |
+
|
| 13 |
+
# Mask various z values.
|
| 14 |
+
mask = np.zeros_like(z, dtype=bool)
|
| 15 |
+
mask[2, 3:5] = True
|
| 16 |
+
mask[3:5, 4] = True
|
| 17 |
+
mask[7, 2] = True
|
| 18 |
+
mask[5, 0] = True
|
| 19 |
+
mask[0, 6] = True
|
| 20 |
+
z = np.ma.array(z, mask=mask)
|
| 21 |
+
print(z)
|
| 22 |
+
|
| 23 |
+
corner_masks = [False, True]
|
| 24 |
+
fig, axs = plt.subplots(ncols=2)
|
| 25 |
+
for ax, corner_mask in zip(axs, corner_masks):
|
| 26 |
+
cs = ax.contourf(x, y, z, corner_mask=corner_mask)
|
| 27 |
+
ax.contour(cs, colors="k")
|
| 28 |
+
ax.set_title(f"{corner_mask=}")
|
| 29 |
+
|
| 30 |
+
# Plot grid.
|
| 31 |
+
ax.grid(c="k", ls="-", alpha=0.3)
|
| 32 |
+
|
| 33 |
+
# Indicate masked points with red circles.
|
| 34 |
+
ax.plot(np.ma.array(x, mask=~mask), y, "ro")
|
examples/requirements.txt
CHANGED
|
@@ -1,2 +1,3 @@
|
|
| 1 |
# Example of a requirements.txt file. LynxKite will automatically install anything you put here.
|
| 2 |
faker
|
|
|
|
|
|
| 1 |
# Example of a requirements.txt file. LynxKite will automatically install anything you put here.
|
| 2 |
faker
|
| 3 |
+
matplotlib
|
lynxkite-app/web/src/Code.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
import Editor, { type Monaco } from "@monaco-editor/react";
|
| 4 |
import type { editor } from "monaco-editor";
|
| 5 |
import { useEffect, useRef } from "react";
|
| 6 |
-
import { useParams } from "react-router";
|
| 7 |
import { WebsocketProvider } from "y-websocket";
|
| 8 |
import * as Y from "yjs";
|
| 9 |
// @ts-ignore
|
|
@@ -22,9 +22,12 @@ export default function Code() {
|
|
| 22 |
const wsProviderRef = useRef<any>();
|
| 23 |
const monacoBindingRef = useRef<any>();
|
| 24 |
const yMonacoRef = useRef<any>();
|
|
|
|
| 25 |
const editorRef = useRef<any>();
|
| 26 |
useEffect(() => {
|
| 27 |
const loadMonaco = async () => {
|
|
|
|
|
|
|
| 28 |
// y-monaco is gigantic. The other Monaco packages are small.
|
| 29 |
yMonacoRef.current = await import("y-monaco");
|
| 30 |
initCRDT();
|
|
@@ -34,7 +37,9 @@ export default function Code() {
|
|
| 34 |
function beforeMount(monaco: Monaco) {
|
| 35 |
monaco.editor.defineTheme("lynxkite", theme);
|
| 36 |
}
|
| 37 |
-
function onMount(_editor: editor.IStandaloneCodeEditor) {
|
|
|
|
|
|
|
| 38 |
editorRef.current = _editor;
|
| 39 |
initCRDT();
|
| 40 |
}
|
|
@@ -66,9 +71,9 @@ export default function Code() {
|
|
| 66 |
return (
|
| 67 |
<div className="workspace">
|
| 68 |
<div className="top-bar bg-neutral">
|
| 69 |
-
<
|
| 70 |
<img alt="" src={favicon} />
|
| 71 |
-
</
|
| 72 |
<div className="ws-name">{path}</div>
|
| 73 |
<div className="tools text-secondary">
|
| 74 |
<button className="btn btn-link">
|
|
@@ -77,9 +82,9 @@ export default function Code() {
|
|
| 77 |
<button className="btn btn-link">
|
| 78 |
<Backspace />
|
| 79 |
</button>
|
| 80 |
-
<
|
| 81 |
<Close />
|
| 82 |
-
</
|
| 83 |
</div>
|
| 84 |
</div>
|
| 85 |
<Editor
|
|
|
|
| 3 |
import Editor, { type Monaco } from "@monaco-editor/react";
|
| 4 |
import type { editor } from "monaco-editor";
|
| 5 |
import { useEffect, useRef } from "react";
|
| 6 |
+
import { Link, useParams } from "react-router";
|
| 7 |
import { WebsocketProvider } from "y-websocket";
|
| 8 |
import * as Y from "yjs";
|
| 9 |
// @ts-ignore
|
|
|
|
| 22 |
const wsProviderRef = useRef<any>();
|
| 23 |
const monacoBindingRef = useRef<any>();
|
| 24 |
const yMonacoRef = useRef<any>();
|
| 25 |
+
const yMonacoLoadingRef = useRef(false);
|
| 26 |
const editorRef = useRef<any>();
|
| 27 |
useEffect(() => {
|
| 28 |
const loadMonaco = async () => {
|
| 29 |
+
if (yMonacoLoadingRef.current) return;
|
| 30 |
+
yMonacoLoadingRef.current = true;
|
| 31 |
// y-monaco is gigantic. The other Monaco packages are small.
|
| 32 |
yMonacoRef.current = await import("y-monaco");
|
| 33 |
initCRDT();
|
|
|
|
| 37 |
function beforeMount(monaco: Monaco) {
|
| 38 |
monaco.editor.defineTheme("lynxkite", theme);
|
| 39 |
}
|
| 40 |
+
function onMount(_editor: editor.IStandaloneCodeEditor, monaco: Monaco) {
|
| 41 |
+
// Do nothing on Ctrl+S. We save after every keypress anyway.
|
| 42 |
+
_editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {});
|
| 43 |
editorRef.current = _editor;
|
| 44 |
initCRDT();
|
| 45 |
}
|
|
|
|
| 71 |
return (
|
| 72 |
<div className="workspace">
|
| 73 |
<div className="top-bar bg-neutral">
|
| 74 |
+
<Link className="logo" to="">
|
| 75 |
<img alt="" src={favicon} />
|
| 76 |
+
</Link>
|
| 77 |
<div className="ws-name">{path}</div>
|
| 78 |
<div className="tools text-secondary">
|
| 79 |
<button className="btn btn-link">
|
|
|
|
| 82 |
<button className="btn btn-link">
|
| 83 |
<Backspace />
|
| 84 |
</button>
|
| 85 |
+
<Link to={`/dir/${parentDir}`} className="btn btn-link">
|
| 86 |
<Close />
|
| 87 |
+
</Link>
|
| 88 |
</div>
|
| 89 |
</div>
|
| 90 |
<Editor
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
|
@@ -222,13 +222,17 @@ def op(env: str, name: str, *, view="basic", outputs=None, params=None, slow=Fal
|
|
| 222 |
_outputs = {name: Output(name=name, type=None) for name in outputs}
|
| 223 |
else:
|
| 224 |
_outputs = {"output": Output(name="output", type=None)} if view == "basic" else {}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
op = Op(
|
| 226 |
func=func,
|
| 227 |
name=name,
|
| 228 |
params=_params,
|
| 229 |
inputs=inputs,
|
| 230 |
outputs=_outputs,
|
| 231 |
-
type=
|
| 232 |
)
|
| 233 |
CATALOGS.setdefault(env, {})
|
| 234 |
CATALOGS[env][name] = op
|
|
@@ -238,6 +242,24 @@ def op(env: str, name: str, *, view="basic", outputs=None, params=None, slow=Fal
|
|
| 238 |
return decorator
|
| 239 |
|
| 240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
def input_position(**kwargs):
|
| 242 |
"""Decorator for specifying unusual positions for the inputs."""
|
| 243 |
|
|
|
|
| 222 |
_outputs = {name: Output(name=name, type=None) for name in outputs}
|
| 223 |
else:
|
| 224 |
_outputs = {"output": Output(name="output", type=None)} if view == "basic" else {}
|
| 225 |
+
_view = view
|
| 226 |
+
if view == "matplotlib":
|
| 227 |
+
_view = "image"
|
| 228 |
+
func = matplotlib_to_image(func)
|
| 229 |
op = Op(
|
| 230 |
func=func,
|
| 231 |
name=name,
|
| 232 |
params=_params,
|
| 233 |
inputs=inputs,
|
| 234 |
outputs=_outputs,
|
| 235 |
+
type=_view,
|
| 236 |
)
|
| 237 |
CATALOGS.setdefault(env, {})
|
| 238 |
CATALOGS[env][name] = op
|
|
|
|
| 242 |
return decorator
|
| 243 |
|
| 244 |
|
| 245 |
+
def matplotlib_to_image(func):
|
| 246 |
+
import matplotlib.pyplot as plt
|
| 247 |
+
import base64
|
| 248 |
+
import io
|
| 249 |
+
|
| 250 |
+
@functools.wraps(func)
|
| 251 |
+
def wrapper(*args, **kwargs):
|
| 252 |
+
func(*args, **kwargs)
|
| 253 |
+
buf = io.BytesIO()
|
| 254 |
+
plt.savefig(buf, format="png")
|
| 255 |
+
plt.close()
|
| 256 |
+
buf.seek(0)
|
| 257 |
+
image_base64 = base64.b64encode(buf.read()).decode("utf-8")
|
| 258 |
+
return f"data:image/png;base64,{image_base64}"
|
| 259 |
+
|
| 260 |
+
return wrapper
|
| 261 |
+
|
| 262 |
+
|
| 263 |
def input_position(**kwargs):
|
| 264 |
"""Decorator for specifying unusual positions for the inputs."""
|
| 265 |
|