Skip to content

Commit

Permalink
Add interact function
Browse files Browse the repository at this point in the history
  • Loading branch information
rapgenic committed Jan 13, 2023
1 parent 3fb0317 commit 50f829e
Show file tree
Hide file tree
Showing 5 changed files with 469 additions and 566 deletions.
18 changes: 18 additions & 0 deletions include/xeus-octave/tk_notebook.hpp
Expand Up @@ -64,6 +64,24 @@ class notebook_graphics_toolkit : public glfw_graphics_toolkit
void show_figure(octave::graphics_object const&) const override;
};

/**
* Stupidly simple toolkit. On each redraw request just sends a display_data
* call to the frontend. Suitable for cases where update_display_data calls are
* not supported (e.g. using xoutput widget)
*
* Should not be used by users
*/
class interact_graphics_toolkit : public glfw_graphics_toolkit
{
public:

interact_graphics_toolkit() : glfw_graphics_toolkit("__interact") {}

bool is_valid() const override { return true; }

void send_figure(octave::graphics_object const&, std::vector<char> const&, int, int, double) const override;
};

/**
* Toolkit that uses a ximage as output region, updating its contents on each
* redraw. When a figure is added to a widget container (e.g. xvbox or xhbox)
Expand Down
861 changes: 295 additions & 566 deletions notebooks/xeus-octave.ipynb

Large diffs are not rendered by default.

114 changes: 114 additions & 0 deletions share/xeus-octave/interact.m
@@ -0,0 +1,114 @@
classdef interact < xvbox
methods
function obj = interact(_fhandle, varargin)
if nargin < 2
print_usage();
end

if ! is_function_handle(_fhandle)
error("FHANDLE must be a function handle");
end

obj.fhandle = _fhandle;
obj.create_widgets(varargin{:});
obj.register_observers();

obj.Children{end+1} = xoutput;

obj.on_update();
end
end

methods (Access = private)
function create_widgets(obj, varargin)
for i = 1:numel(varargin)
param = varargin{i};

if typeinfo(param) != "cell" || numel(param) < 2
error("PARAMS must be cell arrays with at least 2 elements");
end

description = param{1};

if numel(param) == 2
arg = param{2};

switch typeinfo(arg)
case "scalar"
w = xslider;
w.Description = description;
w.Value = arg;
obj.Children{end+1} = w;
end
elseif numel(param) == 3
min = param{2};
max = param{3};

switch typeinfo(min)
case "scalar"
w = xslider;
w.Description = description;
w.Min = min;
w.Max = max;
obj.Children{end+1} = w;
end
elseif numel(param) == 4
min = param{2};
max = param{3};
arg = param{4};

switch typeinfo(arg)
case "scalar"
w = xslider;
w.Description = description;
w.Min = min;
w.Max = max;
w.Value = arg;
obj.Children{end+1} = w;
end
end
end
end

function register_observers(obj)
for i = 1:numel(obj.Children)
obj.Children{i}.observe_Value(@(w) obj.on_update());
end
end

function on_update(obj)
% Prepare arguments to pass to the interactive function
args = cellfun(@(w) w.Value, obj.Children(1:end-1), "UniformOutput", false);
% Clear current figure
set (groot, "currentfigure", []);
% Get current figures
figures_before = findobj("type", "figure");
% Save current graphics toolkit
tksv = graphics_toolkit();
% Change graphics toolkit
graphics_toolkit("__interact");
% Set output capture
obj.Children{end}.capture;
% Clear output
clear_output(true);
% Call function
obj.fhandle(args{:})
% Get current figures
figures_after = findobj("type", "figure");
% Get new figures
figures_new = setdiff(figures_after, figures_before);
% Draw any figure
drawnow;
% Release output
obj.Children{end}.release;
% Close figures
delete(figures_new);
% Restore graphics toolkit
graphics_toolkit(tksv);
end
end

properties (SetAccess = private, GetAccess = public)
fhandle
end
end
22 changes: 22 additions & 0 deletions src/display.cpp
Expand Up @@ -21,6 +21,7 @@
#include <octave/defun-int.h>
#include <octave/interpreter.h>
#include <octave/oct-map.h>
#include <octave/ovl.h>
#include <octave/symtab.h>

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

/**
* Native binding to xeus function clear_output
*/
octave_value_list clear_output(octave_value_list const& args, int /*nargout*/)
{
// Agruments check
if (args.length() > 1)
print_usage();

bool wait = false;

if (args.length() == 1)
wait = args(0).xbool_value("WAIT must be a boolean");

// Invoke xeus method
dynamic_cast<xeus_octave::xoctave_interpreter&>(xeus::get_interpreter()).clear_output(wait);

return ovl();
}

/**
* Native binding to convert an octave matrix to an html table
*
Expand Down Expand Up @@ -214,6 +235,7 @@ namespace xeus_octave::display
void register_all(octave::interpreter& interpreter)
{
utils::add_native_binding(interpreter, "display_data", display_data);
utils::add_native_binding(interpreter, "clear_output", clear_output);
utils::add_native_binding(interpreter, "__matrix_to_html__", matrix_to_html);
utils::add_native_binding(interpreter, "__matrix_to_latex__", matrix_to_latex);
utils::add_native_binding(interpreter, "__latex_fix_sci_not__", latex_fix_sci_not);
Expand Down
20 changes: 20 additions & 0 deletions src/tk_notebook.cpp
Expand Up @@ -324,6 +324,24 @@ void notebook_graphics_toolkit::send_figure(
dynamic_cast<xoctave_interpreter&>(xeus::get_interpreter()).update_display_data(data, meta, tran);
}

void interact_graphics_toolkit::send_figure(
oc::graphics_object const&, std::vector<char> const& img, int width, int height, double dpr
) const
{
nl::json data, meta;

data["image/png"] = xtl::base64encode(std::string(img.begin(), img.end()));
// Send real width and height through metadata for optimal scaling
meta["image/png"] = {
{"width", width / dpr},
{"height", height / dpr},
};

// Update
dynamic_cast<xoctave_interpreter&>(xeus::get_interpreter())
.display_data(data, meta, nl::json(nl::json::value_t::object));
}

bool widget_graphics_toolkit::initialize(oc::graphics_object const& go)
{
bool ret = glfw_graphics_toolkit::initialize(go);
Expand Down Expand Up @@ -366,6 +384,8 @@ void register_all(octave::interpreter& interpreter)
// Install the toolkit into the interpreter
interpreter.get_gtk_manager().register_toolkit("notebook");
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new notebook_graphics_toolkit()));
interpreter.get_gtk_manager().register_toolkit("__interact");
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new interact_graphics_toolkit()));
interpreter.get_gtk_manager().register_toolkit("__widget");
interpreter.get_gtk_manager().load_toolkit(octave::graphics_toolkit(new widget_graphics_toolkit()));
}
Expand Down

0 comments on commit 50f829e

Please sign in to comment.