|
82 | 82 |
|
83 | 83 | RGB_LED_MARGIN = 12
|
84 | 84 |
|
85 |
| -# Resize editor if display is too big (e.g. 8.8" displays are 1920x480) |
| 85 | +# Resize editor if display is too big (e.g. 8.8" displays are 1920x480), can be changed later by zoom buttons |
86 | 86 | RESIZE_FACTOR = 2 if (display.lcd.get_width() > 1000 or display.lcd.get_height() > 1000) else 1
|
87 | 87 |
|
88 | 88 | ERROR_IN_THEME = Image.open("res/docs/error-in-theme.png")
|
@@ -225,6 +225,24 @@ def on_zone_click(event):
|
225 | 225 | label_zone.place_forget()
|
226 | 226 |
|
227 | 227 |
|
| 228 | + def on_mousewheel(event): |
| 229 | + global RESIZE_FACTOR |
| 230 | + if event.delta > 0: |
| 231 | + RESIZE_FACTOR = RESIZE_FACTOR - 0.2 |
| 232 | + else: |
| 233 | + RESIZE_FACTOR = RESIZE_FACTOR + 0.2 |
| 234 | + |
| 235 | + |
| 236 | + def on_zoom_plus(): |
| 237 | + global RESIZE_FACTOR |
| 238 | + RESIZE_FACTOR = RESIZE_FACTOR - 0.2 |
| 239 | + |
| 240 | + |
| 241 | + def on_zoom_minus(): |
| 242 | + global RESIZE_FACTOR |
| 243 | + RESIZE_FACTOR = RESIZE_FACTOR + 0.2 |
| 244 | + |
| 245 | + |
228 | 246 | # Apply system locale to this program
|
229 | 247 | locale.setlocale(locale.LC_ALL, '')
|
230 | 248 |
|
@@ -253,97 +271,118 @@ def on_zone_click(event):
|
253 | 271 | logger.error(f"Error in theme: {e}")
|
254 | 272 | error_in_theme = True
|
255 | 273 |
|
256 |
| - display_width, display_height = int(display.lcd.get_width() / RESIZE_FACTOR), int( |
257 |
| - display.lcd.get_height() / RESIZE_FACTOR) |
258 |
| - |
259 |
| - # Create preview window |
260 |
| - logger.debug("Opening theme preview window with static data") |
261 |
| - viewer = tkinter.Tk() |
262 |
| - viewer.title("Turing SysMon Theme Editor") |
263 |
| - viewer.iconphoto(True, tkinter.PhotoImage(file=config.MAIN_DIRECTORY / "res/icons/monitor-icon-17865/64.png")) |
264 |
| - viewer.geometry(str(display_width + 2 * RGB_LED_MARGIN) + "x" + str(display_height + 2 * RGB_LED_MARGIN + 40)) |
265 |
| - viewer.protocol("WM_DELETE_WINDOW", on_closing) |
266 |
| - viewer.call('wm', 'attributes', '.', '-topmost', '1') # Preview window always on top |
267 |
| - viewer.config(cursor="cross") |
268 |
| - |
269 |
| - # Display RGB backplate LEDs color as background color |
270 |
| - led_color = config.THEME_DATA['display'].get("DISPLAY_RGB_LED", (255, 255, 255)) |
271 |
| - if isinstance(led_color, str): |
272 |
| - led_color = tuple(map(int, led_color.split(', '))) |
273 |
| - viewer.configure(bg='#%02x%02x%02x' % led_color) |
274 |
| - |
275 |
| - circular_mask = Image.open(config.MAIN_DIRECTORY / "res/backgrounds/circular-mask.png") |
276 |
| - |
277 |
| - # Display preview in the window |
278 |
| - if not error_in_theme: |
279 |
| - screen_image = display.lcd.screen_image |
280 |
| - if config.THEME_DATA["display"].get("DISPLAY_SIZE", '3.5"') == '2.1"': |
281 |
| - # This is a circular screen: apply a circle mask over the preview |
282 |
| - screen_image.paste(circular_mask, mask=circular_mask) |
283 |
| - display_image = ImageTk.PhotoImage( |
284 |
| - screen_image.resize((int(screen_image.width / RESIZE_FACTOR), int(screen_image.height / RESIZE_FACTOR)))) |
285 |
| - else: |
286 |
| - size = display_width if display_width < display_height else display_height |
287 |
| - display_image = ImageTk.PhotoImage(ERROR_IN_THEME.resize((size, size))) |
288 |
| - viewer_picture = tkinter.Label(viewer, image=display_image, borderwidth=0) |
289 |
| - viewer_picture.place(x=RGB_LED_MARGIN, y=RGB_LED_MARGIN) |
290 |
| - |
291 |
| - # Allow to click on preview to show coordinates and draw zones |
292 |
| - viewer_picture.bind("<ButtonPress-1>", on_button1_press) |
293 |
| - viewer_picture.bind("<B1-Motion>", on_button1_press_and_drag) |
294 |
| - viewer_picture.bind("<ButtonRelease-1>", on_button1_release) |
295 |
| - |
296 |
| - label_coord = tkinter.Label(viewer, text="Click or draw a zone to show coordinates") |
297 |
| - label_coord.place(x=0, y=display_height + 2 * RGB_LED_MARGIN, |
298 |
| - width=display_width + 2 * RGB_LED_MARGIN) |
299 |
| - |
300 |
| - label_info = tkinter.Label(viewer, text="This preview will reload when theme file is updated") |
301 |
| - label_info.place(x=0, y=display_height + 2 * RGB_LED_MARGIN + 20, |
302 |
| - width=display_width + 2 * RGB_LED_MARGIN) |
303 |
| - |
304 |
| - label_zone = tkinter.Label(viewer, bg='#%02x%02x%02x' % tuple(map(lambda x: 255 - x, led_color))) |
305 |
| - label_zone.bind("<ButtonRelease-1>", on_zone_click) |
306 |
| - viewer.update() |
307 |
| - |
308 |
| - logger.debug("You can now edit the theme file in the editor. When you save your changes, the preview window will " |
309 |
| - "update automatically") |
310 |
| - # Every time the theme file is modified: reload preview |
311 | 274 | while True:
|
312 |
| - if os.path.exists(theme_file) and os.path.getmtime(theme_file) > last_edit_time: |
313 |
| - logger.debug("The theme file has been updated, the preview window will refresh") |
314 |
| - try: |
315 |
| - refresh_theme() |
316 |
| - error_in_theme = False |
317 |
| - except Exception as e: |
318 |
| - logger.error(f"Error in theme: {e}") |
319 |
| - error_in_theme = True |
320 |
| - last_edit_time = os.path.getmtime(theme_file) |
321 |
| - |
322 |
| - # Update the preview.png that is in the theme folder |
323 |
| - display.lcd.screen_image.save(config.THEME_DATA['PATH'] + "preview.png", "PNG") |
324 |
| - |
325 |
| - # Display new picture |
326 |
| - if not error_in_theme: |
327 |
| - screen_image = display.lcd.screen_image |
328 |
| - if config.THEME_DATA["display"].get("DISPLAY_SIZE", '3.5"') == '2.1"': |
329 |
| - # This is a circular screen: apply a circle mask over the preview |
330 |
| - screen_image.paste(circular_mask, mask=circular_mask) |
331 |
| - display_image = ImageTk.PhotoImage( |
332 |
| - screen_image.resize( |
333 |
| - (int(screen_image.width / RESIZE_FACTOR), int(screen_image.height / RESIZE_FACTOR)))) |
334 |
| - else: |
335 |
| - size = display_width if display_width < display_height else display_height |
336 |
| - display_image = ImageTk.PhotoImage(ERROR_IN_THEME.resize((size, size))) |
337 |
| - viewer_picture.config(image=display_image) |
338 |
| - |
339 |
| - # Refresh RGB backplate LEDs color |
340 |
| - led_color = config.THEME_DATA['display'].get("DISPLAY_RGB_LED", (255, 255, 255)) |
341 |
| - if isinstance(led_color, str): |
342 |
| - led_color = tuple(map(int, led_color.split(', '))) |
343 |
| - viewer.configure(bg='#%02x%02x%02x' % led_color) |
344 |
| - label_zone.configure(bg='#%02x%02x%02x' % tuple(map(lambda x: 255 - x, led_color))) |
345 |
| - |
346 |
| - # Regularly update the viewer window even if content unchanged |
| 275 | + display_width, display_height = int(display.lcd.get_width() / RESIZE_FACTOR), int( |
| 276 | + display.lcd.get_height() / RESIZE_FACTOR) |
| 277 | + current_resize_factor = RESIZE_FACTOR |
| 278 | + |
| 279 | + # Create preview window |
| 280 | + logger.debug("Opening theme preview window with static data") |
| 281 | + viewer = tkinter.Tk() |
| 282 | + viewer.title("Turing SysMon Theme Editor") |
| 283 | + viewer.iconphoto(True, tkinter.PhotoImage(file=config.MAIN_DIRECTORY / "res/icons/monitor-icon-17865/64.png")) |
| 284 | + viewer.geometry(str(display_width + 2 * RGB_LED_MARGIN) + "x" + str(display_height + 2 * RGB_LED_MARGIN + 80)) |
| 285 | + viewer.protocol("WM_DELETE_WINDOW", on_closing) |
| 286 | + viewer.call('wm', 'attributes', '.', '-topmost', '1') # Preview window always on top |
| 287 | + viewer.config(cursor="cross") |
| 288 | + |
| 289 | + # Display RGB backplate LEDs color as background color |
| 290 | + led_color = config.THEME_DATA['display'].get("DISPLAY_RGB_LED", (255, 255, 255)) |
| 291 | + if isinstance(led_color, str): |
| 292 | + led_color = tuple(map(int, led_color.split(', '))) |
| 293 | + viewer.configure(bg='#%02x%02x%02x' % led_color) |
| 294 | + |
| 295 | + circular_mask = Image.open(config.MAIN_DIRECTORY / "res/backgrounds/circular-mask.png") |
| 296 | + |
| 297 | + # Display preview in the window |
| 298 | + if not error_in_theme: |
| 299 | + screen_image = display.lcd.screen_image |
| 300 | + if config.THEME_DATA["display"].get("DISPLAY_SIZE", '3.5"') == '2.1"': |
| 301 | + # This is a circular screen: apply a circle mask over the preview |
| 302 | + screen_image.paste(circular_mask, mask=circular_mask) |
| 303 | + display_image = ImageTk.PhotoImage( |
| 304 | + screen_image.resize( |
| 305 | + (int(screen_image.width / RESIZE_FACTOR), int(screen_image.height / RESIZE_FACTOR)))) |
| 306 | + else: |
| 307 | + size = display_width if display_width < display_height else display_height |
| 308 | + display_image = ImageTk.PhotoImage(ERROR_IN_THEME.resize((size, size))) |
| 309 | + viewer_picture = tkinter.Label(viewer, image=display_image, borderwidth=0) |
| 310 | + viewer_picture.place(x=RGB_LED_MARGIN, y=RGB_LED_MARGIN) |
| 311 | + |
| 312 | + # Allow to click on preview to show coordinates and draw zones |
| 313 | + viewer_picture.bind("<ButtonPress-1>", on_button1_press) |
| 314 | + viewer_picture.bind("<B1-Motion>", on_button1_press_and_drag) |
| 315 | + viewer_picture.bind("<ButtonRelease-1>", on_button1_release) |
| 316 | + |
| 317 | + # Allow to resize editor using mouse wheel |
| 318 | + viewer.bind_all("<MouseWheel>", on_mousewheel) |
| 319 | + |
| 320 | + zoom_plus_btn = tkinter.Button(viewer, text="Zoom +", command=lambda: on_zoom_plus()) |
| 321 | + zoom_plus_btn.place(x=RGB_LED_MARGIN, y=display_height + 2 * RGB_LED_MARGIN, height=30, |
| 322 | + width=int(display_width / 2)) |
| 323 | + |
| 324 | + zoom_minus_btn = tkinter.Button(viewer, text="Zoom -", command=lambda: on_zoom_minus()) |
| 325 | + zoom_minus_btn.place(x=int(display_width / 2) + RGB_LED_MARGIN, y=display_height + 2 * RGB_LED_MARGIN, |
| 326 | + height=30, width=int(display_width / 2)) |
| 327 | + |
| 328 | + label_coord = tkinter.Label(viewer, text="Click or draw a zone to show coordinates") |
| 329 | + label_coord.place(x=0, y=display_height + 2 * RGB_LED_MARGIN + 40, |
| 330 | + width=display_width + 2 * RGB_LED_MARGIN) |
| 331 | + |
| 332 | + label_info = tkinter.Label(viewer, text="This preview will reload when theme file is updated") |
| 333 | + label_info.place(x=0, y=display_height + 2 * RGB_LED_MARGIN + 60, |
| 334 | + width=display_width + 2 * RGB_LED_MARGIN) |
| 335 | + |
| 336 | + label_zone = tkinter.Label(viewer, bg='#%02x%02x%02x' % tuple(map(lambda x: 255 - x, led_color))) |
| 337 | + label_zone.bind("<ButtonRelease-1>", on_zone_click) |
347 | 338 | viewer.update()
|
348 | 339 |
|
349 |
| - time.sleep(0.1) |
| 340 | + logger.debug( |
| 341 | + "You can now edit the theme file in the editor. When you save your changes, the preview window will " |
| 342 | + "update automatically") |
| 343 | + |
| 344 | + while current_resize_factor == RESIZE_FACTOR: |
| 345 | + # Every time the theme file is modified: reload preview |
| 346 | + if os.path.exists(theme_file) and os.path.getmtime(theme_file) > last_edit_time: |
| 347 | + logger.debug("The theme file has been updated, the preview window will refresh") |
| 348 | + try: |
| 349 | + refresh_theme() |
| 350 | + error_in_theme = False |
| 351 | + except Exception as e: |
| 352 | + logger.error(f"Error in theme: {e}") |
| 353 | + error_in_theme = True |
| 354 | + last_edit_time = os.path.getmtime(theme_file) |
| 355 | + |
| 356 | + # Update the preview.png that is in the theme folder |
| 357 | + display.lcd.screen_image.save(config.THEME_DATA['PATH'] + "preview.png", "PNG") |
| 358 | + |
| 359 | + # Display new picture |
| 360 | + if not error_in_theme: |
| 361 | + screen_image = display.lcd.screen_image |
| 362 | + if config.THEME_DATA["display"].get("DISPLAY_SIZE", '3.5"') == '2.1"': |
| 363 | + # This is a circular screen: apply a circle mask over the preview |
| 364 | + screen_image.paste(circular_mask, mask=circular_mask) |
| 365 | + display_image = ImageTk.PhotoImage( |
| 366 | + screen_image.resize( |
| 367 | + (int(screen_image.width / RESIZE_FACTOR), int(screen_image.height / RESIZE_FACTOR)))) |
| 368 | + else: |
| 369 | + size = display_width if display_width < display_height else display_height |
| 370 | + display_image = ImageTk.PhotoImage(ERROR_IN_THEME.resize((size, size))) |
| 371 | + viewer_picture.config(image=display_image) |
| 372 | + |
| 373 | + # Refresh RGB backplate LEDs color |
| 374 | + led_color = config.THEME_DATA['display'].get("DISPLAY_RGB_LED", (255, 255, 255)) |
| 375 | + if isinstance(led_color, str): |
| 376 | + led_color = tuple(map(int, led_color.split(', '))) |
| 377 | + viewer.configure(bg='#%02x%02x%02x' % led_color) |
| 378 | + label_zone.configure(bg='#%02x%02x%02x' % tuple(map(lambda x: 255 - x, led_color))) |
| 379 | + |
| 380 | + # Regularly update the viewer window even if content unchanged, or it will appear as "not responding" |
| 381 | + viewer.update() |
| 382 | + |
| 383 | + time.sleep(0.1) |
| 384 | + |
| 385 | + # Zoom level changed, reload editor |
| 386 | + logger.info( |
| 387 | + f"Zoom level changed from {current_resize_factor:.1f} to {RESIZE_FACTOR:.1f}, reloading theme editor") |
| 388 | + viewer.destroy() |
0 commit comments