VC++開發股票軟件
VC炒股軟件開發
文檔說明:
此文檔適合VC++的初學者,高手也可參考(希望能提出寶貴意見)。
開發前準備:
這是一個根據股票信息的數據繪的幾個制界面,數據來源通信達軟件的數據文件,通常在安裝了通信達以后并下載數據到本地以后就有了。
1.數據文件的準備:
假如你安裝通信達的目錄是:D:\jcb_gx。那么對應的數據文件就在D:\jcb_gx\vipdoc\目錄下,里面每一個目錄下就是一類股票的數據,我們開發這個界面需要用到的是每個目錄下的lday目錄下的.lday后綴名的文件。每一個文件里面存放的是一支滾票的數據信息。我當時開發用到了兩類股票的信息,分別對應的目錄是:D:\jcb_gx\vipdoc\sh\lday和D:\jcb_gx\vipdoc\sz\lday。其實每類開發的方法完全一樣,唯一不同就是讀取不同的目錄而已。
2.文件數據結構:
準備好數據以后,還有一點是必須知道的,不然也沒有辦法進行下去,就是文件里面的數據格式是怎樣的。因為我用的是通信達的數據文件,所以只需要在www.g.cn查詢通信達的數據格式就可以了,如下:
typedef struct
{ //共32字節
int date; //4字節 如20091229
int open; //開盤價
int high; //最高價
int low; //最低價
int close; //收盤價
float amount; //成交額
int vol; //成交量
int reservation; //保留值
} StockData;
詳細開發過程(包括我的思路和具體實現):

1.實現如上圖的界面,需要做如下事情
(1)讀一個目錄下的所有文件,并從文件名中提取出相應股票的代碼
可行性分析:
首先我們打開的是一個目錄,然后從這個目錄中讀出里面所有的文件名,目錄存放的內容其實就是此目錄下的文件名或目錄名。用到兩個函數,一個FindFirstFile查找到一個目錄下的第一個文件名,另一個FindNextFile查找下一個文件名。這樣就可以遍歷一個目錄下的所有文件名了。
具體實現:
BOOLCTongXinDaView::ReadFileData(LPCSTR path)
{
//path是這種形式的參數:D:\\jcb_gx\\vipdoc\\sh\\lday\\*
m_iCount = 0;
WIN32_FIND_DATA tFind = {0};
int i = 0;
CString strTemp;
HANDLE hSearch = ::FindFirstFile(path, &tFind);
if (hSearch == INVALID_HANDLE_VALUE)
{
return FALSE;
}
//過濾掉.和..文件
::FindNextFile(hSearch, &tFind);
while (::FindNextFile(hSearch, &tFind))
{
strTemp.Format("%s",tFind.cFileName);
m_File[i].Format("%s",path);
//去掉查詢用到的*通配符
m_File[i] =m_File[i].Left(m_File[i].GetLength()-1);
m_File[i] +=strTemp;
//從文件名中提取股票代碼
m_FileName[i] =strTemp.Mid(2, 6);
i++;
}
m_iPageCount = i / 31 + 1; //求出需要顯示的總頁面數
m_iLeave = i % 31; //最后一頁顯示的數據
m_CurrFile = m_File[m_iCount]; //保存選中的文件名
::FindClose(hSearch);
return TRUE;
}
注意事項:每一個目錄下都有這兩個目錄文件:“.”和“..”。它們分別代表本目錄和父目錄(就是上層目錄),必須過濾掉這兩個目錄文件。還好每次這兩個目錄文件總是最先被讀出,所以前兩次讀出來的信息直接不管就可以了。
上面的函數被相應的每一個菜單項事件調用,就是針對不同的股票用一個菜單項打開。
(2)頁面的顯示:
可行性分析:
先說說我當時需要完成的現實任務,每頁顯示31行(具體可以變動,但是31 行效果比較好),顯示3列,第一列索引號,也就是起個計數的作用,第二列就是剛才我們提取到的股票代碼號,第三列隨便填充4個漢字。還要求畫一條線表示當前選中的股票,鼠標上下滾動和PageDown,PageUp按鍵實現上下翻頁功能,鼠標點擊選中點擊最近的一支股票,按鍵上下鍵也可以移動股票選擇。明白了需要實現的功能,我現在就一步一步來完成。這里需要用到文字輸出函數DrawText。
具體實現:
void CTongXinDaView::DrawText(CDC *pDC, int page)
{
CRect rt;
GetClientRect(&rt);
int high = rt.Width() / 55;
int y = high;//控制每一行顯示數據的增量
CString strLine;
int number = 1;
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(200, 200, 200));
strLine.Format(" 代碼 名稱 日期 開牌價 最高價 最低價 收盤價 多多 成交量 ");
pDC->TextOut(0, 0, strLine);
//控制最后一頁只顯示剩余的
if (page == m_iPageCount-1)
{
for (int i=page * SCREENHEIGHT; i<(page*SCREENHEIGHT + m_iLeave); i++)
{
if (i == m_iCount)
{
CPen pen(PS_SOLID, 1, RGB(255, 255, 0));
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->MoveTo(50, y+high-5);
pDC->LineTo(rt.right, y+high-5);
pDC->SelectObject(pOldPen);
}
strLine.Format("%d", i+1);
pDC->DrawText(strLine, CRect(0, y, 40, y+high), DT_RIGHT);
pDC->DrawText(m_FileName[i], CRect(50, y, 100, y+high), DT_LEFT);
pDC->DrawText("長城開發", CRect(110, y, 180, y+high), DT_LEFT);
y = y + high;
}
}
else
{
for (int i=page * SCREENHEIGHT; i<(page+1)*SCREENHEIGHT; i++)
{
if (i == m_iCount)
{
CPen pen(PS_SOLID, 1, RGB(255, 255, 0));
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->MoveTo(50, y+high-7);
pDC->LineTo(rt.right, y+high-7);
pDC->SelectObject(pOldPen);
}
strLine.Format("%d", i+1);
pDC->DrawText(strLine, CRect(0, y, 40, y+high), DT_RIGHT);
pDC->DrawText(m_FileName[i], CRect(50, y, 100, y+high), DT_LEFT);
pDC->DrawText("長城開發", CRect(110, y, 180, y+high), DT_LEFT);
y = y + high;
}
}
}
注意事項:
1.最后一頁數據條目不夠,需要特殊處理。
2.輸出函數用的是DawText而不是TextOut,是為了使輸出對齊。
3.鼠標和按鍵的響應只是簡單邏輯處理和顯示不同的數據。
(3)分頁顯示和選取當前一支股票的實現思想說明:
可行性分析:
上面已經實現了頁面的顯示,現在說說怎樣控制上下翻頁和鼠標鍵盤實現選中一支股票(我是用一條黃色的線標示)。上下翻頁時通過鍵盤上的PageDown和PageUp,還有鼠標滾輪控制的。其實原理很簡單,只需要我們在讀目錄下每個股票文件時記錄一下這個目錄下的股票數量,也就是我們需要顯示的所有行數。我們一個常量記錄每一頁顯示的數目,用總數除以這個數就是總共需要的頁數。然后用一個變量記錄當前顯示的是第幾頁,上下翻頁就是對這個變量的加減操作了。選中一支股票則是根據我們點擊的鼠標的位置來決定,因為每一行所占的頁面寬度是一樣的,只需要判斷點擊在哪一行所處的位置就可以了。當然也需要用變量記錄選中的是那一只股票,總數剛才我們也記錄了,所以很容易記錄當前的哪一只股票,只是需要注意翻頁后選擇的股票相應的加減一頁的顯示的股票數。最后一點就是注意一些邊界條件的處理。
2.實現如下圖的界面:

(1)讀取選中的股票文件,并保存以為繪圖使用這些數據
可行性分析:
按照固定的數據格式把文件中的數據讀入到一個結構體中保存,用fread每次讀入固定長度的數據格式接可以了。
具體實現:
/*
* 函數名稱: ReadData
* 輸 入:
* 輸 出:
* 功能描述: 從當前文件中讀取數據
* 全局變量:
* 作 者: 吳友強
* 日 期: 2009年11月29日
* 修 改:
* 日 期:
*/
void CTongXinDaView::ReadData()
{
FILE *fp;
m_iDataItemCount = -1;
//打開當前文件
if ((fp = fopen(m_CurrFile, "rb")) == NULL)
{
return ;
}
while (!feof(fp))
{
m_iDataItemCount++;
fread(&m_StockData[m_iDataItemCount],sizeof(StockData),1,fp);
//求最大的日期
if (m_StockData[m_iDataItemCount].date > m_iMaxDate)
{
m_iMaxDate = m_StockData[m_iDataItemCount].date;
}
//求最小的日期
if (m_iDataItemCount == 0)
{
m_iMinDate = m_StockData[m_iDataItemCount].date;
}
else if (m_StockData[m_iDataItemCount].date > 0
&& m_StockData[m_iDataItemCount].date < m_iMinDate)
{
m_iMinDate = m_StockData[m_iDataItemCount].date;
}
}
m_iDataItemCount--;//去掉最后一條無用的記錄
m_iStartDay = m_iDataItemCount;
if (m_iDays > m_iDataItemCount)
{
m_iDays = m_iDataItemCount+1;
}
//以前在這里沒有關閉文件,所以當打開一定數量的時候(windows限制的)在打開文件就會失敗
fclose(fp);
}
(2)提取當前需要顯示的數據:
可行性分析:
根據當前需要顯示多少天的數據來提取,從上面我們從文件里面讀取的數據中提取,以后的所謂放大縮小,左右移動就是提取不同天的數據就是了。
具體實現:
/*
* 函數名稱: GetStockData
* 輸 入: days
* days: 屏幕需要顯示的天數
* 輸 出:
* 功能描述: 得到顯示的數據和求取各個最值
* 全局變量:
* 作 者: 吳友強
* 日 期: 2009年11月30日
* 修 改:
* 日 期:
*/
void CTongXinDaView::GetStockData(int days)
{
m_iLowMax = 0;
m_iHighMax = 0;
m_iVolMax = 0;
if (m_sdCurrData != NULL)
{
delete m_sdCurrData;
}
m_sdCurrData = new StockData[days];
for (int i=0; i<days; i++)
{
m_sdCurrData[days-i-1] = m_StockData[m_iStartDay-i-1];
//求本次顯示成交量的最大值
if (m_sdCurrData[days-i-1].vol > m_iVolMax)
{
m_iVolMax = m_sdCurrData[days-i-1].vol;
}
//求本次顯示最高值的最大值
if (m_sdCurrData[days-i-1].high > m_iHighMax)
{
m_iHighMax = m_sdCurrData[days-i-1].high;
}
//求本次顯示最低值的最小值
if (i == 0)
{
m_iLowMax = m_sdCurrData[days-i-1].low;
}
else if (m_sdCurrData[days-i-1].low < m_iLowMax)
{
m_iLowMax = m_sdCurrData[days-i-1].low;
}
}
}
(3)得到需要顯示的數據以后,我們就可以開始繪圖了
可行性分析:
我們得到需要顯示的數據以后,就需要根據當前顯示的寬度和高度來劃分屏幕了,根據客戶的需求大致需要把屏幕分為上中下三部分,第一部分畫成交量的平均值線和一天中的最高值到最低值的一條豎線,還有開盤價和收盤價的矩形圖;第二部分成交總量;第三部分成交價格除以2,3,4,5刻度控制在-4到4資料的線形圖。首先我們必須確定三部分的高度,然后把3個坐標固定下來并繪畫出來,至于坐標的刻度我們可以動態的根據每次需要顯示的數據的最大值和最小值來計算確定,然后根據刻度的比例來畫所有的圖形。其中很多需要計算,具體的情看代碼。下面是整個系統的黑心部分,具體請看代碼注釋。
具體實現:
/*
* 函數名稱: DrawGraphic
* 輸 入: pDC, days
* pDC: 畫圖的CDC指針
* days: 顯示數據的天數
* 輸 出:
* 功能描述: 畫各種坐標以及圖形
* 全局變量:
* 作 者: 吳友強
* 日 期: 2009年11月29日
* 修 改: 吳友強
* 日 期: 2009年12月4日
*/
void CTongXinDaView::DrawGraphic(CDC *pDC, int days)
{
//設置透明繪圖模式
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(200, 0 ,0));
CRect rt;
GetClientRect(&rt);
float average;
float averPri;
float ftemp, ftext;
CString str;
int i = 0;
float xAver;
pDC->DrawText("當前代碼: " + m_FileName[m_iCount], CRect(0, 0, 200, 20), DT_LEFT);
//初始化各個坐標原點
m_ptOrigin[0].x = rt.Width()-100;
m_ptOrigin[0].y = rt.Height()/8 * 3;
m_ptOrigin[1].x = rt.Width()-100;
m_ptOrigin[1].y = rt.Height()/4 * 2;
m_ptOrigin[2].x = rt.Width()-100;
m_ptOrigin[2].y = rt.Height()/4 * 3;
//劃分屏幕為三部分
CPen penRedSolid(PS_SOLID, 1, RGB(200, 0, 0));
CPen *pOldPen = pDC->SelectObject(&penRedSolid);
pDC->MoveTo(0, m_ptOrigin[0].y);
pDC->LineTo(rt.Width(), m_ptOrigin[0].y);
pDC->MoveTo(0, m_ptOrigin[1].y);
pDC->LineTo(rt.Width(), m_ptOrigin[1].y);
pDC->MoveTo(0, m_ptOrigin[2].y);
pDC->LineTo(rt.Width()-100, m_ptOrigin[2].y);
pDC->MoveTo(rt.Width()-100, 0);
pDC->LineTo(rt.Width()-100, rt.Height());
//每一天顯示的寬度xAver
xAver = (rt.Width()-100) / (float)days;
//平均刻度代表的價格
averPri = (m_iHighMax-m_iLowMax) / 5.0 / 100;
//第一條價格起始線
ftemp = m_iLowMax/ 100.0;
//刻度線的距離
average = m_ptOrigin[0].y / 6.0;
CPen penRedDot(PS_DOT, 1, RGB(200, 0, 0));
//畫K線坐標
for (i=0; i<5; i++)
{
pDC->SelectObject(&penRedDot);
pDC->MoveTo(0, average*(i+1));
pDC->LineTo(rt.Width()-100, average*(i+1));
pDC->SelectObject(&penRedSolid);
pDC->LineTo(rt.Width()-100+10, average*(i+1));
pDC->MoveTo(rt.Width()-100, average*(i+1) + average/2);
pDC->LineTo(rt.Width()-100+5, average*(i+1) + average/2);
pDC->MoveTo(rt.Width()-100, average*(i+1) + average/4);
pDC->LineTo(rt.Width()-100+3, average*(i+1) + average/4);
pDC->MoveTo(rt.Width()-100, average*(i+1) + average/4*3);
pDC->LineTo(rt.Width()-100+3, average*(i+1) + average/4*3);
str.Format("%10.2f", ftemp + averPri * (5-i));
pDC->DrawText(str, CRect(rt.Width()-100, average*(i+1)-7, rt.Width(), average*(i+1)+10), DT_LEFT);
}
CPen penGreen(PS_SOLID, 1, RGB(0, 200, 0));
CBrush brush(NULL_BRUSH);
CBrush brushGreen(RGB(0, 200, 0));
CBrush *pOldBrush = pDC->SelectObject(&brush);
//畫每天最低到最高的線,開盤和收盤的矩形
for (i=0; i<days; i++)
{
if (m_sdCurrData[i].open <= m_sdCurrData[i].close)
{
pDC->SelectObject(&penRedSolid);
pDC->SelectObject(&brush);
pDC->MoveTo(xAver * i+xAver/3, average*5- average/averPri*(m_sdCurrData[i].high/100.0-ftemp));
pDC->LineTo(xAver * i+xAver/3, average*5- average/averPri*(m_sdCurrData[i].low/100.0-ftemp));
pDC->Rectangle(xAver*i, average*5-average/averPri*(m_sdCurrData[i].open/100.0- ftemp),
xAver*i+xAver/3*2, average*5-average/averPri*(m_sdCurrData[i].close/100.0- ftemp));
}
else
{
pDC->SelectObject(&penGreen);
pDC->SelectObject(&brushGreen);
pDC->MoveTo(xAver * i+xAver/3, average*5- average/averPri*(m_sdCurrData[i].high/100.0-ftemp));
pDC->LineTo(xAver * i+xAver/3, average*5- average/averPri*(m_sdCurrData[i].low/100.0-ftemp));
pDC->Rectangle(xAver*i, average*5-average/averPri*(m_sdCurrData[i].close/100.0- ftemp),
xAver*i+xAver/3*2, average*5-average/averPri*(m_sdCurrData[i].open/100.0- ftemp));
}
if (!m_bMouseMove)
{
if (m_iDrawCount == i)
{
m_ptSavePoint.x = xAver * i+xAver/3;
m_ptSavePoint.y = average*5-average/averPri*(m_sdCurrData[i].open/100.0-ftemp);
}
}
}
//計算5日平均值和10日平均值
int *fiveAverData = new int[days];
int *tenAverData = new int[days];
for (i=0; i<days; i++)
{
if (i >= 4)
{
fiveAverData[i] = (m_sdCurrData[i].close + m_sdCurrData[i-1].close +m_sdCurrData[i-2].close +
m_sdCurrData[i-3].close + m_sdCurrData[i-4].close) / 5;
}
else
{
fiveAverData[i] = m_sdCurrData[i].close * 5 / 5;
}
if (i >= 9)
{
tenAverData[i] = (m_sdCurrData[i].close + m_sdCurrData[i-1].close +m_sdCurrData[i-2].close +
m_sdCurrData[i-3].close + m_sdCurrData[i-4].close + m_sdCurrData[i-5].close +
m_sdCurrData[i-6].close +m_sdCurrData[i-7].close + m_sdCurrData[i-8].close +
m_sdCurrData[i-9].close) / 10;
}
else
{
tenAverData[i] = m_sdCurrData[i].close * 10 / 10;
}
}
//畫5日均線和10日均線
CPen penWhite(PS_SOLID, 1, RGB(200, 200, 200));
CPen penYellow(PS_SOLID, 1, RGB(200, 200, 0));
for (i=1; i<days; i++)
{
pDC->SelectObject(&penWhite);
pDC->MoveTo(xAver * (i-1)+xAver/3, average*5-average/averPri*(fiveAverData[i-1]/100.0-ftemp));
pDC->LineTo(xAver * i+xAver/3, average*5-average/averPri*(fiveAverData[i]/100.0-ftemp));
pDC->SelectObject(&penYellow);
pDC->MoveTo(xAver * (i -1)+xAver/3, average*5-average/averPri*(tenAverData[i-1]/100.0-ftemp));
pDC->LineTo(xAver * i+xAver/3, average*5-average/averPri*(tenAverData[i]/100.0-ftemp));
}
//畫柱狀成交量的坐標和柱狀圖
average = (m_ptOrigin[1].y - m_ptOrigin[0].y) / 5;
float averVol = m_iVolMax / 4.0;
int temp = m_iVolMax / 4 / 100000 * 1000;//顯示刻度用的臨時變量
pDC->SelectObject(&penRedSolid);
pDC->SelectObject(&brush);
str.Format("%s", " X100");
pDC->Rectangle(rt.Width()-100, m_ptOrigin[1].y-10, rt.Width()-50, m_ptOrigin[1].y+5);
pDC->DrawText(str, CRect(rt.Width()-100, m_ptOrigin[1].y-10, rt.Width(), m_ptOrigin[1].y+5), DT_LEFT);
for (i=0; i<4; i++)
{
pDC->SelectObject(&penRedDot);
pDC->MoveTo(0, m_ptOrigin[0].y + average*(i+1));
pDC->LineTo(rt.Width()-100, m_ptOrigin[0].y + average*(i+1));
pDC->SelectObject(&penRedSolid);
pDC->LineTo(rt.Width()-100+10, m_ptOrigin[0].y + average*(i+1));
pDC->MoveTo(rt.Width()-100, m_ptOrigin[0].y + average*(i+1) + average / 2);
pDC->LineTo(rt.Width()-100+5, m_ptOrigin[0].y + average*(i+1) + average / 2);
str.Format("%10d", temp * (4-i));
pDC->DrawText(str, CRect(rt.Width()-100, m_ptOrigin[0].y + average*(i+1)-7, rt.Width(),
m_ptOrigin[0].y + average*(i+1)+10), DT_LEFT);
}
for (i=0; i<days; i++)
{
if (m_sdCurrData[i].open <= m_sdCurrData[i].close)
{
pDC->SelectObject(&penRedSolid);
pDC->SelectObject(&brush);
}
else
{
pDC->SelectObject(&penGreen);
pDC->SelectObject(&brushGreen);
}
pDC->Rectangle(xAver*i, m_ptOrigin[1].y - m_sdCurrData[i].vol / averVol * average,
xAver*i+xAver/3*2, m_ptOrigin[1].y);
}
//1.畫收盤價四色線的刻度值
average = (m_ptOrigin[2].y - m_ptOrigin[1].y) / 5;
str.Format("%8d", 0);
pDC->DrawText(str, CRect(rt.Width()-100, m_ptOrigin[2].y-7, rt.Width(), m_ptOrigin[2].y+10), DT_LEFT);
for (i=0; i<4; i++)
{
pDC->SelectObject(&penRedDot);
pDC->MoveTo(0, m_ptOrigin[1].y + average*(i+1));
pDC->LineTo(rt.Width()-100, m_ptOrigin[1].y + average*(i+1));
pDC->SelectObject(&penRedSolid);
pDC->LineTo(rt.Width()-100+10, m_ptOrigin[1].y + average*(i+1));
for (int j=0; j<10; j++)
{
pDC->MoveTo(rt.Width()-100, m_ptOrigin[1].y + average*(i+1) + average / 10 *(j+1));
pDC->LineTo(rt.Width()-100+5, m_ptOrigin[1].y + average*(i+1) + average / 10*(j+1));
}
pDC->SelectObject(&penRedDot);
pDC->MoveTo(0, m_ptOrigin[2].y + average*(i+1));
pDC->LineTo(rt.Width()-100, m_ptOrigin[2].y + average*(i+1));
pDC->SelectObject(&penRedSolid);
pDC->LineTo(rt.Width()-100+10, m_ptOrigin[2].y + average*(i+1));
for (j=0; j<10; j++)
{
pDC->MoveTo(rt.Width()-100, m_ptOrigin[2].y + average*i + average / 10 *(j+1));
pDC->LineTo(rt.Width()-100+5, m_ptOrigin[2].y + average*i + average / 10*(j+1));
}
str.Format("%8d", 4-i);
pDC->DrawText(str, CRect(rt.Width()-100, m_ptOrigin[1].y + average*(i+1)-7, rt.Width(),
m_ptOrigin[1].y + average*(i+1)+10), DT_LEFT);
str.Format("%8d", -(i+1));
pDC->DrawText(str, CRect(rt.Width()-100, m_ptOrigin[2].y + average*(i+1)-7, rt.Width(),
m_ptOrigin[2].y + average*(i+1)+10), DT_LEFT);
}
//2.計算四線的點值
float *fPrice1 = new float[days];
float *fPrice2 = new float[days];
float *fPrice3 = new float[days];
float *fPrice4 = new float[days];
for (i=0; i<days; i++)
{
fPrice1[i] = m_sdCurrData[i].close / 100.0 / 2 - m_sdCurrData[i].close / 100 / 2
+ m_sdCurrData[i].close / 100 / 2 % 4;
fPrice2[i] = m_sdCurrData[i].close / 100.0 / 3 - m_sdCurrData[i].close / 100 / 3
+ m_sdCurrData[i].close / 100 / 3 % 4;
fPrice3[i] = m_sdCurrData[i].close / 100.0 / 4 - m_sdCurrData[i].close / 100 / 4
+ m_sdCurrData[i].close / 100 / 4 % 4;
fPrice4[i] = m_sdCurrData[i].close / 100.0 / 5 - m_sdCurrData[i].close / 100 / 5
+ m_sdCurrData[i].close / 100 / 5 % 4;
}
for (i=0; i<days-1; i++)
{
pDC->SelectObject(&penWhite);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y-average * fPrice1[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y-average *fPrice1[i+1]);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y+average * fPrice1[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y+average *fPrice1[i+1]);
pDC->SelectObject(&penYellow);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y-average * fPrice2[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y-average *fPrice2[i+1]);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y+average * fPrice2[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y+average *fPrice2[i+1]);
pDC->SelectObject(&penRedSolid);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y-average * fPrice3[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y-average *fPrice3[i+1]);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y+average * fPrice3[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y+average *fPrice3[i+1]);
pDC->SelectObject(&penGreen);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y-average * fPrice4[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y-average *fPrice4[i+1]);
pDC->MoveTo(xAver*i, m_ptOrigin[2].y+average * fPrice4[i]);
pDC->LineTo(xAver*(i+1), m_ptOrigin[2].y+average *fPrice4[i+1]);
}
DrawDateText(pDC);
//釋放動態分配的內存
delete fiveAverData;
delete tenAverData;
delete fPrice1;
delete fPrice2;
delete fPrice3;
delete fPrice4;
//還原繪畫環境
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
}
說明:上面最麻煩的是數據的處理,如果處理不好就不能把圖畫到合適的位置,所以計算的時候一定要用float,不然就算是很小的數據差別都會造成繪圖不到指定的位置。還有這個函數代碼量比較多,是因為繪圖都需要用到平均每天的寬度和一些公用的參數,所以就在一個函數里完成了。其實可以寫成很多個函數模塊,比如每一個部分可以寫成一個函數,然后數據計算可以用專門的函數封裝。
(3)補充功能,在最下面顯示當前鼠標對應的日期
/*
* 函數名稱: DrawDateText
* 輸 入: pDC
* pDC: 繪畫用的CDC指針
* 輸 出:
* 功能描述: 顯示日期
* 全局變量:
* 作 者: 吳友強
* 日 期: 2009年12月02日
* 修 改:
* 日 期:
*/
void CTongXinDaView::DrawDateText(CDC *pDC)
{
CRect rt;
GetClientRect(&rt);
float xAver;
CString strDate;
xAver = (rt.Width()-100) / (float)m_iDays;
for (int i=0; i<m_iDays; i++)
{
if ((m_ptDatePoint.x > xAver * i) && (m_ptDatePoint.x < (xAver * i+xAver / 3 *2)))
{
m_iDateCount = i;
}
}
CPen pen(PS_SOLID, 1, RGB(200, 0, 0));
CPen *pOldPen = pDC->SelectObject(&pen);
CBrush brushBlue(RGB(0, 0, 150));
CBrush *pOldBrush = pDC->SelectObject(&brushBlue);
pDC->SetTextColor(RGB(200, 200, 200));
pDC->MoveTo(0, rt.Height()-15);
pDC->LineTo(rt.Width()-100, rt.Height()-15);
strDate.Format("%d", m_sdCurrData[m_iDateCount]);
strDate.Insert(4, '/');
strDate.Insert(7, '/');
pDC->Rectangle(m_ptDatePoint.x, rt.Height()-15, m_ptDatePoint.x+75, rt.Height());
pDC->DrawText(strDate, CRect(m_ptDatePoint.x, rt.Height()-15, m_ptDatePoint.x+75, rt.Height()), DT_LEFT);
pDC->SelectObject(&pOldPen);
pDC->SelectObject(&pOldBrush);
}
說明:為了顯示出這個日期,需要記錄當前屏幕繪畫了哪些天的圖,然后根據鼠標的坐標位置判斷處于哪一天并顯示出來。
(4)圖形放大縮小以及左右平移的實現思路:
其實原理都是一樣的,就是根據需要的天數顯示,放大就是顯示的天數比較少,縮小就是多顯示一些天數,左右移動就是重新提取一些數據。顯示的天數可以根據需要按照固定需要按固定的比例放大和縮小。然后就是通過一些按鍵來控制或是鼠標控制,其中還有一個輔助線的設置比較靠邏輯,需要細心才能弄好。具體的請參看代碼,有注釋。
3.心得體會:
剛開始的時候感覺什么都不會,但是我還是勇敢地邁出了自己的第一步,因為在這幾年的學習過程中我發現自己解決問題的能力提升了很多,所以相信自己能夠完成。然后自己就靜下心來一行一行代碼的寫,經過幾天努力,完成了大部分功能,自己的信心也是越來越強?,F在這個程度還算過的去了吧。
4.存在的不足:
由于對于股票行業術語不是很了解,很多變量的命名不是很合理和規范,代碼的組織也不是很好。
5.得到的幫助與指導:
感謝老師提供的思路,特別是對于所謂的圖形放大和縮小,左右移動,老師的提醒使我恍然大悟,以后基本上沒有什么太大的困難。就是數據的提取問題。
浙公網安備 33010602011771號