QML 读取本地文件内容
QML 对本地文件的读写
QML 里似乎没有提供直接访问本地文件的模块,但是我们能够自己扩展 QML,给它加上访问本地文件的能力。
Qt 官方文档对 QML 是这样介绍的:
It defines and implements the language and engine infrastructure, and provides an API to enable application developers to extend the QML language with custom types and integrate QML code with JavaScript and C++.
自定义模块
我们可以通过自定义 C++ 类,实现文件的读写并整合进 QML 中,使其作为一个文件读写的独立模块。
C++ 里这个类叫做 FileContent
头文件 FileContent.h:
#ifndef FILECONTENT_H
#define FILECONTENT_H 
#include <QObject>
#include <QFile>
#include <QTextStream> 
class FileContent : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(QString content READ getContent)
    Q_PROPERTY(QString filename READ getFileName WRITE setFileName)
    Q_INVOKABLE QString getContent();
    Q_INVOKABLE QString getFileName();
    FileContent(QObject *parent = 0);
    ~FileContent();
private:
    QFile   *file;
    QString content;
    QString filename;
public slots:
    void setFileName(const QString& filename);
    void clearContent();
}; 
#endif // FILECONTENT_H
FileContent 的实现:
#include "filecontent.h"
#include <QDebug> 
FileContent::FileContent(QObject *parent) { 
} 
FileContent::~FileContent() {
    delete file;
} 
QString FileContent::getFileName() {
    return this->filename;
} 
void FileContent::setFileName(const QString &filename) {
    this->filename = filename;
    file = new QFile(filename); 
} 
QString FileContent::getContent() {
    if( content.length() == 0 ) {
        file->open(QIODevice::ReadOnly | QIODevice::Text);
        QTextStream in(file);
        content = in.readAll();
        if( content.length() == 0) {
            qDebug() << "[Warning] FileContent: file " << this->filename << "is empty" << endl;
        }
    }
    return content;
} 
void FileContent::clearContent() {
    content.clear();
}
FileContent 需要继承 QObject 类,并且在类内使用 Qt 的一系列宏。
这里用到了 Q_PROPERTY 宏,声明该类的一个属性,并给出 set 和 get 对应的方法名。还有 Q_INVOKABLE 宏,以便在 QML 中可以调用 FileContent 类的方法。
这里的 FileContent 类有两个属性,一个是文件名 filename,另一个是文件的内容 content。这两个属性可以直接在 QML 中作为 Item 的属性进行赋值。
我们把 FileContent 在 QML 中的名字叫做 FileContentItem,但现在还不能直接在 QML 文件中引用 FileContentItem,我们还需要通过 QmlEngine 提供的 qmlRegisterType 方法,向 Qml 系统注册我们写的这个类。
在 main 函数里面添加:
qmlRegisterType<FileContent>("test.filecontent", 1, 0, "FileContentItem");
然后在 QML 文件里面引用我们定义的 FileContent,然后就可以像使用普通的 Item 一样使用 FileContentItem 了。
import test.filecontent 1.0 
FileContentItem {
    id: content
    filename: ":/obj/craft.obj"   // default is craft.obj
    property bool ready: false
    Component.onCompleted: {
        ready = true;
        GLcode.readFile = processContent;
    } 
    function processContent(process, source) {
        while( !ready ) {
            ;
        } 
        if( source !== undefined ) {
            filename = source;
        }
        console.time('Read file: "' + source + '"');
        process(getContent());
        console.timeEnd('Read file: "' + source + '"');
        clearContent();  // save memory
    }
}
这里 FileContentItem 里的 filename 和 content 属性其实分别对应的 C++ 里面用 Q_PROPERTY 定义的属性。这里并没有考虑要读取的文件内容大小,而是直接用 getContent() 方法直接返回文件的所有内容,如果文件过大,也可以考虑流式读取文件内容。
JavaScript 异步读取文件
如果需要在 QML 里面读取资源文件而不需要将数据写入到文件中,那么其实可以使用 JavaScript 的 XMLHttpRequest 方法来读取文件。当然这个方法与浏览器里面的使用有一点点区别。
这是我从 Qt 自带的 Planets Example 中扎到的实现:
/**
* this function is copied from planets demo of qt version of threejs
* I modified some of it, now it works fine for me
**/
function readFile(url, onLoad, onProgress, onError) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (request.readyState === XMLHttpRequest.DONE) {
        // TODO: Re-visit https://bugreports.qt.io/browse/QTBUG-45581 is solved in Qt
            if (request.status == 200 || request.status == 0) {
//                var response;
//                response = request.responseText;
                console.time('Process file: "' + url + '"');
                onLoad( request.responseText );
                console.timeEnd('Process file: "' + url + '"');
            }
             else if ( onError !== undefined ) {
               onError();
            }
        }
       else if (request.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
           if ( onProgress !== undefined ) {
               onProgress();
            }
       }
    };
    request.open( 'GET', url, true );
    request.send( null );
}
因为我暂时只需要回调 onLoad 方法,所以我只关注这一部分的逻辑,该方法和浏览器中 AJAX 的异步请求并没有太大区别,不过需要注意的是这里有个 bug: request 放回的状态码有可能是 0,而这有可能意味着请求成功。所以在检测请求是否成功返回时应该要加上 request.status == 0 的判断。
总结
此外,如果想要在 QML 里面读写本地的配置文件,还可以使用 QML 已经提供的 Settings 模块,它对应的是 C++ 部分的 QSettings 类,提供平台无关的程序配置。
在 QML 中实现文件的读写有多种方法,具体的做法需要结合具体的需求,由于我做的程序可能需要迁移到 Web 上,因此最终使用 JavaScript 提供的 XMLHttpRequest 来进行异步请求。
QML 读取本地文件内容的更多相关文章
- 手工创建tomcat应用,以及实现js读取本地文件内容
		手工创建tomcat应用: 1.在webapps下面新建应用目录文件夹 2.在文件夹下创建或是从其他应用中复制:META-INF,WEB-INF这两个文件夹, 其中META-INF清空里面,WEB-I ... 
- HTML-点击Div读取本地文件内容
		<!DOCTYPE html> <html> <div id="container" onclick="choosefile();" ... 
- 前台JS(type=‘file’)读取本地文件的内容,兼容各种浏览器
		[自己测了下,能兼容各种浏览器,但是读取中文会出现乱码.自己的解决方法是用notepad++把txt文件编码改为utf-8(应该是和浏览器编码保持一致吧?..)] 原文 http://blog.cs ... 
- ios本地文件内容读取,.json .plist 文件读写
		ios本地文件内容读取,.json .plist 文件读写 本地文件.json .plist文件是较为常用的存储本地数据的文件,对这些文件的操作也是一种常用的基础. 本文同时提供初始化变量的比较标准的 ... 
- H5读取本地文件操作
		H5读取本地文件操作 本文转自:转:http://hushicai.com/2014/03/29/html5-du-qu-ben-di-wen-jian.html感谢大神分享. 常见的语言比如php. ... 
- python 读取本地文件批量插入mysql
		Uin_phone.txt 本地文件内容 有1000条,这里只是展示前几条,供参考 133584752 133584759 133584764 133584773 133584775 13358477 ... 
- FileReader读取本地文件
		FileReader是一种异步读取文件机制,结合input:file可以很方便的读取本地文件. 一.input:type[file] file类型的input会渲染为一个按钮和一段文字.点击按钮可打开 ... 
- 五种方式让你在java中读取properties文件内容不再是难题
		一.背景 最近,在项目开发的过程中,遇到需要在properties文件中定义一些自定义的变量,以供java程序动态的读取,修改变量,不再需要修改代码的问题.就借此机会把Spring+SpringMVC ... 
- .NET 读取本地文件绑定到GridViewRow
		wjgl.aspx.cs: using System; using System.Collections; using System.Configuration; using System.Data; ... 
随机推荐
- 浅聊本人学习React的历程——第一篇生命周期篇
			作为一个前端小白,在踏入前端程序猿行业的第三年接触了React,一直对于框架有种恐惧感,可能是对陌生事物的恐惧心里吧,导致自己一直在使用原生JS和JQ作为开发首选,但是在接触了React之后,发现了其 ... 
- 浅谈UBUNTU
			一 UBUNTU介绍 Ubuntu(乌班图)是一个以桌面应用为主的Linux操作系统,其名称来自非洲南部祖鲁语或豪萨语的"ubuntu"一词,意思是"人性".& ... 
- 解决RegexKitLite导入报错问题
			1.RegexKitLite是什么? RegexKitLite是一个非常方便的处理正则表达式的第三方类库. 本身只有一个RegexKitLite.h和RegexKitLite.m 2.导入RegexK ... 
- socketserver,threading
			一,socketserver #server import socketserver class Myserver(socketserver.BaseRequestHandler): def han ... 
- js  window.open()打开的页面关闭后刷新父页面
			function test(){ var winObj = window.open(URL); var loop = setInterval(function(){ if(winObj.closed) ... 
- Springboot 实现前台动态配置数据源 (修改数据源之后自动重启)
			1.将 db.properties 存放在classpath路径; driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3 ... 
- git相关问题处理
			1.在git push时无法提交代码,相对于git服务器上,本身代码可能不是最新的,因此提交的时候会报以下这个错误 Updates were rejected because the tip of y ... 
- 「洛谷P3768」简单的数学题 莫比乌斯反演+杜教筛
			题目链接 简单的数学题 题目描述 输入一个整数n和一个整数p,你需要求出 \[\sum_{i=1}^n\sum_{j=1}^n (i\cdot j\cdot gcd(i,j))\ mod\ p\] ... 
- python统计字符串中字符个数
			str = "xxx" result = {} for i in set(str):#set将字符串转为集合对象,用于去重,减少计算量 result[i] = str.count( ... 
- POJ1056 IMMEDIATE DECODABILITY & POJ3630 Phone List
			题目来源:http://poj.org/problem?id=1056 http://poj.org/problem?id=3630 两题非常类似,所以在这里一并做了. 1056题目大意: 如果一 ... 
