-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add the ability to invoke QML functions #56
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this! I generally like the idea of having a generic InvokeMethod
call in spix. I guess it will be quite a challenge though to get this into a nice looking and generic implementation.
On Spix architecture
One mayor comment I have is about the general architecture, and I guess I should take some time to finally write that CONTRIBUTING.md
now that more and more PRs are coming in...
One idea in spix is to keep its internals as generic as possible. That means it should be possible to extend it with any kind of frontend, or to remove any of the frontends without impact to the remaining code. E.g. if anyrpc
was no longer cool, it should be possible to replace the anyrpc
api with a gRPC
one by just switching out the TestServer
that is used.
Because of that, none of the internal classes should be dependant on any types provided by anyrpc
. Even now, from the perspective of someone using the plain TestServer
in a gtest, it would be odd if some of the arguments to a command have to be a anyrpc::Variant
, rather than a std::variant
.
I know it's an additional step, but I would prefer if everything from anyrpc was first converted to a std::variant
and then, in the Scene/Qt/*
classes, to a Qt object.
Another part of the idea of spix being as generic as possible is that it should support different backends. This is currently not used, and I think not a problem with your changes, but it should be possible to use spix for testing a wxwidgets app just by adding a new set of Scene/*
classes.
On the whole invokeMethod "mess" 😉
From the top of my head, I also don't know of a non-cumbersome way to do this 😞.
I think as a first step it would help to move as much as possible of the "invokeMethod" related code into a separate file. This would then provide a clean API that QtItem can use to call methods without having to take care of the type conversion business.
At least this would move the "cumbersome" stuff into a separate part of the library.
I found one SO-post where a QVariant
is used for the arguments and return type of invokeMethod
, this would avoid the need for the special variant/conversion struct you have. Only some freestanding functions for converting a QVariant would be needed.
It seems to work without searching for the QMetaMethod
:
QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
Have you looked into something like this already?
lib/src/Utils/AnyRpcUtils.h
Outdated
template <> | ||
anyrpc::Value unpackAnyRpcParam(anyrpc::Value& value) | ||
{ | ||
return value; | ||
} | ||
|
||
template <> | ||
std::vector<anyrpc::Value> unpackAnyRpcParam(anyrpc::Value& value) | ||
{ | ||
if (!value.IsArray()) { | ||
throw anyrpc::AnyRpcException(anyrpc::AnyRpcErrorInvalidParams, "Invalid parameters. Expected Array."); | ||
} | ||
std::vector<anyrpc::Value> result; | ||
for (size_t i = 0; i < value.Size(); i++) | ||
result.push_back(value[i]); | ||
return result; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if I overlooked something, but those are no longer needed in the current version of the code, right?
Thanks for the detailed review!
Sure, that makes sense to me. I initially stuck to
I did look into this, and as you mentioned this method works only for functions that don't have type annotations: function test(arg) {} // QVariant works
function test(arg: int) {} // QVariant doesn't work
function test(arg): int {} // QVariant doesn't work This isn't really a problem for user-defined functions, but it becomes annoying when dealing with Qt-defined functions since almost all of them have type annotations: TextArea for example has type annotations for every method. A user-defined proxy method would fix this issue, but personally I'd rather not define a proxy method every time I want to call a Qt-defined QML function. I'll take a look at implementing the return type as
For sure, I'll tackle this on my next pass. |
@prototypicalpro I think bumping the C++ version to 17 is fine 👍
Too bad. I was hoping there would already be some magic in place to map any type to the QVariant 😞 Thanks for looking into this! |
ccf3a13
to
0959612
Compare
I implemented an intermediary variant ( In addition to the above:
I think concept-wise this PR is good pending review. I'll work on adding docs, tests, and an example next week. Edit: returning objects in Qt5 doesn't appear to be working, so I'll have to check that out as well. |
b94d74c
to
786b620
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! 🙂
@@ -0,0 +1,55 @@ | |||
/*** | |||
* Copyright (C) Noah Koontz. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally makes sense to put your name here. Now that more and more people are contributing, I was thinking about changing all the copyright lines to a "(C) Spix Authors" and adding the names of contributors into a AUTHORS.txt or something like that.
Would you also be fine with that? Would be a separate PR once this is all merged that would be just about changing the copyright text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup that sounds great!
786b620
to
72526c6
Compare
d319c10
to
f3f22d0
Compare
f3f22d0
to
98e9ed9
Compare
I added tests and some text in the README, and with that I think this PR is good to go! |
Sorry for the delay... I have lots of stuff going on right now, but I will get to this PR eventually 😕 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Thank you for contributing this.
This PR adds the ability to invoke arbitrary QML methods for testing purposes. For example:
I'm posting this PR as a draft to get more feedback on my approach before I continue. More specifically, I have written a lot of code devoted to massaging types (
Value
->QVariant
-> parameter type and vice versa) becauseQMetaObject::invokeMethod
andQMetaMethod::invoke
both require the parameter types to exactly match the QML interface. For instance when attempting to invokeTextArea.select(int, int)
:Because of this restriction my code must first search for a matching method interface, perform the numerous conversion steps to put the arguments in, and do the conversion again in reverse with the return value. I have implemented this and it works but is rather cumbersome: I'd appreciate ideas on how to improve this process. One lead I've investigated is finding a way to invoke QML functions JavaScript-style (with no conversions or type checks) using the
QJSValue
orQJSEngine
API, but thus far I haven't found anything spix could use.TODO before this PR is merges (in addition to any feedback):