Wiresheet: Ctrl+click to select several links, delete them together

Ctrl+click toggles a link in the selection; Del or the link popup deletes
every selected link after one confirmation, as a single undoable step.
On a device, links the device refuses stay and are named in one warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 09:55:18 +03:00
co-authored by Claude Opus 5.5
parent d0454688b3
commit 67fbfe03dd
4 changed files with 160 additions and 62 deletions
+70 -24
View File
@@ -843,6 +843,8 @@ def draw_links():
app_state.canvas_link_map.clear()
app_state.knob_link_ends.clear()
app_state.selected_link_tag = None
app_state.selected_link_tags.clear()
_selected_link_restore.clear()
app_state.unroutable_link_count = 0
if app_state.xml_root_element is None:
return
@@ -920,7 +922,8 @@ def draw_links():
app_state.unroutable_link_count = len(jobs) - len(routed)
_selected_link_restore = [] # [(item, fill, outline, width)] captured when a link is selected
# wire tag -> [(item, fill, outline, width)], captured when that link is selected
_selected_link_restore = {}
def trim_to_knobs(x1, y1, x2, y2):
@@ -938,31 +941,36 @@ def trim_to_knobs(x1, y1, x2, y2):
x2 - ux * inset, y2 - uy * inset)
def select_link(wire_tag):
def select_link(wire_tag, add=False):
"""Highlights a link in red, remembering what each of its items looked like.
The colours cannot simply be reset to LINK_COLOR afterwards: a link shown as knobs
may be grey, or half grey, and blanket-restoring would repaint it as an ordinary
on-sheet marker.
With `add` the link joins whatever links are already selected (Ctrl+click);
otherwise it becomes the only one.
"""
canvas = app_state.canvas
deselect_link()
if not add:
deselect_link()
elif wire_tag in app_state.selected_link_tags:
return
if wire_tag and wire_tag in app_state.canvas_link_map:
# A knob can stand for several links. One already selected through another link
# is red by now, so its true colours are taken from that link's record.
captured = {entry[0]: entry for entries in _selected_link_restore.values()
for entry in entries}
restore = _selected_link_restore.setdefault(wire_tag, [])
for item in canvas.find_withtag(wire_tag):
_selected_link_restore.append((
restore.append(captured.get(item) or (
item,
canvas.itemcget(item, "fill"),
canvas.itemcget(item, "outline") if canvas.type(item) != "line" else "",
canvas.itemcget(item, "width"),
))
if canvas.type(item) == "line":
canvas.itemconfig(item, fill=LINK_SELECT_COLOR,
width=LINK_SELECT_WIDTH)
elif "link_knob_core" in canvas.gettags(item):
pass # the white centre stays white while selected
else:
canvas.itemconfig(item, fill=LINK_SELECT_COLOR)
canvas.tag_raise(wire_tag)
_paint_link_selected(wire_tag)
app_state.selected_link_tags.append(wire_tag)
app_state.selected_link_tag = wire_tag
# A link with both ends on this sheet but no drawable route is two knobs with
@@ -975,26 +983,64 @@ def select_link(wire_tag):
*trim_to_knobs(*ends), fill=LINK_SELECT_COLOR,
width=LINK_SELECT_WIDTH, dash=LINK_HINT_DASH,
arrow=tk.LAST, arrowshape=(8, 9, 3),
tags=("link", "link_select_hint")
tags=("link", "link_select_hint", f"link_select_hint_{wire_tag}")
)
canvas.tag_raise("link_select_hint")
canvas.tag_raise("link_knob")
def _paint_link_selected(wire_tag):
"""Paints one link's wire and knobs in the selection red."""
canvas = app_state.canvas
for item in canvas.find_withtag(wire_tag):
if canvas.type(item) == "line":
canvas.itemconfig(item, fill=LINK_SELECT_COLOR, width=LINK_SELECT_WIDTH)
elif "link_knob_core" in canvas.gettags(item):
pass # the white centre stays white while selected
else:
canvas.itemconfig(item, fill=LINK_SELECT_COLOR)
canvas.tag_raise(wire_tag)
def toggle_link(wire_tag):
"""Ctrl+click: adds a link to the selection, or takes it back out."""
if wire_tag in app_state.selected_link_tags:
_restore_link(wire_tag)
app_state.selected_link_tags.remove(wire_tag)
app_state.selected_link_tag = (app_state.selected_link_tags[-1]
if app_state.selected_link_tags else None)
# A knob shared with a link still selected went back to its own colours above.
for still_selected in app_state.selected_link_tags:
_paint_link_selected(still_selected)
if app_state.canvas.find_withtag("link_select_hint"):
app_state.canvas.tag_raise("link_select_hint")
app_state.canvas.tag_raise("link_knob")
else:
select_link(wire_tag, add=True)
def _restore_link(wire_tag):
"""Puts one selected link back exactly as it was drawn."""
canvas = app_state.canvas
canvas.delete(f"link_select_hint_{wire_tag}")
for item, fill, outline, width in _selected_link_restore.pop(wire_tag, ()):
try:
if canvas.type(item) == "line":
canvas.itemconfig(item, fill=fill, width=width)
else:
canvas.itemconfig(item, fill=fill, outline=outline, width=width)
except tk.TclError:
pass # the item went away with a repaint
def deselect_link():
"""Puts the selected link back exactly as it was drawn."""
"""Puts every selected link back exactly as it was drawn."""
canvas = app_state.canvas
canvas.delete("link_select_hint")
if app_state.selected_link_tag:
for item, fill, outline, width in _selected_link_restore:
try:
if canvas.type(item) == "line":
canvas.itemconfig(item, fill=fill, width=width)
else:
canvas.itemconfig(item, fill=fill, outline=outline, width=width)
except tk.TclError:
pass # the item went away with a repaint
app_state.selected_link_tag = None
for wire_tag in app_state.selected_link_tags:
_restore_link(wire_tag)
app_state.selected_link_tags.clear()
app_state.selected_link_tag = None
_selected_link_restore.clear()