GDI+ 绘制模拟钟表

33 篇文章 0 订阅
31 篇文章 0 订阅

关键点:  窗口坐标(X轴向右,Y轴向下); 窗口角度逆时针; 关键函数DrawClock

#if !defined(AFX_COLOCKSTATIC_H__619C9005_4652_45A8_BC24_801ECF6ED3EA__INCLUDED_)
#define AFX_COLOCKSTATIC_H__619C9005_4652_45A8_BC24_801ECF6ED3EA__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// ColockStatic.h : header file
//

#pragma warning( push, 3 )
#undef new
#include <GdiPlus.h>
#pragma comment(lib, "GdiPlus.lib")
#pragma warning( pop )

using namespace Gdiplus;

/
// CColockStatic window

class CColockStatic : public CStatic
{
  
  // Construction
public:
  CColockStatic();
  
  // Attributes
protected:
  ULONG_PTR m_GdiPlusInitToken;
  BOOL m_bTimerStarted;
  Image *m_pImage;
  
  // Operations
public:
  BOOL DrawClock(HDC hDC, LPRECT rcRect);
  
  // Overrides
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CColockStatic)
protected:
  virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
  //}}AFX_VIRTUAL
  
  // Implementation
public:
  virtual ~CColockStatic();
  
  // Generated message map functions
protected:
  //{{AFX_MSG(CColockStatic)
  // NOTE - the ClassWizard will add and remove member functions here.
  //}}AFX_MSG
  
  DECLARE_MESSAGE_MAP()
};

/

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_COLOCKSTATIC_H__619C9005_4652_45A8_BC24_801ECF6ED3EA__INCLUDED_)

 

// ColockStatic.cpp : implementation file
//

#include "stdafx.h"
#include "CoolClock.h"
#include "ColockStatic.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CColockStatic

CColockStatic::CColockStatic()
{
  //初始化GDI+
  GdiplusStartupInput input;
  GdiplusStartupOutput output;
  Status status = GdiplusStartup( &m_GdiPlusInitToken, &input, &output );
  VERIFY( status == Gdiplus::Ok );
  m_bTimerStarted = FALSE;
  m_pImage = NULL;
}

CColockStatic::~CColockStatic()
{
  GdiplusShutdown( m_GdiPlusInitToken );
  if(m_pImage)
  {
    //delete m_pImage;
    m_pImage = NULL;
  }
}


BEGIN_MESSAGE_MAP(CColockStatic, CStatic)
//{{AFX_MSG_MAP(CColockStatic)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CColockStatic message handlers

BOOL CColockStatic::DrawClock(HDC hDC, LPRECT lpRect)
{
  CRect rcDraw = lpRect;
  
  //CRect rcClock = rcRect;
  //中心点
  CPoint ptCent = rcDraw.CenterPoint();
  
  Graphics graphics(hDC);
  //抗锯齿 
  graphics.SetSmoothingMode(SmoothingModeHighQuality);
  
  //填充背景色
  {
    SolidBrush bkBrush(Color(255, 255, 255));        
    graphics.FillRectangle(&bkBrush, 0, 0, rcDraw.Width(), rcDraw.Height());
  }
  
  //背景图片
  {
    if(m_pImage == NULL)
    {
      m_pImage = Image::FromFile(L"Debug\\024.jpg");      
    }
    
    if(m_pImage != NULL)
    {
      float fWidth = (float)m_pImage->GetWidth();
      float fHight = (float)m_pImage->GetHeight();
      
      float alpha = 0.6f; //0~1 0透明1不透明
      ColorMatrix colorMartrix=
      {
        1,0,0,0,0,
        0,1,0,0,0,
        0,0,1,0,0,
        0,0,0,alpha,0,
        0,0,0,0,1
      };
      ImageAttributes imageAttr;
      imageAttr.SetColorMatrix(&colorMartrix);
      
      RectF rect((float)rcDraw.left, (float)rcDraw.top, 
        (float)rcDraw.Width(), (float)rcDraw.Height());
      graphics.DrawImage(m_pImage, rect, 0, 0, fWidth, fHight, UnitPixel, &imageAttr);
      
    }
  }
  
  //取矩形区域
  int iMinSize = min(rcDraw.Width(), rcDraw.Height());
  int iMaxSize = max(rcDraw.Width(), rcDraw.Height());
  
  //atan(1) = π/4
  static double fPi = 4.0*atan(1.0);
  
  //画时表盘刻度 & 字体
  {
    int nWidth, nHeight;
    //nWidth = rcDraw.Width(), nHeight = rcDraw.Height(); //按椭圆处理
    nWidth = nHeight = iMinSize; //按圆处理
    
    int iERadioA = nWidth/2-6; //最外圆
    int iERadioB = nHeight/2-6; 
    int iLHRadioA = iERadioA - 40; //长时刻度
    int iLHRadioB = iERadioB - 40;
    int iSHRadioA = iERadioA - 25; //短时刻度
    int iSHRadioB = iERadioB - 25; 
    int iMRadioA  = iERadioA - 10; //分刻度
    int iMRadioB  = iERadioB - 10;
    int iTxRadioA = iLHRadioA - 20; //字体
    int iTxRadioB = iLHRadioB - 20;
    
    Pen LHPen(Color(255, 0, 0), 8.0f);
    Pen SHPen(Color(200, 0, 0), 4.0f);
    Pen SMpen(Color(0,0,0), 2.0f);
    
    double fAngle = fPi/2, fDAngle = 2.0*fPi/60.0;
    for(int i=0; i<60; i++)
    {
      BOOL bHour = i%5 == 0;
      BOOL bLHong = (i%15) == 0;
      int iIRadioA = bHour? (bLHong? iLHRadioA:iSHRadioA):iMRadioA;
      int iIRadioB = bHour? (bLHong? iLHRadioB:iSHRadioB):iMRadioB;
      
      Point s, e;
      s.X = ptCent.x + (int)(iERadioA * cos(fAngle));
      s.Y = ptCent.y - (int)(iERadioB * sin(fAngle));
      e.X = ptCent.x + (int)(iIRadioA * cos(fAngle));
      e.Y = ptCent.y - (int)(iIRadioB * sin(fAngle));
      
      Pen *plinePen = bHour? (bLHong? &LHPen:&SHPen):&SMpen;
      graphics.DrawLine(plinePen, s, e);
      
      if(bHour /*bLHong */) //写时间
      {
        Font tFont(L"Microsoft Sans Serif", 24);
        StringFormat sf;
        int iHour = 12 + (i+1)/5;
        if(iHour > 12) iHour -= 12;
        WCHAR wchBuff[16];
        swprintf_s(wchBuff, L"%d", iHour);
        
        RectF rectLayer(0,0, 1000, -1), txBound;
        graphics.MeasureString(wchBuff, -1, &tFont, rectLayer, &sf, &txBound);
        
        RectF ts = txBound;
        ts.X = (float)(ptCent.x + iTxRadioA*cos(fAngle) - txBound.Width/2);
        ts.Y = (float)(ptCent.y - iTxRadioB*sin(fAngle) - txBound.Height/2);
        
        SolidBrush txbrush(Color(0,0,0));
        graphics.DrawString(wchBuff, -1, &tFont, ts, &sf,
          &txbrush);
      }
      
      fAngle -= fDAngle;
    }
  }
  
  //绘制Logo
  {
    Font tFont(L"Microsoft Sans Serif", 36, FontStyleBoldItalic);
    StringFormat sf;
    LPCWSTR wstrLogo = L"Cool";
    
    RectF rectLayer(0,0, 1000, -1), txBound;
    graphics.MeasureString(wstrLogo, -1, &tFont, rectLayer, &sf, &txBound);
    
    RectF ts = txBound;
    ts.X = (float)(ptCent.x - txBound.Width/2);
    ts.Y = (float)(ptCent.y + iMinSize/4 - txBound.Height/2);
    
    SolidBrush txbrush(Color(255, 255, 0));
    graphics.DrawString(wstrLogo, -1, &tFont, ts, &sf,  &txbrush);
  }
  
  //画指针
  {
    SYSTEMTIME tmNow;
    GetLocalTime(&tmNow);
    
    int iSRadio = iMinSize/2-10; //秒针
    int iMRadio = iSRadio - 50; //分针
    int iHRadio = iMRadio - 30; //时针
    
    //时针
    {
      Pen sPen(Color(0, 0, 0), 15);
      Point s(ptCent.x, ptCent.y), e;
      double fTime = tmNow.wHour
        + tmNow.wMinute/60.0 
        + tmNow.wSecond/3600.0
        + 0;
      double fAng = fPi/2.0 - fTime * (2.0*fPi) /12.0;
      e.X = (int)(ptCent.x + iHRadio * cos(fAng));
      e.Y = (int)(ptCent.y - iHRadio * sin(fAng));
      
      graphics.DrawLine(&sPen, s, e);
      
    }
    
    //分针
    {
      Pen sPen(Color(0, 0, 0), 8);
      Point s(ptCent.x, ptCent.y), e;
      double fTime = tmNow.wMinute 
        + tmNow.wSecond/60.0
        + 0;
      double fAng = fPi/2.0 - fTime * (2.0*fPi) /60.0;
      e.X = (int)(ptCent.x + iMRadio * cos(fAng));
      e.Y = (int)(ptCent.y - iMRadio * sin(fAng));
      
      graphics.DrawLine(&sPen, s, e);
    }
    
    //秒针
    {
      Pen sPen(Color(255, 0, 0), 3);
      Point s(ptCent.x, ptCent.y), e;
      double fTime = tmNow.wSecond 
        + tmNow.wMilliseconds/1000.0
        + 0;
      double fAng = fPi/2.0 - fTime * (2.0*fPi) /60.0;
      e.X = (int)(ptCent.x + iSRadio * cos(fAng));
      e.Y = (int)(ptCent.y - iSRadio * sin(fAng));
      
      graphics.DrawLine(&sPen, s, e);
    }
    
  }
  
  //中心点
  {
    RectF rc(ptCent.x - 15.0f, ptCent.y - 15.0f, 30.0f, 30.0f);
    SolidBrush brush(Color(255, 0, 0));
    graphics.FillEllipse(&brush, rc);
  }
  
  //画外圆
  {
    Rect rc(rcDraw.left + 4, rcDraw.top+4, rcDraw.Width()-8, rcDraw.Height()-8); //按椭圆
    //Rect rc(ptCent.x + iSize/2 + 4, ptCent.y - iSize/2+4, iSize/2-8, iSize/2-8); //按圆形
    
    Pen exRound(Color(230, 230, 0, 0), 6);
    graphics.DrawEllipse(&exRound, rc);
  }  
  
  
  return TRUE;
}


LRESULT CColockStatic::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
  // TODO: Add your specialized code here and/or call the base class
  switch(message)
  {
  case(WM_CREATE):
    {
      if(SetTimer(1, 100, NULL))
      {
        m_bTimerStarted = -1;
      }
      
      break;
    }
  case(WM_DESTROY):
    {
      if(m_bTimerStarted)
      {
        KillTimer(1);
      }
      break;
    }
  case(WM_TIMER):
    {
      Invalidate(TRUE);
      break;
    }
  case(WM_ERASEBKGND):
    {
      return 1;
    }
  case(WM_PAINT):
    {      
      CRect rcRect;
      GetClientRect(&rcRect);
      
      PAINTSTRUCT ps = {0};
      CDC *pDstDC = BeginPaint(&ps);
      
      //双缓冲绘制
      CDC memDC, *pDC = &memDC;
      pDC->CreateCompatibleDC(pDstDC);
      int nSaveDC = pDC->SaveDC();
      CBitmap memBmp;
      memBmp.CreateCompatibleBitmap(pDstDC, rcRect.Width(), rcRect.Height());
      CBitmap *pOldBmp = pDC->SelectObject(&memBmp);
      
      //画钟
      DrawClock(pDC->m_hDC, rcRect);
      
      //贴图
      pDstDC->BitBlt(rcRect.left, rcRect.top, rcRect.Width(), rcRect.Height(),
        pDC, 
        0, 0, 
        SRCCOPY);
      
      //结束清理      
      pDC->SelectObject(pOldBmp);
      pDC->RestoreDC(nSaveDC);
      
      EndPaint(&ps); 
      
      //启动定时刷新
      if(!m_bTimerStarted
        && SetTimer(1, 100, NULL))
      {
        m_bTimerStarted = -2;
      }
      
      return 0;
    }
  }
  
  return CStatic::WindowProc(message, wParam, lParam);
}

 

//应用

// CoolClockDlg.h : header file
//

#if !defined(AFX_COOLCLOCKDLG_H__E6592336_D273_41DE_9520_CD85332A0F96__INCLUDED_)
#define AFX_COOLCLOCKDLG_H__E6592336_D273_41DE_9520_CD85332A0F96__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "ColockStatic.h"

/
// CCoolClockDlg dialog

class CCoolClockDlg : public CDialog
{
// Construction
public:
	CCoolClockDlg(CWnd* pParent = NULL);	// standard constructor

// Dialog Data
	//{{AFX_DATA(CCoolClockDlg)
	enum { IDD = IDD_COOLCLOCK_DIALOG };
	CColockStatic m_ClockStatic;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CCoolClockDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	//{{AFX_MSG(CCoolClockDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnSize(UINT nType, int cx, int cy);
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_COOLCLOCKDLG_H__E6592336_D273_41DE_9520_CD85332A0F96__INCLUDED_)
// CoolClockDlg.cpp : implementation file
//

#include "stdafx.h"
#include "CoolClock.h"
#include "CoolClockDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CCoolClockDlg dialog

CCoolClockDlg::CCoolClockDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CCoolClockDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CCoolClockDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCoolClockDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CCoolClockDlg)
	DDX_Control(pDX, IDC_COOL_CLOCK, m_ClockStatic);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CCoolClockDlg, CDialog)
	//{{AFX_MSG_MAP(CCoolClockDlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CCoolClockDlg message handlers

BOOL CCoolClockDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCoolClockDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCoolClockDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CCoolClockDlg::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here

  CRect rcClient;
  GetClientRect(&rcClient);

	if(m_ClockStatic.m_hWnd)
  {
    m_ClockStatic.MoveWindow(&rcClient, TRUE);
  }
}

 

效果预览

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用 GDI+ 在窗口中绘制滚动条,你可以使用 `Graphics` 类来绘制滚动条的背景和滑块。下面是一个示例代码,展示了如何在 C++ 中使用 GDI+ 绘制一个简单的垂直滚动条: ```cpp #include <iostream> #include <windows.h> #include <gdiplus.h> using namespace Gdiplus; LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 创建窗口 HWND hWnd; WNDCLASSEX wc = {}; wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = L"ScrollBarExample"; RegisterClassEx(&wc); hWnd = CreateWindowEx(0, L"ScrollBarExample", L"ScrollBar Example", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 300, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); // 初始化 GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); // 消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 清理 GDI+ GdiplusShutdown(gdiplusToken); return static_cast<int>(msg.wParam); } LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // 创建 Graphics 对象 Graphics graphics(hdc); // 获取窗口尺寸 RECT rect; GetClientRect(hWnd, &rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; // 绘制滚动条背景 SolidBrush backBrush(Color(220, 220, 220)); graphics.FillRectangle(&backBrush, width - 20, 0, 20, height); // 绘制滑块 int thumbHeight = 50; // 滑块的高度 int thumbTop = 0; // 滑块的顶部坐标,根据实际需要计算 SolidBrush thumbBrush(Color(100, 100, 100)); graphics.FillRectangle(&thumbBrush, width - 20, thumbTop, 20, thumbHeight); EndPaint(hWnd, &ps); break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } ``` 上述代码创建了一个简单的窗口,并在窗口的右侧绘制了一个垂直滚动条。在 `WM_PAINT` 消息处理函数中,我们创建了一个 `Graphics` 对象,并使用 `FillRectangle` 函数绘制了滚动条的背景和滑块。滑块的位置和大小可以根据实际需求进行计算和调整。 请注意,上述代码中的 `ScrollBarExample` 需要根据你的实际情况进行替换。此外,你需要在项目中链接 GDI+ 库,并包含相应的头文件和命名空间。 希望这对你有帮助!如果有任何问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值