From 541e84076a200f23641052b9a081b40235755212 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sun, 19 Jun 2016 22:13:13 +0300 Subject: [PATCH 1/4] New module: Show A module for gathering code related to visibility automation and the like. Contains DepGraphTools.py and TempoVis.py, to be withdrawn from Part/AttachmentEditor. Also: changes to TempoVis: * keep TempoVis from from raising (TempoVis is non-essential functionality, so breaking execution of unsuspecting routines that use it seems like a bad idea. * implement saving/restoring camera (Unfortunately, I failed to implement nice animated restoration.) * almost proper support of saving to file (I still couldn't figure out a way to restore reference to document and viewer. So, falling to using active ones instead (might cause bugs).) --- CMakeLists.txt | 1 + src/Mod/CMakeLists.txt | 4 + src/Mod/Show/CMakeLists.txt | 21 ++++ src/Mod/Show/DepGraphTools.py | 82 ++++++++++++++++ src/Mod/Show/FrozenClass.py | 37 ++++++++ src/Mod/Show/TempoVis.py | 174 ++++++++++++++++++++++++++++++++++ src/Mod/Show/__init__.py | 4 + 7 files changed, 323 insertions(+) create mode 100644 src/Mod/Show/CMakeLists.txt create mode 100644 src/Mod/Show/DepGraphTools.py create mode 100644 src/Mod/Show/FrozenClass.py create mode 100644 src/Mod/Show/TempoVis.py create mode 100644 src/Mod/Show/__init__.py diff --git a/CMakeLists.txt b/CMakeLists.txt index e45c4f763..b4aab7f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,7 @@ OPTION(BUILD_RAYTRACING "Build the FreeCAD ray tracing module" ON) OPTION(BUILD_REVERSEENGINEERING "Build the FreeCAD reverse engineering module" ON) OPTION(BUILD_ROBOT "Build the FreeCAD robot module" ON) OPTION(BUILD_SHIP "Build the FreeCAD ship module" ON) +OPTION(BUILD_SHOW "Build the FreeCAD Show module (helper module for visibility automation)" ON) OPTION(BUILD_SKETCHER "Build the FreeCAD sketcher module" ON) OPTION(BUILD_SPREADSHEET "Build the FreeCAD spreadsheet module" ON) OPTION(BUILD_START "Build the FreeCAD start module" ON) diff --git a/src/Mod/CMakeLists.txt b/src/Mod/CMakeLists.txt index a0b7b3b62..e310a30f3 100644 --- a/src/Mod/CMakeLists.txt +++ b/src/Mod/CMakeLists.txt @@ -125,3 +125,7 @@ endif(BUILD_JTREADER) if(BUILD_PATH) add_subdirectory(Path) endif(BUILD_PATH) + +if(BUILD_SHOW) + add_subdirectory(Show) +endif(BUILD_SHOW) diff --git a/src/Mod/Show/CMakeLists.txt b/src/Mod/Show/CMakeLists.txt new file mode 100644 index 000000000..8f8602d4f --- /dev/null +++ b/src/Mod/Show/CMakeLists.txt @@ -0,0 +1,21 @@ + +SET(Show_SRCS + __init__.py + DepGraphTools.py + FrozenClass.py + TempoVis.py +) + +SOURCE_GROUP("" FILES ${Show_SRCS}) + +ADD_CUSTOM_TARGET(Show ALL + SOURCES ${Show_SRCS} +) + +fc_copy_sources(Show "${CMAKE_BINARY_DIR}/Mod/Show" ${Show_SRCS}) + +INSTALL( + FILES + ${Show_SRCS} + DESTINATION Mod/Show +) \ No newline at end of file diff --git a/src/Mod/Show/DepGraphTools.py b/src/Mod/Show/DepGraphTools.py new file mode 100644 index 000000000..f5d36b541 --- /dev/null +++ b/src/Mod/Show/DepGraphTools.py @@ -0,0 +1,82 @@ +#/*************************************************************************** +# * Copyright (c) Victor Titov (DeepSOIC) * +# * (vv.titov@gmail.com) 2016 * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This library is free software; you can redistribute it and/or * +# * modify it under the terms of the GNU Library General Public * +# * License as published by the Free Software Foundation; either * +# * version 2 of the License, or (at your option) any later version. * +# * * +# * This library 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 library; see the file COPYING.LIB. If not, * +# * write to the Free Software Foundation, Inc., 59 Temple Place, * +# * Suite 330, Boston, MA 02111-1307, USA * +# * * +# ***************************************************************************/ + +import FreeCAD as App + +def getAllDependencies(feat): + '''getAllDependencies(feat): gets all features feat depends on, directly or indirectly. + Returns a list, with deepest dependencies last. feat is not included in the list, except + if the feature depends on itself (dependency loop).''' + list_traversing_now = [feat] + set_of_deps = set() + list_of_deps = [] + + while len(list_traversing_now) > 0: + list_to_be_traversed_next = [] + for feat in list_traversing_now: + for dep in feat.OutList: + if not (dep in set_of_deps): + set_of_deps.add(dep) + list_of_deps.append(dep) + list_to_be_traversed_next.append(dep) + + list_traversing_now = list_to_be_traversed_next + + return list_of_deps + +def getAllDependent(feat): + '''getAllDependent(feat): gets all features that depend on feat, directly or indirectly. + Returns a list, with deepest dependencies last. feat is not included in the list, except + if the feature depends on itself (dependency loop).''' + list_traversing_now = [feat] + set_of_deps = set() + list_of_deps = [] + + while len(list_traversing_now) > 0: + list_to_be_traversed_next = [] + for feat in list_traversing_now: + for dep in feat.InList: + if not (dep in set_of_deps): + set_of_deps.add(dep) + list_of_deps.append(dep) + list_to_be_traversed_next.append(dep) + + list_traversing_now = list_to_be_traversed_next + + return list_of_deps + +def isContainer(obj): + '''isContainer(obj): returns True if obj is an object container, such as + Group, Part, Body. The important characterisic of an object being a + container is its action on visibility of linked objects. E.g. a + Part::Compound is not a group, because it does not affect visibility + of originals. Documents are considered containers, too.''' + + if obj.isDerivedFrom("App::DocumentObjectGroup"): + return True + if obj.isDerivedFrom("PartDesign::Body"): + return True + if obj.isDerivedFrom("App::Origin"): + return True + if obj.isDerivedFrom('App::Document'): + return True diff --git a/src/Mod/Show/FrozenClass.py b/src/Mod/Show/FrozenClass.py new file mode 100644 index 000000000..1fa76fb5f --- /dev/null +++ b/src/Mod/Show/FrozenClass.py @@ -0,0 +1,37 @@ +#/*************************************************************************** +# * Copyright (c) Victor Titov (DeepSOIC) * +# * (vv.titov@gmail.com) 2016 * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This library is free software; you can redistribute it and/or * +# * modify it under the terms of the GNU Library General Public * +# * License as published by the Free Software Foundation; either * +# * version 2 of the License, or (at your option) any later version. * +# * * +# * This library 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 library; see the file COPYING.LIB. If not, * +# * write to the Free Software Foundation, Inc., 59 Temple Place, * +# * Suite 330, Boston, MA 02111-1307, USA * +# * * +# ***************************************************************************/ + +# adapted from http://stackoverflow.com/a/3603824/6285007 +class FrozenClass(object): + '''FrozenClass: prevents adding new attributes to class outside of __init__''' + __isfrozen = False + def __setattr__(self, key, value): + if self.__isfrozen and not hasattr(self, key): + raise TypeError( "{cls} has no attribute {attr}".format(cls= self.__class__.__name__, attr= key) ) + object.__setattr__(self, key, value) + + def _freeze(self): + self.__isfrozen = True + + def _unfreeze(self): + self.__isfrozen = False diff --git a/src/Mod/Show/TempoVis.py b/src/Mod/Show/TempoVis.py new file mode 100644 index 000000000..823530be8 --- /dev/null +++ b/src/Mod/Show/TempoVis.py @@ -0,0 +1,174 @@ +#/*************************************************************************** +# * Copyright (c) Victor Titov (DeepSOIC) * +# * (vv.titov@gmail.com) 2016 * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This library is free software; you can redistribute it and/or * +# * modify it under the terms of the GNU Library General Public * +# * License as published by the Free Software Foundation; either * +# * version 2 of the License, or (at your option) any later version. * +# * * +# * This library 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 library; see the file COPYING.LIB. If not, * +# * write to the Free Software Foundation, Inc., 59 Temple Place, * +# * Suite 330, Boston, MA 02111-1307, USA * +# * * +# ***************************************************************************/ + +import FreeCAD as App +if App.GuiUp: + import FreeCADGui as Gui + +from Show.FrozenClass import FrozenClass + +from Show.DepGraphTools import getAllDependencies, getAllDependent, isContainer + +class TempoVis(FrozenClass): + '''TempoVis - helper object to save visibilities of objects before doing + some GUI editing, hiding or showing relevant stuff during edit, and + then restoring all visibilities after editing. + + Constructors: + TempoVis(document): creates a new TempoVis. Supplying document is mandatory. Objects not belonging to the document can't be modified via TempoVis.''' + + def __define_attributes(self): + self.data = {} # dict. key = ("Object","Property"), value = original value of the property + + self.cam_string = "" # inventor ASCII string representing the camera + self.viewer = None # viewer the camera is saved from + + self.document = None + self.restore_on_delete = False # if true, restore() gets called upon object deletion. It becomes False after explicit call to Restore, and set to true by many methods. + + self.links_are_lost = False # set to true after restore from JSON. Indictes to attempt to use ActiveDocument/ActiveViewer instead. + + self._freeze() + + def __init__(self, document): + self.__define_attributes() + + self.document = document + + def modifyVPProperty(self, doc_obj_or_list, prop_name, new_value): + '''modifyVPProperty(self, doc_obj_or_list, prop_name, new_value): modifies + prop_name property of ViewProvider of doc_obj_or_list, and remembers + original value of the property. Original values will be restored upon + TempoVis deletion, or call to restore().''' + + if App.GuiUp: + if type(doc_obj_or_list) is not list: + doc_obj_or_list = [doc_obj_or_list] + for doc_obj in doc_obj_or_list: + if not hasattr(doc_obj.ViewObject, prop_name): + App.Console.PrintWarning("TempoVis: object {obj} has no attribute {attr}. Skipped.\n" + .format(obj= doc_obj.Name, attr= prop_name)) + continue # silently ignore if object doesn't have the property... + + if doc_obj.Document is not self.document: #ignore objects from other documents + raise ValueError("Document object to be modified does not belong to document TempoVis was made for.") + oldval = getattr(doc_obj.ViewObject, prop_name) + setattr(doc_obj.ViewObject, prop_name, new_value) + if not self.data.has_key((doc_obj.Name,prop_name)): + self.data[(doc_obj.Name,prop_name)] = oldval + self.restore_on_delete = True + + def show(self, doc_obj_or_list): + '''show(doc_obj_or_list): shows objects (sets their Visibility to True). doc_obj_or_list can be a document object, or a list of document objects''' + self.modifyVPProperty(doc_obj_or_list, "Visibility", True) + + def hide(self, doc_obj_or_list): + '''hide(doc_obj_or_list): hides objects (sets their Visibility to False). doc_obj_or_list can be a document object, or a list of document objects''' + self.modifyVPProperty(doc_obj_or_list, "Visibility", False) + + def hide_all_dependent(self, doc_obj): + '''hide_all_dependent(doc_obj): hides all objects that depend on doc_obj. Groups, Parts and Bodies are not hidden by this.''' + self.hide( [o for o in getAllDependent(doc_obj) if not isContainer(o)]) + + def show_all_dependent(self, doc_obj): + '''show_all_dependent(doc_obj): shows all objects that depend on doc_obj. This method is probably useless.''' + self.show( getAllDependent(doc_obj) ) + + def hide_all_dependencies(self, doc_obj): + '''hide_all_dependencies(doc_obj): hides all objects that doc_obj depends on (directly and indirectly).''' + self.hide( getAllDependencies(doc_obj) ) + + def show_all_dependencies(self, doc_obj): + '''show_all_dependencies(doc_obj): shows all objects that doc_obj depends on (directly and indirectly). This method is probably useless.''' + self.show( getAllDependencies(doc_obj) ) + + def saveCamera(self): + vw = Gui.ActiveDocument.ActiveView + self.cam_string = vw.getCamera() + self.viewer = vw + + self.restore_on_delete = True + + def restoreCamera(self): + if not self.cam_string: + return + vw = self.viewer + if self.links_are_lost: # can happen after save-restore + import FreeCADGui as Gui + vw = Gui.ActiveDocument.ActiveView + + vw.setCamera(self.cam_string) + + def restore(self): + '''restore(): restore all ViewProvider properties modified via TempoVis to their + original values, and saved camera, if any. Called automatically when instance is + destroyed, unless it was called explicitly. Should not raise exceptions.''' + + if self.links_are_lost: + self.document = App.ActiveDocument + self.viewer = Gui.ActiveDocument.ActiveView + self.links_are_lost = False + + for obj_name, prop_name in self.data: + try: + setattr(self.document.getObject(obj_name).ViewObject, prop_name, self.data[(obj_name, prop_name)]) + except Exception as err: + App.Console.PrintWarning("TempoVis: failed to restore {obj}.{prop}. {err}\n" + .format(err= err.message, + obj= obj_name, + prop= prop_name)) + try: + self.restoreCamera() + except Exception as err: + App.Console.PrintWarning("TempoVis: failed to restore camera. {err}\n" + .format(err= err.message)) + self.restore_on_delete = False + + def forget(self): + '''forget(): resets TempoVis''' + self.data = {} + + self.cam_string = "" + self.viewer = None + + self.restore_on_delete = False + + def __del__(self): + if self.restore_on_delete: + self.restore() + + def __getstate__(self): + return (self.data.items(), + self.cam_string, + self.restore_on_delete) + + def __setstate__(self, state): + self.__define_attributes() + + items, self.cam_string, self.restore_on_delete = state + + # need to convert keys to tuples (dict doesn't accept list as key; tuples are converted to lists by json) + items = [(tuple(item[0]), item[1]) for item in items] + self.data = dict(items) + self.links_are_lost = True + \ No newline at end of file diff --git a/src/Mod/Show/__init__.py b/src/Mod/Show/__init__.py new file mode 100644 index 000000000..44b15b8d0 --- /dev/null +++ b/src/Mod/Show/__init__.py @@ -0,0 +1,4 @@ +__doc__ = "Show module: helper code for visibility automation." + +from Show.TempoVis import TempoVis +import Show.DepGraphTools as DepGraphTools \ No newline at end of file From 32f5466df467b3f50519a3ac405db3adba4d4f14 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sat, 18 Jun 2016 19:32:25 +0300 Subject: [PATCH 2/4] Part: AttachmentEditor: withdraw code to use Show module --- src/Mod/Part/App/CMakeLists.txt | 2 - .../Part/AttachmentEditor/DepGraphTools.py | 82 ------------- .../AttachmentEditor/TaskAttachmentEditor.py | 22 +++- src/Mod/Part/AttachmentEditor/TempoVis.py | 110 ------------------ src/Mod/Part/CMakeLists.txt | 2 - 5 files changed, 16 insertions(+), 202 deletions(-) delete mode 100644 src/Mod/Part/AttachmentEditor/DepGraphTools.py delete mode 100644 src/Mod/Part/AttachmentEditor/TempoVis.py diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 18cb8f799..29af1b8ca 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -289,11 +289,9 @@ SET(Part_Scripts JoinFeatures.py AttachmentEditor/__init__.py AttachmentEditor/Commands.py - AttachmentEditor/DepGraphTools.py AttachmentEditor/FrozenClass.py AttachmentEditor/TaskAttachmentEditor.py AttachmentEditor/TaskAttachmentEditor.ui - AttachmentEditor/TempoVis.py ) add_library(Part SHARED ${Part_SRCS}) diff --git a/src/Mod/Part/AttachmentEditor/DepGraphTools.py b/src/Mod/Part/AttachmentEditor/DepGraphTools.py deleted file mode 100644 index f5d36b541..000000000 --- a/src/Mod/Part/AttachmentEditor/DepGraphTools.py +++ /dev/null @@ -1,82 +0,0 @@ -#/*************************************************************************** -# * Copyright (c) Victor Titov (DeepSOIC) * -# * (vv.titov@gmail.com) 2016 * -# * * -# * This file is part of the FreeCAD CAx development system. * -# * * -# * This library is free software; you can redistribute it and/or * -# * modify it under the terms of the GNU Library General Public * -# * License as published by the Free Software Foundation; either * -# * version 2 of the License, or (at your option) any later version. * -# * * -# * This library 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 library; see the file COPYING.LIB. If not, * -# * write to the Free Software Foundation, Inc., 59 Temple Place, * -# * Suite 330, Boston, MA 02111-1307, USA * -# * * -# ***************************************************************************/ - -import FreeCAD as App - -def getAllDependencies(feat): - '''getAllDependencies(feat): gets all features feat depends on, directly or indirectly. - Returns a list, with deepest dependencies last. feat is not included in the list, except - if the feature depends on itself (dependency loop).''' - list_traversing_now = [feat] - set_of_deps = set() - list_of_deps = [] - - while len(list_traversing_now) > 0: - list_to_be_traversed_next = [] - for feat in list_traversing_now: - for dep in feat.OutList: - if not (dep in set_of_deps): - set_of_deps.add(dep) - list_of_deps.append(dep) - list_to_be_traversed_next.append(dep) - - list_traversing_now = list_to_be_traversed_next - - return list_of_deps - -def getAllDependent(feat): - '''getAllDependent(feat): gets all features that depend on feat, directly or indirectly. - Returns a list, with deepest dependencies last. feat is not included in the list, except - if the feature depends on itself (dependency loop).''' - list_traversing_now = [feat] - set_of_deps = set() - list_of_deps = [] - - while len(list_traversing_now) > 0: - list_to_be_traversed_next = [] - for feat in list_traversing_now: - for dep in feat.InList: - if not (dep in set_of_deps): - set_of_deps.add(dep) - list_of_deps.append(dep) - list_to_be_traversed_next.append(dep) - - list_traversing_now = list_to_be_traversed_next - - return list_of_deps - -def isContainer(obj): - '''isContainer(obj): returns True if obj is an object container, such as - Group, Part, Body. The important characterisic of an object being a - container is its action on visibility of linked objects. E.g. a - Part::Compound is not a group, because it does not affect visibility - of originals. Documents are considered containers, too.''' - - if obj.isDerivedFrom("App::DocumentObjectGroup"): - return True - if obj.isDerivedFrom("PartDesign::Body"): - return True - if obj.isDerivedFrom("App::Origin"): - return True - if obj.isDerivedFrom('App::Document'): - return True diff --git a/src/Mod/Part/AttachmentEditor/TaskAttachmentEditor.py b/src/Mod/Part/AttachmentEditor/TaskAttachmentEditor.py index f6b92b120..4c5537316 100644 --- a/src/Mod/Part/AttachmentEditor/TaskAttachmentEditor.py +++ b/src/Mod/Part/AttachmentEditor/TaskAttachmentEditor.py @@ -31,8 +31,16 @@ from Units import Degree as deg from Units import Quantity as Q from AttachmentEditor.FrozenClass import FrozenClass -from AttachmentEditor.TempoVis import TempoVis -from AttachmentEditor.DepGraphTools import getAllDependent +try: + from Show.TempoVis import TempoVis + from Show.DepGraphTools import getAllDependent +except ImportError as err: + def TempoVis(doc): + return None + def getAllDependent(feature): + return [] + App.Console.PrintWarning("AttachmentEditor: Failed to import some code from Show module. Functionality will be limited.\n") + App.Console.PrintWarning(err.message) if App.GuiUp: import FreeCADGui as Gui @@ -280,9 +288,10 @@ class AttachmentEditorTaskPanel(FrozenClass): self.updateRefButtons() self.tv = TempoVis(self.obj.Document) - self.tv.hide_all_dependent(self.obj) - self.tv.show(self.obj) - self.tv.show([obj for (obj,subname) in self.attacher.References]) + if self.tv: # tv will still be None if Show module is unavailable + self.tv.hide_all_dependent(self.obj) + self.tv.show(self.obj) + self.tv.show([obj for (obj,subname) in self.attacher.References]) # task dialog handling def getStandardButtons(self): @@ -582,4 +591,5 @@ class AttachmentEditorTaskPanel(FrozenClass): def cleanUp(self): '''stuff that needs to be done when dialog is closed.''' Gui.Selection.removeObserver(self) - self.tv.restore() \ No newline at end of file + if self.tv: + self.tv.restore() \ No newline at end of file diff --git a/src/Mod/Part/AttachmentEditor/TempoVis.py b/src/Mod/Part/AttachmentEditor/TempoVis.py deleted file mode 100644 index 39b1031d7..000000000 --- a/src/Mod/Part/AttachmentEditor/TempoVis.py +++ /dev/null @@ -1,110 +0,0 @@ -#/*************************************************************************** -# * Copyright (c) Victor Titov (DeepSOIC) * -# * (vv.titov@gmail.com) 2016 * -# * * -# * This file is part of the FreeCAD CAx development system. * -# * * -# * This library is free software; you can redistribute it and/or * -# * modify it under the terms of the GNU Library General Public * -# * License as published by the Free Software Foundation; either * -# * version 2 of the License, or (at your option) any later version. * -# * * -# * This library 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 library; see the file COPYING.LIB. If not, * -# * write to the Free Software Foundation, Inc., 59 Temple Place, * -# * Suite 330, Boston, MA 02111-1307, USA * -# * * -# ***************************************************************************/ - -import FreeCAD as App -if App.GuiUp: - import FreeCADGui as Gui - -from FrozenClass import FrozenClass - -from DepGraphTools import getAllDependencies, getAllDependent, isContainer - -class TempoVis(FrozenClass): - '''TempoVis - helper object to save visibilities of objects before doing - some GUI editing, hiding or showing relevant stuff during edit, and - then restoring all visibilities after editing. - - Constructors: - TempoVis(document): creates a new TempoVis. Supplying document is mandatory. Objects not belonging to the document can't be modified via TempoVis.''' - - def __define_attributes(self): - self.data = {} # dict. key = ("Object","Property"), value = original value of the property - self.document = None - self.restore_on_delete = False - - self._freeze() - - def __init__(self, document): - self.__define_attributes() - - self.document = document - - def modifyVPProperty(self, doc_obj_or_list, prop_name, new_value): - '''modifyVPProperty(self, doc_obj_or_list, prop_name, new_value): modifies - prop_name property of ViewProvider of doc_obj_or_list, and remembers - original value of the property. Original values will be restored upon - TempoVis deletion, or call to restore().''' - - if App.GuiUp: - if type(doc_obj_or_list) is not list: - doc_obj_or_list = [doc_obj_or_list] - for doc_obj in doc_obj_or_list: - if doc_obj.Document is not self.document: #ignore objects from other documents - raise ValueError("Document object to be modified does not belong to document TempoVis was made for.") - oldval = getattr(doc_obj.ViewObject, prop_name) - setattr(doc_obj.ViewObject, prop_name, new_value) - #assert(getattr(doc_obj.ViewObject, prop_name)==new_value) - if not self.data.has_key((doc_obj.Name,prop_name)): - self.data[(doc_obj.Name,prop_name)] = oldval - self.restore_on_delete = True - - def show(self, doc_obj_or_list): - '''show(doc_obj_or_list): shows objects (sets their Visibility to True). doc_obj_or_list can be a document object, or a list of document objects''' - self.modifyVPProperty(doc_obj_or_list, "Visibility", True) - - def hide(self, doc_obj_or_list): - '''hide(doc_obj_or_list): hides objects (sets their Visibility to False). doc_obj_or_list can be a document object, or a list of document objects''' - self.modifyVPProperty(doc_obj_or_list, "Visibility", False) - - def hide_all_dependent(self, doc_obj): - '''hide_all_dependent(doc_obj): hides all objects that depend on doc_obj. Groups, Parts and Bodies are not hidden by this.''' - self.hide( [o for o in getAllDependent(doc_obj) if not isContainer(o)]) - - def show_all_dependent(self, doc_obj): - '''show_all_dependent(doc_obj): shows all objects that depend on doc_obj. This method is probably useless.''' - self.show( getAllDependent(doc_obj) ) - - def hide_all_dependencies(self, doc_obj): - '''hide_all_dependencies(doc_obj): hides all objects that doc_obj depends on (directly and indirectly).''' - self.hide( getAllDependencies(doc_obj) ) - - def show_all_dependencies(self, doc_obj): - '''show_all_dependencies(doc_obj): shows all objects that doc_obj depends on (directly and indirectly). This method is probably useless.''' - self.show( getAllDependencies(doc_obj) ) - - def restore(self): - '''restore(): restore all ViewProvider properties modified via TempoVis to their original values. Called automatically when instance is destroyed, unless it was called explicitly.''' - for obj_name, prop_name in self.data: - setattr(self.document.getObject(obj_name).ViewObject, prop_name, self.data[(obj_name, prop_name)]) - self.restore_on_delete = False - - def forget(self): - '''forget(): resets TempoVis''' - self.data = {} - self.restore_on_delete = False - - def __del__(self): - if self.restore_on_delete: - self.restore() - - \ No newline at end of file diff --git a/src/Mod/Part/CMakeLists.txt b/src/Mod/Part/CMakeLists.txt index 7ed515703..d75a927e7 100644 --- a/src/Mod/Part/CMakeLists.txt +++ b/src/Mod/Part/CMakeLists.txt @@ -20,11 +20,9 @@ INSTALL( FILES AttachmentEditor/__init__.py AttachmentEditor/Commands.py - AttachmentEditor/DepGraphTools.py AttachmentEditor/FrozenClass.py AttachmentEditor/TaskAttachmentEditor.py AttachmentEditor/TaskAttachmentEditor.ui - AttachmentEditor/TempoVis.py DESTINATION Mod/Part/AttachmentEditor ) From d9376eb487f90f81ebc6a3616d7b2750ac0c7eb6 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Thu, 16 Jun 2016 12:51:24 +0300 Subject: [PATCH 3/4] Sketcher: visibility automation Added the following properties to Sketch ViewProvider: * 4 bools to enable/disable parts of automation * TempoVis property to hold instance of TempoVis python object, that helps with the automation --- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 85 +++++++++++++++++++-- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 5 ++ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index e1941ff46..19c301e6d 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -82,6 +82,7 @@ #include #include #include +#include #include #include #include @@ -190,7 +191,6 @@ struct EditData { std::set PreselectConstraintSet; bool blockedPreselection; bool FullyConstrained; - bool visibleBeforeEdit; // container to track our own selected parts std::set SelPointSet; @@ -255,8 +255,20 @@ ViewProviderSketch::ViewProviderSketch() : edit(0), Mode(STATUS_NONE) { - // FIXME Should this be placed in here? ADD_PROPERTY_TYPE(Autoconstraints,(true),"Auto Constraints",(App::PropertyType)(App::Prop_None),"Create auto constraints"); + ADD_PROPERTY_TYPE(TempoVis,(Py::None()),"Visibility automation",(App::PropertyType)(App::Prop_None),"Object that handles hiding and showing other objects when entering/leaving sketch."); + ADD_PROPERTY_TYPE(HideDependent,(true),"Visibility automation",(App::PropertyType)(App::Prop_None),"If true, all objects that depend on the sketch are hidden when opening editing."); + ADD_PROPERTY_TYPE(ShowLinks,(true),"Visibility automation",(App::PropertyType)(App::Prop_None),"If true, all objects used in links to external geometry are shown when opening sketch."); + ADD_PROPERTY_TYPE(ShowSupport,(true),"Visibility automation",(App::PropertyType)(App::Prop_None),"If true, all objects this sketch is attached to are shown when opening sketch."); + ADD_PROPERTY_TYPE(RestoreCamera,(true),"Visibility automation",(App::PropertyType)(App::Prop_None),"If true, camera position before entering sketch is remembered, and restored after closing it."); + + {//visibility automation: update defaults to follow preferences + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + this->HideDependent.setValue(hGrp->GetBool("HideDependent", true)); + this->ShowLinks.setValue(hGrp->GetBool("ShowLinks", true)); + this->ShowSupport.setValue(hGrp->GetBool("ShowSupport", true)); + this->RestoreCamera.setValue(hGrp->GetBool("RestoreCamera", true)); + } sPixmap = "Sketcher_Sketch"; LineColor.setValue(1,1,1); @@ -4219,8 +4231,35 @@ bool ViewProviderSketch::setEdit(int ModNum) edit->MarkerSize = hGrp->GetInt("EditSketcherMarkerSize", 7); createEditInventorNodes(); - edit->visibleBeforeEdit = this->isVisible(); - this->hide(); // avoid that the wires interfere with the edit lines + + //visibility automation + try{ + Gui::Command::addModule(Gui::Command::Gui,"Show.TempoVis"); + try{ + QString cmdstr = QString::fromLatin1( + "ActiveSketch = App.ActiveDocument.getObject('{sketch_name}')\n" + "tv = Show.TempoVis(App.ActiveDocument)\n" + "if ActiveSketch.ViewObject.HideDependent:\n" + " tv.hide_all_dependent(ActiveSketch)\n" + "if ActiveSketch.ViewObject.ShowSupport:\n" + " tv.show([ref[0] for ref in ActiveSketch.Support])\n" + "if ActiveSketch.ViewObject.ShowLinks:\n" + " tv.show([ref[0] for ref in ActiveSketch.ExternalGeometry])\n" + "tv.hide(ActiveSketch)\n" + "ActiveSketch.ViewObject.TempoVis = tv\n" + "del(tv)\n" + ); + cmdstr.replace(QString::fromLatin1("{sketch_name}"),QString::fromLatin1(this->getSketchObject()->getNameInDocument())); + QByteArray cmdstr_bytearray = cmdstr.toLatin1(); + Gui::Command::doCommand(Gui::Command::Gui, cmdstr_bytearray.data()); + } catch (Base::PyException &e){ + Base::Console().Error("ViewProviderSketch::setEdit: visibility automation failed with an error: \n"); + e.ReportException(); + } + } catch (Base::PyException &){ + Base::Console().Warning("ViewProviderSketch::setEdit: could not import Show module. Visibility automation will not work.\n"); + } + ShowGrid.setValue(true); TightGrid.setValue(false); @@ -4565,10 +4604,23 @@ void ViewProviderSketch::unsetEdit(int ModNum) edit->EditRoot->removeAllChildren(); pcRoot->removeChild(edit->EditRoot); - if (edit->visibleBeforeEdit) - this->show(); - else - this->hide(); + //visibility autoation + try{ + QString cmdstr = QString::fromLatin1( + "ActiveSketch = App.ActiveDocument.getObject('{sketch_name}')\n" + "tv = ActiveSketch.ViewObject.TempoVis\n" + "if tv:\n" + " tv.restore()\n" + "ActiveSketch.ViewObject.TempoVis = None\n" + "del(tv)\n" + ); + cmdstr.replace(QString::fromLatin1("{sketch_name}"),QString::fromLatin1(this->getSketchObject()->getNameInDocument())); + QByteArray cmdstr_bytearray = cmdstr.toLatin1(); + Gui::Command::doCommand(Gui::Command::Gui, cmdstr_bytearray.data()); + } catch (Base::PyException &e){ + Base::Console().Error("ViewProviderSketch::unsetEdit: visibility automation failed with an error: \n"); + e.ReportException(); + } delete edit; edit = 0; @@ -4601,6 +4653,23 @@ void ViewProviderSketch::unsetEdit(int ModNum) void ViewProviderSketch::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum) { + //visibility automation: save camera + if (! this->TempoVis.getValue().isNone()){ + try{ + QString cmdstr = QString::fromLatin1( + "ActiveSketch = App.ActiveDocument.getObject('{sketch_name}')\n" + "if ActiveSketch.ViewObject.RestoreCamera:\n" + " ActiveSketch.ViewObject.TempoVis.saveCamera()\n" + ); + cmdstr.replace(QString::fromLatin1("{sketch_name}"),QString::fromLatin1(this->getSketchObject()->getNameInDocument())); + QByteArray cmdstr_bytearray = cmdstr.toLatin1(); + Gui::Command::doCommand(Gui::Command::Gui, cmdstr_bytearray.data()); + } catch (Base::PyException &e){ + Base::Console().Error("ViewProviderSketch::setEdit: visibility automation failed with an error: \n"); + e.ReportException(); + } + } + Base::Placement plm = getPlacement(); Base::Rotation tmp(plm.getRotation()); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 1901404a0..01d8c3896 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -99,6 +99,11 @@ public: virtual ~ViewProviderSketch(); App::PropertyBool Autoconstraints; + App::PropertyPythonObject TempoVis; + App::PropertyBool HideDependent; + App::PropertyBool ShowLinks; + App::PropertyBool ShowSupport; + App::PropertyBool RestoreCamera; /// Draw all constraint icons /*! Except maybe the radius and lock ones? */ From 95f70b0b9d690731fba33065c7b3849815831ed1 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sun, 19 Jun 2016 23:59:42 +0300 Subject: [PATCH 4/4] Sketcher: TV: preferences UI * 4 checkboxes for setting defaults for visibility automation * "Apply to existing sketches" button --- src/Mod/Sketcher/Gui/SketcherSettings.cpp | 41 +++ src/Mod/Sketcher/Gui/SketcherSettings.h | 3 + src/Mod/Sketcher/Gui/SketcherSettings.ui | 293 +++++++++++++++++----- 3 files changed, 274 insertions(+), 63 deletions(-) diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.cpp b/src/Mod/Sketcher/Gui/SketcherSettings.cpp index 9c10869e5..dea9a0487 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.cpp +++ b/src/Mod/Sketcher/Gui/SketcherSettings.cpp @@ -26,14 +26,18 @@ #ifndef _PreComp_ # include # include +# include #endif #include "SketcherSettings.h" #include "ui_SketcherSettings.h" #include "TaskSketcherGeneral.h" +#include +#include #include #include #include +#include using namespace SketcherGui; @@ -82,6 +86,8 @@ SketcherSettings::SketcherSettings(QWidget* parent) ui->comboBox->addItem(QIcon(px), QString(), QVariant(it->second)); } + + connect(ui->btnTVApply, SIGNAL(clicked(bool)), this, SLOT(onBtnTVApplyClicked(bool))); } /** @@ -119,6 +125,10 @@ void SketcherSettings::saveSettings() ui->dialogOnDistanceConstraint->onSave(); ui->continueMode->onSave(); ui->checkBoxAdvancedSolverTaskBox->onSave(); + ui->checkBoxTVHideDependent->onSave(); + ui->checkBoxTVShowLinks->onSave(); + ui->checkBoxTVShowSupport->onSave(); + ui->checkBoxTVRestoreCamera->onSave(); form->saveSettings(); ParameterGrp::handle hViewGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); @@ -157,6 +167,10 @@ void SketcherSettings::loadSettings() ui->dialogOnDistanceConstraint->onRestore(); ui->continueMode->onRestore(); ui->checkBoxAdvancedSolverTaskBox->onRestore(); + ui->checkBoxTVHideDependent->onRestore(); + ui->checkBoxTVShowLinks->onRestore(); + ui->checkBoxTVShowSupport->onRestore(); + ui->checkBoxTVRestoreCamera->onRestore(); form->loadSettings(); std::list sizes = Gui::Inventor::MarkerBitmaps::getSupportedSizes("CIRCLE_FILLED"); @@ -189,5 +203,32 @@ void SketcherSettings::changeEvent(QEvent *e) } } +void SketcherSettings::onBtnTVApplyClicked(bool) +{ + QString errMsg; + try{ + Gui::Command::doCommand(Gui::Command::Gui, + "for name,doc in App.listDocuments().items():\n" + " for sketch in doc.findObjects('Sketcher::SketchObject'):\n" + " sketch.ViewObject.HideDependent = %s\n" + " sketch.ViewObject.ShowLinks = %s\n" + " sketch.ViewObject.ShowSupport = %s\n" + " sketch.ViewObject.RestoreCamera = %s\n", + this->ui->checkBoxTVHideDependent->isChecked() ? "True": "False", + this->ui->checkBoxTVShowLinks->isChecked() ? "True": "False", + this->ui->checkBoxTVShowSupport->isChecked() ? "True": "False", + this->ui->checkBoxTVRestoreCamera->isChecked() ? "True": "False"); + } catch (Base::PyException &e){ + Base::Console().Error("SketcherSettings::onBtnTVApplyClicked:\n"); + e.ReportException(); + errMsg = QString::fromLatin1(e.what()); + } catch (...) { + errMsg = tr("Unexpected C++ exception"); + } + if(errMsg.length()>0){ + QMessageBox::warning(this, tr("Sketcher"),errMsg); + } +} + #include "moc_SketcherSettings.cpp" diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.h b/src/Mod/Sketcher/Gui/SketcherSettings.h index 5f4d85bca..07c2d3b7c 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.h +++ b/src/Mod/Sketcher/Gui/SketcherSettings.h @@ -47,6 +47,9 @@ public: protected: void changeEvent(QEvent *e); +private Q_SLOTS: + void onBtnTVApplyClicked(bool); + private: Ui_SketcherSettings* ui; SketcherGeneralWidget* form; diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.ui b/src/Mod/Sketcher/Gui/SketcherSettings.ui index eb5c1285b..0b60d4197 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.ui +++ b/src/Mod/Sketcher/Gui/SketcherSettings.ui @@ -6,14 +6,49 @@ 0 0 - 404 - 744 + 427 + 1253 Sketcher + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Sketch Solver + + + + + + Show Advanced Solver Control in the Task bar + + + ShowSolverAdvancedWidget + + + Mod/Sketcher + + + + + + @@ -40,7 +75,7 @@ The color of edges being edited - + 255 255 @@ -73,7 +108,7 @@ The color of vertices being edited - + 255 255 @@ -106,7 +141,7 @@ The color of edges being edited - + 255 255 @@ -139,7 +174,7 @@ The color of vertices being edited - + 255 38 @@ -172,7 +207,7 @@ The color of fully constrained geometry in edit mode - + 255 38 @@ -192,7 +227,7 @@ The color of construction geometry in edit mode - + 0 0 @@ -225,7 +260,7 @@ The color of external geometry in edit mode - + 204 51 @@ -271,7 +306,7 @@ The color of fully constrained geometry in edit mode - + 0 255 @@ -397,7 +432,7 @@ - + 0 0 @@ -446,7 +481,7 @@ The color of driving constraints in edit mode - + 255 38 @@ -466,7 +501,7 @@ The color of non-driving constrains or dimensions in edit mode - + 0 38 @@ -492,19 +527,6 @@ Sketch editing - - - - - 182 - 0 - - - - Font size - - - @@ -540,13 +562,16 @@ - - - - - + + + + + 182 + 0 + + - Grid line pattern + Font size @@ -573,6 +598,16 @@ + + + + + + + Grid line pattern + + + @@ -589,44 +624,156 @@ - - - - - - - Sketch Solver - - - - - - Show Advanced Solver Control in the Task bar + + + + true - - ShowSolverAdvancedWidget + + + 0 + 0 + - - Mod/Sketcher + + + 0 + 0 + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + Visibility automation + + + + + + When opening sketch, hide all features that depend on it. + + + Hide all objects that depend on the sketch + + + true + + + HideDependent + + + Mod/Sketcher/General + + + + + + + When opening sketch, show sources for external geometry links. + + + Show objects used for external geometry + + + true + + + ShowLinks + + + Mod/Sketcher/General + + + + + + + When opening sketch, show objects the sketch is attached to. + + + Show object(s) sketch is attached to + + + true + + + ShowSupport + + + Mod/Sketcher/General + + + + + + + When closing sketch, move camera back to where it was before sketch was opened. + + + Restore camera position after editing + + + true + + + RestoreCamera + + + Mod/Sketcher/General + + + + + + + + 0 + 0 + + + + Note: these settings are defaults applied to new sketches. The behavior is remembered for each sketch individually as properties on View tab. + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + true + + + + + + + + 0 + 0 + + + + Apply current smart visibility to all sketches in open documents (update properties to match). + + + Apply to existing sketches + + + + - - - - Qt::Vertical - - - - 20 - 217 - - - - @@ -652,11 +799,31 @@ - CursorTextColor + SketchEdgeColor + SketchVertexColor EditedEdgeColor EditedVertexColor ConstructionColor + ExternalColor FullyConstrainedColor + ConstrainedColor + NonDrivingConstraintColor + DatumColor + SketcherDatumWidth + DefaultSketcherVertexWidth + DefaultSketcherLineWidth + CursorTextColor + EditSketcherFontSize + EditSketcherMarkerSize + comboBox + dialogOnDistanceConstraint + continueMode + checkBoxTVHideDependent + checkBoxTVShowLinks + checkBoxTVShowSupport + checkBoxTVRestoreCamera + btnTVApply + checkBoxAdvancedSolverTaskBox