QT自定义控件工程结构框架

news2024/11/28 4:45:33

目录

  • 前言
  • 一、cutewidgets是什么?
  • 二、工程结构
  • 三、框架的工程配置
    • 1 cutewidgets.pro
    • 2 cutewidgets.pri
      • 2.1 cutewidgetsconfig.pri
      • 2.2 cutewidgetsfunctions.pri
      • 2.3 cutewidgetsbuild.pri
  • 四、源码
    • 1 src
      • 1.1 src.pro
      • 1.2 cutewidgets_global.h
      • 1.3 testedit
      • 1.4 扩展
    • 2 examples
      • 2.1 examples.pro
      • 2.2 examples.pri
      • 2.3 testedit
      • 2.4 拓展
    • 3 designer
      • 3.1 designer.pro
      • 3.2 designer_plugin.h和designer_plugin.cpp
      • 3.3 资源文件
  • 五、如何使用自定义控件
    • 1 确认QT环境下的cutewidgets部署情况
    • 2 在Qt Creator中使用


前言

网上关于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 CUTEWIDGETS_EXPORT
#endif

#endif // CUTEWIDGETS_GLOBAL_H

主要是倒腾CUTEWIDGETS_EXPORT这个变量的,在动态库、静态库或直接引用源码下的使用情况。

1.3 testedit

这个就是我编写的测试自定义控件的源码了。
在这里插入图片描述
testedit.pri:

!contains(DEFINES, DEFINE_CUTEWIDGETS_TESTEDIT) {
    DEFINES += DEFINE_CUTEWIDGETS_TESTEDIT
}

QT += widgets

INCLUDEPATH += $$PWD

HEADERS += \
    $$PWD/testedit.h

SOURCES += \
    $$PWD/testedit.cpp

########################################################
#添加需要install的头文件
########################################################

INSTALL_INCLUDE_FILES += $$PWD/testedit.h

DEFINE_CUTEWIDGETS_TESTEDIT前面已经提到了。主要说下最后一段,给install用的,编译源码主要生成三个文件,bin,include,lib,形成动态库三件套,这里会拷贝相关头文件到工程根目录下的include。

testedit.h和testedit.cpp
纯源码了,内容如下:

#ifndef TESTEDIT_H
#define TESTEDIT_H

#include "cutewidgets_global.h"

#include <QWidget>

class TestEditPrivate;

class CUTEWIDGETS_EXPORT TestEdit : public QWidget
{
    Q_OBJECT

    Q_PROPERTY(QString title READ getName WRITE setName RESET resetName NOTIFY nameChanged)
    Q_PROPERTY(QString value READ getValue WRITE setValue RESET resetValue NOTIFY valueChanged)

public:
    explicit TestEdit(QWidget *parent = nullptr);

    QString getName();
    void setName(const QString &text);
    void resetName();

    QString getValue();
    void setValue(const QString &text);
    void resetValue();

Q_SIGNALS:
    void nameChanged(QString);
    void valueChanged(QString);

private:
    Q_DECLARE_PRIVATE(TestEdit)
    TestEditPrivate *d_ptr;
};

#endif // TESTEDIT_H
#include "testedit.h"

#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>

class TestEditPrivate
{
    Q_DECLARE_PUBLIC(TestEdit)
    TestEdit *q_ptr;

public:
    QLabel *name;
    QLineEdit *input;

    TestEditPrivate(TestEdit *q);
};

TestEdit::TestEdit(QWidget *parent)
    : QWidget{parent}, d_ptr(new TestEditPrivate(this))
{
    Q_D(TestEdit);

    auto vLayout = new QVBoxLayout;
    setLayout(vLayout);

    d->name = new QLabel;
    resetName();
    d->input = new QLineEdit;
    d->input->setPlaceholderText("please input");
    resetValue();
    vLayout->addWidget(d->name);
    vLayout->addWidget(d->input);
}

QString TestEdit::getName()
{
    Q_D(TestEdit);
    return d->name->text();
}

void TestEdit::setName(const QString &text)
{
    Q_D(TestEdit);
    if(d->name->text() == text)
        return;

    d->name->setText(text);
    Q_EMIT nameChanged(text);
}

void TestEdit::resetName()
{
    Q_D(TestEdit);
    d->name->setText("untitled");
}

QString TestEdit::getValue()
{
    Q_D(TestEdit);
    return d->input->text();
}

void TestEdit::setValue(const QString &text)
{
    Q_D(TestEdit);
    if(d->input->text() == text)
        return;

    d->input->setText(text);
    Q_EMIT valueChanged(text);
}

void TestEdit::resetValue()
{
    Q_D(TestEdit);
    d->input->setText("");
}

TestEditPrivate::TestEditPrivate(TestEdit *q) : q_ptr(q)
{

}

很简单,就是把QLabel和QLineEdit放一起了。

TestEdit::TestEdit(QWidget *parent)
    : QWidget{parent}, d_ptr(new TestEditPrivate(this))
{
    Q_D(TestEdit);

    auto vLayout = new QVBoxLayout;
    setLayout(vLayout);

    d->name = new QLabel;
    resetName();
    d->input = new QLineEdit;
    d->input->setPlaceholderText("please input");
    resetValue();
    vLayout->addWidget(d->name);
    vLayout->addWidget(d->input);
}

Q_DECLARE_PRIVATE、Q_DECLARE_PUBLIC、Q_D、Q_Q,没见过的去查,又长知识了啊。

Q_PROPERTY 也是知识点,不过既然要编写自定义控件了,自然应该知道的。
在这里插入图片描述

1.4 扩展

看文件结构也应该清楚,后续再添加其他自定义控件,那无非就是添加一个一个的类似testedit的文件夹了。pri文件参见testedit.pri的写法,其他正常,主要注意install需要的头文件,src.pro文件添加新源码的位置也已标注。

2 examples

例程examples是顾名思义是为了测试编写的自定义控件。其文件结构如下:
在这里插入图片描述

  • examples.pro
  • examples.pri
  • testedit

2.1 examples.pro

没什么说的,简单的一个子项目,内容如下:

include($$PWD/../cutewidgets.pri)

TEMPLATE = subdirs

contains(CUTEWIDGETS_CONFIG, CuteWidgetsTestEdit) {

    SUBDIRS += testedit
}

########################################################################
#                           在此新增测试源码                            #
########################################################################

在尾部新增测试源码。

2.2 examples.pri

测试例程的公共配置,内如如下:

CUTEWIDGETS_ROOT            = $$PWD/..
CUTEWIDGETS_OUTPUT_LIB      = $$CUTEWIDGETS_ROOT/lib

include($$CUTEWIDGETS_ROOT/cutewidgetsfunctions.pri)

TEMPLATE     = app

DESTDIR      = $$CUTEWIDGETS_ROOT/bin

INCLUDEPATH += $$CUTEWIDGETS_ROOT/include
DEPENDPATH += $$CUTEWIDGETS_OUTPUT_LIB

cuteWidgetsAddLibrary($$CUTEWIDGETS_OUTPUT_LIB, cutewidgets)

模板为app,输出到根目录bin,后三行将源码作为正常的动态库或静态库引用。

2.3 testedit

对应源码testedit的测试源码
在这里插入图片描述
testedit.pro文件内容如下:

TEMPLATE = subdirs

SUBDIRS += \
    simple

这里不用要求是子项目模板,若当前测试只有一个工程,也可以直接上app模板,若是多个测试工程那应该考虑使用子项目模板。
simple例程,是一个再简单不过的小工程了,
在这里插入图片描述
主要说下pro文件,


include($${PWD}/../../examples.pri)
QT += widgets

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

include($${PWD}/…/…/examples.pri),所有测试工程的pro文件都要包含这句,配置三方动态库依赖。
mainwindow.cpp文件内容如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "testedit.h"

#include <QVBoxLayout>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    auto v = new QVBoxLayout;
    v->addWidget(new TestEdit(this));
    ui->centralwidget->setLayout(v);
}

MainWindow::~MainWindow()
{
    delete ui;
}

这里就简单添加了一个自定义控件TestEdit。运行结果如下:
在这里插入图片描述

2.4 拓展

同源码src一样,src那边新增源码模块,这边就相应添加测试模块,参见examples.pro尾部。

3 designer

前面提到的src和examples分别是自定义控件的编写和其测试例程的编写,完成后,其实作为第三方动态库的工作已经完成,已经可以供其他项目调用了,那为什么还要再设计一个designer模块呢?

我们都知道QT提供了Qt Designer和Qt Creator两个工具,其中Qt Designer是单独设计UI界面的,很方便,妥妥拽拽就把复杂的界面搭出来了,Qt Creator把Qt Designer做了集成,UI设计也很方便,还有一些Qt Designer没有的功能,例如可以直接右键控件转到源码槽函数等等,Qt Designer内部集成了很多控件,提供了拖拽的基础。

我们编写的自定义控件本质上和原生控件是一样的,如果也集成到Qt Designer中,采用拖拽的方式就把自定义控件加到我们的UI界面中了,那现在的问题就是我们编写的自定义控件它只是普通的UI动态库,无法被Qt Designer识别,自然也就不能像原生控件那样被方便的使用了,这就是为什么还要再添加一个designer模块的原因,将自定义控件包装一下,让它可以被Designer识别。

其实对于我个人而言,添加到designer里的实用意义并不大,因为到一定阶段,你会更钟情于在代码中设计UI,而不是通过Designer工具。可能有人会反驳,说Designer更方便,更高效,这里我们不做争论,在不同阶段使用自己习惯的方式就好。但是将自定义控件集成到Designer的工作还是要做的,毕竟有人是更习惯于Designer的。

下面开始介绍designer模块。
在这里插入图片描述
一共包含四个文件和一个资源文件夹:

  • designer.pro
  • designer_plugin.h
  • designer_plugin.cpp
  • designer_plugin.qrc
  • resource

3.1 designer.pro

内容如下:


include($$PWD/../cutewidgets.pri)

################################################################################
# 若qtcreator路径下已生成cutewidgets_plugin.dll,编译前需要关闭qtcreator并删除qtcreator
# 路径下的cutewidgets_plugin.dll,然后再编译,因为qtcreator打开情况下,该dll是被加载状态,
# 无法覆盖拷贝;也可以将INSTALL_TO_CREATOR置0,编译完后关闭qtcreator,手动拷贝;
# 运行例程时需要置0,避免因为编译错误,无法运行或调试例程
################################################################################

INSTALL_TO_CREATOR = 0

CONFIG(debug_and_release) {

    # When building debug_and_release the designer plugin is built
    # for release only. If you want to have a debug version it has to be
    # done with "CONFIG += debug" only.

    message("debug_and_release: building the cutewidgets designer plugin in release mode only")

    CONFIG          -= debug_and_release
    CONFIG          += release
}

contains(CUTEWIDGETS_CONFIG, CuteWidgetsDesigner) {

    CONFIG          += qt plugin

    greaterThan(QT_MAJOR_VERSION, 4) {
        QT          += designer
    }
    else {
        CONFIG      += designer
    }


    TEMPLATE        = lib
    TARGET          = cutewidgets_plugin

    DESTDIR         = $$CUTEWIDGETS_OUTPUT_PLUGIN

    #########################################################################
    # 定义QT路径下include下文件名,所有install的头文件放于
    # QT_INSTALL_PRFIX/include/CUTEWIDGETS_STR中
    #########################################################################
    DEFINES += CUTEWIDGETS_STR=$$CUTEWIDGETS_INSTALL_DIR_NAME

    INCLUDEPATH     += $$CUTEWIDGETS_ROOT/src


    #包含指定源码
    contains(CUTEWIDGETS_CONFIG, CuteWidgetsTestEdit) {

        include($$CUTEWIDGETS_ROOT/src/testedit/testedit.pri)
    }

    ########################################################################
    #                           在此新增自定义控件源码                         #
    ########################################################################

    #new pri here

    #designer 插件文件
    HEADERS         += designer_plugin.h
    SOURCES         += designer_plugin.cpp
    RESOURCES       += designer_plugin.qrc

    target_designer.path  = $$CUTEWIDGETS_INSTALL_PLUGINS_FOR_DESIGNER
    target_designer.files = $$CUTEWIDGETS_OUTPUT_PLUGIN/$${TARGET}.dll $$CUTEWIDGETS_OUTPUT_PLUGIN/$${TARGET}.lib
    INSTALLS        += target_designer

    equals(INSTALL_TO_CREATOR, 1) {

        target_creator.path  = $$CUTEWIDGETS_INSTALL_PLUGINS_FOR_CREATOR
        target_creator.files = $$CUTEWIDGETS_OUTPUT_PLUGIN/$${TARGET}.dll
        INSTALLS        += target_creator
    }

    target_headers.path  = $$CUTEWIDGETS_INSTALL_HEADERS
    target_headers.files = $$$$CUTEWIDGETS_ROOT/include/*.h
    INSTALLS        += target_headers
    
    target_lib.path  = $$CUTEWIDGETS_INSTALL_LIB
    target_lib.files = $$CUTEWIDGETS_OUTPUT_LIB/cutewidgets.dll $$CUTEWIDGETS_OUTPUT_LIB/cutewidgets.lib
    INSTALLS        += target_lib
}
else {
    TEMPLATE        = subdirs # do nothing
}

  • INSTALL_TO_CREATOR
    开关变量,功能注释的很清楚

  • CONFIG(debug_and_release)
    Designer只使用release插件

下面没什么可说的,可以动态添加包含自定义控件源码,尾部是install。

3.2 designer_plugin.h和designer_plugin.cpp

designer_plugin.h:

#ifndef DESIGNER_PLUGIN_H
#define DESIGNER_PLUGIN_H

#include <QtGlobal>
#include <QtPlugin>

#if QT_VERSION >= 0x050600
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
#else
#include <QDesignerCustomWidgetInterface>
#endif

#define STR(R) #R
#define STRVALUE(R) STR(R)

#ifdef CUTEWIDGETS_STR
const QString strQTInclude = QString("%1/").arg(STRVALUE(CUTEWIDGETS_STR));
#elif
const QString strQTInclude = "";
#endif

class CuteWidgetInterface : public QDesignerCustomWidgetInterface
{
public:
    virtual QString name() const override;
    virtual QString group() const override;
    virtual QString toolTip() const override;
    virtual QString whatsThis() const override;
    virtual QString includeFile() const override;
    virtual QIcon icon() const override;

    virtual bool isContainer() const override;

    virtual bool isInitialized() const override;
    virtual void initialize(QDesignerFormEditorInterface *core) override;

    virtual QString domXml() const override;

    virtual QString codeTemplate() const override;

protected:
    QString m_name;
    QString m_group;
    QString m_toolTip;
    QString m_whatsThis;
    QString m_include;
    QIcon m_icon;
    bool m_isContainer;
    bool m_initialized;
    QString m_domXml;
    QString m_codeTemplate;
};

#ifdef DEFINE_CUTEWIDGETS_TESTEDIT

class TestEditInterface : public CuteWidgetInterface
{
public:
    TestEditInterface();

    virtual QWidget *createWidget(QWidget* parent) override;
};

#endif

/*******************************************************************************************************************/
/*                                          在此以上填写自定义接口类                                                    */
/*******************************************************************************************************************/

class CuteWidgetsCollectionInterface
    : public QObject
    , public QDesignerCustomWidgetCollectionInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)

#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface" )
#endif

public:
    CuteWidgetsCollectionInterface() : QObject()
    {
#ifdef DEFINE_CUTEWIDGETS_TESTEDIT
        m_plugins += new TestEditInterface();
#endif
    }

    virtual ~CuteWidgetsCollectionInterface() override
    {
        qDeleteAll(m_plugins);
    }

    QList<QDesignerCustomWidgetInterface*> customWidgets() const override
    {
        return m_plugins;
    }

private:
    QList<QDesignerCustomWidgetInterface*> m_plugins;
};

#endif // DESIGNER_PLUGIN_H

designer_plugin.cpp:

#include "designer_plugin.h"

#ifdef DEFINE_CUTEWIDGETS_TESTEDIT
#include "testedit.h"
#include <QMessageBox>
#endif

QString CuteWidgetInterface::name() const
{
    return m_name;
}

QString CuteWidgetInterface::group() const
{
    return m_group;
}

QString CuteWidgetInterface::toolTip() const
{
    return m_toolTip;
}

QString CuteWidgetInterface::whatsThis() const
{
    return m_whatsThis;
}

QString CuteWidgetInterface::includeFile() const
{
    return m_include;
}

QIcon CuteWidgetInterface::icon() const
{
    return m_icon;
}

bool CuteWidgetInterface::isContainer() const
{
    return m_isContainer;
}

bool CuteWidgetInterface::isInitialized() const
{
    return m_initialized;
}

void CuteWidgetInterface::initialize(QDesignerFormEditorInterface *core)
{
    if (m_initialized)
        return;

    // Add extension registrations, etc. here

    m_initialized = true;
}

QString CuteWidgetInterface::domXml() const
{
    return m_domXml;
}

QString CuteWidgetInterface::codeTemplate() const
{
    return m_codeTemplate;
}

#ifdef DEFINE_CUTEWIDGETS_TESTEDIT

TestEditInterface::TestEditInterface()
{
    m_name = "TestEdit";
    m_group = "Cute Widgets";
    m_toolTip = "TestEdit";
    m_whatsThis = "TestEdit";
    m_include = strQTInclude + "testedit.h";
    m_icon = QPixmap(":/resource/testedit.png");
    m_isContainer = false;
    m_domXml = QDesignerCustomWidgetInterface::domXml();
    m_codeTemplate = QDesignerCustomWidgetInterface::codeTemplate();
}

QWidget *TestEditInterface::createWidget(QWidget *parent)
{
    auto w = new TestEdit(parent);
    QObject::connect(w, &TestEdit::nameChanged, [](QString name){
        QMessageBox::information(nullptr, "info", QString("name changed : %1").arg(name));
    });

    QObject::connect(w, &TestEdit::valueChanged, [](QString value){
        QMessageBox::information(nullptr, "info", QString("value changed : %1").arg(value));
    });

    return w;
}

#endif

解读下:
QDesignerCustomWidgetCollectionInterface,因为我们这个框架本身就是自定义控件的集合,所以最好用QDesignerCustomWidgetCollectionInterface

QDesignerCustomWidgetInterface,关于这个类,主要说一下容易莫名入坑的地方,

class QDesignerCustomWidgetInterface
{
public:
    virtual ~QDesignerCustomWidgetInterface() {}

    virtual QString name() const = 0;
    virtual QString group() const = 0;
    virtual QString toolTip() const = 0;
    virtual QString whatsThis() const = 0;
    virtual QString includeFile() const = 0;
    virtual QIcon icon() const = 0;

    virtual bool isContainer() const = 0;

    virtual QWidget *createWidget(QWidget *parent) = 0;

    virtual bool isInitialized() const { return false; }
    virtual void initialize(QDesignerFormEditorInterface *core) { Q_UNUSED(core); }

    virtual QString domXml() const
    {
        return QString::fromUtf8("<widget class=\"%1\" name=\"%2\"/>")
            .arg(name()).arg(name().toLower());
    }

    virtual QString codeTemplate() const { return QString(); }
};

QDesignerCustomWidgetInterface::domXml(),按基类这个去写肯定没问题,如果有些人想重写这个函数,那一定注意"<widget class=“%1” name=“%2”/>“,%1和%2位置一定不能一样,比如都写成"TestEdit”,编译不会报错,但在使用该自定义控件时会报错,有兴趣的可以试试,看看会报什么错。

TestEditInterface类的

m_include = strQTInclude + "testedit.h";

前面已经提到过,这里再提一次,需要加上strQTInclude 这个常量。

TestEditInterface::createWidget函数

QWidget *TestEditInterface::createWidget(QWidget *parent)
{
    auto w = new TestEdit(parent);
    QObject::connect(w, &TestEdit::nameChanged, [](QString name){
        QMessageBox::information(nullptr, "info", QString("name changed : %1").arg(name));
    });

    QObject::connect(w, &TestEdit::valueChanged, [](QString value){
        QMessageBox::information(nullptr, "info", QString("value changed : %1").arg(value));
    });

    return w;
}

正常情况下就是new一个对象然后再返回就行,如果想增加一些逻辑,可以添加一些编码,比如测试代码中就添加了两个信号显示QMessageBox::information。但这里请注意,这里的逻辑是对于designer的,而不是对于运行程序的
在designer中拖拽一个TestEdit控件,改变value值,会触发信号,弹窗。
在这里插入图片描述
但是在运行程序里不会出现该弹窗。
在这里插入图片描述
其实这很好理解,只要理清了目标对象,就不会混淆了,

QWidget *TestEditInterface::createWidget(QWidget *parent)
{
    auto w = new TestEdit(parent);
    QObject::connect(w, &TestEdit::nameChanged, [](QString name){
        QMessageBox::information(nullptr, "info", QString("name changed : %1").arg(name));
    });

    QObject::connect(w, &TestEdit::valueChanged, [](QString value){
        QMessageBox::information(nullptr, "info", QString("value changed : %1").arg(value));
    });

    return w;
}

TestEditInterface这个类是对应designer的,所以designer即为它的运行程序,那在designer里一旦触发的信号,必然会弹窗。但使用该自定义控件的程序只是借由designer把TestEdit添加到自己的程序里,而那里只是单纯的使用TestEdit而已,并没有连接上述的两个TestEdit的信号,所以不会触发弹窗,如果在程序中同样连接了该信号,那触发信号时,也必然弹窗。
在这里插入图片描述
这是两个概念。不理解也没关系,记住就行了。

3.3 资源文件

很好理解,给你的自定义控件插件添加一个图标,这个图标会出现在designer中。
在这里插入图片描述
在这里插入图片描述

这个资源文件仅是图标用,如果自定义控件本身需要资源文件,请在自定义控件源码处添加自己的资源文件,不要混淆了。

至此,框架就介绍完了。

五、如何使用自定义控件

1 确认QT环境下的cutewidgets部署情况

将cutewidgets编译好后,去QT安装目录下查询,你应该看到:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 在Qt Creator中使用

新建一个app程序
在这里插入图片描述

切换到designer界面,拖拽一个自定义控件到界面中。
在这里插入图片描述

编译程序,你会看到如下错误:
在这里插入图片描述
很明显是没有添加动态库引用,在pro文件中添加,再次编译。
在这里插入图片描述
点击调试或运行,如果提示缺少cutewidgets.dll无法运行,需要将cutewidgets.dll拷贝到测试例程的运行环境中。
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

互联网中断检测技术窥览与讨论

前言&#xff1a; 如其他人造系统一样&#xff0c;互联网的运行也会出现异常甚至中断。仅在2022年就发生了多起影响重大的互联网中断事件&#xff1a;1月15日汤加火山喷发三个小时后&#xff0c;全国断网&#xff0c;和外界的所有电话与网络联系都无法接通&#xff1b;3月28日…

系统分析师案例必备知识点汇总---2023系列文章二

需求获取 1、需求获取的技术&#xff1a; 用户访谈 优点&#xff1a;具有良好的灵活性&#xff0c;有较宽广的应用范围。 缺点是&#xff1a;用户忙&#xff0c;信息量大&#xff0c;记录困难&#xff0c;需要沟通技巧。 问卷调查 优点&#xff1a;短时间内收集数据。 缺点…

Python aiohttp 库是否值得学?那必须要掌握呀

aiohttp 是一个基于 asyncio 的异步 HTTP 客户端/服务器库。它提供了一组用于编写高性能异步网络应用程序的工具&#xff0c;包括基于协程的客户端和服务器。 库的安装使用 pip install aiohttp Python aiohttp 库通过 aiohttp 搭建服务器aiohttp 路由aiohttp 中间件aiohttp 发…

【机器学习之模型融合】Blending混合法

前言 Stacking堆叠法基础知识&#xff1a;http://t.csdn.cn/dzAna 1、Blending的基本思想与流程&#x1f47f; Blending融合是在Stacking融合的基础上改进过后的算法。在之前的课程中我们提到&#xff0c;堆叠法stacking在level 1上使用算法&#xff0c;这可以令融合本身向着损…

好家伙,这几个隐藏功能,太香了

很多小伙伴可能被小畅的问题搞糊涂了&#xff0c;因为很多人只会在电脑上堆砌各种小应用&#xff0c;而忽略了Windows原有的实用功能。 而我们也千万不要小看这些功能&#xff0c;它们说不定能在关键时刻助你一臂之力&#xff0c;帮到你不少忙呢&#xff01; 那么今天小畅就为大…

MobaXterm连接出现 Network error: Connection timed out 问题解决

MobaXterm连接出现 Network error: Connection timed out&#xff1a; 接前文&#xff1a;CentOS安装, 点此查看文章&#xff0c;安装之后的SSH连接&#xff1a; 解决思路如下&#xff1a; 1、检查虚拟机端是否安装ssh 一般情况是可以自动安装的&#xff0c;直接在终端输入s…

系统分析师案例必备知识点汇总---2023系列文章一

一、系统规划 (视频内容&#xff1a;系统分析师-专业知识模块中的系统规划视频) 1 、可行性研究 经济可行性 技术可行性 法律可行性 用户使用可行性 也称为投资收益分析 或成本效益分析&#xff0c;主 要评估项目的建设成 本、运行成本和项目 建成后可能的经济收 益。 技…

eggjs框架源码解读

文章目录前言Egg进程模型Egg应用程序结构egg运行启动的内幕加载插件扩展内置对象加载中间件加载service加载路由加载配置设置应用信息执行业务逻辑文件加载机制结语前言 eggjs 是阿里在 Nodejs 技术上的一大杰作&#xff0c;也是对开源世界的一大贡献。里面包含了很多技术结晶…

Linux---进程概念

目录 1. 什么是进程&#xff1f; 2. 描述进程---PCB task_struct---PCB的一种 task_ struct内容分类 3. 组织进程 4. 查看进程 通过系统调用获取进程标示符 通过系统调用创建进程---fork初识 1. 什么是进程&#xff1f; 其实&#xff0c;我们启动一个软件&#xf…

SLAM本质剖析番外-李群李代数的微分和导数

0. 简介 这几个月&#xff0c;博主已经从SLAM算法的使用向着算法的数学推导进行了记录和分享&#xff0c;之前也分享了李群李代数关注核心一文&#xff0c;从现象中解释了李群和李代数表达的含义。但是这还不够&#xff0c;所以这次作者作为SLAM本质剖析的番外&#xff0c;来介…

基础数字(一)位运算 哈希(数组中元素出现次数)

目录 力扣剑指 Offer II 070. 排序数组中只出现一次的数字 数组中只出现一次的数&#xff08;其它数出现k次&#xff09;_牛客题霸 数组中只出现一次的两个数字_牛客题霸_牛客网 数组中出现次数超过一半的数字_牛客题霸_牛客网 缺失的第一个正整数_牛客题霸_牛客网 力扣剑指…

[杂记]算法:前缀和与差分数组

这篇讲一下前缀和与差分数组的关系 1. 前缀和 1.1 一维数组前缀和 前缀和在处理数组中的连续子数组的某一段加和的问题中很有用, 因为是拿空间换时间, 可以将线性复杂度降低为常数时间复杂度. 前缀和的道理很简单, 对于数组arr[i],i0,...,n−1arr[i], i 0, ..., n - 1arr[i…

《Linux Shell脚本攻略》学习笔记-第四章

4.1 简介 本章主要介绍sed、awk、grep、cut等命令&#xff0c;这些工具可以相互结合以满足文本处理需求。 正则表达式是一种基础的模式匹配技术。 4.2 使用正则表达式 正则表达式是由字面文本和具有特殊意义的符号组成的。 1&#xff09;位置标记 位置标记锚点是标识字符串位置…

Anaconda安装、opencv环境配置、jupyter notebook使用虚拟环境

目录一、Anaconda 的安装二、opencv 3.4.1.15版本安装三、jupyter notebook使用虚拟环境四、运行报错-缺库一、Anaconda 的安装 Anaconda官网&#xff1a;Anaconda Installers Anaconda历史版本&#xff1a;Anaconda Index of 这边建议和我装一样anaconda3 python3.7&#xf…

opencv的图像基本操作(基于jupyter Notebook)

opencv的基本操作cv2是opencv在python中的缩写&#xff0c;函数开头用cv2cv2.imread(cat.jpg) #读入图片cat.jpgcv2.imwrite(mycat.png,img) #图片img保存为mycat.pngcv2.imshow(image,img) #创建窗口&#xff0c;显示图像cv2.waitKey(10000) #等待时间&#xff0c;以 毫秒为单…

整数分解

问题描述 将 3 分解成两个正整数的和, 有两种分解方法, 分别是 312312 和 321321 。注意顺序不同算不同的方法。 将 5 分解成三个正整数的和, 有 6 种分解方法, 它们是 113122113122 131212221311131212221311 。 请问, 将 2021 分解成五个正整数的和, 有多少种分解方法? …

Android大厂面试100题,涵盖测试技术、环境搭建、人力资源

测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 2、我现在有个程序&#xff0c;发现在Windows上运行得很慢&#xff0c;怎么判别是程序存在问题还是软硬件系统存在问题&#xff1f; 3、测试的策略有哪些&#xff1f; 4、正交表测试用例…

Sinutrain下载安装与开启OPC UA---kalrry

Sinumerik下载安装与开启OPC UA---kalrry前言一、安装前准备二、Win7安装1、软件安装2、开启授权3、文件配置4、客户端连接三、Win10/11安装四、启动后使用前言 本教程只适用于 Sinutrain-v4.7 版本&#xff0c;其他版本配置目录有所改变建议安装到默认路径&#xff0c;否则后…

【云原生】k8s安全机制

内容预知 前言 1. 认证&#xff08;Authentication&#xff09; 1.1 k8s集群内的三种认证方式 1.2 k8s集群内的认证说明 &#xff08;1&#xff09;需要被认证的访问类型 &#xff08;2&#xff09;安全性说明 &#xff08;3&#xff09;证书颁发的方式 &#xff08;4&a…

Qt中使用qt自带的函数实现各种进制间的相互转换,easy.

文章目录一.十进制转各种进制第一种&#xff1a;使用QString的静态函数number第二种&#xff1a;使用QString的拼接函数arg二.各种进制相互转换一.十进制转各种进制 第一种&#xff1a;使用QString的静态函数number ①使用QString的静态函数number即可&#xff0c;如我把字符…