diff --git a/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py b/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py index 5198549a18..e4a692fc6f 100644 --- a/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py +++ b/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # *************************************************************************** # * Copyright (c) 2020 Yorik van Havre * # * * @@ -20,78 +19,108 @@ # * USA * # * * # *************************************************************************** +"""Provides all gui and tools to create and edit annotation styles.""" -""" -Provides all gui and tools to create and edit annotation styles -Provides Draft_AnnotationStyleEditor command -""" - -import FreeCAD,FreeCADGui import json +import PySide.QtGui as QtGui +from PySide.QtCore import QT_TRANSLATE_NOOP -def QT_TRANSLATE_NOOP(ctx,txt): return txt +import FreeCAD as App +import FreeCADGui as Gui +import draftguitools.gui_base as gui_base +from draftutils.translate import _tr -param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") +param = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") DEFAULT = { - "FontName":("font",param.GetString("textfont","Sans")), - "FontSize":("str",str(param.GetFloat("textheight",100))), - "LineSpacing":("str","1 cm"), - "ScaleMultiplier":("float",1), - "ShowUnit":("bool",False), - "UnitOverride":("str",""), - "Decimals":("int",2), - "ShowLines":("bool",True), - "LineWidth":("int",param.GetInt("linewidth",1)), - "LineColor":("color",param.GetInt("color",255)), - "ArrowType":("index",param.GetInt("dimsymbol",0)), - "ArrowSize":("str",str(param.GetFloat("arrowsize",20))), - "DimensionOvershoot":("str",str(param.GetFloat("dimovershoot",20))), - "ExtensionLines":("str",str(param.GetFloat("extlines",300))), - "ExtensionOvershoot":("str",str(param.GetFloat("extovershoot",20))), + "FontName": ("font", param.GetString("textfont", "Sans")), + "FontSize": ("str", str(param.GetFloat("textheight", 100))), + "LineSpacing": ("str", "1 cm"), + "ScaleMultiplier": ("float", 1), + "ShowUnit": ("bool", False), + "UnitOverride": ("str", ""), + "Decimals": ("int", 2), + "ShowLines": ("bool", True), + "LineWidth": ("int", param.GetInt("linewidth", 1)), + "LineColor": ("color", param.GetInt("color", 255)), + "ArrowType": ("index", param.GetInt("dimsymbol", 0)), + "ArrowSize": ("str", str(param.GetFloat("arrowsize", 20))), + "DimensionOvershoot": ("str", str(param.GetFloat("dimovershoot", 20))), + "ExtensionLines": ("str", str(param.GetFloat("extlines", 300))), + "ExtensionOvershoot": ("str", str(param.GetFloat("extovershoot", 20))), } -class Draft_AnnotationStyleEditor: +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=_tr("Annotation style editor")) + self.doc = None self.styles = {} self.renamed = {} + self.form = None def GetResources(self): + """Set icon, menu and tooltip.""" + _tip = "Manage or create annotation styles" - return {'Pixmap' : ":icons/Draft_Annotation_Style.svg", - 'MenuText': QT_TRANSLATE_NOOP("Draft_AnnotationStyleEditor", "Annotation styles..."), - 'ToolTip' : QT_TRANSLATE_NOOP("Draft_AnnotationStyleEditor", "Manage or create annotation styles")} - - def IsActive(self): - - return bool(FreeCAD.ActiveDocument) + return {'Pixmap': ":icons/Draft_Annotation_Style.svg", + 'MenuText': QT_TRANSLATE_NOOP("Draft_AnnotationStyleEditor", + "Annotation styles..."), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_AnnotationStyleEditor", + _tip)} def Activated(self): + """Execute when the command is called. - from PySide import QtGui - + The document attribute is set here by the parent class. + """ + super(AnnotationStyleEditor, self).Activated() # reset rename table self.renamed = {} # load dialog - self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialog_AnnotationStyleEditor.ui") + ui_file = ":/ui/dialog_AnnotationStyleEditor.ui" + self.form = Gui.PySideUic.loadUi(ui_file) # restore stored size - w = param.GetInt("AnnotationStyleEditorWidth",450) - h = param.GetInt("AnnotationStyleEditorHeight",450) - self.form.resize(w,h) + w = param.GetInt("AnnotationStyleEditorWidth", 450) + h = param.GetInt("AnnotationStyleEditorHeight", 450) + self.form.resize(w, h) # center the dialog over FreeCAD window - mw = FreeCADGui.getMainWindow() - self.form.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center()) + 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()) # fill the styles combo self.styles = self.read_meta() @@ -103,10 +132,12 @@ class Draft_AnnotationStyleEditor: self.form.pushButtonDelete.clicked.connect(self.on_delete) self.form.pushButtonRename.clicked.connect(self.on_rename) for attr in DEFAULT.keys(): - control = getattr(self.form,attr) - for signal in ["clicked","textChanged","valueChanged","stateChanged","currentIndexChanged"]: - if hasattr(control,signal): - getattr(control,signal).connect(self.update_style) + control = getattr(self.form, attr) + for signal in ("clicked", "textChanged", + "valueChanged", "stateChanged", + "currentIndexChanged"): + if hasattr(control, signal): + getattr(control, signal).connect(self.update_style) break # show editor dialog @@ -117,69 +148,62 @@ class Draft_AnnotationStyleEditor: self.save_meta(self.styles) # store dialog size - param.SetInt("AnnotationStyleEditorWidth",self.form.width()) - param.SetInt("AnnotationStyleEditorHeight",self.form.height()) - - return + param.SetInt("AnnotationStyleEditorWidth", self.form.width()) + param.SetInt("AnnotationStyleEditorHeight", self.form.height()) def read_meta(self): - - """reads the document Meta property and returns a dict""" - + """Read the document Meta attribute and return a dict.""" styles = {} - meta = FreeCAD.ActiveDocument.Meta - for key,value in meta.items(): + meta = self.doc.Meta + for key, value in meta.items(): if key.startswith("Draft_Style_"): styles[key[12:]] = json.loads(value) return styles - def save_meta(self,styles): - - """saves a dict to the document Meta property and updates objects""" - + def save_meta(self, styles): + """Save a dict to the document Meta attribute and update objects.""" # save meta changedstyles = [] - meta = FreeCAD.ActiveDocument.Meta - for key,value in styles.items(): + meta = self.doc.Meta + for key, value in styles.items(): try: strvalue = json.dumps(value) - except: - print("debug: unable to serialize this:",value) - if ("Draft_Style_"+key in meta) and (meta["Draft_Style_"+key] != strvalue): + 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 + meta["Draft_Style_" + key] = strvalue + # remove deleted styles todelete = [] - for key,value in meta.items(): + 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] - - FreeCAD.ActiveDocument.Meta = meta + + self.doc.Meta = meta # propagate changes to all annotations for obj in self.get_annotations(): - if obj.ViewObject.AnnotationStyle in self.renamed.keys(): + vobj = obj.ViewObject + if vobj.AnnotationStyle in self.renamed.keys(): # temporarily add the new style and switch to it - obj.ViewObject.AnnotationStyle = obj.ViewObject.AnnotationStyle+[self.renamed[obj.ViewObject.AnnotationStyle]] - obj.ViewObject.AnnotationStyle = self.renamed[obj.ViewObject.AnnotationStyle] - if obj.ViewObject.AnnotationStyle in styles.keys(): - if obj.ViewObject.AnnotationStyle in changedstyles: - for attr,attrvalue in styles[obj.ViewObject.AnnotationStyle].items(): - if hasattr(obj.ViewObject,attr): - setattr(obj.ViewObject,attr,attrvalue) + vobj.AnnotationStyle = vobj.AnnotationStyle + [self.renamed[vobj.AnnotationStyle]] + vobj.AnnotationStyle = self.renamed[vobj.AnnotationStyle] + if vobj.AnnotationStyle in styles.keys(): + if vobj.AnnotationStyle in changedstyles: + for attr, attrvalue in styles[vobj.AnnotationStyle].items(): + if hasattr(vobj, attr): + setattr(vobj, attr, attrvalue) else: - obj.ViewObject.AnnotationStyle = "" - obj.ViewObject.AnnotationStyle == [""] + styles.keys() - - def on_style_changed(self,index): - - """called when the styles combobox is changed""" - - from PySide import QtGui + vobj.AnnotationStyle = "" + vobj.AnnotationStyle = [""] + styles.keys() + def on_style_changed(self, index): + """Execute as a callback when the styles combobox changes.""" if index <= 1: # nothing happens self.form.pushButtonDelete.setEnabled(False) @@ -187,19 +211,23 @@ class Draft_AnnotationStyleEditor: self.fill_editor(None) if index == 1: # Add new... entry - reply = QtGui.QInputDialog.getText(None, "Create new style","Style name:") + reply = QtGui.QInputDialog.getText(None, + "Create new style", + "Style name:") if reply[1]: # OK or Enter pressed name = reply[0] if name in self.styles: - reply = QtGui.QMessageBox.information(None,"Style exists","This style name already exists") + reply = QtGui.QMessageBox.information(None, + "Style exists", + "This style name already exists") else: # create new default style self.styles[name] = {} - for key,val in DEFAULT.items(): + for key, val in DEFAULT.items(): self.styles[name][key] = val[1] self.form.comboBoxStyles.addItem(name) - self.form.comboBoxStyles.setCurrentIndex(self.form.comboBoxStyles.count()-1) + self.form.comboBoxStyles.setCurrentIndex(self.form.comboBoxStyles.count() - 1) elif index > 1: # Existing style self.form.pushButtonDelete.setEnabled(True) @@ -207,93 +235,92 @@ class Draft_AnnotationStyleEditor: self.fill_editor(self.form.comboBoxStyles.itemText(index)) def on_delete(self): - - """called when the Delete button is pressed""" - - from PySide import QtGui - + """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 = QtGui.QMessageBox.question(None, "Style in use", "This style is used by some objects in this document. Are you sure?", - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) + reply = QtGui.QMessageBox.question(None, + "Style in use", + "This style is used by some objects in this document. Are you sure?", + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: return self.form.comboBoxStyles.removeItem(index) del self.styles[style] def on_rename(self): - - """called when the Rename button is pressed""" - - from PySide import QtGui - + """Execute as a callback when the rename button is pressed.""" index = self.form.comboBoxStyles.currentIndex() style = self.form.comboBoxStyles.itemText(index) - reply = QtGui.QInputDialog.getText(None, "Rename style","New name:",QtGui.QLineEdit.Normal,style) + + reply = QtGui.QInputDialog.getText(None, + "Rename style", + "New name:", + QtGui.QLineEdit.Normal, + style) if reply[1]: # OK or Enter pressed newname = reply[0] if newname in self.styles: - reply = QtGui.QMessageBox.information(None,"Style exists","This style name already exists") + reply = QtGui.QMessageBox.information(None, + "Style exists", + "This style name already exists") else: - self.form.comboBoxStyles.setItemText(index,newname) + self.form.comboBoxStyles.setItemText(index, newname) value = self.styles[style] del self.styles[style] self.styles[newname] = value self.renamed[style] = newname - def fill_editor(self,style): - - """fills the editor fields with the contents of a style""" - - from PySide import QtGui - + def fill_editor(self, style): + """Fill the editor fields with the contents of a style.""" if style is None: style = {} - for key,val in DEFAULT.items(): + for key, val in DEFAULT.items(): style[key] = val[1] - if not isinstance(style,dict): + + if not isinstance(style, dict): if 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) + 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": - r = ((value>>24)&0xFF)/255.0 - g = ((value>>16)&0xFF)/255.0 - b = ((value>>8)&0xFF)/255.0 - color = QtGui.QColor.fromRgbF(r,g,b) - control.setProperty("color",color) - elif DEFAULT[key][0] in ["int","float"]: + r = ((value >> 24) & 0xFF) / 255.0 + g = ((value >> 16) & 0xFF) / 255.0 + b = ((value >> 8) & 0xFF) / 255.0 + color = QtGui.QColor.fromRgbF(r, g, b) + control.setProperty("color", color) + elif DEFAULT[key][0] in ["int", "float"]: control.setValue(value) elif DEFAULT[key][0] == "bool": control.setChecked(value) elif DEFAULT[key][0] == "index": control.setCurrentIndex(value) - def update_style(self,arg=None): - - """updates the current style with the values from the editor""" - + def update_style(self, arg=None): + """Update the current style with the values from the editor.""" index = self.form.comboBoxStyles.currentIndex() if index > 1: values = {} style = self.form.comboBoxStyles.itemText(index) for key in DEFAULT.keys(): - control = getattr(self.form,key) + 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] = control.property("color").rgb()<<8 - elif DEFAULT[key][0] in ["int","float"]: + values[key] = control.property("color").rgb() << 8 + elif DEFAULT[key][0] in ["int", "float"]: values[key] = control.value() elif DEFAULT[key][0] == "bool": values[key] = control.isChecked() @@ -302,20 +329,16 @@ class Draft_AnnotationStyleEditor: self.styles[style] = values def get_annotations(self): - - """gets all the objects that support annotation styles""" - + """Get all the objects that support annotation styles.""" users = [] - for obj in FreeCAD.ActiveDocument.Objects: + for obj in self.doc.Objects: vobj = obj.ViewObject - if hasattr(vobj,"AnnotationStyle"): + if hasattr(vobj, "AnnotationStyle"): users.append(obj) return users - def get_style_users(self,style): - - """get all objects using a certain style""" - + 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: @@ -323,4 +346,4 @@ class Draft_AnnotationStyleEditor: return users -FreeCADGui.addCommand('Draft_AnnotationStyleEditor', Draft_AnnotationStyleEditor()) +Gui.addCommand('Draft_AnnotationStyleEditor', AnnotationStyleEditor())