2012-12-22 80 views
2

試圖實現windows進度條以大數字顯示字節下載進度,但無法正確執行。Progressbar範圍和位置,如何在C++中處理大數字?

如果我按照以下步驟下載2.5gb,那麼在下載完成後,它會在完整範圍內結束。

double dlSize = getDlSize(); 
unsigned int pbRange = (unsigned int)(dlSize/3000); 
SendMessage(hProgressbar, PBM_SETRANGE, 0, MAKELPARAM(0, pbRange)); 

,然後設置新的位置在每次下載回調爲:

double dlBytes = bytesDownloaded(); 
unsigned int newIncrement = (unsigned int)(dlBytes/3000); 
SendMessage(hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0); 

這是一個非常noobish實現,我不想落於XY情況,所以我的問題是什麼是以大小爲2-5GB的字節實現進度條的正確方法?


我試驗了兩種方法通過@msandiford和@NikBougalis以下建議,通過採取在帳戶中的進度條的寬度,並通過使用百分比,而不是實際數字,我甚至都合併,但在任何情況下,始終newIncrement出來0,也許這是因爲dlSize總是較低(在雙重newIncrement出來像1.15743e + 007,類型投它和它的0)。

我還能做什麼?

新代碼結合兩種方法:

編輯2: 添加幾張支票的代碼,因爲我是不斷地爲newIncrement得到0,看起來像它現在的工作,不知道有多好:

GetClientRect(hProgressbar, &pbRCClient); 
pbWidth = pbRCClient.right - pbRCClient.left; // (pbWidth its a global variable) 
unsigned int pbRange = pbRCClient.right - pbRCClient.left; 
SendMessage(hProgressbar, PBM_SETRANGE, 0, MAKELPARAM(0, pbRange)); 

,並同時更新:

double dlSize = getDlSize(); 
double doubleIncrement = ((dlSize * pbWidth)/totalSize); 

unsigned int newIncrement; 

if ((unsigned int)doubleIncrement < 1) 
{ 
    blockFill += doubleIncrement; 

    if ((unsigned int)blockFill > 1) 
    { 
     newIncrement = (unsigned int)blockFill; 
     SendMessage(hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0); 
     blockFill = 0; 
    } 
} 
else 
{ 
    newIncrement = (unsigned int)(doubleIncrement); 
    SendMessage(hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0); 
    //blockFill = 0; 
} 

編輯3:看起來像它還是提早完成。

+0

使用「PBM_SETPOS」而不是「PBM_DELTAPOS」會容易得多。我的答案中也有一個錯誤,現在已經修復,可能會導致錯誤的方向: – msandiford

+0

@msandiford PBM_SETPOS會將進度條位置重置爲通過WPARAM提供的位置:) – StudentX

+0

是的,這是正確的。計算進度條的絕對位置比計算精確的增量要容易得多。您需要做的就是跟蹤下載的數據總量。 – msandiford

回答

2

使進度條比進度條窗口本身中像素的數量更準確可能沒有多大意義。基於此,您應該可以輕鬆地將目標縮放到像素數量。

RECT rcClient; 
    GetClientRect(hProgressBar, &rcClient); 
    unsigned int pbRange = rcClient.right - rcClient.left; 

    // Need to either keep unitsPerPixel, or recalculate later 
    double pixelsPerUnit = pbRange/dlSize; 
    SendMessage(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, pbRange)); 

然後更新進度會是這樣的:

double dlBytes = totalBytesDownloaded(); 
    unsigned int newProgress = (unsigned int)(dlBytes * pixelsPerUnit); 
    SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)newProgress, 0); 

如果進度窗口在某種程度上改變大小,你需要在響應重新計算pbRange,重置進度條範圍,並重新計算unitsPerPixelWM_SIZE信息。

+0

我試過你的方法,但遇到了不同的麻煩,我更新了上面的新代碼。 – StudentX

+0

Ooops。我的錯。此技術應該使用「PBM_SETPOS」而不是「PBM_DELTAPOS」。已更新以反映更改。 – msandiford

+0

是的,工作,完美,非常感謝您的時間和幫助。結合你的方法和錯誤擴散技術提到的沃恩卡託這裏:http://stackoverflow.com/questions/14014261/how-to-effectively-convert-double-to-int-in-c – StudentX

3

您遇到的最大問題是進度條控件本身的侷限性。 PBM_SETRANGE是有限的,儘管你可以使用PBM_SETRANGE32,如果你需要處理大於2GB的值,你仍然會遇到問題。

巧合的是,爲什麼要使用一個雙?使用UINT64,最大值約爲16,384 PB(如果你下載的東西會溢出...跳過進度條,它只會壓制你和你的客戶)。整數對計算像字節這樣的東西非常有效。

假設您知道要下載的文件的完整大小,解決具有有限最大範圍的進度欄大小的一種方法是使進度欄從0開始到100結束。然後,你可以使用轉換接收到一個個字節的simple rule of three

percent = (bytes_received * 100)/max_bytes; 

如果你想獲得更多的「粒度」你可以在進度條的比例更改爲1000,並相應調整計算;你甚至可以達到10000,但是在那個時候,根據控制器的寬度(或高度),你可能會碰到顯示器的分辨率。

+0

我試過你的方法,但有一個不同類型的麻煩,我更新了上面的新代碼。 – StudentX