1 (изменено: yalanne, 2015-06-25 00:20:34)

Тема: AHK: Работа с GDI+. Линия диаметра круга без остатка

+ скрипт
#NoEnv
#SingleInstance FORCE
SetWinDelay,0
SetBatchLines,-1
#Include gdip.ahk    ;https://www.dropbox.com/s/0e9gdfetbfa8v0o/Gdip_All.ahk?dl=1

Global XPos,YPos,layer1,layer2

; запуск gdi+. ширина\длина,
Gdip_Startup()    ,    Width := Height := 400

; Первый слой
Gui, 1: -Caption +E0x80000 +LastFound +ToolWindow +OwnDialogs +HWNDlayer1
Gui, 1: Show,

; отслеживание сообщений.
OnMessage(0x201, "WM_LBUTTONDOWN") ; нажатие левой кнопки мыши для переноса

;Сообщения
WM_LBUTTONDOWN()
{    
    PostMessage, 0xA1, 2
}

; увеличить рамки картинки под радиус разворота, и развернуть
Angle := 23
Gdip_GetRotatedDimensions(Width, Height, Angle, RWidth, RHeight)
Gdip_GetRotatedTranslation(Width, Height, Angle, xTranslation, yTranslation)



;первый слой
hbm := CreateDIBSection(RWidth, RHeight)     ,    hdc := CreateCompatibleDC()    ,    obm := SelectObject(hdc, hbm)    ,    G := Gdip_GraphicsFromHDC(hdc)    , Gdip_SetInterpolationMode(G, 7)
Gdip_TranslateWorldTransform(G, xTranslation, yTranslation)
Gdip_RotateWorldTransform(G, Angle)

Gdip_SetSmoothingMode(G, 5) ; сглаживание на 5

;первый слой
; рисуем основной круг 
pBrush := Gdip_BrushCreateSolid(0xe69900CC) , Gdip_FillEllipse(G, pBrush, 0, 0, Width, Height) , Gdip_DeleteBrush(pBrush)

;разметка
pPen := Gdip_CreatePen(0xfff000000, 1)
Gdip_DrawLines(G, pPen    , 0 "," 0 "|" Width "," Height ) 
Gdip_DeletePen(pPen) 

; обводка
pPen := Gdip_CreatePen(0xfff000000, 2) , Gdip_DrawEllipse(G, pPen, 0, 0, Width, Height) , Gdip_DeletePen(pPen) 


; показать нарисованное
UpdateLayeredWindow(layer1, hdc, (A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)
return

~1::
;убрать лишние линии которые зашли за границу
pPen := Gdip_CreatePen(0x00000000, 100) , Gdip_SetCompositingMode(G, 1) , Gdip_DrawEllipse(G, pPen, -50, -50, Width+100, Height+100) , Gdip_SetCompositingMode(G, 0) , Gdip_DeletePen(pPen)
; обводка
pPen := Gdip_CreatePen(0xfff000000, 2) , Gdip_DrawEllipse(G, pPen, 0, 0, Width, Height) , Gdip_DeletePen(pPen) 
UpdateLayeredWindow(layer1, hdc, (A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)
return

Этот скрипт рисует круг, а на круге линию диаметра. Проблема в том что линия выходит за круг:

+ открыть спойлер

http://i.imgur.com/pn0JTDQ.png

Приходится отдельно вырезать линии. На кнопку 1 отрезает лишнее.(можно и автоматом, просто для наглядности)

+ открыть спойлер

http://i.imgur.com/gwL9mcA.png

Эта линия рисуется от левого верхнего угла рабочей области до правого нижнего + разворот на 23 градуса
Задача: что бы линия сразу же на рисовалась нужного размера под круг, как это сделать?

2

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

Эта линия рисуется от левого верхнего угла рабочей области до правого нижнего + разворот на 23 градуса

Это так сложно вы объясняете, что линия должна быть под углом 68° к горизонтали?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3 (изменено: Irbis, 2015-06-25 02:36:35)

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

Строка №45:

cut := Round(Width*0.1464), Gdip_DrawLines(G, pPen    , cut "," cut "|" Width - cut "," Height - cut )

Это для частного случая, если W<>H, то расчет будет сложней.

4

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

Чтобы нарисовать круг и линию, никакие повороты и обрезки изображения точно не нужны.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5 (изменено: yalanne, 2015-06-25 12:42:52)

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

+ скрипт
#NoEnv
#SingleInstance FORCE
SetWinDelay,0
SetBatchLines,-1
#Include gdip.ahk    ;https://www.dropbox.com/s/0e9gdfetbfa8v0o/Gdip_All.ahk?dl=1

Global XPos,YPos,layer1,layer2

; запуск gdi+. ширина\длина,
Gdip_Startup()    ,    Width := Height := 400

; Первый слой
Gui, 1: -Caption +E0x80000 +LastFound +OwnDialogs +HWNDlayer1 ;+ToolWindow
Gui, 1: Show,

; отслеживание сообщений.
OnMessage(0x201, "WM_LBUTTONDOWN") ; нажатие левой кнопки мыши для переноса

;Сообщения
WM_LBUTTONDOWN()
{    
    PostMessage, 0xA1, 2
}

; увеличить рамки картинки под радиус разворота, и развернуть
Angle := 23
Gdip_GetRotatedDimensions(Width, Height, Angle, RWidth, RHeight)
Gdip_GetRotatedTranslation(Width, Height, Angle, xTranslation, yTranslation)



;первый слой
hbm := CreateDIBSection(RWidth, RHeight)     ,    hdc := CreateCompatibleDC()    ,    obm := SelectObject(hdc, hbm)    ,    G := Gdip_GraphicsFromHDC(hdc)    , Gdip_SetInterpolationMode(G, 7)
;Gdip_TranslateWorldTransform(G, xTranslation, yTranslation)
;Gdip_RotateWorldTransform(G, Angle)

Gdip_SetSmoothingMode(G, 5) ; сглаживание на 5

;первый слой
; рисуем основной круг 
pBrush := Gdip_BrushCreateSolid(0xe69900CC) , Gdip_FillEllipse(G, pBrush, 0, 0, Width, Height) , Gdip_DeleteBrush(pBrush)

;разметка
pPen := Gdip_CreatePen(0xfff000000, 1)
;Gdip_DrawLines(G, pPen    , 0 "," 0 "|" Width "," Height ) 
cut := Round(Width*0.1464), cut2 := Round((Width//6)*0.1464)

;Gdip_DrawLines(G, pPen    , cut "," cut "|" Width - cut "," Height - cut ) ;       \
;Gdip_DrawLines(G, pPen    , cut "," Height - cut "|" Width - cut "," cut) ;     /

;Gdip_DrawLines(G, pPen    , 0 "," Height//2 "|" Width "," Height//2) ;         ―
;Gdip_DrawLines(G, pPen    , Width//2 "," 0 "|" Width//2 "," Height) ;             |


Gdip_DrawLines(G, pPen    , Width//2 - cut + cut2  "," Height//2 - cut + cut2 "|" cut "," cut )

Gdip_DrawLines(G, pPen    , Width//2 + cut - cut2  "," Height//2 - cut + cut2 "|" Width - cut "," cut)

Gdip_DrawLines(G, pPen    , Width//2  "," Height//2 - cut - cut2 "|" Width//2 "," 0)

Gdip_DrawEllipse(G, pPen, Width//3, Height//3, Width//3, Height//3)

Gdip_DeletePen(pPen) 

; обводка
pPen := Gdip_CreatePen(0xfff000000, 2) , Gdip_DrawEllipse(G, pPen, 0, 0, Width, Height) , Gdip_DeletePen(pPen) 


; показать нарисованное
UpdateLayeredWindow(layer1, hdc, (A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)
return

Спасибо. теперь надо что бы линия доходила до внутреннего кольца

+ открыть спойлер

http://i.imgur.com/pdfKVZ0.png

но не доходит.

п.с Простите меня за мои познания в геометрии.

И еще можно ли сделать заливку определенного куска не вырисовывая отдельную часть нужного цвета?

6 (изменено: teadrinker, 2015-06-29 19:24:37)

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

Первый вариант:

#Include gdip.ahk
Width := Height := 402
GuiX := (A_ScreenWidth-Width)//2
GuiY := (A_ScreenHeight-Height)//2
Radius := 200  ; радиус круга

BackColor := 0xE69900CC  ; цвет круга
CircumferenceColor := 0xff000000  ; цвет окружности
CircumferenceThickness := 2  ; толщина окружности

DiameterColor := 0xff000000   ; цвет диаметра
DiameterThickness := 1  ; толщина диаметра
DiameterAngle := 68  ; угол наклона

oGraph := new Graphics(GuiX, GuiY, Width, Height)
oGraph.DrawCircle(BackColor, 201, 201, Radius)  ; 201, 201 — x и y центра круга
oGraph.DrawCircumference(CircumferenceColor, CircumferenceThickness, 201, 201, Radius - CircumferenceThickness/2)
oGraph.DrawDiameter(DiameterColor, DiameterThickness, 201, 201, Radius, DiameterAngle)
oGraph := ""
Return

Esc:: ExitApp

class Graphics
{
   __New(GuiX, GuiY, Width, Height)  {
      static WS_EX_LAYERED := 0x80000, WS_EX_TRANSPARENT := 0x20
      
      Pi := 4*(4*ATan(1/5) - ATan(1/239))
      this.Gr := Pi/180
      this.Width := Width
      this.Height := Height
      
      this.pToken := Gdip_Startup()
      Gui, Graphics:Default
      Gui, % "-Caption +E" WS_EX_LAYERED " +ToolWindow +AlwaysOnTop +hwndhGui -DPIScale"
      this.hGui := hGui
      Gui, Show, NA x%GuiX% y%GuiY% w%Width% h%Height%
      
      this.GraphicObjects := []
      this.CreateNewGraphicObj(Width, Height)
   }
   
   __Delete()  {
      this.GraphicObjects := ""
      Gdip_Shutdown(this.pToken)
   }
   
   __Get(key)  {
      if RegExMatch(key, "i)(G|hdc)(\d+)", match)
         Return this.GraphicObjects[match2][match1]
   }
   
   CreateNewGraphicObj(Width, Height)  {
      GrObj := new this.NewGraphicObj(Width, Height)
      Return this.GraphicObjects.Push(GrObj)
   }
   
   DrawCircle(color, CX, CY, R)  {
      pBrush := Gdip_BrushCreateSolid(color)
      Gdip_FillEllipse(this.G1, pBrush, CX - R, CY - R, 2*R, 2*R)
      Gdip_DeleteBrush(pBrush)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   DrawCircumference(color, thickness, CX, CY, R)  {
      pPen := Gdip_CreatePen(color, thickness)
      Gdip_DrawEllipse(this.G1, pPen, CX - R, CY - R, 2*R, 2*R)
      Gdip_DeletePen(pPen)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   DrawDiameter(color, thickness, CX, CY, R, angle)  {
      Gr := this.Gr
      x1 := Cos(angle*Gr)*R + CX
      y1 := Sin(angle*Gr)*R + CY
      x2 := Cos((angle + 180)*Gr)*R + CX
      y2 := Sin((angle + 180)*Gr)*R + CY
      pPen := Gdip_CreatePen(color, thickness)
      Gdip_DrawLine(this.G1, pPen, x1, y1, x2, y2)
      Gdip_DeletePen(pPen)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   class NewGraphicObj
   {
      __New(Width, Height)  {
         this.hbm := CreateDIBSection(Width, Height)
         this.hdc := CreateCompatibleDC()
         this.obm := SelectObject(this.hdc, this.hbm)
         this.G := Gdip_GraphicsFromHDC(this.hdc)
         Gdip_SetSmoothingMode(this.G, 4)
      }
      
      __Delete()  {
         SelectObject(this.hdc, this.obm), DeleteObject(this.hbm)
         DeleteDC(this.hdc), Gdip_DeleteGraphics(this.G)
      }
   }
}

Второй по аналогии:

#Include gdip.ahk
Width := Height := 402
GuiX := (A_ScreenWidth-Width)//2
GuiY := (A_ScreenHeight-Height)//2
Radius := 200  ; радиус круга

BackColor := 0xE69900CC  ; цвет круга
CircumferenceColor := 0xff000000  ; цвет окружности

oGraph := new Graphics(GuiX, GuiY, Width, Height)
oGraph.DrawCircle(BackColor, 201, 201, Radius)  ; 201, 201 — x и y центра круга
oGraph.DrawCircumference(CircumferenceColor, 2, 201, 201, Radius - 1)
oGraph.DrawCircumference(CircumferenceColor, 1, 201, 201, Radius/3)

angle := -135  ; угол наклона первого радиуса
Loop 3
   oGraph.DrawRadiusBetweenCircles(0xff000000, 1, 201, 201, Radius - 1, Radius/3, angle + 45 * (A_Index - 1))
Graphics := ""
Return

class Graphics
{
   __New(GuiX, GuiY, Width, Height)  {
      static WS_EX_LAYERED := 0x80000, WS_EX_TRANSPARENT := 0x20
      
      Pi := 4*(4*ATan(1/5) - ATan(1/239))
      this.Gr := Pi/180
      this.Width := Width
      this.Height := Height
      
      this.pToken := Gdip_Startup()
      Gui, Graphics:Default
      Gui, % "-Caption +E" WS_EX_LAYERED " +ToolWindow +AlwaysOnTop +hwndhGui -DPIScale"
      this.hGui := hGui
      Gui, Show, NA x%GuiX% y%GuiY% w%Width% h%Height%
      
      this.GraphicObjects := []
      this.CreateNewGraphicObj(Width, Height)
   }
   
   __Delete()  {
      this.GraphicObjects := ""
      Gdip_Shutdown(this.pToken)
   }
   
   __Get(key)  {
      if RegExMatch(key, "i)(G|hdc)(\d+)", match)
         Return this.GraphicObjects[match2][match1]
   }
   
   CreateNewGraphicObj(Width, Height)  {
      GrObj := new this.NewGraphicObj(Width, Height)
      Return this.GraphicObjects.Push(GrObj)
   }
   
   DrawCircle(color, CX, CY, R)  {
      pBrush := Gdip_BrushCreateSolid(color)
      Gdip_FillEllipse(this.G1, pBrush, CX - R, CY - R, 2*R, 2*R)
      Gdip_DeleteBrush(pBrush)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   DrawCircumference(color, thickness, CX, CY, R)  {
      pPen := Gdip_CreatePen(color, thickness)
      Gdip_DrawEllipse(this.G1, pPen, CX - R, CY - R, 2*R, 2*R)
      Gdip_DeletePen(pPen)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   DrawRadiusBetweenCircles(color, thickness, CX, CY, R1, R2, angle)  {
      Gr := this.Gr
      x1 := Cos(angle*Gr)*R1 + CX
      y1 := Sin(angle*Gr)*R1 + CY
      x2 := Cos(angle*Gr)*R2 + CX
      y2 := Sin(angle*Gr)*R2 + CY
      pPen := Gdip_CreatePen(color, thickness)
      Gdip_DrawLine(this.G1, pPen, x1, y1, x2, y2)
      Gdip_DeletePen(pPen)
      UpdateLayeredWindow(this.hGui, this.hdc1)
   }
   
   class NewGraphicObj
   {
      __New(Width, Height)  {
         this.hbm := CreateDIBSection(Width, Height)
         this.hdc := CreateCompatibleDC()
         this.obm := SelectObject(this.hdc, this.hbm)
         this.G := Gdip_GraphicsFromHDC(this.hdc)
         Gdip_SetSmoothingMode(this.G, 4)
      }
      
      __Delete()  {
         SelectObject(this.hdc, this.obm), DeleteObject(this.hbm)
         DeleteDC(this.hdc), Gdip_DeleteGraphics(this.G)
      }
   }
}

можно ли сделать заливку определенного куска не вырисовывая отдельную часть нужного цвета?

Gdip_FillPolygon()

+ Ну и немного баловства
SetBatchLines, -1
SetWinDelay, 0
global WS_EX_LAYERED := 0x80000, WS_EX_TRANSPARENT := 0x20
#Include gdip.ahk  ; закомментировать, если не нужно

Width := Height := 404
GuiX := (A_ScreenWidth-Width)//2
GuiY := (A_ScreenHeight-Height)//2
R1 := 195, R2 := 130
CX := 202, CY := 202
StartAngle := -90
PenColor := 0xFFFEBA4C
BackColor := 0xCA0000AA

oGraph := new Graphics(GuiX, GuiY, Width, Height)
OnExit, Exit
oGraph.DrawCircle(PenColor, 8, CX, CY, R1, StartAngle, 1500, 1)
Sleep, 200
oGraph.DrawCircle(PenColor, 8, CX, CY, R2, StartAngle, 1500, 1)
Sleep, 200
oGraph.DrawCircle(BackColor, t := (R1 - 4) - (R2 + 4), CX, CY, (R1 - 4) - t/2, StartAngle, 1500, 1)
Sleep, 200

oGraph.DrawRays(PenColor, 2, CX, CY, R1 - 2, R2 + 2, StartAngle, 32, 300, 2)
oGraph.Rotation(PenColor, 2, CX, CY, R1 - 2, R2 + 2, StartAngle, 32, 1000, 12)
Return

Esc:: ExitApp
Exit:
   oGraph.GraphicObjects := ""
   (oGraph.Period && DllCall("Winmm\timeEndPeriod", UInt, oGraph.MinPeriod))
   oGraph := ""
   ExitApp

class Graphics
{
   __New(GuiX, GuiY, Width, Height)  {
      VarSetCapacity(TIMECAPS, 8, 0)
      if DllCall("Winmm\timeGetDevCaps", Ptr, &TIMECAPS, UInt, 8) != 0  {
         MsgBox, Ошибка в timeGetDevCaps()
         ExitApp
      }
      this.MinPeriod := NumGet(TIMECAPS, "UInt")
      
      Pi := 4*(4*ATan(1/5) - ATan(1/239))
      this.Gr := Pi/180
      this.Width := Width
      this.Height := Height
      
      this.pToken := Gdip_Startup()
      Gui, Graphics:Default
      Gui, % "-Caption +E" WS_EX_LAYERED|WS_EX_TRANSPARENT " +ToolWindow +AlwaysOnTop +hwndhGui -DPIScale"
      this.hGui := hGui
      Gui, Show, NA x%GuiX% y%GuiY% w%Width% h%Height%
      
      this.GraphicObjects := []
      this.CreateNewGraphicObj(Width, Height)
   }
   
   __Delete()  {
      this.GraphicObjects := ""
      Gdip_Shutdown(this.pToken)
      (this.Period && DllCall("Winmm\timeEndPeriod", UInt, this.MinPeriod))
   }
   
   __Get(key)  {
      if RegExMatch(key, "i)(G|hdc)(\d+)", match)
         Return this.GraphicObjects[match2][match1]
   }
   
   CreateNewGraphicObj(Width, Height)  {
      GrObj := new this.NewGraphicObj(Width, Height)
      Return this.GraphicObjects.Push(GrObj)
   }
   
   SnapShot()  {
      n := this.CreateNewGraphicObj(this.Width, this.Height)
      BitBlt(this["hdc" n], 0, 0, this.Width, this.Height, this.hdc1, 0, 0)
      Return n
   }
   
   DrawCircle(color, thickness, CX, CY, R, StartAngle, Frequency, Duration)  {
      key := this.SnapShot()
      Grads := 0
      
      pPen := Gdip_CreatePen(color, thickness)
      DllCall("Winmm\timeBeginPeriod", UInt, this.MinPeriod), this.Period := 1
      Loop % Frequency  {
         Gdip_GraphicsClear(this.G1)
         BitBlt(this.hdc1, 0, 0, this.Width, this.Height, this["hdc" key], 0, 0)
         Gdip_DrawArc(this.G1, pPen, CX - R, CY - R, 2*R, 2*R, StartAngle, Grads += 360/Frequency)
         UpdateLayeredWindow(this.hGui, this.hdc1)
         DllCall("Sleep", UInt, Duration)
      }
      DllCall("Winmm\timeEndPeriod", UInt, this.MinPeriod), this.Period := ""
      Gdip_DeletePen(pPen)
      this.GraphicObjects.Pop()
   }
   
   DrawRays(color, thickness, CX, CY, R1, R2, StartAngle, Num, Frequency, Duration)  {
      R := R1
      Gr := this.Gr
      key := this.SnapShot()
      
      pPen := Gdip_CreatePen(color, thickness)
      DllCall("Winmm\timeBeginPeriod", UInt, this.MinPeriod), this.Period := 1
      Loop % Frequency  {
         R -= (R1 - R2)/Frequency
         Gdip_GraphicsClear(this.G1)
         BitBlt(this.hdc1, 0, 0, this.Width, this.Height, this["hdc" key], 0, 0)
         
         Loop % Num  {
            Grads := StartAngle + 360/Num * (A_Index - 1)
            x1 := Cos(Grads*Gr)*R1 + CX
            y1 := Sin(Grads*Gr)*R1 + CY
            x2 := Cos(Grads*Gr)*R + CX
            y2 := Sin(Grads*Gr)*R + CY
            
            Gdip_DrawLine(this.G1, pPen, x1, y1, x2, y2)
         }
         UpdateLayeredWindow(this.hGui, this.hdc1)
         DllCall("Sleep", UInt, Duration)
      }
      DllCall("Winmm\timeEndPeriod", UInt, this.MinPeriod), this.Period := ""
      Gdip_DeletePen(pPen)
   }

   Rotation(color, thickness, CX, CY, R1, R2, StartAngle, Num, Frequency, Duration)  {
      Gr := this.Gr
      shift := 0
      
      pPen := Gdip_CreatePen(color, thickness)
      DllCall("Winmm\timeBeginPeriod", UInt, this.MinPeriod), this.Period := 1
      Loop
      {
         Gdip_GraphicsClear(this.G1)
         BitBlt(this.hdc1, 0, 0, this.Width, this.Height, this["hdc" this.GraphicObjects.MaxIndex()], 0, 0)
         shift += 360/Frequency
         (shift >= 360 && shift := 0)
         Loop % Num
         {
            Grads := StartAngle + 360/Num * (A_Index - 1)
            x1 := Cos(Grads*Gr)*R1 + CX
            y1 := Sin(Grads*Gr)*R1 + CY
            x2 := Cos((Grads + shift)*Gr)*R2 + CX
            y2 := Sin((Grads + shift)*Gr)*R2 + CY
            
            Gdip_DrawLine(this.G1, pPen, x1, y1, x2, y2)
         }
         UpdateLayeredWindow(this.hGui, this.hdc1)
         DllCall("Sleep", UInt, Duration)
      }
      DllCall("Winmm\timeEndPeriod", UInt, this.MinPeriod), this.Period := ""
      Gdip_DeletePen(pPen)
   }

   class NewGraphicObj
   {
      __New(Width, Height)  {
         this.hbm := CreateDIBSection(Width, Height)
         this.hdc := CreateCompatibleDC()
         this.obm := SelectObject(this.hdc, this.hbm)
         this.G := Gdip_GraphicsFromHDC(this.hdc)
         Gdip_SetSmoothingMode(this.G, 4)
      }
      
      __Delete()  {
         SelectObject(this.hdc, this.obm), DeleteObject(this.hbm)
         DeleteDC(this.hdc), Gdip_DeleteGraphics(this.G)
      }
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7

Re: AHK: Работа с GDI+. Линия диаметра круга без остатка

Ну и немного баловства

Интересный пример.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
Win10x64 v20H2, AutoHotkey_L v1.1.33.09 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui