def displayInky(output): from PIL import Image, ImageFont, ImageDraw from inky import InkyPHAT inky_display = InkyPHAT("red") inky_display.set_border(inky_display.RED) img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) headerSize = 12 headerFont = ImageFont.truetype( "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf", headerSize) messageFont = ImageFont.truetype( "/usr/share/fonts/truetype/liberation2/LiberationMono-Regular.ttf", int(75 / (output['bodyLines']))) x = 0 y = 0 draw.text((x, y), output['header'] + ' - ' + output['subHeader'], inky_display.RED, headerFont) draw.text((x, y + headerSize), output['body'], inky_display.BLACK, messageFont) inky_display.set_image(img) inky_display.show()
def updateeink(issdetails, mapdot, trail): """update location text and map to the e-ink screen""" issx = int(mapdot[0]) issy = int(mapdot[1]) inky_display = InkyPHAT("yellow") palette = [255, 255, 255, 0, 0, 0, 255, 0, 0] font10 = ImageFont.truetype('/home/pi/isstracker/FreeSans.ttf', 10) font14 = ImageFont.truetype('/home/pi/isstracker/FreeSans.ttf', 14) font16 = ImageFont.truetype('/home/pi/isstracker/FreeSans.ttf', 16) font18 = ImageFont.truetype('/home/pi/isstracker/FreeSans.ttf', 18) im = Image.open('/home/pi/isstracker/small-world-map.png') d = ImageDraw.ImageDraw(im) taille_pt = 3 isspos = (int(mapdot[0]) - taille_pt, int(mapdot[1]) - taille_pt, int(mapdot[0]) + taille_pt, int(mapdot[1]) + taille_pt) d.ellipse(isspos, fill=2) for item in point2ellipse(trail, 1): d.ellipse(item, fill=2) d.text((3, 80), 'à ' + str(round(issdetails[2])) + ' Km de ' + issdetails[1] + ' (' + issdetails[0] + ')', font=font18, fill=2) from uptime import uptime uptime = round(uptime() / 60, 1) d.text((0, 70), "ut : " + str(uptime) + " min", font=font10, fill=1) inky_display.set_image(im) inky_display.show()
class InkyDriver: def __init__(self): self.inky = InkyPHAT('yellow') def create_new_image(self, countries_cases): current_height = 0 # inky_display.set_rotation(180) self.inky.set_border(self.inky.RED) # Create a new canvas to draw on img = Image.new("P", (self.inky.WIDTH, self.inky.HEIGHT)) draw = ImageDraw.Draw(img) # Load the fonts font = ImageFont.truetype(HankenGroteskBold, 20) # Calculate the positioning and draw the text for countryCases in countries_cases: text = str(countryCases[0]) + ": " + str(countryCases[1]) width, height = font.getsize(text) center = int((self.inky.WIDTH - width) / 2) draw.text((center, current_height), text, self.inky.BLACK, font=font) current_height += height # Display the completed picture self.inky.set_image(img) self.inky.show()
def hype(word): print(word) # Set up the correct display and scaling factors inky_display = InkyPHAT('black') inky_display.set_border(inky_display.BLACK) # inky_display.set_rotation(180) w = inky_display.WIDTH h = inky_display.HEIGHT # Create a new canvas to draw on img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) # Load the fonts font_size = 88 from fonts.ttf import AmaticSC, FredokaOne #font = ImageFont.truetype(SourceSansProSemibold, font_size) font = ImageFont.truetype(AmaticSC, font_size) #font = ImageFont.truetype(FredokaOne, font_size) padding = 20 max_width = w - padding max_height = h - padding below_max_length = False while not below_max_length: p_w, p_h = font.getsize(word) # Width and height of quote #p_h = p_h * (word.count("\n") + 1) # Multiply through by number of lines if p_h < max_height and p_w < max_width: below_max_length = True # The quote fits! Break out of the loop. else: font_size = font_size - 2 #font = ImageFont.truetype(SourceSansProSemibold, font_size) font = ImageFont.truetype(AmaticSC, font_size) #font = ImageFont.truetype(FredokaOne, font_size) continue # x- and y-coordinates for the top left of the quote #word_x = (w - max_width) / 2 #word_x = (max_width - p_w) / 2 #word_y = (max_height - p_h) / 2 word_x = (w - max_width) / 2 #word_y = ((h - max_height) + (max_height - p_h - font.getsize("ABCD ")[1])) / 2 word_y = (h - p_h) / 2 draw.multiline_text((word_x, word_y), word, fill=inky_display.BLACK, font=font, align="left") draw.line((169, 58, 169, 58), 2) # Display the completed canvas on Inky wHAT inky_display.set_image(img) inky_display.show()
def draw_to_display(): # Set up properties of eInk display inky_display = InkyPHAT("red") inky_display.set_border(inky_display.BLACK) # Load previously generated image img = draw_semester_display() # Display generated semester progress image inky_display.set_image(img) inky_display.show()
def run(): inky_display = InkyPHAT(COLOR) # inky_display.set_rotation(180) inky_display.set_border(InkyPHAT.RED) img = Image.new("P", (InkyPHAT.WIDTH, InkyPHAT.HEIGHT)) palletize(img) image_draw = ImageDraw.Draw(img) if DEBUG: # Draw vertical line for y in xrange(InkyPHAT.HEIGHT): img.putpixel((InkyPHAT.WIDTH / 2, y), inky_display.RED) img.putpixel((InkyPHAT.WIDTH / 4, y), inky_display.RED) # Draw horizontal line for x in xrange(InkyPHAT.WIDTH): img.putpixel((x, InkyPHAT.HEIGHT / 2), inky_display.RED) weather = Weather(location_coords) if weather.is_same_as_temp_data(): if DEBUG: print("Not updating the display since the forecast is the same as last time") weather.save_temp_forecast(only_if_no_such_file=True) return else: if DEBUG: print("Updating display since the forecast has changed since the last render") weather.save_temp_forecast() draw_text(image_draw, 2, "UV", "l") draw_text(image_draw, 2, weather.uv_index, "r", color=InkyPHAT.RED if weather.is_uv_warning() else InkyPHAT.BLACK) draw_text(image_draw, 1, get_high_temp_copy(weather), "c") draw_text(image_draw, 4, get_low_temp_copy(weather), "c") if weather.precipitation_is_likely(): precip_chance_str = str(int(round(weather.current_precip_probability * 100))) + "%" draw_text(image_draw, 3, get_sky_icon(weather), "l", is_icon=True) draw_text(image_draw, 3, precip_chance_str, "r", is_icon=False) else: draw_text(image_draw, 3, get_sky_icon(weather), "c", is_icon=True) inky_display.set_image(img) inky_display.show() if DEBUG: print(weather.eink_data_string())
def draw_display_message(text: str): # Set up properties of eInk display inky_display = InkyPHAT("red") inky_display.set_border(inky_display.BLACK) hanked_medium = ImageFont.truetype(HankenGroteskMedium, 20) img = Image.new("P", size=(InkyPHAT.WIDTH, InkyPHAT.HEIGHT)) draw = ImageDraw.Draw(img) text_w, text_h = hanked_medium.getsize(text) text_x = (InkyPHAT.WIDTH - text_w) // 2 text_y = (InkyPHAT.HEIGHT - text_h) // 2 draw.text((text_x, text_y), text, InkyPHAT.BLACK, font=hanked_medium) inky_display.set_image(img) inky_display.show()
def refreshDisplay(intervall, priority, scheduler): logging.info("Refresh Display - start") lastValue = readLastValue() avgMonth = readAvgFromDays(31) avgYear = readAvgFromDays(365) inkyDisplay = InkyPHAT(colour) inkyDisplay.set_border(inkyDisplay.BLACK) # Load our background image img = Image.open("/var/local/cellarsense/cellarsense-background.png") draw = ImageDraw.Draw(img) mainHeaderFont = ImageFont.truetype("FreeMonoBold.ttf", 20) mainFont = ImageFont.truetype("FreeMono.ttf", 20) medianFont = ImageFont.truetype("FreeMono.ttf", 15) lastValuePrint = "{:4.1f}°C {:4.1f}%".format(lastValue["temperature"], lastValue["humidity"]) avgMonthPrint = "øm {:4.1f}°C {:4.1f}%".format(avgMonth["temperature"], avgMonth["humidity"]) avgYearPrint = "øa {:4.1f}°C {:4.1f}%".format(avgYear["temperature"], avgYear["humidity"]) logging.debug("Refresh Display - %s", lastValuePrint) logging.debug("Refresh Display - %s", avgMonthPrint) logging.debug("Refresh Display - %s", avgYearPrint) draw.text((5, 5), "CellarSense", fill=inkyDisplay.BLACK, font=mainHeaderFont) draw.text((20, 35), lastValuePrint, fill=inkyDisplay.BLACK, font=mainFont) draw.text((10, 70), avgMonthPrint, fill=inkyDisplay.BLACK, font=medianFont) draw.text((10, 85), avgYearPrint, fill=inkyDisplay.BLACK, font=medianFont) inkyDisplay.set_image(img) inkyDisplay.show() logging.info("Refresh Display - done") # reschedule scheduler.enter(intervall, priority, refreshDisplay, (intervall, priority, scheduler))
class RenderQueue(): def __init__(self): self.img_stack = [] self.inky_display = InkyPHAT("black") pub.subscribe(self.add_image, 'add_image') def add_image(self, img): self.img_stack.append(img) def run(self): while True: if len(self.img_stack) > 0: most_recent_frame = self.img_stack.pop() self.img_stack = [] self.inky_display.set_image(most_recent_frame) self.inky_display.show() time.sleep(0.1)
def updateScreen(shelf): w, h = (212, 104) dpi = 144 fig, ax = plt.subplots(figsize=(212 / dpi, 104 / dpi), dpi=dpi) fig.subplots_adjust(top=1, bottom=0, left=0.15, right=1) ticks_font = font_manager.FontProperties(fname='04B_03__.TTF', size=4) plt.rcParams['text.antialiased'] = False ax.yaxis.set_ticks(np.arange(0, 3, 1)) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) ax.yaxis.set_tick_params(pad=1, width=1) ax.xaxis.set_ticks([]) ax.set_frame_on(False) plt.autoscale(enable=False) ax.plot(timestamps, values) ax.set_ylim(-.2, 2.2) ax.set_xlim(0, 15) ax.autoscale_view() with io.BytesIO() as f: inky_display = InkyPHAT("yellow") fig.savefig(f, dpi=dpi, cmap="bwr", interpolation="none", origin="lower", pad_inches=0) f.seek(0) i = Image.open(f) # ensure the image is using the correct pallet pal_img = Image.new('P', (1, 1)) pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252) i = i.convert('RGB', palette=pal_img).quantize(palette=pal_img, dither=Image.NONE) inky_display.set_image(i) inky_display.show()
def my_draw(message): inky_display = InkyPHAT("black") inky_display.set_border(inky_display.WHITE) img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) font = ImageFont.truetype(FredokaOne, 30) w, h = font.getsize(message) #x = (inky_display.WIDTH / 2) - (w / 2) #y = (inky_display.HEIGHT / 2) - (h / 2) x = 1 y = 1 draw.text((x, y), message, inky_display.BLACK, font) inky_display.set_image(img) inky_display.show()
def print_text(text): """Displays text in Inky pHAT display. Args: text: String that is displayed on the Inky pHAT """ inky_display = InkyPHAT("black") inky_display.set_border(inky_display.WHITE) img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) font = ImageFont.truetype(FredokaOne, 30) message = text width, height = font.getsize(message) x_axis = (inky_display.WIDTH / 2) - (width / 2) y_axis = (inky_display.HEIGHT / 2) - (height / 2) draw.text((x_axis, y_axis), message, inky_display.BLACK, font) inky_display.set_image(img) inky_display.show()
class MagicBoxScreen(): def __init__(self): self._inky = InkyPHAT("black") self._font = ImageFont.truetype(FredokaOne, 16) def displayMessage(self, message: str="No Message"): w, h = self._font.getsize(message) x = (self._inky.WIDTH / 2) - (w / 2) y = (self._inky.HEIGHT / 2) - (h / 2) img = Image.new("P", (self._inky.WIDTH, self._inky.HEIGHT)) draw = ImageDraw.Draw(img) draw.text((x, y), message, self._inky.BLACK, self._font) self._inky.set_image(img) self._inky.show() def displayQuestion(self, \ questionText: str="What is the speed of light?", \ answerText: List[str] = \ ['0 m/s', '100 m/s', '1 km/s', '3,000 km/s', '300,000 km/s']):
def presentOnInkyScreen(image): logging.info("Loading View onto Inky Phat.") from inky import InkyPHAT #Test invert image. image = image.convert('L') image = ImageOps.invert(image) image = image.convert('1') # Set up the display inky_display = InkyPHAT("black") #inky_display.set_border(inky_display.BLACK) inky_display.v_flip = True inky_display.h_flip = True # Display inky_display.set_border("black") inky_display.set_image(image) inky_display.show() logging.info("View Sent to Device Successfully")
class Clock(): def __init__(self, rotation=180): self.inky_display = InkyPHAT("red") self.inky_display.set_border(self.inky_display.BLACK) self.WIDTH = self.inky_display.WIDTH self.HEIGHT = self.inky_display.HEIGHT self.WHITE = self.inky_display.WHITE self.RED = self.inky_display.RED self.BLACK = self.inky_display.BLACK self.rotation = 180 def create_image(self, currenttime, currentdate, temp, tempmax, tempmin): img = Image.open("./images/backdrop.png") draw = ImageDraw.Draw(img) #Adding the time and date font = ImageFont.truetype(FredokaOne, 28) draw.text((6, 15), currentdate, self.RED, font=font) font = ImageFont.truetype(FredokaOne, 40) draw.text((int(self.WIDTH / 2), 5), currenttime, self.BLACK, font=font) #adding max temp font = ImageFont.truetype(FredokaOne, 20) draw.text((22, 70), temp, self.RED, font=font) draw.text((92, 70), tempmin, self.RED, font=font) draw.text((160, 70), tempmax, self.RED, font=font) self.display_img(img) def display_img(self, img): img = img.rotate(self.rotation) self.inky_display.set_image(img) self.inky_display.show() def test_image(self): img = Image.open("./images/TagTest-212x104.png") self.display_img(img)
class ScreenController(): def __init__(self): self.inky_display = InkyPHAT('red') self.screen_width = self.inky_display.WIDTH self.screen_height = self.inky_display.HEIGHT def display_message(self, message, theme='light'): screen = MessageScreen(message['theme'] or theme, self.inky_display, message) self.inky_display.set_image(screen.image) self.inky_display.show() def display_alert(self, message, theme='light'): screen = AlertScreen(theme, self.inky_display, message) self.inky_display.set_image(screen.image) self.inky_display.show() def display_prompt(self, message, instruction, theme='light'): screen = PromptScreen(theme, self.inky_display, message, instruction) self.inky_display.set_image(screen.image) self.inky_display.show()
class Displayer: lasthash = None def __init__(self, config): cfg = configparser.ConfigParser() cfg.read(config) self.broker = cfg["mqtt"]["broker"] self.ph = InkyPHAT(cfg["display"]["color"]) def client_id(self) -> str: return open("/etc/machine-id").read().strip() def client_topic(self) -> str: cid = self.client_id() return f"phat/client/{cid}" def update_state(self, status: str): client.publish(self.client_topic(), payload=status, qos=1, retain=True) def on_connect(self, client, userdata, flags, rc): logging.info("connected") self.update_state("ALIVE") client.subscribe("phat/image", 1) client.subscribe(f"phat/image/{self.client_id()}", 1) logging.info("setup done") def epaper_display_image(self, img): self.ph.set_image(img) self.ph.show() def epaper_display_error(self, msg): im = Image.new("P", (212, 104)) err_image = ImageDraw.Draw(im) font = ImageFont.truetype(FredokaOne, 22) err_image.text((20, 20), msg, self.ph.RED, font) self.epaper_display_image(im) def on_message(self, client, userdata, message): logging.debug(f"{message.topic}--{str(message.payload.strip())}") try: data = json.loads(message.payload.strip()) except: logging.error(sys.exc_info()) self.epaper_display_error(f"BADJSON") return if self.lasthash == data["hash"]: logging.info("skipping same image update") return self.lasthash = data["hash"] rqh = { "Accept": "image/*", "If-None-Match": self.lasthash, } resp = requests.get(data["url"], headers=rqh, stream=True) if resp.status_code == 200: rspct = resp.headers["Content-Type"] if not rspct.startswith("image/"): self.epaper_display_error(f"resp wasn't image, ignoring: {rspct}") # logging.info(resp.headers) img = Image.open(BytesIO(resp.content)) if img.size != (212, 104): self.epaper_display_error("image size is incorrect!") self.epaper_display_image(img) elif resp.status_code == 304: logging.warning("not updating image due to http 304") else: logging.error(f"unhandled response status: {resp.status_code}") def on_disconnect(self, client, userdata, rc): self.epaper_display_error(f"disconnected rc:{rc}") def debug_mqtt(self, client, userdata, level, buf): logging.debug(f"{level}: {buf}", file=sys.stderr)
class InkyText: def __init__(self, color="red", font_type=FredokaOne, font_size=20, more_text_postfix="..."): self.inkyphat = InkyPHAT(color) self.color = self.__get_inky_color(color) self.more_text_postfix = more_text_postfix self.img = Image.new("P", (self.inkyphat.WIDTH, self.inkyphat.HEIGHT)) self.font = ImageFont.truetype(font_type, font_size) def __get_inky_color(self, color): if color == "red": return self.inkyphat.RED if color == "black": return self.inkyphat.BLACK if color == "yellow": return self.inkyphat.YELLOW raise ValueError("Color {} is not valid!".format(color)) def __get_words_count_line(self, words, word_count): phrase = ' '.join(words[0:word_count]) w, h = self.font.getsize(phrase) if w > self.inkyphat.WIDTH or word_count > len(words): return word_count - 1 else: return self.__get_words_count_line(words, word_count + 1) def __get_text_lines(self, text): words = text.split(" ") words_count = len(words) words_proccessed = 0 words_per_line_count = 0 start_idx = 0 lines = [] while words_proccessed < words_count: words_per_line_count = self.__get_words_count_line( words[start_idx:], 0) end_idx = start_idx + words_per_line_count words_line = words[start_idx:end_idx] lines.append(' '.join(words_line)) start_idx = start_idx + words_per_line_count words_proccessed += words_per_line_count return lines def write_text(self, text): lines = self.__get_text_lines(text) y_cursor = 0 line_idx = 0 lines_count = len(lines) draw = ImageDraw.Draw(self.img) while line_idx < lines_count and y_cursor < self.inkyphat.HEIGHT: line = lines[line_idx] w, h = self.font.getsize(line) draw.text((0, y_cursor), line, self.color, self.font) y_cursor += h line_idx += 1 if line_idx < lines_count: w, h = self.font.getsize(self.more_text_postfix) postfix_x = self.inkyphat.WIDTH - w postfix_y = self.inkyphat.HEIGHT - h draw.text((postfix_x, postfix_y), self.more_text_postfix, self.color, self.font) self.inkyphat.set_image(self.img) self.inkyphat.show()
def main(): # yyyy-mm-dd hh:mm:ss currenttime = strftime("%Y-%m-%d %H:%M:%S", gmtime()) # Write text with weather values to the canvas inkydatetime = strftime("%d/%m %H:%M") # IoT Host Name host = os.uname()[1] # - start timing starttime = datetime.datetime.now().strftime('%m/%d/%Y %H:%M:%S') start = time.time() # Ip address ipaddress = minifiutil.IP_address() parser = argparse.ArgumentParser() parser.add_argument('--price', help='price to be recognized.', required=True) args = parser.parse_args() # end of processing end = time.time() # Output JSON row = {} uuid2 = '{0}_{1}'.format(strftime("%Y%m%d%H%M%S", gmtime()), uuid.uuid4()) cpuTemp = int(float(minifiutil.getCPUtemperature())) usage = psutil.disk_usage("/") # Format Fields row['host'] = os.uname()[1] row['cputemp'] = str(round(cpuTemp, 2)) row['ipaddress'] = str(ipaddress) row['endtime'] = '{0:.2f}'.format(end) row['runtime'] = '{0:.2f}'.format(end - start) row['systemtime'] = datetime.datetime.now().strftime('%m/%d/%Y %H:%M:%S') row['starttime'] = str(starttime) row['diskfree'] = "{:.1f}".format(float(usage.free) / 1024 / 1024) row['memory'] = str(psutil.virtual_memory().percent) row['uuid'] = str(uuid2) row['price'] = str(args.price) # Output JSON json_string = json.dumps(row) print(json_string) # Set up the display inky_display = InkyPHAT("red") inky_display.set_border(inky_display.BLACK) # Create a new canvas to draw on # 212x104 img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) # Load the FredokaOne font font = ImageFont.truetype(FredokaOne, 22) # draw data draw.text((0, 0), "{}".format('Cloudera Mug'), inky_display.BLACK, font=font) draw.text((0, 32), "Price: $ {}".format(args.price), inky_display.RED, font=font) # Display the data on Inky pHAT inky_display.set_image(img) inky_display.show()
class Display(object): """ classdocs """ COLOUR = "black" CLEAR_TIME = 1.0 # seconds DRAW_TIME = 6.0 # seconds DEFAULT_CLEAN_CYCLES = 1 __LOCK_TIMEOUT = 20.0 # seconds __SPI_BUS = 0 __SPI_DEVICE = 0 __SPI_CLOCK = 300000 # Hz was 488000 __SPI_MODE = 1 # ---------------------------------------------------------------------------------------------------------------- def __init__(self, font): """ Constructor """ self.__font = font self.__device = InkyPHAT(self.COLOUR) self.__image = Image.new("P", (self.__device.WIDTH, self.__device.HEIGHT)) self.__drawing = ImageDraw.Draw(self.__image) m_width, m_height = self.__font.getsize("M") self.__text_width = self.__device.WIDTH // m_width self.__text_height = self.__device.HEIGHT // m_height + 1 self.__stop = None self.__render_timeout = Timeout(self.DRAW_TIME) # self.__spi = SPI(self.__SPI_BUS, self.__SPI_DEVICE, None, None) self.__spi = SPI(self.__SPI_BUS, self.__SPI_DEVICE, self.__SPI_MODE, self.__SPI_CLOCK) # ---------------------------------------------------------------------------------------------------------------- def clean(self, cycles=None): try: self.obtain_lock() for _ in range( self.DEFAULT_CLEAN_CYCLES if cycles is None else cycles): # clear... self.__image = Image.new( "P", (self.__device.WIDTH, self.__device.HEIGHT)) self.__drawing = ImageDraw.Draw(self.__image) self.__show() finally: self.release_lock() def clear(self): try: self.obtain_lock() self.__image = Image.new( "P", (self.__device.WIDTH, self.__device.HEIGHT)) self.__drawing = ImageDraw.Draw(self.__image) time.sleep(self.CLEAR_TIME) finally: self.release_lock() def draw_text(self, buffer): try: self.obtain_lock() for row in range(len(buffer)): y_offset = row * self.__text_height self.__drawing.text((0, y_offset), buffer[row], self.__device.BLACK, self.__font) finally: self.release_lock() def render(self): try: self.obtain_lock() print("Display: starting render", file=sys.stderr) sys.stderr.flush() self.__show() except TimeoutError: print("Display: render timeout", file=sys.stderr) sys.stderr.flush() finally: self.release_lock() print("Display: ending render", file=sys.stderr) sys.stderr.flush() # ---------------------------------------------------------------------------------------------------------------- def __show(self): # update... self.__device.set_image(self.__image) # show... with self.__render_timeout: try: self.__spi.acquire_lock() self.__device.show( ) # Do not let the display enter deep sleep - OPC affected!? finally: self.__spi.release_lock() # wait... time.sleep(self.DRAW_TIME) # ---------------------------------------------------------------------------------------------------------------- def obtain_lock(self): Lock.acquire(self.__lock_name, self.__LOCK_TIMEOUT) def release_lock(self): Lock.release(self.__lock_name) @property def __lock_name(self): return self.__class__.__name__ # ---------------------------------------------------------------------------------------------------------------- @property def text_width(self): return self.__text_width @property def text_height(self): return self.__text_height # ---------------------------------------------------------------------------------------------------------------- def __str__(self, *args, **kwargs): return "Display:{text_width:%s, text_height:%s, colour:%s, render_timeout:%s, spi:%s}" % \ (self.text_width, self.text_height, self.COLOUR, self.__render_timeout, self.__spi)
class DisplayUpdater: BATTERY_LOW = 'battery-low' BATTERY_FULL = 'battery-full' MAIN_LINE = 'main-line' def __init__(self, display_resources, sensors, weather_info, moon_info): self.__logger = logging.getLogger(self.__class__.__name__) self.__resources = display_resources self.__sensors = sensors self.__weather_info = weather_info self.__moon_info = moon_info self.__display = InkyPHAT('yellow') self.__display.set_border(InkyPHAT.BLACK) self.__screen_image = Image.open(self.__resources['background']) self.__digit_fonts = {} for name, font in self.__resources['digit-fonts'].items(): self.__digit_fonts[name] = Font(font) self.__labels = {} for name, label in self.__resources['labels'].items(): self.__labels[name] = Font(label) self.__weather_icons_day = {} self.__weather_icons_night = {} for _, icon in self.__resources['weather-icons'].items(): day = Icon(icon['day']) night = Icon(icon['night']) for code in icon['codes']: self.__weather_icons_day[code] = day self.__weather_icons_night[code] = night self.__moon_icons = {} for name, icon in self.__resources['moon-icons'].items(): self.__moon_icons[name] = Icon(icon) self.__power_icons = {} for name, icon in self.__resources['power-icons'].items(): self.__power_icons[name] = Icon(icon) self.__old_temperature = None self.__old_humidity = None self.__old_pressure = None self.__old_date = None self.__old_sunset_time = None self.__old_sunrise_time = None self.__old_today_forecast = None self.__old_tomorrow_forecast = None self.__old_is_daytime = None self.__old_next_moon_phase = None self.__old_next_moon_phase_date = None self.__old_uv_index = None #initialize gpio ofr power monitoring self.__main_line = gpiozero.Button(pin=5, pull_up=False) self.__main_line.when_activated = self.__on_power_state_changed self.__main_line.when_deactivated = self.__on_power_state_changed self.__battery_low = gpiozero.Button(pin=6, pull_up=False) self.__battery_low.when_activated = self.__on_power_state_changed self.__battery_low.when_deactivated = self.__on_power_state_changed #set default power icon icon (battery low) self.__power_state = None self.__on_power_state_changed() @staticmethod def __get_digits(value, length, is_integer): res = [] if is_integer: string_value = '{v:0>{l}d}'.format(v=value, l=length) else: string_value = '{v:*>{l}.1f}'.format(v=value, l=length + 1) for c in string_value: if c != '.': if c == '*': res.append(None) else: res.append(int(c)) return res[:length] def __refresh_temperature(self): res = False tmp = round(self.__sensors.bme280_temperature, 1) if tmp != self.__old_temperature: #draw new value digits = DisplayUpdater.__get_digits(tmp, 3, False) self.__draw_digit(self.__digit_fonts['large'], (164, 2), 8, digits[0]) self.__draw_digit(self.__digit_fonts['large'], (180, 2), 8, digits[1]) self.__draw_digit(self.__digit_fonts['small'], (199, 18), 8, digits[2]) res = True self.__logger.debug('Image update triggered by temperature change') self.__old_temperature = tmp return res def __refresh_humidity(self): res = False tmp = round(self.__sensors.bme280_humidity, 1) if tmp != self.__old_humidity: #draw new value digits = DisplayUpdater.__get_digits(tmp, 4, False) self.__draw_digit(self.__digit_fonts['large'], (148, 37), 1, digits[0]) self.__draw_digit(self.__digit_fonts['large'], (164, 37), 8, digits[1]) self.__draw_digit(self.__digit_fonts['large'], (180, 37), 8, digits[2]) self.__draw_digit(self.__digit_fonts['small'], (199, 53), 8, digits[3]) res = True self.__logger.debug('Image update triggered by humidity change') self.__old_humidity = tmp return res def __refresh_pressure(self): res = False tmp = round(self.__sensors.bme280_pressure, 1) if tmp != self.__old_pressure: #draw new value digits = DisplayUpdater.__get_digits(tmp, 5, False) self.__draw_digit(self.__digit_fonts['large'], (132, 72), 1, digits[0]) self.__draw_digit(self.__digit_fonts['large'], (148, 72), 8, digits[1]) self.__draw_digit(self.__digit_fonts['large'], (164, 72), 8, digits[2]) self.__draw_digit(self.__digit_fonts['large'], (180, 72), 8, digits[3]) self.__draw_digit(self.__digit_fonts['small'], (199, 88), 8, digits[4]) res = True self.__logger.debug('Image update triggered by pressure change') self.__old_pressure = tmp return res def __refresh_date(self): res = False tmp = time.localtime(time.time()) if tmp.tm_year != getattr(self.__old_date, 'tm_year', None): #draw new year value y = DisplayUpdater.__get_digits(tmp.tm_year, 4, True) self.__draw_digit(self.__digit_fonts['small'], (105, 18), 8, y[0]) self.__draw_digit(self.__digit_fonts['small'], (114, 18), 8, y[1]) self.__draw_digit(self.__digit_fonts['small'], (123, 18), 8, y[2]) self.__draw_digit(self.__digit_fonts['small'], (132, 18), 8, y[3]) res = True self.__logger.debug('Image update triggered by year change') if tmp.tm_mon != getattr(self.__old_date, 'tm_mon', None): #draw new mounth value self.__draw_label(self.__labels['month'], (78, 2), tmp.tm_mon) res = True self.__logger.debug('Image update triggered by month change') if tmp.tm_mday != getattr(self.__old_date, 'tm_mday', None): #draw new day value d = DisplayUpdater.__get_digits(tmp.tm_mday, 2, True) self.__draw_digit(self.__digit_fonts['large'], (45, 2), 8, d[0]) self.__draw_digit(self.__digit_fonts['large'], (61, 2), 8, d[1]) #draw new week day value self.__draw_label(self.__labels['week'], (78, 21), tmp.tm_wday + 1) res = True self.__logger.debug('Image update triggered by day change') self.__old_time_date = tmp return res def __refresh_sun_times(self): res = False #sunrise if self.__weather_info.sunrise_time.hour != getattr( self.__old_sunrise_time, 'hour', None): #draw new hour value h = DisplayUpdater.__get_digits( self.__weather_info.sunrise_time.hour, 2, True) self.__draw_digit(self.__digit_fonts['small'], (59, 45), 8, h[0]) self.__draw_digit(self.__digit_fonts['small'], (68, 45), 8, h[1]) res = True self.__logger.debug( 'Image update triggered by sunrise hour change') if self.__weather_info.sunrise_time.minute != getattr( self.__old_sunrise_time, 'minute', None): #draw new minute value m = DisplayUpdater.__get_digits( self.__weather_info.sunrise_time.minute, 2, True) self.__draw_digit(self.__digit_fonts['small'], (79, 45), 8, m[0]) self.__draw_digit(self.__digit_fonts['small'], (88, 45), 8, m[1]) res = True self.__logger.debug( 'Image update triggered by sunrise minute change') self.__old_sunrise_time = self.__weather_info.sunrise_time #sunset if self.__weather_info.sunset_time.hour != getattr( self.__old_sunset_time, 'hour', None): #draw new hour value h = DisplayUpdater.__get_digits( self.__weather_info.sunset_time.hour, 2, True) self.__draw_digit(self.__digit_fonts['small'], (59, 62), 8, h[0]) self.__draw_digit(self.__digit_fonts['small'], (68, 62), 8, h[1]) res = True self.__logger.debug('Image update triggered by sunset hour change') if self.__weather_info.sunset_time.minute != getattr( self.__old_sunset_time, 'minute', None): #draw new minute value m = DisplayUpdater.__get_digits( self.__weather_info.sunset_time.minute, 2, True) self.__draw_digit(self.__digit_fonts['small'], (79, 62), 8, m[0]) self.__draw_digit(self.__digit_fonts['small'], (88, 62), 8, m[1]) res = True self.__logger.debug( 'Image update triggered by sunset minute change') self.__old_sunset_time = self.__weather_info.sunset_time return res def __refresh_weather_icons(self): res = False #today forecast if self.__weather_info.today_forecast != self.__old_today_forecast or \ self.__weather_info.is_daytime != self.__old_is_daytime: #draw new icon if self.__weather_info.is_daytime: self.__draw_icon( self.__weather_icons_day[ self.__weather_info.today_forecast]['draw'], (6, 47)) else: self.__draw_icon( self.__weather_icons_night[ self.__weather_info.today_forecast]['draw'], (6, 47)) res = True self.__logger.debug( 'Image update triggered by today forecast change') self.__old_today_forecast = self.__weather_info.today_forecast #tomorrow forecast if self.__weather_info.tomorrow_forecast != self.__old_tomorrow_forecast or \ self.__weather_info.is_daytime != self.__old_is_daytime: #draw new icon if self.__weather_info.is_daytime: self.__draw_icon( self.__weather_icons_day[ self.__weather_info.tomorrow_forecast]['draw'], (106, 47)) else: self.__draw_icon( self.__weather_icons_night[ self.__weather_info.tomorrow_forecast]['draw'], (106, 47)) res = True self.__logger.debug( 'Image update triggered by tomorrow forecast change') self.__old_tomorrow_forecast = self.__weather_info.tomorrow_forecast self.__old_is_daytime = self.__weather_info.is_daytime return res def __refresh_moon_phase(self): res = False if self.__moon_info.next_moon_phase != self.__old_next_moon_phase: self.__draw_icon( self.__moon_icons[self.__moon_info.next_moon_phase]['draw'], (38, 83)) res = True self.__logger.debug('Image update triggered by moon phase change') self.__old_next_moon_phase = self.__moon_info.next_moon_phase if self.__moon_info.next_moon_phase_date != self.__old_next_moon_phase_date: d = self.__get_digits(self.__moon_info.next_moon_phase_date.day, 2, True) self.__draw_digit(self.__digit_fonts['small'], (60, 86), 8, d[0]) self.__draw_digit(self.__digit_fonts['small'], (69, 86), 8, d[1]) self.__draw_label(self.__labels['month'], (79, 86), self.__moon_info.next_moon_phase_date.month) res = True self.__logger.debug( 'Image update triggered by moon phase date change') self.__old_next_moon_phase_date = self.__moon_info.next_moon_phase_date return res def __refresh_uv_index(self): res = False if self.__weather_info.uv_index != self.__old_uv_index: idx = self.__get_digits(int(self.__weather_info.uv_index), 2, True) self.__draw_digit(self.__digit_fonts['small'], (18, 86), 8, idx[0]) self.__draw_digit(self.__digit_fonts['small'], (27, 86), 8, idx[1]) res = True self.__logger.debug('Image update triggered by uv index change') self.__old_uv_index = self.__weather_info.uv_index return res def __on_power_state_changed(self): main_line = self.__main_line.is_pressed battery_full = self.__battery_low.is_pressed self.__logger.debug('Power Source: {0}'.format( 'Main Line' if main_line else 'Battery')) self.__logger.debug( 'Battery State: {0}'.format('Full' if battery_full else 'Low')) if main_line: new_power_state = DisplayUpdater.MAIN_LINE elif battery_full: new_power_state = DisplayUpdater.BATTERY_FULL else: new_power_state = DisplayUpdater.BATTERY_LOW if new_power_state != self.__power_state: self.__logger.debug('Image update triggered by power state change') self.__power_state = new_power_state try: self.__draw_icon( self.__power_icons[self.__power_state]['draw'], (2, 8)) self.__refresh_display() except Exception as ex: self.__logger.error( 'Fail to update image due to a power state change with error {0}' .format(ex)) self.__logger.exception(ex) def __draw_digit(self, font, position, clean_value, value): try: self.__screen_image.paste(font['clear'], position, font[clean_value]) if value != None: self.__screen_image.paste(font['draw'], position, font[value]) except Exception as ex: self.__logger.error('Fail to draw digit with error {0}'.format(ex)) self.__logger.exception(ex) def __draw_label(self, font, position, value): try: self.__screen_image.paste(font['clear'], position) if value != None: self.__screen_image.paste(font['draw'], position, font[value]) except Exception as ex: self.__logger.error('Fail to draw label with error {0}'.format(ex)) self.__logger.exception(ex) def __draw_icon(self, icon, position): try: self.__screen_image.paste(icon, position) except Exception as ex: self.__logger.error('Fail to draw icon with error {0}'.format(ex)) self.__logger.exception(ex) def __refresh_display(self): try: self.__display.set_image(self.__screen_image) self.__display.show() except Exception as ex: self.__logger.error( 'Fail to refresh display with error {0}'.format(ex)) self.__logger.exception(ex) def update(self): self.__logger.debug('Check for display image update') refresh_needed = False refresh_needed |= self.__refresh_temperature() refresh_needed |= self.__refresh_humidity() refresh_needed |= self.__refresh_pressure() refresh_needed |= self.__refresh_date() refresh_needed |= self.__refresh_sun_times() refresh_needed |= self.__refresh_weather_icons() refresh_needed |= self.__refresh_moon_phase() refresh_needed |= self.__refresh_uv_index() if refresh_needed: self.__logger.debug('Updating display image') self.__refresh_display() else: self.__logger.debug('Update not required for display image')
if len(word_list) <= max_lines: message_does_not_fit = False if font_size < 9: message_does_not_fit = False logging.info("Font size: %s", font_size) offset_x, offset_y = font.getoffset(message) # Rejoin the wrapped lines with newline chars separator = '\n' output_text = separator.join(word_list) w, h = draw.multiline_textsize(output_text, font=font, spacing=0) x = (WIDTH - w)/2 y = (HEIGHT - h - offset_y)/2 draw.multiline_text((x, y), output_text, BLACK, font, align="center", spacing=0) # Rotate and display the image if "ROTATE" in os.environ: img = img.rotate(180) if "WAVESHARE" in os.environ: # epd does not have a set_image method. display.display(display.getbuffer(img)) else: display.set_image(img) display.show()
class draw_words(): def __init__(self): # Start by loading the word list with open('longwords.txt', 'r') as f: self.word_list = f.read().splitlines() self.d = InkyPHAT("red") self.d.set_border(self.d.RED) self.d.h_flip = True self.d.v_flip = True self.font = ImageFont.load("font.pil") def cls(self): img = Image.new("P", (self.d.WIDTH, self.d.HEIGHT)) draw = ImageDraw.Draw(img) draw.rectangle([0, 0, self.d.WIDTH, self.d.HEIGHT], fill=self.d.BLACK) self.d.set_image(img) self.d.show() def draw_list(self, key_code): img = Image.new("P", (self.d.WIDTH, self.d.HEIGHT)) draw = ImageDraw.Draw(img) # Start by filling the screen with a black fill... draw.rectangle([0, 0, self.d.WIDTH, self.d.HEIGHT], fill=self.d.BLACK) int_value = int(key_code, 16) bits_list = [] mask = 0x1FFF for i in range(9): bits_list.append((int_value >> (13 * i)) & mask) bits_list.append(int_value >> 117) # Now we have the word list - we need to pad the final block to 13-bits... bits_list[9] = bits_list[9] << 2 words = [] for block in bits_list: words.append(self.word_list[block]) i = 0 l_col = True for word in words: y = (22 * (i + 1)) - 3 #2 draw.line((0, y, self.d.WIDTH, y), self.d.RED, 2) if l_col: draw.text((5, (i * 22) - 2), word.upper(), self.d.WHITE, self.font) else: draw.text((110, (i * 22) - 2), word.upper(), self.d.WHITE, self.font) i += 1 l_col = not (l_col) draw.line((0, 0, 0, self.d.HEIGHT), self.d.RED, 2) draw.line((self.d.WIDTH / 2, 0, self.d.WIDTH / 2, self.d.HEIGHT), self.d.RED, 2) draw.line((self.d.WIDTH - 2, 0, self.d.WIDTH - 2, self.d.HEIGHT), self.d.RED, 2) self.d.set_image(img) self.d.show()
def set_inky_display(image): inky_display = InkyPHAT(display_colour) inky_display.set_image(image) inky_border = inky_display.WHITE if border_colour == 'white' else inky_display.BLACK inky_display.set_border(inky_border) inky_display.show()
def main(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--driver', default=None, choices=['inky', 'inkyphat', 'waveshare_epd']) parser.add_argument("--pair", default='XETHZUSD', help="currency pair") parser.add_argument('--flip', dest='flip', action='store_true', help="rotate the display") parser.set_defaults(flip=False) parser.add_argument("--output", help="save plot as png") args = parser.parse_args() yesterday = datetime.now() - timedelta(hours=12) quotes = quotes_historical_kraken_ohlc(args.pair, yesterday, interval=15) if not quotes: raise ValueError('Empty OHLC data') candlestick_ohlc(ax, quotes, width=1) ax.xaxis_date() ax.autoscale_view() ymin, ymax = ax.get_ylim() last_low = quotes[-1][3] last_high = quotes[-1][2] last_close = quotes[-1][4] ttf = pkg_resources.open_binary(fonts, '04B_03__.TTF') font = ImageFont.truetype(ttf, 8) RED = (255,0,0) BLACK = (0,0,0) display = None driver = args.driver if driver == 'inky': from inky import InkyPHAT RED = InkyPHAT.RED BLACK = InkyPHAT.BLACK display = InkyPHAT('black') elif driver == 'inkyphat': from inkyphat import RED as inky_RED, BLACK as inky_BLACK, set_image as _set_image, show as _show RED = inky_RED BLACK = inky_BLACK class InkyPHAT(): def __init__(*args): pass set_image = staticmethod(_set_image) show = staticmethod(_show) display = InkyPHAT() elif driver == 'waveshare_epd': from waveshare_epd import epd2in13_V2 class EPD(): def __init__(self, *args): self.epd = epd2in13_V2.EPD() self.epd.init(self.epd.FULL_UPDATE) self.buf = [] def set_image(self, image): self.buf = self.epd.getbuffer(image) def show(self): self.epd.display(self.buf) display = EPD() with io.BytesIO() as f: fig.savefig(f, dpi=dpi, cmap="bwr", interpolation="none", origin="lower", pad_inches=0) f.seek(0) i = Image.open(f) d = ImageDraw.Draw(i) ypos = 0 if ymax - last_high > last_low - ymin else h - 6 d.text((148, ypos), '{:.2f}'.format(last_close), BLACK, font) d.text((176, ypos), args.pair, RED, font) if args.flip: i = i.transpose(Image.ROTATE_180) if display: display.set_image(i.convert("P")) display.show() if args.output: i.save(args.output)
INKY_COLOUR = sys.argv[1] if INKY_COLOUR not in ['red', 'yellow', 'black']: print("Usage: {} <red, yellow, black>".format(sys.argv[0])) sys.exit(1) phat = InkyPHAT(INKY_COLOUR) white = Image.new('P', (212, 104), phat.WHITE) black = Image.new('P', (212, 104), phat.BLACK) while True: print("White") phat.set_border(phat.WHITE) phat.set_image(black) phat.show() time.sleep(1) if INKY_COLOUR == 'red': print("Red") phat.set_border(phat.RED) phat.set_image(white) phat.show() time.sleep(1) if INKY_COLOUR == 'yellow': print("Yellow") phat.set_border(phat.YELLOW) phat.set_image(white) phat.show() time.sleep(1)
#! /usr/bin/env python3 from PIL import Image, ImageFont, ImageDraw from font_fredoka_one import FredokaOne from inky import InkyPHAT from time import sleep if __name__ == "__main__": inkyphat = InkyPHAT("black") font = ImageFont.truetype(FredokaOne, 22) count = 0 try: while True: count += 1 message = f"Hi! Count = {count}" print(message) w, h = font.getsize(message) x = (inkyphat.WIDTH / 2) - (w / 2) y = (inkyphat.HEIGHT / 2) - (h / 2) img = Image.new("P", (inkyphat.WIDTH, inkyphat.HEIGHT)) draw = ImageDraw.Draw(img) draw.text((x, y), message, inkyphat.BLACK, font) inkyphat.set_image(img) inkyphat.show() except KeyboardInterrupt: pass
class Inky(DisplayImpl): def __init__(self, config): super(Inky, self).__init__(config, 'inky') self._display = None def layout(self): fonts.setup(10, 8, 10, 28, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 37) self._layout['name'] = (5, 18) self._layout['channel'] = (0, 0) self._layout['aps'] = (30, 0) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (102, 18), 'font': fonts.status_font(fonts.Small), 'max': 20 } return self._layout def initialize(self): logging.info("initializing inky display") if self.config['color'] == 'fastAndFurious': logging.info("Initializing Inky in 2-color FAST MODE") logging.info( "THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED") logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK") from pwnagotchi.ui.hw.libs.inkyphat.inkyphatfast import InkyPHATFast self._display = InkyPHATFast('black') self._display.set_border(InkyPHATFast.BLACK) elif self.config['color'] == 'auto': from inky.auto import auto self._display = auto() self._display.set_border(self._display.BLACK) self._layout['width'] = self._display.WIDTH self._layout['height'] = self._display.HEIGHT else: from inky import InkyPHAT self._display = InkyPHAT(self.config['color']) self._display.set_border(InkyPHAT.BLACK) def render(self, canvas): if self.config['color'] == 'black' or self.config[ 'color'] == 'fastAndFurious': display_colors = 2 else: display_colors = 3 img_buffer = canvas.convert('RGB').convert('P', palette=1, colors=display_colors) if self.config['color'] == 'red': img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0, # index 1 is black 255, 0, 0 # index 2 is red ]) elif self.config['color'] == 'yellow': img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0, # index 1 is black 255, 255, 0 # index 2 is yellow ]) else: img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0 # index 1 is black ]) self._display.set_image(img_buffer) try: self._display.show() except: logging.exception("error while rendering on inky") def clear(self): self._display.Clear()
# code taken from tutorial at https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-inky-phat # John Harney, 1.17.2020 from inky import InkyPHAT from PIL import Image, ImageFont, ImageDraw from font_fredoka_one import FredokaOne inky_display = InkyPHAT("yellow") inky_display.set_border(inky_display.WHITE) img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) font = ImageFont.truetype(FredokaOne, 22) message = "Centre College" w, h = font.getsize(message) x = (inky_display.WIDTH / 2) - (w / 2) y = (inky_display.HEIGHT / 2) - (h / 2) draw.text((x, y), message, inky_display.YELLOW, font) inky_display.set_image(img) inky_display.show()
class InkyDevice(): def __init__(self): self.display = InkyPHAT("black") self.display.set_border(self.display.WHITE) self.font = ImageFont.truetype('resources/Eden_Mills_Bold.ttf', 12) self.lock = threading.Lock() self.startup() def startup(self): print('writing startup image...') with self.lock: img = Image.open("resources/k8s-bw.png") draw = ImageDraw.Draw(img) self.display.set_image(img) self.display.show() def shutdown(self): print('writing shutdown image...') with self.lock: img = Image.open("resources/8-bit-dino.png") draw = ImageDraw.Draw(img) font = ImageFont.truetype('resources/Eden_Mills_Bold.ttf', 28) message = "offline " message_width, message_height = self.get_text_size(message, font) x, y = Location.CenterRight.place(message, font, message_width, message_height, self.display.WIDTH, self.display.HEIGHT) draw.text((x, y), message, self.display.BLACK, font) self.display.set_image(img) self.display.show() def get_text_size(self, message, font): # render the text in another text buffer to get the dimensions message_width, message_height = 0,0 for line in message.split("\n"): partial_width, partial_height = font.getsize(line) message_width = max(message_width, partial_width) approx_line_spacing = 1.2 message_height += int(partial_height*approx_line_spacing) return message_width, message_height def write(self, render_result): with self.lock: img = Image.new("P", (self.display.WIDTH, self.display.HEIGHT)) draw = ImageDraw.Draw(img) for location, message in render_result.items(): # render the text in another text buffer to get the dimensions message_width, message_height = self.get_text_size(message, self.font) # find the placement of the text buffer and overlay onto the screen buffer x, y = location.place(message, self.font, message_width, message_height, self.display.WIDTH, self.display.HEIGHT) draw.text((x, y), message, self.display.BLACK, self.font) # flush the screen buffer to the device self.display.set_image(img) self.display.show()