Qt 实现自定义Ui控件例子,以自定义的Slider为例(QWidget)

2023-11-05

说明

Qt可以比较方便地实现自定义控件在Qt Creator中使用。网上也有很多大神的控件可以使用,但是如果想要自己简单定制也可以按照这个流程。

本文的要点:

【1】如何实现一个自定义控件?

本文使用的方法有两个步骤:

  1. 先在一个普通项目中实现使用
  2. 新建Qt Ui控件项目编译成dll和相关lib

【2】自定义控件如何在多个环境下使用?

  1. Qt Creator只认自己相匹配的库
  2. 代码中可以使用多个环境的库,前提是要编译链接好相关库。

因此,文章也分两方面去阐述,第一部分编写,和第二部分使用。

第一部分:编写

1、在普通项目中使用

首先,新建一个QWidget项目。

界面上拖拽一个文字显示框(QPlainTextEdit)和一个垂直滑块(QSlider)如图:

然后,新建一个类MySlider,继承于QSlider,Q_OBJECT宏记得要加上。

#ifndef MYSLIDER_H
#define MYSLIDER_H

#include <QSlider>
#include <atomic>

class MySlider : public QSlider
{
    Q_OBJECT
public:
    MySlider(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *ev) override;
    void mousePressEvent(QMouseEvent *ev) override;
    void mouseReleaseEvent(QMouseEvent *ev) override;
    void mouseMoveEvent(QMouseEvent *ev) override;
private:
    std::atomic_bool mValiedMouseFlag{false};
    std::atomic_int  mSilderUp{0};
};

#endif // MYSLIDER_H

 重写的几个方法可以点击到头文件里查看哪些用得到需要重写:

这里重写的是:

    void paintEvent(QPaintEvent *ev) override; //绘制控件
    void mousePressEvent(QMouseEvent *ev) override; //鼠标按下
    void mouseReleaseEvent(QMouseEvent *ev) override; //鼠标松开
    void mouseMoveEvent(QMouseEvent *ev) override; //鼠标移动

 还有需要注意的是在protected区域的对象虽然重写在public也可以运行,也要保持不改,因为编译成控件以后有可能会不识别。

至于下面的两个变量:

  • mValiedMouseFlag用于保存鼠标按下状态(对于一些关键的变量习惯用atom 可以改成普通的bool类型)
  • mSilderUp用于保存按下鼠标时,鼠标指针离滑块顶部的距离(对于一些关键的变量习惯用atom 可以改成普通的int类型)

然后开始在cpp内实现几个方法,首先是绘图:

//滑块高度固定40
const int heightSlider = 40;

void MySlider::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    QRect rect = QRect(4,4,this->width()-8,this->height()-8);

    QBrush brush;   //画刷
    brush.setColor(QColor(52,123,226,150));
    brush.setStyle(Qt::SolidPattern);

    painter.setBrush(brush);
    //传入绘制区域或者直接放入QRect作为参数
    painter.drawRoundedRect(rect, this->width()/4, this->width()/4);

    //最大值
    int maxValue = this->maximum();
    //当前值
    int value = this->value();
    //滑块最低的高度值(最高为0)
    int heightAll = this->height()-heightSlider;
    //滑块y的值
    int y = (maxValue* heightAll - value*heightAll) / maxValue;

    //中间的白线
    int centerX = this->width()/2-5;
    int centerY = 10;
    int centerW = 10;
    int centerH = this->height() - 20;
    QRect centerRect = QRect(centerX, centerY, centerW, centerH);
    brush.setColor(QColor(255, 255, 255, 255));
    painter.setBrush(brush);
    painter.drawRoundedRect(centerRect, 5, 5);

    //绘制滑块
    QRect sliderRect = QRect(0,y,this->width(),heightSlider);
    brush.setColor(QColor(255,255,255,230));

    painter.setBrush(brush);
    //传入绘制区域或者直接放入QRect作为参数
    painter.drawRoundedRect(sliderRect, 10, 10);

}

 这里用到的也比较简单的几个绘图(如果不了解QPainter可以搜相关教程,比较容易理解的绘图类):

1、绘制背景(圆角矩形)

2、绘制中间白线(圆角矩形)

3、计算滑块的位置

4、绘制滑块(圆角矩形)

绘制完成后大概就是这样了(简单调整了下大小,MainWindow的MenuBar删除了,还改成了栅格布局):

 接下来实现拖动滑块的处理,如果不去管它,那这个只能是披着一个新外衣的旧控件,里面可操作的部分还是旧控件的部分。

void MySlider::mousePressEvent(QMouseEvent *ev)
{
    int maxValue = this->maximum();
    int value = this->value();
    int heightAll = this->height()-heightSlider;
    int y = (maxValue* heightAll - value*heightAll) / maxValue;

    //判断是否在滑块区域内
    if ((ev->pos().y()> y) && (ev->pos().y()< y+heightSlider))
    {
        mValiedMouseFlag.store(true);

        //记录鼠标与滑块上下方距离
        mSilderUp.store(ev->pos().y() - y);
    }
    else {
        //如果在滑块外,则按照参数前进或者后退。
        if(ev->pos().y()< y){
            this->setValue( this->value() + this->pageStep());
        }else if (ev->pos().y()> y+heightSlider) {
            this->setValue( this->value() - this->pageStep());
        }

    }
}

void MySlider::mouseReleaseEvent(QMouseEvent *ev)
{
    Q_UNUSED(ev);
    mValiedMouseFlag.store(false);
}

void MySlider::mouseMoveEvent(QMouseEvent *ev)
{

    if(mValiedMouseFlag.load()){
        int tempValue{0};
        int y = ev->pos().y();
        double k = 1;

        //总高度
        int heightAll = this->height()-heightSlider;

        //滑块左上角顶点应该要在的高度(范围在0~总高度之间)
        int sY= y-mSilderUp.load();
        sY = sY<0 ? 0 :sY;
        sY = sY>heightAll ? heightAll :sY;

        //下面两步计算滑块应该要在的高度对应的值
        k= (double)(heightAll - sY)/(double)heightAll;
        tempValue = k* this->maximum();
        this->setValue(tempValue);

    }
}

其实代码也不多:

1、mousePressEvent判断鼠标按下是否在滑块区域内:

如果是的话,就记录下与滑块顶部的距离,并且设置mValiedMouseFlag为true

否则就判断在滑块上面还是下面,步进改变控件value的值。

2、mouseReleaseEvent ,设置mValiedMouseFlag为false

3、mouseMoveEvent,鼠标移动,如果判断mValiedMouseFlag为true则说明在拖动滑块,否则什么也不做。当发现在拖动滑块时,按照位置来改变value的值(这里涉及到一点点不难的计算)。

可见,这3步,没有去改变界面,只是改变了value的值,让控件自行改变,这个是QSlider自带的功能,会自动调用paintEvent重绘图像,如果写其他控件或者其他功能不能重绘的,需要手动调用update()也可以重绘图形。

然后为了检验是否成功,可以在主界面中重写valueChanged(也可以写qDebug打印)

void MainWindow::on_verticalSlider_valueChanged(int value)
{
    ui->plainTextEdit->setPlainText(QString("value:%1").arg(value));
}

运行:

到这一步就完成了在普通项目中使用了。

2、编译成Qt Creator 的控件

首先查看Qt Creator的版本

如果没有这个版本的编译环境就要去下载配置。

新建项目:

选择以后需要用到的环境(MinGW等其他的也可以以后再选)

 

 组的部分可以写 Test Widgets [user] 之后好在界面上区分

然后一路下一步完成。

项目建立后,发现刚才建立的类名称,将之前项目的类的h和cpp文件内容替换:

除此之外,头文件还要再修改一下:

class   MySlider : public QSlider

 改成:

#include <QtUiPlugin/QDesignerExportWidget>

class QDESIGNER_WIDGET_EXPORT MySlider : public QSlider

另外还要修改一个地方:

头文件改成 #include <QtUiPlugin/QDesignerCustomWidgetInterface>

否则之后有可能会有警告。

选择Qt creator的编译环境的release版本编译:

出现了警告:

这个应该大家都遇到过,源码中有中文或者中文注释,而编码又是utf-8不带bom会出现这个。方法有很多,最简单的方法是在pro文件最后加上:

contains( CONFIG, "msvc" ) {
    QMAKE_CXXFLAGS += /utf-8
    QMAKE_CFLAGS +=  /utf-8
}

看到已经生成了dll

一般是项目目录同级的编译目录下

将这个dll拷贝到QtCreator的plugins目录下:我的是 D:\Qt\Tools\QtCreator\bin\plugins\designer

 新建一个项目发现已经有这个控件已经在右下角了(但是还不能马上直接使用,还需要链接库和头文件。使用见下一小节)

 

(如果之前头文件没有加Q_OBJECT宏的话,会识别不出MySlider,只能识别为QSlider)

至此Qt Creator控件编译的过程就结束了。

目前编译的版本是Qt5.15.2 MSVC2019_64 Release,如果之后还想要在其他环境编译使用,则把其他环境都编译一遍:(如果不用其他版本,本小结后面编译可以不看了。)

Qt5.15.2 MSVC2019_64 Debug:点击编译即可。

生成了带d的库文件和lib文件

 

之后编译成功。

第二部分:使用

1、整理头文件和库文件

第一部分编译的头文件和库文件可以保存在不同的目录,可以方便程序清晰地调用。

下面是目录结构 D:\code\common\ui

ui
├─include
│  └─MySlider
│          MySlider.h
│
└─lib
    ├─mingw81_64
    │  ├─Debug
    │  │  └─MySilder
    │  │          libmysliderplugin.a
    │  │          mysliderplugin.dll
    │  │
    │  └─Release
    │      └─MySilder
    │              libmysliderplugin.a
    │              mysliderplugin.dll
    │
    └─MSVC2019_64
        ├─Debug
        │  └─MySilder
        │          mysliderplugind.dll
        │          mysliderplugind.lib
        │
        └─Release
            └─MySlider
                    mysliderplugin.dll
                    mysliderplugin.lib

每个dll和lib都从对应的编译目录中提取,例如:mingw81_64的Debug文件就从编译的目录中取出.a和.dll 文件:

2、ui控件的使用

新建一个项目,使用例子中的两个界面:

现在直接编译会报错:

 在pro增加头文件和链接库(注意MSVC是带d的):

INCLUDEPATH += D:/code/common/ui/include/MySlider

CONFIG +=debug_and_release  #如果项目属性里配置了,这里可以省略
CONFIG(debug,debug|release){
    message("debug")
    msvc{
        message("msvc")
        LIBS += -LD:/code/common/ui/lib/MSVC2019_64/Debug/MySilder/
        LIBS += -lmysliderplugind
    }else{
        message("mingw")
        LIBS += -LD:/code/common/ui/lib/mingw81_64/Debug/MySilder/
        LIBS += -lmysliderplugin
    }
}ELSE {
    message("release")
    msvc{
        message("msvc")
        LIBS += -LD:/code/common/ui/lib/MSVC2019_64/Release/MySilder/
        LIBS += -lmysliderplugin
    }else{
        message("mingw")
        LIBS += -LD:/code/common/ui/lib/mingw81_64/Release/MySilder/
        LIBS += -lmysliderplugin
    }
}

编译运行,就可以愉快地使用了。

再加上之前例子的代码:

void MainWindow::on_mySlider_valueChanged(int value)
{
    ui->plainTextEdit->setPlainText(QString("Value:%1").arg(value));
}

就完成了。

 这个是mingw64编译的

打包之类的不再赘述。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt 实现自定义Ui控件例子,以自定义的Slider为例(QWidget) 的相关文章

  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 向 Nhibernate 发出 SQL 查询

    如何将此 SQL 查询发送给 Nhibernate SELECT Customer name FROM Company INNER JOIN Customer ON Company CompanyId Customer CompanyId
  • 推导指南中的引用和值之间的差异

    考虑类型A template
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 读取文件特定行号的有效方法。 (奖励:Python 手册印刷错误)

    我有一个 100 GB 的文本文件 它是来自数据库的 BCP 转储 当我尝试导入它时BULK INSERT 我在第 219506324 行上收到一个神秘错误 在解决此问题之前 我想看看这一行 但可惜的是我最喜欢的方法 import line
  • 如何从 .resx 文件条目获取注释

    资源文件中的字符串有名称 值和注释 The ResXResourceReader类让我可以访问名称和值 有办法看评论吗 你应该能够得到Comment via ResXDataNode class http msdn microsoft co
  • 在 C# 中循环遍历文件文件夹的最简单方法是什么?

    我尝试编写一个程序 使用包含相关文件路径的配置文件来导航本地文件系统 我的问题是 在 C 中执行文件 I O 这将是从桌面应用程序到服务器并返回 和文件系统导航时使用的最佳实践是什么 我知道如何谷歌 并且找到了几种解决方案 但我想知道各种功
  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 私有模板函数

    我有一堂课 C h class C private template
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • 为什么在setsid()之前fork()

    Why fork before setsid 守护进程 基本上 如果我想将一个进程与其控制终端分离并使其成为进程组领导者 我使用setsid 之前没有分叉就这样做是行不通的 Why 首先 setsid 将使您的进程成为进程组的领导者 但它也
  • Linq-to-entities,在一个查询中获取结果+行数

    我已经看到了有关此事的多个问题 但它们已经有 2 年 或更长 的历史了 所以我想知道这方面是否有任何变化 基本思想是填充网格视图并创建自定义分页 所以 我还需要结果和行数 在 SQL 中 这将类似于 SELECT COUNT id Id N
  • 使用 GROUP 和 SUM 的 LINQ 查询

    请帮助我了解如何使用带有 GROUP 和 SUM 的 LINQ 进行查询 Query the database IEnumerable
  • 检查Windows控制台中是否按下了键[重复]

    这个问题在这里已经有答案了 可能的重复 C 控制台键盘事件 https stackoverflow com questions 2067893 c console keyboard events 我希望 Windows 控制台程序在按下某个

随机推荐

  • IO之字节字符转换流

    1 转换流概述 转换流 可以将一个字节流转换为字符流 也可以将一个字符流转换为字节流 OutputStreamWriter 可以将输出的字符流转换为字节流的输出形式 InputStreamReader 将输入的字节流转换为字符流输入形式 2
  • 多文件编辑作业(2023.1.9)

    第一题 main c include head h int main int argc const char argv char str 10 abcdefg MyStrRev str char a hello StrRevRec a st
  • qt 在ui界面添加控件后在cpp文件中无法调用?

    问题 qt 在ui界面添加控件后在cpp文件中无法调用 解决方法 在build选项中选择 重新build项目 再次在cpp中调用添加的控件发现可以调用了 还有一种情况导致添加控件后无法调用 就是没有导入ui xxx h文件 xxx是ui界面
  • Python图像处理-4.pil调整图片尺寸和旋转角度

    from PIL import Image import matplotlib pyplot as plt pil im1 Image open pic1 png plt figure girlfriend1 plt imshow pil
  • 魔搭开源FaceChain个人写真项目,大幅提升写真多样性,登顶github趋势榜首!

    一 上周数据概览 一周时间获取超过3K star 连续在github trending榜单蝉联top 开发者们纷纷标记star GitHub modelscope facechain FaceChain is a deep learning
  • zlib库VS2017编译步骤

    点击这里下载zlib1 2 11源码 http zlib net zlib1211 zip 下载源码库 从上面给出的源码路径下载zlib源码库 如果不想自己编译 可以使用上面给出的二进制包直接使用 无视本文 编译步骤 编译方法一 解压源码文
  • MyBatis-plus 动态条件构造器总结

    MyBatis plus 动态条件构造器类结构图 MyBatis Plus条件构造器QueryWrapper对应常用SQL语法说明 函数 说明 SQL语法 eq 等于 ne 不等于 lt gt gt 大于 gt lt 小于 lt ge 大于
  • STM32单片机通过ESP8266WiFi模块与Android APP实现数据传输(一)---下位机硬件配置

    事务的难度远远低于对事物的恐惧 STM32F407单片机通过ESP8266 WiFi模块与Android 手机APP连接实现数据的相互传输 在单片机上通过LCD显示屏实时显示连接的状态以及互相传输的数据 先看效果图 STM32单片机 And
  • simulink教程(自动控制原理)

    1 启动simulink 命令行输入simulink或者 会弹出 2 点击blank model 出现新窗口 新建或者打开模型文件 There are two major classes of items in Simulink block
  • GLES3.0中文API-glDrawRangeElements

    名称 glDrawRangeElements 从数组数据渲染基元 C规范 void glDrawRangeElements GLenum mode GLuint start GLuint end GLsizei count GLenum t
  • Open mv识别三角形的办法

    文章目录 前言 带着问题来看 一 函数 二 使用方法 1 find line segments 2 img find template 三 摄像情况及终端结果 1 find line segments 2 img find template
  • 初始C语言——利用Ascll码进行字母大小写转换

    打开Ascll码表 你会发现大写字母和小写字母之间存在这样的关系 图片来自 https img blog csdnimg cn 54404234b42348d6a33bc1c4d5ab24e5 png 小写字母的值始终比大写字母多32 de
  • Node.js

    Node js Node js基础 概念 简单的说 Node js 就是运行在服务端的 JavaScript Node js 是一个基于Chrome JavaScript 运行时建立的一个平台 Node js是一个事件驱动I O服务端Jav
  • (五)决策树

    一 决策树 决策树是监督学习算法 下面为一些样本 本质上是一种特征去结果的相关度 比如你的信贷情况与能否还贷的相关度肯定高 而你有没有结婚的相关度肯定低 二 信息增益 三 ID3算法
  • php 未支付取消订单,【php】用户提交订单,30分钟后没付款取消订单功能分析

    我先在要做这样的功能 用户在创建订单后 订单表中记入的是未付款状态 如果用户在30分钟后 还未付款 然后就把该订单给取消 关于用户创建订单 30分钟后还没付款 取消该订单的逻辑是怎么实现的 我自己的想了两个方案 1 客户端记入这个订单 如果
  • MindNode 5 for Mac(思维导图软件)中文版

    绘制流程图 思维导图 规划图 信息图等自然少不了这款MindNode 5 for Mac 作为优质的思维导图软件 mindnode5 mac破解版的功能很全面 添加文字 链接 图片 扩展注释等非常便捷 而且mindnode 5 破解版会智能
  • Rocketmq原理&最佳实践

    一 MQ背景 选型 消息队列作为高并发系统的核心组件之一 能够帮助业务系统解构提升开发效率和系统稳定性 主要具有以下优势 削峰填谷 主要解决瞬时写压力大于应用服务能力导致消息丢失 系统奔溃等问题 系统解耦 解决不同重要程度 不同能力级别系统
  • Python开发篇——基于React-Dropzone开发上传组件

    这次我要讲述的是在React Flask框架上开发上传组件的技巧 我目前主要以React开发前端 在这个过程中认识到了许多有趣的前端UI框架 React Bootstrap Ant Design Material UI Bulma等 而比较
  • Linux操作系统知识点总结

    1 什么是Linux系统 Linux 全称GNU Linux 是一种免费使用和自由传播的类UNIX操作系统 其内核由林纳斯 本纳第克特 托瓦兹 Linus Benedict Torvalds 于1991年10月5日首次发布 它主要受到Min
  • Qt 实现自定义Ui控件例子,以自定义的Slider为例(QWidget)

    说明 Qt可以比较方便地实现自定义控件在Qt Creator中使用 网上也有很多大神的控件可以使用 但是如果想要自己简单定制也可以按照这个流程 本文的要点 1 如何实现一个自定义控件 本文使用的方法有两个步骤 先在一个普通项目中实现使用 新