Passing data around with QMimeData in Qt drag and drop - c++

I'm trying to understand how data gets passed around when using drag and drop in Qt. From what I understood from the examples I've been studying, you first define a widget as draggable by overriding methods inherited through QWidget.
In the implementation of the overridden method, the examples I've been looking at instantiate a pointer to a QMimeData object, and store information in it by calling setText(const QString &text) and setData(const QByteArray &data). They store information in the QByteArray object with the << operator:
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << labelText << QPoint(ev->pos() - rect().topLeft());
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-fridgemagnet", itemData);
mimeData->setText(labelText);
In the definition of the dropEvent() method in the widget that accepts the drops, both of those variables were retrieved with the >> operator:
QString text;
QPoint offset;
dataStream >> text >> offset;
In the setData() method, application/x-fridgemagnet was passed as a MIME type argument. Was that defined somewhere else or its just something you can make up?
How can I store and retrieve a custom object inside the QMimeData object? I tried this:
dataStream << labelText << QPoint(ev->pos() - rect().topLeft()) << myObject;
and tried to retrieve it like this:
myClass myObject;
dataStream >> text >> offset >> myObject;
But it didn't work, says theres "no match for 'operator >>'". Any tips on what should I do?

In the setData() method, application/x-fridgemagnet was passed as a MIME type argument. Was that defined somewhere else or its just something you can make up?
If the data is in your own proprietary format, then you can make it up. If, however, it's something standardized, like images, you'll probably want to use a known mime-type.
If you already support serialization to XML, then it would be easy to create your own mime-type, serialize to XML, and then de-serialize on the receiving end.
How can I store and retrieve a custom object inside the QMimeData object?
You'll need to create non-member operators (<< and >>) that write out MyObject's member data in a way supported by QDataStream. See the QDataStream documentation under the heading "Reading and Writing other Qt Class."
That will involve creating the following two methods:
QDataStream &operator<<(QDataStream &, const MyObject &);
QDataStream &operator>>(QDataStream &, MyObject &);
Since these are non-member operators, they will be defined outside your class:
class MyObject { /* ... */ };
QDataStream &operator<<(QDataStream &stream, const MyObject &obj) {
/* as long as first_member and second_member are types supported
by QDataStream, I can serialize them directly. If they're not
supported, I'd need an operator for them as well unless I can
convert them to a QString or something else supported by Qt /
QDataStream */
stream << obj.first_member;
stream << obj.second_member;
/* and so on and so forth */
return stream;
}
/* and similarly for operator>> */

Yes, you can make up your own MIME type for use in your own application. Obviously no external applications will have a clue what it is so you won't be able to drag outside your own application.
As regards storing your own object you need to define the stream operator as Qt doesn't know anything about your object. QDataStream only defines stream operators for simple built-in types. You'll need to define something like
QDataStream& operator<<( QDataStream& ds, const myClass& myObject )
{
// Use existing QDataStream stream operators or other methods to serialise your object
// ...
// ...
return ds;
}
Similarly for de-serialising you'll need to define the appropriate operator>>.

Related

Qt6 - Draw custom metatype in table/tree from QAbstractTableModel

If I have an overridden QAbstractTableModel that supplies a non-qt type, it's my understanding that supplying overloads for the << and >> operators will allow Qt to natively represent those types.
I have prepared an example with std::u16string in an attempt to create the most minimal test case, but can't seem to render anything.
Here's how I register the type with Qt:
#include <QtCore>
Q_DECLARE_METATYPE(std::u16string);
QDataStream& operator<<(QDataStream& out, const std::u16string& myObj)
{
return out << QString::fromStdU16String(myObj);
}
QDataStream& operator>>(QDataStream& in, std::u16string& myObj)
{
QString tmp;
in >> tmp;
myObj = tmp.toStdU16String();
return in;
}
My trivial main.cpp which connects the type to the appropriate widget:
#include <QItemEditorFactory>
#include <QLineEdit>
int main()
{
// not sure if this is required.
// this blogpost (https://www.qt.io/blog/whats-new-in-qmetatype-qvariant) suggests it's
// needed for name-to-type conversions, but no idea if that is still needed internally.
qRegisterMetaType<std::u16string>();
// tell qt that we would like to visualise std::u16string with the default text editor.
QItemEditorFactory* factory = new QItemEditorFactory;
factory->registerEditor(QMetaType::fromType<std::u16string>().id(), new QStandardItemEditorCreator<QLineEdit>());
QItemEditorFactory::setDefaultFactory(factory);
// kick off ui, etc
return doApp();
}
And my trivial model, which supplies the external type via a variant:
#include <QAbstractTableModel>
class simple_model : public QAbstractTableModel
{
public:
explicit simple_model(QObject* parent = nullptr) : QAbstractTableModel(parent) {}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
return QVariant::fromValue<std::u16string>(u"Hello, World!");
}
};
Now, when I create QTableView like so:
QTableView* tableView = new QTableView;
tableView->setModel(new simple_model);
I would expect every column and row to print "Hello, World!". However, I just get a blank text box instead. Attaching my debugger to my overloaded << and >> operators shows they don't get run at all.
I feel like I'm missing a link here, but I'm not quite sure what. Some ideas about what could possibly be wrong:
Do I need to create a custom delegate and set it for each row and column which I'm returning this value? Ideally, I'd like my types to be interpreted as automatically and naturally as Qt would allow; I feel like it could create a lot of boilerplate code in my (actual, non-trivial) application.
Does QLineEdit not actually invoke any data conversions to display custom data? Perhaps there's a more appropriate text editor I should be using? I was hoping that QLineEdit would automatically convert them because it's the default QString editor; would be nice to have the exact same behaviour.
In my case, it turns out that the << and >> operators weren't needed at all. Instead, providing an appropriate meta type converter allowed for what I wanted.
Sample code for converter:
struct string_converter
{
static QString toQString(const std::u16string& value)
{
return QString::fromStdU16String(value);
}
static std::u16string tou16String(const QString& value)
{
return value.toStdU16String();
}
};
and to register these converters:
QMetaType::registerConverter<std::u16string, QString>(&string_converter::toQString);
QMetaType::registerConverter<QString, std::u16string>(&string_converter::tou16String);
Once these are provided, no other registration code appears to be needed. The simple_model seems to be able to construct a QVariant from the std::u16string and represent it with a QLineEdit without any extra boilerplate code.
However, we'll need to explicitly convert the modified data back to std::u16string in simple_model, because they get tweaked and returned as a QString from QLineEdit. Eg:
QVariant simple_model::setData(const QModelIndex& index, const QVariant& value, int role) override
{
std::u16string newData = value.value<std::u16string>();
// do something with newData
}
While this has unblocked me, I'm not sure this is preferred Qt approach as it seems to be quite different to what the documentation says (as I cited originally in the question). Any extra feedback or tips would be appreciated.

converting text sent to std::ostream

I have a function that sends stuff to std::ostream.
Something like this
void f(std::ostream& oss) {
oss << "Hello world!";
}
Now, I would like to create my own class that derives from std::ostream that would be parsing outgoing text, and changing it, so it would print "Ola world!", for example.
class StreamConverted : public std::ostream {
...
};
I believe (I am not too experienced with stream manipulation) that I would have to 'play' with the underlying rdbuf() of the stream, so I would have to substitute rdbuf of std::ostream with mine.
MyStreamBuf m_my_streambuf;
std::ostream& m_original_stream
std::streambuf* m_original_streambuf;
public:
StreamConverted(std::ostream& os)
: m_original_stream(os)
, m_original_streambuf(os.rdbuf(&m_my_streambuf))
{}
(Please, forgive me any obvious mistakes or typos. I am writing this all on the flight. I would also add destructor to restore original streambuf.)
This leads me to the need to write my MyStreamBuf that would derive from std::streambuf
class MyStreamBuf : public std::streambuf {
};
And here comes the moment when I need an advice.
Should I create my own buffer by calling std::streambuf::setp(begin, end) and then overwrite the overflow() method to parse the contents of the buffer when it is called, and then send the data out to the original streambuf after 'in some reasonable way' transforming the buffer?
I am not sure if I am going to far with modifying the buffer instead of doing something with ostream...
Any advice?

Qt - How to save data for my application

I'm pretty new to Qt and I have a problem with finding out how to save/load data from your application.
I'm creating a Calendar app and have to save different classes like: Deathlines, appointments, birthdays, etc.
And I've found this tutorial http://qt-project.org/doc/qt-4.8/tutorials-addressbook-part6.html but it only describes how to save one type of class.
So I was wondering if you could help me, because I have no idea how to save/load multiple classes that way, I don't need some detailed description of it (however it would be appreciated of course) but only a gentle push into the right direction.
Because nowhere in this tutorial is explained how to save multiple classes :(
EDIT: This program is for PC (project for school)
You can define your custom class and implement stream operators for it :
class CustomType
{
public:
CustomType()
{
paramter1=0;
paramter2=0;
paramter3="";
}
~CustomType(){}
int paramter1;
double parameter2;
QString parameter3;
};
inline QDataStream& operator<<( QDataStream &out, const CustomType& t )
{
out<<t.paramter1;
out<<t.paramter2;
out<<t.paramter3;
return out;
}
inline QDataStream& operator>>( QDataStream &in, CustomType& t)
{
in>>t.paramter1;
in>>t.paramter2;
in>>t.paramter3;
return in;
}
You should register the stream operators of your class somewhere in your code when starting your application before streaming the class. This could be done in the constructor of your main window :
qRegisterMetaTypeStreamOperators<CustomType>("CustomType");
Now you can save or load objects of your class to or from file.
Saving some objects of your custom classes to a file :
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::information(this, tr("Unable to open file"),
file.errorString());
return;
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8);
out << object1;
out << object2;
loading objects of your custom classes from a file :
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::information(this, tr("Unable to open file"),
file.errorString());
return;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_8);
in >> object1;
in >> object2;
Note that the order of reading and writing to file should be the same.

Qt QMetaType serialization of custom types and QVariant

I wish to gain access to the serialization technique used by QSettings and QVariant. For example, if you create a QRect object and store it to an INI file with QSettings you get a line that looks like this:
value=#Rect(1 2 3 4)
Most of the standard Qt types, and custom ones, have a similar string serialization format for saving/loading. How can I do the same thing?
That is, I have a QVariant and wish to save the data it contains then later load that data back. The saved form should be textual data (like the above) to be usable in normal config files (like INI) or the registry.
Hmm, I looked at the QSettings source code and it just has hard-coded handling for some common types and then used QDataStream for the rest. That would imply there is no generic way to serialize the data in a textual form.
Qt meta-object system is able to register a large number of operators for custom types. One of them is the StreamOperator. This operator is used by QSettings to
write and read QVariant in a configuration file.
So first of all, you will need to implement the two stream operators for the custom type as mentioned by #divanov
QDataStream & operator<< ( QDataStream & stream, const YourClass & yourObject );
QDataStream & operator>> ( QDataStream & stream, YourClass & yourObject );
After that, you need to register these two operators for the custom type to
the Qt meta-object system using qRegisterMetaTypeStreamOperators.
The following example describe all steps previously mentioned with the
custom type Color.
#include <QMetaType>
#include <QDataStream>
#include <QSettings>
#include <cassert>
// Custom type 'Color'
struct Color
{
uint8_t _red;
uint8_t _green;
uint8_t _blue;
// Stream operator used by QSettings to save a value of type Color
// to configuration file
friend QDataStream& operator<<(QDataStream& out, const Color& color)
{
out << color._red;
out << color._green;
out << color._blue;
return out;
}
// Stream operator used by QSettings to load a value of type Color
// from a configuration file
friend QDataStream& operator>>(QDataStream& in, Color& color)
{
in >> color._red;
in >> color._green;
in >> color._blue;
return in;
}
};
Q_DECLARE_METATYPE( Color )
int main(int argc, char* argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
// Register Color to qt meta-object system
qRegisterMetaType<Color>();
// Register its two streams operator to qt meta-object system
qRegisterMetaTypeStreamOperators<Color>();
// Test it with QSettings!
QSettings configFile("config.ini");
// Save the color
Color saveColor { 12, 13, 14 };
configFile.setValue("Color", QVariant::fromValue(saveColor));
// Load the color
Color loadColor = configFile.value("Color", QVariant()).value<Color>();
// Asserts are successful
assert(loadColor._red == 12);
assert(loadColor._green == 13);
assert(loadColor._blue == 14);
}
Personally I find QVariantMap and QVariantList quite handy for these type of things.
Provide convertion functions to your class/structure:
class User {
public:
QVariantMap toVariantMap() const {
QVariantMap map;
map["name"] = m_name;
map["reputation"] = m_reputation;
map["tags"] = m_tags;
return map;
}
static User fromVariantMap(const QVariantMap& map) {
User user;
user.m_name = map["name"].toString();
user.m_reputation = map["reputation"].toInt();
user.m_tags = map["tags"].toStringList();
return user;
}
private:
QString m_name;
int m_reputation;
QStringList m_tags;
}
Save it with toVariantMap:
settings->setValue("user", user.toVariantMap());
Fetch it with fromVariantMap:
auto user = User::fromVariantMap(settings->value("user").toVariantMap());
To save list of items other than QString, QVariantList can be used:
QVariantList list;
for (int i = 0; i < m_list.size(); ++i)
list.append(m_list[i]);
map["list"] = list;
The QDataStream class provides serialization of binary data to a QIODevice.
You should implement two operators:
QDataStream & operator<< ( QDataStream & stream, const YourClass & yourObject );
QDataStream & operator>> ( QDataStream & stream, YourClass & yourObject );
which will be responsible for serialization and de-serialization of data.
Read more about serialization in Qt
In case you are interested in textual serialization, then you should choose QTextStream
as your tool. However, the most of the classes do not have operators able to handle text streams, so you will have to implement them.

Overloading the QDataStream << and >> operators for a user-defined type

I have a an object I'd like to be able to read and write to/from a QDataStream. The header is as follows:
class Compound
{
public:
Compound(QString, QPixmap*, Ui::MainWindow*);
void saveCurrentInfo();
void restoreSavedInfo(QGraphicsScene*);
void setImage(QPixmap*);
QString getName();
private:
QString name, homeNotes, addNotes, expText;
Ui::MainWindow *gui;
QPixmap *image;
struct NMRdata
{
QString hnmrText, cnmrText, hn_nmrText, hn_nmrNucl, notes;
int hnmrFreqIndex, cnmrFreqIndex, hn_nmrFreqIndex,
hnmrSolvIndex, cnmrSolvIndex, hn_nmrSolvIndex;
}*nmr_data;
struct IRdata
{
QString uvConc, lowResMethod,
irText, uvText, lowResText, highResText,
highResCalc, highResFnd, highResFrmla,
notes;
int irSolvIndex, uvSolvIndex;
}*ir_data;
struct PhysicalData
{
QString mpEdit, bpEdit, mpParensEdit, bpParensEdit,
rfEdit, phyText, optAlpha,
optConc, elemText, elemFrmla,
notes;
int phySolvIndex, optSolvIndex;
}*physical_data;
};
For all intents and purposes, the class just serves as an abstraction for a handful of QStrings and a QPixmap. Ideally, I would be able to write a QList to a QDataStream but I'm not exactly sure how to go about doing this.
If operator overloading is a suitable solution, would writing code like
friend QDataStream& operator << (QDataStream&,Compound) { ... }
be a potential solution? I'm very open to suggestions! Please let me know if any further clarification is needed.
I think you've answered your own question! The stream operator
QDataStream& operator<<( QDataStream&, const Compound& )
will work fine. In the implementation you just use the existing stream operators on QDataStream to serialise the individual bits of your Compound. Some Qt classes define non-member QDataStream operators too. QString is one and so is QList so it looks like you're sorted!
If you want to overload the "extract" operator >>, your signature must be:
QDataStream & operator >> (QDataStream & in, MyClass & class);
Hope it helps.