How can i store Custom types in QSettings? - qt

From API docs:
Custom types registered using qRegisterMetaType() and
qRegisterMetaTypeStreamOperators() can be stored using QSettings.
How can I do that? I get the error:
too few template-parameter-lists at qRegisterMetaTypeStreamOperators
My code:
class LineUser {
public:
int uId;
QString passwd;
qint8 statusType;
};
Q_DECLARE_METATYPE(LineUser)
QDataStream &operator<<(QDataStream &out, const LineUser &myObj) {
out<<myObj.uId<<myObj.passwd<<myObj.statusType;
return out;
}
QDataStream &operator>>(QDataStream &in, LineUser &myObj) {
in>>myObj.uId>>myObj.passwd>>myObj.statusType;
return in;
}
qRegisterMetaTypeStreamOperators<LineUser>("LineUser");

qRegisterMetaTypeStreamOperators is a function, not a macro.
You need to call it from a .cpp file, e.g. in your main() method

Related

Is it possible to create binding proxy in qt qml?

i have a cpp struct which have so much fields
struct CloudMusicSongList{ ... };
and i want use it in qml in order input some infomation to it's instance, but i don't want to create a qobject derived class,and create a lot of qproperty ...
i seache in google and get this code snippt from this blog
class ListItem : public QObject
{
Q_OBJECT
public :
ListItem(QObject *parent = 0) : QObject(parent) {}
virtual ~ListItem() {}
virtual int id() const = 0;
virtual QVariant data(int role) const = 0;
virtual QHash<int, QByteArray> roleNames() const = 0;
virtual void triggerItemUpdate() {emit dataChanged();}
signals:
void dataChanged();
};
i just try following code but even not print called data method
defined.h
template <typename T>
class bindingProxy : public bindingProxyBase
{
public:
bindingProxy(QObject* parent = 0)
: bindingProxyBase(parent)
{
}
T tm;
virtual ~bindingProxy()
{
}
virtual QVariant data(int role)
{
qDebug() << "called data method";
QVariant qv;
auto fucs = Prism::Reflection::get_md<T>();
if (role == Qt::DisplayRole)
fucs[0].getFunc(tm, 0, qv);
else
fucs[role].getFunc(tm, role, qv);
return qv;
}
QHash<int, QByteArray> roleNames() const
{
static QHash<int, QByteArray> roles{};
if (roles.count() == 0)
{
for (Prism::Reflection::PrismMetaData<T>& item : Prism::Reflection::get_md<T>())
{
roles[item.offset] = item.name;
}
}
return roles;
}
bool setData(QVariant& value, int role = Qt::EditRole)
{
Prism::Reflection::get_md<T>()[role].setFunc(tm, role, value);
triggerItemUpdate();
return true;
}
};
bodyViewModel.cpp
#include ....
...
..
Q_DECLARE_METATYPE(bindingProxy<CloudMusicSongList>*);
class BodyViewModel : public QObject
{
Q_PROPERTY(bindingProxy<CloudMusicSongList>* editSongListModel READ editSongListModel WRITE setEditSongListModel NOTIFY editSongListModelChanged)
...
...
...
uing.qml
Button {
id:btn_tst
text: BodyViewModelCpp.editSongListModel.name + "hbb"
onClicked: BodyViewModelCpp.editSongListModel.name = "3232"
}
button look like this image after run app rung
i don't know whether it is the X Y problem.
somebody can help? thanks
i want create a bindingProxy template class which twoway bindable in qml
i think it should equivalence dynamicObject in wpf
Based on CloudMusicSongList I'm assuming the thing that you have is a list/array. There are quite a number of ways of exposing this to QML.
QQmlListProperty - https://doc.qt.io/qt-6/qqmllistproperty.html
QAbstractListModel - https://doc-snapshots.qt.io/qt6-dev/qabstractlistmodel.html
QVariant, QVariantList, and/or QVariantMap - https://doc-snapshots.qt.io/qt6-dev/qvariant.html
QObject / Q_OBJECT - create a new QML component and register it
Q_GADGET - register your C++ structure (good for access the class in a signal, but, QML/JS cannot copying it into a Javascript object, only good for the duration of the signal)
QQmlListProperty is a list of QObjects. It requires the underlying QObject be registered, but, allows you to create/manage and walk through a list of the results. The list is owned, usually by another QObject. e.g. we could have a QmlFile object, then implement a QmlFileSystem object that can return a list of QmlFiles.
QAbstactListModel can be used in places where a model is required, e.g. ListView.
QVariant, QVariantList and QVariantMap is the simplest way of creating a Javascript object in QML. The advantage is you do not have to register anything, but, the disadvantage is you do not get intellisense.
QVariant getBooks() {
QVariantList books;
QVariantMap book1;
book1["title"] = QString("Lord of the Rings");
QVariantMap book2;
book2["title"] = QString("The Hobbit");
books.append(book2);
return books;
// results in QML/JS:
// [ { "title": "Lord of the Rings" },
// { "title" : "The Hobbit" } ]
}

How to load QTranslator from database?

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.

Qt tr() translation with static members and namespaces

I'm working on translation our Qt GUI project.
*.ts file is generated successfully.
I filled *.ts file with translations using Qt Linguist.
But at runtime, translation with namespaces and static members does not work.
Other translations (when tr() method calls in class, that are inherited from QObject) works ok.
I have the following code (translation does not work):
Example with namespaces:
// example with namespaces
// declaration in header
namespace Error
{
namespace RadionetworkInput
{
QString alreadyInUse = QT_TR_NOOP("already in use");
char requestFailed[] = QT_TR_NOOP("request failed");
}
}
Usage in cpp:
// usage in cpp code0
QString error0 = Error::RadionetworkInput::alreadyInUse;
QString error1 = tr(Error::RadionetworkInput::requestFailed);
Example with static members:
// example with static members
// declaration in header
namespace Error
{
class RadionetworkInput
{
public:
static const QString alreadyInUse;
static const char requestFailed[];
}
QString Error::RadionetworkInput::alreadyInUse = QT_TR_NOOP("already in use");
char Error::RadionetworkInput::requestFailed[] = QT_TR_NOOP("request failed");
}
Usage in cpp code
// usage in cpp code
QString error0 = Error::RadionetworkInput::alreadyInUse;
QString error1 = tr(Error::RadionetworkInput::requestFailed);
Example, with working translation:
class ViewNetwork : public QObject
{
Q_OBJECT
public:
explicit ViewNetwork(QString name = tr("New Radionetwork"));
};
Usage in code:
ViewNetwork::ViewNetwork(QString name)
{
QString dummy = name;
}
Using QObject::tr() instead QT_TR_NOOP() macro does not help.
Problem is your understanding how it works. With static members variables they are initialized before main function starts.
This means two thing:
they are initialized before QApplication object is created and before yuo load translation files! (I'm surprised that this didn't lead to a crash)
value is calculated only once (not updated if translation changes)!
What you should do? Just change static variable to functions:
// example with namespaces
// declaration in header
namespace Error
{
namespace RadionetworkInput
{
QString alreadyInUse() { return QT_TR_NOOP("already in use"); }
QString requestFailed() { return QT_TR_NOOP("request failed"); }
}
}
and
// example with static members
// declaration in header
namespace Error
{
class RadionetworkInput
{
public:
static QString alreadyInUse();
static QString requestFailed();
}
QString Error::RadionetworkInput::alreadyInUse() { return QT_TR_NOOP("already in use"); }
QString Error::RadionetworkInput::requestFailed() { return QT_TR_NOOP("request failed"); }
}
Returning translation as char[] is pointless, co I've corrected that also (I don't know why you did it).

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());
}

How to use a QFile with std::iostream?

Is it possible to use a QFile like a std::iostream? I'm quite sure there must be a wrapper out there. The question is where?
I have another libs, which requires a std::istream as input parameter, but in my program i only have a QFile at this point.
I came up with my own solution using the following code:
#include <ios>
#include <QIODevice>
class QStdStreamBuf : public std::streambuf
{
public:
QStdStreamBuf(QIODevice *dev) : std::streambuf(), m_dev(dev)
{
// Initialize get pointer. This should be zero so that underflow is called upon first read.
this->setg(0, 0, 0);
}
protected:
virtual std::streamsize xsgetn(std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->read(str, n);
}
virtual std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->write(str, n);
}
virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*__mode*/)
{
switch(dir)
{
case std::ios_base::beg:
break;
case std::ios_base::end:
off = m_dev->size() - off;
break;
case std::ios_base::cur:
off = m_dev->pos() + off;
break;
}
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type off, std::ios_base::openmode /*__mode*/)
{
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::int_type underflow()
{
// Read enough bytes to fill the buffer.
std::streamsize len = sgetn(m_inbuf, sizeof(m_inbuf)/sizeof(m_inbuf[0]));
// Since the input buffer content is now valid (or is new)
// the get pointer should be initialized (or reset).
setg(m_inbuf, m_inbuf, m_inbuf + len);
// If nothing was read, then the end is here.
if(len == 0)
return traits_type::eof();
// Return the first character.
return traits_type::not_eof(m_inbuf[0]);
}
private:
static const std::streamsize BUFFER_SIZE = 1024;
std::streambuf::char_type m_inbuf[BUFFER_SIZE];
QIODevice *m_dev;
};
class QStdIStream : public std::istream
{
public:
QStdIStream(QIODevice *dev) : std::istream(m_buf = new QStdStreamBuf(dev)) {}
virtual ~QStdIStream()
{
rdbuf(0);
delete m_buf;
}
private:
QStdStreamBuf * m_buf;
};
I works fine for reading local files. I haven't tested it for writing files. This code is surely not perfect but it works.
I came up with my own solution (which uses the same idea Stephen Chu suggested)
#include <iostream>
#include <fstream>
#include <cstdio>
#include <QtCore>
using namespace std;
void externalLibFunction(istream & input_stream) {
copy(istream_iterator<string>(input_stream),
istream_iterator<string>(),
ostream_iterator<string>(cout, " "));
}
ifstream QFileToifstream(QFile & file) {
Q_ASSERT(file.isReadable());
return ifstream(::_fdopen(file.handle(), "r"));
}
int main(int argc, char ** argv)
{
QFile file("a file");
file.open(QIODevice::WriteOnly);
file.write(QString("some string").toLatin1());
file.close();
file.open(QIODevice::ReadOnly);
std::ifstream ifs(QFileToifstream(file));
externalLibFunction(ifs);
}
Output:
some string
This code uses std::ifstream move constructor (C++x0 feature) specified in 27.9.1.7 basic_ifstream constructors section of Working Draft, Standard for Programming Language C++:
basic_ifstream(basic_ifstream&& rhs);
Effects: Move constructs from the
rvalue rhs. This is accomplished by
move constructing the base class, and
the contained basic_filebuf. Next
basic_istream::set_rdbuf(&sb) is called to install the contained
basic_filebuf.
See How to return an fstream (C++0x) for discussion on this subject.
If the QFile object you get is not open for read already, you can get filename from it and open an ifstream object.
If it's already open, you can get file handle/descriptor with handle() and go from there. There's no portable way of getting a fstream from platform handle. You will have to find a workaround for your platforms.
Here's a good guide for subclassing std::streambuf to provide a non-seekable read-only std::istream: https://stackoverflow.com/a/14086442/316578
Here is a simple class based on that approach which adapts a QFile into an std::streambuf which can then be wrapped in an std::istream.
#include <iostream>
#include <QFile>
constexpr qint64 ERROR = -1;
constexpr qint64 BUFFER_SIZE = 1024;
class QFileInputStreamBuffer final : public std::streambuf {
private:
QFile &m_file;
QByteArray m_buffer;
public:
explicit QFileInputStreamBuffer(QFile &file)
: m_file(file),
m_buffer(BUFFER_SIZE, Qt::Uninitialized) {
}
virtual int underflow() override {
if (atEndOfBuffer()) {
// try to get more data
const qint64 bytesReadIntoBuffer = m_file.read(m_buffer.data(), BUFFER_SIZE);
if (bytesReadIntoBuffer != ERROR) {
setg(m_buffer.data(), m_buffer.data(), m_buffer.data() + bytesReadIntoBuffer);
}
}
if (atEndOfBuffer()) {
// no more data available
return std::char_traits<char>::eof();
}
else {
return std::char_traits<char>::to_int_type(*gptr());
}
}
private:
bool atEndOfBuffer() const {
return gptr() == egptr();
}
};
If you want to be able to more things like seek, write, etc., then you'd need one of the other more complex solutions here which override more streambuf functions.
If you don't care much for performance you can always read everything from the file and dump it into an std::stringstream and then pass that to your library. (or the otherway, buffer everything to a stringstream and then write to a QFile)
Other than that, it doesn't look like the two can inter-operate. At any rate, Qt to STL inter operations are often a cause for obscure bugs and subtle inconsistencies if the version of STL that Qt was compiled with is different in any way from the version of STL you are using. This can happen for instance if you change the version of Visual Studio.

Resources