Chinaunix首页 | 论坛 | 博客
  • 博客访问: 255874
  • 博文数量: 108
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 314
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-29 10:58
文章分类

全部博文(108)

文章存档

2015年(20)

2014年(88)

我的朋友

分类: 嵌入式

2014-09-29 08:45:54

原文地址:QT 曲线绘制 作者:luozhiyong131


点击(此处)折叠或打开

  1. //*******************************************************
  2. // 曲线绘制类
  3. //*******************************************************
  4. // PROGRAM NAME : plotter.cpp
  5. // Now Version : 1.0
  6. // Description :
  7. //=======================================================
  8. // 版权所有 (c) 2009 南京六都科技有限公司
  9. //*******************************************************

  10. #include <QtGui>
  11. #include <cmath>
  12. #include <QMapIterator>
  13. #include <QVector>
  14. #include "math.h"
  15. //using namespace std;
  16. #include "plotter.h"
  17. //构造
  18. Plotter::Plotter(int fv_margin, QWidget *parent) : QWidget(parent)
  19. {
  20.     Margin = fv_margin;
  21.     setBackgroundRole(QPalette::Dark);//设置曲线图使用”暗“分量显示
  22.     setAutoFillBackground(true);//设置是否自动填充背景色
  23.     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);//设置窗口不见可缩放,在X,Y方向均如此
  24.     setFocusPolicy(Qt::StrongFocus);//设置焦点
  25.     rubberBandIsShown = false;//鼠标圈住的橡皮筋区域是否显示

  26.     zoomInButton = new QToolButton(this);//创建一个放大按钮
  27.     zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
  28.     zoomInButton->adjustSize();
  29.     connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));//设置信号和槽的连接

  30.     zoomOutButton = new QToolButton(this);//创建一个缩小按钮
  31.     zoomOutButton->setIcon(QIcon(":/images/zoomout.png"));
  32.     zoomOutButton->adjustSize();
  33.     connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));//设置信号和槽的连接

  34.     BeginTime="";
  35.     
  36.     setPlotSettings(PlotSettings());
  37. }

  38. //析构
  39. Plotter::~Plotter()
  40. {
  41.     delete zoomInButton;
  42.     delete zoomOutButton;
  43. }

  44. //图形绘制设置
  45. void Plotter::setPlotSettings(const PlotSettings &settings)
  46. {
  47.     zoomStack.clear();//清空堆栈
  48.     zoomStack.append(settings);//重新加载绘图设置
  49.     curZoom = 0;//默认缩放级别是0,未缩放
  50.     zoomInButton->hide();//隐藏放大按钮
  51.     zoomOutButton->hide();//隐藏缩小按钮
  52.     refreshPixmap();//刷新图像区域
  53. }

  54. //图像缩小的槽
  55. void Plotter::zoomOut()
  56. {
  57.     if (curZoom > 0)//当前图像经过了放大
  58.     {
  59.         --curZoom;
  60.         //检测当前缩放级别,若放大,则缩小按钮可用,反之,不可用
  61.         zoomOutButton->setEnabled(curZoom > 0);
  62.         zoomInButton->setEnabled(true);//设置放大按钮可用
  63.         zoomInButton->show();//显示放大按钮
  64.         refreshPixmap();//刷新图像区域
  65.     }
  66. }

  67. //图像放大的槽
  68. void Plotter::zoomIn()
  69. {
  70.     if (curZoom < zoomStack.count() - 1)//当前缩放级别小于可放大的最大级别
  71.     {
  72.         ++curZoom;
  73.         //检测是否已经到最高放大级别,若是,放大按钮不可用,若不是,放大按钮可用
  74.         zoomInButton->setEnabled(curZoom < zoomStack.count() - 1);
  75.         zoomOutButton->setEnabled(true);//将缩小按钮设置为可用
  76.         zoomOutButton->show();//显示缩小按钮
  77.         refreshPixmap();//刷新图像区域
  78.     }
  79. }

  80. //设置给定曲线的ID号,若存在,则替换原数据,若不存在,则插入新曲线
  81. void Plotter::setCurveData(int id, const QVector<QPointF> &data)
  82. {
  83.     curveMap[id] = data;//成员类型为QMap>
  84.     refreshPixmap();//刷新图像
  85. }

  86. //从curveMap中移除给定ID号的曲线
  87. void Plotter::clearCurve(int id)
  88. {
  89.     curveMap.remove(id);
  90.     refreshPixmap();
  91. }

  92. //指定窗口部件理想的最小大小
  93. QSize Plotter::minimumSizeHint() const
  94. {
  95.     return QSize(6 * Margin, 4 * Margin);
  96. }

  97. //指定窗口部件理想大小
  98. QSize Plotter::sizeHint() const
  99. {
  100.     return QSize(12 * Margin, 8 * Margin);
  101. }

  102. //将refreshPixmap中绘制好的图像,复制到窗口部件的(0,0)位置
  103. void Plotter::paintEvent(QPaintEvent * /* event */)
  104. {
  105.     QStylePainter painter(this);
  106.     painter.drawPixmap(0, 0, pixmap);
  107.     if (rubberBandIsShown)//橡皮筋选择框显示中
  108.     {
  109.         painter.setPen(palette().light().color());
  110.         //橡皮筋选择区域显示亮背景,以区别于整个图片区域
  111.         painter.drawRect(rubberBandRect.normalized().adjusted(0, 0, -1, -1));//确保
  112.     }
  113.     if (hasFocus())//判断绘图区域是否拥有焦点
  114.     {
  115.         QStyleOptionFocusRect option;//焦点区域的风格设置
  116.         option.initFrom(this);//按照控件风格初始化焦点框
  117.         option.backgroundColor = palette().dark().color();//设置焦点框颜色
  118.         painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);//控制焦点框,背景,曲线图等控件
  119.     }
  120. }

  121. //当窗口部件大小改变的时候,通过该函数实现绘图区域的大小重定义
  122. void Plotter::resizeEvent(QResizeEvent * /* event */)
  123. {
  124.     //将缩放按钮放在窗口的右上角
  125.     int x = width() - (zoomInButton->width() + zoomOutButton->width() + 10);
  126.     zoomInButton->move(x, 5);
  127.     zoomOutButton->move(x + zoomInButton->width() + 5, 5);
  128.     refreshPixmap();
  129. }

  130. //鼠标左键按下事件
  131. void Plotter::mousePressEvent(QMouseEvent *event)
  132. {
  133.     QRect rect(Margin, Margin,width() - 2 * Margin, height() - 2 * Margin);
  134.     //左键按下
  135.     if (event->button() == Qt::LeftButton)
  136.     {//显示橡皮筋小框框
  137.         if (rect.contains(event->pos()))
  138.         {
  139.             rubberBandIsShown = true;
  140.             rubberBandRect.setTopLeft(event->pos());
  141.             rubberBandRect.setBottomRight(event->pos());
  142.             updateRubberBandRegion();
  143.             setCursor(Qt::CrossCursor);//设置鼠标形状
  144.         }
  145.     }
  146. }

  147. //鼠标移动事件
  148. void Plotter::mouseMoveEvent(QMouseEvent *event)
  149. {
  150.     if (rubberBandIsShown)//鼠标左键已经按下
  151.     {
  152.         updateRubberBandRegion();//更新橡皮筋的大小
  153.         rubberBandRect.setBottomRight(event->pos());
  154.         updateRubberBandRegion();
  155.     }
  156. }

  157. //鼠标释放事件
  158. void Plotter::mouseReleaseEvent(QMouseEvent *event)
  159. {
  160.     //鼠标圈出橡皮筋并且释放
  161.     if ((event->button() == Qt::LeftButton) && rubberBandIsShown)
  162.     {
  163.         rubberBandIsShown = false;//橡皮筋不再显示
  164.         updateRubberBandRegion();//更新橡皮筋
  165.         unsetCursor();//改变鼠标形状

  166.         QRect rect = rubberBandRect.normalized();//取得橡皮筋区域
  167.         if (rect.width() < 4 || rect.height() < 4)//橡皮筋区域小于4X4不作任何操作
  168.             return;
  169.         rect.translate(-Margin, -Margin);//移动画布
  170.         PlotSettings prevSettings = zoomStack[curZoom]; //更改缩放级别
  171.         PlotSettings settings;
  172.         //更改坐标轴长度
  173.         double dx = prevSettings.spanX() / (width() - 2 * Margin);
  174.         double dy = prevSettings.spanY() / (height() - 2 * Margin);
  175.         settings.minX = prevSettings.minX + dx * rect.left();
  176.         settings.maxX = prevSettings.minX + dx * rect.right();
  177.         settings.minY = prevSettings.maxY - dy * rect.bottom();
  178.         settings.maxY = prevSettings.maxY - dy * rect.top();
  179.         settings.adjust();
  180.         //将当前缩放大小压栈并完成放大
  181.         zoomStack.resize(curZoom + 1);
  182.         zoomStack.append(settings);
  183.         zoomIn();
  184.     }
  185. }

  186. //处理键盘按键事件
  187. void Plotter::keyPressEvent(QKeyEvent *event)
  188. {
  189.     switch(event->key())
  190.     {
  191.     case Qt::Key_Plus: //+号键
  192.         zoomIn();
  193.         break;
  194.     case Qt::Key_Minus: //-号键
  195.         zoomOut();
  196.         break;
  197.     case Qt::Key_Left: //方向键左
  198.         zoomStack[curZoom].scroll(-1,0);
  199.         refreshPixmap();
  200.         break;
  201.     case Qt::Key_Right: //方向键右
  202.         zoomStack[curZoom].scroll(+1,0);
  203.         refreshPixmap();
  204.         break;
  205.     case Qt::Key_Down: //方向键下
  206.         zoomStack[curZoom].scroll(0,-1);
  207.         refreshPixmap();
  208.         break;
  209.     case Qt::Key_Up: //方向键上
  210.         zoomStack[curZoom].scroll(0,+1);
  211.         refreshPixmap();
  212.         break;
  213.     default:
  214.         QWidget::keyPressEvent(event);
  215.     }
  216. }

  217. //处理鼠标滚轮滚动事件
  218. void Plotter::wheelEvent(QWheelEvent *event)
  219. {
  220.     int numDegrees=event->delta()/8; //滚轮转动的距离等于角度的8倍
  221.     int numTicks=numDegrees/15; //滚轮步长为15
  222. //    if (event->orientation()==Qt::Horizontal) //滚轮水平滚动
  223.         zoomStack[curZoom].scroll(numTicks,0);
  224. //    else
  225. //        zoomStack[curZoom].scroll(0,numTicks); //滚轮垂直滚动
  226.     refreshPixmap();
  227. }

  228. //完成对橡皮筋的绘制,重绘,擦除
  229. void Plotter::updateRubberBandRegion()
  230. {
  231.     QRect rect=rubberBandRect.normalized();
  232.     update(rect.left(),rect.top(),rect.width(),1);
  233.     update(rect.left(),rect.top(),1,rect.height());
  234.     update(rect.left(),rect.bottom(),rect.width(),1);
  235.     update(rect.right(),rect.top(),1,rect.height());
  236. }

  237. //将绘图区重新绘制到内存的脱屏像素映射上,并更新
  238. void Plotter::refreshPixmap()
  239. {
  240.     //定义一个和绘图区一样大小的画布
  241.     pixmap=QPixmap(size());
  242.     pixmap.fill(this,0,0);
  243.     //从绘图区获取图形,并绘制到脱屏像素上,更新
  244.     QPainter painter(&pixmap);
  245.     painter.initFrom(this);
  246.     drawGrid(&painter);
  247.     drawCurves(&painter);
  248.     update();
  249. }

  250. //设置Y轴最大最小值和X轴点数
  251. void Plotter::setCorrds(double x,double y,int z,int hx)
  252. {
  253.     //调用函数,将按钮设置成给定的大小
  254.     setPlotSettings(PlotSettings(x,y,z,hx));
  255. }

  256. //设置起始刻度
  257. void Plotter::SetBeginTime(QString data)
  258. {
  259.     BeginTime=data;
  260. }

  261. //用来绘制坐标系后面的网格
  262. void Plotter::drawGrid(QPainter *painter)
  263. {    
  264.     QRect rect;
  265.     //取得绘图区域,大小要减去旁白
  266.     if (Margin==40)
  267.         rect=QRect(Margin+25,Margin,width()-2*Margin-10,height()-2*Margin);
  268.     else
  269.         rect=QRect(Margin+45,Margin,width()-2*Margin-50,height()-2*Margin);
  270.     if (!rect.isValid()) //获取失败
  271.         return;

  272.     //设置缩放级别,背景色,画笔颜色,画笔风格
  273.     PlotSettings settings=zoomStack[curZoom];
  274.     QPen quiteDark=palette().dark().color().light();
  275.     QPen light=palette().light().color();
  276.     light.setWidth(2);
  277.     QString str_tail,tempstr;
  278.     int int_head;
  279.     if (BeginTime!="")
  280.     {
  281.         tempstr=BeginTime;
  282.         str_tail=tempstr.left(2);
  283.         tempstr.remove(0,2);
  284.         int_head=tempstr.toInt();
  285.     }

  286.     //绘制X轴上的网格和坐标
  287.     for (int i=0;i<=settings.numXTicks;++i)
  288.     {
  289.         int x=rect.left()+(i*(rect.width()-1)/settings.numXTicks);
  290.         double label=settings.minX+(i*settings.spanX()/settings.numXTicks);
  291.         painter->setPen(quiteDark);
  292.         painter->drawLine(x,rect.top(),x,rect.bottom());
  293.         //横坐标只显示整数的坐标点,防止坐标过密,文字连在一起看不清
  294.         if ((int)label==label)
  295.         {
  296.             if (i!=settings.numXTicks)
  297.             {
  298.                 if (Margin==40)
  299.                     if (BeginTime=="")
  300.                         painter->drawText(x-Margin+15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,QString::number(label));
  301.                     else
  302.                     {
  303.                         int tempint;
  304.                         label=label+int_head;
  305.                         tempint=(int)label;
  306.                         tempint=tempint%24;
  307.                         tempstr=QString::number(tempint);
  308.                         tempstr.append(":");
  309.                         tempstr.append(str_tail);
  310.                         painter->drawText(x-Margin+15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,tempstr);
  311.                     }
  312.                 else
  313.                     if (BeginTime=="")
  314.                         painter->drawText(x-Margin-15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,QString::number(label));
  315.                     else
  316.                     {
  317.                         int tempint;
  318.                         label=label+int_head;
  319.                         tempint=(int)label;
  320.                         tempint=tempint%24;
  321.                         tempstr=QString::number(tempint);
  322.                         tempstr.append(":");
  323.                         tempstr.append(str_tail);
  324.                         painter->drawText(x-Margin-15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,tempstr);
  325.                     }
  326.             }
  327.             else
  328.             {
  329.                 int tempint;
  330.                 if (BeginTime!="")
  331.                 {                    
  332.                     label=label+int_head;                    
  333.                     tempstr.append(":");
  334.                     tempstr.append(str_tail);
  335.                     tempint=(int)label;
  336.                     tempint=tempint%24;
  337.                     tempstr=QString::number(tempint);
  338.                     tempstr.append(tr("(小时)"));                    
  339.                 }
  340.                 else
  341.                 {
  342.                     tempint=(int)label;
  343.                     tempstr=QString::number(tempint);
  344.                     tempstr.append(tr("(小时)"));    
  345.                 }
  346.                 if (Margin==40)
  347.                     painter->drawText(x-Margin+15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,tempstr);
  348.                 else
  349.                     painter->drawText(x-Margin-15,rect.bottom()+5,60,20,Qt::AlignHCenter|Qt::AlignTop,tempstr);
  350.             }
  351.         }
  352.     }

  353.     //绘制Y轴上的网格和坐标
  354.     for (int j=0;j<=settings.numYTicks;++j)
  355.     {
  356.         if (settings.numYTicks==0)
  357.             settings.numYTicks=10;
  358.         int y=rect.bottom()-(j*(rect.height()-1)/settings.numYTicks);
  359.         double label=settings.minY+(j*settings.spanY()/settings.numYTicks);
  360.         painter->setPen(quiteDark);
  361.         painter->drawLine(rect.left()-5,y,rect.right(),y);
  362.         QString tempstr;
  363.         if (Margin==40)
  364.         {
  365.             tempstr=QString::number(label,'f');
  366.             if (tempstr.length()>6)
  367.                 tempstr=tempstr.left(6);
  368.         }
  369.         else
  370.             tempstr=QString::number(label);
  371.         painter->drawText(rect.left()-Margin-20,y-10,Margin+10,20,Qt::AlignRight|Qt::AlignVCenter,tempstr);
  372.     }
  373.     tempstr=str_Y;
  374.     painter->drawText(rect.left()-Margin-20,rect.top()-25,Margin+20,20,Qt::AlignRight|Qt::AlignVCenter,tempstr);
  375.     tempstr=str_Name;
  376.     painter->drawText(rect.left(),rect.top()-25,rect.right()-rect.left(),20,Qt::AlignCenter|Qt::AlignVCenter,tempstr);
  377. }

  378. //绘制曲线
  379. void Plotter::drawCurves(QPainter *painter)
  380. {
  381.     //定义一个枚举类型,存储曲线颜色
  382.     static const QColor colorForIds[10]={Qt::red,Qt::green,Qt::blue,Qt::cyan,Qt::magenta,Qt::yellow,Qt::darkBlue,Qt::darkGreen,Qt::darkRed,Qt::white};
  383.     //获得图形设置参数
  384.     PlotSettings settings=zoomStack[curZoom];
  385.     QRect rect;
  386.     //获得绘图区域
  387.     if (Margin==40)
  388.         rect=QRect(Margin+25,Margin,width()-2*(Margin+5),height()-2*Margin);
  389.     else
  390.         rect=QRect(Margin+45,Margin,width()-2*(Margin+25),height()-2*Margin);
  391.     if (!rect.isValid())//获取绘图区域失败
  392.         return;
  393.     painter->setClipRect(rect.adjusted(+1,+1,-1,-1));//设置Painter的操作区域
  394.     //获得存储曲线坐标的容器
  395.     QMapIterator<int,QVector<QPointF> > i(curveMap);

  396.     //遍历每条曲线
  397.     while (i.hasNext())
  398.     {
  399.         i.next();
  400.         int id=i.key();
  401.         QVector<QPointF> data=i.value();
  402.         QPolygonF polyline(data.count());
  403.         //遍历每个坐标点
  404.         for (int j=0;j<data.count();++j)
  405.         {
  406.             double dx=data[j].x()-settings.minX;
  407.             double dy=data[j].y()-settings.minY;
  408.             double x=rect.left()+(dx*(rect.width()-1)/settings.spanX());
  409.             double y=rect.bottom()-(dy*(rect.height()-1)/settings.spanY());
  410.             polyline[j]=QPointF(x,y);
  411.         }
  412.         //设置曲线颜色
  413.         painter->setPen(colorForIds[uint(id)%10]);
  414.         painter->drawPolyline(polyline);
  415.     }
  416. }

  417. //设置曲线名称及Y轴单位
  418. void Plotter::setTyep(QString strname,QString strY)    
  419. {
  420.     str_Name=strname;
  421.     str_Y=strY;
  422. }

  423. PlotSettings::PlotSettings()
  424. {
  425.     minX = 0.0;
  426.     maxX = 10.0;
  427.     numXTicks = 5;

  428.     minY = 0.0;
  429.     maxY = 10.0;
  430.     numYTicks = 5;
  431. }

  432. //设置坐标系的坐标轴长度,x和y分别是负荷曲线值的最大最小值,num是某一天的预测点个数
  433. PlotSettings::PlotSettings(double x,double y,int num,int hx)
  434. {
  435.     minX=0.0;
  436.     maxX=hx;
  437.     numXTicks=num;

  438.     minY=y;
  439.     maxY=x;
  440.     if (ceil(x-y)<=10)
  441.         numYTicks=ceil(x-y);
  442.     else
  443.         numYTicks=10;
  444. }

  445. //使用两个标记之间的距离,乘以给定的数字来增加减少minX,maxX,minY,maxY
  446. //该功能主要用于鼠标滚轮滚动过程
  447. void PlotSettings::scroll(int dx,int dy)
  448. {
  449.     double stepX=spanX()/numXTicks;
  450.     minX=minX+dx*stepX;
  451.     maxX=maxX+dx*stepX;
  452.     double stepY=spanY()/numYTicks;
  453.     minY=minY+dy*stepY;
  454.     maxY=maxY+dy*stepY;
  455. }

  456. //当橡皮筋释放之后,用来调整坐标轴上的刻度和坐标长度
  457. void PlotSettings::adjust()
  458. {
  459.     //调用私有函数处理单个坐标轴
  460.     adjustAxis(minX,maxX,numXTicks);
  461.     adjustAxis(minY,maxY,numYTicks);
  462. }

  463. void PlotSettings::adjustAxis(double &min, double &max, int &numTicks)
  464. {
  465.     const int MinTicks = 4;//设置最小的刻度
  466.     //简单确定两个坐标点之间的距离,下称步长
  467.     double grossStep = (max - min) / MinTicks;
  468.     //采用10的n次,2X10的n次的形式表示步长
  469.     double step = std::pow(10.0, std::floor(std::log10(grossStep)));
  470.     //确定最合适的步长
  471.     if (5 * step < grossStep)
  472.     {
  473.         step *= 5;
  474.     }
  475.     else if (2 * step < grossStep)
  476.     {
  477.         step *= 2;
  478.     }
  479.     //确定坐标刻度点数
  480.     numTicks = int(std::ceil(max / step) - std::floor(min / step));
  481.     if (numTicks < MinTicks)
  482.         numTicks = MinTicks;
  483.     //得到坐标轴距离
  484.     min = std::floor(min / step) * step;
  485.     max = std::ceil(max / step) * step;
  486. }

源码: plotter.rar   

阅读(1662) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~