关于QT的系统总结(很是全面,很是好)

 

编译环境与开发流程

开发QT有两种IDE可使用,一种是使用 VS + Qt 的插件,另外一种就是使用QtCreator工具。前一种是微软的工具,用的都比较多容易上手,缺点是信号槽的支持不太好,须要手写,不能自动生成,另外可能有中文编码的问题。后一种是Qt的官方IDE,智能提示与调试功能不如VS强大,可是是跨平台的IDE,其QtDesigner设计UI界面操做比较方便,而且因为是QT官方的IDE,对编码等支持都比较好,里面集成了Qt的帮助文档。不得不说Qt的帮助文档作的是很是好的,集成进QtCreator环境以后更加方便。

我开发的时候使用的是QtCreator开发,目前除了调试功能比VS差之外,其余的用的比较顺手,QtCreator是跨平台的,ubuntu上也是可使用,打开以后界面以下:
11java

下面将对QtCreator的界面各个功能进行大体的介绍:1程序员

咱们创建一个示例项目,选择“文件”—“新建文件或项目”—“应用程序”—“QT Widgets Application”选择以后都选择默认设置,根据提示,就获得了一个项目,咱们的UI是一个基于QMainWindow的类,默认提供菜单栏,状态栏。若是不须要这些,能够创建一个基于QWidget的UI类,项目如图所示:
1web

QT项目的构成及原理

将项目切换到编辑模式,以下:
1编程

这个项目中一共有4个文件,入口文件main.cpp、mainwindow.ui文件、mainwindow.h和mainwindow.cpp后台源文件,在main函数中直接调用MainWindow类的show()方法显示主界面,那么咱们切换到UI的设计视图(双击项目中的mainwindow.ui文件),在主界面上添加两个控件:ubuntu

1

咱们看一下MainWindow.cpp的代码里面应该如何操做界面上的控件:
1windows

咱们使用的是ui->txtName->text();这样的语句,也就是说并非像在C#中同样在后台代码中直接能够经过相似this->txtName->text()的语句去访问界面上的控件对象,而MainWindow类中有一个成员变量是ui,其类型是Ui::MainWindow,经过这个ui成员去访问界面上的元素,那么这些界面控件是如何初始化的呢? 咱们须要查看ui成员变量的类型Ui::MainWindow的实现,注意Ui::Mainwindow类与MainWindow类是不一样的两个类,Ui::MainWindow类是在命名空间Ui下的类,而MainWindow是没有命名空间的,咱们在mainwindow.h中能够看到:数组

1

MainWindow中的私有成员变量ui其实是Ui::MainWindow类型的指针,那么Ui::MainWindow是如何定义的呢? 用鼠标点进去就看到了:安全

1

从这里就能够看出为何咱们的MainWindow类的构造函数中一进来就调用ui->setupUi(this)去初始化界面了

QT中的布局

QT中有四种布局方式,分别是:Vertical垂直布局、Horizontal水平布局、Grid布局、Form布局,效果以下:

1

其实Grid布局感受跟HTML中的Table差很少,Form布局好像也是表格的效果,至于这两种布局的差别在哪里我也不是很清楚,项目中基本没有用过这两种布局方式,通常而言全部的效果均可以经过水平布局和垂直布局嵌套实现。结合水平布局和垂直布局,以及他们之间的相互嵌套,再结合使用自动伸缩调节的占位控件HorizontalSpacer和VerticalSpacer就能够实现很是复杂的布局效果。

通常使用布局有两种方式,第一种即拖放这些布局控件到UI界面上,而后将但愿布局的子控件拖放到这些布局控件中,可是这种方式我的认为不够灵活,特别是在控件之间但愿嵌套的时候,工具箱中的布局控件以下:

1

另一种使用方式,QT的容器控件(那些可以放子控件的控件)均可觉得其指定一种布局方式,当为一个容器控件指定布局方式以后,该容器控件就会以这种布局方式来约束其全部子控件,直接在Qt设计器的容器控件中右键就能够设置:

1

咱们在一个QFrame控件中放入两个子控件,一个文本框一个按钮,以后在QFrame的空白处右键单击,在其右键菜单“布局”的子菜单中就能够指定该控件的布局模式了。实际上在代码上的原理是同样的,咱们在QtCreator生成的ui_mainwindow.h中能够看到关于frame以及子控件和其布局设置的代码:

1

能够看到是这么样的关系,QFrame的子控件QPushButton以及QLineEdit(文本框)在构造的时候指定的父对象就是frame,而布局对象QHBoxLayout指定的父控件对象也是frame,也就是说除了咱们在界面上看到的按钮,文本框是frame的子控件之外,咱们经过右键生成的布局对象(QtCreator自动生成的,其对象id也是自动生成的),也是frame的子控件,QHBoxLayout经过addWidget函数将frame的全部直接子控件添加到布局中进行布局。而咱们在工具箱中拖动布局控件到顶级窗口UI界面以后,实际上QtCreator自动生成了一个QWidget做为该布局控件的容器,而且自动生成的这个QWidget的父控件就是顶级的MainWindow窗口。也就是说咱们每往UI界面上拖放一个布局控件,那么QtCreator会为该布局控件自动生成一个QWidget做为该布局控件的容器(也就是父控件),而且该自动生成的QWidget的父控件就是布局控件被拖动到的位置所在的直接容器。例如:

1

当选定一个布局控件(若是该布局控件是从工具箱拖放到UI上的,则其在UI设计器上是能够看到的),或者是选择一个容器控件的时候(若是该容器控件已经经过右键的方式指定了布局方式)。这两种状况下在QtCreator的属性栏上就能够看到布局的相关属性:

1

若是是从工具箱中拖放的布局控件,那么其属性中的Margin默认都是0 ,若是是经过右键为容器控件指定的布局,那么该布局的Margin默认是9,因此这种方式下能够看到若是此时相容器控件中添加子控件,那么子控件与容器控件之间是有间隙的,除非将这里的属性手工改成0,layoutSpacing参数对于这两种方式产生的布局默认值都是6,表示该布局中的子控件之间的间隔是6

QT中的通用控件

QT中最经常使用的控件QPushButton(按钮)、QLineEdit(文本框)、QRadioButton(单选框)、QCheckBox(复选框)、QFrame(通常用做容器控件,配合布局)、QProgressBar(进度条控件)这些控件的使用方法都很是简单,查一下帮助文档就能够搞定,下面的章节中,咱们会讲解另外的一些控件的经常使用可是却不是很容易找到的功能。

QVariant 类型

再讲解其余控件以前,咱们须要先了解Qt中的QVariant类型,为何呢,由于须要为控件绑定数据,就离不开对QVariant类型的了解,下面章节中咱们要说到的一些控件,在绑定数据的时候就会使用QVariant类型。他除了能够包裹Qt中常见的QString,int等类型以外,还能够包裹自定义的类对象。该类型提供了一系列的构造函数以及转换函数来携带常见类型的数据,和转换到常见类型数据的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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提供的是使用频率很高的常见的类型,有时候咱们须要绑定本身定义的类对象,例如实体类:

1
2
3
4
5
6
//设置
MyClass myclass;
QVariant courseModelVariant=QVariant::fromValue(myclass);
     
//获取
myclass = courseModelVariant.value<MyClass>();

这样咱们就可使用QVariant携带任意数据类型了

QComboBox控件

下拉列表框控件最多见的功能需求就是为该控件添加下拉项目,而且为每一个下拉项目添加对应的自定义隐藏数据,例如在下拉列表中每一项上面显示的文字描述是给用户看的,然而在程序中,咱们可能须要该项目对应的隐藏数据,例如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控件

QTableWidget是Qt中的表格显示控件,与C#中的Grid、GridView相似,主要是用来绑定数据。在UI设计界面中选中该控件以后能够在属性栏对控件的属性进行设置,最经常使用的属性有以下:

focusPolicy 焦点策略,若是设置为NoFocus能够去掉单击时候现实的单元格的虚线框
contextMenuPolicy 能够设置右键菜单
frameShape 设置外边框,通常设置为NoFrame去掉边框
editTriggers触发单元格的编辑状态,值NoEditTriggers表示不触发编辑状态
selectionMode选择模式,值ExtendedSelection表示多选
selectionBehavior选择行为,值SelectRows按行选择
showGrid是否显示网格线
rowCount行数
columnCount列数
horizontalHeaderVisible是否显示水平表头
verticalHeaderVIsible是否显示垂直表头
verticalScrollBarPolicy设置垂直滚动条策略
horizontalScrollBarPolicy设置水平滚动条策略

另外的一些比较实用的功能代码:
在单元格中添加控件:

1
2
3
4
QComboBox *comBox =  new  QComboBox();
comBox->addItem( "F" );
comBox->addItem( "M" );
ui->qtablewidget->setCellWidget(0,3,comBox); //这里不是setItem而是setCellWidget

为单元格添加checkBox:

1
2
3
4
5
6
QTableWidgetItem *item =  new  QTableWidgetItem();
//设置item的check状态的时候,item会自动变成QCheckBox的样子,
//没必要经过setCellWidget专门插入QCheckBox控件
//经过item->checkState()能够获取该item是否勾选
item->setCheckState(Qt::Unchecked);
ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);

单元格中显示字符串:

1
2
QTableWidgetItem *item =  new  QTableWidgetItem(QString( "xx" ));
ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);

设置单元格关联的自定义数据:

1
2
3
4
QTableWidgetItem *item =  new  QTableWidgetItem(QString( "" ));
QVariant courseModelVariant=QVariant::fromValue(MyClass( "xx" ));
item->setData(USER_DEFINE_ROLE,courseModelVariant);
this ->ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);

获取单元格关联的自定义数据:

1
2
QTableWidgetItem * item =  this ->ui->tableWidgetCourseList->item(row,col);
Myclass model = item->data(USER_DEFINE_ROLE).value<MyClass>();

设置单元格中的文本对齐方式:

1
ui->tableWidgetCourseList->item(rowIndex, columnIndex)->setTextAlignment(Qt::AlignCenter);

经过x,y坐标获取所在的item对象:

1
2
3
4
QModelIndex index = ui->tableWidgetCourseList->indexAt(QPoint(x,y));
int  row = index.row();
int  col = index.column();
QTableWidgetItem * item = ui->tableWidgetCourseList->item(row,col);

设置表头的列宽:

1
ui->tableWidgetCourseList->horizontalHeader()->resizeSection(colIndex,20); //宽20

设置列宽自适应:

1
ui->tableWidgetCourseList->horizontalHeader()->setSectionResizeMode(colIndex,QHeaderView::Stretch);

初始化表头文本:

1
2
3
4
5
QStringList headerText;
headerText.append( "列1" );
headerText.append( "列2" );
headerText.append( "列3" );
ui->tableWidgetCourseList->setHorizontalHeaderLabels(headerText);

为表头添加复选框按钮:

在表头上添加复选框不能经过在表头单元格中添加QCheckBox的方式实现,必须进行重绘,下面的代码是咱们自定义的表头类
myqheaderview.h的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//该类实现自定义的表头,主要是为了在表头中加入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的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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());
}

使用自定义表头:

1
2
MyQHeaderView*myHeader= new  MyQHeaderView(Qt::Horizontal, ui->tableWidgetCourseList);
ui->tableWidgetCourseList->setHorizontalHeader(myHeader);

为QTableWidget添加一行数据其实是根据行数和列数,循环QTableWidget的全部单元格,对每一个单元格item设置数据来实现的。

QTabWidget控件

该控件类就是一个选项卡控件,有多个tab页,下面是一些实用的方法:

切换到tab:

1
ui->tabWidgetExportEdit->setCurrentIndex(tabIndex);

移除选项卡:

1
ui->tabWidgetExportEdit->removeTab(tabIndex);

关于选项卡控件的操做很少,重要的是怎么美化控件的显示,QSS将会做为单独的一篇文章来说解如何美化Qt中的各类控件。

QWebview控件

该控件是用于在Qt中显示网页的控件,通常而言会将contextMenuPolicy属性设置为NoContextMenu隐藏系统为其提供的默认右键菜单

<1>. 加载网页:

1
2
3
ui->webViewCut->load(QUrl( "http://www.baidu.com" ));
//若是是本地网页,必须使用file:///的前缀做为网页地址
ui->webViewCut->load(QUrl( "file:///c:/test.html " ));

<2>. Qt代码中调用QWebview加载的网页中的js函数:

1
2
3
4
5
6
7
8
9
10
//先做以下设置
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修饰,例如:

1
2
public  :
  Q_INVOKABLE  int  TestQt();

2.若是该函数被声明成一个public slot 也能够不添加Q_INVOKABLE修饰:

1
2
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界面类的构造函数中添加下面的代码:

1
2
connect(ui->webViewCut->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
     this , SLOT(populateJavaScriptWindowObject()));

而后在处理javaScriptWindowObjectCleared()信号的槽函数中实现上述暴露功能:

1
2
3
4
void  MainWindow::populateJavaScriptWindowObject()
{
    ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject( "QtObj" this );
}

根据Qt文档上对该信号的描述javaScriptWindowObjectCleared()这个信号会在咱们调用QwebViewCut::load()加载新的url以前就触发,咱们在这个时候去处理这个信号,将咱们须要暴露的Qt对象暴露给即将载入的网页

<4>. 将Qt的属性暴露出去供js调用,使用以下方法:

1
Q_PROPERTY( int  Qtvalue READ testValue WRITE setTestValue)

将上面的语句加入到类的声明中,在private块下面就能够,最后不须要以分号结尾,例如:

1
2
private :
  Q_PROPERTY( int  Qtvalue READ testValue WRITE setTestValue)

这一行的做用是将属性 Qtvalue 注册到Qt的元对象系统中,在js中能够经过名字Qtvalue来访问该属性,但在js中访问该属性的时候假设Qt暴露给js的对象为QtObj,那么在js中能够这样访问该属性:

1
2
QtObj.Qtvalue = 10;  //设置该属性的时候会调用void setTestValue(int)
alert(QtObj.Qtvalue)  //获取该属性的时候会调用 int testValue()

Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)的结构以下:

1
2
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

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字符串。例如:

1
ui->btnTest->setStyleSheet( "border:1px solid red;" ); //设置按钮的边框

另外咱们能够将全部的QSS放到文件中,例如main.qss,而后将该文件添加到Qt的资源文件中,在主UI界面中加载该main.qss文件,并调用主UI界面类的成员函数设置其下控件的样式:

1
2
3
4
5
6
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程序的时候,包括Qt的例子程序,中常常出现相似以下的代码:

1
2
3
4
5
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中的控件可以发出什么信号,在什么状况下发射信号,这在Qt的文档中有说明,每一个不一样的控件可以发射的信号种类和触发时机也是不一样的。

如何为控件发射的信号指定对应的处理槽函数呢,咱们有两种方式,第一种是在UI设计界面上操做:
1

 

在按钮控件上点击右键,选择“转到槽”菜单以后弹出以下的对话框:

1

 

能够看到按钮控件会发射不少信号,只要选择一个信号,点击OK以后就会生成对应的槽函数对按钮发出的该信号进行处理

1
2
3
void  MainWindow::on_btnTest_clicked()
{
}

选择clicked()信号以后生成的处理该信号的槽函数,除了经过UI界面自动生成槽函数的方式之外,咱们还能够在代码中本身手写槽函数,并经过QObject::connect()函数将特定对象的信号与另一个对象的槽函数进行链接,当该对象的信号发射以后,会被关联的对象的槽函数处理。例如咱们能够用下面的一行代码完成上面的功能:

1
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中绘图,在Qt的控件上绘图,通常是须要重写该控件的重绘事件的,例如:

1
2
3
4
5
6
7
8
9
10
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()函数,这样会直接触发重绘事件进行重绘。

1

QPainter类提供的一系列的draw函数能够帮助咱们绘制各类各样的图形,这里就再也不举例说明,能够自行查阅Qt的帮助文档。

QT的线程

Qt的线程使用起来很是简单,咱们首先要创建一个自定义的类(例如MyThread),继承自QThread,并实现其run方法便可。在使用线程的时候直接获得MyThread的实例,调用其start()函数便可启动线程,线程启动以后会自动调用其实现的run方法,该方法就是线程的执行函数,咱们的线程任务就写在这里,当run退出以后线程基本就结束了,QThread有一个started和finished信号,咱们能够为这两个信号指定槽函数,在线程启动和结束的时候执行一段代码进行资源的初始化和资源的释放操做。

QT中使用第三方的dll

经过QtCreator的向导能够很是方便的在Qt程序中使用第三方的dll,具体步骤以下:

1

在项目上点击右键,选择“添加库”菜单

1

选择外部库

1

指定对应的lib文件,以及头文件的包含路径,设置平台为windows,选择库的链接类型而后点击下一步

1

最后点击完成既可,能够看到实际上在Qt的个工程文件中,也就是pro文件中添加了以下的代码:

1
2
3
4
5
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中为控件添加右键菜单的方法

在Qt中QWidget控件以及其子类均可以添加右键菜单,Qt中全部界面上显示的控件基本都继承自QWidget控件,因此基本上Qt中的控件均可以添加右键菜单,下面举例说明为按钮添加右键菜单的方法:
<1>. 在UI设计界面中选中按钮,在属性栏中设置其属性contextMenuPolicy的值为CustomContextMenu(若是控件是在代码中生成,能够经过控件对象的成员函数setContextMenuPolicy()在代码中设置)
<2>. 在UI设计界面的按钮上单击右键,转到槽,在弹出的对话框中选择customContextMenuRequested(const QPoint&),单击肯定,为按钮的该信号指定槽函数,在代码中能够经过connect手工关联。
<3>. 在该槽函数中生成菜单代码以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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());这条语句是显示菜单用的,执行以后菜单才能显示出来,因此每次槽函数被执行的时候都须要调用一次来呼出菜单。

最终显示效果以下:
1

除了上面的方法以外,还能够经过重写contextMenuEvent()事件来实现右键菜单,这里就不细说了,能够自行百度。

结束语

本篇总结性的讲解了Qt的诸多方面的知识点,有些地方限于篇幅,可能须要单独另起一篇文章进行讲解,有的是我本身也并无彻底弄透彻怕误人子弟。因为公司须要开发一个窗口程序,要求不须要安装附带的框架,因此.NET就被排除在外了,由于公司以前有同事使用WPF开发过其余的程序,界面也比较漂亮,可是工程部的同事在外面部署的时候因为安装框架的缘由常常出现各类系统问题。至于MFC太古老,学习周期长,因此也被排除,另外两个一个是Flex AIR,一个是Qt,权衡之下仍是选择了Qt,通过一个月的边学边作,效果还能够。其实Qt仍是比较好学的,基本上熟练掌握了QSS的话,也能够实现很是好的界面效果,并且仍是跨平台的,特别是在嵌入式系统中,若是须要显示界面的话,会是一个很是好的选择。但愿这篇文章对你们有所帮助,因为篇幅比较长,虽然我已经检查过,若是发现文字错误,还但愿园友不吝指正,我会及时改正。

相关文章
相关标签/搜索