Use Case
The macro Q_INVOKABLE allows developers of other modules to register member-functions in the meta object system. Such functions are registered automatically by the Python Module such that they can be invoked by the user via python scripts.
However, this requires the definition of member function in C++ which may cause unwanted dependencies within a module just to expose certain functionalities.
Example: IntelliGraph Module
Graphs and Nodes can be executed in C++ using the GraphExecutionModel e.g. GraphExecutionModel::evaluateGraph(Graph&) instead of Graph::evaluate(). That way, the classes for graphs and nodes are mostly decoupled from this executor model/executor engine and internal dependencies are kept low. Moreover, it would allow to use easily define other executor models/engines if desired.
If this API design (I have chosen) is better that way, I am unable to tell (I'm open for discussions though). But as a matter of fact, the API is what it is currently.
Now onto the dilemma: In Python it would be much more user-friendly/intuitive if each node and graph has a evaluate function or exposed a nodeData function for reading/writing values to/from the nodes. I.e. ideally one should be able to write graph.evaluate() and graph.nodeData(0).value() instead of graphexecmodel.evaluateGraph(graphobject). Further, the GraphExecutionModel uses of references instead of pointers as arguments/return values and also employs some custom C++ classes that are not derived of QObject which may cause issues for Python (idk).
Goal:
For this use case in particular it would be very handy to have a way of "decorating" a C++-class (derived of QObject) without direct dependencies between IntelliGraph-Module to Python or vice versa between the Python Module to the IntelliGraph-Module (or the need to define an extra Module, e.g. a "PyIntelliGraph-Module").
Possible Solutions:
1. Shared function that allows to decorate an object:
One possible solution I can think of (if it is actually feasible I dont know):
The Python Module may be able to register a shared-function named e.g. decorateObject or decorateClass that takes
- the MetaObject of a
QObject-Class
- a string
- and a functor
as an input. Using these arguments, it may be possible for modules to decorate an object in such a way, that objects of said class (1) get a python-only member-function named according to the given string (2) and execute the functor (3).
E.g. the following sahred-function call
decorateClass(Graph.staticMetaObject, "execute", executeGraphFunctor)
may allow use to write in Python
2. Allow to register shared functions as standalone-function in Python:
Alternatively, modules may register multiple shared-functions which the python module may be able to register as standalone python functions.
Obviously, not all shared-functions should be turned into a standalone function. The Python Module itself may be able to register a shared-fucntion it-self that allows other modules to register specific shared-functions that should be turned into standalone python functions
E.g. the following sahred-function call
registerAsStandalonePyFunction('ModuleId', 'SharedFunctionId', 'pyFunctionId')
may allow use to write in Python
instead of how we would have to write it currently (i.e. access shared function)
pyFunctionId = shared_function('ModuleId', 'SharedFunctionId')
pyFunctionId(...)
This apporach may be more feasible
Use Case
The macro
Q_INVOKABLEallows developers of other modules to register member-functions in the meta object system. Such functions are registered automatically by the Python Module such that they can be invoked by the user via python scripts.However, this requires the definition of member function in C++ which may cause unwanted dependencies within a module just to expose certain functionalities.
Example: IntelliGraph Module
Graphs and Nodes can be executed in C++ using the
GraphExecutionModele.g.GraphExecutionModel::evaluateGraph(Graph&)instead ofGraph::evaluate(). That way, the classes for graphs and nodes are mostly decoupled from this executor model/executor engine and internal dependencies are kept low. Moreover, it would allow to use easily define other executor models/engines if desired.If this API design (I have chosen) is better that way, I am unable to tell (I'm open for discussions though). But as a matter of fact, the API is what it is currently.
Now onto the dilemma: In Python it would be much more user-friendly/intuitive if each node and graph has a
evaluatefunction or exposed anodeDatafunction for reading/writing values to/from the nodes. I.e. ideally one should be able to writegraph.evaluate()andgraph.nodeData(0).value()instead ofgraphexecmodel.evaluateGraph(graphobject). Further, theGraphExecutionModeluses of references instead of pointers as arguments/return values and also employs some custom C++ classes that are not derived ofQObjectwhich may cause issues for Python (idk).Goal:
For this use case in particular it would be very handy to have a way of "decorating" a C++-class (derived of
QObject) without direct dependencies between IntelliGraph-Module to Python or vice versa between the Python Module to the IntelliGraph-Module (or the need to define an extra Module, e.g. a "PyIntelliGraph-Module").Possible Solutions:
1. Shared function that allows to decorate an object:
One possible solution I can think of (if it is actually feasible I dont know):
The Python Module may be able to register a shared-function named e.g.
decorateObjectordecorateClassthat takesQObject-Classas an input. Using these arguments, it may be possible for modules to decorate an object in such a way, that objects of said class (1) get a python-only member-function named according to the given string (2) and execute the functor (3).
E.g. the following sahred-function call
may allow use to write in Python
2. Allow to register shared functions as standalone-function in Python:
Alternatively, modules may register multiple shared-functions which the python module may be able to register as standalone python functions.
Obviously, not all shared-functions should be turned into a standalone function. The Python Module itself may be able to register a shared-fucntion it-self that allows other modules to register specific shared-functions that should be turned into standalone python functions
E.g. the following sahred-function call
may allow use to write in Python
pyFunctionId(...)instead of how we would have to write it currently (i.e. access shared function)
This apporach may be more feasible