0
votes

I have two separate threads. First thread for GUI, and second for application data.

Initially, I wanted to use QUndoStack and QUndoView.

But there was a problem - this view works directly with the stack:

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

In this case I got race condition.

To solve this problem I wrote custom myUndoView using QListView and QAbstractListModel. Now all my slots using queued connections and I store a lightweight copy of the "real" undo stack in the custom view model. This is same size and same order of the "real" undo stack elements. A lightweight element contains only type of the undo command and text.

Now I have another problem. I'm not blame for this ))

I have a QLineEdit that emits signal on value changed when I click Enter key or lost focus. This value in turn is sent to object (app model) with "real" undo stack. It works.

But this does not work when I interact with undo view too. Repeat, I'm not blame for this. QUndoView has the same behavior.

Step by step:

  1. QLineEdit in focus.
  2. Changing value, still in focus.
  3. Click the mouse in the undo view.

Oops.. currentIndexChanged() signal from undo view can be sent first, or signal from QLineEdit can be sent first.

It always differs ..

If signal from QLineEdit was sent first - it works correctly. The history of changes not lost.

I want to make enter/blur and other changes (not in history view) always invoked first. Probably I can use QTimer::singleShot() for delay of emit undo view signals . But not curentIndexChanged() because this signal emit with user interactions and when undo stack updated programmatically. We can not determine who make changes - user or application.

What I tried?

Intercept mouse clicks:

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

But sometimes it loses the clicks. At the bottom of the list item (under the letters) is an area that pass a click to the item. This may be a Qt bug, found in my environment: Debian, Mate, GTK+ Qt-style.

I think, I can place another transparent widget over list, and get coordinates of the click and use it:

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

to get the selected index.

Or I make all wrong? Maybe there is an easier way?

How to make it right?

1
Did you try queued signals as explained in this solution stackoverflow.com/questions/15051553/…Mohammad Kanan
@MohammadKanan all my slots (in app thread) have queued connections. And all signals from GUI sent to app thread event loop. But when line edit is focused click to history item happens very fast and can be sent before line edit signal. And I can not change connections to direct type - it missing thread benefits, gui freezed sometimes.Deep

1 Answers

2
votes

I would try blocking the list model signals while the line edit is focused.

Let's have an event filter like this:

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

which keeps a private reference to the list model as a pointer to QObject, passed in constructor argument.

The filter implementation:

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

Keep a reference to an instance of the filter in the window class (Form, in my example), along with the list model instance reference:

private:
   EventFilter * filter;
   QAbstractListModel * model;

The filter has to be instantiated and installed in line edit, in Form constructor (don't forget to delete it in the destructor):

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

At this point, model events will be blocked when the line edit gets focus. To unlock them, use the line edit editingFinished slot:

void Form::on_lineEdit_editingFinished()
{
    model->blockSignals(false);
}