关于QT的系统总结(非常全面,非常好)

Posted 朝闻道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于QT的系统总结(非常全面,非常好)相关的知识,希望对你有一定的参考价值。

 

编译环境与开发流程

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

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

下面将对QtCreator的界面各个功能进行大致的介绍:1

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

QT项目的构成及原理

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

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

1

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

我们使用的是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(i

以上是关于关于QT的系统总结(非常全面,非常好)的主要内容,如果未能解决你的问题,请参考以下文章

关于C++ const 的全面总结《转》

Qt: 绘图基础(非常简洁明了,全面)

MySQL/Oracle数据库优化总结(非常全面)

qt系统托盘显示无主窗体

张小龙4小时“拖堂”演讲,全面总结微信8年(2019-01-09 转)

Android应用坐标系统全面具体解释