1

私のプロジェクトでは、関数QStyledItemDelegateからカスタム エディターをサブクラス化して返しました。createEditor

QWidget* TagEditDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    TagEditWidget* tagEditWidget = new TagEditWidget(parent, index.data(Qt::UserRole+4).toInt(), index.data(Qt::UserRole+2).toByteArray(), index.data(Qt::UserRole+3).toByteArray(), index.parent().data(Qt::UserRole+4).toInt() == 9, parent->width());
    return tagEditWidget; //tagEditWidget is my custom QWidget
}

編集が終わったら、新しいデータをモデルに書き戻したいです。だから私はオーバーライドしsetModelDataました。

void TagEditDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
    TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
    if (!tagEditWidget)
    {
        QStyledItemDelegate::setModelData(editor, model, index);
        return;
    }

    //Edit model here?
}

これは機能しますが、問題はsetModelData、エディターがどのように閉じられても呼び出されることです。EndEditHint、を使用してエディターを閉じた場合にのみ、新しいデータを書き込みたいですQAbstractItemDelegate::SubmitModelCache。そこで、closeEditor信号を という名前のスロットに接続しましたeditFinished

connect(this, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(editFinished(QWidget*,QAbstractItemDelegate::EndEditHint)));

これで、エディターを介してエディターがどのように閉じられたEndEditHintか、およびデータをモデルに書き戻す必要があるかどうかを確認できるようになりました。Buuuuut、setModelDataget はcloseEditorシグナルの前に呼び出されます。closeEditorシグナルが最後に呼び出されたときに、どのようにデータをモデルに書き戻すのでしょうか? ここで何か不足していますか?

4

2 に答える 2

1

基本的な答え:

あなたのコンセプトはほぼ最後まで良さそうです。その方法に注目したいTagEditDelegate::setModelData

モデル内のデータを実際に更新したくない場合は、変更されていないことを確認してください。つまり、モデルの更新をスキップするoldData == newDataだけです。return;

その他の注意事項:

エディターの作成を見ると、ユーザーに提示される単一の値が保持されていないという印象を受けます。引数を渡しやすくし、エディター データを簡単に比較できるようにするには、別の引数を作成することを検討しclass/structてください。したがって、次のように呼び出すことができます。

new TagEditWidget(parent, editorData, parent->width())

ここで、EditorData は、別の関数によって取得される可能性のあるクラス/構造体になります。

EditorData editorData = readEditorData(index);

メソッドで関数を再利用してsetModelData、条件をチェックできます。

if (tagEditWidget->getEditorData() == readEditorData(index)) return;

また、 のようなマジック ナンバーの使用も避けてくださいQt::UserRole+2。独自の列挙型を作成して、必要なロールを指定します。例えば:

enum class MyRole
{
    Data1 = Qt::UserRole,
    Data2,
    Data3,
};

コメントの議論に従って編集

ユーザーが実際にどちらかの方法でエディションをキャンセルしなかったかどうかを知りたい場合はeventFilter、エディターまたはデリゲート内でオーバーライドできます。コンストラクターでエディター呼び出しinstallEventFilterを作成するとき。実装eventFilterは次のようになります。

bool eventFilter(QObject *object, QEvent *event) override
{
    if (event->type() == QEvent::KeyPress)
    {
        const auto key = static_cast<QKeyEvent *>(event)->key();
        if (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Tab)
            submitted = true;
    }
    else if (event->type() == QEvent::FocusAboutToChange &&
             static_cast<QFocusEvent*>(event)->reason() == Qt::MouseFocusReason)
    {
        submitted = true;
    }
    // extetend the conditions (add else if) to include
    // events which you might want to treat as submitted

    return QLineEdit::eventFilter(object, event);
}

コンストラクターで初期化されるエディター メンバーsubmittedはどこにありますか。次に、getter メソッドを作成すると、メソッド内のステータスを確認する準備が整います。boolfalseisSubmitted()setModelData

if (tagEditWidget->isSubmitted())
{
    // process data or update model here
}
于 2016-12-30T10:01:04.090 に答える
0

私は自分の質問に自分のやり方で答えただけです。Dustehの解決策は「より正しい」と思います。

呼び出されたデリゲート クラスで bool を作成し、shouldCommitそれを false に設定しました。

次にsetModelData、データをモデルに書き込む必要があるかどうかを確認します。

void TagEditDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
    TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
    if (!tagEditWidget)
    {
        QStyledItemDelegate::setModelData(editor, model, index);
        return;
    }

    if(shouldCommit)
    {
        //write data here
    }
}

そして、closeEditorシグナルが発せられたらヒントを確認し、一時的shouldCommitに true に設定してcommitData再度呼び出します。これでshouldCommit、データが書き込まれます。

void TagEditDelegate::editFinished(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
{
    if(hint == QAbstractItemDelegate::SubmitModelCache)
    {
        TagEditWidget * tagEditWidget = qobject_cast<TagEditWidget*>(editor);
        if(tagEditWidget)
        {
            shouldCommit = true;
            commitData(editor);
            shouldCommit = false;
        }
    }
}

これは私のユースケースでは非常にうまく機能しますが、すべての人にとってはそうではないかもしれません。彼の方法は、 をオーバーライドして、eventFilterをいつ呼び出すかについて独自の実装を作成することcommitDataです。これはおそらく、より詳細な制御が必要なユーザーにとってより適切なソリューションです。

しかし、デフォルトのeventFilter実装を使用して単純に を見たい人のためにEndEditHint、これが私の解決策です。

于 2016-12-30T12:33:57.163 に答える