Рисование по таймеру Win32api

  • Автор темы sergg
  • Дата начала
S

sergg

Всем привет.
Как мне на WM_TIMER написать текст в главном окне? роблема вся в том, что выполнение функции TextOut должно происходить если WndProc ловит WM_PAINT. А WM_PAINT может посылаться виндой в разных случаях, а значит нет гарантии что и код который будет выполняться в случае WM_PAINT выполнится только по таймеру.
 
E

European

В обработчике WM_TIMER устанавливайте некоторый флаг, а в обработчике WM_PAINT проверяйте значение этого флага и решайте что именно будете рисовать
 
L

lazybiz

В обработчике WM_TIMER устанавливайте некоторый флаг, а в обработчике WM_PAINT проверяйте значение этого флага и решайте что именно будете рисовать
Не уверен что это сработает. Сообщение WM_PAINT посылается не постоянно.

А вот так работать будет:
C++:
#include <windows.h>
#include <math.h>

#define		W				320
#define		H				240

char			g_szWindowName[]	= "dib";
char			g_szWindowClass[]	= "dib32";
HWND		g_hWnd;
char * 		p_str				= "Hello!";

static LRESULT CALLBACK wnd_proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch ( uMsg ) {
case WM_NCHITTEST: return HTCAPTION;

case WM_TIMER: {
int		i;

char	a;
a = p_str[0];
for ( i = 0; i < strlen( p_str ) - 1; i++ ) p_str[i] = p_str[i + 1];
p_str[strlen( p_str ) - 1] = a;

InvalidateRect( hWnd, NULL, FALSE );
} break;

case WM_PAINT: {
PAINTSTRUCT	ps;
BeginPaint( hWnd, &ps );

TextOut( ps.hdc, 120, 120, p_str, strlen( p_str ) );

EndPaint( hWnd, &ps );
} return 0;

case WM_KEYDOWN: if ( (int)wParam != VK_ESCAPE ) break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
MSG			msg;
WNDCLASS	wc;

memset( &wc, 0, sizeof( WNDCLASS ) );
wc.style			= CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc		= wnd_proc;
wc.hInstance		= hInst;
wc.lpszClassName	= g_szWindowClass;
wc.hCursor			= LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground	= (HBRUSH)GetStockObject( WHITE_BRUSH );
RegisterClass( &wc );

g_hWnd = CreateWindowEx( WS_EX_TOPMOST,
g_szWindowClass, g_szWindowName, WS_POPUP | WS_VISIBLE,
(GetSystemMetrics( SM_CXSCREEN ) >> 1) - (W >> 1),
(GetSystemMetrics( SM_CYSCREEN ) >> 1) - (H >> 1),
W, H, NULL, NULL, hInst, NULL );

SetTimer( g_hWnd, 1000, 200, NULL );

while ( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}

DestroyWindow( g_hWnd );
UnregisterClass( g_szWindowClass, hInst );
return 0;
}
 
E

European

Не уверен что это сработает.
В чём именно вы не уверены?
А вот так работать будет:
Рисовать, то будет. Но обратите внимание на требование:
а значит нет гарантии что и код который будет выполняться в случае WM_PAINT выполнится только по таймеру.
Где в вашем коде гарантии?
 
L

lazybiz

European
Я не вижу в этом смысла. Если ему надо рисовать текст 1 раз в каждые 200 миллисекунд то ему нужно просто воткнуть TextOut в WM_TIMER и WM_PAINT будет здесь вообще не при чем.
Формулировка автора построена не верно. Я заранее предусмотрел этот вариант. Пускай либо переформулирует, либо берет то что дают.
Может конечно поставить флаг но это будет не правильный подход. WM_PAINT вызывается только тогда, когда необходимо обновить часть окна или все окно целиком, больше никак.

В чём именно вы не уверены?
Не то что не уверен, я знаю что это не сработает.
 
E

European

Формулировка автора построена не верно. Я заранее предусмотрел этот вариант.
А если верно? Автору нужно вывести текст только на WM_TIMER, все остальные инициаторы перирисовки должны быть проигнорированы.
Не то что не уверен, я знаю что это не сработает.
На пальцах объясните, что не сработает

Добавлено:
Пускай либо переформулирует, либо берет то что дают.
Следующим шагом вы пошлете автора?
 
L

lazybiz

А если верно? Автору нужно вывести текст только на WM_TIMER, все остальный инициаторы перирисовки должны быть проигнорированы.
Ну тогда мой вариант подойдет ему идеально!!!)))

На пальцах объясните, что не сработает

Объясняю:
В обработчике WM_TIMER устанавливайте некоторый флаг
Установили.

а в обработчике WM_PAINT проверяйте значение этого флага и решайте что именно будете рисовать
MSDN: The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window.
Как вы думаете, сколько раз сработает таймер перед тем как окно получит сообщение WM_PAINT ? Правильно! Сколько угодно.
WM_PAINT вызывается только тогда, когда необходимо обновить часть окна или все окно целиком, больше никак.
 
E

European

Ай, да при чем здесь МСДН? А прекрасно знаю, когда окно может быть перерисовано. А прекрасно понимаю ваше решение, но оно никак не коррелирует с требованиями топикстартера. Ладно, без прояснения требований спорить бесполезно

Добавлено:
Ну тогда мой вариант подойдет ему идеально!!!)))
Ничего он не подходит. После первого же обработчика WM_TIMER строка будет выводится постоянно, а нужно только 1 раз
 
S

sergg

Парни, извиняюсь. Я неправильно сформулировал вопрос, точнее то что я хочу.
Попробую снова:
При нажатии на кнопку у меня должен сработать таймер. Каждую секунду у меня должен меняться текст на форме, при этом прошлый текст должен удалятся. Правда для этого я еще ничего не предпринимал, т.к. возможно он сам удаляется, а я этого не вижу.
Вот мои попытки:
C++:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR Txt1[] = _T("Txt1");
TCHAR Text[] = _T("Text");
switch (message)
{
case WM_COMMAND:
if(LOWORD(wParam)==10001)
{
if(tmrEnbld)
{
KillTimer(hWnd,1);
tmrEnbld=false;
}
PostQuitMessage(0); 
}
if(LOWORD(wParam)==10000) //запускаю таймер
{
tmrEnbld=SetTimer(hWnd, 1, 1000, NULL); //tmrEnbld значит что таймер запущен
}
break;
case WM_TIMER:
isChanged=!isChanged; //просто флаг, означающий, что прошла секунда
UpdateWindow(hWnd);
case WM_PAINT:
if(tmrEnbld) //если таймер запущен
{
hdc = BeginPaint(hWnd, &ps); //определяем контекст устройства
if(isChanged) 
{
TextOut(hdc, 5, 5, Txt1, _tcslen(Txt1)); //выводим тхт1
}
if(!isChanged)
{
TextOut(hdc, 15, 15, Text, _tcslen(Text)); //вводим текст
}
EndPaint(hWnd, &ps);
}
break;

В итоге при нажатии на кнопку появляется слово "текст", а вот слова "тхт" я не вижу, хоть и вызываю updatewindow. Но, если форму передвинуть за рамки экрана (надеюсь вы поняли о чем я), а потом вернуть обратно, то будет нужный текст. И если так делать ежесекундно, то текст на самом деле будет меняться. Значит код работает верно но не до конца. Подскажите где и что надо дописать, что бы все было ок.
 
L

lazybiz

а вот слова "тхт" я не вижу, хоть и вызываю updatewindow
UpdateWindow обновляет только те области, которые помечены как те, которые нужно обновить, а они таковыми не помечены ни разу...

Но, если форму передвинуть за рамки экрана (надеюсь вы поняли о чем я), а потом вернуть обратно, то будет нужный текст.
Все объяснение цитатой выше.

Значит код работает верно но не до конца. Подскажите где и что надо дописать, что бы все было ок.
А ты вообще смотрел тот код что я тебе писал?

а нужно только 1 раз
Да что вы говорите))) А где про это написано?.. что-то никак не найду.. Даже если и нужно один раз тогда возникает вопрос зачем вообще нужен таймер?

Ай, да при чем здесь МСДН? А прекрасно знаю, когда окно может быть перерисовано.
Похоже что не знаете.
На пальцах объяснил. Что не понятно?

Из новых объяснений автора я понял что именно ему нужно.
C++:
#include <windows.h>

#define		W		320
#define		H		240

char		g_szWindowName[]	= "test";
char		g_szWindowClass[]	= "test32";
HWND		g_hWnd;

int			p_i					= 0;
char *		p[]					= { "privet!", "poka!" };

static LRESULT CALLBACK wnd_proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch ( uMsg ) {
case WM_COMMAND: {
switch ( LOWORD( wParam ) ) {
case 10000: {
SetTimer( g_hWnd, 1000, 1000, NULL );
} break;
}
} break;

case WM_TIMER: {
p_i ^= 1;
InvalidateRect( hWnd, NULL, TRUE );
} break;

case WM_PAINT: {
PAINTSTRUCT	ps;
BeginPaint( hWnd, &ps );
TextOut( ps.hdc, 120, 120, p[p_i], strlen( p[p_i] ) );
EndPaint( hWnd, &ps );
} return 0;

case WM_KEYDOWN: if ( (int)wParam != VK_ESCAPE ) break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
MSG			msg;
WNDCLASS	wc;

memset( &wc, 0, sizeof( WNDCLASS ) );
wc.style			= CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc		= wnd_proc;
wc.hInstance		= hInst;
wc.lpszClassName	= g_szWindowClass;
wc.hCursor			= LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground	= (HBRUSH)GetStockObject( WHITE_BRUSH );
RegisterClass( &wc );

g_hWnd = CreateWindowEx( WS_EX_TOPMOST,
g_szWindowClass, g_szWindowName, WS_POPUP | WS_VISIBLE,
(GetSystemMetrics( SM_CXSCREEN ) >> 1) - (W >> 1),
(GetSystemMetrics( SM_CYSCREEN ) >> 1) - (H >> 1),
W, H, NULL, NULL, hInst, NULL );

// Симулирую нажатие клавиши...
SendMessage( g_hWnd, WM_COMMAND, 10000, 0 );

while ( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}

DestroyWindow( g_hWnd );
UnregisterClass( g_szWindowClass, hInst );
return 0;
}

а значит нет гарантии что и код который будет выполняться в случае WM_PAINT выполнится только по таймеру.
Это тебя беспокоить не должно. В противном случае подход будет не верным.
 
S

sergg

Спасибо. Понял. Т.е. все дело в InvalidateRect( hWnd, NULL, TRUE ), так?
 
L

lazybiz

Да!

Добавлено: Альтернатива: InvalidateRgn(...). В данном случае может выполнить ту же функцию.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!