When calling QMetaMethod::invoke() on a method that contains default arguments, the invoke fails.
class MyClass : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyClass() : QObject(nullptr){}
public slots:
int MyMethod(int a = 0)
{
return a*2;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass* object = new MyClass();
QMetaObject *metaObject = object->metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
if(metaObject->method(i).name() == "MyMethod")
{
int returnVal;
//returns false
metaObject->method(i).invoke(object,
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
break;
}
}
return a.exec();
}
If I pass an int as the first argument, then it runs fine. Is there any way to retrieve the default values of the arguments for the method so that way I can pass those instead of passing nothing?
I was about to manually store the defaults within the class for each method, but this is an ugly hack.
Thanks for your time.
If you review the generated .moc you see the following:
void MyClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
MyClass *_t = static_cast<MyClass *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: { int _r = _t->MyMethod((*reinterpret_cast< int(*)>(_a[1])));
if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
case 1: { int _r = _t->MyMethod();
if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
default: ;
}
}
}
As you can see there are 2 methods generated and that can be verified by printing the methods with that name:
#include <QCoreApplication>
#include <QMetaMethod>
#include <QDebug>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
public slots:
int MyMethod(int a = 0){ return a*2;}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass object;
const QMetaObject *metaObject = object.metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
QMetaMethod method = metaObject->method(i);
if(method.name() == QByteArray("MyMethod"))
qDebug()<<i<<method.name();
};
return 0;
}
#include "main.moc"
Output:
5 "MyMethod"
6 "MyMethod"
So what sets them apart? The number of parameters, so you must add a filter that is the parameterCount().
#include <QCoreApplication>
#include <QMetaMethod>
#include <QDebug>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
public slots:
int MyMethod(int a = 0){ return a*2;}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass object;
const QMetaObject *metaObject = object.metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
QMetaMethod method = metaObject->method(i);
if(method.name() == QByteArray("MyMethod") && method.parameterCount() == 0)
{
int returnVal;
bool status = method.invoke(&object,
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
Q_ASSERT(status);
qDebug()<<returnVal;
}
};
return 0;
}
#include "main.moc"
Output:
0
On the other hand if you want to avoid this kind of problems you can use QMetaObject::invokeMethod() that makes that verification:
MyClass object;
int returnVal;
bool status = QMetaObject::invokeMethod(&object,
"MyMethod",
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
Q_ASSERT(status);
qDebug()<<returnVal;
Related
I wrote this database front-end program with Qt and I used a C++ function to return the result of each query. However, I'm not able to assign the results to a list view.
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QString> val READ val WRITE setVal NOTIFY valChanged)
public:
void setVal(QList<QString>);
QList<QString> val() const;
explicit MyObject(QObject *parent = nullptr);
Q_INVOKABLE int registernew(int Sno=0, QString Name="NULL",long long PhoneNo=0, QString Country="NULL",QString State="NULL", QString District="NULL", int PhoneLine=0,long long Whatsapp = 0);
Q_INVOKABLE int querydistrict(QString);
signals:
void valChanged();
private:
QList<QString> m_val;
};
#endif // MYOBJECT_H
MyObject.cpp
.............. void MyObject::setVal(QList<QString> list)
{
if(list != m_val)
{
m_val = list;
emit valChanged();
}
}
QList<QString> MyObject::val() const
{
return m_val;
}....................
main.cpp
#include <QGuiApplication>
#include<QQmlApplicationEngine>
#include "myobject.h"
#include<QQuickView>
#include<QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
///delete later!!
qmlRegisterType<MyObject>("io.qt.examples.MyObject", 1, 0, "MyObject");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
page2.ui.qml
ScrollView {
id: scrollView
anchors.rightMargin: 20
anchors.leftMargin: 20
anchors.bottomMargin: 40
anchors.topMargin: 200
anchors.fill: parent
ListView {
id: listView
model: myobject.val
delegate: ItemDelegate {
text: modelData
}
}
}
Where am i going wrong? The list is never updated with values when i run the program. It is always blank. But the variable m_val, when I return it in MyObject.cpp and use qDebug to output it, outputs relevant strings.
You should not use QList<QString> but QStringList as it is registered. On the other hand if you are going to establish the data from C ++ since you indicate that it is the result of a query then that Q_PROPERTY is not of being written in QML. Finally, although it is not an error, it is better to register the classes before creating the QXApplication.
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList val READ val NOTIFY valChanged)
public:
explicit MyObject(QObject *parent = nullptr);
QStringList val() const;
void setVal(QStringList val);
// other methods
signals:
void valChanged(QStringList val);
private:
QStringList m_val;
};
#endif // MYOBJECT_H
myobject.cpp
#include "myobject.h"
// other methods
QStringList MyObject::val() const{
return m_val;
}
void MyObject::setVal(QStringList val)
{
if (m_val == val)
return;
m_val = val;
emit valChanged(m_val);
}
main.cpp
// ...
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyObject>("io.qt.examples.MyObject", 1, 0, "MyObject");
QGuiApplication app(argc, argv);
// ...
So when you do your query you must pass the result to setVal:
setVal(your_QStringList);
I'm using the Qt settings and it saves the object into a file. it saves to a file called sessionrc.
Now I'm trying to load the object from the settings and save it back.
The problem is I can not identify the object from the settings, so that I can load all the profiles that are saved.
I'm using the following load and save functionality
void ProfileManager::loadFrom(Settings &set, bool ownGroup)
{
qDebug()<<"LOAD";
foreach (const QString &group, set.childGroups()) {
if(group == "Profile")
{
Profile *profile = new Profile();
profile->setObjectName(group);
profile->loadFrom(set);
m_Profiles << profile;
}
}
EraObject::staticLoadFrom(set, this);
}
void ProfileManager::saveTo(Settings &set, bool ownGroup, bool force)
{
EraObject::staticSaveTo(set, this, ownGroup, force);
foreach(Profile * profile, m_Profiles) {
profile->saveTo(set);
}
}
The current setting file is
[www]
Ta=20
Te=48
Texp=38
lim1=0
lim2=0
offset=0
profilename=www
[www] is the profile that is saved. but I have many of it. How would I load it back and save it correctly
// main.cpp
#include <QCoreApplication>
#include <QSettings>
#include <QVector>
#include <QDebug>
#include <QMetaProperty>
class Profile : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName )
Q_PROPERTY(QString title READ title WRITE setTitle )
public:
explicit Profile(QObject *parent = 0) : QObject(parent) {
}
QString name() const {
return name_;
}
void setName(QString name) {
name_ = name;
}
QString title() const {
return title_;
}
void setTitle(QString title) {
title_ = title;
}
void save(QSettings& settings) const {
for(int i=0; i<metaObject()->propertyCount(); ++i) {
const auto& p = metaObject()->property(i);
if(p.isStored(this)) {
settings.setValue(p.name(), property(p.name()));
}
}
}
void load(QSettings& settings) {
for(int i=0; i<metaObject()->propertyCount(); ++i) {
const auto& p = metaObject()->property(i);
if(p.isStored(this)) {
setProperty(p.name(), settings.value(p.name()));
}
}
}
private:
QString name_;
QString title_;
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QObject garbageCollector;
QVector<Profile*> profiles;
{
Profile* p1 = new Profile(&garbageCollector);
p1->setName("profilename1");
p1->setTitle("Profile 1");
Profile* p2 = new Profile(&garbageCollector);
p2->setName("profilename2");
p2->setTitle("Profile 2");
profiles.append(p1);
profiles.append(p2);
}
QSettings s("profiles.ini", QSettings::IniFormat);
// write profiles
{
s.beginGroup("profiles");
foreach(const Profile*p, profiles) {
s.beginGroup(p->name());
p->save(s);
s.endGroup();
}
s.endGroup();
s.sync(); // force write
}
// read profiles
{
s.beginGroup("profiles");
foreach(const QString& g, s.childGroups()) {
Profile p;
s.beginGroup(g);
p.load(s);
s.endGroup();
qDebug() << p.name();
qDebug() << p.title();
}
s.endGroup();
}
return 0;
}
Given a pointer to a method of a QObject derived class: Is there a way of getting the QMetaMethod of the method the pointer is pointing to? I am basically looking for a function like QMetaMethod::fromSignal, but for slots.
Note:
I tried getting the index through static_metacall with QMetaObject::IndexOfMethod and using that for QMetaObject::method:
void(Class::*method)() = &Class::method;
int methodIndex = -1;
void *metaArgs[] = {&methodIndex, reinterpret_cast<void **>(&method)};
const QMetaObject mo = Class::staticMetaObject;
mo.static_metacall(QMetaObject::IndexOfMethod, 0, metaArgs);
qDebug() << methodIndex;
// QMetaMethod mm = mo.method(methodIndex)
The output is always -1.
At the moment, the only solution is to modify moc. The patch is rather trivial, though:
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index d831edf..7dcefcc 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
## -1311,15 +1311,12 ## void Generator::generateStaticMetacall()
isUsed_a = true;
}
- }
- if (!cdef->signalList.isEmpty()) {
- Q_ASSERT(needElse); // if there is signal, there was method.
fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n");
fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n");
fprintf(out, " void **func = reinterpret_cast<void **>(_a[1]);\n");
bool anythingUsed = false;
- for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) {
- const FunctionDef &f = cdef->signalList.at(methodindex);
+ for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) {
+ const FunctionDef &f = methodList.at(methodindex);
if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
continue;
anythingUsed = true;
The following then works as expected:
// https://github.com/KubaO/stackoverflown/tree/master/questions/metamethod-lookup-24577095
#include <QtCore>
class MyObject : public QObject {
Q_OBJECT
public:
Q_SLOT void aSlot() {}
Q_SLOT void aSlot2(int) {}
Q_SLOT int aSlot3(int) { return 0; }
Q_SIGNAL void aSignal();
Q_SIGNAL void aSignal2(int);
};
template <typename Func> int indexOfMethod(Func method)
{
using FuncType = QtPrivate::FunctionPointer<Func>;
int methodIndex = -1;
void *metaArgs[] = {&methodIndex, reinterpret_cast<void **>(&method)};
auto mo = FuncType::Object::staticMetaObject;
mo.static_metacall(QMetaObject::IndexOfMethod, 0, metaArgs);
return methodIndex;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << indexOfMethod(&MyObject::aSlot)
<< indexOfMethod(&MyObject::aSlot3) << indexOfMethod(&MyObject::aSignal2);
return 0;
}
#include "main.moc"
I have written a QT - webkit application. this application fires a callback when my pSeudo driver gets the character 'l'. However, the application crashes during a firecallback - it says - QObject::setParent: Cannot set parent, new parent is in a different thread. I don't know to fix this, I tried doing moveToThread, but it doesn't help. Please help me here.
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject {
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JS_ADDED();
void loadFinished(bool);
private:
};
class DemoThread : public QThread {
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
private :
MyJavaScriptOperations *m_pJavascriptOp;
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
read(filedesc,&buff, 1);
qDebug()<<"The code received is "<< buff;
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
m_pJavascriptOp->firecb();
}
qDebug()<<"Running Thread.";
sleep(6);
}
}
void MyJavaScriptOperations::JS_ADDED()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
// firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JS_ADDED()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
jvs->moveToThread(thread);
thread->start();
return a.exec();
}
#include "main.moc"
This is the crash-error I get -
./QT_DEMO
MyJavaScriptOperations::MyJavaScriptOperations()
loaded the Generic plugin
The code received is 156
Running Thread.
The code received is 166
void MyJavaScriptOperations::firecb()
QObject::setParent: Cannot set parent, new parent is in a different thread
There are few articles on internet how to make multithreaded applications in Qt. Best explanation can be found here:
http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/
You could read also other articles:
https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Well, I got a solution for my problem. Please tell me, am I complicating the solution.
I am using signal and slot. The thread will emit the signal and the slot of other class will emit the callback to the Qtwebkit - the javascript function. IS it right?
Because, I have suggestion using event loop - exec().
#include <QtGui/QApplication>
#include <QApplication>
#include <QDebug>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>
#include <QThread>
/** for reading my driver **/
#include <unistd.h>
#include <fcntl.h>
class DemoThread;
class MyJavaScriptOperations : public QObject
{
Q_OBJECT
public:
QWebView *view;
DemoThread *m_pDemoThread;
MyJavaScriptOperations();
void firecb();
bool slot_installed;
signals:
void alert_script_signal();
public slots:
void JsAdded();
void alertReceived();
void loadFinished(bool);
private:
};
class DemoThread : public QThread
{
Q_OBJECT
private:
MyJavaScriptOperations *m_pJavascriptOp;
public:
DemoThread( MyJavaScriptOperations *pJavascriptOp);
protected:
void run();
signals:
void alertSendSignal();
};
DemoThread::DemoThread(MyJavaScriptOperations *pJavascriptOp):m_pJavascriptOp(pJavascriptOp)
{
connect(this, SIGNAL(alertSendSignal()), m_pJavascriptOp, SLOT(alertReceived()));
}
void DemoThread:: run()
{
int filedesc = open("/dev/pSeudoDrv", O_RDONLY);
if(filedesc < 0)
{
qDebug()<<"Couldn't open Driver.";
}
unsigned char buff;
while(1)
{
if( 1 != read(filedesc,&buff, 1))
{
qDebug()<<"Read Invalid Data";
}
qDebug()<<"The code received is "<< buff;
/** In my laptop, the 166 means the character 'l' **/
if ( (m_pJavascriptOp->slot_installed == true) && (buff == 166))
{
emit alertSendSignal();
}
qDebug()<<"Running Thread.";
}
}
void MyJavaScriptOperations::JsAdded()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", this);
}
void MyJavaScriptOperations::loadFinished(bool oper)
{
qDebug()<<__PRETTY_FUNCTION__<< oper;
slot_installed = true;
}
void MyJavaScriptOperations::alertReceived()
{
qDebug()<<"Sending Firecallback now";
firecb();
}
void MyJavaScriptOperations::firecb()
{
qDebug()<<__PRETTY_FUNCTION__;
view->page()->mainFrame()->evaluateJavaScript("JavaScript_function()");
}
MyJavaScriptOperations::MyJavaScriptOperations()
{
qDebug()<<__PRETTY_FUNCTION__;
view = new QWebView();
view->resize(400, 500);
connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(JsAdded()));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
view->load(QUrl("./index.html"));
view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyJavaScriptOperations *jvs = new MyJavaScriptOperations;
DemoThread *thread = new DemoThread(jvs);
thread->start();
return a.exec();
}
#include "main.moc"
Sorry for basic question. I'm trying to show json in QPlainTextWidget. I have api function which have console output and contains all needed data. Looks like that:
int iperf_run_server(struct iperf_test *test)
{
int result, s, streams_accepted;
fd_set read_set, write_set;
struct iperf_stream *sp;
struct timeval now;
struct timeval* timeout;
......
if (test->json_output)
if (iperf_json_start(test) < 0)
return -1;
if (test->json_output) {
cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
} else if (test->verbose) {
iprintf(test, "%s\n", version);
iprintf(test, "%s", "");
fflush(stdout);
printf("%s\n", get_system_info());
}
.....
cleanup_server(test);
if (test->json_output) {
if (iperf_json_finish(test) < 0)
return -1;
}
....
return 0;
}
For now I have first thread with my gui, and second thread, contains class which run this function on a signal. All things works normally, but i'm not fully understand, how I can "stop" iperf_run_server for "reading/buffering" output, without any changes in api.
The simplest thing to do would be to collect each message in a string, and emit a signal from the object running in the second thread. You can connect that signal to a slot in an object in the GUI thread.A zero-timeout timer is invoked each time the event loop is done processing other events - it is a useful mechanism to leverage to run things "continuously".
For example:
#include <QApplication>
#include <QPlainTextEdit>
#include <QThread>
#include <QBasicTimer>
#include <QTextStream>
//! A thread that's always safe to destruct.
class Thread : public QThread {
private:
// This is a final class.
using QThread::run;
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() {
quit();
wait();
}
};
class IperfTester : public QObject {
Q_OBJECT
struct Test { int n; Test(int n_) : n(n_) {} };
QList<Test> m_tests;
QBasicTimer m_timer;
public:
IperfTester(QObject * parent = 0) : QObject(parent) {
for (int i = 0; i < 50; ++i) m_tests << Test(i+1);
}
//! Run the tests. This function is thread-safe.
Q_SLOT void runTests() {
QMetaObject::invokeMethod(this, "runTestsImpl");
}
Q_SIGNAL void message(const QString &);
private:
Q_INVOKABLE void runTestsImpl() {
m_timer.start(0, this);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
if (m_tests.isEmpty()) {
m_timer.stop();
return;
}
runTest(m_tests.first());
m_tests.removeFirst();
}
void runTest(Test & test) {
// do the work
QString msg;
QTextStream s(&msg);
s << "Version:" << "3.11" << "\n";
s << "Number:" << test.n << "\n";
emit message(msg);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPlainTextEdit log;
// This order is important: the thread must be defined after the object
// to be moved into the thread.
IperfTester tester;
Thread thread;
tester.moveToThread(&thread);
thread.start();
log.connect(&tester, SIGNAL(message(QString)), SLOT(appendPlainText(QString)));
log.show();
tester.runTests();
return a.exec();
// Here, the thread is stopped and destructed first, following by a now threadless
// tester. It would be an error if the tester object was destructed while its
// thread existed (even if it was stopped!).
}
#include "main.moc"