• <menu id="w2i4a"></menu>
  • logo Qt使用教程

    文檔首頁(yè)>>Qt使用教程>>如何在Visual Studio中創(chuàng)建嵌入式Qt quick應(yīng)用程序(下)

    如何在Visual Studio中創(chuàng)建嵌入式Qt quick應(yīng)用程序(下)


    Qt是一個(gè)跨平臺(tái)框架,通常用作圖形工具包,它不僅創(chuàng)建CLI應(yīng)用程序中非常有用。而且它也可以在三種主要的臺(tái)式機(jī)操作系統(tǒng)以及移動(dòng)操作系統(tǒng)(如Symbian,Nokia Belle,Meego Harmattan,MeeGo或BB10)以及嵌入式設(shè)備,Android(Necessitas)和iOS的端口上運(yùn)行。現(xiàn)在我們?yōu)槟闾峁┝嗣赓M(fèi)的試用版。趕快點(diǎn)擊下載Qt最新試用版>>

    點(diǎn)擊獲取更多文章教程

    Qtitan組件集

    • QtitanRibbon| 下載試用: 遵循Microsoft Ribbon UI Paradigm for Qt技術(shù)的Ribbon UI組件,致力于為Windows、Linux和Mac OS X提供功能完整的Ribbon組件。
    • QtitanChart | 下載試用 : 是一個(gè)C ++庫(kù),代表一組控件,這些控件使您可以快速地為應(yīng)用程序提供漂亮而豐富的圖表。并且支持所有主要的桌面操作系統(tǒng)。
    • QtitanDataGrid| 下載試用 : 適用于Qt的商業(yè)化DataGrid 組件,使得表格數(shù)據(jù)可以直接面向終端用戶完全集成了QtDesigner,極易適應(yīng)其他相似開(kāi)發(fā)環(huán)境,保證100%兼容Qt GUI。

    在本文的第1部分中,我們展示了如何在Visual Studio中針對(duì)Windows和嵌入式Linux創(chuàng)建多平臺(tái)Qt Quick應(yīng)用程序項(xiàng)目?,F(xiàn)在,我們將展示如何在嵌入式設(shè)備上運(yùn)行該應(yīng)用程序。然后,我們將繼續(xù)將該項(xiàng)目開(kāi)發(fā)為我們打算創(chuàng)建的完整嵌入式應(yīng)用程序。最后,我們將使用VS調(diào)試器對(duì)應(yīng)用程序的C ++和QML代碼進(jìn)行遠(yuǎn)程調(diào)試。

    在嵌入式設(shè)備上運(yùn)行

    我們已經(jīng)展示了如何交叉編譯在Visual Studio中創(chuàng)建的“ hello world” Qt Quick應(yīng)用程序?,F(xiàn)在,我們將看到如何在Raspberry Pi上運(yùn)行該應(yīng)用程序。由于我們將以全屏模式運(yùn)行,因此我們必須首先向應(yīng)用程序窗口中添加一些內(nèi)容。

    Window {
        visible: true
        title: qsTr("Hello World")
        Text {
            id: clock
            font.pointSize: 72
            Timer {
                interval: 1000; running: true; repeat: true
                onTriggered: clock.text = (new Date).toLocaleTimeString(Qt.locale("de_DE"), "hh:mm:ss");
            }
        }
    }

    和以前一樣,選擇Linux項(xiàng)目配置,然后按F7鍵開(kāi)始交叉編譯。

    1>------ Build started: Project: QuickMirror, Configuration: Debug_RPi x64 ------
    1>rcc qml.qrc
    1>Invoking 'mkdir -p $(dirname qml.qrc); mkdir -p $(dirname /mnt/c/Users/user/Source/Repos/QuickMirror/main.qml); mkdir -p $(dirname /mnt/c/Users/user/Source/Repos/QuickMirror/obj/x64/Debug_RPi/rcc/qrc_qml.cpp); (/home/user/raspi/qt5/bin/rcc /mnt/c/Users/user/Source/Repos/QuickMirror/qml.qrc --name qml -o /mnt/c/Users/user/Source/Repos/QuickMirror/obj/x64/Debug_RPi/rcc/qrc_qml.cpp)', working directory: '/mnt/c/Users/user/Source/Repos/QuickMirror'
    1>Starting remote build
    1>Compiling sources:
    1>qrc_qml.cpp
    1>Linking objects
    1>QuickMirror.vcxproj -> C:\Users\user\Source\Repos\QuickMirror\bin\x64\Debug_RPi\QuickMirror.out
    ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

    要在每次構(gòu)建結(jié)束時(shí)自動(dòng)復(fù)制應(yīng)用程序文件,可以在“ WSL構(gòu)建后事件”屬性頁(yè)中設(shè)置以下命令(ATTN: 這將以明文形式保存設(shè)備密碼)。

    C:\Users\user> scp C:\Users\user\Source\Repos\QuickMirror\bin\x64\Debug_RPi\QuickMirror.out pi@192.168.1.98:/home/pi/
    
    pi@192.168.1.98's password:
    
    QuickMirror.out 100% 465KB 1.6MB/s 00:00
    
    C:\Users\user>

    在啟動(dòng)Qt Quick應(yīng)用程序之前,我們需要設(shè)置一些必需的環(huán)境變量:

    • LD_LIBRARY_PATH
      Qt二進(jìn)制文件安裝目錄的路徑。

    • QT_QPA_PLATFORM
      平臺(tái)插件。

    • QT_QPA_PLATFORM_PLUGIN_PATH
      平臺(tái)插件安裝目錄的路徑。

    • QT_QPA_EGLFS_PHYSICAL_WIDTH
      QT_QPA_EGLFS_PHYSICAL_HEIGHT

      物理屏幕的寬度和高度,以毫米為單位。

    • QML2_IMPORT_PATH
      安裝的QML模塊的路徑。
    pi@raspberry-pi:~$ export LD_LIBRARY_PATH="/usr/local/qt5pi/lib"
    pi@raspberry-pi:~$ export QT_QPA_PLATFORM="eglfs"
    pi@raspberry-pi:~$ export QT_QPA_PLATFORM_PLUGIN_PATH="/usr/local/qt5pi/plugins/platforms"
    pi@raspberry-pi:~$ export QT_QPA_EGLFS_PHYSICAL_WIDTH="326"
    pi@raspberry-pi:~$ export QT_QPA_EGLFS_PHYSICAL_HEIGHT="520"
    pi@raspberry-pi:~$ export QML2_IMPORT_PATH="/usr/local/qt5pi/qml"
    pi@raspberry-pi:~$ ./QuickMirror.out

    樹(shù)莓派顯示器

    在Raspberry Pi中運(yùn)行“ Hello World”應(yīng)用程序

    開(kāi)發(fā)應(yīng)用程序

    我們的應(yīng)用程序的要求包括顯示以下信息:

    • 當(dāng)前時(shí)間
    • 當(dāng)前日期
    • 周年紀(jì)念
    • 天氣預(yù)報(bào)
    • 下次出發(fā)的公共交通工具
    • 新聞

    我們將把每個(gè)項(xiàng)目封裝為專用的QML類型。為此,我們必須首先將QML模塊定義(qmldir)文件添加到項(xiàng)目中:

    • 選擇“項(xiàng)目>添加新項(xiàng)。。> Qt> QML模塊定義”。
    • 在位置字段中,指示將包含QML文件的文件夾的路徑。

    向項(xiàng)目添加新的QML模塊定義

    按下“添加”后,qmldir 文件將在項(xiàng)目樹(shù)中可用。我們將使用此文件來(lái)定義每種QML類型到其對(duì)應(yīng)源文件的映射。

    ApiCall         1.0 QuickMirror.ApiCall.qml
    Calendar        1.0 QuickMirror.Calendar.qml
    Clock           1.0 QuickMirror.Clock.qml
    NewsTicker      1.0 QuickMirror.NewsTicker.qml
    OnThisDay       1.0 QuickMirror.OnThisDay.qml
    PublicTransport 1.0 QuickMirror.PublicTransport.qml
    Weather         1.0 QuickMirror.Weather.qml

    要將新的QML源文件添加到項(xiàng)目中:

    • 選擇“項(xiàng)目>添加新項(xiàng)...> Qt> QML文件”。
    • 將位置設(shè)置qmldir為創(chuàng)建文件的相同目錄。
    • 設(shè)置QML文件名。
    • 按“添加”。


    我們將首先添加QML類型以顯示當(dāng)前時(shí)間,當(dāng)前日期和重要的周年紀(jì)念日。該Clock類型將顯示當(dāng)前時(shí)間,每秒刷新一次。

    QuickMirror.Clock.qml

      function refresh() {
            text = (new Date).toLocaleTimeString(Qt.locale("de_DE"), "hh:mm");
        }
        Component.onCompleted : refresh();
        Timer {
            interval: 1000; running: true; repeat: true onTriggered: parent.refresh();
        }
    } 

    Calendar類型將顯示當(dāng)前日期,并在不同語(yǔ)言環(huán)境之間循環(huán)。

    QuickMirror.Calendar.qml

    Text {
        renderType: Text.NativeRendering
        id: calendar
        color: "white"
        font.family: FontFamily_Bold
        font.styleName: FontStyle_Bold
        font.pointSize: 72
        property var locales: ["en_US", "de_DE", "pt_PT"]
        property var localeIdx: 0
        function capitalize(s) {
            return s.replace(/(^|-)./g, function(c) { return c.toUpperCase(); });
        }
        function setNextLocale() {
            localeIdx = (localeIdx + 1) % locales.length;
        }
        function getCurrentText() {
            var date = new Date;
            var locale = Qt.locale(locales[localeIdx]);
            var calendarText = capitalize(date.toLocaleDateString(locale, "dddd, dd"));
            var monthShort = date.toLocaleDateString(locale, "MMM");
            var monthLong = date.toLocaleDateString(locale, "MMMM");
            if (monthLong.length <= 5) { calendarText += capitalize(monthLong); } else { calendarText += capitalize(monthShort); if (!monthShort.endsWith(".")) calendarText += "."; } calendarText += date.toLocaleDateString(locale, " yyyy"); return calendarText; } Component.onCompleted: { text = getCurrentText(); } Timer { interval: 15000; running: true; repeat: true onTriggered: { setNextLocale(); text = getCurrentText(); } } Behavior on text { SequentialAnimation { NumberAnimation { target: calendar; property: "opacity"; to: 0.0; duration: 1000 } PropertyAction { target: calendar; property: "text" } NumberAnimation { target: calendar; property: "opacity"; to: 1.0; duration: 500 } } } }

    除了日期/時(shí)間,我們的應(yīng)用程序還將依靠Web API來(lái)檢索信息。我們將curl在一個(gè)單獨(dú)的過(guò)程中運(yùn)行以連接到Web API。流程創(chuàng)建將由名為的C ++類處理Process。然后,QML類型ApiCall將使用一個(gè)Process對(duì)象以curl必要的參數(shù)開(kāi)始并收集其輸出。

    QuickMirror.ApiCall.qml

    Item {
        property var url: ""
        property var path: []
        property var query: []
        signal response(var response)
        signal error(var error)
        Process {
            id: curl
            property var path: Q_OS_WIN ? "C:\\Windows\\System32\\curl.exe" : "/usr/bin/curl"
            property var request: ""
            command: path + " -s \"" + request + "\""
        }
        function sendRequest() {
            curl.request = url;
            if (path.length > 0)
                curl.request += "/" + path.join("/");
             if (query.length > 0)
                curl.request += "?" + query.join("&");
            curl.start();
        }
        Connections {
            target: curl
            onExit /*(int exitCode, QByteArray processOutput)*/ : {
                if (exitCode != 0) {
                    console.log("ApiCall: exit " + exitCode);
                    console.log("==== ApiCall: request: " + curl.request);
                    return error("exit " + exitCode);
                }
                try {
                    return response(JSON.parse(processOutput));
                } catch (err) {
                    console.log("ApiCall: error: " + err.toString());
                    console.log("==== ApiCall: request: " + curl.request);
                    console.log("==== ApiCall: response: " + processOutput);
                    return error(err);
                }
            }
        }
    }

    要?jiǎng)?chuàng)建ProcessC ++類:

    • 選擇“項(xiàng)目>添加Qt類> Qt類”
    • 將類名設(shè)置為Process
    • 按“添加”


    class Process : public QProcess
    {
        Q_OBJECT
        Q_PROPERTY(QString command READ command WRITE setCommand NOTIFY commandChanged)
    
    public:
        Process(QObject* parent = 0);
        ~Process();
    
    public:
        Q_INVOKABLE void start();
        void setCommand(const QString& cmd);
        QString command() const;
    
    signals:
        void commandChanged();
        void exit(int exitCode, QByteArray processOutput);
    
    protected:
        void onFinished(int exitCode, QProcess::ExitStatus status);
        void onErrorOccurred(QProcess::ProcessError error);
    
    private:
        QString m_command;
    };

    Process.cpp

    Process(QObject* parent) : QProcess(parent)
    {
        connect(
            this, QOverload::of(&QProcess::finished),
            this, &Process::onFinished);
        connect(
            this, &QProcess::errorOccurred,
            this, &Process::onErrorOccurred);
    }
    
    Process::~Process()
    {
    }
    
    void Process::setCommand(const QString& cmd)
    {
        if (cmd != m_command) {
            m_command = cmd;
            emit commandChanged();
        }
    }
    
    QString Process::command() const
    {
        return m_command;
    }
    
    void Process::start()
    {
        if (state() == ProcessState::NotRunning)
            QProcess::start(m_command);
        else
            qInfo() << "==== QProcess: ERROR already running:" << m_command; } void Process::onFinished(int exitCode, QProcess::ExitStatus status) { emit exit((status == ExitStatus::NormalExit) ? exitCode : -1, readAll()); } void Process::onErrorOccurred(QProcess::ProcessError error) { qInfo() << "==== QProcess: ERROR " << error; }

    main.cpp

    int main(int argc, char* argv[])
    {
        qmlRegisterType("Process", 1, 0, "Process");
    ...

    OnThisDay QML類型將使用的實(shí)例,通過(guò)它們來(lái)獲取顯著的紀(jì)念日列表和循環(huán)每隔幾秒鐘。 ApiCall

    QuickMirror.OnThisDay.qml

    Item {
        id: onThisDay
        clip: true
        property int viewportHeight
        property var events: []
        property var births: []
        property var deaths: []
        property int idxEventType: -1
        ApiCall {
            id: onThisDayApi
            property int month: 0
            property int day: 0
            property string eventType: ""
            url: "https://byabbe.se"; path: ["on-this-day", month, day, eventType + ".json" ]
            onResponse: {
                if ("events" in response) {
                    events = shuffle(response.events);
                    eventType = "births";
                    sendRequest();
                } else if ("births" in response) {
                    births = shuffle(response.births);
                    for (var i in births)
                        births[i].year = "*" + births[i].year;
                    eventType = "deaths";
                    sendRequest();
                } else if ("deaths" in response) {
                    deaths = shuffle(response.deaths);
                    for (var i in deaths)
                        deaths[i].year = "?" + deaths[i].year;
                    next();
                }
            }
        }
        function init() {
            events = [];
            births = [];
            deaths = [];
            idxEventType = -1;
            var today = new Date;
            onThisDayApi.month = today.getMonth() + 1;
            onThisDayApi.day = today.getDate();
            onThisDayApi.eventType = "events";
            onThisDayApi.sendRequest();
        }
        function next() {
            if (events.length + births.length + deaths.length == 0)
                return;
            var today = new Date;
            if (onThisDayApi.month != today.getMonth() + 1 || onThisDayApi.day != today.getDate())
                return init();
            onThisDayText.color = "white";
            idxEventType = (idxEventType + 1) % 3;
            var event;
            switch (idxEventType) {
                case 0:
                    if (events.length == 0)
                        return next();
                    event = events.shift();
                    events = shuffle(events);
                    events.push(event);
                    break;
                case 1:
                    if (births.length == 0)
                        return next();
                    event = births.shift();
                    births = shuffle(births);
                    births.push(event);
                    break;
                case 2:
                    if (deaths.length == 0)
                        return next();
                    event = deaths.shift();
                    deaths = shuffle(deaths);
                    deaths.push(event);
                    break;
            }
            onThisDayText.text = event.year + " – " + event.description;
            showText.start();
        }
        Component.onCompleted: {
            init();
        }
        Timer {
            id: timerRetry
            interval: 10000; running: true; repeat: true
            onTriggered: {
                if (events.length + births.length + deaths.length == 0)
                    init();
            }
        }
        SequentialAnimation {
            id: showText
            PropertyAction { target: onThisDayText; property: "y"; value: 25 }
            NumberAnimation { target: onThisDayText; property: "opacity"; to: 1.0; duration: 500 }
            PauseAnimation { duration: 3000 }
            NumberAnimation {
                target: onThisDayText
                property: "y"
                to: Math.min(-(25 + onThisDayText.contentHeight) + viewportHeight, 25)
                duration: Math.max(0, (Math.abs(to - from) * 1000) / 25)
            }
            PauseAnimation { duration: 3000 }
            NumberAnimation { target: onThisDayText; property: "opacity"; to: 0.0; duration: 1000 }
            onFinished: {
                onThisDay.next();
            }
        }
        Text {
            renderType: Text.NativeRendering
            id: onThisDayText
            wrapMode: Text.WordWrap
            font.family: FontFamily_Normal
            font.styleName: FontStyle_Normal
            font.pointSize: 40
            textFormat: Text.RichText
            color: "white"
            y: 25
            anchors.left: parent.left
            width: parent.width
            height: contentHeight
            opacity: 0
        }
        Rectangle {
            id: top
            anchors.top: parent.top
            anchors.left: parent.left
            width: parent.width
            height: 10
            gradient: Gradient {
                orientation: Gradient.Vertical
                GradientStop { position: 0.0; color: "black" }
                GradientStop { position: 0.5; color: "transparent" }
            }
        }
        Rectangle {
            id: bottomFade
            anchors.top: parent.top
            anchors.topMargin: viewportHeight
            anchors.left: parent.left
            width: parent.width
            height: 0.1 * viewportHeight
            gradient: Gradient {
                orientation: Gradient.Vertical
                GradientStop { position: 0.0; color: "transparent" }
                GradientStop { position: 0.5; color: "black" }
            }
        }
        Rectangle {
            anchors.top: bottomFade.bottom
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            width: parent.width
            color: "black"
        }
    }

    現(xiàn)在,我們已經(jīng)定義了一些應(yīng)用程序的QML類型,我們將它們排列在主QML文件上。

    main.qml

    import "QuickMirrorTypes"
    
    Window {
        visible: true
        title: qsTr("Quick Mirror")
        Flickable {
            anchors.fill: parent
            contentWidth: mirror.width
            contentHeight: mirror.height
            Rectangle {
                id: mirror
                width: 1080
                height: 1920
                color: "black"
    
                Clock {
                    id: clock
                    anchors.top: mirror.top
                    anchors.left: mirror.left
                }
    
                Calendar {
                    id: calendar
                    anchors.top: clock.bottom
                    anchors.topMargin: -20
                    anchors.left: mirror.left
                }
    
                Rectangle {
                    anchors.top: calendar.bottom
                    anchors.topMargin: -5
                    anchors.left: mirror.left
                    width: 800
                    height: 2
                    color: "white"
                }
    
                OnThisDay {
                    id: onThisDay
                    anchors.top: calendar.bottom
                    anchors.left: mirror.left
                    anchors.leftMargin: 10
                    anchors.bottom: mirror.bottom
                    width: 780
                    viewportHeight: 260
                }
            }
        }
    }

    最后,qmldir 必須將QML文件和該文件全部添加到應(yīng)用程序的資源文件中:

    • 雙擊項(xiàng)目樹(shù)中的QRC文件
    • 在“ Qt資源編輯器”窗口中,按“添加>添加文件”
    • 選擇所有QML文件和qmldir文件
    • 在Qt資源編輯器中按“保存”

    構(gòu)建和部署后,我們將能夠啟動(dòng)應(yīng)用程序并查看顯示的信息。

    樹(shù)莓派顯示器

    在Raspberry Pi上運(yùn)行的應(yīng)用程序

    在Visual Studio中進(jìn)行調(diào)試

    VS支持通過(guò)調(diào)試在WSL上運(yùn)行的應(yīng)用程序gdb。要在Raspberry Pi上運(yùn)行時(shí)進(jìn)行調(diào)試,我們將使用啟動(dòng)應(yīng)用程序gdbserver,然后配置gdb為連接到設(shè)備并啟動(dòng)遠(yuǎn)程調(diào)試會(huì)話。

    為此,gdb WSL中安裝的組件必須支持目標(biāo)設(shè)備體系結(jié)構(gòu)。一種簡(jiǎn)單的方法是安裝gdb-multiarch。為了確保VS使用正確的調(diào)試器,我們將創(chuàng)建從gdb到的符號(hào)鏈接gdb-multiarch。

    WSL命令外殼

    user@buildhost:~$ sudo apt-get install gdb-multiarch
    ...
    user@buildhost:~$ cd /usr/bin
    user@buildhost:/usr/bin$ sudo mv gdb gdb-bkup
    user@buildhost:/usr/bin$ sudo ln -s gdb-multiarch gdb
    user@buildhost:/usr/bin$ ls -go gdb*
    lrwxrwxrwx 1 13 Sep 2 11:31 gdb -> gdb-multiarch
    -rwxr-xr-x 1 8440200 Feb 11 2020 gdb-bkup
    -rwxr-xr-x 1 15192808 Feb 11 2020 gdb-multiarch
    user@buildhost:/usr/bin$

    要在Visual Studio中設(shè)置遠(yuǎn)程調(diào)試會(huì)話,必須將兩個(gè)附加命令傳遞給gdb。這是在“ GDB調(diào)試器”屬性頁(yè)面中配置的。

    Project Properties > Debugging > Additional Debugger Commands

    target extended-remote 192.168.1.98:2345
    set remote exec-file /home/pi/QuickMirror.out

    在開(kāi)始遠(yuǎn)程調(diào)試會(huì)話之前,我們必須設(shè)置所需的環(huán)境變量并gdbserver在設(shè)備上啟動(dòng)。

    Raspberry Pi命令外殼

    pi@raspberry-pi:~$ export LD_LIBRARY_PATH="/usr/local/qt5pi/lib"
    pi@raspberry-pi:~$ export QT_QPA_PLATFORM="eglfs"
    pi@raspberry-pi:~$ export QT_QPA_PLATFORM_PLUGIN_PATH="/usr/local/qt5pi/plugins/platforms"
    pi@raspberry-pi:~$ export QT_QPA_EGLFS_PHYSICAL_WIDTH="326"
    pi@raspberry-pi:~$ export QT_QPA_EGLFS_PHYSICAL_HEIGHT="520"
    pi@raspberry-pi:~$ export QML2_IMPORT_PATH="/usr/local/qt5pi/qml"
    pi@raspberry-pi:~$ gdbserver --once --multi :2345
    Listening on port 2345

    按F5將啟動(dòng)遠(yuǎn)程調(diào)試會(huì)話。

    在遠(yuǎn)程調(diào)試期間在C ++代碼中的斷點(diǎn)處停止

    遠(yuǎn)程QML調(diào)試

    在嵌入式設(shè)備上運(yùn)行應(yīng)用程序時(shí),也可以調(diào)試QML代碼。

    • 在Qt設(shè)置中啟用QML調(diào)試:項(xiàng)目屬性> Qt項(xiàng)目設(shè)置
    • 用于啟動(dòng)QML調(diào)試會(huì)話的安裝程序參數(shù)

    項(xiàng)目屬性>調(diào)試>程序參數(shù)

    -qmljsdebugger=port:8989,host:192.168.1.98,block
    


    打包

    我們已經(jīng)展示了如何使用Qt VS Tools擴(kuò)展在帶有Qt Quick的Visual Studio中創(chuàng)建多平臺(tái)嵌入式應(yīng)用程序。這包括:

    • 從頭開(kāi)始創(chuàng)建Qt Quick項(xiàng)目
    • 用QML編寫(xiě)應(yīng)用程序代碼
    • 交叉編譯應(yīng)用程序
    • 在嵌入式設(shè)備上部署和運(yùn)行
    • 在Visual Studio中對(duì)C ++和QML代碼進(jìn)行遠(yuǎn)程調(diào)試

    該項(xiàng)目,包括所有源代碼,可從以下網(wǎng)址獲得:https : //github.com/micosta/quickmirror。

    我們的應(yīng)用程序在嵌入式設(shè)備上運(yùn)行


    應(yīng)用程序在嵌入式設(shè)備上運(yùn)行

    感謝您的閱讀和對(duì)Qt和VS Tools擴(kuò)展的關(guān)注。如果您有任何疑問(wèn)或建議,請(qǐng)?jiān)谙旅姘l(fā)表評(píng)論。

    如果這篇文章沒(méi)能滿足你的需求、點(diǎn)擊獲取更多文章教程!現(xiàn)在立刻下載Qt免費(fèi)試用吧!更多Qt類開(kāi)發(fā)工具QtitanRibbon、QtitanChartQtitanNavigation、QtitanDocking、QtitanDataGrid在線訂購(gòu)現(xiàn)直降1000元,歡迎咨詢慧都在線客服獲取更多優(yōu)惠>>

    掃碼咨詢


    添加微信 立即咨詢

    電話咨詢

    客服熱線
    023-68661681

    TOP
    三级成人熟女影院,欧美午夜成人精品视频,亚洲国产成人乱色在线观看,色中色成人论坛 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();