-
-
Notifications
You must be signed in to change notification settings - Fork 345
Expand file tree
/
Copy pathlcd_simulated.py
More file actions
145 lines (120 loc) · 5.69 KB
/
lcd_simulated.py
File metadata and controls
145 lines (120 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# SPDX-License-Identifier: GPL-3.0-or-later
#
# turing-smart-screen-python - a Python system monitor and library for USB-C displays like Turing Smart Screen or XuanFang
# https://github.com/mathoudebine/turing-smart-screen-python/
#
# Copyright (C) 2021 Matthieu Houdebine (mathoudebine)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import mimetypes
import shutil
from http.server import BaseHTTPRequestHandler, HTTPServer
from library.lcd.lcd_comm import *
SCREENSHOT_FILE = "screencap.png"
WEBSERVER_PORT = 5678
# This webserver offer a blank page displaying simulated screen with auto-refresh
class SimulatedLcdWebServer(BaseHTTPRequestHandler):
def log_message(self, format, *args):
return
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes(f"<img src=\"{SCREENSHOT_FILE}\" id=\"myImage\" />", "utf-8"))
self.wfile.write(bytes("<script>", "utf-8"))
self.wfile.write(bytes("setInterval(function() {", "utf-8"))
self.wfile.write(bytes(" var myImageElement = document.getElementById('myImage');", "utf-8"))
self.wfile.write(bytes(f" myImageElement.src = '{SCREENSHOT_FILE}?rand=' + Math.random();", "utf-8"))
self.wfile.write(bytes("}, 250);", "utf-8"))
self.wfile.write(bytes("</script>", "utf-8"))
elif self.path.startswith(f"/{SCREENSHOT_FILE}"):
with open(SCREENSHOT_FILE, 'rb') as imgfile:
mimetype = mimetypes.MimeTypes().guess_type(SCREENSHOT_FILE)[0]
self.send_response(200)
if mimetype is not None:
self.send_header('Content-type', mimetype)
self.end_headers()
self.wfile.write(imgfile.read())
# Simulated display: write on a file instead of serial port
class LcdSimulated(LcdComm):
def __init__(self, com_port: str = "AUTO", display_width: int = 320, display_height: int = 480,
update_queue: Optional[queue.Queue] = None):
LcdComm.__init__(self, com_port, display_width, display_height, update_queue)
self.screen_image = Image.new("RGB", (self.get_width(), self.get_height()), (255, 255, 255))
self.screen_image.save("tmp", "PNG")
shutil.copyfile("tmp", SCREENSHOT_FILE)
self.orientation = Orientation.PORTRAIT
try:
self.webServer = HTTPServer(("localhost", WEBSERVER_PORT), SimulatedLcdWebServer)
logger.debug(f"To see your simulated screen, open http://localhost:{WEBSERVER_PORT} in a browser")
threading.Thread(target=self.webServer.serve_forever).start()
except OSError:
logger.error("Error starting webserver! An instance might already be running on port %d." % WEBSERVER_PORT)
def __del__(self):
self.closeSerial()
@staticmethod
def auto_detect_com_port() -> Optional[str]:
return None
def closeSerial(self):
logger.debug("Shutting down web server")
self.webServer.shutdown()
def InitializeComm(self):
pass
def Reset(self):
pass
def Clear(self):
self.SetOrientation(self.orientation)
def ScreenOff(self):
pass
def ScreenOn(self):
pass
def SetBrightness(self, level: int = 25):
pass
def SetBackplateLedColor(self, led_color: Tuple[int, int, int] = (255, 255, 255)):
pass
def SetOrientation(self, orientation: Orientation = Orientation.PORTRAIT):
self.orientation = orientation
# Just draw the screen again with the new width/height based on orientation
with self.update_queue_mutex:
self.screen_image = Image.new("RGB", (self.get_width(), self.get_height()), (255, 255, 255))
self.screen_image.save("tmp", "PNG")
shutil.copyfile("tmp", SCREENSHOT_FILE)
def DisplayPILImage(
self,
image: Image.Image,
x: int = 0, y: int = 0,
image_width: int = 0,
image_height: int = 0
):
# If the image height/width isn't provided, use the native image size
if not image_height:
image_height = image.size[1]
if not image_width:
image_width = image.size[0]
# If image is bigger than display, crop it
if image.size[1] > self.get_height():
image_height = self.get_height()
if image.size[0] > self.get_width():
image_width = self.get_width()
if image_width != image.size[0] or image_height != image.size[1]:
image = image.crop((0, 0, image_width, image_height))
assert x <= self.get_width(), 'Image X coordinate must be <= display width'
assert y <= self.get_height(), 'Image Y coordinate must be <= display height'
assert image_height > 0, 'Image height must be > 0'
assert image_width > 0, 'Image width must be > 0'
with self.update_queue_mutex:
self.screen_image.paste(image, (x, y))
self.screen_image.save("tmp", "PNG")
shutil.copyfile("tmp", SCREENSHOT_FILE)