文本区域记录速度慢

Textarea slow for logging

我有一个 Qt 应用程序,我想显示一些日志。我使用 TextArea。但是,如果日志很大或事件来得太快,GUI 就无法绘制 Textarea 足够快。

我已经用 Qt Creator (QML Profiler) 分析了这个问题,如果日志很大,绘制 GUI 需要 300 毫秒。我在 Raspberry Pi2 上使用这个软件。

有什么解决办法吗?我应该使用其他 QML 控件吗?谢谢

QML代码:

TextArea {
    text: appHandler.rawCommunication
    readOnly: true        
}

C++ 代码:

Q_PROPERTY(QString rawCommunication READ rawCommunication WRITE setrawCommunication NOTIFY rawCommunicationChanged)

void setrawCommunication(QString val)
{
    val.append("\n");
    val.append(m_rawCommunication);
    m_rawCommunication = val;
    emit rawCommunicationChanged(m_rawCommunication);
}

使用 view, like ListView. They instantiate their delegates as needed,视图根据用户在列表中的位置显示需要显示的数据。这意味着它们在可视化大量数据方面比 TextArea 这样的项目表现得更好,在您的情况下,后者将在内存中保留一个巨大的、不断增长的字符串。

你的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。更进一步:如果您不需要可编辑的文本,请使用普通的旧 Text。切换到这些可能不会有太大的不同,但如果您仍然发现速度很慢(并且一次可以看到很多代表),则值得一试。

试试这个方法: 创建一个 C++ 记录器 class 将所有日志附加到此 class 并使用一些动作打印它们,例如单击按钮 这将解决您的性能问题

代码示例:

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

使用此方法,当您想要发布软件时,只需更改一行即可停止所有日志

但是,我包含了完整的代码,使用“QAbstractListModel”将大量数据记录到 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();
}

我尝试了 ListView 建议,但它有几个缺点:

  • 添加新输出时,没有简单的方法可以使视图保持在底部
  • lines/delegates
  • 中没有选择

所以我最终使用了缓存 TextArea,每秒更新一次:

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)
            }
        }