qt qml listview加载日志目录菜单及显示日志文件

文章讲述了在QML中通过C++实现本地文件目录加载、目录下日志文件显示,以及使用JSON减少内存负担的过程,包括XMLHttpRequest加载文件和解决中文乱码问题的解决方案。
摘要由CSDN通过智能技术生成

qt qml listview加载日志目录菜单及显示目录下日志文件

*在qml中直接访问本地电脑的文件比较麻烦,使用qml listview配合c++,通过后台QList生成listview所需ListModel折腾很久,还要实现目录加载和文件清单加载,内存管理也是个麻烦事。后面考虑在后台生成json内容,提供给前台qml使用,比较轻巧的方式,实现了目录菜单展开和关闭,目录下日志文件的显示。然后在qml中加载日志文件显示也比较麻烦,遇到一些问题,比如要通过XMLHttpRequest方式加载文件,日志文件内容如何自动换行,汉字乱码等,最终还算得到了解决。希望能对需要的人有所帮助。

下面展示一些代码`。
C++头文件

#ifndef FOLDERLISTDATAPROVIDER_H
#define FOLDERLISTDATAPROVIDER_H

#include <QObject>
#include <QMap>
#include <QVariant>

class FolderlistDataProvider : public QObject
{
    Q_OBJECT
    public:
        explicit FolderlistDataProvider(QObject *parent = nullptr);
        //通过Q_INVOKABLE方式提供前端qml调用
        Q_INVOKABLE QVariantMap getCategoryData(QString path);
    signals:
};

#endif // FOLDERLISTDATAPROVIDER_H

C++代码文件

#include "folderlistdataprovider.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QDir>

FolderlistDataProvider::FolderlistDataProvider(QObject *parent) : QObject(parent)
{

}

QVariantMap FolderlistDataProvider::getCategoryData(QString path){
    QDir directory(path);
    // 检查目录是否存在
    if (!directory.exists()) {
        qDebug() << "日志目录不存在: " << path;
        return QVariantMap();
    }
    // 设置过滤器仅包含目录
    directory.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
    // 设置按名称排序
    directory.setSorting(QDir::Name | QDir::Reversed);
    // 获取目录列表
    QFileInfoList dirList = directory.entryInfoList();
    // 创建一个 QVariantList 并将类别添加到其中
    QVariantList categoriesList;
    // 遍历目录列表并输出目录路径
    foreach (const QFileInfo& dirInfo, dirList) {
        QJsonObject categoryObject;
        categoryObject["category"] = dirInfo.fileName();
        categoryObject["expand"] = false;
        QDir subDirectory(dirInfo.absoluteFilePath());
        if (!subDirectory.exists()){
            qDebug() << "子目录不存在: " << dirInfo.absoluteFilePath();
            return QVariantMap();
        }

        // 设置过滤器仅包含目录
        subDirectory.setFilter(QDir::Files);
        // 设置按名称排序
        subDirectory.setSorting(QDir::Name | QDir::Reversed);
        // 获取文件列表
        QFileInfoList subFileList = subDirectory.entryInfoList();
        QJsonArray itemsArray;
        foreach (const QFileInfo& fileInfo, subFileList) {
            QJsonObject item;
            item["name"] = fileInfo.fileName();
            item["path"] = "file:///" + fileInfo.absoluteFilePath();
            itemsArray.append(item);
        }
        categoryObject["itemsModel"] = itemsArray;
        categoriesList.append(categoryObject);
    }
    // 创建一个 QVariantMap,将 QVariantList 添加到其中
    QVariantMap data;
    data["categoryList"] = QVariant(categoriesList);
    return data;
}

然后在main.cpp中注册导出FolderlistDataProvider类及定义日志文件所在路径

#include <QApplication>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "folderlistdataprovider.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    //QGuiApplication app(argc, argv);
    QApplication app(argc, argv);
    //注册C++类
    qmlRegisterType<FolderlistDataProvider>("mylib.folderlistdataprovider", 1, 0, "FolderlistDataProvider");

    QQmlApplicationEngine engine;
    // 获取应用程序目录路径
    QString appDirPath = QCoreApplication::applicationDirPath();
    //注册路径
    engine.rootContext()->setContextProperty("appDirPath", appDirPath);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

qml生成前端组件和调用C++类函数

import QtQuick 2.12
import mylib.folderlistdataprovider 1.0

Item {
	//引入C++后台对象
    FolderlistDataProvider {
        id: provider
    }
    property variant categoryData: ({})
    property ListModel mymodel:ListModel {
        id: tempModel
    }
    Component {
        id: categoryDelegate
        Column {
            width: parent.width
            Rectangle {
                color:"#1c1c24"
                width: parent.width
                height: 80
                Row {
                    spacing: 10
                    anchors {
                        left: parent.left
                        right: parent.right
                        verticalCenter: parent.verticalCenter
                    }

                    Item {
                        width: 48
                        height: 48
                        MouseArea {
                            anchors.fill: parent
                            onClicked: expand = !expand
                            Image {
                                anchors.fill: parent
                                source: expand ? "qrc:/imgs/folder-open.png" : "qrc:/imgs/folder.png"
                                smooth: true;
                                fillMode: Image.PreserveAspectFit;
                            }
                        }
                    }
                    Text {
                        text: category
                        font.pixelSize: 24
                        anchors.verticalCenter: parent.verticalCenter
                        color:"white"
                    }
                }
            }
            //目录及日志文件显示
            ListView {
                width: parent.width
                //菜单不展开时,高度设置为-1
                height: expand ? contentHeight : -1
                model: itemsModel
                clip: false

                delegate: Item {
                    width: parent.width
                    height: 50
                    Row {
                        spacing: 10
                        anchors {
                            left: parent.left
                            right: parent.right
                            verticalCenter: parent.verticalCenter
                        }

                        Rectangle {
                            width: 24
                            height: 1
                            color: "transparent"
                        }
                        Item {
                            width: 32
                            height: 32
                            MouseArea {
                                anchors.fill: parent
                                onClicked:{
                                    doDisplayLogs(txtDisplay,path);
                                }
                                Image {
                                    anchors.fill: parent
                                    source: "qrc:/imgs/file.png"
                                    smooth: true;
                                    fillMode: Image.PreserveAspectFit;
                                }
                            }
                        }

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            text: name
                            color: "white"
                            font.pixelSize: 20
                        }
                    }
                }
            }
        }
    } 
    
    ListView {
        width: parent.width
        height: parent.height 
        model: mymodel
        clip:true

        delegate: categoryDelegate
    }

    Component.onCompleted: {
    	//appDirPath为定义的日志文件所在路径
        categoryData = provider.getCategoryData(appDirPath + "/logs");
        for (var item of categoryData.categoryList){
            tempModel.append({category: item.category,expand:item.expand,itemsModel: item.itemsModel})
        }
    }
}

main.qml中调用

function doDisplayLogs(dis,path){
        dis.text = "";
        path = path.replace(/\\/g, "/");
        var file = new XMLHttpRequest();
        file.open("GET", path, true); // 将第三个参数设置为 true
        file.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
        file.onreadystatechange = function() {
          if (file.readyState === 4 && file.status === 200) {
              var texts = decodeURIComponent(file.responseText);
              var lines = texts.split('\n');
              for (var i = 0; i < lines.length; i++) {
                  dis.append(lines[i]);
              }
          } else {
            //console.error('Error:', file.status);
          }
        };
        file.send(null);
    }
	Rectangle{
	    
	    id : rectLogTitle
	    anchors.top: parent.top
	    anchors.topMargin: 20
	    anchors.left: parent.left
	    anchors.leftMargin: 20
	    width:400
	    height: parent.height - 30
	    color:"#1c1c24"
	    border.color: "#1f75fc"
	    border.width: 1
	
	    MyFolderListView {
	        anchors.fill: parent
	        anchors.margins: 10
	    }
	}
	Rectangle{
           id: rectLogMain
           anchors.top: parent.top
           anchors.topMargin: 20
           anchors.left: rectLogTitle.right
           anchors.leftMargin: 20
           width:parent.width * 0.7
           height: parent.height - 30
           color:"#1c1c24"
           border.color: "#1f75fc"
           border.width: 1

           ScrollView {
               id: scrollLogView
               anchors.leftMargin: 10
               clip: true
               anchors.fill: parent

               TextArea {
                   id: txtDisplay
                   width: parent.width
                   height: parent.height
                   color: "#FAFAFB"
                   readOnly:true
                   textFormat:TextEdit.RichText
                   text:""
                   wrapMode: TextArea.Wrap
                   function append(strContent)
                   {
                       txtDisplay.text = txtDisplay.text + '\n' + strContent
                   }
               }
           }
       }

最终实现效果为:
在这里插入图片描述

需要自己去找几个文件夹和文件的图片资源

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cgoo_0102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值