0

我有几个不同的类派生自QGraphicsItem其子类(如QGraphicsRectItem)。我现在需要复制这些类的选定对象,但不知道我复制的是哪一个。

由于QGraphicsScene::selectedItems()返回一个选定项目的列表,我决定使用它,但是我不能复制它,QGraphicsItem因为它是一个抽象类。为了解决这个问题,我尝试使用mallocand复制对象memcpy

  1. 主窗口.cpp

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        scene = new QGraphicsScene();
        item = new QGraphicsRectItem(50,50,50,50);
        item->setFlag(QGraphicsItem::ItemIsSelectable);
        scene->addItem(item);
        item->setSelected(true);
        ui->graphicsView->setScene(scene);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        for(QGraphicsItem *item : scene->selectedItems())
        {
            QGraphicsItem *copiedItem=(QGraphicsItem *)malloc(sizeof(*item));  
            memcpy(copiedItem, item, sizeof(*copiedItem));   
            copiedItem->moveBy(50, 50);
            scene->addItem(copiedItem);
    
            qDebug() << item;
            qDebug() << copiedItem;
        }
    }
    
  2. 主窗口.h

    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
        public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
        QGraphicsScene *scene;
        QGraphicsRectItem *item;
    
        private slots:
            void on_pushButton_clicked();
    
        private:
            Ui::MainWindow *ui;
    };
    

对于这个例子,由 QGraphicsView 和 QPushButton 组成的 GUI 就足够了。

此代码似乎有效,item并且copiedItem具有不同的地址,但与返回的属性相同qDebug()

然而,场景返回以下错误:

QGraphicsScene::addItem: item has already been added to this scene

我不太明白为什么场景认为这些物品是相同的,而它们有不同的地址。有没有办法解决这个问题?

编辑:如果可能的话,我想在不修改派生类的代码的情况下执行此操作QGraphicsItem,因为这是一项小组工作,我不想破坏其他功能。

4

3 回答 3

5

如果您查看 qgraphicsitem.h 中的类定义,您将看到以下内容:

private:
    Q_DISABLE_COPY(QGraphicsItem)
    Q_DECLARE_PRIVATE(QGraphicsItem)

这意味着,按照设计,您不应该复制 a QGraphicsItem,每个对象都应该是唯一的。

编辑:我想你被拒绝复制能力的原因是因为 QGraphicsItem 遵循Composite Design Pattern。创建具有子项的单个对象的副本将导致子项具有多个父项。为了解决这个问题,您不仅要复制您感兴趣的项目,还要复制子层次结构中的每个子项。对于非常大的层次结构,这可能成为一项非常耗时的操作。

如果你真的觉得你需要制作副本,你可以创建一个克隆工厂函数/类,通过遍历所有对象的属性并将它们转移到新创建的 QGraphicsItem 来创建 QGraphicsItem 及其所有子项的克隆。

如果这不可行,也许考虑以不同的方式实现你的目标。

于 2013-08-19T14:33:19.593 回答
3

You really want to be using the copy constructor or the assignment operator, not malloc and memcpy. This is how you copy objects in C++:

QGraphicsItem copiedItem = *item; 
于 2013-08-19T13:35:47.107 回答
1

使用 malloc 块复制其内存来复制一个类的问题是,如果该类包含指向对象或数组的指针,那么只会发生浅拷贝

在获取指向 QGraphicsItem 的指针的情况下,您需要确定要复制的项目的类型(它的实际子类,而不是基类)并使用复制构造函数。QGraphicsItem 包含一个名为 type() 的函数,它返回一个 int 值,指示它是哪个项目。您还可以通过实现 type() 函数在您自己的派生类中添加它。例如,来自 Qt 文档:-

class CustomItem : public QGraphicsItem
{
   ...
   enum { Type = UserType + 1 };

   int type() const
   {
       // Enable the use of qgraphicsitem_cast with this item.
       return Type;
   }
   ...
};

或者,如果所有类都是您自己的类型,您可以使用自己的系统。知道类型后,您可以使用其复制构造函数复制该项目:-

// Example, assuming type denotes a QGraphicsItemRect    
QGraphicsItemRect rect = (*originalRect);

请注意,如果您从 QGraphicsItem 继承并添加了指针成员,则需要添加自己的复制构造函数以确保发生深拷贝,而不是浅拷贝。

于 2013-08-19T14:13:57.727 回答