2

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();
}
Tac
  • 25
  • 6

1 Answers1

3

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

Parisa.H.R
  • 3,303
  • 3
  • 19
  • 38
  • 1
    Thanks. It does get the position, but it seems to be its position inside the parent QWidget or something else. Following your example, I added a QGraphicsEllipseItem or a circle to the center of the QRect of the button. I was expecting the circle to be located at the center of the button, but it is not: https://i.gyazo.com/51b121821a68d66fffc3079b7ad54966.gif Somehow this position needs to be mapped to the scene or the view. – Tac Apr 23 '22 at 17:31
  • maybe layout makes this happens. I edited my answer mapToScene give you Scene position – Parisa.H.R Apr 23 '22 at 17:59
  • We are getting closer. This is my output: Button Position: QPoint(-272633456,32766) Button Position Mapped: QPointF(-2.72634e+08,32766) It looks different now. However, when I add the circle to this position(in main) the circle is far away from the button: https://i.gyazo.com/2551ad45b7037021e4505afed6f30f49.gif Notice that I'm using the center of the button's rect as position after mapping it to the scene like in your example. – Tac Apr 23 '22 at 18:24
  • 1
    mapToGlobal() takes it closer but not there yet: https://i.gyazo.com/ad97ce948d23d6ac023d362d4b02c32a.gif – Tac Apr 23 '22 at 18:38
  • @Tac I think `QHBoxLayout` makes problems we should get a pushbutton from it by `layout->itemAt(0)->geometry();` This point is pushbutton center that I print it. – Parisa.H.R Apr 23 '22 at 18:53
  • and we shouldn't `item->setPos(-50, -50);` – Parisa.H.R Apr 23 '22 at 19:04
  • Ok. Now I get the button's geometry directly from the layout as you did and removed ```item->setPos(-50, -50)```. If I don't mapToScene I get something closer to your result: https://i.gyazo.com/826470a15f5c8af1246a16b478b47f93.gif But is that really the center? I thought it would be at the exact center of the button. But if I map it, it goes farther away: https://i.gyazo.com/8f22859352f7e2c6e386280649e026cd.gif – Tac Apr 23 '22 at 19:24
  • Use mapToGlobal not mapToScene – Parisa.H.R Apr 23 '22 at 19:25
  • I also tried mapping it to the item first ```QGraphicsItem::mapToItem``` inside ```Node`` (item), then mapping it to scene in ```main``` but the result is also incorrect. – Tac Apr 23 '22 at 19:26
  • Did you test `QRect(view->mapToGlobal(btnRect.center()).x(), view->mapToGlobal(btnRect.center()).y(), 40, 40)` ? Because as I add my picture it seems it shows button center @Tac – Parisa.H.R Apr 23 '22 at 19:28
  • 1
    Sorry, I didn't noticed that you updated the answer. Now I understand and I get the exact same results as you: https://i.gyazo.com/85c94863d66240a63ed760a5c50f92da.gif So, the center of the button's rect is at the corner? I also noticed that the smaller I get the circle the farther away it goes from the button: https://i.gyazo.com/8fd392f53147007e44b0e046bdd6b936.gif – Tac Apr 23 '22 at 19:57
  • Point is correct it is in the center you can test bottomRight or bottomleft or . .. you can see that point is correct but our width and height makes changes I set 40 *40 your https://i.gyazo.com/85c94863d66240a63ed760a5c50f92da.gif is correct – Parisa.H.R Apr 23 '22 at 20:03
  • 1
    Well, we found the center and that was the question. I guess something else is off. Thanks a lot for your time. I learned quite a bit with this. – Tac Apr 23 '22 at 20:17
  • You are welcome me too – Parisa.H.R Apr 23 '22 at 20:20