【问题标题】:Troubles with undo history (QUndoStack, QUndoView and other)撤消历史问题(QUndoStack、QUndoView 等)
【发布时间】:2018-02-05 12:23:00
【问题描述】:

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

最初,我想使用 QUndoStack 和 QUndoView。

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

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

在这种情况下,我得到了竞争条件。

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

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

我有一个 QLineEdit,当我单击 Enter 键或失去焦点时,它会在值更改时发出信号。 该值又被发送到具有“真实”撤消堆栈的对象(应用程序模型)。它有效。

但是当我也与撤消视图交互时,这不起作用。 重复一遍,这不怪我。 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#indexAt

获取选定的索引。

还是我全都错了? 也许有更简单的方法?

如何做到正确?

【问题讨论】:

  • 您是否尝试过此解决方案中所述的排队信号stackoverflow.com/questions/15051553/…
  • @MohammadKanan 我所有的插槽(在应用程序线程中)都有排队连接。来自 GUI 的所有信号都发送到应用程序线程事件循环。但是当行编辑被聚焦时,点击历史项目发生得非常快,并且可以在行编辑信号之前发送。而且我无法将连接更改为直接类型 - 它缺少线程优势,gui 有时会冻结。

标签: c++ qt qt5 undo-redo


【解决方案1】:

我会在关注行编辑时尝试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);
}

【讨论】:

  • 嗯.. 有趣。但我有许多其他可编辑对象,而不是一行编辑。我在场景中有很多对象可以创建、删除、移动它们。你确定什么实现这个过滤器是个好主意?
  • 我将您的答案标记为有用,因为它很有用。但在我的情况下......所有可编辑的对象都将小型交互式模型(例如您的表单)分开。是的,它的对象继承自基类,但这是不同的地方和不同的范围。这将是对所有内容的硬传递参考/历史视图的指针,以及更多的复制粘贴。
  • 我理解您的担忧。您至少可以为其中一个尝试此解决方案吗?只是看看它是否会起作用。
  • 是的!我今天试试!
  • 好的。我想,将是正确的方法:制作关于开始和结束编辑过程的全局信号(直接连接),因为编辑过程不仅仅是焦点。它是拖放,按热键。看起来像您的过滤器,但不需要为所有人传递一些指针。并且可以用于其他有问题的视图,例如我的撤消历史记录。你怎么看?
猜你喜欢
  • 2012-05-20
  • 2011-08-20
  • 2020-03-24
  • 2014-04-14
  • 2021-06-25
  • 2015-01-15
  • 2020-09-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多