이녁 하늘 아래

소스 / Clock.au3 본문

Autoit3 및 PureBASIC

소스 / Clock.au3

이녁 2026. 1. 15. 13:23

 

Clock.au3

 

 

 

; https://www.autoitscript.com/forum/topic/213036-%F0%9F%95%91-analog-clock/
;----------------------------------------------------------------------------------------
; Title...........: Analog_Clock.au3
; Description.....: Analog clock GUI with alarm functionality.
; AutoIt Version..: 3.3.16.1   Author: ioa747  Script Version: 0.12
; Note............: Testet in Win10 22H2
;----------------------------------------------------------------------------------------
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#include <GDIPlus.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <Math.au3>
#include <Array.au3>
#include <GUIConstantsEx.au3>
#include <TrayConstants.au3>
#include <Misc.au3>
#include <Timers.au3>

Opt("TrayMenuMode", 3)  ; 0=append, 1=no default menu, 2=no automatic check, 4=menuitemID  not return

; Global Constants
Global Const $AC_SRC_ALPHA = 0x01
Global Const $GDIP_TEXTRENDERINGHINT_CLEARTYPEGRIDFIT = 5
Global Const $GDIP_STRINGFORMAT_ALIGNMENT_CENTER = 1
Global Const $GDIP_STRINGFORMAT_LINEALIGNMENT_CENTER = 1

; Global Variables
Global $hGUIClock
Global $hImageBuffer, $hGraphicBuffer
Global $hClockImage = 0
Global $hPenSeconds, $hPenMinutes, $hPenHours, $hBrushCenter
Global $iClockWidth = 600, $iClockHeight = 600

Global $mClock = _GetClockSetting(StringTrimRight(@ScriptFullPath, 4) & ".ini")

_GDIPlus_Startup()

; Try to load a clock background image
_GetClockImage($mClock.Image)

; Start the Clock GUI
_CreateClock($mClock.Width, $mClock.X, $mClock.Y)

Exit ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; Try to load the clock background image
Func _GetClockImage($sImagePath = Default)
    ; The Default clock background image
    If $sImagePath = Default Or $sImagePath = "Default" Then $sImagePath = StringTrimRight(@ScriptFullPath, 4) & ".png"

    If Not FileExists($sImagePath) Then
        ConsoleWrite("WARNING: ClockImage not found or could not be loaded initially. fallback to  Default" & @CRLF)
        ; Set $sImagePath to Default
        $sImagePath = StringTrimRight(@ScriptFullPath, 4) & ".png"
    EndIf

    ; If there isn't one, make one.
    If Not FileExists($sImagePath) Then
        ConsoleWrite("WARNING: Default Image not found or could not be loaded initially. Generating new image..." & @CRLF)
        Local $bOk = _CreateAndSaveClockImage($iClockWidth, $sImagePath)
        If $bOk Then
            ConsoleWrite("INFO: Default Image generated successfully. Attempting to load it now." & @CRLF)
        Else
            ConsoleWrite("ERROR: Failed to generate Default Image Will go to exit." & @CRLF)
            Exit
        EndIf
    EndIf

    $hClockImage = _GDIPlus_ImageLoadFromFile($sImagePath)

    ; Check if loaded image is a valid pointer and file has content
    If IsPtr($hClockImage) And FileGetSize($sImagePath) > 0 Then
        ConsoleWrite("INFO: Loaded ClockImage." & @CRLF)
    Else
        ConsoleWrite("ERROR: Failed to Loaded the ClockImage. Will go to exit." & @CRLF)
        Exit
    EndIf
EndFunc   ;==>_GetClockImage

; Clock GUI - Initialization and Main Loop
Func _CreateClock($iWidth, $iX = -1, $iY = -1)

    ; Tray Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Local $idMove = TrayCreateItem("Move")
    Local $idAlarmActive = TrayCreateItem("Alarm Enable/Disable")
    TrayItemSetState($idAlarmActive, ($mClock.Alarm = "1" ? $TRAY_CHECKED : $TRAY_UNCHECKED))
    Local $iSettings = TrayCreateMenu("Settings")
    Local $idSetAlarm = TrayCreateItem("Alarm Setting", $iSettings)
    Local $idSetColors = TrayCreateItem("Colors Setting", $iSettings)
    Local $idIniFile = TrayCreateItem("Ini file", $iSettings)
    Local $idRestart = TrayCreateItem("Restart", $iSettings)
    TrayCreateItem("") ; separator -----------------------
    Local $idClose = TrayCreateItem("Close")
    TraySetClick($TRAY_CLICK_SECONDARYUP)
    TraySetIcon("mmcndmgr.dll", -15)
    TraySetToolTip("Analog Clock")
    TraySetState($TRAY_ICONSTATE_SHOW) ; Show the tray menu.
    ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Local $hDLL = DllOpen("user32.dll") ; for _IsPressed
    $iClockWidth = $iWidth
    $iClockHeight = $iWidth

    ; The clock GUI
    $hGUIClock = GUICreate("Analog Clock", $iClockWidth, $iClockHeight, $iX, $iY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW))
    Local $idMouseLab = GUICtrlCreateLabel("", $iWidth / 2 - 5, $iWidth / 2 - 5, 10, 10)
    GUISetState(@SW_SHOW)

    $hImageBuffer = _GDIPlus_BitmapCreateFromScan0($iClockWidth, $iClockHeight)
    $hGraphicBuffer = _GDIPlus_ImageGetGraphicsContext($hImageBuffer)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicBuffer, $GDIP_SMOOTHINGMODE_HIGHQUALITY)

    ; Colors from inifile RGB to ARGB
    Local $sColorSeconds = "0xFF" & StringTrimLeft($mClock.ColorSeconds, 2)
    Local $sColorMinutes = "0xFF" & StringTrimLeft($mClock.ColorMinutes, 2)
    Local $sColorHours = "0xFF" & StringTrimLeft($mClock.ColorHours, 2)
    Local $sColorCenter = "0xFF" & StringTrimLeft($mClock.ColorCenter, 2)

    ; hands Width
    Local $iWSeconds = ($iWidth * 0.005 < 2 ? 2 : $iWidth * 0.005)
    Local $iWMinutes = ($iWidth * 0.015 < 4 ? 4 : $iWidth * 0.015)
    Local $iWHours = ($iWidth * 0.020 < 6 ? 6 : $iWidth * 0.020)

    ; Initialize pens and brush for drawing hands and center circle
    $hPenSeconds = _GDIPlus_PenCreate($sColorSeconds, $iWSeconds) ; 0xFFFF0000
    $hPenMinutes = _GDIPlus_PenCreate($sColorMinutes, $iWMinutes) ; 0xFF40C880
    $hPenHours = _GDIPlus_PenCreate($sColorHours, $iWHours)       ; 0xFF0080C0
    $hBrushCenter = _GDIPlus_BrushCreateSolid($sColorCenter)      ; 0xFF000000


    ; SetTimer so it doesn't freeze with the tray menu
    _Timer_SetTimer($hGUIClock, 250, "_RedrawClock")

    Local $bRestart, $aMpos, $iCnt = 0, $Tick = -1
    ;********************************************
    While 1
        ; Check Alarm
        If $Tick <> @MIN Then
            $Tick = @MIN
            _CheckAlarm()
            _ActivityTimeout($mClock.ActivityTimeout)
        EndIf

        ; GUI Msg $idMouseLab
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $GUI_EVENT_PRIMARYDOWN
                ; long press Left mouse button in bullet, to move the clock
                Local $a = GUIGetCursorInfo()
                If $a[4] = $idMouseLab Then
                    While _IsPressed("01", $hDLL)  ; 01 Left mouse button
                        $iCnt += 1
                        If $iCnt > 60 Then
                            $aMpos = MouseGetPos()
                            WinMove($hGUIClock, '', $aMpos[0] - $iWidth / 2, $aMpos[1] - $iWidth / 2)
                            ToolTip("Move it", $aMpos[0] + 30, $aMpos[1] - 10)
                        EndIf
                        Sleep(10)
                    WEnd
                    If $iCnt > 60 Then
                        ToolTip("")
                        $mClock.X = $aMpos[0] - $iWidth / 2
                        $mClock.Y = $aMpos[1] - $iWidth / 2
                        IniWrite($mClock.IniFile, "ClockSetting", "X", $mClock.X)
                        IniWrite($mClock.IniFile, "ClockSetting", "Y", $mClock.Y)
                    EndIf
                    $iCnt = 0
                EndIf
        EndSwitch

        ; Tray Msg
        Switch TrayGetMsg()
            Case $idClose
                ExitLoop
            Case $idMove ; move the clock
                WinActivate($hGUIClock)
                While 1
                    $aMpos = MouseGetPos()
                    WinMove($hGUIClock, '', $aMpos[0] - $iWidth / 2, $aMpos[1] - $iWidth / 2)
                    ToolTip("click to drop it", $aMpos[0] + 30, $aMpos[1] - 10)
                    If _IsPressed("01", $hDLL) Then ExitLoop ; 01 Left mouse button
                    Sleep(10)
                WEnd
                ToolTip("")
                $mClock.X = $aMpos[0] - $iWidth / 2
                $mClock.Y = $aMpos[1] - $iWidth / 2
                IniWrite($mClock.IniFile, "ClockSetting", "X", $mClock.X)
                IniWrite($mClock.IniFile, "ClockSetting", "Y", $mClock.Y)

            Case $idAlarmActive ; Alarm Enable/Disable
                If BitAND(TrayItemGetState($idAlarmActive), $TRAY_CHECKED) Then ; If $TRAY_CHECKED
                    TrayItemSetState($idAlarmActive, $TRAY_UNCHECKED)
                    IniWrite($mClock.IniFile, "ClockSetting", "Alarm", "0")
                    $mClock.Alarm = "0"
                Else                                                            ; If $TRAY_UNCHECKED
                    TrayItemSetState($idAlarmActive, $TRAY_CHECKED)
                    IniWrite($mClock.IniFile, "ClockSetting", "Alarm", "1")
                    $mClock.Alarm = "1"
                EndIf
            Case $idSetAlarm ; Alarm Setting
                _SetAlarm()
            Case $idSetColors ; Colors Setting
                _SetColors()
            Case $idIniFile ; Ini File (open ini file)
                ShellExecute($mClock.IniFile)
            Case $idRestart ; Restart the clock
                $bRestart = True
                ExitLoop
            Case $TRAY_EVENT_PRIMARYDOUBLE ; double click activate clock
                WinActivate($hGUIClock)
        EndSwitch
    WEnd
    ;********************************************

    ; Clean up resources
    _Timer_KillAllTimers($hGUIClock)
    _GDIPlus_PenDispose($hPenSeconds)
    _GDIPlus_PenDispose($hPenMinutes)
    _GDIPlus_PenDispose($hPenHours)
    _GDIPlus_BrushDispose($hBrushCenter)
    _GDIPlus_GraphicsDispose($hGraphicBuffer)
    _GDIPlus_ImageDispose($hImageBuffer)
    _GDIPlus_ImageDispose($hClockImage)
    _GDIPlus_Shutdown()
    DllClose($hDLL)
    GUIDelete($hGUIClock)

    If $bRestart Then
        Local $Cmd = StringRight(@ScriptFullPath, 4) = ".exe" ? '"' & @ScriptFullPath & '"' : '"' & @AutoItExe & '" "' & @ScriptFullPath & '"'
        Exit Run($Cmd)
    EndIf
EndFunc   ;==>_CreateClock

; Helper Function to Draw a Hand
Func _DrawHand($hGraphic, $hPen, $iX1, $iY1, $iLength, $fAngleDegrees)
    Local $fAngleRadians = _Radian($fAngleDegrees)
    Local $iX2 = $iX1 + ($iLength * Cos($fAngleRadians))
    Local $iY2 = $iY1 + ($iLength * Sin($fAngleRadians))
    _GDIPlus_GraphicsDrawLine($hGraphic, $iX1, $iY1, $iX2, $iY2, $hPen)
EndFunc   ;==>_DrawHand

; Redraw Clock Function (CallBack by timer)
Func _RedrawClock($hWnd, $iMsg, $iIDTimer, $iTime)
    #forceref $hWnd, $iMsg, $iIDTimer, $iTime

    Local $iCurrentHour = @HOUR
    Local $iCurrentMinute = @MIN
    Local $iCurrentSecond = @SEC
    Local $iCenterX = $iClockWidth / 2
    Local $iCenterY = $iClockHeight / 2
    Local $iClockRadius = $iClockWidth / 2

    _GDIPlus_GraphicsClear($hGraphicBuffer, 0x00000000)
    _GDIPlus_GraphicsDrawImageRect($hGraphicBuffer, $hClockImage, 0, 0, $iClockWidth, $iClockHeight)

    ; Calculate angles for smooth hand movement
    Local $iDisplayHour = $iCurrentHour
    If $iDisplayHour > 12 Then $iDisplayHour -= 12

    Local $fAngleHours = ($iDisplayHour * 30) + ($iCurrentMinute * 0.5) - 90
    _DrawHand($hGraphicBuffer, $hPenHours, $iCenterX, $iCenterY, $iClockRadius * 0.5, $fAngleHours)

    Local $fAngleMinutes = ($iCurrentMinute * 6) + ($iCurrentSecond * 0.1) - 90
    _DrawHand($hGraphicBuffer, $hPenMinutes, $iCenterX, $iCenterY, $iClockRadius * 0.7, $fAngleMinutes)

    Local $fAngleSeconds = ($iCurrentSecond * 6) - 90  ; + ($iCurrentMillisecond * 0.006) ; - 90
    _DrawHand($hGraphicBuffer, $hPenSeconds, $iCenterX, $iCenterY, $iClockRadius * 0.8, $fAngleSeconds)

    ; Draw center bullet
    Local $iBulletRadius = 10
    _GDIPlus_GraphicsFillEllipse($hGraphicBuffer, $iCenterX - $iBulletRadius, $iCenterY - $iBulletRadius, $iBulletRadius * 2, $iBulletRadius * 2, $hBrushCenter)

    ; Create HBITMAP and display
    Local $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImageBuffer)
    If IsPtr($hHBitmap) Then _WinAPI_BitmapDisplayTransparentInGUI($hHBitmap, $hWnd)
EndFunc   ;==>_RedrawClock

; Custom _WinAPI_BitmapDisplayTransparentInGUI Function (by UEZ)
Func _WinAPI_BitmapDisplayTransparentInGUI(ByRef $hHBitmap, ByRef $hGUI, $bReleaseGDI = True)
    If Not BitAND(GUIGetStyle($hGUI)[1], $WS_EX_LAYERED) Then Return SetError(1, 0, 0)
    Local $tSize = DllStructCreate($tagSIZE), $tSource = DllStructCreate($tagPOINT), $tBlend = DllStructCreate($tagBLENDFUNCTION)
    Local Const $hScrDC = _WinAPI_GetDC(0)
    Local Const $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    Local Const $hOldBitmap = _WinAPI_SelectObject($hMemDC, $hHBitmap)
    Local $tBitmapInfo = DllStructCreate($tagBITMAP)
    _WinAPI_GetObject($hHBitmap, DllStructGetSize($tBitmapInfo), DllStructGetPtr($tBitmapInfo))
    $tSize.X = $tBitmapInfo.bmWidth
    $tSize.Y = $tBitmapInfo.bmHeight
    $tBlend.Alpha = 0xFF
    $tBlend.Format = $AC_SRC_ALPHA
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, DllStructGetPtr($tSize), $hMemDC, DllStructGetPtr($tSource), 0, DllStructGetPtr($tBlend), $ULW_ALPHA)
    _WinAPI_SelectObject($hMemDC, $hOldBitmap)
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_ReleaseDC(0, $hScrDC)
    If $bReleaseGDI Then _WinAPI_DeleteObject($hHBitmap)
    Return True
EndFunc   ;==>_WinAPI_BitmapDisplayTransparentInGUI

; This function draws a clock dial and saves it as a PNG.
Func _CreateAndSaveClockImage($iWidth, $sFilePath)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iWidth)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINT_CLEARTYPEGRIDFIT)

    Local $iCenterX = $iWidth / 2
    Local $iCenterY = $iWidth / 2
    Local $iRadius = $iWidth / 2

    ; Clear with transparent background (ARGB: A=00)
    _GDIPlus_GraphicsClear($hGraphics, 0x00000000)

    ; Colors from inifile RGB to ARGB
    Local $sColorOutline = "0xFF" & StringTrimLeft($mClock.ColorOutline, 2)
    Local $sColorTextHour = "0xFF" & StringTrimLeft($mClock.ColorTextHour, 2)
    Local $sColorTextMin = "0xFF" & StringTrimLeft($mClock.ColorTextMin, 2)

    ; Colors and Pens/Brushes
    Local $hPenOutline = _GDIPlus_PenCreate($sColorOutline, 5) ; 0xFFEEEEEE
    Local $hPenHourMark = _GDIPlus_PenCreate($sColorOutline, 5)
    Local $hPenMinuteMark = _GDIPlus_PenCreate($sColorOutline, 2)
    Local $hBrushTextMin = _GDIPlus_BrushCreateSolid($sColorTextMin)
    Local $hBrushTextHour = _GDIPlus_BrushCreateSolid($sColorTextHour)

    ; String Format for drawing and measuring text (centered)
    Local $hStringFormatCenter = _GDIPlus_StringFormatCreate()
    ; Check if string format object was successfully created
    If Not IsPtr($hStringFormatCenter) Or $hStringFormatCenter = 0 Then
        ConsoleWrite("ERROR: Failed to create GDI+ StringFormat object." & @CRLF)
        ; Clean up other resources before returning failure
        _GDIPlus_PenDispose($hPenOutline)
        _GDIPlus_PenDispose($hPenHourMark)
        _GDIPlus_PenDispose($hPenMinuteMark)
        _GDIPlus_BrushDispose($hBrushTextMin)
        _GDIPlus_BrushDispose($hBrushTextHour)
        _GDIPlus_GraphicsDispose($hGraphics)
        _GDIPlus_ImageDispose($hBitmap)
        Return False
    EndIf
    _GDIPlus_StringFormatSetAlign($hStringFormatCenter, $GDIP_STRINGFORMAT_ALIGNMENT_CENTER)
    _GDIPlus_StringFormatSetLineAlign($hStringFormatCenter, $GDIP_STRINGFORMAT_LINEALIGNMENT_CENTER)

    ; Draw outer circle
    Local $iOutlinePadding = 5
    _GDIPlus_GraphicsDrawEllipse($hGraphics, $iOutlinePadding, $iOutlinePadding, $iWidth - (2 * $iOutlinePadding), $iWidth - (2 * $iOutlinePadding), $hPenOutline)

    ; Draw hour and minute marks
    For $i = 0 To 59
        Local $fAngleDegrees = ($i * 6) - 90
        Local $fAngleRadians = _Radian($fAngleDegrees)

        Local $iStartX, $iStartY, $iEndX, $iEndY
        Local $iMarkLength

        If Mod($i, 5) = 0 Then ; Hour marks (every 5 minutes)
            $iMarkLength = $iRadius * 0.06
            $iStartX = $iCenterX + (($iRadius - ($iRadius * 0.02)) * Cos($fAngleRadians))
            $iStartY = $iCenterY + (($iRadius - ($iRadius * 0.02)) * Sin($fAngleRadians))
            $iEndX = $iCenterX + (($iRadius - $iMarkLength - ($iRadius * 0.02)) * Cos($fAngleRadians))
            $iEndY = $iCenterY + (($iRadius - $iMarkLength - ($iRadius * 0.02)) * Sin($fAngleRadians))
            _GDIPlus_GraphicsDrawLine($hGraphics, $iStartX, $iStartY, $iEndX, $iEndY, $hPenHourMark)
        Else ; Minute marks
            $iMarkLength = $iRadius * 0.04
            $iStartX = $iCenterX + (($iRadius - ($iRadius * 0.02)) * Cos($fAngleRadians))
            $iStartY = $iCenterY + (($iRadius - ($iRadius * 0.02)) * Sin($fAngleRadians))
            $iEndX = $iCenterX + (($iRadius - $iMarkLength - ($iRadius * 0.02)) * Cos($fAngleRadians))
            $iEndY = $iCenterY + (($iRadius - $iMarkLength - ($iRadius * 0.02)) * Sin($fAngleRadians))
            _GDIPlus_GraphicsDrawLine($hGraphics, $iStartX, $iStartY, $iEndX, $iEndY, $hPenMinuteMark)
        EndIf
    Next

    ; Draw numbers (Hours and Minutes)
    Local $iHourTextRadius = $iRadius * 0.66 ; Radius for hour numbers (adjust this to move them closer/further from center)
    Local $iMinuteTextRadius = $iRadius * 0.86 ; Radius for minute numbers

    ; Define font families
    Local $sFontName = "Times New Roman"
    Local $hFontFamily = _GDIPlus_FontFamilyCreate($sFontName)

    ; Critical check for font family handle
    If Not IsPtr($hFontFamily) Or $hFontFamily = 0 Then
        ConsoleWrite("ERROR: _GDIPlus_FontFamilyCreate returned an invalid handle for '" & $sFontName & "'. @error is: " & @error & @CRLF)
        ; Clean up current resources before returning failure
        _GDIPlus_PenDispose($hPenOutline)
        _GDIPlus_PenDispose($hPenHourMark)
        _GDIPlus_PenDispose($hPenMinuteMark)
        _GDIPlus_BrushDispose($hBrushTextMin)
        _GDIPlus_BrushDispose($hBrushTextHour)
        _GDIPlus_StringFormatDispose($hStringFormatCenter)
        _GDIPlus_GraphicsDispose($hGraphics)
        _GDIPlus_ImageDispose($hBitmap)
        Return False
    EndIf

    For $i = 1 To 12
        ; Hour Numbers (1-12)
        Local $sHourDigit = $i
        Local $fHourAngleDegrees = ($i * 30) - 90
        Local $fHourAngleRadians = _Radian($fHourAngleDegrees)
        Local $iHourFontSize = 50
        Local $hHourFont = _GDIPlus_FontCreate($hFontFamily, $iHourFontSize, 1) ; 1 = Bold typeface

        ; Check for font handle
        If Not IsPtr($hHourFont) Or $hHourFont = 0 Then
            ConsoleWrite("ERROR: _GDIPlus_FontCreate failed for hour font. Skipping text drawing." & @CRLF)
            ContinueLoop ; Skip drawing this text if font creation failed
        EndIf

        ; Calculate the approximate position for the text bounding box (large enough to contain text)
        Local $fApproxTextSize = $iHourFontSize * 1.5
        Local $tHourLayoutRect = _GDIPlus_RectFCreate($iCenterX - $fApproxTextSize, $iCenterY - $fApproxTextSize, $fApproxTextSize * 2, $fApproxTextSize * 2)

        ; Measure text to get its exact dimensions
        Local $aHourMeasure = _GDIPlus_GraphicsMeasureString($hGraphics, $sHourDigit, $hHourFont, $tHourLayoutRect, $hStringFormatCenter)
        Local $fHourTextWidth = 0, $fHourTextHeight = 0
        If Not @error And IsArray($aHourMeasure) And DllStructGetData($aHourMeasure[0], "Width") > 0 And DllStructGetData($aHourMeasure[0], "Height") > 0 Then
            $fHourTextWidth = DllStructGetData($aHourMeasure[0], "Width")
            $fHourTextHeight = DllStructGetData($aHourMeasure[0], "Height")
        Else
            ; Fallback: Simple estimation if measurement fails
            $fHourTextWidth = $iHourFontSize * 0.6 * StringLen($sHourDigit)
            $fHourTextHeight = $iHourFontSize * 1.0
            ConsoleWrite("WARNING: Failed to measure hour text '" & $sHourDigit & "'. Fallback: Using estimation." & @CRLF)
        EndIf

        ; Calculate the FINAL position for the text, taking into account text dimensions and desired radius
        Local $iHourTextX = $iCenterX + ($iHourTextRadius * Cos($fHourAngleRadians)) - ($fHourTextWidth / 2)
        Local $iHourTextY = $iCenterY + ($iHourTextRadius * Sin($fHourAngleRadians)) - ($fHourTextHeight / 2)

        ; Create a layout rectangle specific for drawing the text at the calculated position
        Local $tDrawHourLayout = _GDIPlus_RectFCreate($iHourTextX, $iHourTextY, $fHourTextWidth, $fHourTextHeight)

        ; Draw the string using _GDIPlus_GraphicsDrawStringEx
        _GDIPlus_GraphicsDrawStringEx($hGraphics, $sHourDigit, $hHourFont, $tDrawHourLayout, $hStringFormatCenter, $hBrushTextHour)
        _GDIPlus_FontDispose($hHourFont)

        ; Minute Numbers (5, 10, ..., 60)
        Local $sMinuteDigit = $i * 5
        If $sMinuteDigit = 60 Then $sMinuteDigit = "00"
        Local $fMinuteAngleDegrees = ($i * 30) - 90
        Local $fMinuteAngleRadians = _Radian($fMinuteAngleDegrees)
        Local $iMinuteFontSize = 20
        Local $hMinuteFont = _GDIPlus_FontCreate($hFontFamily, $iMinuteFontSize, 1) ; 1 = Bold typeface

        ; Check for font handle
        If Not IsPtr($hMinuteFont) Or $hMinuteFont = 0 Then
            ConsoleWrite("ERROR: _GDIPlus_FontCreate failed for minute font. Skipping text drawing." & @CRLF)
            ContinueLoop ; Skip drawing this text if font creation failed
        EndIf

        Local $fApproxMinuteTextSize = $iMinuteFontSize * 1.5
        Local $tMinuteLayoutRect = _GDIPlus_RectFCreate($iCenterX - $fApproxMinuteTextSize, $iCenterY - $fApproxMinuteTextSize, $fApproxMinuteTextSize * 2, $fApproxMinuteTextSize * 2)

        ; Measure text
        Local $aMinuteMeasure = _GDIPlus_GraphicsMeasureString($hGraphics, $sMinuteDigit, $hMinuteFont, $tMinuteLayoutRect, $hStringFormatCenter)
        Local $fMinuteTextWidth = 0, $fMinuteTextHeight = 0
        If Not @error And IsArray($aMinuteMeasure) And DllStructGetData($aMinuteMeasure[0], "Width") > 0 And DllStructGetData($aMinuteMeasure[0], "Height") > 0 Then
            $fMinuteTextWidth = DllStructGetData($aMinuteMeasure[0], "Width")
            $fMinuteTextHeight = DllStructGetData($aMinuteMeasure[0], "Height")
        Else
            ; Fallback: Simple estimation if measurement fails
            $fMinuteTextWidth = $iMinuteFontSize * 0.6 * StringLen($sMinuteDigit)
            $fMinuteTextHeight = $iMinuteFontSize * 1.0
            ConsoleWrite("WARNING: Failed to measure minute text '" & $sMinuteDigit & "'. Fallback: Using estimation." & @CRLF)
        EndIf

        Local $iMinuteTextX = $iCenterX + ($iMinuteTextRadius * Cos($fMinuteAngleRadians)) - ($fMinuteTextWidth / 2)
        Local $iMinuteTextY = $iCenterY + ($iMinuteTextRadius * Sin($fMinuteAngleRadians)) - ($fMinuteTextHeight / 2)

        Local $tDrawMinuteLayout = _GDIPlus_RectFCreate($iMinuteTextX, $iMinuteTextY, $fMinuteTextWidth, $fMinuteTextHeight)

        ; Draw the string using _GDIPlus_GraphicsDrawStringEx
        _GDIPlus_GraphicsDrawStringEx($hGraphics, $sMinuteDigit, $hMinuteFont, $tDrawMinuteLayout, $hStringFormatCenter, $hBrushTextMin)
        _GDIPlus_FontDispose($hMinuteFont)
    Next

    _GDIPlus_FontFamilyDispose($hFontFamily) ; Dispose font family after the loop

    ; Save the bitmap to a PNG file
    Local $bSuccess = _GDIPlus_ImageSaveToFile($hBitmap, $sFilePath)
    ConsoleWrite("DEBUG: _GDIPlus_ImageSaveToFile $bSuccess=" & $bSuccess & ", @error=" & @error & @CRLF)

    ; Add check for file exist and size after saving
    If FileExists($sFilePath) And FileGetSize($sFilePath) > 0 Then
        Sleep(200) ; Give some time for file system to release the file
    Else
        ConsoleWrite("ERROR: File save verification failed for '" & $sFilePath & "'. Success: " & $bSuccess & ", Exists: " & FileExists($sFilePath) & ", Size: " & FileGetSize($sFilePath) & @CRLF)
        $bSuccess = False
    EndIf

    ; Clean up resources
    _GDIPlus_PenDispose($hPenOutline)
    _GDIPlus_PenDispose($hPenHourMark)
    _GDIPlus_PenDispose($hPenMinuteMark)
    _GDIPlus_BrushDispose($hBrushTextHour)
    _GDIPlus_BrushDispose($hBrushTextMin)
    _GDIPlus_StringFormatDispose($hStringFormatCenter)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_ImageDispose($hBitmap)

    Return $bSuccess
EndFunc   ;==>_CreateAndSaveClockImage

; Make, and read ini to map
Func _GetClockSetting($sIniPath)
    If Not FileExists($sIniPath) Then ; make one
        Local $sTxt = "" ; Write Example data to the ini file
        $sTxt &= "[ClockSetting]" & @CRLF
        $sTxt &= "IniFile=" & $sIniPath & @CRLF
        $sTxt &= "Image=Default" & @CRLF
        $sTxt &= "Width=600" & @CRLF
        $sTxt &= "X=-1" & @CRLF
        $sTxt &= "Y=-1" & @CRLF
        $sTxt &= "ColorSeconds=0xFF0000" & @CRLF
        $sTxt &= "ColorMinutes=0x40C880" & @CRLF
        $sTxt &= "ColorHours=0x0080C0" & @CRLF
        $sTxt &= "ColorCenter=0x000000" & @CRLF
        $sTxt &= "ColorTextHour=0xFFFFFF" & @CRLF
        $sTxt &= "ColorTextMin=0xFFFFFF" & @CRLF
        $sTxt &= "ColorOutline=0xFFFFFF" & @CRLF
        $sTxt &= "Alarm=1" & @CRLF
        $sTxt &= "AlarmSound=C:\Windows\Media\ringout.wav" & @CRLF
        $sTxt &= "AlarmDays=2,3,4,5,6" & @CRLF
        $sTxt &= "AlarmTime=17:30" & @CRLF
        $sTxt &= "ActivityTimeout=3" & @CRLF
        FileWrite($sIniPath, $sTxt)
    EndIf

    ; Read the INI section labelled 'ClockSetting'. This will return a 2 dimensional array.
    Local $aArray = IniReadSection($sIniPath, "ClockSetting")
    Local $mMap[]
    If Not @error Then
        For $i = 1 To $aArray[0][0]
            $mMap[$aArray[$i][0]] = $aArray[$i][1]
        Next
    EndIf

    ; Default colors
    If $mMap.ColorSeconds = "" Then $mMap.ColorSeconds = "0xFF0000"
    If $mMap.ColorMinutes = "" Then $mMap.ColorMinutes = "0x40C880"
    If $mMap.ColorHours = "" Then $mMap.ColorHours = "0x0080C0"
    If $mMap.ColorCenter = "" Then $mMap.ColorCenter = "0x000000"
    If $mMap.ColorTextHour = "" Then $mMap.ColorTextHour = "0xFFFFFF"
    If $mMap.ColorTextMin = "" Then $mMap.ColorTextMin = "0xFFFFFF"
    If $mMap.ColorOutline = "" Then $mMap.ColorOutline = "0xFFFFFF"

    Return $mMap
EndFunc   ;==>_GetClockSetting

; Check Alarm time
Func _CheckAlarm()
    If $mClock.Alarm <> "1" Then Return
    Local $iPos = StringInStr($mClock.AlarmDays, @WDAY)
    If Not @error And $iPos > 0 Then
        If $mClock.AlarmTime = @HOUR & ":" & @MIN Then ShellExecute($mClock.AlarmSound)
    EndIf
EndFunc   ;==>_CheckAlarm

; Set Alarm GUI
Func _SetAlarm()
    Local Const $MARGIN = 10
    Local $hGUIAlarm = GUICreate("Set Alarm", 300, 260, -1, -1, -1, BitOR($WS_EX_TOPMOST, $WS_EX_TOOLWINDOW))
    GUICtrlCreateLabel("Select Days:", $MARGIN, $MARGIN, 100, 20)
    GUICtrlSetFont(-1, 9, 800)
    Local $a_idDays[7]
    Local $aDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    Local $iY = $MARGIN + 25
    For $i = 0 To UBound($aDays) - 1
        $a_idDays[$i] = GUICtrlCreateCheckbox($aDays[$i], 10, $iY, 90, 20)
        Local $iPos = StringInStr($mClock.AlarmDays, $i + 1)
        If Not @error And $iPos > 0 Then GUICtrlSetState(-1, $GUI_CHECKED)
        $iY += (20 + 2)
    Next
    GUICtrlCreateLabel("Set Time (HH:MM):", 170, $MARGIN, 150, 20)
    GUICtrlSetFont(-1, 9, 800) ; Bold
    Local $idInputTime = GUICtrlCreateInput($mClock.AlarmTime, 180, 35, 80, 20)
    GUICtrlSetFont(-1, 10)
    Local $idInfo = GUICtrlCreateLabel("", 100, 65, 190, 100)
    GUICtrlSetFont(-1, 10)
    $iY += $MARGIN
    GUICtrlCreateLabel("Select Sound File:", $MARGIN, $iY, 150, 20)
    GUICtrlSetFont(-1, 9, 800) ; Bold
    Local $idInputSoundFile = GUICtrlCreateInput($mClock.AlarmSound, $MARGIN, $iY + 25, 240, 20)
    Local $idBtnBrowse = GUICtrlCreateButton("...", 260, $iY + 25, 30, 20)
    Local $idBtnSetAlarm = GUICtrlCreateButton("Set Alarm", 190, 170, 100, 20 + 5)
    GUICtrlSetFont(-1, 10, 800)
    GUISetState(@SW_SHOW)

    While 1
        Local $iMsg = GUIGetMsg()
        Switch $iMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idBtnBrowse
                Local $sSoundFile = FileOpenDialog("Select Alarm Sound", @WorkingDir, "Sound Files (*.mp3;*.wav)|All Files (*.*)")
                If Not @error Then GUICtrlSetData($idInputSoundFile, $sSoundFile)
            Case $idBtnSetAlarm
                Local $sTime, $sSoundPath, $sDays = "", $sInfo = ""
                GUICtrlSetData($idInfo, "")
                For $i = 0 To UBound($a_idDays) - 1
                    If GUICtrlRead($a_idDays[$i]) = $GUI_CHECKED Then $sDays &= $i + 1 & ","
                Next
                $sDays = StringTrimRight($sDays, 1) ; remove last ","
                If $sDays = "" Then $sInfo &= "⚠ Please select at least one day." & @CRLF
                $sTime = GUICtrlRead($idInputTime)
                If Not StringRegExp($sTime, "^\d{2}:\d{2}$") Then $sInfo &= "⚠ Please enter time in HH:MM format (e.g., 07:30)." & @CRLF
                $sSoundPath = "" & GUICtrlRead($idInputSoundFile)
                If $sSoundPath = "" Then $sInfo &= "⚠ Please select an sound file."
                GUICtrlSetData($idInfo, $sInfo)
                If $sInfo <> "" Then ContinueCase ; checks if everything is valid
                $mClock.AlarmDays = $sDays
                $mClock.AlarmTime = $sTime
                $mClock.AlarmSound = $sSoundPath
                IniWrite($mClock.IniFile, "ClockSetting", "AlarmDays", $sDays)
                IniWrite($mClock.IniFile, "ClockSetting", "AlarmTime", $sTime)
                IniWrite($mClock.IniFile, "ClockSetting", "AlarmSound", $sSoundPath)
                ExitLoop
        EndSwitch
    WEnd
    GUIDelete($hGUIAlarm)
EndFunc   ;==>_SetAlarm

; Set Colors GUI
Func _SetColors()
    Local $hGUIColors = GUICreate("Set Colors", 180, 250, -1, -1, -1, $WS_EX_TOOLWINDOW)
    GUISetFont(10)
    GUICtrlCreateLabel("Seconds hand", 10, 10, 101, 17, 0x0002)
    GUICtrlCreateLabel("Minutes hand", 10, 40, 101, 17, 0x0002)
    GUICtrlCreateLabel("Hours hand", 10, 70, 101, 17, 0x0002)
    GUICtrlCreateLabel("Center Pin", 10, 100, 101, 17, 0x0002)
    GUICtrlCreateLabel("Hours Didits", 10, 130, 101, 17, 0x0002)
    GUICtrlCreateLabel("Minutes Didits", 10, 160, 101, 17, 0x0002)
    GUICtrlCreateLabel("Image Outline", 10, 190, 101, 17, 0x0002)

    Local $aNames[8] = [7, "ColorSeconds", "ColorMinutes", "ColorHours", "ColorCenter", "ColorTextHour", "ColorTextMin", "ColorOutline"]
    Local $iY = 5
    Local $aColors[8][3]
    $aColors[0][0] = 7

    For $i = 1 To $aColors[0][0]
        $aColors[$i][0] = GUICtrlCreateButton("", 120, $iY, 50, 25, 0x8000)
        $aColors[$i][1] = $mClock[$aNames[$i]]
        GUICtrlSetBkColor(-1, $aColors[$i][1])
        If $i < 5 Then GUICtrlSetTip(-1, "This will be in effect after restart the clock")
        If $i > 4 Then GUICtrlSetTip(-1, "This will be in effect after deleting the existing clock image")
        $iY += 30
    Next

    Local $idSetColors = GUICtrlCreateButton("Set Colors", 70, $iY, 100, 25)
    GUICtrlSetFont(-1, 8, 800, 0, "MS Sans Serif")
    GUISetState(@SW_SHOW)

    Local $nMsg, $iChooseColor

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $aColors[1][0] To $aColors[7][0]
                ;ConsoleWrite("$nMsg=" & $nMsg & @CRLF)
                Local $id = $nMsg - 10
                $iChooseColor = _ChooseColor(2, $aColors[$id][1], 2, $hGUIColors)
                If $iChooseColor <> -1 Then
                    $aColors[$id][1] = $iChooseColor
                    GUICtrlSetBkColor($aColors[$id][0], $aColors[$id][1])
                EndIf
            Case $idSetColors
                For $i = 1 To $aColors[0][0]
                    $mClock[$aNames[$i]] = $aColors[$i][1]
                    IniWrite($mClock.IniFile, "ClockSetting", $aNames[$i], $aColors[$i][1])
                Next
                ExitLoop
        EndSwitch
    WEnd
    GUIDelete($hGUIColors)
EndFunc   ;==>_SetColors

Func _ActivityTimeout($iTimeout = 3)
    Local Static $iMinute, $iMouseX
    Local $aMpos = MouseGetPos()

    If $iMouseX <> $aMpos[0] Then
        $iMouseX = $aMpos[0]
        $iMinute  = 0
        Return
    EndIf

    $iMinute +=1

    If $iMinute = $iTimeout Then
        $iMinute = 0
        WinActivate($hGUIClock)
        ConsoleWrite("INFO: Activity Timeout triggered." & @CRLF)
    EndIf

EndFunc

 

 

 

; https://www.autoitscript.com/forum/topic/213036-%F0%9F%95%91-analog-clock/
;----------------------------------------------------------------------------------------
; Title...........: Analog_Clock.au3
; Description.....: A simple AutoIt script creating a digital analog clock GUI with alarm functionality.
; AutoIt Version..: 3.3.16.1   Author: ioa747  Script Version: 0.1
; Note............: Testet in Win10 22H2
;----------------------------------------------------------------------------------------
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <Math.au3>
#include <Array.au3>
#include <StaticConstants.au3>
#include <TrayConstants.au3>
#include <Misc.au3>

Opt("TrayMenuMode", 3)  ; 0=append, 1=no default menu, 2=no automatic check, 4=menuitemID  not return

Global $hGuiParent, $hGUIBack, $idBackTheme  ; Parent GUI & background GUI
Global $hGUI, $hGraphic                      ; main clock GUI
Global $hPenSeconds, $hPenMinutes, $hPenHours, $hBrushCenter
Global $mAlarm = _GetClockSetting(StringTrimRight(@ScriptFullPath, 4) & ".ini")

_GDIPlus_Startup()

_CreateClock($mAlarm.width, $mAlarm.x, $mAlarm.y)

;---------------------------------------------------------------------------------------
Func _DrawClock($iWidth, $iX = -1, $iY = -1)
    Local $hDLL = DllOpen("user32.dll") ; for _IsPressed
    ; Tray Menou
    Local $idMove = TrayCreateItem("Move")
    Local $idSetAlarm = TrayCreateItem("Alarm Setting")
    Local $idAlarmActive = TrayCreateItem("Alarm Enable/Disable")
    TrayItemSetState($idAlarmActive, ($mAlarm.active = "1" ? $TRAY_CHECKED : $TRAY_UNCHECKED))
    Local $idClockSetting = TrayCreateItem("Clock Setting")
    TrayCreateItem("") ; separator -----------------------
    Local $idClose = TrayCreateItem("Close")
    TraySetClick($TRAY_CLICK_SECONDARYUP)
    TraySetIcon("mmcndmgr.dll", -15)
    TraySetToolTip("Analog Clock")
    TraySetState($TRAY_ICONSTATE_SHOW) ; Show the tray menu.
    ; Clock GUI
    $hGUI = GUICreate("Analog Clock", $iWidth, $iWidth, $iX, $iY, $WS_POPUP, $WS_EX_LAYERED, $hGuiParent)
    GUISetBkColor(0x00FFFF) ; 0x00FFFF
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0x00FFFF) ; 0x00FFFF
    GUISetState(@SW_SHOW)

    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hPenSeconds = _GDIPlus_PenCreate(0xFFFF0000, 2) ; 0xFFFF0000
    $hPenMinutes = _GDIPlus_PenCreate(0xFF0000FF, 8) ; 0xFF0000FF
    $hPenHours = _GDIPlus_PenCreate(0xFF008000, 12)  ; 0xFF00FF00
    $hBrushCenter = _GDIPlus_BrushCreateSolid(0xFF000000) ; 0xFF000000

    Local $Tick = -1, $Tack = -1

    While True
        If $Tick <> @SEC Then
            $Tick = @SEC
            _RedrawClock($iWidth)
        EndIf

        If $Tack <> @MIN Then
            $Tack = @MIN
            _CheckAlarm()
        EndIf

        Switch TrayGetMsg()
            Case $idClose
                ExitLoop
            Case $idMove
                WinActivate($hGuiParent)
                While 1
                    Local $aMpos = MouseGetPos()
                    WinMove($hGuiParent, '', $aMpos[0], $aMpos[1])
                    ToolTip("click to drop it", $aMpos[0] + 30, $aMpos[1] - 10)
                    If _IsPressed("01", $hDLL) Then ExitLoop ; 01 Left mouse button
                    Sleep(10)
                WEnd
                ToolTip("")
                $mAlarm.x = $aMpos[0]
                $mAlarm.y = $aMpos[1]
                IniWrite($mAlarm.file, "ClockSetting", "x", $aMpos[0])
                IniWrite($mAlarm.file, "ClockSetting", "y", $aMpos[1])

            Case $idAlarmActive
                If BitAND(TrayItemGetState($idAlarmActive), $TRAY_CHECKED) Then ; If $TRAY_CHECKED
                    TrayItemSetState($idAlarmActive, $TRAY_UNCHECKED)
                    IniWrite($mAlarm.file, "ClockSetting", "active", "0")
                    $mAlarm.active = "0"
                Else                                                            ; If $TRAY_UNCHECKED
                    TrayItemSetState($idAlarmActive, $TRAY_CHECKED)
                    IniWrite($mAlarm.file, "ClockSetting", "active", "1")
                    $mAlarm.active = "1"
                EndIf
            Case $idSetAlarm
                _SetAlarm()
            Case $idClockSetting
                ShellExecute($mAlarm.file)
            Case $TRAY_EVENT_PRIMARYDOUBLE
                WinActivate($hGuiParent)
        EndSwitch

    WEnd

    DllClose($hDLL)
    _GDIPlus_PenDispose($hPenSeconds)
    _GDIPlus_PenDispose($hPenMinutes)
    _GDIPlus_PenDispose($hPenHours)
    _GDIPlus_BrushDispose($hBrushCenter)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_Shutdown()
EndFunc   ;==>_DrawClock
;---------------------------------------------------------------------------------------
Func _RedrawClock($iWidth)
    Local $iHeight = $iWidth
    Local $iCenterX = $iWidth / 2
    Local $iCenterY = $iHeight / 2
    Local $iClockRadius = $iWidth * 0.45
    Local $hTransparentBrush = _GDIPlus_BrushCreateSolid(0xFF00FFFF) ;0xFF00FFFF
    _GDIPlus_GraphicsFillRect($hGraphic, 0, 0, $iWidth, $iHeight, $hTransparentBrush)
    _GDIPlus_BrushDispose($hTransparentBrush)

    Local $iCurrentHour = @HOUR
    Local $iCurrentMinute = @MIN
    Local $iCurrentSecond = @SEC

    Local $iDisplayHour = $iCurrentHour
    If $iDisplayHour > 12 Then $iDisplayHour -= 12
    Local $fAngleHours = ($iDisplayHour * 30) - 90
    _DrawHand($hGraphic, $hPenHours, $iCenterX, $iCenterY, $iClockRadius * 0.5, $fAngleHours)

    Local $fAngleMinutes = ($iCurrentMinute * 6) - 90
    _DrawHand($hGraphic, $hPenMinutes, $iCenterX, $iCenterY, $iClockRadius * 0.7, $fAngleMinutes)

    Local $fAngleSeconds = ($iCurrentSecond * 6) - 90
    _DrawHand($hGraphic, $hPenSeconds, $iCenterX, $iCenterY, $iClockRadius * 0.8, $fAngleSeconds)

    Local $iBulletRadius = 10
    _GDIPlus_GraphicsFillEllipse($hGraphic, $iCenterX - $iBulletRadius, $iCenterY - $iBulletRadius, $iBulletRadius * 2, $iBulletRadius * 2, $hBrushCenter)
EndFunc   ;==>_RedrawClock
;---------------------------------------------------------------------------------------
Func _DrawHand($hGraphic, $hPen, $iX1, $iY1, $iLength, $fAngleDegrees)
    Local $fAngleRadians = _Radian($fAngleDegrees)
    Local $iX2 = $iX1 + ($iLength * Cos($fAngleRadians))
    Local $iY2 = $iY1 + ($iLength * Sin($fAngleRadians))
    _GDIPlus_GraphicsDrawLine($hGraphic, $iX1, $iY1, $iX2, $iY2, $hPen)
EndFunc   ;==>_DrawHand
;---------------------------------------------------------------------------------------
Func _CreateClock($iWidth, $iX = -1, $iY = -1)
    $hGuiParent = GUICreate("GuiParent", 10, 10, $iX, $iY, $WS_POPUP, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TRANSPARENT))
    $hGUIBack = GUICreate("GUIBack", $iWidth, $iWidth, 10 - ($iWidth / 2), 10 - ($iWidth / 2), $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $hGuiParent)
    $idBackTheme = GUICtrlCreatePic(@ScriptDir & "\Clock-600.gif", 0, 0, $iWidth, $iWidth)
    GUISetState(@SW_SHOW, $hGUIBack)
    GUISetState(@SW_SHOW, $hGuiParent)
    Local $aPos = WinGetPos($hGuiParent)
    _DrawClock($iWidth, $aPos[0] - ($iWidth / 2) + 5, $aPos[1] - ($iWidth / 2) + 5)
EndFunc   ;==>_CreateClock
;---------------------------------------------------------------------------------------
Func _GetClockSetting($sIniPath)
    If Not FileExists($sIniPath) Then ; make one
        Local $sTxt = "" ; Write Example data to the ini file
        $sTxt &= "[ClockSetting]" & @CRLF
        $sTxt &= "file=" & $sIniPath & @CRLF
        $sTxt &= "width=600" & @CRLF
        $sTxt &= "x=-1" & @CRLF
        $sTxt &= "y=-1" & @CRLF
        $sTxt &= "sound=C:\Windows\Media\ringout.wav" & @CRLF
        $sTxt &= "day=1,2,3,4,5" & @CRLF
        $sTxt &= "active=1" & @CRLF
        $sTxt &= "time=17:30" & @CRLF
        FileWrite($sIniPath, $sTxt)
    EndIf

    ; Read the INI section labelled 'ClockSetting'. This will return a 2 dimensional array.
    Local $aArray = IniReadSection($sIniPath, "ClockSetting")
    Local $mMap[]
    If Not @error Then
        For $i = 1 To $aArray[0][0]
            $mMap[$aArray[$i][0]] = $aArray[$i][1]
        Next
    EndIf
    Return $mMap
EndFunc   ;==>_GetClockSetting
;---------------------------------------------------------------------------------------
Func _CheckAlarm()
    If $mAlarm.active <> "1" Then Return
    Local $iPos = StringInStr($mAlarm.day, @WDAY)
    If Not @error And $iPos > 0 Then
        If $mAlarm.time = @HOUR & ":" & @MIN Then ShellExecute($mAlarm.sound)
    EndIf
EndFunc   ;==>_CheckAlarm
;---------------------------------------------------------------------------------------
Func _SetAlarm()
    Local Const $MARGIN = 10
    Local $hGUI = GUICreate("Set Alarm", 300, 260, -1, -1, -1, BitOR($WS_EX_TOPMOST, $WS_EX_TOOLWINDOW))
    GUICtrlCreateLabel("Select Days:", $MARGIN, $MARGIN, 100, 20)
    GUICtrlSetFont(-1, 9, 800)
    Local $a_idDays[7]
    Local $aDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    Local $iY = $MARGIN + 25
    For $i = 0 To UBound($aDays) - 1
        $a_idDays[$i] = GUICtrlCreateCheckbox($aDays[$i], 10, $iY, 90, 20)
        Local $iPos = StringInStr($mAlarm.day, $i + 1)
        If Not @error And $iPos > 0 Then GUICtrlSetState(-1, $GUI_CHECKED)
        $iY += (20 + 2)
    Next
    GUICtrlCreateLabel("Set Time (HH:MM):", 170, $MARGIN, 150, 20)
    GUICtrlSetFont(-1, 9, 800) ; Bold
    Local $idInputTime = GUICtrlCreateInput($mAlarm.time, 180, 35, 80, 20)
    GUICtrlSetFont(-1, 10)
    Local $idInfo = GUICtrlCreateLabel("", 100, 65, 190, 100)
    GUICtrlSetFont(-1, 10)
    $iY += $MARGIN
    GUICtrlCreateLabel("Select Sound File:", $MARGIN, $iY, 150, 20)
    GUICtrlSetFont(-1, 9, 800) ; Bold
    Local $idInputSoundFile = GUICtrlCreateInput($mAlarm.sound, $MARGIN, $iY + 25, 240, 20)
    Local $idBtnBrowse = GUICtrlCreateButton("...", 260, $iY + 25, 30, 20)
    Local $idBtnSetAlarm = GUICtrlCreateButton("Set Alarm", 190, 170, 100, 20 + 5)
    GUICtrlSetFont(-1, 10, 800)
    GUISetState(@SW_SHOW)

    While 1
        Local $iMsg = GUIGetMsg()
        Switch $iMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idBtnBrowse
                Local $sSoundFile = FileOpenDialog("Select Alarm Sound", @WorkingDir, "Sound Files (*.mp3;*.wav)|All Files (*.*)")
                If Not @error Then GUICtrlSetData($idInputSoundFile, $sSoundFile)
            Case $idBtnSetAlarm
                Local $sTime , $sSoundPath, $sDays = "", $sInfo = ""
                GUICtrlSetData($idInfo, "")
                For $i = 0 To UBound($a_idDays) - 1
                    If GUICtrlRead($a_idDays[$i]) = $GUI_CHECKED Then $sDays &= $i + 1 & ","
                Next
                $sDays = StringTrimRight($sDays, 1) ; remove last ","
                If $sDays = "" Then $sInfo &= "⚠ Please select at least one day." & @CRLF
                $sTime = GUICtrlRead($idInputTime)
                If Not StringRegExp($sTime, "^\d{2}:\d{2}$") Then $sInfo &= "⚠ Please enter time in HH:MM format (e.g., 07:30)." & @CRLF
                $sSoundPath = GUICtrlRead($idInputSoundFile)
                If $sSoundPath = "" Then $sInfo &= "⚠ Please select an sound file."
                GUICtrlSetData($idInfo, $sInfo)
                If $sInfo <> "" Then ContinueCase ; checks if everything is valid
                $mAlarm.day = $sDays
                $mAlarm.time = $sTime
                $mAlarm.sound = $sSoundPath
                IniWrite($mAlarm.file, "ClockSetting", "day", $sDays)
                IniWrite($mAlarm.file, "ClockSetting", "time", $sTime)
                IniWrite($mAlarm.file, "ClockSetting", "sound", $sSoundPath)
                ExitLoop
        EndSwitch
    WEnd
    GUIDelete($hGUI)
EndFunc   ;==>_SetAlarm

 

; https://www.autoitscript.com/forum/topic/213036-%F0%9F%95%91-analog-clock/
;----------------------------------------------------------------------------------------
; Title...........: AnalogClockGdi.au3
; Description.....: A simple AutoIt script creating a digital analog clock GUI.
; AutoIt Version..: 3.3.16.1   Author: ioa747  Script Version: 0.1
; Note............: Testet in Win10 22H2
;----------------------------------------------------------------------------------------
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#include <GDIPlus.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <Math.au3>
#include <Array.au3>
#include <StaticConstants.au3>

Global $hGUI, $hGraphic           ; main GUI
Global $hGUIBack, $hGraphicBack   ; background GUI
Global $hPenSeconds, $hPenMinutes, $hPenHours, $hBrushCenter

_GDIPlus_Startup()

_CreateClock(600)

Func _DrawClock($iWidth, $iX = -1, $iY = -1)
    Local $iHeight = $iWidth
    $hGUI = GUICreate("Analog Clock", $iWidth, $iHeight, $iX, $iY, $WS_POPUP, $WS_EX_LAYERED, $hGUIBack)
    GUISetBkColor(0x00FFFF) ; 0x00FFFF
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0x00FFFF) ; 0x00FFFF
    GUISetState(@SW_SHOW)

    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hPenSeconds = _GDIPlus_PenCreate(0xFFFF0000, 2)
    $hPenMinutes = _GDIPlus_PenCreate(0xFF0000FF, 8)
    $hPenHours = _GDIPlus_PenCreate(0xFF00FF00, 12)
    $hBrushCenter = _GDIPlus_BrushCreateSolid(0xFF000000)

    Local $sTime, $Tick = -1

    While True
        If $Tick <> @SEC Then
            $sTime = @HOUR & ":" & @MIN & ":" & @SEC
            ConsoleWrite("- " & $sTime & @CRLF)
            $Tick = @SEC
            _RedrawClock($iWidth)
        EndIf

        Switch GUIGetMsg()
            Case -3 ; $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _GDIPlus_GraphicsDispose($hGraphicBack)
    _GDIPlus_PenDispose($hPenSeconds)
    _GDIPlus_PenDispose($hPenMinutes)
    _GDIPlus_PenDispose($hPenHours)
    _GDIPlus_BrushDispose($hBrushCenter)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_Shutdown()

EndFunc   ;==>_DrawClock

Func _RedrawClock($iWidth)
    Local $iHeight = $iWidth
    Local $iCenterX = $iWidth / 2
    Local $iCenterY = $iHeight / 2
    Local $iClockRadius = $iWidth * 0.5
    Local $hTransparentBrush = _GDIPlus_BrushCreateSolid(0xFF00FFFF)
    _GDIPlus_GraphicsFillRect($hGraphic, 0, 0, $iWidth, $iHeight, $hTransparentBrush)
    _GDIPlus_BrushDispose($hTransparentBrush)

    Local $iCurrentHour = @HOUR
    Local $iCurrentMinute = @MIN
    Local $iCurrentSecond = @SEC

    Local $iDisplayHour = $iCurrentHour
    If $iDisplayHour > 12 Then $iDisplayHour -= 12
    Local $fAngleHours = ($iDisplayHour * 30) - 90
    _DrawHand($hGraphic, $hPenHours, $iCenterX, $iCenterY, $iClockRadius * 0.5, $fAngleHours)

    Local $fAngleMinutes = ($iCurrentMinute * 6) - 90
    _DrawHand($hGraphic, $hPenMinutes, $iCenterX, $iCenterY, $iClockRadius * 0.7, $fAngleMinutes)

    Local $fAngleSeconds = ($iCurrentSecond * 6) - 90
    _DrawHand($hGraphic, $hPenSeconds, $iCenterX, $iCenterY, $iClockRadius * 0.8, $fAngleSeconds)

    Local $iBulletRadius = 10
    _GDIPlus_GraphicsFillEllipse($hGraphic, $iCenterX - $iBulletRadius, $iCenterY - $iBulletRadius, $iBulletRadius * 2, $iBulletRadius * 2, $hBrushCenter)
EndFunc   ;==>_RedrawClock

Func _DrawHand($hGraphic, $hPen, $iX1, $iY1, $iLength, $fAngleDegrees)
    Local $fAngleRadians = _Radian($fAngleDegrees)
    Local $iX2 = $iX1 + ($iLength * Cos($fAngleRadians))
    Local $iY2 = $iY1 + ($iLength * Sin($fAngleRadians))
    _GDIPlus_GraphicsDrawLine($hGraphic, $iX1, $iY1, $iX2, $iY2, $hPen)
EndFunc   ;==>_DrawHand

Func _CreateClock($iWidth, $iX = -1, $iY = -1)
    Local $iHeight = $iWidth
    $hGUIBack = GUICreate("Clock Background", $iWidth, $iHeight, $iX, $iY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW))
    GUISetBkColor(0x00FFFF) ; 0x00FFFF
    _WinAPI_SetLayeredWindowAttributes($hGUIBack, 0x00FFFF) ; 0x00FFFF
    GUISetState(@SW_SHOW)

    ; Get the graphic context for the background GUI
    $hGraphicBack = _GDIPlus_GraphicsCreateFromHWND($hGUIBack)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicBack, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    Local $iCenterX = $iWidth / 2
    Local $iCenterY = $iHeight / 2
    Local $iClockRadius = $iWidth * 0.5
    Local $iTextRadius = $iClockRadius * 0.90 ; Adjust this value for number placement (closer to edge)

    For $i = 1 To 12
        Local $sDigit = $i
        Local $fAngleDegrees = ($i * 30) - 90
        Local $fAngleRadians = _Radian($fAngleDegrees)
        Local $iTextX = $iCenterX + ($iTextRadius * Cos($fAngleRadians))
        Local $iTextY = $iCenterY + ($iTextRadius * Sin($fAngleRadians))

        Local $iDigitWidth = 36  ; Estimate based on font size 30
        Local $iDigitHeight = 30 ; Estimate based on font size 30

        ; Draw the number - 0xFFFFFFFF = White color for numbers (ARGB)
        _GDIPlus_GraphicsDrawString($hGraphicBack, $sDigit, $iTextX - ($iDigitWidth / 2), $iTextY - ($iDigitHeight / 2), "Arial", 30, Default, 0xFFFFFFFF)
    Next

    _DrawClock($iWidth, $iX, $iY)

EndFunc   ;==>_DrawStaticClockBackground

 

 

첨부 파일 :

Clock-600.gif
0.04MB

 

 

 

출처 : 바로가기 

 

 

 

 

Comments