Skip to content

Commit

Permalink
Add observer pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
rapgenic committed Jan 13, 2023
1 parent 63271d0 commit 067beee
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 8 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Expand Up @@ -72,6 +72,8 @@ option(
# ============

find_package(xeus-zmq REQUIRED)
find_package(xwidgets REQUIRED)
find_package(xproperty REQUIRED)
find_package(PNG REQUIRED)
find_package(glad REQUIRED)
find_package(glfw3)
Expand Down Expand Up @@ -228,7 +230,7 @@ macro(xeus_octave_create_target target_name linkage output_name)

target_link_libraries(
${target_name}
PUBLIC xtl PkgConfig::octinterp
PUBLIC xtl xwidgets PkgConfig::octinterp
PRIVATE glad::glad glfw PNG::PNG
)
if(XEUS_OCTAVE_USE_SHARED_XEUS)
Expand Down
3 changes: 3 additions & 0 deletions environment-dev.yml
Expand Up @@ -3,6 +3,7 @@ channels:
dependencies:
# Build dependencies
- cxx-compiler
- fortran-compiler
- c-compiler
- cmake
- make
Expand All @@ -11,6 +12,7 @@ dependencies:
- libuuid
- xtl
- xeus-zmq =1.*
- xwidgets =0.27.3
- nlohmann_json
- cppzmq
- octave =7.*
Expand All @@ -37,4 +39,5 @@ dependencies:
- cmake-format
- plotly
- ipywidgets
- widgetsnbextension =3.6.1
- jupyter-dash
13 changes: 13 additions & 0 deletions include/xeus-octave/xwidgets2.hpp
Expand Up @@ -27,6 +27,7 @@
#include <octave/ov.h>
#include <octave/ovl.h>

#include <string>
#include <xwidgets/xcommon.hpp>

namespace xeus_octave::widgets
Expand Down Expand Up @@ -55,9 +56,21 @@ class xwidget : public octave::handle_cdef_object, public xw::xcommon
void apply_patch(nl::json const&, xeus::buffer_sequence const&);
void handle_message(xeus::xmessage const&);

/**
* @brief call any observers set in the octave interpreter context for the
* specified property name
*/
void notify_backend(std::string const&) const;
/**
* @brief send to the frontend a new value for the specified property.
* Octave value is automatically converted to a json value
*/
void notify_frontend(std::string const&, octave_value const&);

private:

static octave_value_list cdef_constructor(octave_value_list const&, int);
static octave_value_list cdef_observe(octave_value_list const&, int);
static octave_value_list cdef_display(octave_value_list const&, int);
static octave_value_list cdef_id(octave_value_list const&, int);

Expand Down
73 changes: 66 additions & 7 deletions src/xwidgets2.cpp
Expand Up @@ -32,6 +32,7 @@
#include <octave/ov-classdef.h>
#include <octave/ov.h>
#include <octave/ovl.h>
#include <octave/parse.h>
#include <xwidgets/xcommon.hpp>

#include "xeus-octave/json.hpp"
Expand Down Expand Up @@ -98,7 +99,7 @@ namespace
* @param property reference to a property definition object
* @return true if property has attribute "Sync" set to true
*/
bool is_sync_property(octave::cdef_property& property)
inline bool is_sync_property(octave::cdef_property& property)
{
return !property.get("Sync").isempty() && property.get("Sync").bool_value();
}
Expand All @@ -115,8 +116,8 @@ void xwidget::serialize_state(nl::json& state, xeus::buffer_sequence& buffers) c
octave::cdef_property property = property_tuple.second;
if (is_sync_property(property))
{
octave_value ov = this->get(property.get_name());
xw::xwidgets_serialize(ov, state[property.get_name()], buffers);
octave_value ov = this->get(property_tuple.first);
xw::xwidgets_serialize(ov, state[property_tuple.first], buffers);
}
}
}
Expand All @@ -129,10 +130,11 @@ void xwidget::apply_patch(nl::json const& state, xeus::buffer_sequence const&)
for (auto property_tuple : properties)
{
octave::cdef_property property = property_tuple.second;
if (is_sync_property(property) && state.contains(property.get_name()))
if (properties.count(property_tuple.first) && is_sync_property(property) && state.contains(property_tuple.first))
{
// Call superclass put to avoid notifying the view again in a loop
octave::handle_cdef_object::put(property.get_name(), state[property.get_name()]);
octave::handle_cdef_object::put(property_tuple.first, state[property_tuple.first]);
this->notify_backend(property_tuple.first);
}
}
}
Expand All @@ -145,10 +147,35 @@ void xwidget::put(std::string const& pname, octave_value const& val)
octave::cdef_class cls = this->get_class();
auto properties = cls.get_property_map(octave::cdef_class::property_all);

if (is_sync_property(properties[pname]))
if (properties.count(pname) && is_sync_property(properties[pname]))
{
std::clog << "Notify change " << pname << std::endl;
this->notify(pname, val);
this->notify_frontend(pname, val);
this->notify_backend(pname);
}
}
}

void xwidget::notify_frontend(std::string const& name, octave_value const& value)
{
xw::xcommon::notify(name, value);
}

void xwidget::notify_backend(std::string const& pname) const
{
// Get the observer property name
std::string oname = "__" + pname + "_observers__";
// Get the current list of handles
octave_value fcn_list_ov = this->get(oname);
// Call the observers
if (!fcn_list_ov.isempty())
{
Cell fcn_list = fcn_list_ov.cell_value();
for (octave_idx_type i = 0; i < fcn_list.numel(); i++)
{
// Object reference
octave::cdef_object obj(this->clone());
octave::feval(fcn_list(i), octave::to_ov(obj));
}
}
}
Expand Down Expand Up @@ -219,6 +246,37 @@ octave_value_list xwidget::cdef_constructor(octave_value_list const& args, int)
}
}

octave_value_list xwidget::cdef_observe(octave_value_list const& args, int)
{
// Object reference
octave_classdef* obj = args(0).classdef_object_value();
// Property to observe
std::string pname = args(1).xstring_value("PNAME must be a string with the property name");
std::string oname = "__" + pname + "_observers__";
// Observer callback
octave_value fcn = args(2);
if (!fcn.is_function_handle())
error("HANDLE must be a function handle");

// Get the current list of handles
octave_value fcn_list_ov = obj->get_object_ref().get(oname);

std::clog << "Is empty: " << fcn_list_ov.isempty() << std::endl;

// Append the function handle
if (fcn_list_ov.isempty())
obj->get_object_ref().put(oname, Cell(fcn));
else
{
Cell fcn_list = obj->get_object_ref().get(oname).cell_value();
fcn_list.resize1(fcn_list.numel() + 1);
fcn_list(fcn_list.numel()) = fcn;
obj->get_object_ref().put(oname, fcn_list);
}

return ovl();
}

octave_value_list xwidget::cdef_display(octave_value_list const& args, int)
{
get_widget(args(0).classdef_object_value())->display();
Expand All @@ -244,6 +302,7 @@ void register_all2(octave::interpreter& interpreter)
octave::cdef_class cls = cm.make_class(XWIDGET_CLASS_NAME, cm.find_class("handle"));

cls.install_method(cm.make_method(cls, XWIDGET_CLASS_NAME, xwidget::cdef_constructor));
cls.install_method(cm.make_method(cls, "observe", xwidget::cdef_observe));
cls.install_method(cm.make_method(cls, "display", xwidget::cdef_display));
cls.install_method(cm.make_method(cls, "id", xwidget::cdef_id));

Expand Down

0 comments on commit 067beee

Please sign in to comment.