使用QStyle定制QSlider外观

           关于定制Qt中QSlidre外观,网上多是使用setStyleSheet函数设置sytlesheet进行修改。对于固定外观的定制倒是十分的方便,但对于需要在程序运行时改变外观,并且外观图片需要在内存中进行处理时,使用setStyleSheet函数就不适合了。  
        本文介绍使用QStyle类的子类进行外观的定制。要使用QStyle进行组件外观的改变,首先需要从QCommonStyle、QWindowsStyle、QMotifStyle、QPlastiqueStyle等类进行继承,生成新类,而这些类都是继承于QStyle类,并重载draw*函数。在本例中,MySliderStyle类继承于QPlastiqueStyle类。


     MySliderStyle.h内容如下:

#ifndef MYSLIDERSTYLE_H
#define MYSLIDERSTYLE_H

#include <QPlastiqueStyle>

class QPainter;
class QWidget;

class MySliderStyle : public QPlastiqueStyle
{
public:
    MySliderStyle(){}

    void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *w) const; // 画出Slider中groove、handle和填充条外观
    int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const; // 让handle滑块直接移到鼠标点击处
    int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const; // 修改handle滑块大小
    void polish(QWidget *widget); // 设置滑动条响应鼠标移入移出事件
    void unpolish(QWidget *widget); // 取消设置
};

#endif // MYSLIDERSTYLE_H


   MySliderStyle.cpp内容如下:

#include "mysliderstyle.h"
#include "myslider.h"
#include <QPainter>
#include <QWidget>
#include <QStyleOptionComplex>
#include <QDebug>

int MySliderStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const{

    if(hint == QStyle::SH_Slider_AbsoluteSetButtons)
        return (Qt::LeftButton);
    return QPlastiqueStyle::styleHint(hint, option, widget, returnData);
}
int MySliderStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const{
    // 对Slidr上的handle而言返回的是 横向 宽度;纵向 高度;
    if(const MySlider *ms = qobject_cast<const MySlider *>(widget)){
        if(ms->orientation() == Qt::Vertical && ms->getHandle(0) != NULL){
            return ms->getHandle(0)->height();
        }else if(ms->orientation() == Qt::Horizontal && ms->getHandle(0) != NULL){
            return ms->getHandle(0)->width();
        }else if(ms->getHandle(0) == NULL){
            return 1;
        }
    }
    return QPlastiqueStyle::pixelMetric(metric, option, widget);
}

void MySliderStyle::polish(QWidget *widget){
    widget->setAttribute(Qt::WA_Hover, true);
}
void MySliderStyle::unpolish(QWidget *widget){
    widget->setAttribute(Qt::WA_Hover, false);
}

void MySliderStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *w) const{

    if(cc == CC_Slider){
        QRect groove = subControlRect(CC_Slider, opt, SC_SliderGroove, w);
        QRect handle = subControlRect(CC_Slider, opt, SC_SliderHandle, w);
        //        qDebug() << handle.topLeft() << handle.size();

        p->save();

        if(const MySlider *ms = qobject_cast<const MySlider *>(w)){
            if(const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)){

                if ((opt->subControls & SC_SliderGroove) && groove.isValid() && handle.isValid()) { // 背景
                    QPixmap *t_groove = ms->getGroove();
                    p->drawPixmap(QRect(groove.x(), groove.y(),
                                        groove.width(), groove.height()), *(t_groove));
                    QPixmap *fill = ms->getFill();
                    if(ms->orientation() == Qt::Vertical){
                        p->drawPixmap(ms->getFillPoint(), handle.y(),fill->copy(0, handle.y(), fill->width(), groove.height() - handle.y()));
                    }else if(ms->orientation() == Qt::Horizontal){
                        p->drawPixmap(0,ms->getFillPoint(), fill->copy(0, 0, handle.x() == 0 ? 1 : handle.x(), fill->height()));
                    }
                }
                if(opt->subControls & SC_SliderHandle){
                    QPixmap *p0 = NULL;
                    QPixmap *p1 = NULL;
                    QPixmap *p2 = NULL;
                    QPixmap * p3 = NULL;
                    bool isNew = false;
                    if(ms->getHandle(0) == NULL){
                        QPixmap *fill = ms->getFill();
                        if(ms->orientation() == Qt::Vertical)
                            p0 = new QPixmap(fill->copy(0, handle.y(), fill->width(), 1));
                        else
                            p0 = new QPixmap(fill->copy(handle.x(), 0, 1, fill->height()));

                        p1 = p0;
                        p2 = p0;
                        p3 = p0;
                        isNew = true;

                    }else{
                        p0 = ms->getHandle(0);
                        p1 = ms->getHandle(1);
                        p2 = ms->getHandle(2);
                        p3 = ms->getHandle(3);
                    }
                    if((slider->state & QStyle::State_MouseOver) && (slider->state & State_Sunken)){ // handle处理 点击
                        if(ms->orientation() == Qt::Vertical){
                            p->drawPixmap(ms->getHandlePoint(),handle.y(), *p2);
                        }else if(ms->orientation() == Qt::Horizontal){
                            p->drawPixmap(handle.x(),ms->getHandlePoint(), *p2);
                        }
                    }else  if(slider->state & QStyle::State_MouseOver){ // handle处理 鼠标进入
                        if(ms->orientation() == Qt::Vertical)
                            p->drawPixmap(ms->getHandlePoint(),handle.y(), *p1);
                        else
                            p->drawPixmap(handle.x(),ms->getHandlePoint(), *p1);
                    }else if(!(slider->state & QStyle::State_Enabled)){ // handle处理 失效
                        if(ms->orientation() == Qt::Vertical)
                            p->drawPixmap(ms->getHandlePoint(),handle.y(), *p3);
                        else
                            p->drawPixmap(handle.x(),ms->getHandlePoint(), *p3);
                    }else if(slider->state & QStyle::State_Enabled){ // handle处理 有效
                        if(ms->orientation() == Qt::Vertical)
                            p->drawPixmap(ms->getHandlePoint(),handle.y(), *p0);
                        else
                            p->drawPixmap(handle.x(),ms->getHandlePoint(), *p0);
                    }
                    if(isNew){
                        delete p0;
                        p0 = NULL;
                        p1 = NULL;
                        p2 = NULL;
                        p3 = NULL;
                    }

                }
            }

        }
        p->restore();
    }
    //    QPlastiqueStyle::drawComplexControl(cc, opt, p, w);

}


 

        drawComplexControl函数用于实现绘制QSlider中groove、handle滑块部分外观。首先判断当前组件是否为滑动条(CC_Slider为QT中定义的复杂组件滑动条的标识),然后获取groove在父组件中的区域信息和handle滑块在滑动条的区域信息,subControlRect函数为QStyle类的成员函数,在Qt文档中说返回的是屏幕坐标(screen coordinates),但据我观察返回的是相对父组件的坐标(groove返回是QSlider在父组件中的坐标位置,handle滑块返回是相对QSlider的坐标位置)。下面进行外观绘制,方法是使用QPainter类的drawPixmap函数里绘制。
       groove部分绘制。由于在QStyle中只定义了SC_SliderGroove和SC_SliderHandle子控件,而没有sub-page和add-page相关的子控件定义。所以填充条将在这个地方绘制,依据handle滑块的位置绘制进度条。
        handle滑块部分绘制。滑块的绘制分为四个部分,滑块为有效状态、鼠标进入、点击和滑块为无效状态时分别使用不同图片进行绘制。在进行判断时,从条件复杂的开始,不然简单条件始终满足,将不会绘制复杂条件。如将鼠标点击部分放到鼠标移入的后面,则点击部分语句将不会被执行,因为鼠标移入部分的判断条件始终满足。在绘制部分的4个QPixmap指针对象为滑块各种状态下的图片。p0为有效状态(滑块可用,但没有鼠标移入和点击),p1为鼠标移入,p2为鼠标点击,p3为无效状态(滑块不可用)。在这个地方进行处理,当滑块为不为固定形状时和为固定形状时两种情况。当不为固定形状时假定从填充条相应位置去取一个像素图形。

       在程序用到的MySlider类为自定义的QSlider的子类。因为所有的图片都是在处理完后以QPixmap方式存储在内存,所以创建子类保存图片信息,并且按图片大小进行了位置调整,将填充条和滑块进行剧中处理。

     MySlider.h内容如下:

#ifndef MYSLIDER_H
#define MYSLIDER_H

#include <QSlider>
#include <QTemporaryFile>
#include "mysliderstyle.h"
#include "TTSkin/sliderinfo.h"

class QMouseEvent;
class QPaintEvent;

class MySlider : public QSlider
{
    Q_OBJECT
public:
    explicit MySlider(QWidget *parent = 0);
    ~MySlider();

    int initSlider(SliderInfo &si);  // 设置滑动条信息,包括坐标位置,大小,需要绘制的各种图片

    QPixmap *getGroove() const;      // 返回背景图片指针
    QPixmap *getFill() const;        // 返回填充条图片指针
    QPixmap *getHandle(int i) const; // 返回滑块图片指针,0 有效, 1 鼠标移入, 2 点击, 3 无效
    int getFillPoint() const;        // 返回填充条绘制的调整坐标,横向时为 y轴坐标,垂直时为 x轴坐标
    int getHandlePoint() const;      // 返回滑块绘制的调整坐标,横向时为 y轴坐标,垂直时为 x轴坐标

    SliderInfo *getSliderInfo();    // 返回当前组件的所有信息,此信息为从配置文件中读取并设置

protected:
    void mouseMoveEvent(QMouseEvent *ev);

private:
    QPixmap *groove;                // 背景图片
    QPixmap *fill;                  // 填充条图片,大小与背景图片一致,横向时长与背景图片相同,垂直时高与背景图片相同
    QPixmap *handle_p0;             // 滑块有效时图片
    QPixmap *handle_p1;             // 鼠标移入时图片
    QPixmap *handle_p2;             // 鼠标点击时图片
    QPixmap *handle_p3;             // 滑块无效时图片

    int fill_p;                     // 填充条绘制时调整的坐标,横向时为 y轴坐标,垂直时为 x轴坐标
    int handle_p;                   // 滑块绘制时调整的坐标,横向时为 y轴坐标,垂直时为 x轴坐标

    static MySliderStyle *mss;      // 样式对象,使用静态对象,是为了由多个滑动条对象复用

    SliderInfo *si;                 // 当前组件的所有信息,此信息为从配置文件中读取并设置

signals:
    
public slots:
    
};

#endif // MYSLIDER_H

 

   MySlider.cpp内容如下:

#include "myslider.h"
#include "../TTSkin/ttwin.h"
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOptionSlider>
#include <QPaintEvent>
#include <QDir>

MySlider::MySlider(QWidget *parent) :
    QSlider(parent)
{
    this->setRange(0, 100);                             // 设置滑动条取值范围
    this->setMouseTracking(true);                       // 设置鼠标跟踪
    this->setCursor(Qt::PointingHandCursor);            // 设置手开指针
    si = NULL;

    this->setStyle(mss);                                // 设置样式
}

MySlider::~MySlider(){
    if(mss != NULL)
        delete mss;
    mss = NULL;
}

void MySlider::mouseMoveEvent(QMouseEvent *ev){
    QSlider::mouseMoveEvent(ev);
    ev->accept();
}

MySliderStyle *MySlider::mss = new MySliderStyle();

QPixmap *MySlider::getGroove() const{
    return this->groove;
}

QPixmap *MySlider::getFill() const{
    return this->fill;
}

QPixmap *MySlider::getHandle(int i) const{
    switch(i){
    case 0:
        return this->handle_p0;
    case 1:
        return this->handle_p1;
    case 2:
        return this->handle_p2;
    case 3:
        return this->handle_p3;
    default:
        return 0;
    }
}

int MySlider::getFillPoint() const{
    return fill_p;
}
int MySlider::getHandlePoint() const{
    return handle_p;
}
SliderInfo *MySlider::getSliderInfo(){
    return this->si;
}
int MySlider::initSlider(SliderInfo &si){
    this->si = &si;

    this->setOrientation(si.getVertical() ? Qt::Vertical : Qt::Horizontal);

    this->setGeometry(*(si.getRect()));
    this->groove = si.getGroove();

    this->fill = si.getFill();

    this->handle_p0 = si.getP0();
    this->handle_p1 = si.getP1();
    this->handle_p2 = si.getP2();
    this->handle_p3 = si.getP3();

    if(si.getVertical()){ // 进行位置调整
        handle_p = (si.getRect()->width() - (handle_p0 == NULL ? si.getRect()->width() : handle_p0->width())) / 2;
        fill_p = (si.getRect()->width() - this->fill->width()) / 2;
    }else{
        handle_p = (si.getRect()->height() - (handle_p0 == NULL ? si.getRect()->height() : handle_p0->height())) / 2;
        fill_p = (si.getRect()->height() - this->fill->height()) / 2;
    }
    return 0;
}


      如果不需要对图片进行处理,使用固定的图片,就可以不用继承QSlider类,在MySliderStyle中直接使用QPixmap对象载入图片。

      效果图如下:

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fengdavid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值