Download src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 18.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py
-
curl -L -o gui_annotationstyleeditor.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py
18.4 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2020 Yorik van Havre <yorik@uncreated.net> * | |
| # * * | |
| # * 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 * | |
| # * * | |
| # *************************************************************************** | |
| """Provides GUI tools to create and edit annotation styles.""" | |
| ## @package gui_annotationstyleeditor | |
| # \ingroup draftguitools | |
| # \brief Provides GUI tools to create and edit annotation styles. | |
| ## \addtogroup draftguitools | |
| # @{ | |
| import json | |
| import PySide.QtGui as QtGui | |
| import PySide.QtWidgets as QtWidgets | |
| from PySide.QtCore import QT_TRANSLATE_NOOP | |
| import FreeCAD as App | |
| import FreeCADGui as Gui | |
| from draftguitools import gui_base | |
| from draftutils import params | |
| from draftutils import utils | |
| from draftutils.translate import translate | |
| class AnnotationStyleEditor(gui_base.GuiCommandSimplest): | |
| """Annotation style editor for text and dimensions. | |
| It inherits `GuiCommandSimplest` to set up the document, | |
| `IsActive`, and other behavior. See this class for more information. | |
| Attributes | |
| ---------- | |
| doc: App::Document | |
| The active document when the command is used, so that the styles | |
| are saved to this document. | |
| styles: dict | |
| A dictionary with key-value pairs that define the new style. | |
| renamed: dict | |
| A dictionary that holds the name of the style that is renamed | |
| by the editor. | |
| form: PySide.QtWidgets.QDialog | |
| Holds the loaded interface from the `.ui` file. | |
| """ | |
| def __init__(self): | |
| super(AnnotationStyleEditor, self).__init__( | |
| name=translate("draft", "Annotation Style Editor") | |
| ) | |
| self.doc = None | |
| self.styles = {} | |
| self.renamed = {} | |
| self.current_style = None | |
| self.form = None | |
| def GetResources(self): | |
| """Set icon, menu and tooltip.""" | |
| return { | |
| "Pixmap": ":icons/Draft_Annotation_Style.svg", | |
| "MenuText": QT_TRANSLATE_NOOP("Draft_AnnotationStyleEditor", "Annotation Styles"), | |
| "ToolTip": QT_TRANSLATE_NOOP( | |
| "Draft_AnnotationStyleEditor", | |
| "Opens an editor to manage or create annotation styles", | |
| ), | |
| } | |
| def Activated(self): | |
| """Execute when the command is called. | |
| The document attribute is set here by the parent class. | |
| """ | |
| super(AnnotationStyleEditor, self).Activated() | |
| # reset rename table and current style | |
| self.renamed = {} | |
| self.current_style = None | |
| # load dialog | |
| ui_file = ":/ui/dialog_AnnotationStyleEditor.ui" | |
| self.form = Gui.PySideUic.loadUi(ui_file) | |
| # restore stored size | |
| w = params.get_param("AnnotationStyleEditorWidth") | |
| h = params.get_param("AnnotationStyleEditorHeight") | |
| self.form.resize(w, h) | |
| # center the dialog over FreeCAD window | |
| mw = Gui.getMainWindow() | |
| self.form.move( | |
| mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() | |
| ) | |
| # set icons | |
| self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_Annotation_Style.svg")) | |
| self.form.pushButtonDelete.setIcon(QtGui.QIcon(":/icons/edit_Cancel.svg")) | |
| self.form.pushButtonRename.setIcon(QtGui.QIcon(":/icons/accessories-text-editor.svg")) | |
| self.form.pushButtonDelete.resize(self.form.pushButtonDelete.sizeHint()) | |
| self.form.pushButtonRename.resize(self.form.pushButtonRename.sizeHint()) | |
| self.form.pushButtonImport.setIcon(QtGui.QIcon(":/icons/Std_Import.svg")) | |
| self.form.pushButtonExport.setIcon(QtGui.QIcon(":/icons/Std_Export.svg")) | |
| # fill the styles combo | |
| self.styles = self.read_meta() | |
| for style in self.styles.keys(): | |
| self.form.comboBoxStyles.addItem(style) | |
| # connect signals/slots | |
| self.form.comboBoxStyles.currentIndexChanged.connect(self.on_style_changed) | |
| self.form.pushButtonDelete.clicked.connect(self.on_delete) | |
| self.form.pushButtonRename.clicked.connect(self.on_rename) | |
| self.form.pushButtonImport.clicked.connect(self.on_import) | |
| self.form.pushButtonExport.clicked.connect(self.on_export) | |
| self.form.buttonBox.accepted.connect(self.update_style) | |
| # show editor dialog | |
| self.fill_editor() | |
| result = self.form.exec_() | |
| # process if OK was clicked | |
| if result: | |
| self.save_meta(self.styles) | |
| # store dialog size | |
| params.set_param("AnnotationStyleEditorWidth", self.form.width()) | |
| params.set_param("AnnotationStyleEditorHeight", self.form.height()) | |
| def read_meta(self): | |
| """Read the document Meta attribute and return a dict.""" | |
| styles = {} | |
| meta = self.doc.Meta | |
| for key, value in meta.items(): | |
| if key.startswith("Draft_Style_"): | |
| styles[key[12:]] = utils.repair_annotation_style(json.loads(value)) | |
| return styles | |
| def save_meta(self, styles): | |
| """Save a dict to the document Meta attribute and update objects.""" | |
| # save meta | |
| changedstyles = [] | |
| meta = self.doc.Meta | |
| for key, value in styles.items(): | |
| try: | |
| strvalue = json.dumps(value) | |
| except Exception: | |
| print("debug: unable to serialize this:", value) | |
| if "Draft_Style_" + key in meta and meta["Draft_Style_" + key] != strvalue: | |
| changedstyles.append(key) | |
| meta["Draft_Style_" + key] = strvalue | |
| # remove deleted styles | |
| todelete = [] | |
| for key, value in meta.items(): | |
| if key.startswith("Draft_Style_"): | |
| if key[12:] not in styles: | |
| todelete.append(key) | |
| for key in todelete: | |
| del meta[key] | |
| self.doc.Meta = meta | |
| # propagate changes to all annotations | |
| for obj in self.get_annotations(): | |
| vobj = obj.ViewObject | |
| try: | |
| current = vobj.AnnotationStyle | |
| except AssertionError: | |
| # empty annotation styles list | |
| pass | |
| else: | |
| if vobj.AnnotationStyle in self.renamed: | |
| # the style has been renamed | |
| # temporarily add the new style and switch to it | |
| vobj.AnnotationStyle = [ | |
| vobj.AnnotationStyle, | |
| self.renamed[vobj.AnnotationStyle], | |
| ] | |
| vobj.AnnotationStyle = self.renamed[vobj.AnnotationStyle] | |
| if vobj.AnnotationStyle in styles: | |
| if vobj.AnnotationStyle in changedstyles: | |
| # the style has changed | |
| for attr, value in styles[vobj.AnnotationStyle].items(): | |
| if hasattr(vobj, attr): | |
| try: | |
| if vobj.getTypeIdOfProperty(attr) == "App::PropertyColor": | |
| value = value | 0x000000FF | |
| setattr(vobj, attr, value) | |
| except TypeError: | |
| pass | |
| else: | |
| # the style has been removed | |
| vobj.AnnotationStyle = "" | |
| vobj.AnnotationStyle = [""] + list(styles) | |
| def on_style_changed(self, index): | |
| """Execute as a callback when the styles combobox changes.""" | |
| if index == 0: | |
| # nothing happens | |
| self.form.pushButtonDelete.setEnabled(False) | |
| self.form.pushButtonRename.setEnabled(False) | |
| elif index == 1: | |
| # Add new... entry | |
| reply = QtWidgets.QInputDialog.getText( | |
| None, translate("draft", "New Style"), translate("draft", "Style name") | |
| ) | |
| if reply[1]: | |
| # OK or Enter pressed | |
| name = reply[0].strip() | |
| if name == "": | |
| QtWidgets.QMessageBox.information( | |
| None, | |
| translate("draft", "Style name required"), | |
| translate("draft", "No style name specified"), | |
| ) | |
| self.form.comboBoxStyles.setCurrentIndex(0) | |
| elif name in self.styles: | |
| QtWidgets.QMessageBox.information( | |
| None, | |
| translate("draft", "Style exists"), | |
| translate("draft", "This style name already exists"), | |
| ) | |
| self.form.comboBoxStyles.setCurrentIndex(0) | |
| else: | |
| # create new style from current editor values | |
| self.styles[name] = self.get_editor_values() | |
| self.form.comboBoxStyles.addItem(name) | |
| self.form.comboBoxStyles.setCurrentIndex(self.form.comboBoxStyles.count() - 1) | |
| self.current_style = name | |
| else: | |
| # Cancel or Escape pressed | |
| self.form.comboBoxStyles.setCurrentIndex(0) | |
| else: | |
| # Existing style | |
| if self.current_style is not None: | |
| # save editor values to current style first | |
| self.styles[self.current_style] = self.get_editor_values() | |
| self.current_style = self.form.comboBoxStyles.itemText(index) | |
| self.fill_editor(self.current_style) | |
| self.form.pushButtonDelete.setEnabled(True) | |
| self.form.pushButtonRename.setEnabled(True) | |
| def on_delete(self): | |
| """Execute as a callback when the delete button is pressed.""" | |
| index = self.form.comboBoxStyles.currentIndex() | |
| style = self.form.comboBoxStyles.itemText(index) | |
| if self.get_style_users(style): | |
| reply = QtWidgets.QMessageBox.question( | |
| None, | |
| translate("draft", "Style in use"), | |
| translate("draft", "This style is used by some objects in this document. Proceed?"), | |
| QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, | |
| QtWidgets.QMessageBox.No, | |
| ) | |
| if reply == QtWidgets.QMessageBox.No: | |
| return | |
| del self.styles[style] | |
| # We need to reset self.current_style, which is the deleted style, | |
| # to stop on_style_changed from adding that style again: | |
| self.current_style = None | |
| self.form.comboBoxStyles.currentIndexChanged.disconnect(self.on_style_changed) | |
| self.form.comboBoxStyles.removeItem(index) | |
| if not self.styles: | |
| self.form.comboBoxStyles.setCurrentIndex(0) | |
| # Update the dialog and self.current_style: | |
| self.on_style_changed(self.form.comboBoxStyles.currentIndex()) | |
| self.form.comboBoxStyles.currentIndexChanged.connect(self.on_style_changed) | |
| def on_rename(self): | |
| """Execute as a callback when the rename button is pressed.""" | |
| index = self.form.comboBoxStyles.currentIndex() | |
| style = self.form.comboBoxStyles.itemText(index) | |
| reply = QtWidgets.QInputDialog.getText( | |
| None, | |
| translate("draft", "Rename Style"), | |
| translate("draft", "New name"), | |
| QtWidgets.QLineEdit.Normal, | |
| style, | |
| ) | |
| if reply[1]: | |
| # OK or Enter pressed | |
| newname = reply[0] | |
| if newname in self.styles: | |
| reply = QtWidgets.QMessageBox.information( | |
| None, | |
| translate("draft", "Style exists"), | |
| translate("draft", "This style name already exists"), | |
| ) | |
| else: | |
| self.form.comboBoxStyles.setItemText(index, newname) | |
| value = self.styles[style] | |
| del self.styles[style] | |
| self.styles[newname] = value | |
| self.renamed[style] = newname | |
| self.current_style = newname | |
| def on_import(self): | |
| """Import styles from a json file.""" | |
| filename = QtWidgets.QFileDialog.getOpenFileName( | |
| QtWidgets.QApplication.activeWindow(), | |
| translate("draft", "Open Styles File"), | |
| None, | |
| translate("draft", "JSON files (*.json *.JSON)"), | |
| ) | |
| if filename and filename[0]: | |
| nstyles = {} | |
| with open(filename[0]) as f: | |
| for key, val in json.load(f).items(): | |
| nstyles[key] = utils.repair_annotation_style(val) | |
| if nstyles: | |
| self.styles.update(nstyles) | |
| for style in self.styles.keys(): | |
| if self.form.comboBoxStyles.findText(style) == -1: | |
| self.form.comboBoxStyles.addItem(style) | |
| self.fill_editor(self.current_style) # The current style may have changed. | |
| print("Styles updated from " + filename[0]) | |
| def on_export(self): | |
| """Export styles to a json file.""" | |
| filename = QtWidgets.QFileDialog.getSaveFileName( | |
| QtWidgets.QApplication.activeWindow(), | |
| translate("draft", "Save Styles File"), | |
| None, | |
| translate("draft", "JSON file (*.json)"), | |
| ) | |
| if filename and filename[0]: | |
| self.update_style() | |
| with open(filename[0], "w") as f: | |
| json.dump(self.styles, f, indent=4) | |
| print("Styles saved to " + filename[0]) | |
| def fill_editor(self, style=None): | |
| """Fill the editor fields with the contents of a style.""" | |
| default = utils.get_default_annotation_style() | |
| if style is None or style == "": | |
| style = {} | |
| for key, val in default.items(): | |
| style[key] = val[1] | |
| elif isinstance(style, dict): | |
| pass | |
| elif isinstance(style, str) and style in self.styles: | |
| style = self.styles[style] | |
| else: | |
| print("debug: unable to fill dialog from style", style) | |
| for key, value in style.items(): | |
| control = getattr(self.form, key) | |
| if default[key][0] == "str": | |
| control.setText(value) | |
| elif default[key][0] == "font": | |
| control.setCurrentFont(QtGui.QFont(value)) | |
| elif default[key][0] == "color": | |
| color = QtGui.QColor(utils.rgba_to_argb(value)) | |
| control.setProperty("color", color) | |
| elif default[key][0] == "int": | |
| control.setValue(value) | |
| elif default[key][0] == "float": | |
| if hasattr(control, "setText"): | |
| control.setText(App.Units.Quantity(value, App.Units.Length).UserString) | |
| else: | |
| control.setValue(value) | |
| elif default[key][0] == "bool": | |
| control.setChecked(value) | |
| elif default[key][0] == "index": | |
| control.setCurrentIndex(value) | |
| def update_style(self): | |
| """Update the current style with the values from the editor.""" | |
| index = self.form.comboBoxStyles.currentIndex() | |
| if index > 1: | |
| style = self.form.comboBoxStyles.itemText(index) | |
| self.styles[style] = self.get_editor_values() | |
| def get_editor_values(self): | |
| default = utils.get_default_annotation_style() | |
| values = {} | |
| for key in default.keys(): | |
| control = getattr(self.form, key) | |
| if default[key][0] == "str": | |
| values[key] = control.text() | |
| elif default[key][0] == "font": | |
| values[key] = control.currentFont().family() | |
| elif default[key][0] == "color": | |
| values[key] = utils.argb_to_rgba(control.property("color").rgba()) | |
| elif default[key][0] == "int": | |
| values[key] = control.value() | |
| elif default[key][0] == "float": | |
| if hasattr(control, "setText"): | |
| values[key] = App.Units.Quantity(control.text()).Value | |
| else: | |
| values[key] = control.value() | |
| elif default[key][0] == "bool": | |
| values[key] = control.isChecked() | |
| elif default[key][0] == "index": | |
| values[key] = control.currentIndex() | |
| return values | |
| def get_annotations(self): | |
| """Get all objects that support annotation styles.""" | |
| users = [] | |
| for obj in self.doc.Objects: | |
| vobj = obj.ViewObject | |
| if "AnnotationStyle" in vobj.PropertiesList: | |
| users.append(obj) | |
| return users | |
| def get_style_users(self, style): | |
| """Get all objects using a certain style.""" | |
| users = [] | |
| for obj in self.get_annotations(): | |
| if obj.ViewObject.AnnotationStyle == style: | |
| users.append(obj) | |
| return users | |
| Gui.addCommand("Draft_AnnotationStyleEditor", AnnotationStyleEditor()) | |
| ## @} | |