Qt - How to save data for my application - c++

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.

Related

C++ how to write a ifstream for multiple files?

So I have several files that form a contingent number of lines, so let's call them file1, file2, file3 and so forth. What I want to do is to create a istream class which streams all the files as if they were one. One thing i got is not to subclass std::istream but to reimplement streambuf. My question is: how would I go about this - how would i be able to read from multiple files without having them all in memory?
Is it possible to make an istream that reads multiple files?
Yes, but you'll have to make it yourself. You can implement your own istream class by extending std::istream, and then implementing the methods it defines:
class custom_istream : public std::istream {
std::ifstream underlying;
//... implement methods
};
The interface of std::istream is sufficiently flexible to allow you to do what you want. However, implementing std::istream would require a lot of work, as you'd have to implement the entirety of the interface.
Is there an easier solution?
If you only need a subset of the functionality provided by std::istream, you could just write your own class.
For example, if you only need to be able to read lines from the file, the below class will work just fine for multiple files:
class MultiFileReader {
std::ifstream filestream;
std::ios_base::openmode mode;
size_t file_index = 0;
std::vector<std::string> filenames;
void open_next_file() {
file_index++;
filestream = std::ifstream(filenames.at(file_index), mode);
}
public:
MultiFileReader(std::vector<std::string> const& files, std::ios_base::openmode mode)
: filestream(files[0], mode), mode(mode) {}
// Checks if there's no more files to open, and no more to read
// in the current file
bool hasMoreToRead() {
if(file_index == filenames.size()) return false;
if(file_index + 1 == filenames.size())
return not filestream.eof();
return true;
}
std::string read_line() {
if(not hasMoreToRead()) {
throw std::logic_error("No more to read");
}
// If at the end of the file, open the next file
if(filestream.eof()) {
open_next_file();
}
else {
std::string line;
std::getline(filestream, line);
return line;
}
}
};

QFile init/assignment op issue when objects are class members

So i have a QFile and QTextStream member as part of my class... trying to init. them together in my constructor:
Class.h:
QFile _file;
QTextStream _textstrm;
Class.cpp:
_file = QFile (/*file name*/);
_file.open(/*set stuff*/);
_textstrm = QTextTream ( &_file );
And the comp error i get, C2248, says the objects to have access to the operators in their own class..
The problem is that you are creating a new object and you are adding an attribute that has no access, you must use the functions provided by the object.
_file.setFileName(/*file name*/);
_file.open(/*set stuff*/);
_textstrm.setDevice( &_file );

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.

Passing data around with QMimeData in Qt drag and drop

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>>.