前言
网上关于QT自定义控件的介绍很多,本文不做具体自定义控件编写的详细介绍,本文的主要目的是介绍一个自己在用的一个工程框架cutewidgets。
一、cutewidgets是什么?
cutewidgets是一个编写Qt Designer自定义控件的框架工程,主要作用的方便自动化部署到本地QT环境,其中也包含测试例程的编写,方便测试。内含一个简单的自定义控件和测试例程。
友情提示:新手在看下面内容时,最好先下载源码,对照阅读,效果会更好。老手就不用了,看个思路就行,当然若是老手能使用这个框架,那也不胜荣幸。
源码放在GitHub上了,可自行下载。本来是想放在gitee上的,无奈因为一些认证原因,就没折腾。GitHub的无奈用过的人都知道。
二、工程结构
项目 | 说明 |
---|---|
Git相关 | .git,.gitattributes,.gitignore,README.md为git版本控制相关文件,不做解释 |
bin | 测试例程运行目录 |
designer | 自定义控件插件源码 |
examples | 测试例程 |
include | 拷贝源码相关头文件的目标路径,作为普通动态库提供的头文件 |
lib | 动态库或静态库输出目录 |
plugin | 自定义控件插件输出目录 |
src | 自定义控件源码 |
配置文件 | cutewidgets.pri,cutewidgets.pro,cutewidgetsbuild.pri,cutewidgetsconfig.pri,cutewidgetsfunctions.pri工程及编译相关配置文件 |
三、框架的工程配置
本部分内容着重介绍cutewidgets框架的工程配置,主要涉及pro,pri等文件的编写。
1 cutewidgets.pro
工程的总入口,内容如下:
TEMPLATE = subdirs
CONFIG += ordered
include(cutewidgets.pri)
SUBDIRS += src
contains(CUTEWIDGETS_CONFIG, CuteWidgetsExamples) {
SUBDIRS += examples
}
contains(CUTEWIDGETS_CONFIG, CuteWidgetsDesigner) {
SUBDIRS += designer
}
模板为子项目subdirs,比较简单,src默认加载,examples和designer项目按工程配置选择性加载。
这里有个小问题,不知是不是Qt Creator的bug,在配置文件去除examples和designer的配置后,虽然这两个子项目不参与编译,但它们还在整个工程结构里,但可以看到加载的文件都变灰了。这就引入了一个问题。这些灰掉的文件在还在使用它们的子项目里变成了有错误的代码,而QT6版本的Creator有个尿性就是一旦你的文件有错误提示,它的查看或者补全等便捷的编码工具都失效了。这样写起代码来就太痛苦了。所以在编码阶段最好不要把examples和designer去掉。
2 cutewidgets.pri
整个工程的公共配置文件,内容如下:
################################################################################
# CONFIG配置文件
################################################################################
include($$PWD/cutewidgetsconfig.pri)
################################################################################
# 函数定义
################################################################################
include($$PWD/cutewidgetsfunctions.pri)
################################################################################
# 构建配置
################################################################################
include($$PWD/cutewidgetsbuild.pri)
它其实就是三个配置文件的包,方便引用,所有引用该文件的地方都会有这么一个包出现
2.1 cutewidgetsconfig.pri
顾名思义,这个文件为配置文件,内容如下:
################################################################################
# Source paths
################################################################################
CUTEWIDGETS_ROOT = $$PWD
CUTEWIDGETS_OUTPUT_LIB = $$CUTEWIDGETS_ROOT/lib
CUTEWIDGETS_OUTPUT_PLUGIN = $$CUTEWIDGETS_ROOT/plugin
######################################################################
# Install paths
######################################################################
CUTEWIDGETS_INSTALL_PREFIX = $$[QT_INSTALL_PREFIX]
CUTEWIDGETS_INSTALL_DIR_NAME = QtCuteWidgets
CUTEWIDGETS_INSTALL_HEADERS = $${CUTEWIDGETS_INSTALL_PREFIX}/include/$$CUTEWIDGETS_INSTALL_DIR_NAME
CUTEWIDGETS_INSTALL_LIB = $${CUTEWIDGETS_INSTALL_PREFIX}/lib
######################################################################
# Designer plugin
# creator/designer load designer plugins from certain default
# directories ( f.e the path below QT_INSTALL_PREFIX ) and the
# directories listed in the QT_PLUGIN_PATH environment variable.
# When using the path below CUTEWIDGETS_INSTALL_PREFIX you need to
# add $${CUTEWIDGETS_INSTALL_PREFIX}/plugins to QT_PLUGIN_PATH in the
# runtime environment of designer/creator.
######################################################################
CUTEWIDGETS_INSTALL_PLUGINS_FOR_DESIGNER = $${CUTEWIDGETS_INSTALL_PREFIX}/plugins/designer
CUTEWIDGETS_INSTALL_PLUGINS_FOR_CREATOR = $${CUTEWIDGETS_INSTALL_PREFIX}/../../Tools/QtCreator/bin/plugins/designer
######################################################################
# Build the static/shared libraries.
# If CuteWidgetsDll is enabled, a shared library is built, otherwise
# it will be a static library.
######################################################################
CUTEWIDGETS_CONFIG += CuteWidgetsDll
######################################################################
# If you want to build the CuteWidgets designer plugin,
# enable the line below.
# Otherwise you have to build it from the designer directory.
######################################################################
CUTEWIDGETS_CONFIG += CuteWidgetsDesigner
######################################################################
# If you want to auto build the examples, enable the line below
# Otherwise you have to build them from the examples directory.
######################################################################
CUTEWIDGETS_CONFIG += CuteWidgetsExamples
######################################################################
### custom widget below ###
######################################################################
######################################################################
# CuteWidgetsTestEdit enables all classes, that are needed to use the
# CuteWidgetsTestEdit classes.
######################################################################
CUTEWIDGETS_CONFIG += CuteWidgetsTestEdit
前半部分是一些变量的定义,包括编译输出路径和部署的QT环境下的一些路径定义。
CUTEWIDGETS_INSTALL_DIR_NAME变量做下说明,QT下include文件夹下都会增加一层模块的文件夹,然后才是相关头文件定义,为了保持统一,我们也定义一个。
CUTEWIDGETS_INSTALL_DIR_NAME = QtCuteWidgets
CUTEWIDGETS_INSTALL_HEADERS = $${CUTEWIDGETS_INSTALL_PREFIX}/include/$$CUTEWIDGETS_INSTALL_DIR_NAME
再延伸下,pro文件定义的变量如何在cpp中使用,就拿我们的测试控件里的内容说明。
我们在designer.pro文件中定义了CUTEWIDGETS_STR变量,并且赋值为CUTEWIDGETS_INSTALL_DIR_NAME
#########################################################################
# 定义QT路径下include下文件名,所有install的头文件放于
# QT_INSTALL_PRFIX/include/CUTEWIDGETS_STR中
#########################################################################
DEFINES += CUTEWIDGETS_STR=$$CUTEWIDGETS_INSTALL_DIR_NAME
下面我们看看如何在designer_plugin.h中把这个值转换成c++可识别的变量。
其实主要是这两行起到的作用
#define STR(R) #R
#define STRVALUE(R) STR(R)
第一行用 # 获取宏的名称,第二行获取宏的值。这样就获取了pro文件的变量,这样做的目的是为了统一,install的路径和自定义控件的头文件定义就一致了,其实QT的自定义控件模板工程的做法是直接把自定义控件的头文件放在QT的include文件下,我们是为了和人家文件结构保持一致,才加了这么个变量,没办法,谁叫咱有洁癖呢 (^ _ ^)。
后半部分为CUTEWIDGETS_CONFIG变量的配置,前面提到的配置examples和designer是否加载即在这里,这里简单说下qmake函数contains,熟悉qmake函数的请自行跳过。
contains(CUTEWIDGETS_CONFIG, CuteWidgetsDesigner) {
...
} else {
...
}
就是判断第一个变量里是否包含第二个变量,qmake的函数的左大括号必须和函数在一行,不能另一起一行
#错误用法
contains(CUTEWIDGETS_CONFIG, CuteWidgetsDesigner)
{
...
} else
{
...
}
最后一段开始是你可以自由发挥的地方,注释说的也很清楚了,再添加其他自定义控件时,依次添加就好了。
######################################################################
### custom widget below ###
######################################################################
######################################################################
# CuteWidgetsTestEdit enables all classes, that are needed to use the
# CuteWidgetsTestEdit classes.
######################################################################
CUTEWIDGETS_CONFIG += CuteWidgetsTestEdit
CUTEWIDGETS_CONFIG += CuteWidgetsXXX1
CUTEWIDGETS_CONFIG += CuteWidgetsXXX2
类似CuteWidgetsTestEdit这种配置会同时决定src,examples和designer三个子项目工程是否添加该自定义控件。这里编写时可以参考自带的测试控件例程。
DEFINE_CUTEWIDGETS_TESTEDIT不用想也肯定和cutewidgetsconfig.pri里的CuteWidgetsTestEdit有关,在CuteWidgetsTestEdit决定的testedit.pri文件里定义如下:
总之CuteWidgetsTestEdit的目的就是添加或抹除所有和该变量相关的工程文件,后续再添加的自定义控件的写法要参照这个测试控件。
2.2 cutewidgetsfunctions.pri
这个文件比较简单,就是参照QT的写法定义了两个函数
defineReplace(cuteWidgetsLibraryTarget) {
unset(LIBRARY_NAME)
LIBRARY_NAME = $$1
contains(TEMPLATE, .*lib):CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
win32:RET = $$member(LIBRARY_NAME, 0)d
}
}
isEmpty(RET):RET = $$LIBRARY_NAME
return($$RET)
}
defineTest(cuteWidgetsAddLibrary) {
LIB_PATH = $$1
LIB_NAME = $$2
LIBS *= -L$${LIB_PATH}
unset(LINKAGE)
isEmpty(LINKAGE) {
if(!debug_and_release|build_pass):CONFIG(debug, debug|release) {
win32:LINKAGE = -l$${LIB_NAME}d
}
}
isEmpty(LINKAGE) {
LINKAGE = -l$${LIB_NAME}
}
!isEmpty(QMAKE_LSB) {
QMAKE_LFLAGS *= --lsb-shared-libs=$${LIB_NAME}
}
LIBS += $$LINKAGE
export(LIBS)
export(QMAKE_LFLAGS)
export(QMAKE_LIBDIR_FLAGS)
return(true)
}
虽然只有两个函数还把defineTest和defineReplace都用上了,没见过的可以去百度一下,涨涨知识。
cuteWidgetsLibraryTarget这个函数的功能就是如果是debug模式那就在原来的名字上加一个d,这在QT的库里太常见了。
cuteWidgetsAddLibrary的功能就是添加依赖库,第一个变量是路径,第二个变量是库名称,其实就是实现了
LIBS += -Ldir -llib
我想看这个博客的人不会不知道LIBS的用法的。
2.3 cutewidgetsbuild.pri
这个文件顾名思义是配置编译的,内容如下:
######################################################################
# qmake internal options
######################################################################
CONFIG += qt
CONFIG += warn_on
CONFIG += no_keywords
CONFIG += silent
CONFIG -= depend_includepath
CONFIG += c++17
######################################################################
# release/debug mode
######################################################################
win32 {
# On Windows you can't mix release and debug libraries.
# The designer is built in release mode. If you like to use it
# you need a release version. For your own application development you
# might need a debug version.
# Enable debug_and_release + build_all if you want to build both.
#CONFIG += debug_and_release
#CONFIG += build_all
CONFIG += release
}
这里我没写太多内容,因为本人只在Windows系统上做开发,所以这个框架也没那么QT,可以多平台使用,主要是本人没有其他系统的使用需求,故而未作深入研究。
四、源码
下面进入源码的介绍环节。该部分还是主要介绍几个子项目的工程配置文件,捎带说说源码内容,因为自定义控件源码的编写网上的内容太多了,我就不做详细介绍了。
1 src
- src.pro
- cutewidgets_global.h
- testedit
1.1 src.pro
也是一个子项目,内容如下:
include($$PWD/../cutewidgets.pri)
TEMPLATE = lib
TARGET = $$cuteWidgetsLibraryTarget(cutewidgets)
DESTDIR = $$CUTEWIDGETS_OUTPUT_LIB
contains(CUTEWIDGETS_CONFIG, CuteWidgetsDll) {
CONFIG += dll
DEFINES += CUTEWIDGETS_DLL CUTEWIDGETS_LIBRARY
DLLDESTDIR = $$CUTEWIDGETS_ROOT/bin
}
else {
CONFIG += staticlib
}
INCLUDEPATH += $$CUTEWIDGETS_ROOT/src
unset(INSTALL_INCLUDE_FILES)
INSTALL_INCLUDE_FILES += $$CUTEWIDGETS_ROOT/src/cutewidgets_global.h
#包含指定源码
contains(CUTEWIDGETS_CONFIG, CuteWidgetsTestEdit) {
include($$CUTEWIDGETS_ROOT/src/testedit/testedit.pri)
}
########################################################################
# 在此新增自定义控件源码 #
########################################################################
#new pri here
########################################################################
#头文件拷贝
########################################################################
target_headers.path = $$CUTEWIDGETS_ROOT/include
target_headers.files = $$INSTALL_INCLUDE_FILES
INSTALLS += target_headers
捡有用的说几个吧。
项目 | 说明 |
---|---|
include($$PWD/…/cutewidgets.pri) | 不必多说,定义这玩意就是让引用的。 |
DESTDIR = $$CUTEWIDGETS_OUTPUT_LIB | 定义动态库或静态库输出位置 |
contains(CUTEWIDGETS_CONFIG, CuteWidgetsDll) | 决定是编译动态库还是静态库,动态库的话,输出dll到bin |
INCLUDEPATH += $$CUTEWIDGETS_ROOT/src | 这个特殊说明下,这个路径下有cutewidgets_global.h文件,为了可以在所有文件中直接包含#include “cutewidgets_global.h”,这样做的用意是编译designer插件时会把所有相关头文件都拷贝到QT的include/QtCuteWidgets下,和代码的文件结构不同了,避免头文件引用错误 |
contains(CUTEWIDGETS_CONFIG, CuteWidgetsTestEdit) | 前文提到了,根据配置选择是否加载该部分源码 |
INSTALLS += target_headers | 头文件拷贝,具体配置请参考QT INSTALLS使用 |
1.2 cutewidgets_global.h
内容如下:
#ifndef CUTEWIDGETS_GLOBAL_H
#define CUTEWIDGETS_GLOBAL_H
#include <QtCore/qglobal.h>
#ifdef CUTEWIDGETS_DLL
#ifdef CUTEWIDGETS_LIBRARY
# define CUTEWIDGETS_EXPORT Q_DECL_EXPORT
#else
# define CUTEWIDGETS_EXPORT Q_DECL_IMPORT
#endif
#else
#define