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, 0, 0);
 99     mainLayout->addWidget(dateEdit, 0, 1);
100     mainLayout->addWidget(timeLabel, 0, 3);
101     mainLayout->addWidget(timeEdit, 0, 4);
102     mainLayout->addWidget(durationLabel, 1, 0);
103     mainLayout->addWidget(durationSpinBox, 1, 1);
104     mainLayout->addWidget(priceLabel, 1, 3);
105     mainLayout->addWidget(priceSpinBox, 1, 4);
106     mainLayout->addWidget(agendaTreeWidget, 2, 1, 2, 4);
107     mainLayout->addWidget(agendaLabel, 2, 0);
108     mainLayout->addWidget(reminderCheckBox, 4, 0, 1, 2);
109     mainLayout->addWidget(editableCheckBox, 4, 3, 1, 2, Qt::AlignRight);
110     mainLayout->addWidget(buttonBox, 5, 0, 1, 5);
111 
112     setLayout(mainLayout);
113 
114     setWindowTitle(tr("Bronze"));
115 }
116 
117 //edit被選中
118 void BronzeDialog::editableStateChanged(bool editable)
119 {
120     dateLabel->setEnabled(editable);
121     timeLabel->setEnabled(editable);
122     durationLabel->setEnabled(editable);
123     priceLabel->setEnabled(editable);
124     agendaLabel->setEnabled(editable);
125     dateEdit->setEnabled(editable);
126     timeEdit->setEnabled(editable);
127     durationSpinBox->setEnabled(editable);
128     priceSpinBox->setEnabled(editable);
129     reminderCheckBox->setEnabled(editable);
130     agendaTreeWidget->setEnabled(editable);
131     buttonBox->button(QDialogButtonBox::Save)->setEnabled(editable);
132 }
133 
134 void BronzeDialog::populateAgendaTreeWidget()
135 {
136     QTreeWidgetItem *item1 = new QTreeWidgetItem(agendaTreeWidget);
137     item1->setText(0, tr("1. Call to order"));
138 
139     QTreeWidgetItem *item2 = new QTreeWidgetItem(agendaTreeWidget);
140     item2->setText(0, tr("2. Approval of Minutes"));
141 
142     QTreeWidgetItem *item3 = new QTreeWidgetItem(agendaTreeWidget);
143     item3->setText(0, tr("3. New Business"));
144     item3->setExpanded(true);
145 
146     QTreeWidgetItem *item31 = new QTreeWidgetItem(item3);
147     item31->setText(0, tr("3.1. Introduction of task force members"));
148 
149     QTreeWidgetItem *item32 = new QTreeWidgetItem(item3);
150     item32->setText(0, tr("3.2. Welcome address"));
151 
152     QTreeWidgetItem *item33 = new QTreeWidgetItem(item3);
153     item33->setText(0, tr("3.3. Review timeline for task force work"));
154 
155     QTreeWidgetItem *item4 = new QTreeWidgetItem(agendaTreeWidget);
156     item4->setText(0, tr("4. Arrangements for future meetings"));
157 
158     QTreeWidgetItem *item41 = new QTreeWidgetItem(item4);
159     item41->setText(0, tr("4.1. Dates"));
160 
161     QTreeWidgetItem *item42 = new QTreeWidgetItem(item4);
162     item42->setText(0, tr("4.2. Agenda items for next meeting"));
163 
164     QTreeWidgetItem *item5 = new QTreeWidgetItem(agendaTreeWidget);
165     item5->setText(0, tr("5. Ajournment"));
166 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM