第五章 创建自定义窗口部件

2023-11-14

对已经存在的Qt窗口进行子类化或者直接对QWidget子类化可以快速创建自己的自定义窗口部件。


一、自定义窗口部件 十六进制的QSpinBox 

本来QSpinBox仅支持十进制数据的,现在子类化接收并显示十六进制数值。

头文件

hexspinbox.h

#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H

#include <QSpinBox>

class QRegExpValidator;

class HexSpinBox : public QSpinBox
{
	Q_OBJECT

public:
	HexSpinBox(QWidget *parent = 0);

protected:
	QValidator::State validate(QString &text, int &pos) const;
	int valueFromText(const QString &text) const;
	QString textFromValue(int value) const;

private:
	QRegExpValidator *validator;
};

#endif


hexspinbox.cpp

#include <QtGui>

#include "hexspinbox.h"

HexSpinBox::HexSpinBox(QWidget *parent)
	: QSpinBox(parent)
{
	//设置默认接收数据范围
	setRange(0, 255);

	//正则表达式限制接收数据格式为十六进制
	validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
}

QValidator::State HexSpinBox::validate(QString &text, int &pos) const
{
	//用于检验输入的合法性
	return validator->validate(text, pos);
}

int HexSpinBox::valueFromText(const QString &text) const
{
	//数值型字符串转换成整型
	bool ok;
	return text.toInt(&ok, 16);
}

QString HexSpinBox::textFromValue(int value) const
{
	//把一个字符串转换成整数值
	return QString::number(value, 16).toUpper();
}

main.cpp

#include <QApplication>
#include <QHBoxLayout>

#include "hexspinbox.h"

int main(int argc, char *argv[])
{
	QApplication app(argc, argv);
	HexSpinBox spinBox;
	spinBox.setWindowTitle(QObject::tr("Hex Spin Box"));
	spinBox.show();
	return app.exec();
}


二、子类化QWidget (IconEdit鼠标图片编辑控件)

iconEditor.h

#ifndef ICONEDITOR_H
#define ICONEDITOR_H

#include <QColor>
#include <QImage>
#include <QWidget>
#include <QRegion>
class IconEditor : public QWidget
{
    Q_OBJECT
	//定义三个宏,实现三个自定义属性
    Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor)
    Q_PROPERTY(QImage iconImage READ iconImage WRITE setIconImage)
    Q_PROPERTY(int zoomFactor READ zoomFactor WRITE setZoomFactor)

public:
    IconEditor(QWidget *parent = 0);

    void setPenColor(const QColor &newColor);
    QColor penColor() const { return curColor; }
    void setZoomFactor(int newZoom);
    int zoomFactor() const { return zoom; }
    void setIconImage(const QImage &newImage);
    QImage iconImage() const { return image; }
    QSize sizeHint() const;

protected:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);

private:
    void setImagePixel(const QPoint &pos, bool opaque);
    QRect pixelRect(int i, int j) const;

    QColor curColor;
    QImage image;
    int zoom;
};

#endif


iconEditor.cpp

#include <QtGui>
#include "iconeditor.h"

IconEditor::IconEditor(QWidget *parent)
    : QWidget(parent)
{
    setAttribute(Qt::WA_StaticContents);
    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);

    curColor = Qt::black;	//画笔颜色
    zoom = 8;				//缩放因子为8

	//加载的图片
    image = QImage(16, 16, QImage::Format_ARGB32);
    image.fill(qRgba(0, 0, 0, 0));
}

void IconEditor::setPenColor(const QColor &newColor)
{
    curColor = newColor;
}

void IconEditor::setZoomFactor(int newZoom)
{
    if (newZoom < 1)
        newZoom = 1;

    if (newZoom != zoom) {
        zoom = newZoom;
        update();
        updateGeometry();
    }
}

void IconEditor::setIconImage(const QImage &newImage)
{
    if (newImage != image) {
        image = newImage.convertToFormat(QImage::Format_ARGB32);
        update();
        updateGeometry();
    }
}

QSize IconEditor::sizeHint() const
{
	//图片放大
    QSize size = zoom * image.size();
    if (zoom >= 3)
        size += QSize(1, 1);
    return size;
}

void IconEditor::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        setImagePixel(event->pos(), true);
    } else if (event->button() == Qt::RightButton) {
        setImagePixel(event->pos(), false);
    }
}

void IconEditor::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        setImagePixel(event->pos(), true);
    } else if (event->buttons() & Qt::RightButton) {
        setImagePixel(event->pos(), false);
    }
}

void IconEditor::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    if (zoom >= 3) {
        painter.setPen(palette().foreground().color());
        for (int i = 0; i <= image.width(); ++i)
            painter.drawLine(zoom * i, 0,
                             zoom * i, zoom * image.height());
        for (int j = 0; j <= image.height(); ++j)
            painter.drawLine(0, zoom * j,
                             zoom * image.width(), zoom * j);
    }

    for (int i = 0; i < image.width(); ++i) {
        for (int j = 0; j < image.height(); ++j) {
            QRect rect = pixelRect(i, j);
            if (!event->region()./*intersect*/intersected(rect).isEmpty()) {
                QColor color = QColor::fromRgba(image.pixel(i, j));
                if (color.alpha() < 255)
                    painter.fillRect(rect, Qt::white);
                painter.fillRect(rect, color);
            }
        }
    }
}

void IconEditor::setImagePixel(const QPoint &pos, bool opaque)
{
    int i = pos.x() / zoom;
    int j = pos.y() / zoom;

    if (image.rect().contains(i, j)) {
        if (opaque) {
            image.setPixel(i, j, penColor().rgba());
        } else {
            image.setPixel(i, j, qRgba(0, 0, 0, 0));
        }

        update(pixelRect(i, j));
    }
}

QRect IconEditor::pixelRect(int i, int j) const
{
    if (zoom >= 3) {
        return QRect(zoom * i + 1, zoom * j + 1, zoom - 1, zoom - 1);
    } else {
        return QRect(zoom * i, zoom * j, zoom, zoom);
    }
}


update();
强制产生一个绘制事件,repaint()也可以实现重绘功能,但是它是即时绘制,而update是在Qt下一次绘制时才开始,如果下一次绘制前有多个update()则会融合成一个单一的绘制事件,可以避免闪烁。

pos指的是鼠标的实时位置,就可以方便的获取到绘制图形的位置了。


构造函数的这句话是为了控制窗口部件的内容不变,效果如图所示

setAttribute(Qt::WA_StaticContents);

绘制事件的区域会被严格限制在之前没有显示的像素部分上。这就意味即时窗口比内容小也不会触发绘制事件。还挺有意思的诶


三、在Qt Designer中自定义窗口部件

在自定义之前,应该让Qt Designer察觉到他们的存在,改进法和插件法;

改进法比较简单,选择一个内置Qt窗口部件,接口与自己的需求当然类似才好用。

改进法使用方法:

*在Qt Designer 中拖进一个QSpinBox控件;

*右击选择“提升为...”(提升的窗口部件或者成为改进程自定义窗口部件)

*HexSpixBox作为类名,hexspinbox.h作为头文件名

缺点是无法对部件特定的属性进行访问。


插件法:需要创建一个插件库,Qt设计师会在运行时加载这个库,并且可以利用该库创建窗口部件的实例。

首先必须对QDesignerCustomWidgetInterface进行子类化,并且需要重新实现一些虚函数。假设源代码在iconeditorplugin文件夹中,并且IconEdit的源代码放在iconeditorplugin文件夹同一级iconedit目录中。

类定义:

iconeditorplugin.h

#ifndef ICONEDITORPLUGIN_H
#define ICONEDITORPLUGIN_H

#include <QDesignerCustomWidgetInterface>

class IconEditorPlugin : public QObject,
                         public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetInterface)

public:
    IconEditorPlugin(QObject *parent = 0);

    QString name() const;
    QString includeFile() const;
    QString group() const;
    QIcon icon() const;
    QString toolTip() const;
    QString whatsThis() const;
    bool isContainer() const;
    QWidget *createWidget(QWidget *parent);
};

#endif

IconEditorPlugin子类是一个封装了这个IconEdit窗口部件的工厂类。


iconeditorplugin.cpp

#include <QtPlugin>

#include "../iconeditor/iconeditor.h"
#include "iconeditorplugin.h"

IconEditorPlugin::IconEditorPlugin(QObject *parent)
    : QObject(parent)
{
}

QString IconEditorPlugin::name() const
{
    return "IconEditor";
}

QString IconEditorPlugin::includeFile() const
{
    return "iconeditor.h";
}

QString IconEditorPlugin::group() const
{
    return tr("Image Manipulation Widgets");
}

QIcon IconEditorPlugin::icon() const
{
    return QIcon("iconeditor.png");
}

QString IconEditorPlugin::toolTip() const
{
    return tr("An icon editor widget");
}

QString IconEditorPlugin::whatsThis() const
{
    return tr("This widget is presented in Chapter 5 of <i>C++ GUI "
              "Programming with Qt 4</i> as an example of a custom Qt "
              "widget.");
}

bool IconEditorPlugin::isContainer() const
{
    return false;
}

QWidget *IconEditorPlugin::createWidget(QWidget *parent)
{
    return new IconEditor(parent);
}

Q_EXPORT_PLUGIN2(iconeditorplugin, IconEditorPlugin)

QWidget *IconEditorPlugin::createWidget(QWidget *parent)
Qt设计师会调用该函数,利用给定的父对象创建该窗口部件类的一个实例。

Q_EXPORT_PLUGIN2(iconeditorplugin, IconEditorPlugin)
实现该插件类的源文件末尾必须使用该宏,这让他可以在Qt设计师中使用哦~

这一部分还未验证通过,在VS工具中还在研究怎么添加控件到设计师。

不过可以参考http://blog.csdn.net/panshun888/article/details/51923927

学习继续中...




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

第五章 创建自定义窗口部件 的相关文章

  • 是否有可能劫持标准输出

    我正在尝试使用 C 重定向 Windows XP 上已运行进程的标准输出 我知道如果我自己生成进程 我可以做到这一点 但对于这个应用程序 我更喜欢一个 监听器 我可以附加到另一个进程 这在纯 Net 中可能吗 如果不可能 在 Win32 中
  • 如何在特定时间以毫秒精度触发 C# 函数?

    我有两台计算机 它们的时间通过 NTP 同步 确保时间仅相差几毫秒 其中一台计算机将通过 TCP 向另一台计算机发送一条消息 以在两台计算机上的未来指定时间启动某个 c 函数 我的问题是 如何在特定时间以毫秒精度 或更好 触发 C 中的函数
  • 使用 Json.NET 序列化子类

    我正在尝试使用 Json NET 序列化子类 生成的 json 包含超类的序列化属性 但是not子类对象的属性 这似乎与我发现的一个问题有关这里就这样 https stackoverflow com q 5863496 498969 但必须
  • .crt 部分?这个警告是什么意思?

    我最近收到此警告 VC 2010 warning LNK4210 CRT section exists there may be unhandled static initializers or terminators 我假设这是关键部分
  • STL之类的容器typedef快捷方式?

    STL 容器的常见模式是这样的 map
  • 最新 .Net MongoDb.Driver 的连接问题

    我创建了一个 MongoLab 沙箱数据库 我与 MongoChef 连接 效果很好 我通过 Nuget 安装了 MongoDB Driver 2 2 2 我编写了一些简单的 C 演示代码 但就是无法使其工作 连接字符串是直接从 Mongo
  • 警告 C4800:“int”:强制值为 bool“true”或“false”(性能警告)

    我的代码中有这个问题 bool CBase isNumber return id MID NUMBER bool CBase isVar return id MID VARIABLE bool CBase isSymbol return i
  • 局部函数声明有什么用处吗?

    大多数像我这样的 C 程序员都曾犯过以下错误 class C int main C c declares a function c taking no arguments returning a C not as intended by m
  • C# 无法捕获 SerializationException

    我的程序在加载序列化文件的部分遇到问题 如果文件无法反序列化 我希望很好地失败 但由于某种原因 我的程序将中断而不是进入 catch 子句 这是我的代码 using FileStream fs new FileStream openFile
  • Monotouch全局异常处理

    我在野外发现了一只令人讨厌的虫子 但我无法确定它的具体情况 有没有办法拥有全局 Try Catch 块 或者有办法处理 Monotouch 中未处理的任何异常 我可以包起来吗UIApplication Main args 在 try cat
  • .NET 5 EF Core SaveChangesAsync 因错误而挂起

    尽管这个问题有很多结果 但没有一个真正给我明确的答案 每次我尝试通过 AddAsync 和 SaveChangesAsync 方法插入错误数据 例如重复的主键 时 我都会看到以下日志 执行 DbCommand 失败 15 毫秒 我还在 SQ
  • 我在使用 ado.net 时收到错误 Argument 2 may not be pass with ref keywords

    int t 0 cmd Parameters AddWithValue Res ref t 我在第二行收到错误 参数 2 不能与 ref 关键字一起传递 您只能通过引用传递参数ref if the 范围 is a ref参数也是如此 Add
  • 我可以将 UseCSharpNullComparisonBehavior 用于单个查询吗?

    我有一个查询 该查询曾经是存储过程 现已转换为 EF 查询 现在已经超时了 使用 SQL Profiler 我可以看到生成的 SQL 的唯一区别是 EF 转变的新行为entity Property value into entity Pro
  • 如何构建一棵与或树?

    我需要一个支持 与 和 或 的树结构 例如 给定一个正则表达式 如ab c d e 我想把它变成一棵树 所以 一开始我们有两个 或 分支 它可以向下ab or c d e 如果你低头ab分支 你得到两个节点 a and b or a其次是b
  • C#:自定义转换为值类型

    是否可以将自定义类转换为值类型 这是一个例子 var x new Foo var y int x Does not compile 是否有可能实现上述情况 我需要超载一些东西吗Foo 您将必须重载强制转换运算符 public class F
  • valgrind 在 Raspberry Pi 上返回未处理的指令

    我最近一直在尝试在运行 Debian GNU Linux7 0 喘息 的树莓派 型号 b 上使用 valgrind 来调试分段错误 每次我在编译的 C 程序上运行 valgrind 时 都会得到类似以下内容的信息 disInstr arm
  • 如何同步nosql db(ravendb)中的更改

    我已经开始在 RavenDB 的示例上学习 NoSQL 我从一个最简单的模型开始 假设我们有由用户创建的主题 public class Topic public string Id get protected set public stri
  • 如何从 Access 数据库中读取“是/否”值作为布尔值?

    帮我找回YES NO来自 MS Access 的布尔格式数据类型 我尝试解析它 但它总是返回 false 更新 实际上不是问题抱歉 它确实接受 YES NO 作为布尔值 OleDbconnection dbConnect new OleDb
  • Xcode 7 调试器不会中断内联标头函数

    过去五年我一直在各种 C 项目中使用 Xcode 没有出现这个问题 今天 我打开了一个较旧的项目 大约 2 年前 并尝试通过在该函数中放置一个活动断点来调试头文件中的内联函数 由于某种原因 调试器不会中断此代码 但是 如果我在调用该函数的
  • 当我的进程被终止时到底会发生什么?

    我有一个包含本机代码和托管代码的混合进程 在 Windows Server 2003 上运行 当我从进程资源管理器中终止进程时 它会进入 100 cpu 的状态 并在消失之前保持这种状态一段时间 有时甚至 10 分钟 在此期间我无法 杀死

随机推荐

  • FIND_IN_SET

    FIND IN SET str strlist str 要查询的字符串 strlist 字段名 参数以 分隔 如 1 2 6 8 查询字段 strlist 中包含 str 的结果 返回结果为null或记录 假如字符串str在由N个子链组成的
  • 【NLP】3 种强大的长文本摘要方法和实例

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Python 全栈系列220 Tornado的服务搭建

    说明 想法变的真快 本来是没打算用Tornado的 主要是想节约时间 但是现在看来不用还是不行 目前用gevent flask部署的时候 启动多核的worker似乎存在问题 另外 有很多内部基础的数据服务 其实并不需要flask的各种组件
  • stata解决内生性问题--样本选择

    目录 简单介绍 1 内生性 2 为什么要解决内生性问题 3 内生性问题产生的原因 4 例子 代码 简洁版 代码 详细分析版 结果 简单介绍 1 内生性 x与误差项有相关关系 2 为什么要解决内生性问题 内生性会破坏参数估计的 一致性 参数估
  • 沁恒ch32V208处理器开发(四)串口通信

    目录 串口资源 资源配置 同步模式 单线半双工模式 中断 DMA 串口的初始化 串口通信的实现 串口资源 资源配置 CH32V208 系列 是基于 RISC V 指令架构设计的 32 位 RISC 内核 MCU 根据封装的不同 可用的USA
  • 永磁同步电机的矢量控制策略(十四)一一一位置环的仿真

    14 永磁同步电机的矢量控制策略 十四 14 1 永磁同步电机的三闭环矢量控制 之前的博客文章中所述的双闭环矢量控制系统 电流环属于内环 其作用是使电机电流跟随给定电流 速度环输出 的变化 对系统响应的快速性与准确性有着重要影响 转速环属于
  • git提交代码报 vue-cli-service lint found some errors. Please fix them and try committing again

    原因 问过度娘在提交代码的时候 它会在提交代码前运行做代码风格检查 如果代码不符合相应规则 则报错 解决 直接把pre commit文件删除 进入项目 git文件夹 hooks 删除 如何你的项目文件夹下没有找到 git文件夹 检查一下看看
  • java sheet 设置名称_Java 实现 给Excel模板赋值(直接打开表格赋值或者用自定义了名称的单元格(一块区域)赋值)...

    1 需求 直接打开表格填充数据到模板后的效果可能出现表格重叠的问题 用自定义名称填充数据到模板后表格互不影响 Excel自身有一个 定义名称 的功能 1 可以给任意的单元格定义一个名称 比如定义某个单元格的名称为 testA1 如何给这个名
  • IO复用进化史

    IO复用的历史和多进程一样长 Linux很早就提供了select系统调用 可以在一个进程内维护1024个连接 后来加入poll系统调用 poll做了一系列改进后解决了1024个连接的限制问题 可以维持任意数量的连接 但是select和pol
  • QT信槽编程,QObject::connect: Cannot connect (null)报错的两种成因

    connect ui btnHelp SIGNAL clicked this SLOT OnBtnHelp connect ui btnHelp SIGNAL toggled bool this SLOT OnBtnHelpChanged
  • Python的10个常用代码简写技术

    今天我给大家整理了一份10个程序员常用的代码简写技术 看懂一种是入门 全懂就是大神 你能知道几个呢 1 三元操作符 当想写if else语句时 使用三元操作符来代替 const x 20 let answer if x gt 10 简写 c
  • unity中物体移动的几种方式

    1 简介 在unity3d中 有多种方式可以改变物体的坐标 实现移动的目的 其本质是每帧修改物体的position 2 通过Transform组件移动物体 Transform 组件用于描述物体在空间中的状态 它包括 位置 position
  • 人脸识别引擎

    最近人工智能异常火爆 各路诸侯蠢蠢欲动 特别是金融业 安防业都在追求人工智能的落地 人工智能新秀云从 商汤 旷世 老牌王者海康 华为都在建设自己的人工智能平台 人工智能领域人脸识别目前来看是最先落地的技术 本文将介绍人脸识别产品中人脸识别引
  • STM3216位编码器溢出问题

    STM3216位编码器溢出问题 STM32定时器有编码器接口 但是它的计数器只有16位 当要记录的数过大时 会溢出 下文介绍了一种方法 能有效解决因计数器位数过少引起的溢出问题 在网上搜了好多 感觉他们说的方法都不准 这个方法经过我自己验证
  • StableDiffusion负面标签自动复制

    随着人工智能AI的兴起 现在AI画图已经风靡全球 其中StableDiffusion以开源 可以本地部署 免费白嫖 引起了包括本人在内的打工人的兴趣 但使用StableDiffusion时 时常会出现诸如 三只手 三只脚 畸形的五官等问题
  • C语言--八大排序之直接插入排序算法

    排序 把无序的数据变得有序 默认升序 笔试面试排名第一的内容 1 直接 简单 插入排序 例如 扑克牌发牌时 每发一张 将牌有序插入 从当前位置开始 从后往前找比当前数字小的 找到后插入到这个小的数字后面 在找的过程中 如果发现一个比当前数字
  • 【Docker网络】容器之间的网络是如何连通的?

    一 Docker0网络详解 1 1 宿主机获取IP l0 本机地址 eth0 阿里云内网地址 docker0 docker的网卡 1 2 docker如何处理容器之间的网络的 1 3 启动一个tomcat01查看docker容器内部的IP地
  • 【STM32】将KEIL下的工程移植到IAR下

    出现问题 IAR无法识别启动文件 汇编 原因 KEIL 和 IAR 中的汇编是不一样的 参考 https blog csdn net u011303443 article details 83177726
  • ES6基础详解

    文章目录 ES6理解 1 let和const 2 解构赋值 3 promise 4 async和await 5 Set和Map 6 箭头函数 7 函数的扩展 8 扩展运算符 ES6理解 当问到ES6时 通常指的是JavaScript的ECM
  • 第五章 创建自定义窗口部件

    对已经存在的Qt窗口进行子类化或者直接对QWidget子类化可以快速创建自己的自定义窗口部件 一 自定义窗口部件 十六进制的QSpinBox 本来QSpinBox仅支持十进制数据的 现在子类化接收并显示十六进制数值 头文件 hexspinb