44.Qt通过子类化qstyle实现自定义外观

Posted 喵小喵~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了44.Qt通过子类化qstyle实现自定义外观相关的知识,希望对你有一定的参考价值。

main.cpp

 1 #include <QtGui>
 2 
 3 #include "brozedialog.h"
 4 #include "bronzestyle.h"
 5 
 6 
 7 
 8 int main(int argc, char *argv[])
 9 {
10     QApplication app(argc, argv);
11     QApplication::setStyle(new BronzeStyle);
12     BronzeDialog dialog;
13     dialog.layout()->setSpacing(7);
14     dialog.layout()->setMargin(7);
15     dialog.show();
16     return app.exec();
17 }

bronzestyle.h

#ifndef BRONZESTYLE_H
#define BRONZESTYLE_H

#include <QWindowsStyle>

class BronzeStyle : public QWindowsStyle
{
    Q_OBJECT

public:
    BronzeStyle() {}

    void polish(QPalette &palette);
    void polish(QWidget *widget);
    void unpolish(QWidget *widget);
    int styleHint(StyleHint which, const QStyleOption *option,
                  const QWidget *widget = 0,
                  QStyleHintReturn *returnData = 0) const;
    int pixelMetric(PixelMetric which, const QStyleOption *option,
                    const QWidget *widget = 0) const;
    void drawPrimitive(PrimitiveElement which,
                       const QStyleOption *option, QPainter *painter,
                       const QWidget *widget = 0) const;
    void drawComplexControl(ComplexControl which,
                            const QStyleOptionComplex *option,
                            QPainter *painter,
                            const QWidget *widget = 0) const;
    QRect subControlRect(ComplexControl whichControl,
                         const QStyleOptionComplex *option,
                         SubControl whichSubControl,
                         const QWidget *widget = 0) const;

public slots:
    QIcon standardIconImplementation(StandardPixmap which,
                                     const QStyleOption *option,
                                     const QWidget *widget = 0) const;

private:
    void drawBronzeFrame(const QStyleOption *option,
                         QPainter *painter) const;
    void drawBronzeBevel(const QStyleOption *option,
                         QPainter *painter) const;
    void drawBronzeCheckBoxIndicator(const QStyleOption *option,
                                     QPainter *painter) const;
    void drawBronzeSpinBoxButton(SubControl which,
                                 const QStyleOptionComplex *option,
                                 QPainter *painter) const;
};

#endif

bronzestyle.cpp

#include <QtGui>

#include "bronzestyle.h"

//通用属性
void BronzeStyle::polish(QPalette &palette)
{
    //设置背景
    QPixmap backgroundImage(":/images/background.png");
    //设置按钮颜色
    QColor bronze(207, 155, 95);
    //设置背景框颜色
    QColor veryLightBlue(239, 239, 247);
    //要加深的颜色
    QColor lightBlue(223, 223, 239);
    //选中后的颜色
    QColor darkBlue(95, 95, 191);

    //创建一个新的面板
    palette = QPalette(bronze);
    //设置背景
    palette.setBrush(QPalette::Window, backgroundImage);
    //设置背景框颜色
    palette.setBrush(QPalette::Base, veryLightBlue);
    //设置加深的颜色
    palette.setBrush(QPalette::AlternateBase, lightBlue);
    //设置选中的颜色
    palette.setBrush(QPalette::Highlight, darkBlue);
    //设置样式
    palette.setBrush(QPalette::Disabled, QPalette::Highlight,
                     Qt::darkGray);
}

//当样式应用到窗口部件时
void BronzeStyle::polish(QWidget *widget)
{
    //设置Qt::WA-Hover属性,鼠标进入或者离开窗口部件所在区域
    //会产生一个绘制事件
    if (qobject_cast<QAbstractButton *>(widget)
            || qobject_cast<QAbstractSpinBox *>(widget))
        widget->setAttribute(Qt::WA_Hover, true);
}

//取消polish的作用
void BronzeStyle::unpolish(QWidget *widget)
{
    if (qobject_cast<QAbstractButton *>(widget)
            || qobject_cast<QAbstractSpinBox *>(widget))
        widget->setAttribute(Qt::WA_Hover, false);
}

//函数返回一些关于样式外观的提示
int BronzeStyle::styleHint(StyleHint which, const QStyleOption *option,
                           const QWidget *widget,
                           QStyleHintReturn *returnData) const
{
    switch (which) {
    case SH_DialogButtonLayout:
        return int(QDialogButtonBox::MacLayout);
    case SH_EtchDisabledText:
        return int(true);
    case SH_DialogButtonBox_ButtonsHaveIcons:
        return int(true);
    case SH_UnderlineShortcut:
        return int(false);
    default:
        return QWindowsStyle::styleHint(which, option, widget,
                                        returnData);
    }
}

//返回一个像素值,用于用户界面元素中
int BronzeStyle::pixelMetric(PixelMetric which,
                             const QStyleOption *option,
                             const QWidget *widget) const
{
    switch (which) {
    //返回0是因为不希望在默认的按钮旁边保留额外的空间
    case PM_ButtonDefaultIndicator:
        return 0;
    //指示器大小是一个正方形
    case PM_IndicatorWidth:
    case PM_IndicatorHeight:
        return 16;
    //控制指示器和其右边的文字之间的距离
    case PM_CheckBoxLabelSpacing:
        return 8;
    //定义QFrame,QPushButton,QSpinBox以及其他的一些窗口部件的
    //线宽.
    case PM_DefaultFrameWidth:
        return 2;
    //对于其他的PM_xxx值,从基类中继承像素规格的值
    default:
        return QWindowsStyle::pixelMetric(which, option, widget);
    }
}

//绘制基本的用户界面元素
void BronzeStyle::drawPrimitive(PrimitiveElement which,
                                const QStyleOption *option,
                                QPainter *painter,
                                const QWidget *widget) const
{
    switch (which) {
    //会被QCheckBox,QGroupBox和QItemDelegate用来绘制选择指示器
    case PE_IndicatorCheckBox:
        drawBronzeCheckBoxIndicator(option, painter);
        break;
    case PE_PanelButtonCommand:
        drawBronzeBevel(option, painter);
        break;
    case PE_Frame:
        drawBronzeFrame(option, painter);
        break;
        //对与PE_FrameDefaultButton什么都不做
        //避免在默认的按钮旁边另外再绘制一个多余的边框
    case PE_FrameDefaultButton:
        break;
    default:
        QWindowsStyle::drawPrimitive(which, option, painter, widget);
    }
}

//绘制多重辅助控制器窗口部件
void BronzeStyle::drawComplexControl(ComplexControl which,
                                     const QStyleOptionComplex *option,
                                     QPainter *painter,
                                     const QWidget *widget) const
{
    //重新实现了drawComplexControl
    if (which == CC_SpinBox) {
        drawBronzeSpinBoxButton(SC_SpinBoxDown, option, painter);
        drawBronzeSpinBoxButton(SC_SpinBoxUp, option, painter);

        QRect rect = subControlRect(CC_SpinBox, option,
                                    SC_SpinBoxEditField)
                     .adjusted(-1, 0, +1, 0);
        painter->setPen(QPen(option->palette.mid(), 1.0));
        painter->drawLine(rect.topLeft(), rect.bottomLeft());
        painter->drawLine(rect.topRight(), rect.bottomRight());
    } else {
        return QWindowsStyle::drawComplexControl(which, option, painter,
                                                 widget);
    }
}

//确认辅助控制器窗口部件的位置
QRect BronzeStyle::subControlRect(ComplexControl whichControl,
                                  const QStyleOptionComplex *option,
                                  SubControl whichSubControl,
                                  const QWidget *widget) const
{
    if (whichControl == CC_SpinBox) {
        int frameWidth = pixelMetric(PM_DefaultFrameWidth, option,
                                     widget);
        int buttonWidth = 16;

        switch (whichSubControl) {
        case SC_SpinBoxFrame:
            return option->rect;
        case SC_SpinBoxEditField:
            return option->rect.adjusted(+buttonWidth, +frameWidth,
                                         -buttonWidth, -frameWidth);
        //返回矩形区域
        case SC_SpinBoxDown:
            return visualRect(option->direction, option->rect,
                              QRect(option->rect.x(), option->rect.y(),
                                    buttonWidth,
                                    option->rect.height()));
        case SC_SpinBoxUp:
            return visualRect(option->direction, option->rect,
                              QRect(option->rect.right() - buttonWidth,
                                    option->rect.y(),
                                    buttonWidth,
                                    option->rect.height()));
        default:
            return QRect();
        }
    } else {
        return QWindowsStyle::subControlRect(whichControl, option,
                                             whichSubControl, widget);
    }
}

//调用standardIconImplementation()槽获取用在用户界面上的标准图标.
QIcon BronzeStyle::standardIconImplementation(StandardPixmap which,
        const QStyleOption *option, const QWidget *widget) const
{
    //调用基类的standardPixmap()获取图标,并绘制浅蓝色
    QImage image = QWindowsStyle::standardPixmap(which, option, widget)
                   .toImage();
    if (image.isNull())
        return QIcon();

    QPalette palette;
    if (option) {
        palette = option->palette;
    } else if (widget) {
        palette = widget->palette();
    }

    QPainter painter(&image);
    //着色可以通过在图标上绘制25%不透明的蓝色实现
    painter.setOpacity(0.25);
    //确保原来透明的部分依然透明
    painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
    painter.fillRect(image.rect(), palette.highlight());
    painter.end();

    return QIcon(QPixmap::fromImage(image));
}

//会被drawPrimitive调用,用来绘制一个PE_Frame私有元素
void BronzeStyle::drawBronzeFrame(const QStyleOption *option,
                                  QPainter *painter) const
{
    //为了保证QPainter保存原来的状态
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(QPen(option->palette.foreground(), 1.0));
    painter->drawRect(option->rect.adjusted(+1, +1, -1, -1));
    //最后恢复
    painter->restore();
}

//用来绘制QPushButton背景
void BronzeStyle::drawBronzeBevel(const QStyleOption *option,
                                  QPainter *painter) const
{
    QColor buttonColor = option->palette.button().color();
    //coeff使按钮有弹起效果
    int coeff = (option->state & State_MouseOver) ? 115 : 105;

    //上方是亮色,下方是暗色,其间是渐变的棕色
    QLinearGradient gradient(0, 0, 0, option->rect.height());
    gradient.setColorAt(0.0, option->palette.light().color());
    gradient.setColorAt(0.2, buttonColor.lighter(coeff));
    gradient.setColorAt(0.8, buttonColor.darker(coeff));
    gradient.setColorAt(1.0, option->palette.dark().color());

    //Bronze样式默认按钮使用2像素宽的边框,否则使用1像素宽的边框
    double penWidth = 1.0;
    //把option强转为const   QStyleOptionButton*类型,检测其features成员变量
    if (const QStyleOptionButton *buttonOpt =
            qstyleoption_cast<const QStyleOptionButton *>(option)) {
        if (buttonOpt->features & QStyleOptionButton::DefaultButton)
            penWidth = 2.0;
    }


    QRect roundRect = option->rect.adjusted(+1, +1, -1, -1);
    if (!roundRect.isValid())
        return;

    int diameter = 12;
    //指定按钮的圆角程度,根据diameter计算
    int cx = 100 * diameter / roundRect.width();
    int cy = 100 * diameter / roundRect.height();

    //执行绘图
    painter->save();
    painter->setPen(Qt::NoPen);
    painter->setBrush(gradient);
    painter->drawRoundRect(roundRect, cx, cy);

    if (option->state & (State_On | State_Sunken)) {
        QColor slightlyOpaqueBlack(0, 0, 0, 63);
        painter->setBrush(slightlyOpaqueBlack);
        painter->drawRoundRect(roundRect, cx, cy);
    }

    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setPen(QPen(option->palette.foreground(), penWidth));
    painter->setBrush(Qt::NoBrush);
    painter->drawRoundRect(roundRect, cx, cy);
    painter->restore();
}

//绘制复选框
void BronzeStyle::drawBronzeCheckBoxIndicator(
        const QStyleOption *option, QPainter *painter) const
{
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing, true);

    if (option->state & State_MouseOver) {
        painter->setBrush(option->palette.alternateBase());
    } else {
        painter->setBrush(option->palette.base());
    }
    painter->drawRoundRect(option->rect.adjusted(+1, +1, -1, -1));

    if (option->state & (State_On | State_NoChange)) {
        QPixmap pixmap;
        if (!(option->state & State_Enabled)) {
            pixmap.load(":/images/checkmark-disabled.png");
        } else if (option->state & State_NoChange) {
            pixmap.load(":/images/checkmark-partial.png");
        } else {
            pixmap.load(":/images/checkmark.png");
        }

        QRect pixmapRect = pixmap.rect()
                                 .translated(option->rect.topLeft())
                                 .translated(+2, -6);
        QRect painterRect = visualRect(option->direction, option->rect,
                                       pixmapRect);
        if (option->direction == Qt::RightToLeft) {
            painter->scale(-1.0, +1.0);
            painterRect.moveLeft(-painterRect.right() - 1);
        }
        painter->drawPixmap(painterRect, pixmap);
    }
    painter->restore();
}

//绘制微调框向上向下按钮
void BronzeStyle::drawBronzeSpinBoxButton(SubControl which,
        const QStyleOptionComplex *option, QPainter *painter) const
{
    PrimitiveElement arrow = PE_IndicatorArrowLeft;
    QRect buttonRect = option->rect;
    if ((which == SC_SpinBoxUp)
            != (option->direction == Qt::RightToLeft)) {
        arrow = PE_IndicatorArrowRight;
        buttonRect.translate(buttonRect.width() / 2, 0);
    }
    buttonRect.setWidth((buttonRect.width() + 1) / 2);

    QStyleOption buttonOpt(*option);

    painter->save();
    painter->setClipRect(buttonRect, Qt::IntersectClip);
    if (!(option->activeSubControls & which))
        buttonOpt.state &= ~(State_MouseOver | State_On | State_Sunken);
    drawBronzeBevel(&buttonOpt, painter);

    QStyleOption arrowOpt(buttonOpt);
    arrowOpt.rect = subControlRect(CC_SpinBox, option, which)
                    .adjusted(+3, +3, -3, -3);
    if (arrowOpt.rect.isValid())
        drawPrimitive(arrow, &arrowOpt, painter);
    painter->restore();
}

brozedialog.h

#ifndef BRONZEDIALOG_H
#define BRONZEDIALOG_H

#include <QDialog>

class QCheckBox;
class QDateEdit;
class QDialogButtonBox;
class QDoubleSpinBox;
class QLabel;
class QSpinBox;
class QTimeEdit;
class QTreeWidget;

class BronzeDialog : public QDialog
{
    Q_OBJECT

public:
    BronzeDialog(QWidget *parent = 0);

private slots:
    void editableStateChanged(bool editable);

private:
    void populateAgendaTreeWidget();

    //日期标签
    QLabel *dateLabel;
    //时间标签
    QLabel *timeLabel;
    //duration标签
    QLabel *durationLabel;
    //价格标签
    QLabel *priceLabel;
    //议程标签
    QLabel *agendaLabel;
    //日期编辑框
    QDateEdit *dateEdit;
    //时间编辑框
    QTimeEdit *timeEdit;
    //持续时间编辑框
    QSpinBox *durationSpinBox;
    //价格编辑框
    QDoubleSpinBox *priceSpinBox;
    //reminder选择框
    QCheckBox *reminderCheckBox;
    //可编辑选择框
    QCheckBox *editableCheckBox;
    //树形控件框
    QTreeWidget *agendaTreeWidget;
    //按钮框
    QDialogButtonBox *buttonBox;
};

#endif

brozedialog.cpp

  1 #include <QtGui>
  2 
  3 #include "brozedialog.h"
  4 
  5 BronzeDialog::BronzeDialog(QWidget *parent)
  6     : QDialog(parent)
  7 {
  8     //日期编辑框
  9     dateEdit = new QDateEdit(QDate::currentDate());
 10     //设置居中
 11     dateEdit->setAlignment(Qt::AlignCenter);
 12 
 13     //标签
 14     dateLabel = new QLabel(tr("&Date:"));
 15     //设置为一类
 16     dateLabel->setBuddy(dateEdit);
 17 
 18     //时间编辑框
 19     timeEdit = new QTimeEdit(QTime(9, 15, 0));
 20     //设置居中
 21     timeEdit->setAlignment(Qt::AlignCenter);
 22 
 23     //时间标签
 24     timeLabel = new QLabel(tr("&Time:"));
 25     //设置为一类
 26     timeLabel->setBuddy(timeEdit);
 27 
 28     //持续时间编辑框
 29     durationSpinBox = new QSpinBox;
 30     //设置居中
 31     durationSpinBox->setAlignment(Qt::AlignCenter);
 32     //设置后面显示的值
 33     durationSpinBox->setSuffix(tr(" hour"));
 34     //设置默认的值
 35     durationSpinBox->setValue(3);
 36 
 37     //设置标签
 38     durationLabel = new QLabel(tr("D&uration:"));
 39     //设置为一类
 40     durationLabel->setBuddy(durationSpinBox);
 41 
 42     //设置价格框
 43     priceSpinBox = new QDoubleSpinBox;
 44     //设置居中显示
 45     priceSpinBox->setAlignment(Qt::AlignCenter);
 46     //设置范围
 47     priceSpinBox->setMaximum(10000.00);
 48     priceSpinBox->setValue(500.00);
 49 
 50     //设置标签
 51     priceLabel = new QLabel(tr("&Price:"));
 52     //设置为一类
 53     priceLabel->setBuddy(priceSpinBox);
 54 
 55     //创建树形控件
 56     agendaTreeWidget = new QTreeWidget;
 57     //基本属性设置
 58     agendaTreeWidget->setAlternatingRowColors(true);
 59     agendaTreeWidget->setHorizontalScrollBarPolicy(
 60             Qt::ScrollBarAlwaysOff);
 61     agendaTreeWidget->setVerticalScrollBarPolicy(
 62             Qt::ScrollBarAlwaysOff);
 63     //设置一列
 64     agendaTreeWidget->setColumnCount(1);
 65     //设置头部隐藏
 66     agendaTreeWidget->header()->hide();
 67     //设置tree控件
 68     populateAgendaTreeWidget();
 69 
 70     //设置标签
 71     agendaLabel = new QLabel(tr("&Agenda:"));
 72     //绑定
 73     agendaLabel->setBuddy(agendaTreeWidget);
 74     //设置位置
 75     agendaLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
 76 
 77     //设置复选框
 78     reminderCheckBox = new QCheckBox(tr("&Send me a reminder"));
 79     reminderCheckBox->setCheckState(Qt::PartiallyChecked);
 80 
 81     //设置复选框
 82     editableCheckBox = new QCheckBox(tr("&Item is editable"));
 83     editableCheckBox->setChecked(true);
 84 
 85     //创建按钮
 86     buttonBox = new QDialogButtonBox(QDialogButtonBox::Save
 87                                      | QDialogButtonBox::Cancel);
 88 
 89     //editable槽函数
 90     connect(editableCheckBox, SIGNAL(toggled(bool)),
 91             this, SLOT(editableStateChanged(bool)));
 92     //按钮槽函数
 93     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
 94     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
 95 
 96     //创建布局
 97     QGridLayout *mainLayout = new QGridLayout;
 98     mainLayout->addWidget(dateLabel, 定制QSpinBox外观

我将 UIButton 子类化以创建自己的自定义按钮外观,但它不会重绘所有触摸事件

QT自定义控件消息实现

如何从 QStyle 获取 QTreeView 的标识宽度

[Qt]自定义QStyle——实现QProgressBar自定义样式

从以往子类化跟踪MouseLeave深入讨论VB6的自定义Hook类