From 5b3fea59e0efd2981b6dac41698c3ffb73ebcb11 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 22 Apr 2020 13:38:17 +0200 Subject: [PATCH] Draft: Annotation editor --- src/Mod/Draft/CMakeLists.txt | 1 + src/Mod/Draft/DraftTools.py | 1 + .../ui/dialog_AnnotationStyleEditor.ui | 609 +++++++++--------- .../gui_annotationstyleeditor.py | 182 ++++-- .../Draft/draftobjects/draft_annotation.py | 5 +- src/Mod/Draft/draftutils/init_tools.py | 2 +- 6 files changed, 443 insertions(+), 357 deletions(-) diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 02f2d4e9aa..d62862af72 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -102,6 +102,7 @@ SET(Creator_tools SET(Draft_GUI_tools draftguitools/__init__.py + draftguitools/gui_annotationstyleeditor.py draftguitools/gui_base.py draftguitools/gui_base_original.py draftguitools/gui_tool_utils.py diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index a107284705..398666b2de 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -92,6 +92,7 @@ from draftguitools.gui_heal import Heal from draftguitools.gui_dimension_ops import Draft_FlipDimension from draftguitools.gui_lineslope import Draft_Slope import draftguitools.gui_arrays +import draftguitools.gui_annotationstyleeditor # import DraftFillet import drafttaskpanels.task_scale as task_scale diff --git a/src/Mod/Draft/Resources/ui/dialog_AnnotationStyleEditor.ui b/src/Mod/Draft/Resources/ui/dialog_AnnotationStyleEditor.ui index eddf18cecc..01cf23af84 100644 --- a/src/Mod/Draft/Resources/ui/dialog_AnnotationStyleEditor.ui +++ b/src/Mod/Draft/Resources/ui/dialog_AnnotationStyleEditor.ui @@ -6,12 +6,12 @@ 0 0 - 418 - 694 + 453 + 424 - Dialog + Annotation Styles Editor @@ -82,307 +82,314 @@ - - - Text + + + QFrame::Plain - - - - - Font size - - - - - - - Font name - - - - - - - Line spacing - - - - - - - The size of the text in real-world units - - - - - - - - - - The spacing between lines of text in real-world units - - - - - - - - - - The font to use for texts and dimensions - - - - - - - - - - Units + + Qt::ScrollBarAsNeeded - - - - - Scale multiplier - - - - - - - Decimals - - - - - - - Únit override - - - - - - - Show unit - - - - - - - A multiplier value that affects distances shown by dimensions - - - 4 - - - 1.000000000000000 - - - - - - - Forces dimensions to be shown in a specific unit - - - - - - - The number of decimals to show on dimensions - - - - - - - Shows the units suffix on dimensions or not - - - Qt::RightToLeft - - - - - - - - - - - - - Line and arrows + + true - - - - - Line width - - - - - - - Extension overshoot - - - - - - - Arrow size - - - - - - - Show lines - - - - - - - Dimension overshoot - - - - - - - Extension lines - - - - - - - Arrow type - - - - - - - Line / text color - - - - - - - Shows the dimension line or not - - - Qt::RightToLeft - - - - - - true - - - - - - - The width of the dimension lines - - - px - - - 1 - - - - - - - The color of dimension lines, arrows and texts - - - - 0 - 0 - 0 - - - - - - - - The typeof arrows to use for dimensions - - - - Dot + + + + 0 + -110 + 420 + 589 + + + + + + + Text - - - - Arrow + + + + + Font size + + + + + + + Line spacing + + + + + + + Font name + + + + + + + The font to use for texts and dimensions + + + + + + + + + + + + + + + + + + + + + + + + Units - - - - Tick + + + + + Scale multiplier + + + + + + + Decimals + + + + + + + Unit override + + + + + + + Show unit + + + + + + + A multiplier value that affects distances shown by dimensions + + + 4 + + + 1.000000000000000 + + + + + + + Forces dimensions to be shown in a specific unit + + + + + + + The number of decimals to show on dimensions + + + + + + + Shows the units suffix on dimensions or not + + + Qt::RightToLeft + + + + + + + + + + + + + Line and arrows - - - - - - - The size of dimension arrows - - - - - - - - - - How far must the main dimension line extend pass the measured points - - - - - - - - - - The length of extension lines - - - - - - - - - - How far must the extension lines extend above the main dimension line - - - - - - - + + + + + Line width + + + + + + + Extension overshoot + + + + + + + Arrow size + + + + + + + Show lines + + + + + + + Dimension overshoot + + + + + + + Extension lines + + + + + + + Arrow type + + + + + + + Line / text color + + + + + + + Shows the dimension line or not + + + Qt::RightToLeft + + + + + + true + + + + + + + The width of the dimension lines + + + px + + + 1 + + + + + + + The color of dimension lines, arrows and texts + + + + 0 + 0 + 0 + + + + + + + + The typeof arrows to use for dimensions + + + + Dot + + + + + Arrow + + + + + Tick + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -399,9 +406,9 @@ - Gui::QuantitySpinBox - QWidget -
Gui/QuantitySpinBox.h
+ Gui::InputField + QLineEdit +
Gui/InputField.h
Gui::ColorButton diff --git a/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py b/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py index 08c1dd698b..5198549a18 100644 --- a/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py +++ b/src/Mod/Draft/draftguitools/gui_annotationstyleeditor.py @@ -29,22 +29,26 @@ Provides Draft_AnnotationStyleEditor command import FreeCAD,FreeCADGui import json -EMPTYSTYLE = { - "FontName":"Sans", - "FontSize":0, - "LineSpacing":0, - "ScaleMultiplier":1, - "ShowUnit":False, - "UnitOverride":"", - "Decimals":0, - "ShowLines":True, - "LineWidth":1, - "LineColor":255, - "ArrowType":0, - "ArrowSize":0, - "DimensionOvershoot":0, - "ExtensionLines":0, - "ExtensionOvershoot":0, +def QT_TRANSLATE_NOOP(ctx,txt): return txt + +param = FreeCAD.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))), } @@ -53,10 +57,11 @@ class Draft_AnnotationStyleEditor: def __init__(self): self.styles = {} + self.renamed = {} def GetResources(self): - return {'Pixmap' : ":icons/Draft_AnnotationStyleEditor.svg", + 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")} @@ -68,16 +73,25 @@ class Draft_AnnotationStyleEditor: from PySide import QtGui + # reset rename table + self.renamed = {} + # load dialog self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialog_AnnotationStyleEditor.ui") + # restore stored size + 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()) # 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/edit_Cancel.svg")) + self.form.pushButtonRename.setIcon(QtGui.QIcon(":/icons/accessories-text-editor.svg")) # fill the styles combo self.styles = self.read_meta() @@ -88,9 +102,9 @@ class Draft_AnnotationStyleEditor: 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) - for attr in EMPTYSTYLE.keys(): + for attr in DEFAULT.keys(): control = getattr(self.form,attr) - for signal in ["textChanged","valueChanged","stateChanged"]: + for signal in ["clicked","textChanged","valueChanged","stateChanged","currentIndexChanged"]: if hasattr(control,signal): getattr(control,signal).connect(self.update_style) break @@ -102,6 +116,10 @@ class Draft_AnnotationStyleEditor: if result: self.save_meta(self.styles) + # store dialog size + param.SetInt("AnnotationStyleEditorWidth",self.form.width()) + param.SetInt("AnnotationStyleEditorHeight",self.form.height()) + return def read_meta(self): @@ -123,60 +141,78 @@ class Draft_AnnotationStyleEditor: changedstyles = [] meta = FreeCAD.ActiveDocument.Meta for key,value in styles.items(): - strvalue = json.dumps(value) - if meta["Draft_Style_"+key] and (meta["Draft_Style_"+key] != strvalue): - changedstyles.append(style) + 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): + 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] + FreeCAD.ActiveDocument.Meta = meta # propagate changes to all annotations for obj in self.get_annotations(): + if obj.ViewObject.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) else: - obj.ViewObject.AnnotationStyle = " " - obj.ViewObject.AnnotationStyle == [" "] + styles.keys() + 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 - if index <= 1: + if index <= 1: # nothing happens self.form.pushButtonDelete.setEnabled(False) self.form.pushButtonRename.setEnabled(False) self.fill_editor(None) - if index == 1: + if index == 1: # Add new... entry reply = QtGui.QInputDialog.getText(None, "Create new style","Style name:") - if reply[1]: + 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") else: # create new default style - self.styles[name] = EMPTYSTYLE + self.styles[name] = {} + 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) - elif index > 1: + elif index > 1: # Existing style self.form.pushButtonDelete.setEnabled(True) self.form.pushButtonRename.setEnabled(True) self.fill_editor(self.form.comboBoxStyles.itemText(index)) def on_delete(self): - + """called when the Delete button is pressed""" from PySide import QtGui - index = self.form.comboBox.currentIndex() + 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?", @@ -187,48 +223,86 @@ class Draft_AnnotationStyleEditor: del self.styles[style] def on_rename(self): - + """called when the Rename button is pressed""" from PySide import QtGui - index = self.form.comboBox.currentIndex() + index = self.form.comboBoxStyles.currentIndex() style = self.form.comboBoxStyles.itemText(index) reply = QtGui.QInputDialog.getText(None, "Rename style","New name:",QtGui.QLineEdit.Normal,style) - if reply[1]: + if reply[1]: # OK or Enter pressed newname = reply[0] - self.form.comboBoxStyles.setItemText(index,newname) - value = self.styles[style] - del self.styles[style] - self.styles[newname] = value + if newname in self.styles: + reply = QtGui.QMessageBox.information(None,"Style exists","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 def fill_editor(self,style): - + """fills the editor fields with the contents of a style""" + from PySide import QtGui + if style is None: - style = EMPTYSTYLE + style = {} + for key,val in DEFAULT.items(): + style[key] = val[1] + 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(): - setattr(self.form,key,value) + 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"]: + 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""" - - index = self.form.comboBox.currentIndex() + + index = self.form.comboBoxStyles.currentIndex() if index > 1: values = {} style = self.form.comboBoxStyles.itemText(index) - for key in EMPTYSTYLE.keys(): + for key in DEFAULT.keys(): control = getattr(self.form,key) - for attr in ["text","value","state"]: - if hasattr(control,attr): - values[key] = getattr(control,attr) + 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.value() + elif DEFAULT[key][0] == "bool": + values[key] = control.isChecked() + elif DEFAULT[key][0] == "index": + values[key] = control.currentIndex() self.styles[style] = values def get_annotations(self): - + """gets all the objects that support annotation styles""" users = [] @@ -239,14 +313,14 @@ class Draft_AnnotationStyleEditor: 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 - + FreeCADGui.addCommand('Draft_AnnotationStyleEditor', Draft_AnnotationStyleEditor()) diff --git a/src/Mod/Draft/draftobjects/draft_annotation.py b/src/Mod/Draft/draftobjects/draft_annotation.py index 1ee3ab5432..4cf03ee57a 100644 --- a/src/Mod/Draft/draftobjects/draft_annotation.py +++ b/src/Mod/Draft/draftobjects/draft_annotation.py @@ -48,7 +48,10 @@ class DraftAnnotation(object): def __setstate__(self,state): if state: - self.Type = state + if isinstance(state,dict) and ("Type" in state): + self.Type = state["Type"] + else: + self.Type = state def execute(self,obj): diff --git a/src/Mod/Draft/draftutils/init_tools.py b/src/Mod/Draft/draftutils/init_tools.py index c7f484286d..1a93dc2474 100644 --- a/src/Mod/Draft/draftutils/init_tools.py +++ b/src/Mod/Draft/draftutils/init_tools.py @@ -49,7 +49,7 @@ def get_draft_drawing_commands(): def get_draft_annotation_commands(): """Return the annotation commands list.""" return ["Draft_Text", "Draft_Dimension", - "Draft_Label"] + "Draft_Label","Draft_AnnotationStyleEditor"] def get_draft_array_commands():