Аналоговые часы (WinAPI + GDI + C++) ?

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
Praid
Сообщения: 5
Зарегистрирован: 12 май 2010, 00:05

Задали написать программу выводящую на экран изображение работающих часов, у которых есть секунудная и минутная стрелка. Нашел bmp изображение часов (без стрелок) создал по нему регион, вывел изображение часов (без стрелок), добавил обработчики событий что бы можно было перетаскивать часы левой кнопкой мыши, а выключать правой.
В общем не знаю как реализовать движение стрелок, у меня получается что стрелки вы водятся на не исчезают при изменение координат. Как сделать так что бы казалось что стрелка движется ? И что бы когда часы были закрыты другим окном они не сбивались ?
Вот сам проект. http://rghost.ru/1594117

А вот код СPP:

Код: Выделить всё

// Clock-3.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Clock-3.h"
#include <math.h>
#include <conio.h>
#include <windows.h>


#define MAX_LOADSTRING 100

.............

............

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	HDC hdcBits;

	switch (message)
	{
        case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		hdcBits=::CreateCompatibleDC(hdc);
		SelectObject(hdcBits, maskBitmap);
		BitBlt(hdc, 0, 0, 280, 280, hdcBits, 0, 0, SRCCOPY);
// SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); 
// SetLayeredWindowAttributes(hWnd, 0, 220, LWA_ALPHA);

		  { // секундная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,2,RGB(255,100,100));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
		      LineTo(hdc,119,35);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // минутная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(80,80,80));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
			  LineTo(hdc,155,65);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // заглушка в центре циферблата
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(170,170,170));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);
			  HBRUSH hBrush,hBrushOld;
			  hBrush = CreateSolidBrush(RGB(80,0,0));
			  hBrushOld = (HBRUSH)SelectObject(hdc,hBrush);

			  Ellipse(hdc,113,113,125,125);

			  SelectObject(hdc,hBrushOld);
			  DeleteObject(hBrush);
			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
			  return WM_PAINT;
		  }

		DeleteDC(hdcBits);
		EndPaint(hWnd, &ps);
		break;

    case WM_NCHITTEST:
	   {
		   return HTCAPTION;
	   }

    case WM_NCRBUTTONUP:

	   {
		   PostQuitMessage(0);
	   }

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
WinAPI изучаю всего две недели.
Спасибо за ответ !
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

1. return WM_PAINT зачем делаешь?
2. Для того, чтобы обрабатывать сообщения от мышки, нужно ловить сообщения WM_LBUTTONUP и WM_RBUTTONUP, а ты пытаешься перехватить клик мышки по неклиентской части окна (NC - это non-client).
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Praid
Сообщения: 5
Зарегистрирован: 12 май 2010, 00:05

Romeo писал(а):1. return WM_PAINT зачем делаешь?
2. Для того, чтобы обрабатывать сообщения от мышки, нужно ловить сообщения WM_LBUTTONUP и WM_RBUTTONUP, а ты пытаешься перехватить клик мышки по неклиентской части окна (NC - это non-client).
1. Это случайно, не удалил.
2. Я же написал: что бы можно было перетаскивать часы левой кнопкой мыши, а выключать правой. Получается когда щелкаю левой кнопкой мыши я как бы обманываю и говоря что кликнул по заголовку тогда то и мое самодельное окно и перетаскивается.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

А, всё, я понял сакральный смысл. Теперь нужно сделать, чтобы часы шли, правильно? Всё очень просто.

1. Делаем таймер, вызывающийся раз в секунду.
2. В таймере увеличиваем две переменные. Угол секундной стрелки увеличиваем на PI/30. Проверяем, если угол секундной стрелки стал большим, чем 2*PI, то сбрасываем его в ноль у увеличиваем угол минутной стрелки на PI/30.
3. После увеличения переменных вызываем инвалидацию окна (InvalidateRect).
4. Поправляем код WM_PAINT. Рисуем стрелки о точки (Xcenter, Ycenter) до точки (Xcenter + Radius*sin(углаСтрелки), Ycenter + Radius*cos(углаСтрелки)).
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Praid
Сообщения: 5
Зарегистрирован: 12 май 2010, 00:05

Romeo писал(а):А, всё, я понял сакральный смысл. Теперь нужно сделать, чтобы часы шли, правильно?
Именно !
Romeo писал(а): 1. Делаем таймер, вызывающийся раз в секунду.
2. В таймере увеличиваем две переменные. Угол секундной стрелки увеличиваем на PI/30. Проверяем, если угол секундной стрелки стал большим, чем 2*PI, то сбрасываем его в ноль у увеличиваем угол минутной стрелки на PI/30.
3. После увеличения переменных вызываем инвалидацию окна (InvalidateRect).
4. Поправляем код WM_PAINT. Рисуем стрелки о точки (Xcenter, Ycenter) до точки (Xcenter + Radius*sin(углаСтрелки), Ycenter + Radius*cos(углаСтрелки)).
Вот как-то так наверно но все равно что то не хочет работать:

Код: Выделить всё

int nTimerID; // error C2086: 'int nTimerID' : redefinition
int nTimerID = SetTimer(hWnd, FIRST_TIMER, 1000, NULL); // таймер посылает сообщения с кодом WM_TIMER каждую секунду


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	HDC hdcBits;
	double alpha=0,beta=0;

	switch (message)
	{
        case WM_PAINT:
     
	for (int i=0; i<5; i++)
	  { 
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		hdcBits=::CreateCompatibleDC(hdc);
		SelectObject(hdcBits, maskBitmap);
		BitBlt(hdc, 0, 0, 280, 280, hdcBits, 0, 0, SRCCOPY);
// SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); 
// SetLayeredWindowAttributes(hWnd, 0, 220, LWA_ALPHA);

		   { // секундная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,2,RGB(255,100,100));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
		      LineTo(hdc,119,35);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // минутная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(80,80,80));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
			  LineTo(hdc,119+65*sin(alpha),119+65*cos(alpha));//x,y);//155,65);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // заглушка в центре циферблата
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(170,170,170));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);
			  HBRUSH hBrush,hBrushOld;
			  hBrush = CreateSolidBrush(RGB(80,0,0));
			  hBrushOld = (HBRUSH)SelectObject(hdc,hBrush);

			  Ellipse(hdc,113,113,125,125);

			  SelectObject(hdc,hBrushOld);
			  DeleteObject(hBrush);
			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		DeleteDC(hdcBits);
		EndPaint(hWnd, &ps);
	}
		break;

	case WM_TIMER:
		{
			alpha+=180/30; 
			beta+=180/30;
			InvalidateRect(hWnd,0,NULL);
		}
		break;

    case WM_NCHITTEST:
	   {
		   return HTCAPTION;
	   }

    case WM_NCRBUTTONUP:

	   {
		   PostQuitMessage(0);
	   }

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
	KillTimer(hWnd, FIRST_TIMER); // уничтожения таймера
}
IceFlame
Сообщения: 62
Зарегистрирован: 29 ноя 2009, 03:54

Дык у тебя секундная стрелка как рисовалась по жестко заданным координатам, так и рисуется дальше. Вообще лучше получай локальное время из системы и рисуй по нему часы.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

А ещё у тебя неверно создаётся, убивается и обрабатывается таймер. Он должен создаваться по WM_CREATE и убиваться по WM_QUIT. Обработчика для WM_TIMER я вообще не вижу.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Praid
Сообщения: 5
Зарегистрирован: 12 май 2010, 00:05

IceFlame писал(а):Дык у тебя секундная стрелка как рисовалась по жестко заданным координатам, так и рисуется дальше.
Мне хотябы одну запустить, а с остальными я сам разберусь.
IceFlame писал(а):Дык у тебя секундная стрелка как рисовалась по жестко заданным координатам, так и рисуется дальше. Вообще лучше получай локальное время из системы и рисуй по нему часы.
Именно так и собирался сделать, когда получится заставить стрелки двигаться.
Romeo писал(а):А ещё у тебя неверно создаётся, убивается и обрабатывается таймер. Он должен создаваться по WM_CREATE и убиваться по WM_QUIT. Обработчика для WM_TIMER я вообще не вижу.
Вот, сделал как ты сказал, но изображение по прежнему статично:

Код: Выделить всё

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	HDC hdcBits;
	double alpha=0,beta=0;

	switch (message)
	{
        case WM_PAINT:

		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		hdcBits=::CreateCompatibleDC(hdc);
		SelectObject(hdcBits, maskBitmap);
		BitBlt(hdc, 0, 0, 280, 280, hdcBits, 0, 0, SRCCOPY);
// SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); 
// SetLayeredWindowAttributes(hWnd, 0, 220, LWA_ALPHA);

		   { // секундная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,2,RGB(255,100,100));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
		      LineTo(hdc,119,35);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // минутная стрелка
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(80,80,80));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);

			  MoveToEx(hdc,119,119,NULL);
			  LineTo(hdc,119+65*sin(alpha),119+65*cos(alpha));//x,y);//155,65);

			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		  { // заглушка в центре циферблата
			  HPEN hpen,hPenOld;
			  hpen = CreatePen(PS_SOLID,3,RGB(170,170,170));
			  hPenOld = (HPEN)SelectObject(hdc,hpen);
			  HBRUSH hBrush,hBrushOld;
			  hBrush = CreateSolidBrush(RGB(80,0,0));
			  hBrushOld = (HBRUSH)SelectObject(hdc,hBrush);

			  Ellipse(hdc,113,113,125,125);

			  SelectObject(hdc,hBrushOld);
			  DeleteObject(hBrush);
			  SelectObject(hdc,hPenOld);
			  DeleteObject(hpen);
		  }

		DeleteDC(hdcBits);
		EndPaint(hWnd, &ps);
		break;

// ----------------------TIMER-------------------------------
    case WM_CREATE:  // обработка таймера, у которого нет своей процедуры таймера
	        SetTimer(hWnd,1,1000,NULL);     // таймер срабатывает каждые две секунды
            break;

	case WM_TIMER: // обработка сообщения WM_TIMER
		{  
			alpha+=180/30; 
			beta+=180/30;
			InvalidateRect(hWnd,0,NULL);
			return FALSE;
		}
            break;

	case WM_QUIT:
            KillTimer(hWnd,1);  // уничтожение таймера
			break;

// ---------------------END TIMER-----------------------------
    case WM_NCHITTEST:
	   {
		   return HTCAPTION;
	   }

    case WM_NCRBUTTONUP:

	   {
		   PostQuitMessage(0);
	   }

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

sin и cos принимают угол в радианах. А ты в градусах тулишь. Аргумент у обоих функций должен быть таким: 3.1415*alpha/180.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Praid
Сообщения: 5
Зарегистрирован: 12 май 2010, 00:05

Вот сделал свой таймер:

Код: Выделить всё

   int sec=0,sec2=0;
   SYSTEMTIME st;
   GetSystemTime(&st);
   sec2 = st.wSecond;
   do
   {
      SYSTEMTIME st;
      GetSystemTime(&st);
      sec = st.wSecond;
   }
   while (sec!=sec2+1);
Но он сильно грузит проц и часы подлагивают когда пытаешься их переместить.
Можно ли заменить SetTimer другим таймеров без отправки сообщений?
Ответить