Jump to content

_CircularProgress


Go to solution Solved by UEZ,

Recommended Posts

Posted (edited)

  3i7dy.png


I created _CircularProgress , with the idea of calling it as a single function.

; https://www.autoitscript.com/forum/topic/213118-_circularprogress/
;----------------------------------------------------------------------------------------
; Title...........: _CircularProgress.au3
; Description.....: Creates a customizable circular progress bar using GDI+ graphics.
; AutoIt Version..: 3.3.16.1   Author: ioa747  Script Version: 0.6
; Note............: Testet in Win10 22H2       Date:09/09/2025
; Based on post ..: https://www.autoitscript.com/forum/topic/213118-_circularprogress/#findComment-1545797
;----------------------------------------------------------------------------------------
#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 <WinAPISysWin.au3>
#include <WinAPIConstants.au3>
#include <Constants.au3>
#include <WindowsConstants.au3>
#include <Array.au3>


_Example() ; as demo

;~ _Example2() ; as demo


Func _Example()
    Local $aList[2000]
    Local $iCnt = UBound($aList)
    ConsoleWrite("$iCnt=" & $iCnt & @CRLF)
    Local $nProgress

    For $i = 1 To UBound($aList)
        $nProgress = Int($i / $iCnt * 100)
        ConsoleWrite($i & ")  $nProgress=" & $nProgress & @CRLF)
        _CircularProgress($nProgress)
    Next

    Sleep(500) ; Give it some time to show the 100%.
    _CircularProgress(101) ; Clean up the progress bar window
EndFunc   ;==>_Example

Func _Example2()
    Local $aList[2000]
    Local $iCnt = UBound($aList)
    Local $nProgress
    _CircularProgress(0, 300, @DesktopWidth * 0.8, @DesktopHeight * 0.3)
    For $i = 1 To UBound($aList)
        $nProgress = Int($i / $iCnt * 100)
        _CircularProgress($nProgress)
    Next

    For $i = 1 To 2
        _CircularProgress($nProgress, -1, -1, -1, -1, 0xFFBFBFBF, 0xFF4CFF00, 0xFFFFFF00)
        Sleep(500)
        _CircularProgress($nProgress, -1, -1, -1, -1, 0xFFBFBFBF, 0xFFFFFF00, 0xFF4CFF00)
        Sleep(500)
    Next

    _CircularProgress(101) ; Clean up the progress bar window
EndFunc   ;==>_Example

; #FUNCTION# ====================================================================================================================
; Name...........: _CircularProgress
; Description....: Creates a customizable circular progress bar using GDI+ graphics.
; Syntax.........: _CircularProgress($iPercent [, $iSize = 300 [, $iLeft = -1 [, $iTop = -1 [, $iThickness = 23 [, $TextCol = 0xFFBFBFBF [, $StartCol = 0xFFFFFF00, $EndCol = 0xFFFF0000]]]]]])
; Parameters.....: $iPercent     - The percentage complete for the progress bar (0 to 100).
;                  $iSize        - [optional] width and height of the circular progress bar. (Default is 300)
;                  $iLeft        - [optional] X-coordinate of the top-left corner. (Default is -1 for center)
;                  $iTop         - [optional] Y-coordinate of the top-left corner. (Default is -1 for center)
;                  $iThickness   - [optional] Thickness of the progress arc. (Default is 23)
;                  $TextCol      - [optional] Color of the text within the progress bar. (Default is Silver)
;                  $StartCol     - [optional] Start color of the progress arc gradient. (Default is Yellow)
;                  $EndCol       - [optional] End color of the progress arc gradient. (Default is Red)
; Return values .: Success: create the progress bar GUI
; Author ........: ioa747 , Thank to UEZ
; Modified ......: 09/09/2025 - v0.6
; Remarks .......: Cleanup is handled automatically by passing an $iPercent > 100.
;                  Avoid using 0x050505 Color for $TextCol, $StartCol, or $EndCol, as it is used as a transparency color for the background.
; Related .......: _GDIPlus_Startup, _GDIPlus_GraphicsCreateFromHWND, etc.
; Link ..........: https://www.autoitscript.com/forum/topic/213113-simple-circular-progressbar-with-smooth-edges-gradient-color/#findComment-1545755
; Example .......: _CircularProgress(50, 300, -1, -1, 23, 0xFFBFBFBF, 0xFFFFFF00, 0xFFFF0000)
; ===============================================================================================================================
Func _CircularProgress($iPercent, $iSize = 300, $iLeft = -1, $iTop = -1, $iThickness = 23, $TextCol = 0xFFDCDCDC, $StartCol = 0xFFFFFF00, $EndCol = 0xFFFF0000)
    Local Static $hGUI, $inSize, $inThickness, $iRadius, $iX, $iY, $bInit = False

    If Not $bInit Then
        _GDIPlus_Startup()
        $inThickness = $iThickness
        $inSize = $iSize
        $iRadius = ($inSize - 4) / 2
        $iX = $inSize / 2
        $iY = $iX
        $hGUI = GUICreate("RingProgressBar", $inSize, $inSize, $iLeft, $iTop, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
        GUISetState(@SW_SHOWNOACTIVATE)
        $bInit = True
    EndIf

    If $iPercent > 100 Then
        _GDIPlus_Shutdown()
        GUIDelete($hGUI)
        $bInit = False
        Return
    EndIf

    ; Create an off-screen bitmap for double-buffering
    Local $hBmp = _GDIPlus_BitmapCreateFromScan0($inSize, $inSize)
    Local $hBmpGraphics = _GDIPlus_ImageGetGraphicsContext($hBmp)
    _GDIPlus_GraphicsSetSmoothingMode($hBmpGraphics, 4)
    _GDIPlus_GraphicsSetTextRenderingHint($hBmpGraphics, 4)

    ; Draw progress arc as pie
    Local $angle = ($iPercent / 100) * 360
    Local $hBrushProg = _GDIPlus_LineBrushCreate($iX - $iRadius, $iY, $iX + $iRadius, $iY, $EndCol, $StartCol, 1)
    Local $hPen = _GDIPlus_PenCreate2($hBrushProg, $inThickness)
    _GDIPlus_GraphicsDrawArc($hBmpGraphics, $iX - $iRadius + $inThickness / 2, $iY - $iRadius + $inThickness / 2, _
            $iRadius * 2 - $inThickness, $iRadius * 2 - $inThickness, -90, $angle, $hPen)

    _GDIPlus_BrushDispose($hBrushProg)
    _GDIPlus_PenDispose($hPen)

    ; Draw percentage text
    Local $hFontFamily = _GDIPlus_FontFamilyCreate("Times New Roman")
    Local $iFontSize = $iRadius * 0.23
    Local $hFont = _GDIPlus_FontCreate($hFontFamily, $iFontSize, 1)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    Local $hBrushText = _GDIPlus_BrushCreateSolid($TextCol)
    _GDIPlus_StringFormatSetAlign($hFormat, 2)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 2)

    ; black outline
    Local $hBrushOutline = _GDIPlus_BrushCreateSolid(0xFF000000)
    Local $rect = _GDIPlus_RectFCreate(($inSize - ($iFontSize * 4)) / 2, ($inSize - ($iFontSize * 2)) / 2, $iFontSize * 4, $iFontSize * 2)
    Local $iOffset = 1 ; Offset for the outline, Adjust this value for thicker/thinner outline

    ; Draw outline text by offsetting the position
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, _GDIPlus_RectFCreate($rect.X - $iOffset, $rect.Y, $rect.Width, $rect.Height), $hFormat, $hBrushOutline)
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, _GDIPlus_RectFCreate($rect.X + $iOffset, $rect.Y, $rect.Width, $rect.Height), $hFormat, $hBrushOutline)
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, _GDIPlus_RectFCreate($rect.X, $rect.Y - $iOffset, $rect.Width, $rect.Height), $hFormat, $hBrushOutline)
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, _GDIPlus_RectFCreate($rect.X, $rect.Y + $iOffset, $rect.Width, $rect.Height), $hFormat, $hBrushOutline)

    ; Draw the main text on top
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, $rect, $hFormat, $hBrushText)

    _GDIPlus_BrushDispose($hBrushOutline)
    _GDIPlus_FontFamilyDispose($hFontFamily)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_BrushDispose($hBrushText)

    ; Create HBITMAP and display
    Local $hHBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBmp)
    ;_WinAPI_BitmapDisplayTransparentInGUI($hHBmp, $hGUI)
    Local $iOpacity = 0xFF, $iFlags = $ULW_ALPHA, $bReleaseGDI = True, $tDest = Null, $iBGR = 0
    If Not BitAND(GUIGetStyle($hGUI)[1], $WS_EX_LAYERED) = $WS_EX_LAYERED Then Return SetError(1, 0, 0)
    Local $tDim = DllStructCreate($tagBITMAP)
    If Not _WinAPI_GetObject($hHBmp, DllStructGetSize($tDim), DllStructGetPtr($tDim)) Then Return SetError(2, 0, 0)
    Local $tSize = DllStructCreate($tagSIZE), $tSource = DllStructCreate($tagPOINT), $tBlend = DllStructCreate($tagBLENDFUNCTION)
    Local Const $hScrDC = _WinAPI_GetDC(0), $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC), $hOld = _WinAPI_SelectObject($hMemDC, $hHBmp)
    $tSize.X = $tDim.bmWidth
    $tSize.Y = $tDim.bmHeight
    $tBlend.Alpha = $iOpacity
    $tBlend.Format = 1
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, $tDest, $tSize, $hMemDC, $tSource, $iBGR, $tBlend, $iFlags)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteDC($hMemDC)
    If $bReleaseGDI Then _WinAPI_DeleteObject($hHBmp)
    ;Return True

    _GDIPlus_BitmapDispose($hBmp)
    _GDIPlus_GraphicsDispose($hBmpGraphics)
EndFunc   ;==>_CircularProgress

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

 

 

Edited by ioa747
update to Version: 0.6

I know that I know nothing

Posted (edited)

It looks ugly on my Notebook:

 

Circular-Progress.jpg

You may use:

Func _WinAPI_BitmapDisplayTransparentInGUI(ByRef $hHBitmap, ByRef $hGUI, $iOpacity = 0xFF, $iFlags = $ULW_ALPHA, $bReleaseGDI = True, $tDest = Null, $iBGR = 0)
    If Not BitAND(GUIGetStyle($hGUI)[1], $WS_EX_LAYERED) = $WS_EX_LAYERED Then Return SetError(1, 0, 0)
    Local $tDim = DllStructCreate($tagBITMAP)
    If Not _WinAPI_GetObject($hHBitmap, DllStructGetSize($tDim), DllStructGetPtr($tDim)) Then Return SetError(2, 0, 0)
    Local $tSize = DllStructCreate($tagSIZE), $tSource = DllStructCreate($tagPOINT), $tBlend = DllStructCreate($tagBLENDFUNCTION)
    Local Const $hScrDC = _WinAPI_GetDC(0), $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC), $hOld = _WinAPI_SelectObject($hMemDC, $hHBitmap)
    $tSize.X = $tDim.bmWidth
    $tSize.Y = $tDim.bmHeight
    $tBlend.Alpha = $iOpacity
    $tBlend.Format = 1
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, $tDest, $tSize, $hMemDC, $tSource, $iBGR, $tBlend, $iFlags)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteDC($hMemDC)
    If $bReleaseGDI Then _WinAPI_DeleteObject($hHBitmap)
    Return True
EndFunc   ;==>_WinAPI_BitmapDisplayTransparentInGUI

to get a proper display. See 

for an example. 

_WinAPI_SetLayeredWindowAttributes() only makes is ugly!

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted (edited)

UEZ Thank you very much for your advice.

From what I understand
the trace you see in the image is a side effect of antialiasing, (in combination with _WinAPI_SetLayeredWindowAttributes)
which can be mitigated by changing the value to 1,
or avoided by changing the value to 0,
in the line

_GDIPlus_GraphicsSetSmoothingMode($hBmpGraphics, 2)

at a small cost to quality  😞

Smoothing mode:
    0 - Smoothing is not applied
    1 - Smoothing is applied using an 8 X 4 box filter
    2 - Smoothing is applied using an 8 X 8 box filter
(The color I have chosen for the background it is almost black, is suitable for a dark environment.)

The other solution I found is
to not make the large circle transparent, so that the ring has a background color that will fill in progressively. (which will hide the trace of the antialiasing.)


I tried to adapt _WinAPI_BitmapDisplayTransparentInGUI to my script, but I couldn't make the circle above the pie transparent, so that only the ring is visible.

Edited by ioa747

I know that I know nothing

  • Solution
Posted

To use _WinAPI_BitmapDisplayTransparentInGUI() you must create a complete frame within the function.

Example:

; https://www.autoitscript.com/forum/topic/213118-_circularprogress/
;----------------------------------------------------------------------------------------
; Title...........: _CircularProgress.au3
; Description.....: Creates a customizable circular progress bar using GDI+ graphics.
; AutoIt Version..: 3.3.16.1   Author: ioa747  Script Version: 0.5
; Note............: Testet in Win10 22H2       Date:08/09/2025
; Based on post ..: https://www.autoitscript.com/forum/topic/213113-simple-circular-progressbar-with-smooth-edges-gradient-color/#findComment-1545755
;----------------------------------------------------------------------------------------
#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 <WinAPISysWin.au3>
#include <Constants.au3>
#include <WindowsConstants.au3>
#include <WinAPIConstants.au3>
#include <Array.au3>

_GDIPlus_Startup()
Global $hGUI

_Example() ; as demo

_GDIPlus_Shutdown()


Func _Example()
    $hGUI = GUICreate("RingProgressBar", 300, 300, -1, -1, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST))
    GUISetState(@SW_SHOW)
    Local $aList[2000]
    Local $iCnt = UBound($aList)
    ConsoleWrite("$iCnt=" & $iCnt & @CRLF)
    Local $nProgress

    For $i = 1 To UBound($aList)
        $nProgress = Int($i / $iCnt * 100)
        ConsoleWrite($i & ")  $nProgress=" & $nProgress & @CRLF)
        _CircularProgress($nProgress)
    Next

    Sleep(200) ; Give it some time to see the 100%.
EndFunc   ;==>_Example

; #FUNCTION# ====================================================================================================================
; Name...........: _CircularProgress
; Description....: Creates a customizable circular progress bar using GDI+ graphics.
; Syntax.........: _CircularProgress($iPercent [, $iLeft = -1 [, $iTop = -1 [, $iSize = 300 [, $iThickness = 30 [, $TextCol = 0xFFFFFF [, $StartCol = 0xFF0000, $EndCol = 0xFFFF00]]]]]])
; Parameters.....: $iPercent     - The percentage complete for the progress bar (0 to 100).
;                  $iLeft        - [optional] X-coordinate of the top-left corner. (Default is -1 for center)
;                  $iTop         - [optional] Y-coordinate of the top-left corner. (Default is -1 for center)
;                  $iSize        - [optional] width and height of the circular progress bar. (Default is 300)
;                  $iThickness   - [optional] Thickness of the progress arc. (Default is 30)
;                  $TextCol      - [optional] Color of the text within the progress bar. (Default is White)
;                  $StartCol     - [optional] Start color of the progress arc gradient. (Default is Yellow)
;                  $EndCol       - [optional] End color of the progress arc gradient. (Default is Red)
; Return values .: Success: create the progress bar GUI
; Author ........: ioa747
; Modified ......: 08/09/2025 - v0.5
; Remarks .......: Cleanup is handled automatically by passing an $iPercent > 100.
;                  Avoid using 0x050505 Color for $TextCol, $StartCol, or $EndCol, as it is used as a transparency color for the background.
; Related .......: _GDIPlus_Startup, _GDIPlus_GraphicsCreateFromHWND, etc.
; Link ..........: https://www.autoitscript.com/forum/topic/213113-simple-circular-progressbar-with-smooth-edges-gradient-color/#findComment-1545755
; Example .......: _CircularProgress(50, -1, -1, 300, 20, 0x00FF00, 0xFF00FF, 0xFFFFFF)
; ===============================================================================================================================
Func _CircularProgress($iPercent, $iSize = 300, $iThickness = 30, $TextCol = 0xFF404040, $StartCol = 0xFFFFFF00, $EndCol = 0xFFFF0000)
    Local $hBmp, $hBmpGraphics, $inSize, $iRadius, $iX, $iY

    $inSize = $iSize
    $iRadius = ($inSize - 4) / 2
    $iX = $inSize / 2
    $iY = $iX

    ; Create an off-screen bitmap for double-buffering
    $hBmp = _GDIPlus_BitmapCreateFromScan0($inSize, $inSize)
    $hBmpGraphics = _GDIPlus_ImageGetGraphicsContext($hBmp)
    _GDIPlus_GraphicsSetSmoothingMode($hBmpGraphics, 4)
    _GDIPlus_GraphicsSetTextRenderingHint($hBmpGraphics, 4)

    ; Draw progress arc as pie
    Local $angle = ($iPercent / 100) * 360
    Local $hBrushProg = _GDIPlus_LineBrushCreate($iX - $iRadius, $iY, $iX + $iRadius, $iY, $EndCol, $StartCol, 1)
    Local $hPen = _GDIPlus_PenCreate2($hBrushProg, $iThickness)
    _GDIPlus_GraphicsDrawArc($hBmpGraphics, $iX - $iRadius + $iThickness / 2, $iY - $iRadius + $iThickness / 2, $iRadius * 2 - $iThickness, $iRadius * 2 - $iThickness, -90, $angle, $hPen)
    _GDIPlus_BrushDispose($hBrushProg)
    _GDIPlus_PenDispose($hPen)

    Local $hFontFamily = _GDIPlus_FontFamilyCreate("Times New Roman")
    Local $iFontSize = $iRadius * 0.3
    Local $hFont = _GDIPlus_FontCreate($hFontFamily, $iFontSize, 1)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 2)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 2)
    Local $hBrushText = _GDIPlus_BrushCreateSolid($TextCol)

    ; Draw percentage text
    Local $rect = _GDIPlus_RectFCreate(($inSize - ($iFontSize * 4)) / 2, ($inSize - ($iFontSize * 2)) / 2, $iFontSize * 4, $iFontSize * 2)
    _GDIPlus_GraphicsDrawStringEx($hBmpGraphics, $iPercent & "%", $hFont, $rect, $hFormat, $hBrushText)

    _GDIPlus_FontFamilyDispose($hFontFamily)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_BrushDispose($hBrushText)


    Local $hHBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBmp)
    _WinAPI_BitmapDisplayTransparentInGUI($hHBmp, $hGUI)
    _GDIPlus_BitmapDispose($hBmp)

    _GDIPlus_GraphicsDispose($hBmpGraphics)
EndFunc   ;==>_CircularProgress

Func _WinAPI_BitmapDisplayTransparentInGUI(ByRef $hHBitmap, ByRef $hGUI, $iOpacity = 0xFF, $iFlags = $ULW_ALPHA, $bReleaseGDI = True, $tDest = Null, $iBGR = 0)
    If Not BitAND(GUIGetStyle($hGUI)[1], $WS_EX_LAYERED) = $WS_EX_LAYERED Then Return SetError(1, 0, 0)
    Local $tDim = DllStructCreate($tagBITMAP)
    If Not _WinAPI_GetObject($hHBitmap, DllStructGetSize($tDim), DllStructGetPtr($tDim)) Then Return SetError(2, 0, 0)
    Local $tSize = DllStructCreate($tagSIZE), $tSource = DllStructCreate($tagPOINT), $tBlend = DllStructCreate($tagBLENDFUNCTION)
    Local Const $hScrDC = _WinAPI_GetDC(0), $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC), $hOld = _WinAPI_SelectObject($hMemDC, $hHBitmap)
    $tSize.X = $tDim.bmWidth
    $tSize.Y = $tDim.bmHeight
    $tBlend.Alpha = $iOpacity
    $tBlend.Format = 1
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, $tDest, $tSize, $hMemDC, $tSource, $iBGR, $tBlend, $iFlags)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteDC($hMemDC)
    If $bReleaseGDI Then _WinAPI_DeleteObject($hHBitmap)
    Return True
EndFunc   ;==>_WinAPI_BitmapDisplayTransparentInGUI

 

If you have static variable, such as bitmap, brushes, etc., you can put out these parts off the _CircularProgress() function.

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted (edited)

I see that the 'trick' was the change of approach from _GDIPlus_GraphicsFillPie  to _GDIPlus_GraphicsDrawArc,
and the truth is that I tried it all afternoon yesterday, with _GDIPlus_GraphicsDrawArc, I didn't succeed (I didn't know)

:thumbsup: Thank you very much for the intervention,
and the enlightenment

 

Update to Version: 0.6  in the first post

Edited by ioa747

I know that I know nothing

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...