Search code examples
qtuser-interfaceqtwidgets

Get the position of a QWidget that is inside a QGraphicsItem


I have a QWidget with a QPushButton, at the same time, this QWidget is embedded into a QGraphicsItem, which is inside a QGraphicsScene.

I need to draw a line between two of those QGraphicsItems pointing to the QPushButton. For that, I need to get the position of the QPushButton. It looks like this:

picture

I tried getting the position of the QPushButton inside the constructor of the QGraphicsItem, but it returns 0,0. I guess this is the position of the button inside the QWidget. I guess what I need is a way to get the position on the screen.

Minimal Example: Simplified as much as possible. QWidget:

NodeFrame::NodeFrame()
{
  setFixedSize(200,80);
  setStyleSheet("QFrame { background-color: #2e4076; }");
  
  // Creates and add a QPushButton to the frame.
  // I need the position of this button on the QGraohicsScene
  auto button = new QPushButton("B");
  button->setFixedSize(40,20);
  
  auto layout = new QHBoxLayout();
  layout->addWidget(button);
  setLayout(layout);
}

QGraphicsItem:

class Node : public QGraphicsItem
{
public:
  Node();
  QRectF boundingRect() const override;
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};

Node::Node()
{
  setFlag(ItemIsMovable);

  // Create a GraphicsProxyWidget to insert the nodeFrame into the scene
  auto proxyWidget = new QGraphicsProxyWidget(this);
  auto frame = new NodeFrame();
  proxyWidget->setWidget(frame);
  // Center the widget(frame) at the center of the QGraphicsItem
  proxyWidget->setPos(boundingRect().center() - proxyWidget->boundingRect().center());
}

QRectF Node::boundingRect() const
{
  return QRectF(-10, -10, 280, 150);
}

void Node::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
  QPainterPath path;
  path.addRoundedRect(boundingRect(), 10, 10);
  painter->drawPath(path);
}

Main:

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);
  
  // Create scene and view
  auto scene = new QGraphicsScene();
  auto view = new QGraphicsView(scene);
  
  view->setMinimumSize(800, 800);
  
  // Create the QGraphicsItem and add it to the scene
  auto item = new Node();
  scene->addItem(item);
  item->setPos(-50, -50);
  
  // Show the the view
  view->show();
  return app.exec();
}

Solution

  • In nodeframe.cpp I add one function getButtonRect() :

    #ifndef NODEFRAME_H
    #define NODEFRAME_H
    
    #include <QWidget>
    #include <QPushButton>
    #include <QRect>
    
    
    class NodeFrame: public QWidget
    {
    public:
        NodeFrame();
    
        QRect  getButtonRect();
    
    private:
        QPushButton *button;
        QHBoxLayout *layout;
    
    };
    
    #endif // NODEFRAME_H
    

    nodeframe.cpp

    #include "nodeframe.h"
    
    NodeFrame::NodeFrame()
    {
        setFixedSize(200, 80);
        setStyleSheet("QFrame { background-color: #2e4076; }");
    
        // Creates and add a QPushButton to the frame.
        // I need the position of this button on the QGraohicsScene
        button = new QPushButton("B");
        button->setFixedSize(40, 20);
    
        layout = new QHBoxLayout();
        layout->addWidget(button);
        setLayout(layout);
    }
    
    QRect  NodeFrame::getButtonRect()
    {
        return layout->itemAt(0)->geometry();
    }
    
    

    and in Node pass this function to main.cpp because QGraphicsView is there:

    node.cpp:

    #include "node.h"
    
    #include <QGraphicsProxyWidget>
    #include <QPainter>
    
    Node::Node()
    {
        setFlag(ItemIsMovable);
    
        // Create a GraphicsProxyWidget to insert the nodeFrame into the scene
        auto  proxyWidget = new QGraphicsProxyWidget(this);
        frame = new NodeFrame();
        proxyWidget->setWidget(frame);
        // Center the widget(frame) at the center of the QGraphicsItem
        proxyWidget->setPos(boundingRect().center() - proxyWidget->boundingRect().center());
    }
    
    QRectF  Node::boundingRect() const
    {
        return QRectF(-10, -10, 280, 150);
    }
    
    void  Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        QPainterPath  path;
    
        path.addRoundedRect(boundingRect(), 10, 10);
        painter->drawPath(path);
    }
    
    QRect  Node::getButtonRect()
    {
        return frame->getButtonRect();
    }
    

    main.cpp

    #include "node.h"
    
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    
    int  main(int argc, char *argv[])
    {
        QApplication  app(argc, argv);
    
        // Create scene and view
        auto  scene = new QGraphicsScene();
        auto  view  = new QGraphicsView(scene);
    
        view->setMinimumSize(800, 800);
    
        // Create the QGraphicsItem and add it to the scene
        auto  item = new Node();
        scene->addItem(item);
        item->setPos(0, 0);
    
    // qDebug() << "RECT bottomLeft= " << view->mapToScene(item->getButtonRect().bottomLeft());
    // qDebug() << "RECT bottomRight= " << view->mapToScene(item->getButtonRect().bottomRight());
    // qDebug() << "RECT topLeft= " << view->mapToScene(item->getButtonRect().topLeft());
    // qDebug() << "RECT topRight= " << view->mapToScene(item->getButtonRect().topRight());
    
        auto  btnRect = item->getButtonRect();
        auto  ellipse = new QGraphicsEllipseItem(QRect(view->mapToGlobal(btnRect.center()).x(), view->mapToGlobal(btnRect.center()).y(), 40, 40));
        qDebug() << "Center" << view->mapToGlobal(btnRect.center());
        scene->addItem(ellipse);
    
        // Show the the view
        view->show();
    
        return app.exec();
    }
    

    enter image description here

    enter image description here