Inserting text into QTextEdit freezes UI
-
I'm working on my project. I want to make something like tree-sitter playground.
I'm parsing code from one QTextEdit and tokens insert parsing output into another QTextEdit. When typing an the first QTextEdit UI is freezing because of text insertion (imo, mb it's something different clear operation maybe the reason also). How to fix it and make UI responsive due typing or text insertion action?
video demo (YouTube)
-
Also there is some implementation details:
class Playground : public QTextEdit { Q_OBJECT public: explicit Playground(QWidget *parent = nullptr) : QTextEdit(parent) { setTextInteractionFlags(Qt::NoTextInteraction); m_worker = new Worker(); connect(this, &Playground::updateNodes, m_worker, &Worker::process); connect(m_worker, &Worker::started, this, &QTextEdit::clear); connect(m_worker, &Worker::success, this, &Playground::handle); } void setSource(QTextEdit *textEdit); signals: void updateNodes(QString text); private slots: void onTextChanged() { emit updateNodes(m_source->toPlainText()); } void handle(QString text); private: Worker *m_worker; QTextEdit *m_source = nullptr; }; class Worker : public QObject { Q_OBJECT public: explicit Worker(QObject *parent = nullptr) { m_thread.reset(new QThread); moveToThread(m_thread.get()); m_thread->start(); } ~Worker(); ... public slots: void process(QString text); private slots: void cleanup(); signals: void started(); void success(QString result); void finished(); private: ... std::unique_ptr<QThread> m_thread; };
Playground is the widget on the right side of the app. Worker is located within Playground and has its own QThread. They are communicate through signals and slots.
-
@SGaist Yeah, you right that connect
textChanged
signal toonTextChanged
slot is a bit of oopsy doopsy. So I add debounce for key presses.Add timer and reimplement
onTextChanged
slot.class Playground : public QTextEdit { Q_OBJECT public: explicit Playground(QWidget *parent = nullptr) : QTextEdit(parent) { setTextInteractionFlags(Qt::NoTextInteraction); m_timer = new QTimer(this); m_timer->setSingleShot(true); m_worker = new Worker(); connect(m_timer, &QTimer::timeout, this, &Playground::process); connect(this, &Playground::updateNodes, m_worker, &Worker::process); connect(m_worker, &Worker::started, this, &QTextEdit::clear); connect(m_worker, &Worker::success, this, &Playground::handle); } void setSource(QTextEdit *textEdit); signals: void updateNodes(QString text); private slots: void onTextChanged() { if (m_timer->isActive()) { m_timer->stop(); } m_timer->start(500); } void handle(QString text); private: QTimer *m_timer; Worker *m_worker; QTextEdit *m_source = nullptr; }; class Worker : public QObject { Q_OBJECT public: explicit Worker(QObject *parent = nullptr) { m_thread.reset(new QThread); moveToThread(m_thread.get()); m_thread->start(); } ~Worker(); ... public slots: void process(QString text); private slots: void cleanup(); signals: void started(); void success(QString result); void finished(); private: ... std::unique_ptr<QThread> m_thread; };
But It does not solve the problem that the UI is freezed by
setPlainText
orclear
(or something like that I don't really know what is the problem in here).
DemoAlso there are input and output from my program:
input.txt
output.txt -
@tzorake
You should be able to test both these quickly, with your existing data, in a standalone program outside your current code:- Does it behave badly if you use a
QPlainTextEdit
instead of aQTextEdit
? - Does it behave badly if you remove any code you have for threads and just test in single-threaded with a
QPlainTextEdit
?
- Does it behave badly if you use a
-
@JonB Okey. I tested each of the cases.
The results:- Single threaded application behaves the same way as before (nothing changed).
- QPlainTextEdit works fine. It does not freeze while data is setting in it. Demo
It works exactly how I would like QTextEdit to work. Thanks a lot, everyone! I really appreciate your answer @JonB.
Just in case, do you know why QTextEdit and QPlainTextEdit behave itself differently in this case?
-
@tzorake said in Inserting text into QTextEdit freezes UI:
Just in case, do you know why QTextEdit and QPlainTextEdit behave itself differently in this case?
Well,
QTextEdit
accepts Qt's rich text, a subset of HTML. It has potentially a lot of work to do to parse/format what you hand it. Depends what your input was. And I don't know which method ofQTextEdit
you are using to set its content?QTextEdit
has asetPlainText()
method. Try that? It's not the same asQPlainTextEdit
though, that is its own dedicated plain text widget. Use that if you know you will only have plain text. If your text has HTML this won't do you, but it looks like whatever you have is taking some time inQTextEdit
. -
T tzorake has marked this topic as solved on
-
@tzorake said in Inserting text into QTextEdit freezes UI:
@JonB Yes, I used setPlainText to set up the text in QTextEdit. Thanks for the explanation!
Then I don't know why you see
QPlainTextEdit
much better performant thanQTextEdit::setPlainText()
. MaybeQTextEdit
is not optimized for plain text, it just accepts it as a convenience. But really it is supporting rich text and more complex formatting. -
Then I'm not sure why you think QTextEdit::setPlainText() is significantly less performant than QPlainTextEdit. It's possible that QTextEdit only supports plain text as a convenience and isn't optimized for it. In actuality, though, it is supporting more intricate formatting and rich text.
-