diff --git a/README.md b/README.md index a730a94b..789d2629 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,14 @@ Operating systems supported : macOS, Windows, Linux (incl. Raspberry Pi) and all This is a 3.5" USB-C display that shows as a serial port once connected. It cannot be seen by the operating system as a monitor but picture can be displayed on it. -A Windows-only software is [available here](https://translate.google.com/translate?sl=auto&u=https://gitee.com/emperg/usblcd/raw/master/dev0/realse.ini) to manage this display. +A Windows-only software is [available in Chinese](https://lgb123-1253504678.cos.ap-beijing.myqcloud.com/35inch.rar) or [in English](https://lgb123-1253504678.cos.ap-beijing.myqcloud.com/35inchENG.rar) to manage this display. This software allows creating themes to display your computer sensors on the screen, but does not offer a simple way to display custom pictures or text. This Python script has been created to do some simple operations on this display like : - **Display custom picture** - **Display text** +- **Display progress bar** - Clear the screen (blank) - Turn the screen on/off - Display soft reset - Set brightness - - diff --git a/main.py b/main.py index a6418a88..af05714c 100644 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ class Command: DISPLAY_BITMAP = 197 -def SendReg(ser, cmd, x, y, ex, ey): +def SendReg(ser: serial.Serial, cmd: int, x: int, y: int, ex: int, ey: int): byteBuffer = bytearray(6) byteBuffer[0] = (x >> 2) byteBuffer[1] = (((x & 3) << 6) + (y >> 4)) @@ -39,31 +39,35 @@ def SendReg(ser, cmd, x, y, ex, ey): ser.write(bytes(byteBuffer)) -def Reset(ser): +def Reset(ser: serial.Serial): SendReg(ser, Command.RESET, 0, 0, 0, 0) -def Clear(ser): +def Clear(ser: serial.Serial): SendReg(ser, Command.CLEAR, 0, 0, 0, 0) -def ScreenOff(ser): +def ScreenOff(ser: serial.Serial): SendReg(ser, Command.SCREEN_OFF, 0, 0, 0, 0) -def ScreenOn(ser): +def ScreenOn(ser: serial.Serial): SendReg(ser, Command.SCREEN_ON, 0, 0, 0, 0) -def SetBrightness(ser, level): +def SetBrightness(ser: serial.Serial, level: int): # Level : 0 (brightest) - 255 (darkest) + assert 255 >= level >= 0, 'Brightness level must be [0-255]' SendReg(ser, Command.SET_BRIGHTNESS, level, 0, 0, 0) -def DisplayPILImage(ser, image, x, y): +def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int): image_height = image.size[1] image_width = image.size[0] + assert image_height > 0, 'Image width must be > 0' + assert image_width > 0, 'Image height must be > 0' + SendReg(ser, Command.DISPLAY_BITMAP, x, y, x + image_width - 1, y + image_height - 1) pix = image.load() @@ -78,7 +82,7 @@ def DisplayPILImage(ser, image, x, y): line += struct.pack('H', rgb) # Send image data by multiple of DISPLAY_WIDTH bytes - if len(line) >= DISPLAY_WIDTH*4: + if len(line) >= DISPLAY_WIDTH * 8: ser.write(line) line = bytes() @@ -89,20 +93,23 @@ def DisplayPILImage(ser, image, x, y): sleep(0.01) # Wait 10 ms after picture display -def DisplayBitmap(ser, bitmap_path, x=0, y=0): +def DisplayBitmap(ser: serial.Serial, bitmap_path: str, x=0, y=0): image = Image.open(bitmap_path) DisplayPILImage(ser, image, x, y) -def DisplayText(ser, text, x=0, y=0, +def DisplayText(ser: serial.Serial, text: str, x=0, y=0, font="roboto/Roboto-Regular.ttf", font_size=20, font_color=(0, 0, 0), background_color=(255, 255, 255), - background_image=None): + background_image: str = None): # Convert text to bitmap using PIL and display it # Provide the background image path to display text with transparent background + assert len(text) > 0, 'Text must not be empty' + assert font_size > 0, "Font size must be > 0" + if background_image is None: # A text bitmap is created with max width/height by default : text with solid background text_image = Image.new('RGB', (DISPLAY_WIDTH, DISPLAY_HEIGHT), background_color) @@ -122,6 +129,41 @@ def DisplayText(ser, text, x=0, y=0, DisplayPILImage(ser, text_image, x, y) +def DisplayProgressBar(ser: serial.Serial, x: int, y: int, width: int, height: int, min_value=0, max_value=100, + value=50, + bar_color=(0, 0, 0), + bar_outline=True, + background_color=(255, 255, 255), + background_image: str = None): + # Generate a progress bar and display it + # Provide the background image path to display progress bar with transparent background + + assert x + width <= DISPLAY_WIDTH, 'Progress bar width exceeds display width' + assert y + height <= DISPLAY_HEIGHT, 'Progress bar height exceeds display height' + assert min_value <= value <= max_value, 'Progress bar value shall be between min and max' + + if background_image is None: + # A bitmap is created with solid background + bar_image = Image.new('RGB', (width, height), background_color) + else: + # A bitmap is created from provided background image + bar_image = Image.open(background_image) + + # Crop bitmap to keep only the progress bar background + bar_image = bar_image.crop(box=(x, y, x + width, y + height)) + + # Draw progress bar + bar_filled_width = value / (max_value - min_value) * width + draw = ImageDraw.Draw(bar_image) + draw.rectangle([0, 0, bar_filled_width-1, height-1], fill=bar_color, outline=bar_color) + + if bar_outline: + # Draw outline + draw.rectangle([0, 0, width-1, height-1], fill=None, outline=bar_color) + + DisplayPILImage(ser, bar_image, x, y) + + stop = False if __name__ == "__main__": @@ -130,6 +172,7 @@ def sighandler(signum, frame): global stop stop = True + # Set the signal handlers, to send a complete frame to the LCD before exit signal.signal(signal.SIGINT, sighandler) signal.signal(signal.SIGTERM, sighandler) @@ -173,7 +216,8 @@ def sighandler(signum, frame): font_color=(255, 255, 255), background_image="res/example.png") - # Display the current time as fast as possible + # Display the current time and some progress bars as fast as possible + bar_value = 0 while not stop: DisplayText(lcd_comm, str(datetime.now().time()), 160, 2, font="roboto/Roboto-Bold.ttf", @@ -181,4 +225,18 @@ def sighandler(signum, frame): font_color=(255, 0, 0), background_image="res/example.png") + DisplayProgressBar(lcd_comm, 10, 40, + width=140, height=30, + min_value=0, max_value=100, value=bar_value, + bar_color=(255, 255, 0), bar_outline=True, + background_image="res/example.png") + + DisplayProgressBar(lcd_comm, 160, 40, + width=140, height=30, + min_value=0, max_value=19, value=bar_value % 20, + bar_color=(0, 255, 0), bar_outline=False, + background_image="res/example.png") + + bar_value = (bar_value + 2) % 101 + lcd_comm.close()