zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    IntelliJ IDEA 常用快捷键和设置
    Code Project精彩系列(2)
    Code Project精彩系列(2)
    Code Project精彩系列(2)
    Apache和Tomcat区别
    Apache和Tomcat区别
    Apache和Tomcat区别
    如何在 GitHub 建立个人主页和项目演示页面
    如何在 GitHub 建立个人主页和项目演示页面
    如何在 GitHub 建立个人主页和项目演示页面
  • 原文地址:https://www.cnblogs.com/rmdmn/p/11211636.html
Copyright © 2011-2022 走看看