Using QList as the input for QwtPlotCurves setSamples - qt

I have two QList (XList, YList) which saves the dynamic data. I want to use these as the input of the QwtPlotCurves by setSamples. After I check the documentation:
void setSamples (const double *xData, const double *yData, int size)
void setSamples (const QVector< double > &xData, const QVector< double > &yData)
void setSamples (const QVector< QPointF > &)
It seems do not support QList. Is there workaround to this or do I have to overload it?
Julio

There is method in QList that returns a const QVector.
So:
setSamples( XList.toVector(), YList.toVector() )
Check QVector QList::toVector () const

Related

Converting QMap<QString, QString> to Json string results empty

I have a function defined and used as this:
// usage:
QMap<QString, QString> map = ...;
foo(map);
// defination:
QString stringMapToJson(const QMap<QString, QString>& arg) {
QVariant v = QVariant::fromValue(arg);
JsonDocument doc = QJsonDocument::fromVariant(v);
...
}
Then I realized v is empty.
Is there a method to convert QMap<String, QString> to QMap<String, QVariant>, so above v could be valid?
Why above v is empty? I read people were saying QVariant and qMetaData, I don't understand given the following valid, why QString have a qMetaData problem:
QString s = "";
QVariant v = s;
(A Java programmer starts her pleasant C++ journey.)
Thanks.
There are 2 ways to do this. The first is to convert your map to a QMap<QString, QVariant> like you mentioned:
QByteArray stringMapToJson1(const QMap<QString, QString>& arg)
{
QVariantMap vmap;
for(auto it = arg.cbegin(); it != arg.cend(); ++it)
{
vmap.insert(it.key(), it.value());
}
const QVariant v = QVariant::fromValue(vmap);
const QJsonDocument doc = QJsonDocument::fromVariant(v);
return doc.toJson();
}
Alternatively, you can build the json object directly from the map. In this case it's the same amount of code:
QByteArray stringMapToJson2(const QMap<QString, QString>& arg)
{
QJsonObject jObj;
for(auto it = arg.cbegin(); it != arg.cend(); ++it)
{
jObj.insert(it.key(), it.value());
}
QJsonDocument doc;
doc.setObject(jObj);
return doc.toJson();
}
This seems like a stylistic choice and I am unsure which would be faster. Both produce the same output.
One thing to note: The conversion from QString to QVariant is predefined in Qt, so the first method works fine. For objects of your own classes you would have to register that type and provide a suitable conversion which can be a bit tough to get right. In the second method you could do this conversion inline in the loop.

in qtXml library, why qt use qhash to store xml element attributes Instead of QMap?

I am using qtxml to write a xml file, I found that the output xml file's element attributes has different order each time I run my program.
I read the source code and found that qtxml use QHash to store element attributes, which will lead to output XML file's element attributes has different order each time I run my program.
Why not use QMap to store elements' attributes? which will produce an ordered attribute.
What's the difference between QHash and QMap in this scenario?
QMap is a Red-black tree.
QHash is implemented using a hash table
QMap is slower than QHash. QMap searches are faster than QHash with fewer than 10 items.
#include <QtCore/QtCore>
#include <unordered_map>
#ifndef CONTAINER
#error CONTAINER must be defined to QMap, QHash, std::map or std::unordered_map
#endif
namespace std {
/* std::hash specialization for QString so it can be used
* as a key in std::unordered_map */
template <class Key>
struct hash;
template <>
struct hash<QString> {
typedef QString Key;
typedef uint result_type;
inline uint operator()(const QString& s) const { return qHash(s); }
};
}
int main(int argc, char** argv)
{
if (argc < 2)
qFatal("" Missing number of element to add "");
QByteArray a = argv[1];
uint num = a.toUInt();
// creates an array of random keys
QVector<QString> strs(num);
for (int i = 0; i < num; ++i)
strs[i] = qvariant_cast<QString>(qrand());
CONTAINER<QString, QString> c;
for (uint i = 0; i < num; ++i) {
QString& k = strs[i];
c[k] = QString::number(i);
}
quint64 it = 0;
const QString* arr = strs.constData();
QElapsedTimer t;
t.start();
while (t.elapsed() < 1000) {
const QString& k = arr[(++it) * 797 % num];
c[k]; // perform a lookup
}
qDebug() << it / 1000;
}
The higher the iteration value, the best. The scale of the number of elements is logarithmic. It should be expected that for QHash the value will not change with increasing number of elements, and for QMap it should be equal to log N, which corresponds to a straight line on a logarithmic scale.
However, with a large number of elements, the results are not in favor of QMap.
This is most likely the reason why QHash is used.

QList generic join() function with template

I am trying to make a generic join() function for QList (like join() for QStringList) in order to make a toString() function for a QList of any type.
This function takes a QList, a separator and a function to dertermine how to print items.
Consider this code :
#include <QList>
#include <QDebug>
template <class T>
static QString join(const QList<T> &list, const QString &separator, const std::function< QString (const T &item) > toStringFunction)
{
QString out;
for(int i = 0; i<list.size(); i++)
out+= (i ? separator : "") + toStringFunction(list[i]);
return out;
}
int main(int argc, char *argv[])
{
QList <double> list;
list<<1.<<2.<<3.<<4.;
int precision = 1;
QString out = join(list, ",",[precision](const double &item)->QString{
return QString::number(item,'f',precision);
});
qDebug()<<out;
return 1;
}
Here the errors I have :
src\main.cpp(18): error C2672: 'join': no matching overloaded function found
src\main.cpp(20): error C2784: 'QString join(const QList<T> &,const QString &,const std::function<QString(const T &)>)': could not deduce template argument for 'const std::function<QString(const T &)>' from 'main::<lambda_f1fd4bbd6b8532d33a84751b7c214924>'
src\main.cpp(5): note: see declaration of 'join'
Clearly I dont care about this function, plenty of solutions to do it. But I don't understand what I am doing wrong with templates here.
could not deduce template argument ???
NB :
out = join<double>(list, ",",[precision](const double &item)->QString{
return QString::number(item,'f',precision);
});
=> Works fine
const std::function<QString(const double &item)> toStringFunction = [precision](const double &item)->QString{
return QString::number(item,'f',precision);
};
out = join(list, ",",toStringFunction);
=> Works fine
I'm not sure what's going on with the C++ internals, but it does work with this declaration:
template <class T>
static QString join(const QList<T> &list,
const QString &separator,
const std::function< QString (const typename QList<T>::value_type &) > toStringFunction)
I think QList can determine the template type from the list being passed, while the join template itself can't.

Mime type for custom data in tree view

The items in the tree view hold a instance of class container.
I want to implement drag and drop functionality in the view.
According to the QT tutorial for the data to copy i need specify the mime type and than write the Mimedata and dropMimeData functions.
The QT Example is dealing with a simple string so i am totally clueless of how to implement these function in case of custom objects.
1) What should be the mime type in my case ?
2) How to implement the current mimedata function for Container object data?
3) How to implement the current dropmimedata function for Container object data?
/////////////////////////////////////////
class Container
{
private:
std::string stdstrContainerName;
std::string stdstrPluginType;
int iSegments;
float fRadius;
public:
Container();
Container(std::string , std::string , int , float);
Container(const Container& obj);
~Container();
std::string GetName();
std::string GetType();
void SetName(std::string stdstrName);
};
Q_DECLARE_METATYPE( Container )
////////////////////////////////////////////////////////////
QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes)
const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
foreach (const QModelIndex &index, indexes) {
if (index.isValid()) {
QString text = data(index, Qt::DisplayRole).toString();
// I have a GetContainer function which returns the Container
//object and i can use the GetContainer instead of data function.
stream << text;
}
}
mimeData->setData("application/vnd.text.list", encodedData);
return mimeData;
}
//////////////////////////////////////////////////////////////////
bool DragDropListModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex
&parent)
{
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat("application/vnd.text.list"))
return false;
if (column > 0)
return false;
int beginRow;
if (row != -1)
beginRow = row;
else if (parent.isValid())
beginRow = parent.row();
else
beginRow = rowCount(QModelIndex());
QByteArray encodedData = data->data("application/vnd.text.list");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QStringList newItems;
int rows = 0;
while (!stream.atEnd()) {
QString text;
stream >> text;
newItems << text;
++rows;
}
insertRows(beginRow, rows, QModelIndex());
foreach (const QString &text, newItems) {
QModelIndex idx = index(beginRow, 0, QModelIndex());
setData(idx, text);
beginRow++;
}
return true;
}
The header file for TreeItem.
class TreeItem
{
public:
explicit TreeItem( const Container &data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);
TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const;
bool setData(const Container &data , QVariant value);
void setContainer(const Container &data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
private:
QList<TreeItem*> childItems;
Container itemData;
TreeItem* parentItem;
}
You can add your custom mime types to specify the type of container you want to drag/drop. See this post for details.
The QDrag object constructed by the source contains a list of MIME types that it uses to represent the data (ordered from most appropriate to least appropriate), and the drop target uses one of these to access the data.
First of all, try to find a compatible standard mime type. Those are the most common one assigned by the IANA.
If the one you are looking for is not in the list, then you can label your custom one and serialize your data into a QByteArray to share it.
QByteArray output;
// do whatever
mimeData->setData("my-awesome-mime-type", output);
Now, in your custom widget, don't forget to accept the drops of this mime type:
void Window::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasFormat("my-awesome-mime-type"))
event->acceptProposedAction();
}
You can find a complete example in this project.

How to convert QList<QVariant> to QList<T>

For my serialization method i need to store a QList<T> where T is my custom Type, in a QVariantList.
QList<T> l;
l.append(T());
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
var.value<QList<T>>(); //returns an empty list which is not my serialized list;
My problem is that i cannot convert back the variant list into QList<T>
EDIT:
#define PROPERTY(type, name) \
Q_PROPERTY(type name MEMBER name) \
type name;
class Measurement
{
Q_GADGET
public:
PROPERTY(int, index)
PROPERTY(QString, name)
PROPERTY(QString, unit)
PROPERTY(double, factor)
PROPERTY(bool, isVisible)
PROPERTY(quint8, decimal)
bool operator ==(const Measurement &other)
{
return (this->index == other.index);
}
};
you can consider this class as my custom type (T). i also save the class name (here "Measurement") along with serialized data for furthur uses, because as you know we can get the registered type with QMetaType::type(char*) but with that type i can only construct a QVariant with QVariant(int typeId, const void *copy) but here i want to construct the QList<Measurement> itself.
You will need to deserialize the QVariant list one item at a time. I am also not sure that this line:
var = list;
is performing what you intended. It will take your QVariantList list and wrap it inside another QVariant called var, which is of type QVariant(QVariantList, (QVariant(MyType, ), QVariant(MyType, ))). There doesn't seem to be much benefit to doing this.
Nonetheless, the example below shows a way to recover the list from var.
#include <QCoreApplication>
#include <QVariant>
class MyType {
public:
MyType() {}
MyType(QString value) { m_value = value; }
QString m_value;
};
Q_DECLARE_METATYPE(MyType)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<MyType> l;
l.append(MyType("foo"));
l.append(MyType("bar"));
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
QList<MyType> deserializedList;
foreach(QVariant v, var.value<QVariantList>()) {
deserializedList << v.value<MyType>();
}
return a.exec();
}

Resources