Subclassing QWidget

iconeditor.h
 1 #ifndef ICONEDITOR_H
 2 #define ICONEDITOR_H
 3 
 4 #include <QColor>
 5 #include <QImage>
 6 #include <QWidget>
 7 
 8 class IconEditor : public QWidget
 9 {
10     Q_OBJECT
11     Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor)
12     Q_PROPERTY(QImage iconImage READ iconImage WRITE setIconImage)
13     Q_PROPERTY(int zoomFactor READ zoomFactor WRITE setZoomFactor)
14 
15 public:
16     IconEditor(QWidget *parent = 0);
17 
18     void setPenColor(const QColor &newColor);
19     QColor penColor() const { return curColor; }
20     void setZoomFactor(int newZoom);
21     int zoomFactor() const { return zoom; }
22     void setIconImage(const QImage &newImage);
23     QImage iconImage() const { return image; }
24     QSize sizeHint() const;
25 
26 protected:
27     void mousePressEvent(QMouseEvent *event);
28     void mouseMoveEvent(QMouseEvent *event);
29     void paintEvent(QPaintEvent *event);
30 
31 private:
32     void setImagePixel(const QPoint &pos, bool opaque);
33     QRect pixelRect(int i, int j) const;
34 
35     QColor curColor;
36     QImage image;
37     int zoom;
38 };
39 #endif

  Q_PROPERTY宏声明三个自定义属性:penColor,iconImage,zoomFactor。每一个属性都有一个数据类型,一个“读”函数,和一个可以选的“写”函数。比如:penColor类型是QColor,可以用penColor()和setPenColor函数进行读写。

Q_PROPERTY(...)被用在类中声明继承自QObject的属性。这些属性就像类的数据成员一样,但是他们通过Meta-Object System具有附加的特性。

 Q_PROPERTY(type name
            READ getFunction
            [WRITE setFunction]
            [RESET resetFunction]
            [NOTIFY notifySignal]
            [DESIGNABLE bool]
            [SCRIPTABLE bool]
            [STORED bool]
            [USER bool]
            [CONSTANT]
            [FINAL])


属性名字、类型和READ函数是必须的。类型可以是任何QVariant支持的类型,或用户自定义类型。其他项是可选的,但是通常都有WRITE函数。这些属性的默认值都为true,除了USER是false。

The Meta-Object System

Meta-Object系统基于以下三点:

1、QObject类为目标类提供基类以支持这个目标类利用Meta-Object系统。

2、类声明中必须有O_OBJECT宏,使能Meta-Object的属性,如:动态属性、信号、槽。

3、Meta-Object Compiler(moc)为每个QObject的子类华提供必要代码以实现meta-object特性。

  IconEditor重实现了三个QWidget的protected function:mousePressEven,mouseMoveEvent,paintEvent。 三个私有变量保存前面定义的三个属性的值。

iconeditor.cpp
 1 #include <QtGui>
 2 
 3 #include "iconeditor.h"
 4 
 5 IconEditor::IconEditor(QWidget *parent)
 6     : QWidget(parent)
 7 {
 8     setAttribute(Qt::WA_StaticContents);
 9     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
10 
11     curColor = Qt::black;
12     zoom = 8;
13 
14     image = QImage(16, 16, QImage::Format_ARGB32);
15     image.fill(qRgba(0, 0, 0, 0));
16 }

  pen color设为黑色,zoom设为8,意味着在icon中每个像素为8X8的正方形。

  icon数据保存在i,mage成员变量中,可以通过setIconImage()、iconImage()函数访问。一个icon编辑程序通常在打开一个icon文件时调用setIconImage(),在保存时调用iconImage()去读回icon(原句retrieve the icon)。

  QImage可以把图像存为1-bit,8-bit,32-bit depth。 32位用8位分别表示红、绿、蓝,剩下的8位表示透明度。比如纯红色:255,0,0,255。

QRgb red = qRgba(255, 0, 0, 255);

或者颜色为不透明的

QRgb red = qRgb(255, 0, 0);

QRgb即是unsigned int, qRgb() 和 qRgb()都是用来把它们组成一个32位的ARGB整数值的内嵌函数。也可以写成如下形式:

QRgb red = 0xFFFF0000;

Qt提供QRgb 和QColor两种类型存储颜色。但是QRgb只用在QImage中存储32位像素的别名,QColor用途则更广泛。

iconeditor.cpp
 1 QSize IconEditor::sizeHint() const
 2 {
 3     QSize size = zoom * image.size();
 4     if (zoom >= 3)
 5         size += QSize(1, 1);
 6     return size;
 7 }
 8 
 9 void IconEditor::setPenColor(const QColor &newColor)
10 {
11     curColor = newColor;
12 }
13 
14 void IconEditor::setIconImage(const QImage &newImage)
15 {
16     if (newImage != image) {
17         image = newImage.convertToFormat(QImage::Format_ARGB32);
18         update();
19         updateGeometry();
20     }
21 }
22 
23 void IconEditor::setZoomFactor(int newZoom)
24 {
25     if (newZoom < 1)
26         newZoom = 1;
27 
28     if (newZoom != zoom) {
29         zoom = newZoom;
30         update();
31         updateGeometry();
32     }
33 }

sizeHint()是从QWidget中重新实现的。用图像的大小乘以缩放因子作为窗口部件的理想大小,如果缩放因子大于等于3,则向每个方向增加一个像素,以容纳网格线(如果是2或1则网络线会占据大部分空间,以至像素可以忽略)。
QWidget::update() 使用新的图像强制重绘这个窗口部件,但是这个函数不立即重绘,而是产生一人下paint event ,让Qt返回主事件循环时执行。

QWidget::updateGeometry()告诉包含这个窗口部件的任意布局,这个窗口大小已经改变。

iconeditor.cpp
 1 void IconEditor::paintEvent(QPaintEvent *event)
 2 {
 3     QPainter painter(this);
 4     if (zoom >= 3) {
 5         painter.setPen(palette().foreground().color());
 6         for (int i = 0; i <= image.width(); ++i)
 7             painter.drawLine(zoom * i, 0,
 8                              zoom * i, zoom * image.height());
 9         for (int j = 0; j <= image.height(); ++j)
10             painter.drawLine(0, zoom * j,
11                              zoom * image.width(), zoom * j);
12     }
13     for (int i = 0; i < image.width(); ++i) {
14         for (int j = 0; j < image.height(); ++j) {
15             QRect rect = pixelRect(i, j);
16             if (!event->region().intersect(rect).isEmpty()) {
17                 QColor color = QColor::fromRgba(image.pixel(i, j));
18                 if (color.alpha() < 255)
19                     painter.fillRect(rect, Qt::white);
20                 painter.fillRect(rect, color);
21             }
22         }
23     }
24 }
25 
26 QRect IconEditor::pixelRect(int i, int j) const
27 {
28     if (zoom >= 3) {
29         return QRect(zoom * i + 1, zoom * j + 1, zoom - 1, zoom - 1);
30     } else {
31         return QRect(zoom * i, zoom * j, zoom, zoom);
32     }
33 }

Qt中很多情况下都会产生绘制事件,调用paintEvent()函数:

1.         当控件第一次显示时,Qt自动产生绘制事件强制控件绘制自身。
2.         当控件尺寸发生变化时,系统产生绘制事件
3.         如果控件被其他的窗口遮住,窗口移走再现时,产生绘制被遮住部分的事件。

一个窗口部件的调色板由三个颜色组构成:Active, Inactive, Disabled 

Active 可用于当前激活窗口中的那些窗口部件。

Inactive可用于其他窗口中的那些窗口头部件。

Disabled可用于任意窗口中的那些不可用的窗口部件。

QWidgt::paletet()函数返回窗口部件的调色板,它是一个QPalette型对象。

foreground()是Qt3支持的QColorGroup成员,QPalette::foreground()返回一个画笔。

IconEditor::pixelRect()返回一个QRect,其中定义了需要重新绘制的区域。

const QRegion & QPaintEvent::region() const返回一个需要更新的区域。

QRegion QRegion::intersect ( const QRegion & r ) const

intersect()是QRegion废弃的成员,新的为:

QRegion QRegion::intersected ( const QRect & rect ) const

返回该区域与给定Rect相交的部分。

bool QRegion::isEmpty () const

QRegion r1(10, 10, 20, 29);

r1.isEmpty();                             //false

QRegion r3;

r3.isEmpty();          // true

QRegionr2(40, 40, 20, 20);

r3 = r1.intersected(r2);      //r3: intersection(交叉) of r1 and r2

r3.isEmpty();          //false

r3 = r1.united(r2);       // r3:union of r1 and r2

r3.isEmpty();          /false

QColor QColor::fromRgba ( QRgb rgba ) [static]

把rgba值变成QColor ,QRgb QImage::piixel(const QPoint & position) const 返回给定位置像素的颜色,如果该位置无效,则返回值未定义。

void QPainter::fillRect ( const QRectF & rectangle, const QBrush & brush )

用指定的brush填充指定的区域。painter.fillRect(rect,color);即是将载入的图像画在窗口部件指定区域上。

   综上所述,只需要void IconEditor::setIconImage(const QImage &newImage);    void paintEvent(QPaintEvent *event);   QRect pixelRect(inti, intj) const; 就可以把":/images/mouse.png"显示在窗口部件上:

  1、main函数中调用 setIconImage()载入图片。

  2、在第一次显示窗口时产生paint event。

  3、主程序循环时,处理paint event,调用paintEvent()绘图(在这个函数中用到pixelRect()定位要绘制的区域)。

iconeditor.cpp
 1 void IconEditor::mousePressEvent(QMouseEvent *event)
 2 {
 3     if (event->button() == Qt::LeftButton) {
 4         setImagePixel(event->pos(), true);
 5     } else if (event->button() == Qt::RightButton) {
 6         setImagePixel(event->pos(), false);
 7     }
 8 }
 9 
10 void IconEditor::mouseMoveEvent(QMouseEvent *event)
11 {
12     if (event->buttons() & Qt::LeftButton) {
13         setImagePixel(event->pos(), true);
14     } else if (event->buttons() & Qt::RightButton) {
15         setImagePixel(event->pos(), false);
16     }
17 }
18 
19 void IconEditor::setImagePixel(const QPoint &pos, bool opaque)
20 {
21     int i = pos.x() / zoom;
22     int j = pos.y() / zoom;
23 
24     if (image.rect().contains(i, j)) {
25         if (opaque) {
26             image.setPixel(i, j, penColor().rgba());
27         } else {
28             image.setPixel(i, j, qRgba(0, 0, 0, 0));
29         }
30         update(pixelRect(i, j));
31     }
32 }

  以上三个函数用于响应鼠标事件,用鼠标绘制图

main.cpp
 1 #include <QApplication>
 2 
 3 #include "iconeditor.h"
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication app(argc, argv);
 8     IconEditor iconEditor;
 9     iconEditor.setWindowTitle(QObject::tr("Icon Editor"));
10     iconEditor.setIconImage(QImage(":/images/mouse.png"));
11     iconEditor.show();
12     return app.exec();
13 }

 

 

posted @ 2012-12-17 22:25  永不指步  阅读(349)  评论(0编辑  收藏  举报