通过对一个已经存在的Qt窗口部件进行子类化或者直接对QWidget进行子类化,就可以创建自定义窗口部件,主要是继承内置窗口部件类,然后重新实现那些能帮助我们实现自己想要的函数
用书中的创建十六进制微调框的例子
class HexSpinBox:public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent=0);
protected:
QValidator::State validate(QString &input, int &pos) const;
int valueFromText(const QString &text) const;
QString textFromValue(int val) const;
private:
QRegExpValidator *validator;
};
HexSpinBox::HexSpinBox(QWidget *parent)
:QSpinBox(parent)
{
setRange(0,255);
validator=new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"),this);
}
QRegExpValidator是用于检查正则表达式的字符串,头文件中的保护函数全都是QSpinBox所有的
QValidator::State HexSpinBox::validate(QString &input, int &pos) const
{
return validator->validate(input,pos);
}
这个函数由QSpinBox调用,用来检查目前为止用户输入文本的合法性,会返回三种结果,无效(Invalid),部分有效(Intermediate)和有效(Acceptable),只需要调用QRegExpValidator的成员函数就可以返回所需要的
QString HexSpinBox::textFromValue(int value) const
{
return QString::number(value,16).toUpper();
}
当用户按下微调框的向上或者向下箭头,QSpinBox就会调用它来更新微调框的编辑器部分
int HexSpinBox::valueFromText(const QString &text) const
{
bool ok;
return text.toInt(&ok,16);//以16为基数
}
当用户在微调框输入一个值并且按下enter时,QSpinBox就会调用它。
当要创建自己的窗口部件时,可以通过子类化QWidget,并且通过重新实现一些用来绘制窗口部件和响应鼠标的事件处理器就可
双缓冲把一个窗口部件渲染到一个脱屏像素映射中以及把这像素映射复制到显示器上,简单来说就是在进行绘制时,先把所有内容都绘制到一个绘图设备上,然后再将整个图像绘制到部件上显示,对于绘制复杂而又需要不断重复绘制的情况,双缓冲很有用,可以避免出现闪烁现象。
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
// 缓冲区
QPixmap pix;
// 临时缓冲区
QPixmap tempPix;
QPoint startPoint;
QPoint endPoint;
// 是否正在绘图的标志
bool isDrawing;
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
};
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
pix = QPixmap(400, 300);
pix.fill(Qt::white);
tempPix = pix;
isDrawing = false;
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
// 当鼠标左键按下时获取当前位置作为矩形的开始点
startPoint = event->pos();
// 标记正在绘图
isDrawing = true;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton) {
// 当按着鼠标左键进行移动时,获取当前位置作为结束点,绘制矩形
endPoint = event->pos();
// 将缓冲区的内容复制到临时缓冲区,这样进行动态绘制时,
// 每次都是在缓冲区图像的基上进行绘制,就不会产生拖影现象了
tempPix = pix;
// 更新显示
update();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
// 当鼠标左键松开时,获取当前位置为结束点,完成矩形绘制
endPoint = event->pos();
// 标记已经结束绘图
isDrawing = false;
update();
}
}
void Widget::paintEvent(QPaintEvent *event)
{
int x = startPoint.x();
int y = startPoint.y();
int width = endPoint.x() - x;
int height = endPoint.y() - y;
QPainter painter;
painter.begin(&tempPix);
painter.drawRect(x, y, width, height);
painter.end();
painter.begin(this);
painter.drawPixmap(0, 0, tempPix);
// 如果已经完成了绘制,那么更新缓冲区
if(!isDrawing)
pix = tempPix;
}