0

我有一个QListWidget和一个QGraphicsView两个子类来覆盖他们的一些成员。我准备了一个最小的可验证示例,显示了我在这里遇到的问题

QListWidget我可以拖放特定字段(由 QTableWidget 表示)并将它们放入 aQGraphicsView中,为了做到这一点,我使用QGraphicsProxyWidget如下所示的方法。

问题

现在,如何将a 的单元QRadioButton格内的2QTableWidget与另一个单元格的另一个单元格连接起来QTableWidget

值得一提的是,QGraphicsRectItem它用于在绿色周围移动QTableWidget以及调整其尺寸。

在我能够到达的结果下方:

pr3

低于我一直试图达到的预期结果:

pr4

下面是代码最重要的部分:

场景.h

#ifndef SCENE_H
#define SCENE_H

#include <QGraphicsScene>

class Scene : public QGraphicsScene
{
public:
    Scene(QObject *parent = nullptr);

protected:
  void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
  void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
  void dropEvent(QGraphicsSceneDragDropEvent *event);
};

#endif // SCENE_H

场景.cpp

#include "arrow.h"

#include <QGraphicsSceneDragDropEvent>
#include <QMimeData>
#include <QTableWidget>
#include <QGraphicsProxyWidget>
#include <QVBoxLayout>
#include <QMetaEnum>
#include <QEvent>
#include <QSizeGrip>
#include <QRadioButton>


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

}

void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
  if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    event->setAccepted(true);
}

void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) {
  if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    event->setAccepted(true);
}

void Scene::dropEvent(QGraphicsSceneDragDropEvent *event) {
    QByteArray encoded =
      event->mimeData()->data("application/x-qabstractitemmodeldatalist");
    QDataStream stream(&encoded, QIODevice::ReadOnly);

    QStringList rosTables;
    QString newString;

    while (!stream.atEnd()) {
    int row, col;
    QMap<int, QVariant> roleDataMap;
    stream >> row >> col >> roleDataMap;
    rosTables << roleDataMap[Qt::DisplayRole].toString();
    }
    for (const QString &tableType : rosTables) {
        if (tableType == "Images") {
            QPoint initPos(0, 0);
            auto *wgt = new CustomTableWidget;
            auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
                                         QBrush(Qt::darkGreen));
            auto *sizeGrip = new QSizeGrip(wgt);
            auto *layout = new QHBoxLayout(wgt);

            layout->setContentsMargins(0, 0, 0, 0);
            layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom);

            connect(wgt, &CustomTableWidget::sizeChanged, [wgt, proxyControl](){
                proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
            });

            wgt->setColumnCount(4);
            wgt->setRowCount(4);

            for (int ridx = 0; ridx < wgt->rowCount(); ridx++) {
                for (int cidx = 0; cidx < wgt->columnCount(); cidx++) {
                    QRadioButton *radio1, *radio2;
                    auto* item = new QTableWidgetItem();
                    item->setText(QString("%1").arg(ridx));
                    wgt->setItem(ridx,cidx,item);
                    radio1 = new QRadioButton;
                    radio2 = new QRadioButton;
                    wgt->setCellWidget(cidx, 0, radio1);
                    wgt->setCellWidget(cidx, 3, radio2);
                    Arrow *arrow = new Arrow; 
                }
            }

            auto *const proxy = addWidget(wgt);

            proxy->setPos(initPos.x(), initPos.y()
                          + proxyControl->rect().height());
            proxy->setParentItem(proxyControl);

            proxyControl->setPos(initPos.x(), initPos.y());
            proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
            proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);
            proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
        }
    }
}

图表项.h

#ifndef DIAGRAMITEM_H
#define DIAGRAMITEM_H

#include <QGraphicsPolygonItem>

class Arrow;
class DiagramItem : public QGraphicsPolygonItem
{
public:

    DiagramItem(QMenu *contextMenu, QGraphicsItem *parent = Q_NULLPTR);

    void removeArrow(Arrow *arrow);
    void removeArrows();
    void addArrow(Arrow *arrow);
    QPixmap image() const;
protected:
    void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
    QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
private:
    QPolygonF myPolygon;
    QList<Arrow*> arrows;
    QMenu *myContextMenu;
};

#endif // DIAGRAMITEM_H

图表项.cpp

#include "diagramitem.h"
#include "arrow.h"
#include <QPainter>
#include <QGraphicsScene>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>


DiagramItem::DiagramItem(QMenu *contextMenu, QGraphicsItem *parent) : QGraphicsPolygonItem(parent)
{
    myContextMenu = contextMenu;
    setPolygon(myPolygon);
    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);

}

void DiagramItem::removeArrow(Arrow *arrow)
{
    int index = arrows.indexOf(arrow);

    if (index != -1)
        arrows.removeAt(index);
}

void DiagramItem::removeArrows()
{
    foreach (Arrow *arrow, arrows) {
        arrow->startItem()->removeArrow(arrow);
        arrow->endItem()->removeArrow(arrow);
        scene()->removeItem(arrow);
        delete arrow;
    }
}

void DiagramItem::addArrow(Arrow *arrow)
{
    arrows.append(arrow);
}

void DiagramItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    scene()->clearSelection();
    setSelected(true);
    myContextMenu->exec(event->screenPos());
}

QVariant DiagramItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    if (change == QGraphicsItem::ItemPositionChange) {
        foreach (Arrow *arrow, arrows) {
            arrow->updatePosition();
        }
    }
    return value;
}

箭头.h

#ifndef ARROW_H
#define ARROW_H

#include <QGraphicsLineItem>
#include "diagramitem.h"
class Arrow : public QGraphicsLineItem
{
public:

    enum { Type = UserType + 4 };
    Arrow(DiagramItem *startItem, DiagramItem *endItem,
      QGraphicsItem *parent = nullptr);

    DiagramItem *startItem() const { return myStartItem; }
    DiagramItem *endItem() const { return myEndItem; }
    QPainterPath shape() const override;

    void setColor(const QColor &color) {
        myColor = color;
    }

    int type() const override { return Type; }
    void updatePosition();

protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

private:
    QColor myColor;
    DiagramItem *myStartItem;
    DiagramItem *myEndItem;
    QPolygonF arrowHead;
};
#endif // ARROW_H

箭头.cpp

#include "arrow.h"
#include <QPen>
#include <QPainter>
#include "qmath.h"

Arrow::Arrow(DiagramItem *startItem, DiagramItem *endItem, QGraphicsItem *parent) : QGraphicsLineItem(parent)
{
    myStartItem = startItem;
    myEndItem = endItem;
    myColor = Qt::GlobalColor::black;
    setPen(QPen(myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
    setFlag(QGraphicsItem::ItemIsSelectable, true);
}

QPainterPath Arrow::shape() const
{
    QPainterPath path = QGraphicsLineItem::shape();
    path.addPolygon(arrowHead);
    return path;
}

void Arrow::updatePosition()
{
    QLineF line(mapFromItem(myStartItem, 0, 0), mapFromItem(myEndItem, 0, 0));
    setLine(line);
}

void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    if (myStartItem->collidesWithItem(myEndItem))
        return;

    QPen myPen = pen();
    myPen.setColor(myColor);
    qreal arrowSize = 20;
    painter->setPen(myPen);
    painter->setBrush(myColor);

    QLineF centerLine(myStartItem->pos(), myEndItem->pos());
    QPolygonF endPolygon = myEndItem->polygon();
    QPointF p1 = endPolygon.first() + myEndItem->pos();
    QPointF p2;
    QPointF intersectPoint;
    QLineF polyLine;
    for (int i = 1; i < endPolygon.count(); ++i) {
        p2 = endPolygon.at(i) + myEndItem->pos();
        polyLine = QLineF(p1, p2);
        QLineF::IntersectType intersectType =
            polyLine.intersect(centerLine, &intersectPoint);
        if (intersectType == QLineF::BoundedIntersection)
            break;
        p1 = p2;
    }

    setLine(QLineF(intersectPoint, myStartItem->pos()));

    double angle = std::atan2(-line().dy(), line().dx());

    QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
                                    cos(angle + M_PI / 3) * arrowSize);
    QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                                    cos(angle + M_PI - M_PI / 3) * arrowSize);

    arrowHead.clear();
    arrowHead << line().p1() << arrowP1 << arrowP2;

    painter->drawLine(line());
    painter->drawPolygon(arrowHead);
    if (isSelected()) {
        painter->setPen(QPen(myColor, 1, Qt::DashLine));
        QLineF myLine = line();
        myLine.translate(0, 4.0);
        painter->drawLine(myLine);
        myLine.translate(0,-8.0);
        painter->drawLine(myLine);
    }
}

到目前为止我为解决问题所做的工作:

1)我看到这篇文章有助于理解如何做到这一点的最初想法,但它并没有真正提供一种方法,或者关于如何最好地进行的实施想法

2)我研究了官方文档,在问这个问题之前,我浏览了提供的整个图表场景示例,并了解了如何创建Arrow对象。关于这方面的文档非常好,让我了解了图形行项目是如何形成的。但是,我无法(回到我的示例)如何“意识到”QRadioButton我正在尝试将其中心用作箭头广告的起点,因此,我如何“意识到”QRadioButton另一个单元格中的目的地它必须在那里连接吗?

下面是我的意思:

pr5

所以基本上QRadioButton改变颜色(或风格)的起点和到达点也改变颜色。

3)我认为该Arrow对象必须在子类中创建,QGraphicsScene因为它已经处理了mouse events.

4)尽管到目前为止我已经尝试过,但我找不到任何其他有用的帮助。虽然我仍在研究如何做到这一点。

如果有人遇到过同样的情况,请提供有关如何更好地解决此问题并找到解决此问题的方法的指导。

4

1 回答 1

1

解决方案

当检查启动和结束单按钮时,您需要用这些按钮作为开始和结束节点创建箭头,例如:

void Backend::onInputRadioButton(bool checked)
{
    m_endNode = checked ? static_cast<QRadioButton *>(sender()) : nullptr;

    if (m_startNode && m_endNode)
        m_scene->addItem(new ArrowItem(m_startNode, m_endNode));
}

然后,您需要将保存表格的最顶层图形项的信号与 的updatePosition插槽连接ArrowItem,例如:

connect(m_startItem->property("item").value<MovableItem *>(),
        &MovableItem::itemMoved, this, &ArrowItem::updatePosition);
connect(m_endItem->property("item").value<MovableItem *>(),
        &MovableItem::itemMoved, this, &ArrowItem::updatePosition);

注意:我使用一个属性来保存对容器项的引用。

最后,您需要更新箭头线,例如:

void ArrowItem::updatePosition()
{
    QPointF offset(7, 15);
    QPointF p1 = m_startItem->property("item").value<MovableItem *>()->pos()
                 + m_startItem->parentWidget()->mapToParent(m_startItem->pos())
                 + offset;
    QPointF p2 = m_endItem->property("item").value<MovableItem *>()->pos()
                 + m_endItem->parentWidget()->mapToParent(m_endItem->pos())
                 + offset;

    setLine(QLineF(p1, p2));
}

例子

我敢于对您的代码提出改进建议。您可以在GitHub 上找到我为您编写的完整示例。

结果

提供的示例产生以下结果:

具有互连表的应用程序窗口

注意:箭头不见了。再次检查图表场景示例以了解如何绘制它们。

于 2020-10-21T16:23:23.983 回答