11from abc import ABC , abstractmethod
22from enum import IntEnum
3+ from PIL import Image , ImageDraw , ImageFont
34
45from library import config
56
@@ -43,6 +44,10 @@ def get_height() -> int:
4344
4445
4546class LcdComm (ABC ):
47+ @abstractmethod
48+ def InitializeComm (self ):
49+ pass
50+
4651 @abstractmethod
4752 def Reset (self ):
4853 pass
@@ -64,33 +69,123 @@ def SetBrightness(self, level: int):
6469 pass
6570
6671 @abstractmethod
67- def SetOrientation (self , orientation : Orientation ):
72+ def SetBackplateLedColor (self , led_color : tuple [ int , int , int ] ):
6873 pass
6974
7075 @abstractmethod
71- def DisplayBitmap (self , bitmap_path : str , x : int , y : int , width : int , height : int ):
76+ def SetOrientation (self , orientation : Orientation ):
7277 pass
7378
7479 @abstractmethod
75- def DisplayText (
80+ def DisplayPILImage (
7681 self ,
77- text : str ,
78- x : int ,
79- y : int ,
80- font : str ,
81- font_size : int ,
82- font_color : tuple [int , int , int ],
83- background_color : tuple [int , int , int ],
84- background_image : str
82+ image : Image ,
83+ x : int = 0 , y : int = 0 ,
84+ image_width : int = 0 ,
85+ image_height : int = 0
8586 ):
8687 pass
8788
88- @abstractmethod
89- def DisplayProgressBar (self , x : int , y : int , width : int , height : int , min_value : int ,
90- max_value : int ,
91- value : int ,
92- bar_color : tuple [int , int , int ],
93- bar_outline : bool ,
94- background_color : tuple [int , int , int ],
95- background_image : str ):
96- pass
89+ def DisplayBitmap (self , bitmap_path : str , x : int = 0 , y : int = 0 , width : int = 0 , height : int = 0 ):
90+ image = Image .open (bitmap_path )
91+ self .DisplayPILImage (image , x , y , width , height )
92+
93+ def DisplayText (
94+ self ,
95+ text : str ,
96+ x : int = 0 ,
97+ y : int = 0 ,
98+ font : str = "roboto-mono/RobotoMono-Regular.ttf" ,
99+ font_size : int = 20 ,
100+ font_color : tuple [int , int , int ] = (0 , 0 , 0 ),
101+ background_color : tuple [int , int , int ] = (255 , 255 , 255 ),
102+ background_image : str = None
103+ ):
104+ # Convert text to bitmap using PIL and display it
105+ # Provide the background image path to display text with transparent background
106+
107+ if isinstance (font_color , str ):
108+ font_color = tuple (map (int , font_color .split (', ' )))
109+
110+ if isinstance (background_color , str ):
111+ background_color = tuple (map (int , background_color .split (', ' )))
112+
113+ assert x <= get_width (), 'Text X coordinate must be <= display width'
114+ assert y <= get_height (), 'Text Y coordinate must be <= display height'
115+ assert len (text ) > 0 , 'Text must not be empty'
116+ assert font_size > 0 , "Font size must be > 0"
117+
118+ if background_image is None :
119+ # A text bitmap is created with max width/height by default : text with solid background
120+ text_image = Image .new (
121+ 'RGB' ,
122+ (get_width (), get_height ()),
123+ background_color
124+ )
125+ else :
126+ # The text bitmap is created from provided background image : text with transparent background
127+ text_image = Image .open (background_image )
128+
129+ # Draw text with specified color & font
130+ font = ImageFont .truetype ("./res/fonts/" + font , font_size )
131+ d = ImageDraw .Draw (text_image )
132+ d .text ((x , y ), text , font = font , fill = font_color )
133+
134+ # Crop text bitmap to keep only the text (also crop if text overflows display)
135+ left , top , text_width , text_height = d .textbbox ((0 , 0 ), text , font = font )
136+ text_image = text_image .crop (box = (
137+ x , y ,
138+ min (x + text_width , get_width ()),
139+ min (y + text_height , get_height ())
140+ ))
141+
142+ self .DisplayPILImage (text_image , x , y )
143+
144+ def DisplayProgressBar (self , x : int , y : int , width : int , height : int , min_value : int = 0 , max_value : int = 100 ,
145+ value : int = 50 ,
146+ bar_color : tuple [int , int , int ] = (0 , 0 , 0 ),
147+ bar_outline : bool = True ,
148+ background_color : tuple [int , int , int ] = (255 , 255 , 255 ),
149+ background_image : str = None ):
150+ # Generate a progress bar and display it
151+ # Provide the background image path to display progress bar with transparent background
152+
153+ if isinstance (bar_color , str ):
154+ bar_color = tuple (map (int , bar_color .split (', ' )))
155+
156+ if isinstance (background_color , str ):
157+ background_color = tuple (map (int , background_color .split (', ' )))
158+
159+ assert x <= get_width (), 'Progress bar X coordinate must be <= display width'
160+ assert y <= get_height (), 'Progress bar Y coordinate must be <= display height'
161+ assert x + width <= get_width (), 'Progress bar width exceeds display width'
162+ assert y + height <= get_height (), 'Progress bar height exceeds display height'
163+
164+ # Don't let the set value exceed our min or max value, this is bad :)
165+ if value < min_value :
166+ value = min_value
167+ elif max_value < value :
168+ value = max_value
169+
170+ assert min_value <= value <= max_value , 'Progress bar value shall be between min and max'
171+
172+ if background_image is None :
173+ # A bitmap is created with solid background
174+ bar_image = Image .new ('RGB' , (width , height ), background_color )
175+ else :
176+ # A bitmap is created from provided background image
177+ bar_image = Image .open (background_image )
178+
179+ # Crop bitmap to keep only the progress bar background
180+ bar_image = bar_image .crop (box = (x , y , x + width , y + height ))
181+
182+ # Draw progress bar
183+ bar_filled_width = value / (max_value - min_value ) * width
184+ draw = ImageDraw .Draw (bar_image )
185+ draw .rectangle ([0 , 0 , bar_filled_width - 1 , height - 1 ], fill = bar_color , outline = bar_color )
186+
187+ if bar_outline :
188+ # Draw outline
189+ draw .rectangle ([0 , 0 , width - 1 , height - 1 ], fill = None , outline = bar_color )
190+
191+ self .DisplayPILImage (bar_image , x , y )
0 commit comments