Qwt源码解读之QwtLegend类

    QwtLegend类表征一个示例图容器部件【The legend widget.】,继承自QFrame。其上面使用QwtDynGridLayout动态排列了多个示例图,每个示例图可以是任何类型的QWidget窗体部件,但通常是QwtLegendItem。

代码分析:

1、构造函数:

/*!
  Constructor

  \param parent Parent widget
*/
QwtLegend::QwtLegend( QWidget *parent ):
    QFrame( parent )
{
    setFrameStyle( NoFrame );

    d_data = new QwtLegend::PrivateData;
    d_data->itemMode = QwtLegend::ReadOnlyItem;

    d_data->view = new QwtLegend::PrivateData::LegendView( this );
    d_data->view->setObjectName( "QwtLegendView" );
    d_data->view->setFrameStyle( NoFrame );

    QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
        d_data->view->contentsWidget );
    gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );

    d_data->view->contentsWidget->installEventFilter( this );

    QVBoxLayout *layout = new QVBoxLayout( this );
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->addWidget( d_data->view );
}
1)LegendView继承自QScrollArea,是一个滚动区域部件。即,

class QwtLegend::PrivateData::LegendView: public QScrollArea

2)d_data->view->contentsWidget->installEventFilter( this );  在d_data->view->contentsWidget(是一个QWidget)安装事件过滤器this (是一个QScrollArea),即发送给 contentsWidget 的事件都会经过 this 的 eventFilter() 函数过滤。

3)d_data->view->contentsWidget 作为视口的窗体部件,也是所有示例图元的父部件。

    QWidget *contentsWidget();
    const QWidget *contentsWidget() const;
该接口的文档说明如下:

The contents widget is the only child of the viewport of the internal QScrollArea and the parent widget of all legend items.
Returns:
       Container widget of the legend items。

2、在QwtLegend::PrivateData数据内部存储了QWidget(代表QwtLegend里的一个示例图元QwtLegendItem) 和 QwtLegendItemManager (代表画布QwtCanvas里的一个图元QwtPlotItem)的映射关系。

    private:
        QMap<QWidget *, const QwtLegendItemManager *> d_widgetMap;
        QMap<const QwtLegendItemManager *, QWidget *> d_itemMap;
在QwtLegend里提供了访问它们的接口:

    void insert( const QwtLegendItemManager *, QWidget * );
    void remove( const QwtLegendItemManager * );

    QWidget *find( const QwtLegendItemManager * ) const;
    QwtLegendItemManager *find( const QWidget * ) const;

    virtual QList<QWidget *> legendItems() const;

3、QwtLegend的事件过滤函数:

/*!
  Handle QEvent::ChildRemoved andQEvent::LayoutRequest events 
  for the contentsWidget().

  \param object Object to be filtered
  \param event Event
*/
bool QwtLegend::eventFilter( QObject *object, QEvent *event )
{
    if ( object == d_data->view->contentsWidget )
    {
        switch ( event->type() )
        {
            case QEvent::ChildRemoved: // 删除子项事件,Qt内部事件
            {
                const QChildEvent *ce = 
                    static_cast<const QChildEvent *>(event);
                if ( ce->child()->isWidgetType() )
                {
                    QWidget *w = static_cast< QWidget * >( ce->child() );
                    d_data->map.remove( w );
                }
                break;
            }
            case QEvent::LayoutRequest: // 布局请求事件,Qt内部事件
            {
                layoutContents();
                break;
            }
            default:
                break;
        }
    }

    return QFrame::eventFilter( object, event );
}
如果要处理用户自定义事件,则需要自己定义事件类 UserEvent :

class UserEvent : public QEvent
{
public:
    static const QEvent::Type eventType;

    explicit UserEvent () : QEvent(eventType) { }

    QString m_userName;
    int m_userAge;
    double m_userSalary;
};
在对应的源文件中初始化  static const QEvent::Type eventType;

const QEvent::Type UserEvent::eventType = static_cast<QEvent::Type>(QEvent::registerEventType());
这样就可以在eventFilter()函数中处理/过滤自定义事件UserEvent 了。

4、事件的发送:

1) bool QCoreApplication::sendEvent ( QObject * receiver, QEvent * event )   [static]

    virtual bool viewportEvent( QEvent *e )
    {
        bool ok = QScrollArea::viewportEvent( e );

        if ( e->type() == QEvent::Resize )
        {
            QEvent event( QEvent::LayoutRequest );
            QApplication::sendEvent( contentsWidget, &event ); // 发送事件,函数要在该事件被处理后才返回
        }
        return ok;
    }
Qt文档:

bool QCoreApplication::sendEvent ( QObject * receiver, QEvent * event )   [static]

Sends event event directly to receiver receiver, using the notify() function. Returns the value that was returned from the event handler.
The event is not deleted when the event has been sent. The normal approach is to create the event on the stack, for example:

 QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
 QApplication::sendEvent(mainWindow, &event);
See also postEvent() and notify().

2)void QCoreApplication::postEvent ( QObject * receiver, QEvent * event, int priority )   [static]

    if ( parentWidget() && parentWidget()->layout() == NULL )
    {
        /*
           updateGeometry() doesn't post LayoutRequest in certain
           situations, like when we are hidden. But we want the
           parent widget notified, so it can show/hide the legend
           depending on its items.
         */
        QApplication::postEvent( parentWidget(),
            new QEvent( QEvent::LayoutRequest ) ); // 发送事件至接收器的事件队列中(可能不会被立即执行)
    }
Qt文档:

void QCoreApplication::postEvent ( QObject * receiver, QEvent * event )   [static]

Adds the event event, with the object receiver as the receiver of the event,to an event queueand returns immediately.
The event must be allocated on the heap since the post event queue will take ownership of the event and delete it once it has been posted.It is not safe to modify or delete the event after it has been posted.
When control returns to the main event loop, all events that are stored in the queue will be sent using the notify() function.
Events are processed in the order posted. For more control over the processing order, use the postEvent() overload below, which takes a priority argument. This function posts all event with a Qt::NormalEventPriority.
Note: This function is thread-safe.
See also sendEvent(), notify(), and sendPostedEvents().


展开阅读全文

没有更多推荐了,返回首页