這一節貫穿了可能使用Qt5編寫的不一樣類型的應用程序。沒有任何建議的選擇,只是想告訴讀者Qt5一般狀況下能作些什麼。前端
一個控制檯應用程序不須要提供任何人機交互圖形界面一般被稱做系統服務,或者經過命令行來運行。Qt5附帶了一系列現成的組件來幫助你很是有效的建立跨平臺的控制檯應用程序。例如網絡應用程序編程接口或者文件應用程序編程接口,字符串的處理,自Qt5.1發佈的高效的命令解析器。因爲Qt是基於C++的高級應用程序接口,你可以快速的編程而且程序擁有快速的執行速度。不要認爲Qt僅僅只是用戶界面工具,它也提供了許多其它的功能。c++
字符串處理算法
在第一個例子中咱們展現了怎樣簡單的增長兩個字符串常量。這不是一個有用的應用程序,但能讓你瞭解本地端C++應用程序沒有事件循環時是什麼樣的。sql
// module or class includes #include <QtCore> // text stream is text-codec aware QTextStream cout(stdout, QIODevice::WriteOnly); int main(int argc, char** argv) { // avoid compiler warnings Q_UNUSED(argc) Q_UNUSED(argv) QString s1("Paris"); QString s2("London"); // string concatenation QString s = s1 + " " + s2 + "!"; cout << s << endl; }
容器類數據庫
這個例子在應用程序中增長了一個鏈表和一個鏈表迭代器。Qt自帶大量方便使用的容器類,而且其中的元素使用相同的應用程序接口模式。編程
QString s1("Hello"); QString s2("Qt"); QList<QString> list; // stream into containers list << s1 << s2; // Java and STL like iterators QListIterator<QString> iter(list); while(iter.hasNext()) { cout << iter.next(); if(iter.hasNext()) { cout << " "; } } cout << "!" << endl;
這裏咱們展現了一些高級的鏈表函數,容許你在一個字符串中加入一個鏈表的字符串。當你須要持續的文本輸入時很是的方便。使用QString::split()函數能夠將這個操做逆向(將字符串轉換爲字符串鏈表)。後端
QString s1("Hello"); QString s2("Qt"); // convenient container classes QStringList list; list << s1 << s2; // join strings QString s = list.join(" ") + "!"; cout << s << endl;
文件IO網絡
下一個代碼片斷咱們從本地讀取了一個CSV文件而且遍歷提取每一行的每個單元的數據。咱們從CSV文件中獲取大約20行的編碼。文件讀取僅僅給了咱們一個比特流,爲了有效的將它轉換爲可使用的Unicode文本,咱們須要使用這個文件做爲文本流的底層流數據。編寫CSV文件,你只須要以寫入的方式打開一個文件而且一行一行的輸入到文件流中。app
QList<QStringList> data; // file operations QFile file("sample.csv"); if(file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); // loop forever macro forever { QString line = stream.readLine(); // test for empty string 'QString("")' if(line.isEmpty()) { continue; } // test for null string 'String()' if(line.isNull()) { break; } QStringList row; // for each loop to iterate over containers foreach(const QString& cell, line.split(",")) { row.append(cell.trimmed()); } data.append(row); } } // No cleanup necessary.
如今咱們結束Qt關於基於控制檯應用程序小節。dom
基於控制檯的應用程序很是方便,可是有時候你須要有一些用戶界面。可是基於用戶界面的應用程序須要後端來寫入/讀取文件,使用網絡進行通信或者保存數據到一個容器中。
在第一個基於窗口的應用程序代碼片斷,咱們僅僅只建立了一個窗口並顯示它。沒有父對象的窗口部件是Qt世界中的一個窗口。咱們使用智能指針來確保當智能指針指向範圍外時窗口會被刪除掉。
這個應用程序對象封裝了Qt的運行,調用exec開始咱們的事件循環。從這裏開始咱們的應用程序只響應由鼠標或者鍵盤或者其它的例如網絡或者文件IO的事件觸發。應用程序也只有在事件循環退出時才退出,在應用程序中調用"quit()"或者關掉窗口來退出。
當你運行這段代碼的時候你能夠看到一個240乘以120像素的窗口。
#include <QtGui> int main(int argc, char** argv) { QApplication app(argc, argv); QScopedPointer<QWidget> widget(new CustomWidget()); widget->resize(240, 120); widget->show(); return app.exec(); }
自定義窗口部件
當你使用用戶界面時你須要建立一個自定義的窗口部件。典型的窗口是一個窗口部件區域的繪製調用。附加一些窗口部件內部如何處理外部觸發的鍵盤或者鼠標輸入。爲此咱們須要繼承QWidget而且重寫幾個函數來繪製和處理事件。
#ifndef CUSTOMWIDGET_H #define CUSTOMWIDGET_H #include <QtWidgets> class CustomWidget : public QWidget { Q_OBJECT public: explicit CustomWidget(QWidget *parent = 0); void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); private: QPoint m_lastPos; }; #endif // CUSTOMWIDGET_H
在實現中咱們繪製了窗口的邊界並在鼠標最後的位置上繪製了一個小的矩形框。這是一個很是典型的低層次的自定義窗口部件。鼠標或者鍵盤事件會改變窗口的內部狀態並觸發從新繪製。咱們不須要更加詳細的分析這個代碼,你應該有能力分析它。Qt自帶了大量現成的桌面窗口部件,你有很大的概率不須要再作這些工做。
#include "customwidget.h" CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent) { } void CustomWidget::paintEvent(QPaintEvent *) { QPainter painter(this); QRect r1 = rect().adjusted(10,10,-10,-10); painter.setPen(QColor("#33B5E5")); painter.drawRect(r1); QRect r2(QPoint(0,0),QSize(40,40)); if(m_lastPos.isNull()) { r2.moveCenter(r1.center()); } else { r2.moveCenter(m_lastPos); } painter.fillRect(r2, QColor("#FFBB33")); } void CustomWidget::mousePressEvent(QMouseEvent *event) { m_lastPos = event->pos(); update(); } void CustomWidget::mouseMoveEvent(QMouseEvent *event) { m_lastPos = event->pos(); update(); }
桌面窗口
Qt的開發者們已經爲你作好大量現成的桌面窗口部件,在不一樣的操做系統中他們看起來都像是本地的窗口部件。你的工做只須要在一個打的窗口容器中安排不一樣的的窗口部件。在Qt中一個窗口部件可以包含其它的窗口部件。這個操做由分配父子關係來完成。這意味着咱們須要準備相似按鈕(button),複選框(check box),單選按鈕(radio button)的窗口部件而且對它們進行佈局。下面展現了一種完成的方法。
這裏有一個頭文件就是所謂的窗口部件容器。
class CustomWidget : public QWidget { Q_OBJECT public: explicit CustomWidgetQWidget *parent = 0); private slots: void itemClicked(QListWidgetItem* item); void updateItem(); private: QListWidget *m_widget; QLineEdit *m_edit; QPushButton *m_button; };
在實現中咱們使用佈局來更好的安排咱們的窗口部件。當容器窗口部件大小被改變後它會按照窗口部件的大小策略進行從新佈局。在這個例子中咱們有一個鏈表窗口部件,行編輯器與按鈕垂直排列來編輯一個城市的鏈表。咱們使用Qt的信號與槽來鏈接發送和接收對象。
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); m_widget = new QListWidget(this); layout->addWidget(m_widget); m_edit = new QLineEdit(this); layout->addWidget(m_edit); m_button = new QPushButton("Quit", this); layout->addWidget(m_button); setLayout(layout); QStringList cities; cities << "Paris" << "London" << "Munich"; foreach(const QString& city, cities) { m_widget->addItem(city); } connect(m_widget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemClicked(QListWidgetItem*))); connect(m_edit, SIGNAL(editingFinished()), this, SLOT(updateItem())); connect(m_button, SIGNAL(clicked()), qApp, SLOT(quit())); } void CustomWidget::itemClicked(QListWidgetItem *item) { Q_ASSERT(item); m_edit->setText(item->text()); } void CustomWidget::updateItem() { QListWidgetItem* item = m_widget->currentItem(); if(item) { item->setText(m_edit->text()); } }
繪製圖形
有一些問題最好用可視化的方式表達。若是手邊的問題看起來有點像幾何對象,qt graphics view是一個很好的選擇。一個圖形視窗(graphics view)可以在一個場景(scene)排列簡單的幾何圖形。用戶能夠與這些圖形交互,它們使用必定的算法放置在場景(scene)上。填充一個圖形視圖你須要一個圖形窗口(graphics view)和一個圖形場景(graphics scene)。一個圖形場景(scene)鏈接在一個圖形窗口(view)上,圖形對象(graphics item)是被放在圖形場景(scene)上的。這裏有一個簡單的例子,首先頭文件定義了圖形窗口(view)與圖形場景(scene)。
class CustomWidgetV2 : public QWidget { Q_OBJECT public: explicit CustomWidgetV2(QWidget *parent = 0); private: QGraphicsView *m_view; QGraphicsScene *m_scene; };
在實現中首先將圖形場景(scene)與圖形窗口(view)鏈接。圖形窗口(view)是一個窗口部件,可以被咱們的窗口部件容器包含。最後咱們添加一個小的矩形框在圖形場景(scene)中。而後它會被渲染到咱們的圖形窗口(view)上。
#include "customwidgetv2.h" CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent) { m_view = new QGraphicsView(this); m_scene = new QGraphicsScene(this); m_view->setScene(m_scene); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); layout->addWidget(m_view); setLayout(layout); QGraphicsItem* rect1 = m_scene->addRect(0,0, 40, 40, Qt::NoPen, QColor("#FFBB33")); rect1->setFlags(QGraphicsItem::ItemIsFocusable|QGraphicsItem::ItemIsMovable); }
到如今咱們已經知道了大多數的基本數據類型,而且知道如何使用窗口部件和圖形視圖(graphics views)。一般在應用程序中你須要處理大量的結構體數據,也可能須要不停的儲存它們,或者這些數據須要被用來顯示。對於這些Qt使用了模型的概念。下面一個簡單的模型是字符串鏈表模型,它被一大堆字符串填滿而後與一個鏈表視圖(list view)鏈接。
m_view = new QListView(this); m_model = new QStringListModel(this); view->setModel(m_model); QList<QString> cities; cities << "Munich" << "Paris" << "London"; model->setStringList(cities);
另外一個比較廣泛的用法是使用SQL(結構化數據查詢語言)來存儲和讀取數據。Qt自身附帶了嵌入式版的SQLLite而且也支持其它的數據引擎(好比MySQL,PostgresSQL,等等)。首先你須要使用一個模式來建立你的數據庫,好比像這樣:
CREATE TABLE city (name TEXT, country TEXT); INSERT INTO city value ("Munich", "Germany"); INSERT INTO city value ("Paris", "France"); INSERT INTO city value ("London", "United Kingdom");
爲了可以在使用sql,咱們須要在咱們的項目文件(*.pro)中加入sql模塊。
QT += sql
而後咱們須要c++來打開咱們的數據庫。首先咱們須要獲取一個指定的數據庫引擎的數據對象。使用這個數據庫對象咱們能夠打開數據庫。對於SQLLite這樣的數據庫咱們能夠指定一個數據庫文件的路徑。Qt提供了一些高級的數據庫模型,其中有一種表格模型(table model)使用表格標示符和一個選項分支語句(where clause)來選擇數據。這個模型的結果可以與一個鏈表視圖鏈接,就像以前鏈接其它數據模型同樣。
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName('cities.db'); if(!db.open()) { qFatal("unable to open database"); } m_model = QSqlTableModel(this); m_model->setTable("city"); m_model->setHeaderData(0, Qt::Horizontal, "City"); m_model->setHeaderData(1, Qt::Horizontal, "Country"); view->setModel(m_model); m_model->select();
對高級的模型操做,Qt提供了一種分類文件代理模型,容許你使用基礎的分類排序和數據過濾來操做其它的模型。
QSortFilterProxyModel* proxy = new QSortFilterProxyModel(this); proxy->setSourceModel(m_model); view->setModel(proxy); view->setSortingEnabled(true);
數據過濾基於列號與一個字符串參數完成。
proxy->setFilterKeyColumn(0); proxy->setFilterCaseSensitive(Qt::CaseInsensitive); proxy->setFilterFixedString(QString)
過濾代理模型比這裏演示的要強大的多,如今咱們只須要知道有它的存在就夠了。
注意
這裏是綜述了你能夠在Qt5中開發的不一樣類型的經典應用程序。桌面應用程序正在發生着改變,不久以後移動設備將會爲佔據咱們的世界。移動設備的用戶界面設計很是不一樣。它們相對於桌面應用程序更加簡潔,只須要專一的作一件事情。動畫效果是一個很是重要的部分,用戶界面須要生動活潑。傳統的Qt技術已經不適於這些市場了。
接下來:Qt Quick將會解決這個問題。
在現代的軟件開發中有一個內在的衝突,用戶界面的改變速度遠遠高於咱們的後端服務。在傳統的技術中咱們開發的前端須要與後端保持相同的步調。當一個項目在開發時用戶想要改變用戶界面,或者在一個項目中開發一個用戶界面的想法就會引起這個衝突。敏捷項目須要敏捷的方法。
Qt Quick 提供了一個相似HTML聲明語言的環境應用程序做爲你的用戶界面前端(the front-end),在你的後端使用本地的c++代碼。這樣容許你在兩端都遊刃有餘。
下面是一個簡單的Qt Quick UI的例子。
import QtQuick 2.0 Rectangle { width: 240; height: 1230 Rectangle { width: 40; height: 40 anchors.centerIn: parent color: '#FFBB33' } }
這種聲明語言被稱做QML,它須要在運行時啓動。Qt提供了一個典型的運行環境叫作qmlscene,可是想要寫一個自定義的容許環境也不是很困難,咱們須要一個快速視圖(quick view)而且將QML文檔做爲它的資源。剩下的事情就只是展現咱們的用戶界面了。
QQuickView* view = new QQuickView(); QUrl source = Qurl::fromLocalUrl("main.qml"); view->setSource(source); view.show();
回到咱們以前的例子,在一個例子中咱們使用了一個c++的城市數據模型。若是咱們可以在QML代碼中使用它將會更加的好。
爲了實現它咱們首先要編寫前端代碼怎樣展現咱們須要使用的城市數據模型。在這一個例子中前端指定了一個對象叫作cityModel,咱們能夠在鏈表視圖(list view)中使用它。
import QtQuick 2.0 Rectangle { width: 240; height: 120 ListView { width: 180; height: 120 anchors.centerIn: parent model: cityModel delegate: Text { text: model.city } } }
爲了使用cityModel,咱們一般須要重複使用咱們之前的數據模型,給咱們的根環境(root context)加上一個內容屬性(context property)。(root context是在另外一個文檔的根元素中)。
m_model = QSqlTableModel(this); ... // some magic code QHash<int, QByteArray> roles; roles[Qt::UserRole+1] = "city"; roles[Qt::UserRole+2] = "country"; m_model->setRoleNames(roles); view->rootContext()->setContextProperty("cityModel", m_model);
警告
這不是徹底正確的用法,做爲包含在SQL表格模型列中的數據,一個QML模型的任務是來表達這些數據。因此須要作一個在列和任務之間的映射關係。請查看來QML and QSqlTableModel得到更多的信息。