Qt绘制chrome风格的Tab标签

前言

Qt中改变现有的控件的外观有多种方式,最常见的是使用样式表,根据样式表中的各种选择器并对属性、状态和sub-control进行样式的修改,也是比较方便的方式。最近由于要使用cef进行一个多标签浏览器的开发,需要修改tab的外观,这里使用QStyle来进行修改。

QStyle

Qt中对于QStyle的简单描述
简单来说就是Qstyle是一个抽象类,封装了不同平台上的GUI的外观。对于QStyle的详细介绍,请参考其他的资料或者Qt的帮助文档,百度一大堆。
QProxyStyle实现了QStyle所有的抽象接口,并且默认保持系统风格。

这里我们继承一个QProxStyle,然后实现其中两个接口便可实现。
第一个是sizeFromContent,返回控件的大小,函数原型如下:


virtual QSize sizeFromContents(ContentsType type, const QStyleOptio *option, const QSize &size, const QWidget *widget) const

第二个是drawControl,用于绘制控件的元素。函数原型如下:


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

效果图

设置后的效果图
效果图2

绘制代码中的思路

比较笨的控制大小,数格子!!
由windows自带的画图数格子,然后用整体的大小确定比例,用比例绘制比较妥。
在这里插入图片描述

方式1

很明显了吧,
方式1

方式2

其实和方式1一样,只是椭圆不一样。高度比较小的时候,两者看着都差不多,我都试过。
方式2

方式3

二次贝塞尔曲线,就很简单。绘制出来可以说和chome基本一样
效果:
方式3效果
代码:

void Widget::drawShape1(QPainter *p, const QRect &drawRect)
{
    QRectF r(drawRect);

    qreal per = r.height() / 8.0;
    qreal topMargin = 4.0;

    QPainterPath path;
    path.moveTo(r.bottomLeft() - QPointF(per, 0));
    path.quadTo(QPointF(r.left(), r.bottom()),
                QPointF(r.left(), r.bottom() - per));
    path.lineTo(r.left(), r.top() + per + topMargin);
    path.quadTo(QPointF(r.left() , r.top() + topMargin),
                QPointF(r.left() +  per, r.top() + topMargin));
    path.lineTo(r.right() - per, r.top() + topMargin);
    path.quadTo(QPointF(r.right(), r.top() + topMargin),
                QPointF(r.right(), r.top() + per + topMargin));
    path.lineTo(r.right(), r.bottom() - per);
    path.quadTo(QPointF(r.right(), r.bottom()),
                r.bottomRight() + QPointF(per, 0));

    p->setRenderHint(QPainter::Antialiasing);
    p->setBrush(Qt::green);
    p->drawPath(path);
}

void Widget::drawShape2(QPainter *p, const QRect &drawRect)
{
    QRectF r(drawRect);

    qreal per = r.height() / 8.0;
    qreal topMargin = 4.0;

    QPainterPath path;
    path.moveTo(r.bottomLeft() + QPointF(per, 0));
    path.quadTo(QPointF(r.left(), r.bottom()),
                QPointF(r.left(), r.bottom() - per));
    path.lineTo(r.left(), r.top() + per + topMargin);
    path.quadTo(QPointF(r.left(), r.top() + topMargin),
                QPointF(r.left() - per, r.top() + topMargin));
    path.lineTo(r.right() - per, r.top() + topMargin);
    path.quadTo(QPointF(r.right(), r.top() + topMargin),
                QPointF(r.right(), r.top() + per + topMargin));
    path.lineTo(r.right(), r.bottom() - per);
    path.quadTo(QPointF(r.right(), r.bottom()),
                r.bottomRight() + QPointF(per, 0 ));

    p->setRenderHint(QPainter::Antialiasing);
    p->setBrush(Qt::red);
    p->drawPath(path);
}

实现代码

控件大小

QSize NewChromeTabStyle::sizeFromContents(QStyle::ContentsType ct, const QStyleOption *opt,
                                          const QSize &contentsSize, const QWidget *w) const
{
//    return QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
    QSize s = QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
    if (ct == QStyle::CT_TabBarTab) {
//        s.transpose();
        s.rwidth() = contentsSize.width();
        s.rheight() = 32;
    }
    return s;
}

绘制样式

void NewChromeTabStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option,
                                    QPainter *painter, const QWidget *w) const
{
    painter->save();
    painter->setPen(Qt::red);
//    painter->drawRect(w->rect());
    painter->restore();
    switch (element) {
    case CE_TabBarTabLabel:
    {
        const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option);
        QRect textRect = subElementRect(QStyle::SE_TabBarTabText, tab, w);

        if (tab->state & QStyle::State_Selected) {
            painter->save();
            painter->setPen(QPen("#000000"));
            QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
            option.setWrapMode(QTextOption::NoWrap);
            painter->drawText(textRect, tab->text, option);
            painter->restore();
        }
        else if(tab->state & QStyle::State_MouseOver) {
            painter->save();
            painter->setPen(QPen("#000000"));
            QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
            option.setWrapMode(QTextOption::NoWrap);
            painter->drawText(textRect, tab->text, option);
            painter->restore();
        }
        else
        {
            painter->save();
            painter->setPen(QPen("#666666"));
            QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
            option.setWrapMode(QTextOption::NoWrap);
            painter->drawText(textRect, tab->text, option);
            painter->restore();
        }
    }
        break;
    case CE_TabBarTabShape:
    {
        const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option);
        // Set Anti-aliasing effect
        painter->setRenderHint(QPainter::Antialiasing);

        QRectF shapeRect = tab->rect;
        qreal arcLen = shapeRect.height() / 2;

        if (tab->state & QStyle::State_Selected) {
            painter->save();
            QPen pen(Qt::red);
            pen.setWidthF(0.5);
            painter->setPen(pen);
//            painter->drawRect(shapeRect);
            painter->restore();

            painter->save();
            shapeRect.setX(shapeRect.x() - arcLen);
            shapeRect.setWidth(shapeRect.width() + arcLen *3/ 4);

            QPainterPath path;

            QRectF arcRect1(shapeRect.x(),
                            shapeRect.y() + shapeRect.height() / 3,
                            arcLen,
                            shapeRect.height() * 2 / 3);

            path.moveTo(QPointF(arcRect1.x(),
                                 arcRect1.y() + arcRect1.height()));

            path.lineTo(QPointF(arcRect1.x() + arcRect1.width() / 2,
                                 arcRect1.y() + arcRect1.height()));

            // Starting from 270°, returning to the arc of 90°
            path.arcTo(arcRect1, 270.0, 90.0);

            path.lineTo(QPointF(arcRect1.x() + arcRect1.width(),
                                 arcRect1.y()));

            QRectF arcRect2(shapeRect.x() + arcRect1.width(),
                            shapeRect.y(),
                            arcLen,
                            shapeRect.height() * 2 / 3);

            // Starting from 180°, returning to the arc of -90°
            path.arcTo(arcRect2, 180.0, -90.0);

            path.lineTo(QPointF(arcRect2.x() + shapeRect.width() -  2 * (arcRect2.x()  + arcRect2.width()) + arcRect2.width() / 2,
                                 arcRect2.y()));

            QRectF arcRect3(shapeRect.x() + shapeRect.width() - 2 * arcRect1.width(),
                            shapeRect.y(),
                            arcRect1.width(),
                            shapeRect.height() * 2 / 3);

            path.arcTo(arcRect3, 90.0, -90.0);

            path.lineTo(QPointF(arcRect3.x() + arcRect3.width(),
                                 arcRect3.y() + arcRect3.height()));

            QRectF arcRect4(arcRect3.x() + arcRect3.width(),
                            arcRect3.y() + arcRect3.height() / 2,
                            arcRect1.width(),
                            shapeRect.height() * 2 / 3);

            path.arcTo(arcRect4, 180.0, 90.0);

            path.lineTo(QPointF(arcRect1.x(),
                                arcRect1.y() + arcRect1.height()));

            painter->setPen(Qt::NoPen);
            painter->setBrush(Qt::white);
            QPolygonF polygon = path.toFillPolygon();
            painter->drawPolygon(polygon);

            painter->restore();
        }else if(tab->state & QStyle::State_MouseOver)
        {
//            return;
            painter->save();
            QPen pen(Qt::black);
            pen.setWidthF(0.5);
            painter->setPen(pen);
//            painter->drawRect(shapeRect);
            painter->restore();
            painter->save();

            shapeRect.setX(shapeRect.x() - arcLen);
            shapeRect.setWidth(shapeRect.width() + arcLen *3/ 4);

            QPainterPath path;

            QRectF arcRect1(shapeRect.x(),
                            shapeRect.y() + shapeRect.height() / 3,
                            arcLen,
                            shapeRect.height() * 2 / 3);

            path.moveTo(QPointF(arcRect1.x(),
                                 arcRect1.y() + arcRect1.height()));

            path.lineTo(QPointF(arcRect1.x() + arcRect1.width() / 2,
                                 arcRect1.y() + arcRect1.height()));

            // Starting from 270°, returning to the arc of 90°
            path.arcTo(arcRect1, 270.0, 90.0);

            path.lineTo(QPointF(arcRect1.x() + arcRect1.width(),
                                 arcRect1.y()));

            QRectF arcRect2(shapeRect.x() + arcRect1.width(),
                            shapeRect.y(),
                            arcLen,
                            shapeRect.height() * 2 / 3);

            // Starting from 180°, returning to the arc of -90°
            path.arcTo(arcRect2, 180.0, -90.0);

            path.lineTo(QPointF(arcRect2.x() + shapeRect.width() -  2 * (arcRect2.x()  + arcRect2.width()) + arcRect2.width() / 2,
                                 arcRect2.y()));

            QRectF arcRect3(shapeRect.x() + shapeRect.width() - 2 * arcRect1.width(),
                            shapeRect.y(),
                            arcRect1.width(),
                            shapeRect.height() * 2 / 3);

            path.arcTo(arcRect3, 90.0, -90.0);

            path.lineTo(QPointF(arcRect3.x() + arcRect3.width(),
                                 arcRect3.y() + arcRect3.height()));

            QRectF arcRect4(arcRect3.x() + arcRect3.width(),
                            arcRect3.y() + arcRect3.height() / 2,
                            arcRect1.width(),
                            shapeRect.height() * 2 / 3);

            path.arcTo(arcRect4, 180.0, 90.0);

            path.lineTo(QPointF(arcRect1.x(),
                                arcRect1.y() + arcRect1.height()));

            painter->setPen(Qt::NoPen);
            painter->setBrush(QColor("#FBFCFC"));
            QPolygonF polygon = path.toFillPolygon();
            painter->drawPolygon(polygon);

            painter->restore();

        }else
        {
            painter->save();

            shapeRect.setX(shapeRect.x() - arcLen);
            shapeRect.setWidth(shapeRect.width() + arcLen / 2);

            QPen pen(Qt::black);
            pen.setWidthF(0.5);
            painter->setPen(pen);

            /// Draw a dividing line on the left
            painter->drawLine(QPointF(shapeRect.x() + arcLen*2/3 -1, shapeRect.height() / 3),
                              QPointF(shapeRect.x() + arcLen*2/3 -1, shapeRect.height() * 2 / 3));

            /// Draw a dividing line on the right
//            painter->drawLine(baseR.x() + baseR.width() - arcLen,
//                              baseR.height() / 4,
//                              baseR.x() + baseR.width() - arcLen,
//                              baseR.height() * 3 / 4);

            painter->restore();
        }
    }
        break;
    default:
        QProxyStyle::drawControl(element, option, painter, w);
        break;
    }
}

其他

tab的大小

在sizeFromContents函数返回的控件尺寸可以设置成固定的,也可以直接返回默认。
在设置成默认的尺寸以后,每个tab的大小都固定了,这样对于Tab文字比较少的那种,看起来不太友好。仿照嘛,就要仿照得像一点。这里可以结合样式表来动态调整tab的大小。在每次增加和移除tab的时候调用这个函数自动调整tab的大小。

void MainWindow::AdjustTabWidth()
{
    int max_tabwidget_width =  width() - 150;
    tabWidget->tabBar()->setMaximumWidth(max_tabwidget_width);
    // get the average with of the tab
    int average_width = tabWidget->count() > (max_tabwidget_width / MaxTabWidth) ?
                max_tabwidget_width / tabWidget->count() : MaxTabWidth;

    tabWidget->setStyleSheet(QString::fromUtf8("BTabWidget::pane{"
                                               "border:none;"
                                               "}"
                                               "BTabWidget::tab-bar {"
                                               "left: 6px;"
                                               "}"

                                               "QTabBar::tab {"
                                               "min-width:78px;"
                                               "width:%1px;"
                                               "}"
                                               ).arg(average_width));
}

老版本chrome标签样式

也可以用这个绘制老版本的谷歌浏览器的样式。效果图如下。方式也一样,代码就不贴了,也没绘制好,后面的Demo中有实现。
老版本Chrome标签样式
没画好。。。需要这种效果的再改改吧。

最后

完整的Demo: 源代码.
测试Demo。比较乱,如果有需要就凑合着看吧!!🙂

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值