效果图
实现的效果有tab页文字从上到下显示,如果超出规定长度显示省略号,关闭按钮在最下端。原理是通过继承QTabBar
重写tabSizeHint
paintEvent
mousePressEvent
等函数来实现的。因为QTabWidget的setTabBar
函数权限是protect
,所以要想使用必须在自定义一个QTabWidget
。
关键代码
CustomTabBar.cpp
#include "CustomTabBar.h"
#include <QPainter>
#include <QDebug>
#define TAB_WIDTH 30
#define TAB_HEIGHT 120
#define PIXMAP_WIDTH 16
#define PIXMAP_HEIGHT 16
#define TEXTLABEL_TOP_MARGIN 8
#define PIXMAP_TOP_MARGIN 4
#define FACTOR 7
#define COLOR_HOVER "#CDC9C9"
#define COLOR_SELECT_0 "#4F94CD"
#define COLOR_SELECT_1 "#EEB422"
#define COLOR_NORMAL_0 "#63B8FF"
#define COLOR_NORMAL_1 "#FFFF00"
#define COLOR_MARK "#FF3333"
CustomTabBar::CustomTabBar(QWidget *parent) : QTabBar(parent)
{
}
QSize CustomTabBar::tabSizeHint(int index) const
{
Q_UNUSED(index);
return QSize(TAB_WIDTH,TAB_HEIGHT);
}
void CustomTabBar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(this);
for(int i = 0 ; i < count(); i++)
{
QRect rc = tabRect(i);
QStyleOptionTabV2 option;
initStyleOption(&option, i);
//tab背景
p.setPen(Qt::NoPen);
if(QStyle::State_MouseOver & option.state)
{
p.setBrush(QColor(COLOR_HOVER));
}
else if(QStyle::State_Selected & option.state)
{
if(i == 0)
{
p.setBrush(QColor(COLOR_SELECT_0));
}
else if(i == 1)
{
p.setBrush(QColor(COLOR_SELECT_1));
}
}
else
{
if(i == 0)
{
p.setBrush(QColor(COLOR_NORMAL_0));
}
else if(i == 1)
{
p.setBrush(QColor(COLOR_NORMAL_1));
}
}
p.drawRect(rc);
//tab文字
p.setPen(Qt::black);
p.setBrush(Qt::NoBrush);
QRect textLabelRect = rc;
//顶部间距8个像素
textLabelRect.setY(rc.y() + TEXTLABEL_TOP_MARGIN);
textLabelRect.setHeight(textLabelRect.height() - PIXMAP_HEIGHT - PIXMAP_TOP_MARGIN);
QString showStr = tabText(i);
showStr = getElidedText(QFont(), showStr, textLabelRect.height());
showStr = showStr.split("",QString::SkipEmptyParts).join("\n");
p.drawText(textLabelRect,Qt::AlignTop | Qt::AlignHCenter, showStr);
//tab图片rect 16x16
QRect imageRect(rc.x() + rc.width() / 2 - PIXMAP_WIDTH / 2, rc.y() + TEXTLABEL_TOP_MARGIN + textLabelRect.height() + PIXMAP_TOP_MARGIN, 16, 16);
p.drawPixmap(imageRect, QPixmap(":/pic/close.png"));
}
}
void CustomTabBar::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
int index = tabAt(event->pos());
if(index != -1)
{
QRect rc = tabRect(index);
//图片框 (rc.height() - PIXMAP_HEIGHT - PIXMAP_TOP_MARGIN - TEXTLABEL_TOP_MARGIN)是计算textLabelRect.height()
QRect imageRect(rc.x() + rc.width() / 2 - PIXMAP_WIDTH / 2, rc.y() + TEXTLABEL_TOP_MARGIN + (rc.height() - PIXMAP_HEIGHT - PIXMAP_TOP_MARGIN - TEXTLABEL_TOP_MARGIN) + PIXMAP_TOP_MARGIN, PIXMAP_WIDTH, PIXMAP_HEIGHT);
if(imageRect.contains(event->pos()))
{
emit tabCloseRequested(index);
}
}
}
QTabBar::mousePressEvent(event);
}
QString CustomTabBar::getElidedText(QFont font, QString str, int MaxWidth)
{
if (str.isEmpty())
{
return "";
}
QFontMetrics fontWidth(font);
//计算字符串宽度
int width = fontWidth.width(str);
//当字符串宽度大于最大宽度时进行转换
if (width >= MaxWidth)
{
//右部显示省略号
str = fontWidth.elidedText(str, Qt::ElideRight, MaxWidth);
}
//返回处理后的字符串
return str;
}
CustomTabBar.h
#ifndef CUSTOMTABBAR_H
#define CUSTOMTABBAR_H
#include <QtWidgets>
#include <QTabBar>
class CustomTabBar : public QTabBar
{
Q_OBJECT
public:
explicit CustomTabBar(QWidget *parent = nullptr);
protected:
QSize tabSizeHint(int index) const override;
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
QString getElidedText(QFont font, QString str, int MaxWidth);
};
#endif // CUSTOMTABBAR_H
CustomTabWidget.cpp
#include "CustomTabWidget.h"
#include "CustomTabBar.h"
CustomTabWidget::CustomTabWidget(QWidget *parent) : QTabWidget(parent)
{
setTabBar(new CustomTabBar(this));
}
CustomTabWidget.h
#ifndef CUSTOMTABWIDGET_H
#define CUSTOMTABWIDGET_H
#include <QWidget>
#include <QTabWidget>
class CustomTabWidget : public QTabWidget
{
Q_OBJECT
public:
explicit CustomTabWidget(QWidget *parent = nullptr);
};
#endif // CUSTOMTABWIDGET_H
然后使用自定义的CustomTabWidget
即可