前言
由于相关的工作和学习需要使用到Qt,因此,接下来的一段时间就对Qt进行学习并做记录。
基础
首先,对于Qt是什么、干嘛的,就不赘述了,也没什么意思。
在从官网上下载了Qt之后,我们直接在本地得到了Qt Creater这个专门用来开发Qt相关程序的IDE。
不过现在VS也是支持Qt,之后我们也需要转到VS上来使用Qt。
HelloWorld
对于Qt,它的Hello World,就是输出一个标题为Hello World的窗口罢了。
首先,打开Qt后,新建项目,然后选择一个空的qmake project,就可以进行代码的尝试和学习了。
打开后,会有一个.pro
的文件。
此时,右键新建一个文件,选择.cpp
,可以将其命名为main
,来作为我们的主函数。
在main.cpp
中,代码如下:
//学习HelloWork与按钮机制
//应用程序抽象类
#include <QApplication>
//窗口类
#include <QWidget>
//按钮类
#include <QPushButton>
int main(int argc, char* argv[])
{
QApplication app(argc,argv);//构建了一个应用程序
QWidget w;//w是一个窗口对象
//注意,按钮也是一个窗口
//如果我们不去构建父子关系,那么按钮和上面的窗口会独立的显示
//控件,都是窗口
QPushButton button;
button.setText("button!");
//窗口对象的父子关系,会影响显示的位置
//没有父亲窗口的窗口,称之为主窗口
button.setParent(&w); //这里,构建了一个对象的父子关系
//当button被点击时,窗口就被关闭了
//此为Qt对C++的拓展
//前两个参数称为信号
//后两个参数称为槽
//调用信号函数时,会导致槽函数被调用
//用第三方的方式,将两个模块联系起来
QObject::connect(&button, SIGNAL(clicked()), &w, SLOT(close()));
w.setWindowTitle("hello");
//显示窗口
w.show();
return app.exec(); //其中是一个死循环——消息循环
}
但是,当我们添加过上面所写的头文件:
//应用程序抽象类
#include <QApplication>
//窗口类
#include <QWidget>
//按钮类
#include <QPushButton>>
此时是会报错的,我们需要打开.pro
文件,此时在其中已经有了:
SOURCES += \
main.cpp
我们只需要添加一段代码,完整版如下:
SOURCES += \
main.cpp
QT += widgets gui
这样,代码就能顺利通过编译了。
在上面的例子中,需要注意的是:
- 头文件不要写错了。
.pro
文件内的代码一定要进行添加,否则头文件会报错。- 所有的控件都是一种窗体。
- 对象的父子关系,需要搞清楚。如果不声明这种父子关系,就会让每个窗体都成为主窗口。
- 信号和槽机制是Qt的一大特点,也是对C++的一个拓展。
w.show()
语句一定要在没有什么要改变和声明时,再写。app.exec()
是一种消息循环,即一种死循环;其功能有点像C++中的system("pause");
EditLine
在此之后, 对Qt中的输入窗体学习一下。
首先,在.pro
文件内的代码是不变的。
一样的,创建一个main.cpp
,其中,代码如下:
//学习输入窗体
//应用程序抽象类
#include <QApplication>
//窗口类
#include <QWidget>
#include <QLineEdit>
#include <QCompleter>
int main(int argc, char* argv[])
{
QApplication app(argc,argv);//构建了一个应用程序
QWidget w;//w是一个窗口对象
QLineEdit edit;
edit.show();
edit.setParent(&w);
//输入密码
//edit.setEchoMode(QLineEdit::Password);
//edit.setPlaceholderText("please input text"); //在输入框中显示信息
//自动补全
QCompleter completer(QStringList()<<"lan"<<"apple"<<"bri");
//设置匹配规则,如果不设置,默认以开头做匹配;这里设置的是以包含做匹配
completer.setFilterMode(Qt::MatchContains);
edit.setCompleter(&completer); //提示并自动补全
w.setWindowTitle("hello");
//显示窗口
w.show();
return app.exec(); //其中是一个死循环——消息循环
}
在上面的例子中,需要注意的是:
- 需要添加的新的头文件为
#include <QLineEdit>
和#include <QCompleter>
。 - 当我们想要输入密码时,即输入的是黑色的、不可见的点,实现的语句为
edit.setEchoMode(QLineEdit::Password);
其中,Password
为一个关键字,可以换成别的关键字以实现不同的密码键入类型。 - 实现自动补全时,不能在密码模式里。(废话
- 上面的
completer
也是一个对象,其中,在构造函数时,定义了它用于自动联想的一种字符串表。 completer.setFilterMode(Qt::MatchContains);
中,MatchContains也是一种匹配关键字。- 仔细想想,就是常见一个对应类型的对象,然后在调用它们各自对应类型内的函数与方法而已。很像Unity3D的模式。
Cood
接着,对Qt中的坐标系统进行一些学习:
//学习相关的坐标系统
//应用程序抽象类
#include <QApplication>
//窗口类
#include <QWidget>
//按钮类
#include <QPushButton>
int main(int argc, char* argv[])
{
QApplication app(argc,argv);//构建了一个应用程序
QWidget w;//w是一个窗口对象
QPushButton button;
button.setText("button!");
//窗口对象的父子关系,会影响显示的位置
//没有父亲窗口的窗口,称之为主窗口
button.setParent(&w); //这里,构建了一个对象的父子关系
//设置button的坐标与大小
//其中,坐标体系的值是基于父对象的坐标体系的值
button.setGeometry(30,30,100,30);
QObject::connect(&button, SIGNAL(clicked()), &w, SLOT(close()));
w.setWindowTitle("hello");
w.show();
return app.exec(); //其中是一个死循环——消息循环
}
值得注意的是:
- 坐标系统不需要单独的头文件,是包含在窗体的一个
setGeometry()
方法。 - 在这个
setGeometry()
方法中,有四个参数:前两个参数是对应的窗体的坐标;后两个参数则是窗体对应得大小。单位为像素。 - 坐标体系的值是基于父对象的坐标体系的值。
Layout
如果我们每次都使用setGeometry()
方法来设置窗体的位置,未免太过于麻烦。因此,在Qt中,还有一个Layout可供使用:
//学习Layout
//应用程序抽象类
#include <QApplication>
//窗口类
#include <QWidget>
//按钮类
#include <QPushButton>
//标签类
#include <QLabel>
//以竖直的方式进行排列的layout
#include <QVBoxLayout>
//以水平的方式进行排列的layout
#include <QHBoxLayout>
//带有网格的layout
#include <QGridLayout>
#include <QLineEdit>
int main(int argc, char* argv[])
{
QApplication app(argc,argv);//构建了一个应用程序
QWidget w;//w是一个窗口对象
QPushButton button;
button.setText("button!");
//button.setParent(&w);
QLineEdit edit;
//edit.setParent(&w);
//同时,我们也可以将button和edit对于w的父子关系的声明给取消
//因为两个子窗口包含在了layout中,而layout又在w中
//但是,需要注意的时,layout本身不是一个窗口
//layout继承自QObject
#if 0 //表示将代码注释
//QVBoxLayout layout;
QHBoxLayout layout;
//添加了一根弹簧
//这样,当我们拖动窗口时,子窗口部件就不会随着窗口变化而变化了
layout.addStretch(1);
layout.addWidget(&edit,1);
layout.addSpacing(50); //在两个窗体中间添加一点距离
layout.addWidget(&button,1);
layout.addStretch(1);
//当我们左右都添加了弹簧后,子窗体自然就被挤到中间了
//同时,在addWidget中,我们也可以设置第二个参数
//第二个参数也是stretch,表示当前窗体所占比例
//如面所示,此时,相当于一种四分天下
#endif
#if 0
//带有网格的layout
//其中,参数表示第几行、第几列
QGridLayout layout;
layout.addWidget(&button,0,0);
layout.addWidget(&edit,0,1);
layout.addWidget(new QPushButton("aaa"),1,0 );
layout.addWidget(new QPushButton("bbb"),1,1);
//后面两个参数,表示按钮所占的大小
//即,占有一行两列
layout.addWidget(new QPushButton("ccc"),2,0,1,2);
//设置行和列的弹簧,将四个子窗口挤到左上角
layout.setColumnStretch(2,1);//设置第2列的弹簧,弹簧的比重是1
layout.setRowStretch(3,1); //设置第3行的弹簧,弹簧的比重是1
//需要注意的是
//因为带有网格的layout是有坐标的,
//因此,我们就不需要像前面所说的两类layout那样,必须注意先后顺序
#endif
QGridLayout layout;
QLineEdit *password;
layout.setColumnStretch(3,1);
layout.setRowStretch(4,1);
layout.setColumnStretch(0,1);
layout.setRowStretch(0,1);
layout.addWidget(new QLabel("Username "),1,1);
layout.addWidget(new QLineEdit(),1,2);
layout.addWidget(new QLabel("Password "),2,1);
layout.addWidget(password = new QLineEdit(),2,2);
password->setEchoMode(QLineEdit::Password);
//用layout的方法书写一个登陆界面
//layout可以被嵌套
QHBoxLayout *hBox;
//在一个layout中添加另一个layout,以实现单独的操作
layout.addLayout(hBox = new QHBoxLayout,3,2);
hBox->addStretch(1);
hBox->addWidget(new QPushButton("Login"));
w.setWindowTitle("hello");
w.show();
w.setLayout(&layout);
//此时,层次关系如下:
//窗口之中有一个layout
//layout中有两个子窗口
//在layout中的窗口,都会自动的根据layout中的规则来进行排列
QObject::connect(&button, SIGNAL(clicked()), &w, SLOT(close()));
return app.exec();
}
Layout中的点比较多,值得注意的如下:
- Layout需要头文件。不同形式的Layout所需要的头文件也各不相同。具体可以见上述的代码。
#if 0
和endif
是一种注释方法。- Layout是可以被嵌套的。形式见上面的代码。
- Layout其实就是相当于一种已经内嵌好的排列办法,当声明使用了这样的Layout之后,系统会自动的帮助你进行排列。
- 实现的层次关系:窗口之中包含了一个layout,而layout之下又嵌套了一个layout和两个窗口。因此,子窗口对于父窗口的声明关系可以不写,因为已经包含在了layout之中。
- 不过,layout本身并不是一个窗口,它继承自
QObject
。 - 上述代码实现的结果图: