对已经存在的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
学习继续中...