Detecting user input using QTextEdit (and distinguishing it from application changes)

Gtk3 rich text widget machine (based on GtkTextBuffer and GtkTextView ) has "begin-user-action" and "end-user-actions" , which allows you to quickly process user input (and distinguish it) from the generated application changes to the buffer or view).

But it seems like there are no similar things in Qt5. For example, my incomplete understanding is that QTextEdit :: insertHtml or QTextDocument :: contentChanged or QTextDocument :: contentsChanged does not separate the changes associated with user input (keyboard or insert, etc.) ..) from those that are executed application.

I mean some syntax oriented editor.

Perhaps I misunderstand support for Qt5 text editor support.

(For curious people: I will redesign and reorient my MELT monitor from C and GTK to something with C ++ 11 and Qt5, conditionally called Basixmo ; all this is free GPL software, but I haven't encoded the Qt5 thing yet)

Example

I have a window with a button sayand QTextEditas its central widget. When I click the button, a line is inserted in the document "hello", and I call that the application changes (you can imagine that the button is replaced by something not related to the user, for example, some network input). When I enter some keystrokes in a text editor, some lines are also inserted from this custom action change. I want to distinguish between one and the other.

#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QToolBar>
#include <fstream>
#include <iostream>
int main(int argc, char**argv)
{
  QApplication app(argc, argv);
  auto win = new QMainWindow;
  auto tb = win->addToolBar("Basile example");
  auto ted = new QTextEdit;
  win->setCentralWidget(ted);
  tb->addAction("say",[=]{ted->insertPlainText("hello");});
  auto doc = ted->document();
  QObject::connect(doc,&QTextDocument::contentsChange,
                   [=](int pos, int removedchars, int addedchars)
                      { std::clog << "contentChange: pos=" << pos
                                  << " removedchars=" << removedchars
                                  << " addedchars=" << addedchars
                                  << std::endl; });
  win->show();
  app.exec();
  delete win;
}


//// Local Variables:
//// compile-command: "g++ -std=c++11 -Wall -g $(pkg-config --cflags Qt5Core Qt5Widgets Qt5Gui) -fPIC $(pkg-config --libs Qt5Core Qt5Widgets Qt5Gui) -o eqb eqb.cc"
//// End:

But when I run the above code, the signal contentsChange(connected to my lambda function outputting to std::clog) is triggered both for user action changes and for application changes.

QTextEdit QTextDocument QTextCursor, ( QTextEdit , ..) . (, QWidget::keyPressEvent ..), , , , QTextEdit.

, : , emacs Qt5 ++ 11 (, à la Guile).

PS. , Debian/testing/x86-64. Qt - 5.6.1, GCC 6.2; .

+4
2

, , QTextDocument, QTextEdit.
QSignalBlocker , :

#include <QSignalBlocker>

// ...

tb->addAction("say", [=]{
    const QSignalBlocker blocker{ted->document()};
    ted->insertPlainText("hello");
});

, contentsChange, .

. .

, QTextEdit .
, QTextDocument ( , ).
, , ( , internalContentChange).
.
, , , .


, , , , .
QTextEdit (,) insertPlainText QTextDocument. , , ( , ) QTextEdit.
, . , QTextEdit , ( QTextDocument).

, , , ( ).
, , QTextEdit insertPlainText QTextEditControl, a QWidgetTextControl ... ..
, .

+3

@skypjack answer , :

bool application_change = false;
tb->addAction("say",
    [=, &application_change]{
        application_change = true;
        ted->insertPlainText("hello");
        application_change = false;
    }
);

// ...

QObject::connect(doc, &QTextDocument::contentsChange,
    [=, &application_change](int pos, int removedchars, int addedchars){
        std::clog << "contentChange: pos=" << pos
            << " removedchars=" << removedchars
            << " addedchars=" << addedchars
            << std::endl;
        if(application_change)
            std::clog << "this is an application change" << std::endl;
    }
);

application_change :

struct BoolLocker
{
    bool& locked;

    BoolLocker(bool& to_lock_):
        locked(to_lock_)
    {
        locked = true;
    }

    ~BoolLocker(bool& to_lock_)
    {
        locked = false;
    }
};

// ...

bool application_change = false;
tb->addAction("say",
    [=, &application_change]{
        BoolLocker locker(application_change);
        ted->insertPlainText("hello");
    }
);

factory:

bool application_change = false;

template<typename F>
auto make_application_change(F&& f)
{
    return [&f]{
        application_change = true;
        f();
        application_change = false;
    };
}

// ...

tb->addAction("say",
    make_application_change(
        [=]{
            ted->insertPlainText("hello");
        }
    )
);

, , . ( ), .

0

Source: https://habr.com/ru/post/1659344/


All Articles