Как получить новую координату виджета при перемещении окна? - PullRequest
0 голосов
/ 22 октября 2018

У меня есть маленький диалог, подобный этому:

enter image description here

Когда я перемещаю диалог в другое место на рабочем столе, как я могу получить новыйглобальная координата элемента в диалоге (например, в этом случае верхняя левая точка кнопки Ok)?Представьте, что у меня есть подкласс MyButton для кнопки OK, и я хочу использовать QEvent для этого класса, и Я работаю в этом классе, а не в 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 );
}

Эта функция неправильнаяпотому что относительное положение кнопки с диалоговым окном никогда не меняется, но я не знаю, как это исправить, чтобы получить новую глобальную координату кнопки при перемещении диалога. Мне нужны новые координаты постоянно, а не после того, как я отпущу мышь.

    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);

1 Ответ

0 голосов
/ 23 октября 2018

Qt Doc.есть хорошее введение в фильтры событий: Система событий - Фильтры событий , включая небольшой пример.

Я упускаю две важные вещи в вопросе OP:

  1. Как создается QDialog?
  2. Где установлен фильтр событий (в MyButton)?

Кроме того, кажется, что OPне знать о QWidget::mapToGlobal():

Переводит координату виджета pos в глобальные экранные координаты.Например, mapToGlobal(QPoint(0,0)) даст глобальные координаты верхнего левого пикселя виджета.

Что касается mapToGlobal, в SO уже есть хотя бы еще один Q / A:

SO: Qt - Определить абсолютный виджет и положение курсора

Однако я сделал MCVE , чтобы продемонстрировать решение - 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();
}

Проект Qt для сборки - testQButtonGlobalPos.pro:

SOURCES = testQButtonGlobalPos.cc

QT += widgets

Скомпилировано и протестировано в cygwin64 в 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)

Каждыйиз строк, начинающихся с QWidget Pos.: (кроме первых двух), появились после Я переместил диалог на новую позицию.Первые две строки были напечатаны, когда я открыл диалог.Итак, первое, кажется, отражает промежуточное состояние, когда диалог еще не был размещен на рабочем столе.

Snapshot of testQButtonGlobalPos

Примечания:

  1. Основной принцип фильтров событий состоит из объекта, который обрабатывает отфильтрованные события в своем виртуальном / переопределенном методе eventFilter().Для этого у объекта должен быть класс, производный от QObject.Подход ОП, имеющий class MyButton: public QPushButton, будет достаточным.Однако, на самом деле, любой класс, производный от QObject, также может это сделать (как показано в моем примере).

  2. Чтобы заставить работать объект фильтра событий, важно установить еговызывая QObject::installEventFilter() для объекта для наблюдения.В моем случае это была QDialog qDlg, к которой принадлежит кнопка в квесте.

  3. Из любопытства я попробовал альтернативу: переопределить QWidget::moveEvent() в class MyButton: public QPushButton.Это не обеспечило то, что OP / I намеревался.MyButton::moveEvent() был вызван один раз, когда QDialog qDlg был открыт.Перемещение диалога мышью не вызывало его снова.Кажется, что события перемещения принимаются QDialog, но не распространяются далее на дочерние виджеты.Это настолько разумно, что перемещение всего диалогового окна не меняет его внутреннюю планировку.Следовательно, подход OP с использованием фильтра событий для этого был правильным путем.

...