Add OxPage WYSIWYG editor panel for ontrolOx HMI pages

New ox_page_view.py with OxPageViewPanel: canvas rendering at device
resolution (800x480 Orion, 320x240 Rion), RGB565 color conversion,
widget tree, selection handles, drag-move, device frame image support.
Wired into main.py with context menu, history, panel lifecycle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 21:06:56 +03:00
co-authored by Claude Opus 4.6
parent d5ef8eea6f
commit 16edd144e9
3 changed files with 589 additions and 4 deletions
+58 -3
View File
@@ -18,6 +18,7 @@ import properties
import sax_file
import schema
import undo
import ox_page_view
import user_service_view
import wiresheet
from sax_dom import (find_component_by_path, find_parent_element, get_component_path,
@@ -79,6 +80,7 @@ HISTORY_LIMIT = 50
VIEW_SHEET = "sheet"
VIEW_PROPERTIES = "properties"
VIEW_LINKS = "links"
VIEW_PAGE = "page"
VIEW_USERS = "users"
# Tk class names of the widgets that own the keyboard while they have focus. Del and
@@ -124,6 +126,7 @@ show_left_column_var = None
properties_panel = None
link_panel = None
kits_panel = None
ox_page_panel = None
user_service_panel = None
# The sheet to put the tree back on when a sheet change is refused because of unsaved
@@ -192,6 +195,12 @@ def refresh_all_views():
else:
close_editor_panel(force=True)
if ox_page_panel is not None:
if _component_in_document(ox_page_panel.comp):
ox_page_panel.refresh()
else:
close_editor_panel(force=True)
if user_service_panel is not None:
if _component_in_document(user_service_panel.comp):
user_service_panel.refresh()
@@ -219,7 +228,7 @@ def delete_component(comp):
than going through refresh_all_views, so the panel would otherwise be left describing
a component the document no longer has.
"""
panel = properties_panel or link_panel or user_service_panel
panel = properties_panel or link_panel or ox_page_panel or user_service_panel
showing = panel is not None and panel.comp is comp
if sax_file.execute_cascading_deletion(comp) and showing:
close_editor_panel(force=True)
@@ -228,7 +237,8 @@ def delete_component(comp):
def editor_panel_showing():
"""Whether something other than the wiresheet has the editor pane."""
return (properties_panel is not None or link_panel is not None
or kits_panel is not None or user_service_panel is not None)
or kits_panel is not None or ox_page_panel is not None
or user_service_panel is not None)
def _take_editor_pane():
@@ -340,6 +350,38 @@ def dismiss_link_view(restore_canvas=True):
_give_editor_pane_back()
def show_ox_page_view(comp):
"""Puts the WYSIWYG page editor for an OxPage in the editor pane."""
global ox_page_panel
if comp is None:
return
if not close_editor_panel():
return
_take_editor_pane()
ox_page_panel = ox_page_view.build_ox_page_view_panel(
frame_editor, comp, on_change=refresh_all_views,
on_close=dismiss_ox_page_view,
)
ox_page_panel.frame.pack(fill=tk.BOTH, expand=True)
record_visit(VIEW_PAGE, comp)
app_state.set_status(f"Page Editor: {comp.get('name', '?')}")
def dismiss_ox_page_view(restore_canvas=True):
"""Takes the OxPage editor down and gives the pane back."""
global ox_page_panel
if ox_page_panel is None:
return
ox_page_panel.destroy()
ox_page_panel = None
if restore_canvas:
_give_editor_pane_back()
def show_user_service(comp):
"""Puts the User Manager for a sys::UserService in the editor pane."""
global user_service_panel
@@ -420,6 +462,9 @@ def close_editor_panel(force=False):
if kits_panel is not None:
dismiss_kits()
return True
if ox_page_panel is not None:
dismiss_ox_page_view()
return True
if user_service_panel is not None:
dismiss_user_service()
return True
@@ -987,6 +1032,9 @@ def on_tree_right_click(event):
def _open_special_view(comp):
"""Opens the type-specific view for a component, or Properties as fallback."""
if ox_page_view.is_ox_page(comp):
show_ox_page_view(comp)
return
if user_service_view.is_user_service(comp):
show_user_service(comp)
return
@@ -1288,6 +1336,11 @@ def fill_component_popup(menu, comp):
label="Link View",
command=lambda: show_link_view(comp),
)
if ox_page_view.is_ox_page(comp):
menu.add_command(
label="Page Editor",
command=lambda: show_ox_page_view(comp),
)
if user_service_view.is_user_service(comp):
menu.add_command(
label="User Manager",
@@ -1730,6 +1783,8 @@ def _show_history_entry(position):
_show_sheet_entry(element)
elif kind == VIEW_PROPERTIES:
show_properties(element)
elif kind == VIEW_PAGE:
show_ox_page_view(element)
elif kind == VIEW_USERS:
show_user_service(element)
else:
@@ -1795,7 +1850,7 @@ def on_tree_filled():
# A different file has no such component, and a rebuild after a delete may have taken
# it. Neither is a moment to ask about edits that now have nowhere to go.
panel = properties_panel or link_panel or user_service_panel
panel = properties_panel or link_panel or ox_page_panel or user_service_panel
if panel is not None and not _component_in_document(panel.comp):
close_editor_panel(force=True)