Call QtDBus method with array-of-strings arguments - qt

I try to call a method with arguments.
For example, list services.
This code
#include <QtDBus>
#include <QDebug>
QDBusMessage callDbusMethod(QString method, QList<QVariant> args= {}) {
QDBusMessage msg;
if(QDBusConnection::systemBus().isConnected()) {
QDBusInterface iface("org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", QDBusConnection::systemBus());
if(iface.isValid())
msg= args.isEmpty() ? iface.call(QDBus::AutoDetect, method.toLatin1())
: iface.callWithArgumentList(QDBus::AutoDetect, method.toLatin1(), args);
if(msg.type() == QDBusMessage::ErrorMessage)
qDebug() << msg.errorMessage(); }
return msg;
}
typedef struct {
QString path, state; } UnitFile;
int main() {
QDBusMessage msg= callDbusMethod("ListUnitFilesByPatterns",
QList<QVariant>{ QVariant(""), QVariant("*.service") });
//QDBusMessage msg= callDbusMethod("ListUnitFiles");
if(msg.arguments().size()) {
const QDBusArgument argUnitFiles= msg.arguments().at(0).value<QDBusArgument>();
argUnitFiles.beginArray();
while(!argUnitFiles.atEnd()) {
UnitFile unit;
argUnitFiles.beginStructure();
argUnitFiles >> unit.path >> unit.state;
argUnitFiles.endStructure();
qDebug() << unit.path; }
argUnitFiles.endArray(); }
}
Return
"Invalid arguments 'ss' to call org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns(), expecting 'asas'."
And how to call the method correctly.

You already answered your question in the comments. But I figured I'd explain more here.
In the submission you are passing the argument list of ss since your QList has two QVariants which are strings. The signature of org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns is asas or two array of strings. According to qt's dbus typesystem, as is automatically de-marshaled into a QStringList. Therefore you'd want a QList of QVariants which are QStringLists.
So..
QList<QVariant> args = { QVariant(""), QVariant("*.service") };
should be
QList<QVariant> args = { QStringList() << "", QStringList() << "*.service" };

Related

How to make Q_INVOKABLE method that takes variable number of arguments from QML

As everyone knows, in JavaScript all functions can take any number of arguments.
In Qt you can make QObject, whose methods accessible from QML, having marked it by Q_INVOKABLE. For example:
class myObj: public QObject
{
Q_OBJECT
//...
public slots:
Q_INVOKABLE QJSValue myFunction(QJSValue value);
//...
};
And then you can call it from JS:
(function(){
//like this:
var result = myObj.myFunction("test");
//but also like this:
var result2 = myObj.myFunction(1,2,3,4,5); //,6,7,8, ..., 9998, 9999
})();
So, how to handle variable number of parameters on C++ side? On JS side we have "arguments" object. Is there any analog for Q_INVOKABLE methods?
It seems to be impossible to do by normal way. For now I've ended up with workaround.
QJSValue can contain any JS type. Including array. So method could seems like that:
QJSValue myObject::test(QJSValue arg1, QJSValue arg2, QJSValue rest)
{
qDebug() << "arg1 = " << arg1.toString();
qDebug() << "arg2 = " << arg2.toString();
auto args = toJSValueList(rest);
qDebug() << "args = [";
for(auto arg : args) {
qDebug() << " " << arg.toString() << ",";
}
qDebug() << "]";
return (arg1.isUndefined() ? 0 : 1) + (arg2.isUndefined() ? 0 : 1) + args.length();
}
Arguments arg1 and arg2 are required. Rest of arguments can be passed to method as third argument - an array. Method toJSValueList is just a helper that converts QJSValue, contained array to QJSValueList.
QJSValueList myObject::toJSValueList(QJSValue arg)
{
QJSValueList list;
auto length = arg.property("length");
if(length.isNumber()){
for(int i = 0, intLength = length.toInt(); i < intLength; ++i){
list << arg.property(static_cast<quint32>(i));
}
} else if(!arg.isUndefined()){
list << arg;
}
return list;
}
This is enough to make things work. But if someone actually need to pass any number of arguments direct to function (not through array), it can be done with a little hack.
class myObject: public QObject, public QmlSingletonProvider<QmlTimer>
{
Q_OBJECT
public:
//...
// declare property of type QJSValue which will actually be a function
Q_PROPERTY(QJSValue variadic_test READ variadic_test)
public slots:
Q_INVOKABLE QJSValue test(QJSValue arg1, QJSValue arg2, QJSValue rest);
private:
QJSValueList toJSValueList(QJSValue arg);
QJSValue variadic_test(); // getter for property
QJSValue variadic_test_fn; // stored JS function
};
// variadic wrapper for method test()
QJSValue myObject::variadic_test()
{
if(variadic_test_fn.isCallable())
return variadic_test_fn;
auto engine = qjsEngine(this);
if(!engine) return QJSValue();
variadic_test_fn = engine->evaluate("function(){ return this.test( arguments[0], arguments[1], Array.prototype.slice.call( arguments, 2 ) ); }");
return variadic_test_fn;
}
QJSValue can also be a function. So you can add an readonly Q_PROPERTY with type of QJSValue and assign JS function to it on first property access. This function will be a wrapper, that just gather all its arguments to array and pass it to your actual method. From JS you can see and call both functions: the actual one and the variadic wrapper. In the example I've passed first two arguments as is ("undefined" will be passed, if number of arguments is less than 2.), since method "test" waiting for at least 2 arguments. And rest of arguments gathered to an array by "Array.prototype.slice.call( arguments, 2 )". You can use "function(){ return this.test( Array.prototype.slice.call( arguments ) ); }" if you don't have required parameters.
Of course your C++ method should be ready for possible "undefined" values. And type of each argument need to be checked by hand. It's seems more like "JS code", even though it written on C++.
It's looks like this is impossible for every QObject, but you can wrap your singleton C++ backend SLOT with custom JS script, which will join together multiple arguments to single array. This can be done through the qmlRegisterSingletonType method callback, which provides access to the QJSEngine instance.
QJSValue ApiProvider::initSingletonType(QQmlEngine *qmlEngine, QJSEngine *jsEngine) {
qDebug() << "ApiProvider initSingletonType";
Q_UNUSED(qmlEngine);
QJSValue instance=jsEngine->newQObject(new ApiProvider(jsEngine));
QJSValue restProvider=jsEngine->evaluate(
"(function (instance) {" \
" instance.createElement=function(type,...child){" \
" return instance.createElementInternal(type,child);" \
" }; " \
" return instance;" \
"})"
);
if (restProvider.isError()) {
qCritical() << restProvider.toString();
return QJSValue();
} else {
QJSValue result=restProvider.call({instance});
if (result.isError()) {
qCritical() << result.toString();
return QJSValue();
} else {
return instance;
}
}
}
...
qmlRegisterSingletonType(
"com.tripolskypetr.quitejs",
1, 0,
"Api",
ApiProvider::initSingletonType
);
This sample implements a conditional jsx factory, where each element can have an arbitrary number of descendants in rest parameters. The source code is ready to production.
You can take QList<QVariant> as the argument to your C++ Q_INVOKABLE function and pass any array in qml side like ["string", 1, 2, 3]. QVariant is convertible to most of all basic data types.

How to use the callback function in vc++ dll to respond to the click event in the widget UI dll based on Qt?

When a click event happens on the Qt based UI dll, how to call the processing function in a VC++ .dll or .exe? (qtwinmigrate` is used in my project). In short, When pushButton is clicked how to call function elementsExampleCreateShapeActive().
This is my QT dll code.
//define the callback function pointer
typedef wchar_t const* WCharCP;
typedef void(CALLBACK *FunCallBack)(WCharCP);
//define the callback function
FunCallBack OnEvent = NULL;
//define the callback function var
int par;
WCharCP unparsed;
extern "C" __declspec(dllexport) void SetFunCallBack(FunCallBack fun, WCharCP var)
{
OnEvent =fun;
unparsed = var;
}
void CDialog::on_pushButton_clicked()
{
OnEvent(unparsed);
}
And Below is the code for pulgin dll in VC++.
typedef void (CALLBACK *FunCallBack)(WCharCP);
void CALLBACK handleEvent(WCharCP unparsed)
{
elementsExampleCreateShapeActive(unparsed);
}
void attachLibrary()
{
char* dllName = "qtdialog.dll";
HMODULE hDLL = LoadLibrary(dllName);
if (hDLL != NULL)
{
typedef int(*pMain)(int, char *[]);
pMain dialog2 = pMain(GetProcAddress(hDLL, "main"));
if (dialog2 != NULL)
{
dialog2(0, 0);
}
typedef void (CALLBACK *PFunCallBack)(FunCallBack);
PFunCallBack SetFunCallBack = (PFunCallBack)GetProcAddress(hDLL, "SetFunCallBack");
if (SetFunCallBack)
{
SetFunCallBack(handleEvent);
}
FreeLibrary(hDLL);
}
else
{
cout << "Cannot find" << dllName << endl;
}
}

Int variable's value won't change after being moved

I've read the basics of move semantics and I did a couple of tests.
Case #1:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string st = "hello";
vector<string> vec;
vec.push_back(st);
cout << st;
cin.get();
}
In this case, the program will not print anything because "hello" has been moved to vector[0].
Case #2:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int num=5;
vector<int> vec;
vec.push_back(num);
cout << num;
cin.get();
}
Why does the program print "5"? I thought num would be 0 or something undefined.
Case #1 should print "hello". If not then your compiler has bug and you should upgrade to a newer version or complain to who ever wrote it.
Case #2 correctly prints "5".
However, if you changed line 10 in case 2 from:
vec.push_back(st);
to:
vec.push_back(std::move(st));
you will get what you expected, a print to console of "" because vector "stole" the value in st.
int is a fundamental type in c++ and trying to "steal" from an int variable doesn't realy work since it does't own any resource.
std::string is a resource owner. It "owns" a char array (this isn't always true, but for simplicity we will pretend it is).
So when we pass std::move(st) to push_back we are calling the T&& overload of push_back which does the "stealing" by calling the move constructor of std::string which releases st's handle and gives it to the newly created std::string inside vec.
But if we called push_back like this: vec.push_back(st); this will not "steal" any thing. Instead, it will call the const T& overload of push_back which just does a simple copy by calling the normal copy constructor of std::string such that we will have st set to "hello" and vec[0] set with its own version of "hello".
Try this code below to see how all this works out:
#include <iostream>
#include <vector>
using namespace std;
struct Foo
{
Foo() // default constructor
{
cout << "Foo()" << endl;
}
Foo(const Foo&) // copy constructor
{
cout << "Foo(const Foo&)" << endl;
}
Foo(Foo&&) // move constructor
{
cout << "Foo(Foo&&)" << endl;
}
Foo& operator=(const Foo&) // copy assignment operator
{
cout << "operator=(const Foo&)" << endl;
return *this;
}
Foo& operator=(Foo&&) // move assignment operator
{
cout << "operator=(Foo&&)" << endl;
return *this;
}
~Foo()
{
cout << "~Foo()" << endl;
}
};
int main()
{
Foo f; // print: Foo();
vector<Foo> vec;
vec.push_back(f); // print: Foo(const Foo&)
vec.push_back(std::move(f)); // print: Foo(Foo&&)
Foo f2; // print: Foo()
f2 = f; // print: operator=(const Foo&)
f2 = std::move(f); // print: operator=(Foo&&)
cin.get();
}

Pass QScopedPointer to function

How can I pass a QScopedPointer object to another function like that:
bool addChild(QScopedPointer<TreeNodeInterface> content){
TreeNode* node = new TreeNode(content);
}
TreeNode:
TreeNode::TreeNode(QScopedPointer<TreeNodeInterface> content)
{
mContent.reset(content.take());
}
I get:
error: 'QScopedPointer::QScopedPointer(const QScopedPointer&) [with T = TreeNodeInterface; Cleanup = QScopedPointerDeleter]' is private
How can I solve it? Thanks!
You can do it by accepting a reference to the pointer - that way you can swap the null local pointer with the one that was passed to you:
#include <QScopedPointer>
#include <QDebug>
class T {
Q_DISABLE_COPY(T)
public:
T() { qDebug() << "Constructed" << this; }
~T() { qDebug() << "Destructed" << this; }
void act() { qDebug() << "Acting on" << this; }
};
void foo(QScopedPointer<T> & p)
{
using std::swap;
QScopedPointer<T> local;
swap(local, p);
local->act();
}
int main()
{
QScopedPointer<T> p(new T);
foo(p);
qDebug() << "foo has returned";
return 0;
}
Output:
Constructed 0x7ff5e9c00220
Acting on 0x7ff5e9c00220
Destructed 0x7ff5e9c00220
foo has returned

How to get human-readable event type from QEvent?

I want to debug event handling code and would like to convert QEvent::Type enum's value to a human-readable string. QEvent has a Q_GADGET macro, so presumably there's a way of pulling that off?
Recent versions of Qt do the right thing when outputting events to the debug stream, so the below isn't neccessary. If you get an error similar to warning C4273: 'operator <<' : inconsistent dll linkage, it means that your version of Qt already supports this without need for the code below.
The Q_GADGET macro adds a QMetaObject staticMetaObject member to the class. The static metaobject's definition is generated by moc, and it - in the case of QEvent - contains the enumeration information.
Below is an example of how to leverage that to give a more reasonable QDebug output of events.
#include <QEvent>
#include <QMetaEnum>
#include <QDebug>
/// Gives human-readable event type information.
QDebug operator<<(QDebug str, const QEvent * ev) {
static int eventEnumIndex = QEvent::staticMetaObject
.indexOfEnumerator("Type");
str << "QEvent";
if (ev) {
QString name = QEvent::staticMetaObject
.enumerator(eventEnumIndex).valueToKey(ev->type());
if (!name.isEmpty()) str << name; else str << ev->type();
} else {
str << (void*)ev;
}
return str.maybeSpace();
}
Use example:
void MyObject::event(QEvent* ev) {
qDebug() << "handling an event" << ev;
}
Q_GADGET and Q_ENUM can be combined to get the following template:
template<typename EnumType>
QString ToString(const EnumType& enumValue)
{
const char* enumName = qt_getEnumName(enumValue);
const QMetaObject* metaObject = qt_getEnumMetaObject(enumValue);
if (metaObject)
{
const int enumIndex = metaObject->indexOfEnumerator(enumName);
return QString("%1::%2::%3").arg(metaObject->className(), enumName, metaObject->enumerator(enumIndex).valueToKey(enumValue));
}
return QString("%1::%2").arg(enumName).arg(static_cast<int>(enumValue));
}
Example:
void MyObject::event(QEvent* ev)
{
qDebug() << ToString(ev->type());
}

Resources