Macro cross section/de: Difference between revisions
No edit summary |
(Created page with "ver 00.10 19/12/2020 : Upgrade durch Fix von Chris_G, der App.Parts von den Eingabeobjekten ausschließt, siehe [https://forum.freecadweb.org/viewtopic.php?f=22&t=53423 cross...") |
||
(39 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
|||
{{Macro|Icon=Text-x-python|Name=Cross_section|Description=Dynamically slidable cross-section|Author=aleph0|Version=1.0}} |
|||
{{Macro/de |
|||
|Name=Cross_section |
|||
|Icon=Macro_cross_section.png |
|||
|Translate=Cross_section |
|||
|Description=Dynamisch verschiebbarer Querschnitt |
|||
|Author=aleph0 |
|||
|Version=0.10 |
|||
|Date=2020-12-19 |
|||
|FCVersion=0.15 and above |
|||
|Download=[https://www.freecadweb.org/wiki/images/f/f5/Macro_cross_section.png ToolBar Icon] |
|||
}} |
|||
==Beschreibung== |
==Beschreibung== |
||
Durch den oder die Körper wird ein ebener Schnitt geführt. Die Normale der Schnittebene entsteht durch Vorgabe je eines Vektors in den drei Ebenen des xyz-Koordinatensystems. Die Schnittebene kann parallel zu sich stetig hin und her verschoben werden. Angezeigt werden entweder die durch den Schnitt enstandenen Linien (Durchdringungsfigur) oder der Rest des oder der Körper (einer der beiden durch den Schnitt entstandenen Teile des oder der Körper wird ausgeblendet). |
Durch den oder die Körper wird ein ebener Schnitt geführt. Die Normale der Schnittebene entsteht durch Vorgabe je eines Vektors in den drei Ebenen des xyz-Koordinatensystems. Die Schnittebene kann parallel zu sich stetig hin und her verschoben werden. Angezeigt werden entweder die durch den Schnitt enstandenen Linien (Durchdringungsfigur) oder der Rest des oder der Körper (einer der beiden durch den Schnitt entstandenen Teile des oder der Körper wird ausgeblendet). |
||
Bildlegende: |
|||
[[File:Cross-section.png]] |
[[File:Cross-section.png]] |
||
Line 12: | Line 21: | ||
Schnitt durch zwei teiweise ineinander steckende Köper; |
Schnitt durch zwei teiweise ineinander steckende Köper; |
||
links: Dialogfeld "Körper-Schnitt"; |
links: Dialogfeld "Körper-Schnitt"; |
||
rechts: geschnittener Körper-Rest |
rechts: "geschnittener Körper-Rest" (schräge Schnittebene, Schnittebenen-Normale (Sliding axis direction) unter 45° im 1. Sektor der xz-Ebene (X=1, Z=1)) |
||
[[File:Cross-section.png|center]] |
|||
. |
|||
[[File:Macro Cross Section 02.png|center]] |
|||
==Anwendung== |
==Anwendung== |
||
Line 24: | Line 36: | ||
# Der/die zu schneidender/n Körper wird/werden bereits geschnitten dargestellt. |
# Der/die zu schneidender/n Körper wird/werden bereits geschnitten dargestellt. |
||
# Über dem "Dialog-Fenster" (Combo-Ansicht) erscheint das oben abgebildete Dialogfeld "Körper-Schnitt" (Cross section ). |
# Über dem "Dialog-Fenster" (Combo-Ansicht) erscheint das oben abgebildete Dialogfeld "Körper-Schnitt" (Cross section ). |
||
## Wahl der Schnittebenen-Normale durch einsetzen von Werten für die Vektorlängen ", y und/ |
## Wahl der Schnittebenen-Normale durch einsetzen von Werten für die Vektorlängen "x, y und/oder z" |
||
## Den blauen Schieber "Position des Schnittes entlang der Schnittflächen-Normale" (Position along axis) nach Wahl zwischen 0% und 100% einstellen (grün blinkende Skala, Anfangswert 50%) |
## Den blauen Schieber "Position des Schnittes entlang der Schnittflächen-Normale" (Position along axis) nach Wahl zwischen 0% und 100% einstellen (grün blinkende Skala, Anfangswert 50%) |
||
## Wahl des gewünschten Schnittbildes durch Anklicken von "Durchdringungsfigur" (Outline, ist voreingestellt) oder "Geschnittener Körper-Rest" (Cut Objects) |
## Wahl des gewünschten Schnittbildes durch Anklicken von "Durchdringungsfigur" (Outline, ist voreingestellt) oder "Geschnittener Körper-Rest" (Cut Objects) |
||
# |
# Wahlweises "Speichern des Schnitt-Ergebnisses " (Keep the sectional view) erfolgt beim Schließen des Dialogfeldes "Körper-Schnitt" (Cross section , Kreuz rechts oben). |
||
==LINKS== |
==LINKS== |
||
siehe [http://forum.freecadweb.org/viewtopic.php?f=28&t=15084&p=120159#p120159 forum] |
|||
Mario52 has created a new version of this macro that allows you to save both the object and the outline of the section. |
|||
See [http://forum.freecadweb.org/viewtopic.php?f=28&t=15084&p=120159#p120159 forum] |
|||
[[File:Macro_Cross_Section_01.png|center|150px]] |
[[File:Macro_Cross_Section_01.png|center|150px]] |
||
siehe rechts unten: "Speichern des Schnitt-Ergebnisses" (Keep the sectional view). |
|||
==Skript== |
|||
Changes by Mario52 include. |
|||
Changes by Sam include. |
|||
Changes by Gift include. |
|||
Changes by duke24 include. |
|||
Changes by g.becu include. |
|||
Changes by Chris_G include. |
|||
ToolBar Icon [[Image:Macro_cross_section.png]] |
|||
'''Macro cross section.FCMacro''' |
|||
==Script== |
|||
{{ |
{{MacroCode|code= |
||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
""" |
""" |
||
Line 79: | Line 108: | ||
__title__ = "Cross-Section" |
__title__ = "Cross-Section" |
||
__author__ = "Aleph0" |
__author__ = "Aleph0" |
||
__version__ = "00. |
__version__ = "00.10" |
||
__date__ = " |
__date__ = "19/12/2020" |
||
# "29/01/2016" "10/06/2016" "29/09/2016 * 2" "17/08/2017" "06/09/2017" "17/09/2017" "04/07/2019" "31/08/2019" "19/12/2020" |
|||
__Comment__ = "Dynamic cross section viewer" |
|||
# Upgrade: |
|||
# 17/08/2017 by Sam #https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=10#p187030 |
|||
# 06/09/2017 17/09/2017 by Gift #https://www.forum.freecadweb.org/viewtopic.php?f=13&t=24130 |
|||
# 04/07/2019 by duke24 replace tab to space #https://forum.freecadweb.org/viewtopic.php?f=13&t=37449 |
|||
# 31/08/2019 by g.becu adding line 334 #https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=20#p330351 |
|||
# 19/12/2020 by Chris_G : exlude App.Part from the list of input objects + many code style fixes (PEP8) |
|||
# See forum : https://forum.freecadweb.org/viewtopic.php?f=22&t=53423 |
|||
__Comment__ = "Dynamic cross section viewer" |
|||
__Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_cross_section" |
__Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_cross_section" |
||
__Help__ = "see first few lines of macro text" |
__Help__ = "see first few lines of macro text" |
||
Line 89: | Line 127: | ||
__Requires__ = "freecad 0.15" |
__Requires__ = "freecad 0.15" |
||
# OS: Ubuntu 14.04.3 LTS |
# OS: Ubuntu 14.04.3 LTS # OS: Windows 10 |
||
# Word size of OS: 64-bit |
# Word size of OS: 64-bit # Word size of OS: 64-bit |
||
# Word size of FreeCAD: 64-bit |
# Word size of FreeCAD: 64-bit # Word size of FreeCAD: 64-bit |
||
# Version: 0.15.4671 (Git) |
# Version: 0.15.4671 (Git) # Version: 0.19.17171 (Git) |
||
# Branch: releases/FreeCAD-0-15 |
# Branch: releases/FreeCAD-0-15 # Build type: Release |
||
# Hash: 244b3aef360841646cbfe80a1b225c8b39c8380c |
# Hash: 244b3aef360841646cbfe80a1b225c8b39c8380c# Branch: master |
||
# Python version: 2.7.6 |
# Python version: 2.7.6 # Hash: d19470a9711ea604f3ca6c93e46afadf64d5bb87 |
||
# Qt version: 4.8.6 |
# Qt version: 4.8.6 # Python version: 3.6.6 |
||
# Coin version: 4.0.0a |
# Coin version: 4.0.0a # Qt version: 5.6.2 |
||
# OCC version: 6.8.0.oce-0.17 |
# OCC version: 6.8.0.oce-0.17 # Coin version: 4.0.0a |
||
# # OCC version: 7.3.0 |
|||
import PySide |
import PySide |
||
from PySide import QtCore, QtGui |
from PySide import QtCore, QtGui |
||
import FreeCAD as App |
|||
import FreeCADGui |
import FreeCADGui |
||
import Part |
|||
import Draft |
|||
from FreeCAD import Base |
|||
try: |
try: |
||
Line 113: | Line 155: | ||
try: |
try: |
||
_encoding = QtGui.QApplication.UnicodeUTF8 |
_encoding = QtGui.QApplication.UnicodeUTF8 |
||
def _translate(context, text, disambig): |
def _translate(context, text, disambig): |
||
return QtGui.QApplication.translate(context, text, disambig, _encoding) |
return QtGui.QApplication.translate(context, text, disambig, _encoding) |
||
Line 120: | Line 163: | ||
width = 350 # width of our window |
width = 350 # width of our window |
||
height = 310 # height of our window |
height = 310 # height of our window |
||
global FreeCADRootWindow # main window before we start |
global FreeCADRootWindow # main window before we start |
||
# This is the interactive window that the macro creates |
# This is the interactive window that the macro creates |
||
# Its main function is to have a close box and tidy up when closed |
# Its main function is to have a close box and tidy up when closed |
||
class CrossSectionWindow(PySide.QtGui.QMainWindow): |
class CrossSectionWindow(PySide.QtGui.QMainWindow): |
||
# automagically called when the window is created |
# automagically called when the window is created |
||
def __init__(self): |
def __init__(self): |
||
Line 139: | Line 182: | ||
self.setCentralWidget(self.child) |
self.setCentralWidget(self.child) |
||
self.child.initUI(self) |
self.child.initUI(self) |
||
if self.child.startup_failed |
if self.child.startup_failed: |
||
self.destroy() |
self.destroy() |
||
else: |
else: |
||
Line 148: | Line 191: | ||
# User closed the window, tidy up |
# User closed the window, tidy up |
||
def closeEvent(self, event) |
def closeEvent(self, event): |
||
self.child.restoreObjects() |
self.child.restoreObjects() |
||
Line 154: | Line 197: | ||
# With FreeCAD's default layout, this will be over the docking area |
# With FreeCAD's default layout, this will be over the docking area |
||
# so it will not obscure the 3D view |
# so it will not obscure the 3D view |
||
def setPosition(self, parent) |
def setPosition(self, parent): |
||
geom = parent.geometry() |
geom = parent.geometry() |
||
xpos = geom.left() + 50 |
xpos = geom.left() + 50 |
||
Line 160: | Line 203: | ||
self.setGeometry(xpos, ypos, width, height) |
self.setGeometry(xpos, ypos, width, height) |
||
self.setFixedSize(width, height) |
self.setFixedSize(width, height) |
||
# This is the widget which does almost all of the work |
# This is the widget which does almost all of the work |
||
# Widgets don't have close boxes, so closing is dealt with in CrossSectionWindow |
# Widgets don't have close boxes, so closing is dealt with in CrossSectionWindow |
||
class CrossSection(PySide.QtGui.QWidget): |
class CrossSection(PySide.QtGui.QWidget): |
||
# Lay out the interactive elements |
# Lay out the interactive elements |
||
def initUI(self, parent): |
def initUI(self, parent): |
||
self.parent = parent |
self.parent = parent |
||
font = QtGui.QFont() |
font = QtGui.QFont() |
||
font.setFamily("Times New Roman") |
font.setFamily("Times New Roman") |
||
Line 175: | Line 216: | ||
font.setWeight(10) |
font.setWeight(10) |
||
font.setBold(True) |
font.setBold(True) |
||
self.hideObjects() |
self.hideObjects() |
||
margin = 10 |
margin = 10 |
||
alw = 20 # axis label width |
alw = 20 # axis label width |
||
sbp = margin + alw + margin # spin boxes to right of axis labels |
sbp = margin + alw + margin # spin boxes to right of axis labels |
||
slw = width - 2 * margin # width of slider and progress bar |
slw = width - 2 * margin # width of slider and progress bar |
||
ypos = margin |
ypos = margin |
||
Line 189: | Line 229: | ||
# The length of the normal is immaterial as long as it is nonzero |
# The length of the normal is immaterial as long as it is nonzero |
||
self.label_1 = QtGui.QLabel(self) |
self.label_1 = QtGui.QLabel(self) |
||
self.label_1.setGeometry(QtCore.QRect(margin, ypos, slw, 22)) |
self.label_1.setGeometry(QtCore.QRect(margin, ypos, slw, 22)) |
||
self.label_1.setObjectName(_fromUtf8("label_1")) |
self.label_1.setObjectName(_fromUtf8("label_1")) |
||
self.label_1.setText(_translate( |
self.label_1.setText(_translate( |
||
Line 204: | Line 244: | ||
self.doubleSpinBox_X = QtGui.QDoubleSpinBox(self) |
self.doubleSpinBox_X = QtGui.QDoubleSpinBox(self) |
||
self.doubleSpinBox_X.setGeometry(QtCore.QRect(sbp, ypos, |
self.doubleSpinBox_X.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) |
||
self.doubleSpinBox_X.setMinimum(-10000.0) |
self.doubleSpinBox_X.setMinimum(-10000.0) |
||
self.doubleSpinBox_X.setMaximum(10000.0) |
self.doubleSpinBox_X.setMaximum(10000.0) |
||
Line 211: | Line 251: | ||
self.doubleSpinBox_X.setObjectName(_fromUtf8("doubleSpinBox_X")) |
self.doubleSpinBox_X.setObjectName(_fromUtf8("doubleSpinBox_X")) |
||
self.doubleSpinBox_X.valueChanged.connect( |
self.doubleSpinBox_X.valueChanged.connect( |
||
self.on_doubleSpinBox_X_valueChanged) |
self.on_doubleSpinBox_X_valueChanged) |
||
self.doubleSpinBox_X.setToolTip(_translate( |
self.doubleSpinBox_X.setToolTip(_translate( |
||
"MainWindow", "Sliding axis X", None)) |
"MainWindow", "Sliding axis X", None)) |
||
Line 222: | Line 262: | ||
self.doubleSpinBox_Y = QtGui.QDoubleSpinBox(self) |
self.doubleSpinBox_Y = QtGui.QDoubleSpinBox(self) |
||
self.doubleSpinBox_Y.setGeometry(QtCore.QRect(sbp, ypos, |
self.doubleSpinBox_Y.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) |
||
self.doubleSpinBox_Y.setMinimum(-10000.0) |
self.doubleSpinBox_Y.setMinimum(-10000.0) |
||
self.doubleSpinBox_Y.setMaximum(10000.0) |
self.doubleSpinBox_Y.setMaximum(10000.0) |
||
Line 229: | Line 269: | ||
self.doubleSpinBox_Y.setObjectName(_fromUtf8("doubleSpinBox_Y")) |
self.doubleSpinBox_Y.setObjectName(_fromUtf8("doubleSpinBox_Y")) |
||
self.doubleSpinBox_Y.valueChanged.connect( |
self.doubleSpinBox_Y.valueChanged.connect( |
||
self.on_doubleSpinBox_Y_valueChanged) |
self.on_doubleSpinBox_Y_valueChanged) |
||
self.doubleSpinBox_Y.setToolTip(_translate( |
self.doubleSpinBox_Y.setToolTip(_translate( |
||
"MainWindow", "Sliding axis Y", None)) |
"MainWindow", "Sliding axis Y", None)) |
||
Line 240: | Line 280: | ||
self.doubleSpinBox_Z = QtGui.QDoubleSpinBox(self) |
self.doubleSpinBox_Z = QtGui.QDoubleSpinBox(self) |
||
self.doubleSpinBox_Z.setGeometry(QtCore.QRect(sbp, ypos, |
self.doubleSpinBox_Z.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) |
||
self.doubleSpinBox_Z.setMinimum(-10000.0) |
self.doubleSpinBox_Z.setMinimum(-10000.0) |
||
self.doubleSpinBox_Z.setMaximum(10000.0) |
self.doubleSpinBox_Z.setMaximum(10000.0) |
||
self.doubleSpinBox_Z.setValue( |
self.doubleSpinBox_Z.setValue(1.0) |
||
self.doubleSpinBox_Z.setSingleStep(1) |
self.doubleSpinBox_Z.setSingleStep(1) |
||
self.doubleSpinBox_Z.setObjectName(_fromUtf8("doubleSpinBox_Z")) |
self.doubleSpinBox_Z.setObjectName(_fromUtf8("doubleSpinBox_Z")) |
||
self.doubleSpinBox_Z.valueChanged.connect( |
self.doubleSpinBox_Z.valueChanged.connect( |
||
self.on_doubleSpinBox_Z_valueChanged) |
self.on_doubleSpinBox_Z_valueChanged) |
||
self.doubleSpinBox_Z.setToolTip( |
self.doubleSpinBox_Z.setToolTip( |
||
_translate("MainWindow", "Sliding axis Z", None)) |
_translate("MainWindow", "Sliding axis Z", None)) |
||
ypos = ypos + 40 |
ypos = ypos + 40 |
||
# Make the interactive slider control |
# Make the interactive slider control |
||
# As you move this slider with the mouse, the cross-section slides |
# As you move this slider with the mouse, the cross-section slides |
||
Line 263: | Line 302: | ||
self.horizontalSlider = QtGui.QSlider(self) |
self.horizontalSlider = QtGui.QSlider(self) |
||
self.horizontalSlider.setGeometry(QtCore.QRect(margin, ypos, slw, 20)) |
self.horizontalSlider.setGeometry(QtCore.QRect(margin, ypos, slw - 50, 20)) |
||
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal) |
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal) |
||
self.horizontalSlider.setInvertedAppearance(False) |
self.horizontalSlider.setInvertedAppearance(False) |
||
Line 277: | Line 316: | ||
self.progressBar_1 = QtGui.QProgressBar(self) |
self.progressBar_1 = QtGui.QProgressBar(self) |
||
self.progressBar_1.setGeometry(QtCore.QRect(margin, ypos, slw, 23)) |
self.progressBar_1.setGeometry(QtCore.QRect(margin, ypos, slw - 50, 23)) |
||
self.progressBar_1.setValue(int(self.fraction * 100.0)) |
self.progressBar_1.setValue(int(self.fraction * 100.0)) |
||
self.progressBar_1.setOrientation(QtCore.Qt.Horizontal) |
self.progressBar_1.setOrientation(QtCore.Qt.Horizontal) |
||
Line 285: | Line 324: | ||
"MainWindow", "Percent position of cross-section", None)) |
"MainWindow", "Percent position of cross-section", None)) |
||
ypos = ypos + 40 |
ypos = ypos + 40 |
||
################################## add |
|||
self.SpinBox_PBar = QtGui.QSpinBox(self) |
|||
self.SpinBox_PBar.setGeometry(QtCore.QRect(sbp+255, ypos - 40, 45, 23)) |
|||
self.SpinBox_PBar.setMinimum(0.0) |
|||
self.SpinBox_PBar.setMaximum(100.0) |
|||
self.SpinBox_PBar.setValue(int(self.fraction * 100.0)) |
|||
self.SpinBox_PBar.setSingleStep(1) |
|||
self.SpinBox_PBar.setObjectName(_fromUtf8("SpinBox_PBar")) |
|||
self.SpinBox_PBar.valueChanged.connect(self.on_SpinBox_PBar_valueChanged) |
|||
self.SpinBox_PBar.setToolTip(_translate( |
|||
"MainWindow", "Sliding percent", None)) |
|||
################################# add |
|||
# Select the display format for the cross-section |
# Select the display format for the cross-section |
||
Line 309: | Line 361: | ||
self.radioButton_2.setToolTip(_translate( |
self.radioButton_2.setToolTip(_translate( |
||
"MainWindow", "Make cross-section as cut objects", None)) |
"MainWindow", "Make cross-section as cut objects", None)) |
||
################################## |
|||
# Keep the sectional view |
|||
self.CB_00 = QtGui.QCheckBox(self) |
|||
self.CB_00.setText(_fromUtf8("Keep the sectional view")) |
|||
self.CB_00.setGeometry(QtCore.QRect(margin+168, ypos-30, slw, 20)) |
|||
self.CB_00.setObjectName(_fromUtf8("CB_00")) |
|||
self.CB_00.setChecked(False) |
|||
self.CB_00.setToolTip(_translate( |
|||
"MainWindow", "Keep the sectional view or erase", None)) |
|||
################################## |
|||
# Called at macro start up by initUI() |
# Called at macro start up by initUI() |
||
Line 321: | Line 384: | ||
# We must have something to do a cross-section on |
# We must have something to do a cross-section on |
||
if App.ActiveDocument is None |
if App.ActiveDocument is None: |
||
QtGui.QMessageBox.warning(None, _translate("MainWindow", |
QtGui.QMessageBox.warning(None, _translate("MainWindow", |
||
"Cross-section", None), |
"Cross-section", None), |
||
Line 329: | Line 392: | ||
QtGui.QMessageBox.Cancel) |
QtGui.QMessageBox.Cancel) |
||
self.startup_failed = True |
self.startup_failed = True |
||
self.parent.destroy() # This will close the window |
self.parent.destroy() # This will close the window |
||
else |
else: |
||
# Make a list of the user's objects |
# Make a list of the user's objects |
||
Line 336: | Line 399: | ||
# This list is persistent. We'll get confused if the user deletes or |
# This list is persistent. We'll get confused if the user deletes or |
||
# adds objects while the macro is active |
# adds objects while the macro is active |
||
self.oblist = App.ActiveDocument.Objects |
self.oblist = [] # App.ActiveDocument.Objects |
||
for obj in App.ActiveDocument.Objects: |
|||
try: |
|||
obj.getPropertyByName("Shape") |
|||
self.oblist.append(obj) |
|||
except AttributeError: |
|||
pass |
|||
# self.oblist = [obj for obj in self.oblist if hasattr(obj,"Shape")] |
|||
# Create the cross-section object |
# Create the cross-section object |
||
self.cs = |
self.cs = App.ActiveDocument.addObject("Part::Feature", "Generated__cross-section") |
||
"Generated__cross-section") |
|||
self.OriginalVisibilities = list() |
self.OriginalVisibilities = list() |
||
self.xmin = |
self.xmin = 1000.0 |
||
self.xmax = - |
self.xmax = -1000.0 |
||
self.ymin = |
self.ymin = 1000.0 |
||
self.ymax = - |
self.ymax = -1000.0 |
||
self.zmin = |
self.zmin = 1000.0 |
||
self.zmax = - |
self.zmax = -1000.0 |
||
n = 0 |
# n = 0 |
||
for p in self.oblist: # stupid Python list has no length function |
# for p in self.oblist: # stupid Python list has no length function |
||
n = n + 1 |
# n = n + 1 |
||
############################# Skip the <group object> ######################## |
|||
b0 = [] |
|||
for x0 in self.oblist: |
|||
if str(x0) != "<group object>": |
|||
b0.append(x0) |
|||
self.oblist = b0 |
|||
n = len(self.oblist) |
|||
############################################################################### |
|||
# Make a list of the visible objects and make them invisible while |
# Make a list of the visible objects and make them invisible while |
||
# the macro is active |
# the macro is active |
||
Line 358: | Line 433: | ||
vis = self.oblist[i].ViewObject.Visibility |
vis = self.oblist[i].ViewObject.Visibility |
||
self.OriginalVisibilities.append(vis) |
self.OriginalVisibilities.append(vis) |
||
if vis |
if vis: |
||
self.oblist[i]. |
if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add |
||
self.oblist[i].ViewObject.Visibility = False |
|||
b = self.oblist[i].Shape.BoundBox |
|||
if b.XMin < self.xmin: |
|||
self.xmin = b.XMin |
|||
if b.XMax > self.xmax: |
|||
self.xmax = b.XMax |
|||
if b.YMin < self.ymin: |
|||
self.ymin = b.YMin |
|||
if b.YMax > self.ymax: |
|||
self.ymax = b.YMax |
|||
if b.ZMin < self.zmin: |
|||
self.zmin = b.ZMin |
|||
if b.ZMax > self.zmax: |
|||
self.zmax = b.ZMax |
|||
# Moan and give up if there is nothing to cross-section |
# Moan and give up if there is nothing to cross-section |
||
if self.xmax <= self.xmin \ |
if self.xmax <= self.xmin \ |
||
or self.ymax <= self.ymin \ |
or self.ymax <= self.ymin \ |
||
or self.zmax <= self.zmin |
or self.zmax <= self.zmin: |
||
QtGui.QMessageBox.warning(None, _translate("MainWindow", |
QtGui.QMessageBox.warning(None, _translate("MainWindow", |
||
"Cross-section", None), |
"Cross-section", None), |
||
Line 386: | Line 462: | ||
self.restoreObjects() |
self.restoreObjects() |
||
self.startup_failed = True |
self.startup_failed = True |
||
App.ActiveDocument.removeObject("Generated__cross_section") |
|||
self.parent.destroy() # This will close the window |
|||
self.parent.destroy() # This will close the window |
|||
else : |
|||
else: |
|||
# Trigger initial display |
# Trigger initial display |
||
self.updateAxis() |
self.updateAxis() |
||
Line 395: | Line 472: | ||
# and destroy our cross-section object |
# and destroy our cross-section object |
||
def restoreObjects(self): |
def restoreObjects(self): |
||
n = 0 |
n = 0 |
||
for p in self.oblist: # stupid Python list has no length function |
for p in self.oblist: # stupid Python list has no length function |
||
n = n + 1 |
n = n + 1 |
||
for i in range(n): |
for i in range(n): |
||
self.oblist[i].ViewObject.Visibility = self.OriginalVisibilities[i] |
self.oblist[i].ViewObject.Visibility = self.OriginalVisibilities[i] |
||
try: |
|||
App.ActiveDocument.removeObject("Generated__cross_section") |
|||
if self.CB_00.isChecked() is False: |
|||
App.ActiveDocument.removeObject("Generated__cross_section") |
|||
except Exception: |
|||
None |
|||
# Something has changed, recalculate the cross-section |
# Something has changed, recalculate the cross-section |
||
def updateGui(self): |
def updateGui(self): |
||
# default to no visible cross-ection |
# default to no visible cross-ection |
||
self.cs.ViewObject.Visibility = False |
self.cs.ViewObject.Visibility = False |
||
Line 416: | Line 497: | ||
QtGui.QMessageBox.Ok, |
QtGui.QMessageBox.Ok, |
||
QtGui.QMessageBox.Ok) |
QtGui.QMessageBox.Ok) |
||
else |
else: |
||
# First we compute the 0% and 100% positions on the sliding axis |
# First we compute the 0% and 100% positions on the sliding axis |
||
if self.xdir < 0.0 |
if self.xdir < 0.0: |
||
x = self.xmax + self.fraction * self.xdir |
x = self.xmax + self.fraction * self.xdir |
||
else |
else: |
||
x = self.xmin + self.fraction * self.xdir |
x = self.xmin + self.fraction * self.xdir |
||
if self.ydir < 0.0 |
if self.ydir < 0.0: |
||
y = self.ymax + self.fraction * self.ydir |
y = self.ymax + self.fraction * self.ydir |
||
else |
else: |
||
y = self.ymin + self.fraction * self.ydir |
y = self.ymin + self.fraction * self.ydir |
||
if self.zdir < 0.0 |
if self.zdir < 0.0: |
||
z = self.zmax + self.fraction * self.zdir |
z = self.zmax + self.fraction * self.zdir |
||
else |
else: |
||
z = self.zmin + self.fraction * self.zdir |
z = self.zmin + self.fraction * self.zdir |
||
r = 2 * max(self.xmax - self.xmin, self.ymax - self.ymin, |
r = 2 * max(self.xmax - self.xmin, self.ymax - self.ymin, |
||
self.zmax - self.xmin) |
self.zmax - self.xmin) |
||
# Display the dimensions |
|||
dimDep = "X " + str(x) + " mm , Y " + str(y) + " mm , Z " + str(z) + " mm" |
|||
App.Console.PrintMessage(dimDep + "\n") |
|||
# Create a big enough disc in the cross-section plane |
# Create a big enough disc in the cross-section plane |
||
c = Part.makeCircle(r, Base.Vector(x, y, z), |
c = Part.makeCircle(r, Base.Vector(x, y, z), |
||
Base.Vector(self.xdir, |
Base.Vector(self.xdir, self.ydir, self.zdir)) |
||
f = Part.Face(Part.Wire(c)) |
f = Part.Face(Part.Wire(c)) |
||
part_list = list() # list for cross-section parts |
|||
n = 0 |
n = 0 |
||
for p in self.oblist: # stupid Python list has no length function |
for p in self.oblist: # stupid Python list has no length function |
||
n = n + 1 |
n = n + 1 |
||
something = |
something = 0 |
||
if self.cross_section_type == 1 |
if self.cross_section_type == 1: |
||
# wire line cross-section |
# wire line cross-section |
||
for i in range(n): |
for i in range(n): |
||
if self. |
if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add |
||
if self.OriginalVisibilities[i]: |
|||
ob = self.oblist[i].Shape.section(f) |
|||
# check for valid section part |
|||
if ob.BoundBox.XMin <= ob.BoundBox.XMax: |
|||
part_list.append(ob) |
|||
something = 1 |
|||
else: |
|||
# cut shape cross-section |
# cut shape cross-section |
||
# extrude our cross-ection disc into a cylinder |
# extrude our cross-ection disc into a cylinder |
||
cyl = f.extrude(Base.Vector(self.xdir, |
cyl = f.extrude(Base.Vector(self.xdir, self.ydir, self.zdir)) |
||
doc = App.ActiveDocument |
|||
for i in range(n): |
for i in range(n): |
||
if self. |
if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add |
||
if self.OriginalVisibilities[i]: |
|||
if (self.oblist[i].Shape.Volume <= 0.0): |
|||
continue |
|||
acut = doc.addObject("Part::Cut", "Cut") |
|||
abase = Draft.clone(self.oblist[i]) |
|||
abase.ViewObject.DiffuseColor = self.oblist[i].ViewObject.DiffuseColor |
|||
abase.ViewObject.Transparency = self.oblist[i].ViewObject.Transparency |
|||
doc.recompute() |
|||
atool = doc.addObject("Part::Feature") |
|||
atool.Shape = cyl |
|||
atool.ViewObject.ShapeColor = self.oblist[i].ViewObject.ShapeColor # (0.25,0.57,0.35) |
|||
atool.ViewObject.Transparency = self.oblist[i].ViewObject.Transparency |
|||
doc.recompute() |
|||
acut.Base = abase |
|||
acut.Tool = atool |
|||
acut.ViewObject.DiffuseColor = abase.ViewObject.DiffuseColor |
|||
doc.recompute() |
|||
if acut.Shape.isValid() and acut.Shape.Volume > 0.0: |
|||
ob = doc.addObject("Part::Feature") |
|||
ob.Shape = acut.Shape |
|||
ob.ViewObject.DiffuseColor = acut.ViewObject.DiffuseColor |
|||
doc.recompute() |
|||
part_list.append(ob) |
|||
something = 2 |
|||
doc.removeObject(acut.Name) |
|||
doc.recompute() |
|||
doc.removeObject(abase.Name) |
|||
doc.recompute() |
|||
doc.removeObject(atool.Name) |
|||
doc.recompute() |
|||
if something : |
if something == 1: |
||
s = Part.makeCompound( |
s = Part.makeCompound(part_list) |
||
if s.isValid() |
if s.isValid(): |
||
self.cs.Shape = s |
self.cs.Shape = s |
||
self.cs.ViewObject.Visibility = True |
self.cs.ViewObject.Visibility = True |
||
if something == 2: |
|||
feature = App.ActiveDocument.addObject("Part::Compound", "Compound") |
|||
feature.Links = part_list |
|||
App.ActiveDocument.recompute() |
|||
if feature.Shape.isValid(): |
|||
self.cs.Shape = feature.Shape.copy() |
|||
self.cs.ViewObject.DiffuseColor = feature.ViewObject.DiffuseColor |
|||
self.cs.ViewObject.Visibility = True |
|||
App.ActiveDocument.recompute() |
|||
for od in part_list: |
|||
App.ActiveDocument.removeObject(od.Name) |
|||
App.ActiveDocument.recompute() |
|||
App.ActiveDocument.removeObject(feature.Name) |
|||
FreeCADGui.updateGui() |
FreeCADGui.updateGui() |
||
Line 474: | Line 601: | ||
def updateAxis(self): |
def updateAxis(self): |
||
ldir = 0.0 |
ldir = 0.0 |
||
if self.axisX != 0.0 |
if self.axisX != 0.0: |
||
ldir = (self.xmax - self.xmin) / abs(self.axisX) |
ldir = (self.xmax - self.xmin) / abs(self.axisX) |
||
if self.axisY != 0.0 |
if self.axisY != 0.0: |
||
ldir = max(ldir, (self.ymax - self.ymin) / abs(self.axisY)) |
ldir = max(ldir, (self.ymax - self.ymin) / abs(self.axisY)) |
||
if self.axisZ != 0.0 |
if self.axisZ != 0.0: |
||
ldir = max(ldir, (self.zmax - self.zmin) / abs(self.axisZ)) |
ldir = max(ldir, (self.zmax - self.zmin) / abs(self.axisZ)) |
||
self.xdir = self.axisX * ldir |
self.xdir = self.axisX * ldir |
||
self.ydir = self.axisY * ldir |
self.ydir = self.axisY * ldir |
||
self.zdir = self.axisZ * ldir |
self.zdir = self.axisZ * ldir |
||
self.updateGui() # recalculate the cross-section |
self.updateGui() # recalculate the cross-section |
||
def on_horizontal_slider(self, val): |
def on_horizontal_slider(self, val): |
||
self.fraction = val / 100.0 |
self.fraction = val / 100.0 |
||
self.progressBar_1.setValue(val) |
self.progressBar_1.setValue(val) |
||
self.SpinBox_PBar.setValue(val) |
|||
self.updateGui() |
self.updateGui() |
||
Line 502: | Line 630: | ||
self.updateAxis() |
self.updateAxis() |
||
def |
def on_SpinBox_PBar_valueChanged(self, val): |
||
self.progressBar_1.setValue(val) |
|||
self.horizontalSlider.setValue(int(val)) |
|||
self.updateGui() |
|||
def onRadioButton(self, wasChecked): |
|||
if self.radioButton_1.isChecked(): |
|||
self.cross_section_type = 1 |
self.cross_section_type = 1 |
||
else |
else: |
||
self.cross_section_type = 2 |
self.cross_section_type = 2 |
||
self.updateGui() |
self.updateGui() |
||
####################################### |
####################################### |
||
# Find FreeCAD's root window |
# Find FreeCAD's root window |
||
FreeCADRootWindow = |
FreeCADRootWindow = FreeCADGui.getMainWindow() |
||
# Create the window and start it |
# Create the window and start it |
||
Line 518: | Line 652: | ||
# The CrossSectionWindow will do all the work |
# The CrossSectionWindow will do all the work |
||
}} |
}} |
||
{{clear}} |
{{clear}} |
||
==Beispiel== |
|||
[[File:Macro Cross Section 03.gif|center]] |
|||
==Links== |
==Links== |
||
Die Forumsdiskussion [http://forum.freecadweb.org/viewtopic.php?f=22&t=14049 Posting a new macro] |
|||
<languages/> |
|||
==Version== |
|||
ver 00.10 19/12/2020 : Upgrade durch Fix von Chris_G, der App.Parts von den Eingabeobjekten ausschließt, siehe [https://forum.freecadweb.org/viewtopic.php?f=22&t=53423 cross section macro does not work on bodies within a part container] |
|||
ver 00.09 31/08/2019 : upgrade by g.becu adding line 334 #[https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=20#p330351 Sezione Dinamica] |
|||
ver 00.08 04/07/2019 : upgrade replace Tab to Space by duke24 [https://forum.freecadweb.org/viewtopic.php?f=13&t=37449 Macro cross_section update] |
|||
ver 00.07 17/09/2017 : Aktualisieren Sie mehrere Objekte mit verschiedenen Farben von Gift |
|||
ver 00.06 06/09/2017 : Upgrade von Gift siehe [https://www.forum.freecadweb.org/viewtopic.php?f=13&t=24130 Optischer Schnitt durch Baugruppe, z.B. für Ventilgehäuse] Akzeptieren Sie mehrere Objekte mit unterschiedlichen Farben |
|||
ver 00.05 17/08/2017 : upgrade for 0.17 FreeCAD version by Sam see [https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=10#p187030 Sezione Dinamica] |
Latest revision as of 20:21, 26 April 2022
Beschreibung |
---|
Dynamisch verschiebbarer Querschnitt Versionsmakro : 0.10 Datum der letzten Änderung : 2020-12-19 FreeCAD version : 0.15 and above Herunterladen : ToolBar Icon Autor: aleph0 |
Autor |
aleph0 |
Herunterladen |
ToolBar Icon |
Links |
Makros Rezepte Wie installiere ich Makros So passen Sie Symbolleisten an |
Macro-Version |
0.10 |
Datum der letzten Änderung |
2020-12-19 |
FreeCAD-Version(s) |
0.15 and above |
Standardverknüpfung |
None |
Siehe auch |
None |
Beschreibung
Durch den oder die Körper wird ein ebener Schnitt geführt. Die Normale der Schnittebene entsteht durch Vorgabe je eines Vektors in den drei Ebenen des xyz-Koordinatensystems. Die Schnittebene kann parallel zu sich stetig hin und her verschoben werden. Angezeigt werden entweder die durch den Schnitt enstandenen Linien (Durchdringungsfigur) oder der Rest des oder der Körper (einer der beiden durch den Schnitt entstandenen Teile des oder der Körper wird ausgeblendet).
Abbildung: Schnitt durch zwei teiweise ineinander steckende Köper; links: Dialogfeld "Körper-Schnitt"; rechts: "geschnittener Körper-Rest" (schräge Schnittebene, Schnittebenen-Normale (Sliding axis direction) unter 45° im 1. Sektor der xz-Ebene (X=1, Z=1))
Anwendung
- Der unten stehende Programm-Code ist als Text-Datei mit der Endung .FCMacro und z.B. mit dem Namen "Schnitt" in den Dateiordner ... /Freecad/makro Ihres PC zu übertragen.
- zu schneidende/n Körper aktivieren
- Aufruf des Makros : "Makro > Makros > Schnitt.FCMacro > Ausführen "
- Der/die zu schneidender/n Körper wird/werden bereits geschnitten dargestellt.
- Über dem "Dialog-Fenster" (Combo-Ansicht) erscheint das oben abgebildete Dialogfeld "Körper-Schnitt" (Cross section ).
- Wahl der Schnittebenen-Normale durch einsetzen von Werten für die Vektorlängen "x, y und/oder z"
- Den blauen Schieber "Position des Schnittes entlang der Schnittflächen-Normale" (Position along axis) nach Wahl zwischen 0% und 100% einstellen (grün blinkende Skala, Anfangswert 50%)
- Wahl des gewünschten Schnittbildes durch Anklicken von "Durchdringungsfigur" (Outline, ist voreingestellt) oder "Geschnittener Körper-Rest" (Cut Objects)
- Wahlweises "Speichern des Schnitt-Ergebnisses " (Keep the sectional view) erfolgt beim Schließen des Dialogfeldes "Körper-Schnitt" (Cross section , Kreuz rechts oben).
LINKS
siehe forum
siehe rechts unten: "Speichern des Schnitt-Ergebnisses" (Keep the sectional view).
Skript
Changes by Mario52 include.
Changes by Sam include.
Changes by Gift include.
Changes by duke24 include.
Changes by g.becu include.
Changes by Chris_G include.
Macro cross section.FCMacro
# -*- coding: utf-8 -*- """ *************************************************************************** * * * This widget makes an interactively moveable cross-section of the * * currently visible objects in the currently active document. The * * cross-sectioning plane is specified by its normal, and the cross- * * section can be moved along that normal using a slider bar. It can * * show the cross-section either as an outline or a view of the sliced * * objects. To run the macro, first download it from this site and * * install it in your macros directory, then use the menu to call up * * your list of macros and double-click on this one. It will display * * a default cross-section and pop up a window to enable you to set * * the cross-sectioning parameters. Closing the pop-up window will * * restore the scene to its previous state before the macro was * * started. * * * *************************************************************************** * Copyright (c) 2016 Richard P. Parkins, M. A. * * * * This file is a supplement to 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. * * * * This software is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this macro; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * *************************************************************************** """ __title__ = "Cross-Section" __author__ = "Aleph0" __version__ = "00.10" __date__ = "19/12/2020" # "29/01/2016" "10/06/2016" "29/09/2016 * 2" "17/08/2017" "06/09/2017" "17/09/2017" "04/07/2019" "31/08/2019" "19/12/2020" # Upgrade: # 17/08/2017 by Sam #https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=10#p187030 # 06/09/2017 17/09/2017 by Gift #https://www.forum.freecadweb.org/viewtopic.php?f=13&t=24130 # 04/07/2019 by duke24 replace tab to space #https://forum.freecadweb.org/viewtopic.php?f=13&t=37449 # 31/08/2019 by g.becu adding line 334 #https://forum.freecadweb.org/viewtopic.php?f=28&t=15084&start=20#p330351 # 19/12/2020 by Chris_G : exlude App.Part from the list of input objects + many code style fixes (PEP8) # See forum : https://forum.freecadweb.org/viewtopic.php?f=22&t=53423 __Comment__ = "Dynamic cross section viewer" __Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_cross_section" __Help__ = "see first few lines of macro text" __Status__ = "stable" __Requires__ = "freecad 0.15" # OS: Ubuntu 14.04.3 LTS # OS: Windows 10 # Word size of OS: 64-bit # Word size of OS: 64-bit # Word size of FreeCAD: 64-bit # Word size of FreeCAD: 64-bit # Version: 0.15.4671 (Git) # Version: 0.19.17171 (Git) # Branch: releases/FreeCAD-0-15 # Build type: Release # Hash: 244b3aef360841646cbfe80a1b225c8b39c8380c# Branch: master # Python version: 2.7.6 # Hash: d19470a9711ea604f3ca6c93e46afadf64d5bb87 # Qt version: 4.8.6 # Python version: 3.6.6 # Coin version: 4.0.0a # Qt version: 5.6.2 # OCC version: 6.8.0.oce-0.17 # Coin version: 4.0.0a # # OCC version: 7.3.0 import PySide from PySide import QtCore, QtGui import FreeCAD as App import FreeCADGui import Part import Draft from FreeCAD import Base try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s try: _encoding = QtGui.QApplication.UnicodeUTF8 def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) width = 350 # width of our window height = 310 # height of our window global FreeCADRootWindow # main window before we start # This is the interactive window that the macro creates # Its main function is to have a close box and tidy up when closed class CrossSectionWindow(PySide.QtGui.QMainWindow): # automagically called when the window is created def __init__(self): super(CrossSectionWindow, self).__init__() self.setWindowFlags(PySide.QtCore.Qt.WindowStaysOnTopHint) self.setWindowTitle(_translate( "MainWindow", "Cross-section", None)) # Create and set the window's widget, which does all the work self.child = CrossSection(self) self.setCentralWidget(self.child) self.child.initUI(self) if self.child.startup_failed: self.destroy() else: # Position our window relative to the FreeCAD root self.setPosition(FreeCADRootWindow) self.child.show() self.show() # User closed the window, tidy up def closeEvent(self, event): self.child.restoreObjects() # Set a sensible default position for the window # With FreeCAD's default layout, this will be over the docking area # so it will not obscure the 3D view def setPosition(self, parent): geom = parent.geometry() xpos = geom.left() + 50 ypos = geom.center().y() - height / 2 self.setGeometry(xpos, ypos, width, height) self.setFixedSize(width, height) # This is the widget which does almost all of the work # Widgets don't have close boxes, so closing is dealt with in CrossSectionWindow class CrossSection(PySide.QtGui.QWidget): # Lay out the interactive elements def initUI(self, parent): self.parent = parent font = QtGui.QFont() font.setFamily("Times New Roman") font.setPointSize(10) font.setWeight(10) font.setBold(True) self.hideObjects() margin = 10 alw = 20 # axis label width sbp = margin + alw + margin # spin boxes to right of axis labels slw = width - 2 * margin # width of slider and progress bar ypos = margin self.setObjectName(_fromUtf8("CrossSection")) # The cross-section plane is defined by its normal # The length of the normal is immaterial as long as it is nonzero self.label_1 = QtGui.QLabel(self) self.label_1.setGeometry(QtCore.QRect(margin, ypos, slw, 22)) self.label_1.setObjectName(_fromUtf8("label_1")) self.label_1.setText(_translate( "MainWindow", "Sliding axis direction", None)) self.label_1.setToolTip(_translate( "MainWindow", "Cross-section is taken perpendicular to this axis", None)) ypos = ypos + 30 self.label_2 = QtGui.QLabel(self) self.label_2.setGeometry(QtCore.QRect(margin, ypos, alw, 25)) self.label_2.setObjectName(_fromUtf8("label_2")) self.label_2.setText(_translate("MainWindow", "X", None)) self.doubleSpinBox_X = QtGui.QDoubleSpinBox(self) self.doubleSpinBox_X.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) self.doubleSpinBox_X.setMinimum(-10000.0) self.doubleSpinBox_X.setMaximum(10000.0) self.doubleSpinBox_X.setValue(0.0) self.doubleSpinBox_X.setSingleStep(1) self.doubleSpinBox_X.setObjectName(_fromUtf8("doubleSpinBox_X")) self.doubleSpinBox_X.valueChanged.connect( self.on_doubleSpinBox_X_valueChanged) self.doubleSpinBox_X.setToolTip(_translate( "MainWindow", "Sliding axis X", None)) ypos = ypos + 30 self.label_3 = QtGui.QLabel(self) self.label_3.setGeometry(QtCore.QRect(margin, ypos, alw, 25)) self.label_3.setObjectName(_fromUtf8("label_3")) self.label_3.setText(_translate("MainWindow", "Y", None)) self.doubleSpinBox_Y = QtGui.QDoubleSpinBox(self) self.doubleSpinBox_Y.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) self.doubleSpinBox_Y.setMinimum(-10000.0) self.doubleSpinBox_Y.setMaximum(10000.0) self.doubleSpinBox_Y.setValue(0.0) self.doubleSpinBox_Y.setSingleStep(1) self.doubleSpinBox_Y.setObjectName(_fromUtf8("doubleSpinBox_Y")) self.doubleSpinBox_Y.valueChanged.connect( self.on_doubleSpinBox_Y_valueChanged) self.doubleSpinBox_Y.setToolTip(_translate( "MainWindow", "Sliding axis Y", None)) ypos = ypos + 30 self.label_4 = QtGui.QLabel(self) self.label_4.setGeometry(QtCore.QRect(margin, ypos, alw, 25)) self.label_4.setObjectName(_fromUtf8("label_4")) self.label_4.setText(_translate("MainWindow", "Z", None)) self.doubleSpinBox_Z = QtGui.QDoubleSpinBox(self) self.doubleSpinBox_Z.setGeometry(QtCore.QRect(sbp, ypos, 62, 25)) self.doubleSpinBox_Z.setMinimum(-10000.0) self.doubleSpinBox_Z.setMaximum(10000.0) self.doubleSpinBox_Z.setValue(1.0) self.doubleSpinBox_Z.setSingleStep(1) self.doubleSpinBox_Z.setObjectName(_fromUtf8("doubleSpinBox_Z")) self.doubleSpinBox_Z.valueChanged.connect( self.on_doubleSpinBox_Z_valueChanged) self.doubleSpinBox_Z.setToolTip( _translate("MainWindow", "Sliding axis Z", None)) ypos = ypos + 40 # Make the interactive slider control # As you move this slider with the mouse, the cross-section slides # through the model self.label_5 = QtGui.QLabel(self) self.label_5.setGeometry(QtCore.QRect(margin, ypos, slw, 22)) self.label_5.setObjectName(_fromUtf8("label_5")) self.label_5.setText(_translate( "MainWindow", "Position along axis", None)) ypos = ypos + 30 self.horizontalSlider = QtGui.QSlider(self) self.horizontalSlider.setGeometry(QtCore.QRect(margin, ypos, slw - 50, 20)) self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider.setInvertedAppearance(False) self.horizontalSlider.setObjectName(_fromUtf8("horizontalSlider")) self.horizontalSlider.setRange(0, 100) self.horizontalSlider.setValue(int(self.fraction * 100.0)) self.horizontalSlider.setToolTip(_translate( "MainWindow", "Slide to move cross-section along axis", None)) # must be called after setValue() self.horizontalSlider.valueChanged.connect(self.on_horizontal_slider) ypos = ypos + 30 self.progressBar_1 = QtGui.QProgressBar(self) self.progressBar_1.setGeometry(QtCore.QRect(margin, ypos, slw - 50, 23)) self.progressBar_1.setValue(int(self.fraction * 100.0)) self.progressBar_1.setOrientation(QtCore.Qt.Horizontal) self.progressBar_1.setAlignment(QtCore.Qt.AlignCenter) self.progressBar_1.setObjectName(_fromUtf8("progressBar_1")) self.progressBar_1.setToolTip(_translate( "MainWindow", "Percent position of cross-section", None)) ypos = ypos + 40 ################################## add self.SpinBox_PBar = QtGui.QSpinBox(self) self.SpinBox_PBar.setGeometry(QtCore.QRect(sbp+255, ypos - 40, 45, 23)) self.SpinBox_PBar.setMinimum(0.0) self.SpinBox_PBar.setMaximum(100.0) self.SpinBox_PBar.setValue(int(self.fraction * 100.0)) self.SpinBox_PBar.setSingleStep(1) self.SpinBox_PBar.setObjectName(_fromUtf8("SpinBox_PBar")) self.SpinBox_PBar.valueChanged.connect(self.on_SpinBox_PBar_valueChanged) self.SpinBox_PBar.setToolTip(_translate( "MainWindow", "Sliding percent", None)) ################################# add # Select the display format for the cross-section # This button shows a wire line in the cross-section plane self.radioButton_1 = QtGui.QRadioButton(self) self.radioButton_1.setGeometry(QtCore.QRect(margin, ypos, slw, 20)) self.radioButton_1.setText(_fromUtf8("Outline")) self.radioButton_1.setChecked(True) self.radioButton_1.toggled.connect(self.onRadioButton) self.radioButton_1.setObjectName(_fromUtf8("radioButton_1")) self.radioButton_1.setToolTip(_translate( "MainWindow", "Make cross-section as wire outline", None)) ypos = ypos + 30 # Select the display format for the cross-section # This button shows a normal 3D display with everything above the # cross-section plane cut away self.radioButton_2 = QtGui.QRadioButton(self) self.radioButton_2.setGeometry(QtCore.QRect(margin, ypos, slw, 20)) self.radioButton_2.setText(_fromUtf8("Cut objects")) self.radioButton_2.setChecked(False) self.radioButton_2.toggled.connect(self.onRadioButton) self.radioButton_2.setObjectName(_fromUtf8("radioButton_2")) self.radioButton_2.setToolTip(_translate( "MainWindow", "Make cross-section as cut objects", None)) ################################## # Keep the sectional view self.CB_00 = QtGui.QCheckBox(self) self.CB_00.setText(_fromUtf8("Keep the sectional view")) self.CB_00.setGeometry(QtCore.QRect(margin+168, ypos-30, slw, 20)) self.CB_00.setObjectName(_fromUtf8("CB_00")) self.CB_00.setChecked(False) self.CB_00.setToolTip(_translate( "MainWindow", "Keep the sectional view or erase", None)) ################################## # Called at macro start up by initUI() def hideObjects(self): # Initialise the cross-section state variables self.startup_failed = False self.fraction = 0.5 self.axisX = 0.0 self.axisY = 0.0 self.axisZ = 1.0 self.cross_section_type = 1 # We must have something to do a cross-section on if App.ActiveDocument is None: QtGui.QMessageBox.warning(None, _translate("MainWindow", "Cross-section", None), _translate("MainWindow", "There is no Active Document\n" + "Create one and run this macro again.", None), QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) self.startup_failed = True self.parent.destroy() # This will close the window else: # Make a list of the user's objects # WARNING!! # This list is persistent. We'll get confused if the user deletes or # adds objects while the macro is active self.oblist = [] # App.ActiveDocument.Objects for obj in App.ActiveDocument.Objects: try: obj.getPropertyByName("Shape") self.oblist.append(obj) except AttributeError: pass # self.oblist = [obj for obj in self.oblist if hasattr(obj,"Shape")] # Create the cross-section object self.cs = App.ActiveDocument.addObject("Part::Feature", "Generated__cross-section") self.OriginalVisibilities = list() self.xmin = 1000.0 self.xmax = -1000.0 self.ymin = 1000.0 self.ymax = -1000.0 self.zmin = 1000.0 self.zmax = -1000.0 # n = 0 # for p in self.oblist: # stupid Python list has no length function # n = n + 1 ############################# Skip the <group object> ######################## b0 = [] for x0 in self.oblist: if str(x0) != "<group object>": b0.append(x0) self.oblist = b0 n = len(self.oblist) ############################################################################### # Make a list of the visible objects and make them invisible while # the macro is active # Also we compute the bounding box of the model for i in range(n): vis = self.oblist[i].ViewObject.Visibility self.OriginalVisibilities.append(vis) if vis: if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add self.oblist[i].ViewObject.Visibility = False b = self.oblist[i].Shape.BoundBox if b.XMin < self.xmin: self.xmin = b.XMin if b.XMax > self.xmax: self.xmax = b.XMax if b.YMin < self.ymin: self.ymin = b.YMin if b.YMax > self.ymax: self.ymax = b.YMax if b.ZMin < self.zmin: self.zmin = b.ZMin if b.ZMax > self.zmax: self.zmax = b.ZMax # Moan and give up if there is nothing to cross-section if self.xmax <= self.xmin \ or self.ymax <= self.ymin \ or self.zmax <= self.zmin: QtGui.QMessageBox.warning(None, _translate("MainWindow", "Cross-section", None), _translate("MainWindow", "There are no visible solid objects\n" + "Create some and run this macro again.", None), QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) self.restoreObjects() self.startup_failed = True App.ActiveDocument.removeObject("Generated__cross_section") self.parent.destroy() # This will close the window else: # Trigger initial display self.updateAxis() # Called when macro is closed # Restore the original visibility of the user's objects # and destroy our cross-section object def restoreObjects(self): n = 0 for p in self.oblist: # stupid Python list has no length function n = n + 1 for i in range(n): self.oblist[i].ViewObject.Visibility = self.OriginalVisibilities[i] try: if self.CB_00.isChecked() is False: App.ActiveDocument.removeObject("Generated__cross_section") except Exception: None # Something has changed, recalculate the cross-section def updateGui(self): # default to no visible cross-ection self.cs.ViewObject.Visibility = False # check if the sliding axis is invalid if self.xdir == 0.0 and self.ydir == 0.0 and self.zdir == 0.0 : QtGui.QMessageBox.warning(None, _translate("MainWindow", "Cross-section", None), _translate("MainWindow", "The sliding axis has zero length\n" + "Please set a valid axis.", None), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok) else: # First we compute the 0% and 100% positions on the sliding axis if self.xdir < 0.0: x = self.xmax + self.fraction * self.xdir else: x = self.xmin + self.fraction * self.xdir if self.ydir < 0.0: y = self.ymax + self.fraction * self.ydir else: y = self.ymin + self.fraction * self.ydir if self.zdir < 0.0: z = self.zmax + self.fraction * self.zdir else: z = self.zmin + self.fraction * self.zdir r = 2 * max(self.xmax - self.xmin, self.ymax - self.ymin, self.zmax - self.xmin) # Display the dimensions dimDep = "X " + str(x) + " mm , Y " + str(y) + " mm , Z " + str(z) + " mm" App.Console.PrintMessage(dimDep + "\n") # Create a big enough disc in the cross-section plane c = Part.makeCircle(r, Base.Vector(x, y, z), Base.Vector(self.xdir, self.ydir, self.zdir)) f = Part.Face(Part.Wire(c)) part_list = list() # list for cross-section parts n = 0 for p in self.oblist: # stupid Python list has no length function n = n + 1 something = 0 if self.cross_section_type == 1: # wire line cross-section for i in range(n): if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add if self.OriginalVisibilities[i]: ob = self.oblist[i].Shape.section(f) # check for valid section part if ob.BoundBox.XMin <= ob.BoundBox.XMax: part_list.append(ob) something = 1 else: # cut shape cross-section # extrude our cross-ection disc into a cylinder cyl = f.extrude(Base.Vector(self.xdir, self.ydir, self.zdir)) doc = App.ActiveDocument for i in range(n): if self.oblist[i].TypeId != str("Drawing::FeatureViewPython"): # add if self.OriginalVisibilities[i]: if (self.oblist[i].Shape.Volume <= 0.0): continue acut = doc.addObject("Part::Cut", "Cut") abase = Draft.clone(self.oblist[i]) abase.ViewObject.DiffuseColor = self.oblist[i].ViewObject.DiffuseColor abase.ViewObject.Transparency = self.oblist[i].ViewObject.Transparency doc.recompute() atool = doc.addObject("Part::Feature") atool.Shape = cyl atool.ViewObject.ShapeColor = self.oblist[i].ViewObject.ShapeColor # (0.25,0.57,0.35) atool.ViewObject.Transparency = self.oblist[i].ViewObject.Transparency doc.recompute() acut.Base = abase acut.Tool = atool acut.ViewObject.DiffuseColor = abase.ViewObject.DiffuseColor doc.recompute() if acut.Shape.isValid() and acut.Shape.Volume > 0.0: ob = doc.addObject("Part::Feature") ob.Shape = acut.Shape ob.ViewObject.DiffuseColor = acut.ViewObject.DiffuseColor doc.recompute() part_list.append(ob) something = 2 doc.removeObject(acut.Name) doc.recompute() doc.removeObject(abase.Name) doc.recompute() doc.removeObject(atool.Name) doc.recompute() if something == 1: s = Part.makeCompound(part_list) if s.isValid(): self.cs.Shape = s self.cs.ViewObject.Visibility = True if something == 2: feature = App.ActiveDocument.addObject("Part::Compound", "Compound") feature.Links = part_list App.ActiveDocument.recompute() if feature.Shape.isValid(): self.cs.Shape = feature.Shape.copy() self.cs.ViewObject.DiffuseColor = feature.ViewObject.DiffuseColor self.cs.ViewObject.Visibility = True App.ActiveDocument.recompute() for od in part_list: App.ActiveDocument.removeObject(od.Name) App.ActiveDocument.recompute() App.ActiveDocument.removeObject(feature.Name) FreeCADGui.updateGui() # User changed axis vector # Compute a new axis vector whose length just covers the model def updateAxis(self): ldir = 0.0 if self.axisX != 0.0: ldir = (self.xmax - self.xmin) / abs(self.axisX) if self.axisY != 0.0: ldir = max(ldir, (self.ymax - self.ymin) / abs(self.axisY)) if self.axisZ != 0.0: ldir = max(ldir, (self.zmax - self.zmin) / abs(self.axisZ)) self.xdir = self.axisX * ldir self.ydir = self.axisY * ldir self.zdir = self.axisZ * ldir self.updateGui() # recalculate the cross-section def on_horizontal_slider(self, val): self.fraction = val / 100.0 self.progressBar_1.setValue(val) self.SpinBox_PBar.setValue(val) self.updateGui() def on_doubleSpinBox_X_valueChanged(self, val): self.axisX = val self.updateAxis() def on_doubleSpinBox_Y_valueChanged(self, val): self.axisY = val self.updateAxis() def on_doubleSpinBox_Z_valueChanged(self, val): self.axisZ = val self.updateAxis() def on_SpinBox_PBar_valueChanged(self, val): self.progressBar_1.setValue(val) self.horizontalSlider.setValue(int(val)) self.updateGui() def onRadioButton(self, wasChecked): if self.radioButton_1.isChecked(): self.cross_section_type = 1 else: self.cross_section_type = 2 self.updateGui() ####################################### # Find FreeCAD's root window FreeCADRootWindow = FreeCADGui.getMainWindow() # Create the window and start it myWidget = CrossSectionWindow() # The CrossSectionWindow will do all the work
Beispiel
Links
Die Forumsdiskussion Posting a new macro
Version
ver 00.10 19/12/2020 : Upgrade durch Fix von Chris_G, der App.Parts von den Eingabeobjekten ausschließt, siehe cross section macro does not work on bodies within a part container
ver 00.09 31/08/2019 : upgrade by g.becu adding line 334 #Sezione Dinamica
ver 00.08 04/07/2019 : upgrade replace Tab to Space by duke24 Macro cross_section update
ver 00.07 17/09/2017 : Aktualisieren Sie mehrere Objekte mit verschiedenen Farben von Gift
ver 00.06 06/09/2017 : Upgrade von Gift siehe Optischer Schnitt durch Baugruppe, z.B. für Ventilgehäuse Akzeptieren Sie mehrere Objekte mit unterschiedlichen Farben
ver 00.05 17/08/2017 : upgrade for 0.17 FreeCAD version by Sam see Sezione Dinamica