|
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