Skip to content

Commit 50f829e

Browse files
committed
Add interact function
1 parent 3fb0317 commit 50f829e

File tree

5 files changed

+469
-566
lines changed

5 files changed

+469
-566
lines changed

include/xeus-octave/tk_notebook.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,24 @@ class notebook_graphics_toolkit : public glfw_graphics_toolkit
6464
void show_figure(octave::graphics_object const&) const override;
6565
};
6666

67+
/**
68+
* Stupidly simple toolkit. On each redraw request just sends a display_data
69+
* call to the frontend. Suitable for cases where update_display_data calls are
70+
* not supported (e.g. using xoutput widget)
71+
*
72+
* Should not be used by users
73+
*/
74+
class interact_graphics_toolkit : public glfw_graphics_toolkit
75+
{
76+
public:
77+
78+
interact_graphics_toolkit() : glfw_graphics_toolkit("__interact") {}
79+
80+
bool is_valid() const override { return true; }
81+
82+
void send_figure(octave::graphics_object const&, std::vector<char> const&, int, int, double) const override;
83+
};
84+
6785
/**
6886
* Toolkit that uses a ximage as output region, updating its contents on each
6987
* redraw. When a figure is added to a widget container (e.g. xvbox or xhbox)

notebooks/xeus-octave.ipynb

Lines changed: 295 additions & 566 deletions
Large diffs are not rendered by default.

share/xeus-octave/interact.m

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
classdef interact < xvbox
2+
methods
3+
function obj = interact(_fhandle, varargin)
4+
if nargin < 2
5+
print_usage();
6+
end
7+
8+
if ! is_function_handle(_fhandle)
9+
error("FHANDLE must be a function handle");
10+
end
11+
12+
obj.fhandle = _fhandle;
13+
obj.create_widgets(varargin{:});
14+
obj.register_observers();
15+
16+
obj.Children{end+1} = xoutput;
17+
18+
obj.on_update();
19+
end
20+
end
21+
22+
methods (Access = private)
23+
function create_widgets(obj, varargin)
24+
for i = 1:numel(varargin)
25+
param = varargin{i};
26+
27+
if typeinfo(param) != "cell" || numel(param) < 2
28+
error("PARAMS must be cell arrays with at least 2 elements");
29+
end
30+
31+
description = param{1};
32+
33+
if numel(param) == 2
34+
arg = param{2};
35+
36+
switch typeinfo(arg)
37+
case "scalar"
38+
w = xslider;
39+
w.Description = description;
40+
w.Value = arg;
41+
obj.Children{end+1} = w;
42+
end
43+
elseif numel(param) == 3
44+
min = param{2};
45+
max = param{3};
46+
47+
switch typeinfo(min)
48+
case "scalar"
49+
w = xslider;
50+
w.Description = description;
51+
w.Min = min;
52+
w.Max = max;
53+
obj.Children{end+1} = w;
54+
end
55+
elseif numel(param) == 4
56+
min = param{2};
57+
max = param{3};
58+
arg = param{4};
59+
60+
switch typeinfo(arg)
61+
case "scalar"
62+
w = xslider;
63+
w.Description = description;
64+
w.Min = min;
65+
w.Max = max;
66+
w.Value = arg;
67+
obj.Children{end+1} = w;
68+
end
69+
end
70+
end
71+
end
72+
73+
function register_observers(obj)
74+
for i = 1:numel(obj.Children)
75+
obj.Children{i}.observe_Value(@(w) obj.on_update());
76+
end
77+
end
78+
79+
function on_update(obj)
80+
% Prepare arguments to pass to the interactive function
81+
args = cellfun(@(w) w.Value, obj.Children(1:end-1), "UniformOutput", false);
82+
% Clear current figure
83+
set (groot, "currentfigure", []);
84+
% Get current figures
85+
figures_before = findobj("type", "figure");
86+
% Save current graphics toolkit
87+
tksv = graphics_toolkit();
88+
% Change graphics toolkit
89+
graphics_toolkit("__interact");
90+
% Set output capture
91+
obj.Children{end}.capture;
92+
% Clear output
93+
clear_output(true);
94+
% Call function
95+
obj.fhandle(args{:})
96+
% Get current figures
97+
figures_after = findobj("type", "figure");
98+
% Get new figures
99+
figures_new = setdiff(figures_after, figures_before);
100+
% Draw any figure
101+
drawnow;
102+
% Release output
103+
obj.Children{end}.release;
104+
% Close figures
105+
delete(figures_new);
106+
% Restore graphics toolkit
107+
graphics_toolkit(tksv);
108+
end
109+
end
110+
111+
properties (SetAccess = private, GetAccess = public)
112+
fhandle
113+
end
114+
end

src/display.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <octave/defun-int.h>
2222
#include <octave/interpreter.h>
2323
#include <octave/oct-map.h>
24+
#include <octave/ovl.h>
2425
#include <octave/symtab.h>
2526

2627
#include <nlohmann/json.hpp>
@@ -77,6 +78,26 @@ octave_value_list display_data(octave_value_list const& args, int /*nargout*/)
7778
return ovl();
7879
}
7980

81+
/**
82+
* Native binding to xeus function clear_output
83+
*/
84+
octave_value_list clear_output(octave_value_list const& args, int /*nargout*/)
85+
{
86+
// Agruments check
87+
if (args.length() > 1)
88+
print_usage();
89+
90+
bool wait = false;
91+
92+
if (args.length() == 1)
93+
wait = args(0).xbool_value("WAIT must be a boolean");
94+
95+
// Invoke xeus method
96+
dynamic_cast<xeus_octave::xoctave_interpreter&>(xeus::get_interpreter()).clear_output(wait);
97+
98+
return ovl();
99+
}
100+
80101
/**
81102
* Native binding to convert an octave matrix to an html table
82103
*
@@ -214,6 +235,7 @@ namespace xeus_octave::display
214235
void register_all(octave::interpreter& interpreter)
215236
{
216237
utils::add_native_binding(interpreter, "display_data", display_data);
238+
utils::add_native_binding(interpreter, "clear_output", clear_output);
217239
utils::add_native_binding(interpreter, "__matrix_to_html__", matrix_to_html);
218240
utils::add_native_binding(interpreter, "__matrix_to_latex__", matrix_to_latex);
219241
utils::add_native_binding(interpreter, "__latex_fix_sci_not__", latex_fix_sci_not);

src/tk_notebook.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,24 @@ void notebook_graphics_toolkit::send_figure(
324324
dynamic_cast<xoctave_interpreter&>(xeus::get_interpreter()).update_display_data(data, meta, tran);
325325
}
326326

327+
void interact_graphics_toolkit::send_figure(
328+
oc::graphics_object const&, std::vector<char> const& img, int width, int height, double dpr
329+
) const
330+
{
331+
nl::json data, meta;
332+
333+
data["image/png"] = xtl::base64encode(std::string(img.begin(), img.end()));
334+
// Send real width and height through metadata for optimal scaling
335+
meta["image/png"] = {
336+
{"width", width / dpr},
337+
{"height", height / dpr},
338+
};
339+
340+
// Update
341+
dynamic_cast<xoctave_interpreter&>(xeus::get_interpreter())
342+
.display_data(data, meta, nl::json(nl::json::value_t::object));
343+
}
344+
327345
bool widget_graphics_toolkit::initialize(oc::graphics_object const& go)
328346
{
329347
bool ret = glfw_graphics_toolkit::initialize(go);
@@ -366,6 +384,8 @@ void register_all(octave::interpreter& interpreter)
366384
// Install the toolkit into the interpreter
367385
interpreter.get_gtk_manager().register_toolkit("notebook");
368386
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new notebook_graphics_toolkit()));
387+
interpreter.get_gtk_manager().register_toolkit("__interact");
388+
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new interact_graphics_toolkit()));
369389
interpreter.get_gtk_manager().register_toolkit("__widget");
370390
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new widget_graphics_toolkit()));
371391
}

0 commit comments

Comments
 (0)