QWidget::mapToGlobal() not giving right result
-
Summary: In a nutshell, is
QWidget::mapToGlobal()
supposed to give correct result if the widget is on a [box] layout?I am using
maptoGlobal()
to display a popup window off aQPushButton
. I want the popup window to appear right where the button is. So I am using just what you would expect:QWidget _popup; // no parent _popup.setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); // this is QPushButton, with parentage, on layout connect(this, &QPushButton::clicked, this, &UIUtils::PushButtonWithPopup::doPopup); /*slot*/ void UIUtils::PushButtonWithPopup::doPopup() { QPoint pos = this->mapToGlobal(this->pos()); _popup.show(); _popup.move(pos.x(), pos.y()); }
What I am finding is: when the button is at the lefthand side of window --- i.e.
pos.x() ==~ 0
--- the global position is good. But as it moves right ---pos.x() > 100
etc. ---mapToGlobal(pos())
is returning anx()
which is getting larger & larger, so the popup appears further & further to the right of the button. They()
however remains correct.Now, the button is on a
QHBoxlayout
. I started to read an old post
https://www.qtcentre.org/threads/40574-mapToGlobal-mapFromGlobal-isn-t-working-properly
which said at one pointSo it is managed by a layout which might explain why your own geometry management fails. If you wish to do your own moving/resizing, you can't use layouts at the same time.
Is that what is going on?
Please don't ask me to repro a full example for now, it will take too long. I would just like to quickly know: if I place a widget on a layout, does that mean I won't be able to use
mapToGlobal(pos())
to get the correct global screen coordinate to position a popup at? If so, how can I map from a widget on a layout to its correct screen position? Thanks. -
@JonB said in QWidget::mapToGlobal() not giving right result:
QPoint pos = this->mapToGlobal(this->pos());
That's wrong.
pos()
is a position in the parent, andthis->mapToGlobal
maps it relatively to this, not the parent, so simply said - you're mixing different coordinate systems - local and parent. Additionally, in case of a top level widget that has no parent,pos()
is already a global position.If you want the global position of the button you either map its position in parent relative to that parent:
QPoint global_pos = btn->parentWidget()->mapToGlobal(btn->pos());
or, simpler, and IMO more appropriate here, just map the upper left corner of your button relative to it:
QPoint global_pos = btn->mapToGlobal(QPoint(0,0));
-
Summary: In a nutshell, is
QWidget::mapToGlobal()
supposed to give correct result if the widget is on a [box] layout?I am using
maptoGlobal()
to display a popup window off aQPushButton
. I want the popup window to appear right where the button is. So I am using just what you would expect:QWidget _popup; // no parent _popup.setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); // this is QPushButton, with parentage, on layout connect(this, &QPushButton::clicked, this, &UIUtils::PushButtonWithPopup::doPopup); /*slot*/ void UIUtils::PushButtonWithPopup::doPopup() { QPoint pos = this->mapToGlobal(this->pos()); _popup.show(); _popup.move(pos.x(), pos.y()); }
What I am finding is: when the button is at the lefthand side of window --- i.e.
pos.x() ==~ 0
--- the global position is good. But as it moves right ---pos.x() > 100
etc. ---mapToGlobal(pos())
is returning anx()
which is getting larger & larger, so the popup appears further & further to the right of the button. They()
however remains correct.Now, the button is on a
QHBoxlayout
. I started to read an old post
https://www.qtcentre.org/threads/40574-mapToGlobal-mapFromGlobal-isn-t-working-properly
which said at one pointSo it is managed by a layout which might explain why your own geometry management fails. If you wish to do your own moving/resizing, you can't use layouts at the same time.
Is that what is going on?
Please don't ask me to repro a full example for now, it will take too long. I would just like to quickly know: if I place a widget on a layout, does that mean I won't be able to use
mapToGlobal(pos())
to get the correct global screen coordinate to position a popup at? If so, how can I map from a widget on a layout to its correct screen position? Thanks.@JonB I can reproduce it:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget w; QLabel l; l.setStyleSheet("background-color:red;"); w.resize(200,50); QHBoxLayout *hLay = new QHBoxLayout(&w); for(int i(0); i <9; i++) { QPushButton *btn = new QPushButton(QString("Btn%1").arg(i)); QObject::connect(btn, &QPushButton::clicked, &l, [=, &l]()->void{ QPoint p = btn->mapToGlobal(btn->pos()); l.resize(btn->size()); l.move(p); }); hLay->addWidget(btn); } w.show(); l.show(); return app.exec(); }
-
@JonB said in QWidget::mapToGlobal() not giving right result:
QPoint pos = this->mapToGlobal(this->pos());
That's wrong.
pos()
is a position in the parent, andthis->mapToGlobal
maps it relatively to this, not the parent, so simply said - you're mixing different coordinate systems - local and parent. Additionally, in case of a top level widget that has no parent,pos()
is already a global position.If you want the global position of the button you either map its position in parent relative to that parent:
QPoint global_pos = btn->parentWidget()->mapToGlobal(btn->pos());
or, simpler, and IMO more appropriate here, just map the upper left corner of your button relative to it:
QPoint global_pos = btn->mapToGlobal(QPoint(0,0));
-
@JonB said in QWidget::mapToGlobal() not giving right result:
QPoint pos = this->mapToGlobal(this->pos());
That's wrong.
pos()
is a position in the parent, andthis->mapToGlobal
maps it relatively to this, not the parent, so simply said - you're mixing different coordinate systems - local and parent. Additionally, in case of a top level widget that has no parent,pos()
is already a global position.If you want the global position of the button you either map its position in parent relative to that parent:
QPoint global_pos = btn->parentWidget()->mapToGlobal(btn->pos());
or, simpler, and IMO more appropriate here, just map the upper left corner of your button relative to it:
QPoint global_pos = btn->mapToGlobal(QPoint(0,0));
@Chris-Kawa +1 that explains so much of my past experience 🤦♂️
with that in mind, this works now as well:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget w; QLabel l; l.setStyleSheet("background-color:red;"); w.resize(500,50); QHBoxLayout *hLay = new QHBoxLayout(&w); for(int i(0); i <9; i++) { QPushButton *btn = new QPushButton(&w); btn->setText(QString("Btn%1").arg(i)); QObject::connect(btn, &QPushButton::clicked, &l, [=, &l, &w]()->void{ QPoint p = w.mapToGlobal(btn->pos()); l.resize(btn->size()); l.move(p); }); hLay->addWidget(btn); } w.show(); l.show(); return app.exec(); }
-
Thank you both for prompt responses!
Additionally, in case of a top level widget that has no parent, pos() is already a global position.
I'm a little lost here. I am trying to get the button's coordinates, the button does have parentage. It is the popup which will not have parentage, and so need global coordinates...?
Meanwhile, I am about to try out your solutions and will report back....
UPDATE
Still not sure what you meant byQPoint global_pos = btn->mapToGlobal(QPoint(0,0));
That's what I had originally ([EDIT No, see below]this == btn
); I think you meant that if it had no parent?Anyway, altered to
QPoint pos = this->parentWidget()->mapToGlobal(this->pos());
and lo & behold the global position is correct, whether the button is at left or right of its parent/layout.
You are an genius, thank you! :)
EDIT
OIC,btn->mapToGlobal(QPoint(0,0))
, that'sQPoint(0, 0)
notbtn->pos()
. Got it! So now I have:QPoint pos = this->mapToGlobal(QPoint(0, 0));
I understand what's going on!!