📄 chartrx.cpp
字号:
// 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 + -