k线、指标绘制
我接触的绘制有两种:gdi+和qt绘图。可以灵活的绘制任何想要的东西。
先上效果图吧。
如下:基于gdi+的股指和股票的绘制。上面是沪深成分股实时生成的股票指数走势,下面是IF主力走势和开平仓位置。
如下,基于qt绘图的期货数据显示,模仿的博易大师。
现在贴上代码:
1、gdi+
#pragma once #include <comdef.h> #include <GdiPlus.h> #include <Windows.h> #include <map> #include <list> #include <vector> #include <thread> #include "my_protocol_struct.h" using namespace Gdiplus; using namespace std; #pragma comment(lib, "gdiplus.lib") // 其他指标信息容器 int iColorArr[]; struct OtherInfo { char pName[MAX_PATH]; Color color; int penStyle; double dBound; bool bShow; list<double>* listInfo; // 其他辅助信息 OtherInfo() { penStyle = 0; dBound = 1.5f; pName[0] = 0; color = iColorArr[0]; bShow = true; listInfo = NULL; } }; // 选中后需要显示的其他信息 struct OtherShowInfo { char pName[MAX_PATH]; double dValue; Color color; }; class DrawModule { public: DrawModule(); ~DrawModule(); private: RECT m_rc; unsigned int m_nOffset; //Gdi初始化变量 GdiplusStartupInput m_Gdistart; ULONG_PTR m_GdiplusToken; //透明度 BYTE m_Transparency; // 画布 Bitmap *bitmap_background; Bitmap *bitmap_kline_index; Bitmap *bitmap_user_show_info; //双缓存绘图 Gdiplus::Graphics *drawObj; // 当前屏幕上的最大最小值 double m_dMax; double m_dMin; int nIntervalTime; int RIGHT_SPACE; bool bOtherGreen; int m_minCycle; int m_maxCycle; SIndexData m_pankou; int m_nSelectKlineDate; int m_nSelectKlineTime; HANDLE m_hEvent; bool m_bThread; shared_ptr<thread> m_thdDraw; public: // 显示十字光标 int m_nShowCursel; bool m_bSetOther; private: list<Skline>* m_pListKline; list<Skline>* m_pCompareListKline; vector<OtherInfo> m_vecOtherInfo; vector<STradeData>* m_vecTradeData; //vector<OpenDot> m_vecOpenDot; private: // 绘制背景色等 bool PreDrawInfo(); // 绘制坐标线 void DrawBackGround(); // 绘制数字 void DrawNumber(); // 绘制K线 void DrawKLine(bool bDrawAll = true); // 局部重绘 void DoDrawAll(); // 数据转点 Point DataToPoint(double dValue,int nCount,double dCurMin,double dUnit); void DrawOther(bool bDrawAll = true); bool IsSameCycle(int nTime, int nKlineTime); void ThreadDraw(); public: // 初始化传入控件句柄 void IniDrawModule(HWND hWnd); //是否半透明 void SetTransparency(BYTE b); // 塞入想要绘制的信息 // K线 其他信息 NULL结尾 void SetInfoToDraw(list<Skline>* pList); // void SetCompareKline(list<Skline>* pList); // 清空指标容器 void ClearOther(); // 塞入指标 void SetOtherInfo(OtherInfo& pInfo); // 盘口数据 void SetPankou(SIndexData& data); // 设置开平仓点 void SetTradeDot(vector<STradeData>* p); // 设置其他的绿柱颜色 void SetOtherGreenColor(); // 设置纵向周期 秒数 void SetCrossCycle(int min_cycle, int max_cycle); // 放大缩小重置间距 void DefaultResetInterval(bool bAdd); // 左移和右移重置偏移量 void DefaultResetOffset(bool bLeft); // 需要显示的信息 bool InfoToShow(unsigned int nX, unsigned int nY); // 获取点击的k线 Skline* GetSelectKline(unsigned int nX); // 左移和右移重置偏移量 void PercentResetOffset(unsigned int nPercent); // 执行绘制 void DoDraw(); //开始线程 void Start(); // 结束绘制 void Stop(); };
// GDIPLUS4KLINE.cpp : 定义 DLL 应用程序的导出函数。 // #include "stdafx.h" #include "GDIPLUS4KLINE.h" #include <Windows.h> #define TOP_INFO_SPACE 30 int iColorArr[] = {Color::Yellow,Color::Brown,Color::DarkGoldenrod,Color::Magenta,Color::Blue,Color::Purple,Color::White, Color::DarkOrange,Color::DarkRed,Color::DarkMagenta,Color::DarkGray,Color::DarkBlue,Color::Gray, Color::LightPink,Color::LightSalmon,Color::LightYellow,Color::LightGray,Color::LightSkyBlue,Color::LightSeaGreen}; int iInterval[] = {1,1,2,3,4,5,6,7,13,19,25,35}; int iSpace[] = {0,1,1,1,1,1,2,3, 4, 4, 4, 6}; static int to_second(int time) { int nHour = time / 10000; int nMini = time / 100 - nHour * 100; int nSec = time - nHour * 10000 - nMini * 100; return nHour * 60 * 60 + nMini * 60 + nSec; } DrawModule::DrawModule() { nIntervalTime = 4; m_nOffset = 0; m_pListKline = NULL; m_dMax = DBL_MIN; m_dMin = DBL_MAX; bitmap_background = NULL; bitmap_kline_index = NULL; bitmap_user_show_info = NULL; drawObj = NULL; m_nShowCursel = 0; bOtherGreen = false; m_Transparency = 255; RIGHT_SPACE = 80; m_minCycle = 0; m_maxCycle = 0; m_nSelectKlineDate = 0; m_nSelectKlineTime = 0; GdiplusStartup(&m_GdiplusToken,& m_Gdistart,NULL); m_vecTradeData = nullptr; m_pCompareListKline = nullptr; m_pankou = { 0 }; m_bThread = true; m_bSetOther = false; m_thdDraw = nullptr; m_hEvent = ::CreateEvent(nullptr, false, false, nullptr); } DrawModule::~DrawModule() { GdiplusShutdown(m_GdiplusToken); } void DrawModule::IniDrawModule(HWND hWnd) { if (!drawObj) { drawObj = new Graphics(hWnd); } else { delete drawObj; drawObj = new Graphics(hWnd); } if (bitmap_user_show_info!=NULL) { delete bitmap_user_show_info; } bitmap_user_show_info = new Bitmap(m_rc.right-m_rc.left,m_rc.bottom-m_rc.top); GetClientRect(hWnd,&m_rc); DrawBackGround(); if (RIGHT_SPACE != 0) RIGHT_SPACE = 65; } void DrawModule::SetTransparency(BYTE b) { m_Transparency = b; DrawBackGround(); } void DrawModule::DrawBackGround() { if (bitmap_background!=NULL) { delete bitmap_background; } bitmap_background = new Bitmap(m_rc.right-m_rc.left, m_rc.bottom-m_rc.top); Graphics graphics_temp(bitmap_background); if (m_Transparency != 255) { SolidBrush blackBrush_clear(Color(255, 255, 255)); graphics_temp.FillRectangle(&blackBrush_clear, 0, 0, m_rc.right - m_rc.left, m_rc.bottom - m_rc.top); } SolidBrush blackBrush(Color(m_Transparency, 10, 10, 10)); graphics_temp.FillRectangle(&blackBrush,0,0,m_rc.right-m_rc.left,m_rc.bottom-m_rc.top); /*if (m_Transparency != 255) return;*/ // 2、坐标线 Pen redPen_Solid(Color(180,40,40),0.3f); // 实线画笔 redPen_Solid.SetDashStyle(DashStyleSolid); Pen redPen_Dash(Color(200,50,50),0.3f); // 虚线画笔 redPen_Dash.SetDashStyle(DashStyleDash); graphics_temp.DrawLine(&redPen_Solid, m_rc.right-m_rc.left-RIGHT_SPACE, 0, m_rc.right-m_rc.left-RIGHT_SPACE, m_rc.bottom-m_rc.top-TOP_INFO_SPACE);//右数据轴 | // 3、虚线 for (unsigned int i=1;i<=5;i++) { graphics_temp.DrawLine(&redPen_Dash, 0, (m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/6*i+TOP_INFO_SPACE, m_rc.right-m_rc.left-RIGHT_SPACE, (m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/6*i+TOP_INFO_SPACE); } } void DrawModule::SetInfoToDraw(list<Skline>* pList) { m_pListKline = pList; } void DrawModule::SetCompareKline(list<Skline>* pList) { m_pCompareListKline = pList; } void DrawModule::ClearOther() { m_vecOtherInfo.clear(); } void DrawModule::SetOtherInfo(OtherInfo& pInfo) { m_vecOtherInfo.push_back(pInfo); } void DrawModule::SetPankou(SIndexData& data) { m_pankou = data; } void DrawModule::SetTradeDot(vector<STradeData>* p) { m_vecTradeData = p; } void DrawModule::SetOtherGreenColor() { bOtherGreen = true; } void DrawModule::SetCrossCycle(int min_cycle, int max_cycle) { m_minCycle = min_cycle; m_maxCycle = max_cycle; } void DrawModule::DefaultResetInterval(bool bAdd) { if (nIntervalTime>0 && nIntervalTime<11) { if (bAdd) ++nIntervalTime; else --nIntervalTime; }else if (nIntervalTime==0 && bAdd) { ++nIntervalTime; }else if (nIntervalTime==11 && !bAdd) { --nIntervalTime; } else return; DoDraw(); } void DrawModule::DefaultResetOffset(bool bLeft) { if (m_pListKline==NULL) { return; } unsigned int oldOffset = m_nOffset; int nShowCount = (m_rc.right-m_rc.left-RIGHT_SPACE)/(iInterval[nIntervalTime]+2*iSpace[nIntervalTime]); int nListCount = m_pListKline->size(); if (nListCount <= nShowCount) { m_nOffset = 0; } else { if (bLeft) { int nCanOffset = nListCount - m_nOffset - nShowCount; if (nCanOffset > 0) { m_nOffset++; } } else if(m_nOffset >0) { m_nOffset--; } } if (m_nOffset!=oldOffset) { DoDraw(); } } void DrawModule::PercentResetOffset(unsigned int nPercent) { if (nPercent>100 || m_pListKline==NULL) { return; } int nShowCount = (m_rc.right-m_rc.left-RIGHT_SPACE)/(iInterval[nIntervalTime]+2*iSpace[nIntervalTime]); int nListCount = m_pListKline->size(); int oldOffset = m_nOffset; int nOffset = (100-nPercent)*nListCount/100; if ((nOffset+nShowCount)>nListCount) { m_nOffset = nListCount - nShowCount; } else { m_nOffset = nOffset; } if (m_nOffset!=nOffset) { DoDraw(); } } bool DrawModule::PreDrawInfo() { if (m_pListKline == NULL) { return false; } int nKlineCount = m_pListKline->size(); list<Skline>::reverse_iterator rIter = m_pListKline->rbegin(); if (rIter != m_pListKline->rend() && m_nOffset) {// 指针偏移 int nCount = 0; while (nCount<m_nOffset) { rIter++; nCount++; } } double dMax = -1.79769313486231570E+308,dMin = DBL_MAX; int nMaxShow = (m_rc.right-m_rc.left-RIGHT_SPACE)/(iInterval[nIntervalTime]+2*iSpace[nIntervalTime]); while (rIter != m_pListKline->rend() && nMaxShow) {// 取频幕上最大最小值 if (dMax<rIter->dHigh) { dMax = rIter->dHigh; } if (dMin>rIter->dLow) { dMin = rIter->dLow; } rIter++; nMaxShow--; } for (unsigned int i = 0; i < m_vecOtherInfo.size(); i++) { if (m_vecOtherInfo[i].bShow) { list<double>::reverse_iterator rOtherIter = m_vecOtherInfo[i].listInfo->rbegin(); if (rOtherIter != m_vecOtherInfo[i].listInfo->rend() && m_nOffset) {// 指标偏移 int nCount = 0; while (nCount < m_nOffset && rOtherIter != m_vecOtherInfo[i].listInfo->rend()) { rOtherIter++; nCount++; } } nMaxShow = (m_rc.right - m_rc.left - RIGHT_SPACE) / (iInterval[nIntervalTime] + 2 * iSpace[nIntervalTime]); while (rOtherIter != m_vecOtherInfo[i].listInfo->rend() && nMaxShow) { if (dMax < *rOtherIter) { dMax = *rOtherIter; } if (dMin > *rOtherIter) { dMin = *rOtherIter; } rOtherIter++; nMaxShow--; } } } m_dMax = dMax; m_dMin = dMin; return true; } void DrawModule::DrawNumber() { double dUnit = (m_dMax - m_dMin)/(m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/0.9; // 数据轴单位 double dCurMin = m_dMin - 0.05*(m_rc.bottom-m_rc.top-TOP_INFO_SPACE)*dUnit; // 数据轴最小值 //if (m_Transparency != 255) // return; FontFamily fontFamily(L"楷体"); Gdiplus::Font font(&fontFamily, 15, FontStyleRegular, UnitPixel); StringFormat stringformat; stringformat.SetAlignment(StringAlignmentNear); stringformat.SetLineAlignment(StringAlignmentCenter); if (bitmap_kline_index!=NULL) { delete bitmap_kline_index; } bitmap_kline_index = new Bitmap(m_rc.right-m_rc.left, m_rc.bottom-m_rc.top); Graphics graphics_kline_index(bitmap_kline_index); graphics_kline_index.SetTextRenderingHint(TextRenderingHintAntiAlias); char szValue[50]; wchar_t wcstring[100]; SolidBrush red_brush(Color(240,10,10)); for (unsigned int i=1;i<=5;i++) { int nHorien = (m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/6*(6-i)+TOP_INFO_SPACE; _snprintf_s(szValue,50,"%.2f",dCurMin+dUnit*(m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/6*i); MultiByteToWideChar(CP_ACP,0,szValue,50,wcstring,100); graphics_kline_index.DrawString(wcstring,wcslen(wcstring),&font, RectF(m_rc.right-m_rc.left-RIGHT_SPACE+2,nHorien-8,RIGHT_SPACE-2,16),&stringformat,&red_brush); } if (m_pankou.ask_volume != 0 && m_pankou.bid_volume != 0) { SolidBrush white_brush(Color(255, 255, 255)); Point pt_new = DataToPoint(m_pankou.index_tick, 1, dCurMin, dUnit); SolidBrush RedBrush(Color(240, 10, 10)); graphics_kline_index.FillRectangle(&RedBrush, m_rc.right - m_rc.left - RIGHT_SPACE, pt_new.Y - 14, 70, 28); _snprintf_s(szValue, 100, "%.2f", m_pankou.ask_price); MultiByteToWideChar(CP_ACP, 0, szValue, 100, wcstring, 200); graphics_kline_index.DrawString(wcstring, wcslen(wcstring), &font, RectF(m_rc.right - m_rc.left - RIGHT_SPACE, pt_new.Y - 14, strlen(szValue) * 8 + 6, TOP_INFO_SPACE / 2), &stringformat, &white_brush); _snprintf_s(szValue, 100, "%.2f", m_pankou.bid_price); MultiByteToWideChar(CP_ACP, 0, szValue, 100, wcstring, 200); graphics_kline_index.DrawString(wcstring, wcslen(wcstring), &font, RectF(m_rc.right - m_rc.left - RIGHT_SPACE, pt_new.Y , strlen(szValue) * 8 + 6, TOP_INFO_SPACE / 2), &stringformat, &white_brush); } } bool DrawModule::InfoToShow(unsigned int nX, unsigned int nY) { if (nX>=(m_rc.right-m_rc.left-RIGHT_SPACE) || m_pListKline==NULL) { return false; } int nScreenOffset = (m_rc.right-m_rc.left - nX - RIGHT_SPACE)/(iInterval[nIntervalTime]+2*iSpace[nIntervalTime]); // 屏幕偏移 int nScreenCount = nScreenOffset; nScreenOffset += m_nOffset; // 容器偏移 list<Skline>::reverse_iterator rIter = m_pListKline->rbegin(); while(rIter != m_pListKline->rend() && nScreenOffset>0) { nScreenOffset--; rIter++; } if (rIter == m_pListKline->rend()) { return false; } if (nY<TOP_INFO_SPACE) { return false; } vector<OtherShowInfo> vecOther; vector<OtherInfo>::iterator iterOther = m_vecOtherInfo.begin(); while (iterOther != m_vecOtherInfo.end()) { nScreenOffset = (m_rc.right - m_rc.left - nX - RIGHT_SPACE) / (iInterval[nIntervalTime] + 2 * iSpace[nIntervalTime]) + m_nOffset; list<double>::reverse_iterator subrIterOther = iterOther->listInfo->rbegin(); while (subrIterOther != iterOther->listInfo->rend() && nScreenOffset) { nScreenOffset--; subrIterOther++; } if (subrIterOther != iterOther->listInfo->rend()) { OtherShowInfo osi; strcpy_s(osi.pName, iterOther->pName); osi.dValue = *subrIterOther; osi.color = iterOther->color; vecOther.push_back(osi); } iterOther++; } Skline& m_lastKline = *rIter; if (bitmap_user_show_info!=NULL) { delete bitmap_user_show_info; } bitmap_user_show_info = new Bitmap(m_rc.right-m_rc.left,m_rc.bottom-m_rc.top); Graphics graphics_user_show_info(bitmap_user_show_info); graphics_user_show_info.DrawImage(bitmap_background,0,0,bitmap_background->GetWidth(),bitmap_background->GetHeight()); graphics_user_show_info.DrawImage(bitmap_kline_index,0,0,bitmap_kline_index->GetWidth(),bitmap_kline_index->GetHeight()); int y = 2; FontFamily fontFamily(L"楷体"); Gdiplus::Font font(&fontFamily, 15, FontStyleRegular, UnitPixel); StringFormat stringformat; stringformat.SetAlignment(StringAlignmentNear); stringformat.SetLineAlignment(StringAlignmentCenter); graphics_user_show_info.SetTextRenderingHint(TextRenderingHintAntiAlias); char szValue[1000]; wchar_t wcstring[2000]; _snprintf_s(szValue,1000,"%d,%d,开:%.2f,高:%.2f,低:%.2f,收:%.2f,成交量:%d,持仓量:%d;", m_lastKline.nDate,m_lastKline.nTime, m_lastKline.dOpen,m_lastKline.dHigh,m_lastKline.dLow,m_lastKline.dClose, m_lastKline.nVolume,m_lastKline.dInterest); MultiByteToWideChar(CP_ACP,0,szValue,1000,wcstring,2000); SolidBrush white_brush(Color(255,255,255)); graphics_user_show_info.DrawString(wcstring,wcslen(wcstring),&font, RectF(2,y,strlen(szValue)*8,TOP_INFO_SPACE/2-2),&stringformat,&white_brush); y = TOP_INFO_SPACE / 2 + 2; int x = 2; for (int i = 0; i < vecOther.size(); i++) { SolidBrush the_brush(vecOther[i].color); _snprintf_s(szValue, 100, "%s:%.2f;", vecOther[i].pName, vecOther[i].dValue); MultiByteToWideChar(CP_ACP, 0, szValue, 100, wcstring, 200); graphics_user_show_info.DrawString(wcstring, wcslen(wcstring), &font, RectF(x, y, strlen(szValue) * 8, TOP_INFO_SPACE / 2 - 2), &stringformat, &the_brush); x += strlen(szValue) * 8; } if (m_nShowCursel > 0) { Pen WhitePen(Color(240,240,240),1.0f); // 横、纵、数字 if (m_nShowCursel > 1) { double dUnit = (m_dMax - m_dMin) / (m_rc.bottom - m_rc.top - TOP_INFO_SPACE) / 0.9; double dCurMin = m_dMin - 0.05*(m_rc.bottom - m_rc.top - TOP_INFO_SPACE)*dUnit; // 数据轴最小值 Point ptLeft; ptLeft.X = 0; ptLeft.Y = nY; Point ptRight; ptRight.X = m_rc.right - m_rc.left - RIGHT_SPACE; ptRight.Y = nY; graphics_user_show_info.DrawLine(&WhitePen, ptLeft, ptRight); SolidBrush RedBrush(Color(240, 10, 10)); graphics_user_show_info.FillRectangle(&RedBrush, m_rc.right - m_rc.left - RIGHT_SPACE, nY - 7, 70, 14); _snprintf_s(szValue, 100, "%.2f", (m_rc.bottom - m_rc.top - nY)*dUnit + dCurMin); MultiByteToWideChar(CP_ACP, 0, szValue, 100, wcstring, 200); graphics_user_show_info.DrawString(wcstring, wcslen(wcstring), &font, RectF(m_rc.right - m_rc.left - RIGHT_SPACE, nY - 7, strlen(szValue) * 8 + 6, TOP_INFO_SPACE / 2), &stringformat, &white_brush); } int nKlineX = m_rc.right-m_rc.left-RIGHT_SPACE - nScreenCount*(iInterval[nIntervalTime]+iSpace[nIntervalTime]*2) - iInterval[nIntervalTime]/2 - iSpace[nIntervalTime]; Point ptTop; ptTop.X = nKlineX;ptTop.Y = TOP_INFO_SPACE; Point ptBottom; ptBottom.X = nKlineX; ptBottom.Y = m_rc.bottom-m_rc.top; graphics_user_show_info.DrawLine(&WhitePen, ptTop, ptBottom); } drawObj->DrawImage(bitmap_user_show_info,0,0,bitmap_user_show_info->GetWidth(),bitmap_user_show_info->GetHeight()); return true; } Skline* DrawModule::GetSelectKline(unsigned int nX) { if (nX >= (m_rc.right - m_rc.left - RIGHT_SPACE) || m_pListKline == NULL) { return nullptr; } int nScreenOffset = (m_rc.right - m_rc.left - nX - RIGHT_SPACE) / (iInterval[nIntervalTime] + 2 * iSpace[nIntervalTime]); // 屏幕偏移 int nScreenCount = nScreenOffset; nScreenOffset += m_nOffset; // 容器偏移 list<Skline>::reverse_iterator rIter = m_pListKline->rbegin(); while (rIter != m_pListKline->rend() && nScreenOffset > 0) { nScreenOffset--; rIter++; } if (rIter == m_pListKline->rend()) { return nullptr; } vector<OtherShowInfo> vecOther; vector<OtherInfo>::iterator iterOther = m_vecOtherInfo.begin(); while (iterOther != m_vecOtherInfo.end()) { nScreenOffset = (m_rc.right - m_rc.left - nX - RIGHT_SPACE) / (iInterval[nIntervalTime] + 2 * iSpace[nIntervalTime]) + m_nOffset; list<double>::reverse_iterator subrIterOther = iterOther->listInfo->rbegin(); while (subrIterOther != iterOther->listInfo->rend() && nScreenOffset) { nScreenOffset--; subrIterOther++; } if (subrIterOther != iterOther->listInfo->rend()) { OtherShowInfo osi; strcpy_s(osi.pName, iterOther->pName); osi.dValue = *subrIterOther; osi.color = iterOther->color; vecOther.push_back(osi); } iterOther++; } Skline& m_lastKline = *rIter; m_nSelectKlineDate = m_lastKline.nDate; m_nSelectKlineTime = m_lastKline.nTime; return &m_lastKline; } void DrawModule::DrawKLine(bool bDrawAll) { if (m_pListKline==NULL) return; list<Skline>::reverse_iterator rIter = m_pListKline->rbegin(); int nCountOffset = 0; if (rIter != m_pListKline->rend() && m_nOffset) {// K线偏移 while (nCountOffset<m_nOffset) { rIter++; nCountOffset++; } } list<Skline>::reverse_iterator rIterCompare; if (m_pCompareListKline) { rIterCompare = m_pCompareListKline->rbegin(); advance(rIterCompare, nCountOffset); } double dUnit = (m_dMax - m_dMin)/(m_rc.bottom-m_rc.top-TOP_INFO_SPACE)/0.9; // 数据轴单位 double dCurMin = m_dMin - 0.05*(m_rc.bottom-m_rc.top-TOP_INFO_SPACE)*dUnit; // 数据轴最小值 // 画笔 Pen RedPen(Color(240,10,10),1.5f); Pen LightRedPen(Color(120, 10, 10), 0.5f); LightRedPen.SetDashStyle(DashStyleDash); Pen GreenPen(Color(50,210,50),1.5f); Pen OtherGreenPen(Color(141, 238, 238), 1.5f); Pen WhitePen(Color(240,240,240),1.5f); Pen yellowPen(Color(230, 236, 29), 1.5f); SolidBrush GreenPenTrade(Color(100, 200, 100)); SolidBrush RedPenTrade(Color(210, 50, 50)); // 画刷 SolidBrush GreenBrush(Color(50,210,50)); SolidBrush RedBrush(Color(240, 10, 10)); SolidBrush OtherGreenBrush(Color(141, 238, 238)); SolidBrush yellowBrush(Color(230, 236, 29)); Graphics graphics_kline_index(bitmap_kline_index); int nMaxCount = (m_rc.right-m_rc.left-RIGHT_SPACE)/(iInterval[nIntervalTime]+2*iSpace[nIntervalTime]); int nCount = 1;// 画图上第多少个 double dMaxScreen=DBL_MIN,dMinScreen=DBL_MAX; Point ptMax,ptMin; if (iInterval[nIntervalTime]<2) {// 间距小于4只画线 while (rIter != m_pListKline->rend() && nMaxCount) { double dMulti = rIter->dClose - rIter->dOpen; Point ptHigh = DataToPoint(rIter->dHigh,nCount,dCurMin,dUnit); Point ptLow = DataToPoint(rIter->dLow,nCount,dCurMin,dUnit); if (m_maxCycle != 0 && m_minCycle != 0) { int hX = ptLow.X + iInterval[nIntervalTime] / 2 + iSpace[nIntervalTime]; // 纵向网格 int sec = to_second(rIter->nTime); if (sec % m_maxCycle == 0) { graphics_kline_index.DrawLine(&RedPen, hX, TOP_INFO_SPACE, hX, m_rc.bottom - m_rc.top); } else if (sec % m_minCycle == 0) { graphics_kline_index.DrawLine(&LightRedPen, hX, TOP_INFO_SPACE, hX, m_rc.bottom - m_rc.top); } } if (dMulti< -0.01) {// 绿线 graphics_kline_index.DrawLine(bOtherGreen ? &OtherGreenPen : &GreenPen,ptLow,ptHigh); } else {// 红线 graphics_kline_index.DrawLine(&RedPen,ptLow,ptHigh); } if (rIter->dHigh > dMaxScreen) { dMaxScreen = rIter->dHigh; ptMax = ptHigh; } if (dMinScreen > rIter->dLow) { dMinScreen = rIter->dLow; ptMin = ptLow; } nCount++; rIter++; if (m_pCompareListKline && rIterCompare!= m_pCompareListKline->rend()) rIterCompare++; nMaxCount--; } } else { while (rIter != m_pListKline->rend() && nMaxCount) { double dMulti = rIter->dClose - rIter->dOpen; Point ptOpen = DataToPoint(rIter->dOpen,nCount,dCurMin,dUnit); Point ptHigh = DataToPoint(rIter->dHigh,nCount,dCurMin,dUnit); Point ptLow = DataToPoint(rIter->dLow,nCount,dCurMin,dUnit); Point ptClose = DataToPoint(rIter->dClose,nCount,dCurMin,dUnit); if (m_maxCycle != 0 && m_minCycle != 0) { int hX = ptLow.X + iInterval[nIntervalTime] / 2 + iSpace[nIntervalTime]; // 纵向网格 int sec = to_second(rIter->nTime); if (sec % m_maxCycle == 0) { graphics_kline_index.DrawLine(&RedPen, hX, TOP_INFO_SPACE, hX, m_rc.bottom - m_rc.top); } else if (sec % m_minCycle == 0) { graphics_kline_index.DrawLine(&LightRedPen, hX, TOP_INFO_SPACE, hX, m_rc.bottom - m_rc.top); } } if (dMulti< -0.01) {// 绿柱 if (m_pCompareListKline && rIterCompare != m_pCompareListKline->rend() && rIterCompare->dOpen <= rIterCompare->dClose) { graphics_kline_index.DrawRectangle((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &GreenPen, ptOpen.X - iInterval[nIntervalTime] / 2, ptOpen.Y, iInterval[nIntervalTime], ptClose.Y - ptOpen.Y); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &GreenPen, ptHigh, ptOpen); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &GreenPen, ptLow, ptClose); } else { graphics_kline_index.FillRectangle((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowBrush : &GreenBrush, ptOpen.X - iInterval[nIntervalTime] / 2, ptOpen.Y, iInterval[nIntervalTime], ptClose.Y - ptOpen.Y); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &GreenPen, ptHigh, ptLow); } } else if (dMulti < 0.01) {// 白十字 graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &WhitePen,ptHigh,ptLow); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &WhitePen, ptOpen.X-iInterval[nIntervalTime]/2,ptOpen.Y, ptOpen.X+iInterval[nIntervalTime]/2,ptOpen.Y); } else {// 红柱 if (m_pCompareListKline && rIterCompare != m_pCompareListKline->rend() && rIterCompare->dOpen <= rIterCompare->dClose) { graphics_kline_index.FillRectangle((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowBrush : &RedBrush, ptClose.X - iInterval[nIntervalTime] / 2, ptClose.Y, iInterval[nIntervalTime], ptOpen.Y - ptClose.Y); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &RedPen, ptHigh, ptLow); } else { graphics_kline_index.DrawRectangle((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &RedPen, ptClose.X - iInterval[nIntervalTime] / 2, ptClose.Y, iInterval[nIntervalTime], ptOpen.Y - ptClose.Y); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &RedPen, ptHigh, ptClose); graphics_kline_index.DrawLine((rIter->nDate == m_nSelectKlineDate && rIter->nTime == m_nSelectKlineTime) ? &yellowPen : &RedPen, ptLow, ptOpen); } } if (m_vecTradeData) { for (auto iter_trade = m_vecTradeData->begin(); iter_trade != m_vecTradeData->end(); iter_trade++) { if (iter_trade->nDate == rIter->nDate && IsSameCycle(iter_trade->nTime, rIter->nTime)) {////▲▼▼▲↑ Point ptPrice = DataToPoint(iter_trade->dPrice, nCount, dCurMin, dUnit); SolidBrush KongBrush(Color(89, 66, 237)); SolidBrush DuoBrush(Color(244, 186, 247)); SolidBrush checkBrush(Color(100, 100, 140)); FontFamily fontFamily(L"楷体"); Gdiplus::Font font(&fontFamily, 14, FontStyleRegular, UnitPixel); StringFormat stringformat; stringformat.SetAlignment(StringAlignmentCenter); stringformat.SetLineAlignment(StringAlignmentCenter); graphics_kline_index.SetTextRenderingHint(TextRenderingHintAntiAlias); if (iter_trade->chDir == '0') { char szValue[50]; wchar_t wcstring[100]; _snprintf_s(szValue, 50, "▲\n%d|%d", iter_trade->nVolume, iter_trade->nNetPos); MultiByteToWideChar(CP_ACP, 0, szValue, 50, wcstring, 100); //graphics_kline_index.FillRectangle(&checkBrush, RectF(ptPrice.X - 28, ptPrice.Y, 56, 28)); graphics_kline_index.DrawString(wcstring, wcslen(wcstring), &font, RectF(ptPrice.X - 28, ptPrice.Y, 56, 28), &stringformat, &DuoBrush); } else { char szValue[50]; wchar_t wcstring[100]; _snprintf_s(szValue, 50, "%d|%d\n▼", iter_trade->nVolume, iter_trade->nNetPos); MultiByteToWideChar(CP_ACP, 0, szValue, 50, wcstring, 100); //graphics_kline_index.FillRectangle(&checkBrush, RectF(ptPrice.X - 28, ptPrice.Y - 28, 56, 28)); graphics_kline_index.DrawString(wcstring, wcslen(wcstring), &font, RectF(ptPrice.X - 28, ptPrice.Y-24, 56, 28), &stringformat, &KongBrush); } //_snprintf_s(szValue, 50, "%d", iter_trade->nNetPos); //MultiByteToWideChar(CP_ACP, 0, szValue, 50, wcstring, 100); //graphics_kline_index.DrawString(wcstring, wcslen(wcstring), &font, // RectF(ptPrice.X + 1, iter_trade->chDir == '1' ? ptPrice.Y - 7 : ptPrice.Y, 14, 28), &stringformat, &WhiteBrush); } } } if (rIter->dHigh > dMaxScreen) { dMaxScreen = rIter->dHigh; ptMax = ptHigh; } if (dMinScreen > rIter->dLow) { dMinScreen = rIter->dLow; ptMin = ptLow; } nCount++; rIter++; if (m_pCompareListKline && rIterCompare != m_pCompareListKline->rend()) rIterCompare++; nMaxCount--; } } if (dMinScreen!=DBL_MAX && dMaxScreen!=DBL_MIN) { SolidBrush WhiteBrush(Color(255,255,240)); FontFamily fontFamily(L"楷体"); Gdiplus::Font font(&fontFamily, 14, FontStyleRegular, UnitPixel); StringFormat stringformat; stringformat.SetAlignment(StringAlignmentNear); stringformat.SetLineAlignment(StringAlignmentCenter); graphics_kline_index.SetTextRenderingHint(TextRenderingHintAntiAlias); char szValue[50];wchar_t wcstring[100]; _snprintf_s(szValue,50,"←%.2f",dMinScreen); MultiByteToWideChar(CP_ACP,0,szValue,50,wcstring,100); graphics_kline_index.DrawString(wcstring,wcslen(wcstring),&font, RectF(ptMin.X,ptMin.Y-7,RIGHT_SPACE,14),&stringformat,&WhiteBrush); _snprintf_s(szValue,50,"←%.2f",dMaxScreen); MultiByteToWideChar(CP_ACP,0,szValue,50,wcstring,100); graphics_kline_index.DrawString(wcstring,wcslen(wcstring),&font, RectF(ptMax.X,ptMax.Y-7,RIGHT_SPACE,14),&stringformat,&WhiteBrush); } } Point DrawModule::DataToPoint(double dValue, int nCount, double dCurMin, double dUnit) { Point pt; pt.X = m_rc.right-m_rc.left-RIGHT_SPACE-(iInterval[nIntervalTime]+iSpace[nIntervalTime]*2)*nCount+iSpace[nIntervalTime]+iInterval[nIntervalTime]/2; pt.Y = m_rc.bottom-m_rc.top-(dValue-dCurMin)/dUnit; return pt; } void DrawModule::DrawOther(bool bDrawAll) { double dUnit = (m_dMax - m_dMin) / (m_rc.bottom - m_rc.top - TOP_INFO_SPACE) / 0.9; // 数据轴单位 double dCurMin = m_dMin - 0.05*(m_rc.bottom - m_rc.top - TOP_INFO_SPACE)*dUnit; // 数据轴最小值 Graphics graphics_kline_index(bitmap_kline_index); for (unsigned int i = 0; i < m_vecOtherInfo.size(); i++) { Pen randomPen(m_vecOtherInfo[i].color, m_vecOtherInfo[i].dBound); randomPen.SetDashStyle(DashStyle(m_vecOtherInfo[i].penStyle)); if (m_vecOtherInfo[i].bShow) { list<double>::reverse_iterator rOtherIter = m_vecOtherInfo[i].listInfo->rbegin(); if (rOtherIter != m_vecOtherInfo[i].listInfo->rend() && m_nOffset) { int nCount = 0; while (nCount < m_nOffset && rOtherIter != m_vecOtherInfo[i].listInfo->rend()) { rOtherIter++; nCount++; } } int nMaxCount = (m_rc.right - m_rc.left - RIGHT_SPACE) / (iInterval[nIntervalTime] + 2 * iSpace[nIntervalTime]); int nCount = 1; Point ptLast; while (rOtherIter != m_vecOtherInfo[i].listInfo->rend() && nMaxCount) { Point ptThis = DataToPoint(*rOtherIter, nCount, dCurMin, dUnit); if (nCount > 1) { graphics_kline_index.DrawLine(&randomPen, ptLast, ptThis); } rOtherIter++; nCount++; nMaxCount--; ptLast = ptThis; } } } } bool DrawModule::IsSameCycle(int nTime, int nKlineTime) { int n1 = nTime / 100; int n2 = nKlineTime / 100; if (n1 % 100 == 59) { n1 += 41; } else { n1 += 1; } if (n1 == n2) return true; return false; } void DrawModule::ThreadDraw() { while (m_bThread) { if (WaitForSingleObject(m_hEvent, 100) == WAIT_TIMEOUT) continue; if (PreDrawInfo()) { DoDrawAll(); } } } void DrawModule::DoDraw() { ::SetEvent(m_hEvent); //if(PreDrawInfo()) //{ // DoDrawAll(); //} } void DrawModule::Start() { if (m_thdDraw) return; m_thdDraw = make_shared<thread>(&DrawModule::ThreadDraw, this); } void DrawModule::Stop() { m_bThread = false; if (m_thdDraw && m_thdDraw->joinable()) m_thdDraw->join(); } void DrawModule::DoDrawAll() { // 在画布上绘制 DrawNumber(); DrawKLine(); DrawOther(); // 显示到控件上 if (!bitmap_user_show_info) return; Gdiplus::Graphics graphics_user_show_info(bitmap_user_show_info); graphics_user_show_info.DrawImage(bitmap_background,0,0,bitmap_background->GetWidth(),bitmap_background->GetHeight()); graphics_user_show_info.DrawImage(bitmap_kline_index,0,0,bitmap_kline_index->GetWidth(),bitmap_kline_index->GetHeight()); drawObj->DrawImage(bitmap_user_show_info,0,0,bitmap_user_show_info->GetWidth(),bitmap_user_show_info->GetHeight()); }
2、qt绘图
#pragma once #include <QVBoxLayout> #include <QFrame> #include <QPixmap> #include <QLabel> #include <QList> #include <QPen> #include <QBrush> #include <list> #include "user_define_struct.h" #include "TradeStruct.h" //关联外部定义结构体到绘图模块,实现必要的函数 typedef Skline QDrawKline; typedef SIG_TYPE QDrawSignalType; typedef STRUCT_RSP_SIGNAL QDrawSignal; typedef PositionDetail QDrawPosition; class QKlineInfo : public QWidget { public: QKlineInfo(QWidget *parent) : QWidget(parent), m_precision(0) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(1, 1, 1, 1); layout->addWidget(new QLabel(u8"<span style='color:white;'>日期</span>")); m_lb_date = new QLabel(this); layout->addWidget(m_lb_date); layout->addWidget(new QLabel(u8"<span style='color:white;'>时间</span>")); m_lb_time = new QLabel(this); layout->addWidget(m_lb_time); layout->addWidget(new QLabel(u8"<span style='color:white;'>开盘</span>")); m_lb_open = new QLabel(this); layout->addWidget(m_lb_open); layout->addWidget(new QLabel(u8"<span style='color:white;'>最高</span>")); m_lb_high = new QLabel(this); layout->addWidget(m_lb_high); layout->addWidget(new QLabel(u8"<span style='color:white;'>最低</span>")); m_lb_low = new QLabel(this); layout->addWidget(m_lb_low); layout->addWidget(new QLabel(u8"<span style='color:white;'>收盘</span>")); m_lb_close = new QLabel(this); layout->addWidget(m_lb_close); layout->addWidget(new QLabel(u8"<span style='color:white;'>成交</span>")); m_lb_volume = new QLabel(this); layout->addWidget(m_lb_volume); layout->addWidget(new QLabel(u8"<span style='color:white;'>持仓</span>")); m_lb_interest = new QLabel(this); layout->addWidget(m_lb_interest); } void SetPrecision(int precision) { m_precision = precision; } void UpdateKline(QDrawKline& k, double& last_close) { m_lb_date->setText(QString("<span style='color:white;'>%1</span>").arg(k.Date())); m_lb_time->setText(QString("<span style='color:white;'>%1</span>").arg(k.Time())); m_lb_open->setText(QString("<span style='color:%1;'>%2</span>").arg(GetColor(k.Open(), last_close)).arg(QString::number(k.Open(), 'f', m_precision))); m_lb_high->setText(QString("<span style='color:%1;'>%2</span>").arg(GetColor(k.High(), last_close)).arg(QString::number(k.High(), 'f', m_precision))); m_lb_low->setText(QString("<span style='color:%1;'>%2</span>").arg(GetColor(k.Low(), last_close)).arg(QString::number(k.Low(), 'f', m_precision))); m_lb_close->setText(QString("<span style='color:%1;'>%2</span>").arg(GetColor(k.Close(), last_close)).arg(QString::number(k.Close(), 'f', m_precision))); m_lb_volume->setText(QString("<span style='color:yellow;'>%1</span>").arg(k.Volume())); m_lb_interest->setText(QString("<span style='color:yellow;'>%1</span>").arg(QString::number(k.Interest(), 'f', 0))); } protected: QString GetColor(const double& this_value,const double& last_value) { if (this_value > last_value) return "#DB3320"; if (this_value < last_value) return "#00E700"; return "#E7E7E7"; } protected: QLabel* m_lb_date; QLabel* m_lb_time; QLabel* m_lb_open; QLabel* m_lb_high; QLabel* m_lb_low; QLabel* m_lb_close; QLabel* m_lb_volume; QLabel* m_lb_interest; int m_precision; }; class QDrawModule : public QFrame { Q_OBJECT using kline_riter = std::list<QDrawKline>::reverse_iterator; using curve_riter = std::list<double>::reverse_iterator; struct DataCoverage { std::list<QDrawKline>* m_pListKline; std::list<double>* m_pListCurve; kline_riter m_offset_kline_iter; curve_riter m_offset_curve_iter; QPixmap m_pixCoverage; QString name; QColor clr; DataCoverage() { m_pListKline = nullptr; m_pListCurve = nullptr; } bool operator ==(const DataCoverage& other) { bool bRet = m_pListKline == other.m_pListKline; bRet &= m_pListCurve == other.m_pListCurve; return bRet; } void ResetIter() { if (m_pListKline) m_offset_kline_iter = m_pListKline->rbegin(); if (m_pListCurve) m_offset_curve_iter = m_pListCurve->rbegin(); } }; struct UserPosition { int user_key; QDrawPosition* m_position; UserPosition() { user_key = 0; m_position = nullptr; } }; #define SHOW_ALL_POSITION INT_MAX int m_showUserPosition; QList<UserPosition> m_userPosition;//用户仓位 struct UserSignal { int user_key; std::list<QDrawSignal>* m_signal; }; QList<UserSignal> m_userSignal; public: QDrawModule(QWidget *parent); ~QDrawModule(); static int PushOneKey() { return ++user_key_index; } public: void SetListKline(std::list<QDrawKline>* p);//设置K线容器 void SetCurve(std::list<double>* p, QString name, QColor clr);//设置曲线容器 void RemoveCoverage(void* p);//移除容器对应的图层 void SetInterval(int interval);//间距 void SetSpace(int top, int bottom, int left, int right);//上下空挡 void SetDataCount(int count);//总数 void SetOffset(int offset);//与最后一根的距离 void SetPrecision(int precision);//设置精度,小数点后保留位数,类似期权数据大于0小于1的情况 void SetUserPosition(int user_key, QDrawPosition* p); void SetShowUserPosition(int user_key); public slots: void DoDraw(bool draw_all); void PageUpDown(bool up_down); protected: void DrawCoordinate();//坐标线 void DrawProcess();//绘图过程 //更新最大最小值 void UpdateMaxMin(); void UpdateMaxMin(kline_riter riter_start, kline_riter riter_end, kline_riter& offset_riter); void UpdateMaxMin(curve_riter riter_start, curve_riter riter_end, curve_riter& offset_riter); void DrawKline(DataCoverage& dc);//K线 void DrawCurve(DataCoverage& dc);//曲线 void DrawSignal();//信号位置 void DrawPosition();//仓位线 void ErasePixmap(QPixmap& pix, QRect& rc);//部分擦除 int DataToPointY(const double& value, const double& min_data, const double& unit_data);//数值转为Y坐标 double PointYToData(int& y, const double& min_data, const double& unit_data); void CenterPointCount();//中心点个数 void UnitData();//Y轴单位 void DrawOneKline(QPixmap& pix, int x, QDrawKline& k, QColor& clr);//画一个K线 protected: void resizeEvent(QResizeEvent *event); void focusInEvent(QFocusEvent *event); void focusOutEvent(QFocusEvent *event); void leaveEvent(QEvent *event); void mouseMoveEvent(QMouseEvent *event);//数值提示 void mouseDoubleClickEvent(QMouseEvent *event);//十字线 void keyReleaseEvent(QKeyEvent *event);//上下左右 void paintEvent(QPaintEvent *event); protected: QList<DataCoverage> m_dataCoverage;//数据&图层 static int user_key_index; private: bool m_draw_all;//是否全部重绘 //数据结构参数 double m_try_min_data, m_try_max_data;//更新之后的最大最小 double m_min_data, m_max_data;//实际最大最小 int m_min_data_x, m_max_data_x;//实际最大最小对应的位置 double m_unit_data;//一个像素点对应的高度数值 int m_max_center_point;//中心点的最大数 int m_widget_width, m_widget_height; int m_interval; bool m_cross; QRect m_space; int m_precision; int m_total_count; int m_offset; int m_last_offset; //绘画工具 QPixmap m_pixCoor; QPixmap m_pixCross; QPixmap m_pixPosition; QPen m_pen; QBrush m_brush; QColor m_clrRed; QColor m_clrGreen; QKlineInfo* m_widget_info; };
#include "QDrawModule.h" #include <QPainter> #include <QKeyEvent> #include <QMouseEvent> #include <QFocusEvent> #include "qcoreevent.h" int QDrawModule::user_key_index = 0; QDrawModule::QDrawModule(QWidget *parent) : QFrame(parent) { m_clrGreen = QColor(0, 255, 255); m_clrRed = QColor(255, 60, 57); m_offset = 0; m_last_offset = m_offset; m_interval = 11; m_precision = 0; m_cross = false; m_showUserPosition = SHOW_ALL_POSITION; m_try_min_data = m_min_data = 1.79769313486231570E+308; m_try_max_data = m_max_data = -1.79769313486231570E+308; m_widget_info = new QKlineInfo(this); m_widget_info->setObjectName("m_widget_info"); m_widget_info->setStyleSheet("#m_widget_info{border:1px solid white}"); if (!m_cross) m_widget_info->hide(); setMouseTracking(true); setFocusPolicy(Qt::FocusPolicy::ClickFocus); } QDrawModule::~QDrawModule() { } void QDrawModule::SetListKline(std::list<QDrawKline>* p) { DataCoverage dc; dc.m_pListKline = p; dc.ResetIter(); m_dataCoverage.push_back(dc); } void QDrawModule::SetCurve(std::list<double>* p, QString name, QColor clr) { DataCoverage dc; dc.m_pListCurve = p; dc.name = name; dc.clr = clr; dc.ResetIter(); m_dataCoverage.push_back(dc); } void QDrawModule::RemoveCoverage(void * p) { for (auto& dc : m_dataCoverage) { if (dc.m_pListKline == p || dc.m_pListCurve == p) { m_dataCoverage.removeOne(dc); break; } } } void QDrawModule::SetInterval(int interval) { m_interval = interval; } void QDrawModule::SetSpace(int top, int bottom, int left, int right) { m_space.setTop(top); m_space.setBottom(bottom); m_space.setLeft(left); m_space.setRight(right); m_widget_info->setFixedSize(left - 1, 340); } void QDrawModule::SetDataCount(int count) { m_total_count = count; } void QDrawModule::SetOffset(int offset) { m_offset = offset; } void QDrawModule::SetPrecision(int precision) { m_precision = precision; m_widget_info->SetPrecision(precision); } void QDrawModule::SetUserPosition(int user_key, QDrawPosition* p) { UserPosition up; up.user_key = user_key; up.m_position = p; m_userPosition.push_back(up); } void QDrawModule::SetShowUserPosition(int user_key) { m_showUserPosition = user_key; } void QDrawModule::DoDraw(bool draw_all) { m_draw_all = draw_all; CenterPointCount(); UpdateMaxMin(); if (m_try_max_data != m_max_data || m_try_min_data != m_min_data) { m_max_data = m_try_max_data; m_min_data = m_try_min_data; m_draw_all = true; } UnitData(); DrawProcess(); if (m_cross) { m_pixCross = QPixmap(m_widget_width, m_widget_height); m_pixCross.fill(Qt::transparent); } update(); } void QDrawModule::PageUpDown(bool up_down) { if (up_down) { m_offset -= m_max_center_point; if (m_offset < 0) m_offset = 0; } else { int max_offset = m_max_center_point; for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) { int offset_count = 0; auto riter = dc.m_offset_kline_iter; while (riter != dc.m_pListKline->rend() && offset_count < m_max_center_point) { riter++; offset_count++; } if (max_offset > offset_count) max_offset = offset_count; } if (dc.m_pListCurve) { int offset_count = 0; auto riter = dc.m_offset_curve_iter; while (riter != dc.m_pListCurve->rend() && offset_count < m_max_center_point) { riter++; offset_count++; } if (max_offset > offset_count) max_offset = offset_count; } } m_offset += max_offset; } } void QDrawModule::DrawCoordinate() { int work_height = m_widget_height - m_space.top() - m_space.bottom(); int line_numb = work_height / 50;//需要绘制的坐标线个数 double line_interval = (m_max_data - m_min_data) / (line_numb + 1); double fundation = 1; if (line_interval > 1) { int interval = line_interval; while (interval / 10 > 0) { interval /= 10; fundation *= 10; } fundation *= interval + 1; } else if (line_interval > 0) { while (line_interval < 1) { line_interval *= 10; fundation *= 10; } line_interval = (int)line_interval; fundation = line_interval / fundation; } else { return; } double start_line = ((int)(m_min_data / fundation) + 1) * fundation; m_pixCoor = QPixmap(m_widget_width, m_widget_height); m_pixCoor.fill(Qt::transparent); QPainter painter(&m_pixCoor); m_pen.setColor(QColor(132, 0, 0)); m_pen.setWidth(1); m_pen.setStyle(Qt::PenStyle::SolidLine); painter.setPen(m_pen); painter.drawLine(m_space.left() - 1, 0, m_space.left() - 1, m_widget_height); m_pen.setStyle(Qt::PenStyle::DotLine); QPen white_pen = m_pen; white_pen.setColor(QColor(192, 192, 192)); white_pen.setWidth(1); white_pen.setStyle(Qt::PenStyle::SolidLine); do { int point_y = DataToPointY(start_line, m_min_data, m_unit_data); painter.setPen(m_pen); painter.drawLine(m_space.left(), point_y, m_widget_width - m_space.right(), point_y); painter.setPen(white_pen); painter.drawText(0, point_y - 15, m_space.left() - 2, 30, Qt::AlignRight | Qt::AlignCenter, QString::number(start_line, 'f', m_precision)); start_line += fundation; } while (start_line <= m_max_data); } void QDrawModule::UpdateMaxMin() { m_try_min_data = 1.79769313486231570E+308; m_try_max_data = -1.79769313486231570E+308; for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) { UpdateMaxMin(dc.m_pListKline->rbegin(), dc.m_pListKline->rend(), dc.m_offset_kline_iter); } if (dc.m_pListCurve) { UpdateMaxMin(dc.m_pListCurve->rbegin(), dc.m_pListCurve->rend(), dc.m_offset_curve_iter); } } } void QDrawModule::DrawProcess() { if (m_draw_all) DrawCoordinate(); for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) DrawKline(dc); if (dc.m_pListCurve) DrawCurve(dc); } DrawSignal(); DrawPosition(); } #define UPDATE_OFFSET_RITER()\ int bak_offset = m_offset;\ if (bak_offset > m_last_offset)\ {\ while (bak_offset != m_last_offset)\ {\ if (offset_riter != riter_end)\ {\ offset_riter++;\ }\ bak_offset--;\ }\ }\ else if (bak_offset < m_last_offset)\ {\ while (bak_offset != m_last_offset)\ {\ if (offset_riter != riter_start)\ {\ offset_riter--;\ }\ bak_offset++;\ }\ } void QDrawModule::UpdateMaxMin(kline_riter riter_start, kline_riter riter_end, kline_riter& offset_riter) { UPDATE_OFFSET_RITER() riter_start = offset_riter; int start_x = m_max_center_point; while (riter_start != riter_end && start_x > 0) { if (riter_start->High() > m_try_max_data) { m_try_max_data = riter_start->High(); m_max_data_x = start_x; } if (riter_start->Low() < m_try_min_data) { m_try_min_data = riter_start->Low(); m_min_data_x = start_x; } riter_start++; start_x--; } } void QDrawModule::UpdateMaxMin(curve_riter riter_start, curve_riter riter_end, curve_riter& offset_riter) { UPDATE_OFFSET_RITER() riter_start = offset_riter; int start_x = m_max_center_point; while (riter_start != riter_end && start_x > 0) { if (*riter_start > m_try_max_data) m_try_max_data = *riter_start; if (*riter_start < m_try_min_data) m_try_min_data = *riter_start; start_x--; riter_start++; } } void QDrawModule::DrawKline(DataCoverage& dc) { QPixmap& myPix = dc.m_pixCoverage; kline_riter riter_start = dc.m_offset_kline_iter == dc.m_pListKline->rend() ? dc.m_pListKline->rbegin() : dc.m_offset_kline_iter; kline_riter riter_end = dc.m_pListKline->rend(); int start_x = m_max_center_point; if (m_draw_all) { myPix = QPixmap(m_widget_width - m_space.left(), m_widget_height); myPix.fill(Qt::transparent); while (riter_start != riter_end && start_x > 0) { DrawOneKline(myPix, start_x, *riter_start, dc.clr); riter_start++; start_x--; } } else { QRect rc; rc.setLeft((m_max_center_point - 1) * (m_interval + m_interval / 5)); rc.setTop(0); rc.setHeight(m_widget_height); rc.setWidth(m_interval); ErasePixmap(myPix, rc); DrawOneKline(myPix, start_x, *riter_start, dc.clr); } } void QDrawModule::DrawCurve(DataCoverage& dc) { QPixmap& myPix = dc.m_pixCoverage; curve_riter riter_start = dc.m_offset_curve_iter == dc.m_pListCurve->rend() ? dc.m_pListCurve->rbegin() : dc.m_offset_curve_iter; curve_riter riter_end = dc.m_pListCurve->rend(); if (m_draw_all) { myPix = QPixmap(m_widget_width - m_space.left(), m_widget_height); myPix.fill(Qt::transparent); } else { QRect rc; int cent_x2 = (m_max_center_point - 2) * (m_interval + m_interval / 5) + m_interval / 2; rc.setLeft(cent_x2); rc.setTop(0); rc.setHeight(m_widget_height); rc.setWidth(m_interval); ErasePixmap(myPix, rc); } QPainter painter(&myPix); m_pen.setColor(dc.clr); m_pen.setWidth(1); m_pen.setStyle(Qt::SolidLine); painter.setPen(m_pen); int start_x = m_max_center_point; bool bFirstPoint = true; QPoint last_point; if (m_draw_all) { while (riter_start != riter_end && start_x > 0) { int cent_x = (start_x - 1) * (m_interval + m_interval / 5) + m_interval / 2; QPoint this_point(cent_x, DataToPointY(*riter_start, m_min_data, m_unit_data)); if (!bFirstPoint) { painter.drawLine(this_point, last_point); } last_point = this_point; bFirstPoint = false; riter_start++; start_x--; } } else { if (riter_start != riter_end && start_x > 0) { int cent_x = (start_x - 1) * (m_interval + m_interval / 5) + m_interval / 2; last_point = QPoint(cent_x, DataToPointY(*riter_start, m_min_data, m_unit_data)); riter_start++; start_x--; } if (riter_start != riter_end && start_x > 0) { int cent_x = (start_x - 1) * (m_interval + m_interval / 5) + m_interval / 2; QPoint this_point(cent_x, DataToPointY(*riter_start, m_min_data, m_unit_data)); painter.drawLine(this_point, last_point); } } } void QDrawModule::DrawSignal() { } void QDrawModule::DrawPosition() { if (m_userPosition.size() == 0) return; m_pixPosition = QPixmap(m_widget_width - m_space.left() - m_space.right(), m_widget_height); m_pixPosition.fill(Qt::transparent); QPainter painter(&m_pixPosition); auto lambda_func = [&](int user_key, QColor clr, int& count, double& avg_price) { m_pen.setColor(clr); m_pen.setStyle(Qt::PenStyle::DashLine); painter.setPen(m_pen); int y = DataToPointY(avg_price, m_min_data, m_unit_data); QString strInfo = QString(u8"[%1]%2手,均价:%3").arg(user_key). arg(count).arg(QString::number(avg_price, 'f', m_precision + 1)); int txt_length = strInfo.size() * 8; if (y < m_space.top()) { painter.drawText(1, m_space.top(), txt_length, 20, Qt::AlignLeft | Qt::AlignCenter, strInfo); painter.drawLine(txt_length, m_space.top() + 10, m_widget_width - m_space.right(), m_space.top() + 10); } else if (y > m_widget_height - m_space.bottom()) { painter.drawText(1, m_widget_height - m_space.bottom() - 20, txt_length, 20, Qt::AlignLeft | Qt::AlignCenter, strInfo); painter.drawLine(txt_length, m_widget_height - m_space.bottom() - 10, m_widget_width - m_space.right(), m_widget_height - m_space.bottom() - 10); } else { painter.drawText(1, y - 10, txt_length, 20, Qt::AlignLeft | Qt::AlignCenter, strInfo); painter.drawLine(txt_length, y, m_widget_width - m_space.right(), y); } }; for (auto& up : m_userPosition) { if (!up.m_position) continue; if (m_showUserPosition == SHOW_ALL_POSITION || m_showUserPosition == up.user_key) { if (up.m_position->GetLongHolding() > 0) { lambda_func(up.user_key, "#FF5C5C", up.m_position->GetLongHolding(), up.m_position->GetLongAvgPrice()); } else { lambda_func(up.user_key, "#00FF00", up.m_position->GetShortHolding(), up.m_position->GetShortAvgPrice()); } } } } void QDrawModule::ErasePixmap(QPixmap& pix, QRect& rc) { QPainter painter(&pix); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.fillRect(rc, Qt::transparent); } int QDrawModule::DataToPointY(const double& value, const double& min_data, const double& unit_data) { return m_widget_height - m_space.bottom() - (value - min_data) / unit_data; } double QDrawModule::PointYToData(int& y, const double& min_data, const double& unit_data) { return (m_widget_height - m_space.bottom() - y) * unit_data + min_data; } void QDrawModule::CenterPointCount() { int work_width = m_widget_width - m_space.left() - m_space.right(); m_max_center_point = work_width / (m_interval + m_interval / 5); } void QDrawModule::UnitData() { int work_height = m_widget_height - m_space.top() - m_space.bottom(); m_unit_data = (m_max_data - m_min_data) / work_height; } void QDrawModule::DrawOneKline(QPixmap & pix, int x, QDrawKline & k, QColor& clr) { QPainter painter(&pix); int clr_idx; if (k.Open() > k.Close()) { m_pen.setColor(m_clrGreen); clr_idx = 1; } else if (k.Open() < k.Close()) { m_pen.setColor(m_clrRed); clr_idx = 0; } else { m_pen.setColor(Qt::white); clr_idx = 2; } m_pen.setWidth(1); m_pen.setStyle(Qt::PenStyle::SolidLine); painter.setPen(m_pen); painter.setBrush(m_brush); int left_x = (x - 1) * (m_interval + m_interval / 5); int cent_x = left_x + m_interval / 2; int right_x = left_x + m_interval - 1; int top_y = DataToPointY(k.High(), m_min_data, m_unit_data); int open_y = DataToPointY(k.Open(), m_min_data, m_unit_data); int close_y = DataToPointY(k.Close(), m_min_data, m_unit_data); int low_y = DataToPointY(k.Low(), m_min_data, m_unit_data); if (clr_idx == 0) {//红色空心 painter.drawRect(QRect(left_x, close_y, m_interval - 1, open_y - close_y)); painter.drawLine(cent_x, top_y, cent_x, close_y); painter.drawLine(cent_x, low_y, cent_x, open_y); } else if (clr_idx == 1) {//绿色实心 painter.fillRect(QRect(left_x, open_y, m_interval, close_y - open_y), m_clrGreen); painter.drawLine(cent_x, top_y, cent_x, low_y); } else {//白色十字 painter.drawLine(left_x, open_y, right_x, open_y); painter.drawLine(cent_x, top_y, cent_x, low_y); } if (x == m_min_data_x) {//最小值 m_pen.setColor(Qt::white); painter.setPen(m_pen); if (cent_x > 60) painter.drawText(cent_x - 60, low_y - 15, 60, 30, Qt::AlignRight | Qt::AlignCenter, QString(u8"%1→").arg(QString::number(k.Low(), 'f', m_precision))); else painter.drawText(cent_x - 4, low_y - 15, 60, 30, Qt::AlignLeft | Qt::AlignCenter, QString(u8"←%1").arg(QString::number(k.Low(), 'f', m_precision))); } if (x == m_max_data_x) {//最大值 m_pen.setColor(Qt::white); painter.setPen(m_pen); if (cent_x > 60) painter.drawText(cent_x - 60, top_y - 15, 60, 30, Qt::AlignRight | Qt::AlignCenter, QString(u8"%1→").arg(QString::number(k.High(), 'f', m_precision))); else painter.drawText(cent_x - 4, top_y - 15, 60, 30, Qt::AlignLeft | Qt::AlignCenter, QString(u8"←%1").arg(QString::number(k.High(), 'f', m_precision))); } } void QDrawModule::resizeEvent(QResizeEvent * event) { m_widget_width = this->width(); m_widget_height = this->height(); DoDraw(true); m_widget_info->move(0, m_space.top()); } void QDrawModule::focusInEvent(QFocusEvent *event) { grabKeyboard(); } void QDrawModule::focusOutEvent(QFocusEvent *event) { releaseKeyboard(); } void QDrawModule::leaveEvent(QEvent *event) { m_pixCross = QPixmap(m_widget_width, m_widget_height); m_pixCross.fill(Qt::transparent); update(); } void QDrawModule::mouseMoveEvent(QMouseEvent *event) { if (event->pos().x() <= m_space.left() || event->pos().x() >= (m_widget_width - m_space.right())) { leaveEvent(nullptr); return; } m_pixCross = QPixmap(m_widget_width, m_widget_height); m_pixCross.fill(Qt::transparent); int y = event->pos().y(); m_pen.setStyle(Qt::PenStyle::SolidLine); m_pen.setColor(Qt::white); QPainter painter(&m_pixCross); painter.setPen(m_pen); painter.fillRect(0, y - 10, m_space.left() - 2, 20, QColor(0, 97, 255)); painter.drawText(0, y - 10, m_space.left() - 2, 20, Qt::AlignRight | Qt::AlignCenter, QString::number(PointYToData(y, m_min_data, m_unit_data), 'f', m_precision)); if (m_cross) { for (auto& dc : m_dataCoverage) { painter.drawPixmap(m_space.left(), 0, dc.m_pixCoverage); } painter.setCompositionMode(QPainter::CompositionMode_Difference); painter.setPen(m_pen); painter.drawLine(m_space.left(), y, m_widget_width - m_space.right(), y); int draw_x = 0; int str_x = m_space.left() + 1; double last_close; for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) { if (dc.m_offset_kline_iter == dc.m_pListKline->rend()) continue; auto riter = dc.m_offset_kline_iter; int x = m_max_center_point; int main_x = event->pos().x() - m_space.left(); int last_left = m_widget_width - m_space.left(); while (x > 0 && riter != dc.m_pListKline->rend()) { int left_x = (x - 1) * (m_interval + m_interval / 5); if (main_x >= left_x && main_x <= last_left) { draw_x = left_x + m_interval / 2; QDrawKline k = *riter; last_close = k.Open(); riter++; if (riter != dc.m_pListKline->rend()) last_close = riter->dClose; if (m_widget_info->isVisible()) m_widget_info->UpdateKline(k, last_close); break; } riter++; x--; last_left = left_x; } } if (dc.m_pListCurve) { if (dc.m_offset_curve_iter == dc.m_pListCurve->rend()) continue; auto riter = dc.m_offset_curve_iter; int x = m_max_center_point; int main_x = event->pos().x() - m_space.left(); int last_left = m_widget_width - m_space.left(); while (x > 0 && riter != dc.m_pListCurve->rend()) { int left_x = (x - 1) * (m_interval + m_interval / 5); if (main_x >= left_x && main_x <= last_left) { draw_x = left_x + m_interval / 2; m_pen.setColor(dc.clr); painter.setPen(m_pen); QString strIndex = dc.name + ":"; strIndex.append(QString::number(*riter, 'f', m_precision)); painter.drawText(str_x, 0, strIndex.length() * 8, 20, Qt::AlignLeft | Qt::AlignCenter, strIndex); str_x += strIndex.length() * 8 + 5; break; } riter++; x--; last_left = left_x; } } } m_pen.setColor(Qt::white); painter.setPen(m_pen); if (draw_x != 0) painter.drawLine(draw_x + m_space.left(), 0, draw_x + m_space.left(), m_widget_height); } update(); } void QDrawModule::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_cross = !m_cross; if (m_cross) setCursor(Qt::BlankCursor); else setCursor(Qt::ArrowCursor); for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) { if (!m_cross) { m_widget_info->hide(); } else { m_widget_info->show(); } break; } } mouseMoveEvent(event); } } void QDrawModule::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up) { if (m_interval < 61) { m_interval += 2; } else return; } else if (event->key() == Qt::Key_Down) { if (m_interval > 1) { m_interval -= 2; } else return; } else if (event->key() == Qt::Key_Left) { QPoint point = mapFromGlobal(QCursor::pos()); if (!m_cross || point.x() <= (m_space.left() + m_interval + m_interval / 5)) { bool enable_offset = true; for (auto& dc : m_dataCoverage) { if (dc.m_pListKline) { auto riter = dc.m_offset_kline_iter; enable_offset &= riter != dc.m_pListKline->rend(); } if (dc.m_pListCurve) { auto riter = dc.m_offset_curve_iter; enable_offset &= riter != dc.m_pListCurve->rend(); } } if (enable_offset) m_offset++; } if (m_cross) { if (point.x() > (m_space.left() + m_interval + m_interval / 5) && point.x() <= (m_widget_width - m_space.right())) { QCursor::setPos(mapToGlobal(QPoint(point.x() - m_interval - m_interval / 5, point.y()))); } } } else if (event->key() == Qt::Key_Right) { QPoint point = mapFromGlobal(QCursor::pos()); if (!m_cross || point.x() > (m_space.left() + (m_max_center_point - 1) * (m_interval + m_interval / 5))) { if (m_offset > 0) { m_offset--; } } if (m_cross) { QPoint point = mapFromGlobal(QCursor::pos()); if (point.x() > m_space.left() && point.x() <= (m_space.left() + (m_max_center_point - 1) * (m_interval + m_interval / 5))) { QCursor::setPos(mapToGlobal(QPoint(point.x() + m_interval + m_interval / 5, point.y()))); } } } else if (event->key() == Qt::Key_PageUp) { PageUpDown(true); } else if (event->key() == Qt::Key_PageDown) { PageUpDown(false); } else { return; } DoDraw(true); m_last_offset = m_offset; } void QDrawModule::paintEvent(QPaintEvent * event) { QPainter painter(this); painter.drawPixmap(0, 0, m_pixCoor); for (auto& dc : m_dataCoverage) { painter.drawPixmap(m_space.left(), 0, dc.m_pixCoverage); } painter.drawPixmap(m_space.left(), 0, m_pixPosition); painter.drawPixmap(0, 0, m_pixCross); }