Проблемы с историей отмены (QUndoStack, QUndoView и другие)

У меня есть две отдельные темы. Первый поток для GUI, а второй для данных приложения.

Изначально я хотел использовать QUndoStack и QUndoView.

Но возникла проблема - это представление работает напрямую со стеком:

https://code.woboq.org/qt5/qtbase/src/widgets/util/qundoview.cpp.html

В этом случае я получил состояние гонки.

Чтобы решить эту проблему, я написал собственный myUndoView, используя QListView и QAbstractListModel. Теперь все мои слоты используют соединения с очередями, и я храню облегченную копию "реального" стека отмены в модели пользовательского представления. Это тот же самый размер и тот же порядок "настоящих" элементов стека отмены. Облегченный элемент содержит только тип команды отмены и текст.

Теперь у меня есть другая проблема. Я не виноват в этом))

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

Но это не работает, когда я взаимодействую с видом отмены тоже. Повторяю, я не виноват в этом. QUndoView имеет такое же поведение.

Шаг за шагом:

  1. QLineEdit в фокусе.
  2. Изменение значения, все еще в фокусе.
  3. Щелкните мышью в представлении отмены.

Упс.. currentIndexChanged() сигнал от просмотра отмены может быть послан сначала, или сигнал от QLineEdit может быть послан сначала.

Это всегда отличается..

Если сигнал от QLineEdit был послан первым - он работает правильно. История изменений не пропала.

Я хочу сделать ввод / размытие и другие изменения (не в виде истории) всегда вызывается первым. Вероятно, я могу использовать QTimer::singleShot() для задержки сигналов отмена отката. Но не curentIndexChanged(), потому что этот сигнал испускается при взаимодействиях с пользователем и при отмене стека обновляется программно. Мы не можем определить, кто вносит изменения - пользователь или приложение.

Что я пробовал?

Перехватывать щелчки мыши:

myUndoView::mousePressEvent(QMouseEvent *event)
{
    event->ignore();
    qDebug() << "catched!";
}

Но иногда он теряет щелчки. В нижней части элемента списка (под буквами) находится область, в которой щелчок передается на элемент. Это может быть ошибка Qt, найденная в моей среде: Debian, Mate, GTK+ Qt-style.

Я думаю, я могу разместить другой прозрачный виджет над списком, получить координаты клика и использовать его:

http://doc.qt.io/qt-5/qabstractitemview.html

чтобы получить выбранный индекс.

Или я все делаю неправильно? Может быть, есть более простой способ?

Как сделать это правильно?

1 ответ

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

Давайте создадим фильтр событий, подобный этому:

class EventFilter : public QObject
{
    Q_OBJECT
public:
    EventFilter(QObject * model) : _model(model){}
    bool eventFilter(QObject *watched, QEvent *event);
private:
    QObject * _model;
};

который сохраняет частную ссылку на модель списка в качестве указателя на QObject, переданный в аргументе конструктора.

Реализация фильтра:

bool EventFilter::eventFilter(QObject *watched, QEvent *event)
{
    if(event->type() == QEvent::FocusIn)
    {
        _model->blockSignals(true);
    }
    return false;
}

Сохранить ссылку на экземпляр фильтра в классе окна (Formв моем примере) вместе со ссылкой на экземпляр модели списка:

private:
   EventFilter * filter;
   QAbstractListModel * model;

Фильтр должен быть создан и установлен в редакторе строк, в Form конструктор (не забудьте удалить его в деструкторе):

filter = new EventFilter(model); //the model is passed to the filter in construction
ui->lineEdit->installEventFilter(filter);

На этом этапе события модели будут заблокированы, когда редактирование линии получит фокус. Чтобы разблокировать их, используйте строку редактирования editingFinished слот:

void Form::on_lineEdit_editingFinished()
{
    model->blockSignals(false);
}
Другие вопросы по тегам