diff --git a/src/Gui/DlgSettingsImage.ui b/src/Gui/DlgSettingsImage.ui index d47dd263bd..a06837cf41 100644 --- a/src/Gui/DlgSettingsImage.ui +++ b/src/Gui/DlgSettingsImage.ui @@ -6,8 +6,8 @@ 0 0 - 459 - 552 + 291 + 498 @@ -29,122 +29,6 @@ 6 - - - - false - - - Image comment - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Insert MIBA - - - true - - - - - - - false - - - - - - - Insert comment - - - - - - - Add watermark - - - - - - - - - - Image properties - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Back&ground: - - - comboBackground - - - - - - - - Current - - - - - White - - - - - Black - - - - - Transparent - - - - - - - @@ -183,57 +67,14 @@ 6 - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 1 - - - 32767 - - - - - - - 1 - - - 32767 - - - - - + + - Pixel + Standard sizes: - - - - &Width: - - - spinWidth - - - - + @@ -352,46 +193,33 @@ - - + + - Standard sizes: + &Width: + + + spinWidth - - - - Qt::Horizontal + + + + 1 - - - 40 - 20 - + + 32767 - + - - + + Pixel - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -402,6 +230,23 @@ + + + + 1 + + + 32767 + + + + + + + Pixel + + + @@ -429,7 +274,7 @@ - + &Screen @@ -439,7 +284,7 @@ - + &4:3 @@ -449,7 +294,7 @@ - + 1&6:9 @@ -459,7 +304,7 @@ - + &1:1 @@ -473,25 +318,144 @@ - - + + - Method + Image properties - + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + Back&ground: + + + comboBackground + + + + + + + + Current + + + + + White + + + + + Black + + + + + Transparent + + + + + Creation method: - + + + + + false + + + Image comment + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + Insert MIBA + + + true + + + + + + + Insert comment + + + + + + + false + + + + 0 + 0 + + + + + 0 + 70 + + + + + + + + Add watermark + + + + + + @@ -504,6 +468,7 @@ buttonRatio16x9 buttonRatio1x1 comboBackground + comboMethod radioButtonMiba radioButtonComment textEditComment diff --git a/src/Gui/DlgSettingsImageImp.cpp b/src/Gui/DlgSettingsImageImp.cpp index 1e981e9a19..126c4f5f03 100644 --- a/src/Gui/DlgSettingsImageImp.cpp +++ b/src/Gui/DlgSettingsImageImp.cpp @@ -252,10 +252,10 @@ void DlgSettingsImageImp::on_comboMethod_activated(int index) { QByteArray data = ui->comboMethod->itemData(index).toByteArray(); if (data == QByteArray("GrabFramebuffer")) { - ui->groupBoxProp->setEnabled(false); + ui->comboBackground->setEnabled(false); } else { - ui->groupBoxProp->setEnabled(true); + ui->comboBackground->setEnabled(true); } } diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index f359d0a266..3bfd2a2db6 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -290,16 +290,6 @@ void Gui::QuantitySpinBox::setExpression(boost::shared_ptr expr) } } -void Gui::QuantitySpinBox::setTooltipLE(const QString &name) -{ - lineEdit()->setToolTip(name); -} - -void Gui::QuantitySpinBox::setTooltipIL(const QString &name) -{ - iconLabel->setToolTip(name); -} - QString QuantitySpinBox::boundToName() const { if (isBound()) { @@ -394,8 +384,7 @@ void Gui::QuantitySpinBox::onChange() p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - iconLabel->setToolTip(QString()); - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -406,7 +395,6 @@ void Gui::QuantitySpinBox::onChange() lineEdit()->setPalette(p); iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } @@ -460,7 +448,7 @@ void QuantitySpinBox::resizeEvent(QResizeEvent * event) p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -470,9 +458,8 @@ void QuantitySpinBox::resizeEvent(QResizeEvent * event) QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } catch (const Base::Exception & e) { setReadOnly(true); @@ -489,7 +476,7 @@ void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent *event) if (event->text() == QString::fromUtf8("=") && isBound()) openFormulaDialog(); else if (!hasExpression()) - QAbstractSpinBox::keyPressEvent(event); + QAbstractSpinBox::keyPressEvent(event); } @@ -853,6 +840,16 @@ void QuantitySpinBox::showEvent(QShowEvent * event) bool QuantitySpinBox::event(QEvent * event) { + // issue #0004059: Tooltips for Gui::QuantitySpinBox not showing + // Here we must not try to show the tooltip of the icon label + // because it would override a custom tooltip set to this widget. + // + // We could also check if the text of this tooltip is empty but + // it will fail in cases where the widget is embedded into the + // property editor and the corresponding item has set a tooltip. + // Instead of showing the item's tooltip it will again show the + // tooltip of the icon label. +#if 0 if (event->type() == QEvent::ToolTip) { if (isBound() && getExpression() && lineEdit()->isReadOnly()) { QHelpEvent * helpEvent = static_cast(event); @@ -862,6 +859,7 @@ bool QuantitySpinBox::event(QEvent * event) return true; } } +#endif return QAbstractSpinBox::event(event); } diff --git a/src/Gui/QuantitySpinBox.h b/src/Gui/QuantitySpinBox.h index 0a4a2b5da7..cc9c9ddaea 100644 --- a/src/Gui/QuantitySpinBox.h +++ b/src/Gui/QuantitySpinBox.h @@ -129,10 +129,6 @@ public: bool event(QEvent *event); void setExpression(boost::shared_ptr expr); - /// Sets a tooltip for the LineEdit - void setTooltipLE(const QString &name); - /// Sets a tooltip for the IconLabel - void setTooltipIL(const QString &name); void bind(const App::ObjectIdentifier &_path); bool apply(const std::string &propName); diff --git a/src/Gui/SpinBox.cpp b/src/Gui/SpinBox.cpp index 0a0470cda0..c0d6114c29 100644 --- a/src/Gui/SpinBox.cpp +++ b/src/Gui/SpinBox.cpp @@ -292,7 +292,7 @@ void UIntSpinBox::onChange() { p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -300,9 +300,8 @@ void UIntSpinBox::onChange() { QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } @@ -344,7 +343,7 @@ void UIntSpinBox::resizeEvent(QResizeEvent * event) p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -354,9 +353,8 @@ void UIntSpinBox::resizeEvent(QResizeEvent * event) QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } catch (const Base::Exception & e) { setReadOnly(true); @@ -489,7 +487,7 @@ void IntSpinBox::onChange() { p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -497,9 +495,8 @@ void IntSpinBox::onChange() { QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } void IntSpinBox::resizeEvent(QResizeEvent * event) @@ -525,7 +522,7 @@ void IntSpinBox::resizeEvent(QResizeEvent * event) p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -535,9 +532,8 @@ void IntSpinBox::resizeEvent(QResizeEvent * event) QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } catch (const Base::Exception & e) { setReadOnly(true); @@ -670,7 +666,7 @@ void DoubleSpinBox::onChange() { p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -678,9 +674,8 @@ void DoubleSpinBox::onChange() { QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } void DoubleSpinBox::resizeEvent(QResizeEvent * event) @@ -706,7 +701,7 @@ void DoubleSpinBox::resizeEvent(QResizeEvent * event) p.setColor(QPalette::Text, Qt::lightGray); lineEdit()->setPalette(p); } - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -716,9 +711,8 @@ void DoubleSpinBox::resizeEvent(QResizeEvent * event) QPalette p(lineEdit()->palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); lineEdit()->setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } catch (const Base::Exception & e) { setReadOnly(true); @@ -727,7 +721,6 @@ void DoubleSpinBox::resizeEvent(QResizeEvent * event) lineEdit()->setPalette(p); iconLabel->setToolTip(QString::fromLatin1(e.what())); } - } void DoubleSpinBox::openFormulaDialog() diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index b760d2da4e..fb331c4550 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -1489,7 +1489,7 @@ void ExpLineEdit::onChange() { QPalette p(palette()); p.setColor(QPalette::Text, Qt::lightGray); setPalette(p); - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -1497,9 +1497,8 @@ void ExpLineEdit::onChange() { QPalette p(palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } void ExpLineEdit::resizeEvent(QResizeEvent * event) @@ -1520,7 +1519,7 @@ void ExpLineEdit::resizeEvent(QResizeEvent * event) QPalette p(palette()); p.setColor(QPalette::Text, Qt::lightGray); setPalette(p); - setToolTip(Base::Tools::fromStdString(getExpression()->toString())); + iconLabel->setToolTip(Base::Tools::fromStdString(getExpression()->toString())); } else { setReadOnly(false); @@ -1530,9 +1529,8 @@ void ExpLineEdit::resizeEvent(QResizeEvent * event) QPalette p(palette()); p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text)); setPalette(p); - + iconLabel->setToolTip(QString()); } - iconLabel->setToolTip(QString()); } catch (const Base::Exception & e) { setReadOnly(true); diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 08342febc6..3bd5f36d47 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -589,6 +589,10 @@ class CommandAddonManager: pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") self.config.checkUpdates.setChecked(pref.GetBool("AutoCheck",False)) self.config.customRepositories.setPlainText(pref.GetString("CustomRepositories","")) + self.config.radioButtonNoProxy.setChecked(pref.GetBool("NoProxyCheck",True)) + self.config.radioButtonSystemProxy.setChecked(pref.GetBool("SystemProxyCheck",False)) + self.config.radioButtonUserProxy.setChecked(pref.GetBool("UserProxyCheck",False)) + self.config.userProxy.setPlainText(pref.GetString("ProxyUrl","")) # center the dialog over the Addon Manager self.config.move(self.dialog.frameGeometry().topLeft() + self.dialog.rect().center() - self.config.rect().center()) @@ -599,6 +603,10 @@ class CommandAddonManager: # OK button has been pressed pref.SetBool("AutoCheck",self.config.checkUpdates.isChecked()) pref.SetString("CustomRepositories",self.config.customRepositories.toPlainText()) + pref.SetBool("NoProxyCheck",self.config.radioButtonNoProxy.isChecked()) + pref.SetBool("SystemProxyCheck",self.config.radioButtonSystemProxy.isChecked()) + pref.SetBool("UserProxyCheck",self.config.radioButtonUserProxy.isChecked()) + pref.SetString("ProxyUrl",self.config.userProxy.toPlainText()) def check_updates(addon_name,callback): diff --git a/src/Mod/AddonManager/AddonManagerOptions.ui b/src/Mod/AddonManager/AddonManagerOptions.ui index cb88650cd6..709e222fc4 100644 --- a/src/Mod/AddonManager/AddonManagerOptions.ui +++ b/src/Mod/AddonManager/AddonManagerOptions.ui @@ -7,7 +7,7 @@ 0 0 390 - 183 + 247 @@ -24,6 +24,9 @@ installed addons will be checked for available updates Automatically check for updates at start (requires GitPython) + + false + @@ -41,6 +44,47 @@ sto be scanned for available addons + + + + Qt::Horizontal + + + + + + + Proxy + + + + + + + No proxy + + + + + + + User system proxy + + + + + + + User defined proxy : + + + true + + + + + + @@ -62,12 +106,12 @@ sto be scanned for available addons accept() - 248 - 254 + 257 + 237 157 - 274 + 246 @@ -78,12 +122,12 @@ sto be scanned for available addons reject() - 316 - 260 + 325 + 237 286 - 274 + 246 diff --git a/src/Mod/AddonManager/addonmanager_macro.py b/src/Mod/AddonManager/addonmanager_macro.py index c23e5e5319..e7039ff500 100644 --- a/src/Mod/AddonManager/addonmanager_macro.py +++ b/src/Mod/AddonManager/addonmanager_macro.py @@ -103,6 +103,9 @@ class Macro(object): except: print("AddonManager: Debug: unable to open URL",url) return + if u is None : + print("AddonManager: Debug: connection is lost (proxy setting changed?)",url) + return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode('utf-8') diff --git a/src/Mod/AddonManager/addonmanager_utilities.py b/src/Mod/AddonManager/addonmanager_utilities.py index 037e22124b..799cb64f36 100644 --- a/src/Mod/AddonManager/addonmanager_utilities.py +++ b/src/Mod/AddonManager/addonmanager_utilities.py @@ -40,12 +40,12 @@ except ImportError: pass else: try: - ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) except AttributeError: pass + - - + def translate(context, text, disambig=None): "Main translation function" @@ -93,18 +93,35 @@ def urlopen(url): import urllib2 else: import urllib.request as urllib2 + + # Proxy an ssl configuration + pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") + if pref.GetBool("NoProxyCheck",True): + proxies = {} + else: + if pref.GetBool("SystemProxyCheck",False): + proxy = urllib2.getproxies() + proxies = {"http": proxy.get('http'),"https": proxy.get('http')} + elif pref.GetBool("UserProxyCheck",False): + proxy = pref.GetString("ProxyUrl","") + proxies = {"http": proxy, "https": proxy} + if ssl_ctx: + handler = urllib2.HTTPSHandler(context=ssl_ctx) + else: + handler = {} + proxy_support = urllib2.ProxyHandler(proxies) + opener = urllib2.build_opener(proxy_support, handler) + urllib2.install_opener(opener) + + # Url opening try: - if ssl_ctx: - u = urllib2.urlopen(url, context=ssl_ctx, timeout=timeout) - else: - u = urllib2.urlopen(url, timeout=timeout) + u = urllib2.urlopen(url, timeout=timeout) except: return None else: return u - def getserver(url): """returns the server part of an url""" diff --git a/src/Mod/Arch/exportIFC.py b/src/Mod/Arch/exportIFC.py index cfeee7e9f2..c5f2717310 100644 --- a/src/Mod/Arch/exportIFC.py +++ b/src/Mod/Arch/exportIFC.py @@ -224,8 +224,8 @@ def export(exportList,filename,colors=None,preferences=None): objectslist = [obj for obj in objectslist if obj != contextCreator.project_object] if Draft.getObjectsOfType(objectslist, "Site"): # we assume one site and one representation context only - trueNorthX = math.tan(-Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian)) - contextCreator.model_context.TrueNorth.DirectionRatios = (trueNorthX, 1.) + decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian) + contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2)) products = {} # { Name: IfcEntity, ... } subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 8295110fe5..23fc52ae45 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -836,9 +836,10 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): obj.PostalCode = product.SiteAddress.PostalCode project = product.Decomposes[0].RelatingObject modelRC = next((rc for rc in project.RepresentationContexts if rc.ContextType == "Model"), None) - if modelRC and modelRC.TrueNorth and modelRC.TrueNorth.DirectionRatios[1] > 0: - obj.Declination = -math.degrees(math.atan(modelRC.TrueNorth.DirectionRatios[0] / modelRC.TrueNorth.DirectionRatios[1])) - if(FreeCAD.GuiUp): + if modelRC and modelRC.TrueNorth: + (x, y) = modelRC.TrueNorth.DirectionRatios[:2] + obj.Declination = ((math.degrees(math.atan2(y,x))-90+180)%360)-180 + if (FreeCAD.GuiUp): obj.ViewObject.CompassRotation.Value = obj.Declination try: 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 8a5501891a..52da755062 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,132 +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 autogroup is present, object is added to autogroup. - If an Arch container is active, add given object to it. - If an App::Part container is active, add given object to it - and update the object placement so it doesn't jump on other - position in respect of it's creation. - - Parameter - -------- - obj : object.Name - Name of the object to add to the group. - - TODO - -------- - add support for App::Part with Draft Annotations. - - """ - if FreeCAD.GuiUp: - # Arch active container - active_part = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("part") - # Arch active container - active_arch_obj = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") - if hasattr(FreeCADGui,"draftToolBar"): - if (hasattr(FreeCADGui.draftToolBar,"autogroup") - and not FreeCADGui.draftToolBar.isConstructionMode() - ): - if FreeCADGui.draftToolBar.autogroup is not None: - active_group = FreeCAD.ActiveDocument.getObject(FreeCADGui.draftToolBar.autogroup) - if active_group: - found = False - for o in active_group.Group: - if o.Name == obj.Name: - found = True - if not found: - gr = active_group.Group - gr.append(obj) - active_group.Group = gr - elif active_arch_obj: - active_arch_obj.addObject(obj) - elif active_part: - inverse_placement = active_part.getGlobalPlacement().inverse() - if getType(obj) == 'Point': - # point vector have a kind of placement, so should be - # processed before generic object with placement - point_vector = FreeCAD.Vector(obj.X, obj.Y, obj.Z) - real_point = inverse_placement.multVec(point_vector) - obj.X = real_point.x - obj.Y = real_point.y - obj.Z = real_point.z - elif hasattr(obj,"Placement"): - obj.Placement = FreeCAD.Placement(inverse_placement.multiply(obj.Placement)) - active_part.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 @@ -266,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 @@ -358,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/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index dbef8c3ba2..67865dd3a8 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -1218,8 +1218,9 @@ def offsetWire(wire,dvec,bind=False,occ=False,widthList=None, offsetMode=None, a 'dvec' vector to offset is now derived (and can be ignored) in this function if widthList and alignList are provided - 'dvec' to be obsolete in future ? ''' - # Accept 'wire' as a list of edges, use the list directly, or previously a wire - if isinstance(wire,Part.Wire): + # Accept 'wire' as a list of edges (use the list directly), or previously as a wire or a face (Draft Wire with MakeFace True or False supported) + + if isinstance(wire,Part.Wire) or isinstance(wire,Part.Face): edges = wire.Edges # Seems has repeatedly sortEdges, remark out here - edges = Part.__sortEdges__(wire.Edges) elif isinstance(wire, list): if isinstance(wire[0],Part.Edge): @@ -1273,7 +1274,7 @@ def offsetWire(wire,dvec,bind=False,occ=False,widthList=None, offsetMode=None, a firstDir = 1 firstAlign = 'Center' except: - print ("alignListC[0] has no value ") # Should no longer happen for ArchWall - as aligns are 'filled in' by ArchWall + pass # Should no longer happen for ArchWall - as aligns are 'filled in' by ArchWall # If not provided by alignListC checked above, check the direction of offset in dvec (not 'align') diff --git a/src/Mod/Draft/drafttests/test_modification.py b/src/Mod/Draft/drafttests/test_modification.py index ca6d0fc4df..75e51b3ee2 100644 --- a/src/Mod/Draft/drafttests/test_modification.py +++ b/src/Mod/Draft/drafttests/test_modification.py @@ -106,7 +106,26 @@ class DraftModification(unittest.TestCase): self.assertTrue(obj.Start.isEqual(c, 1e-12), "'{}' failed".format(operation)) - def test_offset(self): + def test_offset_open(self): + """Create a wire, then produce an offset copy.""" + operation = "Draft Offset" + _msg(" Test '{}'".format(operation)) + a = Vector(0, 2, 0) + b = Vector(2, 4, 0) + c = Vector(5, 2, 0) + _msg(" Wire") + _msg(" a={0}, b={1}".format(a, b)) + _msg(" c={0}".format(c)) + wire = Draft.makeWire([a, b, c]) + App.ActiveDocument.recompute() + + offset = Vector(-1, 1, 0) + _msg(" Offset") + _msg(" vector={}".format(offset)) + obj = Draft.offset(wire, offset, copy=True) + self.assertTrue(obj, "'{}' failed".format(operation)) + + def test_offset_closed(self): """Create a rectangle, then produce an offset copy.""" operation = "Draft Offset" _msg(" Test '{}'".format(operation)) 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 diff --git a/src/Mod/Fem/feminout/importCcxFrdResults.py b/src/Mod/Fem/feminout/importCcxFrdResults.py index 0a4bb2b4b8..81917224d4 100644 --- a/src/Mod/Fem/feminout/importCcxFrdResults.py +++ b/src/Mod/Fem/feminout/importCcxFrdResults.py @@ -74,6 +74,8 @@ def importFrd( m = read_frd_result(filename) result_mesh_object = None + res_obj = None + if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) result_mesh_object = ObjectsFem.makeMeshResult( @@ -166,15 +168,29 @@ def importFrd( else: error_message = ( - "We have nodes but no results in frd file, " - "which means we only have a mesh in frd file. " - "Usually this happens for analysis type 'NOANALYSIS' " - "or if CalculiX returned no results because " - "of nonpositive jacobian determinant in at least one element.\n" + "Nodes, but no results found in frd file. " + "It means there only is a mesh but no results in frd file. " + "Usually this happens for: \n" + "- analysis type 'NOANALYSIS'\n" + "- if CalculiX returned no results " + "(happens on nonpositive jacobian determinant in at least one element)\n" + "- just no frd results where requestet in input file " + "(neither 'node file' nor 'el file' in output section')\n" ) Console.PrintMessage(error_message) + + # create a result obj, even if we have no results but a result mesh in frd file + # see error message above for more information + if not res_obj: + if result_name_prefix: + results_name = ("{}_Results".format(result_name_prefix)) + else: + results_name = ("Results".format(result_name_prefix)) + res_obj = ObjectsFem.makeResultMechanical(FreeCAD.ActiveDocument, results_name) + res_obj.Mesh = result_mesh_object + # TODO, node numbers in result obj could be set if analysis: - analysis.addObject(result_mesh_object) + analysis.addObject(res_obj) if FreeCAD.GuiUp: if analysis: @@ -186,6 +202,9 @@ def importFrd( Console.PrintError( "Problem on frd file import. No nodes found in frd file.\n" ) + # None will be returned + # or would it be better to raise an exception if there are not even nodes in frd file + return res_obj diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index a4e988b5a6..4668f3036f 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -215,12 +215,6 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider,QWidget Gui::Document* document = Gui::Application::Instance->getDocument(ViewProvider->getObject()->getDocument()); connectDelObject = document->signalDeletedObject.connect(bnd1); connectDelDocument = document->signalDeleteDocument.connect(bnd2); - - // set tooltips because the ones in the .ui file are not taken for QuantitySpinBoxes (see bug https://freecadweb.org/tracker/view.php?id=4059) - // FIXME: remove this once the bug is fixed - ui->attachmentOffsetRoll->setTooltipLE(QString::fromUtf8("Rotation around the x-axis\nNote: The placement is expressed in local coordinate system\nof object being attached.")); - ui->attachmentOffsetPitch->setTooltipLE(QString::fromUtf8("Rotation around the y-axis\nNote: The placement is expressed in local coordinate system\nof object being attached.")); - ui->attachmentOffsetYaw->setTooltipLE(QString::fromUtf8("Rotation around the z-axis\nNote: The placement is expressed in local coordinate system\nof object being attached.")); } TaskAttacher::~TaskAttacher() @@ -745,10 +739,12 @@ void TaskAttacher::updateAttachmentOffsetUI() ui->attachmentOffsetPitch->setEnabled(!bRotationBound); ui->attachmentOffsetRoll->setEnabled(!bRotationBound); - QString tooltip = bRotationBound ? tr("Not editable because rotation of AttachmentOffset is bound by expressions.") : QString(); - ui->attachmentOffsetYaw->setToolTip(tooltip); - ui->attachmentOffsetPitch->setToolTip(tooltip); - ui->attachmentOffsetRoll->setToolTip(tooltip); + if (bRotationBound) { + QString tooltip = tr("Not editable because rotation of AttachmentOffset is bound by expressions."); + ui->attachmentOffsetYaw->setToolTip(tooltip); + ui->attachmentOffsetPitch->setToolTip(tooltip); + ui->attachmentOffsetRoll->setToolTip(tooltip); + } bBlock = false; ui->attachmentOffsetX->blockSignals(bBlock); diff --git a/src/Mod/PartDesign/WizardShaft/SegmentFunction.py b/src/Mod/PartDesign/WizardShaft/SegmentFunction.py index 3aac232e44..5a9bdc7ee5 100644 --- a/src/Mod/PartDesign/WizardShaft/SegmentFunction.py +++ b/src/Mod/PartDesign/WizardShaft/SegmentFunction.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c) 2012 Jan Rheinländer * # * * diff --git a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py index 026e4d9683..b0fdf29f72 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c) 2012 Jan Rheinländer * # * * diff --git a/src/Mod/PartDesign/WizardShaft/ShaftFeature.py b/src/Mod/PartDesign/WizardShaft/ShaftFeature.py index 9413ca1a35..82e9271335 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftFeature.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftFeature.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c) 2012 Jan Rheinländer * # * * diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaft.py b/src/Mod/PartDesign/WizardShaft/WizardShaft.py index 35d2b1db7e..e08da93870 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaft.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaft.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c) 2012 Jan Rheinländer * # * * diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py index f3dcb726c4..a37b205183 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c) 2012 Jan Rheinländer * # * * diff --git a/src/Mod/PartDesign/WizardShaft/__init__.py b/src/Mod/PartDesign/WizardShaft/__init__.py index d32e9e3c29..670cb443f4 100644 --- a/src/Mod/PartDesign/WizardShaft/__init__.py +++ b/src/Mod/PartDesign/WizardShaft/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Shaft Wizard """