说明
最近加入T公司,需要使用Qt进行PC下的界面应用开发,因此总结了一些Qt的常见用法在这里,以便学习(我一个linux后台开始玩Qt,也是造化弄人);
QT UI
QT开发过程中,QT通过一个后缀为ui的文件来描述应用程序的界面信息,该ui文件内容实质上是要给xml文件。
在开发过程中,可以通过相关的UI工具(例如qtDesinger)等工具生成该ui文件。
在编译过程中,该ui文件会被qt的ui处理工具uic转换为标准的c++文件, 生成的标准c++文件可以被引用以及被标准c++编译器编译。
非集成开发环境下,uic工具使用如下:
在集成开发环境中,大部分情况下ui文件会被转换为ui_XXX.h文件。
Qt 资源文件
在QT应用开发中,通过qrc文件描述应用使用到的资源文件(图片,音视频等)。Qrc文件的实质是一个xml文件,在文件中描述了资源名称与资源文件的关系,qt提供了一个工具rcc将qrc文件转换成标准的cpp文件,然后可以通过标准c++编译器使用该资源文件信息。
对于外挂资源,可以通过在rcc工具执行时添加-binary参数达到,添加后生成一个rcc文件,在代码中通过QResource::registerResource("/path/to/myresource.rcc");可以将该外挂资源集成到应用程序内部进行使用。
QT UI文件编译
Vs2019开发环境与QT集成后,ui界面文件x.ui对应的ui_XX.h文件由uic.exe自动生成,该生成文件路径可以通过以下方式设置:
右键ui文件选择属性,调整output变量取值。
QT+VS2019命令行编译
项目目录下执行以下命令
qmake -project QT+=widgets
qmake
nmake Debug Release
注意事项
打开的vs2019 cmd命令行,需要根据实际情况选择对应的架构x86或x64
QT MOC机制
QT的元对象系统是一个基于标准C++的扩展,能够使c++更好的适应GUI编程,支持开发人员在执行期获取对象的信息,同时这一机制也支持属性以及文本翻译(国际化,多语言)。
由于标准C++并没有提供动态元信息的支持,因此qt通过一个单独的工具moc来处理这个问题,moc工具会解析头文件中被Q_OBJECT宏声明的类,并生成一个单独的标准cpp文件来实现,因此对任何c++编译器而言,QT的moc机制都可以正常工作。
在具体的代码编写中,只有继承自QObject的类中,才可以通过宏Q_OBJECT进行修饰,从而转变成要给元对象。
对于一个元对象,它有如下一些特点
1:可以支持QT特有的signals和slots机制,在不同对象之间进行通信。
2:可以支持QObject::metaObject方法,用于返回类关联的元对象。
3:可以支持QMetaObject::className,用于在执行期获取对象的类名。
4:可以支持QObject::inherits方法,用于判断一个对象是否继承自QObject
5:可以支持QObject::setProperty和QObject::property方法,用于动态的设置和获取属性名称。
6:可以支持通过QMetaObject::newInstance用于构造一个新的实例。
在使用QT的MOC机制时,存在的一些限制
1:不能对模板类使用MOC机制(模板类无法继承自QObject)。
2:多重继承时QObject必须是第一个,且不支持QObject的虚继承。
3:函数指针不能作为信号或槽的参数。但通过typedef简化后的函数指针可以作为槽函数的参数。
4:信号和槽函数的参数必须是完全名称限定。
5:内嵌类不能有信号和槽。
6:信号和槽函数返回不能是引用。
QT中的信号和槽
信号(signal),就是特定情况下被发射的事件。
槽(slot)就是对信号的响应函数,就是一个函数。
Signal与slot之间的关联,是通过QObject::connect函数实现, 基本格式如下:
QObject::connect(sender,SIGNAL(signal()), receiver, SLOT(slot()))。
一个singal可以connect到多个slots;多个signal可以connect到同一个slot;一个signal可以connect到另外一个signal。
在使用signal与slots的类中,该类必须被宏Q_OBJECT修饰。
一般情况下,当一个signal被发射,与其关联的slot函数会立即被执行,只有所有slot函数被执行完成后,signal后的代码才会被执行。
自定义slot函数:
任意的类成员函数,普通全局函数,静态函数都可以当做slot函数处理。Slot函数必须与signal信号一致(参数和返回值)。
自定义信号:
信号必须通过关键字signal进行声明,signal没有返回值,但可以有参数,signal就是函数声明,无需实现。通过emit关键字进行signal的发射。
QT中几种connect到slot的方式
// 传统Qt4连接方式为 信号发送者,信号,信号接受者,处理函数
QObject::connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(qT4_slot()));
//Qt5的新方法,在编译的时候就会有监测,如果我们手误操作失误,就会出现问题
QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&Widget::qT5_slot);
//Qt5 Lambda表达式
//这里需要注意 Lambda表达式是C++ 11 的内容,所以,需要再Pro项目文件中加入CONFIG += C++ 11
QObject::connect(ui->pushButton_3,&QPushButton::clicked,[=](){qDebug()<<"lambda表达式";});
QT事件机制
事件的产生:
当用户按下鼠标/键盘等外部输入时产生事件,系统定时器,其他组件主动发起等也产生事件。
Qt中所有的事件都继承自QEvent基类。
任何事件发生后,都会生成一个Event对象,该对象会被传递给特定组件的event处理函数,该event对象包含事件发生时所需要交互的所有信息。
在应用执行过程中,用户可以产生自定义事件,有两种方式产生事件,一种是调用postEvent接口,另外是通过sendEvent接口产生,postEvent产生的是异步事件,事件会被放入Qt消息队列中,等待被依次处理,sendEvent产生的事件不会放入队列,而是直接派发和处理。
事件的处理:
每个组件都定义了一组mousePressEvent相关的函数,用于处理对应的事件。对于自定义组件,可以通过重写这些方法对事件进行特殊处理。
此外通过重新组件的event接口可以控制是否对某个事件进行处理。
事件过滤:
有多个组件对象,例如A,B,通过A监视B的事件,可以在A中处理传递给B的事件。其基本过程如下:
首先B调用installEventFilter接口注册监视对象A(如果有多个组件都需要被A监视,其他组件也需要调用该接口注册A)。
在A的eventFilter接口中对事件进行处理,即如果一个事件被发送给B,在该事件被B处理之前,会优先调用A的eventFilter接口,在该接口中可以具体完成特定功能。
QT关键组件及使用方法
GraphicView
Graphic View是QT4之后面向对象的绘图机制,主要包括三个主要的类,QGraphicsScene(场景),QGraphicsView(视图),QGraphicsItem(图元),其中QGraphicsScene本身不可见,是一个存储QGraphicsItem的容器,要呈现QGraphicsScene内的内容,必须将QGraphicsScene与QGraphicsView关联起来进行交互;QGraphicsScene主要提供了操作QGraphicsItem的接口,事件传递以及无变换绘制等功能。
QGraphicsView提供了一个窗口,用于显示QGraphicsScene中的QGraphicsItem,一个QGraphicsScene场景可以关联多个QGraphicsView进行显示。
QGraphicsItem是需要呈现的基础类,QT提供了一些已经封装好的标准类,主要有下面这些:
QGraphicsSimpleTextItem:提供了一个简单的文本标签项
QGraphicsTextItem:提供了一个格式化的文本项
QGraphicsLineItem:提供了一个直线项
QGraphicsPixmapItem:提供了一个图像项
QGraphicsRectItem:提供了一个矩形项
QGraphicsEllipseItem:提供了一个椭圆项
QGraphicsPathItem:提供了一个路径项
QGraphicsPolygonItem:提供了一个多边形项
QgraphicsView坐标系
Graphics View坐标系基于笛卡尔坐标系,图元的位置和几何形状通过x坐标和y坐标表示,当使用没有变换的试图观察场景时,场景中的一个单位对应屏幕上的一个像素。在GraphicsView架构中,主要有3个有效坐标系统。
图元坐标:图元坐标位于自己的本地坐标上,图元的坐标通常以图元中心为原点,x轴向右,y轴向下,在自定义图元时,只需要注意图元的坐标既可。
子图元的坐标与父图元的坐标相关,如果子图元无变换,子图元坐标与父图元坐标相同,父图元变换时,子图元会隐式的被变换,但子图元的坐标不会被父图元的变换所影响。
场景坐标:场景坐标是所有图元的基础坐标,场景坐标描述了最顶层的图元位置,每个图元在场景中都有场景坐标和边界矩形,场景坐标的原点在场景中心,x轴向右,y轴向下。
视图坐标:视图坐标是窗口部件的坐标,视图坐标的单位是像素,左上角是远点,为了与图元交互,需要将图元坐标转换为场景坐标。
在Graphics View中,提供了对应的接口在几个坐标系之间进行转换。
QGraphicsView::mapToScene()视图到场景
QGraphicsView::mapFromScene()场景到视图
QGraphicsItem::mapFromScene() 场景到图元
QGraphicsItem::mapToScene() 图元到场景
QGraphicsItem::mapToParent() 子图元到父图元
QGraphicsItem::mapFromParent()父图元到子图元
QGraphicsItem::mapToItem()本图元到其他图元
QGraphicsItem::mapFromItem()其他图元到本图元
例如如果想在view坐标的原点开始绘制一条线段,可以通过如下方式处理
1:调用view接口QGraphicsView::mapToScene()将view原点变换到scence坐标系中。
2:调用item接口QGraphicsItem::mapFromScene() 将坐标从scence变换为item坐标系,然后调用item的setLine接口进行线段绘制。
样例代码如下:
QgraphicsView上绘制可编辑文字
QgraphicsView展示图片
QGraphicsView上展示图片可以通过QPixmap类以及接口addPixmap实现。样例代码如下:
常见问题
1:error C2001: 常量中有换行符
QT编码中,字符串中存在中文等字符时,有时候会报“error C2001: 常量中有换行符”错误,对于该错误,可以在字符串后面添加一个空格规避。
2:QgraphicsView坐标对齐
默认情况下scence的原点会出现在view的中间,此时可以通过调用setAlignment进行对齐。
例如以左上角方式对齐:
view->setAlignment(Qt::AlignLeft | Qt::AlignTop); 在这种方式下绘图比较方便。
3:QgraphicsView曲线平滑
此外进行线条绘制时,默认并没有开启线条平滑,此时可以通过调用view->setRenderHint(QPainter::Antialiasing);设置平滑线条。