Page 1 of 2

Circle progress bar

Posted: 31 Mar 2015, 23:43
by Bruttosozialprodukt
I posted these functions in another thread and thought, why not share them with everyone:

Code: Select all

CreateCircleProgress(diameter:=50,thickness:=5,color:=0x99009933,xPos:="center",yPos:="center",guiId:=1) {
    width := height := diameter+thickness*2
    xPos := (xPos=="center" ? A_ScreenWidth/2-diameter/2-thickness : xPos)
    yPos := (yPos=="center" ? A_ScreenHeight/2-diameter/2-thickness : yPos)
    Gui, %guiId%: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
    Gui, %guiId%: Show, NA
    
    hwnd := WinExist()
    hbm := CreateDIBSection(width, height)
    hdc := CreateCompatibleDC()
    obm := SelectObject(hdc, hbm)
    G := Gdip_GraphicsFromHDC(hdc)
    Gdip_SetSmoothingMode(G, 4)
    
    pen:=Gdip_CreatePen(color, thickness)
    Gdip_SetCompositingMode(G, 1)
    Return {hwnd:hwnd, hdc:hdc, obm:obm, hbm:hbm, pen:pen, G:G, diameter: diameter, thickness:thickness, xPos:xPos, yPos:yPos, width:width, height:height}
}
UpdateCircleProgress(circleObj,percent) {
    Gdip_Drawarc(circleObj.G, circleObj.pen, circleObj.thickness, circleObj.thickness, circleObj.diameter, circleObj.diameter, 270, 360/100*percent)
    UpdateLayeredWindow(circleObj.hwnd, circleObj.hdc, circleObj.xPos, circleObj.yPos, circleObj.width, circleObj.height)
}
DestroyCircleProgress(circleObj) {
    Gui % circleObj.hwnd ":Destroy"
    SelectObject(circleObj.hdc, circleObj.obm)
    DeleteObject(circleObj.hbm)
    DeleteDC(circleObj.hdc)
    Gdip_DeleteGraphics(circleObj.G)
}
Example 1: (Slowly fill a small progress circle)

Code: Select all

#Include, Gdip.ahk ; http://www.autohotkey.com/board/topic/29449-gdi-standard-library-145-by-tic/
#Include progressCircle.ahk

If !pToken := Gdip_Startup() {
    MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
    ExitApp
}

circleObj := CreateCircleProgress(50,5)
Loop, 100 {
    UpdateCircleProgress(circleObj,A_Index)
    Sleep, 10
}
DestroyCircleProgress(circleObj)

Gdip_Shutdown(pToken)
ExitApp
Example 2: (huge progress circle for downloads)

Code: Select all

#Include, Gdip.ahk ; http://www.autohotkey.com/board/topic/29449-gdi-standard-library-145-by-tic/
#Include progressCircle.ahk

If !pToken := Gdip_Startup() {
    MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
    ExitApp
}

DownloadFile("http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe", "firefox_setup.exe")

Gdip_Shutdown(pToken)
ExitApp

DownloadFile(UrlToFile, SaveFileAs, Overwrite := True, UseProgressBar := True) {
    ;Check if the file already exists and if we must not overwrite it
      If (!Overwrite && FileExist(SaveFileAs))
          Return
    ;Check if the user wants a progressbar
      If (UseProgressBar) {
          ;Initialize the WinHttpRequest Object
            WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
          ;Download the headers
            WebRequest.Open("HEAD", UrlToFile)
            WebRequest.Send()
          ;Store the header which holds the file size in a variable:
            FinalSize := WebRequest.GetResponseHeader("Content-Length")
          ;Create the progressbar and the timer
            Progress, H80, , Downloading..., %UrlToFile%
            circleObj := CreateCircleProgress(450,60)
            SetTimer, __UpdateProgressBar, 100
      }
    ;Download the file
      UrlDownloadToFile, %UrlToFile%, %SaveFileAs%
    ;Remove the timer and the progressbar because the download has finished
      If (UseProgressBar) {
          Progress, Off
          SetTimer, __UpdateProgressBar, Off
          DestroyCircleProgress(circleObj)
      }
    Return
    
    ;The label that updates the progressbar
      __UpdateProgressBar:
          ;Get the current filesize and tick
            CurrentSize := FileOpen(SaveFileAs, "r").Length ;FileGetSize wouldn't return reliable results
            CurrentSizeTick := A_TickCount
          ;Calculate the downloadspeed
            Speed := Round((CurrentSize/1024-LastSize/1024)/((CurrentSizeTick-LastSizeTick)/1000)) . " Kb/s"
          ;Save the current filesize and tick for the next time
            LastSizeTick := CurrentSizeTick
            LastSize := FileOpen(SaveFileAs, "r").Length
          ;Calculate percent done
            PercentDone := Round(CurrentSize/FinalSize*100)
          ;Update the ProgressBar
            Progress, %PercentDone%, %PercentDone%`% Done, Downloading...  (%Speed%), Downloading %SaveFileAs% (%PercentDone%`%)
            UpdateCircleProgress(circleObj,percentDone)
      Return
}
Screenshot:
Image

Re: Circle progress bar

Posted: 01 Apr 2015, 01:35
by Learning one
Nice work! :) I quickly rewrote it and used Class syntax, auto GDI+ on/off, auto clear-up, click through style and hopefully simplified usage.
Edit: Added more options; ability to display text, draw background, added option to disable click through style, some minor drawing modifications...
Image

Example 1 - simple:

Code: Select all

CircleProgress := new CircleProgressClass()
Loop, 100 {
	CircleProgress.Update(A_Index, "Downloading`nAutoHotkey.exe`n`n" A_Index "% done")
	Sleep, 50
}
return
Example 2 - intermediate:

Code: Select all

pToken := Gdip_Startup()	; when using multiple CircleProgressClass objects or any other GDI+ stuff, you should manually turn GDI+ on/off and release CircleProgressClass objects
CircleProgressCCleaner := new CircleProgressClass({y: 300, BackgroundColor: "ffeeeeee", BarColor: "ffaaaaaa", BarThickness: 6})
CircleProgressFirefox := new CircleProgressClass({y: 500, BackgroundColor: "ff000000", BarColor: "bbFFB200", TextColor: "ffffc018", TextStyle: "Bold", BarThickness: 40, BarDiameter: 100})
Loop, 200 {
	if (A_Index < 101)
		CircleProgressCCleaner.Update(A_Index, "Downloading`nCCleaner`n`n" A_Index "% done")
	CircleProgressFirefox.Update(A_Index/2, "Downloading`nFirefox`n`n" Round(A_Index/2) "% done")
	Sleep, 50
}
Sleep, 500
CircleProgressCCleaner := "", CircleProgressFirefox := "", Gdip_Shutdown(pToken)	; release objects, shut down GDI+
ExitApp
Class:

Code: Select all

class CircleProgressClass {		; http://ahkscript.org/boards/viewtopic.php?p=41794#p41794
	; Credits: Bruttosozialprodukt, Learning one. This code is public domain.
	static Version := 1.04
	__New(Options="") {
		this.BarDiameter := (Options.HasKey("BarDiameter") = 1) ? Options.BarDiameter : 110
		this.BarThickness := (Options.HasKey("BarThickness") = 1) ? Options.BarThickness : 16
		this.BarColor := (Options.HasKey("BarColor") = 1) ? Options.BarColor : "dd228822"
		this.BackgroundColor := (Options.HasKey("BackgroundColor") = 1) ? Options.BackgroundColor : "ffffffff"
		this.TextColor := (Options.HasKey("TextColor") = 1) ? Options.TextColor : "ee000000"
		this.TextSize := (Options.HasKey("TextSize") = 1) ? Options.TextSize : 11
		this.TextRendering := (Options.HasKey("TextRendering") = 1) ? Options.TextRendering : 5
		this.TextFont := (Options.HasKey("TextFont") = 1) ? Options.TextFont : "Arial"
		this.TextStyle := (Options.HasKey("TextStyle") = 1) ? Options.TextStyle : ""									; you can use for example  "Bold Italic"
		this.X := (Options.HasKey("X") = 1) ? Options.X : Round(A_ScreenWidth/2-this.BarDiameter/2-this.BarThickness)	; centered is defualt
		this.Y := (Options.HasKey("Y") = 1) ? Options.Y : Round(A_ScreenHeight/2-this.BarDiameter/2-this.BarThickness)	; centered is defualt
		this.W := this.BarDiameter+this.BarThickness*2+2	; it's good to add 2 extra pixels
		this.UseClickThrough := (Options.HasKey("UseClickThrough") = 1) ? Options.UseClickThrough : 1					; 1 = use it, 0 = don't use it
		
		Gui, New, +Hwndhwnd
		Gui %hwnd%: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
		Gui %hwnd%: Show, NA
		if (this.UseClickThrough = 1)
			WinSet, ExStyle, +0x20, % "ahk_id " hwnd	; click through style
		
		hbm := CreateDIBSection(this.W, this.W), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
		pPen:=Gdip_CreatePen("0x" this.BarColor, this.BarThickness)
		if (pPen = 0) {	; GDI+ is not started up - start it up now and shut it down in __Delete() automatically.
			pToken := Gdip_Startup()
			pPen:=Gdip_CreatePen("0x" this.BarColor, this.BarThickness)	; call it again (with GDI+ started up now)
		}
		G := Gdip_GraphicsFromHDC(hdc), Gdip_SetSmoothingMode(G, 4)
		
		if (this.BackgroundColor > 0)
			pBrush:=Gdip_BrushCreateSolid("0x" this.BackgroundColor)
			
		this.hwnd := hwnd, this.hdc := hdc, this.obm := obm, this.hbm := hbm, this.pPen := pPen, this.pBrush := pBrush, this.G := G, this.pToken := pToken
	}
	Update(Percent=0, Text="") {
		Gdip_GraphicsClear(this.G)		
		if (this.BackgroundColor > 0)
			Gdip_FillEllipse(this.G, this.pBrush, this.BarThickness+1, this.BarThickness+1, this.BarDiameter,this.BarDiameter)
		if (Percent>0)
			Gdip_DrawArc(this.G, this.pPen, Round(this.BarThickness/2)+1,  Round(this.BarThickness/2)+1, this.BarDiameter+this.BarThickness, this.BarDiameter+this.BarThickness, 270, Round(360/100*percent))
		if (Text!="") {
			Options := Trim("x1 y1 w" this.W-2 " h" this.W-2 " Center Vcenter r" this.TextRendering " s" this.TextSize " c" this.TextColor A_Space this.TextStyle)
			Gdip_TextToGraphics(this.G, Text, Options, this.TextFont)
		}
		;pControlPen:=Gdip_CreatePen("0xffff0000", 1), Gdip_DrawRectangle(this.G, pControlPen, 1, 1, this.W-2,this.W-2), Gdip_DeletePen(pControlPen)
		UpdateLayeredWindow(this.hwnd, this.hdc, this.X, this.Y, this.W, this.W)
	}
	Clear() {	; Just clears the graphics and updates layered window. Doesn't destroy object nor clear resources.
		Gdip_GraphicsClear(this.G)
		UpdateLayeredWindow(this.hwnd, this.hdc, this.X, this.Y, this.W, this.W)
	}
	__Delete() {
		Gdip_DeletePen(this.pPen)
		if (this.BackgroundColor > 0)
			Gdip_DeleteBrush(this.pBrush)
		Gdip_DeleteGraphics(this.G)
		SelectObject(this.hdc, this.obm)
		DeleteObject(this.hbm)
		DeleteDC(this.hdc)
		if (this.pToken != "") 	; GDI+ was obviously automatically started up in __New(), and therefore shut it down automatically now
			Gdip_Shutdown(this.pToken)
		hwnd := this.hwnd
		Gui %hwnd%: Destroy
	}
}

Re: Circle progress bar

Posted: 01 Apr 2015, 08:22
by joedf
Very nice! :D

Re: Circle progress bar

Posted: 01 Apr 2015, 08:55
by fischgeek
Oh my God I love this.

Re: Circle progress bar

Posted: 01 Apr 2015, 09:39
by Ferry
Nice! but it is not working on Unicode 64 bit for now.

Re: Circle progress bar

Posted: 01 Apr 2015, 10:12
by fischgeek
I finally get to make my circle clock with ease! Thank you!

Code: Select all

progSecond := new CircleProgressClass({BarDiameter: 75, BarThickness: 25, TextSize: 25, TextFont: "Segoe UI Light"})
progMinute := new CircleProgressClass({BarDiameter: 100, BarThickness: 25, BackgroundColor: 0})
progHour := new CircleProgressClass({BarDiameter: 150, BarThickness: 25, BackgroundColor: 0})
SetTimer, updateTime, 500
return

updateTime:
{
	FormatTime, day,, d`nMMM
	progSecond.Update(A_Sec*1.666666666666667, day)
	progMinute.Update(A_Min*1.666666666666667)
	progHour.Update(A_Hour*8.333333333333333)
	return
}

Esc::
	ExitApp

Re: Circle progress bar

Posted: 01 Apr 2015, 13:28
by joedf
Somebody make a gist!
like "Circle.ahk" or something ;)

Re: Circle progress bar

Posted: 01 Apr 2015, 13:58
by fischgeek

Re: Circle progress bar

Posted: 01 Apr 2015, 16:37
by FanaticGuru
Ferry wrote:Nice! but it is not working on Unicode 64 bit for now.
You just need the 64 bit version of gdip.

https://www.dropbox.com/s/0e9gdfetbfa8v0o/Gdip_All.ahk

You have to rename that to Gdip.ahk and put in your lib folder.

FG

Re: Circle progress bar

Posted: 01 Apr 2015, 17:34
by FanaticGuru
Learning one wrote:Class:

Code: Select all

class CircleProgressClass {		; http://ahkscript.org/boards/viewtopic.php?p=41794#p41794
	static Version := 1.02, WebSite := "http://ahkscript.org/boards/viewtopic.php?p=41794#p41794"
	__New(Options="") {
		this.BarDiameter := (Options.HasKey("BarDiameter") = 1) ? Options.BarDiameter : 120
		this.BarThickness := (Options.HasKey("BarThickness") = 1) ? Options.BarThickness : 25
		this.BarColor := (Options.HasKey("BarColor") = 1) ? Options.BarColor : "ff22aa22"
		this.BackgroundColor := (Options.HasKey("BackgroundColor") = 1) ? Options.BackgroundColor : "ffffffff"
		this.TextColor := (Options.HasKey("TextColor") = 1) ? Options.TextColor : "ee000000"
		this.TextSize := (Options.HasKey("TextSize") = 1) ? Options.TextSize : 11
		this.TextRendering := (Options.HasKey("TextRendering") = 1) ? Options.TextRendering : 5
		this.TextFont := (Options.HasKey("TextFont") = 1) ? Options.TextFont : "Arial"
		this.TextStyle := (Options.HasKey("TextStyle") = 1) ? Options.TextStyle : ""									; you can use for example  "Bold Italic"
		this.X := (Options.HasKey("X") = 1) ? Options.X : Round(A_ScreenWidth/2-this.BarDiameter/2-this.BarThickness)	; centered is defualt
		this.Y := (Options.HasKey("Y") = 1) ? Options.Y : Round(A_ScreenHeight/2-this.BarDiameter/2-this.BarThickness)	; centered is defualt
		this.W := this.BarDiameter+this.BarThickness*2
		
		Gui, New, +Hwndhwnd
		Gui %hwnd%: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
		Gui %hwnd%: Show, NA
		WinSet, ExStyle, +0x20, % "ahk_id " hwnd	; click through style
		
		hbm := CreateDIBSection(this.W, this.W), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
		pPen:=Gdip_CreatePen("0x" this.BarColor, this.BarThickness)
		if (pPen = 0) {	; GDI+ is not started up - start it up now and shut it down in __Delete() automatically 
			pToken := Gdip_Startup()
			pPen:=Gdip_CreatePen("0x" this.BarColor, this.BarThickness)	; call it again (with GDI+ started up now)
		}
		G := Gdip_GraphicsFromHDC(hdc), Gdip_SetSmoothingMode(G, 4)
		
		if (this.BackgroundColor > 0)
			pBrush:=Gdip_BrushCreateSolid("0x" this.BackgroundColor)
			
		this.hwnd := hwnd, this.hdc := hdc, this.obm := obm, this.hbm := hbm, this.pPen := pPen, this.pBrush := pBrush, this.G := G, this.pToken := pToken
	}
	Update(Percent=0, Text="") {
		Gdip_GraphicsClear(this.G)		
		if (this.BackgroundColor > 0)
			Gdip_FillEllipse(this.G, this.pBrush, this.BarThickness, this.BarThickness, this.BarDiameter,this.BarDiameter)
		if (Percent>0)
			Gdip_DrawArc(this.G, this.pPen, Round(this.BarThickness/2),  Round(this.BarThickness/2), this.BarDiameter+this.BarThickness-1, this.BarDiameter+this.BarThickness-1, 270, Round(360/100*percent))
		if (Text!="") {
			Options := Trim("x0 y0 w" this.W " h" this.W " Center Vcenter r" this.TextRendering " s" this.TextSize " c" this.TextColor A_Space this.TextStyle)
			Gdip_TextToGraphics(this.G, Text, Options, this.TextFont, this.BarDiameter, this.BarDiameter)
		}
		UpdateLayeredWindow(this.hwnd, this.hdc, this.X, this.Y, this.W, this.W)
	}
	Clear() {	; Just clears the graphics and updates layered window. Doesn't destroy object nor clear resources.
		Gdip_GraphicsClear(this.G)
		UpdateLayeredWindow(this.hwnd, this.hdc, this.X, this.Y, this.W, this.W)
	}
	__Delete() {
		Gdip_DeletePen(this.pPen)
		if (this.BackgroundColor > 0)
			Gdip_DeleteBrush(this.pBrush)
		Gdip_DeleteGraphics(this.G)
		SelectObject(this.hdc, this.obm)
		DeleteObject(this.hbm)
		DeleteDC(this.hdc)
		if (this.pToken != "") 	; GDI+ was obviously automatically started up in __New(), and therefore shut it down automatically now
			Gdip_Shutdown(this.pToken)
		hwnd := this.hwnd
		Gui %hwnd%: Destroy
	}
}
This line causes an error of Call to nonexistent function:
hbm := CreateDIBSection(this.W, this.W), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
It does not seem like this line should occur before a pToken := Gdip_Startup().

Works fine if I forgo the auto detect and insert a pToken := Gdip_Startup().

FG

Re: Circle progress bar

Posted: 02 Apr 2015, 01:38
by Ferry
FanaticGuru wrote:
Ferry wrote:Nice! but it is not working on Unicode 64 bit for now.
You just need the 64 bit version of gdip.

https://www.dropbox.com/s/0e9gdfetbfa8v0o/Gdip_All.ahk

You have to rename that to Gdip.ahk and put in your lib folder.

FG
That did the trick. Thanks!

Re: Circle progress bar

Posted: 02 Apr 2015, 05:08
by Learning one
I'm glad you like it guys :)
Nice example fischgeek!
Ferry, yes, you need Gdip_All.ahk, as FanaticGuru said.
FanaticGuru, I don't get such errors...
Bruttosozialprodukt, it seems I hijacked your thread :( I'm sorry do you want me to stop posting?

In v1.03 (see updated second post of this thread) I added option to disable click through style, and did some minor drawing modifications...

What about this concept - multiple circle progres;
Image

Re: Circle progress bar

Posted: 02 Apr 2015, 06:55
by joedf
@learningOne nonsense, Id say just add a comment header with versioning and proper credit to Bruttosozialprodukt and to you, with an open source license of your choice or public domain, etc ;)

Re: Circle progress bar

Posted: 02 Apr 2015, 08:29
by Bruttosozialprodukt
Learning one wrote:Bruttosozialprodukt, it seems I hijacked your thread :( I'm sorry do you want me to stop posting?
Yes, stop posting immediately and delete your account or I'm gonna sue you! xD

No, it's great to see that so many people like the idea so much. And I didn't plan to extend/improve the functions anyway. ;)

Re: Circle progress bar

Posted: 02 Apr 2015, 08:31
by fischgeek
@Learning one - That's awesome. I like that you changed the click-through style - though, I did like that I can click through it. I made some mods to my clock and it stays in the bottom left, but still allows me to click through. Also, thanks for the class implementation. Nice work! ;)

I do have a couple questions though about the capability if anyone can answer them...

1) Is there a physical window for this that would make it move-able? Usually in Gui's I do +ToolWindow -Caption and add a gLabel to a control with PostMessage, 0xA1, 2,,, A so that I can drag it around with a left click and drag. It would be nice to be able to do that with this. I'm assuming without wrapping this in some kind of gui it's not possible.

2) I cannot for the life of me figure out the color code is it hex with a transparency attached to it? What's the format here?

My mind is going crazy with all the possibilities and ideas I have planned for this.

Re: Circle progress bar

Posted: 02 Apr 2015, 08:59
by Bruttosozialprodukt
Color format should be 0xAARRGGBB
AA=Alpha (Opacity), RR=Red, GG=Green, BB=Blue
0xFFFF0000 - Red with full opacity
0x440000FF - Blue with low opacity

Re: Circle progress bar

Posted: 02 Apr 2015, 09:17
by fischgeek
Thank you.

Re: Circle progress bar

Posted: 02 Apr 2015, 10:28
by Learning one
@Bruttosozialprodukt: lol :) 8-) As joedf suggested, I would like to put the following text in CircleProgressClass comments: "Credits: Bruttosozialprodukt, Learning one. This code is public domain." Is that OK with you?
fischgeek wrote:... make it move-able ...
No problem :)

Code: Select all

OnMessage(0x201, "WM_LBUTTONDOWN")
CircleProgress := new CircleProgressClass({UseClickThrough: 0})	; don't use click-through style so we can click and drag it
Loop, 100 {
	CircleProgress.Update(A_Index, "Drag me...`n`n" A_Index "%")
	Sleep, 150
}
return

WM_LBUTTONDOWN() {
	global CircleProgress
	PostMessage, 0xA1, 2
	KeyWait, LButton
	WinGetPos, x,y,,, % "ahk_id " CircleProgress.hwnd
	CircleProgress.x := x, CircleProgress.y := y	; update coords
}

Re: Circle progress bar

Posted: 02 Apr 2015, 11:22
by toralf
Wouldn't it be possible to include this in the class itself, since OnMessage() does now work for more then one CallBackFunc?

Re: Circle progress bar

Posted: 02 Apr 2015, 12:20
by Bruttosozialprodukt
Learning one wrote:As joedf suggested, I would like to put the following text in CircleProgressClass comments: "Credits: Bruttosozialprodukt, Learning one. This code is public domain." Is that OK with you?
Of course. ;)