Search code examples
c++qtmallocmemcpyqgraphicsscene

Copying classes derived from QGraphicsItem using memcpy does not creat new object


I have several different classes derived from QGraphicsItem or its children (like QGraphicsRectItem). I am at the point when I need to copy selected objects of those classes while not knowing exactly which one I copy.

Since QGraphicsScene::selectedItems() return a list of selected items I decided to use it, however I cannot copy the QGraphicsItem since its an abstract class. To address this I am trying to copy the object using malloc and memcpy.

  1. MainWindow.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. MainWindow.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;
    };
    

GUI consisting of QGraphicsView and QPushButton is sufficient for this example.

This code seem to work, item and copiedItem have different addresses but the same properties as qDebug() returns.

The scene however return the following error:

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

I don't quite understand why the scene thinks the items are the same while they have different addresses. Is there any way to solve this issue?

EDIT: If possible I would like to do this without modifying the code of classes derived from QGraphicsItem, since this is a group work and I would not like to bug other functionalities.


Solution

  • The problem of copying a class by block copying its memory with malloc is that if the class contains pointers to objects or arrays, then only a shallow copy will occur.

    In your case of getting pointers to a QGraphicsItem, you'll need to identify the type of item you're copying (its actual child class, not base class) and use the copy constructor. QGraphicsItem includes a function called type(), which returns an int that indicates which item it is. You can also add to this in your own derived classes by implementing the type() function. For example, from the Qt docs: -

    class CustomItem : public QGraphicsItem
    {
       ...
       enum { Type = UserType + 1 };
    
       int type() const
       {
           // Enable the use of qgraphicsitem_cast with this item.
           return Type;
       }
       ...
    };
    

    Alternatively, if all the classes are your own type, you could use your own system. Once you know the type, you can then copy the item with its copy constructor: -

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

    Note that if you have inherited from QGraphicsItem and added members that are pointers, you'll need to add your own copy constructor to ensure a deep copy occurs, instead of a shallow copy.