diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index ebc882625d..5d7476dcd6 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -50,6 +50,7 @@ SET(Draft_tests SET(Draft_utilities draftutils/__init__.py draftutils/utils.py + draftutils/gui_utils.py ) SET(Draft_objects diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 09ecea6176..bb903e40e3 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -80,6 +80,8 @@ makeLayer = DraftLayer.makeLayer # General functions #--------------------------------------------------------------------------- import draftutils.utils +import draftutils.gui_utils + arrowtypes = draftutils.utils.ARROW_TYPES stringencodecoin = draftutils.utils.string_encode_coin @@ -110,20 +112,8 @@ get_type = draftutils.utils.get_type getObjectsOfType = draftutils.utils.get_objects_of_type get_objects_of_type = draftutils.utils.get_objects_of_type - -def get3DView(): - """get3DView(): returns the current view if it is 3D, or the first 3D view found, or None""" - if FreeCAD.GuiUp: - import FreeCADGui - v = FreeCADGui.ActiveDocument.ActiveView - if "View3DInventor" in str(type(v)): - return v - #print("Debug: Draft: Warning, not working in active view") - v = FreeCADGui.ActiveDocument.mdiViewsOfType("Gui::View3DInventor") - if v: - return v[0] - return None - +get3DView = draftutils.gui_utils.get_3d_view +get_3d_view = draftutils.gui_utils.get_3d_view isClone = draftutils.utils.is_clone is_clone = draftutils.utils.is_clone @@ -133,98 +123,21 @@ get_group_names = draftutils.utils.get_group_names ungroup = draftutils.utils.ungroup +autogroup = draftutils.gui_utils.autogroup -def autogroup(obj): - """adds a given object to the autogroup, if applicable""" - if FreeCAD.GuiUp: - if hasattr(FreeCADGui,"draftToolBar"): - if hasattr(FreeCADGui.draftToolBar,"autogroup") and (not FreeCADGui.draftToolBar.isConstructionMode()): - if FreeCADGui.draftToolBar.autogroup != None: - g = FreeCAD.ActiveDocument.getObject(FreeCADGui.draftToolBar.autogroup) - if g: - found = False - for o in g.Group: - if o.Name == obj.Name: - found = True - if not found: - gr = g.Group - gr.append(obj) - g.Group = gr - else: - # Arch active container - a = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") - if a: - a.addObject(obj) - -def dimSymbol(symbol=None,invert=False): - """returns the current dim symbol from the preferences as a pivy SoMarkerSet""" - if symbol is None: - symbol = getParam("dimsymbol",0) - from pivy import coin - if symbol == 0: - return coin.SoSphere() - elif symbol == 1: - marker = coin.SoMarkerSet() - marker.markerIndex = FreeCADGui.getMarkerIndex("circle", 9) - return marker - elif symbol == 2: - marker = coin.SoSeparator() - t = coin.SoTransform() - t.translation.setValue((0,-2,0)) - t.center.setValue((0,2,0)) - if invert: - t.rotation.setValue(coin.SbVec3f((0,0,1)),-math.pi/2) - else: - t.rotation.setValue(coin.SbVec3f((0,0,1)),math.pi/2) - c = coin.SoCone() - c.height.setValue(4) - marker.addChild(t) - marker.addChild(c) - return marker - elif symbol == 3: - marker = coin.SoSeparator() - c = coin.SoCoordinate3() - c.point.setValues([(-1,-2,0),(0,2,0),(1,2,0),(0,-2,0)]) - f = coin.SoFaceSet() - marker.addChild(c) - marker.addChild(f) - return marker - elif symbol == 4: - return dimDash((-1.5,-1.5,0),(1.5,1.5,0)) - else: - print("Draft.dimsymbol: Not implemented") - return coin.SoSphere() - -def dimDash(p1, p2): - """dimDash(p1, p2): returns pivy SoSeparator. - Used for making Tick-2, DimOvershoot, ExtOvershoot dashes. - """ - from pivy import coin - dash = coin.SoSeparator() - v = coin.SoVertexProperty() - v.vertex.set1Value(0, p1) - v.vertex.set1Value(1, p2) - l = coin.SoLineSet() - l.vertexProperty = v - dash.addChild(l) - return dash +dimSymbol = draftutils.gui_utils.dim_symbol +dim_symbol = draftutils.gui_utils.dim_symbol +dimDash = draftutils.gui_utils.dim_dash +dim_dash = draftutils.gui_utils.dim_dash shapify = draftutils.utils.shapify getGroupContents = draftutils.utils.get_group_contents get_group_contents = draftutils.utils.get_group_contents - -def removeHidden(objectslist): - """removeHidden(objectslist): removes hidden objects from the list""" - newlist = objectslist[:] - for o in objectslist: - if o.ViewObject: - if not o.ViewObject.isVisible(): - newlist.remove(o) - return newlist - +removeHidden = draftutils.gui_utils.remove_hidden +remove_hidden = draftutils.gui_utils.remove_hidden printShape = draftutils.utils.print_shape print_shape = draftutils.utils.print_shape @@ -232,91 +145,16 @@ print_shape = draftutils.utils.print_shape compareObjects = draftutils.utils.compare_objects compare_objects = draftutils.utils.compare_objects +formatObject = draftutils.gui_utils.format_object +format_object = draftutils.gui_utils.format_object -def formatObject(target,origin=None): - """ - formatObject(targetObject,[originObject]): This function applies - to the given target object the current properties - set on the toolbar (line color and line width), - or copies the properties of another object if given as origin. - It also places the object in construction group if needed. - """ - if not target: - return - obrep = target.ViewObject - if not obrep: - return - ui = None - if gui: - if hasattr(FreeCADGui,"draftToolBar"): - ui = FreeCADGui.draftToolBar - if ui: - doc = FreeCAD.ActiveDocument - if ui.isConstructionMode(): - col = fcol = ui.getDefaultColor("constr") - gname = getParam("constructiongroupname","Construction") - grp = doc.getObject(gname) - if not grp: - grp = doc.addObject("App::DocumentObjectGroup",gname) - grp.addObject(target) - if hasattr(obrep,"Transparency"): - obrep.Transparency = 80 - else: - col = ui.getDefaultColor("ui") - fcol = ui.getDefaultColor("face") - col = (float(col[0]),float(col[1]),float(col[2]),0.0) - fcol = (float(fcol[0]),float(fcol[1]),float(fcol[2]),0.0) - lw = ui.linewidth - fs = ui.fontsize - if not origin or not hasattr(origin,'ViewObject'): - if "FontSize" in obrep.PropertiesList: obrep.FontSize = fs - if "TextColor" in obrep.PropertiesList: obrep.TextColor = col - if "LineWidth" in obrep.PropertiesList: obrep.LineWidth = lw - if "PointColor" in obrep.PropertiesList: obrep.PointColor = col - if "LineColor" in obrep.PropertiesList: obrep.LineColor = col - if "ShapeColor" in obrep.PropertiesList: obrep.ShapeColor = fcol - else: - matchrep = origin.ViewObject - for p in matchrep.PropertiesList: - if not p in ["DisplayMode","BoundingBox","Proxy","RootNode","Visibility"]: - if p in obrep.PropertiesList: - if not obrep.getEditorMode(p): - if hasattr(getattr(matchrep,p),"Value"): - val = getattr(matchrep,p).Value - else: - val = getattr(matchrep,p) - try: - setattr(obrep,p,val) - except Exception: - pass - if matchrep.DisplayMode in obrep.listDisplayModes(): - obrep.DisplayMode = matchrep.DisplayMode - if hasattr(matchrep,"DiffuseColor") and hasattr(obrep,"DiffuseColor"): - obrep.DiffuseColor = matchrep.DiffuseColor +getSelection = draftutils.gui_utils.get_selection +get_selection = draftutils.gui_utils.get_selection -def getSelection(): - """getSelection(): returns the current FreeCAD selection""" - if gui: - return FreeCADGui.Selection.getSelection() - return None - -def getSelectionEx(): - """getSelectionEx(): returns the current FreeCAD selection (with subobjects)""" - if gui: - return FreeCADGui.Selection.getSelectionEx() - return None - -def select(objs=None): - """select(object): deselects everything and selects only the passed object or list""" - if gui: - FreeCADGui.Selection.clearSelection() - if objs: - if not isinstance(objs,list): - objs = [objs] - for obj in objs: - if obj: - FreeCADGui.Selection.addSelection(obj) +getSelectionEx = draftutils.gui_utils.get_selection_ex +get_selection_ex = draftutils.gui_utils.get_selection_ex +select = draftutils.gui_utils.select loadSvgPatterns = draftutils.utils.load_svg_patterns load_svg_patterns = draftutils.utils.load_svg_patterns @@ -324,86 +162,8 @@ load_svg_patterns = draftutils.utils.load_svg_patterns svgpatterns = draftutils.utils.svg_patterns svg_patterns = draftutils.utils.svg_patterns - -def loadTexture(filename,size=None): - """loadTexture(filename,[size]): returns a SoSFImage from a file. If size - is defined (an int or a tuple), and provided the input image is a png file, - it will be scaled to match the given size.""" - if gui: - from pivy import coin - from PySide import QtGui,QtSvg - try: - p = QtGui.QImage(filename) - # buggy - TODO: allow to use resolutions - #if size and (".svg" in filename.lower()): - # # this is a pattern, not a texture - # if isinstance(size,int): - # size = (size,size) - # svgr = QtSvg.QSvgRenderer(filename) - # p = QtGui.QImage(size[0],size[1],QtGui.QImage.Format_ARGB32) - # pa = QtGui.QPainter() - # pa.begin(p) - # svgr.render(pa) - # pa.end() - #else: - # p = QtGui.QImage(filename) - size = coin.SbVec2s(p.width(), p.height()) - buffersize = p.byteCount() - numcomponents = int (float(buffersize) / ( size[0] * size[1] )) - - img = coin.SoSFImage() - width = size[0] - height = size[1] - byteList = [] - isPy2 = sys.version_info.major < 3 - - for y in range(height): - #line = width*numcomponents*(height-(y)); - for x in range(width): - rgb = p.pixel(x,y) - if numcomponents == 1: - if isPy2: - byteList.append(chr(QtGui.qGray( rgb ))) - else: - byteList.append(chr(QtGui.qGray( rgb )).encode('latin-1')) - elif numcomponents == 2: - if isPy2: - byteList.append(chr(QtGui.qGray( rgb ))) - byteList.append(chr(QtGui.qAlpha( rgb ))) - else: - byteList.append(chr(QtGui.qGray( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qAlpha( rgb )).encode('latin-1')) - elif numcomponents == 3: - if isPy2: - byteList.append(chr(QtGui.qRed( rgb ))) - byteList.append(chr(QtGui.qGreen( rgb ))) - byteList.append(chr(QtGui.qBlue( rgb ))) - else: - byteList.append(chr(QtGui.qRed( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qGreen( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qBlue( rgb )).encode('latin-1')) - elif numcomponents == 4: - if isPy2: - byteList.append(chr(QtGui.qRed( rgb ))) - byteList.append(chr(QtGui.qGreen( rgb ))) - byteList.append(chr(QtGui.qBlue( rgb ))) - byteList.append(chr(QtGui.qAlpha( rgb ))) - else: - byteList.append(chr(QtGui.qRed( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qGreen( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qBlue( rgb )).encode('latin-1')) - byteList.append(chr(QtGui.qAlpha( rgb )).encode('latin-1')) - #line += numcomponents - - bytes = b"".join(byteList) - img.setValue(size, numcomponents, bytes) - except: - print("Draft: unable to load texture") - return None - else: - return img - return None - +loadTexture = draftutils.gui_utils.load_texture +load_texture = draftutils.gui_utils.load_texture getMovableChildren = draftutils.utils.get_movable_children get_movable_children = draftutils.utils.get_movable_children diff --git a/src/Mod/Draft/draftutils/gui_utils.py b/src/Mod/Draft/draftutils/gui_utils.py new file mode 100644 index 0000000000..6f1cbb7ce2 --- /dev/null +++ b/src/Mod/Draft/draftutils/gui_utils.py @@ -0,0 +1,612 @@ +"""This module provides GUI utility functions for the Draft Workbench. + +This module should contain auxiliary functions which require +the graphical user interface (GUI). +""" +## @package gui_utils +# \ingroup DRAFT +# \brief This module provides utility functions for the Draft Workbench + +# *************************************************************************** +# * (c) 2009, 2010 * +# * Yorik van Havre , Ken Cline * +# * (c) 2019 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + + +import FreeCAD +import FreeCADGui +from .utils import _msg +from .utils import _wrn +# from .utils import _log +from .utils import _tr +from .utils import getParam +from pivy import coin +from PySide import QtGui +# from PySide import QtSvg # for load_texture +import os +import math +import six + + +def get_3d_view(): + """Return the current 3D view. + + Returns + ------- + Gui::View3DInventor + Return the current `ActiveView` in the active document, + or the first `Gui::View3DInventor` view found. + + Return `None` if the graphical interface is not available. + """ + if FreeCAD.GuiUp: + v = FreeCADGui.ActiveDocument.ActiveView + if "View3DInventor" in str(type(v)): + return v + + # print("Debug: Draft: Warning, not working in active view") + v = FreeCADGui.ActiveDocument.mdiViewsOfType("Gui::View3DInventor") + if v: + return v[0] + + _wrn(_tr("No graphical interface")) + return None + + +get3DView = get_3d_view + + +def autogroup(obj): + """Adds a given object to the defined Draft autogroup, if applicable. + + This function only works if the graphical interface is available. + It checks that the `FreeCAD.draftToolBar` class is available, + which contains the group to use to automatically store + new created objects. + + Originally, it worked with standard groups (`App::DocumentObjectGroup`), + and Arch Workbench containers like `'Site'`, `'Building'`, `'Floor'`, + and `'BuildingPart'`. + + Now it works with Draft Layers. + + Parameters + ---------- + obj : App::DocumentObject + Any type of object that will be stored in the group. + """ + doc = FreeCAD.ActiveDocument + if FreeCAD.GuiUp: + view = FreeCADGui.ActiveDocument.ActiveView + if hasattr(FreeCADGui, "draftToolBar"): + if (hasattr(FreeCADGui.draftToolBar, "autogroup") + and not FreeCADGui.draftToolBar.isConstructionMode()): + if FreeCADGui.draftToolBar.autogroup is not None: + g = doc.getObject(FreeCADGui.draftToolBar.autogroup) + if g: + found = False + for o in g.Group: + if o.Name == obj.Name: + found = True + if not found: + gr = g.Group + gr.append(obj) + g.Group = gr + else: + # Arch active container + a = view.getActiveObject("Arch") + if a: + a.addObject(obj) + + +def dim_symbol(symbol=None, invert=False): + """Return the specified dimension symbol. + + Parameters + ---------- + symbol : int, optional + It defaults to `None`, in which it gets the value from the parameter + database, `get_param("dimsymbol", 0)`. + + A numerical value defines different markers + * 0, `SoSphere` + * 1, `SoMarkerSet` with a circle + * 2, `SoSeparator` with a `soCone` + * 3, `SoSeparator` with a `SoFaceSet` + * 4, `SoSeparator` with a `SoLineSet`, calling `dim_dash` + * Otherwise, `SoSphere` + + invert : bool, optional + It defaults to `False`. + If it is `True` and `symbol=2`, the cone will be rotated + -90 degrees around the Z axis, otherwise the rotation is positive, + +90 degrees. + + Returns + ------- + Coin.SoNode + A `Coin.SoSphere`, or `Coin.SoMarkerSet` (circle), + or `Coin.SoSeparator` (cone, face, line) + that will be used as a dimension symbol. + """ + if symbol is None: + symbol = getParam("dimsymbol", 0) + + if symbol == 0: + return coin.SoSphere() + elif symbol == 1: + marker = coin.SoMarkerSet() + marker.markerIndex = FreeCADGui.getMarkerIndex("circle", 9) + return marker + elif symbol == 2: + marker = coin.SoSeparator() + t = coin.SoTransform() + t.translation.setValue((0, -2, 0)) + t.center.setValue((0, 2, 0)) + if invert: + t.rotation.setValue(coin.SbVec3f((0, 0, 1)), -math.pi/2) + else: + t.rotation.setValue(coin.SbVec3f((0, 0, 1)), math.pi/2) + c = coin.SoCone() + c.height.setValue(4) + marker.addChild(t) + marker.addChild(c) + return marker + elif symbol == 3: + marker = coin.SoSeparator() + c = coin.SoCoordinate3() + c.point.setValues([(-1, -2, 0), (0, 2, 0), + (1, 2, 0), (0, -2, 0)]) + f = coin.SoFaceSet() + marker.addChild(c) + marker.addChild(f) + return marker + elif symbol == 4: + return dimDash((-1.5, -1.5, 0), (1.5, 1.5, 0)) + else: + _wrn(_tr("Symbol not implemented. Use a default symbol.")) + return coin.SoSphere() + + +dimSymbol = dim_symbol + + +def dim_dash(p1, p2): + """Return a SoSeparator with a line used to make dimension dashes. + + It is used by `dim_symbol` to create line end symbols + like `'Tick-2'`, `'DimOvershoot'`, and `'ExtOvershoot'` dashes. + + Parameters + ---------- + p1 : tuple of three floats or Base::Vector3 + A point to define a line vertex. + + p2 : tuple of three floats or Base::Vector3 + A point to define a line vertex. + + Returns + ------- + Coin.SoSeparator + A Coin object with a `SoLineSet` created from `p1` and `p2` + as vertices. + """ + dash = coin.SoSeparator() + v = coin.SoVertexProperty() + v.vertex.set1Value(0, p1) + v.vertex.set1Value(1, p2) + line = coin.SoLineSet() + line.vertexProperty = v + dash.addChild(line) + return dash + + +dimDash = dim_dash + + +def remove_hidden(objectslist): + """Return only the visible objects in the list. + + This function only works if the graphical interface is available + as the `Visibility` attribute is a property of the view provider + (`obj.ViewObject`). + + Parameters + ---------- + objectslist : list of App::DocumentObject + List of any type of object. + + Returns + ------- + list + Return a copy of the input list without those objects + for which `obj.ViewObject.Visibility` is `False`. + + If the graphical interface is not loaded + the returned list is just a copy of the input list. + """ + newlist = objectslist[:] + for obj in objectslist: + if obj.ViewObject: + if not obj.ViewObject.isVisible(): + newlist.remove(obj) + _msg(_tr("Visibility off; removed from list: ") + obj.Label) + return newlist + + +removeHidden = remove_hidden + + +def format_object(target, origin=None): + """Apply visual properties from the Draft toolbar or another object. + + This function only works if the graphical interface is available + as the visual properties are attributes of the view provider + (`obj.ViewObject`). + + Parameters + ---------- + target : App::DocumentObject + Any type of scripted object. + + This object will adopt the applicable visual properties, + `FontSize`, `TextColor`, `LineWidth`, `PointColor`, `LineColor`, + and `ShapeColor`, defined in the Draft toolbar + (`FreeCADGui.draftToolBar`) or will adopt + the properties from the `origin` object. + + The `target` is also placed in the construction group + if the construction mode in the Draft toolbar is active. + + origin : App::DocumentObject, optional + It defaults to `None`. + If it exists, it will provide the visual properties to assign + to `target`, with the exception of `BoundingBox`, `Proxy`, + `RootNode` and `Visibility`. + """ + if not target: + return + obrep = target.ViewObject + if not obrep: + return + ui = None + if FreeCAD.GuiUp: + if hasattr(FreeCADGui, "draftToolBar"): + ui = FreeCADGui.draftToolBar + if ui: + doc = FreeCAD.ActiveDocument + if ui.isConstructionMode(): + col = fcol = ui.getDefaultColor("constr") + gname = getParam("constructiongroupname", "Construction") + grp = doc.getObject(gname) + if not grp: + grp = doc.addObject("App::DocumentObjectGroup", gname) + grp.addObject(target) + if hasattr(obrep, "Transparency"): + obrep.Transparency = 80 + else: + col = ui.getDefaultColor("ui") + fcol = ui.getDefaultColor("face") + col = (float(col[0]), float(col[1]), float(col[2]), 0.0) + fcol = (float(fcol[0]), float(fcol[1]), float(fcol[2]), 0.0) + lw = ui.linewidth + fs = ui.fontsize + if not origin or not hasattr(origin, 'ViewObject'): + if "FontSize" in obrep.PropertiesList: + obrep.FontSize = fs + if "TextColor" in obrep.PropertiesList: + obrep.TextColor = col + if "LineWidth" in obrep.PropertiesList: + obrep.LineWidth = lw + if "PointColor" in obrep.PropertiesList: + obrep.PointColor = col + if "LineColor" in obrep.PropertiesList: + obrep.LineColor = col + if "ShapeColor" in obrep.PropertiesList: + obrep.ShapeColor = fcol + else: + matchrep = origin.ViewObject + for p in matchrep.PropertiesList: + if p not in ("DisplayMode", "BoundingBox", + "Proxy", "RootNode", "Visibility"): + if p in obrep.PropertiesList: + if not obrep.getEditorMode(p): + if hasattr(getattr(matchrep, p), "Value"): + val = getattr(matchrep, p).Value + else: + val = getattr(matchrep, p) + try: + setattr(obrep, p, val) + except Exception: + pass + if matchrep.DisplayMode in obrep.listDisplayModes(): + obrep.DisplayMode = matchrep.DisplayMode + if (hasattr(matchrep, "DiffuseColor") + and hasattr(obrep, "DiffuseColor")): + obrep.DiffuseColor = matchrep.DiffuseColor + + +formatObject = format_object + + +def get_selection(gui=FreeCAD.GuiUp): + """Return the current selected objects. + + This function only works if the graphical interface is available + as the selection module only works on the 3D view. + + It wraps around `FreeCADGui.Selection.getSelection` + + Parameters + ---------- + gui : bool, optional + It defaults to the value of `FreeCAD.GuiUp`, which is `True` + when the interface exists, and `False` otherwise. + + This value can be set to `False` to simulate + when the interface is not available. + + Returns + ------- + list of App::DocumentObject + Returns a list of objects in the current selection. + It can be an empty list if no object is selected. + + If the interface is not available, it returns `None`. + """ + if gui: + return FreeCADGui.Selection.getSelection() + return None + + +getSelection = get_selection + + +def get_selection_ex(gui=FreeCAD.GuiUp): + """Return the current selected objects together with their subelements. + + This function only works if the graphical interface is available + as the selection module only works on the 3D view. + + It wraps around `FreeCADGui.Selection.getSelectionEx` + + Parameters + ---------- + gui : bool, optional + It defaults to the value of `FreeCAD.GuiUp`, which is `True` + when the interface exists, and `False` otherwise. + + This value can be set to `False` to simulate + when the interface is not available. + + Returns + ------- + list of Gui::SelectionObject + Returns a list of `Gui::SelectionObject` in the current selection. + It can be an empty list if no object is selected. + + If the interface is not available, it returns `None`. + + Selection objects + ----------------- + One `Gui::SelectionObject` has attributes that indicate which specific + subelements, that is, vertices, wires, and faces, were selected. + This can be useful to operate on the subelements themselves. + If `G` is a `Gui::SelectionObject` + * `G.Object` is the selected object + * `G.ObjectName` is the name of the selected object + * `G.HasSubObjects` is `True` if there are subelements in the selection + * `G.SubObjects` is a tuple of the subelements' shapes + * `G.SubElementNames` is a tuple of the subelements' names + + `SubObjects` and `SubElementNames` should be empty tuples + if `HasSubObjects` is `False`. + """ + if gui: + return FreeCADGui.Selection.getSelectionEx() + return None + + +getSelectionEx = get_selection_ex + + +def select(objs=None, gui=FreeCAD.GuiUp): + """Unselects everything and selects only the given list of objects. + + This function only works if the graphical interface is available + as the selection module only works on the 3D view. + + Parameters + ---------- + objs : list of App::DocumentObject, optional + It defaults to `None`. + Any type of scripted object. + It may be a list of objects or a single object. + + gui : bool, optional + It defaults to the value of `FreeCAD.GuiUp`, which is `True` + when the interface exists, and `False` otherwise. + + This value can be set to `False` to simulate + when the interface is not available. + """ + if gui: + FreeCADGui.Selection.clearSelection() + if objs: + if not isinstance(objs, list): + objs = [objs] + for obj in objs: + if obj: + FreeCADGui.Selection.addSelection(obj) + + +def load_texture(filename, size=None, gui=FreeCAD.GuiUp): + """Return a Coin.SoSFImage to use as a texture for a 2D plane. + + This function only works if the graphical interface is available + as the visual properties that can be applied to a shape + are attributes of the view provider (`obj.ViewObject`). + + Parameters + ---------- + filename : str + A path to a pixel image file (PNG) that can be used as a texture + on the face of an object. + + size : tuple of two int, or a single int, optional + It defaults to `None`. + If a tuple is given, the two values define the width and height + in pixels to which the loaded image will be scaled. + If it is a single value, it is used for both dimensions. + + If it is `None`, the size will be determined from the `QImage` + created from `filename`. + + CURRENTLY the input `size` parameter IS NOT USED. + It always uses the `QImage` to determine this information. + + gui : bool, optional + It defaults to the value of `FreeCAD.GuiUp`, which is `True` + when the interface exists, and `False` otherwise. + + This value can be set to `False` to simulate + when the interface is not available. + + Returns + ------- + coin.SoSFImage + An image object with the appropriate size, number of components + (grayscale, grayscale and transparency, color, + color and transparency), and byte data. + + It returns `None` if the interface is not available, + or if there is a problem creating the image. + """ + if gui: + # from pivy import coin + # from PySide import QtGui, QtSvg + try: + p = QtGui.QImage(filename) + + if p.isNull(): + _wrn("load_texture: " + _tr("image is Null")) + + if not os.path.exists(filename): + raise FileNotFoundError(-1, + _tr("filename does not exist " + "on the system or " + "on the resource file"), + filename) + + # This is buggy so it was de-activated. + # + # TODO: allow SVGs to use resolutions + # if size and (".svg" in filename.lower()): + # # this is a pattern, not a texture + # if isinstance(size, int): + # size = (size, size) + # svgr = QtSvg.QSvgRenderer(filename) + # p = QtGui.QImage(size[0], size[1], + # QtGui.QImage.Format_ARGB32) + # pa = QtGui.QPainter() + # pa.begin(p) + # svgr.render(pa) + # pa.end() + # else: + # p = QtGui.QImage(filename) + size = coin.SbVec2s(p.width(), p.height()) + buffersize = p.byteCount() + width = size[0] + height = size[1] + numcomponents = int(float(buffersize) / (width * height)) + + img = coin.SoSFImage() + byteList = [] + # isPy2 = sys.version_info.major < 3 + isPy2 = six.PY2 + + # The SoSFImage needs to be filled with bytes. + # The pixel information is converted into a Qt color, gray, + # red, green, blue, or transparency (alpha), + # depending on the input image. + # + # If Python 2 is used, the color is turned into a character, + # which is of type 'byte', and added to the byte list. + # If Python 3 is used, characters are unicode strings, + # so they need to be encoded into 'latin-1' + # to produce the correct bytes for the list. + for y in range(height): + # line = width*numcomponents*(height-(y)); + for x in range(width): + rgb = p.pixel(x, y) + if numcomponents == 1 or numcomponents == 2: + gray = chr(QtGui.qGray(rgb)) + if isPy2: + byteList.append(gray) + else: + byteList.append(gray.encode('latin-1')) + + if numcomponents == 2: + alpha = chr(QtGui.qAlpha(rgb)) + if isPy2: + byteList.append(alpha) + else: + byteList.append(alpha.encode('latin-1')) + elif numcomponents == 3 or numcomponents == 4: + red = chr(QtGui.qRed(rgb)) + green = chr(QtGui.qGreen(rgb)) + blue = chr(QtGui.qBlue(rgb)) + + if isPy2: + byteList.append(red) + byteList.append(green) + byteList.append(blue) + else: + byteList.append(red.encode('latin-1')) + byteList.append(green.encode('latin-1')) + byteList.append(blue.encode('latin-1')) + + if numcomponents == 4: + alpha = chr(QtGui.qAlpha(rgb)) + if isPy2: + byteList.append(alpha) + else: + byteList.append(alpha.encode('latin-1')) + # line += numcomponents + + _bytes = b"".join(byteList) + img.setValue(size, numcomponents, _bytes) + except FileNotFoundError as exc: + _wrn("load_texture: {0}, {1}".format(exc.strerror, + exc.filename)) + return None + except Exception as exc: + _wrn(str(exc)) + _wrn("load_texture: " + _tr("unable to load texture")) + return None + else: + return img + return None + + +loadTexture = load_texture