在Qt4.0里Scribble example里画线条是先draw在一个image上,image在通过drawImage画到widget上,给出了这样的理由
At this point, you might wonder why we don't just draw directly onto the widget instead of drawing in a QImage and copying the QImage onto screen in paintEvent(). There are at least three good reasons for this:

The window system requires us to be able to redraw the widget at any time. For example, if the window is minimized and restored, the window system might have forgotten the contents of the widget and send us a paint event. In other words, we can't rely on the window system to remember our image.
Qt normally doesn't allow us to paint outside of paintEvent(). In particular, we can't paint from the mouse event handlers. (This behavior can be changed using the Qt::WA_PaintOnScreen widget attribute, though.)
If initialized properly, a QImage is guaranteed to use 8-bit for each color channel (red, green, blue, and alpha), whereas a QWidget might have a lower color depth, depending on the monitor configuration. This means that if we load a 24-bit or 32-bit image and paint it onto a QWidget, then copy the QWidget into a QImage again, we might lose some information.

image比widget要稍大一点,理由如下
When the user starts the Scribble application, a resize event is generated and an image is created and displayed in the scribble area. We make this initial image slightly larger than the application's main window and scribble area, to avoid always resizing the image when the user resizes the main window (which would be very inefficient). But when the main window becomes larger than this initial size, the image needs to be resized.

当需要重绘的事件paintEvent时,通过传给update()一个参数可能会提高效率,当然前提是你知道这个参数
We could call the update() function with no parameter, but as an easy optimization we pass a QRect that specifies the rectangle inside the scribble are needs updating, to avoid a complete repaint of the widget.
例如这里传入的是,鼠标新画的线段 与笔的大小组成的一个矩形区域。这样只会重新绘制这里了~~
 Painter painter(&image);
     painter.setPen(QPen(myPenColor, myPenWidth, Qt::SolidLine, Qt::RoundCap,
                         Qt::RoundJoin));
     painter.drawLine(lastPoint, endPoint);
     modified = true;

     int rad = (myPenWidth / 2) + 2;
     update(QRect(lastPoint, endPoint).normalized()
                                      .adjusted(-rad, -rad, +rad, +rad));

当有多个signal连接到一个槽时,例如在保存文件时,给出的QAction有 BMP JPG JPEG 等,此时只需要一个save()的槽函数,save()函数需要知道是哪个signal发送过来,此时将根据QObject * QObject::sender () const   [protected]来判断sender
该函数介绍如下:
QObject * QObject::sender () const   [protected]
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.

The pointer returned by this function becomes invalid if the sender is destroyed, or if the slot is disconnected from the sender's signal.

Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.

Warning: As mentioned above, the return value of this function is not valid when the slot is called via a Qt::DirectConnection from a thread different from this object's thread. Do not use this function in this type of scenario.
具体scribble 里代码如下
 void MainWindow::save()
 {
     QAction *action = qobject_cast<QAction *>(sender());
     QByteArray fileFormat = action->data().toByteArray();
     saveFile(fileFormat);
 }
第一句:
QAction *action = qobject_cast<QAction *>(sender());
虽然不知道sender()具体是哪一个QAction,但我们知道肯定是一个QAction,因此可以cast过来。
用static_cast<>也是可以的,这里使用qobject_cast的原因例子里说明如下
but as a defensive programming technique we use a qobject_cast(). The advantage is that if the object has the wrong type, a null pointer is returned. Crashes due to null pointers are much easier to diagnose than crashes due to unsafe casts.
第二句:
QAction里有setdata() 和 data()两个函数作用是这样的
1.
QVariant QAction::data () const
Returns the user data as set in QAction::setData.
2.
void QAction::setData ( const QVariant & userData )
Sets the action's internal data to the given userData.

在创建QAction的时候,我们setData()
     foreach (QByteArray format, QImageWriter::supportedImageFormats()) {
         QString text = tr("%1...").arg(QString(format).toUpper());

         QAction *action = new QAction(text, this);
         action->setData(format);
         connect(action, SIGNAL(triggered()), this, SLOT(save()));
         saveAsActs.append(action);
     }
此时通过ction->data().toByteArray();取出来
然后传给第三句,saveFile函数就知道该存为哪种格式了

ps:
在界面编程里,例如写一个save()函数,可能saveFile()才是实现保存文件的作用的。
但点击saveaction时,调用save(),实际上需要判断各种情况,例如文件名合法/同名等,需要根据不同情况弹出不同的对话框,当全都判断true后,才会调用saveFile()函数,这可能就是跟之前自己编程差别比较大的情况了~~