first commit where the whole thing works in at least common scenarios
This commit is contained in:
parent
895139e14e
commit
f64439f789
119
backend/layer.py
119
backend/layer.py
|
|
@ -10,8 +10,8 @@ class Layer:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.visible = True
|
self.visible = True
|
||||||
self.on_board_items = [] # List of {'uuid': str, 'class': str}
|
self.on_board_items = []
|
||||||
self.stored_items = [] # List of filenames (str) pointing to item JSONs
|
self.stored_items = []
|
||||||
|
|
||||||
def add_item(self, item):
|
def add_item(self, item):
|
||||||
if not item: return False
|
if not item: return False
|
||||||
|
|
@ -24,37 +24,58 @@ class Layer:
|
||||||
self.on_board_items.append({'uuid': uuid, 'class': cls})
|
self.on_board_items.append({'uuid': uuid, 'class': cls})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _safe_deselect(self, item):
|
||||||
|
if not item: return
|
||||||
|
try:
|
||||||
|
if hasattr(item, 'ClearSelected'): item.ClearSelected()
|
||||||
|
if hasattr(item, 'ClearBrightened'): item.ClearBrightened()
|
||||||
|
group = item.GetParentGroup()
|
||||||
|
if group:
|
||||||
|
if hasattr(group, 'ClearSelected'): group.ClearSelected()
|
||||||
|
if hasattr(group, 'ClearBrightened'): group.ClearBrightened()
|
||||||
|
except: pass
|
||||||
|
|
||||||
def _clear_selection_safe(self):
|
def _clear_selection_safe(self):
|
||||||
try:
|
try:
|
||||||
sel = pcbnew.GetCurrentSelection()
|
sel = pcbnew.GetCurrentSelection()
|
||||||
items = [i for i in sel]
|
items = [i for i in sel]
|
||||||
for item in items:
|
for item in items:
|
||||||
if hasattr(item, 'ClearSelected'): item.ClearSelected()
|
self._safe_deselect(item)
|
||||||
if hasattr(item, 'ClearBrightened'): item.ClearBrightened()
|
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
def hide(self):
|
def hide(self):
|
||||||
self.manager.log(f"Layer {self.name}: Hiding...")
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
|
self.manager.log(f"Layer {self.name}: Hiding... ({len(self.on_board_items)} items)")
|
||||||
try:
|
try:
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
|
|
||||||
|
# 1. Deselect specific items
|
||||||
|
for entry in self.on_board_items:
|
||||||
|
try:
|
||||||
|
item = find_item_by_uuid(board, entry['uuid'])
|
||||||
|
if item: self._safe_deselect(item)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
# 2. Clear general selection
|
||||||
self._clear_selection_safe()
|
self._clear_selection_safe()
|
||||||
pcbnew.Refresh()
|
|
||||||
|
|
||||||
new_stored_items = list(self.stored_items)
|
new_stored_items = list(self.stored_items)
|
||||||
items_to_remove = []
|
items_to_remove = []
|
||||||
failed_entries = []
|
failed_entries = []
|
||||||
|
|
||||||
|
# 3. Serialize
|
||||||
for entry in self.on_board_items:
|
for entry in self.on_board_items:
|
||||||
try:
|
try:
|
||||||
uuid = entry['uuid']
|
uuid = entry['uuid']
|
||||||
|
self.manager.log(f" Processing {uuid}...")
|
||||||
item = find_item_by_uuid(board, uuid)
|
item = find_item_by_uuid(board, uuid)
|
||||||
if not item:
|
if not item:
|
||||||
self.manager.log(f"Hide: Item {uuid} not found (skipping)")
|
self.manager.log(f" Item not found on board (skipping)")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
sexpr, err = Serializer.serialize(item)
|
sexpr, err = Serializer.serialize(item)
|
||||||
if not sexpr:
|
if not sexpr:
|
||||||
self.manager.log(f"Hide: Serialize failed {uuid}: {err}")
|
self.manager.log(f" Serialize failed: {err}")
|
||||||
failed_entries.append(entry)
|
failed_entries.append(entry)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -64,32 +85,42 @@ class Layer:
|
||||||
|
|
||||||
filename = self.manager.storage.save_item(item_data, uuid)
|
filename = self.manager.storage.save_item(item_data, uuid)
|
||||||
if filename:
|
if filename:
|
||||||
|
self.manager.log(f" Saved to {filename}")
|
||||||
new_stored_items.append(filename)
|
new_stored_items.append(filename)
|
||||||
items_to_remove.append(item)
|
items_to_remove.append(item)
|
||||||
else:
|
else:
|
||||||
|
self.manager.log(f" Storage save failed")
|
||||||
failed_entries.append(entry)
|
failed_entries.append(entry)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.manager.log(f"Hide: Error entry {entry}: {e}")
|
self.manager.log(f" Error processing entry: {e}")
|
||||||
failed_entries.append(entry)
|
failed_entries.append(entry)
|
||||||
|
|
||||||
|
# 4. Remove
|
||||||
|
self.manager.log(f" Removing {len(items_to_remove)} items from board...")
|
||||||
for item in items_to_remove:
|
for item in items_to_remove:
|
||||||
try:
|
try:
|
||||||
if item.IsLocked(): item.SetLocked(False)
|
if item.IsLocked(): item.SetLocked(False)
|
||||||
board.Remove(item)
|
board.Remove(item)
|
||||||
except: pass
|
except Exception as e:
|
||||||
|
self.manager.log(f" Remove failed: {e}")
|
||||||
|
|
||||||
|
# 5. Update State
|
||||||
self.stored_items = new_stored_items
|
self.stored_items = new_stored_items
|
||||||
self.on_board_items = failed_entries
|
self.on_board_items = failed_entries
|
||||||
if not self.on_board_items: self.visible = False
|
if not self.on_board_items: self.visible = False
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
self.manager.log("Hide: Complete")
|
self.manager.log(f"Hide: Complete. Stored: {len(self.stored_items)}, Remaining on board: {len(self.on_board_items)}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.manager.log(f"Hide: Critical Error: {e}")
|
self.manager.log(f"Hide: Critical Error: {e}")
|
||||||
self.manager.log(traceback.format_exc())
|
self.manager.log(traceback.format_exc())
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
try:
|
try:
|
||||||
self.manager.log(f"Layer {self.name}: Showing (Force Restore)...")
|
self.manager.log(f"Layer {self.name}: Showing... Stored items: {len(self.stored_items)}")
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
self.manager.is_restoring = True
|
self.manager.is_restoring = True
|
||||||
|
|
||||||
|
|
@ -97,51 +128,72 @@ class Layer:
|
||||||
restored_files = []
|
restored_files = []
|
||||||
|
|
||||||
for filename in self.stored_items:
|
for filename in self.stored_items:
|
||||||
|
self.manager.log(f" Processing file {filename}...")
|
||||||
try:
|
try:
|
||||||
item_data = self.manager.storage.load_item(filename)
|
item_data = self.manager.storage.load_item(filename)
|
||||||
if not item_data: continue
|
if not item_data:
|
||||||
uuid = item_data.get('uuid')
|
self.manager.log(f" Failed to load JSON data")
|
||||||
|
continue
|
||||||
|
|
||||||
# Force Restore: Remove existing ghost if present
|
uuid = item_data.get('uuid')
|
||||||
existing = find_item_by_uuid(board, uuid)
|
self.manager.log(f" UUID: {uuid}")
|
||||||
if existing:
|
|
||||||
try:
|
# Note: Ghost detection removed to prevent segfaults on stale pointers.
|
||||||
if existing.IsLocked(): existing.SetLocked(False)
|
# We assume items are not on board.
|
||||||
board.Remove(existing)
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
item, err = Serializer.deserialize(item_data, board)
|
item, err = Serializer.deserialize(item_data, board)
|
||||||
if item:
|
if item:
|
||||||
try:
|
try:
|
||||||
board.Add(item)
|
board.Add(item)
|
||||||
|
self.manager.log(f" Board.Add success")
|
||||||
|
|
||||||
new_uuid = get_item_uuid(item)
|
new_uuid = get_item_uuid(item)
|
||||||
if new_uuid:
|
if new_uuid:
|
||||||
found = any(e['uuid'] == new_uuid for e in new_on_board_items)
|
found = any(e['uuid'] == new_uuid for e in new_on_board_items)
|
||||||
if not found:
|
if not found:
|
||||||
new_on_board_items.append({'uuid': new_uuid, 'class': item.GetClass()})
|
new_on_board_items.append({'uuid': new_uuid, 'class': item.GetClass()})
|
||||||
restored_files.append(filename)
|
restored_files.append(filename)
|
||||||
|
else:
|
||||||
|
self.manager.log(f" Warning: Added item has no UUID")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.manager.log(f"Show: Add failed {uuid}: {e}")
|
self.manager.log(f" Board.Add failed: {e}")
|
||||||
|
else:
|
||||||
|
self.manager.log(f" Deserialize failed: {err}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.manager.log(f"Show: Error file {filename}: {e}")
|
self.manager.log(f" Error processing file: {e}")
|
||||||
|
self.manager.log(traceback.format_exc())
|
||||||
|
|
||||||
self.on_board_items = new_on_board_items
|
self.on_board_items = new_on_board_items
|
||||||
for f in restored_files:
|
|
||||||
if f in self.stored_items: self.stored_items.remove(f)
|
# Rebuild stored_items to only contain those that failed to restore
|
||||||
|
failed_files = [f for f in self.stored_items if f not in restored_files]
|
||||||
|
self.stored_items = failed_files
|
||||||
|
|
||||||
self.visible = True
|
self.visible = True
|
||||||
self.manager.is_restoring = False
|
self.manager.is_restoring = False
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
self.manager.log("Show: Complete")
|
self.manager.log(f"Show: Complete. Restored: {len(restored_files)}, Failed/Remaining: {len(self.stored_items)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.manager.log(f"Show: Critical Error: {e}")
|
self.manager.log(f"Show: Critical Error: {e}")
|
||||||
|
self.manager.log(traceback.format_exc())
|
||||||
self.manager.is_restoring = False
|
self.manager.is_restoring = False
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def delete_content(self, delete_items_from_board):
|
def delete_content(self, delete_items_from_board):
|
||||||
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
|
try:
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
if self.visible:
|
if self.visible:
|
||||||
if delete_items_from_board:
|
if delete_items_from_board:
|
||||||
|
for entry in self.on_board_items:
|
||||||
|
try:
|
||||||
|
item = find_item_by_uuid(board, entry['uuid'])
|
||||||
|
if item: self._safe_deselect(item)
|
||||||
|
except: pass
|
||||||
|
|
||||||
self._clear_selection_safe()
|
self._clear_selection_safe()
|
||||||
|
|
||||||
for entry in self.on_board_items:
|
for entry in self.on_board_items:
|
||||||
item = find_item_by_uuid(board, entry['uuid'])
|
item = find_item_by_uuid(board, entry['uuid'])
|
||||||
if item:
|
if item:
|
||||||
|
|
@ -161,6 +213,8 @@ class Layer:
|
||||||
self.manager.is_restoring = False
|
self.manager.is_restoring = False
|
||||||
self.stored_items = []
|
self.stored_items = []
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def clear_items(self):
|
def clear_items(self):
|
||||||
self.delete_content(delete_items_from_board=True)
|
self.delete_content(delete_items_from_board=True)
|
||||||
|
|
@ -180,6 +234,8 @@ class Layer:
|
||||||
return l
|
return l
|
||||||
|
|
||||||
def add_from_text(self, text):
|
def add_from_text(self, text):
|
||||||
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
|
try:
|
||||||
uuids = set(re.findall(r'\(uuid\s+"([^"]+)"\)', text))
|
uuids = set(re.findall(r'\(uuid\s+"([^"]+)"\)', text))
|
||||||
if not uuids: return 0, "No UUIDs found."
|
if not uuids: return 0, "No UUIDs found."
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
|
|
@ -193,6 +249,8 @@ class Layer:
|
||||||
except: pass
|
except: pass
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
return count, f"Added {count} items."
|
return count, f"Added {count} items."
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def _get_selection_candidates(self, board):
|
def _get_selection_candidates(self, board):
|
||||||
candidates = []
|
candidates = []
|
||||||
|
|
@ -202,7 +260,6 @@ class Layer:
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
if not candidates:
|
if not candidates:
|
||||||
# Fallback scan
|
|
||||||
for lst in [board.GetTracks(), board.GetFootprints(), board.GetDrawings(), board.Zones(), board.Groups()]:
|
for lst in [board.GetTracks(), board.GetFootprints(), board.GetDrawings(), board.Zones(), board.Groups()]:
|
||||||
try:
|
try:
|
||||||
for item in lst:
|
for item in lst:
|
||||||
|
|
@ -211,6 +268,8 @@ class Layer:
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
def add_selection(self):
|
def add_selection(self):
|
||||||
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
|
try:
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
candidates = self._get_selection_candidates(board)
|
candidates = self._get_selection_candidates(board)
|
||||||
items_to_add = {}
|
items_to_add = {}
|
||||||
|
|
@ -234,8 +293,12 @@ class Layer:
|
||||||
if self.add_item(item): count += 1
|
if self.add_item(item): count += 1
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
return count, f"Added {count} items."
|
return count, f"Added {count} items."
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def remove_selection(self):
|
def remove_selection(self):
|
||||||
|
pcbnew.Refresh() # Brush teeth (Start)
|
||||||
|
try:
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
candidates = self._get_selection_candidates(board)
|
candidates = self._get_selection_candidates(board)
|
||||||
uuids_to_remove = set()
|
uuids_to_remove = set()
|
||||||
|
|
@ -259,6 +322,8 @@ class Layer:
|
||||||
removed = initial_count - len(self.on_board_items)
|
removed = initial_count - len(self.on_board_items)
|
||||||
self.manager.save()
|
self.manager.save()
|
||||||
return removed, f"Removed {removed} items."
|
return removed, f"Removed {removed} items."
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # Brush teeth (End)
|
||||||
|
|
||||||
def inspect(self):
|
def inspect(self):
|
||||||
return f"Layer: {self.name}\nVisible: {self.visible}\nOn Board: {len(self.on_board_items)}\nStored: {len(self.stored_items)}"
|
return f"Layer: {self.name}\nVisible: {self.visible}\nOn Board: {len(self.on_board_items)}\nStored: {len(self.stored_items)}"
|
||||||
|
|
@ -13,14 +13,37 @@ class LayerListener(pcbnew.BOARD_LISTENER):
|
||||||
self.mgr = mgr
|
self.mgr = mgr
|
||||||
|
|
||||||
def OnBoardItemAdded(self, item):
|
def OnBoardItemAdded(self, item):
|
||||||
|
# 1. Record State (The "Wake Up Tape")
|
||||||
|
# Capture UUID before any refresh invalidates the item pointer or tool state.
|
||||||
|
target_uuid = get_item_uuid(item)
|
||||||
|
if not target_uuid: return
|
||||||
|
|
||||||
|
# 2. Brush Teeth (Start) - Sync with reality
|
||||||
|
# This ensures we are facing the actual playing field.
|
||||||
|
pcbnew.Refresh()
|
||||||
|
|
||||||
|
try:
|
||||||
if self.mgr.is_restoring: return
|
if self.mgr.is_restoring: return
|
||||||
|
|
||||||
|
# 3. Re-acquire State (Watch the tape)
|
||||||
|
# The 'item' pointer passed by the event might be unsafe now.
|
||||||
|
# We look up the item freshly from the board using the recorded UUID.
|
||||||
|
board = pcbnew.GetBoard()
|
||||||
|
fresh_item = find_item_by_uuid(board, target_uuid)
|
||||||
|
|
||||||
|
if not fresh_item: return
|
||||||
|
|
||||||
|
# 4. Perform Action
|
||||||
if self.mgr.active_layer_index is not None:
|
if self.mgr.active_layer_index is not None:
|
||||||
try:
|
try:
|
||||||
layer = self.mgr.layers[self.mgr.active_layer_index]
|
layer = self.mgr.layers[self.mgr.active_layer_index]
|
||||||
if layer.visible:
|
if layer.visible:
|
||||||
trackable = resolve_to_trackable_item(item)
|
trackable = resolve_to_trackable_item(fresh_item)
|
||||||
if trackable: layer.add_item(trackable)
|
if trackable: layer.add_item(trackable)
|
||||||
except: pass
|
except: pass
|
||||||
|
finally:
|
||||||
|
# 5. Brush Teeth (End) - Finalize
|
||||||
|
pcbnew.Refresh()
|
||||||
|
|
||||||
class LayerManager:
|
class LayerManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -108,6 +131,8 @@ class LayerManager:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def analyze_clipboard_text(self, text):
|
def analyze_clipboard_text(self, text):
|
||||||
|
pcbnew.Refresh() # Start
|
||||||
|
try:
|
||||||
self.log(f"Analyze: Input text length: {len(text)}")
|
self.log(f"Analyze: Input text length: {len(text)}")
|
||||||
uuids = set(re.findall(r'\(uuid\s+"([^"]+)"\)', text))
|
uuids = set(re.findall(r'\(uuid\s+"([^"]+)"\)', text))
|
||||||
if not uuids: return self.get_log_text()
|
if not uuids: return self.get_log_text()
|
||||||
|
|
@ -124,8 +149,12 @@ class LayerManager:
|
||||||
except: pass
|
except: pass
|
||||||
self.log(f"Analyze: Total Matches on Board: {matched}")
|
self.log(f"Analyze: Total Matches on Board: {matched}")
|
||||||
return self.get_log_text()
|
return self.get_log_text()
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # End
|
||||||
|
|
||||||
def recover_orphans(self):
|
def recover_orphans(self):
|
||||||
|
pcbnew.Refresh() # Start
|
||||||
|
try:
|
||||||
if not self.storage.setup_paths(): return 0, "Storage not ready"
|
if not self.storage.setup_paths(): return 0, "Storage not ready"
|
||||||
tracked_uuids = set()
|
tracked_uuids = set()
|
||||||
stored_files = set()
|
stored_files = set()
|
||||||
|
|
@ -149,8 +178,12 @@ class LayerManager:
|
||||||
rec_layer.visible = False
|
rec_layer.visible = False
|
||||||
self.save()
|
self.save()
|
||||||
return len(orphans), f"Recovered {len(orphans)} items."
|
return len(orphans), f"Recovered {len(orphans)} items."
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # End
|
||||||
|
|
||||||
def hard_refresh(self):
|
def hard_refresh(self):
|
||||||
|
pcbnew.Refresh() # Start
|
||||||
|
try:
|
||||||
self.log("Hard Refresh: Starting...")
|
self.log("Hard Refresh: Starting...")
|
||||||
board = pcbnew.GetBoard()
|
board = pcbnew.GetBoard()
|
||||||
for layer in self.layers:
|
for layer in self.layers:
|
||||||
|
|
@ -177,5 +210,7 @@ class LayerManager:
|
||||||
except: pass
|
except: pass
|
||||||
self.save()
|
self.save()
|
||||||
self.log("Hard Refresh: Complete")
|
self.log("Hard Refresh: Complete")
|
||||||
|
finally:
|
||||||
|
pcbnew.Refresh() # End
|
||||||
|
|
||||||
manager = LayerManager()
|
manager = LayerManager()
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# backend/serializer.py
|
# backend/serializer.py
|
||||||
import pcbnew
|
import pcbnew
|
||||||
|
import traceback
|
||||||
|
|
||||||
class Serializer:
|
class Serializer:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -64,17 +65,56 @@ class Serializer:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None, f"Constructor failed for {cls_name}: {e}"
|
return None, f"Constructor failed for {cls_name}: {e}"
|
||||||
|
|
||||||
# Deserialize with KiCad version compatibility
|
# Ensure sexpr is a string
|
||||||
# KiCad 9+ requires (data, source_name), older versions take (data)
|
if not isinstance(sexpr, str):
|
||||||
|
sexpr = str(sexpr)
|
||||||
|
|
||||||
|
# DEBUG LOGGING
|
||||||
|
print(f"[DEBUG] Deserializing {cls_name}")
|
||||||
|
print(f"[DEBUG] Sexpr (first 50 chars): {sexpr[:50]}")
|
||||||
|
|
||||||
|
lr = None
|
||||||
try:
|
try:
|
||||||
|
# Primary method: KiCad 9+
|
||||||
lr = pcbnew.STRING_LINE_READER(sexpr, "restore")
|
lr = pcbnew.STRING_LINE_READER(sexpr, "restore")
|
||||||
except:
|
except TypeError:
|
||||||
|
# Fallback: Older KiCad versions (1 arg)
|
||||||
try:
|
try:
|
||||||
lr = pcbnew.STRING_LINE_READER(sexpr)
|
lr = pcbnew.STRING_LINE_READER(sexpr)
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"STRING_LINE_READER init failed (Fallback): {e}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None, f"STRING_LINE_READER init failed: {e}"
|
return None, f"STRING_LINE_READER init failed: {e}"
|
||||||
|
|
||||||
|
if lr is None:
|
||||||
|
return None, "STRING_LINE_READER is None"
|
||||||
|
|
||||||
|
print(f"[DEBUG] LineReader created: {lr}")
|
||||||
|
|
||||||
|
try:
|
||||||
item.Deserialize(lr)
|
item.Deserialize(lr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DEBUG] Deserialize Exception: {e}")
|
||||||
|
|
||||||
|
# Check for the specific Protobuf overload mismatch
|
||||||
|
if "google::protobuf::Any" in str(e):
|
||||||
|
print("[DEBUG] Protobuf overload mismatch detected. Attempting PCB_IO_KICAD_SEXPR Parse fallback...")
|
||||||
|
try:
|
||||||
|
# Hail Mary: Try using the IO plugin directly to parse the string
|
||||||
|
# This bypasses the ambiguous Deserialize overload on the item itself
|
||||||
|
plugin = pcbnew.PCB_IO_KICAD_SEXPR()
|
||||||
|
if hasattr(plugin, 'Parse'):
|
||||||
|
# Parse usually returns a new item
|
||||||
|
parsed_item = plugin.Parse(sexpr)
|
||||||
|
if parsed_item:
|
||||||
|
print("[DEBUG] Fallback Parse successful!")
|
||||||
|
return parsed_item, None
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"[DEBUG] Fallback Parse failed: {ex}")
|
||||||
|
|
||||||
|
# If fallback didn't work or wasn't applicable, re-raise
|
||||||
|
raise e
|
||||||
|
|
||||||
return item, None
|
return item, None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None, f"Deserialization error for {cls_name}: {e}"
|
return None, f"Deserialization error for {cls_name}: {e}"
|
||||||
Loading…
Reference in New Issue