Numeric1 Posted May 31 Posted May 31 (edited) Most of the 7-Segment display examples I've encountered are rigid and inflexible: it's difficult to easily change the color of the clock or resize it with a single click. While they may be effective, they lack flexibility. Say no to unmaintainable code. This script creates a dynamic graphical clock that displays the current time in segmented digits. It utilizes the GDI+ and AutoItObject libraries to draw an intuitive and responsive graphical interface. This detailed presentation highlights the technical and professional aspects of the code. Objectives and Features Dynamic Time Display: The main goal is to visually and legibly represent the current time. The time digits are displayed as segments, with ":" separators indicating hours, minutes, and seconds. Interface Adaptability: The graphical interface dynamically adjusts to the window size, ensuring optimal presentation on any screen. When the window is resized, the grid and graphical elements are recalculated and redrawn to maintain a consistent appearance. Use of External Libraries: The script leverages the advanced features of GDI+ for graphical rendering and AutoItObject for object-oriented management. These external libraries offer extensive functionalities and a high-level abstraction to simplify development. expandcollapse popup#include-once #include <Array.au3> #include <AutoItObject.au3> #include <GDIPlus.au3> #include <GUIConstantsEx.au3> #include <WinAPI.au3> #include <WinAPIGdi.au3> #include <WindowsConstants.au3> Global $g___bEXIT = False HotKeySet("{ESC}", "_Exit") _GDIPlus_Startup() _AutoItObject_Startup() Const $COLOR_RED = 0xFFFF0000 Const $COLOR_GREEN = 0xFF00FF00 ; Line Class Func Line($x1, $y1, $x2, $y2, $color = $COLOR_RED, $thickness = 2) Local $cLine = _AutoItObject_Class() With $cLine .AddProperty("x1", $ELSCOPE_PUBLIC, $x1) .AddProperty("y1", $ELSCOPE_PUBLIC, $y1) .AddProperty("x2", $ELSCOPE_PUBLIC, $x2) .AddProperty("y2", $ELSCOPE_PUBLIC, $y2) .AddProperty("color", $ELSCOPE_PUBLIC, $color) .AddProperty("thickness", $ELSCOPE_PUBLIC, $thickness) EndWith Return $cLine.Object EndFunc ;==>Line ; GridPane Class Func GridPane($numCells, $title, $width, $height) Local $cGridPane = _AutoItObject_Class() Local $hGUI = GUICreate($title, $width, $height, -1, -1, $WS_SIZEBOX + $WS_SYSMENU) GUISetBkColor(0x000000, $hGUI) GUISetState(@SW_SHOW, $hGUI) Local $hDC = _WinAPI_GetDC($hGUI) Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC) Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $width, $height) Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC) Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap) Local $hMemGraphics = _GDIPlus_GraphicsCreateFromHDC($hMemDC) Local $aArray[0] $width = _WinAPI_GetClientWidth($hGUI) $height = _WinAPI_GetClientHeight($hGUI) With $cGridPane .AddProperty("hGUI", $ELSCOPE_PUBLIC, $hGUI) .AddProperty("hDC", $ELSCOPE_PUBLIC, $hDC) .AddProperty("hGraphics", $ELSCOPE_PUBLIC, $hGraphics) .AddProperty("hBitmap", $ELSCOPE_PUBLIC, $hBitmap) .AddProperty("hMemDC", $ELSCOPE_PUBLIC, $hMemDC) .AddProperty("hOldBmp", $ELSCOPE_PUBLIC, $hOldBmp) .AddProperty("hMemGraphics", $ELSCOPE_PUBLIC, $hMemGraphics) .AddProperty("numCells", $ELSCOPE_PUBLIC, $numCells) .AddProperty("cellWidth", $ELSCOPE_PUBLIC, $width / $numCells) .AddProperty("cellHeight", $ELSCOPE_PUBLIC, $height) .AddProperty("lines", $ELSCOPE_PUBLIC, $aArray) .AddMethod("AddLine", "_AddLine") .AddMethod("Draw", "_Draw") .AddMethod("DisplayNumber", "_DisplayNumber") .AddMethod("DisplayFullTime", "_DisplayFullTime") .AddMethod("DisplaySeparator", "_DisplaySeparator") .AddMethod("Resize", "_Resize") .AddDestructor("_GridPaneDestroy") EndWith Return $cGridPane.Object EndFunc ;==>GridPane Func _AddLine($this, $line) Local $aArray = $this.lines _ArrayAdd($aArray, $line) $this.lines = $aArray EndFunc ;==>_AddLine Func _Draw($this) _GDIPlus_GraphicsClear($this.hMemGraphics, 0xFF000000) Local $aArray = $this.lines For $line In $aArray Local $hPen = _GDIPlus_PenCreate($line.color, $line.thickness) _GDIPlus_GraphicsDrawLine($this.hMemGraphics, $line.x1, $line.y1, $line.x2, $line.y2, $hPen) _GDIPlus_PenDispose($hPen) Next _WinAPI_BitBlt($this.hDC, 0, 0, _WinAPI_GetClientWidth($this.hGUI), _WinAPI_GetClientHeight($this.hGUI), $this.hMemDC, 0, 0, $SRCCOPY) EndFunc ;==>_Draw Func _GridPaneDestroy($this) _GDIPlus_GraphicsDispose($this.hMemGraphics) _WinAPI_SelectObject($this.hMemDC, $this.hOldBmp) _WinAPI_DeleteObject($this.hBitmap) _WinAPI_DeleteDC($this.hMemDC) _GDIPlus_GraphicsDispose($this.hGraphics) _WinAPI_ReleaseDC($this.hGUI, $this.hDC) EndFunc ;==>_GridPaneDestroy Func _DisplayNumber($this, $number, $cellIndex) ; Define segments for each digit (0 to 9) Local $segments[10][7] = [ _ [1, 1, 1, 1, 1, 1, 0], _ ; 0 [0, 1, 1, 0, 0, 0, 0], _ ; 1 [1, 1, 0, 1, 1, 0, 1], _ ; 2 [1, 1, 1, 1, 0, 0, 1], _ ; 3 [0, 1, 1, 0, 0, 1, 1], _ ; 4 [1, 0, 1, 1, 0, 1, 1], _ ; 5 [1, 0, 1, 1, 1, 1, 1], _ ; 6 [1, 1, 1, 0, 0, 0, 0], _ ; 7 [1, 1, 1, 1, 1, 1, 1], _ ; 8 [1, 1, 1, 1, 0, 1, 1] _ ; 9 ] ; Coordinates for the segments (x1, y1, x2, y2) relative to cell size Local $coords[7][4] = [ _ [0.1, 0.1, 0.9, 0.1], _ ; A (top) [0.9, 0.1, 0.9, 0.5], _ ; B (top right) [0.9, 0.5, 0.9, 0.9], _ ; C (bottom right) [0.1, 0.9, 0.9, 0.9], _ ; D (bottom) [0.1, 0.5, 0.1, 0.9], _ ; E (bottom left) [0.1, 0.1, 0.1, 0.5], _ ; F (top left) [0.1, 0.5, 0.9, 0.5] _ ; G (middle) ] ; Calculate offsets Local $xOffset = $cellIndex * $this.cellWidth Local $yOffset = 0 ; Add lines for the given digit with offsets For $i = 0 To 6 If $segments[$number][$i] Then $this.AddLine(Line($coords[$i][0] * $this.cellWidth + $xOffset, $coords[$i][1] * $this.cellHeight + $yOffset, $coords[$i][2] * $this.cellWidth + $xOffset, $coords[$i][3] * $this.cellHeight + $yOffset, $COLOR_GREEN, 2)) EndIf Next EndFunc ;==>_DisplayNumber Func _DisplaySeparator($this, $cellIndex) Local $xOffset = $cellIndex * $this.cellWidth Local $yOffset = 0 ; Draw two small dots to form the ":" $this.AddLine(Line($xOffset + 0.5 * $this.cellWidth, $yOffset + 0.3 * $this.cellHeight, $xOffset + 0.5 * $this.cellWidth, $yOffset + 0.4 * $this.cellHeight, $COLOR_RED, 4)) $this.AddLine(Line($xOffset + 0.5 * $this.cellWidth, $yOffset + 0.6 * $this.cellHeight, $xOffset + 0.5 * $this.cellWidth, $yOffset + 0.7 * $this.cellHeight, $COLOR_RED, 4)) EndFunc ;==>_DisplaySeparator Func _DisplayFullTime($this) ; Clear current lines Local $aArray = $this.lines ReDim $aArray[0] $this.lines = $aArray ; Get the current time Local $hours = @HOUR Local $minutes = @MIN Local $seconds = @SEC ; Display each part of the time in separate cells $this.DisplayNumber(Int(StringMid($hours, 1, 1)), 0) ; First digit of hours $this.DisplayNumber(Int(StringMid($hours, 2, 1)), 1) ; Second digit of hours ; Add first separator $this.DisplaySeparator(2) ; First separator $this.DisplayNumber(Int(StringMid($minutes, 1, 1)), 3) ; First digit of minutes $this.DisplayNumber(Int(StringMid($minutes, 2, 1)), 4) ; Second digit of minutes ; Add second separator $this.DisplaySeparator(5) ; Second separator $this.DisplayNumber(Int(StringMid($seconds, 1, 1)), 6) ; First digit of seconds $this.DisplayNumber(Int(StringMid($seconds, 2, 1)), 7) ; Second digit of seconds $this.Draw() EndFunc ;==>_DisplayFullTime Func _Resize($this) Local $width = _WinAPI_GetClientWidth($this.hGUI) Local $height = _WinAPI_GetClientHeight($this.hGUI) $this.cellWidth = $width / $this.numCells $this.cellHeight = $height ; Update memory bitmap size _GDIPlus_GraphicsDispose($this.hMemGraphics) _WinAPI_SelectObject($this.hMemDC, $this.hOldBmp) _WinAPI_DeleteObject($this.hBitmap) $this.hBitmap = _WinAPI_CreateCompatibleBitmap($this.hDC, $width, $height) $this.hOldBmp = _WinAPI_SelectObject($this.hMemDC, $this.hBitmap) $this.hMemGraphics = _GDIPlus_GraphicsCreateFromHDC($this.hMemDC) $this.DisplayFullTime() EndFunc ;==>_Resize Func _Exit() $g___bEXIT = True EndFunc ;==>_Exit ; Example usage Global $gridPane = GridPane(8, "Horloge", 480, 130) AdlibRegister("displayTime", 1000) While 1 If $g___bEXIT Then ExitLoop Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $GUI_EVENT_RESIZED $gridPane.Resize() EndSwitch WEnd $gridPane = 0 _AutoItObject_Shutdown() _GDIPlus_Shutdown() Func displayTime() $gridPane.DisplayFullTime() EndFunc ;==>displayTime Edited May 31 by Numeric1 Implementation of double buffering to prevent flickering during clock updates. Gianni and UEZ 2
UEZ Posted May 31 Posted May 31 Good work but it flickers on refresh. I would suggest to use double buffering to avoid flashing. 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
Numeric1 Posted May 31 Author Posted May 31 1 hour ago, UEZ said: Good work but it flickers on refresh. I would suggest to use double buffering to avoid flashing. Thank you @UEZ for the pertinent suggestion. I have edited and revised the code by applying double buffering, and everything works correctly. UEZ 1
AndyG Posted June 1 Posted June 1 Hi! if it has to look more like a "real" 7-segment ...how about this? (I wrote it many years ago....@UEZ, do you remember^^) expandcollapse popup#include <GDIPlus.au3> #include <WindowsConstants.au3> #include <GUIConstantsEx.au3> Dim $ws[12] = [0, 0, 10, -7, 50, -7, 60, 0, 50, 7, 10, 7] ;Koordinaten Polygonzug: von links nach rechts WAAGRECHTER Balken, von rechts nach links SENKRECHTER Balken Dim $ziffer[10] = [239, 10, 118, 94, 154, 220, 253, 14, 254, 222] ;alle gesetzten bits des Indexes in der 7-segmentanzeige ergeben die Ziffer Dim $balkenpos[8] = [0, "60.0", "0.0", "60.60", "0.60", "0.60", "0.120", "0.0"] ;position der Leuchtbalken der 7-Segmentanzeige innerhalb der Ziffer Dim $p[7][2] ;nimmt die polygonzug-koordinaten zum Zeichnen auf $p[0][0] = 6 ;6 Punkte im Polygonzug _GDIPlus_Startup() Global $hgui = GUICreate('Uhr', 620, 175, -1, -1, $WS_POPUP) ;GUI erstellen ohne Rahmen Global $hWnd = WinGetHandle($hgui) ;Handle holen Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd) ;"Leinwand" erstellen, auf der gezeichnet werden kann GUICtrlCreateLabel("", 0, 0, 620, 175, Default, $GUI_WS_EX_PARENTDRAG) ;verschieben des Fensters möglich machen durch 2. Fenster GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT) ;das 2. Fenster transparent machen GUISetBkColor(0x000000) ;Hintergrund der GUI schwarz GUISetState() ;GUI anzeigen AdlibRegister("_time", 1000) ;jede Zehntelsekunde ein Refresh der Zeitanzeige Do ;Endlosschleife, solange bis.. Until GUIGetMsg() = -3 ;..ESC gedrückt wird _GDIPlus_GraphicsDispose($hGraphic) ;freigeben _GDIPlus_Shutdown() Func _time() ;Uhrzeit anzeigen :o) For $k = 1 To 6 ;die 6 Ziffern der Uhrzeit $setbits = $ziffer[StringMid(String(@HOUR & @MIN & @SEC), $k, 1)] ;gesetzte Bits in der siebensegmentanzeige anhand der Ziffer in der Uhrzeit holen For $bitnr = 7 To 1 Step -1 ;alle Bits durchlaufen _drawpolygon(BitAND($setbits, 128), $k * 100 - 80 + ($k = 1 Or $k = 3 Or $k = 5) * 20, $bitnr) ;parameter: bit gesetzt ja/nein, position der gesamten ziffer,nummer des bits(gerade=waagrechter balken, ungerade=senkrechter balken) $setbits = BitShift($setbits, -1) ;nächstes Bit holen Next Next $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + (@SEC / 2 = Int(@SEC / 2)) * 0xBB0000) ;Pinsel erstellen, wenn Ziffer gerade, dann farbig, ansonsten schwarz _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 55, 15, 15, $brush) ;Punkte zeichnen _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 55, 15, 15, $brush) _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 105, 15, 15, $brush) _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 105, 15, 15, $brush) _GDIPlus_BrushDispose($brush) ;Pinsel auflösen EndFunc ;==>_time Func _drawpolygon($bit, $xpos, $bitnr) ;zeichnet einen polygonzug ("Balken") an die entsprechende Position $split = StringSplit($balkenpos[$bitnr], ".", 3) ;x- und y-koordinaten des Balkens innerhalb der Ziffer holen $b = (($bitnr / 2) = Int($bitnr / 2)) ;$bit gerade => $b = true => Balken waagrecht, ansonsten Balken senkrecht $step = -1 + 2 * $b ;schrittweite durch das $WS-Array For $i = 11 - 11 * $b To 11 * $b Step 2 * $step ;array mit waagrechten (bit=gerade) oder (Bit=ungerade) senkrechten Balken füllen $r = Int(Abs((11 * (Not ($b))) - $i) / 2) + 1 ;abhängig von der Reihenfolge, egal ob $i von 0 bis 11 oder von 11 bis 0, $r muss immer 1,2,3,4,5,6 $p[$r][0] = $ws[$i] + $split[0] + $xpos ;x- und $p[$r][1] = $ws[$i + $step] + $split[1] + 30 ;y-position in das polygonarray schreiben Next $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + ($bit <> 0) * 0xBB0000) ;wenn bit gesetzt, dann farbig, ansonsten schwarz _GDIPlus_GraphicsFillPolygon($hGraphic, $p, $brush) ;Balken zeichnen _GDIPlus_BrushDispose($brush) EndFunc ;==>_drawpolygon If you want to know how it works....read the code!
RTFC Posted June 1 Posted June 1 And if we're going to do a proper comparison then one should include the efforts of @timmy2 + @UEZ with enhancements by @Beege, @MrCreatoR, and @Malkey (cobbled together by yours truly). My Contributions and Wrappers Spoiler BitMaskSudokuSolver BuildPartitionTable CodeCrypter CodeScanner DigitalDisplay Eigen4AutoIt FAT Suite HighMem MetaCodeFileLibrary OSgrid Pool RdRand SecondDesktop SimulatedAnnealing Xbase I/O
Numeric1 Posted June 1 Author Posted June 1 I highlighted this in my first message. However, I am rather referring to the rigidity of your codes. My clock, for example, allows for: a change of color, the resizing of the graphical interface, as well as the display of only the hour, the hour and minutes, or the hour, minutes, and seconds. And all of this without having to modify the code. Here, I am expressing the inherent flexibility of object-oriented programming. ; Example usage of the GridPane function ; GridPane($numCells, $title, $width, $height) ; You can easily omit the seconds ; By setting the parameter $numCells to 5, the seconds are omitted. Global $gridPane = GridPane(5, "Clock", 480, 130) ; You can display only the hours if you want ; By setting the parameter $numCells to 2, only the hours are displayed. Global $gridPane = GridPane(2, "Clock", 480, 130)
argumentum Posted June 1 Posted June 1 1 hour ago, Numeric1 said: I am rather referring to the rigidity of your codes. My clock, .... I see you making a valiant push for more OO code. Wouldn't it be beautiful if the whole of AutoIt was written as OO ? In any case, as handy as the AutoItObject is, if I can code something in Vanilla AutoIt, I'll take that route. And thanks for all the OO examples you are posting. They are simple to understand and take OO for a spin Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Numeric1 Posted June 1 Author Posted June 1 1 hour ago, argumentum said: I see you making a valiant push for more OO code. Wouldn't it be beautiful if the whole of AutoIt was written as OO ? What would be interesting is to integrate more object-oriented programming into AutoIt. I don't want all of AutoIt to be object-oriented, but we could add some. This mix would enhance AutoIt's conceptual capabilities. It's true that a train cannot run off the tracks, but it can go faster and further. argumentum 1
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now