Im currently working with an UI tool (Qt Creator 9.5.9) to create UI Interfaces. While messing with the tool i came across following problem:
The following code is from an automatically generated cpp file which is generated when creating a new project.
At the top there are a few functions which I assume can be used to access and possibly change data points.
I want to use the function SetWriteDP() to write my data to the data points.
/**
// register ids
bool registerReadIds(const QList<unsigned int> &ids);
bool registerReadIds(const QUintSet &ids);
bool registerReadIds(const QUintSet &ids, void (*func)(void*, const QUintSet &));
bool registerWriteIds(const QList<unsigned int> &ids);
bool registerWriteIds(const QUintSet &ids);
// read data point values
unsigned int GetReadDP(const unsigned int &id) const;
int GetReadDPInt(const unsigned int &id) const;
float GetReadDPFloat(const unsigned int &id) const;
QString GetReadDPString(const unsigned int &id) const;
// write data point values
void SetWriteDP(const unsigned int &id, const unsigned int &value);
void SetWriteDP(const unsigned int &id, const int &value);
void SetWriteDP(const unsigned int &id, const float &value);
void SetWriteDP(const unsigned int &id, const QString &value);
// execute sql statement
QSqlQuery execSqlQuery(const QString &query, bool &success) const;
**/
#include "hmi_api.h"
#include "widget.h"
#include "ui_arbaseform.h"
#include <iostream>
HMI_API::HMI_API(QWidget *parent) :
AbstractAPI(parent), m_ui(NULL)
{
Widget *widget = dynamic_cast<Widget *>(parent);
if(!widget) return;
m_ui = widget->ui;
QUintSet readIdsToRegister, writeIdsToRegister;
writeIdsToRegister.insert(10001);
registerReadIds(readIdsToRegister);
registerWriteIds(writeIdsToRegister);
SetWriteDP(100001, 69);
}
I tried using the function in another cpp file in different ways:
HMI_API.SetWriteDP()
HMI_API.Abstract_API.SetWriteDP()
This resulted in this error: expected unqualified-id before . token
AbstractAPI::SetWriteDP()
which resulted in this error: cannot call member function 'void DPObject::SetWriteDP(const unsigned int&, const int&, unsigned int)' without object AbstractAPI::SetWriteDP();
the i tried making a DPObject which resulted in this error: cannot declare variable 'test' to be of abstract type 'DPObject'
Im really at my wits end now how to access this function. Can someone maybe explain to me what happens after "HMI_API::HMI_API(QWidget *parent) :" and why it is possible to use the function in that block and how i can make it possible for me to use this function.
I tried reading the documentation but nowwhere in the documentation this function is ever mentioned.
The function works in the code snippet i posted but doesnt when i want to use it in another function, i know its because of some stuff regarding classes but im dont understand how to work around this in this case.
Thanks in advance!
how i can make it possible for me to use this function.
I might be wrong but from my understanding of C++ you would first have to create an object of the class, in this case that would be
HMI_API *uiName = new HMI_API(some_parent_obj);
With QWidget being your earlier created QWidget, you can then call the function using a .
uiName.SetWriteDP(x,y);
Can someone maybe explain to me what happens after "HMI_API::HMI_API(QWidget *parent) :
after "HMI_API::HMI_API(QWidget *parent)" the class makes it clear that it inherits base functionality from the classes AbstractAPI and m_ui, you can learn more about inheritance here :https://www.learncpp.com/cpp-tutorial/basic-inheritance-in-c/
Afterwards im not sure but it looks like it just creates some basic functionality so you can call the functions using the class.
I found an answer to my problem which in hindsight might have been obvious but i dont know how i should have known.
I was able to use the functions by declaring a new function like this:
void HMI_API::myFunction(int arg1){
my code with the functions i wanted to use
}
I really hope this will help someone that might had some understanding problems as well.
Related
I have a requirement where I want my QLineEdit should accept hexadecimal values ranging from [0 - FFFFF]. can someone help me out with this?
I have tried the below code, but it holds good for only 1 char display.
I don't see any code in your post, but what you're looking for is a validator, derived from the QValidator class. Create a sub-class, say HexValidator, and implement the "validate" method. You check the input string for the allowed characters and range and return the appropriate state.
You then assign the validator to the QLineEdit using the QLineEdit::setValidator method. Note that the QLineEdit doesn't take ownership of the validator, so you need to make sure you delete it separately or give it a parent so that it gets cleaned up when the parent is deleted. You can create a single validator and assign it to multiple fields if needed.
I was wrong in my comment about Qt docs having a Hex spin box example... so I found one in my archive instead. It could be more flexible, but OTOH it's very simple and short. I know this isn't a QLineEdit but maybe it'll help anyway. The QValidator used here could be used in a line edit also.
#ifndef _HEXSPINBOX_H_
#define _HEXSPINBOX_H_
#include <QSpinBox>
#include <QRegularExpressionValidator>
// NOTE: Since QSpinBox uses int as the storage type, the effective editing range
// is +/- 0x7FFF FFFF, so it can't handle a full unsigned int.
// QDoubleSpinBox would be a more suitable base class if a wider range is needed.
class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent = nullptr,
bool showPrefix = false,
const QString &format = QStringLiteral("%x")) :
QSpinBox(parent),
format(format)
{
// Validates hex strings up to 8 chars with or w/out leading "0x" prefix.
// For arbitrary prefix/suffix, the regex could be built dynamically
// in validate(), or override setPrefix()/setSuffix() methods.
const QRegularExpression rx("(?:0[xX])?[0-9A-Fa-f]{1,8}");
validator = new QRegularExpressionValidator(rx, this);
setShowPrefix(showPrefix);
}
public slots:
void setShowPrefix(bool show)
{
if (show)
setPrefix(QStringLiteral("0x"));
else
setPrefix(QString());
}
void setFormat(const QString &text)
{
format = text;
lineEdit()->setText(textFromValue(value()));
}
protected:
QValidator::State validate(QString &text, int &pos) const override
{
return validator->validate(text, pos);
}
int valueFromText(const QString &text) const override
{
return text.toInt(0, 16);
}
QString textFromValue(int value) const override
{
return QString().sprintf(qPrintable(format), value);
}
private:
QRegularExpressionValidator *validator;
QString format;
};
#endif // _HEXSPINBOX_H_
Example:
#include "HexSpinBox.h"
#include <QApplication>
#include <QBoxLayout>
#include <QDialog>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog d;
d.setLayout(new QVBoxLayout);
HexSpinBox* sb = new HexSpinBox(&d);
sb->setMaximum(0xFFFFF);
sb->setValue(0x0A234);
d.layout()->addWidget(sb);
HexSpinBox* sb2 = new HexSpinBox(&d, true, QStringLiteral("%05X"));
sb2->setMaximum(0xFFFFF);
sb2->setValue(0x0A234);
d.layout()->addWidget(sb2);
return d.exec();
}
There is no way to load QTranslator from my own way.
I want to exclude .ts files from architecture of my app. I just want to load my languages from databse, whitch will be update from anywhere. And i don't want to load any files(.ts). Does exists the way somthing like this:
QTranslator::load(QStringList)??? QStringList is a language pairs.
The QTranslator::translate method is virtual - which means you can simply create your own translator that extends QTranslator and override this (and one other) method:
class MyTranslator : public QTranslator
{
public:
MyTranslator(QStringList data, QObject* parent) :
QTranslator(parent)
{
// ...
}
bool isEmpty() const override {
return false; //or use your own logic to determine if data contains translations
}
QString translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1) const override {
// Use the data to somehow find your translation
}
};
I understand your goal. Why don't you get the data from your database, save it as temporary file, load via QTranslator (regular way), then delete the temporary file?
Another option is maybe the overload for:
bool QTranslator::load(const uchar *data, int len, const QString
&directory = QString())
(from: http://doc.qt.io/qt-5/qtranslator.html#load-2 ), which would allow you to load from your own structure without temp file.
Is it possible to create variadic signal and connect generic lambda as slot? I mean something like (say, all definitions of involved functions are visible where needed (e.g. at points of instantiation)):
#include <QCoreApplication>
#include <QObject>
#include <QTime>
class A
: public QObject
{
Q_OBJECT
public :
A(QObject * const parent = Q_NULLPTR)
: QObject{parent}
{ ; }
signals :
template< typename ...Ts >
void infoMessage(Ts... args);
public slots :
void run()
{
emit infoMessage("Started at ", QTime::currentTime());
}
};
#include <QTimer>
#include <QtDebug>
#include "main.moc"
int main(int argc, char * argv [])
{
QCoreApplication a{argc, argv};
A a;
auto printInfoMessage = [&] (auto... args)
{
(qInfo() << ... << args);
};
QObject::connect(&a, SIGNAL(infoMessage), printInfoMessage);
QTimer::singleShot(0, &a, &A::run);
return a.exec();
}
Currently it gives an error message:
AUTOGEN: error: process for main.cpp:18: Error: Template function as signal or slot
moc failed...
Here macro SLOT() instead of &A::infoMessage does not help a lot. Is there any workarounds to overcome this limitation?
I know, that some of the answers will contain a using of std::make_tuple and std::index_sequence stuff. But is there less verbose solution?
There is no direct workaround for having template. On of thea reason is that the moc indexes all signals and slots, and this cannot be done for function templates as function templates will generate several functions depending code that is generally not accessible from the moc.
I don't think you can make it work with tuple and such as these are also templates.
A solution could be to use QVariant and/or QVariantList for your arguments.
Please note that the error is not caused by the QObject::connect line, but the the signal declaration in class A.
Also, you cannot replace SIGNAL() and SLOT() at your will, it is either a signal or a slot, it cannot be both.
And finally you should be using this form:
QObject::connect(&a, &A::infoMessage, printInfoMessage);
And since printInfoMessage is using auto parameters, you might need to force the auto resolution using qOverload:
QObject::connect(&a, &A::infoMessage, qOverload<QVariantList>(printInfoMessage));
I know that I can use QMetaType to create an object without parameters.
Another possible option is to use QMetaObject and call newInstance. But I need to get QMetaObject from something.
I tried to use QMetaType::metaObjectForType, but it always returns null pointer (but QMetaType is able to create the object).
QMetaObject const* metaObject = QMetaType::metaObjectForType(id); // return null pointer
QObject* object = (QObject*)QMetaType::create(id); // create the object
QMetaObject const* metaObject = object->metaObject(); // return not-null pointer
UPDATE:
I think the question is why metaObjectForType does not work for me.
The class is registered with qRegisterMetaType, also Q_DECLARE_METATYPE and Q_OBJECT are applied.
First of all, to pass parameters to methods, you needs some kind of reflection framework beyond plain C++. With Qt, the obivous choice is Qt Meta Object system with it's QMetaObject, though then you must derive your classes for QObject. After that, you need to do two things:
1. make constructor invokable
Signals and slots are invokable by default, but any other method you want to invoke through the meta object system needs to be explicitly marked as such. Example myqobject.h:
#ifndef MYQOBJECT_H
#define MYQOBJECT_H
#include <QObject>
class MyQObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyQObject(QObject *parent = 0); // tested with empty constructor in .cpp
};
#endif // MYQOBJECT_H
2. create your own mapping from class name string to the QMetaObject
QMetaType doc says: "any class or struct that has a public default constructor, a public copy constructor, and a public destructor can be registered." This rules out QObject, because they can't have copy constructors. You need to create your own mapping from name to the meta object. An example shown in this main.cpp:
#include <QCoreApplication>
#include <QtCore>
#include "myqobject.h"
// a global map for mapping strings to QMetaObjects,
// you need header file like this if you want to access it from other .cpp files:
//
// #include <QHash>
// #include <QString>
// class QMetaObject; // forward declaration, enough when only pointer is needed
// extern QHash<QString, const QMetaObject*> metaObjs;
//
QHash<QString, const QMetaObject*> metaObjs;
// optional: create a macro to avoid typing class name twice,
// #c surrounds macro argument with double quotes converting it to string
#define METAOBJS_INSERT(c) (metaObjs.insert(#c, &c::staticMetaObject))
int main()
{
METAOBJS_INSERT(MyQObject);
const QMetaObject *meta = metaObjs["MyQObject"];
qDebug() << "Class name from staticMetaObject: " << meta->className();
QObject *o = meta->newInstance(); // add constructor arguments as needed
MyQObject *mo = qobject_cast<MyQObject*>(o);
if (mo) qDebug() << "Class name from object instance: " << mo->metaObject()->className();
else qDebug() << "Instance creation failed or created wrong class!";
return 0;
}
If you do not want to use QObject, then you need to come up with some similar (probably lighter-weight and without separate compiler step) mechanism of your own.
I had the same problem.
The solution is in two steps:
call qRegisterMetaType() with a pointer type:
qRegisterMetaType<MyClass*>(). this will give you a valid
QMetaObject with call of QMetaType::metaObjectForType(id);
make your constructor Q_INVOKABLE. this will enable a valid call to
QMetaObject::newInstance
And also be sure that your class is derived from QObject and have a Q_OBJECT macro in it.
class A : public QObject
{
Q_OBJECT
public:
A(const A&) {}; // dummy copy contructor that do nothing to disable error message
Q_INVOKABLE A(int test_value = 99999) : TestValue(test_value) {};
int TestValue;
};
Q_DECLARE_METATYPE(A);
int main(int argc, char *argv[])
{
qRegisterMetaType<A>(); //you do not need this
qRegisterMetaType<A*>();//the real registration
int type_id_for_A = QMetaType::type("A"); // valid type id (in my case = 403)
const QMetaObject *meta_object_for_A = QMetaType::metaObjectForType(type_id_for_A); // returns NULL
int type_id_for_A_ptr = QMetaType::type("A*"); // valid type id (in my case = 404)
const QMetaObject *meta_object_for_A_tr = QMetaType::metaObjectForType(type_id_for_A_ptr); // returns NOT NULL
A* A_obj= dynamic_cast<A*>(meta_object_for_A_tr->newInstance(Q_ARG(int, 12345)));
int test_value = A_obj->TestValue; // returns 12345, not 99999
}
I am adding QScript to my Qt application. I have already added metadata and use some of the metadata functions to interrogate through C++ code. That works fine - I can navigate the object heirarchy and print out values (including enums).
But, I can't seen to get enums working in Qt script.
I have my class...
class HalPin : public QObject
{
Q_OBJECT
public:
enum EHalPinType
{
Bit = HAL_BIT,
Float = HAL_FLOAT,
S32 = HAL_S32,
U32 = HAL_U32
};
enum EHalPinDirection
{
In = HAL_IN,
Out = HAL_OUT,
IO = HAL_IO
};
Q_ENUMS(EHalPinType)
Q_ENUMS(EHalPinDirection)
public:
explicit HalPin(QObject *parent = 0);
signals:
public slots:
};
Q_DECLARE_METATYPE(HalPin::EHalPinType)
Q_DECLARE_METATYPE(HalPin::EHalPinDirection)
Q_DECLARE_METATYPE(HalPin*)
I have another class that has a method that takes the enums as arguments...
class EmcHal : public QObject
{
Q_OBJECT
public:
explicit EmcHal(QString moduleName, QObject *parent = 0);
signals:
public slots:
QObject *createHalPin( HalPin::EHalPinType, HalPin::EHalPinDirection, QString name );
};
This class is exposed in another class - sorry I should have simplified the example. If I write the following jscript code,
var nextPagePin1 = Emc.hal.createHalPin();
I get an error I expect...
SyntaxError: too few arguments in call to createHalPin(); candidates are createHalPin(HalPin::EHalPinType,HalPin::EHalPinDirection,QString)
So, it appears that the enum types are known to qtscript.
What I am struggling to do is to set the enum arguments from jscript. I've tried many combinations...
Bit
EHalPinType.Bit
HalPin.EHalPinType.Bit
and many more.
If I try to use integers, I get...
TypeError: cannot call createHalPin(): argument 1 has unknown type `HalPin::EHalPinType' (register the type with qScriptRegisterMetaType())
which seems to imply jscript doesn't know about my enums.
Any suggestions?
Do I need to use qRegisterMetaType or qScriptRegisterMetaType to access my enums? The documentation doesn't suggest I need to do this. Do I need to implement the converter functions for the qScriptRegisterMetaType method.
Or is my syntax just wrong for the jscript?
Does someone have a working example?
Thanks,
Frank
To answer my own question...
Well, not so much an answer to why, but a "meh, this works" example...
As I mentioned above, I wasn't able to get the enums working in both the metadata and jscript at the same time using the qt macros. Even though the enum appeared in qscript (I checked in the browser of the script debugger), it didn't evaluate to the correct integer.
I had to add a QMetaObject for the enum. That gave me the enum items, and correct integer values.
But that still gave me the unknown type error, so I needed to use qScriptRegisterMetaType() to register conversion functions for the types.
This is the class I use for 1 enum. It is as minimal as I can make it. I should be able to use macros to shrink it down a bit more, but there are limitations on what can be macroised, because of the qt moc requirements.
#include <QObject>
#include <QMetaType>
#include <QScriptEngine>
#include "hal.h"
class CEHalPinType : public QObject
{
Q_OBJECT
public:
explicit CEHalPinType(QObject *parent = 0) : QObject(parent) {}
explicit CEHalPinType(const CEHalPinType &other) : QObject(other.parent()) {}
virtual ~CEHalPinType() {}
enum EHalPinType
{
Bit = HAL_BIT,
Float = HAL_FLOAT,
S32 = HAL_S32,
U32 = HAL_U32
};
Q_ENUMS( EHalPinType )
private:
static QScriptValue toScriptValue(QScriptEngine *engine, const EHalPinType &s)
{
return engine->newVariant((int)s);
}
static void fromScriptValue(const QScriptValue &obj, EHalPinType &s)
{
s = (EHalPinType)obj.toInt32();
}
static QScriptValue qscriptConstructor( QScriptContext *context, QScriptEngine *engine )
{
return engine->newQObject( new CEHalPinType(context->argument(0).toQObject()), QScriptEngine::ScriptOwnership);
}
public:
static void Init( const char *name, QScriptEngine *engine )
{
qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue);
QScriptValue metaObject = engine->newQMetaObject( &staticMetaObject, engine->newFunction(qscriptConstructor) );
engine->globalObject().setProperty( name, metaObject );
}
};
Q_DECLARE_METATYPE(CEHalPinType::EHalPinType)
And my jscript looks like...
var nextPagePin = Emc.hal.createHalPin(EHalPinType.Bit,EHalPinDirection.In,"nexis.NextPage");
Oops. I jumped the gun on this one. Although the scripting worked, I broke the ability to convert enums to strings using the qmetaobject data.
And there doesn't seem to be an automatic way of doing it.
The problem is, I moved the enums out of the class where the properties that used the enums were defined. Although the Q_ENUMS and Q_PROPERTY compile, if I use the QMetaProperty to to read an enum, it doesn't work. The QVariant that is returned shows the correct data type, "CEHalPinType::EHalPinType", but it fails the isEnum() test and canConvert(QVariant::String) fails too. This is because when the qmetaobject code goes searching for the enum type, it only looks in the current class and its derived classes. It doesn't search other classes. Which is why it worked when the enum was a member of the class which also had the properties.
My work around, as suggested elsewhere, was to create my own QMap of known enums, storing the string name to qmetaobject mapping. I used a templated base class and used T::staticMetaObject to get the meta object.