在 Qt 里如何自定义一个控件?
13 个回答
手机上面的APP有很多流行的元素,开关按钮个人非常喜欢,手机QQ、360卫士、金山毒霸等,都有很多开关控制一些操作,在Qt widgets应用项目上,在项目中应用些类似的开关按钮,估计也会为项目增添不少新鲜感。
总结了大部分的开关按钮控件,基本上有两大类,第一类是纯代码绘制,这种对代码的掌控度要求比较高,但是灵活性比较好。第二类是贴图,专业的美工做好的各种状态的背景图片,只需要用代码将该图片画到界面上即可。为了能够涵盖两大类的开关按钮,特意将常见的四种类型(圆角矩形/内圆形/外圆形/图片)都集成到了自定义的开关按钮中。
运行效果:
1:纯代码绘制
纯代码绘制开关按钮,可以很灵活的设置各种颜色、间隔、文字等,还可以产生动画过度的滑动效果。
产生滑动效果采用定时器绘制的方式,自动计算滑块的X轴开始坐标,当滑块的X轴开始坐标到达滑块的X轴结束坐标时停止定时器。
void SwitchButton::updateValue()
if (checked) {
if (startX < endX) {
startX = startX + step;
} else {
startX = endX;
timer->stop();
} else {
if (startX > endX) {
startX = startX - step;
} else {
startX = endX;
timer->stop();
update();
}
2:贴图绘制
void SwitchButton::drawImage(QPainter *painter)
painter->save();
QPixmap pix;
if (!checked) {
pix = QPixmap(imageOff);
} else {
pix = QPixmap(imageOn);
//自动等比例平滑缩放居中显示
int targetWidth = pix.width();
int targetHeight = pix.height();
pix = pix.scaled(targetWidth, targetHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
int pixX = rect().center().x() - targetWidth / 2;
int pixY = rect().center().y() - targetHeight / 2;
QPoint point(pixX, pixY);
painter->drawPixmap(point, pix);
painter->restore();
}
完整代码:
switchbutton.h
#ifndef SWITCHBUTTON_H
#define SWITCHBUTTON_H
* 作者:feiyangqingyun(QQ:517216493) 2016-11-6
* 1:可设置开关按钮的样式 圆角矩形/内圆形/外圆形/图片
* 2:可设置选中和未选中时的背景颜色
* 3:可设置选中和未选中时的滑块颜色
* 4:可设置显示的文本
* 5:可设置滑块离背景的间隔
* 6:可设置圆角角度
#include <QWidget>
class QTimer;
class SwitchButton: public QWidget
Q_OBJECT
public:
enum ButtonStyle {
ButtonStyle_Rect = 0, //圆角矩形
ButtonStyle_CircleIn = 1, //内圆形
ButtonStyle_CircleOut = 2,//外圆形
ButtonStyle_Image = 3 //图片
SwitchButton(QWidget *parent = 0);
~SwitchButton();
protected:
void mousePressEvent(QMouseEvent *);
void resizeEvent(QResizeEvent *);
void paintEvent(QPaintEvent *);
void drawBg(QPainter *painter);
void drawSlider(QPainter *painter);
void drawText(QPainter *painter);
void drawImage(QPainter *painter);
private:
bool checked; //是否选中
ButtonStyle buttonStyle; //开关按钮样式
QColor bgColorOff; //关闭时背景颜色
QColor bgColorOn; //打开时背景颜色
QColor sliderColorOff; //关闭时滑块颜色
QColor sliderColorOn; //打开时滑块颜色
QColor textColorOff; //关闭时文本颜色
QColor textColorOn; //打开时文本颜色
QString textOff; //关闭时显示的文字
QString textOn; //打开时显示的文字
QString imageOff; //关闭时显示的图片
QString imageOn; //打开时显示的图片
int space; //滑块离背景间隔
int rectRadius; //圆角角度
int step; //每次移动的步长
int startX; //滑块开始X轴坐标
int endX; //滑块结束X轴坐标
QTimer *timer; //定时器绘制
private slots:
void updateValue();
public:
bool getChecked()const
return checked;
ButtonStyle getButtonStyle()const
return buttonStyle;
QColor getBgColorOff()const
return bgColorOff;
QColor getBgColorOn()const
return bgColorOn;
QColor getSliderColorOff()const
return sliderColorOff;
QColor getSliderColorOn()const
return sliderColorOn;
QColor getTextColorOff()const
return textColorOff;
QColor getTextColorOn()const
return textColorOn;
QString getTextOff()const
return textOff;
QString getTextOn()const
return textOn;
QString getImageOff()const
return imageOff;
QString getImageOn()const
return imageOn;
int getSpace()const
return space;
int getRectRadius()const
return rectRadius;
public slots:
//设置是否选中
void setChecked(bool checked);
//设置风格样式
void setButtonStyle(ButtonStyle buttonStyle);
//设置背景颜色
void setBgColor(QColor bgColorOff, QColor bgColorOn);
//设置滑块颜色
void setSliderColor(QColor sliderColorOff, QColor sliderColorOn);
//设置文本颜色
void setTextColor(QColor textColorOff, QColor textColorOn);
//设置文本
void setText(QString textOff, QString textOn);
//设置背景图片
void setImage(QString imageOff, QString imageOn);
//设置间隔
void setSpace(int space);
//设置圆角角度
void setRectRadius(int rectRadius);
signals:
void checkedChanged(bool checked);
#endif // SWITCHBUTTON_H
switchbutton.cpp
#include "switchbutton.h"
#include "qpainter.h"
#include "qevent.h"
#include "qtimer.h"
#include "qdebug.h"
SwitchButton::SwitchButton(QWidget *parent): QWidget(parent)
checked = false;
buttonStyle = ButtonStyle_Rect;
bgColorOff = QColor(225, 225, 225);
bgColorOn = QColor(250, 250, 250);
sliderColorOff = QColor(100, 100, 100);
sliderColorOn = QColor(100, 184, 255);
textColorOff = QColor(255, 255, 255);
textColorOn = QColor(10, 10, 10);
textOff = "";
textOn = "";
imageOff = ":/image/btncheckoff1.png";
imageOn = ":/image/btncheckon1.png";
space = 2;
rectRadius = 5;
step = width() / 50;
startX = 0;
endX = 0;
timer = new QTimer(this);
timer->setInterval(5);
connect(timer, SIGNAL(timeout()), this, SLOT(updateValue()));
setFont(QFont("Microsoft Yahei", 10));
SwitchButton::~SwitchButton()
void SwitchButton::mousePressEvent(QMouseEvent *)
checked = !checked;
emit checkedChanged(checked);
//每次移动的步长为宽度的 50分之一
step = width() / 50;
//状态切换改变后自动计算终点坐标
if (checked) {
if (buttonStyle == ButtonStyle_Rect) {
endX = width() - width() / 2;
} else if (buttonStyle == ButtonStyle_CircleIn) {
endX = width() - height();
} else if (buttonStyle == ButtonStyle_CircleOut) {
endX = width() - height() + space;
} else {
endX = 0;
timer->start();
void SwitchButton::resizeEvent(QResizeEvent *)
//每次移动的步长为宽度的 50分之一
step = width() / 50;
//尺寸大小改变后自动设置起点坐标为终点
if (checked) {
if (buttonStyle == ButtonStyle_Rect) {
startX = width() - width() / 2;
} else if (buttonStyle == ButtonStyle_CircleIn) {
startX = width() - height();
} else if (buttonStyle == ButtonStyle_CircleOut) {
startX = width() - height() + space;
} else {
startX = 0;
update();
void SwitchButton::paintEvent(QPaintEvent *)
//绘制准备工作,启用反锯齿
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
if (buttonStyle == ButtonStyle_Image) {
//绘制图片
drawImage(&painter);
} else {
//绘制背景
drawBg(&painter);
//绘制滑块
drawSlider(&painter);
//绘制文字
drawText(&painter);
void SwitchButton::drawBg(QPainter *painter)
painter->save();
painter->setPen(Qt::NoPen);
if (!checked) {
painter->setBrush(bgColorOff);
} else {
painter->setBrush(bgColorOn);
if (buttonStyle == ButtonStyle_Rect) {
painter->drawRoundedRect(rect(), rectRadius, rectRadius);
} else if (buttonStyle == ButtonStyle_CircleIn) {
QRect rect(0, 0, width(), height());
//半径为高度的一半
int radius = rect.height() / 2;
//圆的宽度为高度
int circleWidth = rect.height();
QPainterPath path;
path.moveTo(radius, rect.left());
path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180);
path.lineTo(rect.width() - radius, rect.height());
path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180);
path.lineTo(radius, rect.top());
painter->drawPath(path);
} else if (buttonStyle == ButtonStyle_CircleOut) {
QRect rect(space, space, width() - space * 2, height() - space * 2);
painter->drawRoundedRect(rect, rectRadius, rectRadius);
painter->restore();
void SwitchButton::drawSlider(QPainter *painter)
painter->save();
painter->setPen(Qt::NoPen);
if (!checked) {
painter->setBrush(sliderColorOff);
} else {
painter->setBrush(sliderColorOn);
if (buttonStyle == ButtonStyle_Rect) {
int sliderWidth = width() / 2 - space * 2;
int sliderHeight = height() - space * 2;
QRect sliderRect(startX + space, space, sliderWidth , sliderHeight);
painter->drawRoundedRect(sliderRect, rectRadius, rectRadius);
} else if (buttonStyle == ButtonStyle_CircleIn) {
QRect rect(0, 0, width(), height());
int sliderWidth = rect.height() - space * 2;
QRect sliderRect(startX + space, space, sliderWidth, sliderWidth);
painter->drawEllipse(sliderRect);
} else if (buttonStyle == ButtonStyle_CircleOut) {
QRect rect(0, 0, width() - space, height() - space);
int sliderWidth = rect.height();
QRect sliderRect(startX, space / 2, sliderWidth, sliderWidth);
painter->drawEllipse(sliderRect);
painter->restore();
void SwitchButton::drawText(QPainter *painter)
painter->save();
if (!checked) {
painter->setPen(textColorOff);
painter->drawText(width() / 2, 0, width() / 2 - space, height(), Qt::AlignCenter, textOff);
} else {
painter->setPen(textColorOn);
painter->drawText(0, 0, width() / 2 + space * 2, height(), Qt::AlignCenter, textOn);
painter->restore();
void SwitchButton::drawImage(QPainter *painter)
painter->save();
QPixmap pix;
if (!checked) {
pix = QPixmap(imageOff);
} else {
pix = QPixmap(imageOn);
//自动等比例平滑缩放居中显示
int targetWidth = pix.width();
int targetHeight = pix.height();
pix = pix.scaled(targetWidth, targetHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
int pixX = rect().center().x() - targetWidth / 2;
int pixY = rect().center().y() - targetHeight / 2;
QPoint point(pixX, pixY);
painter->drawPixmap(point, pix);
painter->restore();
void SwitchButton::updateValue()
if (checked) {
if (startX < endX) {
startX = startX + step;
} else {
startX = endX;
timer->stop();
} else {
if (startX > endX) {
startX = startX - step;
} else {
startX = endX;
timer->stop();
update();
void SwitchButton::setChecked(bool checked)
if (this->checked != checked) {
this->checked = checked;
emit checkedChanged(checked);
update();
void SwitchButton::setButtonStyle(SwitchButton::ButtonStyle buttonStyle)
this->buttonStyle = buttonStyle;
update();
void SwitchButton::setBgColor(QColor bgColorOff, QColor bgColorOn)
this->bgColorOff = bgColorOff;
this->bgColorOn = bgColorOn;
update();
void SwitchButton::setSliderColor(QColor sliderColorOff, QColor sliderColorOn)
this->sliderColorOff = sliderColorOff;
this->sliderColorOn = sliderColorOn;
update();
void SwitchButton::setTextColor(QColor textColorOff, QColor textColorOn)
this->textColorOff = textColorOff;
this->textColorOn = textColorOn;
update();
void SwitchButton::setText(QString textOff, QString textOn)
this->textOff = textOff;
this->textOn = textOn;
update();
void SwitchButton::setImage(QString imageOff, QString imageOn)
this->imageOff = imageOff;
this->imageOn = imageOn;
update();
void SwitchButton::setSpace(int space)
this->space = space;
update();