開發QT有兩種IDE可使用,一種是使用 VS + Qt 的插件,另外一種就是使用QtCreator工具。前一種是微軟的工具,用的都比較多容易上手,缺點是信號槽的支持不太好,須要手寫,不能自動生成,另外可能有中文編碼的問題。後一種是Qt的官方IDE,智能提示與調試功能不如VS強大,可是是跨平臺的IDE,其QtDesigner設計UI界面操做比較方便,而且因爲是QT官方的IDE,對編碼等支持都比較好,裏面集成了Qt的幫助文檔。不得不說Qt的幫助文檔作的是很是好的,集成進QtCreator環境以後更加方便。
我開發的時候使用的是QtCreator開發,目前除了調試功能比VS差之外,其餘的用的比較順手,QtCreator是跨平臺的,ubuntu上也是可使用,打開以後界面以下:
javascript
下面將對QtCreator的界面各個功能進行大體的介紹:
html
咱們創建一個示例項目,選擇「文件」—「新建文件或項目」—「應用程序」—「QT Widgets Application」選擇以後都選擇默認設置,根據提示,就獲得了一個項目,咱們的UI是一個基於QMainWindow的類,默認提供菜單欄,狀態欄。若是不須要這些,能夠創建一個基於QWidget的UI類,項目如圖所示:
java
將項目切換到編輯模式,以下:
程序員
這個項目中一共有4個文件,入口文件main.cpp、mainwindow.ui文件、mainwindow.h和mainwindow.cpp後臺源文件,在main函數中直接調用MainWindow類的show()方法顯示主界面,那麼咱們切換到UI的設計視圖(雙擊項目中的mainwindow.ui文件),在主界面上添加兩個控件:web
咱們看一下MainWindow.cpp的代碼裏面應該如何操做界面上的控件:
編程
咱們使用的是ui->txtName->text();
這樣的語句,也就是說並非像在C#中同樣在後臺代碼中直接能夠經過相似this->txtName->text()
的語句去訪問界面上的控件對象,而MainWindow
類中有一個成員變量是ui,其類型是Ui::MainWindow
,經過這個ui成員去訪問界面上的元素,那麼這些界面控件是如何初始化的呢? 咱們須要查看ui成員變量的類型Ui::MainWindow
的實現,注意Ui::Mainwindow
類與MainWindow
類是不一樣的兩個類,Ui::MainWindow
類是在命名空間Ui下的類,而MainWindow
是沒有命名空間的,咱們在mainwindow.h中能夠看到:ubuntu
MainWindow中的私有成員變量ui其實是Ui::MainWindow
類型的指針,那麼Ui::MainWindow
是如何定義的呢? 用鼠標點進去就看到了:windows
從這裏就能夠看出爲何咱們的MainWindow類的構造函數中一進來就調用ui->setupUi(this)
去初始化界面了數組
QT中有四種佈局方式,分別是:Vertical垂直佈局、Horizontal水平佈局、Grid佈局、Form佈局,效果以下:安全
其實Grid佈局感受跟HTML中的Table差很少,Form佈局好像也是表格的效果,至於這兩種佈局的差別在哪裏我也不是很清楚,項目中基本沒有用過這兩種佈局方式,通常而言全部的效果均可以經過水平佈局和垂直佈局嵌套實現。結合水平佈局和垂直佈局,以及他們之間的相互嵌套,再結合使用自動伸縮調節的佔位控件HorizontalSpacer和VerticalSpacer就能夠實現很是複雜的佈局效果。
通常使用佈局有兩種方式,第一種即拖放這些佈局控件到UI界面上,而後將但願佈局的子控件拖放到這些佈局控件中,可是這種方式我的認爲不夠靈活,特別是在控件之間但願嵌套的時候,工具箱中的佈局控件以下:
另一種使用方式,QT的容器控件(那些可以放子控件的控件)均可覺得其指定一種佈局方式,當爲一個容器控件指定佈局方式以後,該容器控件就會以這種佈局方式來約束其全部子控件,直接在Qt設計器的容器控件中右鍵就能夠設置:
咱們在一個QFrame控件中放入兩個子控件,一個文本框一個按鈕,以後在QFrame的空白處右鍵單擊,在其右鍵菜單「佈局」的子菜單中就能夠指定該控件的佈局模式了。實際上在代碼上的原理是同樣的,咱們在QtCreator生成的ui_mainwindow.h中能夠看到關於frame以及子控件和其佈局設置的代碼:
能夠看到是這麼樣的關係,QFrame的子控件QPushButton以及QLineEdit(文本框)在構造的時候指定的父對象就是frame,而佈局對象QHBoxLayout指定的父控件對象也是frame,也就是說除了咱們在界面上看到的按鈕,文本框是frame的子控件之外,咱們經過右鍵生成的佈局對象(QtCreator自動生成的,其對象id也是自動生成的),也是frame的子控件,QHBoxLayout經過addWidget函數將frame的全部直接子控件添加到佈局中進行佈局。而咱們在工具箱中拖動佈局控件到頂級窗口UI界面以後,實際上QtCreator自動生成了一個QWidget做爲該佈局控件的容器,而且自動生成的這個QWidget的父控件就是頂級的MainWindow窗口。也就是說咱們每往UI界面上拖放一個佈局控件,那麼QtCreator會爲該佈局控件自動生成一個QWidget做爲該佈局控件的容器(也就是父控件),而且該自動生成的QWidget的父控件就是佈局控件被拖動到的位置所在的直接容器。例如:
當選定一個佈局控件(若是該佈局控件是從工具箱拖放到UI上的,則其在UI設計器上是能夠看到的),或者是選擇一個容器控件的時候(若是該容器控件已經經過右鍵的方式指定了佈局方式)。這兩種狀況下在QtCreator的屬性欄上就能夠看到佈局的相關屬性:
若是是從工具箱中拖放的佈局控件,那麼其屬性中的Margin默認都是0 ,若是是經過右鍵爲容器控件指定的佈局,那麼該佈局的Margin默認是9,因此這種方式下能夠看到若是此時相容器控件中添加子控件,那麼子控件與容器控件之間是有間隙的,除非將這裏的屬性手工改成0,layoutSpacing參數對於這兩種方式產生的佈局默認值都是6,表示該佈局中的子控件之間的間隔是6
QT中最經常使用的控件QPushButton(按鈕)、QLineEdit(文本框)、QRadioButton(單選框)、QCheckBox(複選框)、QFrame(通常用做容器控件,配合佈局)、QProgressBar(進度條控件)這些控件的使用方法都很是簡單,查一下幫助文檔就能夠搞定,下面的章節中,咱們會講解另外的一些控件的經常使用可是卻不是很容易找到的功能。
再講解其餘控件以前,咱們須要先了解Qt中的QVariant類型,爲何呢,由於須要爲控件綁定數據,就離不開對QVariant類型的瞭解,下面章節中咱們要說到的一些控件,在綁定數據的時候就會使用QVariant類型。他除了能夠包裹Qt中常見的QString,int等類型以外,還能夠包裹自定義的類對象。該類型提供了一系列的構造函數以及轉換函數來攜帶常見類型的數據,和轉換到常見類型數據的方法:
QVariant(int val) QVariant(uint val) QVariant(qlonglong val) QVariant(qulonglong val) QVariant(bool val) QVariant(double val) QVariant(float val) QVariant(const char * val) QVariant(const QString & val) QVariant(const QDate & val) QVariant(const QTime & val) QVariant(const QDateTime & val) bool toBool() const QByteArray toByteArray() const QChar toChar() const QDate toDate() const QDateTime toDateTime() const double toDouble(bool * ok = 0) const float toFloat(bool * ok = 0) const int toInt(bool * ok = 0) const QJsonArray toJsonArray() const qlonglong toLongLong(bool * ok = 0) const QString toString() const QTime toTime() const uint toUInt(bool * ok = 0) const qulonglong toULongLong(bool * ok = 0) const
這只是其中的一部分,其實還包括了一些畫圖相關的類型的封裝,例如QPoint,QRect等,固然Qt提供的是使用頻率很高的常見的類型,有時候咱們須要綁定本身定義的類對象,例如實體類:
//設置 MyClass myclass; QVariant courseModelVariant=QVariant::fromValue(myclass); //獲取 myclass = courseModelVariant.value<MyClass>();
這樣咱們就可使用QVariant攜帶任意數據類型了
下拉列表框控件最多見的功能需求就是爲該控件添加下拉項目,而且爲每一個下拉項目添加對應的自定義隱藏數據,例如在下拉列表中每一項上面顯示的文字描述是給用戶看的,然而在程序中,咱們可能須要該項目對應的隱藏數據,例如ID甚至是自定義的對象。
QComboBox
類使用QComboBox::addItem(const QString &atext, const QVariant &auserData)
成員函數爲下拉列表添加項目,第一個參數text表示顯示在下拉項中的文字,而第二個參數咱們能夠利用來爲該項綁定自定義的數據,其類型爲QVariant類型。咱們能夠經過QVariant類型方便的爲該下拉項關聯任意自定義的數據類型。
在獲取數據的時候,經過QComboBox:: currentData(int role = Qt::UserRole)
函數獲取當前選中下拉項關聯的QVariant類型的數據,也能夠經過QComboBox:: itemData(int index, int role = Qt::UserRole)
獲取指定下拉項的關聯數據。經過currentText()、itemText(int index)能夠獲取下拉項上顯示的文本。
QTableWidget是Qt中的表格顯示控件,與C#中的Grid、GridView相似,主要是用來綁定數據。在UI設計界面中選中該控件以後能夠在屬性欄對控件的屬性進行設置,最經常使用的屬性有以下:
focusPolicy 焦點策略,若是設置爲NoFocus能夠去掉單擊時候現實的單元格的虛線框
contextMenuPolicy 能夠設置右鍵菜單
frameShape 設置外邊框,通常設置爲NoFrame去掉邊框
editTriggers觸發單元格的編輯狀態,值NoEditTriggers表示不觸發編輯狀態
selectionMode選擇模式,值ExtendedSelection表示多選
selectionBehavior選擇行爲,值SelectRows按行選擇
showGrid是否顯示網格線
rowCount行數
columnCount列數
horizontalHeaderVisible是否顯示水平表頭
verticalHeaderVIsible是否顯示垂直表頭
verticalScrollBarPolicy設置垂直滾動條策略
horizontalScrollBarPolicy設置水平滾動條策略
另外的一些比較實用的功能代碼:
在單元格中添加控件:
QComboBox *comBox = new QComboBox(); comBox->addItem("F"); comBox->addItem("M"); ui->qtablewidget->setCellWidget(0,3,comBox);//這裏不是setItem而是setCellWidget
爲單元格添加checkBox:
QTableWidgetItem *item = new QTableWidgetItem(); //設置item的check狀態的時候,item會自動變成QCheckBox的樣子, //沒必要經過setCellWidget專門插入QCheckBox控件 //經過item->checkState()能夠獲取該item是否勾選 item->setCheckState(Qt::Unchecked); ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
單元格中顯示字符串:
QTableWidgetItem *item = new QTableWidgetItem(QString("xx")); ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
設置單元格關聯的自定義數據:
QTableWidgetItem *item = new QTableWidgetItem(QString("")); QVariant courseModelVariant=QVariant::fromValue(MyClass("xx")); item->setData(USER_DEFINE_ROLE,courseModelVariant); this->ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
獲取單元格關聯的自定義數據:
QTableWidgetItem * item = this->ui->tableWidgetCourseList->item(row,col); Myclass model = item->data(USER_DEFINE_ROLE).value<MyClass>();
設置單元格中的文本對齊方式:
ui->tableWidgetCourseList->item(rowIndex, columnIndex)->setTextAlignment(Qt::AlignCenter);
經過x,y座標獲取所在的item對象:
QModelIndex index = ui->tableWidgetCourseList->indexAt(QPoint(x,y)); int row = index.row(); int col = index.column(); QTableWidgetItem * item = ui->tableWidgetCourseList->item(row,col);
設置表頭的列寬:
ui->tableWidgetCourseList->horizontalHeader()->resizeSection(colIndex,20);//寬20
設置列寬自適應:
ui->tableWidgetCourseList->horizontalHeader()->setSectionResizeMode(colIndex,QHeaderView::Stretch);
初始化表頭文本:
QStringList headerText; headerText.append("列1"); headerText.append("列2"); headerText.append("列3"); ui->tableWidgetCourseList->setHorizontalHeaderLabels(headerText);
爲表頭添加複選框按鈕:
在表頭上添加複選框不能經過在表頭單元格中添加QCheckBox的方式實現,必須進行重繪,下面的代碼是咱們自定義的表頭類
myqheaderview.h的內容:
//該類實現自定義的表頭,主要是爲了在表頭中加入CheckBox控件 class MyQHeaderView : public QHeaderView { Q_OBJECT public: explicit MyQHeaderView(Qt::Orientation orientation, QWidget *parent = 0); void setChecked(bool checked); signals: void headCheckBoxToggled(bool checked); protected: void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; void mousePressEvent(QMouseEvent *event); private: QRect checkBoxRect(const QRect &sourceRect) const; bool m_isOn; };
myqheadview.cpp的內容:
MyQHeaderView::MyQHeaderView(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent) , m_isOn(false) { // set clickable by default setChecked(false); } void MyQHeaderView::setChecked(bool checked) { if (isEnabled() && m_isOn != checked) { m_isOn = checked; updateSection(0); emit headCheckBoxToggled(m_isOn); } } void MyQHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (logicalIndex == 0) { QStyleOptionButton option; if (isEnabled()) option.state |= QStyle::State_Enabled; option.rect = checkBoxRect(rect); if (m_isOn) option.state |= QStyle::State_On; else option.state |= QStyle::State_Off; style()->drawControl(QStyle::CE_CheckBox, &option, painter); } } void MyQHeaderView::mousePressEvent(QMouseEvent *event) { if (isEnabled() && logicalIndexAt(event->pos()) == 0) { m_isOn = !m_isOn; updateSection(0); emit headCheckBoxToggled(m_isOn); } else QHeaderView::mousePressEvent(event); } QRect MyQHeaderView::checkBoxRect(const QRect &sourceRect) const { QStyleOptionButton checkBoxStyleOption; QRect checkBoxRect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkBoxStyleOption); QPoint checkBoxPoint(sourceRect.x()+5, sourceRect.y() + sourceRect.height() / 2 - checkBoxRect.height() / 2); return QRect(checkBoxPoint, checkBoxRect.size()); }
使用自定義表頭:
MyQHeaderView*myHeader=new MyQHeaderView(Qt::Horizontal, ui->tableWidgetCourseList); ui->tableWidgetCourseList->setHorizontalHeader(myHeader);
爲QTableWidget添加一行數據其實是根據行數和列數,循環QTableWidget的全部單元格,對每一個單元格item設置數據來實現的。
該控件類就是一個選項卡控件,有多個tab頁,下面是一些實用的方法:
切換到tab:
ui->tabWidgetExportEdit->setCurrentIndex(tabIndex);
移除選項卡:
ui->tabWidgetExportEdit->removeTab(tabIndex);
關於選項卡控件的操做很少,重要的是怎麼美化控件的顯示,QSS將會做爲單獨的一篇文章來說解如何美化Qt中的各類控件。
該控件是用於在Qt中顯示網頁的控件,通常而言會將contextMenuPolicy屬性設置爲NoContextMenu隱藏系統爲其提供的默認右鍵菜單
<1>. 加載網頁:
ui->webViewCut->load(QUrl("http://www.baidu.com")); //若是是本地網頁,必須使用file:///的前綴做爲網頁地址 ui->webViewCut->load(QUrl("file:///c:/test.html "));
<2>. Qt代碼中調用QWebview加載的網頁中的js函數:
//先做以下設置 ui->webViewCut->page()->setForwardUnsupportedContent(true); ui->webViewCut->page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, true); ui->webViewCut->page()->settings()->setAttribute(QWebSettings::PluginsEnabled, true); ui->webViewCut->page()->settings()->setAttribute(QWebSettings::JavaEnabled, true); ui->webViewCut->page()->settings()->setAttribute(QWebSettings::AutoLoadImages, true); //而後在QWebview的loadFinished槽函數中調用js,該槽函數表示網頁已經加載完畢 QString js = QString("alert(\'hello Qt!\')"); ui->webViewCut->page()->mainFrame()->evaluateJavaScript(js);
<3>. 在QWebview加載的html的js代碼中調用Qt的函數:
默認狀況下在QwebViewCut中的網頁裏面的js不能直接調用Qt中的相關功能,這涉及到安全性問題。要知足js中調用Qt的功能必須知足下面的條件:
在Qt中暴露一個對象給js,而後js就能夠在網頁中直接使用這個對象以及該對象的[特定]函數,要求是被暴露Qt對象必須繼承自QObject類,而且在js中調用這個暴露的對象的成員函數的定義是有要求的,該對象的知足下面的要求的成員函數均可以直接被js調用:
1.必須是該對象的公共函數,而且在函數聲明前面添加Q_INVOKABLE修飾,例如:
public : Q_INVOKABLE int TestQt();
2.若是該函數被聲明成一個public slot 也能夠不添加Q_INVOKABLE修飾:
public slots: void TestQt();
我的認爲第一種方法更好,由於能夠設置返回值,而Qt的槽函數是沒有返回值的,都是返回void,只須要調用this->ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject("QtObj", this);
就能夠將一個Qt對象,也就是這裏傳遞的this表明的對象,固然也能夠直接傳遞其餘對象指針,暴露給網頁中的javascript,網頁中的javascript在調用的時候能夠直接使用 QtObj 去引用咱們的Qt對象,以及經過QtObj去直接調用符合條件的Qt對象的成員函數。
那麼this->ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject("QtObj", this);
代碼在何時執行呢? 推薦是在QWebFrame的信號javaScriptWindowObjectCleared
發出的時候執行,因此咱們能夠在當前UI界面類的構造函數中添加下面的代碼:
connect(ui->webViewCut->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(populateJavaScriptWindowObject()));
而後在處理javaScriptWindowObjectCleared()
信號的槽函數中實現上述暴露功能:
void MainWindow::populateJavaScriptWindowObject() { ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject("QtObj", this); }
根據Qt文檔上對該信號的描述javaScriptWindowObjectCleared()
這個信號會在咱們調用QwebViewCut::load()
加載新的url以前就觸發,咱們在這個時候去處理這個信號,將咱們須要暴露的Qt對象暴露給即將載入的網頁
<4>. 將Qt的屬性暴露出去供js調用,使用以下方法:
Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)
將上面的語句加入到類的聲明中,在private塊下面就能夠,最後不須要以分號結尾,例如:
private: Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)
這一行的做用是將屬性 Qtvalue 註冊到Qt的元對象系統中,在js中能夠經過名字Qtvalue來訪問該屬性,但在js中訪問該屬性的時候假設Qt暴露給js的對象爲QtObj,那麼在js中能夠這樣訪問該屬性:
QtObj.Qtvalue = 10; //設置該屬性的時候會調用void setTestValue(int) alert(QtObj.Qtvalue) //獲取該屬性的時候會調用 int testValue()
Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)的結構以下:
Q_PROPERTY( 類型 屬性名 READ 返回屬性值的函數 WRITE 設置屬性值的函數 ) int Qtvalue int testValue() void setTestValue(int)
也就是說在js中咱們能夠直接使用Qtvalue,當獲取Qtvalue的值的時候會自動調用暴露對象的 int testValue() 函數 ,Qt規定其返回值必須與Q_PROPERTY語句中指定的類型相同,而且必須沒有參數。當咱們爲Qtvalue設置值的時候會調用暴露對象的void setTestValue(int)
函數,該函數必須有一個int類型的參數(類型也必須與前面Q_PROPERTY語句中指定的類型相同),而且不能有返回值。
通過實驗int testValue()
與void setTestValue(int)
函數的聲明在private區域也能夠,好像無所謂。其實這兩個函數的名字是能夠隨意定的,對js暴露的屬性名是Qtvalue,當訪問Qtvalue屬性的時候,會自動調用Q_PROPERTY聲明中READ後面指定的函數去獲取值,而且調用WRITE後面指定的函數去設置值,而不在意這兩個函數的名字。
另外這兩個函數獲取的值或者設置的值從哪裏得來呢,咱們能夠在Qt對象中定義一個私有變量來保存這個值,而這個私有變量的名字是無所謂的,甚至若是須要的話,咱們也沒必要保存這個值,直接在函數testValue裏面返回一個常量值,也就是說是否應該定義一個私有變量來保存Qtvalue相關聯的屬性值,這個也不是必須的。
更多Qt QWidget與js的交互能夠在Qt文檔中搜索 The Qt WebKit Bridge
關鍵字,其實Q_PROPERTY並非專用於暴露屬性給js的,Q_PROPERTY是Qt元對象系統的一部分。
<5>. 若是在QWebview加載的網頁中有Flex應用程序,而且Qt中調用該QWebview加載的網頁中的js函數中須要調用flex程序暴露給js的接口,那麼還須要做以下設置:
在"%appdata%\Macromedia\Flash Player\#Security\FlashPlayerTrust\"
路徑下新建xxx.cfg文件,將當前flex應用程序所在位置(也就是swf文件所在的目錄)填寫到該文件中便可,該xxx.cfg的名字是無所謂的,隨便什麼名字,在xxx.cfg文件中指定的目錄路徑中的swf文件的運行是被信任的。xxx.cfg文件中能夠指定多個目錄,每行一個。實際上%appdata%\Macromedia\Flash Player\#Security\FlashPlayerTrust\
路徑下也能夠有多個文件名不一樣的cfg文件。xxx.cfg文件中指定的目錄實際上能夠直接指定爲根目錄,例如swf文件的路徑是F:/xxx/yyy/zzz/test.swf
,那麼咱們新建的xxx.cfg中的內容的第一行能夠直接指定爲F:/便可。
其實FlexBuilder在創建項目的時候,其生成的swf所在的目錄都被添加到了%appdata%\Macromedia\Flash Player\#Security\FlashPlayerTrust\
下面的flashbuilder.cfg中了,因此使用FlexBuilder調試項目的時候,運行的swf都是被信任的。
QSS是Qt中的樣式表,用來定義Qt中控件的外觀,實際上QSS的語法與屬性大量參考了CSS,若是你有web的CSS開發經驗,幾乎沒有任何障礙就能夠掌握QSS,QSS中的選擇器基本上與CSS中的相同,可是QSS只有幾種經常使用的選擇器類型。
QSS中選擇器的類型:
<1>. 類型選擇器,例如:QPushButton{} 設置全部類型是QPushButton或者繼承自QPushButton的控件的樣式。
<2>. 屬性選擇器,例如:QPushButton[flat="false"]{} 設置全部flat屬性是false的QPushButton控件的樣式。
<3>. 類選擇器,例如:.QPushButton{} 設置全部QPushButton的樣式,可是不會設置繼承自QPushButton類型的控件的樣式,QSS中的類選擇器與CSS中的含義不一樣,QSS中的類選擇器點號後面指定的類的名稱,而CSS中的類選擇器中的點號後面指定的是HTML標籤中的class屬性的名稱。
<4>. ID選擇器,例如:#okButton{} 設置全部對象名(object name)爲okButton的控件的樣式。
<5>. 後代選擇器,例如:QDialog QPushButton{} 設置全部QDialog中的QPushButton子控件的樣式,只要是QDialog的子控件都會應用該樣式,包括直接或非直接的子控件。
<5>. 直接子選擇器:例如 QDialog > QPushButton{} 設置全部是QDialog直接子控件的QPushButton的樣式。
<6>. QSS支持選擇器分組,支持選擇器組合,例如:QPushButton#okButton{} 設置全部ID爲okButton的QPushButton控件的樣式。#okButton,#cancelButton{} 設置id爲okButton、cancelButton的控件的樣式。
那麼如何在Qt中使用這些QSS設置控件的外觀呢,通常在代碼中經過調用控件對象的setStyleSheet(QString)成員函數進行設置,參數便是QSS字符串。例如:
ui->btnTest->setStyleSheet("border:1px solid red;");//設置按鈕的邊框
另外咱們能夠將全部的QSS放到文件中,例如main.qss,而後將該文件添加到Qt的資源文件中,在主UI界面中加載該main.qss文件,並調用主UI界面類的成員函數設置其下控件的樣式:
QFile file(":/qss/main.qss"); file.open(QFile::ReadOnly); QTextStream filetext(&file); QString stylesheet = filetext.readAll(); this->setStyleSheet(stylesheet); file.close();
要注意的是main.qss中設置的樣式應該是針對當前UI界面上的控件的,也就是這裏調用的this->setStyleSheet(stylesheet);
中的this就是當前UI界面的類的實例。
有關QSS的細節不少,並且每一個控件的美化技巧不一樣,同時QSS中還提供了僞類,子控件樣式等功能,限於篇幅,本節只作一個大體的介紹,後面將會單獨一篇文章詳細講解QSS的細節,以及如何美化Qt中的各類常見控件。
以前在寫Qt程序的時候,若是在原文件中的字符串直接寫中文,例若有些地方須要彈出錯誤或者警告的對話框提示,那麼提示內容就是中文信息,我發現有部分字符會出現亂碼,而且有時候編譯的時候會報錯:error C2001: 常量中有換行符
這通常是編碼問題。我是這麼解決的,在包含中文(即便是註釋中有中文有時候也報錯)的源文件的開頭加入 #pragma execution_character_set("utf-8")
這一行指定文件的編碼,同時使用UE編輯器打開該文件,另存爲UTF-8的編碼,在QtCreator中從新打開便可。遇到跟我一樣問題的人也能夠試一下這個辦法。
這一小節說題目命名爲QT的內存管理,題目有點過大,其實我在寫Qt程序的時候,包括Qt的例子程序,中常常出現相似以下的代碼:
void MainWidget::on_btnClick() { QLabel * lblMessage = new QLabel(「hello」,this); lblMessage->show(); }
彷佛Qt中new出來的控件類型都只負責new不用delete的,感到很奇怪,後來通過查資料發現不少人有一樣的疑問,有人給出緣由是由於Qt中的全部的控件類是繼承自QObject類,若是在new的時候指定了父親(在構造函數的參數中有parent這個參數),那麼它的清理是在其父親被delete的時候被delete的。Qt不建議程序員在代碼中手工delete一個QObject,若是必定要這麼作,須要使用QObject的deleteLater()函數,不然可能出現Qt正在一級一級的從一個父親類開始清理下面的全部子對象的時候,程序中手工調用delete也去清理其中的子對象,那麼這個時候就可能出現問題,因此建議使用deleteLater()函數,它會讓全部事件都發送完成以後再清理該片內存。
在大多數Qt的編程中,咱們經過Qt信號槽機制來對鼠標或鍵盤在界面上的操做進行響應處理,例如鼠標點擊按鈕的處理。Qt中的控件可以發出什麼信號,在什麼狀況下發射信號,這在Qt的文檔中有說明,每一個不一樣的控件可以發射的信號種類和觸發時機也是不一樣的。
如何爲控件發射的信號指定對應的處理槽函數呢,咱們有兩種方式,第一種是在UI設計界面上操做:
在按鈕控件上點擊右鍵,選擇「轉到槽」菜單以後彈出以下的對話框:
能夠看到按鈕控件會發射不少信號,只要選擇一個信號,點擊OK以後就會生成對應的槽函數對按鈕發出的該信號進行處理
void MainWindow::on_btnTest_clicked() { }
選擇clicked()信號以後生成的處理該信號的槽函數,除了經過UI界面自動生成槽函數的方式之外,咱們還能夠在代碼中本身手寫槽函數,並經過QObject::connect()函數將特定對象的信號與另一個對象的槽函數進行鏈接,當該對象的信號發射以後,會被關聯的對象的槽函數處理。例如咱們能夠用下面的一行代碼完成上面的功能:
connect(ui->btnTest,SIGNAL(clicked()),this,SLOT(on_btnTest_clicked()));
使用代碼的好處是,不少控件的信號在上面的對話框中並無顯示出來,也就是說上面的對話框中其實只列出了該控件對象的一部分信號,另外若是咱們的對象是在程序中經過代碼動態構建的,那麼咱們也就須要在代碼中爲該控件的信號指定處理的槽函數了。上面的connect代碼是咱們直接在UI界面類的構造函數中寫的(固然在任何地方均可以,並不必定要在構造函數中),因爲UI界面類也是繼承自QObject因此天然也繼承了connect函數,經過connect函數咱們能夠將一個對象的信號與另外一個對象的槽函數進行鏈接,當個該對象的信號發射的時候(信號的發射時機有可能在代碼中調用對象的某個成員函數觸發,也有可能在程序的UI界面上操做鼠標,鍵盤等觸發)。
另外信號與槽在經過connect函數鏈接的時候,其參數類型必須徹底一致,不然是沒有效果的。實際上信號槽的原理,是依賴於Qt的元對象系統,Qt的一系列的構建工具爲程序員作了不少自動化的工做,自動生成了一些代碼,因此使得咱們看起來只須要用connect函數進行關聯以後,在信號發射的時候(經過emit發射信號),槽函數會被自動調用。在咱們的Qt的項目的debug目錄下,咱們每每會看到不少以moc_爲前綴的cpp文件,打開這些文件咱們就能夠看到該文件中的qt_meta_data_爲前綴的靜態數組裏面描述了信號槽的關聯信息,而在qt_static_metacall函數的實現中,咱們能夠大體看到經過一系列的case分支,對應的槽函數被調用。若是要詳細研究Qt的信號槽的實現原理,能夠研究QObject類的源碼,以及Qt的元對象系統。
槽函數被slots修飾,固然它能夠是普通的成員函數。信號被signals修飾。一個信號能夠關聯多個槽函數,當信號被髮射的時候,這些槽函數依次被執行,可是執行的順序是未知的,一個槽函數能夠被多個信號關聯。一個信號也能夠關聯另一個信號,當該信號被髮射的時候,與它關聯的信號也被髮射。經過disconnect函數能夠取消信號與槽函數之間的關聯關係。在槽函數中直接調用sender()就能夠得到觸發該槽函數的信號源對象,該函數是QObject的成員函數,返回的也是一個QObject類型的指針。
另外信號槽能夠在不一樣的線程之間使用,可是使用的時候須要注意調用connect時候指定鏈接的方式,不一樣的線程之間Qt能夠經過消息隊列來實現信號與槽函數的關聯,我常常在UI線程中關聯另一個工做線程的信號到UI界面類中的成員函數,以便在工做線程中經過發送信號的方式來調用UI主線程中的UI界面類的成員函數,來達到更新UI界面的效果。Qt中不能在工做線程中直接對UI界面控件進行操做。有關信號的鏈接方式能夠參考這篇文章:對信號與事件的認識(http://blog.chinaunix.net/uid-25147458-id-3706122.html)
咱們能夠在Qt中繪圖,在Qt的控件上繪圖,通常是須要重寫該控件的重繪事件的,例如:
void MovieImageWidget::paintEvent(QPaintEvent*p) { QPainter painter(this); if(this->currentImagePath!="") { QImage image(this->currentImagePath); QRect rect(0,0,this->width(),this->height()); painter.drawImage(rect,image); } }
在重繪事件中,咱們先創建一個基於控件的QPainter對象,而後在重繪事件函數中,咱們就能夠利用該painter對象的一系列的繪製函數進行繪圖操做了,繪製的圖形會在該Painter關聯的控件上顯示,其原點座標是從該控件的左上角開始的。在須要的時候咱們能夠手工調用控件的update()函數,這樣會直接觸發重繪事件進行重繪。
QPainter類提供的一系列的draw函數能夠幫助咱們繪製各類各樣的圖形,這裏就再也不舉例說明,能夠自行查閱Qt的幫助文檔。
Qt的線程使用起來很是簡單,咱們首先要創建一個自定義的類(例如MyThread),繼承自QThread,並實現其run方法便可。在使用線程的時候直接獲得MyThread的實例,調用其start()函數便可啓動線程,線程啓動以後會自動調用其實現的run方法,該方法就是線程的執行函數,咱們的線程任務就寫在這裏,當run退出以後線程基本就結束了,QThread有一個started和finished信號,咱們能夠爲這兩個信號指定槽函數,在線程啓動和結束的時候執行一段代碼進行資源的初始化和資源的釋放操做。
經過QtCreator的嚮導能夠很是方便的在Qt程序中使用第三方的dll,具體步驟以下:
在項目上點擊右鍵,選擇「添加庫」菜單
選擇外部庫
指定對應的lib文件,以及頭文件的包含路徑,設置平臺爲windows,選擇庫的鏈接類型而後點擊下一步
最後點擊完成既可,能夠看到實際上在Qt的個工程文件中,也就是pro文件中添加了以下的代碼:
win32: LIBS += -L$$PWD/E://trans/ -lTransAPI INCLUDEPATH += $$PWD/E:/trans/include DEPENDPATH += $$PWD/E:/trans/include win32:!win32-g++: PRE_TARGETDEPS += $$PWD/E:/trans/TransAPI.lib else:win32-g++: PRE_TARGETDEPS += $$PWD/E:/trans/libTransAPI.a
在須要使用的地方,包含頭文件以後就能夠就能夠直接調用庫裏面的函數了,使用方式與VC中沒有區別。
在Qt中QWidget控件以及其子類均可以添加右鍵菜單,Qt中全部界面上顯示的控件基本都繼承自QWidget控件,因此基本上Qt中的控件均可以添加右鍵菜單,下面舉例說明爲按鈕添加右鍵菜單的方法:
<1>. 在UI設計界面中選中按鈕,在屬性欄中設置其屬性contextMenuPolicy
的值爲CustomContextMenu
(若是控件是在代碼中生成,能夠經過控件對象的成員函數setContextMenuPolicy()
在代碼中設置)
<2>. 在UI設計界面的按鈕上單擊右鍵,轉到槽,在彈出的對話框中選擇customContextMenuRequested(const QPoint&)
,單擊肯定,爲按鈕的該信號指定槽函數,在代碼中能夠經過connect手工關聯。
<3>. 在該槽函數中生成菜單代碼以下:
void MainWindow::on_menu_click(bool checked) { //經過sender()獲得信號的發送對象,也就是哪一個菜單項被單擊 } void MainWindow::on_btnTest_customContextMenuRequested(const QPoint &pos) { QMenu *cmenu = new QMenu(ui->btnTest); QAction *action1 = cmenu->addAction("Menu 1"); QAction *action2 = cmenu->addAction("Menu 2"); QAction *action3 = cmenu->addAction("Menu 3"); connect(action1, SIGNAL(triggered(bool)), this, SLOT(on_menu_click(bool))); connect(action2, SIGNAL(triggered(bool)), this, SLOT(on_menu_click(bool))); connect(action3, SIGNAL(triggered(bool)), this, SLOT(on_menu_click(bool))); cmenu->exec(QCursor::pos()); }
固然這裏僅僅是demo代碼,每次點擊右鍵的時候,咱們都要從新new出菜單來,這樣確定會耗費資源,這些菜單建立的代碼能夠放在一個全局的函數中,只須要建立一次,可是cmenu->exec(QCursor::pos());
這條語句是顯示菜單用的,執行以後菜單才能顯示出來,因此每次槽函數被執行的時候都須要調用一次來呼出菜單。
最終顯示效果以下:
除了上面的方法以外,還能夠經過重寫contextMenuEvent()
事件來實現右鍵菜單,這裏就不細說了,能夠自行百度。
本篇總結性的講解了Qt的諸多方面的知識點,有些地方限於篇幅,可能須要單獨另起一篇文章進行講解,有的是我本身也並無徹底弄透徹怕誤人子弟。因爲公司須要開發一個窗口程序,要求不須要安裝附帶的框架,因此.NET就被排除在外了,由於公司以前有同事使用WPF開發過其餘的程序,界面也比較漂亮,可是工程部的同事在外面部署的時候因爲安裝框架的緣由常常出現各類系統問題。至於MFC太古老,學習週期長,因此也被排除,另外兩個一個是Flex AIR,一個是Qt,權衡之下仍是選擇了Qt,通過一個月的邊學邊作,效果還能夠。其實Qt仍是比較好學的,基本上熟練掌握了QSS的話,也能夠實現很是好的界面效果,並且仍是跨平臺的,特別是在嵌入式系統中,若是須要顯示界面的話,會是一個很是好的選擇。但願這篇文章對你們有所幫助,因爲篇幅比較長,雖然我已經檢查過,若是發現文字錯誤,還但願園友不吝指正,我會及時改正。