项目中需要使用QT进行窗口自绘,前期先做一下技术探索,参考相关资料代码熟悉流程。本着代码是最好的老师原则,在此记录一下。
目录
1.运行效果
2.代码结构
3.具体代码
myspeed.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
drawdialog.cpp \
main.cpp \
meter1.cpp \
meter2.cpp
HEADERS += \
drawdialog.h \
meter1.h \
meter2.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
drawdialog.h
#include
class DrawDialog : public QDialog
{
Q_OBJECT
public:
explicit DrawDialog(QWidget *parent = 0);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event) ;
void keyPressEvent( QKeyEvent * event );
private:
QPoint m_CurrentPos;
};
meter1.h
#ifndef METER1_H
#define METER1_H
#include
#include
// 仪表盘开发参考博客
// https://blog.csdn.net/yyz_1987/article/details/126958420#comments_29151980
class MyMeter1 : public QWidget
{
Q_OBJECT
public:
MyMeter1(QWidget *parent = 0);
~MyMeter1();
void setValue(double val);
protected:
void paintEvent(QPaintEvent *);
void drawCrown(QPainter *painter);
void drawBackground(QPainter *painter);
void drawScale(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawTitle(QPainter *painter);
void drawIndicator(QPainter *painter);
void drawNumericValue(QPainter *painter);
protected:
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
QPoint mousePoint;
bool mouse_press;
private:
QColor m_background;
QColor m_foreground;
int m_maxValue;
int m_minValue;
int m_startAngle;
int m_endAngle;
int m_scaleMajor;
int m_scaleMinor;
double m_value;
int m_precision;
QTimer *m_updateTimer;
QString m_units;
QString m_title;
public Q_SLOTS:
void UpdateAngle();
private:
};
#endif // METER1_H
meter2.h
#ifndef METER2_H
#define METER2_H
#include
class MyMeter2 : public QWidget
{
Q_OBJECT
public:
MyMeter2(QWidget *parent = nullptr);
~MyMeter2();
void paintEvent(QPaintEvent *event);
void timerEvent(QTimerEvent *e);
private:
void drawFrame(QPainter *painter);
void drawScale(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawPointer(QPainter *painter);
void drawSpeed(QPainter *painter);
void drawUnit(QPainter *painter);
int speed;
int time_id;
int status;
qreal m_angle;
QColor m_foreground;
int m_maxValue;
int m_minValue;
int m_startAngle;
int m_endAngle;
int m_scaleMajor;
int m_scaleMinor;
double m_value;
int m_precision;
//QTimer *m_updateTimer;
QString m_units;
QString m_title;
};
#endif // METER2_H
drawdialog.cpp
#include "drawdialog.h"
#include
#include
#include
DrawDialog::DrawDialog(QWidget *parent) : QDialog(parent)
{
//让程序无边框
setWindowFlags( Qt::FramelessWindowHint );
//让程序背景透明
setAttribute(Qt::WA_TranslucentBackground, true);
}
void DrawDialog::mousePressEvent(QMouseEvent *event)
{
//当鼠标左键按下时,记录当前位置
if(event->button() == Qt::LeftButton)
{
m_CurrentPos = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
QDialog::mousePressEvent(event);
}
void DrawDialog::mouseMoveEvent(QMouseEvent *event)
{
//支持窗体移动
if (event->buttons() & Qt::LeftButton)
{
move(event->globalPos() - m_CurrentPos);
event->accept();
}
QDialog::mouseMoveEvent(event);
}
//绘制图形
void DrawDialog::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
//反走样
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setPen( QPen(Qt::green, 2) );
painter.setBrush( Qt::blue );
QRect rect(10,10,200,260);
//绘制一个椭圆
painter.drawEllipse(rect);
}
void DrawDialog::keyPressEvent( QKeyEvent * event )
{
//按下esc键时,关闭
if(event->key() == Qt::Key_Escape)
{
close();
}
}
meter1.cpp
#include "meter1.h"
#include
// 构造函数
MyMeter1::MyMeter1(QWidget *parent){
m_background = Qt::black;
m_foreground = Qt::white;
m_startAngle = 60;
m_endAngle = 60;
m_scaleMajor = 10;
m_minValue = 0;
m_maxValue = 100;
m_scaleMajor = 10;//分度
m_scaleMinor = 10;
m_units = "L/min";
m_title = "仪表盘";
m_precision = 0;
m_value = 0;
mouse_press = false;
setWindowFlags(Qt::FramelessWindowHint);//无窗体
setAttribute(Qt::WA_TranslucentBackground);//背景透明
resize(500, 500);
}
MyMeter1::~MyMeter1()
{
}
void MyMeter1::setValue(double val)
{
m_value = val;
}
//绘制表冠
void MyMeter1::drawCrown(QPainter *painter)
{
painter->save();
int radius = 100;
QLinearGradient lg1(0, -radius, 0, radius);
lg1.setColorAt(0, Qt::white); //设置渐变的颜色和路径比例
lg1.setColorAt(1, Qt::gray); //只是粗略的颜色,具体的可以参考RGB颜色查询对照表
painter->setBrush(lg1); // 创建QBrush对象,把这个渐变对象传递进去:
painter->setPen(Qt::NoPen); //边框线无色
painter->drawEllipse(-radius, -radius, radius << 1, radius << 1);
painter->setBrush(m_background = Qt::black);
painter->drawEllipse(-92, -92, 184, 184);
painter->restore();
}
//绘制刻度数字
void MyMeter1::drawScaleNum(QPainter *painter)
{
painter->save();
painter->setPen(m_foreground);
//m_startAngle是起始角度,m_endAngle是结束角度,m_scaleMajor在一个量程中分成的刻度数
double startRad = ( 270-m_startAngle) * (3.14 / 180);
double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;
double sina,cosa;
int x, y;
QFontMetricsF fm(this->font());
double w, h, tmpVal;
QString str;
for (int i = 0; i <= m_scaleMajor; i++)
{
sina = sin(startRad - i * deltaRad);
cosa = cos(startRad - i * deltaRad);
tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;
// tmpVal = 50;
str = QString( "%1" ).arg(tmpVal); //%1作为占位符 arg()函数比起 sprintf()来是类型安全的
w = fm.size(Qt::TextSingleLine,str).width();
h = fm.size(Qt::TextSingleLine,str).height();
x = 82 * cosa - w / 2;
y = -82 * sina + h / 4;
painter->drawText(x, y, str); //函数的前两个参数是显示的坐标位置,后一个是显示的内容,是字符类型""
}
painter->restore();
}
// 绘制刻度线
void MyMeter1::drawScale(QPainter *painter) //绘制刻度线
{
painter->save();
painter->rotate(m_startAngle);
int steps = (m_scaleMajor * m_scaleMinor); //相乘后的值是分的份数
double angleStep = (360.0 - m_startAngle - m_endAngle) / steps; //每一个份数的角度
// painter->setPen(m_foreground); //m_foreground是颜色的设置
// QPen pen = painter->pen(); //第一种方法
QPen pen ;
pen.setColor(Qt::green); //推荐使用第二种方式
for (int i = 0; i <= steps; i++)
{
if (i % m_scaleMinor == 0)//整数刻度显示加粗
{
pen.setWidth(1); //设置线宽
painter->setPen(pen); //使用面向对象的思想,把画笔关联上画家。通过画家画出来
painter->drawLine(0, 62, 0, 72); //两个参数应该是两个坐标值
}
else
{
pen.setWidth(0);
painter->setPen(pen);
painter->drawLine(0, 67, 0, 72);
}
painter->rotate(angleStep);
}
painter->restore();
}
void MyMeter1::drawTitle(QPainter *painter)
{
painter->save();
painter->setPen(m_foreground);
//painter->setBrush(m_foreground);
QString str(m_title); //显示仪表的功能
QFontMetricsF fm(this->font());
double w = fm.size(Qt::TextSingleLine,str).width();
painter->drawText(-w / 2, -30, str);
painter->restore();
}
// 显示的单位,与数值
void MyMeter1::drawNumericValue(QPainter *painter)
{
QString str = QString("%1 %2").arg(m_value, 0, 'f', m_precision).arg(m_units);
QFontMetricsF fm(font());
double w = fm.size(Qt::TextSingleLine,str).width();
painter->setPen(m_foreground);
painter->drawText(-w / 2, 42, str);
}
void MyMeter1::UpdateAngle()
{
update();
}
// 绘制表针,和中心点
void MyMeter1::drawIndicator(QPainter *painter)
{
painter->save();
QPolygon pts;
pts.setPoints(3, -2, 0, 2, 0, 0, 60); /* (-2,0)/(2,0)/(0,60) *///第一个参数是 ,坐标的个数。后边的是坐标
painter->rotate(m_startAngle);
double degRotate = (360.0 - m_startAngle - m_endAngle) / (m_maxValue - m_minValue)*(m_value - m_minValue);
//画指针
painter->rotate(degRotate); //顺时针旋转坐标系统
QRadialGradient haloGradient(0, 0, 60, 0, 0); //辐射渐变
haloGradient.setColorAt(0, QColor(60, 60, 60));
haloGradient.setColorAt(1, QColor(160, 160, 160)); //灰
painter->setPen(Qt::white); //定义线条文本颜色 设置线条的颜色
painter->setBrush(haloGradient);//刷子定义形状如何填满 填充后的颜色
painter->drawConvexPolygon(pts); //这是个重载函数,绘制多边形。
painter->restore();
//画中心点
QColor niceBlue(150, 150, 200);
QConicalGradient coneGradient(0, 0, -90.0); //角度渐变
coneGradient.setColorAt(0.0, Qt::darkGray);
coneGradient.setColorAt(0.2, niceBlue);
coneGradient.setColorAt(0.5, Qt::white);
coneGradient.setColorAt(1.0, Qt::darkGray);
painter->setPen(Qt::NoPen); //没有线,填满没有边界
painter->setBrush(coneGradient);
painter->drawEllipse(-5, -5, 10, 10);
}
// 重绘函数
void MyMeter1 ::paintEvent(QPaintEvent *)
{
int width=this->width();
int height=this->height();
QPainter painter(this);//一个类中的this表示一个指向该类自己的指针
painter.setRenderHint(QPainter::Antialiasing); /* 使用反锯齿(如果可用) */
painter.translate(width/2, height/2); /* 坐标变换为窗体中心 */
int side = qMin(width, height);
painter.scale(side / 200.0, side / 200.0); /* 比例缩放 */
drawCrown(&painter); /* 画表盘边框 */
drawScaleNum(&painter); /* 画刻度数值值 */
drawScale(&painter); /* 画刻度线 */
drawTitle(&painter); /* 画单位 */
drawNumericValue(&painter); /* 画数字显示 */
drawIndicator(&painter); /* 画表针 */
}
void MyMeter1::mousePressEvent(QMouseEvent *event)
{
if( (event->button() == Qt::LeftButton) ){
mouse_press = true;
mousePoint = event->globalPos() - this->pos();
// event->accept();
}
else if(event->button() == Qt::RightButton){
//如果是右键
this->close();
}
}
void MyMeter1::mouseMoveEvent(QMouseEvent *event)
{
// if(event->buttons() == Qt::LeftButton){ //如果这里写这行代码,拖动会有点问题
if(mouse_press){
move(event->globalPos() - mousePoint);
// event->accept();
}
}
void MyMeter1::mouseReleaseEvent(QMouseEvent *event)
{
mouse_press = false;
}
meter2.cpp
#include "meter2.h"
#include
#include
#include
#include
#include
#include
#include
MyMeter2::MyMeter2(QWidget *parent)
: QWidget(parent)
{
//resize(800, 480);
//setWindowTitle("test");
m_foreground = Qt::red;//Qt::black;
speed = 0;
status = 0;
m_startAngle = 45;
m_endAngle = 45;
m_minValue = 0;
m_maxValue = 100;
m_scaleMajor = 10;//分度
m_scaleMinor = 10;
m_units = "KM/H";
m_title = "My Speed";
m_precision = 0;
m_value = 0;
m_angle = (qreal)270/(m_maxValue-1);
//setWindowFlags(Qt::FramelessWindowHint);//无窗体
//setAttribute(Qt::WA_TranslucentBackground);//背景透明
time_id = this->startTimer(50);
}
MyMeter2::~MyMeter2()
{
}
void MyMeter2::paintEvent(QPaintEvent *event)
{
int width=this->width();
int height=this->height();
QPainter painter(this);
painter.translate(width/2, height/2);
int side = qMin(width, height);
painter.scale(side / 200.0, side / 200.0); /* 比例缩放 */
drawFrame(&painter);
drawScale(&painter); /* 画刻度线 */
drawScaleNum(&painter); /* 画刻度数值值 */
drawUnit(&painter);
drawPointer(&painter);
drawSpeed(&painter);
}
void MyMeter2::drawFrame(QPainter *painter)
{
painter->save();
// 半径100
int radius = 100;
painter->setBrush(QBrush(QColor(0, 255, 0, 255), Qt::SolidPattern));
painter->drawArc(-radius, -radius, radius<<1, radius<<1, -135*16, -270*16);
painter->restore();
}
// 绘制刻度线
void MyMeter2::drawScale(QPainter *painter)
{
painter->save();
painter->rotate(m_startAngle);
int steps = (m_scaleMajor * m_scaleMinor); //相乘后的值是分的份数
double angleStep = (360.0 - m_startAngle - m_endAngle) / steps; //每一个份数的角度
// painter->setPen(m_foreground); //m_foreground是颜色的设置
// QPen pen = painter->pen(); //第一种方法
QPen pen ;
pen.setColor(m_foreground); //推荐使用第二种方式
for (int i = 0; i <= steps; i++)
{
if (i % m_scaleMinor == 0)//整数刻度显示加粗
{
pen.setWidth(1); //设置线宽
painter->setPen(pen); //使用面向对象的思想,把画笔关联上画家。通过画家画出来
painter->drawLine(0, 90, 0, 100); //两个参数应该是两个坐标值
}
else
{
pen.setWidth(0);
painter->setPen(pen);
painter->drawLine(0, 95, 0, 100);
}
painter->rotate(angleStep);
}
painter->restore();
}
// 绘制刻度
void MyMeter2::drawScaleNum(QPainter *painter)
{
painter->save();
painter->setPen(m_foreground);
//m_startAngle是起始角度,m_endAngle是结束角度,m_scaleMajor在一个量程中分成的刻度数
double startRad = ( 270-m_startAngle) * (3.14 / 180);
double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;
double sina,cosa;
int x, y;
QFontMetricsF fm(this->font());
double w, h, tmpVal;
QString str;
for (int i = 0; i <= m_scaleMajor; i++)
{
sina = sin(startRad - i * deltaRad);
cosa = cos(startRad - i * deltaRad);
tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;
// tmpVal = 50;
str = QString( "%1" ).arg(tmpVal); //%1作为占位符 arg()函数比起 sprintf()来是类型安全的
w = fm.size(Qt::TextSingleLine,str).width();
h = fm.size(Qt::TextSingleLine,str).height();
x = 82 * cosa - w / 2;
y = -82 * sina + h / 4;
painter->drawText(x, y, str); //函数的前两个参数是显示的坐标位置,后一个是显示的内容,是字符类型""
}
painter->restore();
}
void MyMeter2::drawPointer(QPainter *painter)
{
int radius = 100;
QPoint point[4] = {
QPoint(0, 10),
QPoint(-10, 0),
QPoint(0, -80),
QPoint(10, 0),
};
painter->save();
QLinearGradient linear;
linear.setStart(-radius, -radius);
linear.setFinalStop(radius<<1, radius<<1);
linear.setColorAt(0, QColor(0, 255, 255, 0));
linear.setColorAt(1, QColor(0, 255, 255, 255));
painter->setPen(Qt::NoPen);
painter->setBrush(linear);
painter->drawPie(-radius, -radius, radius<<1, radius<<1, 225 * 16, -(m_angle * this->speed) * 16);
painter->restore();
painter->save();
painter->setBrush(QBrush(QColor(0, 0, 0, 255), Qt::SolidPattern));
painter->rotate(-135 + this->speed * m_angle);
painter->drawPolygon(point, 4);
painter->restore();
}
void MyMeter2::drawSpeed(QPainter *painter)
{
painter->save();
painter->setPen(QColor("#0"));
// 绘制速度
QFont font("Times", 10, QFont::Bold);
font.setBold(true);
font.setPixelSize(46);
painter->setFont(font);
painter->drawText(-60, 0, 120, 92, Qt::AlignCenter, QString::number(speed));
painter->restore();
}
void MyMeter2::drawUnit(QPainter *painter)
{
QString str = QString("%1").arg(m_units);
QFontMetricsF fm(font());
double w = fm.size(Qt::TextSingleLine,str).width();
painter->setPen(m_foreground);
painter->drawText(-w / 2, 82, str);
}
void MyMeter2::timerEvent(QTimerEvent *e)
{
int timerId = e->timerId();
if(this->time_id == timerId) {
if(this->status == 0) {
this->speed += 1;
if(this->speed >= m_maxValue)
this->status = 1;
}else {
this->speed -= 1;
if(this->speed <= 0)
this->status = 0;
}
this->update();
}
}
main.cpp
#include "mainwindow.h"
#include "meter1.h"
#include "meter2.h"
#include "drawdialog.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyMeter1 w1;
w1.setValue(33.12);
w1.show();
MyMeter2 w2;
w2.show();
DrawDialog w3;
w3.show();
return a.exec();
}