Compare commits
2
Commits
85b6fa62f3
...
9b1d029b14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b1d029b14 | ||
|
|
dbeeb78916 |
+1
-1
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = "0.0.0.139"
|
VERSION = "0.0.0.141"
|
||||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||||
DEFAULT_GRID_SIZE = 10
|
DEFAULT_GRID_SIZE = 10
|
||||||
DEFAULT_ADD_TIMESTAMP = True
|
DEFAULT_ADD_TIMESTAMP = True
|
||||||
|
|||||||
+17
-4
@@ -21,8 +21,9 @@ import app_state
|
|||||||
class ConnectDialog:
|
class ConnectDialog:
|
||||||
"""Modal dialog for entering device connection parameters."""
|
"""Modal dialog for entering device connection parameters."""
|
||||||
|
|
||||||
def __init__(self, parent, on_connect_callback):
|
def __init__(self, parent, on_connect_callback, on_before_connect=None):
|
||||||
self._callback = on_connect_callback
|
self._callback = on_connect_callback
|
||||||
|
self._before_connect = on_before_connect
|
||||||
self._thread = None
|
self._thread = None
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
|
|
||||||
@@ -103,6 +104,11 @@ class ConnectDialog:
|
|||||||
self._status_label.config(text="Invalid port number", foreground="#c00000")
|
self._status_label.config(text="Invalid port number", foreground="#c00000")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Runs here, on the main thread, rather than in the worker: it is where a
|
||||||
|
# device already connected is let go, and that takes the UI down with it.
|
||||||
|
if self._before_connect is not None:
|
||||||
|
self._before_connect()
|
||||||
|
|
||||||
self._btn_connect.config(state="disabled")
|
self._btn_connect.config(state="disabled")
|
||||||
self._status_label.config(text="Connecting...", foreground="#666666")
|
self._status_label.config(text="Connecting...", foreground="#666666")
|
||||||
|
|
||||||
@@ -169,9 +175,10 @@ class DiscoverDialog:
|
|||||||
"""Modal dialog that broadcasts DASP DISCOVER, lists responding
|
"""Modal dialog that broadcasts DASP DISCOVER, lists responding
|
||||||
devices, and lets the user pick one to connect to."""
|
devices, and lets the user pick one to connect to."""
|
||||||
|
|
||||||
def __init__(self, parent, on_connect_callback):
|
def __init__(self, parent, on_connect_callback, on_before_connect=None):
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
self._callback = on_connect_callback
|
self._callback = on_connect_callback
|
||||||
|
self._before_connect = on_before_connect
|
||||||
self._discover_thread = None
|
self._discover_thread = None
|
||||||
|
|
||||||
self._dlg = tk.Toplevel(parent)
|
self._dlg = tk.Toplevel(parent)
|
||||||
@@ -318,7 +325,7 @@ class DiscoverDialog:
|
|||||||
host = values[0]
|
host = values[0]
|
||||||
port = self._port_var.get().strip()
|
port = self._port_var.get().strip()
|
||||||
self._dlg.destroy()
|
self._dlg.destroy()
|
||||||
_CredentialsDialog(self._parent, self._callback, host, port)
|
_CredentialsDialog(self._parent, self._callback, host, port, self._before_connect)
|
||||||
|
|
||||||
|
|
||||||
# ────────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────────
|
||||||
@@ -328,8 +335,9 @@ class DiscoverDialog:
|
|||||||
class _CredentialsDialog:
|
class _CredentialsDialog:
|
||||||
"""Small modal that asks for username and password, then connects."""
|
"""Small modal that asks for username and password, then connects."""
|
||||||
|
|
||||||
def __init__(self, parent, on_connect_callback, host, port):
|
def __init__(self, parent, on_connect_callback, host, port, on_before_connect=None):
|
||||||
self._callback = on_connect_callback
|
self._callback = on_connect_callback
|
||||||
|
self._before_connect = on_before_connect
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
self._thread = None
|
self._thread = None
|
||||||
@@ -397,6 +405,11 @@ class _CredentialsDialog:
|
|||||||
self._status_label.config(text="Invalid port", foreground="#c00000")
|
self._status_label.config(text="Invalid port", foreground="#c00000")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Runs here, on the main thread, rather than in the worker: it is where a
|
||||||
|
# device already connected is let go, and that takes the UI down with it.
|
||||||
|
if self._before_connect is not None:
|
||||||
|
self._before_connect()
|
||||||
|
|
||||||
self._btn_connect.config(state="disabled")
|
self._btn_connect.config(state="disabled")
|
||||||
self._status_label.config(text="Connecting...", foreground="#666666")
|
self._status_label.config(text="Connecting...", foreground="#666666")
|
||||||
|
|
||||||
|
|||||||
@@ -2316,7 +2316,8 @@ def do_device_connect():
|
|||||||
wiresheet.render_components([])
|
wiresheet.render_components([])
|
||||||
app_state.window.title(app_state.base_window_title())
|
app_state.window.title(app_state.base_window_title())
|
||||||
|
|
||||||
connect_dialog.ConnectDialog(app_state.window, _connect_to_device)
|
connect_dialog.ConnectDialog(app_state.window, _connect_to_device,
|
||||||
|
on_before_connect=_drop_device_before_connect)
|
||||||
|
|
||||||
|
|
||||||
def do_device_discover():
|
def do_device_discover():
|
||||||
@@ -2337,7 +2338,18 @@ def do_device_discover():
|
|||||||
wiresheet.render_components([])
|
wiresheet.render_components([])
|
||||||
app_state.window.title(app_state.base_window_title())
|
app_state.window.title(app_state.base_window_title())
|
||||||
|
|
||||||
connect_dialog.DiscoverDialog(app_state.window, _connect_to_device)
|
connect_dialog.DiscoverDialog(app_state.window, _connect_to_device,
|
||||||
|
on_before_connect=_drop_device_before_connect)
|
||||||
|
|
||||||
|
|
||||||
|
def _drop_device_before_connect():
|
||||||
|
"""One device at a time: the one already connected goes before the next is tried.
|
||||||
|
|
||||||
|
Called when Connect is pressed, not when the dialog opens, so backing out of the
|
||||||
|
dialog leaves the current connection alone.
|
||||||
|
"""
|
||||||
|
if app_state.device_session is not None:
|
||||||
|
do_device_disconnect()
|
||||||
|
|
||||||
|
|
||||||
def _connect_to_device(host, port, user, password):
|
def _connect_to_device(host, port, user, password):
|
||||||
@@ -2358,8 +2370,6 @@ def _connect_to_device(host, port, user, password):
|
|||||||
def _on_connected(ds, root_comp):
|
def _on_connected(ds, root_comp):
|
||||||
"""Main-thread callback after successful connect."""
|
"""Main-thread callback after successful connect."""
|
||||||
app_state.window.title(app_state.base_window_title_live())
|
app_state.window.title(app_state.base_window_title_live())
|
||||||
menu_device.entryconfig("Connect...", state="disabled")
|
|
||||||
menu_device.entryconfig("Discover...", state="disabled")
|
|
||||||
menu_device.entryconfig("Disconnect", state="normal")
|
menu_device.entryconfig("Disconnect", state="normal")
|
||||||
menu_device.entryconfig("Download to SAX...", state="normal")
|
menu_device.entryconfig("Download to SAX...", state="normal")
|
||||||
app_state.set_status(f"Connected to {app_state.device_host}:{app_state.device_port}")
|
app_state.set_status(f"Connected to {app_state.device_host}:{app_state.device_port}")
|
||||||
@@ -2435,6 +2445,30 @@ def do_device_disconnect():
|
|||||||
wiresheet.render_components([])
|
wiresheet.render_components([])
|
||||||
|
|
||||||
|
|
||||||
|
def quit_app():
|
||||||
|
"""File > Exit and the window's close box: let go of the device before going.
|
||||||
|
|
||||||
|
Leaving without a proper close keeps the device's Sox session and subscriptions
|
||||||
|
alive until it times them out, so the next connect may be turned away.
|
||||||
|
"""
|
||||||
|
global _cov_poll_id
|
||||||
|
|
||||||
|
if _cov_poll_id is not None:
|
||||||
|
app_state.window.after_cancel(_cov_poll_id)
|
||||||
|
_cov_poll_id = None
|
||||||
|
|
||||||
|
ds = app_state.device_session
|
||||||
|
app_state.device_session = None
|
||||||
|
app_state.live_mode = False
|
||||||
|
if ds is not None:
|
||||||
|
try:
|
||||||
|
ds.disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
app_state.window.destroy()
|
||||||
|
|
||||||
|
|
||||||
def _start_cov_polling():
|
def _start_cov_polling():
|
||||||
"""Starts the 100ms COV event polling loop on the main thread."""
|
"""Starts the 100ms COV event polling loop on the main thread."""
|
||||||
global _cov_poll_id
|
global _cov_poll_id
|
||||||
@@ -2616,7 +2650,7 @@ menu_file.add_command(label="Save As...", command=sax_file.save_file_as)
|
|||||||
menu_file.add_separator()
|
menu_file.add_separator()
|
||||||
menu_file.add_command(label="Print...", command=wiresheet.print_sheet, accelerator=f"{accel_mod}+P")
|
menu_file.add_command(label="Print...", command=wiresheet.print_sheet, accelerator=f"{accel_mod}+P")
|
||||||
menu_file.add_separator()
|
menu_file.add_separator()
|
||||||
menu_file.add_command(label="Exit", command=window.quit)
|
menu_file.add_command(label="Exit", command=quit_app)
|
||||||
menu_bar.add_cascade(label="File", menu=menu_file)
|
menu_bar.add_cascade(label="File", menu=menu_file)
|
||||||
|
|
||||||
menu_edit = tk.Menu(menu_bar, tearoff=0)
|
menu_edit = tk.Menu(menu_bar, tearoff=0)
|
||||||
@@ -3000,6 +3034,8 @@ paned_window.add(frame_editor, weight=3)
|
|||||||
app_state.dirty_display_callback = refresh_dirty_display
|
app_state.dirty_display_callback = refresh_dirty_display
|
||||||
undo.set_refresh_callback(refresh_all_views)
|
undo.set_refresh_callback(refresh_all_views)
|
||||||
sax_file.set_tree_filled_callback(on_tree_filled)
|
sax_file.set_tree_filled_callback(on_tree_filled)
|
||||||
|
sax_file.set_disconnect_device_callback(do_device_disconnect)
|
||||||
|
window.protocol("WM_DELETE_WINDOW", quit_app)
|
||||||
refresh_palette_kits()
|
refresh_palette_kits()
|
||||||
apply_left_pane_width()
|
apply_left_pane_width()
|
||||||
|
|
||||||
|
|||||||
+14
-13
@@ -31,6 +31,15 @@ def set_tree_filled_callback(callback):
|
|||||||
_tree_filled = callback
|
_tree_filled = callback
|
||||||
|
|
||||||
|
|
||||||
|
_disconnect_device = None
|
||||||
|
|
||||||
|
|
||||||
|
def set_disconnect_device_callback(callback):
|
||||||
|
"""Registers how to drop the live device session before a file takes its place."""
|
||||||
|
global _disconnect_device
|
||||||
|
_disconnect_device = callback
|
||||||
|
|
||||||
|
|
||||||
def parse_xml_to_tree(treeview, parent_node, xml_element):
|
def parse_xml_to_tree(treeview, parent_node, xml_element):
|
||||||
"""Recursively traverses XML elements and adds <comp> tags to the Treeview."""
|
"""Recursively traverses XML elements and adds <comp> tags to the Treeview."""
|
||||||
for child in xml_element:
|
for child in xml_element:
|
||||||
@@ -132,19 +141,6 @@ def open_file():
|
|||||||
messagebox.showwarning("Action Blocked", "Cannot load SAX profiles because 'Sedona home folder' configuration is missing or empty.")
|
messagebox.showwarning("Action Blocked", "Cannot load SAX profiles because 'Sedona home folder' configuration is missing or empty.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if app_state.live_mode:
|
|
||||||
answer = messagebox.askyesnocancel(
|
|
||||||
"Device Connected",
|
|
||||||
"A live device connection is active.\n\n"
|
|
||||||
"Disconnect from the device and open a file?\n\n"
|
|
||||||
"Choose Yes to disconnect and open, No to cancel.",
|
|
||||||
)
|
|
||||||
if not answer:
|
|
||||||
return
|
|
||||||
# Import here to avoid circular import at module level
|
|
||||||
from main import do_device_disconnect
|
|
||||||
do_device_disconnect()
|
|
||||||
|
|
||||||
file_path = filedialog.askopenfilename(
|
file_path = filedialog.askopenfilename(
|
||||||
title="Select SAX File",
|
title="Select SAX File",
|
||||||
filetypes=[("SAX Files", "*.sax"), ("XML Files", "*.xml"), ("All Files", "*.*")],
|
filetypes=[("SAX Files", "*.sax"), ("XML Files", "*.xml"), ("All Files", "*.*")],
|
||||||
@@ -152,6 +148,11 @@ def open_file():
|
|||||||
if not file_path:
|
if not file_path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# A session is a file or a device, never both. The device goes only once a file has
|
||||||
|
# actually been chosen, so cancelling the dialog leaves the connection as it was.
|
||||||
|
if app_state.device_session is not None and _disconnect_device is not None:
|
||||||
|
_disconnect_device()
|
||||||
|
|
||||||
canvas = app_state.canvas
|
canvas = app_state.canvas
|
||||||
tree = app_state.tree
|
tree = app_state.tree
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user