c++ - 如何使用 QDatastream 在 QT 中正确序列化和反序列化 QList 类?
问题描述
我正在尝试序列化自定义类Layer*
并使用 QDataStream 将其读回。现在,Layer
是一个具有虚方法的抽象类,它被不同类型的层继承:RasterLayer
、TextLayer
等AdjustmentLayer
。
我有一个QList<Layer*> layers
跟踪所有图层的方法,并且对图层所做的任何调整都会在列表中更新。我需要将 QList 序列化和反序列化为其原始状态并恢复各个层(不同类型)的属性。
这是layer.h:
#ifndef LAYER_H
#define LAYER_H
#include <QString>
#include <QImage>
#include <QDebug>
#include <QListWidgetItem>
#include <QGraphicsItem>
#include <QPixmap>
class Layer : public QListWidgetItem
{
public:
enum LayerType{
RASTER,
VECTOR,
TEXT,
ADJUSTMENT
};
Layer(QString name, LayerType type);
~Layer();
inline void setName(QString &name) { _name = name; }
inline QString getName() { return _name; }
inline LayerType getType() { return _type; }
virtual void setSceneSelected(bool select) = 0;
virtual void setLayerSelected(bool select) = 0;
virtual void setZvalue(int z) = 0;
virtual void setParent(QGraphicsItem *parent) = 0;
protected:
QString _name;
LayerType _type;
};
#endif // LAYER_H
这由RasterLayer类扩展:
#ifndef RASTERLAYER_H
#define RASTERLAYER_H
#include <QGraphicsPixmapItem>
#include <QPainter>
#include <QGraphicsScene>
#include "layer.h"
class RasterLayer : public Layer, public QGraphicsPixmapItem
{
public:
RasterLayer(const QString &name, const QImage &image);
RasterLayer();
~RasterLayer();
void setLocked(bool lock);
void setSceneSelected(bool select);
void setLayerSelected(bool select);
void setZvalue(int z);
void setParent(QGraphicsItem *parent);
inline QPixmap getPixmap() const { return pixmap(); }
inline QPointF getPos() const { return QGraphicsPixmapItem::pos(); }
inline void setLayerPos(QPointF pos) { setPos(pos);}
inline void setLayerPixmap(QPixmap pixmap) { setPixmap(pixmap); }
friend QDataStream& operator<<(QDataStream& ds, RasterLayer *&layer)
{
ds << layer->getPixmap() << layer->getName() << layer->getPos();
return ds;
}
friend QDataStream& operator>>(QDataStream& ds, RasterLayer *layer)
{
QString name;
QPixmap pixmap;
QPointF pos;
ds >> pixmap >> name >> pos;
layer->setName(name);
layer->setPixmap(pixmap);
layer->setPos(pos);
return ds;
}
protected:
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget);
private:
QImage _image;
};
#endif // RASTERLAYER_H
我目前正在尝试测试这样的序列化反序列化RasterLayer
:
QFile file(fileName);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
Layer *layer = paintWidget->getItems().at(1);
// Gets the second element in the list
RasterLayer *raster = dynamic_cast<RasterLayer*> (layer);
out << raster;
file.close();
现在,正如您在此处看到的那样,我专门将其转换Layer*
为RasterLayer*
要序列化的 a,这很有效,因为到目前为止我只处理了一种类型的层。所以我的第一个问题是:
如何将此序列化过程推广到所有类型的层?
每种类型的层都有不同的序列化方式,因为每个层都有不同的属性。此外,这里的铸造感觉有点代码味道,可能是一个糟糕的设计选择。因此,将整个层列表序列化并调用其相应的重载运算符将是预期的场景。
我的第二个问题是:
如何正确反序列化数据?
这是我目前对个人进行序列化的方式RasterLayer
:
QFile newFile(fileName);
newFile.open(QIODevice::ReadOnly);
QDataStream in(&newFile);
RasterLayer *layer2 = new RasterLayer;
in >> layer2;
paintWidget->pushLayer(layer2);
ui->layerView->updateItems(paintWidget->getItems());
首先,我不认为在这种情况下我应该对指针进行序列化,但我不确定还能做什么或如何做得更好。其次,反序列化在这里有效,但它并没有完全达到我期望的效果。尽管我在重载运算符中使用了 setter,但实际上并没有正确更新图层。我需要调用构造函数来创建一个新层。
我试过这个:使用 Qt 进行序列化,但我不太确定如何Layer*
将其转换为Layer
、序列化、反序列化,然后将其转换回Layer*
. 所以我需要添加第三步:
RasterLayer *layer3 = new RasterLayer(layer2->getName(), layer2->getPixmap().toImage());
layer3->setPos(layer2->pos());
然后推layer3
送到列表以使其真正起作用。根据这篇文章:https ://stackoverflow.com/a/23697747/6109408 ,我真的不应该new RasterLayer...
在运算符重载函数内部做一个(否则我会在地狱里炸),我正在遵循给出的第一个建议那里,这对我来说不是很有效,我不知道正确的方法。
另外,我如何为Layer*
s 的一般 QList 反序列化它,而不必创建新的特定层实例并将反序列化数据注入它们?虽然这很相似:Serialize a class with a Qlist of custom classes as member (using QDataStream),答案不够清楚,我无法理解。
我有一个关于中间值持有者类的想法,我将使用它来序列化各种层,并让它根据层的类型创建和注入参数,但我不确定这是否可行。
谢谢你的协助。
解决方案
我希望下面的例子能给你一个大致的想法:
#include <iostream>
#include <fstream>
#include <list>
class A{
int a=0;
public:
virtual int type(){return 0;}
virtual void serialize(std::ostream& stream)const{
stream<<a<<std::endl;
}
virtual void deserialize(std::istream& stream){
stream>>a;
}
friend std::ostream& operator <<(std::ostream& stream, const A& object){
object.serialize(stream);
return stream;
}
friend std::istream& operator >>(std::istream& stream, A& object){
object.deserialize(stream);
return stream;
}
virtual ~A(){}
};
class B : public A{
int b=1;
public:
virtual int type(){return 1;}
virtual void serialize(std::ostream& stream)const{
A::serialize(stream);
stream<<b<<std::endl;
}
virtual void deserialize(std::istream& stream){
A::deserialize(stream);
stream>>b;
}
};
class C : public A{
int c=2;
public:
virtual int type(){return 2;}
virtual void serialize(std::ostream& stream)const{
A::serialize(stream);
stream<<c<<std::endl;
}
virtual void deserialize(std::istream& stream){
A::deserialize(stream);
stream>>c;
}
};
std::ostream& operator <<(std::ostream& stream, const std::list<A*>& l){
stream<<l.size()<<std::endl;
for(auto& a_ptr: l){
stream<<a_ptr->type()<<std::endl;
stream<<*a_ptr;
}
}
std::istream& operator >>(std::istream& stream, std::list<A*>& l){
l.clear();
int size, type;
stream>>size;
A* tmp;
for(int i =0; i<size; ++i){
stream>>type;
if(type==0){
tmp = new A;
}
if(type==1){
tmp = new B;
}
if(type==2){
tmp = new C;
}
stream>>(*tmp);
l.push_back(tmp);
}
return stream;
}
int main(){
A* a = new A;
A* b = new B;
A* c = new C;
std::list<A*> List{ a, b, c };
std::list<A*> List2;
std::ofstream ofs("D:\\temp.txt");
ofs<<List;
ofs.flush();
ofs.close();
std::ifstream ifs("D:\\temp.txt");
ifs>>List2;
std::cout<<List2;
for(auto& a_ptr : List2){
delete a_ptr;
}
delete c;
delete b;
delete a;
return 0;
}
编辑:在我没有考虑到序列化列表时我们应该为成功反序列化编写列表大小和元素类型的事实,所以我修改了示例。
推荐阅读
- c++ - 我的程序没有错误就停止了,我做错了什么?
- python - Python MySQL Connector fetchone not returning dict
- security - 虚拟机 SharePoint 2013 问题 - SpFarm.Local 为空。本地机器可能不知道 VM 上的 SharePoint?
- docker - docker compose up 后,去后端到 redis 连接被拒绝
- angular - 如何在角度应用程序中部分导入引导程序?
- r - 在 R 中对具有相似名称/前缀的列求和
- flutter - 如何在不将旧根保存在堆栈中的情况下切换到新根?
- swift - 如何在 Swift 结果生成器中恢复类型泛型类型推断?
- bash - terraform 外部数据不返回 bash 输出
- firebase - 产品信息的多语言应用程序