QComboBox редактировать LineEdit, когда PopUp активен

У меня есть QComboBox, заполненный некоторыми данными. Я хочу отредактировать lineEdit comboBox, и когда я делаю это, чтобы comboBox отображал его всплывающее окно во время редактирования. Проблема в том, что я потерял фокус на редактировании строки, и я могу написать только одно письмо за раз.

Это то, что я делаю на тривиальном уровне:

ui->comboBox->addItem("This");
ui->comboBox->addItem("is");
ui->comboBox->addItem("a");
ui->comboBox->addItem("comboBox");

ui->comboBox->setEditable(true);
connect(ui->comboBox, SIGNAL(currentTextChanged(QString)), this, SLOT(PrintTextLineEdit(QString)));

void MainWindow::PrintTextLineEdit(QString str)
{
  ui->comboBox->showPopup();
  ui->comboBox->lineEdit()->setFocus();
}

Также, если я использую blockSignal на lineEdit, пока я показываю, всплывающее окно бесполезно. Какие-либо предложения?

РЕДАКТИРОВАТЬ

Кажется, мне нужно предоставить некоторые дополнительные детали. Я должен быть в состоянии написать целое слово за раз, не теряя фокус, когда я использую ui->comboBox->showPopUp() в currentTextChanged сигнал.

Или, проще говоря: курсор не должен исчезать из QLineEdit после того, как сигнал испущен и всплывающее окно отображается.

5 ответов

Решение

Вы должны получить свой собственный класс поля со списком из QComboBox и переопределить showPopup() виртуальный метод, чтобы вернуть фокус обратно на строку редактирования.

void CMyComboBox::showPopup()
{
  QComboBox::showPopup();

  // Put the focus back later, after all pending events are processed.
  QTimer::singleShot(0, [this](){ lineEdit()->setFocus(); });
}

В особом случае время QTimer с таймаутом 0 истечет, как только будут обработаны все события в очереди событий оконной системы.

РЕДАКТИРОВАТЬ:

Это работает (хотя можно считать это взломом):

class CMyComboBox : public QComboBox
{
  public:
    CMyComboBox(QWidget* parent) 
      : QComboBox(parent) 
    {
      view()->installEventFilter(this);
    }

    // Event filter forwards view key events to the line edit.
    bool eventFilter(QObject *watched, QEvent *event)
    {
      if (event->type() == QEvent::KeyPress)
      {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        QKeyEvent* newEvent = new QKeyEvent(keyEvent->type(), keyEvent->key(), keyEvent->modifiers(), 
                                            keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());

        QFocusEvent* focusEvent = new QFocusEvent(QEvent::FocusIn, Qt::OtherFocusReason);
        QCoreApplication::postEvent(lineEdit(), focusEvent);
        QCoreApplication::postEvent(lineEdit(), newEvent);
      }

      return false;
    }
};

Но лично я бы, наверное, использовал отдельный QMenu для отображения списка слов, а не всплывающее меню со списком.

Каждый комбинированный список имеет значение по умолчанию QCompleter это может показать варианты завершения во всплывающем окне. Я думаю, что вы можете достичь того, что вы хотите, установив этот режим завершения PopupCompletion,

ui->comboBox->completer()->setCompletionMode(QCompleter::PopupCompletion);

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

Ваша проблема вызвана grabKeyboard. Клавиатура захватывается всплывающими окнами, поэтому решение простое, просто установите редактирование для повторного захвата клавиатуры, используя grabKeyBoard()

Когда я смотрю в коде QCombobox, я вижу следующее внутри кода showPopup:

container->show();
container->updateScrollers();
view()->setFocus(); // <<-- focus command here

view()->scrollTo(view()->currentIndex(),
                 style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)
                         ? QAbstractItemView::PositionAtCenter
                         : QAbstractItemView::EnsureVisible);

Так что, возможно, если вы выполните следующее (здесь не тестировалось), вы можете получить желаемый результат:

ui->comboBox->addItem("This");
ui->comboBox->addItem("is");
ui->comboBox->addItem("a");
ui->comboBox->addItem("comboBox");

ui->comboBox->setEditable(true);
ui->comboBox->view()->setFocusPolicy(Qt::FocusPolicy::NoFocus); // don't allow focusing of the view of the popup
connect(ui->comboBox, SIGNAL(currentTextChanged(QString)), this, SLOT(PrintTextLineEdit(QString)));

void MainWindow::PrintTextLineEdit(QString str)
{
  ui->comboBox->showPopup();
  ui->comboBox->lineEdit()->setFocus();
}

Стоит попробовать.

С использованиемQCompleterособенностьQLineEditможет быть лучшим/менее хакерским решением:

      QStringListModel *model; // or another QAbstractItemModel
model->setStringList({"This", "is", "a", "combobox"});

QLineEdit *lineEdit;
lineEdit->setCompleter(new QCompleter(model, lineEdit));
lineEdit->completer()->setFilterMode(Qt::MatchContains); // set filter mode as desired

Всплывающее окно будет показано, как только пользователь начнет редактировать строку редактирования. Единственным недостатком этого подхода является отсутствие раскрывающегося значка для отображения всех возможных (нефильтрованных) элементов.

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