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