Qwt提供了自己的缩放机制。
QwtMagnifier类:提供逐步放大缩小功能的抽象基类。被QwtPlotMagnifier继承。继承关系如下图所示:
代码分析:
一、QwtMagnifier类
1、构造函数:
/*! Constructor \param parent Widget to be magnified */ QwtMagnifier::QwtMagnifier( QWidget *parent ): QObject( parent ) { d_data = new PrivateData(); setEnabled( true ); }
构造函数要求传入被放大缩小的部件parent (通常是QwtPlotCanvas)。
2、安装/卸载事件过滤器:
QwtMagnifier对象对“鼠标、滚轮,键盘”事件的响应(获取)是通过事件机制传递的。在setEnabled()函数中,通过安装/卸载事件过滤器来达到开启/禁用缩放功能的目的。
/*! \brief En/disable the magnifier When enabled is true an event filter is installed for the observed widget, otherwise the event filter is removed. \param on true or false \sa isEnabled(), eventFilter() */ void QwtMagnifier::setEnabled( bool on ) { if ( d_data->isEnabled != on ) { d_data->isEnabled = on; QObject *o = parent(); if ( o ) { if ( d_data->isEnabled ) o->installEventFilter( this ); else o->removeEventFilter( this ); } } }这是一个利用Qt事件机制的极好例子。 注意以上代码:如果开启,则为对象o(即parent)安装this(即QwtMagnifier)的事件过滤器,也就是说,所有发送到对象o的事件都会先传给QwtMagnifier的eventFilter()函数过滤(即处理)。如果要禁用缩放功能,则只需卸载即可。
3、看一下事件过滤器函数的代码:
/*! \brief Event filter When isEnabled() the mouse events of the observed widget are filtered. \param object Object to be filtered \param event Event \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent() widgetKeyReleaseEvent() */ bool QwtMagnifier::eventFilter( QObject *object, QEvent *event ) { if ( object && object == parent() ) // 判断事件属主对象 { switch ( event->type() ) { case QEvent::MouseButtonPress: { widgetMousePressEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( ( QMouseEvent * )event ); break; } case QEvent::Wheel: { widgetWheelEvent( ( QWheelEvent * )event ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( ( QKeyEvent * )event ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( ( QKeyEvent * )event ); break; } default:; } } return QObject::eventFilter( object, event ); }4、再看看获取父类对象的函数,可以学习一个新的方法: bool QObject::inherits ( const char * className ) const:
//! \return Parent widget, where the rescaling happens QWidget *QwtMagnifier::parentWidget() { if ( parent()->inherits( "QWidget" ) ) return ( QWidget * )parent(); return NULL; } //! \return Parent widget, where the rescaling happens const QWidget *QwtMagnifier::parentWidget() const { if ( parent()->inherits( "QWidget" ) ) return ( const QWidget * )parent(); return NULL; }判断一个对象是否是名称为className 的类的实例或其子类实例。代码如下:
inline bool inherits(const char *classname) const { return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }不过有一个疑问:这个地方为什么不使用 T qobject_cast ( QObject * object ) ???
5、QwtMagnifier是一个抽象基类,有一个负责具体实现放大缩小的功能函数:
protected: /*! Rescale the parent widget \param factor Scale factor */ virtual void rescale( double factor ) = 0;纯虚函数,其具体实现被延迟到子类中。
二、QwtPlotMagnifier类
首先我们看看Qwt文档对QwtPlotMagnifier类的说明:
QwtPlotMagnifier provides zooming, by magnifying in steps.
Using QwtPlotMagnifier a plot can be zoomed in/out in steps using keys, the mouse wheel or moving a mouse button in vertical direction.
Together with QwtPlotZoomer and QwtPlotPanner it is possible to implement individual and powerful navigation of the plot canvas.
1、构造函数:
/*! Constructor \param canvas Plot canvas to be magnified */ QwtPlotMagnifier::QwtPlotMagnifier( QwtPlotCanvas *canvas ): QwtMagnifier( canvas ) { d_data = new PrivateData(); }
注意:传递进去的参数是QwtPlotCanvas而非QwtPlot。
2、可以只“设置指定的轴”被放大或缩小,而其它轴保持不变:/*! \brief En/Disable an axis Only Axes that are enabled will be zoomed. All other axes will remain unchanged. \param axis Axis, see QwtPlot::Axis \param on On/Off \sa isAxisEnabled() */ void QwtPlotMagnifier::setAxisEnabled( int axis, bool on ) { if ( axis >= 0 && axis < QwtPlot::axisCnt ) d_data->isAxisEnabled[axis] = on; }3、返回QwtPlotMagnifier类操作的画布对象QwtPlotCanvas:
//! Return observed plot canvas QwtPlotCanvas *QwtPlotMagnifier::canvas() { return qobject_cast<QwtPlotCanvas *>( parent() ); } //! Return Observed plot canvas const QwtPlotCanvas *QwtPlotMagnifier::canvas() const { return qobject_cast<const QwtPlotCanvas *>( parent() ); }这里使用了 T qobject_cast ( QObject * object ) 。
4、一个好的防御性编码实现例子:
//! Return plot widget, containing the observed plot canvas QwtPlot *QwtPlotMagnifier::plot() { QwtPlotCanvas *w = canvas(); if ( w ) return w->plot(); return NULL; } //! Return plot widget, containing the observed plot canvas const QwtPlot *QwtPlotMagnifier::plot() const { const QwtPlotCanvas *w = canvas(); if ( w ) return w->plot(); return NULL; }我经常写成:
return canvas()->plot();
编码时总是求快,着急看到效果,没有防错意识。这样的编码习惯何谈提高代码质量? 差距啊!
5、最后,也贴上功能函数rescale( factor )的实现代码:
/*! Zoom in/out the axes scales \param factor A value < 1.0 zooms in, a value > 1.0 zooms out. */ void QwtPlotMagnifier::rescale( double factor ) { factor = qAbs( factor ); if ( factor == 1.0 || factor == 0.0 ) return; bool doReplot = false; QwtPlot* plt = plot(); const bool autoReplot = plt->autoReplot(); plt->setAutoReplot( false ); for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) { const QwtScaleDiv *scaleDiv = plt->axisScaleDiv( axisId ); if ( isAxisEnabled( axisId ) && scaleDiv->isValid() ) { const double center = scaleDiv->lowerBound() + scaleDiv->range() / 2; const double width_2 = scaleDiv->range() / 2 * factor; plt->setAxisScale( axisId, center - width_2, center + width_2 ); doReplot = true; } } plt->setAutoReplot( autoReplot ); if ( doReplot ) plt->replot(); }以后实现自己特定的缩放类时可以参考。
三、疑问:
现在对缩放操作类QwtMagnifier里的鼠标、滚轮、键盘的设置的作用不清楚。
void setMouseButton( int button, int buttonState = Qt::NoButton ); void setWheelButtonState( int buttonState ); void setZoomInKey( int key, int modifiers ); void setZoomOutKey( int key, int modifiers );1、试了很多情况,也没看出有什么效果?