Download src/Mod/CAM/Path/Tool/library/ui/editor.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 27 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/library/ui/editor.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Tool/library/ui/editor.py
-
curl -L -o editor.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/library/ui/editor.py
27 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> * | |
| # * 2020 Schildkroet * | |
| # * 2025 Samuel Abels <knipknap@gmail.com> * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * This program is distributed in the hope that it will be useful, * | |
| # * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| # * GNU Library General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with this program; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| import yaml | |
| import pathlib | |
| import FreeCAD | |
| import FreeCADGui | |
| import Path | |
| from PySide.QtGui import ( | |
| QStandardItem, | |
| QStandardItemModel, | |
| QPixmap, | |
| QDialog, | |
| QMessageBox, | |
| QWidget, | |
| ) | |
| from PySide.QtCore import Qt, QEvent | |
| from typing import List, cast, Tuple, Optional | |
| from ...assets import AssetUri | |
| from ...assets.ui import AssetOpenDialog, AssetSaveDialog | |
| from ...camassets import cam_assets, ensure_assets_initialized | |
| from ...shape.ui.shapeselector import ShapeSelector | |
| from ...toolbit import ToolBit | |
| from ...toolbit.serializers import all_serializers as toolbit_serializers | |
| from ...toolbit.ui import ToolBitEditor | |
| from ...toolbit.ui.toollist import ToolBitUriListMimeType | |
| from ...toolbit.ui.util import natural_sort_key | |
| from ...toolbit.util import setToolBitSchema | |
| from ..serializers import all_serializers as library_serializers | |
| from ..models import Library | |
| from .browser import LibraryBrowserWidget | |
| from .properties import LibraryPropertyDialog | |
| if False: | |
| Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) | |
| Path.Log.trackModule(Path.Log.thisModule()) | |
| else: | |
| Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) | |
| _LibraryRole = Qt.UserRole + 1 | |
| translate = FreeCAD.Qt.translate | |
| class LibraryEditor(QWidget): | |
| """LibraryEditor is the controller for | |
| displaying/selecting/creating/editing a collection of ToolBits.""" | |
| def __init__(self, parent=None): | |
| super().__init__(parent=parent) | |
| Path.Log.track() | |
| ensure_assets_initialized(cam_assets) | |
| self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitLibraryEdit.ui") | |
| self.form.installEventFilter(self) # to forward keypress events | |
| self._base_title = self.form.windowTitle() | |
| # Create the library list. | |
| self.listModel = QStandardItemModel() | |
| self.form.TableList.setModel(self.listModel) | |
| self.form.TableList.clicked.connect(self._on_library_selected) | |
| # Enable drop support for the library list | |
| self.form.TableList.viewport().installEventFilter(self) # Also on viewport | |
| # Create the LibraryBrowserWidget | |
| self.browser = LibraryBrowserWidget( | |
| asset_manager=cam_assets, | |
| parent=self, | |
| ) | |
| self.browser.setDragEnabled(True) | |
| self.form.verticalLayout_2.layout().replaceWidget(self.form.toolTable, self.browser) | |
| self.form.toolTable.hide() | |
| # Connect signals. | |
| self.browser.itemDoubleClicked.connect(self.browser._on_edit_requested) | |
| self.form.addLibraryButton.clicked.connect(self._on_add_library_requested) | |
| self.form.removeLibraryButton.clicked.connect(self._on_remove_library_requested) | |
| self.form.renameLibraryButton.clicked.connect(self._on_rename_library_requested) | |
| self.form.importLibraryButton.clicked.connect(self._on_import_library_requested) | |
| self.form.exportLibraryButton.clicked.connect(self._on_export_library_requested) | |
| self.form.addToolBitButton.clicked.connect(self._on_add_toolbit_requested) | |
| self.form.importToolBitButton.clicked.connect(self._on_import_toolbit_requested) | |
| self.form.exportToolBitButton.clicked.connect(self._on_export_toolbit_requested) | |
| # Populate the UI. | |
| self._refresh_library_list() | |
| self._select_last_library() | |
| self._update_button_states() | |
| def _highlight_row(self, index): | |
| """Highlights the row at the given index using the selection model.""" | |
| if not index.isValid(): | |
| return | |
| self.form.TableList.setCurrentIndex(index) | |
| def _clear_highlight(self): | |
| """Clears the highlighting from the previously highlighted row.""" | |
| self.form.TableList.selectionModel().clear() | |
| def eventFilter(self, obj, event): | |
| if event.type() == QEvent.KeyPress and self.form.TableList.hasFocus(): | |
| if event.key() == Qt.Key_F2: | |
| Path.Log.debug("F2 pressed on library list.") | |
| self._on_rename_library_requested() | |
| return True | |
| elif event.key() == Qt.Key_Delete: | |
| Path.Log.debug("Del pressed on library list.") | |
| self._on_remove_library_requested() | |
| return True | |
| if obj == self.form.TableList.viewport(): | |
| if event.type() == QEvent.DragEnter or event.type() == QEvent.DragMove: | |
| return self._handle_drag_enter(event) | |
| elif event.type() == QEvent.DragLeave: | |
| self._handle_drag_leave(event) | |
| return True | |
| elif event.type() == QEvent.Drop: | |
| return self._handle_drop(event) | |
| return super().eventFilter(obj, event) | |
| def _handle_drag_enter(self, event): | |
| """Handle drag enter and move events for the library list.""" | |
| mime_data = event.mimeData() | |
| Path.Log.debug(f"_handle_drag_enter: MIME formats: {mime_data.formats()}") | |
| if not mime_data.hasFormat(ToolBitUriListMimeType): | |
| Path.Log.debug("_handle_drag_enter: Invalid MIME type, ignoring") | |
| return True | |
| # Get the row being hovered. | |
| pos = event.pos() | |
| event.acceptProposedAction() | |
| index = self.form.TableList.indexAt(pos) | |
| if not index.isValid(): | |
| self._clear_highlight() | |
| return True | |
| # Prevent drop into "All Toolbits" | |
| item = self.listModel.itemFromIndex(index) | |
| if not item or item.data(_LibraryRole) == "all_tools": | |
| self._clear_highlight() | |
| return True | |
| self._highlight_row(index) | |
| return True | |
| def _handle_drag_leave(self, event): | |
| """Handle drag leave event for the library list.""" | |
| self._clear_highlight() | |
| def _handle_drop(self, event): | |
| """Handle drop events to move or copy toolbits to the target library.""" | |
| mime_data = event.mimeData() | |
| if not (mime_data.hasFormat(ToolBitUriListMimeType)): | |
| event.ignore() | |
| return True | |
| self._clear_highlight() | |
| pos = event.pos() | |
| index = self.form.TableList.indexAt(pos) | |
| if not index.isValid(): | |
| event.ignore() | |
| return True | |
| item = self.listModel.itemFromIndex(index) | |
| if not item or item.data(_LibraryRole) == "all_tools": | |
| event.ignore() | |
| return True | |
| target_library_id = item.data(_LibraryRole) | |
| target_library_uri = f"toolbitlibrary://{target_library_id}" | |
| target_library = cast(Library, cam_assets.get(target_library_uri, depth=1)) | |
| try: | |
| clipboard_content_yaml = mime_data.data(ToolBitUriListMimeType).data().decode("utf-8") | |
| clipboard_data_dict = yaml.safe_load(clipboard_content_yaml) | |
| if not isinstance(clipboard_data_dict, dict) or "toolbits" not in clipboard_data_dict: | |
| event.ignore() | |
| return True | |
| uris = clipboard_data_dict["toolbits"] | |
| new_uris = set() | |
| # Get the current library from the browser | |
| current_library = self.browser.get_current_library() | |
| for uri in uris: | |
| try: | |
| toolbit = cast(ToolBit, cam_assets.get(AssetUri(uri), depth=0)) | |
| if toolbit: | |
| added_toolbit = target_library.add_bit(toolbit) | |
| if added_toolbit: | |
| new_uris.add(str(toolbit.get_uri())) | |
| # Remove the toolbit from the current library if it exists and | |
| # it's not "all_tools" | |
| if current_library and current_library.get_id() != "all_tools": | |
| current_library.remove_bit(toolbit) | |
| except Exception as e: | |
| Path.Log.error(f"Failed to load toolbit from URI {uri}: {e}") | |
| continue | |
| if new_uris: | |
| cam_assets.add(target_library) | |
| # Save the current library if it was modified | |
| if current_library and current_library.get_id() != "all_tools": | |
| cam_assets.add(current_library) | |
| self.browser.refresh() | |
| self.browser.select_by_uri(list(new_uris)) | |
| self._update_button_states() | |
| event.acceptProposedAction() | |
| except Exception as e: | |
| Path.Log.error(f"Failed to process drop event: {e}") | |
| event.ignore() | |
| return True | |
| def get_selected_library_id(self) -> Optional[str]: | |
| index = self.form.TableList.currentIndex() | |
| if not index.isValid(): | |
| return None | |
| item = self.listModel.itemFromIndex(index) | |
| if not item: | |
| return None | |
| return item.data(_LibraryRole) | |
| def get_selected_library(self, depth=1) -> Optional[Library]: | |
| library_id = self.get_selected_library_id() | |
| if not library_id: | |
| return None | |
| uri = f"toolbitlibrary://{library_id}" | |
| return cast(Library, cam_assets.get(uri, depth=depth)) | |
| def select_library_by_uri(self, uri: AssetUri): | |
| # Find it in the list. | |
| index = 0 | |
| for i in range(self.listModel.rowCount()): | |
| item = self.listModel.item(i) | |
| if item and item.data(_LibraryRole) == uri.asset_id: | |
| index = i | |
| break | |
| else: | |
| return | |
| # Select it. | |
| if index <= self.listModel.rowCount(): | |
| item = self.listModel.item(index) | |
| if item: # Should always be true, but... | |
| self.form.TableList.setCurrentIndex(self.listModel.index(index, 0)) | |
| self._on_library_selected() | |
| def _select_last_library(self): | |
| # Find the last used library. | |
| last_used_lib_identifier = Path.Preferences.getLastToolLibrary() | |
| if last_used_lib_identifier: | |
| uri = Library.resolve_name(last_used_lib_identifier) | |
| self.select_library_by_uri(uri) | |
| def open(self): | |
| Path.Log.track() | |
| return self.form.exec_() | |
| def _refresh_library_list(self): | |
| """Clears and repopulates the self.listModel with available libraries.""" | |
| Path.Log.track() | |
| self.listModel.clear() | |
| # Add "All Toolbits" item | |
| all_tools_item = QStandardItem(translate("CAM", "All Toolbits")) | |
| all_tools_item.setData("all_tools", _LibraryRole) | |
| # all_tools_item.setIcon(QPixmap(":/icons/CAM_ToolTable.svg")) | |
| # Make the "All Toolbits" item bold and italic | |
| font = all_tools_item.font() | |
| font.setBold(True) | |
| font.setItalic(True) | |
| all_tools_item.setFont(font) | |
| self.listModel.appendRow(all_tools_item) | |
| # Use AssetManager to fetch library assets (depth=0 for shallow fetch) | |
| try: | |
| # Fetch library assets themselves, not their deep dependencies (toolbits). | |
| # depth=0 means "fetch this asset, but not its dependencies" | |
| # The 'fetch' method returns actual Asset objects. | |
| libraries = cast(List[Library], cam_assets.fetch(asset_type="toolbitlibrary", depth=0)) | |
| except Exception as e: | |
| Path.Log.error(f"Failed to fetch toolbit libraries: {e}") | |
| return | |
| # Sort by label for consistent ordering, falling back to asset_id if label is missing | |
| for library in sorted( | |
| libraries, | |
| key=lambda library: natural_sort_key(library.label or library.get_id()), | |
| ): | |
| lib_uri_str = str(library.get_uri()) | |
| libItem = QStandardItem(library.label or library.get_id()) | |
| libItem.setToolTip(f"ID: {library.get_id()}\nURI: {lib_uri_str}") | |
| libItem.setData(library.get_id(), _LibraryRole) # Store the library ID | |
| libItem.setIcon(QPixmap(":/icons/CAM_ToolTable.svg")) | |
| self.listModel.appendRow(libItem) | |
| Path.Log.debug("model rows: {}".format(self.listModel.rowCount())) | |
| self.listModel.setHorizontalHeaderLabels(["Library"]) | |
| def _on_library_selected(self): | |
| """Sets the current library in the browser when a library is selected.""" | |
| Path.Log.debug("_on_library_selected: Called.") | |
| index = self.form.TableList.currentIndex() | |
| item = self.listModel.itemFromIndex(index) | |
| if not item: | |
| return | |
| if item.data(_LibraryRole) == "all_tools": | |
| selected_library = None | |
| else: | |
| selected_library = self.get_selected_library() | |
| self.browser.set_current_library(selected_library) | |
| self._update_window_title() | |
| self._update_button_states() | |
| def _update_window_title(self): | |
| """Updates the window title with the current library name.""" | |
| current_library = self.browser.get_current_library() | |
| if current_library: | |
| title = f"{self._base_title} - {current_library.label}" | |
| else: | |
| title = self._base_title | |
| self.form.setWindowTitle(title) | |
| def _update_button_states(self): | |
| """Updates the enabled state of library management buttons.""" | |
| library_selected = self.browser.get_current_library() is not None | |
| self.form.addLibraryButton.setEnabled(True) | |
| self.form.removeLibraryButton.setEnabled(library_selected) | |
| self.form.renameLibraryButton.setEnabled(library_selected) | |
| self.form.exportLibraryButton.setEnabled(library_selected) | |
| self.form.importLibraryButton.setEnabled(True) | |
| self.form.addToolBitButton.setEnabled( | |
| True | |
| ) # Always enabled - can create standalone toolbits | |
| # TODO: self.form.exportToolBitButton.setEnabled(toolbit_selected) | |
| def _save_library(self): | |
| """Internal method to save the current tool library asset""" | |
| Path.Log.track() | |
| library = self.browser.get_current_library() | |
| if not library: | |
| return | |
| # Save the modified library asset. | |
| try: | |
| cam_assets.add(library) | |
| Path.Log.debug(f"Library {library.get_uri()} saved") | |
| except Exception as e: | |
| Path.Log.error(f"Failed to save library {library.get_uri()}: {e}") | |
| QMessageBox.critical( | |
| self.form, | |
| translate("CAM_ToolBit", "Error Saving Library"), | |
| str(e), | |
| ) | |
| raise | |
| def _on_add_library_requested(self): | |
| Path.Log.debug("_on_add_library_requested: Called.") | |
| new_library = Library(FreeCAD.Qt.translate("CAM", "New Library")) | |
| dialog = LibraryPropertyDialog(new_library, new=True, parent=self) | |
| if dialog.exec_() != QDialog.Accepted: | |
| return | |
| uri = cam_assets.add(new_library) | |
| Path.Log.debug(f"_on_add_library_requested: New library URI = {uri}") | |
| self._refresh_library_list() | |
| self.select_library_by_uri(uri) | |
| self._update_button_states() | |
| def _on_remove_library_requested(self): | |
| """Handles request to remove the selected library.""" | |
| Path.Log.debug("_on_remove_library_requested: Called.") | |
| current_library = self.browser.get_current_library() | |
| if not current_library: | |
| return | |
| reply = QMessageBox.question( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Confirm Library Removal"), | |
| FreeCAD.Qt.translate( | |
| "CAM", | |
| "Are you sure you want to remove the library '{0}'?\n" | |
| "This will not delete the toolbits contained within it.", | |
| ).format(current_library.label), | |
| QMessageBox.Yes | QMessageBox.No, | |
| QMessageBox.No, | |
| ) | |
| if reply != QMessageBox.Yes: | |
| return | |
| try: | |
| library_uri = current_library.get_uri() | |
| cam_assets.delete(library_uri) | |
| Path.Log.info(f"Library {current_library.label} deleted.") | |
| self._refresh_library_list() | |
| self.browser.refresh() | |
| self._update_button_states() | |
| except FileNotFoundError as e: | |
| Path.Log.error(f"Failed to delete library {current_library.label}: {e}") | |
| QMessageBox.critical( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Error"), | |
| FreeCAD.Qt.translate("CAM", "Failed to delete library '{0}': {1}").format( | |
| current_library.label, str(e) | |
| ), | |
| ) | |
| def _on_rename_library_requested(self): | |
| """Handles request to rename the selected library.""" | |
| Path.Log.debug("_on_rename_library_requested: Called.") | |
| current_library = self.browser.get_current_library() | |
| if not current_library: | |
| return | |
| dialog = LibraryPropertyDialog(current_library, new=False, parent=self) | |
| if dialog.exec_() != QDialog.Accepted: | |
| return | |
| cam_assets.add(current_library) | |
| self._refresh_library_list() | |
| self._update_button_states() | |
| def _on_import_library_requested(self): | |
| """Handles request to import a library.""" | |
| Path.Log.debug("_on_import_library_requested: Called.") | |
| dialog = AssetOpenDialog( | |
| cam_assets, asset_class=Library, serializers=library_serializers, parent=self | |
| ) | |
| response = dialog.exec_() | |
| if not response: | |
| return | |
| file_path, library = cast(Tuple[pathlib.Path, Library], response) | |
| try: | |
| cam_assets.add(library) | |
| self._refresh_library_list() | |
| self._update_button_states() | |
| except Exception as e: | |
| Path.Log.error(f"Failed to import library: {file_path} {e}") | |
| QMessageBox.critical( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Error"), | |
| FreeCAD.Qt.translate("CAM", f"Failed to import library: {file_path} {e}"), | |
| ) | |
| def _on_export_library_requested(self): | |
| """Handles request to export the selected library.""" | |
| Path.Log.debug("_on_export_library_requested: Called.") | |
| current_library = self.browser.get_current_library() | |
| if not current_library: | |
| return | |
| dialog = AssetSaveDialog(asset_class=Library, serializers=library_serializers, parent=self) | |
| dialog.exec_(current_library) | |
| self._update_button_states() | |
| def _on_add_toolbit_requested(self): | |
| """Handles request to add a new toolbit to the current library or create standalone.""" | |
| Path.Log.debug("_on_add_toolbit_requested: Called.") | |
| current_library = self.browser.get_current_library() | |
| # Select the shape for the new toolbit | |
| selector = ShapeSelector() | |
| shape = selector.show() | |
| if shape is None: # user canceled | |
| return | |
| try: | |
| # Find the appropriate ToolBit subclass based on the shape name | |
| tool_bit_classes = {b.SHAPE_CLASS.name: b for b in ToolBit.__subclasses__()} | |
| tool_bit_class = tool_bit_classes.get(shape.name) | |
| if not tool_bit_class: | |
| raise ValueError(f"No ToolBit subclass found for shape '{shape.name}'") | |
| # Create a new ToolBit instance | |
| new_toolbit = tool_bit_class(shape) | |
| new_toolbit.label = FreeCAD.Qt.translate("CAM", "New Toolbit") | |
| # Save the individual toolbit asset first | |
| tool_asset_uri = cam_assets.add(new_toolbit) | |
| Path.Log.debug(f"_on_add_toolbit_requested: Saved tool with URI: {tool_asset_uri}") | |
| # Add the toolbit to the current library if one is selected | |
| if current_library: | |
| toolno = current_library.add_bit(new_toolbit) | |
| Path.Log.debug( | |
| f"_on_add_toolbit_requested: Added toolbit {new_toolbit.get_id()} (URI: {new_toolbit.get_uri()}) " | |
| f"to current_library with number {toolno}." | |
| ) | |
| # Save the library | |
| cam_assets.add(current_library) | |
| else: | |
| Path.Log.debug( | |
| f"_on_add_toolbit_requested: Created standalone toolbit {new_toolbit.get_id()} (URI: {new_toolbit.get_uri()})" | |
| ) | |
| except Exception as e: | |
| Path.Log.error(f"Failed to create or add new toolbit: {e}") | |
| QMessageBox.critical( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Error Creating Toolbit"), | |
| str(e), | |
| ) | |
| raise | |
| setToolBitSchema() # Ensure correct schema is set for the new toolbit | |
| self.browser.refresh() | |
| self.browser.select_by_uri([str(new_toolbit.get_uri())]) | |
| self._update_button_states() | |
| def _on_import_toolbit_requested(self): | |
| """Handles request to import a toolbit.""" | |
| Path.Log.debug("_on_import_toolbit_requested: Called.") | |
| current_library = self.browser.get_current_library() | |
| if not current_library: | |
| Path.Log.warning("Cannot import toolbit: No library selected.") | |
| QMessageBox.warning( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Warning"), | |
| FreeCAD.Qt.translate("CAM", "Please select a library first."), | |
| ) | |
| return | |
| dialog = AssetOpenDialog( | |
| cam_assets, asset_class=ToolBit, serializers=toolbit_serializers, parent=self | |
| ) | |
| response = dialog.exec_() | |
| if not response: | |
| return | |
| file_path, toolbit = cast(Tuple[pathlib.Path, ToolBit], response) | |
| # Debug logging for imported toolbit | |
| Path.Log.info( | |
| f"IMPORT TOOLBIT: file_path={file_path}, toolbit.id={toolbit.id}, toolbit.label={toolbit.label}" | |
| ) | |
| import traceback | |
| stack = traceback.format_stack() | |
| caller_info = "".join(stack[-3:-1]) | |
| Path.Log.info(f"IMPORT TOOLBIT CALLER:\n{caller_info}") | |
| # Check if toolbit already exists in asset manager | |
| toolbit_uri = toolbit.get_uri() | |
| Path.Log.info(f"IMPORT CHECK: toolbit_uri={toolbit_uri}") | |
| existing_toolbit = None | |
| try: | |
| existing_toolbit = cam_assets.get(toolbit_uri, store=["local", "builtin"], depth=0) | |
| Path.Log.info( | |
| f"IMPORT CHECK: Toolbit {toolbit.id} already exists, using existing reference" | |
| ) | |
| Path.Log.info( | |
| f"IMPORT CHECK: existing_toolbit.id={existing_toolbit.id}, existing_toolbit.label={existing_toolbit.label}" | |
| ) | |
| except FileNotFoundError: | |
| # Toolbit doesn't exist, save it as new | |
| Path.Log.info(f"IMPORT CHECK: Toolbit {toolbit.id} is new, saving to disk") | |
| new_uri = cam_assets.add(toolbit) | |
| Path.Log.info(f"IMPORT CHECK: Toolbit saved with new URI: {new_uri}") | |
| existing_toolbit = toolbit | |
| # Add the toolbit (existing or new) to the current library | |
| Path.Log.info( | |
| f"IMPORT ADD: Adding toolbit {existing_toolbit.id} to library {current_library.label}" | |
| ) | |
| added_toolbit = current_library.add_bit(existing_toolbit) | |
| if added_toolbit: | |
| Path.Log.info(f"IMPORT ADD: Successfully added toolbit to library") | |
| cam_assets.add(current_library) # Save the modified library | |
| self.browser.refresh() | |
| self.browser.select_by_uri([str(existing_toolbit.get_uri())]) | |
| self._update_button_states() | |
| else: | |
| Path.Log.warning(f"IMPORT ADD: Failed to add toolbit {existing_toolbit.id} to library") | |
| Path.Log.warning( | |
| f"IMPORT FAILED: Failed to import toolbit from {file_path} to library {current_library.label}." | |
| ) | |
| QMessageBox.warning( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Warning"), | |
| FreeCAD.Qt.translate( | |
| "CAM", | |
| f"Failed to import toolbit from '{file_path}' to library '{current_library.label}'.", | |
| ), | |
| ) | |
| def _on_export_toolbit_requested(self): | |
| """Handles request to export the selected toolbit.""" | |
| Path.Log.debug("_on_export_toolbit_requested: Called.") | |
| selected_toolbits = self.browser.get_selected_bits() | |
| if not selected_toolbits: | |
| Path.Log.warning("Cannot export toolbit: No toolbit selected.") | |
| QMessageBox.warning( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Warning"), | |
| FreeCAD.Qt.translate("CAM", "Please select a toolbit to export."), | |
| ) | |
| return | |
| if len(selected_toolbits) > 1: | |
| Path.Log.warning("Cannot export multiple toolbits: Please select only one.") | |
| QMessageBox.warning( | |
| self, | |
| FreeCAD.Qt.translate("CAM", "Warning"), | |
| FreeCAD.Qt.translate("CAM", "Please select only one toolbit to export."), | |
| ) | |
| return | |
| toolbit_to_export = selected_toolbits[0] | |
| dialog = AssetSaveDialog(asset_class=ToolBit, serializers=toolbit_serializers, parent=self) | |
| dialog.exec_(toolbit_to_export) # This will open the save dialog and handle the export | |
| self._update_button_states() | |