具有撤消历史记录的故障(QUndoStack,QUndoView和其他)

时间:2018-02-05 12:23:00

标签: c++ qt qt5 undo-redo

我有两个独立的主题。 第一个用于GUI的线程,第二个用于应用程序数据。

最初,我想使用QUndoStack和QUndoView。

但是有一个问题 - 这个视图直接与堆栈一起工作:

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

在这种情况下,我得到了比赛条件。

为了解决这个问题,我使用QListView和QAbstractListModel编写了自定义myUndoView。 现在我的所有插槽都使用排队连接,并且我在自定义视图模型中存储了“真实”撤消堆栈的轻量级副本。 这与“真实”撤消堆栈元素的大小和顺序相同。 轻量级元素仅包含撤消命令和文本的类型。

现在我有另一个问题。我不应该责怪这个))

当我点击Enter键或失去焦点时,我有一个QLineEdit,它会在值发生变化时发出信号。 该值又被发送到具有“真实”撤销堆栈的对象(app model)。它有效。

但是当我与撤销视图交互时,这不起作用。 重复一遍,我不应该为此负责。 QUndoView具有相同的行为。

一步一步:

  1. QLineEdit聚焦。
  2. 改变价值,仍然是焦点。
  3. 在撤消视图中单击鼠标。
  4. 可以先发送来自撤消视图的Oops .. currentIndexChanged()信号, 或者来自QLineEdit的信号可以先发送。

    它总是不同......

    如果首先发送来自QLineEdit的信号 - 它可以正常工作。 变化的历史不会丢失。

    我想首先调用enter / blur和其他更改(不在历史记录视图中)。可能我可以使用QTimer :: singleShot()来延迟发出撤消视图信号。但不是curentIndexChanged(),因为此信号通过用户交互发出,并且以编程方式更新撤消堆栈。我们无法确定谁进行了更改 - 用户或应用程序。

    我尝试了什么?

    拦截鼠标点击:

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

    但有时它会失去点击次数。 在列表项的底部(在字母下面)是一个将点击传递给项目的区域。 这可能是我的环境中发现的Qt错误:Debian,Mate,GTK + Qt风格。

    我想,我可以在列表上放置另一个透明小部件,并获取点击的坐标并使用它:

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

    获取所选索引。

    或者我搞错了? 也许有一种更简单的方法?

    如何正确使用?

1 个答案:

答案 0 :(得分:2)

我会尝试blocking the list model signals,同时重点关注行编辑。

让我们有一个这样的事件过滤器:

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);
}