0

I have a small dialog like this:

enter image description here

When I move the dialog into another place on the desktop, how can I get the new global coordinate of an element in the dialog (for example, in this case the top left point of the Ok button)? Imagine I have a subclass MyButton for the OK button and I want to use QEvent for this class and I am working in this class, not in QMainWindow.

bool MyButton::eventFilter( QObject *p_obj, QEvent *p_event )
{     
  if ( p_event->type() == QEvent::Move )
  {
     QPoint point = this->contentsRect().topLeft();
     point = mapToGlobal( point );
     qDebug() << point;
  }
  return QWidget::eventFilter( p_obj, p_event );
}

This function is wrong because the relative position of the button with the dialog never changes, but I dont know how to correct it to get the new global coordinate of the button when moving the dialog. I need the new coordinates continously, not after I release the mouse.

    gridLayout = new QGridLayout(Form);
    gridLayout->setObjectName(QStringLiteral("gridLayout"));
    horizontalLayout = new QHBoxLayout();
    horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
    lb_username = new QLabel(Form);
    lb_username->setObjectName(QStringLiteral("lb_username"));

    horizontalLayout->addWidget(lb_username);

    le_username = new QLineEdit(Form);
    le_username->setObjectName(QStringLiteral("le_username"));

    horizontalLayout->addWidget(le_username);

    gridLayout->addLayout(horizontalLayout, 0, 0, 1, 1);

    horizontalLayout_2 = new QHBoxLayout();
    horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
    lb_password = new QLabel(Form);
    lb_password->setObjectName(QStringLiteral("lb_password"));

    horizontalLayout_2->addWidget(lb_password);

    le_password = new QLineEdit(Form);
    le_password->setObjectName(QStringLiteral("le_password"));

    horizontalLayout_2->addWidget(le_password);

    gridLayout->addLayout(horizontalLayout_2, 1, 0, 1, 1);

    horizontalLayout_3 = new QHBoxLayout();
    horizontalLayout_3->setObjectName(QStringLiteral("horizontalLayout_3"));
    horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

    horizontalLayout_3->addItem(horizontalSpacer);

    btn_ok = new MyButton();
    btn_ok->setObjectName(QStringLiteral("btn_ok"));

    horizontalLayout_3->addWidget(btn_ok);

    btn_cancel = new MyButton();
    btn_cancel->setObjectName(QStringLiteral("btn_cancel"));

    horizontalLayout_3->addWidget(btn_cancel);

    gridLayout->addLayout(horizontalLayout_3, 2, 0, 1, 1);

    verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);

    gridLayout->addItem(verticalSpacer, 3, 0, 1, 1);
songvan
  • 369
  • 5
  • 21
  • Sorry, you have posted too much irrelevant code. Digging into, I missed some of the relevant code (e.g. construction of `QDialog` itself, installing the event filter) but, may be, you are just not aware about that... – Scheff's Cat Oct 23 '18 at 07:11

1 Answers1

2

Qt Doc. has a nice introduction about event filters: The Event System – Event Filters including a small sample.

I'm missing two essential things in the question of OP:

  1. How is the QDialog constructed?
  2. Where is the event filter (in MyButton) installed?

Additionally, the OP seems not to know about QWidget::mapToGlobal():

Translates the widget coordinate pos to global screen coordinates. For example, mapToGlobal(QPoint(0,0)) would give the global coordinates of the top-left pixel of the widget.

Regarding mapToGlobal, there is already at least one other Q/A in SO:

SO: Qt - Determine absolute widget and cursor position

However, I made an MCVE to demonstrate a solution – testQButtonGlobalPos.cc:

#include <QtWidgets>

class WidgetPosFilter: public QObject {
  private:
    QWidget &qWidget;

  public:
    WidgetPosFilter(
      QWidget &qWidget, QObject *pQParent = nullptr):
      QObject(pQParent), qWidget(qWidget)
    { }
    virtual ~WidgetPosFilter() = default;
    WidgetPosFilter(const WidgetPosFilter&) = delete;
    WidgetPosFilter& operator=(const WidgetPosFilter&) = delete;

  protected:
    virtual bool eventFilter(QObject *pQbj, QEvent *pQEvent) override;

};

bool WidgetPosFilter::eventFilter(
  QObject *pQObj, QEvent *pQEvent)
{
  if (pQEvent->type() == QEvent::Move) {
    qDebug() << "QWidget Pos.:"
      << "local:" << qWidget.pos()
      << "global:" << qWidget.mapToGlobal(QPoint(0, 0));
  }
  return QObject::eventFilter(pQObj, pQEvent);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup UI of main window
  QPushButton qBtnOpenDlg(
    QString::fromUtf8("Open new dialog..."));
  qBtnOpenDlg.show();
  // setup UI of dialog
  QDialog qDlg(&qBtnOpenDlg);
  QVBoxLayout qVBox;
  QDialogButtonBox qDlgBtns;
  QPushButton qBtn(QString::fromUtf8("The Button"));
  qDlgBtns.addButton(&qBtn, QDialogButtonBox::AcceptRole);
  qVBox.addWidget(&qDlgBtns);
  qDlg.setLayout(&qVBox);
  WidgetPosFilter qBtnPosFilter(qBtn);
  // install signal handlers
  QObject::connect(&qBtnOpenDlg, &MyButton::clicked,
    [&](bool) { qDlg.show(); });
  qDlg.installEventFilter(&qBtnPosFilter);
  // runtime loop
  return app.exec();
}

The Qt project to build – testQButtonGlobalPos.pro:

SOURCES = testQButtonGlobalPos.cc

QT += widgets

Compiled and tested in cygwin64 on Windows 10:

$ qmake-qt5 testQButtonGlobalPos.pro

$ make && ./testQButtonGlobalPos
Qt Version: 5.9.4
QWidget Pos.: local: QPoint(0,0) global: QPoint(11,11)
QWidget Pos.: local: QPoint(83,0) global: QPoint(2690,68)
QWidget Pos.: local: QPoint(83,0) global: QPoint(98,45)
QWidget Pos.: local: QPoint(83,0) global: QPoint(2658,42)
QWidget Pos.: local: QPoint(83,0) global: QPoint(5218,46)
QWidget Pos.: local: QPoint(83,0) global: QPoint(3097,219)
QWidget Pos.: local: QPoint(83,0) global: QPoint(2251,197)

Each of the lines starting with QWidget Pos.: (except the first two) appeared after I had moved the dialog to a new position. The first two lines were printed instead when I opened the dialog. So, the first seems to reflect an intermediate state when dialog has not yet been positioned on desktop.

Snapshot of testQButtonGlobalPos

Notes:

  1. The basic principle of event filters consists of an object which processes filtered events in its virtual/overridden eventFilter() method. For this, the object must have a class derived from QObject. The approach of OP, having a class MyButton: public QPushButton would be sufficient. However, actually, any class derived from QObject could do this as well (as demonstrated in my example).

  2. To make an event filter object work, it is important to install it calling QObject::installEventFilter() for the object to watch. In my case, this was the QDialog qDlg to which the button in quest belongs to.

  3. Out of curiosity, I tried an alternative: overriding the QWidget::moveEvent() in a class MyButton: public QPushButton. This didn't provide what OP/I intended. The MyButton::moveEvent() was called once when the QDialog qDlg was opened. Moving the dialog with mouse didn't call it again. It seems that move events are receieved by the QDialog but not further propagated to child widgets. This is in so far reasonable as moving the whole dialog window doesn't change its interior layout. Hence, the approach of the OP using an event filter for this was the right track.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56