QStyledItemDelegate: определить причину для closeEditor() или setModelData()

Я использую QTableWidget и мне нужна специальная обработка редактирования, поэтому я установил для него QStyledItemDelegate.

Когда пользователь заканчивает редактирование, излучается сигнал closeEditor(), к которому я подключаюсь для обработки введенных данных. Этот сигнал испускается как при нажатии Enter/Return, так и когда пользователь щелкает в другом месте (за пределами редактируемого QTableWidgetItem).

Мой вопрос: можно ли отличить, если пользователь нажимает «Ввод/Ввод» или щелкает где-то еще? Я хотел бы обработать «щелчок снаружи» так же, как нажатие ESC (данные не изменяются, и исходное значение QTableWidgetItem восстанавливается). К настоящему времени оба случая меняют данные.

QAbstractItemDelegate::EndEditHint (который испускается с closeEditor()) не дает мне эту информацию.

Спасибо за помощь!

Редактировать:

Чтобы обработать данные до того, как они будут записаны обратно в модель, можно реализовать setModelData(), но все равно, кажется, нет способа, если бы эта функция была вызвана нажатием Enter/Return или кликом где-то еще…


person Tobias Leupold    schedule 10.03.2018    source источник


Ответы (1)


Недавно я сделал образец, чтобы лучше ознакомиться с QStyledItemDelegate, касающимся встроенного редактирования. клеток:

В этом примере я обновил базовую модель данных, перегружая QStyledItemDelegate::setModelData(). .

Прочитав этот вопрос, я только что проверил, что произойдет, если ввод будет завершен (или, строго говоря, прерван) с помощью ESC. Вот что я наблюдал:

  1. Базовые данные остаются неизменными.

  2. Копнув глубже (т.е. установив точку останова в моем перегруженном setModelData()), я заметил, что setModelData() в этом случае даже не вызывается.

Может быть, проблему с ОП можно легко решить, немного изменив дизайн его делегата.

Наконец, мой производный класс (объявление), который я использовал для получения «полнофункционального» делегата для редактирования ячеек таблицы в строке:

class ValueNameDelegate: public QStyledItemDelegate {

  // methods:
  public:
    /// @name Construction & Destruction
    //@{

    /// constructor.
    ValueNameDelegate();

    /// destructor.
    virtual ~ValueNameDelegate() = default;

    // disabled:
    ValueNameDelegate(const ValueNameDelegate&) = delete;
    ValueNameDelegate& operator=(const ValueNameDelegate&) = delete;

    //@}
  protected:
    /// @name Overloaded Event Handlers
    //@{

    // inserts editor in table (by setParent(pQParent)) and
    // returns the editor widget to edit cell.
    virtual QWidget* createEditor(
      QWidget *pQParent, const QStyleOptionViewItem &qOption,
      const QModelIndex &qMIndex) const override;

    // removes editor from table (by setParent(nullptr)).
    virtual void destroyEditor(
      QWidget *pQEditor, const QModelIndex &qMIndex) const override;

    // reads data from table model and updates editor.
    virtual void setEditorData(
      QWidget *pQEditor, const QModelIndex &qMIndex) const override;

    // reads data from editor and updates table model.
    virtual void setModelData(
      QWidget *pQEditor, QAbstractItemModel *pQModel,
      const QModelIndex &qMIndex) const override;

    //@}
};

Примечание:

В моем случае заранее создан только один виджет редактора. Он повторно используется для каждого редактирования ячейки (вместо создания для каждого редактирования нового).

Это немного отличается от примера делегата Qt Spin Box, но работает как положено.

Я не могу себе представить, как это не должно иметь никакого значения в отношении проблемы OP.


Согласно обратной связи, OP хочет обработать событие потери фокуса, например прерывание ввода. Это противоречит тому, как это обрабатывается по умолчанию в QLineEdit.

Итак, мое решение состоит в том, чтобы предоставить перегруженную версию QLineEdit. Вместо изменения поведения QLineEdit он просто отслеживает в члене bool _confirmed, Ввод был нажат. Самым сложным было определить другое подходящее событие или сигнал для сброса члена. Наконец, я решил, что focusOutEvent() просто вызывается в нужное время и, следовательно, подходит для этой задачи.

testQLineEdit-Finished.cc:

#include <QtWidgets>

class LineEdit: public QLineEdit {
  private:
    // flag: true ... last finished editing was confirmed
    bool _confirmed;
  public:
    // Construction & Destruction
    explicit LineEdit(
      const QString &contents = QString(), QWidget *pQParent = nullptr):
      QLineEdit(contents, pQParent),
      _confirmed(false)
    {
      QObject::connect(this, &QLineEdit::returnPressed,
        [this](){ onSigReturnPressed(); });
    }
    LineEdit(QWidget *pQParent): LineEdit(QString(), pQParent) { }
    virtual ~LineEdit() = default;
    LineEdit(const LineEdit&) = delete;
    LineEdit& operator=(const LineEdit&) = delete;

  public:
    // returns whether last finished editing was confirmed.
    bool isConfirmed() { return _confirmed; }
  protected:
    virtual void focusOutEvent(QFocusEvent *pQEvent) override
    {
      _confirmed = false;
      QLineEdit::focusOutEvent(pQEvent);
    }
  private:
    void onSigReturnPressed() { _confirmed = true; }
};

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup GUI
  LineEdit qEdit(QString::fromUtf8("Hello World"));
  qEdit.show();
  // install signal handlers
  QObject::connect(&qEdit, &LineEdit::editingFinished,
    [&]() {
      qDebug() << "Edit confirmed:" << qEdit.isConfirmed();
    });
  // runtime loop
  return app.exec();
}

Снимок testQLineEdit-Finished

Вывод Edit confirmed: true был достигнут нажатием Enter, false строк - щелчком мыши.

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

person Scheff's Cat    schedule 10.03.2018
comment
Спасибо за определение класса! Но как он будет различать нажатие клавиши Enter → вызов setModelData() и нажатие кнопки где-то еще → вызов setModelData()? - person Tobias Leupold; 10.03.2018
comment
В ПОРЯДКЕ. Я должен признать: щелчок где-то еще — это то, что мы всегда хотим подтвердить вводом. (Мы проделали много дополнительной работы, чтобы добиться этого в gtkmm 2, и были очень довольны тем, что в Qt это бесплатно.) В этом случае вам, вероятно, придется изменить поведение QLineEdit. - person Scheff's Cat; 10.03.2018
comment
Я расширил свой ответ и сделал довольно простой подход, который не меняет поведение QLineEdit, но отслеживает, завершено ли редактирование нажатием [Enter]. - person Scheff's Cat; 10.03.2018