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. on screen logging
Forum Updated to NodeBB v4.3 + New Features

on screen logging

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 6 Posters 1.4k Views 1 Watching
  • 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.
  • W Offline
    W Offline
    wasawi2
    wrote on last edited by
    #1

    I'm implementing a log that is displayed on my main window using a QPlainTextEdit.

    void Logger::appendMessage(const QString& text)
    {
        this->appendPlainText(text); // Adds the message to the widget
        this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()); // Scrolls to the bottom
        QCoreApplication::processEvents();
    }
    

    The way to use it now is by passing the instance of the log to all my classes that have to use it:

    ChildClass::ChildClass(QWidget *parent, Logger* _log)
    {
        log = _log;
    }
    
    void ChildClass::someFunction() {
        log->appendMessage(" a message ");
    }
    

    This is ok but I would like to know if there is a better way to do it. Ideally I would like to call something like:

    myLog() << "a message";
    

    Without having to pass the instance of the log to all of my child classes.

    Is there any good way in Qt to implement an on screen logging? I guess that it is a very common thing to have... Am I reinventing the wheel?

    Christian EhrlicherC JonBJ 2 Replies Last reply
    0
    • W wasawi2

      I'm implementing a log that is displayed on my main window using a QPlainTextEdit.

      void Logger::appendMessage(const QString& text)
      {
          this->appendPlainText(text); // Adds the message to the widget
          this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()); // Scrolls to the bottom
          QCoreApplication::processEvents();
      }
      

      The way to use it now is by passing the instance of the log to all my classes that have to use it:

      ChildClass::ChildClass(QWidget *parent, Logger* _log)
      {
          log = _log;
      }
      
      void ChildClass::someFunction() {
          log->appendMessage(" a message ");
      }
      

      This is ok but I would like to know if there is a better way to do it. Ideally I would like to call something like:

      myLog() << "a message";
      

      Without having to pass the instance of the log to all of my child classes.

      Is there any good way in Qt to implement an on screen logging? I guess that it is a very common thing to have... Am I reinventing the wheel?

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Maybe use the qt internal logging system?

      QCoreApplication::processEvents();

      please remove this - it's uneeded and potential dangerous.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      1
      • W wasawi2

        I'm implementing a log that is displayed on my main window using a QPlainTextEdit.

        void Logger::appendMessage(const QString& text)
        {
            this->appendPlainText(text); // Adds the message to the widget
            this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()); // Scrolls to the bottom
            QCoreApplication::processEvents();
        }
        

        The way to use it now is by passing the instance of the log to all my classes that have to use it:

        ChildClass::ChildClass(QWidget *parent, Logger* _log)
        {
            log = _log;
        }
        
        void ChildClass::someFunction() {
            log->appendMessage(" a message ");
        }
        

        This is ok but I would like to know if there is a better way to do it. Ideally I would like to call something like:

        myLog() << "a message";
        

        Without having to pass the instance of the log to all of my child classes.

        Is there any good way in Qt to implement an on screen logging? I guess that it is a very common thing to have... Am I reinventing the wheel?

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #3

        @wasawi2
        Consider instead emitting a signal from anywhere wanting to log, with the message as a parameter. You need a QObject instance to emit signals, probably best done in a singleton class. Connect that (once) to your logging window widget. I think this is preferable to having each emitter accessing the logger directly.

        The only thing is that this relies on signal processing, and hence the event loop running. May not be great if there are problems there. Then again, if the event loop is messed you wouldn't see the text edit get updated anyway.

        There are also QMessageLogger, QLoggingCategory and the global qInstallMessageHandler.

        I think it is nicer if you set up to log the message no matter what. Have something to test whether your log window/widget exists and is open, and send the message there additionally if so. This provides flexibility,

        1 Reply Last reply
        2
        • W Offline
          W Offline
          wasawi2
          wrote on last edited by
          #4

          Thank you @Christian-Ehrlicher and @JonB for your replies.

          I have removed

          @Christian-Ehrlicher said in on screen logging:

          QCoreApplication::processEvents();

          And I am now using Signals and Slots instead of passing the instance of my logger to each and every class that uses it. I haven't done the Singleton yet.

          What I don't really grasp is how to use the Qt internal logging system or the QMessageLogger while printing to screen at the same time... I guess it can only be done separately, is that correct?

          Thanks again!
          w

          Christian EhrlicherC 1 Reply Last reply
          0
          • W wasawi2

            Thank you @Christian-Ehrlicher and @JonB for your replies.

            I have removed

            @Christian-Ehrlicher said in on screen logging:

            QCoreApplication::processEvents();

            And I am now using Signals and Slots instead of passing the instance of my logger to each and every class that uses it. I haven't done the Singleton yet.

            What I don't really grasp is how to use the Qt internal logging system or the QMessageLogger while printing to screen at the same time... I guess it can only be done separately, is that correct?

            Thanks again!
            w

            Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #5

            @wasawi2 said in on screen logging:

            I guess it can only be done separately, is that correct?

            No, why?
            Install a qt message handler and do whatever you want.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            1 Reply Last reply
            0
            • S Offline
              S Offline
              SimonSchroeder
              wrote on last edited by
              #6

              We are using qInstallMessageHandler in our software. It works perfectly well. It's really the best option so you don't have to fully reinvent the wheel for logging. Just concern yourself with how to show the output of the log.

              1 Reply Last reply
              0
              • W Offline
                W Offline
                wasawi2
                wrote on last edited by
                #7

                Thanks @Christian-Ehrlicher and @SimonSchroeder.

                I just got it working. I used code from:
                https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                and
                https://stackoverflow.com/questions/22485208/redirect-qdebug-to-qtextedit
                It seems that the best way to go is using static variables.

                here is a description of what i have:

                in my main.cpp

                #include "mainwindow.h"
                #include <QApplication>
                #include <QtDebug>
                #include <QFile>
                #include <QTextStream>
                #include <QDateTime>
                
                static QTextStream output_ts;
                static QFile outFile(QString("%1.log").arg(QDateTime::currentDateTime().toString("ddMMyyyy-hhmmss")));
                
                void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
                {
                    //https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                    QString txt;
                
                    switch (type) {
                    case QtDebugMsg:
                        txt = QString("Debug: %1").arg(msg);
                        if (MainWindow::s_textEdit != 0)
                            MainWindow::s_textEdit->appendPlainText(msg);
                        break;
                    case QtWarningMsg:
                        txt = QString("Warning: %1").arg(msg);
                        break;
                    case QtCriticalMsg:
                        txt = QString("Critical: %1").arg(msg);
                        break;
                    case QtFatalMsg:
                        txt = QString("Fatal: %1").arg(msg);
                        abort();
                    }
                
                    txt.append(" (" + QString::fromUtf8(context.file) + ")");
                    txt.append(" line: " + QString::number(context.line));
                
                    outFile.open(QIODevice::WriteOnly | QIODevice::Append);
                    QTextStream ts(&outFile);
                    ts << txt << Qt::endl;
                }
                
                int main(int argc, char *argv[])
                {
                    qInstallMessageHandler(myMessageHandler);
                
                    QApplication a(argc, argv);
                
                    MainWindow w;
                    w.show();
                    return a.exec();
                }
                
                

                and in my mainwindow.cpp

                #include "mainwindow.h"
                #include "ui_mainwindow.h"
                
                QPlainTextEdit* MainWindow::s_textEdit = 0;
                
                MainWindow::MainWindow(QWidget *parent)
                    : QMainWindow(parent)
                    , ui(new Ui::MainWindowUI)
                {
                    ui->setupUi(this);
                
                    //Logging signals to screen
                //https://stackoverflow.com/questions/22485208/redirect-qdebug-to-qtextedit
                    s_textEdit = new QPlainTextEdit("Starting .. ");
                    QMenu* menu;
                    menu = this->menuBar()->addMenu("About");
                    menu->setObjectName(menu->title());
                    QDockWidget* dock;
                    dock = new QDockWidget("Console", this);
                    dock->setObjectName(dock->windowTitle());
                    dock->setWidget(s_textEdit);
                    s_textEdit->setReadOnly(true);
                    s_textEdit->setMaximumBlockCount(1000);
                    this->addDockWidget(Qt::RightDockWidgetArea, dock);
                    this->findChild<QMenu*>("About")->addAction(dock->toggleViewAction());
                
                

                This works well for me so far. I guess it can be improved but so far its good.

                Thank you everyone for your replies!

                JonBJ 1 Reply Last reply
                0
                • W wasawi2 has marked this topic as solved on
                • W wasawi2

                  Thanks @Christian-Ehrlicher and @SimonSchroeder.

                  I just got it working. I used code from:
                  https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                  and
                  https://stackoverflow.com/questions/22485208/redirect-qdebug-to-qtextedit
                  It seems that the best way to go is using static variables.

                  here is a description of what i have:

                  in my main.cpp

                  #include "mainwindow.h"
                  #include <QApplication>
                  #include <QtDebug>
                  #include <QFile>
                  #include <QTextStream>
                  #include <QDateTime>
                  
                  static QTextStream output_ts;
                  static QFile outFile(QString("%1.log").arg(QDateTime::currentDateTime().toString("ddMMyyyy-hhmmss")));
                  
                  void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
                  {
                      //https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                      QString txt;
                  
                      switch (type) {
                      case QtDebugMsg:
                          txt = QString("Debug: %1").arg(msg);
                          if (MainWindow::s_textEdit != 0)
                              MainWindow::s_textEdit->appendPlainText(msg);
                          break;
                      case QtWarningMsg:
                          txt = QString("Warning: %1").arg(msg);
                          break;
                      case QtCriticalMsg:
                          txt = QString("Critical: %1").arg(msg);
                          break;
                      case QtFatalMsg:
                          txt = QString("Fatal: %1").arg(msg);
                          abort();
                      }
                  
                      txt.append(" (" + QString::fromUtf8(context.file) + ")");
                      txt.append(" line: " + QString::number(context.line));
                  
                      outFile.open(QIODevice::WriteOnly | QIODevice::Append);
                      QTextStream ts(&outFile);
                      ts << txt << Qt::endl;
                  }
                  
                  int main(int argc, char *argv[])
                  {
                      qInstallMessageHandler(myMessageHandler);
                  
                      QApplication a(argc, argv);
                  
                      MainWindow w;
                      w.show();
                      return a.exec();
                  }
                  
                  

                  and in my mainwindow.cpp

                  #include "mainwindow.h"
                  #include "ui_mainwindow.h"
                  
                  QPlainTextEdit* MainWindow::s_textEdit = 0;
                  
                  MainWindow::MainWindow(QWidget *parent)
                      : QMainWindow(parent)
                      , ui(new Ui::MainWindowUI)
                  {
                      ui->setupUi(this);
                  
                      //Logging signals to screen
                  //https://stackoverflow.com/questions/22485208/redirect-qdebug-to-qtextedit
                      s_textEdit = new QPlainTextEdit("Starting .. ");
                      QMenu* menu;
                      menu = this->menuBar()->addMenu("About");
                      menu->setObjectName(menu->title());
                      QDockWidget* dock;
                      dock = new QDockWidget("Console", this);
                      dock->setObjectName(dock->windowTitle());
                      dock->setWidget(s_textEdit);
                      s_textEdit->setReadOnly(true);
                      s_textEdit->setMaximumBlockCount(1000);
                      this->addDockWidget(Qt::RightDockWidgetArea, dock);
                      this->findChild<QMenu*>("About")->addAction(dock->toggleViewAction());
                  
                  

                  This works well for me so far. I guess it can be improved but so far its good.

                  Thank you everyone for your replies!

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #8

                  @wasawi2 said in on screen logging:

                  It seems that the best way to go is using static variables.

                  I'm slightly surprised your code with global static QFile outFile works. QFile is derived from QObject, and you are not supposed to create until after executing QApplication a(argc, argv);. Maybe that only applies to QWidgets?

                  Personally I cannot see why QTextStream/QFile are globals? This would be avoided by putting them inside your myMessageHandler() instead? [You don't even use your global QTextStream output_ts?]

                  W 1 Reply Last reply
                  1
                  • kkoehneK Offline
                    kkoehneK Offline
                    kkoehne
                    Moderators
                    wrote on last edited by
                    #9
                    void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
                    {
                        //https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                        QString txt;
                    
                        switch (type) {
                        case QtDebugMsg:
                            txt = QString("Debug: %1").arg(msg);
                            if (MainWindow::s_textEdit != 0)
                                MainWindow::s_textEdit->appendPlainText(msg);
                            break;
                    

                    Please be aware your code is not thread-safe. That is, myMessageHandler() might be called from different threads, and potentially in parallel. This will cause issues if you're not protecting calls to s_textEdit::appendPlainText().

                    One way to avoid this is to queue the calls to appendPlainText():

                            if (MainWindow::s_textEdit != 0)
                                QMetaObject::invokeMethod(MainWindow::s_textEdit, "appendPlainText", Qt::QueuedConnection,  Q_ARG(QString, msg);
                    

                    Director R&D, The Qt Company

                    W 1 Reply Last reply
                    3
                    • JonBJ JonB

                      @wasawi2 said in on screen logging:

                      It seems that the best way to go is using static variables.

                      I'm slightly surprised your code with global static QFile outFile works. QFile is derived from QObject, and you are not supposed to create until after executing QApplication a(argc, argv);. Maybe that only applies to QWidgets?

                      Personally I cannot see why QTextStream/QFile are globals? This would be avoided by putting them inside your myMessageHandler() instead? [You don't even use your global QTextStream output_ts?]

                      W Offline
                      W Offline
                      wasawi2
                      wrote on last edited by
                      #10

                      @JonB Thank you for your reply!

                      First of all sorry about my bad programming skills. I'm still learning C++... and i made some mistakes.

                      You are right. "QTextStream output_ts" is not used and should not be there.

                      Also Im not sure i need the static keyword on "static QFile outFile(...)". But what i know is that if I put it inside myMessageHandler() then the file is created every time i send something to the log. But instead I just wanted one file everytime the application runs.

                      and once again sorry about my misleading words. When I said:

                      It seems that the best way to go is using static variables.

                      I wanted to say: using global variables!
                      I was referring to:

                      QPlainTextEdit* MainWindow::s_textEdit = 0;
                      

                      on mainwindow.cpp.

                      I have been a bit reluctant to use global variables but in this case I don't see any other option.

                      Thanks again for your comments!

                      jsulmJ S 2 Replies Last reply
                      0
                      • kkoehneK kkoehne
                        void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
                        {
                            //https://stackoverflow.com/questions/4954140/how-to-redirect-qdebug-qwarning-qcritical-etc-output
                            QString txt;
                        
                            switch (type) {
                            case QtDebugMsg:
                                txt = QString("Debug: %1").arg(msg);
                                if (MainWindow::s_textEdit != 0)
                                    MainWindow::s_textEdit->appendPlainText(msg);
                                break;
                        

                        Please be aware your code is not thread-safe. That is, myMessageHandler() might be called from different threads, and potentially in parallel. This will cause issues if you're not protecting calls to s_textEdit::appendPlainText().

                        One way to avoid this is to queue the calls to appendPlainText():

                                if (MainWindow::s_textEdit != 0)
                                    QMetaObject::invokeMethod(MainWindow::s_textEdit, "appendPlainText", Qt::QueuedConnection,  Q_ARG(QString, msg);
                        
                        W Offline
                        W Offline
                        wasawi2
                        wrote on last edited by
                        #11

                        @kkoehne thank you for your suggestion.

                        I'm not so aware about thread safety specially not in Qt. Thank you for your insight. It helps a lot, specially if the program grows and i start to make use of multiple threads. For now it worked fine so far.

                        Thank you very much.

                        1 Reply Last reply
                        0
                        • W wasawi2

                          @JonB Thank you for your reply!

                          First of all sorry about my bad programming skills. I'm still learning C++... and i made some mistakes.

                          You are right. "QTextStream output_ts" is not used and should not be there.

                          Also Im not sure i need the static keyword on "static QFile outFile(...)". But what i know is that if I put it inside myMessageHandler() then the file is created every time i send something to the log. But instead I just wanted one file everytime the application runs.

                          and once again sorry about my misleading words. When I said:

                          It seems that the best way to go is using static variables.

                          I wanted to say: using global variables!
                          I was referring to:

                          QPlainTextEdit* MainWindow::s_textEdit = 0;
                          

                          on mainwindow.cpp.

                          I have been a bit reluctant to use global variables but in this case I don't see any other option.

                          Thanks again for your comments!

                          jsulmJ Offline
                          jsulmJ Offline
                          jsulm
                          Lifetime Qt Champion
                          wrote on last edited by
                          #12

                          @wasawi2 said in on screen logging:

                          then the file is created every time i send something to the log

                          If that's not what you want then open the file in append mode.

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

                          1 Reply Last reply
                          0
                          • W wasawi2

                            @JonB Thank you for your reply!

                            First of all sorry about my bad programming skills. I'm still learning C++... and i made some mistakes.

                            You are right. "QTextStream output_ts" is not used and should not be there.

                            Also Im not sure i need the static keyword on "static QFile outFile(...)". But what i know is that if I put it inside myMessageHandler() then the file is created every time i send something to the log. But instead I just wanted one file everytime the application runs.

                            and once again sorry about my misleading words. When I said:

                            It seems that the best way to go is using static variables.

                            I wanted to say: using global variables!
                            I was referring to:

                            QPlainTextEdit* MainWindow::s_textEdit = 0;
                            

                            on mainwindow.cpp.

                            I have been a bit reluctant to use global variables but in this case I don't see any other option.

                            Thanks again for your comments!

                            S Offline
                            S Offline
                            SimonSchroeder
                            wrote on last edited by
                            #13

                            @wasawi2 said in on screen logging:

                            I wanted to say: using global variables!

                            I just now noticed that you are actually putting static in front of your global variable (in the code earlier posted). This makes the global variable not global anymore. It just makes it 'global' for the current translation unit (in most cases a single translation unit is a single .cpp file). If you would have a 'global' variable with static in a header file, each translation unit would get its own copy. As a beginner you most likely never want to use static for global variables. However, you can easily pull that line into your myMessageHandler function (including the static). Inside a function static means that the variable is created only once. Keep the call to the constructor in the same line as you declare the variable and this will also only be executed once. You would not notice the difference between a global variable and a local static variable.

                            Before calling outFile.open(...) you should check if the file is already opened.

                            In the same way you can make the QTextStream persistent by placing a static in front. This will work if you don't reopen the file every time you place a message into the log.

                            W 1 Reply Last reply
                            1
                            • S SimonSchroeder

                              @wasawi2 said in on screen logging:

                              I wanted to say: using global variables!

                              I just now noticed that you are actually putting static in front of your global variable (in the code earlier posted). This makes the global variable not global anymore. It just makes it 'global' for the current translation unit (in most cases a single translation unit is a single .cpp file). If you would have a 'global' variable with static in a header file, each translation unit would get its own copy. As a beginner you most likely never want to use static for global variables. However, you can easily pull that line into your myMessageHandler function (including the static). Inside a function static means that the variable is created only once. Keep the call to the constructor in the same line as you declare the variable and this will also only be executed once. You would not notice the difference between a global variable and a local static variable.

                              Before calling outFile.open(...) you should check if the file is already opened.

                              In the same way you can make the QTextStream persistent by placing a static in front. This will work if you don't reopen the file every time you place a message into the log.

                              W Offline
                              W Offline
                              wasawi2
                              wrote on last edited by
                              #14

                              @SimonSchroeder thank you very much for your help. It is more clear now to me the use of static keyword in global variables which I did not understand.

                              I have moved the static call to myMessageHandler, also checked if file is already open. So everything is safer and more meaningful now.

                              thanks again for your help!

                              1 Reply Last reply
                              0

                              • Login

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