Subclassing QWidget
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。 三个私有变量保存前面定义的三个属性的值。
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用途则更广泛。
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()告诉包含这个窗口部件的任意布局,这个窗口大小已经改变。
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()函数:
一个窗口部件的调色板由三个颜色组构成: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()定位要绘制的区域)。
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 }
以上三个函数用于响应鼠标事件,用鼠标绘制图
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 }