如何使用箭头将QTableWidget单元格的QRadioButton与另一个QTableWidget单元格连接?

如何解决如何使用箭头将QTableWidget单元格的QRadioButton与另一个QTableWidget单元格连接?

我有一个QListWidgetQGraphicsView都被子类化以覆盖其某些成员。我准备了一个最小的可验证示例,以显示我遇到的问题here

我可以从QListWidget拖放特定字段(由QTableWidget表示)并将其拖放到QGraphicsView中,为此,我使用QGraphicsProxyWidget方法如下所示。

问题

现在,如何将qradiobutton一个单元格内的2个QTableWidget与另一个QTableWidget的另一个单元格相连?

重要的是要提到绿色的QGraphicsRectItem用于在QTableWidget周围移动并调整其尺寸。

到目前为止,我可以得到以下结果:

pr3

在我一直试图达到的预期结果以下:

pr4

代码最重要的部分下面:

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,QPen(Qt::black),QBrush(Qt::darkGreen));
            auto *sizegrip = new QSizegrip(wgt);
            auto *layout = new QHBoxLayout(wgt);

            layout->setContentsMargins(0,0);
            layout->addWidget(sizegrip,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,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));
        }
    }
}

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,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),mapFromItem(myEndItem,0));
    setLine(line);
}

void Arrow::paint(QPainter *painter,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)我碰到了this post,它对于理解有关如何执行操作的最初想法很有用,但它并没有真正提供一种方法或有关如何实现的实现想法最好地进行

2)我研究了官方文档,在问这个问题之前,我仔细阅读了提供的整个Diagram Scene示例,并了解了如何创建Arrow对象。关于该文档的文档非常好,让我了解了必须如何形成图形订单项。 但是,我无法(返回示例)如何使qradiobutton“意识到”我试图将其中心用作箭头广告的起点,因此,如何使“ {wareness}”目的地qradiobutton在另一个必须连接在那里的单元格中?

以下是我的意思:

pr5

因此,qradiobutton的起点基本上会更改颜色(或样式),到达点也会更改颜色。

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

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

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

解决方法

解决方案

选中开始和结束单选按钮后,您需要使用这些按钮作为开始和结束节点来创建箭头,例如:

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的{​​{1}}插槽相连,例如:

ArrowItem

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

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

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

示例

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

结果

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

App window with interconnected tables

注意:缺少箭头。再次检查Diagram Scene Example,以了解如何绘制它们。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?