Как получить новую координату виджета при перемещении окна?
У меня есть маленький диалог, как это:
Когда я перемещаю диалоговое окно в другое место на рабочем столе, как я могу получить новую глобальную координату элемента в диалоговом окне (например, в этом случае верхняя левая точка кнопки 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 ответ
Qt Doc. есть хорошее введение в фильтры событий: Система событий - Фильтры событий, включая небольшой пример.
Я упускаю две важные вещи в вопросе ОП:
- Как
QDialog
построен? - Где находится фильтр событий (в
MyButton
) установлены?
Кроме того, ОП, кажется, не знает о 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.:
(кроме первых двух) появился после того, как я переместил диалог на новую позицию. Первые две строки были напечатаны вместо, когда я открыл диалог. Итак, первое, кажется, отражает промежуточное состояние, когда диалог еще не размещен на рабочем столе.
Заметки:
Основной принцип фильтров событий состоит из объекта, который обрабатывает отфильтрованные события в своем виртуальном / переопределенном
eventFilter()
метод. Для этого у объекта должен быть класс, производный отQObject
, Подход ОП, имеющийclass MyButton: public QPushButton
было бы достаточно. Однако, на самом деле, любой класс, производный отQObject
мог бы сделать это также (как продемонстрировано в моем примере).Чтобы заставить объект фильтра событий работать, важно установить его, вызывая
QObject::installEventFilter()
для объекта для просмотра. В моем случае это былQDialog qDlg
к которой принадлежит кнопка в квесте.Из любопытства я попробовал альтернативу: переопределить
QWidget::moveEvent()
вclass MyButton: public QPushButton
, Это не обеспечило то, что OP/I намеревался.MyButton::moveEvent()
был вызван один раз, когдаQDialog qDlg
был открыт. Перемещение диалога мышью не вызывало его снова. Похоже, что ход событий получаетQDialog
но не распространяется далее на дочерние виджеты. Это настолько разумно, что перемещение всего диалогового окна не меняет его внутреннюю планировку. Следовательно, подход OP, использующий фильтр событий для этого, был правильным путем.