Как я могу запретить клавише ввода закрывать мой QDialog (Qt 4.8.1)

У меня есть QDialog с QDialogButtonBox, Кнопки OK и Отмена активны. Иногда я отключаю или скрываю кнопку ОК в зависимости от состояния моего диалога. Кажется, что бы я ни делал, клавиша Enter всегда активирует кнопку ОК. Я действительно не хочу, чтобы это случилось. Я пытался:

  • Установка свойств по умолчанию и autoDefault в false каждый раз, когда я показываю / скрываю / включаю / отключаю / любую кнопку
  • установка фильтра событий на кнопку ОК для перехвата ключевых событий (нажата и отпущена) для возврата, ввода и пробела
  • Установка политики фокуса на кнопке на NoFocus

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

9 ответов

Фильтрация событий нажатия клавиши должна выполняться в самом диалоге, потому что код, обрабатывающий пересылку Return а также Enter ключи к кнопке по умолчанию в QDialog::keyPressEvent,

void Dialog::keyPressEvent(QKeyEvent *evt)
{
    if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)
        return;
    QDialog::keyPressEvent(evt);
}

Или же

theDialog−>installEventFilter(anotherClassObject);

bool AnotherClass::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(evt);
        if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return )
            return true; // mark the event as handled
    }
    return false;
}

Если у вас в диалоговом окне есть обычные кнопки QPushButton, то если для кнопок заданы свойства autoDefault и / или значения по умолчанию, то вы получите кнопку по умолчанию - то, что вызывает клавиша ввода. В этом случае избавьтесь от autoDefault на кнопках, и нажатие клавиши ввода в другом виджете больше не закрывает диалоговое окно.

В случае QDialogButtonBox вы, вероятно, можете перебирать кнопки, чтобы отключить эту вещь в ctor вашего диалога. Не проверено здесь, но должно работать. Если нет, то вам также нужно посмотреть, есть ли кнопка по умолчанию, которая также устанавливается на самом QDialog.

Один из вариантов - переопределить событие show вашего диалога, чтобы разрешить отображение QDialogButtonBox, после чего он установит кнопку по умолчанию с AcceptRole, а затем установить для всех кнопок значения, отличные от значений по умолчанию.

      void MyDialog::showEvent(QShowEvent* event)
{
    // When a QDialogButtonBox is shown, it will set a default button if none are found so we need to disable the
    // default buttons after the button box has been shown.
    QDialog::showEvent(event);

    // For example, with a dialog which has two buttons, Save and Cancel, we remove all defaults
    // It might be good enough to remove the default on just the buttons with have the AcceptRole, but
    // I didn't test extensively enough to see what would happen if any buttons had "autoDefault" set or
    // verify this behavior on all platforms.
    ui->buttonBox->button(QDialogButtonBox::Save)->setDefault(false);
    ui->buttonBox->button(QDialogButtonBox::Cancel)->setDefault(false);
}

Попытка удалить значения по умолчанию до отображения QDialogButtonBox, например, в конструкторе вашего диалога, будет просто отменена кодом в QDialogButtonBox ::showEvent().

Проблема в том, что фильтр событий не должен быть установлен на кнопку ОК.

Если ваша кнопка OK отключена, то она не будет получать входное событие. Какой бы виджет не был в фокусе, будет. И если они не принимают событие ввода, то QDialog собирается accept() сам.

Два способа решения проблемы:

1) Переопределить QDialog::accept()и позвоните QDialogПринимайте метод в новом accept функционировать только если ОК включен

void MyDialog::accept() {
    if (okEnabled) {
        QDialog::accept();
    }
}

2) Установите фильтр событий на каждый виджет в диалоге, который не принимает клавишу ввода (правка строк,...).

Фильтр событий будет выглядеть так:

class KeyPressEater : public QObject
{
    Q_OBJECT

protected:
    bool eventFilter(QObject *obj, QEvent *event);
};

bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        bool res = QObject::eventFilter(obj, event);

        if (keyEvent->key() == Qt::Key_Return) {
            return true; /* Always accept return */
        } else {
            return res;
        }
    } else {
        // standard event processing
        return QObject::eventFilter(obj, event);
    }
}

И в вашем коде для каждого виджета в диалоге:

myWidget->installEventFilter(myKeyPressEater);

QDialog имеет закрытый слот, который называется accept(), Всякий раз, когда QDialogButtonBox испускает accepted() (нажав клавишу возврата или нажав Ok), этот частный слот вызывается. Поэтому попробуйте отключить их.

disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));

Это сработало для меня.

Чтобы избежать закрытия кнопки "ОК" или "Ввод" из диалогового окна: в XML-файле пользовательского интерфейса удалите соединение / слот для принятия / отклонения. Затем в вашем коде emmit accept(), когда и когда это необходимо;

Пример из файла пользовательского интерфейса, который подключает слот accept ():

 <connections>  
  <connection>  

   <sender>products_ButtonBox</sender>

    <signal>accepted()</signal>  

     <receiver>Products_Dialog</receiver>
      <slot>accept()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>248</x>
         <y>254</y>
        </hint>
        <hint type="destinationlabel">
         <x>157</x>
         <y>274</y>
        </hint>e
       </hints>
      </connection>

В вашем диалоге accept() Метод, проверьте кнопку Ok для фокуса:

void accept() override
{ if (!dialogButtonBox->button(QDialogButtonBox::Ok)->hasFocus())
  return;

...

  QDialog::accept();
}

Ключ состоит в том, чтобы установить свои собственные кнопки, все с NoRole, и не принимать и не отклонять сигналы от панели кнопок. Это позволит вам установить собственное поведение для кнопки по умолчанию.

class Dialog(QDialog):
    def __init__():
        super(Dialog, self).__init__()
        self.buttonBox = QDialogButtonBox()

        self.btn_save = self.buttonBox.addButton('Save', QDialogButtonBox.NoRole)
        self.btn_cancel = self.buttonBox.addButton('Cancel', QDialogButtonBox.NoRole)
        self.btn_save.clicked.connect(self.onAccept)
        self.btn_save.setMouseTracking(True)
        self.btn_cancel.clicked.connect(self.onReject)

        #  STATUS BAR
        self.status = QStatusBar()
        self.status.addPermanentWidget(self.buttonBox)

    def onAccept(self):
        if not self.btn_save.underMouse():
            return
        self.submitChanges(self.model)
        self.accept()

    def onReject(self):
        self.reject()

В PySide (и я представляю PyQt) мне удалось переопределить функции принятия и отклонения QDialog.

def custom_accept ():
    # perform custom actions when you hit open
    pass

def custom_reject ():
    # perform custom actions when you hit cancel
    pass

file_dialog = QtGui.QFileDialog(directory=".")
file_dialog.accept = custom_accept
file_dialog.reject = custom_reject

Это препятствовало закрытию диалогового окна файла и давало мне доступ к данным, когда были запущены функции "ОК" (принять) или "Отмена" (отклонить) (либо нажатием клавиши "Ввод", либо нажатием кнопок).

Другие вопросы по тегам