Allow usage without display and SPI when setting render->False
Generated images will be available in the images folder
This commit is contained in:
		| @@ -10,210 +10,210 @@ from PIL import Image | |||||||
| from inkycal.custom import top_level | from inkycal.custom import top_level | ||||||
| import glob | import glob | ||||||
|  |  | ||||||
|  |  | ||||||
| class Display: | class Display: | ||||||
|   """Display class for inkycal |     """Display class for inkycal | ||||||
|  |  | ||||||
|   Creates an instance of the driver for the selected E-Paper model and allows |     Creates an instance of the driver for the selected E-Paper model and allows | ||||||
|   rendering images and calibrating the E-Paper display |     rendering images and calibrating the E-Paper display | ||||||
|  |  | ||||||
|   args: |     args: | ||||||
|     - epaper_model: The name of your E-Paper model. |       - epaper_model: The name of your E-Paper model. | ||||||
|  |  | ||||||
|  |  | ||||||
|   """ |  | ||||||
|  |  | ||||||
|   def __init__(self, epaper_model): |  | ||||||
|     """Load the drivers for this epaper model""" |  | ||||||
|  |  | ||||||
|     if 'colour' in epaper_model: |  | ||||||
|       self.supports_colour = True |  | ||||||
|     else: |  | ||||||
|       self.supports_colour = False |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|       driver_path = f'inkycal.display.drivers.{epaper_model}' |  | ||||||
|       driver = import_module(driver_path) |  | ||||||
|       self._epaper = driver.EPD() |  | ||||||
|       self.model_name = epaper_model |  | ||||||
|  |  | ||||||
|     except ImportError: |  | ||||||
|       raise Exception('This module is not supported. Check your spellings?') |  | ||||||
|  |  | ||||||
|     except FileNotFoundError: |  | ||||||
|       raise Exception('SPI could not be found. Please check if SPI is enabled') |  | ||||||
|  |  | ||||||
|   def render(self, im_black, im_colour = None): |  | ||||||
|     """Renders an image on the selected E-Paper display. |  | ||||||
|  |  | ||||||
|     Initlializes the E-Paper display, sends image data and executes command |  | ||||||
|     to update the display. |  | ||||||
|  |  | ||||||
|     Args: |  | ||||||
|       - im_black: The image for the black-pixels. Anything in this image that is |  | ||||||
|         black is rendered as black on the display. This is required and ideally |  | ||||||
|         should be a black-white image. |  | ||||||
|  |  | ||||||
|       - im_colour: For E-Paper displays supporting colour, a separate image, |  | ||||||
|         ideally black-white is required for the coloured pixels. Anything that is |  | ||||||
|         black in this image will show up as either red/yellow. |  | ||||||
|  |  | ||||||
|     Rendering an image for black-white E-Paper displays: |  | ||||||
|  |  | ||||||
|     >>> sample_image = PIL.Image.open('path/to/file.png') |  | ||||||
|     >>> display = Display('my_black_white_display') |  | ||||||
|     >>> display.render(sample_image) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     Rendering black-white on coloured E-Paper displays: |  | ||||||
|  |  | ||||||
|     >>> sample_image = PIL.Image.open('path/to/file.png') |  | ||||||
|     >>> display = Display('my_coloured_display') |  | ||||||
|     >>> display.render(sample_image, sample_image) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     Rendering coloured image where 2 images are available: |  | ||||||
|  |  | ||||||
|     >>> black_image = PIL.Image.open('path/to/file.png') # black pixels |  | ||||||
|     >>> colour_image = PIL.Image.open('path/to/file.png') # coloured pixels |  | ||||||
|     >>> display = Display('my_coloured_display') |  | ||||||
|     >>> display.render(black_image, colour_image) |  | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     epaper = self._epaper |     def __init__(self, epaper_model): | ||||||
|  |         """Load the drivers for this epaper model""" | ||||||
|  |  | ||||||
|     if self.supports_colour == False: |         if 'colour' in epaper_model: | ||||||
|       print('Initialising..', end = '') |             self.supports_colour = True | ||||||
|       epaper.init() |         else: | ||||||
|       print('Updating display......', end = '') |             self.supports_colour = False | ||||||
|       epaper.display(epaper.getbuffer(im_black)) |  | ||||||
|       print('Done') |  | ||||||
|  |  | ||||||
|     elif self.supports_colour == True: |         try: | ||||||
|       if not im_colour: |             driver_path = f'inkycal.display.drivers.{epaper_model}' | ||||||
|         raise Exception('im_colour is required for coloured epaper displays') |             driver = import_module(driver_path) | ||||||
|       print('Initialising..', end = '') |             self._epaper = driver.EPD() | ||||||
|       epaper.init() |             self.model_name = epaper_model | ||||||
|       print('Updating display......', end = '') |  | ||||||
|       epaper.display(epaper.getbuffer(im_black), epaper.getbuffer(im_colour)) |  | ||||||
|       print('Done') |  | ||||||
|  |  | ||||||
|     print('Sending E-Paper to deep sleep...', end = '') |         except ImportError: | ||||||
|     epaper.sleep() |             raise Exception('This module is not supported. Check your spellings?') | ||||||
|     print('Done') |  | ||||||
|  |  | ||||||
|   def calibrate(self, cycles=3): |         except FileNotFoundError: | ||||||
|     """Calibrates the display to retain crisp colours |             raise Exception('SPI could not be found. Please check if SPI is enabled') | ||||||
|  |  | ||||||
|     Flushes the selected display several times with it's supported colours, |     def render(self, im_black, im_colour=None): | ||||||
|     removing any previous effects of ghosting. |         """Renders an image on the selected E-Paper display. | ||||||
|  |  | ||||||
|     Args: |         Initlializes the E-Paper display, sends image data and executes command | ||||||
|       - cycles: -> int. The number of times to flush the display with it's |         to update the display. | ||||||
|         supported colours. |  | ||||||
|  |  | ||||||
|     It's recommended to calibrate the display after every 6 display updates |         Args: | ||||||
|     for best results. For black-white only displays, calibration is less |           - im_black: The image for the black-pixels. Anything in this image that is | ||||||
|     critical, but not calibrating regularly results in grey-ish text. |             black is rendered as black on the display. This is required and ideally | ||||||
|  |             should be a black-white image. | ||||||
|  |  | ||||||
|     Please note that calibration takes a while to complete. 3 cycles may |           - im_colour: For E-Paper displays supporting colour, a separate image, | ||||||
|     take 10 mins on black-white E-Papers while it takes 20 minutes on coloured |             ideally black-white is required for the coloured pixels. Anything that is | ||||||
|     E-Paper displays. |             black in this image will show up as either red/yellow. | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     epaper = self._epaper |         Rendering an image for black-white E-Paper displays: | ||||||
|     epaper.init() |  | ||||||
|  |  | ||||||
|     display_size = self.get_display_size(self.model_name) |         >>> sample_image = PIL.Image.open('path/to/file.png') | ||||||
|  |         >>> display = Display('my_black_white_display') | ||||||
|     white = Image.new('1', display_size, 'white') |         >>> display.render(sample_image) | ||||||
|     black = Image.new('1', display_size, 'black') |  | ||||||
|  |  | ||||||
|     print('----------Started calibration of ePaper display----------') |  | ||||||
|     if self.supports_colour == True: |  | ||||||
|       for _ in range(cycles): |  | ||||||
|         print('Calibrating...', end= ' ') |  | ||||||
|         print('black...', end= ' ') |  | ||||||
|         epaper.display(epaper.getbuffer(black), epaper.getbuffer(white)) |  | ||||||
|         print('colour...', end = ' ') |  | ||||||
|         epaper.display(epaper.getbuffer(white), epaper.getbuffer(black)) |  | ||||||
|         print('white...') |  | ||||||
|         epaper.display(epaper.getbuffer(white), epaper.getbuffer(white)) |  | ||||||
|         print(f'Cycle {_+1} of {cycles} complete') |  | ||||||
|  |  | ||||||
|     if self.supports_colour == False: |  | ||||||
|       for _ in range(cycles): |  | ||||||
|         print('Calibrating...', end= ' ') |  | ||||||
|         print('black...', end = ' ') |  | ||||||
|         epaper.display(epaper.getbuffer(black)) |  | ||||||
|         print('white...') |  | ||||||
|         epaper.display(epaper.getbuffer(white)), |  | ||||||
|         print(f'Cycle {_+1} of {cycles} complete') |  | ||||||
|  |  | ||||||
|       print('-----------Calibration complete----------') |  | ||||||
|       epaper.sleep() |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   @classmethod |         Rendering black-white on coloured E-Paper displays: | ||||||
|   def get_display_size(cls, model_name): |  | ||||||
|     """Returns the size of the display as a tuple -> (width, height) |  | ||||||
|  |  | ||||||
|     Looks inside drivers folder for the given model name, then returns it's |         >>> sample_image = PIL.Image.open('path/to/file.png') | ||||||
|     size. |         >>> display = Display('my_coloured_display') | ||||||
|  |         >>> display.render(sample_image, sample_image) | ||||||
|  |  | ||||||
|     Args: |  | ||||||
|       - model_name: str -> The name of the E-Paper display to get it's size. |  | ||||||
|  |  | ||||||
|     Returns: |         Rendering coloured image where 2 images are available: | ||||||
|       (width, height) ->tuple, showing the size of the display |  | ||||||
|  |  | ||||||
|     You can use this function directly without creating the Display class: |         >>> black_image = PIL.Image.open('path/to/file.png') # black pixels | ||||||
|  |         >>> colour_image = PIL.Image.open('path/to/file.png') # coloured pixels | ||||||
|  |         >>> display = Display('my_coloured_display') | ||||||
|  |         >>> display.render(black_image, colour_image) | ||||||
|  |         """ | ||||||
|  |  | ||||||
|     >>> Display.get_display_size('model_name') |         epaper = self._epaper | ||||||
|     """ |  | ||||||
|     if not isinstance(model_name, str): |  | ||||||
|       print('model_name should be a string') |  | ||||||
|       return |  | ||||||
|     else: |  | ||||||
|       driver_files = top_level+'/inkycal/display/drivers/*.py' |  | ||||||
|       drivers = glob.glob(driver_files) |  | ||||||
|       drivers = [i.split('/')[-1].split('.')[0] for i in drivers] |  | ||||||
|       drivers.remove('__init__') |  | ||||||
|       drivers.remove('epdconfig') |  | ||||||
|       if model_name not in drivers: |  | ||||||
|         print('This model name was not found. Please double check your spellings') |  | ||||||
|         return |  | ||||||
|       else: |  | ||||||
|         with open(top_level+'/inkycal/display/drivers/'+model_name+'.py') as file: |  | ||||||
|           for line in file: |  | ||||||
|             if 'EPD_WIDTH=' in line.replace(" ", ""): |  | ||||||
|               width = int(line.rstrip().replace(" ", "").split('=')[-1]) |  | ||||||
|             if 'EPD_HEIGHT=' in line.replace(" ", ""): |  | ||||||
|               height = int(line.rstrip().replace(" ", "").split('=')[-1]) |  | ||||||
|         return width, height |  | ||||||
|  |  | ||||||
|   @classmethod |         if not self.supports_colour: | ||||||
|   def get_display_names(cls): |             print('Initialising..', end='') | ||||||
|     """Prints all supported E-Paper models. |             epaper.init() | ||||||
|  |             print('Updating display......', end='') | ||||||
|  |             epaper.display(epaper.getbuffer(im_black)) | ||||||
|  |             print('Done') | ||||||
|  |  | ||||||
|     Fetches all filenames in driver folder and prints them on the console. |         elif self.supports_colour: | ||||||
|  |             if not im_colour: | ||||||
|  |                 raise Exception('im_colour is required for coloured epaper displays') | ||||||
|  |             print('Initialising..', end='') | ||||||
|  |             epaper.init() | ||||||
|  |             print('Updating display......', end='') | ||||||
|  |             epaper.display(epaper.getbuffer(im_black), epaper.getbuffer(im_colour)) | ||||||
|  |             print('Done') | ||||||
|  |  | ||||||
|     Returns: |         print('Sending E-Paper to deep sleep...', end='') | ||||||
|       Printed version of all supported Displays. |         epaper.sleep() | ||||||
|  |         print('Done') | ||||||
|  |  | ||||||
|     Use one of the models to intilialize the Display class in order to gain |     def calibrate(self, cycles=3): | ||||||
|     access to the E-Paper. |         """Calibrates the display to retain crisp colours | ||||||
|  |  | ||||||
|     You can use this function directly without creating the Display class: |         Flushes the selected display several times with it's supported colours, | ||||||
|  |         removing any previous effects of ghosting. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |           - cycles: -> int. The number of times to flush the display with it's | ||||||
|  |             supported colours. | ||||||
|  |  | ||||||
|  |         It's recommended to calibrate the display after every 6 display updates | ||||||
|  |         for best results. For black-white only displays, calibration is less | ||||||
|  |         critical, but not calibrating regularly results in grey-ish text. | ||||||
|  |  | ||||||
|  |         Please note that calibration takes a while to complete. 3 cycles may | ||||||
|  |         take 10 minutes on black-white E-Papers while it takes 20 minutes on coloured | ||||||
|  |         E-Paper displays. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         epaper = self._epaper | ||||||
|  |         epaper.init() | ||||||
|  |  | ||||||
|  |         display_size = self.get_display_size(self.model_name) | ||||||
|  |  | ||||||
|  |         white = Image.new('1', display_size, 'white') | ||||||
|  |         black = Image.new('1', display_size, 'black') | ||||||
|  |  | ||||||
|  |         print('----------Started calibration of ePaper display----------') | ||||||
|  |         if self.supports_colour: | ||||||
|  |             for _ in range(cycles): | ||||||
|  |                 print('Calibrating...', end=' ') | ||||||
|  |                 print('black...', end=' ') | ||||||
|  |                 epaper.display(epaper.getbuffer(black), epaper.getbuffer(white)) | ||||||
|  |                 print('colour...', end=' ') | ||||||
|  |                 epaper.display(epaper.getbuffer(white), epaper.getbuffer(black)) | ||||||
|  |                 print('white...') | ||||||
|  |                 epaper.display(epaper.getbuffer(white), epaper.getbuffer(white)) | ||||||
|  |                 print(f'Cycle {_ + 1} of {cycles} complete') | ||||||
|  |  | ||||||
|  |         if not self.supports_colour: | ||||||
|  |             for _ in range(cycles): | ||||||
|  |                 print('Calibrating...', end=' ') | ||||||
|  |                 print('black...', end=' ') | ||||||
|  |                 epaper.display(epaper.getbuffer(black)) | ||||||
|  |                 print('white...') | ||||||
|  |                 epaper.display(epaper.getbuffer(white)), | ||||||
|  |                 print(f'Cycle {_ + 1} of {cycles} complete') | ||||||
|  |  | ||||||
|  |             print('-----------Calibration complete----------') | ||||||
|  |             epaper.sleep() | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_display_size(cls, model_name): | ||||||
|  |         """Returns the size of the display as a tuple -> (width, height) | ||||||
|  |  | ||||||
|  |         Looks inside "drivers" folder for the given model name, then returns it's | ||||||
|  |         size. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |           - model_name: str -> The name of the E-Paper display to get it's size. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |           (width, height) ->tuple, showing the size of the display | ||||||
|  |  | ||||||
|  |         You can use this function directly without creating the Display class: | ||||||
|  |  | ||||||
|  |         >>> Display.get_display_size('model_name') | ||||||
|  |         """ | ||||||
|  |         if not isinstance(model_name, str): | ||||||
|  |             print('model_name should be a string') | ||||||
|  |             return | ||||||
|  |         else: | ||||||
|  |             driver_files = top_level + '/inkycal/display/drivers/*.py' | ||||||
|  |             drivers = glob.glob(driver_files) | ||||||
|  |             drivers = [i.split('/')[-1].split('.')[0] for i in drivers] | ||||||
|  |             drivers.remove('__init__') | ||||||
|  |             drivers.remove('epdconfig') | ||||||
|  |             if model_name not in drivers: | ||||||
|  |                 print('This model name was not found. Please double check your spellings') | ||||||
|  |                 return | ||||||
|  |             else: | ||||||
|  |                 with open(top_level + '/inkycal/display/drivers/' + model_name + '.py') as file: | ||||||
|  |                     for line in file: | ||||||
|  |                         if 'EPD_WIDTH=' in line.replace(" ", ""): | ||||||
|  |                             width = int(line.rstrip().replace(" ", "").split('=')[-1]) | ||||||
|  |                         if 'EPD_HEIGHT=' in line.replace(" ", ""): | ||||||
|  |                             height = int(line.rstrip().replace(" ", "").split('=')[-1]) | ||||||
|  |                 return width, height | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_display_names(cls): | ||||||
|  |         """Prints all supported E-Paper models. | ||||||
|  |  | ||||||
|  |         Fetches all filenames in driver folder and prints them on the console. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |           Printed version of all supported Displays. | ||||||
|  |  | ||||||
|  |         Use one of the models to intilialize the Display class in order to gain | ||||||
|  |         access to the E-Paper. | ||||||
|  |  | ||||||
|  |         You can use this function directly without creating the Display class: | ||||||
|  |  | ||||||
|  |         >>> Display.get_display_names() | ||||||
|  |         """ | ||||||
|  |         driver_files = top_level + '/inkycal/display/drivers/*.py' | ||||||
|  |         drivers = glob.glob(driver_files) | ||||||
|  |         drivers = [i.split('/')[-1].split('.')[0] for i in drivers] | ||||||
|  |         drivers.remove('__init__') | ||||||
|  |         drivers.remove('epdconfig') | ||||||
|  |         print(*drivers, sep='\n') | ||||||
|  |  | ||||||
|     >>> Display.get_display_names() |  | ||||||
|     """ |  | ||||||
|     driver_files = top_level+'/inkycal/display/drivers/*.py' |  | ||||||
|     drivers = glob.glob(driver_files) |  | ||||||
|     drivers = [i.split('/')[-1].split('.')[0] for i in drivers] |  | ||||||
|     drivers.remove('__init__') |  | ||||||
|     drivers.remove('epdconfig') |  | ||||||
|     print(*drivers, sep='\n') |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|   print("Running Display class in standalone mode") |     print("Running Display class in standalone mode") | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1066
									
								
								inkycal/main.py
									
									
									
									
									
								
							
							
						
						
									
										1066
									
								
								inkycal/main.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user