Проблемы с историей отмены (QUndoStack, QUndoView и другие)
У меня есть две отдельные темы. Первый поток для GUI, а второй для данных приложения.
Изначально я хотел использовать QUndoStack и QUndoView.
Но возникла проблема - это представление работает напрямую со стеком:
https://code.woboq.org/qt5/qtbase/src/widgets/util/qundoview.cpp.html
В этом случае я получил состояние гонки.
Чтобы решить эту проблему, я написал собственный myUndoView, используя QListView и QAbstractListModel. Теперь все мои слоты используют соединения с очередями, и я храню облегченную копию "реального" стека отмены в модели пользовательского представления. Это тот же самый размер и тот же порядок "настоящих" элементов стека отмены. Облегченный элемент содержит только тип команды отмены и текст.
Теперь у меня есть другая проблема. Я не виноват в этом))
У меня есть QLineEdit, который излучает сигнал при изменении значения, когда я нажимаю клавишу ввода или теряю фокус. Это значение в свою очередь отправляется объекту (модели приложения) с "реальным" стеком отмены. Оно работает.
Но это не работает, когда я взаимодействую с видом отмены тоже. Повторяю, я не виноват в этом. QUndoView имеет такое же поведение.
Шаг за шагом:
- QLineEdit в фокусе.
- Изменение значения, все еще в фокусе.
- Щелкните мышью в представлении отмены.
Упс.. 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);
}