以下笔记为在看书和实践的过程中的部分记录总结:
0. 窗口布局
1) 支持绝对布局和布局管理器布局;
2) 绝对布局不够灵活、无法自动调整大小,需要手动编写代码调整;
3) 布局管理器管理布局比较灵活、可自动调整管理的其下部件大小、位置;
4) 布局管理器目前有基本布局QBoxLayout(水平布局QHBoxLayout、垂直QVBoxLayout)、栅格布局QGridLayout、表单布局QFormLayout、栈布局QStackedLayout;
5) 还有其他的布局,不过一般用于其他目的,如QPageLayout用于页文档。
1. 对象模型(要求必须继承自QObject类或其子类,部分特性需要meta-object system支持(即Moc预编译器))
一、信号与槽
1) 支持多对多的信号与槽函数的关联关系,QObject的connect静态函数实现信号与槽的关联(connect有多个重载函数);
2) 当某个信号关联到不同的槽函数时,不同槽执行的顺序是按照建立某信号时的顺序;
3) 信号只能声明且无返回值,此外由关键字signals指定,也不可有其他修饰限定符如public、static等;
4) 发射信号用emit关键字以及信号函数或可能的参数值,注意信号的参数应与槽的参数对应,并且信号的参数个数可以比槽的参数个数少,反之不可;
5) 槽函数slots关键字作为修饰限定符,可以有其他的修饰限定符如public、static等,也可以为虚函数;
6) connect函数最后一个参数为关联关系,默认为AutoConnection即槽执行完成后才返回执行emit后的代码;若为其他的如QueuedConnection则会继续执行emit后
的代码而无论槽是否执行或执行完成;具体效果还需要信号与槽所在线程是否为同一线程与否有不同的表现,disconnect可断开关联关系;
7) 支持信号与槽机制时,需要继承自QObject类或其子类并且在类声明开始时添加Q_OBJECT宏;
8) 自动关联信号与槽,一般声明槽格式如:on_pushButton_clicked(),其以on、部件对象名、信号加下划线组成;不再需要connect建立关联;
另外一定要在setupUI调用之前设置部件的对象名(因setupUI内部调用了connectSlotsByName,若setupUI之后设置组件对象名自动关联可能会失效),此外自动关联
只能支持已预先定义好的信号,不支持自定义信号;
9) 信号与槽机制使得发送者和接收者为松耦合的,参数类型、参数个数任意比较灵活,但是性能较差于回调函数机制的方式。
二、事件与事件过滤器
1. 事件顺序
1) 父部件对象的eventFilter;
2) 子部件的event;
3) 子部件的keyPressEvent或其他事件处理函数;
4) 父部件对象的keyPressEvent或与3)对象的事件处理函数;
5) 以上顺序依赖于每一步返回值或者3)步骤中调用了ignore函数与否;对于1),2)若返回值为true,则不会继续向下传递,3)若未调用ignore则不会向下传递。
2. 发送事件或自定义事件
1) QCoreApplication有静态成员函数sendEvent,postEvent以发送事件;
2) sendEvent发送事件将立即执行,postEvent则发送到等待调度的事件队列后立即返回;
3) sendEvent的事件对象需要在栈上创建QEvent对象或者事件执行后自行删除在堆上的QEvent对象;postEvent则需要在堆上创建QEvent对象,由事件队列中处理后自动
删除该QEvent对象(此时QEvent不可以为栈上的非静态对象)。
三、基于元对象的属性系统
1) 可通过在类声明中使用宏Q_PROPERTY向元对象系统注册属性,具体格式要求见文档说明;
2) 支持属性系统时,需要继承自QObject类或其子类并且在类声明Q_OBJECT宏后添加Q_PROPERTY;
3) 可使用setProperty、property接口函数设置或获取属性值;此外也可以通过此两个接口函数动态添加额外的类不存在的属性/值,不过只针对当前类对象有效,动态属性
可实现存取额外数据的目的。
四、国际化翻译机制
一般通过QObject的tr或trUtf8实现字符串翻译国际化;
五、定时器
1) 基于QObject的startTimer,需要killTimer销毁该计时器,此外虚函数timerEvent将被定时调用;
2) 基于QTimer以及信号与槽机制的定时器(timeout信号);
3) 基于QTimer的singleShot静态成员函数执行一次的定时器。
六、分层结构、对象树拥有权
0) 使用对象树组织、管理所有继承于QObject或其子类的对象;
1) 若构造组件对象时,指定了父部件对象或者setparent,则父部件对象在销毁时会自动销毁其维护的子部件对象列表;
2) 对于未指定父部件且在堆上构造却未调用delete的将出现内存泄露;
3) 对于指定了父部件且在栈上构造的将出现两次销毁该组件对象,导致异常;
4) 对于指定了父部件且在堆上构造,后手动销毁该组件对象,此将自动使得其从父部件对象的子部件对象列表中移除,不会出现销毁多次的情况;
5) 一般策略:指定父部件且堆上构造,或者不指定在栈上;又或者对于QWidget或对话框之类的可以在堆上构造且不指定父部件对象、
设置Qt::WA_DeleteOnClose属性也可不用手动销毁该组件对象,其会在关闭窗口的时候自动销毁该组件对象;
6) 重定义父部件,一般为将不同的且未指定父部件对象的组件添加入布局管理器,此后将该布局管理器设置到QWidget或其子类的窗口部件的布局中时,其管理的
各个组件的父部件会自动设置为该QWidget或其子类的窗口部件对象,达到了生命拥有权移交。
七、守卫指针QPointer
针对QObject或其子类的对象的指针的范围管理,当引用的对象自动释放的时候置为0,避免其引用的对象变为野指针。
八、动态转换机制dynamic cast
主要使用qobject_cast转换QObject类型的对象;类似于C++的dynamic_cast,但不需要RTTI的支持。
九、支持自定义类型创建(QMetaType元类型对象)
2. 模态对话框和非模态对话框
1) 一般情况下调用exec或open则为模态对象框,调用show则为非模态对话框;
2) 调用show之前,通过设置setModal(true)或setWindowModality(Qt::WindowModal/Qt::ApplicationModal),也可成为模态对话框;
3) setModal(true)效果同setWindowModality(Qt::ApplicationModal);
4) 一定要注意该对话框的使用,即对象模型中的对象树生命周期。
3. 容器类型、迭代器
1) 常用的几种容器对象以及对应的STL风格的迭代器、Java风格的迭代器、部分部基于模板的算法(只实现了部分类似于STL的重要的算法);
2) 部分容器采用隐式共享机制,以减少内存占用和拷贝操作等。
4. 界面外观
1) 目前一般有QT风格QStyle、调色板QPalette、QT样式表Qt Style Sheets、paintEvent事件重绘实现、以及针对Text Widgets的HTML标记支持(如QLabel、QTextEdit等
有rich text支持的);
2) QStyle、QPalette、Qt Style Sheets均可作用于整个窗体或某个组件,若均有设置则生效时按各个组件的设置优先(待验证);
3) 基于风格QStyle一般子类化QStyle或其子类(当前平台支持的风格类)(此外还可以子类化QCommonStyle、QProxyStyle重实现其部分接口即可),此外调用setStyle设置指定风格;
4) 调色板QPalette通过palette获取调色板,setPalette设置指定颜色角色的颜色(调色板受限于不同平台的准则和平台主题引擎);
5) QT样式表Qt Style Sheets可通过setStyleSheet设置窗体或组件的样式(如背景图片、渐变、颜色、字体等);此外还可以通过设计器界面设置sheet属性实现,具体格式见文档说明;
6) Qt Style Sheets设置部分组件或窗体时支持的风格有限,可结合paintEvent事件自定义重绘实现;
7) QT样式表层叠(即通过QApplication的以及该组件的父类或祖先类样式,与自己的样式合并后的)以至于冲突时,规则为:特殊优先于一般,同等级别下,则后面的设置优先于前面的设置
(即某对象优先于某类,某对象伪状态优先于某对象等)。
5. 自定义部件界面外观、换肤
1) 部件模型:使用样式表时,部件可视作四层环形(矩形)盒;由外至内分别为:边距(margin)、边框(border)、填衬(padding)、内容(content),默认边距、边框、填衬为0重合;
2) 背景图片(background-image)覆盖:默认背景图片覆盖边框以内且不随大小改变,border-image可自动缩放,当同时指定时border-image覆盖在background-image之上,另外
image也可在border-image之上绘制;
3) 结合样式表以及其他相关属性值的设定以自定义部件的外观;
4) 换肤功能:一般做法为将设置好的样式表内容放置在文本文件中如qss文件,通过读取内容调用setStyleSheet达到换肤的目的;另外一种通过执行可执行文件时附加参数实现,
即:someApp(或someApp.exe) -stylesheet xxx.qss;不同的皮肤加载不同的qss文件即可。
6. 规则、不规则窗体、透明窗体或部件
1) 规则窗体可使用样式表实现如矩形、圆形、椭圆等外形;
2) 不规则窗体可通过图片的大小以及掩码来设置当前窗体或部件的大小和掩码(resize/setMask),并在paintEvent绘制事件中通过QPainter来绘制当前不规则背景图片;
除了图片来设置掩码,还可以通过QRegion等设置掩码;
3) 透明窗体或部件,对于部件则可通过样式表的背景色的rgba中的alpha通道值来实现透明;而对于*窗口则可调用setWindowOpacity实现透明,不过会影响*窗口中的
各部件也会透明;若需要使得子部件不受影响,则需要调用setAttribute(Qt::WA_TranslucentBackground)(windows下还需要调用Qt::FramelessWindowHint窗口标识)此可能
导致全透明且没有标题栏(若需要则需要手动添加),为了实现半透明可重写paintEvent重绘事件(即可通过QPainter、fillRect以及RGBA来实现指定区域的半透明);
4) 除了以上的方式实现窗体或部件效果外,还有其他的方式实现如QT的图形视图框架。
7. 国际化
一、翻译机制操作步骤
1) 使用QObject的tr函数或者QCoreApplication的translate函数来引用需要显示、翻译的字符串信息(一般为可见的部件或窗口的文本、加速键);
或者QT_TR_NOOP、QT_TRANSLATE_NOOP等宏包含的字符串也便于lupdate提取;此外tr函数可结合arg的%1~%99等实现动态参数文本的翻译;
2) 配置工程pro文件,添加TRANSLATIONS = i18n_zh_CN.ts,文件名称可任意,一般以区域+语言的形式命名;
3) 通过lupdate命令工具(lupdate xxx.pro)或者QTcreator中的工具->外部工具->Qt预言家->更新翻译(lupdate);
从工程UI文件(包括窗体、部件、加速键等文本)以及C++等文件中提取待翻译的字符串信息;并产生i18n_zh_CN.ts文件(xml格式);
4) 通过QT Linguist工具,手动翻译ts文件中的各个待翻译的字符串并保存;
5) 运行lrelease命令工具(lrelease i18n_zh_CN.ts)或者QTcreator中的工具->外部工具->Qt预言家->发布翻译(lrelease);将ts文件转化为qm二进制文件;
6) 使用qm文件:一般在main中QApplication a(arg, argv)后添加QTranslator translator;translator.load(file,dir);a.installTranslator (&translator);以实现加载并
安装翻译对象(注意:一定要在创建部件代码之前安装翻译对象);(file不用带qm文件格式后缀,dir不用/作为目录路径结束符)
对于国际化非QT类,则需要在类声明开始处添加Q_DECLARE_TR_FUNCTIONS宏(事实上该宏内部添加了一个tr静态函数以及QT_DECLARE_DEPRECATED_TR_FUNCTIONS宏,
QT_DECLARE_DEPRECATED_TR_FUNCTIONS宏内部有trUtf8静态函数,tr函数以及trUtf8函数内部其实调用的是QCoreApplication的translate静态函数实现翻译显示)。
二、编码转换、读写
1) QTextCodec类提供文本编码间转换的功能;如unicode与非unicode编码的相互转换(fromUnicode、toUnicode或fromUft8、fromLocal8Bit),以及转化文本地8位的toLocal8Bit,
转化为utf8的toUft8;
此外还有很多其他的转换接口;QTextCodec还提供了一系列codecForXXX的获取编码对象,常用的codecForName、codecForLocale;
2) QTextStream类提供了方便的文本读写操作的功能。
8. 帮助系统
0) 通过QToolTip的showText等静态函数实现信息提示;
1) 通过QAction的setToolTip、setStatusTip来设置tooltips、状态栏提示,以支持简单的帮助信息;
2) 通过添加QAction的QWhatsThis支持,以进入 What's This?模式(WhatsThis::createAction);
3) 通过QTextBrowser支持富文本只读信息提示,也可支持HTML格式内容;
4) 通过定制QT Assistant;
步骤:
1. 创建编辑各HTML帮助文档信息(一般为html格式);
2. 创建编辑QT 帮助项目文件qhp(xml格式内容,文件名后缀qhp),其内部主要组织步骤1中的各个HTML文件;
3. 通过qhelpgenerator命令工具将qhp生成QT帮助压缩文件qch(二进制格式);
4. 事实上不需第三步,第四步中可包含第三步骤的操作;创建编辑qhcp文件(xml格式内容,文件名后缀qhcp)同样的组织步骤1、步骤2的文件;
5. 通过qcollectiongenerator命令工具将qhcp生成为帮助集合qhc文件(二进制格式);可通过"assistant -collectionFile xxx.qhc"测试该文件;
6. 应用程序内部QT Assistant;一般做法为程序启动一个进程,即由QProcess开启assistant进程,进程参数为“-collectionFile xxx.qhc”,此外一般还需要
在开启进程后默认指定显示的页面,则通过向该进程发送"setSource qthelp://xxx.html\n"命令;
以上步骤中各类文件的编写可参考QT的 Customizing QT Assistant 或The Qt Help Framework关键字手册;
5) 通过QHelpEngine API内嵌帮助文档(QHelpEngineCore)(通过加载qhc帮助集合文件),一般由QHelpEngineCore的fileData获取的文件数据并借助于QTextBrowser显示,
当然也可以通过QWhatsThis显示。
9. update与repaint区别
update 内部使用postEvent,请求刷新,一般情况下不能立即刷新,此外对于频繁调用updte则可能会被合并为一条更新请求(内部维护一个事件列表,若该请求与已存在的事件列表中一致,
则新的请求将不会被加入事件列表,直到事件列表执行完旧的事件为止)。此外update一般可用在同一个线程或者不同线程调用刷新;因真正刷新的是GUI线程。
repaint 内部使用sendEvent,一般可立即刷新,因是立即刷新,故一般情况下因在同一个线程调用repaint和GUI的刷新。尤其是OpenGL绘制渲染多线程不可简单的直接刷新,
可采用moveToThread来实现多线程(QThread线程)渲染。此外若不同线程仍然调用repaint刷新,则还可以采用发送事件或者信号(信号连接类型为Qt::BlockingQueuedConnection)。
10. 绘图系统
待续......