Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.172.213] |
|
Сообщ.
#1
,
|
|
|
Просьба помочь избавиться от мерцания при перерисовке дочернего контрола вывода процентов на прогресс-баре.
Если скорость прогресса не высока (THREAD_PAUSE>40), то мерцаний не видно. А если меньше, например, 10 как выставлено в примере, то выглядит не очень симпатично. #include <tchar.h> #include <windows.h> #include <commctrl.h> #include <sstream> #include <string> //#include <stdio.h> //#include <richedit.h> #define TIMER_PAUSE 5 // пауза таймера - как часто проверять значение счётчика #define THREADS_NUMBER 1 // кол-во одновременно запущенных тридов, тут не нужно #define ITERATIONS_NUMBER 101 //сколько раз выполнить трид, должно быть 100 (%), сейчас специально выставлено 101 #define THREAD_PAUSE 10 // пауза в триде - скорость счётчика #define PROGRESS_MAX 100 // #define EXIT_ERR(mess) {MessageBox(NULL, TEXT(mess), TEXT("Error"), MB_OK); return 0;}// выход из программы при ошибке const enum { ID_PROGRESS_1=100, ID_EDIT_1, ID_TIMER_1 }; DWORD GL_COUNTER=0;//счётчик LRESULT APIENTRY WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT APIENTRY EditSubclassProc(HWND, UINT, WPARAM, LPARAM); template <typename T> std::string to_string (const T &x);// INT, указатель в STRING DWORD WINAPI ThreadProc(CONST LPVOID); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hMainWnd; HANDLE hThreads[THREADS_NUMBER]; CONST HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); TCHAR szClassName[] = TEXT("MyClass"); MSG msg; WNDCLASSEX wc; wc.cbSize =sizeof(wc); wc.style =CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc =WndProc; wc.cbClsExtra =0; wc.cbWndExtra =0; wc.hInstance =hInstance; wc.hIcon =LoadIcon(NULL, IDI_APPLICATION); wc.hCursor =LoadCursor(NULL, IDC_ARROW); wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName =NULL; wc.lpszClassName=szClassName; wc.hIconSm =LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) EXIT_ERR("Cannot register the class") hMainWnd = CreateWindow(szClassName, TEXT("Application"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, 0, 700, 500, (HWND)NULL, (HMENU)NULL, (HINSTANCE)hInstance, NULL); if (!hMainWnd) EXIT_ERR("Cannot creat the Main Win") ShowWindow(hMainWnd, nCmdShow); if (!SetTimer(hMainWnd, ID_TIMER_1, TIMER_PAUSE, (TIMERPROC) NULL)) EXIT_ERR("Cannot creat the Timer") hThreads[0]=CreateThread(NULL, 0, &ThreadProc, hMutex, 0, NULL); if (!hThreads[0]) EXIT_ERR("Cannot creat the Thread") while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } CloseHandle(hThreads[0]); CloseHandle(hMutex); return msg.wParam; } LRESULT APIENTRY WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hWndEdit, hProgBar; switch (msg) { case WM_CREATE: // прогресс hProgBar=CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | PBS_SMOOTH, 60, 40, 140, 40, hWnd, (HMENU)ID_PROGRESS_1, NULL, NULL); SendMessage(hProgBar, PBM_SETRANGE, 0, (LPARAM)MAKELONG(0, PROGRESS_MAX));// мин и макс SendMessage(hProgBar, PBM_SETSTEP, (WPARAM)1, 0);// шаг - по умолчанию 10 SendMessage(hProgBar, PBM_SETBARCOLOR, 0, (LPARAM)RGB(0, 255, 0)); SendMessage(hProgBar, PBM_SETBKCOLOR, 0, (LPARAM)RGB(255,255,255)); // дочерний от прогресса контрол для показа процентов hWndEdit=CreateWindow(TEXT("EDIT"), TEXT(""), WS_VISIBLE | WS_CHILD| ES_READONLY | SS_CENTER, 50, 10, 40, 20, hProgBar, (HMENU)ID_EDIT_1, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE)/*зачем это?*/, NULL); SetWindowLongPtr(hProgBar, GWL_USERDATA, SetWindowLongPtr(hProgBar, GWL_WNDPROC, (LPARAM)EditSubclassProc));// вешаем новый обработчик и одновременно запоминаем адрес первого (вносим в DATA) break; case WM_TIMER: switch (wParam) { case ID_TIMER_1: if (SendMessage(hProgBar, PBM_GETPOS, 0, 0)!=GL_COUNTER) { SendMessage(hProgBar, PBM_SETPOS, GL_COUNTER, 0); SetWindowText(hWndEdit, (to_string(GL_COUNTER)+"%").c_str()); } return 0; } break; case WM_CHAR: switch (LOWORD(wParam)) { if (wParam == 13) { MessageBox(hWnd,(to_string(hWnd)+" : ").c_str(),TEXT("MessageBox"),MB_OK); return 0; } } break; case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: SetWindowLongPtr(hWndEdit,GWLP_WNDPROC,(LPARAM)GetWindowLongPtr(hWndEdit,GWLP_WNDPROC)); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return DefWindowProc(hWnd, msg, wParam, lParam); } LRESULT APIENTRY EditSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {// обработчик событий №2- контрол прогресс бара на которм дочерний EDIT с процентами switch(msg) { case WM_COMMAND:// перерисовать EDIT что бы текст с процентами отрисовывался правильно if(HIWORD(wParam)==EN_CHANGE)//(иначе на прозрачном фоне бардак) { RECT rect;// GetClientRect(HWND(lParam), &rect); InvalidateRect(HWND(lParam), &rect, TRUE); MapWindowPoints(HWND(lParam), hWnd, (POINT *) &rect, 2); RedrawWindow(hWnd, &rect, NULL, RDW_ERASE | RDW_INVALIDATE);//*/ // ShowWindow(HWND(lParam),SW_HIDE);// можно сделать проще всего двумя стироками // ShowWindow(HWND(lParam),SW_SHOW);// но так мерцает вроде сильнее } break; case WM_CTLCOLORSTATIC: SetBkMode((HDC)wParam, TRANSPARENT);// это делает фон под шрифтом прозрачным // return (LRESULT)CreateSolidBrush(RGB(255, 255, 255));// это устанавлмвает цвет полностью фона return (LRESULT)GetStockObject(NULL_BRUSH);// это делает фон прозрачным } // PostMessage(GetParent(GetParent(hWnd)), msg, wParam, lParam); return CallWindowProc((WNDPROC)GetWindowLongPtr(hWnd, GWL_USERDATA), hWnd, msg, wParam, lParam); } template <typename T> std::string to_string(const T &x) { std::stringstream buf; buf << x; return buf.str(); } DWORD WINAPI ThreadProc(CONST LPVOID lpParam) { const HANDLE hMutex = (CONST HANDLE)lpParam; DWORD i; for(i = 0; i < ITERATIONS_NUMBER; i++) { WaitForSingleObject(hMutex, INFINITE); GL_COUNTER++; ReleaseMutex(hMutex); Sleep(THREAD_PAUSE); } ExitThread(0); } |
Сообщ.
#2
,
|
|
|
Скорее просто уменьшите количество обновлений в таймере (TIMER_PAUSE = 100 // 1000 / 100 = 10 раз в секунду). Этого вполне достаточно. Не надо обновлять компоненты слишком часто (все равно кадров у монитора 50 - 100). У вас же перерисовывается 200 раз (TIMER_PAUSE = 5 // 1000 / 5 = 200).
|
Сообщ.
#3
,
|
|
|
macomics, да, вы правы.
Только при обновлении в 100 мс и при высокой скорости прогресса его движение получается не очень плавным. А вот при 50 нормально. И мерцание пропало полностью. По крайней мере на моём компе. Спасибо! |
Сообщ.
#4
,
|
|
|
Timon K, я бы попытался сделать другое.
Использовал бы PostMessage для изменения прогресса. Не стал бы читать прогресс. И пользоваться таймером. --- Можно при изменении счётчика считать процент (запоминая предыдущий). При значимом изменении процента слать сообщение эдиту прямо из ThreadProc. |
Сообщ.
#5
,
|
|
|
ЫукпШ, а безопасно управлять контролами напрямую из другого потока?
|
Сообщ.
#6
,
|
|
|
Посредством сообщений - да.
Точно помню, что прогрессом из рабочего потока я управлял. --- Вот сейчас (на всякий случай) попробовал - в Эдит из другого потока выводится строка посредством SendMessage. Устойчиво работает. |
Сообщ.
#7
,
|
|
|
Мерцание вызвано частыми перерисовками. Лечится не только уменьшением частоты обновлений, но и применением двойной буферизации. Системная двойная буферизация включается стилем WS_EX_COMPOSITED для родительского окна, должно помочь
|
Сообщ.
#8
,
|
|
|
Да, отлично, спасибо всем!
От мерцания избавились. А как сделать так, что бы цвет текста тоже менялся (например, на белый) при наползании на него заливки прогресса? |
Сообщ.
#9
,
|
|
|
Например, рисовать заливку самостоятельно используя xor.
|
Сообщ.
#10
,
|
|
|
Цитата macomics @ Например, рисовать заливку самостоятельно используя xor. Сделал так. При событии PAINT: 1. Залил прямоугольник в желаемом месте и под желаемый размер заливкой пустого прогресс (белый) 2. Нарисовал посередине текст (статус прогресса в процентах) цветом для пустого прогресса (чёрный), с прозрачным фоном. Предварительно задал желаемый шрифт и вычислил длину текущего текста в пикселях. 3. Залил прямоугольник на тех же начальных координатах, той жы высоты, но ширина = статусу прогресса заливкой прогресса (например, красным). 4. Нарисовал текст с теми же координатами, что и в пункте 2, только такой длины, что бы текст обрывался там, где кончается заливка - текущий статус прогресса. Цвет шрифта белый с прозрачным фоном. При этом, если заливка ещё не наползла на показ процентов, то длина текста = 0 и он не затирает уже имеющийся чёрный. При событии TIMER: 1. Проверяю, не изменился ли счётчик (глобальная переменная, менять может, например, трид в котором функция работы с устройством через USB). 2. Если изменился, нужно перерисовать, вызываю InvalidateRect, который снова посылает сообщение PAINT. Прогресс меняет статус. Вроде пока нормально работает отдельным приложением, но в теле своего проекта ещё не проверял. |
Сообщ.
#11
,
|
|
|
Цитата Timon K @ А как сделать так, что бы цвет текста тоже менялся (например, на белый) при наползании на него заливки прогресса? Рисуем в памяти две копии прогресс-бара. Не залитую и 100% залитую системной заливкой. В оба образа выводим одну и ту же цифру процента по одинаковым координатам, но своего цвета. Вычисляем процент выполнения в пикселах и копируем этот процент залитого образа в окно. Остальное копируем из не залитого. --- По моему это не удобно. Лучше поверх прогресс-бара вывести маленькое окно и туда выводить цифры. Вот так: |
Сообщ.
#12
,
|
|
|
Сообщ.
#13
,
|
|
|