Qt Quick综合实例之文件查看器

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

    如果你基于Qt SDK 5.3.1来创建一个Qt Quick App项目,项目模板为你准备的main.qml文档的根元素是ApplicationWindow或Window。这次我们就以ApplicationWindow为例,围绕着它实现一个综合实例:文件查看器。通过文件查看器的实现,我们来再次领略一下Qt Quick的犀利。

    版权所有foruok,转载请注明出处:http://blog.csdn.net/foruok

    本实例将会用到下列特性:

  • ApplicationWindow
  • MenuBar
  • ToolBar、ToolButton
  • Action
  • StatusBar
  • MediaPlayer
  • Image
  • XMLHttpRequest
  • ColorDialog
  • FileDialog
  • TextArea
  • 动态创建QML组件
  • 多界面切换

    我们之前的文章都是就某一个主题来展开的,这次要出个大招。先看点儿图。

文件查看器效果

    图1,初始状态


                  图1 初始状态

    图2 是浏览文本文件的效果:


              图2 浏览文本文件

    图3是浏览图片:


           图3 浏览图片

    图4是播放视频:


              图4 播放视频

    图5是播放音乐:


               图5 播放音乐


    好啦,看代码前,先大概介绍下用到的Qt Quick元素。

    Image、动态创建组件、界面切换这些没什么好说的,我之前的文章中已经涉及到。只讲新的了。

ApplicationWindow

    ApplicationWindow,类似Qt C++中的QMainWindow,请对照着理解。

    ApplicationWindow有菜单栏(menuBar属性)、工具栏(toolBar属性)、状态栏(statusBar属性)。咦,没有centralWidget哈,别急,有的,就是ContentItem,就是那个你不与任何属性绑定的Item。例如:

ApplicationWindow{    ...    Rectangle{        id: centralView;        anchors.fill: parent;        color: "red";    }}

    上面代码中的centralView就对应QMainWindow的centralWidget了。

    看起来没有DockWidgets……兄弟,别要求太高了,你想要么,真的想要么,你真的确定你想要么?好吧,自己实现就好了,QML里很多元素的哈。

MenuBar

    MenuBar就是菜单栏一长条区域了,它干两件事:

  1.     维护一个Menu列表(menus属性)
  2.     绘制菜单栏背景色(style属性)

Menu

    看截图中的“文件”、“设置”、“帮助”三个菜单,点一下弹出一个子菜单,Menu就代表文件这个菜单和它下面的子菜单。

    列表属性items指向Menu的子菜单项,它的类型是list<Object>,是默认属性。

    title属性是string类型,你看到的“文件”、“设置”等字样就是它指定的。

    有这两个属性,Menu就可以开始干活了。看Qt帮助里的示例代码片段:

Menu {    title: "Edit"    MenuItem {        text: "Cut"        shortcut: "Ctrl+X"        onTriggered: ...    }    MenuItem {        text: "Copy"        shortcut: "Ctrl+C"        onTriggered: ...    }    MenuItem {        text: "Paste"        shortcut: "Ctrl+V"        onTriggered: ...    }    MenuSeparator { }    Menu {        title: "More Stuff"        MenuItem {            text: "Do Nothing"        }    }}

MenuItem

    Menu的孩子啊,最受待见的就是MenuItem了。

    MenuItem代表一个具体的菜单项,点一下就干一件事情的角色。你可以实现onTriggered信号处理响应用户对它的选择。

    MenuItem的text属性指定菜单文字,iconSource属性指定菜单图标。

    Action属性很强大哦,MenuItem的text、iconSource、trigger信号等实现的效果,都可以通过Action来实现,这两种方式是等同的。咱们来看Action。

Action    

    Action类有text、iconSource等属性,还有toggled、triggered两个信号。这些在MenuItem里有对应的属性,不必多说了。

    使用Action的一大好处是,你可以给它指定一个id(比如叫open),然后在使用ToolButton构造ToolBar时指定ToolBatton的action属性为之前定义的id为open的那个action,这样工具栏的按钮就和菜单关联起来了。


    好啦,总结一下,实现菜单栏的典型代码结构是酱紫的:

MenuBar {    Menu{        title:"文件";        MenuItem{            text:"打开";            iconSource: "res/ic_open.png";            onTriggered:{                ...            }        }        MenuItem{            action: Action {                text:"保存";                iconSource: "res/ic_save.png";                onTriggered:{                    ...                }            }        }            }}

    如你所见,我使用了两种构造MenuItem的方式,一种用到了Action,一种没有。

ToolBar

    ToolBar就是工具栏对应的类,它只有一个属性,contentItem,类型为Item。一般我们可以将一个Row或者RowLayout对象赋值给contentItem,而Row或RowLayout则管理一组ToolButton来作为工具栏上的按钮。

ToolButton

    ToolButton是Button的派生类,专为ToolBar而生,一般情况下定义ToolButton对象时只需要指定其iconSource属性即可。例如:

ToolButton {    iconSource: "res/ic_open.png";}

    还有一种方式是将一个已定义好的Action对象关联到ToolButton对象上。例如:

ToolButton{    action: openAction;}

    这样的话,ToolButton会使用Action定义的iconSource或iconName作为其图标。


    好啦,构造工具栏的典型代码结构如下:

ToolBar{    RowLayout {        ToolButton{            action: textAction;        }        ToolButton{            action: imageAction;        }        ToolButton{            iconSource: "res/exit.png";            onClicked:{                ...            }        }    }}

状态栏

    ApplicationWindow的属性statusBar代表状态栏,其类型为Item,你可以将任意的Item赋值给它,可以随心所欲构建你妖娆多姿的状态栏。比如这样:

ApplicationWindow{    statusBar: Text {        text: "status bar";        color: "blue";    }}

MediaPlayer

    Qt Quick里,播放视频、音频文件都直接使用MediaPlayer类,它是万能的,不要说你万万没想到哈。

    对于音乐,最简单,设置source属性,调用play()方法,就可以了。例如:

ApplicationWindow{    ...    MediaPlayer{        id: player;        source: "xxx.mp3";    }    Component.onCompleted:{        player.play();    }}

    对于视频,MediaPlayer还得寻求场外帮助,求助对象就是它的好基友:VideoOutput !简单的示例代码:

ApplicationWindow{    ...    MediaPlayer {        id: player;        source: "test.ts";    }        VideoOutput {        anchors.fill: parent;        source: player;    }    Component.onCompleted: player.play();    }

    哦,黄小琥唱过的歌:没那么简单。是的,实际做项目比较复杂,你还要关注各种播放状态,要seek,要pause,要处理错误,简直烦死人了。请移步到Qt帮助里了解详情。

XMLHttpRequest

    在Qt Quick里,要访问网络肿么办泥?答案是:XMLHttpRequest !

    不要被它傲娇的外表迷惑,以为它只接受XML文档,其实,它什么都能处理,txt、html、json、binary……它温柔坚定强悍无比。

    本实例用它来加载本地的文本文件,耶,这样都可以哈。谁叫Qt Quick不提供直接访问本地文件的类库呢!我可不想跑到C++里用QFile、QTextStream这对黄金搭档。

    XMLHttpRequest的文档,Qt帮助里语焉不详,只有一个示例,请看这里:

    http://www.w3school.com.cn/xml/xml_http.asp

TextArea

    这有什么好讲的,看Qt帮助吧。只提一点:

    TextArea自动处理翻页按键、上下键、鼠标中键,正确的滚动文本;而TextEdit,抱歉,我是来打酱油的。


标准对话框

    Qt Quick提供了很多标准对话框,比如FileDialog用来选择文件或文件夹,ColorDialog用来选择颜色,MessageDialog用来显示一些提示信息。这些我们实例中用到了,参考Qt帮助吧。

    我只说点儿经验。

    我一开始使用qmlscene来加载main.qml,出来的界面比较正常,工具栏的图标、菜单项前也有图标。可是当我创建了一个Qt Quick App,灵异事件发生了:

    菜单项前面没有图标了……

    工具栏图标好大好大……

    颜色对话框、消息框,点击右上角的关闭按钮,收不到rejected信号啊……

    查了老半天,猛回头,警世钟响起,我了悟了。原来是酱紫的:

    Qt Quick提供了这些标准对话框的默认实现,如果应用运行的平台没有可用的,就用这些默认实现。那在Windows上,如果你的main()函数,使用QGuiApplication而非QApplication,就会用到Qt Quick实现的版本,一切都变了模样

资源管理

    Qt SDK 5.3之后,Qt Creator创建的Qt Quick App项目,就为我们建立了一个qrc文件,把main.qml扔里啦。我在实例中也把很多图标扔里了。

    使用qrc来管理资源,这是跨平台的,推荐使用。

    我还自己画了些图标,真费劲,弄得也不好看。不过应用的大眼睛图标,看起来还像那么一回事儿。

源代码

    前戏太长,也许你已经失去了兴趣。好吧,G点来咧。

    QML代码

    版权所有foruok,转载请注明出处:http://blog.csdn.net/foruok

    所有QML代码都在这里了,竟然有450行啊亲。

import QtQuick 2.2import QtQuick.Window 2.1import QtQuick.Controls 1.2import QtQuick.Controls.Styles 1.2import QtQuick.Layouts 1.1import QtQuick.Dialogs 1.1import QtMultimedia 5.0ApplicationWindow {    visible: true    width: 480    height: 360;    color: "black";    title: "文件查看器";    id: root;    property var aboutDlg: null;    property var colorDlg: null;    property color textColor: "green";    property color textBackgroundColor: "black";    menuBar: MenuBar{        Menu {            title: "文件";            MenuItem{                iconSource: "res/txtFile.png";                action: Action{                    id: textAction;                    iconSource: "res/txtFile.png";                    text: "文本文件";                    onTriggered: {                        fileDialog.selectedNameFilter = fileDialog.nameFilters[0];                        fileDialog.open();                    }                    tooltip: "打开txt等文本文件";                }            }            MenuItem{                action: Action {                    id: imageAction;                    text: "图片";                    iconSource: "res/imageFile.png";                    onTriggered: {                        fileDialog.selectedNameFilter = fileDialog.nameFilters[1];                        fileDialog.open();                    }                    tooltip: "打开jpg等格式的图片";                }            }            MenuItem{                action: Action {                    id: videoAction;                    iconSource: "res/videoFile.png";                    text: "视频";                    onTriggered: {                        fileDialog.selectedNameFilter = fileDialog.nameFilters[2];                        fileDialog.open();                    }                    tooltip: "打开TS、MKV、MP4等格式的文件";                }            }            MenuItem{                action: Action {                    id: audioAction;                    iconSource: "res/audioFile.png";                    text: "音乐";                    onTriggered: {                        fileDialog.selectedNameFilter = fileDialog.nameFilters[3];                        fileDialog.open();                    }                    tooltip: "打开mp3、wma等格式的文件";                }            }            MenuItem{                text: "退出";                onTriggered: Qt.quit();            }        }        Menu {            title: "设置";            MenuItem {                action: Action {                    id: textColorAction;                    iconSource: "res/ic_textcolor.png";                    text: "文字颜色";                    onTriggered: root.selectColor(root.onTextColorSelected);                }            }            MenuItem {                action: Action{                    id: backgroundColorAction;                    iconSource: "res/ic_bkgndcolor.png";                    text: "文字背景色";                    onTriggered: root.selectColor(root.onTextBackgroundColorSelected);                }            }            MenuItem {                action: Action{                    id: fontSizeAddAction;                    iconSource: "res/ic_fontsize2.png";                    text: "增大字体";                    onTriggered: textView.font.pointSize += 1;                }            }            MenuItem {                action: Action{                    id: fontSizeMinusAction;                    iconSource: "res/ic_fontsize1.png";                    text: "减小字体";                    onTriggered: textView.font.pointSize -= 1;                }            }        }        Menu {            title: "帮助";            MenuItem{                text: "关于";                onTriggered: root.showAbout();            }            MenuItem{                text: "访问作者博客";                onTriggered: Qt.openUrlExternally("http://blog.csdn.net/foruok");            }        }    }    toolBar: ToolBar{        RowLayout {            ToolButton{                action: textAction;            }            ToolButton{                action: imageAction;            }            ToolButton{                action: videoAction;            }            ToolButton{                action: audioAction;            }            ToolButton{                action: textColorAction;            }            ToolButton {                action: backgroundColorAction;            }            ToolButton {                action: fontSizeAddAction;            }            ToolButton {                action: fontSizeMinusAction;            }        }    }    statusBar: Rectangle {        color: "lightgray";        implicitHeight: 30;        width: parent.width;        property alias text: status.text;        Text {            id: status;            anchors.fill: parent;            anchors.margins: 4;            font.pointSize: 12;        }    }    Item {        id: centralView;        anchors.fill: parent;        visible: true;        property var current: null;        BusyIndicator {            id: busy;            anchors.centerIn: parent;            running: false;            z: 3;        }        Image {            id: imageViewer;            anchors.fill: parent;            visible: false;            asynchronous: true;            fillMode: Image.PreserveAspectFit;            onStatusChanged: {                if (status === Image.Loading) {                    centralView.busy.running = true;                }                else if(status === Image.Ready){                    centralView.busy.running = false;                }                else if(status === Image.Error){                    centralView.busy.running = false;                    centralView.statusBar.text = "图片无法显示";                }            }        }        TextArea {            id: textView;            anchors.fill: parent;            readOnly: true;            visible: false;            wrapMode: TextEdit.WordWrap;            font.pointSize: 12;            style: TextAreaStyle{                backgroundColor: root.textBackgroundColor;                textColor: root.textColor;                selectionColor: "steelblue";                selectedTextColor: "#a00000";            }            property var xmlhttp: null;            function onReadyStateChanged(){                if(xmlhttp.readyState == 4){                    text = xmlhttp.responseText;                    xmlhttp.abort();                }            }            function loadText(fileUrl){                if(xmlhttp == null){                    xmlhttp = new XMLHttpRequest();                    xmlhttp.onreadystatechange = onReadyStateChanged;                }                if(xmlhttp.readyState == 0){                    xmlhttp.open("GET", fileUrl);                    xmlhttp.send(null);                }            }        }        VideoOutput {            id: videoOutput;            anchors.fill: parent;            visible: false;            source: player;            onVisibleChanged: {                playerState.visible = visible;            }            MouseArea {                anchors.fill: parent;                onClicked: {                    switch(player.playbackState){                    case MediaPlayer.PausedState:                    case MediaPlayer.StoppedState:                        player.play();                        break;                    case MediaPlayer.PlayingState:                        player.pause();                        break;                    }                }            }        }        Rectangle {            id: playerState;            color: "gray";            radius: 16;            opacity: 0.8;            visible: false;            z: 2;            implicitHeight: 80;            implicitWidth: 200;            anchors.horizontalCenter: parent.horizontalCenter;            anchors.bottom: parent.bottom;            anchors.bottomMargin: 16;            Column {                anchors.fill: parent;                anchors.leftMargin: 12;                anchors.rightMargin: 12;                anchors.topMargin: 6;                anchors.bottomMargin: 6;                spacing: 4;                Text {                    id: state;                    font.pointSize: 14;                    color: "blue";                }                Text {                    id: progress;                    font.pointSize: 12;                    color: "white";                }            }        }        MediaPlayer {            id: player;            property var utilDate: new Date();            function msecs2String(msecs){                utilDate.setTime(msecs);                return Qt.formatTime(utilDate, "mm:ss");            }            property var sDuration;            onPositionChanged: {                progress.text = msecs2String(position) + sDuration;            }            onDurationChanged: {                sDuration = " / " + msecs2String(duration);            }            onPlaybackStateChanged: {                switch(playbackState){                case MediaPlayer.PlayingState:                    state.text = "播放中";                    break;                case MediaPlayer.PausedState:                    state.text = "已暂停";                    break;                case MediaPlayer.StoppedState:                    state.text = "停止";                    break;                }            }            onStatusChanged: {                switch(status){                case MediaPlayer.Loading:                case MediaPlayer.Buffering:                    busy.running = true;                    break;                case MediaPlayer.InvalidMedia:                    root.statusBar.text = "无法播放";                case MediaPlayer.Buffered:                case MediaPlayer.Loaded:                    busy.running = false;                    break;                }            }        }    }    function processFile(fileUrl, ext){        var i = 0;        for(; i < fileDialog.nameFilters.length; i++){            if(fileDialog.nameFilters[i].search(ext) != -1) break;        }        switch(i){        case 0:            //text file            if(centralView.current != textView){                if(centralView.current != null){                    centralView.current.visible = false;                }                textView.visible = true;                centralView.current = textView;            }            textView.loadText(fileUrl);            break;        case 1:            if(centralView.current != imageViewer){                if(centralView.current != null){                    centralView.current.visible = false;                }                imageViewer.visible = true;                centralView.current = imageViewer;            }            imageViewer.source = fileUrl;            break;        case 2:        case 3:            if(centralView.current != videoOutput){                if(centralView.current != null){                    centralView.current.visible = false;                }                videoOutput.visible = true;                centralView.current = videoOutput;            }            player.source = fileUrl;            player.play();            break;        default:            statusBar.text = "抱歉,处理不了";            break;        }    }    function showAbout(){        if(aboutDlg == null){            aboutDlg = Qt.createQmlObject(                        'import QtQuick 2.2;import QtQuick.Dialogs 1.1;MessageDialog{icon: StandardIcon.Information;title: "关于";\ntext: "仅仅是个示例撒";\nstandardButtons:StandardButton.Ok;}'                        , root, "aboutDlg");            aboutDlg.accepted.connect(onAboutDlgClosed);            aboutDlg.rejected.connect(onAboutDlgClosed);            aboutDlg.visible = true;        }    }    function selectColor(func){        if(colorDlg == null){            colorDlg = Qt.createQmlObject(                        'import QtQuick 2.2;import QtQuick.Dialogs 1.1;ColorDialog{}',                        root, "colorDlg");            colorDlg.accepted.connect(func);            colorDlg.accepted.connect(onColorDlgClosed);            colorDlg.rejected.connect(onColorDlgClosed);            colorDlg.visible = true;        }    }    function onAboutDlgClosed(){        aboutDlg.destroy();        aboutDlg = null;    }    function onColorDlgClosed(){        colorDlg.destroy();        colorDlg = null;    }    function onTextColorSelected(){        root.textColor = colorDlg.color;    }    function onTextBackgroundColorSelected(){        root.textBackgroundColor = colorDlg.color;    }    FileDialog {        id: fileDialog;        title: qsTr("Please choose an image file");        nameFilters: [            "Text Files (*.txt *.ini *.log *.c *.h *.java *.cpp *.html *.xml)",            "Image Files (*.jpg *.png *.gif *.bmp *.ico)",            "Video Files (*.ts *.mp4 *.avi *.flv *.mkv *.3gp)",            "Audio Files (*.mp3 *.ogg *.wav *.wma *.ape *.ra)",            "*.*"        ];        onAccepted: {            var filepath = new String(fileUrl);            //remove file:///            if(Qt.platform.os == "windows"){                root.statusBar.text = filepath.slice(8);            }else{                root.statusBar.text = filepath.slice(7);            }            var dot = filepath.lastIndexOf(".");            var sep = filepath.lastIndexOf("/");            if(dot > sep){                var ext = filepath.substring(dot);                root.processFile(fileUrl, ext.toLowerCase());            }else{                root.statusBar.text = "Not Supported!";            }        }    }}

    C++代码

    其实,我只对模板生成的C++代码改动了三行,一行include,一行QApplication,一行设置应用图标。main.cpp如下:

#include <QApplication>#include <QQmlApplicationEngine>#include <QIcon>int main(int argc, char *argv[]){    QApplication app(argc, argv);    app.setWindowIcon(QIcon(":/res/eye.png"));    QQmlApplicationEngine engine;    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));    return app.exec();}

PRO文件

    有人喊,兄弟,这也要贴!你凑字数呢……你管,就放这里了:

TEMPLATE = appQT += qml quick network multimedia widgetsSOURCES += main.cppRESOURCES += qml.qrc# Additional import path used to resolve QML modules in Qt Creator's code modelQML_IMPORT_PATH =# Default rules for deployment.include(deployment.pri)HEADERS +=


    版权所有foruok,转载请注明出处:http://blog.csdn.net/foruok

    好啦,彪悍的人生不需要解释,都扒光了,你自己仔细看吧。

    回顾一下我的Qt Quick系列文章:



           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页