| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides GUI tools to create and edit annotation styles.""" |
| | |
| | |
| | |
| |
|
| | |
| | |
| | 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() |
| | |
| | self.renamed = {} |
| | self.current_style = None |
| |
|
| | |
| | ui_file = ":/ui/dialog_AnnotationStyleEditor.ui" |
| | self.form = Gui.PySideUic.loadUi(ui_file) |
| |
|
| | |
| | w = params.get_param("AnnotationStyleEditorWidth") |
| | h = params.get_param("AnnotationStyleEditorHeight") |
| | self.form.resize(w, h) |
| |
|
| | |
| | mw = Gui.getMainWindow() |
| | self.form.move( |
| | mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() |
| | ) |
| |
|
| | |
| | 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")) |
| |
|
| | |
| | self.styles = self.read_meta() |
| | for style in self.styles.keys(): |
| | self.form.comboBoxStyles.addItem(style) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | self.fill_editor() |
| | result = self.form.exec_() |
| |
|
| | |
| | if result: |
| | self.save_meta(self.styles) |
| |
|
| | |
| | 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.""" |
| | |
| | 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 |
| |
|
| | |
| | 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 |
| |
|
| | |
| | for obj in self.get_annotations(): |
| | vobj = obj.ViewObject |
| | try: |
| | current = vobj.AnnotationStyle |
| | except AssertionError: |
| | |
| | pass |
| | else: |
| | if vobj.AnnotationStyle in self.renamed: |
| | |
| | |
| | vobj.AnnotationStyle = [ |
| | vobj.AnnotationStyle, |
| | self.renamed[vobj.AnnotationStyle], |
| | ] |
| | vobj.AnnotationStyle = self.renamed[vobj.AnnotationStyle] |
| | if vobj.AnnotationStyle in styles: |
| | if vobj.AnnotationStyle in changedstyles: |
| | |
| | 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: |
| | |
| | vobj.AnnotationStyle = "" |
| | vobj.AnnotationStyle = [""] + list(styles) |
| |
|
| | def on_style_changed(self, index): |
| | """Execute as a callback when the styles combobox changes.""" |
| | if index == 0: |
| | |
| | self.form.pushButtonDelete.setEnabled(False) |
| | self.form.pushButtonRename.setEnabled(False) |
| | elif index == 1: |
| | |
| | reply = QtWidgets.QInputDialog.getText( |
| | None, translate("draft", "New Style"), translate("draft", "Style name") |
| | ) |
| | if reply[1]: |
| | |
| | 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: |
| | |
| | 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: |
| | |
| | self.form.comboBoxStyles.setCurrentIndex(0) |
| | else: |
| | |
| | if self.current_style is not None: |
| | |
| | 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] |
| | |
| | |
| | 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) |
| | |
| | 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]: |
| | |
| | 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) |
| | 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()) |
| |
|
| | |
| |
|