1

I have a small example .ui where the user can drag and drop from a QListWidget to a QGraphicsView using QGraphicsProxyWidget a specific widget (in my case is a QTableWidget) as shown below.

Basically the behavior I have been looking for is:

If I drag and drop "Imgaes" than the QTableWidget on the QGraphicsView will have 2 columns and 2 rows; (which is correct) If I drag and drop "Path" than the QTableWidget on the QGraphicsView will have 3 columns and 3 rows; (which is wrong)

pr1

[1] : https://i.stack.imgur.com/oVuea.jpg

Below the selection of "Path", which still give 2 rows and 2 columns instead of 3 rows and 3 columns

pr2

[2] : https://i.stack.imgur.com/kLRSx.jpg

Below the code:

scene.h

class Scene : public QGraphicsScene {

    enum LaserTableWidget {
      Images,
      Path
    };
    Q_ENUM(LaserTableWidget)

public:
  Scene(QObject *parent = nullptr);

  void compare(const QString& comp);

  template<typename QEnum>
  std::string QtEnumToString (const QEnum value)
  {
    return std::string(QMetaEnum::fromType<QEnum>().valueToKey(value));
  }

protected:
  void dropEvent(QGraphicsSceneDragDropEvent *event);

scene.cpp

Scene::Scene(QObject *parent) : QGraphicsScene(parent) {
    setBackgroundBrush(Qt::lightGray);
}

void Scene::compare(const QString &comp)
{
    // get information about the enum named "LaserTableWidget"
    QMetaObject MetaObject = this->staticMetaObject;
    QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator("LaserTableWidget"));
    QStringList tabs;

    switch (MetaEnum.keyToValue(comp.toUpper().toLatin1()))
    // or simply switch (MetaEnum.keyToValue(word)) if no string modification is needed
    {
        case Images:
        for (const QString &color : tabs) {
            QPoint initPos(0,0);

            QTableWidget *wgt = new QTableWidget;
            QGraphicsRectItem *proxyControl = addRect(initPos.x(), initPos.y(), wgt->width(), 20, QPen(Qt::black), QBrush(Qt::darkGreen)); // widget->width() works properly here because of the resize(layout->sizeHint()) that we have used inside it
            proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
            proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);

            wgt->setColumnCount(2);
            wgt->setRowCount(2);
            for (int ridx = 0 ; ridx < wgt->rowCount() ; ridx++ )
            {
                for (int cidx = 0 ; cidx < wgt->columnCount() ; cidx++)
                {
                    QTableWidgetItem* item = new QTableWidgetItem();
                    item->setText(QString("%1").arg(ridx));
                    wgt->setItem(ridx,cidx,item);
                }
            }
            QGraphicsProxyWidget * const proxy = addWidget(wgt);
            // In my case the rectangular graphics item is supposed to be above my widget so the position of the widget is shifted along the Y axis based on the height of the rectangle of that graphics item
            proxy->setPos(initPos.x(), initPos.y()+proxyControl->rect().height());
            proxy->setParentItem(proxyControl);
        }

        break;
        case Path:
        for (const QString &color : tabs) {
            QPoint initPos(0,0);

            QTableWidget *wgt = new QTableWidget;
            QGraphicsRectItem *proxyControl = addRect(initPos.x(), initPos.y(), wgt->width(), 20, QPen(Qt::black), QBrush(Qt::darkGreen)); // widget->width() works properly here because of the resize(layout->sizeHint()) that we have used inside it
            proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
            proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);

            wgt->setColumnCount(3);
            wgt->setRowCount(3);
            for (int ridx = 0 ; ridx < wgt->rowCount() ; ridx++ )
            {
                for (int cidx = 0 ; cidx < wgt->columnCount() ; cidx++)
                {
                    QTableWidgetItem* item = new QTableWidgetItem();
                    item->setText(QString("%1").arg(ridx));
                    wgt->setItem(ridx,cidx,item);
                }
            }
            QGraphicsProxyWidget * const proxy = addWidget(wgt);
            // In my case the rectangular graphics item is supposed to be above my widget so the position of the widget is shifted along the Y axis based on the height of the rectangle of that graphics item
            proxy->setPos(initPos.x(), initPos.y()+proxyControl->rect().height());
            proxy->setParentItem(proxyControl);
        }
        break;

        default:
        break;
    }
}


void Scene::dropEvent(QGraphicsSceneDragDropEvent *event) {

  QByteArray encoded =
      event->mimeData()->data("application/x-qabstractitemmodeldatalist");
  QDataStream stream(&encoded, QIODevice::ReadOnly);

  QStringList rosTables;

  while (!stream.atEnd()) {
    int row, col;
    QMap<int, QVariant> roleDataMap;
    stream >> row >> col >> roleDataMap;
    rosTables << roleDataMap[Qt::DisplayRole].toString();
  }

  compare(const QString &comp)
}

I tried to use the template function declared on the header file that for completeness I am also attaching below:

  template<typename QEnum>
  std::string QtEnumToString (const QEnum value)
  {
    return std::string(QMetaEnum::fromType<QEnum>().valueToKey(value));
  }

Maybe I the template function is the wrong choice? I was trying to find a way to use it if possible.

That is the reason why I switched to a void compare(const QString& comp); function and tried to delegate the job to a switch - case statement but that also is not working as I expect and I still see the same exact QtableWidget dropped onto the QGraphicsView.

Of course I did more research and came across this source and above all this post which was useful to understand the basic comparison and following this post I decided to go ahead and try to apply the Q_ENUM - QString or even the QStringList as a valuable tool. But I could not figure out what I was doing wrong.

Can anyone please shed light on which approach could be better? (or maybe they are both correct) and try to explain what I am missing to solve this problem.

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • I don't think that the given code can even produce the wrong output. `tabs` is just an empty list, then for each element in `tabs`, a table is displayed? – Minh Oct 14 '20 at 06:13
  • I assume `comp` to be "Images" or "Path", why is `toUpper()` used? – Minh Oct 14 '20 at 06:14
  • Hello @NgocMinhNguyen and thanks for stopping by and reading the question. Yes `comp` is used for either "Images" or "Path". The problem I have is that I was trying to combine that with using the `template` function. It is a way for me to practice using `template<>` compared with `QEnum` and `QString` but something is missing and can't figure out what could that be. – Emanuele Oct 14 '20 at 15:18
  • You haven't answer my questions – Minh Oct 14 '20 at 17:20
  • Sorry you are right :) ok so: 1) yes for each element in `tabs` a table is displayed. 2) I was using `toUpper()` in a way to make the darg-drop from `QListWidget` choices (e.g. "Images", "Path") to `QGraphicsView` as case sensitive. So if there is "images" instead of "Images" the `MetaEnum` should throw and error and therefore the `template` shoud be able to handle that. – Emanuele Oct 14 '20 at 17:33
  • 1) But `tabs` is an empty list, the loops are not entered. 2) Since there is no "IMAGE" and "PATH" in your enum, the call to `keyToValue()` should always return -1 – Minh Oct 14 '20 at 18:33
  • Could you please answer with some code so that I am able to better understand your suggestion? :) Thank you very much so far for your time! – Emanuele Oct 14 '20 at 20:01

1 Answers1

1

There are two problems I see in your code:

  1. Wrong parameter passed to keyToValue(). Since you have Image and Path in your enum, the valid values to pass to keyToValue() are "Image" (returns 0), and "Path" (return 1), other values will returns -1.

  2. In the function Scene::compare(), tabs is just created as an empty QStringList, so the code inside the loops for (const QString &color : tabs) are never executed

Below is a test program to show you what I mean:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMetaEnum>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    enum LaserTableWidget
    {
        Images,
        Path
    };
    Q_ENUM(LaserTableWidget)

    template<typename enum_type>
    QString QtEnumToString (const enum_type value)
    {
        return QMetaEnum::fromType<enum_type>().valueToKey(value);
    }
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

#include <QDebug>
#include <QStringList>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // First problem: wrong parameter passed to keyToValue()

    qDebug() << 1 << QtEnumToString<LaserTableWidget>(Images);
    qDebug() << 2 << QtEnumToString<LaserTableWidget>(Path);

    QMetaObject MetaObject = this->staticMetaObject;
    QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator("LaserTableWidget"));

    qDebug() << 3 << MetaEnum.keyToValue(QtEnumToString<LaserTableWidget>(Path).toUpper().toLatin1());
    qDebug() << 4 << MetaEnum.keyToValue(QtEnumToString<LaserTableWidget>(Path).toStdString().c_str());

    switch (MetaEnum.keyToValue(QtEnumToString<LaserTableWidget>(Path).toUpper().toLatin1()))
    {
        case Images:
            qDebug() << "switch Images";
            break;
        case Path:
            qDebug() << "switch Path";
            break;
        default:
            qDebug() << "switch default";
            break;
    }

    // Second problem: tabs is empty
    QStringList tabs;
    for (const QString &color : tabs)
        qDebug() << color; // this line is never executed
}

MainWindow::~MainWindow()
{
}

Output:

1 "Images"
2 "Path"
3 -1
4 1
switch default
Minh
  • 1,630
  • 1
  • 8
  • 18
  • Thanks for your time in answering my question. I understand now what you mean with the `Q_ENUM` and how to correctly use it. Your time is greatly appreciated :) . Also if you think my question was clear please give me a thumbs up :) – Emanuele Oct 15 '20 at 17:50