⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chartrx.cpp

📁 实现波形的显示,可以作为示波器的一个模块
💻 CPP
📖 第 1 页 / 共 3 页
字号:
// ChartRx.cpp : implementation file
//

#include "stdafx.h"
#include "ChartRx.h"
#include <math.h>

#include "ChartSerial.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//#define DEBUG_OUTPUT
/////////////////////////////////////////////////////////////////////////////
// CChartRx

CChartRx::CChartRx()
{
	// 这里只初始化画图的基本参数
	pMemDC = NULL;
	pTempDC = NULL;
	m_pBackGround = NULL;
	m_pForeGround = NULL;
	m_pScreen = NULL;
	pBrush = NULL;
	crBackGround = RGB(0, 60, 0);
	crForeGround = RGB(220, 0, 0);

	SerialCount = 0;

	// 标记初始化示例数据Init(),因为用到了成员类,所以推迟:
	isFirstTime = TRUE; 
	//
	n_x_Format = 0; 
	n_y_Format = 3; 
	x_LabelFormat = "%.0f";		// x轴标签的格式化字符串
	y_LabelFormat = "%.3f";		// y轴标签的格式化字符串
	x_LabelFormatSel = "%.0f";
	y_LabelFormatSel = "%.3f";
	tip_x = "%.3f";				// 打标和鼠标位置信息的格式化信息
	tip_y = "%.3f";

	n_x_TickOneLabel = 1;      // 几个大刻度一个标签
	n_y_TickOneLabel = 1;
	n_x_TickOneLabelSel = 1;   // 几个大刻度一个标签
	n_y_TickOneLabelSel = 1;

	MajorTick = 4;
	MiniTick = 3;
	x_max = 100;
	x_min = 0;
	y_max = 2.;
	y_min = -2.0;

	// 图上,表格分割
	x_Divisions = 10;
	y_Divisions = 10;
	x_miniPerSec = 5;
	y_miniPerSec = 5;

	// 表格基本线的画笔
	x_Axis.SetStyle(1, PS_SOLID, RGB(0, 192, 0));         // x轴
	y_Axis.SetStyle(1, PS_SOLID, RGB(0, 192, 0));         // y轴
	h_Grid.SetStyle(1, PS_DOT, RGB(0, 192, 0));           // 横格线
	v_Grid.SetStyle(1, PS_DOT, RGB(0, 192, 0));           // 竖格线
	x_Degree.SetStyle(1, PS_SOLID, RGB(0, 192, 0));       // x轴上刻度
	y_Degree.SetStyle(1, PS_SOLID, RGB(0, 192, 0));       // y轴上刻度
	uptLine.SetStyle(1, PS_SOLID, RGB(255, 255, 0));   // 用户指定点的十字
	bHGrig = TRUE;
	bVGrid = TRUE;
	

	// 设置父,用来得到HDC
	userPointsLabel.pChart = this;   // 用户点的标签
	TitleLabel.pChart = this;
	x_AxisTitle.pChart = this;       // x轴标题
	y_AxisTitle.pChart = this;       // y轴标题
	x_AxisLabel.pChart = this;       // 不存储字符串,只存储颜色和字体
	y_AxisLabel.pChart = this;
	TipLabel.pChart = this;

	// 各个标题的字体和颜色
	TitleLabel.SetColor(RGB(255, 255, 0));
	x_AxisTitle.SetColor(RGB(255, 255, 0)); // 128
	y_AxisTitle.SetColor(RGB(255, 255, 0));
	x_AxisLabel.SetColor(RGB(255, 255, 0));
	y_AxisLabel.SetColor(RGB(255, 255, 0));

	// 位置设置相关参数
	TitleLabel.SetFontSize(11);
	x_AxisTitle.SetFontSize(9);
	y_AxisTitle.SetFontStyle(9, TRUE);
	TitleLabel.text = "ChartRx1.0 曲线图";
	x_AxisTitle.text = "X轴[单位]";
	y_AxisTitle.text = "Y轴[单位]";

	x_AxisLabel.SetFontSize(9);
	y_AxisLabel.SetFontSize(9);
	x_AxisLabel.text = "1234.567";   // 
	y_AxisLabel.text = "1234.567";   // 用来计算左边需要的宽度,4个汉字,保存最大一个

	
	TipLabel.text = "位置信息";
	TipLabel.SetFontSize(9);
	TipLabel.rt = CRect(0, 0, 0, 0);
	TipLabel.SetColor(RGB(255, 255, 255));

	// 用户指定点的标签
	userPointsLabel.text = "位置信息";
	userPointsLabel.SetFontSize(9);
	userPointsLabel.SetColor(RGB(255, 255, 0));
	userPointsLabel.rt = CRect(0, 0, 0, 0);


//	shouldReDrawChart = FALSE;       // 当y轴标签超过预期宽度时候,重新画背景
	isMagnified = FALSE;             // 已经放大了标记
	isMagnifying = FALSE;
	nChartType = 1;                  // 折线图
	AxisSecendVisible = TRUE;        // 是否要画辅助坐标轴
	
	bCanMagnify = TRUE;              //是否可以放大,直方不可以

	bShowTip = TRUE;

}

CChartRx::~CChartRx()
{
	if (m_pBackGround)
		m_pBackGround->DeleteObject();  
	if (pMemDC)
		pMemDC->DeleteDC();
	
	if (m_pBackGround)
		delete m_pBackGround;
	if (m_pScreen)
		delete m_pScreen;
	if (pMemDC)
		delete pMemDC;
	if (pTempDC)
		delete pTempDC;
	if (pBrush)
		delete pBrush;
}


BEGIN_MESSAGE_MAP(CChartRx, CStatic)
//{{AFX_MSG_MAP(CChartRx)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_KEYDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartRx message handlers

BOOL CChartRx::Init(CPaintDC * pDC)
{	
	// 初始化绘图资源,背景位图
	if (pMemDC == NULL)
	{
		pMemDC = new CDC();
		pMemDC->CreateCompatibleDC(NULL);  
	}	
	if (m_pBackGround == NULL)
	{
		GetClientRect(&rt_Back);
		m_pBackGround = new CBitmap();
		m_pBackGround->CreateCompatibleBitmap(pDC, rt_Back.Width(), rt_Back.Height()); 
	}
	if (pBrush)
	{
		pBrush = new CBrush();
		pBrush->CreateSolidBrush(crForeGround);
	}
	//////////////////////////////////////////////////////////////////////////
	// 初始化绘图资源,前景位图
	if (pTempDC == NULL)
	{
		pTempDC = new CDC();
		pTempDC->CreateCompatibleDC(NULL);
	}
	if (m_pForeGround == NULL)
	{
		m_pForeGround = new CBitmap();
		m_pForeGround->CreateCompatibleBitmap(pDC, rt_Back.Width(), rt_Back.Height());
	}
	if (m_pScreen == NULL)
	{
		m_pScreen = new CBitmap();
		m_pScreen->CreateCompatibleBitmap(pDC, rt_Back.Width(), rt_Back.Height()); 
	}

// 数据设置,以下部分也可以由用户来设置,然后调用initChart()

	x_minSel = x_min;
	x_maxSel = x_max;
	y_minSel = y_min;
	y_maxSel = y_max;

//	x_LabelFormat = "%.0f";   // x轴标签的格式化字符串
//	y_LabelFormat = "%.3f";   // y轴标签的格式化字符串


	ModifyStyle(this->m_hWnd, NULL, SS_NOTIFY, 0);
	hCurCross = ::LoadCursor(NULL, MAKEINTRESOURCE(IDC_CROSS));
	hCurHand = ::LoadCursor(NULL, MAKEINTRESOURCE(32649));//MAKEINTRESOURCE(IDC_APPSTARTING)

	// 标记计算各个部分的位置,调用InitChart()
	isIniting = TRUE;  
	return TRUE;
}

void CChartRx::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	if (isFirstTime)     // 推迟的初始化,只做一次!
	{
		Init(&dc);          // 默认的初始化参数设置
		isFirstTime = FALSE;
		shouldReDrawChart = TRUE;
	}
	
	if ((pMemDC != NULL) && (m_pBackGround != NULL))
	{	
		DrawPlot();
		DrawScreen(&dc);               // 写屏幕
	}	 
}

void CChartRx::InitChart()
{
	// 初始化各个部分的位置:第一次画图时候调用,以及由用户在设置参数后调用,以便重定位各个元素位置,
	GetClientRect(&rt_Back);
	GetClientRect(&rt_Plot);
	rt_Plot.left += 6;
	rt_Plot.top = 5;
	rt_Plot.bottom -= 5;
	rt_Plot.right -= 25;
	
	SIZE size;
	// 标题的部分
	TitleLabel.GetStringSize(pMemDC->m_hDC, TitleLabel.text, TitleLabel.text.GetLength(), &size);
	TitleLabel.rt = CRect(0, 0, rt_Back.Width(), size.cy + 10);
	rt_Plot.top += TitleLabel.rt.Height();       // 调整表格区域的矩形
	
	// X轴标题的部分
	x_AxisTitle.GetStringSize(pMemDC->m_hDC, x_AxisTitle.text, x_AxisTitle.text.GetLength(), &size);
	x_AxisTitle.rt = CRect(0, rt_Back.Height() - size.cy, rt_Back.Width(), rt_Back.Height());
	rt_Plot.bottom -= x_AxisTitle.rt.Height();  // 调整表格区域的矩形
	
	// Y轴标题的部分
	y_AxisTitle.GetStringSize(pMemDC->m_hDC, y_AxisTitle.text, y_AxisTitle.text.GetLength(), &size);
	y_AxisTitle.rt = CRect(0, 0, size.cy, rt_Back.Height());     // 垂直画
	rt_Plot.left += size.cy;             // 调整表格区域的矩形
	
	// 通过x轴上标签的高度,得到plot最终的位置
	x_AxisLabel.GetStringSize(pMemDC->m_hDC, x_AxisLabel.text, x_AxisLabel.text.GetLength(), &size);
	rt_Plot.bottom -= size.cy;          // 调整表格区域的矩形

	// 通过y轴上标签的宽度,得到plot最终的位置
//	y_AxisLabel.SetFontSize(9, pMemDC->m_hDC);
	y_AxisLabel.GetStringSize(pMemDC->m_hDC, y_AxisLabel.text, y_AxisLabel.text.GetLength(), &size);
	rt_Plot.left += size.cx;

	// 调整参数
	isMagnified = FALSE;             // 已经放大了标记
	isDraging = FALSE;
	isMagnify = FALSE;
	x_minSel = x_min;
	x_maxSel = x_max;
	y_minSel = y_min;
	y_maxSel = y_max;

	shouldReDrawChart = TRUE;        // 标记重新画背景
}

BOOL CChartRx::DrawChart()
{
	CBitmap   *pOldBit = pMemDC->SelectObject(m_pBackGround); 
	pMemDC->FillSolidRect(0, 0, rt_Back.Width(), rt_Back.Height(), crBackGround); 
	if (isIniting)
	{
		isIniting = FALSE;
		InitChart();   // 计算各个部分的位置,得到plot位置
	}
	// title部分
	DrawTitle();
	DrawXTitle();
	DrawYTitle();

	// 计算x,y轴真实增加值,用来画label
	double x_addData = (x_maxSel - x_minSel) / x_Divisions;
	double y_addData = (y_maxSel - y_minSel) / y_Divisions;
	CString LabelText;
	// y 轴
	BOOL ret;
	HGDIOBJ pPenOld = pMemDC->SelectObject(y_Axis.pen);
	pMemDC->MoveTo(rt_Plot.left, rt_Plot.bottom + MajorTick);
	pMemDC->LineTo(rt_Plot.left, rt_Plot.top);
	LabelText.Format(y_LabelFormat, y_minSel);
	ret = DrawYLabel(LabelText, rt_Plot.left, rt_Plot.bottom);
	if (!ret)
		return ret;          // 根据Y标签的宽度重新计算位置

	// x 轴
	pMemDC->SelectObject(x_Axis.pen);
	pMemDC->MoveTo(rt_Plot.left - MajorTick, rt_Plot.bottom);
	pMemDC->LineTo(rt_Plot.right, rt_Plot.bottom);
	LabelText.Format(x_LabelFormat, x_minSel);
	ret = DrawXLabel(LabelText, rt_Plot.left, rt_Plot.bottom, 0);
	if (!ret)
		return ret;

	// grid 竖线
	double addPix = (float)rt_Plot.Width() / (float)(x_Divisions);  // x方向,图上的增量
	int i = 0, j = 0;
	double tempPix = rt_Plot.right;
	double tempData = x_maxSel;
	// 用来画小刻度的变量
	double tempDegreeAdd;
	double tempDegreePix; 
	int lableCount = 0;
	for (i=x_Divisions; i>0; i--)
	{	
		if (bVGrid)   // 是否画竖格线
		{
			pMemDC->SelectObject(v_Grid.pen);
			pMemDC->MoveTo((int)tempPix, rt_Plot.top);
			pMemDC->LineTo((int)tempPix, rt_Plot.bottom);
		}
		pMemDC->SelectObject(x_Degree.pen);
		pMemDC->MoveTo((int)tempPix, rt_Plot.bottom + MajorTick);
		pMemDC->LineTo((int)tempPix, rt_Plot.bottom);
		
		LabelText.Format(x_LabelFormat, tempData);
		ret = DrawXLabel(LabelText, (int)tempPix, rt_Plot.bottom, i);
		if (!ret)
			return ret; 

		tempData -= x_addData;
		tempPix -= addPix;
		// 画细刻度线
		tempDegreeAdd = addPix / x_miniPerSec;  // 一个刻度的象素增长
		tempDegreePix = tempPix; 
		//pMemDC->SelectObject(x_Degree.pen);
		for (j=1; j<x_miniPerSec; j++)
		{
			tempDegreePix += tempDegreeAdd;
			pMemDC->MoveTo((int)tempDegreePix , rt_Plot.bottom);
			pMemDC->LineTo((int)tempDegreePix, rt_Plot.bottom + MiniTick);
		}
	}
	// grid 横线
	addPix = (float)rt_Plot.Height() / (float)(y_Divisions);  // y方向,图上的增量
	tempPix = rt_Plot.top;
	tempData = y_maxSel;
	for (i=y_Divisions; i>0; i--)
	{	
		pMemDC->SelectObject(h_Grid.pen);
		pMemDC->MoveTo(rt_Plot.right, (int)tempPix);
		pMemDC->LineTo(rt_Plot.left, (int)tempPix);	
		
		pMemDC->SelectObject(y_Degree.pen);   //  - MajorTick
		pMemDC->MoveTo(rt_Plot.left - MajorTick, (int)tempPix);	
		pMemDC->LineTo(rt_Plot.left, (int)tempPix);

		LabelText.Format(y_LabelFormat, tempData);
		ret = DrawYLabel(LabelText, rt_Plot.left, (int)tempPix);
		if (!ret)
			return ret; 

		tempData -= y_addData;
		tempPix += addPix;
		// 画细刻度线
		tempDegreeAdd = addPix / y_miniPerSec;  // 一个刻度的象素增长
		tempDegreePix = tempPix; 
		pMemDC->SelectObject(y_Degree.pen);
		for (j=1; j<y_miniPerSec; j++)
		{
			tempDegreePix -= tempDegreeAdd;
			pMemDC->MoveTo(rt_Plot.left, (int)tempDegreePix);
			pMemDC->LineTo(rt_Plot.left - MiniTick, (int)tempDegreePix);
		}
	}
	// 辅助坐标轴,画右边的Y轴
	if ((nChartType == 2) || (AxisSecendVisible))
	{
		pMemDC->MoveTo(rt_Plot.right, rt_Plot.bottom);
		pMemDC->SelectObject(y_Axis.pen);
		pMemDC->LineTo(rt_Plot.right, rt_Plot.top);
		pMemDC->SelectObject(x_Axis.pen);
		pMemDC->LineTo(rt_Plot.left, rt_Plot.top);
	}
	pMemDC->SelectObject(pPenOld);
	return TRUE;
}

void CChartRx::OnSize(UINT nType, int cx, int cy) 
{

	CStatic::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here
	if ((nType == SIZE_MAXHIDE) || (nType == SIZE_MAXSHOW) || (nType == SIZE_MINIMIZED))
	{
		//CString temp;
		//temp.Format("不刷新的消息  %d,  %d", rt_Back.Width(), rt_Back.Height());
		//MessageBox(temp);
		return;
	}

	CRect rt;
	GetClientRect(&rt);
	//CString temp;
	//temp.Format(" %d,  %d", rt.Width(), rt.Height());
	//MessageBox(temp);
	if ((rt.Width() == 0) || (rt.Height() == 0))
		return;
	if ((rt.Width() == rt_Back.Width()) && (rt.Height() == rt_Back.Height()))
		return;
	rt_Back = rt;

	shouldReDrawChart = TRUE;
	if (m_pBackGround != NULL)
	{
		GetClientRect(&rt_Back);
		m_pBackGround->DeleteObject();
		m_pBackGround->CreateCompatibleBitmap(pMemDC, rt_Back.Width(), rt_Back.Height()); 
	}

	if (m_pForeGround != NULL)
	{
		m_pForeGround->DeleteObject();
		m_pForeGround->CreateCompatibleBitmap(pMemDC, rt_Back.Width(), rt_Back.Height());
	}
	if (m_pScreen != NULL)
	{
		m_pScreen->DeleteObject();
		m_pScreen->CreateCompatibleBitmap(pMemDC, rt_Back.Width(), rt_Back.Height()); 
		
		// 窗口变大时候,重新计算位置,但是不需要改变选择范围
		double x1, x2, y1, y2;
		x1 = x_minSel;
		x2 = x_maxSel;
		y1 = y_minSel;
		y2 = y_maxSel;
		InitChart();
		x_minSel = x1;
		x_maxSel = x2;
		y_minSel = y1;
		y_maxSel = y2;

		DrawPlot();
		Invalidate();
		shouldReDrawChart = TRUE;
	}
	
}

void CChartRx::DrawTitle()
{
	// title
	TitleLabel.ShowText(pMemDC->m_hDC, TitleLabel.text, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

void CChartRx::DrawXTitle()
{
	x_AxisTitle.ShowText(pMemDC->m_hDC, x_AxisTitle.text, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

void CChartRx::DrawYTitle()
{
	y_AxisTitle.ShowText(pMemDC->m_hDC, y_AxisTitle.text, DT_VCENTER | DT_SINGLELINE);
}

BOOL CChartRx::DrawXLabel(CString str, int x_Mark, int y_Mark, int index)
{	
	if ((index % n_x_TickOneLabel) != 0)
		return TRUE;

	static int x_label_left;         // 标记前一次画时候左边是多少,判断是否重叠
	static double x_label_val;       // 前一次的值

	SIZE size;
	int nLabels = xLabelPoints.GetPointCount();
	// 如果没有指定自己的标签,就根据范围计算。
	if (nLabels == 0)
	{	
		x_AxisLabel.GetStringSize(pMemDC->m_hDC, str, str.GetLength(), &size);
		int top = rt_Back.Height() - x_AxisTitle.rt.Height() - size.cy;
		int left = x_Mark - size.cx/2;
		x_AxisLabel.rt = CRect(left, top, left + size.cx, top + size.cy);
		x_AxisLabel.ShowText(pMemDC->m_hDC, str,  DT_CENTER | DT_VCENTER | DT_SINGLELINE);

		// 判断重叠
		if (index == 0)
		{
			x_label_left = 12800;
		}
		else
		{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -