如何使用箭头将 QTableWidget 单元格的 QRadioButton 与另一个 QTableWidget 单元格连接? [英] How to connect a QRadioButton of a QTableWidget cell with another QTableWidget cell using an arrow?

查看:62
本文介绍了如何使用箭头将 QTableWidget 单元格的 QRadioButton 与另一个 QTableWidget 单元格连接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 QListWidget 和一个 QGraphicsView 子类来覆盖它们的一些成员.我准备了一个最小的可验证示例,显示我在对于理解如何做到这一点的最初想法很有用,但它并没有真正提供一种方法,或关于如何最好地进行的实施想法

2) 我研究了官方文档,在问这个问题之前,我浏览了整个 上找到我为您编写的完整示例.

结果

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

注意:缺少箭头.再次检查 Diagram Scene Example 以获得一个如何绘制它们的想法.

I have a QListWidget and a QGraphicsView both subclassed to overwrite some of their members. I prepared a minimal verifiable example showing the problem I have here

From the QListWidget I can drag and drop specific field (represented by a QTableWidget) and drop them into a QGraphicsView and in order to do that I am using a QGraphicsProxyWidget approach as shown below.

The Problem

Now, how do I connect 2 QRadioButton inside cell of a QTableWidget with another cell of another QTableWidget?

It is important to mention that the green QGraphicsRectItem it is used to move around the QTableWidget as well as adjusting its dimension.

Below the result I was able to arrive so far:

And below the expected result I have been trying to achieve:

Below the most important part of the code:

scene.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

scene.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));
        }
    }
}

diagramitem.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

diagramitem.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;
}

arrow.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

arrow.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);
    }
}

What I have done so far to solve the problem:

1) I came across this post which was useful to understand the initial idea on how to do that, but it didn't really provide a way, or an implementation idea on how to best proceed

2) I researched the official documentation and before asking this question I went through the whole Diagram Scene example provided and understood how to create an Arrow object. The documentation about that was very good and made me understand how the graphics line item has to be formed. However I was not able (coming back to my example) how to make "aware" the QRadioButton that I am trying to use its center as starting point for an arrow ad, therefore, how do I make "aware" the destination QRadioButton in another cell that it has to be connected there?

Below a particular of what I mean:

So basically the start point of the QRadioButton change color (or style) and the arrival point also change color.

3) I thought that the Arrow object has to be created inside the subclassed QGraphicsScene since it already handles the mouse events.

4) Despite what I tried so far I could not find any other useful help. Although I am still investigating how to do that.

If anyone has ever been in the same situation please provide guidance on how to better proceed to solve this problem and find a solution to this issue.

解决方案

Solution

When a start and end radio buttons are checked, you need to create the arrow with those buttons as start and end nodes, e.g.:

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));
}

Then you need to connect the signal of the top-most graphics items, which hold the tables, with the updatePosition slot of the ArrowItem, e.g.:

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

Note: I am using a property to hold a reference to the container item.

Finally, you need to update the arrow line, e.g.:

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));
}

Example

I have dared to suggest improvements in your code. You can find the complete example I wrote for you on GitHub.

Result

The provided example produces the following result:

Note: The arrow heads are missing. Check once again the Diagram Scene Example to get an idea of how to draw them.

这篇关于如何使用箭头将 QTableWidget 单元格的 QRadioButton 与另一个 QTableWidget 单元格连接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆