使用QStyle 高端定制弹出菜单 QPushButton源码剖析(一)

这段时间研究了一下,qt的样式源码。同时也顺便自己做了一个样式。为了使大家能够看下去,先贴一贴效果。

使用QStyle 高端定制弹出菜单 QPushButton源码剖析(一)_第1张图片

以上效果,看似好像用qss采用setStyleSheet()的方式也可以更改;但结果证明是不行的,采用qss的方式只能设置一下颜色,皮肤。

而上面的菜单背景为透明,子菜单的的三角标志已经是圆了,所以靠样式表是不行的。

QStyle接口实现了qt在各种不同平台之间的各种控件的基本外观,查看QStyle源码可以看见定义了非常多的枚举变量;主要有以下几类

ComplexControl、ControlElement、PrimitiveElement、PixelMetric、ContentsType、SubElement、SubControl。我们的QPusButton,QLlineEdit,QMenu等待

都是靠这些枚举变量,样式层里分成不同的小块,在各自的接口函数里面用QPainter一点一点绘制出来的。主要的接口函数有这几个


virtual void 
drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, 

QPainter *painter, const QWidget *widget = Q_NULLPTR) const ; //绘制复杂的控件,具体是哪些呢,这个就是通过枚举ComplexControl来控件了,通过

帮助文档我们可以看出该函数处理的控件元素有这些CC_SpinBox、CC_ComboBox、CC_ScrollBar、CC_Slider、CC_ToolButton、CC_TitleBar、CC_GroupBox、

CC_Dial、CC_MdiControls。


virtual void 
drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const ;

//可处理的控件元素是这些CE_PushButton、CE_PushButtonBevel、CE_PushButtonLabel、CE_DockWidgetTitle...这个枚举有很多,就不写完了。可以

看出它画的是一些简单一些的控件,像按钮、进度条等;而这些又被分成几个元素来绘制,如按钮又分为CE_PushButtonBevel、CE_PushButtonLabel。

然后在细分的枚举里面再去绘制更具体的部分


virtual void 
drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const;

//可处理的元素非常多PE_PanelButtonCommand、PE_FrameDefaultButton、PE_PanelButtonBevel;从文档可以看出它画的是一些不可再分的原子组件

如按钮的背景面板,QLineEdit的背景面板等。


virtual int 
pixelMetric(PixelMetric metric, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR) const;

//返回对应枚举的像素公制,通常做为控件的线宽,文字和图案,图案与边角的margin值;可处理的对象有PM_ButtonMargin、PM_ButtonShiftVertical、

等等。


virtual void 
polish(QWidget *widget) 在控件初始时被调用,只有一次调用。


virtual void 
polish(QPalette &palette) 在控件初始化被调用,只调用一次。


virtual void 
unpolish(QWidget *widget) 在当前样式被卸载时调用,没被卸载时,在polish(QWidget*widget)中一般会启用WA_Hover,就是当鼠标滑过控件时会重绘。

当样式卸载时,就在这里将这个属性失效。


virtual QRect 
subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget = Q_NULLPTR) const ;

//返回一个复杂控件ComplexControl的子控件SubControl的位置,通常在drawComplexControl函数中被调用,用于获取子控件的位置。所以它控制着复杂控件

的布局


virtual QRect 
subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget = Q_NULLPTR) const ;

//返回一个子元素内容位置,如QPushButton的文本显示区,QLineEdit的文本区,各种控件的焦点区域等。


virtual int 
styleHint(StyleHint hint, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR, QStyleHintReturn *returnData = Q_NULLPTR) const ;

//当状态发生变化时,反回一个让你感觉状态变化了的实际对应控件的对应某个参数的int值。为bool时一般表示,是否具有某个属性

;为整型时,表示一个值,如透明度等。


了解了以上这些接口后,我们再来看一段QStyle的实现源代码QCommonStyle的drawPrimitivet的源码

void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
                                 const QWidget *widget) const
{
    Q_D(const QCommonStyle);
    switch (pe) {
    case PE_FrameButtonBevel:
    case PE_FrameButtonTool:
        qDrawShadeRect(p, opt->rect, opt->palette,
                       opt->state & (State_Sunken | State_On), 1, 0);
        break;
    case PE_PanelButtonCommand:
    case PE_PanelButtonBevel:
    case PE_PanelButtonTool:
    case PE_IndicatorButtonDropDown:
        qDrawShadePanel(p, opt->rect, opt->palette,
                        opt->state & (State_Sunken | State_On), 1,
                        &opt->palette.brush(QPalette::Button));
        break;
    case PE_IndicatorViewItemCheck:
        proxy()->drawPrimitive(PE_IndicatorCheckBox, opt, p, widget);
        break;
    case PE_IndicatorCheckBox:
        if (opt->state & State_NoChange) {
            p->setPen(opt->palette.foreground().color());
            p->fillRect(opt->rect, opt->palette.brush(QPalette::Button));
            p->drawRect(opt->rect);
            p->drawLine(opt->rect.topLeft(), opt->rect.bottomRight());
        } else {
            qDrawShadePanel(p, opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height(),
                            opt->palette, opt->state & (State_Sunken | State_On), 1,
                            &opt->palette.brush(QPalette::Button));
        }
        break;
    case PE_IndicatorRadioButton: {
        QRect ir = opt->rect;
        p->setPen(opt->palette.dark().color());
        p->drawArc(opt->rect, 0, 5760);
        if (opt->state & (State_Sunken | State_On)) {
            ir.adjust(2, 2, -2, -2);
            p->setBrush(opt->palette.foreground());
            bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting);
            p->setRenderHint(QPainter::Qt4CompatiblePainting);
            p->drawEllipse(ir);
            p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting);
        }
        break; }

这里根据不同的枚举值能过QPainter画出了,一些基本的图形;具体画在哪儿,那就是QPaineter的目标绘图设备了

void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
                                      QPainter *p, const QWidget *widget) const
{
    switch (cc) {
#ifndef QT_NO_SLIDER
    case CC_Slider:
        if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) {
            if (slider->subControls == SC_SliderTickmarks) {
                int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
                int ticks = slider->tickPosition;
                int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
                int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
                int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
                int interval = slider->tickInterval;
                if (interval <= 0) {
                    interval = slider->singleStep;
                    if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
                                                        available)
                        - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
                                                          0, available) < 3)
                        interval = slider->pageStep;
                }
                if (!interval)
                    interval = 1;
                int fudge = len / 2;
                int pos;

这个接口用QPainter绘制了一些复杂控件,在其中使用了pixelMetric,subControlRect等其它函数获取子控件位置、margin值等;我只截取了一部分,

在后面的代码中,还会根据需要的原子控件,构造一个枚举调用drawPrimitivet函数;当然也可以在这里用QPainter画,也就是给你一个复杂控件的枚举

和一片区域和参数,在这片区域上你想怎么画就怎么画,只不有些原子组件和子控件可以调用其它接口。


void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
                               QPainter *p, const QWidget *widget) const
{
    Q_D(const QCommonStyle);
    switch (element) {

    case CE_PushButton:
        if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) {
            proxy()->drawControl(CE_PushButtonBevel, btn, p, widget);
            QStyleOptionButton subopt = *btn;
            subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
            proxy()->drawControl(CE_PushButtonLabel, &subopt, p, widget);
            if (btn->state & State_HasFocus) {
                QStyleOptionFocusRect fropt;
                fropt.QStyleOption::operator=(*btn);
                fropt.rect = subElementRect(SE_PushButtonFocusRect, btn, widget);
                proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
            }
        }
        break;

这个接口绘制了一些简单的控件,如按钮、QCheckBox等;原理同上,可以看到其中的原子组件也是调用drawPrimitive完成的,如果要在原子组件和其它原子组件

之间加东西,就可以在这个接口里面用QPainter画了,当然全部都在这里画也是可以的。


void QCommonStyle::polish(QWidget *widget)
{
    QStyle::polish(widget);
}
QCommonStyle里没有实现这个接口,因为QCommonStyle是QStyle接口实现的第一层,实现了一些公有的样式,在QWindowsXpStyle里我们可以看到这样一行代码

widget->setAttribute(Qt::WA_Hover);
//意思就是当鼠标进入该控件时,该控件就重绘


了解完了上面这些后,对qt的样式机制应该明白了不少吧。下面来看一个最简单的QPushButton实现的源代码

我们直接进入paintEvent绘图事件

void QPushButton::paintEvent(QPaintEvent *)
{
    QStylePainter p(this);  //指定绘图设备会this
    QStyleOptionButton option; //初始化一个样式按钮数据类,包含了按钮的各种参数
    initStyleOption(&option); //给按钮参数赋值
    p.drawControl(QStyle::CE_PushButton, option); //调用QPushButton采用的style对象的drawControl函数画一个枚举为CE_PushButton的控件,也就是按钮啦
}

在initStyleOption函数中我们可以看到给按钮的参数赋值了

void QPushButton::initStyleOption(QStyleOptionButton *option) const
{
    if (!option)
        return;

    Q_D(const QPushButton);
    option->initFrom(this);
    option->features = QStyleOptionButton::None;
    if (d->flat)
        option->features |= QStyleOptionButton::Flat;
#ifndef QT_NO_MENU
    if (d->menu)
        option->features |= QStyleOptionButton::HasMenu;
#endif
    if (autoDefault())
        option->features |= QStyleOptionButton::AutoDefaultButton;
    if (d->defaultButton)
        option->features |= QStyleOptionButton::DefaultButton;
    if (d->down || d->menuOpen)
        option->state |= QStyle::State_Sunken;
    if (d->checked)
        option->state |= QStyle::State_On;
    if (!d->flat && !d->down)
        option->state |= QStyle::State_Raised;
    option->text = d->text;
    option->icon = d->icon;
    option->iconSize = iconSize();
}

在这里,大家应该明白了按钮的绘制原理了吧,在paintEvent里面将painter的绘图设备指明为this,初始化一个按钮参数样式类并赋值,根据控件是复杂控件

还是简单控件(可查看QStyle接口的枚举得知)调用对应的接口函数,最后就进入了QStyle的绘画接口了,在接口中根据传入的枚举值和参数,使用QPainter来

画出控件。

但真实的按钮并不是这么简单,因为前面我们看的是QCommonStyle,在windows平台,Widget默认使用的是QWindowsVistaStyle,可以使用qDebug() << object->style()

打印出来,QWindowsVistaStyle是通过几次继承从QCommonStyle继承下来的;QStyle的接口是虚函数,所以具体是怎样展示的还要看它使用的样式的实现接口。

我们不难看出windows平台上面的按钮有一个动画过程,当鼠标放上去,点击和移开都可以发现。这个动画的影子在QWindowsVistaStyle中就能看到影子,就是在drawControl

的接口函数中

void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
                                  QPainter *painter, const QWidget *widget) const
{
    QWindowsVistaStylePrivate *d = const_cast(d_func());

    if (!QWindowsVistaStylePrivate::useVista()) {
        QWindowsStyle::drawControl(element, option, painter, widget);
        return;
    }

    bool selected = option->state & State_Selected;
    bool pressed = option->state & State_Sunken;
    bool disabled = !(option->state & State_Enabled);

    int state = option->state;
    int themeNumber  = -1;

    QRect rect(option->rect);
    State flags = option->state;
    int partId = 0;
    int stateId = 0;

    if (d->transitionsEnabled() && canAnimate(option))
    {
        if (element == CE_PushButtonBevel) {
            QRect oldRect;
            QRect newRect;

            QObject *styleObject = option->styleObject;

            int oldState = styleObject->property("_q_stylestate").toInt();
            oldRect = styleObject->property("_q_stylerect").toRect();
            newRect = option->rect;
            styleObject->setProperty("_q_stylestate", (int)option->state);
            styleObject->setProperty("_q_stylerect", option->rect);

            bool wasDefault = false;
            bool isDefault = false;
            if (const QStyleOptionButton *button = qstyleoption_cast(option)) {
                wasDefault = styleObject->property("_q_isdefault").toBool();
                isDefault = button->features & QStyleOptionButton::DefaultButton;
                styleObject->setProperty("_q_isdefault", isDefault);
            }

看见没,其中的的canAnimate就是检测能否动画,其方法如下

bool canAnimate(const QStyleOption *option) {
    return option
            && option->styleObject
            && !option->styleObject->property("_q_no_animation").toBool();
}
这就显而易见了,如果我们这样QPushButton *btn = new QPushButton;btn->setProperty("_q_noanimation",false)这样你就会发现按钮不会有动画过程了
接着说,上面经过判断后如果支持动画,会构建一个启动动画的Buffer和一个结束动画的Buffer(说白了就是一个QImage),最后会调用d->startAnimation()函数

这个d为一个QWindowsVistaStylePrivate类,在其定义里未找到该函数定义,该类经由QCommonStylePrivate继承,在QCommonStylePrivate中我们找到了定义

void QCommonStylePrivate::startAnimation(QStyleAnimation *animation) const
{
    Q_Q(const QCommonStyle);
    stopAnimation(animation->target());
    q->connect(animation, SIGNAL(destroyed()), SLOT(_q_removeAnimation()), Qt::UniqueConnection);
    animations.insert(animation->target(), animation);
    animation->start();
}

传入的是QStyleAnimation类,先停止当前动画,当动画停止后,从容器中删除该动画(所以当按下按钮,动画未结束时再按,动画就重新开始了)。最后启动该动画。

而启动动画又是怎样的呢,这又要看QStyleAnimaion类了。

QStyleAnimation类未找到start()函数定义,它继承自QAbstractAnimation,在QAbstractAnimation中start()函数定义如下

void QAbstractAnimation::start(DeletionPolicy policy)
{
    Q_D(QAbstractAnimation);
    if (d->state == Running)
        return;
    d->deleteWhenStopped = policy;
    d->setState(Running);
}
最后调用了setState(),跟踪其中一段结果如下

    case QAbstractAnimation::Running:
        {

            // this ensures that the value is updated now that the animation is running
            if (oldState == QAbstractAnimation::Stopped) {
                if (isTopLevel) {
                    // currentTime needs to be updated if pauseTimer is active
                    QAnimationTimer::ensureTimerUpdate();
                    q->setCurrentTime(totalCurrentTime);
                }
            }
        }
        break;

追踪setCurrentTime()结果如下
void QAbstractAnimation::setCurrentTime(int msecs)
{
    Q_D(QAbstractAnimation);
    msecs = qMax(msecs, 0);

    // Calculate new time and loop.
    int dura = duration();
    int totalDura = dura <= 0 ? dura : ((d->loopCount < 0) ? -1 : dura * d->loopCount);
    if (totalDura != -1)
        msecs = qMin(totalDura, msecs);
    d->totalCurrentTime = msecs;

    // Update new values.
    int oldLoop = d->currentLoop;
    d->currentLoop = ((dura <= 0) ? 0 : (msecs / dura));
    if (d->currentLoop == d->loopCount) {
        //we're at the end
        d->currentTime = qMax(0, dura);
        d->currentLoop = qMax(0, d->loopCount - 1);
    } else {
        if (d->direction == Forward) {
            d->currentTime = (dura <= 0) ? msecs : (msecs % dura);
        } else {
            d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
            if (d->currentTime == dura)
                --d->currentLoop;
        }
    }

    updateCurrentTime(d->currentTime);

最后调用了updateCurrentTime()函数,在头文件中我们可以看见它是一个纯虚函数

    virtual void updateCurrentTime(int currentTime) = 0;

这里我们可以大概知道,QStyleAnimation是一个继承自QAbstractAnimation的动画,动画靠时间来进行推进,分成了loopCount等,每次更新时间调用updateCurrentTime()

接口,而具体的动画,就要看updateCurrentTime()实现是怎么样了,我们再找到QStyleAnimation的代码可以看到updateCurrentTime()的定义

void QStyleAnimation::updateCurrentTime(int)
{
    if (++_skip >= _fps) {
        _skip = 0;
        if (target() && isUpdateNeeded())
            updateTarget();
    }
}

做了一个推进后,调用了updateTarget(),根据意思应该是刷新目标的意思,其它定义如下

void QStyleAnimation::updateTarget()
{
    QEvent event(QEvent::StyleAnimationUpdate);
    event.setAccepted(false);
    QCoreApplication::sendEvent(target(), &event);
    if (!event.isAccepted())
        stop();
}
然后我们在QPushButton中并未找到该事件的处理方法,然后在父类QWidget源码的event()函数中看到了其处理方法

    case QEvent::StyleAnimationUpdate:
        if (isVisible() && !window()->isMinimized()) {
            event->accept();
            update();
        }
        break;
可以看到,进行了重绘;update()就不用我说了吧,它会调用paintEvent();这样每过一段时间重绘一次就形成了动画。绘画的内容是什么呢,当然是QWindowsVistaStyle的

drawControl函数当element为CE_PushButton时,painter在该时该是怎么画的了,前面介绍的是所有调用该接口的都会有的操作不区分控件的;而在QWindowsVistaStyle的该接口中我们未找到该枚举的定义方法,向上层层追溯我们在QCommStyle中找到了定义,也就是最开始介绍的啦。

void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt,
                               QPainter *p, const QWidget *widget) const
{
    Q_D(const QCommonStyle);
    switch (element) {

    case CE_PushButton:
        if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) {
            proxy()->drawControl(CE_PushButtonBevel, btn, p, widget);
            QStyleOptionButton subopt = *btn;
            subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
            proxy()->drawControl(CE_PushButtonLabel, &subopt, p, widget);
            if (btn->state & State_HasFocus) {
                QStyleOptionFocusRect fropt;
                fropt.QStyleOption::operator=(*btn);
                fropt.rect = subElementRect(SE_PushButtonFocusRect, btn, widget);
                proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
            }
        }
        break;
可以看到其处理分为三步,分别是画按钮的斜面(让人感觉是一个按钮),画按钮上面的文字,画按钮的焦点;而这里的proxy()返回的是当前实例对象也就是QWindowsVistaStyle了,所以按钮具体怎样,就需要看QWindowsVistaStyle中drawControl接口对CE_PushButtonBevel、CE_PushButtonLabel的处理方法,

drawPrimitive接口对PE_FrameFocusRect的处理情况了。在QWindowsVistaStyle的drawControl对CE_PushButtonBevel的处理如下

    case CE_PushButtonBevel:
        if (const QStyleOptionButton *btn = qstyleoption_cast(option))
        {
            themeNumber = QWindowsXPStylePrivate::ButtonTheme;
            partId = BP_PUSHBUTTON;
            if (btn->features & QStyleOptionButton::CommandLinkButton)
                partId = BP_COMMANDLINK;
            bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
            if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
                stateId = PBS_DISABLED;
            else if (justFlat)
                ;
            else if (flags & (State_Sunken | State_On))
                stateId = PBS_PRESSED;
            else if (flags & State_MouseOver)
                stateId = PBS_HOT;
            else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
                stateId = PBS_DEFAULTED;
            else
                stateId = PBS_NORMAL;

            if (!justFlat) {

                if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) &&
                        !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) &&
                        (state & State_Enabled) && (state & State_Active))
                {
                    QWindowsVistaAnimation *anim = qobject_cast(d->animation(styleObject(option)));

                    if (!anim) {
                        QImage startImage = createAnimationBuffer(option, widget);
                        QImage alternateImage = createAnimationBuffer(option, widget);

                        QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option));

                        QPainter startPainter(&startImage);
                        stateId = PBS_DEFAULTED;
                        XPThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect);
                        d->drawBackground(theme);

                        QPainter alternatePainter(&alternateImage);
                        theme.stateId = PBS_DEFAULTED_ANIMATING;
                        theme.painter = &alternatePainter;
                        d->drawBackground(theme);
                        pulse->setStartImage(startImage);
                        pulse->setEndImage(alternateImage);
                        pulse->setStartTime(QTime::currentTime());
                        pulse->setDuration(2000);
                        d->startAnimation(pulse);
                        anim = pulse;
                    }

                    if (anim)
                        anim->paint(painter, option);
                    else {
                        XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
                        d->drawBackground(theme);
                    }
                }
                else {
                    XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
                    d->drawBackground(theme);
                }
            }

            if (btn->features & QStyleOptionButton::HasMenu) {
                int mbiw = 0, mbih = 0;
                XPThemeData theme(widget, 0, QWindowsXPStylePrivate::ToolBarTheme,
                                  TP_DROPDOWNBUTTON);
                if (theme.isValid()) {
                    const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
                    if (!size.isEmpty()) {
                        mbiw = qRound(size.width());
                        mbih = qRound(size.height());
                    }
                }
                QRect ir = subElementRect(SE_PushButtonContents, option, 0);
                QStyleOptionButton newBtn = *btn;
                newBtn.rect = QStyle::visualRect(option->direction, option->rect,
                                                QRect(ir.right() - mbiw - 2,
                                                      option->rect.top() + (option->rect.height()/2) - (mbih/2),
                                                      mbiw + 1, mbih + 1));
                proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
            }
            return;
        }
可以看到启用了动画,并使用d->drawBackground()画出了该时刻按钮的背景,从前面知道QStyleAnimation会触发重绘,这样就不断以不一样的参数绘制

出了按钮的动画效果,当然这里还区分了按钮有菜单的情况,对CE_PushButtonLabel的处理未找到,自己追溯上一级吧,方法类似。

这样整个过程就出来了

1.QPushButton的paintEvent中以本对象为绘图设备创建QPainter,还有QStyleOptionButton; 初始化QStyleOptionButton,调用QStyle的drawControl

2.windows平台QPushButton用的是QWindowsVistaStyle可打印QPushButton的style()返回值查看

3.QWindowsVistaStyle的drawControl()根据property(_qt_no_animaiotn)判断是否能够动画,如果能动画,创建两个Image,一个开始,一个结束

4.启用QWindowsVistaStylePrivate的startAnimation(),传入构建的QStyleAnimation。

5.QStyleAnimation通过updateCurrentTime()定时的通过QCoreApplication::sendEvent()向绘图设备对象发送StyleAnimationUpdate事件

6.QWindowsVistaStyle对按钮的背景斜面板,文字、焦点实时绘制


Qt的样式机制和动画控制着界面的外观和动态效果,做为图形开发框架,了解它是非常必要的,祝工作愉快。


你可能感兴趣的:(Qt,c++)