LogicStuff
LogicStuff

Reputation: 19607

Qt - resolve two sequential calls on a slot and perform action only once

I have a CodeEditor class which inherits from QPlainTextEdit. This class has to emit requests when textCursor()'s position or selection changes. The thing is, I don't want 2 calls to assembleIntellisenseRequest and pickListSortRequest signals when both cursorPositionChanged and selectionChanged is emitted. I solved this with adding bool update_ member to CodeEditor, which is set to true in constructor. 500ms delay is just for clarity.

void CodeEditor::makeConnections()
{
    auto updateRequest = [this]()
    {
        if(update_ && !textCursor().hasSelection())
        {
            update_ = false;
            QTimer::singleShot(500, [this]() { update_ = true; });

            emit assembleIntellisenseRequest(textCursor().blockNumber());
            emit pickListSortRequest(textCursor().positionInBlock());
        }
    };

    connect(this, &CodeEditor::cursorPositionChanged, updateRequest);
    connect(this, &CodeEditor::selectionChanged, updateRequest);
}

Is there any better way to accomplish this? Also, why was in this case, when lambda captures by reference this1: printout not equal to this:? It was silent, I just knew, that update_ is still false.

auto updateRequest = [this]()
{
    cout << "this: " << this << endl;

    if(update_ && !textCursor().hasSelection())
    {
        update_ = false;
        QTimer::singleShot(500, [&]()
        {
            cout << "this1: " << this << endl;
            update_ = true;
        });

        emit assembleIntellisenseRequest(textCursor().blockNumber());
        emit pickListSortRequest(textCursor().positionInBlock());
    }
};

Thank you in advance.

Upvotes: 1

Views: 416

Answers (1)

Dmitry Sazonov
Dmitry Sazonov

Reputation: 8994

You can apply next pattern to your code:

class MyClass : public QObject
{
private slots:
  void updateRequest();  

private:
  QTimer *_timer;
  CodeEditor *_editor;
};


MyClass::MyClass()
{
  // Init members
  // ...
  _timer->setSingleShot( true );
  _timer->setInterval( 0 );

  connect( _editor, &CodeEditor:: cursorPositionChanged, _timer, &QTimer::start);
  connect( _editor, &CodeEditor:: selectionChanged, _timer, &QTimer::start);
  connect( _timer, &QTimer::timeout, this, &MyClass::updateRequest );
}

In this solution, timer is a "proxy for signals". Each time signal is emited timer will start immedeately (when flow will return to an event loop). Each emitting of signal will call QTimer::start slot. But all calls of start will place only one call of timeout signal to event queue. So, when control flow will return to event loop, your updateRequest slot will be called only once, even if a lot of signals were emited.

QTimer is a "Qt way" to replace your update_ variable, without any timeouts.

Upvotes: 2

Related Questions