文章目录
- 0 引入
- 1、带有标尺的温度/湿度计控件
- 1.头文件
- 2.核心代码
- 2、竖起来的温度/湿度计控件
- 1.头文件
- 2.实现
- 3、引用
0 引入
QT原生控件没有实现如仪表盘或者温度计的控件,只好自己实现,文章代码部分参考引用的文章。直接上图
图一 带有标尺的温度计、湿度
图二 温度计、湿度
控件最核心的部分:在函数paintEvent绘制部分,如果需要动画效果还需要加一个QPropertyAnimation ,这是最主要的,剩下的细节根据需求增加减少即可。
1、带有标尺的温度/湿度计控件
因为只做数据显示用,所以只需要向控件传数据即可。
主要功能:1、可设置显示范围;
2、显示过程中加了动画效果;
3、背景色和前景色以及刻度尺颜色可变;
4、刻度尺间距可变,控件大小随着QWidget适应;
1.头文件
代码如下(示例):
protected:
void paintEvent(QPaintEvent *);
void drawBg(QPainter *painter);
void drawProgress(QPainter *painter);
void drawRulerTop(QPainter *painter);
void drawRulerBottom(QPainter *painter);
private:
QPropertyAnimation *m_valueAnimation;
double minValue; //最小值
double maxValue; //最大值
qreal value; //当前值
int longStep; //长线条等分步长
int shortStep; //短线条等分步长
bool rulerTop; //刻度线在上面
bool isAdd; //是否为增加,默认为的增加
QColor bgColor; //背景颜色
QColor lineColor; //线条颜色
QColor progressColor; //进度颜色
public:
qreal getValue() const;
void setrulerTop(bool istop); //设定刻度线再上还是在下,默认是在上
void setValue(qreal v);
void setRange(int minValue, int maxValue);
void startAnimation();
void updateValue(double value);
void setBgColor(const QColor &bgColor); //设置背景颜色
void setLineColor(const QColor &lineColor); //设置线条颜色
void setProgressColor(const QColor &progressColor); //设置进度颜色
2.核心代码
绘制部分参考引用2代码,感谢刘典武老师的无私开源,有需要的可去做定制。
代码如下:
void HumidityProgress::startAnimation()
{
qreal startValue = value;
if(isAdd)
{
m_valueAnimation->setKeyValueAt(0.5, value+0.5);
m_valueAnimation->setKeyValueAt(1, value);
m_valueAnimation->setStartValue(startValue-0.5);
m_valueAnimation->start();
}
else{
m_valueAnimation->setKeyValueAt(0.5, value-0.5);
m_valueAnimation->setKeyValueAt(1, value);
m_valueAnimation->setStartValue(startValue+0.5);
m_valueAnimation->start();
}
}
void HumidityProgress::paintEvent(QPaintEvent *)
{
//绘制准备工作,启用反锯齿
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
//按照绘制顺序
drawBg(&painter);
drawProgress(&painter);
if (rulerTop) {
drawRulerTop(&painter);
} else {
drawRulerBottom(&painter);
}
}
void HumidityProgress::drawBg(QPainter *painter)
{
painter->save();
painter->setPen(lineColor);
painter->setBrush(bgColor);
painter->drawRect(rect());
painter->restore();
}
void HumidityProgress::drawProgress(QPainter *painter)
{
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(progressColor);
double length = width();
double increment = length / (maxValue - minValue);
double initX = (value - minValue) * increment;
QRect rect(0, 0, initX, height());
painter->drawRect(rect);
painter->restore();
}
void HumidityProgress::drawRulerTop(QPainter *painter)
{
painter->save();
painter->setPen(lineColor);
double initX = 0;
//绘制横向标尺上部分底部线
double initTopY = 0;
QPointF lineTopLeftPot = QPointF(initX, initTopY);
QPointF lineTopRightPot = QPointF(width() - initX, initTopY);
painter->drawLine(lineTopLeftPot, lineTopRightPot);
//绘制上部分及下部分横向标尺刻度
double length = width();
//计算每一格移动多少
double increment = length / (maxValue - minValue);
//长线条短线条长度
int longLineLen = 15;
int shortLineLen = 10;
//根据范围值绘制刻度值及刻度值 长线条需要移动10像素 短线条需要移动5像素
for (int i = minValue; i <= maxValue; i = i + shortStep) {
if (i % longStep == 0) {
QPointF topPot = QPointF(initX, initTopY);
QPointF bottomPot = QPointF(initX, initTopY + longLineLen);
painter->drawLine(topPot, bottomPot);
//第一个值和最后一个值不要绘制
if (i == minValue || i == maxValue) {
initX += increment * shortStep;
continue;
}
QString strValue = QString("%1").arg((double)i, 0, 'f', 0);
double textWidth = fontMetrics().width(strValue);
double textHeight = fontMetrics().height();
QPointF textPot = QPointF(initX - textWidth / 2, initTopY + textHeight + longLineLen);
painter->drawText(textPot, strValue);
} else {
if (i % (longStep / 2) == 0) {
shortLineLen = 10;
} else {
shortLineLen = 6;
}
QPointF topPot = QPointF(initX, initTopY);
QPointF bottomPot = QPointF(initX, initTopY + shortLineLen);
painter->drawLine(topPot, bottomPot);
}
initX += increment * shortStep;
}
painter->restore();
}
该处使用的url网络请求的数据。
2、竖起来的温度/湿度计控件
因为只做数据显示用,所以只需要向控件传数据即可。
控件是好看,但是大小不能改变,所以这里需要自己实现了,源码作者已经放上。
1.头文件
代码如下(示例):
#ifndef THERMOMETREDLG_H
#define THERMOMETREDLG_H
#include <QWidget>
#include <QPropertyAnimation>
#include <QPainter>
#include <QTimer>
class thermometreDlg : public QWidget
{
Q_OBJECT
Q_PROPERTY(qreal value READ getValue WRITE setValue) //声明属性
public:
explicit thermometreDlg(QWidget *parent = nullptr);
qreal getValue();
void setValue(qreal value);
void changeValue(qreal value);
protected:
void paintEvent(QPaintEvent *e);
public slots:
void startAnimation();
signals:
private:
qreal m_value;
qreal curValue;
int m_width;
QRectF m_rect;
int maxValue, minValue;
qreal m_radius;
QPropertyAnimation *m_valueAnimation;
void updateRect();
};
#endif // THERMOMETREDLG_H
2.实现
代码如下(示例):
#include "thermometredlg.h"
#include <QDebug>
thermometreDlg::thermometreDlg(QWidget *parent) : QWidget(parent)
{
m_width = 20;
maxValue = 100;
minValue = 0;
m_radius = 1.05;
m_value = 0;
curValue = m_value;
QTimer *at = new QTimer(this);
at->start(1000);
m_valueAnimation = new QPropertyAnimation(this, "value");
m_valueAnimation->setDuration(1000);
m_valueAnimation->setEasingCurve(QEasingCurve::OutCubic);
m_valueAnimation->setLoopCount(1);
connect(at, SIGNAL(timeout()), this, SLOT(startAnimation()));
}
void thermometreDlg::updateRect()
{
m_rect.setX(0);
m_rect.setY(20 - height()/2);
m_rect.setWidth(m_width);
m_rect.setHeight(height() - 40 - m_width* m_radius);
}
void thermometreDlg::setValue(qreal value)
{
m_value = value;
update();
}
void thermometreDlg::changeValue(qreal value)
{
if(value > maxValue)
value = maxValue;
if(value < minValue)
value = minValue;
curValue = value;
}
qreal thermometreDlg::getValue()
{
return m_value;
}
void thermometreDlg::paintEvent(QPaintEvent *e)
{
updateRect();
QPainter painter(this);
QPen pen(Qt::black);
painter.translate(this->width()/2, this->height()/2); //坐标轴移动到中心点
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing); // 启用反锯齿
painter.save();
//绘制上方的柱状
painter.fillRect(m_rect, QColor(168,200, 225));
//绘制底部的圆
QRectF tmpRect = QRectF(m_rect.bottomLeft(), QPointF(m_width, height()/2- m_width*m_radius));
painter.fillRect(tmpRect, QColor(255, 0, 0));
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 0, 0));
painter.drawEllipse(tmpRect.bottomLeft()+QPointF(tmpRect.width()/2, 0),m_width*m_radius, m_width*m_radius);
painter.restore();
//绘制刻度以及刻度值
painter.save();
painter.setPen(QColor(Qt::black));
int nYCount = (maxValue - minValue)/10+1;
qreal perHeight = (m_rect.height())/nYCount;
for (int i=0; i<nYCount; ++i) {
QPointF basePoint = m_rect.bottomLeft() - QPointF(0, perHeight/2) - QPointF(0, perHeight*i);
//左侧大刻度
painter.drawLine(basePoint, basePoint+QPointF(-10, 0));
for (int j=1; j<10; ++j) {
if(i == nYCount -1)
continue;
painter.drawLine(basePoint-QPointF(0, perHeight/10*j),basePoint-QPointF(5, perHeight/10*j));
}
painter.drawText(basePoint+QPointF(-28, 4), QString("%1").arg(minValue+i*10));
//右侧大刻度
basePoint = m_rect.bottomRight() - QPointF(0, perHeight/2) - QPointF(0, perHeight*i);
painter.drawLine(basePoint, basePoint+QPointF(10, 0));
for (int j=1; j<10; ++j) {
if(i == nYCount -1)
continue;
painter.drawLine(basePoint-QPointF(0, perHeight/10*j),basePoint-QPointF(-5, perHeight/10*j));
}
}
painter.restore();
//根据值填充m_rect
qreal h = (m_value-minValue)/(maxValue-minValue)*(m_rect.height()-perHeight);
if(h<0)
h = 0;
if(h > m_rect.height())
h = m_rect.height();
painter.fillRect(m_rect.adjusted(0, m_rect.height()-h-perHeight/2-1 , 0, 0), QColor(255, 0, 0));
QWidget::paintEvent(e);
}
void thermometreDlg::startAnimation()
{
qreal startValue = getValue();
m_valueAnimation->setKeyValueAt(0, startValue-1);
m_valueAnimation->setKeyValueAt(0.5, curValue+1);
m_valueAnimation->setKeyValueAt(1, curValue);
m_valueAnimation->setStartValue(startValue-2);
m_valueAnimation->start();
}
3、引用
1、用qt实现一个温度计控件
2、Qt编写自定义控件2-进度条标尺
3、Qt动画框架:QPropertyAnimation(属性动画)