0
votes

I'm trying to create a layer system like most photo editor programs (Photoshop) and I'm basically drawing a single QGraphicsPixmapItem using QGraphicsPixmapItem::setPixmap(QPixmap *image); on QGraphicsScene. How could I do this but instead I can add many QPixmaps and remove them at will. I tried creating a list of QPixmaps and one of QGraphicsPixmapItems but it gets messy if I remove or rearrange the order of my QPixmaps is there a better way to do this?

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(draw())); //calls the function below to redraw sc
timer->start(30);

This updates the GraphicsScene every 30ms so any drawing I do on the pixmap *image gets drawn but now I want to get a list a QPixmap and add them to the scene everytime draw() is called but the problem is I need a list of QGraphicsPixmapItems and if I delete a layer or move the order of them I want the associated QGraphicsPixmapItem to also be removed/moved. I guess I can do this but it seems very complicated so any advice?

void PaintArea::draw()
{
    m_item->setPixmap(*image); //Do this but call layerManager.getListOfLayers() and add each to the scene
}
2
Have you tried using a QGraphicsItemGroup? Here's its documentation if you want to take a look. If I understand your problem correctly, you'll just need to group them, and affect the layer of the group as a whole.Osama Kawish
Can't edit last comment within 5 minutes, so here it is. Use QGraphicsScene::destroyItemGroup to delete the group. Use QGraphicsItem::setPos on the item group as a whole to move it collectively. (Keep in mind QGraphicsItemGroup inherits QGraphicsItem)Osama Kawish

2 Answers

0
votes

The following small example app shows how you might proceed. Basically, there are two models and two views. The models are the QGraphicsScene and a standard QStandardItemModel, whereas the views are a QListView and a QGraphicsView.

The main task is to keep both models in sync, by using signal and slots.

The model can be modified with the Add button and the context menu. For this small app one can only add, remove and change the picture of your pixmap. It is really simple to add other actions like moving the items with drag and drop and also to hide/visible them using a checkable action and other custom user role.

#include <QApplication>
#include <QFileDialog>
#include <QGraphicsPixmapItem>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QListView>
#include <QMenu>
#include <QPushButton>
#include <QStandardItemModel>

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);
    auto frame = new QFrame;
    frame->setLayout(new QHBoxLayout);
    auto listView = new QListView;
    frame->layout()->addWidget(listView);
    auto graphicsView = new QGraphicsView;
    frame->layout()->addWidget(graphicsView);
    auto graphicsScene = new QGraphicsScene;
    graphicsView->setScene(graphicsScene);
    auto myModel = new QStandardItemModel;
    auto btnAdd = new QPushButton("Add");
    frame->layout()->addWidget(btnAdd);
    QObject::connect(btnAdd, &QPushButton::clicked, [&]() {
        auto item = new QStandardItem("Pixmap");
        item->setData(QString("./data/test.png"), Qt::ItemDataRole::UserRole + 1);
        myModel->appendRow(item);
    });
    listView->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);

    QObject::connect(listView, &QListView::customContextMenuRequested, [&](const QPoint& pos) {
        auto index = listView->indexAt(pos);
        QMenu menu;

        auto remove = menu.addAction("Remove", [&]() {
            myModel->removeRow(index.row(), index.parent());
            });
        if (!index.isValid()) remove->setEnabled(false);

        auto changeImage = menu.addAction("Change...", [&]() {
            auto file=QFileDialog::getOpenFileName(frame, "Select PNG file", "./data/", "(*.png)");
            if (file.isEmpty()) return;
            myModel->setData(index,  file, Qt::ItemDataRole::UserRole + 1);
        });
        if (!index.isValid()) changeImage->setEnabled(false);

        menu.exec(listView->mapToGlobal(pos));
    });

    QObject::connect(myModel, &QStandardItemModel::dataChanged, [&](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles = QVector<int>()) {
        if (auto item = myModel->itemFromIndex(topLeft)) {
            if (auto pixItem = dynamic_cast<QGraphicsPixmapItem*>(graphicsScene->items()[topLeft.row()])) {
                pixItem->setPixmap(QPixmap(item->data(Qt::ItemDataRole::UserRole + 1).toString()));
            }
        }
    });
    QObject::connect(myModel, &QStandardItemModel::rowsInserted, [&](const QModelIndex& parent, int first, int last) {
        for (auto iter = first; iter <= last; iter++) {
            auto index=myModel->index(iter, 0, parent);
            auto pixmap=myModel->data(index, Qt::ItemDataRole::UserRole + 1).toString();;
            auto item=graphicsScene->addPixmap(QPixmap(pixmap));
        }
    });

    QObject::connect(myModel, &QStandardItemModel::rowsRemoved, [&](const QModelIndex& parent, int first, int last) {
        auto items = graphicsScene->items();
        for (auto iter = first; iter <= last; iter++) {
            graphicsScene->removeItem(items[iter]);
        }
    });

    listView->setModel(myModel);
    frame->show();
    return app.exec();
}
0
votes

Header file:

...
QGraphicsScene *scene; QGraphicsItemGroup *itemGroup; 
...

.cpp file:

void PaintArea::draw()
{
    m_item->setPixmap(*image); m_item->setGroup(itemGroup); // Layers-related code
}

void PaintArea::deleteGroup(QGraphicsItemGroup *group)
{
    scene->destroyItemGroup(group); // Layers-related code
}