LINUX.ORG.RU

Автоматическое нажатие QPushButton внутри QDialog

 ,


0

1
#include <QApplication>
#include <QDebug>
#include <QDialog>
#include <QPushButton>
#include <QTreeWidget>
#include <QVBoxLayout>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    QDialog dialog;
    QVBoxLayout layout(&dialog);

    QTreeWidget treeWidget;
    treeWidget.insertTopLevelItem(0, new QTreeWidgetItem(&treeWidget));
    QObject::connect(&treeWidget, &QTreeWidget::activated, [&treeWidget]() {
        auto secondDialog = new QDialog(&treeWidget);
        auto layout = new QVBoxLayout(secondDialog);
        auto button = new QPushButton();
        QObject::connect(button, &QPushButton::clicked, []() {
            qDebug() << "button clicked";
        });
        layout->addWidget(button);
        secondDialog->show();
    });
    layout.addWidget(&treeWidget);

    dialog.show();

    return app.exec();
}

При активации элемента внутри QTreeWidget открывается дилог с кнопкой. Проблема в том, что если это делать нажатием Enter, то нажимается кнопка внутри диалога (и если на нее законнектить принятие диалога, то он сразу же закроется). Почему это происходит, и как от этого избавиться?

★★

Не совсем по теме, но зачем каждый раз создавать диалог? Может все-таки создать его один раз и показывать когда нужно?

CrossFire ★★★★★
()
Ответ на: комментарий от CrossFire

Диалог не модальный, поэтому для каждого элемента должна быть возможность открыть свой экземпляр. В диалоге отображается дополнительная информация для элемента модели.

equeim ★★
() автор топика
Ответ на: комментарий от RazrFalcon

Проверил в дебиане с Qt 5.3, то же самое.

equeim ★★
() автор топика
Ответ на: комментарий от no-such-file

Каким образом? Если заменить родителя на dialog, то ничего не изменится. Если родителя убрать вообще, то нажатия кнопки не будет. Но в моем случае родитель у диалога должен быть (с точки зрения HIG). И вообще это явно ненормальное поведение.

equeim ★★
() автор топика
Ответ на: комментарий от equeim

Просто у QDialog это такая фишка, что при нажатии Enter нажимается дефолтная кнопка. Убирать его не вариант. Скорее всего проблема в том, что это нажатие в QTreeView зачем-то утекает в диалог. Или может там типа QTreeView слушает нажатие, создаёт диалог. А диалог слушает отжатие и уже создан к тому моменту. Пойду гляну...

ilammy ★★★
()

В общем, глянул. Всё ещё прозаичнее. Сначала событие «нажат Enter» получает QTreeWidget, отрабатывает его activate(), создаёт диалог, выходит. Но QTreeWidget это событие не поглощает, поэтому вызывается обработчик QDialog, который нажимает на кнопку.

Можно, например, навесить на QTreeWidget фильтр событий (man QObject::installEventFilter) или отнаследоваться от QTreeWidget и написать свою реализацию keyPressEvent(), которая вызывает родительскую, а потом делает event->accept() всем нажатиям на кнопку Qt::Key_Enter и Qt::Key_Return.

ilammy ★★★
()
Ответ на: комментарий от ilammy

В качестве магического шайтан-решения ещё можно лямбду с созданием диалога подключать к сигналу как Qt::QueuedConnection. Тогда диалог будет создаваться после обработки события с нажатием Enter.

ilammy ★★★
()
Ответ на: комментарий от ilammy

Спасибо, сработало!

В общем, глянул. Всё ещё прозаичнее. Сначала событие «нажат Enter» получает QTreeWidget, отрабатывает его activate(), создаёт диалог, выходит. Но QTreeWidget это событие не поглощает, поэтому вызывается обработчик QDialog, который нажимает на кнопку.

Можно поподробнее, каким образом событие переходит к дочернему диалогу? QAbstrackItemView после emit activated() делает event->ignore(). В документации написано, что «Clearing the accept parameter indicates that the event receiver does not want the event. Unwanted events might be propagated to the parent widget.». Но этот диалог является потомком, а не родителем. Т.е. насколько я понял, если заигнорить событие, то оно переходит к родительскому виджету, а не в обратную сторону.

И почему проблема не возникает, если корневой диалог, в котором находится QTreeWidget, заменить на QWidget?

equeim ★★
() автор топика
Последнее исправление: equeim (всего исправлений: 1)
Ответ на: комментарий от equeim

И вообще это явно ненормальное поведение.

Это почему? Разработчики видимо полагали, что наследниками QTreeWidget будут какие-то виджеты _внутри_ дерева, но никак не диалоги и поэтому передают событие активации дальше, чтобы дочерний элемент его также получил.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Тут все несколько сложнее. Проблема не возникает, если родителем QTreeWidget является QWidget, а не QDialog. Т.е. QTreeWidget игнорит событие нажатия энтера и оно передается его родителю (корневому диалогу). А тот каким-то образом, мне непонятным, передает его второму диалогу, если его родителем является QTreeWidget или сам корневой диалог.

equeim ★★
() автор топика
Ответ на: комментарий от no-such-file

Переделал пример:

#include <QApplication>
#include <QDebug>
#include <QDialog>
#include <QPushButton>
#include <QTreeWidget>
#include <QVBoxLayout>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    QDialog dialog;
    QVBoxLayout layout(&dialog);
    QTreeWidget treeWidget;
    treeWidget.insertTopLevelItem(0, new QTreeWidgetItem(&treeWidget));
    layout.addWidget(&treeWidget);
    dialog.show();

    QDialog dialog2(&dialog);
    QVBoxLayout layout2(&dialog2);
    QPushButton button;
    QObject::connect(&button, &QPushButton::clicked, []() {
        qDebug() << "button clicked";
    });
    layout2.addWidget(&button);
    dialog2.show();

    return app.exec();
}
При нажатии энтера на элементе QTreeWidget в первом диалоге нажимается кнопка во втором диалоге. Это можно решить так, как предложил ilammy (отлавливать события нажатия энтера у QTreeWidget и делать event->accept()), но это явно выглядит как баг.

equeim ★★
() автор топика
Ответ на: комментарий от equeim

Ясно. Похоже, QDialog при нажатии энтера ищет QPushButton'ы среди своих потомков рекурсивно (а не только среди своих прямых потомков) и нажимает, если они дефолтные. Проблема в том, что QTreeWidget не съедает нажатие энтера.

equeim ★★
() автор топика
Ответ на: комментарий от equeim

Не похоже, а он именно так и делает, если посмотреть в сорцы. И он не может делать по-другому, так как кнопка может находиться в каком-то дочернем QGroupBox или ещё что. Он мог бы, конечно, смотреть, нет ли между ним и кнопкой какого-то виджета-окна, но это как-то сильно сложно и нужно только в странных юз-кейсах.

ilammy ★★★
()
Ответ на: комментарий от ilammy

Ясно. Но то, что QAbstractItemView не съедает нажатие энтера после сигнала activated(), выглядит очень странно.

equeim ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.