天天看點

使用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對象載入圖檔。

      效果圖如下:

使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀
使用QStyle定制QSlider外觀

繼續閱讀