Textarea slow for logging

后端 未结 4 553
北海茫月
北海茫月 2020-12-21 14:35

I have a Qt application and I\'d like to show some log. I use a TextArea. However, if the log is large or the events come too fast the GUI can\'t draw Te

相关标签:
4条回答
  • 2020-12-21 14:41

    try this approach: create a c++ logger class append all logs to this class and print them using some action, example a button click this will solve your performance issue

    Example of code:

    Logger.h

    #ifndef LOGGER_H
    #define LOGGER_H
    
    #include <QQmlContext>
    #include <QObject>
    #include <QStringList>
    #include <QQmlEngine>
    #include <QString>
    #include <QtCore>
    #include <QDebug>
    
    class Logger : public QObject
    {
        Q_OBJECT
    public:
        explicit Logger(QObject *parent = 0);
        ~Logger();
        Q_INVOKABLE QStringList *getLogStream();
        Q_INVOKABLE void printLogStream();
        Q_INVOKABLE void appendLog(QString log);
        Q_INVOKABLE void log(QString log="");
        Q_INVOKABLE void log(QString fileName, QString log);
    signals:
    
    public slots:
    
    private:
         QStringList* stringStream_;
    };
    
    #endif // LOGGER_H
    

    Logger.cpp

        #include "logger.h"
    
        Logger::Logger(QObject *parent) :
            QObject(parent),
            stringStream_(new QStringList)
        {
        }
    
         ~Logger(){
                    if(stringStream_ != NULL)
                    {
                        delete stringStream_;
                        stringStream_ = NULL;
                     }
                  }
        QStringList* Logger::getLogStream(){
            return stringStream_;
        }
    
        void Logger::printLogStream()
        {
            QStringListIterator itr(*stringStream_);
                while (itr.hasNext())
        qDebug()<< itr.next()<<"\n";
        }
    
         void Logger::appendLog(QString log){
             stringStream_->push_back(log) ;
        }
    void Logger::log(QString fileName,QString log)
    {
    
    #ifdef ENABLElogs
    
        fileName.push_front(" [");
        if(!fileName.contains(".qml"))
        {
            fileName.append(".qml]:");
        }
        qDebug()<<fileName<<log;
    #else
        Q_UNUSED(log);
        Q_UNUSED(fileName);
    #endif
    }
    void Logger::log(QString log)
    {
    
    #ifdef ENABLElogs
        qDebug()<<log;
    #else
        Q_UNUSED(log);
    #endif
    }
    

    main.cpp

    #include <QtGui/QGuiApplication>
    #include "qtquick2applicationviewer.h"
    #include "logger.h"
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QtQuick2ApplicationViewer *viewer = new QtQuick2ApplicationViewer;
        Logger* stream = new Logger;
        viewer->rootContext()->setContextProperty("Stream",stream);
        viewer->setMainQmlFile(QStringLiteral("qml/project/main.qml"));
        viewer->showExpanded();
    
    
        return app.exec();
    }
    

    main.qml

    import QtQuick 2.0
    import QtQuick.Controls 1.1
    
    Rectangle {
        width: 800
        height: 480
        Text {
            text: qsTr("Hello World")
            anchors.centerIn: parent
            Component.onCompleted: Stream.appendLog("Text object is completed")
        }
        Column{
            x:300
        Button{
            text:"append"
            onClicked: {
                Stream.appendLog("MouseArea object clicked")
            }
            Component.onCompleted: Stream.appendLog("Button object is completed")
        }
        Button{
            text:"logger"
            onClicked: {
             Stream.printLogStream()
            }
            Component.onCompleted: Stream.appendLog("Button logger object is completed")
        }
    }
    
    
        TextArea{
            text:"blablabla"
            Component.onCompleted: Stream.appendLog("TextArea object is completed")
        }
        Component.onCompleted: Stream.appendLog("the main object is completed")
    }
    

    project.pro

        #add this line
        # comment it, run qmake and recompile to disable logs
        DEFINES += ENABLElogs
    

    using this approch you can stop all logs with a single line change when you want to release your soft

    0 讨论(0)
  • 2020-12-21 14:54

    However, I have included complete code, using "QAbstractListModel" for a Logging heavy data to QML

    listmodel.h

    #ifndef LISTMODEL_H
    #define LISTMODEL_H
    #include <QAbstractListModel>
    
    class ListModel: public QAbstractListModel
    {
        Q_OBJECT
    public:
        ListModel();
    
       // Q_PROPERTY(QStringList logs READ name WRITE  nameChanged)
        int rowCount(const QModelIndex & parent = QModelIndex()) const;
        QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
        Q_INVOKABLE QVariant activate(int i);
    
    private:
        QStringList m_list;
    };
    
    #endif // LISTMODEL_H
    

    listmodel.cpp

    #include "listmodel.h"
    #include <QFile>
    #include <QHash>
    
    
    ListModel::ListModel()
    {
        QFile file("/home/ashif/LogFile");
        if(!file.open(QIODevice::ReadOnly))
        {
            qDebug( "Log file open failed" );
        }
        bool isContinue = true;
        do
        {
            if(file.atEnd())
            {
                isContinue = false;
            }
             m_list.append(file.readLine());
        }
        while( isContinue);
    }
    
    int ListModel::rowCount(const QModelIndex & parent ) const
    {
    
        return m_list.count();
    }
    QVariant ListModel::data(const QModelIndex & index, int role ) const
    {
        if(!index.isValid()) {
               return QVariant("temp");
           }
        return m_list.value(index.row());
    }
    QVariant ListModel::activate(int i)
    {
        return m_list[i];
    }
    

    main.qml

    import QtQuick 2.3
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    
    Window {
        visible: true
    
        ListView
        {
            width: 200; height: 250
            anchors.centerIn: parent
            model:mylistModel
            delegate: Text
            {
                text:mylistModel.activate(index)
            }
        }
    }
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlContext>
    #include <QQmlApplicationEngine>
    #include "logger.h"
    #include "listmodel.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        Logger myLogger;
        ListModel listModel;    
        engine.rootContext()->setContextProperty("mylistModel", &listModel);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        return app.exec();
    }
    
    0 讨论(0)
  • 2020-12-21 14:57

    I tried the ListView suggestion but it has several drawbacks:

    • No easy way to keep the view positioned at the bottom when new output is added
    • No selection across lines/delegates

    So I ended up using a cached TextArea, updating once every second:

    TextArea {
                id: outputArea_text
                wrapMode: TextArea.Wrap
                readOnly: true
                font.family: "Ubuntu Mono, times"
    
                function appendText(text){
                    logCache += text + "\n";
                    update_timer.start();
                }
    
                property string logCache: ""
    
                Timer {
                    id: update_timer
                    // Update every second
                    interval: 1000
                    running: false
                    repeat: false
                    onTriggered: {
                        outputArea_text.append(outputArea_text.logCache);
                        outputArea_text.logCache = "";
                    }
                }
    
                Component.onCompleted: {
                    my_signal.connect(outputArea_text.appendText)
                }
            }
    
    0 讨论(0)
  • 2020-12-21 15:05

    Use a view, like ListView. They instantiate their delegates as needed, based on which data the view says it needs to show depending on the position the user is at in the list. This means that they perform much better for visualising large amounts of data than items like TextArea, which in your case is going to keep a massive, ever-growing string in memory.

    Your delegate could then be a TextArea, so you'd have one editable block of text per log line. However, if you don't need styling, I'd recommend going with something a bit lighter, like TextEdit. Taking it one step further: if you don't need editable text, use plain old Text. Switching to these might not make much of a difference, but if you're still seeing slowness (and have lots of delegates visible at a time), it's worth a try.

    0 讨论(0)
提交回复
热议问题