QTextDocument in thread issue
-
Hello!
I want to display the print preview dialog with some data before printing. The data is very big, so it freezes. I have added it to a thread and added the progress dialog but the progress dialog seems also freeze. I have found the issue is with
QTextDocument setPlainTextmethod in GUI (main thread). So, I added it in the thread as well.I created the QTextDocument object in the thread, setPlainText("some data") there. Now, I have issue when I emit
QTextDocumentobject from a worker class to use it in main thread in the following lambda connect:connect(printPreviewDlg, &QPrintPreviewDialog::paintRequested, this, [txtDoc](QPrinter *printer) { txtDoc->print(printer); });It crashes with a read access violation error. I need this object to call
printmethod inQPrintPreviewDialogconnect. Any ideas/examples how to sendQTextDocumentobject between worker/main GUI threads properly? Thank you. -
Hi,
Are you sure txtDoc is a valid pointer ?
-
From this docs: https://doc.qt.io/qt-5/threads-modules.html I have found that I can pass a copy between threads by using the
clonemethod. I tried it but it returns:QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x2314adccdf0), parent's thread is QThread(0x2314b524020), current thread is QThread(0x23139204630) -
Yes, it is valid. I initialized it, set the data and emit it from a worker.
Code:
QFont docFont; docFont.setPointSize(14); QTextDocument *textDoc = new QTextDocument(this); textDoc->setDefaultFont(docFont); textDoc->setPlainText("some data"); emit generatedPreview(textDoc); // crashes in the slot emit generatedPreviewData(textDoc->clone()); //works but returns QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x2314adccdf0), parent's thread is QThread(0x2314b524020), current thread is QThread(0x23139204630) -
Why not create the QTextDocument in your thread and then move it to the main thread once it's ready ?
-
Why not create the QTextDocument in your thread and then move it to the main thread once it's ready ?
@SGaist said in QTextDocument in thread issue:
Why not create the QTextDocument in your thread and then move it to the main thread once it's ready ?
What do you mean by
your thread? I created it in a worker class and emit it to the main thread, then it crashes. When I pass it from a worker to main thread as a clone it displays the following issue:QObject: Cannot create children for a parent that is in a different thread..I can create QTextDocument object in the main thread, pass it to worker,
setPlainTextthere and then emitQTextDocumentobject back to main thread? -
If by passing you mean updating a pointer value, then you are not moving things around.
Since QTextDocument is a QObject, hence the suggestion of using moveToThread.
-
If by passing you mean updating a pointer value, then you are not moving things around.
Since QTextDocument is a QObject, hence the suggestion of using moveToThread.
So, I should call
moveToThreadonQTextDocument objectin a worker or in main GUI? Thanks. -
Since you create it in your secondary thread, you need to push it to your main thread.
-
So, I should call
moveToThreadonQTextDocument objectin a worker or in main GUI? Thanks.@Cobra91151 QTextDocument should not live in a second thread so you get that error. QTextDocument will interact with QObjects that live in the main thread (for example QTextEdit, QPrinter, etc).
-
@Cobra91151 QTextDocument should not live in a second thread so you get that error. QTextDocument will interact with QObjects that live in the main thread (for example QTextEdit, QPrinter, etc).
From this old post, I can use
QTextDocumentobject in worker class (second thread) and pass it between the threads: https://www.qt.io/blog/2007/09/27/multi-threaded-text-layout-and-printingIt contains the old thread design but I want to check out the full source code. Where I can find this snapshot example? Thank you.
-
Based on the name, I would go with the Qt 4 sources of Assistant.
-
@Cobra91151 QTextDocument should not live in a second thread so you get that error. QTextDocument will interact with QObjects that live in the main thread (for example QTextEdit, QPrinter, etc).
@eyllanesc said in QTextDocument in thread issue:
@Cobra91151 QTextDocument should not live in a second thread so you get that error. QTextDocument will interact with QObjects that live in the main thread (for example QTextEdit, QPrinter, etc).
I had
QTextDocumentin my main thread butsetPlainTextmethod freezes theGUIfor a couple of seconds. So, I need to fix it. That is why I put it to the worker (second thread). Thanks. -
@eyllanesc said in QTextDocument in thread issue:
@Cobra91151 QTextDocument should not live in a second thread so you get that error. QTextDocument will interact with QObjects that live in the main thread (for example QTextEdit, QPrinter, etc).
I had
QTextDocumentin my main thread butsetPlainTextmethod freezes theGUIfor a couple of seconds. So, I need to fix it. That is why I put it to the worker (second thread). Thanks.@Cobra91151 Qt UI classes should not be used in other threads than main (UI) thread!
How big is the text you're trying to load? -
@Cobra91151 Qt UI classes should not be used in other threads than main (UI) thread!
How big is the text you're trying to load?It is 280 pages for printing. I run some debugging and found out that
setPlainTextdoes not lead to such issue. The actual problem is withprintmethod. It takes too much time to paint all these pages to theQPrintPreviewDialog.Code:
connect(printPreviewDlg, &QPrintPreviewDialog::paintRequested, this, [txtDoc](QPrinter *printer) { txtDoc->print(printer); });I will subclass the
QTextDocumentand reimplement theprintmethod to display some progress dialog. Thanks. -
Nope, it does not work. The progress dialog still freezing.
-
Nope, it does not work. The progress dialog still freezing.
@Cobra91151 Can you show ther code?
-
@Cobra91151 Can you show ther code?
Yes, I will create the test example and post it soon. Thank you.
-
Yes, I will create the test example and post it soon. Thank you.
@Cobra91151
Also for 280 pages just how long does it take/freeze? Oh you said "a couple of seconds". Does your end-user not have the patience to wait 2 seconds for 280 pages? :) If the user prints to printer how long does it take? Users are over-pampered these days.... -
@Cobra91151
Also for 280 pages just how long does it take/freeze? Oh you said "a couple of seconds". Does your end-user not have the patience to wait 2 seconds for 280 pages? :) If the user prints to printer how long does it take? Users are over-pampered these days....Here is my test example:
printexample.h
#ifndef PRINTEXAMPLE_H #define PRINTEXAMPLE_H #include <QWidget> #include <QPushButton> #include <QHBoxLayout> #include <QThread> #include <QPrinter> #include <QPrintPreviewDialog> #include <QToolBar> #include <QTextDocument> #include <QDebug> #include "printworker.h" class PrintExample : public QWidget { Q_OBJECT public: PrintExample(QWidget *parent = nullptr); ~PrintExample(); private slots: void printData(); void setPrintPreviewData(QString printData); void setPreviewDataGenerationFailed(); private: QLayout *printLayout(QPushButton *btn); }; #endif // PRINTEXAMPLE_Hprintexample.cpp
#include "printexample.h" PrintExample::PrintExample(QWidget *parent) : QWidget(parent) { setWindowTitle("Print example"); setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); setFixedSize(500, 400); QPushButton *printBtn = new QPushButton("Print", this); printBtn->setFixedSize(120, 50); connect(printBtn, &QPushButton::clicked, this, &PrintExample::printData); setLayout(printLayout(printBtn)); } QLayout *PrintExample::printLayout(QPushButton *btn) { QHBoxLayout *btnLayout = new QHBoxLayout(); btnLayout->setAlignment(Qt::AlignHCenter); btnLayout->addWidget(btn); return btnLayout; } void PrintExample::printData() { //This is just test example data QString testBigData = "QTextDocument is a container for structured rich text documents, providing support for styled text and various types of document elements, such as lists, tables, " "frames, and images. They can be created for use in a QTextEdit, or used independently. Each document element is described by an associated format object. " "Each format object is treated as a unique object by QTextDocuments, and can be passed to objectForFormat() to obtain the document element that it is applied to. " "A QTextDocument can be edited programmatically using a QTextCursor, and its contents can be examined by traversing the document structure. " "The entire document structure is stored as a hierarchy of document elements beneath the root frame, found with the rootFrame() function. " "Alternatively, if you just want to iterate over the textual contents of the document you can use begin(), end(), and findBlock() to retrieve text blocks that you can " "examine and iterate over. The layout of a document is determined by the documentLayout(); you can create your own QAbstractTextDocumentLayout subclass and set it using " "setDocumentLayout() if you want to use your own layout logic. The document's title and other meta-information can be obtained by calling the metaInformation() " "function. For documents that are exposed to users through the QTextEdit class, the document title is also available via the QTextEdit::documentTitle() " "function. The toPlainText() and toHtml() convenience functions allow you to retrieve the contents of the document as plain text and HTML. " "The document's text can be searched using the find() functions. Undo/redo of operations performed on the document can be controlled using the setUndoRedoEnabled() " "function. The undo/redo system can be controlled by an editor widget through the undo() and redo() slots; the document also provides contentsChanged(), " "undoAvailable(), and redoAvailable() signals that inform connected editor widgets about the state of the undo/redo system. " "The following are the undo/redo operations of a QTextDocument."; QThread *printDataThread = new QThread(); PrintWorker *printWorker = new PrintWorker(); printWorker->moveToThread(printDataThread); connect(printWorker, &PrintWorker::generatedPreviewData, this, &PrintExample::setPrintPreviewData); connect(printWorker, &PrintWorker::previewDataGenerationFailed, this, &PrintExample::setPreviewDataGenerationFailed); connect(printWorker, &PrintWorker::finished, printDataThread, &QThread::quit); connect(printWorker, &PrintWorker::finished, printWorker, &PrintWorker::deleteLater); connect(printDataThread, &QThread::finished, printDataThread, &QThread::deleteLater); QMetaObject::invokeMethod(printWorker, "generatePrintPreview", Q_ARG(QString, testBigData)); printDataThread->start(); } void PrintExample::setPrintPreviewData(QString printData) { QPrinter *previewPrinter = new QPrinter(QPrinter::HighResolution); previewPrinter->setOutputFormat(QPrinter::NativeFormat); previewPrinter->setPaperSize(QPrinter::A4); previewPrinter->setPageMargins(QMarginsF(15, 15, 15, 15)); QPrintPreviewDialog *printPreviewDlg = new QPrintPreviewDialog(previewPrinter, this); printPreviewDlg->setGeometry(100, 100, 700, 500); printPreviewDlg->setMinimumSize(700, 500); printPreviewDlg->setWindowModality(Qt::ApplicationModal); printPreviewDlg->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); printPreviewDlg->installEventFilter(this); QFont docFont; docFont.setPointSize(14); QTextDocument *textDoc = new QTextDocument(this); textDoc->setDefaultFont(docFont); textDoc->setPlainText(printData); connect(printPreviewDlg, &QPrintPreviewDialog::paintRequested, this, [this, textDoc](QPrinter *printer) { textDoc->print(printer); }); connect(printPreviewDlg, &QPrintPreviewDialog::finished, this, [textDoc, previewPrinter, printPreviewDlg]() { textDoc->deleteLater(); delete previewPrinter; printPreviewDlg->deleteLater(); }); printPreviewDlg->show(); } void PrintExample::setPreviewDataGenerationFailed() { //Failed to generate preview } PrintExample::~PrintExample() { }printworker.h:
#ifndef PRINTWORKER_H #define PRINTWORKER_H #include <QObject> class PrintWorker : public QObject { Q_OBJECT public: explicit PrintWorker(QObject *parent = nullptr); public slots: void generatePrintPreview(QString previewData); signals: void generatedPreviewData(QString data); void previewDataGenerationFailed(); void finished(); }; #endif // PRINTWORKER_Hprintworker.cpp:
#include "printworker.h" PrintWorker::PrintWorker(QObject *parent) : QObject(parent) { } void PrintWorker::generatePrintPreview(QString previewData) { if (!previewData.isEmpty()) { emit generatedPreviewData(previewData); } else { emit previewDataGenerationFailed(); } emit finished(); }Screenshot:

So, it does not freeze much for 1 page. Anyway, I want to display some progress dialog before the preview dialog. Any ideas? Thanks.