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);
}

 

posted @ 2019-07-19 10:15  股市的小黄花  阅读(1800)  评论(0编辑  收藏  举报