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
"""