其他分享
首页 > 其他分享> > c – 如何使用QDatastream在QT中正确序列化和反序列化QList类?

c – 如何使用QDatastream在QT中正确序列化和反序列化QList类?

作者:互联网

我正在尝试序列化自定义类Layer *并使用QDataStream将其读回.现在,Layer是一个带有虚方法的抽象类,它由不同类型的层继承:RasterLayer,TextLayer,AdjustmentLayer等.

我有一个QList< Layer *>跟踪所有图层的图层,以及对图层所做的任何调整都会在列表中更新.我需要将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 *进行序列化,这是有效的,因为到目前为止我只处理过一种类型的层.所以我的第一个问题是:

如何将此序列化过程推广到所有类型的层?

每种类型的图层都有不同的序列化方式,因为每种图层都有不同的属性.此外,这里的铸件感觉有点代码味道和可能的糟糕设计选择.因此,像序列化整个层的列表调用它们相应的重载运算符这样的东西将是预期的场景.

我的第二个问题是:

如何正确反序列化数据?
以下是我目前正在序列化单个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,但它实际上并没有正确地更新图层.我需要调用构造函数来创建一个新层.

我试过这个:Serialization with Qt,但我不太确定如何将Layer *转换为Layer,序列化,反序列化然后将其转换回Layer *.
所以我需要添加第三步:

RasterLayer *layer3 = new RasterLayer(layer2->getName(), layer2->getPixmap().toImage());
layer3->setPos(layer2->pos());

然后将layer3推送到列表以实际使其工作.根据这篇文章:https://stackoverflow.com/a/23697747/6109408,我真的不应该做一个新的RasterLayer …在运算符重载函数内部(或者我会在地狱里煎炸),我遵循那里给出的第一个建议,这不是很在我的情况下工作很多,我不知道正确的方法.

另外,如何为Layer *的一般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,serialization,qt,qdatastream
来源: https://codeday.me/bug/20190823/1697370.html