|
3 | 3 | from queue import Queue, Empty |
4 | 4 | import re |
5 | 5 |
|
| 6 | +from maix._maix.image import Image |
| 7 | + |
| 8 | +class PagedText: |
| 9 | + def __init__(self, page_width = -1, page_height = -1): |
| 10 | + """ |
| 11 | + page_width: 每页宽度 |
| 12 | + page_height: 每页最大行数 |
| 13 | + char_width_func: 一个函数, 输入字符返回宽度 (例如 lambda c: 1 或字典映射) |
| 14 | + """ |
| 15 | + self.page_width = page_width |
| 16 | + self.page_height = page_height |
| 17 | + self.pages = [[]] # 每个元素是 page, page 是行的列表, 行是 (text, width) |
| 18 | + |
| 19 | + def reset(self, page_width, page_height): |
| 20 | + self.page_width = page_width |
| 21 | + self.page_height = page_height |
| 22 | + self.pages = [[]] |
| 23 | + |
| 24 | + def add_text(self, text): |
| 25 | + current_page = self.pages[-1] |
| 26 | + if not current_page: |
| 27 | + current_page.append(("", 0, 0)) # 初始化第一行 |
| 28 | + |
| 29 | + page_height_used = sum(line[2] for line in current_page) |
| 30 | + |
| 31 | + for ch in text: |
| 32 | + line_text, _, line_h = current_page[-1] |
| 33 | + new_line_text = line_text + ch |
| 34 | + size = image.string_size(new_line_text) |
| 35 | + ch_w = size[0] |
| 36 | + ch_h = size[1] |
| 37 | + |
| 38 | + # 尝试放到当前行 |
| 39 | + if ch_w <= self.page_width: |
| 40 | + # 更新行 |
| 41 | + new_w = ch_w |
| 42 | + new_h = max(line_h, ch_h) |
| 43 | + # 替换行 |
| 44 | + current_page[-1] = (new_line_text, new_w, new_h) |
| 45 | + else: |
| 46 | + # 需要换行 |
| 47 | + if page_height_used + line_h <= self.page_height: |
| 48 | + current_page.append((ch, ch_w, ch_h)) |
| 49 | + page_height_used += line_h # 累加上一行高度 |
| 50 | + else: |
| 51 | + # 需要换页 |
| 52 | + self.pages.append([(ch, ch_w, ch_h)]) |
| 53 | + current_page = self.pages[-1] |
| 54 | + page_height_used = ch_h |
| 55 | + |
| 56 | + page_height_used = sum(line[2] for line in current_page) |
| 57 | + # print("OVER", line_text, line_w, line_h, page_height_used) |
| 58 | + |
| 59 | + def clear(self): |
| 60 | + self.pages = [[]] |
| 61 | + |
| 62 | + def print(self): |
| 63 | + for i, page in enumerate(self.pages): |
| 64 | + print(f"Page {i+1}:") |
| 65 | + page_height = sum(line[2] for line in page) |
| 66 | + for line_text, line_width, line_height in page: |
| 67 | + print(f" '{line_text}' (w={line_width}, h={line_height})") |
| 68 | + print(f" -> total height used = {page_height}") |
| 69 | + print() |
| 70 | + |
| 71 | + def draw_last_page_on(self, img:image.Image, color: image.Color = image.COLOR_WHITE): |
| 72 | + if img.width() != self.page_width or img.height() != self.page_height: |
| 73 | + return |
| 74 | + |
| 75 | + current_page = self.pages[-1] |
| 76 | + if not current_page: |
| 77 | + return |
| 78 | + |
| 79 | + height = 0 |
| 80 | + for line_text, _, line_height in current_page: |
| 81 | + img.draw_string(0, height, line_text, color, wrap_space=0) |
| 82 | + height += line_height |
| 83 | + |
6 | 84 | class App: |
7 | 85 | def __init__(self): |
8 | 86 | image.load_font("sourcehansans", "/maixapp/share/font/SourceHanSansCN-Regular.otf", size = 20) |
@@ -74,15 +152,18 @@ def __init__(self): |
74 | 152 | self.tts_thread = threading.Thread(target=self.tts_thread_handle, daemon=True) |
75 | 153 | self.tts_thread.start() |
76 | 154 |
|
| 155 | + self.page_text = PagedText() |
| 156 | + |
77 | 157 | def player_thread_handle(self): |
| 158 | + bytes_per_frame = 2 |
78 | 159 | while not app.need_exit(): |
79 | 160 | try: |
80 | 161 | pcm = self.player_queue.get(timeout=500) |
81 | 162 | t = time.ticks_ms() |
82 | 163 | print('self.player.remaining size1', self.player.get_remaining_frames()) |
83 | 164 | while not app.need_exit(): |
84 | 165 | idle_frames = self.player.get_remaining_frames() |
85 | | - write_frames = len(pcm) / self.player.frame_size() |
| 166 | + write_frames = len(pcm) / bytes_per_frame |
86 | 167 | print('idle', idle_frames, 'write', write_frames) |
87 | 168 | if idle_frames >= write_frames: |
88 | 169 | break |
@@ -110,10 +191,22 @@ def tts_thread_handle(self): |
110 | 191 |
|
111 | 192 | def __llm_on_reply(self, obj, resp): |
112 | 193 | print(resp.msg_new, end="") |
| 194 | + ts_data = self.ts.read() |
113 | 195 | img = image.Image(320, 240, bg=image.COLOR_BLACK) |
114 | 196 | self.__draw_string_upper_center(img, text="Run LLM..", color=image.COLOR_GREEN) |
115 | 197 | # img.draw_string(0, 0, "Run LLM..", image.COLOR_GREEN) |
116 | | - img.draw_string(0, 30, resp.msg, image.COLOR_WHITE) |
| 198 | + |
| 199 | + self.page_text.add_text(resp.msg_new) |
| 200 | + new_img = image.Image(320, 210, bg=image.COLOR_BLACK) |
| 201 | + self.page_text.draw_last_page_on(new_img, image.COLOR_WHITE) |
| 202 | + img.draw_image(0, 30, new_img) |
| 203 | + |
| 204 | + exit_img_x = 0 |
| 205 | + exit_img_y = 0 |
| 206 | + img.draw_image(exit_img_x, exit_img_y, self.exit_img) |
| 207 | + if ts_data[2] and 0<=ts_data[0]<=self.exit_img.width() + exit_img_x*2 and 0 <=ts_data[1]<=self.exit_img.height() + exit_img_y*2: |
| 208 | + print('exit') |
| 209 | + app.set_exit_flag(True) |
117 | 210 | self.disp.show(img) |
118 | 211 |
|
119 | 212 | self.llm_last_msg += resp.msg_new |
@@ -207,7 +300,9 @@ class Status: |
207 | 300 | if asr_result: |
208 | 301 | img.draw_string(0, 30, asr_result, image.COLOR_WHITE) |
209 | 302 | elif llm_result: |
210 | | - img.draw_string(0, 30, llm_result, image.COLOR_WHITE) |
| 303 | + new_img = image.Image(320, 210, bg=image.COLOR_BLACK) |
| 304 | + self.page_text.draw_last_page_on(new_img, image.COLOR_WHITE) |
| 305 | + img.draw_image(0, 30, new_img) |
211 | 306 |
|
212 | 307 | exit_img_x = 0 |
213 | 308 | exit_img_y = 0 |
@@ -247,6 +342,7 @@ class Status: |
247 | 342 | status = Status.LLM |
248 | 343 | elif status == Status.LLM: |
249 | 344 | if asr_result: |
| 345 | + self.page_text.reset(320, 210) |
250 | 346 | llm_result0 = self.llm.send(asr_result) |
251 | 347 | llm_result = llm_result0.msg |
252 | 348 | self.llm.clear_context() |
|
0 commit comments