目录

  • 背景
  • 闲聊
  • 效果
  • 源码
    • 八卦图
      • 头文件
      • 实现文件
    • 测试文件
      • 头文件
      • 实现文件
      • main文件

背景

这几天自己绘制了一些小部件,主要就是重写paintEvent()函数,这其中会涉及到坐标系相关内容,这次以绘制八卦图为契机,了解并掌握绘制事件中的坐标平移与旋转。
希望这个例子能起到抛砖引玉的作用,同时也希望对Qt有兴趣的伙伴们一起进步。

闲聊

“易有太极,是生两仪,两仪生四象,四象生八卦。”
大家对这句话应该都挺熟,我就不多说了。
八卦即八个卦相,分别为乾、坤、震、艮、离、坎、兑、巽。
八卦图分为伏羲八卦与文王八卦,也叫先天八卦与后天八卦。
这些大家应该也很熟,我就不卖弄啦。

效果

刚开始没有发现两条鱼的界限部分绘制反了,应该是反S形的,结果绘制成了S形了。道法自然,赶紧改正。
修改前的效果:

修改后的效果:

源码

八卦图

头文件

yinyang.h

/******************************************************************************** @brief        the Eight Diagrams* @details      通过绘制阴阳八卦,掌握对坐标系的平移与旋转* @author       wujz* @date         2020-10-22 09:56:36* @version      1.0.0* @par Copyright (c): * @par History:
*******************************************************************************/
#ifndef YINYANG_H
#define YINYANG_H#include <QWidget>class Yinyang : public QWidget
{Q_OBJECT
public:/* 八卦样式 */enum class EC_STYLE{XIAN_TIAN,      /* 伏羲先天八卦 */HOU_TIAN        /* 文王后天八卦 */};/* 爻 */enum class EC_YAO {YANG_YAO,       /* 阳爻 */YIN_YAO         /* 阴爻 */};/* 卦 */enum class EC_DIAGRAMS {QIAN,           /* 乾三连 */KUN,            /* 坤六断 */ZHEN,           /* 震仰盂 */GEN,            /* 艮覆碗 */LI,             /* 离中虚 */KAN,            /* 坎中满 */DUI,            /* 兑上缼 */XUN             /* 巽下断 */};explicit Yinyang(QWidget *parent = nullptr);virtual ~Yinyang();void set_angle(int angle);void set_radius(int radius);void set_style(EC_STYLE style);void set_yang_yao_length(int len);void set_yao_height(int height);void set_yao_space(int space);protected:void paintEvent(QPaintEvent * event) override;void draw_yin_yang_yao(QPainter & painter, const QPointF & p1,const QPointF & p2, EC_YAO yao);                /* 绘制爻 */void draw_diagram(QPainter & painter, EC_DIAGRAMS diagram);   /* 绘制卦 */void draw_the_eight_diagrams(QPainter & painter);             /* 绘制八卦图 */void draw_tai_ji(QPainter & painter);                         /* 绘制太极图 */signals:private:EC_STYLE my_style;int my_angle;        /* 旋转的角度 */int my_radius;       /* 太极图半径 */QTimer * my_timer;   /* 让太极图动起来 */int my_yang_yao_len; /* 阳爻长度 */int my_yao_height;   /* 爻高度 */int my_yao_space;    /* 卦中爻间距 */
};#endif // YINYANG_H

实现文件

yinyang.cpp

#include <QPaintEvent>
#include <QPainter>
#include <QTimer>
#include "yinyang.h"Yinyang::Yinyang(QWidget *parent): QWidget(parent), my_style(EC_STYLE::XIAN_TIAN), my_angle(0), my_radius(100), my_yang_yao_len(70), my_yao_height(10), my_yao_space(10)
{my_timer = new QTimer(0);my_timer->setInterval(200);connect(my_timer, &QTimer::timeout, [=]{my_angle += 5;my_angle %= 360;update();});my_timer->start();
}Yinyang::~Yinyang()
{if (my_timer) {if (my_timer->isActive())my_timer->stop();delete my_timer;my_timer = nullptr;}
}void Yinyang::set_angle(int angle)
{my_angle = angle % 360;
}void Yinyang::set_radius(int radius)
{my_radius = radius;
}void Yinyang::set_style(Yinyang::EC_STYLE style)
{my_style = style;
}void Yinyang::set_yang_yao_length(int len)
{my_yang_yao_len = len;
}void Yinyang::set_yao_height(int height)
{my_yao_height = height;
}void Yinyang::set_yao_space(int space)
{my_yao_space = space;
}void Yinyang::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);/* 1. background */painter.setPen(Qt::NoPen);painter.setBrush(Qt::gray);painter.drawRect(rect());/* 将坐标原点移到窗口中间位置 */painter.translate(width()>>1, height()>>1);/* 2. the Eight Diagrams */draw_the_eight_diagrams(painter);/* 3. Taiji */draw_tai_ji(painter);
}/*** @brief Yinyang::draw_yin_yang_yao* 绘制爻* @param painter* @param p1   爻起点* @param p2   爻终点* @param yao  阴爻/阳爻* @remark 若yao为YIN_YAO,则p1,p2分别为阴爻前半部分的起点与终点*/
void Yinyang::draw_yin_yang_yao(QPainter &painter, const QPointF &p1,const QPointF &p2, Yinyang::EC_YAO yao)
{painter.save();painter.setPen(QPen(Qt::black, my_yao_height, Qt::SolidLine, Qt::FlatCap)); // do not cover the end of the linepainter.drawLine(p1, p2);if (EC_YAO::YIN_YAO == yao) {qreal yin_yao_len = my_yang_yao_len*3/7.0;qreal space = my_yang_yao_len/7.0;QPointF pstart(p1), pstop(p2);pstart.setX(p2.x() + space);pstop.setX(p2.x() + yin_yao_len + space);painter.drawLine(pstart, pstop);}painter.restore();
}/*** @brief Yinyang::draw_diagram* 绘制卦* @param painter* @param diagram 需要绘制的卦* @remark 根据需要绘制的卦,分别设置该卦的三个爻的起点,终点,属性,*         依据设置的样式,判定该卦的位置,设置相应的旋转角度*/
void Yinyang::draw_diagram(QPainter &painter, EC_DIAGRAMS diagram)
{painter.save();EC_YAO top = EC_YAO::YANG_YAO;        /* 三爻 */EC_YAO middle = EC_YAO::YANG_YAO;     /* 二爻 */EC_YAO bottom = EC_YAO::YANG_YAO;     /* 初爻 */QPointF p01, p02;                     /* 初爻起点与终点 */QPointF p11, p12;                     /* 二爻起点与终点 */QPointF p21, p22;                     /* 三爻起点与终点 */qint16 x(my_yang_yao_len>>1), y(my_yao_height + my_yao_space);qint16 twice_y(y<<1);qint16 triple_y(twice_y + y);qreal yin_yao_len = my_yang_yao_len*3/7.0; /* 设置阴爻间空隙为1/7阳爻长度 */qreal yin_yao_stop = x - yin_yao_len;/* 基于先天八卦的乾位旋转 */if (EC_DIAGRAMS::QIAN == diagram) { // 三连p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(x, -my_radius - triple_y);// topp11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(x, -my_radius - twice_y);  // middlep01 = QPointF(-x, -my_radius - y), p02 = QPointF(x, -my_radius - y);              // bottomif (EC_STYLE::HOU_TIAN == my_style)painter.rotate(135);}else if (EC_DIAGRAMS::KUN == diagram) { // 三断p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(-yin_yao_stop, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(-yin_yao_stop, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(-yin_yao_stop, -my_radius - y);top = middle = bottom = EC_YAO::YIN_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(180);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(45);}else if (EC_DIAGRAMS::DUI == diagram) { // 上缺p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(-yin_yao_stop, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(x, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(x, -my_radius - y);top = EC_YAO::YIN_YAO;middle = bottom = EC_YAO::YANG_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(-45);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(90);}else if (EC_DIAGRAMS::LI == diagram) { // 中虚p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(x, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(-yin_yao_stop, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(x, -my_radius - y);top = bottom = EC_YAO::YANG_YAO;middle = EC_YAO::YIN_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(-90);}else if (EC_DIAGRAMS::ZHEN == diagram) { // 仰盂p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(-yin_yao_stop, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(-yin_yao_stop, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(x, -my_radius - y);top = middle = EC_YAO::YIN_YAO;bottom = EC_YAO::YANG_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(-135);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(-90);}else if (EC_DIAGRAMS::XUN == diagram) { // 下断p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(x, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(x, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(-yin_yao_stop, -my_radius - y);top = middle = EC_YAO::YANG_YAO;bottom = EC_YAO::YIN_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(45);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(-45);}else if (EC_DIAGRAMS::KAN == diagram) { // 中满p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(-yin_yao_stop, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(x, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(-yin_yao_stop, -my_radius - y);middle = EC_YAO::YANG_YAO;top = bottom = EC_YAO::YIN_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(90);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(180);}else if (EC_DIAGRAMS::GEN == diagram) { // 覆碗p21 = QPointF(-x, -my_radius - triple_y), p22 = QPointF(x, -my_radius - triple_y);p11 = QPointF(-x, -my_radius - twice_y), p12 = QPointF(-yin_yao_stop, -my_radius - twice_y);p01 = QPointF(-x, -my_radius - y), p02 = QPointF(-yin_yao_stop, -my_radius - y);top = EC_YAO::YANG_YAO;middle = bottom = EC_YAO::YIN_YAO;if (EC_STYLE::XIAN_TIAN == my_style)painter.rotate(135);else if (EC_STYLE::HOU_TIAN == my_style)painter.rotate(-135);}else {painter.restore();return;}draw_yin_yang_yao(painter, p21, p22, top);draw_yin_yang_yao(painter, p11, p12, middle);draw_yin_yang_yao(painter, p01, p02, bottom);painter.restore();
}/* 绘制八卦 */
void Yinyang::draw_the_eight_diagrams(QPainter &painter)
{painter.save();draw_diagram(painter, EC_DIAGRAMS::QIAN);draw_diagram(painter, EC_DIAGRAMS::KUN);draw_diagram(painter, EC_DIAGRAMS::LI);draw_diagram(painter, EC_DIAGRAMS::KAN);draw_diagram(painter, EC_DIAGRAMS::DUI);draw_diagram(painter, EC_DIAGRAMS::XUN);draw_diagram(painter, EC_DIAGRAMS::ZHEN);draw_diagram(painter, EC_DIAGRAMS::GEN);painter.restore();
}/* 绘制太极图 */
void Yinyang::draw_tai_ji(QPainter &painter)
{painter.save();/* 旋转坐标系统 */painter.rotate(my_angle);/* 白鱼 */QPainterPath circle, tmp;circle.arcTo(-my_radius, -my_radius, my_radius<<1, my_radius<<1, 270, 180);tmp.addEllipse(QPointF(0, -my_radius>>1), my_radius>>1, my_radius>>1);circle -= tmp; /* 上半部减去半个圆 */tmp.clear();tmp.addEllipse(QPointF(0, my_radius>>1), my_radius>>1, my_radius>>1);circle += tmp; /* 下半部加上半个圆 */painter.fillPath(circle, Qt::white);tmp.clear();tmp.addEllipse(QPointF(0, my_radius>>1), my_radius>>2, my_radius>>2);painter.fillPath(tmp, Qt::black); /* 画上鱼眼 *//* 黑鱼 */circle.clear();circle.arcTo(-my_radius, -my_radius, my_radius<<1, my_radius<<1, 90, 180);tmp.clear();tmp.addEllipse(QPointF(0, -my_radius>>1), my_radius>>1, my_radius>>1);circle += tmp; /* 上半部加上半个圆 */tmp.clear();tmp.addEllipse(QPointF(0, my_radius>>1), my_radius>>1, my_radius>>1);circle -= tmp; /* 下半部减去半个圆 */painter.fillPath(circle, Qt::black);tmp.clear();tmp.addEllipse(QPointF(0, -my_radius>>1), my_radius>>2, my_radius>>2);painter.fillPath(tmp, Qt::white); /* 画上黑鱼的大白眼 */painter.restore();
}

测试文件

头文件

testtaiji.h

#ifndef TESTTAIJI_H
#define TESTTAIJI_H#include <QWidget>
#include <QGridLayout>
#include "yinyang.h"class TestTaiji : public QWidget
{Q_OBJECT
public:explicit TestTaiji(QWidget *parent = nullptr);virtual ~TestTaiji();signals:private:QGridLayout * my_gridlayout;Yinyang * my_yinyang_xian_tian;Yinyang * my_yinyang_hou_tian;
};#endif // TESTTAIJI_H

实现文件

#include "testtaiji.h"TestTaiji::TestTaiji(QWidget *parent) : QWidget(parent)
{my_yinyang_xian_tian = new Yinyang;my_yinyang_hou_tian = new Yinyang;my_yinyang_hou_tian->set_style(Yinyang::EC_STYLE::HOU_TIAN);my_gridlayout = new QGridLayout;my_gridlayout->addWidget(my_yinyang_xian_tian, 0, 0);my_gridlayout->addWidget(my_yinyang_hou_tian, 0, 1);setLayout(my_gridlayout);
}TestTaiji::~TestTaiji()
{if (my_yinyang_xian_tian) {delete my_yinyang_xian_tian;my_yinyang_xian_tian = nullptr;}if (my_yinyang_hou_tian) {delete my_yinyang_hou_tian;my_yinyang_hou_tian = nullptr;}if (my_gridlayout) {delete my_gridlayout;my_gridlayout = nullptr;}
}

main文件

#include <QApplication>
#include "testtaiji.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);TestTaiji ttj;ttj.show();return a.exec();
}

Qt 自绘控件练习 太极八卦图相关推荐

  1. Qt自绘控件之扇形统计图

    首先绘制区域扇形需要先注意一下几点: QPainter中绘制完整的圆等于5760(16 * 360),此处数值用于计算每一块扇形区域所显示的 需要了解一下扇形二等分线的计算方法 要注意做坐标原点转换 ...

  2. Qt界面美化-飞扬青云自绘控件插件的使用-避免采坑

    1.先上一个集成到QtCreater中的效果图: 2.飞扬青云自绘控件插件下载地址:https://gitcode.net/mirrors/feiyangqingyun/qucsdk?utm_sour ...

  3. QT中树控件QTreeView开发实例

    转自:http://mobile.51cto.com/symbian-268700.htm 本文讲解了QT中树控件QTreeView开发实例,对于QTreeView没有过多的讲解,那么不说废话了,看代 ...

  4. 虚拟桌面模拟查找点击自绘控件

    // VDesktopClick.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include <string> #inclu ...

  5. QT旋转按钮控件的实现

    目录 一.实现需求:鼠标hover时旋转 二.实现方式:计时器做动画 三.代码实现 QT旋转按钮控件的实现 一.实现需求:鼠标hover时旋转 需求是:实现类似于WINDOWS下,某些软件窗口关闭按钮 ...

  6. Qt Creator 窗体控件自适应窗口大小布局

    常见的软件窗口大小改变(最大化.手动改变时)需要窗口的部件能够自适应布局,而在Qt的应用程序界面设计中,对于像我一样的初学者如何实现窗口自适应调整还是要绕点弯路的.网上百度了很多,多数说的很含糊,还有 ...

  7. Qt QTableWidget表格控件的用法(非常详细)

    Qt QTableWidget表格控件的用法(非常详细) QTableWidget 是 Qt 提供的一种表格控件(如图 1 所示),类似于我们经常使用的 Excel 表格,可以将数据以表格的方式展示给 ...

  8. QT自定义Widget控件及其使用

    今天来给大家分享一下QT自定义widget控件及其使用,当ui设计器提供的界面不满足实际需求时,可以从QWidget继承自定义的界面组件.有两种方法一种是提升法,另一种是ui设计器自定义界面组件wid ...

  9. Qt 之 ActiveX控件跑官方例程记录

    转载自:Qt 之 ActiveX控件跑官方例程记录 通过自己的实践,补充了一些技术细节 目录(?)[-] 开发环境 基础知识了解 QT的ActiveX控件形式 in-process运行的ActiveX ...

  10. qt 容器组控件 Containers

    qt 容器组控件 Containers 控件名: Group Box:组合框,提供带有标题的组合框框架 Scroll Area:滚动区域 Tool Box:工具箱 Tab Widget:标签小部件 S ...

最新文章

  1. 和12岁小同志搞创客开发:手撕代码,做一款人体感应灯
  2. [C#]从URL中获取路径的最简单方法-new Uri(url).AbsolutePath
  3. poj1509最小表示法
  4. python versions compatibility_为什么在python中迭代一个字典时必须调用.iteritems()?...
  5. scala学习笔记-Map与Tuple(10)
  6. 剑指offer之partition算法
  7. jstl c:choose、c:when和c:otherwise标签的简单使用介绍
  8. (需求实战_进阶_02)SSM集成RabbitMQ 关键代码讲解、开发、测试
  9. ServerAlias指令
  10. Flume 开发者指南V1.5.2
  11. 测试开发之缺陷报告上篇
  12. 仿王者荣耀HTML示例代码
  13. linux 批量解压war,图文处理war包解压【搞定方式】
  14. cuda 10.1 下载
  15. Markdown字体,字号,颜色和背景色设置
  16. 弗兰克赫兹大物实验数据处理
  17. fftn N 维快速傅里叶变换(Matlab)
  18. 1.MySQL增删改查(初阶)
  19. 中冠百年|家庭理财投资必备资金规划有哪些
  20. 线上科技展厅vr全景展厅设计 广交会布展

热门文章

  1. network secruity studay day4
  2. CAM350 - 导出 DXF 文件
  3. 网站服务器入侵检查,如何检查服务器是否被入侵?
  4. 华硕计算机电源已连接未充电,笔记本电池显示“电源已接通,未充电”
  5. 阿里P7、P8、P9级别需要多少年的工作经验,还是因能力而定?
  6. 汽车CAN总线 CAN2.0
  7. 物联网的未来是什么样的
  8. 看网页的宽度设置应该基于1024以上了
  9. 微信公众号生成个性化推广二维码
  10. Android 音视频采集那些事