Тема: AHK: Анимированная картинка «ожидание»
В продолжение темы «Эмуляция множественных потоков».
Иногда нужно показать пользователю, что скрипт ожидает какого-либо события, а не просто завис. Для такого случая можно воспользоваться анимированной картинкой. Предлагаю функцию Waiting(), создающую подобные изображения.
Каждое анимированное изображение запускается в отдельном процессе, и не загружает основной скрипт. Функция возвращает PID созданного процесса.
При корректном завершении процесса-родителя все созданные процессы также завершаются.
^1::Waiting() ; запуск по умолчанию для демонстрации
; запуск двух экземпляров одновременно с блокнотом, как окном-владельцем,
; картинки будут перемещаться вместе с окном блокнота.
; при закрытии окна-владельца процессы картинок завершатся.
^2::
DetectHiddenWindows, On
Run, notepad.exe,, hide, PID
WinWait, ahk_pid %PID%
WinMove,,, (A_ScreenWidth - 400)//2, 100, 400, 300
WinShow
hParent := WinExist()
PID1 := Waiting("", 130, 160, 50, 11, 10, 0xCCFFBE00, 10, hParent)
PID2 := Waiting("", 260, 160, 100, 22, 10, 0xCC5A5192, 6, hParent)
return
^3::Waiting(PID2) ; завершаем процесс с идентификатором PID2
; Прикольно :)
^4::Waiting("", A_ScreenWidth//2, A_ScreenHeight//2, d := A_ScreenHeight - 100, d//4, 10, 0xCCFFBE00, 5)
^Space::Waiting(0) ; закрываем все созданные процессы, передавая в функцию 0
Esc::ExitApp
return
Waiting(PID="", x="", y="", Diameter=70, d=17, Number=9, Color=0xCC70A0D0, Speed=5, hParent="")
{
/*
PID — если пустое значение — запускается новый процесс,
если числовое значекние — закрывается процесс с данным идентификатором
если 0 — закрываются все процессы, запущенные функцией
x, y — координаты центра вращения, если в параметре hParent указан хэндл
окна-владельца — считаются от его верхнего левого угла, иначе — относительно экрана
Diameter — общий диаметр изображения
d — диаметр элемента изображения
Number — количество элементов
Color — прозрачность и цвет изображения в формате 0xFFFFFFFF,
где старший байт — прозрачность, остальные — цвет в RGB
Speed — скорость вращения изображения от 0 до 10
hParent — хэндл окна-владельца, если не указан, картинка будет AlwaysOnTop
Функция возвращает PID запущенного процесса.
*/
static Waiting := new Waiting_Graphics
(x = "") ? x := A_ScreenWidth//2
(y = "") ? y := A_ScreenHeight//2
if (PID = "")
Return Waiting.RunScript(x, y, Diameter, d, Number, Color, Speed, hParent)
else
Waiting.TerminateScript(PID)
}
class Waiting_Graphics
{
__New()
{
this.PIDs := []
}
__Delete()
{
for k,v in this.PIDs
WinClose, % "ahk_pid" v
}
RunScript(x, y, Diameter, d, Number, Color, Speed, hWnd)
{
VarSetCapacity(TmpFilePath, MAX_PATH := 260)
DllCall("GetTempFileName", Str, A_Temp, Str, "Wtg", UInt, 0, Ptr, &TmpFilePath)
VarSetCapacity(TmpFilePath, -1)
FileAppend, % this.GetScript(x, y, Diameter, d, Number, Color, Speed, hWnd), % TmpFilePath
Run, % A_AhkPath " """ TmpFilePath """",,, PID
DetectHiddenWindows, On
WinWait, % "ahk_pid" PID
FileDelete, % TmpFilePath
this.PIDs.Insert(PID)
Return PID
}
TerminateScript(PID)
{
if PID
{
PostMessage, DllCall("RegisterWindowMessage", Str, "WM_INFO"),,,, % "ahk_pid" PID
for k,v in this.PIDs
if (v = PID)
this.PIDs.Remove(k)
}
else
{
for k,v in this.PIDs
PostMessage, DllCall("RegisterWindowMessage", Str, "WM_INFO"),,,, % "ahk_pid" v
this.PIDs := []
}
}
GetScript(x, y, Diameter, d, Number, Color, Speed, hWnd)
{
Script =
(
#NoTrayIcon
SetBatchLines, -1
TimePeriod = 3
DllCall("Winmm\timeBeginPeriod", UInt, TimePeriod)
OnExit, Exit
x := %x%, y := %y%
Color := %Color% & 0xFFFFFF
_trans := %Color%>>24
Number := %Number%
Width := %Diameter%
_Width := %d%
dW := _Width/Number
Speed := (%Speed% > 10 ? 10 : %Speed%)
hParent := %hWnd%
R := Width//2 - _Width//2
xR := yR := Width//2
pi := 3.141593
; WS_EX_LAYERED := 0x80000, WS_EX_TRANSPARENT := 0x20
Gui, `% "-Caption +E0x80020 +hwndID +ToolWindow " (hParent ? "+Owner" hParent : "+AlwaysOnTop")
Gui, Show, NA
OnMessage(DllCall("RegisterWindowMessage", Str, "WM_INFO"), "WM_INFO_RECEIVER")
pToken := GdipStartup()
hbm := CreateDIBSection(Width+1, Width+1)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := GraphicsFromHDC(hdc)
SetSmoothingMode(G, 4)
i := 0, Angle := pi/2 - 2*pi/Number
Time := Round(65 - Speed*(Speed < 8 ? 5 : 4.4))
Loop
{
Angle += 2*pi/Number
i := i < Number ? ++i : Number
Loop 10
{
if ExitEvent
{
_trans -= 4
if _trans <= 0
ExitApp
}
trans := _trans - (A_Index - 1)*2 + (dTrans := 20)
Width_ := _Width - 0.1*dW*(A_Index - 1) + dW
GraphicsClear(G)
Loop `% i
{
_Angle := Angle - (A_Index-1)*2*pi/Number
trans := (t := trans - dTrans) < 0 ? 0 : t
pBrush := BrushCreateSolid(trans<<24|Color)
if ( Width_ -= dW) > 0
FillEllipse(G, pBrush, Round(xR - Cos(_Angle)*R)- (Width_/2)
, Round(yR - Sin(_Angle)*R)- (Width_/2), Width_, Width_)
DllCall("gdiplus\GdipDeleteBrush", "ptr", pBrush)
}
if hParent
{
WinGetPos, xp, yp,,, ahk_id `%hParent`%
if (xp = "")
ExitApp
_x := xp + x, _y := yp + y
}
else
_x := x, _y := y
UpdateLayeredWindow(ID, hdc, _x-Width//2, _y-Width//2, Width+1, Width+1)
DllCall("Sleep", UInt, Time//10)
}
if (Angle > pi*2)
Angle -= pi*2
DllCall("Sleep", UInt, Time)
}
return
GuiClose:
ExitApp
Exit:
SelectObject(hdc, obm), DllCall("DeleteObject", "ptr", hbm)
DllCall("DeleteDC", "ptr", hdc), DllCall("gdiplus\GdipDeleteGraphics", "ptr", G)
GdipShutdown(pToken), pToken := ""
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)
ExitApp
WM_INFO_RECEIVER()
{
global ExitEvent := 1
}
GdipStartup()
{
if !DllCall("GetModuleHandle", "str", "gdiplus", "ptr")
DllCall("LoadLibrary", "str", "gdiplus")
VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
DllCall("gdiplus\GdiplusStartup", "uptr*", pToken, "ptr", &si, "ptr", 0)
return pToken
}
GdipShutdown(pToken)
{
DllCall("gdiplus\GdiplusShutdown", "uptr", pToken)
if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "ptr")
DllCall("FreeLibrary", "ptr", hModule)
return 0
}
CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
hdc2 := hdc ? hdc : DllCall("GetDC", "ptr", 0, "ptr")
VarSetCapacity(bi, 40, 0)
NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint"), NumPut(40, bi, 0, "uint")
, NumPut(1, bi, 12, "ushort"), NumPut(0, bi, 16), NumPut(bpp, bi, 14, "ushort")
hbm := DllCall("CreateDIBSection", "ptr" , hdc2, "ptr" , &bi
, "uint" , 0, "ptr*", ppvBits, "ptr" , 0, "uint" , 0, "ptr")
if !hdc
DllCall("ReleaseDC", "ptr", 0, "ptr", hdc2)
return hbm
}
CreateCompatibleDC(hdc=0)
{
return DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
}
SelectObject(hdc, hgdiobj)
{
return DllCall("SelectObject", "ptr", hdc, "ptr", hgdiobj, "ptr")
}
GraphicsFromHDC(hdc)
{
DllCall("gdiplus\GdipCreateFromHDC", "ptr", hdc, "ptr*", pGraphics)
return pGraphics
}
SetSmoothingMode(pGraphics, SmoothingMode)
{
return DllCall("gdiplus\GdipSetSmoothingMode", "ptr", pGraphics, "int", SmoothingMode)
}
GraphicsClear(pGraphics, ARGB=0x00ffffff)
{
return DllCall("gdiplus\GdipGraphicsClear", "ptr", pGraphics, "uint", ARGB)
}
BrushCreateSolid(ARGB=0xff000000)
{
DllCall("gdiplus\GdipCreateSolidFill", "uint", ARGB, "ptr*", pBrush)
return pBrush
}
FillEllipse(pGraphics, pBrush, x, y, w, h)
{
return DllCall("gdiplus\GdipFillEllipse", "ptr", pGraphics, "ptr", pBrush, "float", x, "float", y, "float", w, "float", h)
}
UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
{
if ((x != "") && (y != ""))
VarSetCapacity(pt, 8), NumPut(x, pt, 0, "uint"), NumPut(y, pt, 4, "uint")
if (w = "") ||(h = "")
WinGetPos,,, w, h, ahk_id `%hwnd`%
return DllCall("UpdateLayeredWindow", "ptr", hwnd, "ptr", 0, "ptr", ((x = "") && (y = "")) ? 0 : &pt
, "int64*", w|h<<32, "ptr", hdc, "int64*", 0, "uint", 0, "uint*", Alpha<<16|1<<24, "uint", 2)
}
)
return Script
}
}