C# GDI 图表 chart 串口 示波器

1 篇文章 0 订阅
1 篇文章 0 订阅

作为嵌入式工程师在设计数字电源时需要将串口传上来的电压电流等数据通过图表方式实时显示在电脑上,刚开始使用第三方图表控件msChart,这个控件功能还是蛮强大的,该要的效果一般都能实现,但在数字电源开发这种应用场合该控件就不太合适了,因为以50Hz的交流电波形举例,1秒有100个周期变化再乘以每周期至少100个点,那1秒内就将有10000个数据需要在图表上显示出来,用第三方图表控件没有哪个有这么快的刷新速度。

怎么解决该问题呢?最好的方式是通过GDI绘制曲线图,因为只有自已绘制才能最大效率利用CPU来作图,而不是像第三方控件为一些无关紧要的任务浪费时间。

主要代码封装成Chart.dll中,外围使用非常简单
1、资源管理器中引用Chart.dll
2、using MyChart;
3、按顺序申明线段名称(例如:string[] Field = new string[] { “线a”, “线b” };
4、申明Chart对象(例如:Chart chart;)
5、构造Chart对象(例如:chart = new Chart(Field); )
6、调用方法将窗口显示出来(例如:chart.Show(); )
7、更新需要显示的数据到GDI图表中(例如:chart.Updata(temp);)
8、此时数据将显示到图表窗口中,右击鼠标有使用说明。

该Chart.dll不仅能显示曲线图还能将数据库存到Access数据库
本应用因绘图效率比一般图表控件高(如msChart等),所以较合适作为软件示波器用

范例工程使用说明视频:https://www.bilibili.com/video/BV1MZ4y1V74D/
范例工程下载地址1:https://download.csdn.net/download/iejinshan/10467169
范例工程下载地址2:https://pan.baidu.com/s/1wvh8554-NagXJacfjxp8LA?pwd=6666

代码和视频中有点忘记说明了,chart对象会有个Show方法,一定要调用一次该方法图表才会显示出来,默认图表是隐藏的,请您特别注意。

注意: 上述范例核心代码封装在dll文件中,并未开放全部源代码,介意的请勿下载,谢谢。

这里写图片描述
如下提供一个绘图类代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

using System.Threading;

public class GDIChart
  {
    Bitmap bmp;
    Graphics graphics;
    PictureBox pictureBox;
    DataGridView GridView1, GridView2;
    HScrollBar hScrollBar;

    public double Xoffset, Yoffset,Ymax,Ymin;
    int YMoveUpValue;   //向上偏移量
    int XMoveRightValue;//向右偏移量

    //double Xp;
    int NowID, RowSum, NowRow;
    //int Xwidth = 0;
    //int DotSumCopy;
    Point pCopy;
    string[] FieldName;
    //double[] mLabel;
    //int[] yLabel;
    int[,] XY;

    Point p;//得到鼠标xy位置
    
    int Help;

    public GDIChart(ref PictureBox bx, ref DataGridView dataGridView1, ref DataGridView dataGridView2,ref HScrollBar hscrollBar)
    {
      YMoveUpValue = 16;
      pictureBox = bx;
      GridView1 = dataGridView1;
      GridView2 = dataGridView2;
      hScrollBar = hscrollBar;

      FieldName = new string[GridView1.Rows.Count];
      //mLabel = new double[GridView1.Rows.Count];
      //yLabel = new int[GridView1.Rows.Count];
      XY = new int[GridView1.Rows.Count,2];

      for (int i = 0; i < FieldName.Length; i++)
      {
        FieldName[i] = GridView1.Rows[i].HeaderCell.Value.ToString();
      }

      Ymax = 3600;
      Ymin = 0;

      Help = 0;

      pictureBox.Resize += new System.EventHandler(EventHandlerBmpInit);//pictureBox大小变化时产生事件重置Bitmap对象大小

      pictureBox.MouseDown += new MouseEventHandler(pictureBox_MouseDown);
      pictureBox.MouseWheel += new MouseEventHandler(PictureBox_MouseWheel);
      pictureBox.KeyDown += new KeyEventHandler(pictureBox_KeyDown);
      //pictureBox.MouseEnter += new EventHandler(pictureBox_MouseEnter);
      //pictureBox.MouseLeave += new EventHandler(pictureBox_MouseLeave);
    }
    private bool 打印字符_带背景色(ref Graphics g, ref Bitmap bmp,Font font, int x, int y, string str)
    {
      //Font font = new Font("宋体", 9f);
      PointF pointF = new PointF(x, y);
      SizeF sizeF = g.MeasureString(str, font);
      g.FillRectangle(Brushes.PowderBlue, new RectangleF(pointF, sizeF));
      g.DrawString(str, font, Brushes.Black, pointF);

      return true;
    }

    //去除小数点后多余数值
    private bool RemoveDecimal(double In, ref double Out)
    {
      try
      {
        if (In == 0) { return true; }
        
        if (In >= 10 || In <= -10)
        {//In有整数
          Out = (double)((int)In);
          Out += 0;
        }
        else
        {//In无整数
          int cnt = 1;
          if (In > 0)
          {//正数
            while (In < 10)
            {
              cnt *= 10;
              In *= 10;
            }
          }
          else
          {//负数
            while (In < -10)
            {
              cnt *= 10;
              In *= 10;
            }
          }
          Out = (double)(int)(In+0) / cnt;
        }
        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("RemoveDecimal " + ex.Message.ToString()); }
      return false;
    }

    //初始化参数
    private bool Init(ref DataTable dt,int DotSum,double y1,double y2)
    {
      try
      {
        Xoffset = (double)(bmp.Width - XMoveRightValue) / DotSum;  //计算出X轴每个像素值
        Yoffset = (y2 - y1) / (double)(bmp.Height - YMoveUpValue);       //计算出Y轴每个像素值

        

        RemoveDecimal(Xoffset, ref Xoffset);//去除小数点后多余数值
        RemoveDecimal(Yoffset, ref Yoffset);//去除小数点后多余数值

        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("ChartInit " + ex.Message.ToString()); }
      return false;
    }

    //绘制曲线
    private bool DrawingCurve(ref DataTable dt,DataGridView gridView,Color[] color,double Ystart)
    {
      try
      { 
        Pen pen;
        pen = new Pen(System.Drawing.Color.Red, 1);
        int x1, y1, x2=0, y2,flag;

        p = pictureBox.PointToClient(Control.MousePosition);//得到鼠标xy位置
        p.X -= XMoveRightValue;
        p.Y += YMoveUpValue;

        graphics.TranslateTransform(XMoveRightValue, -YMoveUpValue);
        for (int a = 0; a < gridView.Rows.Count;a++ )
        {
          if (Convert.ToBoolean(gridView.Rows[a].Cells["Enable"].Value) == true)
          {
            flag = 0;
            pen.Color = color[a];
            
            for (int b = 0; b < dt.Rows.Count-1;b++)
            {
              double value0 = Convert.ToDouble(dt.Rows[b + 0][a + 2]);
              double value1 = Convert.ToDouble(dt.Rows[b + 1][a + 2]);

              value0 -= Ystart;
              value1 -= Ystart;

              value0 += Convert.ToDouble(gridView.Rows[a].Cells["Move"].Value);
              value1 += Convert.ToDouble(gridView.Rows[a].Cells["Move"].Value);

              value0 *= Convert.ToDouble(gridView.Rows[a].Cells["Zoom"].Value);
              value1 *= Convert.ToDouble(gridView.Rows[a].Cells["Zoom"].Value);

              x1 = (int)((b + 0) * Xoffset);
              x2 = (int)((b + 1) * Xoffset);
              y1 = bmp.Height - (int)(value0 / Yoffset);
              y2 = bmp.Height - (int)(value1 / Yoffset);
              
              graphics.DrawLine(pen, x1, y1, x2, y2);
              
              if (flag == 0 && x1 >= p.X) 
              {
                flag = 1;
                XY[a, 0] = x1;
                XY[a, 1] = y1;
              }

            }
          }
        }
        
        graphics.TranslateTransform(-XMoveRightValue, YMoveUpValue);
        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("DrawingCurve " + ex.Message.ToString()); }
      return false;
    }

    //绘制标签
    private bool DrawingLabel(ref DataTable dt,Color[] color)
    {
      try
      {
        if (p.X >= 0 && p.Y >= YMoveUpValue && p.X <= pictureBox.Width - XMoveRightValue && p.Y <= pictureBox.Height)
        {
          int x1, y1, x2, y2;
          Pen pen = new Pen(Color.Red, 1);
          pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

          graphics.TranslateTransform(XMoveRightValue, -YMoveUpValue);

          x1 = p.X;
          x2 = p.X;
          y1 = 3;
          y2 = pictureBox.Height;
          graphics.DrawLine(pen, x1, y1, x2, y2);

          x1 = 0;
          x2 = pictureBox.Width;
          y1 = p.Y;
          y2 = p.Y;
          graphics.DrawLine(pen, x1, y1, x2, y2);

          pen.Width = 2;
          int row = (int)((double)(p.X) / Xoffset) + 1;



          if (row >= 0 && row < dt.Rows.Count)
          {
            NowRow = row;
            RowSum = dt.Rows.Count;
            NowID = Convert.ToInt32(dt.Rows[row][0]);

            DataTable SetTable = (GridView1.DataSource as DataTable);
            if (pCopy != p)
            {
              for (int i = 0; i < dt.Columns.Count - 2; i++)
              {
                SetTable.Rows[i]["Value"] = Convert.ToDouble(dt.Rows[row][i + 2]);
                SetTable.Rows[i]["ID"] = Convert.ToDouble(dt.Rows[row][0]);
                SetTable.Rows[i]["Time"] = Convert.ToDateTime(dt.Rows[row][1]);
              }
              GridView1.Refresh();
            }

            string str;
            Font font = new Font("宋体", 10);
            int Height = font.Height / 2;
            for (int i = 0; i < dt.Columns.Count - 2; i++)
            {
              if (Convert.ToBoolean(GridView1.Rows[i].Cells["Enable"].Value) == true)
              {
                pen.Color = color[i];
                str = dt.Rows[row][i + 2].ToString();

                x1 = XY[i, 0];
                y1 = XY[i, 1];
                x2 = XY[i, 0] + 30;
                y2 = XY[i, 1];
                if (y2 < 15 + YMoveUpValue + Height) { y2 += 15; } else { y2 -= 15; }
                graphics.DrawLine(pen, x1, y1, x2, y2);
                打印字符_带背景色(ref graphics, ref bmp, font, x2, y2-Height, SetTable.Rows[i][0].ToString() + "->" + str);
              }
            }

          }
          else
          {
            NowID = 0;
            RowSum = 0;
            NowRow = 0;
          }
          graphics.TranslateTransform(-XMoveRightValue, YMoveUpValue);
        }
        else
        {
          NowID = 0;
          RowSum = 0;
          NowRow = 0;
        }
        pCopy = p;

        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("DrawingLabel " + ex.Message.ToString()); }
      return false;
    }

    //绘制help
    private bool DrawingHelp(double Ystart)
    {
      try
      {
        if (Help == 1)
        {
          p.Y = bmp.Height - p.Y;
          p.X = p.X - XMoveRightValue;

          string str = "";
          //str += "Yoffset=" + Yoffset.ToString() + "\n";
          //str += "Xoffset=" + Xoffset.ToString() + "\n";
          str += "Dot=" + sys.DotSum.ToString() + "\n";
          //str += "Width=" + (pictureBox.Width - XMoveRightValue).ToString() + "\n";
          //str += "Row=" + sys.ShowDataTable.Rows.Count.ToString() + "\n";
          str += "Y=" + ((double)p.Y * Yoffset + Ystart).ToString() + "\n";
          //str += "X=" + p.X / Xoffset + "\n";
          打印字符_带背景色(ref graphics, ref bmp,new Font("宋体", 10), XMoveRightValue + 16, 0, str);
        }
        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("DrawingHelp " + ex.Message.ToString()); }
      return false;
    }

    //绘制刻度表
    private bool DrawingScale(double Ystart, ref DataTable dt)
    {
      try
      {
        int x1, y1, x2, y2;
        SizeF sf = new SizeF();

        //计算最大字符长度
        XMoveRightValue = 0;
        for (int y = YMoveUpValue; y < bmp.Height - 5; )
        {
          string str = (Yoffset * (y - YMoveUpValue) + Ystart).ToString();
          sf = graphics.MeasureString(str, new Font("宋体", 10));
          if (XMoveRightValue < sf.Width)
          {
            XMoveRightValue = (int)sf.Width;
          }
          y += 50;
        }
        //画Y轴刻度
        x1 = XMoveRightValue;
        y1 = bmp.Height - YMoveUpValue;
        x2 = XMoveRightValue;
        y2 = 2;
        graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画长线

        for (int y = YMoveUpValue; y < bmp.Height - 5; )
        {
          if ((y - YMoveUpValue) % 50 == 0)
          {
            x1 = XMoveRightValue;
            y1 = bmp.Height - y;
            x2 = XMoveRightValue + 10;
            y2 = bmp.Height - y;
            graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画刻度线max
            string str = (Yoffset * (y - YMoveUpValue) + Ystart).ToString();
            sf = graphics.MeasureString(str, new Font("宋体", 10));
            graphics.DrawString(str, new Font("宋体", 10), Brushes.Black, new PointF(XMoveRightValue - sf.Width, y1 - 8));
          }
          else
          {
            x1 = XMoveRightValue;
            y1 = bmp.Height - y;
            x2 = XMoveRightValue + 5;
            y2 = bmp.Height - y;
            graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画刻度线min
          }
          y += 5;
        }
        /**/
        //画X轴刻度
        x1 = XMoveRightValue;
        y1 = bmp.Height - YMoveUpValue;
        x2 = bmp.Width - 5;
        y2 = bmp.Height - YMoveUpValue;
        graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画长线

        for (int i = XMoveRightValue; i < bmp.Width - 10; )
        {
          if ((i - XMoveRightValue) % 50 == 0)
          {
            x1 = i;
            y1 = bmp.Height - YMoveUpValue - 10;
            x2 = i;
            y2 = bmp.Height - YMoveUpValue;
            graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画刻度线max

            int row = (int)((double)(i - XMoveRightValue) / Xoffset);
            if (dt.Rows.Count > row && row > 0 && sf.Width < 50)
            {
              string str = dt.Rows[row][0].ToString();
              sf = graphics.MeasureString(str, new Font("宋体", 10));
              graphics.DrawString(str, new Font("宋体", 10), Brushes.Black, new PointF(i - sf.Width / 2, bmp.Height - 12));
            }
            else
            {
              sf.Width = 0;
            }
          }
          else
          {
            x1 = i;
            y1 = bmp.Height - YMoveUpValue - 5;
            x2 = i;
            y2 = bmp.Height - YMoveUpValue;
            graphics.DrawLine(new Pen(Color.Black, 1), x1, y1, x2, y2);//画刻度线min
          }
          i += 5;
        }

        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("DrawingScale " + ex.Message.ToString()); }
      return false;
    }

    
    //int sum = 0,start = 0,stop = 0;
    public bool Updata(ref DataTable data, Color[] color, int DotSum)
    {
      try
      {
        Init(ref data, DotSum, Ymin, Ymax);

        DrawingCurve(ref data, GridView1, color, Ymin);

        DrawingLabel(ref data, color);

        DrawingHelp(Ymin);

        DrawingScale(Ymin, ref data);

        pictureBox.CreateGraphics().DrawImage(bmp, 0, 0);

        graphics.Clear(Color.White);//清缓存

        return true;
      }
      catch (Exception ex) { sys.ToolWindows.LogAdd("ChartUpdata " + ex.Message.ToString()); }
      return false;
    }

    //pictureBox大小变化时需要同时调整Bitmap对象大小
    private void EventHandlerBmpInit(object sender, EventArgs e)
    {
      bmp = new Bitmap(pictureBox.Width, pictureBox.Height);
      graphics = Graphics.FromImage(bmp);
    }
    //让PictureBox支持MouseWheel事件
    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
      pictureBox.Focus();
    }
    private void PictureBox_MouseWheel(object sender, MouseEventArgs e)
    {
      if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
      {
        double offset = (Ymax - Ymin) / pictureBox.Height;
        double Y = pictureBox.Height - e.Y;
        double UpWidth = (double)(pictureBox.Height - Y) * Yoffset * 0.2;
        double DownWidth = (double)(Y - 0) * Yoffset * 0.2;

        if (e.Delta > 0)
        {//Mouse Wheeled Up(缩小)
          Ymax -= UpWidth;
          Ymin += DownWidth;
        }
        else
        {//Mouse Wheeled Down(放大)
          Ymax += UpWidth;
          Ymin -= DownWidth;
        }
        if (Ymax < Ymin) { Ymax = Ymin + 10; }
        RemoveDecimal(Ymax, ref Ymax);
        RemoveDecimal(Ymin, ref Ymin);
        //Console.WriteLine("delte="+e.Delta.ToString() + ", X=" + e.X.ToString() + ", Y=" + e.Y.ToString()); 
      }
      else
      {
        if (e.Delta > 0)
        {
          sys.DotSum = (int)((double)sys.DotSum * 0.8);
        }
        else
        {
          sys.DotSum = (int)((double)sys.DotSum * 1.2);
        }
        if (sys.DotSum < 10) { sys.DotSum = 10; }
        else if (sys.DotSum > 50000) { sys.DotSum = 50000; }

        int length = (int)((double)NowRow / RowSum * sys.DotSum);
        int value = NowID - length;

        if (value <= hScrollBar.Maximum && value > hScrollBar.Minimum)
        {
          hScrollBar.Value = value;
        }
        
      }
    }

    private void pictureBox_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.F1)
      {
        if (Help == 0) { Help = 1; } else { Help = 0; }
      }
      else if ((Control.ModifierKeys & Keys.Control) == Keys.Control && e.KeyCode == Keys.F2)
      {
        if (sys.ToolWindows.Visible == false)
        {
          sys.ToolWindows.Show();
        }
        else
        {
          sys.ToolWindows.Close();
        }
      }
    }


  }
  • 12
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值