Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to make a QTextStream stream to QTextBrowser
QtWS25 Last Chance

How to make a QTextStream stream to QTextBrowser

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 4 Posters 1.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Amelia SZKA Offline
    Amelia SZKA Offline
    Amelia SZK
    wrote on last edited by
    #1

    My application can interact with users in either console or GUI mode. Both modes have the sole purpose of collecting user inputs (source file, destination file, etc etc...), do some verifications, and then pass those inputs as parameters to the function that will take care of the computations.

    However, there are 2 parameters that aren't user inputs. These are outStream and errStream, and they are used by the computation function to report current status and errors. Both parameters are of type QTextStream.

    If in console mode, the function will receive QTextStream outStream(stdout) and QTextStream outStream(stderr), effectively sending the messages to stdout and stderr.

    If in GUI mode, I want those message to appear in a QTextBrowser (with the error messages in red, but whatever). To do this, I wrote a class TextBrowserStream that extends QTextStream and implements (overrides?) the << operator. This solution behaves as intended as long as I stay in the MainWindow method where the TextBrowserStream instances were created. When those instances are passed in argument, all I get is messages on stderr saying "QTextStream: No device".

    I searched for solutions, and notably found those two posts, presenting a few solutions in the replies:

    • Streaming to QTextEdit via QTextStream
    • [SOLVED] Streaming Text Continuously to QTextEdit Without Creating File

    Two problems:

    • My situation requires the use of QTextStream because this is how the reports are sent to stdout and stderr when in console mode. This requirement isn't part of the posts I linked.
    • I am a beginner in both Qt and C++ and I cannot discern wether those solutions can be applied to my situation, or if they're fundamentally flawed, or if it's my basic design (using QTextStream to print on either stdout/stderr or a QTextBrowser) that doomed me from the start.

    Code snippets

    I tried to edit the snippets down to the minimum, so I omitted most of the include directives, notably.

    main.cpp

    int main(int argc, char *argv[]) {
        if (argc == 1) {
            QApplication a(argc, argv);
            MainWindow w;
            w.show();
            return a.exec();
        } else {
            return mainConsole(argc, argv);
        }
    }
    

    mainconsole.cpp

    int mainConsole(int argc, char *argv[]) {
        QTextStream outStream(stdout);
        QTextStream errStream(stderr);
        return doItAll(outStream, errStream); //terrific name, I know
    }
    

    mainwindow.cpp

    #include "textbrowserstream.h"
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent), ui(new Ui::MainWindow) {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow() {
        delete ui;
    }
    
    void MainWindow::on_buttonBox_accepted() {
        TextBrowserStream outStream(*ui->statusTextBrowser, Qt::GlobalColor::black);
        TextBrowserStream errStream(*ui->statusTextBrowser, Qt::GlobalColor::red);
        QString const HelloCpp = "Hello C++";
        outStream << HelloCpp << endl;  // Will appear in statusTextBrowser
        doItAll(outStream, errStream);
    }
    

    doitall.cpp

    #include "textbrowserstream.h"
    int doItAll(QTextStream &outStream, QTextStream &errStream) {
        QString const HelloQt = "Hello Qt";
        outStream << HelloQt << endl;
        // When called by mainConsole, will send "Hello Qt" to stdout, as intended  
        // When called by MainWindow, will send "QTextStream: No device" to stderr (!)
        return 0;
    }
    

    textbrowserstream.h

    This one is an unedited copy-pasta from my code, since this is where I am struggling.

    #ifndef TEXTBROWSERSTREAM_H
    #define TEXTBROWSERSTREAM_H
    
    #include <QWidget>
    #include <QTextStream>
    #include <QTextBrowser>
    #include <QColor>
    
    class TextBrowserStream : public QTextStream
    {
        QTextBrowser &browser;
        QColor const &color;
    
    public:
        TextBrowserStream(QTextBrowser &browser,
                          QColor const &color = Qt::GlobalColor::black)
            : browser(browser), color(color)
        {
        }
    
        // Based on gregseth's answer at https://stackoverflow.com/a/2351479
        TextBrowserStream &operator<<(QString const &string)
        {
            browser.setTextColor(color);
            browser.append(string);
    
            return *this;
        }
    };
    
    #endif // TEXTBROWSERSTREAM_H
    
    

    Final Notes

    • My code compiles, and my app outputs correct results both in console and GUI. The current issue is strictly a matter of communicating progress or errors to the user
    • I am using both Qt 5.9.5 and 5.15.0 to build (long story), and the two of them have the same behaviour
    • Let me know if more code snippets are needed
    • Thank you for your time
    jsulmJ 1 Reply Last reply
    0
    • VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by
      #3

      TextBrowserStream &operator<<(QString const &string) does not override QTextStream &operator<<(QString const &string) so there is no why doItAll even knows the first function exists.

      What you should do, instead of subclassing the stream is to subclass a QIODevice, override write() so that it writes to the text browser and then use a normal QTextStream on such device.

      The quick-and-easy solution, however is have a QBuffer operate on a QByteArray, use the QBuffer as device for QTextStream then connect to the QBuffer::channelBytesWritten signal to know when new data was writted by the QTextStream and copy that data to he text browser

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      Amelia SZKA 1 Reply Last reply
      2
      • Amelia SZKA Amelia SZK

        My application can interact with users in either console or GUI mode. Both modes have the sole purpose of collecting user inputs (source file, destination file, etc etc...), do some verifications, and then pass those inputs as parameters to the function that will take care of the computations.

        However, there are 2 parameters that aren't user inputs. These are outStream and errStream, and they are used by the computation function to report current status and errors. Both parameters are of type QTextStream.

        If in console mode, the function will receive QTextStream outStream(stdout) and QTextStream outStream(stderr), effectively sending the messages to stdout and stderr.

        If in GUI mode, I want those message to appear in a QTextBrowser (with the error messages in red, but whatever). To do this, I wrote a class TextBrowserStream that extends QTextStream and implements (overrides?) the << operator. This solution behaves as intended as long as I stay in the MainWindow method where the TextBrowserStream instances were created. When those instances are passed in argument, all I get is messages on stderr saying "QTextStream: No device".

        I searched for solutions, and notably found those two posts, presenting a few solutions in the replies:

        • Streaming to QTextEdit via QTextStream
        • [SOLVED] Streaming Text Continuously to QTextEdit Without Creating File

        Two problems:

        • My situation requires the use of QTextStream because this is how the reports are sent to stdout and stderr when in console mode. This requirement isn't part of the posts I linked.
        • I am a beginner in both Qt and C++ and I cannot discern wether those solutions can be applied to my situation, or if they're fundamentally flawed, or if it's my basic design (using QTextStream to print on either stdout/stderr or a QTextBrowser) that doomed me from the start.

        Code snippets

        I tried to edit the snippets down to the minimum, so I omitted most of the include directives, notably.

        main.cpp

        int main(int argc, char *argv[]) {
            if (argc == 1) {
                QApplication a(argc, argv);
                MainWindow w;
                w.show();
                return a.exec();
            } else {
                return mainConsole(argc, argv);
            }
        }
        

        mainconsole.cpp

        int mainConsole(int argc, char *argv[]) {
            QTextStream outStream(stdout);
            QTextStream errStream(stderr);
            return doItAll(outStream, errStream); //terrific name, I know
        }
        

        mainwindow.cpp

        #include "textbrowserstream.h"
        MainWindow::MainWindow(QWidget *parent)
            : QMainWindow(parent), ui(new Ui::MainWindow) {
            ui->setupUi(this);
        }
        
        MainWindow::~MainWindow() {
            delete ui;
        }
        
        void MainWindow::on_buttonBox_accepted() {
            TextBrowserStream outStream(*ui->statusTextBrowser, Qt::GlobalColor::black);
            TextBrowserStream errStream(*ui->statusTextBrowser, Qt::GlobalColor::red);
            QString const HelloCpp = "Hello C++";
            outStream << HelloCpp << endl;  // Will appear in statusTextBrowser
            doItAll(outStream, errStream);
        }
        

        doitall.cpp

        #include "textbrowserstream.h"
        int doItAll(QTextStream &outStream, QTextStream &errStream) {
            QString const HelloQt = "Hello Qt";
            outStream << HelloQt << endl;
            // When called by mainConsole, will send "Hello Qt" to stdout, as intended  
            // When called by MainWindow, will send "QTextStream: No device" to stderr (!)
            return 0;
        }
        

        textbrowserstream.h

        This one is an unedited copy-pasta from my code, since this is where I am struggling.

        #ifndef TEXTBROWSERSTREAM_H
        #define TEXTBROWSERSTREAM_H
        
        #include <QWidget>
        #include <QTextStream>
        #include <QTextBrowser>
        #include <QColor>
        
        class TextBrowserStream : public QTextStream
        {
            QTextBrowser &browser;
            QColor const &color;
        
        public:
            TextBrowserStream(QTextBrowser &browser,
                              QColor const &color = Qt::GlobalColor::black)
                : browser(browser), color(color)
            {
            }
        
            // Based on gregseth's answer at https://stackoverflow.com/a/2351479
            TextBrowserStream &operator<<(QString const &string)
            {
                browser.setTextColor(color);
                browser.append(string);
        
                return *this;
            }
        };
        
        #endif // TEXTBROWSERSTREAM_H
        
        

        Final Notes

        • My code compiles, and my app outputs correct results both in console and GUI. The current issue is strictly a matter of communicating progress or errors to the user
        • I am using both Qt 5.9.5 and 5.15.0 to build (long story), and the two of them have the same behaviour
        • Let me know if more code snippets are needed
        • Thank you for your time
        jsulmJ Offline
        jsulmJ Offline
        jsulm
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @Amelia-SZK said in How to make a QTextStream stream to QTextBrowser:

        int doItAll(QTextStream &outStream, QTextStream &errStream)

        Change it to

        int doItAll(QTextStream *outStream, QTextStream *errStream)
        

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        0
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #3

          TextBrowserStream &operator<<(QString const &string) does not override QTextStream &operator<<(QString const &string) so there is no why doItAll even knows the first function exists.

          What you should do, instead of subclassing the stream is to subclass a QIODevice, override write() so that it writes to the text browser and then use a normal QTextStream on such device.

          The quick-and-easy solution, however is have a QBuffer operate on a QByteArray, use the QBuffer as device for QTextStream then connect to the QBuffer::channelBytesWritten signal to know when new data was writted by the QTextStream and copy that data to he text browser

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          Amelia SZKA 1 Reply Last reply
          2
          • VRoninV VRonin

            TextBrowserStream &operator<<(QString const &string) does not override QTextStream &operator<<(QString const &string) so there is no why doItAll even knows the first function exists.

            What you should do, instead of subclassing the stream is to subclass a QIODevice, override write() so that it writes to the text browser and then use a normal QTextStream on such device.

            The quick-and-easy solution, however is have a QBuffer operate on a QByteArray, use the QBuffer as device for QTextStream then connect to the QBuffer::channelBytesWritten signal to know when new data was writted by the QTextStream and copy that data to he text browser

            Amelia SZKA Offline
            Amelia SZKA Offline
            Amelia SZK
            wrote on last edited by
            #4

            @VRonin I subclassed a QIODevice with this stackoverflow answer (with some modifications for color support), and it worked. My outStream messages appeared in black in the QTextBrowser, and my errStream messages appeared in red, as intended.

            However, the messages are meant to inform the user on how the computations are progressing, and currently they only appear all together once doItAll has returned. Is that normal for a QIODevice?

            Would using your QBuffer solution fix this problem? I tried to do it, but couldn't manage to connect the signals and do something with them.

            Updated code snippets

            QIODevice

            This is my current implementation, using the QIODevice solution.

            mainwindow.cpp

            TextBrowserStream has been replaced by QTextStream.

            #include "texteditiodevice.h"
            // constructor and destructor omitted
            
            void MainWindow::on_buttonBox_accepted() {
                // User has clicked on OK button
            
                // Credits to TimW https://stackoverflow.com/a/2354778
                QTextStream outStream(new TextEditIoDevice(ui->statusTextBrowser, this, Qt::GlobalColor::black));
                QTextStream errStream(new TextEditIoDevice(ui->statusTextBrowser, this, Qt::GlobalColor::red));
                QString const HelloCpp = "Hello C++";
                outStream << HelloCpp << endl;  // Will appear in statusTextBrowser
                doItAll(outStream, errStream);
            }
            

            doitall.cpp

            Updated to show the new behaviour.

            int doItAll(QTextStream &outStream, QTextStream &errStream) {
                QString const Start = "Starting...";
                QString const End = "Finished.";
                outStream << Start << endl; // Should appear immediately after clicking OK. 
                                            // Instead, it appears ~4 seconds after
                /* 
                Computations taking ~4 seconds...
                */
                outStream << End << endl; // Should appear ~4 seconds after clicking OK
                return 0;
            }
            

            texteditiodevice.h

            Just like TextBrowserStream in the original snippets, this one is an unedited copy-paste.

            #ifndef TEXTEDITIODEVICE_H
            #define TEXTEDITIODEVICE_H
            
            #include <QObject>
            #include <QTextBrowser>
            #include <QPointer>
            
            // Credits to TimW https://stackoverflow.com/a/2354778
            class TextEditIoDevice : public QIODevice
            {
                Q_OBJECT
            
            public:
                TextEditIoDevice(QTextEdit *const textEdit, QObject *const parent, QColor color)
                    : QIODevice(parent), textEdit(textEdit), color(color)
                {
                    open(QIODevice::WriteOnly | QIODevice::Text);
                }
            
            protected:
                qint64 readData(char *data, qint64 maxSize) { return 0; }
                qint64 writeData(const char *data, qint64 maxSize)
                {
                    if (textEdit)
                    {
                        textEdit->setTextColor(color);
                        textEdit->insertPlainText(data);
                    }
                    return maxSize;
                }
            
            private:
                QPointer<QTextEdit> textEdit;
                QColor color;
            };
            
            #endif // TEXTEDITIODEVICE_H
            
            

            QBuffer

            This snippet describe my attempt to use the QBuffer solution. It compiled, but during runtime, the "Hello C++" printed before the call to doItAll stopped appearing, and I got those messages on stderr:

            QObject::connect: No such signal QBuffer::channelBytesWritten()
            QObject::connect:  (receiver name: 'MainWindow')
            

            mainwindow.cpp

            This is how I tried to implement the QBuffer solution. This code is no longer part of the "current" implementation.

            // constructor and destructor omitted
            
            void MainWindow::on_buttonBox_accepted() {
                // User has clicked on OK button
            
                QBuffer outBuffer(this);
                QBuffer errBuffer(this);
            
                outBuffer.open(QIODevice::ReadWrite | QIODevice::Text);
                errBuffer.open(QIODevice::ReadWrite | QIODevice::Text);
            
                QTextStream outStream(&outBuffer);
                QTextStream errStream(&errBuffer);
            
                connect(&outBuffer, SIGNAL(channelBytesWritten()),
                        this, SLOT(on_stream_write(&outBuffer, Qt::GlobalColor::black)));
            
                connect(&errBuffer, SIGNAL(channelBytesWritten()),
                        this, SLOT(on_stream_write(&outBuffer, Qt::GlobalColor::red)));
            
                QString const HelloCpp = "Hello C++";
                outStream << HelloCpp << endl;  // Will appear in statusTextBrowser
                doItAll(outStream, errStream);
            }
            
            void MainWindow::on_stream_write(QBuffer &buffer, Qt::GlobalColor color) {
                ui->statusTextBrowser->setTextColor(color);
                while (!buffer.atEnd()) {
                    ui->statusTextBrowser->append(buffer.readLine());
                }
            }
            

            So I guess that my follow-up questions are:

            • Is it normal & expected that the QIODevice doesn't print to QTextBrowser before doItAll returns?
            • Can this be fixed with signals and slots?
            • When I tried to implement the QBuffer solution, was I on the right track?

            Thank you for your time

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #5

              Hi,

              Your doItAll function is blocking the event loop, therefore no repaint is done while it's doing its stuff hence you only see the messages once done.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              2

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved