Merge pull request #5422 from sliptonic/bug/translationThreadMill
[PATH] Bug/translation (threadmill, tools, toolcontroller, adaptive)
This commit is contained in:
@@ -105,6 +105,7 @@
|
||||
<file>panels/PageDepthsEdit.ui</file>
|
||||
<file>panels/PageDiametersEdit.ui</file>
|
||||
<file>panels/PageHeightsEdit.ui</file>
|
||||
<file>panels/PageOpAdaptiveEdit.ui</file>
|
||||
<file>panels/PageOpCustomEdit.ui</file>
|
||||
<file>panels/PageOpDeburrEdit.ui</file>
|
||||
<file>panels/PageOpDrillingEdit.ui</file>
|
||||
|
||||
276
src/Mod/Path/Gui/Resources/panels/PageOpAdaptiveEdit.ui
Normal file
276
src/Mod/Path/Gui/Resources/panels/PageOpAdaptiveEdit.ui
Normal file
@@ -0,0 +1,276 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>437</width>
|
||||
<height>764</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Tool Controller</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="ToolController"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Coolant</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="coolantController"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="15" column="1">
|
||||
<widget class="QDoubleSpinBox" name="LiftDistance">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>How much to lift the tool up during the rapid linking moves over cleared regions.</p><p>If linking path is not clear tool is raised to clearence height.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Lift Distance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QDoubleSpinBox" name="HelixDiameterLimit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If &gt;0 it limits the helix ramp diameter</p><p>otherwise the 75 percent of tool diameter is used</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Helix Ramp Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QDoubleSpinBox" name="HelixConeAngle">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Angle of the helix entry cone</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Stock to Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Cut Region</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="Side">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Cut inside or outside of the selected shapes</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Step Over Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="24" column="0">
|
||||
<widget class="QCheckBox" name="useOutline">
|
||||
<property name="text">
|
||||
<string>Use Outline</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1">
|
||||
<widget class="QDoubleSpinBox" name="KeepToolDownRatio">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Max length of keep-tool-down linking path compared to direct distance between points.</p><p>If exceeded link will be done by raising the tool to clearence height.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Helix Max Diameter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Helix Cone Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Keep Tool Down Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="1">
|
||||
<widget class="QDoubleSpinBox" name="StockToLeave">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>How much material to leave (i.e. for finishing operation)</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="22" column="0">
|
||||
<widget class="QCheckBox" name="ForceInsideOut">
|
||||
<property name="text">
|
||||
<string>Force Clearing Inside-out</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Operation Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QDoubleSpinBox" name="HelixAngle">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Angle of the helix ramp entry</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="23" column="0">
|
||||
<widget class="QCheckBox" name="FinishingProfile">
|
||||
<property name="text">
|
||||
<string>Finishing Profile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="OperationType">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Type of adaptive operation</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="threadFitLabel">
|
||||
<property name="text">
|
||||
<string>Accuracy vs Performance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="Tolerance">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Influences calculation performance vs stability and accuracy</p></body></html></string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="sliderPosition">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="StepOver">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Optimal value for tool stepover</p></body></html></string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="StopButton">
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -30,18 +30,8 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="threadOrientation">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Left Hand</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Right Hand</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
@@ -52,23 +42,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="threadType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Custom</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Metric - internal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>SAE - internal</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="threadType"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="threadName"/>
|
||||
@@ -208,18 +182,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opDirection">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Climb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Conventional</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="opDirection"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="leadInOut">
|
||||
@@ -236,7 +199,7 @@
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
||||
@@ -150,7 +150,7 @@ class PathWorkbench(Workbench):
|
||||
projcmdlist.append("Path_Sanity")
|
||||
prepcmdlist.append("Path_Shape")
|
||||
extracmdlist.extend(["Path_Area", "Path_Area_Workplane"])
|
||||
specialcmdlist.append("Path_Thread_Milling")
|
||||
specialcmdlist.append("Path_ThreadMilling")
|
||||
twodopcmdlist.append("Path_Slot")
|
||||
|
||||
if PathPreferences.advancedOCLFeaturesEnabled():
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,142 +22,41 @@
|
||||
# ***************************************************************************
|
||||
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide import QtCore
|
||||
import PathScripts.PathAdaptive as PathAdaptive
|
||||
import PathScripts.PathFeatureExtensionsGui as PathFeatureExtensionsGui
|
||||
import FreeCADGui
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
def initPage(self, obj):
|
||||
self.setTitle("Adaptive path operation")
|
||||
|
||||
def getForm(self):
|
||||
form = QtGui.QWidget()
|
||||
layout = QtGui.QVBoxLayout()
|
||||
formLayout = QtGui.QFormLayout()
|
||||
"""getForm() ... return UI"""
|
||||
|
||||
# tool controller
|
||||
form.ToolController = QtGui.QComboBox()
|
||||
formLayout.addRow(QtGui.QLabel("Tool Controller"), form.ToolController)
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpAdaptiveEdit.ui")
|
||||
comboToPropertyMap = [("Side", "Side"), ("OperationType", "OperationType")]
|
||||
|
||||
# Coolant controller
|
||||
form.coolantController = QtGui.QComboBox()
|
||||
formLayout.addRow(QtGui.QLabel("Coolant Mode"), form.coolantController)
|
||||
enumTups = PathAdaptive.PathAdaptive.propertyEnumerations(dataType="raw")
|
||||
|
||||
# cut region
|
||||
form.Side = QtGui.QComboBox()
|
||||
form.Side.addItem("Inside")
|
||||
form.Side.addItem("Outside")
|
||||
form.Side.setToolTip("Cut inside or outside of the selected shapes")
|
||||
formLayout.addRow(QtGui.QLabel("Cut Region"), form.Side)
|
||||
|
||||
# operation type
|
||||
form.OperationType = QtGui.QComboBox()
|
||||
form.OperationType.addItem("Clearing")
|
||||
form.OperationType.addItem("Profiling")
|
||||
form.OperationType.setToolTip("Type of adaptive operation")
|
||||
formLayout.addRow(QtGui.QLabel("Operation Type"), form.OperationType)
|
||||
|
||||
# step over
|
||||
form.StepOver = QtGui.QSpinBox()
|
||||
form.StepOver.setMinimum(15)
|
||||
form.StepOver.setMaximum(75)
|
||||
form.StepOver.setSingleStep(1)
|
||||
form.StepOver.setValue(25)
|
||||
form.StepOver.setToolTip("Optimal value for tool stepover")
|
||||
formLayout.addRow(QtGui.QLabel("Step Over Percent"), form.StepOver)
|
||||
|
||||
# tolerance
|
||||
form.Tolerance = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
form.Tolerance.setMinimum(5)
|
||||
form.Tolerance.setMaximum(15)
|
||||
form.Tolerance.setTickInterval(1)
|
||||
form.Tolerance.setValue(10)
|
||||
form.Tolerance.setTickPosition(QtGui.QSlider.TicksBelow)
|
||||
form.Tolerance.setToolTip("Influences calculation performance vs stability and accuracy.")
|
||||
formLayout.addRow(QtGui.QLabel("Accuracy vs Performance"), form.Tolerance)
|
||||
|
||||
# helix angle
|
||||
form.HelixAngle = QtGui.QDoubleSpinBox()
|
||||
form.HelixAngle.setMinimum(1)
|
||||
form.HelixAngle.setMaximum(89)
|
||||
form.HelixAngle.setSingleStep(1)
|
||||
form.HelixAngle.setValue(5)
|
||||
form.HelixAngle.setToolTip("Angle of the helix ramp entry")
|
||||
formLayout.addRow(QtGui.QLabel("Helix Ramp Angle"), form.HelixAngle)
|
||||
|
||||
# helix cone angle
|
||||
form.HelixConeAngle = QtGui.QDoubleSpinBox()
|
||||
form.HelixConeAngle.setMinimum(0)
|
||||
form.HelixConeAngle.setMaximum(6)
|
||||
form.HelixConeAngle.setSingleStep(1)
|
||||
form.HelixConeAngle.setValue(0)
|
||||
form.HelixConeAngle.setToolTip("Angle of the helix entry cone")
|
||||
formLayout.addRow(QtGui.QLabel("Helix Cone Angle"), form.HelixConeAngle)
|
||||
|
||||
# helix diam. limit
|
||||
form.HelixDiameterLimit = QtGui.QDoubleSpinBox()
|
||||
form.HelixDiameterLimit.setMinimum(0.0)
|
||||
form.HelixDiameterLimit.setMaximum(90)
|
||||
form.HelixDiameterLimit.setSingleStep(0.1)
|
||||
form.HelixDiameterLimit.setValue(0)
|
||||
form.HelixDiameterLimit.setToolTip("If >0 it limits the helix ramp diameter\notherwise the 75 percent of tool diameter is used as helix diameter")
|
||||
formLayout.addRow(QtGui.QLabel("Helix Max Diameter"), form.HelixDiameterLimit)
|
||||
|
||||
# lift distance
|
||||
form.LiftDistance = QtGui.QDoubleSpinBox()
|
||||
form.LiftDistance.setMinimum(0.0)
|
||||
form.LiftDistance.setMaximum(1000)
|
||||
form.LiftDistance.setSingleStep(0.1)
|
||||
form.LiftDistance.setValue(1.0)
|
||||
form.LiftDistance.setToolTip("How much to lift the tool up during the rapid linking moves over cleared regions.\nIf linking path is not clear tool is raised to clearence height.")
|
||||
formLayout.addRow(QtGui.QLabel("Lift Distance"), form.LiftDistance)
|
||||
|
||||
# KeepToolDownRatio
|
||||
form.KeepToolDownRatio = QtGui.QDoubleSpinBox()
|
||||
form.KeepToolDownRatio.setMinimum(1.0)
|
||||
form.KeepToolDownRatio.setMaximum(10)
|
||||
form.KeepToolDownRatio.setSingleStep(1)
|
||||
form.KeepToolDownRatio.setValue(3.0)
|
||||
form.KeepToolDownRatio.setToolTip("Max length of keep-tool-down linking path compared to direct distance between points.\nIf exceeded link will be done by raising the tool to clearence height.")
|
||||
formLayout.addRow(QtGui.QLabel("Keep Tool Down Ratio"), form.KeepToolDownRatio)
|
||||
|
||||
# stock to leave
|
||||
form.StockToLeave = QtGui.QDoubleSpinBox()
|
||||
form.StockToLeave.setMinimum(0.0)
|
||||
form.StockToLeave.setMaximum(1000)
|
||||
form.StockToLeave.setSingleStep(0.1)
|
||||
form.StockToLeave.setValue(0)
|
||||
form.StockToLeave.setToolTip("How much material to leave (i.e. for finishing operation)")
|
||||
formLayout.addRow(QtGui.QLabel("Stock to Leave"), form.StockToLeave)
|
||||
|
||||
# Force inside out
|
||||
form.ForceInsideOut = QtGui.QCheckBox()
|
||||
form.ForceInsideOut.setChecked(True)
|
||||
formLayout.addRow(QtGui.QLabel("Force Clearing Inside-Out"), form.ForceInsideOut)
|
||||
|
||||
# Finishing profile
|
||||
form.FinishingProfile = QtGui.QCheckBox()
|
||||
form.FinishingProfile.setChecked(True)
|
||||
formLayout.addRow(QtGui.QLabel("Finishing Profile"), form.FinishingProfile)
|
||||
|
||||
# Use outline checkbox
|
||||
form.useOutline = QtGui.QCheckBox()
|
||||
form.useOutline.setChecked(False)
|
||||
formLayout.addRow(QtGui.QLabel("Use outline"), form.useOutline)
|
||||
|
||||
layout.addLayout(formLayout)
|
||||
|
||||
# stop button
|
||||
form.StopButton = QtGui.QPushButton("Stop")
|
||||
form.StopButton.setCheckable(True)
|
||||
layout.addWidget(form.StopButton)
|
||||
|
||||
form.setLayout(layout)
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
return form
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
signals = []
|
||||
# signals.append(self.form.button.clicked)
|
||||
signals.append(self.form.Side.currentIndexChanged)
|
||||
@@ -189,10 +88,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.HelixConeAngle.setValue(obj.HelixConeAngle)
|
||||
self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit)
|
||||
self.form.LiftDistance.setValue(obj.LiftDistance)
|
||||
if hasattr(obj, 'KeepToolDownRatio'):
|
||||
if hasattr(obj, "KeepToolDownRatio"):
|
||||
self.form.KeepToolDownRatio.setValue(obj.KeepToolDownRatio)
|
||||
|
||||
if hasattr(obj, 'StockToLeave'):
|
||||
if hasattr(obj, "StockToLeave"):
|
||||
self.form.StockToLeave.setValue(obj.StockToLeave)
|
||||
|
||||
# self.form.ProcessHoles.setChecked(obj.ProcessHoles)
|
||||
@@ -202,17 +101,17 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.setupToolController(obj, self.form.ToolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
self.form.StopButton.setChecked(obj.Stopped)
|
||||
obj.setEditorMode('AdaptiveInputState', 2) # hide this property
|
||||
obj.setEditorMode('AdaptiveOutputState', 2) # hide this property
|
||||
obj.setEditorMode('StopProcessing', 2) # hide this property
|
||||
obj.setEditorMode('Stopped', 2) # hide this property
|
||||
obj.setEditorMode("AdaptiveInputState", 2) # hide this property
|
||||
obj.setEditorMode("AdaptiveOutputState", 2) # hide this property
|
||||
obj.setEditorMode("StopProcessing", 2) # hide this property
|
||||
obj.setEditorMode("Stopped", 2) # hide this property
|
||||
|
||||
def getFields(self, obj):
|
||||
if obj.Side != str(self.form.Side.currentText()):
|
||||
obj.Side = str(self.form.Side.currentText())
|
||||
if obj.Side != str(self.form.Side.currentData()):
|
||||
obj.Side = str(self.form.Side.currentData())
|
||||
|
||||
if obj.OperationType != str(self.form.OperationType.currentText()):
|
||||
obj.OperationType = str(self.form.OperationType.currentText())
|
||||
if obj.OperationType != str(self.form.OperationType.currentData()):
|
||||
obj.OperationType = str(self.form.OperationType.currentData())
|
||||
|
||||
obj.StepOver = self.form.StepOver.value()
|
||||
obj.Tolerance = 1.0 * self.form.Tolerance.value() / 100.0
|
||||
@@ -221,37 +120,41 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value()
|
||||
obj.LiftDistance = self.form.LiftDistance.value()
|
||||
|
||||
if hasattr(obj, 'KeepToolDownRatio'):
|
||||
if hasattr(obj, "KeepToolDownRatio"):
|
||||
obj.KeepToolDownRatio = self.form.KeepToolDownRatio.value()
|
||||
|
||||
if hasattr(obj, 'StockToLeave'):
|
||||
if hasattr(obj, "StockToLeave"):
|
||||
obj.StockToLeave = self.form.StockToLeave.value()
|
||||
|
||||
obj.ForceInsideOut = self.form.ForceInsideOut.isChecked()
|
||||
obj.FinishingProfile = self.form.FinishingProfile.isChecked()
|
||||
obj.UseOutline = self.form.useOutline.isChecked()
|
||||
obj.Stopped = self.form.StopButton.isChecked()
|
||||
if(obj.Stopped):
|
||||
if obj.Stopped:
|
||||
self.form.StopButton.setChecked(False) # reset the button
|
||||
obj.StopProcessing = True
|
||||
|
||||
self.updateToolController(obj, self.form.ToolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
obj.setEditorMode('AdaptiveInputState', 2) # hide this property
|
||||
obj.setEditorMode('AdaptiveOutputState', 2) # hide this property
|
||||
obj.setEditorMode('StopProcessing', 2) # hide this property
|
||||
obj.setEditorMode('Stopped', 2) # hide this property
|
||||
obj.setEditorMode("AdaptiveInputState", 2) # hide this property
|
||||
obj.setEditorMode("AdaptiveOutputState", 2) # hide this property
|
||||
obj.setEditorMode("StopProcessing", 2) # hide this property
|
||||
obj.setEditorMode("Stopped", 2) # hide this property
|
||||
|
||||
def taskPanelBaseLocationPage(self, obj, features):
|
||||
if not hasattr(self, 'extensionsPanel'):
|
||||
self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage(obj, features) # pylint: disable=attribute-defined-outside-init
|
||||
if not hasattr(self, "extensionsPanel"):
|
||||
self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage(
|
||||
obj, features
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
return self.extensionsPanel
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Adaptive',
|
||||
PathAdaptive.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Adaptive',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Adaptive", "Adaptive"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Adaptive", "Adaptive clearing and profiling"),
|
||||
PathAdaptive.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Adaptive",
|
||||
PathAdaptive.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Adaptive",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Adaptive", "Adaptive"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Adaptive", "Adaptive clearing and profiling"),
|
||||
PathAdaptive.SetupProperties,
|
||||
)
|
||||
|
||||
@@ -24,37 +24,46 @@ import FreeCADGui
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PySide
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class AdvancedPreferencesPage:
|
||||
def __init__(self, parent=None):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':preferences/Advanced.ui')
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":preferences/Advanced.ui")
|
||||
self.form.WarningSuppressAllSpeeds.stateChanged.connect(self.updateSelection)
|
||||
self.form.EnableAdvancedOCLFeatures.stateChanged.connect(self.updateSelection)
|
||||
|
||||
def saveSettings(self):
|
||||
PathPreferences.setPreferencesAdvanced(
|
||||
self.form.EnableAdvancedOCLFeatures.isChecked(),
|
||||
self.form.WarningSuppressAllSpeeds.isChecked(),
|
||||
self.form.WarningSuppressRapidSpeeds.isChecked(),
|
||||
self.form.WarningSuppressSelectionMode.isChecked(),
|
||||
self.form.WarningSuppressOpenCamLib.isChecked())
|
||||
self.form.EnableAdvancedOCLFeatures.isChecked(),
|
||||
self.form.WarningSuppressAllSpeeds.isChecked(),
|
||||
self.form.WarningSuppressRapidSpeeds.isChecked(),
|
||||
self.form.WarningSuppressSelectionMode.isChecked(),
|
||||
self.form.WarningSuppressOpenCamLib.isChecked(),
|
||||
)
|
||||
|
||||
def loadSettings(self):
|
||||
self.form.WarningSuppressAllSpeeds.setChecked(PathPreferences.suppressAllSpeedsWarning())
|
||||
self.form.WarningSuppressRapidSpeeds.setChecked(PathPreferences.suppressRapidSpeedsWarning(False))
|
||||
self.form.WarningSuppressSelectionMode.setChecked(PathPreferences.suppressSelectionModeWarning())
|
||||
self.form.EnableAdvancedOCLFeatures.setChecked(PathPreferences.advancedOCLFeaturesEnabled())
|
||||
self.form.WarningSuppressOpenCamLib.setChecked(PathPreferences.suppressOpenCamLibWarning())
|
||||
self.form.WarningSuppressAllSpeeds.setChecked(
|
||||
PathPreferences.suppressAllSpeedsWarning()
|
||||
)
|
||||
self.form.WarningSuppressRapidSpeeds.setChecked(
|
||||
PathPreferences.suppressRapidSpeedsWarning(False)
|
||||
)
|
||||
self.form.WarningSuppressSelectionMode.setChecked(
|
||||
PathPreferences.suppressSelectionModeWarning()
|
||||
)
|
||||
self.form.EnableAdvancedOCLFeatures.setChecked(
|
||||
PathPreferences.advancedOCLFeaturesEnabled()
|
||||
)
|
||||
self.form.WarningSuppressOpenCamLib.setChecked(
|
||||
PathPreferences.suppressOpenCamLibWarning()
|
||||
)
|
||||
self.updateSelection()
|
||||
|
||||
def updateSelection(self, state=None):
|
||||
self.form.WarningSuppressOpenCamLib.setEnabled(self.form.EnableAdvancedOCLFeatures.isChecked())
|
||||
self.form.WarningSuppressOpenCamLib.setEnabled(
|
||||
self.form.EnableAdvancedOCLFeatures.isChecked()
|
||||
)
|
||||
if self.form.WarningSuppressAllSpeeds.isChecked():
|
||||
self.form.WarningSuppressRapidSpeeds.setChecked(True)
|
||||
self.form.WarningSuppressRapidSpeeds.setEnabled(False)
|
||||
else:
|
||||
self.form.WarningSuppressRapidSpeeds.setEnabled(True)
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ def select(op):
|
||||
opsel["Vcarve"] = vcarveselect
|
||||
opsel["Probe"] = probeselect
|
||||
opsel["Custom"] = customselect
|
||||
opsel["Thread Milling"] = drillselect
|
||||
opsel["ThreadMilling"] = drillselect
|
||||
opsel["TurnFace"] = turnselect
|
||||
opsel["TurnProfile"] = turnselect
|
||||
opsel["TurnPartoff"] = turnselect
|
||||
|
||||
@@ -28,26 +28,25 @@ import PathScripts.PathCircularHoleBase as PathCircularHoleBase
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Thread Milling Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Path thread milling operation."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None):
|
||||
'''internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread.'''
|
||||
def radiiInternal(majorDia, minorDia, toolDia, toolCrest=None):
|
||||
"""internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread."""
|
||||
PathLog.track(majorDia, minorDia, toolDia, toolCrest)
|
||||
if toolCrest is None:
|
||||
toolCrest = 0.0
|
||||
@@ -57,20 +56,25 @@ def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None):
|
||||
# - The major diameter is 3/8 * H bigger than the pitch diameter
|
||||
# Since we already have the outer diameter it's simpler to just add 1/8 * H
|
||||
# to get the outer tip of the thread.
|
||||
H = ((majorDia - minorDia) / 2.0 ) * 1.6 # (D - d)/2 = 5/8 * H
|
||||
H = ((majorDia - minorDia) / 2.0) * 1.6 # (D - d)/2 = 5/8 * H
|
||||
outerTip = majorDia / 2.0 + H / 8.0
|
||||
# Compensate for the crest of the tool
|
||||
toolTip = outerTip - toolCrest * 0.8660254037844386 # math.sqrt(3)/2 ... 60deg triangle height
|
||||
toolTip = (
|
||||
outerTip - toolCrest * 0.8660254037844386
|
||||
) # math.sqrt(3)/2 ... 60deg triangle height
|
||||
return ((minorDia - toolDia) / 2.0, toolTip - toolDia / 2.0)
|
||||
|
||||
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest = None):
|
||||
|
||||
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest=None):
|
||||
PathLog.track(count, radii, majorDia, minorDia, toolDia, toolCrest)
|
||||
minor, major = radii(majorDia, minorDia, toolDia, toolCrest)
|
||||
dr = float(major - minor) / count
|
||||
dr = float(major - minor) / count
|
||||
return [major - dr * (count - (i + 1)) for i in range(count)]
|
||||
|
||||
|
||||
class _InternalThread(object):
|
||||
'''Helper class for dealing with different thread types'''
|
||||
"""Helper class for dealing with different thread types"""
|
||||
|
||||
def __init__(self, cmd, zStart, zFinal, pitch):
|
||||
self.cmd = cmd
|
||||
if zStart < zFinal:
|
||||
@@ -82,33 +86,34 @@ class _InternalThread(object):
|
||||
self.zFinal = zFinal
|
||||
|
||||
def overshoots(self, z):
|
||||
'''overshoots(z) ... returns true if adding another half helix goes beyond the thread bounds'''
|
||||
"""overshoots(z) ... returns true if adding another half helix goes beyond the thread bounds"""
|
||||
if self.pitch < 0:
|
||||
return z + self.hPitch < self.zFinal
|
||||
return z + self.hPitch > self.zFinal
|
||||
|
||||
def adjustX(self, x, dx):
|
||||
'''adjustX(x, dx) ... move x by dx, the direction depends on the thread settings'''
|
||||
"""adjustX(x, dx) ... move x by dx, the direction depends on the thread settings"""
|
||||
if self.isG3() == (self.pitch > 0):
|
||||
return x + dx
|
||||
return x - dx
|
||||
|
||||
def adjustY(self, y, dy):
|
||||
'''adjustY(y, dy) ... move y by dy, the direction depends on the thread settings'''
|
||||
"""adjustY(y, dy) ... move y by dy, the direction depends on the thread settings"""
|
||||
if self.isG3():
|
||||
return y - dy
|
||||
return y - dy
|
||||
|
||||
def isG3(self):
|
||||
'''isG3() ... returns True if this is a G3 command'''
|
||||
return self.cmd in ['G3', 'G03', 'g3', 'g03']
|
||||
"""isG3() ... returns True if this is a G3 command"""
|
||||
return self.cmd in ["G3", "G03", "g3", "g03"]
|
||||
|
||||
def isUp(self):
|
||||
'''isUp() ... returns True if the thread goes from the bottom up'''
|
||||
"""isUp() ... returns True if the thread goes from the bottom up"""
|
||||
return self.pitch > 0
|
||||
|
||||
|
||||
def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
'''internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius) ... returns the g-code to mill the given internal thread'''
|
||||
"""internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius) ... returns the g-code to mill the given internal thread"""
|
||||
thread = _InternalThread(cmd, zStart, zFinal, pitch)
|
||||
|
||||
yMin = loc.y - radius
|
||||
@@ -118,30 +123,29 @@ def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
# at this point the tool is at a safe height (depending on the previous thread), so we can move
|
||||
# into position first, and then drop to the start height. If there is any material in the way this
|
||||
# op hasn't been setup properly.
|
||||
path.append(Path.Command('G0', {'X': loc.x, 'Y': loc.y}))
|
||||
path.append(Path.Command('G0', {'Z': thread.zStart}))
|
||||
path.append(Path.Command("G0", {"X": loc.x, "Y": loc.y}))
|
||||
path.append(Path.Command("G0", {"Z": thread.zStart}))
|
||||
if leadInOut:
|
||||
path.append(Path.Command(thread.cmd, {'Y': yMax, 'J': (yMax - loc.y) / 2}))
|
||||
path.append(Path.Command(thread.cmd, {"Y": yMax, "J": (yMax - loc.y) / 2}))
|
||||
else:
|
||||
path.append(Path.Command('G1', {'Y': yMax}))
|
||||
path.append(Path.Command("G1", {"Y": yMax}))
|
||||
|
||||
z = thread.zStart
|
||||
r = -radius
|
||||
i = 0
|
||||
while True:
|
||||
z = thread.zStart + i*thread.hPitch
|
||||
z = thread.zStart + i * thread.hPitch
|
||||
if thread.overshoots(z):
|
||||
break
|
||||
if 0 == (i & 0x01):
|
||||
y = yMin
|
||||
else:
|
||||
y = yMax
|
||||
path.append(Path.Command(thread.cmd, {'Y': y, 'Z': z + thread.hPitch, 'J': r}))
|
||||
path.append(Path.Command(thread.cmd, {"Y": y, "Z": z + thread.hPitch, "J": r}))
|
||||
r = -r
|
||||
i = i + 1
|
||||
|
||||
|
||||
z = thread.zStart + i*thread.hPitch
|
||||
z = thread.zStart + i * thread.hPitch
|
||||
if PathGeom.isRoughly(z, thread.zFinal):
|
||||
x = loc.x
|
||||
else:
|
||||
@@ -151,48 +155,183 @@ def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
dx = math.sin(k * math.pi)
|
||||
y = thread.adjustY(loc.y, r * dy)
|
||||
x = thread.adjustX(loc.x, r * dx)
|
||||
path.append(Path.Command(thread.cmd, {'X': x, 'Y': y, 'Z': thread.zFinal, 'J': r}))
|
||||
path.append(
|
||||
Path.Command(thread.cmd, {"X": x, "Y": y, "Z": thread.zFinal, "J": r})
|
||||
)
|
||||
|
||||
if leadInOut:
|
||||
path.append(Path.Command(thread.cmd, {'X': loc.x, 'Y': loc.y, 'I': (loc.x - x) / 2, 'J': (loc.y - y) / 2}))
|
||||
path.append(
|
||||
Path.Command(
|
||||
thread.cmd,
|
||||
{"X": loc.x, "Y": loc.y, "I": (loc.x - x) / 2, "J": (loc.y - y) / 2},
|
||||
)
|
||||
)
|
||||
else:
|
||||
path.append(Path.Command('G1', {'X': loc.x, 'Y': loc.y}))
|
||||
path.append(Path.Command("G1", {"X": loc.x, "Y": loc.y}))
|
||||
return path
|
||||
|
||||
class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
'''Proxy object for thread milling operation.'''
|
||||
|
||||
LeftHand = 'LeftHand'
|
||||
RightHand = 'RightHand'
|
||||
ThreadTypeCustom = 'Custom'
|
||||
ThreadTypeMetricInternal = 'Metric - internal'
|
||||
ThreadTypeImperialInternal = 'Imperial - internal'
|
||||
DirectionClimb = 'Climb'
|
||||
DirectionConventional = 'Conventional'
|
||||
class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
"""Proxy object for thread milling operation."""
|
||||
|
||||
LeftHand = "LeftHand"
|
||||
RightHand = "RightHand"
|
||||
ThreadTypeCustom = "Custom"
|
||||
ThreadTypeMetricInternal = "MetricInternal"
|
||||
ThreadTypeImperialInternal = "ImperialInternal"
|
||||
DirectionClimb = "Climb"
|
||||
DirectionConventional = "Conventional"
|
||||
|
||||
ThreadOrientations = [LeftHand, RightHand]
|
||||
ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal, ThreadTypeImperialInternal]
|
||||
Directions = [DirectionClimb, DirectionConventional]
|
||||
ThreadTypes = [
|
||||
ThreadTypeCustom,
|
||||
ThreadTypeMetricInternal,
|
||||
ThreadTypeImperialInternal,
|
||||
]
|
||||
Directions = [DirectionClimb, DirectionConventional]
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
enums = {
|
||||
"ThreadType": [
|
||||
(translate("Path_ThreadMilling", "Custom"), "Custom"),
|
||||
(translate("Path_ThreadMilling", "Metric Internal"), "MetricInternal"),
|
||||
(
|
||||
translate("Path_ThreadMilling", "Imperial Internal"),
|
||||
"ImperialInternal",
|
||||
),
|
||||
], # this is the direction that the profile runs
|
||||
"ThreadOrientation": [
|
||||
(translate("Path_ThreadMilling", "LeftHand"), "LeftHand"),
|
||||
(translate("Path_ThreadMilling", "RightHand"), "RightHand"),
|
||||
], # side of profile that cutter is on in relation to direction of profile
|
||||
"Direction": [
|
||||
(translate("Path_ThreadMilling", "Climb"), "Climb"),
|
||||
(translate("Path_ThreadMilling", "Conventional"), "Conventional"),
|
||||
], # side of profile that cutter is on in relation to direction of profile
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
return PathOp.FeatureBaseGeometry
|
||||
|
||||
def initCircularHoleOperation(self, obj):
|
||||
obj.addProperty("App::PropertyEnumeration", "ThreadOrientation", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread orientation"))
|
||||
obj.ThreadOrientation = self.ThreadOrientations
|
||||
obj.addProperty("App::PropertyEnumeration", "ThreadType", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Currently only internal"))
|
||||
obj.ThreadType = self.ThreadTypes
|
||||
obj.addProperty("App::PropertyString", "ThreadName", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Defines which standard thread was chosen"))
|
||||
obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's major diameter"))
|
||||
obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's minor diameter"))
|
||||
obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's pitch - used for metric threads"))
|
||||
obj.addProperty("App::PropertyInteger", "TPI", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's TPI (turns per inch) - used for imperial threads"))
|
||||
obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
|
||||
obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
|
||||
obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Direction of thread cutting operation"))
|
||||
obj.addProperty("App::PropertyBool", "LeadInOut", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set to True to get lead in and lead out arcs at the start and end of the thread cut"))
|
||||
obj.addProperty("App::PropertyLink", "ClearanceOp", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Operation to clear the inside of the thread"))
|
||||
obj.Direction = self.Directions
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ThreadOrientation",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread orientation"),
|
||||
)
|
||||
# obj.ThreadOrientation = self.ThreadOrientations
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ThreadType",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Currently only internal"),
|
||||
)
|
||||
# obj.ThreadType = self.ThreadTypes
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"ThreadName",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Defines which standard thread was chosen"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"MajorDiameter",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread's major diameter"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"MinorDiameter",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"Pitch",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set thread's pitch - used for metric threads"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"TPI",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set thread's TPI (turns per inch) - used for imperial threads",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"ThreadFit",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set how many passes are used to cut the thread"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"Passes",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set how many passes are used to cut the thread"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Direction",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"LeadInOut",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set to True to get lead in and lead out arcs at the start and end of the thread cut",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"ClearanceOp",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Operation to clear the inside of the thread"
|
||||
),
|
||||
)
|
||||
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
def threadStartDepth(self, obj):
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
@@ -225,25 +364,25 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
PathLog.track(obj.Label)
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
PathLog.track(obj.Label, 'G2')
|
||||
return 'G2'
|
||||
PathLog.track(obj.Label, 'G3')
|
||||
return 'G3'
|
||||
PathLog.track(obj.Label, "G2")
|
||||
return "G2"
|
||||
PathLog.track(obj.Label, "G3")
|
||||
return "G3"
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
PathLog.track(obj.Label, 'G3')
|
||||
return 'G3'
|
||||
PathLog.track(obj.Label, 'G2')
|
||||
return 'G2'
|
||||
PathLog.track(obj.Label, "G3")
|
||||
return "G3"
|
||||
PathLog.track(obj.Label, "G2")
|
||||
return "G2"
|
||||
|
||||
def threadSetup(self, obj):
|
||||
# the thing to remember is that Climb, for an internal thread must always be G3
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
return ('G3', obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ('G3', obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ("G3", obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ("G3", obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
return ('G2', obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ('G2', obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ("G2", obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ("G2", obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
|
||||
def threadPassRadii(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
@@ -251,7 +390,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
rMinor = (obj.MinorDiameter.Value - self.tool.Diameter) / 2.0
|
||||
if obj.Passes < 1:
|
||||
obj.Passes = 1
|
||||
rPass = (rMajor - rMinor) / obj.Passes
|
||||
rPass = (rMajor - rMinor) / obj.Passes
|
||||
passes = [rMajor]
|
||||
for i in range(1, obj.Passes):
|
||||
passes.append(rMajor - rPass * i)
|
||||
@@ -260,20 +399,33 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
def executeThreadMill(self, obj, loc, gcode, zStart, zFinal, pitch):
|
||||
PathLog.track(obj.Label, loc, gcode, zStart, zFinal, pitch)
|
||||
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command("G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid})
|
||||
)
|
||||
|
||||
for radius in threadPasses(obj.Passes, radiiInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, float(self.tool.Diameter), float(self.tool.Crest)):
|
||||
commands = internalThreadCommands(loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut)
|
||||
for radius in threadPasses(
|
||||
obj.Passes,
|
||||
radiiInternal,
|
||||
obj.MajorDiameter.Value,
|
||||
obj.MinorDiameter.Value,
|
||||
float(self.tool.Diameter),
|
||||
float(self.tool.Crest),
|
||||
):
|
||||
commands = internalThreadCommands(
|
||||
loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut
|
||||
)
|
||||
for cmd in commands:
|
||||
p = cmd.Parameters
|
||||
if cmd.Name in ['G0']:
|
||||
p.update({'F': self.vertRapid})
|
||||
if cmd.Name in ['G1', 'G2', 'G3']:
|
||||
p.update({'F': self.horizFeed})
|
||||
if cmd.Name in ["G0"]:
|
||||
p.update({"F": self.vertRapid})
|
||||
if cmd.Name in ["G1", "G2", "G3"]:
|
||||
p.update({"F": self.horizFeed})
|
||||
cmd.Parameters = p
|
||||
self.commandlist.extend(commands)
|
||||
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command("G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid})
|
||||
)
|
||||
|
||||
def circularHoleExecute(self, obj, holes):
|
||||
PathLog.track()
|
||||
@@ -290,11 +442,17 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
|
||||
# rapid to clearance height
|
||||
for loc in holes:
|
||||
self.executeThreadMill(obj, FreeCAD.Vector(loc['x'], loc['y'], 0), cmd, zStart, zFinal, pitch)
|
||||
self.executeThreadMill(
|
||||
obj,
|
||||
FreeCAD.Vector(loc["x"], loc["y"], 0),
|
||||
cmd,
|
||||
zStart,
|
||||
zFinal,
|
||||
pitch,
|
||||
)
|
||||
else:
|
||||
PathLog.error("No suitable Tool found for thread milling operation")
|
||||
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
obj.ThreadOrientation = self.RightHand
|
||||
obj.ThreadType = self.ThreadTypeMetricInternal
|
||||
@@ -306,8 +464,8 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
obj.LeadInOut = True
|
||||
|
||||
def isToolSupported(self, obj, tool):
|
||||
'''Thread milling only supports thread milling cutters.'''
|
||||
return hasattr(tool, 'Diameter') and hasattr(tool, 'Crest')
|
||||
"""Thread milling only supports thread milling cutters."""
|
||||
return hasattr(tool, "Diameter") and hasattr(tool, "Crest")
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
@@ -327,11 +485,10 @@ def SetupProperties():
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a thread milling operation.'''
|
||||
"""Create(name) ... Creates and returns a thread milling operation."""
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectThreadMilling(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathCircularHoleBaseGui as PathCircularHoleBaseGui
|
||||
import PathScripts.PathThreadMilling as PathThreadMilling
|
||||
import PathScripts.PathGui as PathGui
|
||||
@@ -30,6 +30,9 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import csv
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Path Thread Milling Operation UI."
|
||||
@@ -37,52 +40,88 @@ __author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "UI and Command for Path Thread Milling Operation."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
def setupCombo(combo, selections):
|
||||
combo.clear()
|
||||
for item in selections:
|
||||
combo.addItem(item)
|
||||
|
||||
def fillThreads(combo, dataFile):
|
||||
combo.blockSignals(True)
|
||||
combo.clear()
|
||||
with open("{}Mod/Path/Data/Threads/{}.csv".format(FreeCAD.getHomePath(), dataFile)) as fp:
|
||||
with open(
|
||||
"{}Mod/Path/Data/Threads/{}.csv".format(FreeCAD.getHomePath(), dataFile)
|
||||
) as fp:
|
||||
reader = csv.DictReader(fp)
|
||||
for row in reader:
|
||||
combo.addItem(row['name'], row)
|
||||
combo.addItem(row["name"], row)
|
||||
combo.setEnabled(True)
|
||||
combo.blockSignals(False)
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
'''Controller for the thread milling operation's page'''
|
||||
"""Controller for the thread milling operation's page"""
|
||||
|
||||
def initPage(self, obj):
|
||||
self.majorDia = PathGui.QuantitySpinBox(self.form.threadMajor, obj, 'MajorDiameter') # pylint: disable=attribute-defined-outside-init
|
||||
self.minorDia = PathGui.QuantitySpinBox(self.form.threadMinor, obj, 'MinorDiameter') # pylint: disable=attribute-defined-outside-init
|
||||
self.pitch = PathGui.QuantitySpinBox(self.form.threadPitch, obj, 'Pitch') # pylint: disable=attribute-defined-outside-init
|
||||
self.majorDia = PathGui.QuantitySpinBox(
|
||||
self.form.threadMajor, obj, "MajorDiameter"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.minorDia = PathGui.QuantitySpinBox(
|
||||
self.form.threadMinor, obj, "MinorDiameter"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.pitch = PathGui.QuantitySpinBox(
|
||||
self.form.threadPitch, obj, "Pitch"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations)
|
||||
setupCombo(self.form.threadType, obj.Proxy.ThreadTypes)
|
||||
setupCombo(self.form.opDirection, obj.Proxy.Directions)
|
||||
# setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations)
|
||||
# setupCombo(self.form.threadType, obj.Proxy.ThreadTypes)
|
||||
# setupCombo(self.form.opDirection, obj.Proxy.Directions)
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... return UI'''
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpThreadMillingEdit.ui")
|
||||
"""getForm() ... return UI"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpThreadMillingEdit.ui")
|
||||
comboToPropertyMap = [
|
||||
("threadOrientation", "ThreadOrientation"),
|
||||
("threadType", "ThreadType"),
|
||||
("opDirection", "Direction"),
|
||||
]
|
||||
enumTups = PathThreadMilling.ObjectThreadMilling.propertyEnumerations(
|
||||
dataType="raw"
|
||||
)
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
|
||||
return form
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... update obj's properties with values from the UI'''
|
||||
"""getFields(obj) ... update obj's properties with values from the UI"""
|
||||
PathLog.track()
|
||||
|
||||
self.majorDia.updateProperty()
|
||||
self.minorDia.updateProperty()
|
||||
self.pitch.updateProperty()
|
||||
|
||||
obj.ThreadOrientation = self.form.threadOrientation.currentText()
|
||||
obj.ThreadType = self.form.threadType.currentText()
|
||||
obj.ThreadOrientation = self.form.threadOrientation.currentData()
|
||||
obj.ThreadType = self.form.threadType.currentData()
|
||||
obj.ThreadName = self.form.threadName.currentText()
|
||||
obj.Direction = self.form.opDirection.currentText()
|
||||
obj.Direction = self.form.opDirection.currentData()
|
||||
obj.Passes = self.form.opPasses.value()
|
||||
obj.LeadInOut = self.form.leadInOut.checkState() == QtCore.Qt.Checked
|
||||
obj.TPI = self.form.threadTPI.value()
|
||||
@@ -90,23 +129,22 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... update UI with obj properties' values'''
|
||||
"""setFields(obj) ... update UI with obj properties' values"""
|
||||
PathLog.track()
|
||||
|
||||
self.form.threadOrientation.setCurrentText(obj.ThreadOrientation)
|
||||
self.selectInComboBox(obj.ThreadOrientation, self.form.threadOrientation)
|
||||
self.selectInComboBox(obj.ThreadType, self.form.threadType)
|
||||
self.selectInComboBox(obj.Direction, self.form.opDirection)
|
||||
|
||||
self.form.threadType.blockSignals(True)
|
||||
self.form.threadName.blockSignals(True)
|
||||
self.form.threadType.setCurrentText(obj.ThreadType)
|
||||
self._updateFromThreadType()
|
||||
self.form.threadName.setCurrentText(obj.ThreadName)
|
||||
self.form.threadType.blockSignals(False)
|
||||
self.form.threadName.blockSignals(False)
|
||||
self.form.threadTPI.setValue(obj.TPI)
|
||||
|
||||
self.form.opPasses.setValue(obj.Passes)
|
||||
self.form.opDirection.setCurrentText(obj.Direction)
|
||||
self.form.leadInOut.setCheckState(QtCore.Qt.Checked if obj.LeadInOut else QtCore.Qt.Unchecked)
|
||||
self.form.leadInOut.setCheckState(
|
||||
QtCore.Qt.Checked if obj.LeadInOut else QtCore.Qt.Unchecked
|
||||
)
|
||||
|
||||
self.majorDia.updateSpinBox()
|
||||
self.minorDia.updateSpinBox()
|
||||
@@ -114,16 +152,24 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
|
||||
|
||||
def _isThreadMetric(self):
|
||||
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeMetricInternal
|
||||
return (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeMetricInternal
|
||||
)
|
||||
|
||||
def _isThreadImperial(self):
|
||||
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeImperialInternal
|
||||
return (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeImperialInternal
|
||||
)
|
||||
|
||||
def _updateFromThreadType(self):
|
||||
|
||||
if self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom:
|
||||
if (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom
|
||||
):
|
||||
self.form.threadName.setEnabled(False)
|
||||
self.form.threadFit.setEnabled(False)
|
||||
self.form.threadFitLabel.setEnabled(False)
|
||||
@@ -140,7 +186,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadTPI.setEnabled(False)
|
||||
self.form.threadTPILabel.setEnabled(False)
|
||||
self.form.threadTPI.setValue(0)
|
||||
fillThreads(self.form.threadName, 'metric-internal')
|
||||
fillThreads(self.form.threadName, "metric-internal")
|
||||
|
||||
if self._isThreadImperial():
|
||||
self.form.threadFit.setEnabled(True)
|
||||
@@ -150,24 +196,24 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadTPI.setEnabled(True)
|
||||
self.form.threadTPILabel.setEnabled(True)
|
||||
self.pitch.updateSpinBox(0)
|
||||
fillThreads(self.form.threadName, 'imperial-internal')
|
||||
fillThreads(self.form.threadName, "imperial-internal")
|
||||
|
||||
def _updateFromThreadName(self):
|
||||
thread = self.form.threadName.currentData()
|
||||
fit = float(self.form.threadFit.value()) / 100
|
||||
mamin = float(thread['dMajorMin'])
|
||||
mamax = float(thread['dMajorMax'])
|
||||
mamin = float(thread["dMajorMin"])
|
||||
mamax = float(thread["dMajorMax"])
|
||||
major = mamin + (mamax - mamin) * fit
|
||||
mimin = float(thread['dMinorMin'])
|
||||
mimax = float(thread['dMinorMax'])
|
||||
mimin = float(thread["dMinorMin"])
|
||||
mimax = float(thread["dMinorMax"])
|
||||
minor = mimin + (mimax - mimin) * fit
|
||||
|
||||
if self._isThreadMetric():
|
||||
pitch = float(thread['pitch'])
|
||||
pitch = float(thread["pitch"])
|
||||
self.pitch.updateSpinBox(pitch)
|
||||
|
||||
if self._isThreadImperial():
|
||||
tpi = int(thread['tpi'])
|
||||
tpi = int(thread["tpi"])
|
||||
self.form.threadTPI.setValue(tpi)
|
||||
minor = minor * 25.4
|
||||
major = major * 25.4
|
||||
@@ -178,7 +224,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.setDirty()
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model"""
|
||||
signals = []
|
||||
|
||||
signals.append(self.form.threadMajor.editingFinished)
|
||||
@@ -200,12 +246,17 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadFit.valueChanged.connect(self._updateFromThreadName)
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Thread Milling',
|
||||
PathThreadMilling.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_ThreadMilling',
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Thread Milling"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Creates a Path Thread Milling operation from features of a base object"),
|
||||
PathThreadMilling.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"ThreadMilling",
|
||||
PathThreadMilling.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_ThreadMilling",
|
||||
QT_TRANSLATE_NOOP("Path_ThreadMilling", "Thread Milling"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_ThreadMilling",
|
||||
"Creates a Path Thread Milling operation from features of a base object",
|
||||
),
|
||||
PathThreadMilling.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathThreadMillingGui ... done\n")
|
||||
|
||||
@@ -25,30 +25,32 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathPropertyBag as PathPropertyBag
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PySide
|
||||
import json
|
||||
import os
|
||||
import zipfile
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Tool bits."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Class to deal with and represent a tool bit."
|
||||
|
||||
PropertyGroupShape = 'Shape'
|
||||
PropertyGroupShape = "Shape"
|
||||
|
||||
_DebugFindTool = False
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule()
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
def _findToolFile(name, containerFile, typ):
|
||||
PathLog.track(name)
|
||||
@@ -74,7 +76,6 @@ def _findToolFile(name, containerFile, typ):
|
||||
return (True, fullPath)
|
||||
return (False, None)
|
||||
|
||||
|
||||
for p in paths:
|
||||
found, path = _findFile(p, name)
|
||||
if found:
|
||||
@@ -83,26 +84,26 @@ def _findToolFile(name, containerFile, typ):
|
||||
|
||||
|
||||
def findToolShape(name, path=None):
|
||||
'''findToolShape(name, path) ... search for name, if relative path look in path'''
|
||||
"""findToolShape(name, path) ... search for name, if relative path look in path"""
|
||||
PathLog.track(name, path)
|
||||
return _findToolFile(name, path, 'Shape')
|
||||
return _findToolFile(name, path, "Shape")
|
||||
|
||||
|
||||
def findToolBit(name, path=None):
|
||||
'''findToolBit(name, path) ... search for name, if relative path look in path'''
|
||||
"""findToolBit(name, path) ... search for name, if relative path look in path"""
|
||||
PathLog.track(name, path)
|
||||
if name.endswith('.fctb'):
|
||||
return _findToolFile(name, path, 'Bit')
|
||||
return _findToolFile("{}.fctb".format(name), path, 'Bit')
|
||||
if name.endswith(".fctb"):
|
||||
return _findToolFile(name, path, "Bit")
|
||||
return _findToolFile("{}.fctb".format(name), path, "Bit")
|
||||
|
||||
|
||||
# Only used in ToolBit unit test module: TestPathToolBit.py
|
||||
def findToolLibrary(name, path=None):
|
||||
'''findToolLibrary(name, path) ... search for name, if relative path look in path'''
|
||||
"""findToolLibrary(name, path) ... search for name, if relative path look in path"""
|
||||
PathLog.track(name, path)
|
||||
if name.endswith('.fctl'):
|
||||
return _findToolFile(name, path, 'Library')
|
||||
return _findToolFile("{}.fctl".format(name), path, 'Library')
|
||||
if name.endswith(".fctl"):
|
||||
return _findToolFile(name, path, "Library")
|
||||
return _findToolFile("{}.fctl".format(name), path, "Library")
|
||||
|
||||
|
||||
def _findRelativePath(path, typ):
|
||||
@@ -110,7 +111,7 @@ def _findRelativePath(path, typ):
|
||||
relative = path
|
||||
for p in PathPreferences.searchPathsTool(typ):
|
||||
if path.startswith(p):
|
||||
p = path[len(p):]
|
||||
p = path[len(p) :]
|
||||
if os.path.sep == p[0]:
|
||||
p = p[1:]
|
||||
if len(p) < len(relative):
|
||||
@@ -130,23 +131,52 @@ def findRelativePathTool(path):
|
||||
|
||||
|
||||
def findRelativePathLibrary(path):
|
||||
return _findRelativePath(path, 'Library')
|
||||
return _findRelativePath(path, "Library")
|
||||
|
||||
|
||||
class ToolBit(object):
|
||||
|
||||
def __init__(self, obj, shapeFile, path=None):
|
||||
PathLog.track(obj.Label, shapeFile, path)
|
||||
self.obj = obj
|
||||
obj.addProperty('App::PropertyFile', 'BitShape', 'Base', translate('PathToolBit', 'Shape for bit shape'))
|
||||
obj.addProperty('App::PropertyLink', 'BitBody', 'Base', translate('PathToolBit', 'The parametrized body representing the tool bit'))
|
||||
obj.addProperty('App::PropertyFile', 'File', 'Base', translate('PathToolBit', 'The file of the tool'))
|
||||
obj.addProperty('App::PropertyString', 'ShapeName', 'Base', translate('PathToolBit', 'The name of the shape file'))
|
||||
obj.addProperty('App::PropertyStringList', 'BitPropertyNames', 'Base', translate('PathToolBit', 'List of all properties inherited from the bit'))
|
||||
obj.addProperty(
|
||||
"App::PropertyFile",
|
||||
"BitShape",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Shape for bit shape"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"BitBody",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The parametrized body representing the tool bit"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyFile",
|
||||
"File",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The file of the tool"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"ShapeName",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The name of the shape file"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"BitPropertyNames",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "List of all properties inherited from the bit"
|
||||
),
|
||||
)
|
||||
|
||||
if path:
|
||||
obj.File = path
|
||||
if shapeFile is None:
|
||||
obj.BitShape = 'endmill.fcstd'
|
||||
obj.BitShape = "endmill.fcstd"
|
||||
self._setupBitShape(obj)
|
||||
self.unloadBitBody(obj)
|
||||
else:
|
||||
@@ -159,7 +189,7 @@ class ToolBit(object):
|
||||
|
||||
def __setstate__(self, state):
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if hasattr(obj, 'Proxy') and obj.Proxy == self:
|
||||
if hasattr(obj, "Proxy") and obj.Proxy == self:
|
||||
self.obj = obj
|
||||
break
|
||||
return None
|
||||
@@ -168,14 +198,21 @@ class ToolBit(object):
|
||||
# when files are shared it is essential to be able to change/set the shape file,
|
||||
# otherwise the file is hard to use
|
||||
# obj.setEditorMode('BitShape', 1)
|
||||
obj.setEditorMode('BitBody', 2)
|
||||
obj.setEditorMode('File', 1)
|
||||
obj.setEditorMode('Shape', 2)
|
||||
if not hasattr(obj, 'BitPropertyNames'):
|
||||
obj.addProperty('App::PropertyStringList', 'BitPropertyNames', 'Base', translate('PathToolBit', 'List of all properties inherited from the bit'))
|
||||
obj.setEditorMode("BitBody", 2)
|
||||
obj.setEditorMode("File", 1)
|
||||
obj.setEditorMode("Shape", 2)
|
||||
if not hasattr(obj, "BitPropertyNames"):
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"BitPropertyNames",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "List of all properties inherited from the bit"
|
||||
),
|
||||
)
|
||||
propNames = []
|
||||
for prop in obj.PropertiesList:
|
||||
if obj.getGroupOfProperty(prop) == 'Bit':
|
||||
if obj.getGroupOfProperty(prop) == "Bit":
|
||||
val = obj.getPropertyByName(prop)
|
||||
typ = obj.getTypeIdOfProperty(prop)
|
||||
dsc = obj.getDocumentationOfProperty(prop)
|
||||
@@ -185,10 +222,10 @@ class ToolBit(object):
|
||||
|
||||
PathUtil.setProperty(obj, prop, val)
|
||||
propNames.append(prop)
|
||||
elif obj.getGroupOfProperty(prop) == 'Attribute':
|
||||
elif obj.getGroupOfProperty(prop) == "Attribute":
|
||||
propNames.append(prop)
|
||||
obj.BitPropertyNames = propNames
|
||||
obj.setEditorMode('BitPropertyNames', 2)
|
||||
obj.setEditorMode("BitPropertyNames", 2)
|
||||
|
||||
for prop in obj.BitPropertyNames:
|
||||
if obj.getGroupOfProperty(prop) == PropertyGroupShape:
|
||||
@@ -202,7 +239,7 @@ class ToolBit(object):
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
PathLog.track(obj.Label, prop)
|
||||
if prop == 'BitShape' and 'Restore' not in obj.State:
|
||||
if prop == "BitShape" and "Restore" not in obj.State:
|
||||
self._setupBitShape(obj)
|
||||
|
||||
def onDelete(self, obj, arg2=None):
|
||||
@@ -212,7 +249,11 @@ class ToolBit(object):
|
||||
|
||||
def _updateBitShape(self, obj, properties=None):
|
||||
if obj.BitBody is not None:
|
||||
for attributes in [o for o in obj.BitBody.Group if hasattr(o, 'Proxy') and hasattr(o.Proxy, 'getCustomProperties')]:
|
||||
for attributes in [
|
||||
o
|
||||
for o in obj.BitBody.Group
|
||||
if hasattr(o, "Proxy") and hasattr(o.Proxy, "getCustomProperties")
|
||||
]:
|
||||
for prop in attributes.Proxy.getCustomProperties():
|
||||
# the property might not exist in our local object (new attribute in shape)
|
||||
# for such attributes we just keep the default
|
||||
@@ -291,7 +332,7 @@ class ToolBit(object):
|
||||
dsc = orig.getDocumentationOfProperty(prop)
|
||||
|
||||
obj.addProperty(typ, prop, grp, dsc)
|
||||
if 'App::PropertyEnumeration' == typ:
|
||||
if "App::PropertyEnumeration" == typ:
|
||||
setattr(obj, prop, orig.getEnumerationsOfProperty(prop))
|
||||
|
||||
obj.setEditorMode(prop, 1)
|
||||
@@ -315,16 +356,21 @@ class ToolBit(object):
|
||||
if bitBody.ViewObject:
|
||||
bitBody.ViewObject.Visibility = False
|
||||
|
||||
PathLog.debug("bitBody.{} ({}): {}".format(bitBody.Label, bitBody.Name, type(bitBody)))
|
||||
PathLog.debug(
|
||||
"bitBody.{} ({}): {}".format(bitBody.Label, bitBody.Name, type(bitBody))
|
||||
)
|
||||
|
||||
propNames = []
|
||||
for attributes in [o for o in bitBody.Group if PathPropertyBag.IsPropertyBag(o)]:
|
||||
for attributes in [
|
||||
o for o in bitBody.Group if PathPropertyBag.IsPropertyBag(o)
|
||||
]:
|
||||
PathLog.debug("Process properties from {}".format(attributes.Label))
|
||||
for prop in attributes.Proxy.getCustomProperties():
|
||||
self._setupProperty(obj, prop, attributes)
|
||||
propNames.append(prop)
|
||||
if not propNames:
|
||||
PathLog.error(translate('PathToolBit', 'Did not find a PropertyBag in {} - not a ToolBit shape?'.format(docName)))
|
||||
PathLog.error("Did not find a PropertyBag in {} - not a ToolBit shape?".format(
|
||||
docName))
|
||||
|
||||
# has to happen last because it could trigger op.execute evaluations
|
||||
obj.BitPropertyNames = propNames
|
||||
@@ -332,20 +378,32 @@ class ToolBit(object):
|
||||
self._copyBitShape(obj)
|
||||
|
||||
def toolShapeProperties(self, obj):
|
||||
'''toolShapeProperties(obj) ... return all properties defining it's shape'''
|
||||
return sorted([prop for prop in obj.BitPropertyNames if obj.getGroupOfProperty(prop) == PropertyGroupShape])
|
||||
"""toolShapeProperties(obj) ... return all properties defining it's shape"""
|
||||
return sorted(
|
||||
[
|
||||
prop
|
||||
for prop in obj.BitPropertyNames
|
||||
if obj.getGroupOfProperty(prop) == PropertyGroupShape
|
||||
]
|
||||
)
|
||||
|
||||
def toolAdditionalProperties(self, obj):
|
||||
'''toolShapeProperties(obj) ... return all properties unrelated to it's shape'''
|
||||
return sorted([prop for prop in obj.BitPropertyNames if obj.getGroupOfProperty(prop) != PropertyGroupShape])
|
||||
"""toolShapeProperties(obj) ... return all properties unrelated to it's shape"""
|
||||
return sorted(
|
||||
[
|
||||
prop
|
||||
for prop in obj.BitPropertyNames
|
||||
if obj.getGroupOfProperty(prop) != PropertyGroupShape
|
||||
]
|
||||
)
|
||||
|
||||
def toolGroupsAndProperties(self, obj, includeShape=True):
|
||||
'''toolGroupsAndProperties(obj) ... returns a dictionary of group names with a list of property names.'''
|
||||
"""toolGroupsAndProperties(obj) ... returns a dictionary of group names with a list of property names."""
|
||||
category = {}
|
||||
for prop in obj.BitPropertyNames:
|
||||
group = obj.getGroupOfProperty(prop)
|
||||
if includeShape or group != PropertyGroupShape:
|
||||
properties = category.get(group, [])
|
||||
properties = category.get(group, [])
|
||||
properties.append(prop)
|
||||
category[group] = properties
|
||||
return category
|
||||
@@ -354,10 +412,10 @@ class ToolBit(object):
|
||||
if obj.BitShape:
|
||||
path = findToolShape(obj.BitShape)
|
||||
if path:
|
||||
with open(path, 'rb') as fd:
|
||||
with open(path, "rb") as fd:
|
||||
try:
|
||||
zf = zipfile.ZipFile(fd)
|
||||
pf = zf.open('thumbnails/Thumbnail.png', 'r')
|
||||
pf = zf.open("thumbnails/Thumbnail.png", "r")
|
||||
data = pf.read()
|
||||
pf.close()
|
||||
return data
|
||||
@@ -368,55 +426,58 @@ class ToolBit(object):
|
||||
def saveToFile(self, obj, path, setFile=True):
|
||||
PathLog.track(path)
|
||||
try:
|
||||
with open(path, 'w') as fp:
|
||||
json.dump(self.templateAttrs(obj), fp, indent=' ')
|
||||
with open(path, "w") as fp:
|
||||
json.dump(self.templateAttrs(obj), fp, indent=" ")
|
||||
if setFile:
|
||||
obj.File = path
|
||||
return True
|
||||
except (OSError, IOError) as e:
|
||||
PathLog.error("Could not save tool {} to {} ({})".format(obj.Label, path, e))
|
||||
PathLog.error(
|
||||
"Could not save tool {} to {} ({})".format(obj.Label, path, e)
|
||||
)
|
||||
raise
|
||||
|
||||
def templateAttrs(self, obj):
|
||||
attrs = {}
|
||||
attrs['version'] = 2 # Path.Tool is version 1
|
||||
attrs['name'] = obj.Label
|
||||
attrs["version"] = 2 # Path.Tool is version 1
|
||||
attrs["name"] = obj.Label
|
||||
if PathPreferences.toolsStoreAbsolutePaths():
|
||||
attrs['shape'] = obj.BitShape
|
||||
attrs["shape"] = obj.BitShape
|
||||
else:
|
||||
# attrs['shape'] = findRelativePathShape(obj.BitShape)
|
||||
# Extract the name of the shape file
|
||||
__, filShp = os.path.split(obj.BitShape) # __ is an ignored placeholder acknowledged by LGTM
|
||||
attrs['shape'] = str(filShp)
|
||||
__, filShp = os.path.split(
|
||||
obj.BitShape
|
||||
) # __ is an ignored placeholder acknowledged by LGTM
|
||||
attrs["shape"] = str(filShp)
|
||||
params = {}
|
||||
for name in obj.BitPropertyNames:
|
||||
params[name] = PathUtil.getPropertyValueString(obj, name)
|
||||
attrs['parameter'] = params
|
||||
attrs["parameter"] = params
|
||||
params = {}
|
||||
attrs['attribute'] = params
|
||||
attrs["attribute"] = params
|
||||
return attrs
|
||||
|
||||
|
||||
def Declaration(path):
|
||||
PathLog.track(path)
|
||||
with open(path, 'r') as fp:
|
||||
with open(path, "r") as fp:
|
||||
return json.load(fp)
|
||||
|
||||
|
||||
class ToolBitFactory(object):
|
||||
|
||||
def CreateFromAttrs(self, attrs, name='ToolBit', path=None):
|
||||
def CreateFromAttrs(self, attrs, name="ToolBit", path=None):
|
||||
PathLog.track(attrs, path)
|
||||
obj = Factory.Create(name, attrs['shape'], path)
|
||||
obj.Label = attrs['name']
|
||||
params = attrs['parameter']
|
||||
obj = Factory.Create(name, attrs["shape"], path)
|
||||
obj.Label = attrs["name"]
|
||||
params = attrs["parameter"]
|
||||
for prop in params:
|
||||
PathUtil.setProperty(obj, prop, params[prop])
|
||||
obj.Proxy._updateBitShape(obj)
|
||||
obj.Proxy.unloadBitBody(obj)
|
||||
return obj
|
||||
|
||||
def CreateFrom(self, path, name='ToolBit'):
|
||||
def CreateFrom(self, path, name="ToolBit"):
|
||||
PathLog.track(name, path)
|
||||
try:
|
||||
data = Declaration(path)
|
||||
@@ -426,9 +487,9 @@ class ToolBitFactory(object):
|
||||
PathLog.error("%s not a valid tool file (%s)" % (path, e))
|
||||
raise
|
||||
|
||||
def Create(self, name='ToolBit', shapeFile=None, path=None):
|
||||
def Create(self, name="ToolBit", shapeFile=None, path=None):
|
||||
PathLog.track(name, shapeFile, path)
|
||||
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', name)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Proxy = ToolBit(obj, shapeFile, path)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -20,25 +20,37 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts
|
||||
import PathScripts.PathLog as PathLog
|
||||
import os
|
||||
|
||||
from PySide import QtCore
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class CommandToolBitCreate:
|
||||
'''
|
||||
"""
|
||||
Command used to create a new Tool.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_ToolBit',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Create Tool"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Creates a new ToolBit object")}
|
||||
return {
|
||||
"Pixmap": "Path_ToolBit",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_ToolBitCreate", "Create Tool"),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolBitCreate", "Creates a new ToolBit object"
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
@@ -47,26 +59,33 @@ class CommandToolBitCreate:
|
||||
obj = PathScripts.PathToolBit.Factory.Create()
|
||||
obj.ViewObject.Proxy.setCreate(obj.ViewObject)
|
||||
|
||||
|
||||
class CommandToolBitSave:
|
||||
'''
|
||||
"""
|
||||
Command used to save an existing Tool to a file.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, saveAs):
|
||||
self.saveAs = saveAs
|
||||
|
||||
def GetResources(self):
|
||||
if self.saveAs:
|
||||
menuTxt = QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save Tool as...")
|
||||
menuTxt = QT_TRANSLATE_NOOP("Path_ToolBitSaveAs", "Save Tool as...")
|
||||
else:
|
||||
menuTxt = QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save Tool")
|
||||
return {'Pixmap': 'Path_ToolBit',
|
||||
'MenuText': menuTxt,
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save an existing ToolBit object to a file")}
|
||||
menuTxt = QT_TRANSLATE_NOOP("Path_ToolBitSave", "Save Tool")
|
||||
return {
|
||||
"Pixmap": "Path_ToolBit",
|
||||
"MenuText": menuTxt,
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolBitSave", "Save an existing ToolBit object to a file"
|
||||
),
|
||||
}
|
||||
|
||||
def selectedTool(self):
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if 1 == len(sel) and isinstance(sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit):
|
||||
if 1 == len(sel) and isinstance(
|
||||
sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit
|
||||
):
|
||||
return sel[0].Object
|
||||
return None
|
||||
|
||||
@@ -80,6 +99,7 @@ class CommandToolBitSave:
|
||||
|
||||
def Activated(self):
|
||||
from PySide import QtGui
|
||||
|
||||
tool = self.selectedTool()
|
||||
if tool:
|
||||
path = None
|
||||
@@ -87,35 +107,47 @@ class CommandToolBitSave:
|
||||
if tool.File:
|
||||
fname = tool.File
|
||||
else:
|
||||
fname = os.path.join(PathScripts.PathPreferences.lastPathToolBit(), tool.Label + '.fctb')
|
||||
foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Tool", fname, "*.fctb")
|
||||
fname = os.path.join(
|
||||
PathScripts.PathPreferences.lastPathToolBit(),
|
||||
tool.Label + ".fctb",
|
||||
)
|
||||
foo = QtGui.QFileDialog.getSaveFileName(
|
||||
QtGui.QApplication.activeWindow(), "Tool", fname, "*.fctb"
|
||||
)
|
||||
if foo:
|
||||
path = foo[0]
|
||||
else:
|
||||
path = tool.File
|
||||
|
||||
if path:
|
||||
if not path.endswith('.fctb'):
|
||||
path += '.fctb'
|
||||
if not path.endswith(".fctb"):
|
||||
path += ".fctb"
|
||||
tool.Proxy.saveToFile(tool, path)
|
||||
PathScripts.PathPreferences.setLastPathToolBit(os.path.dirname(path))
|
||||
|
||||
|
||||
class CommandToolBitLoad:
|
||||
'''
|
||||
"""
|
||||
Command used to load an existing Tool from a file into the current document.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_ToolBit',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Load Tool"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Load an existing ToolBit object from a file")}
|
||||
return {
|
||||
"Pixmap": "Path_ToolBit",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_ToolBitLoad", "Load Tool"),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolBitLoad", "Load an existing ToolBit object from a file"
|
||||
),
|
||||
}
|
||||
|
||||
def selectedTool(self):
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if 1 == len(sel) and isinstance(sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit):
|
||||
if 1 == len(sel) and isinstance(
|
||||
sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit
|
||||
):
|
||||
return sel[0].Object
|
||||
return None
|
||||
|
||||
@@ -126,12 +158,18 @@ class CommandToolBitLoad:
|
||||
if PathScripts.PathToolBitGui.LoadTools():
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_ToolBitCreate', CommandToolBitCreate())
|
||||
FreeCADGui.addCommand('Path_ToolBitLoad', CommandToolBitLoad())
|
||||
FreeCADGui.addCommand('Path_ToolBitSave', CommandToolBitSave(False))
|
||||
FreeCADGui.addCommand('Path_ToolBitSaveAs', CommandToolBitSave(True))
|
||||
|
||||
CommandList = ['Path_ToolBitCreate', 'Path_ToolBitLoad', 'Path_ToolBitSave', 'Path_ToolBitSaveAs']
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand("Path_ToolBitCreate", CommandToolBitCreate())
|
||||
FreeCADGui.addCommand("Path_ToolBitLoad", CommandToolBitLoad())
|
||||
FreeCADGui.addCommand("Path_ToolBitSave", CommandToolBitSave(False))
|
||||
FreeCADGui.addCommand("Path_ToolBitSaveAs", CommandToolBitSave(True))
|
||||
|
||||
CommandList = [
|
||||
"Path_ToolBitCreate",
|
||||
"Path_ToolBitLoad",
|
||||
"Path_ToolBitSave",
|
||||
"Path_ToolBitSaveAs",
|
||||
]
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathToolBitCmd... done\n")
|
||||
|
||||
@@ -20,32 +20,30 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCADGui
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathPropertyEditor as PathPropertyEditor
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import os
|
||||
import re
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class _Delegate(QtGui.QStyledItemDelegate):
|
||||
'''Handles the creation of an appropriate editing widget for a given property.'''
|
||||
ObjectRole = QtCore.Qt.UserRole + 1
|
||||
"""Handles the creation of an appropriate editing widget for a given property."""
|
||||
|
||||
ObjectRole = QtCore.Qt.UserRole + 1
|
||||
PropertyRole = QtCore.Qt.UserRole + 2
|
||||
EditorRole = QtCore.Qt.UserRole + 3
|
||||
EditorRole = QtCore.Qt.UserRole + 3
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = index.data(self.EditorRole)
|
||||
@@ -64,14 +62,18 @@ class _Delegate(QtGui.QStyledItemDelegate):
|
||||
# called to update the model with the data from the widget
|
||||
editor = index.data(self.EditorRole)
|
||||
editor.setModelData(widget)
|
||||
index.model().setData(index, PathUtil.getPropertyValueString(editor.obj, editor.prop), QtCore.Qt.DisplayRole)
|
||||
index.model().setData(
|
||||
index,
|
||||
PathUtil.getPropertyValueString(editor.obj, editor.prop),
|
||||
QtCore.Qt.DisplayRole,
|
||||
)
|
||||
|
||||
|
||||
class ToolBitEditor(object):
|
||||
'''UI and controller for editing a ToolBit.
|
||||
"""UI and controller for editing a ToolBit.
|
||||
The controller embeds the UI to the parentWidget which has to have a
|
||||
layout attached to it.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, tool, parentWidget=None, loadBitBody=True):
|
||||
PathLog.track()
|
||||
@@ -84,7 +86,7 @@ class ToolBitEditor(object):
|
||||
self.tool = tool
|
||||
self.loadbitbody = loadBitBody
|
||||
if not tool.BitShape:
|
||||
self.tool.BitShape = 'endmill.fcstd'
|
||||
self.tool.BitShape = "endmill.fcstd"
|
||||
|
||||
if self.loadbitbody:
|
||||
self.tool.Proxy.loadBitBody(self.tool)
|
||||
@@ -107,7 +109,7 @@ class ToolBitEditor(object):
|
||||
# which aren't being needed anymore.
|
||||
|
||||
def labelText(name):
|
||||
return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', name))
|
||||
return re.sub("([A-Z][a-z]+)", r" \1", re.sub("([A-Z]+)", r" \1", name))
|
||||
|
||||
layout = self.form.bitParams.layout()
|
||||
ui = FreeCADGui.UiLoader()
|
||||
@@ -125,12 +127,12 @@ class ToolBitEditor(object):
|
||||
label.show()
|
||||
qsb.show()
|
||||
else:
|
||||
qsb = ui.createWidget('Gui::QuantitySpinBox')
|
||||
qsb = ui.createWidget("Gui::QuantitySpinBox")
|
||||
editor = PathGui.QuantitySpinBox(qsb, tool, name)
|
||||
label = QtGui.QLabel(labelText(name))
|
||||
label = QtGui.QLabel(labelText(name))
|
||||
self.widgets.append((label, qsb, editor))
|
||||
PathLog.debug("create row: {} [{}] {}".format(nr, name, type(qsb)))
|
||||
if hasattr(qsb, 'editingFinished'):
|
||||
if hasattr(qsb, "editingFinished"):
|
||||
qsb.editingFinished.connect(self.updateTool)
|
||||
|
||||
if nr >= layout.rowCount():
|
||||
@@ -156,10 +158,10 @@ class ToolBitEditor(object):
|
||||
PathLog.track()
|
||||
|
||||
setup = True
|
||||
if not hasattr(self, 'delegate'):
|
||||
if not hasattr(self, "delegate"):
|
||||
self.delegate = _Delegate(self.form.attrTree)
|
||||
self.model = QtGui.QStandardItemModel(self.form.attrTree)
|
||||
self.model.setHorizontalHeaderLabels(['Property', 'Value'])
|
||||
self.model.setHorizontalHeaderLabels(["Property", "Value"])
|
||||
else:
|
||||
self.model.removeRows(0, self.model.rowCount())
|
||||
setup = False
|
||||
@@ -175,21 +177,22 @@ class ToolBitEditor(object):
|
||||
label.setEditable(False)
|
||||
|
||||
value = QtGui.QStandardItem()
|
||||
value.setData(PathUtil.getPropertyValueString(tool, prop), QtCore.Qt.DisplayRole)
|
||||
value.setData(
|
||||
PathUtil.getPropertyValueString(tool, prop), QtCore.Qt.DisplayRole
|
||||
)
|
||||
value.setData(tool, _Delegate.ObjectRole)
|
||||
value.setData(prop, _Delegate.PropertyRole)
|
||||
|
||||
group.appendRow([label, value])
|
||||
self.model.appendRow(group)
|
||||
|
||||
|
||||
if setup:
|
||||
self.form.attrTree.setModel(self.model)
|
||||
self.form.attrTree.setItemDelegateForColumn(1, self.delegate)
|
||||
self.form.attrTree.expandAll()
|
||||
self.form.attrTree.resizeColumnToContents(0)
|
||||
self.form.attrTree.resizeColumnToContents(1)
|
||||
#self.form.attrTree.collapseAll()
|
||||
# self.form.attrTree.collapseAll()
|
||||
|
||||
def accept(self):
|
||||
PathLog.track()
|
||||
@@ -215,7 +218,7 @@ class ToolBitEditor(object):
|
||||
# editors fires an event and tries to access its old property, which
|
||||
# might not exist anymore.
|
||||
for lbl, qsb, editor in self.widgets:
|
||||
editor.attachTo(self.tool, 'File')
|
||||
editor.attachTo(self.tool, "File")
|
||||
self.tool.BitShape = shapePath
|
||||
self.setupTool(self.tool)
|
||||
self.form.toolName.setText(self.tool.Label)
|
||||
@@ -260,7 +263,9 @@ class ToolBitEditor(object):
|
||||
path = self.tool.BitShape
|
||||
if not path:
|
||||
path = PathPreferences.lastPathToolShape()
|
||||
foo = QtGui.QFileDialog.getOpenFileName(self.form, "Path - Tool Shape", path, "*.fcstd")
|
||||
foo = QtGui.QFileDialog.getOpenFileName(
|
||||
self.form, "Path - Tool Shape", path, "*.fcstd"
|
||||
)
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolShape(os.path.dirname(foo[0]))
|
||||
self.form.shapePath.setText(foo[0])
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathIconViewProvider as PathIconViewProvider
|
||||
@@ -29,26 +31,24 @@ import PathScripts.PathToolBit as PathToolBit
|
||||
import PathScripts.PathToolBitEdit as PathToolBitEdit
|
||||
import os
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Tool Bit UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a ToolBit"
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ViewProvider(object):
|
||||
'''ViewProvider for a ToolBit.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit.'''
|
||||
"""ViewProvider for a ToolBit.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit."""
|
||||
|
||||
def __init__(self, vobj, name):
|
||||
PathLog.track(name, vobj.Object)
|
||||
@@ -67,9 +67,9 @@ class ViewProvider(object):
|
||||
png = self.obj.Proxy.getBitThumbnail(self.obj)
|
||||
if png:
|
||||
pixmap = QtGui.QPixmap()
|
||||
pixmap.loadFromData(png, 'PNG')
|
||||
pixmap.loadFromData(png, "PNG")
|
||||
return QtGui.QIcon(pixmap)
|
||||
return ':/icons/Path_ToolBit.svg'
|
||||
return ":/icons/Path_ToolBit.svg"
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
@@ -84,7 +84,7 @@ class ViewProvider(object):
|
||||
|
||||
def getDisplayMode(self, mode):
|
||||
# pylint: disable=unused-argument
|
||||
return 'Default'
|
||||
return "Default"
|
||||
|
||||
def _openTaskPanel(self, vobj, deleteOnReject):
|
||||
PathLog.track()
|
||||
@@ -117,15 +117,16 @@ class ViewProvider(object):
|
||||
if os.path.exists(vobj.Object.BitShape):
|
||||
self.setEdit(vobj)
|
||||
else:
|
||||
msg = translate('PathToolBit',
|
||||
'Toolbit cannot be edited: Shapefile not found')
|
||||
diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, 'Error', msg)
|
||||
msg = translate(
|
||||
"PathToolBit", "Toolbit cannot be edited: Shapefile not found"
|
||||
)
|
||||
diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Error", msg)
|
||||
diag.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
diag.exec_()
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
'''TaskPanel for the SetupSheet - if it is being edited directly.'''
|
||||
"""TaskPanel for the SetupSheet - if it is being edited directly."""
|
||||
|
||||
def __init__(self, vobj, deleteOnReject):
|
||||
PathLog.track(vobj.Object.Label)
|
||||
@@ -134,15 +135,14 @@ class TaskPanel:
|
||||
self.editor = PathToolBitEdit.ToolBitEditor(self.obj)
|
||||
self.form = self.editor.form
|
||||
self.deleteOnReject = deleteOnReject
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit',
|
||||
'Edit ToolBit'))
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit ToolBit")
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
self.editor.reject()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
if self.deleteOnReject:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Uncreate ToolBit'))
|
||||
FreeCAD.ActiveDocument.openTransaction("Uncreate ToolBit")
|
||||
self.editor.reject()
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
@@ -169,18 +169,18 @@ class TaskPanel:
|
||||
|
||||
|
||||
class ToolBitGuiFactory(PathToolBit.ToolBitFactory):
|
||||
|
||||
def Create(self, name='ToolBit', shapeFile=None, path=None):
|
||||
'''Create(name = 'ToolBit') ... creates a new tool bit.
|
||||
It is assumed the tool will be edited immediately so the internal bit body is still attached.'''
|
||||
def Create(self, name="ToolBit", shapeFile=None, path=None):
|
||||
"""Create(name = 'ToolBit') ... creates a new tool bit.
|
||||
It is assumed the tool will be edited immediately so the internal bit body is still attached."""
|
||||
|
||||
PathLog.track(name, shapeFile, path)
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Create ToolBit'))
|
||||
FreeCAD.ActiveDocument.openTransaction("Create ToolBit")
|
||||
tool = PathToolBit.ToolBitFactory.Create(self, name, shapeFile, path)
|
||||
PathIconViewProvider.Attach(tool.ViewObject, name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
return tool
|
||||
|
||||
|
||||
def isValidFileName(filename):
|
||||
print(filename)
|
||||
try:
|
||||
@@ -194,9 +194,9 @@ def GetNewToolFile(parent=None):
|
||||
if parent is None:
|
||||
parent = QtGui.QApplication.activeWindow()
|
||||
|
||||
foo = QtGui.QFileDialog.getSaveFileName(parent, 'Tool',
|
||||
PathPreferences.lastPathToolBit(),
|
||||
'*.fctb')
|
||||
foo = QtGui.QFileDialog.getSaveFileName(
|
||||
parent, "Tool", PathPreferences.lastPathToolBit(), "*.fctb"
|
||||
)
|
||||
if foo and foo[0]:
|
||||
if not isValidFileName(foo[0]):
|
||||
msgBox = QtGui.QMessageBox()
|
||||
@@ -212,9 +212,9 @@ def GetNewToolFile(parent=None):
|
||||
def GetToolFile(parent=None):
|
||||
if parent is None:
|
||||
parent = QtGui.QApplication.activeWindow()
|
||||
foo = QtGui.QFileDialog.getOpenFileName(parent, 'Tool',
|
||||
PathPreferences.lastPathToolBit(),
|
||||
'*.fctb')
|
||||
foo = QtGui.QFileDialog.getOpenFileName(
|
||||
parent, "Tool", PathPreferences.lastPathToolBit(), "*.fctb"
|
||||
)
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolBit(os.path.dirname(foo[0]))
|
||||
return foo[0]
|
||||
@@ -224,9 +224,9 @@ def GetToolFile(parent=None):
|
||||
def GetToolFiles(parent=None):
|
||||
if parent is None:
|
||||
parent = QtGui.QApplication.activeWindow()
|
||||
foo = QtGui.QFileDialog.getOpenFileNames(parent, 'Tool',
|
||||
PathPreferences.lastPathToolBit(),
|
||||
'*.fctb')
|
||||
foo = QtGui.QFileDialog.getOpenFileNames(
|
||||
parent, "Tool", PathPreferences.lastPathToolBit(), "*.fctb"
|
||||
)
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolBit(os.path.dirname(foo[0][0]))
|
||||
return foo[0]
|
||||
@@ -243,8 +243,9 @@ def GetToolShapeFile(parent=None):
|
||||
elif not os.path.isdir(location):
|
||||
location = PathPreferences.filePath()
|
||||
|
||||
fname = QtGui.QFileDialog.getOpenFileName(parent, 'Select Tool Shape',
|
||||
location, '*.fcstd')
|
||||
fname = QtGui.QFileDialog.getOpenFileName(
|
||||
parent, "Select Tool Shape", location, "*.fcstd"
|
||||
)
|
||||
if fname and fname[0]:
|
||||
if fname != location:
|
||||
newloc = os.path.dirname(fname[0])
|
||||
@@ -255,21 +256,21 @@ def GetToolShapeFile(parent=None):
|
||||
|
||||
|
||||
def LoadTool(parent=None):
|
||||
'''
|
||||
"""
|
||||
LoadTool(parent=None) ... Open a file dialog to load a tool from a file.
|
||||
'''
|
||||
"""
|
||||
foo = GetToolFile(parent)
|
||||
return PathToolBit.Factory.CreateFrom(foo) if foo else foo
|
||||
|
||||
|
||||
def LoadTools(parent=None):
|
||||
'''
|
||||
"""
|
||||
LoadTool(parent=None) ... Open a file dialog to load a tool from a file.
|
||||
'''
|
||||
"""
|
||||
return [PathToolBit.Factory.CreateFrom(foo) for foo in GetToolFiles(parent)]
|
||||
|
||||
|
||||
# Set the factory so all tools are created with UI
|
||||
PathToolBit.Factory = ToolBitGuiFactory()
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider('ToolBit', ViewProvider)
|
||||
PathIconViewProvider.RegisterViewProvider("ToolBit", ViewProvider)
|
||||
|
||||
@@ -20,65 +20,83 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PySide.QtCore as QtCore
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class CommandToolBitSelectorOpen:
|
||||
'''
|
||||
"""
|
||||
Command to toggle the ToolBitSelector Dock
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_ToolTable',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "ToolBit Dock"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Toggle the Toolbit Dock"),
|
||||
'Accel': "P, T",
|
||||
'CmdType': "ForEdit"}
|
||||
return {
|
||||
"Pixmap": "Path_ToolTable",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_ToolBitDock", "ToolBit Dock"),
|
||||
"ToolTip": QT_TRANSLATE_NOOP("Path_ToolBitDock", "Toggle the Toolbit Dock"),
|
||||
"Accel": "P, T",
|
||||
"CmdType": "ForEdit",
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
|
||||
def Activated(self):
|
||||
import PathScripts.PathToolBitLibraryGui as PathToolBitLibraryGui
|
||||
|
||||
dock = PathToolBitLibraryGui.ToolBitSelector()
|
||||
dock.open()
|
||||
|
||||
|
||||
class CommandToolBitLibraryOpen:
|
||||
'''
|
||||
"""
|
||||
Command to open ToolBitLibrary editor.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_ToolTable',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "ToolBit Library editor"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Open an editor to manage ToolBit libraries"),
|
||||
'CmdType': "ForEdit"}
|
||||
return {
|
||||
"Pixmap": "Path_ToolTable",
|
||||
"MenuText": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolBitLibraryOpen", "ToolBit Library editor"
|
||||
),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolBitLibraryOpen", "Open an editor to manage ToolBit libraries"
|
||||
),
|
||||
"CmdType": "ForEdit",
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
|
||||
def Activated(self):
|
||||
import PathScripts.PathToolBitLibraryGui as PathToolBitLibraryGui
|
||||
|
||||
library = PathToolBitLibraryGui.ToolBitLibrary()
|
||||
|
||||
library.open()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_ToolBitLibraryOpen', CommandToolBitLibraryOpen())
|
||||
FreeCADGui.addCommand('Path_ToolBitDock', CommandToolBitSelectorOpen())
|
||||
FreeCADGui.addCommand("Path_ToolBitLibraryOpen", CommandToolBitLibraryOpen())
|
||||
FreeCADGui.addCommand("Path_ToolBitDock", CommandToolBitSelectorOpen())
|
||||
|
||||
BarList = ['Path_ToolBitDock']
|
||||
MenuList = ['Path_ToolBitLibraryOpen', 'Path_ToolBitDock']
|
||||
BarList = ["Path_ToolBitDock"]
|
||||
MenuList = ["Path_ToolBitLibraryOpen", "Path_ToolBitDock"]
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathToolBitLibraryCmd... done\n")
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
@@ -42,15 +42,19 @@ import uuid as UUID
|
||||
from functools import partial
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
_UuidRole = PySide.QtCore.Qt.UserRole + 1
|
||||
_PathRole = PySide.QtCore.Qt.UserRole + 2
|
||||
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def checkWorkingDir():
|
||||
# users shouldn't use the example toolbits and libraries.
|
||||
@@ -60,30 +64,37 @@ def checkWorkingDir():
|
||||
workingdir = os.path.dirname(PathPreferences.lastPathToolLibrary())
|
||||
defaultdir = os.path.dirname(PathPreferences.pathDefaultToolsPath())
|
||||
|
||||
PathLog.debug('workingdir: {} defaultdir: {}'.format(workingdir, defaultdir))
|
||||
PathLog.debug("workingdir: {} defaultdir: {}".format(workingdir, defaultdir))
|
||||
|
||||
dirOK = lambda : workingdir != defaultdir and (os.access(workingdir, os.W_OK))
|
||||
dirOK = lambda: workingdir != defaultdir and (os.access(workingdir, os.W_OK))
|
||||
|
||||
if dirOK():
|
||||
return True
|
||||
|
||||
qm = PySide.QtGui.QMessageBox
|
||||
ret = qm.question(None,'', "Toolbit working directory not set up. Do that now?", qm.Yes | qm.No)
|
||||
ret = qm.question(
|
||||
None, "", "Toolbit working directory not set up. Do that now?", qm.Yes | qm.No
|
||||
)
|
||||
|
||||
if ret == qm.No:
|
||||
return False
|
||||
|
||||
msg = translate("Path", "Choose a writable location for your toolbits", None)
|
||||
msg = translate(
|
||||
"Path_ToolBit", "Choose a writable location for your toolbits", None
|
||||
)
|
||||
while not dirOK():
|
||||
workingdir = PySide.QtGui.QFileDialog.getExistingDirectory(None, msg,
|
||||
PathPreferences.filePath())
|
||||
workingdir = PySide.QtGui.QFileDialog.getExistingDirectory(
|
||||
None, msg, PathPreferences.filePath()
|
||||
)
|
||||
|
||||
if workingdir[-8:] == os.path.sep + 'Library':
|
||||
if workingdir[-8:] == os.path.sep + "Library":
|
||||
workingdir = workingdir[:-8] # trim off trailing /Library if user chose it
|
||||
|
||||
PathPreferences.setLastPathToolLibrary("{}{}Library".format(workingdir, os.path.sep))
|
||||
PathPreferences.setLastPathToolLibrary(
|
||||
"{}{}Library".format(workingdir, os.path.sep)
|
||||
)
|
||||
PathPreferences.setLastPathToolBit("{}{}Bit".format(workingdir, os.path.sep))
|
||||
PathLog.debug('setting workingdir to: {}'.format(workingdir))
|
||||
PathLog.debug("setting workingdir to: {}".format(workingdir))
|
||||
|
||||
# Copy only files of default Path\Tools folder to working directory (targeting the README.md help file)
|
||||
src_toolfiles = os.listdir(defaultdir)
|
||||
@@ -94,7 +105,7 @@ def checkWorkingDir():
|
||||
shutil.copy(full_file_name, workingdir)
|
||||
|
||||
# Determine which subdirectories are missing
|
||||
subdirlist = ['Bit', 'Library', 'Shape']
|
||||
subdirlist = ["Bit", "Library", "Shape"]
|
||||
mode = 0o777
|
||||
for dir in subdirlist.copy():
|
||||
subdir = "{}{}{}".format(workingdir, os.path.sep, dir)
|
||||
@@ -103,9 +114,16 @@ def checkWorkingDir():
|
||||
|
||||
# Query user for creation permission of any missing subdirectories
|
||||
if len(subdirlist) >= 1:
|
||||
needed = ', '.join([str(d) for d in subdirlist])
|
||||
needed = ", ".join([str(d) for d in subdirlist])
|
||||
qm = PySide.QtGui.QMessageBox
|
||||
ret = qm.question(None,'', "Toolbit Working directory {} needs these sudirectories:\n {} \n Create them?".format(workingdir, needed), qm.Yes | qm.No)
|
||||
ret = qm.question(
|
||||
None,
|
||||
"",
|
||||
"Toolbit Working directory {} needs these sudirectories:\n {} \n Create them?".format(
|
||||
workingdir, needed
|
||||
),
|
||||
qm.Yes | qm.No,
|
||||
)
|
||||
|
||||
if ret == qm.No:
|
||||
return False
|
||||
@@ -115,27 +133,37 @@ def checkWorkingDir():
|
||||
subdir = "{}{}{}".format(workingdir, os.path.sep, dir)
|
||||
os.mkdir(subdir, mode)
|
||||
# Query user to copy example files into subdirectories created
|
||||
if dir != 'Shape':
|
||||
if dir != "Shape":
|
||||
qm = PySide.QtGui.QMessageBox
|
||||
ret = qm.question(None,'', "Copy example files to new {} directory?".format(dir), qm.Yes | qm.No)
|
||||
ret = qm.question(
|
||||
None,
|
||||
"",
|
||||
"Copy example files to new {} directory?".format(dir),
|
||||
qm.Yes | qm.No,
|
||||
)
|
||||
if ret == qm.Yes:
|
||||
src="{}{}{}".format(defaultdir, os.path.sep, dir)
|
||||
src = "{}{}{}".format(defaultdir, os.path.sep, dir)
|
||||
src_files = os.listdir(src)
|
||||
for file_name in src_files:
|
||||
full_file_name = os.path.join(src, file_name)
|
||||
if os.path.isfile(full_file_name):
|
||||
shutil.copy(full_file_name, subdir)
|
||||
|
||||
|
||||
# if no library is set, choose the first one in the Library directory
|
||||
if PathPreferences.lastFileToolLibrary() is None:
|
||||
libFiles = [f for f in glob.glob(PathPreferences.lastPathToolLibrary() + os.path.sep + '*.fctl')]
|
||||
libFiles = [
|
||||
f
|
||||
for f in glob.glob(
|
||||
PathPreferences.lastPathToolLibrary() + os.path.sep + "*.fctl"
|
||||
)
|
||||
]
|
||||
PathPreferences.setLastFileToolLibrary(libFiles[0])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class _TableView(PySide.QtGui.QTableView):
|
||||
'''Subclass of QTableView to support rearrange and copying of ToolBits'''
|
||||
"""Subclass of QTableView to support rearrange and copying of ToolBits"""
|
||||
|
||||
def __init__(self, parent):
|
||||
PySide.QtGui.QTableView.__init__(self, parent)
|
||||
@@ -169,9 +197,15 @@ class _TableView(PySide.QtGui.QTableView):
|
||||
for col in range(model.columnCount()):
|
||||
srcItem = model.item(srcRow, col)
|
||||
|
||||
model.setData(model.index(dstRow, col), srcItem.data(PySide.QtCore.Qt.EditRole), PySide.QtCore.Qt.EditRole)
|
||||
model.setData(
|
||||
model.index(dstRow, col),
|
||||
srcItem.data(PySide.QtCore.Qt.EditRole),
|
||||
PySide.QtCore.Qt.EditRole,
|
||||
)
|
||||
if col == 0:
|
||||
model.setData(model.index(dstRow, col), srcItem.data(_PathRole), _PathRole)
|
||||
model.setData(
|
||||
model.index(dstRow, col), srcItem.data(_PathRole), _PathRole
|
||||
)
|
||||
# Even a clone of a tool gets its own uuid so it can be identified when
|
||||
# rearranging the order or inserting/deleting rows
|
||||
model.setData(model.index(dstRow, col), UUID.uuid4(), _UuidRole)
|
||||
@@ -185,7 +219,7 @@ class _TableView(PySide.QtGui.QTableView):
|
||||
def dropEvent(self, event):
|
||||
PathLog.track()
|
||||
mime = event.mimeData()
|
||||
data = mime.data('application/x-qstandarditemmodeldatalist')
|
||||
data = mime.data("application/x-qstandarditemmodeldatalist")
|
||||
stream = PySide.QtCore.QDataStream(data)
|
||||
srcRows = []
|
||||
while not stream.atEnd():
|
||||
@@ -205,8 +239,7 @@ class _TableView(PySide.QtGui.QTableView):
|
||||
|
||||
|
||||
class ModelFactory(object):
|
||||
''' Helper class to generate qtdata models for toolbit libraries
|
||||
'''
|
||||
"""Helper class to generate qtdata models for toolbit libraries"""
|
||||
|
||||
def __init__(self, path=None):
|
||||
PathLog.track()
|
||||
@@ -221,35 +254,37 @@ class ModelFactory(object):
|
||||
with open(path) as fp:
|
||||
library = json.load(fp)
|
||||
|
||||
for toolBit in library['tools']:
|
||||
for toolBit in library["tools"]:
|
||||
try:
|
||||
nr = toolBit['nr']
|
||||
bit = PathToolBit.findToolBit(toolBit['path'], path)
|
||||
nr = toolBit["nr"]
|
||||
bit = PathToolBit.findToolBit(toolBit["path"], path)
|
||||
if bit:
|
||||
PathLog.track(bit)
|
||||
tool = PathToolBit.Declaration(bit)
|
||||
datamodel.appendRow(self._toolAdd(nr, tool, bit))
|
||||
else:
|
||||
PathLog.error("Could not find tool #{}: {}".format(nr, toolBit['path']))
|
||||
PathLog.error(
|
||||
"Could not find tool #{}: {}".format(nr, toolBit["path"])
|
||||
)
|
||||
except Exception as e:
|
||||
msg = "Error loading tool: {} : {}".format(toolBit['path'], e)
|
||||
msg = "Error loading tool: {} : {}".format(toolBit["path"], e)
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
|
||||
def _toolAdd(self, nr, tool, path):
|
||||
|
||||
strShape = os.path.splitext(os.path.basename(tool['shape']))[0]
|
||||
strShape = os.path.splitext(os.path.basename(tool["shape"]))[0]
|
||||
# strDiam = tool['parameter']['Diameter']
|
||||
tooltip = "{}".format(strShape)
|
||||
|
||||
toolNr = PySide.QtGui.QStandardItem()
|
||||
toolNr.setData(nr, PySide.QtCore.Qt.EditRole)
|
||||
toolNr.setToolTip(tool['shape'])
|
||||
toolNr.setToolTip(tool["shape"])
|
||||
toolNr.setData(path, _PathRole)
|
||||
toolNr.setData(UUID.uuid4(), _UuidRole)
|
||||
toolNr.setToolTip(tooltip)
|
||||
|
||||
toolName = PySide.QtGui.QStandardItem()
|
||||
toolName.setData(tool['name'], PySide.QtCore.Qt.EditRole)
|
||||
toolName.setData(tool["name"], PySide.QtCore.Qt.EditRole)
|
||||
toolName.setEditable(False)
|
||||
toolName.setToolTip(tooltip)
|
||||
|
||||
@@ -260,9 +295,9 @@ class ModelFactory(object):
|
||||
return [toolNr, toolName, toolShape]
|
||||
|
||||
def newTool(self, datamodel, path):
|
||||
'''
|
||||
"""
|
||||
Adds a toolbit item to a model
|
||||
'''
|
||||
"""
|
||||
PathLog.track()
|
||||
|
||||
try:
|
||||
@@ -278,15 +313,15 @@ class ModelFactory(object):
|
||||
datamodel.appendRow(self._toolAdd(nr, tool, path))
|
||||
|
||||
def findLibraries(self, model):
|
||||
'''
|
||||
"""
|
||||
Finds all the fctl files in a location
|
||||
Returns a QStandardItemModel
|
||||
'''
|
||||
"""
|
||||
PathLog.track()
|
||||
path = PathPreferences.lastPathToolLibrary()
|
||||
|
||||
if os.path.isdir(path): # opening all tables in a directory
|
||||
libFiles = [f for f in glob.glob(path + os.path.sep + '*.fctl')]
|
||||
libFiles = [f for f in glob.glob(path + os.path.sep + "*.fctl")]
|
||||
libFiles.sort()
|
||||
for libFile in libFiles:
|
||||
loc, fnlong = os.path.split(libFile)
|
||||
@@ -294,17 +329,17 @@ class ModelFactory(object):
|
||||
libItem = PySide.QtGui.QStandardItem(fn)
|
||||
libItem.setToolTip(loc)
|
||||
libItem.setData(libFile, _PathRole)
|
||||
libItem.setIcon(PySide.QtGui.QPixmap(':/icons/Path_ToolTable.svg'))
|
||||
libItem.setIcon(PySide.QtGui.QPixmap(":/icons/Path_ToolTable.svg"))
|
||||
model.appendRow(libItem)
|
||||
|
||||
PathLog.debug('model rows: {}'.format(model.rowCount()))
|
||||
PathLog.debug("model rows: {}".format(model.rowCount()))
|
||||
return model
|
||||
|
||||
def libraryOpen(self, model, lib=""):
|
||||
'''
|
||||
"""
|
||||
opens the tools in library
|
||||
Returns a QStandardItemModel
|
||||
'''
|
||||
"""
|
||||
PathLog.track(lib)
|
||||
|
||||
if lib == "":
|
||||
@@ -316,23 +351,23 @@ class ModelFactory(object):
|
||||
if os.path.isfile(lib): # An individual library is wanted
|
||||
self.__libraryLoad(lib, model)
|
||||
|
||||
PathLog.debug('model rows: {}'.format(model.rowCount()))
|
||||
PathLog.debug("model rows: {}".format(model.rowCount()))
|
||||
return model
|
||||
|
||||
|
||||
class ToolBitSelector(object):
|
||||
'''Controller for displaying a library and creating ToolControllers'''
|
||||
"""Controller for displaying a library and creating ToolControllers"""
|
||||
|
||||
def __init__(self):
|
||||
checkWorkingDir()
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':/panels/ToolBitSelector.ui')
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitSelector.ui")
|
||||
self.factory = ModelFactory()
|
||||
self.toolModel = PySide.QtGui.QStandardItemModel(0, len(self.columnNames()))
|
||||
self.setupUI()
|
||||
self.title = self.form.windowTitle()
|
||||
|
||||
def columnNames(self):
|
||||
return ['#', 'Tool']
|
||||
return ["#", "Tool"]
|
||||
|
||||
def currentLibrary(self, shortNameOnly):
|
||||
libfile = PathPreferences.lastFileToolLibrary()
|
||||
@@ -357,14 +392,19 @@ class ToolBitSelector(object):
|
||||
self.loadData()
|
||||
self.form.tools.setModel(self.toolModel)
|
||||
self.form.tools.selectionModel().selectionChanged.connect(self.enableButtons)
|
||||
self.form.tools.doubleClicked.connect(partial(self.selectedOrAllToolControllers))
|
||||
self.form.tools.doubleClicked.connect(
|
||||
partial(self.selectedOrAllToolControllers)
|
||||
)
|
||||
self.form.libraryEditorOpen.clicked.connect(self.libraryEditorOpen)
|
||||
self.form.addToolController.clicked.connect(self.selectedOrAllToolControllers)
|
||||
|
||||
def enableButtons(self):
|
||||
selected = (len(self.form.tools.selectedIndexes()) >= 1)
|
||||
selected = len(self.form.tools.selectedIndexes()) >= 1
|
||||
if selected:
|
||||
jobs = len([1 for j in FreeCAD.ActiveDocument.Objects if j.Name[:3] == "Job"]) >= 1
|
||||
jobs = (
|
||||
len([1 for j in FreeCAD.ActiveDocument.Objects if j.Name[:3] == "Job"])
|
||||
>= 1
|
||||
)
|
||||
self.form.addToolController.setEnabled(selected and jobs)
|
||||
|
||||
def libraryEditorOpen(self):
|
||||
@@ -373,17 +413,17 @@ class ToolBitSelector(object):
|
||||
self.loadData()
|
||||
|
||||
def selectedOrAllTools(self):
|
||||
'''
|
||||
"""
|
||||
Iterate the selection and add individual tools
|
||||
If a group is selected, iterate and add children
|
||||
'''
|
||||
"""
|
||||
|
||||
itemsToProcess = []
|
||||
for index in self.form.tools.selectedIndexes():
|
||||
item = index.model().itemFromIndex(index)
|
||||
|
||||
if item.hasChildren():
|
||||
for i in range(item.rowCount()-1):
|
||||
for i in range(item.rowCount() - 1):
|
||||
if item.child(i).column() == 0:
|
||||
itemsToProcess.append(item.child(i))
|
||||
|
||||
@@ -398,10 +438,10 @@ class ToolBitSelector(object):
|
||||
return tools
|
||||
|
||||
def selectedOrAllToolControllers(self, index=None):
|
||||
'''
|
||||
"""
|
||||
if no jobs, don't do anything, otherwise all TCs for all
|
||||
selected toolbits
|
||||
'''
|
||||
"""
|
||||
jobs = PathUtilsGui.PathUtils.GetJobs()
|
||||
if len(jobs) == 0:
|
||||
return
|
||||
@@ -417,12 +457,14 @@ class ToolBitSelector(object):
|
||||
tools = self.selectedOrAllTools()
|
||||
|
||||
for tool in tools:
|
||||
tc = PathToolControllerGui.Create("TC: {}".format(tool[1].Label), tool[1], tool[0])
|
||||
tc = PathToolControllerGui.Create(
|
||||
"TC: {}".format(tool[1].Label), tool[1], tool[0]
|
||||
)
|
||||
job.Proxy.addToolController(tc)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def open(self, path=None):
|
||||
''' load library stored in path and bring up ui'''
|
||||
"""load library stored in path and bring up ui"""
|
||||
docs = FreeCADGui.getMainWindow().findChildren(PySide.QtGui.QDockWidget)
|
||||
for doc in docs:
|
||||
if doc.objectName() == "ToolSelector":
|
||||
@@ -434,13 +476,16 @@ class ToolBitSelector(object):
|
||||
return
|
||||
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
mw.addDockWidget(PySide.QtCore.Qt.RightDockWidgetArea, self.form,
|
||||
PySide.QtCore.Qt.Orientation.Vertical)
|
||||
mw.addDockWidget(
|
||||
PySide.QtCore.Qt.RightDockWidgetArea,
|
||||
self.form,
|
||||
PySide.QtCore.Qt.Orientation.Vertical,
|
||||
)
|
||||
|
||||
|
||||
class ToolBitLibrary(object):
|
||||
'''ToolBitLibrary is the controller for
|
||||
displaying/selecting/creating/editing a collection of ToolBits.'''
|
||||
"""ToolBitLibrary is the controller for
|
||||
displaying/selecting/creating/editing a collection of ToolBits."""
|
||||
|
||||
def __init__(self):
|
||||
PathLog.track()
|
||||
@@ -449,9 +494,11 @@ class ToolBitLibrary(object):
|
||||
self.temptool = None
|
||||
self.toolModel = PySide.QtGui.QStandardItemModel(0, len(self.columnNames()))
|
||||
self.listModel = PySide.QtGui.QStandardItemModel()
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':/panels/ToolBitLibraryEdit.ui')
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitLibraryEdit.ui")
|
||||
self.toolTableView = _TableView(self.form.toolTableGroup)
|
||||
self.form.toolTableGroup.layout().replaceWidget(self.form.toolTable, self.toolTableView)
|
||||
self.form.toolTableGroup.layout().replaceWidget(
|
||||
self.form.toolTable, self.toolTableView
|
||||
)
|
||||
self.form.toolTable.hide()
|
||||
self.setupUI()
|
||||
self.title = self.form.windowTitle()
|
||||
@@ -503,7 +550,9 @@ class ToolBitLibrary(object):
|
||||
|
||||
def toolDelete(self):
|
||||
PathLog.track()
|
||||
selectedRows = set([index.row() for index in self.toolTableView.selectedIndexes()])
|
||||
selectedRows = set(
|
||||
[index.row() for index in self.toolTableView.selectedIndexes()]
|
||||
)
|
||||
for row in sorted(list(selectedRows), key=lambda r: -r):
|
||||
self.toolModel.removeRows(row, 1)
|
||||
|
||||
@@ -512,7 +561,7 @@ class ToolBitLibrary(object):
|
||||
self.form.toolDelete.setEnabled(sel)
|
||||
|
||||
def tableSelected(self, index):
|
||||
''' loads the tools for the selected tool table '''
|
||||
"""loads the tools for the selected tool table"""
|
||||
PathLog.track()
|
||||
item = index.model().itemFromIndex(index)
|
||||
libpath = item.data(_PathRole)
|
||||
@@ -525,7 +574,9 @@ class ToolBitLibrary(object):
|
||||
|
||||
def libraryPath(self):
|
||||
PathLog.track()
|
||||
path = PySide.QtGui.QFileDialog.getExistingDirectory(self.form, 'Tool Library Path', PathPreferences.lastPathToolLibrary())
|
||||
path = PySide.QtGui.QFileDialog.getExistingDirectory(
|
||||
self.form, "Tool Library Path", PathPreferences.lastPathToolLibrary()
|
||||
)
|
||||
if len(path) == 0:
|
||||
return
|
||||
|
||||
@@ -588,10 +639,14 @@ class ToolBitLibrary(object):
|
||||
pass
|
||||
else:
|
||||
tbpath = item.data(_PathRole)
|
||||
self.temptool = PathToolBit.ToolBitFactory().CreateFrom(tbpath, 'temptool')
|
||||
self.editor = PathToolBitEdit.ToolBitEditor(self.temptool, self.form.toolTableGroup, loadBitBody=False)
|
||||
self.temptool = PathToolBit.ToolBitFactory().CreateFrom(tbpath, "temptool")
|
||||
self.editor = PathToolBitEdit.ToolBitEditor(
|
||||
self.temptool, self.form.toolTableGroup, loadBitBody=False
|
||||
)
|
||||
|
||||
QBtn = PySide.QtGui.QDialogButtonBox.Ok | PySide.QtGui.QDialogButtonBox.Cancel
|
||||
QBtn = (
|
||||
PySide.QtGui.QDialogButtonBox.Ok | PySide.QtGui.QDialogButtonBox.Cancel
|
||||
)
|
||||
buttonBox = PySide.QtGui.QDialogButtonBox(QBtn)
|
||||
buttonBox.accepted.connect(self.accept)
|
||||
buttonBox.rejected.connect(self.reject)
|
||||
@@ -603,24 +658,31 @@ class ToolBitLibrary(object):
|
||||
|
||||
def toolEditDone(self, success=True):
|
||||
FreeCAD.ActiveDocument.removeObject("temptool")
|
||||
print('all done')
|
||||
print("all done")
|
||||
|
||||
def libraryNew(self):
|
||||
TooltableTypeJSON = translate("PathToolLibraryManager", "Tooltable JSON (*.fctl)")
|
||||
TooltableTypeJSON = translate("Path_ToolBit", "Tooltable JSON (*.fctl)")
|
||||
|
||||
filename = PySide.QtGui.QFileDialog.getSaveFileName(self.form,
|
||||
translate("TooltableEditor", "Save toolbit library", None),
|
||||
PathPreferences.lastPathToolLibrary(), "{}".format(TooltableTypeJSON))
|
||||
filename = PySide.QtGui.QFileDialog.getSaveFileName(
|
||||
self.form,
|
||||
translate("Path_ToolBit", "Save toolbit library", None),
|
||||
PathPreferences.lastPathToolLibrary(),
|
||||
"{}".format(TooltableTypeJSON),
|
||||
)
|
||||
|
||||
if not (filename and filename[0]):
|
||||
self.loadData()
|
||||
|
||||
path = filename[0] if filename[0].endswith('.fctl') else "{}.fctl".format(filename[0])
|
||||
path = (
|
||||
filename[0]
|
||||
if filename[0].endswith(".fctl")
|
||||
else "{}.fctl".format(filename[0])
|
||||
)
|
||||
library = {}
|
||||
tools = []
|
||||
library['version'] = 1
|
||||
library['tools'] = tools
|
||||
with open(path, 'w') as fp:
|
||||
library["version"] = 1
|
||||
library["tools"] = tools
|
||||
with open(path, "w") as fp:
|
||||
json.dump(library, fp, sort_keys=True, indent=2)
|
||||
|
||||
self.loadData()
|
||||
@@ -628,22 +690,26 @@ class ToolBitLibrary(object):
|
||||
def librarySave(self):
|
||||
library = {}
|
||||
tools = []
|
||||
library['version'] = 1
|
||||
library['tools'] = tools
|
||||
library["version"] = 1
|
||||
library["tools"] = tools
|
||||
for row in range(self.toolModel.rowCount()):
|
||||
toolNr = self.toolModel.data(self.toolModel.index(row, 0), PySide.QtCore.Qt.EditRole)
|
||||
toolNr = self.toolModel.data(
|
||||
self.toolModel.index(row, 0), PySide.QtCore.Qt.EditRole
|
||||
)
|
||||
toolPath = self.toolModel.data(self.toolModel.index(row, 0), _PathRole)
|
||||
if PathPreferences.toolsStoreAbsolutePaths():
|
||||
bitPath = toolPath
|
||||
else:
|
||||
# bitPath = PathToolBit.findRelativePathTool(toolPath)
|
||||
# Extract the name of the shape file
|
||||
__, filShp = os.path.split(toolPath) # __ is an ignored placeholder acknowledged by LGTM
|
||||
__, filShp = os.path.split(
|
||||
toolPath
|
||||
) # __ is an ignored placeholder acknowledged by LGTM
|
||||
bitPath = str(filShp)
|
||||
tools.append({'nr': toolNr, 'path': bitPath})
|
||||
tools.append({"nr": toolNr, "path": bitPath})
|
||||
|
||||
if self.path is not None:
|
||||
with open(self.path, 'w') as fp:
|
||||
with open(self.path, "w") as fp:
|
||||
json.dump(library, fp, sort_keys=True, indent=2)
|
||||
|
||||
def libraryOk(self):
|
||||
@@ -658,7 +724,7 @@ class ToolBitLibrary(object):
|
||||
return lib, loc
|
||||
|
||||
def columnNames(self):
|
||||
return ['Nr', 'Tool', 'Shape']
|
||||
return ["Nr", "Tool", "Shape"]
|
||||
|
||||
def loadData(self, path=None):
|
||||
PathLog.track(path)
|
||||
@@ -680,7 +746,7 @@ class ToolBitLibrary(object):
|
||||
self.path = path
|
||||
self.form.setWindowTitle("{}".format(PathPreferences.lastPathToolLibrary()))
|
||||
self.toolModel.setHorizontalHeaderLabels(self.columnNames())
|
||||
self.listModel.setHorizontalHeaderLabels(['Library'])
|
||||
self.listModel.setHorizontalHeaderLabels(["Library"])
|
||||
|
||||
# Select the current library in the list of tables
|
||||
curIndex = None
|
||||
@@ -723,19 +789,29 @@ class ToolBitLibrary(object):
|
||||
|
||||
def librarySaveAs(self, path):
|
||||
|
||||
TooltableTypeJSON = translate("PathToolLibraryManager", "Tooltable JSON (*.fctl)")
|
||||
TooltableTypeLinuxCNC = translate("PathToolLibraryManager", "LinuxCNC tooltable (*.tbl)")
|
||||
TooltableTypeJSON = translate("Path_ToolBit", "Tooltable JSON (*.fctl)")
|
||||
TooltableTypeLinuxCNC = translate("Path_ToolBit", "LinuxCNC tooltable (*.tbl)")
|
||||
|
||||
filename = PySide.QtGui.QFileDialog.getSaveFileName(self.form,
|
||||
translate("TooltableEditor", "Save toolbit library", None),
|
||||
PathPreferences.lastPathToolLibrary(), "{};;{}".format(TooltableTypeJSON,
|
||||
TooltableTypeLinuxCNC))
|
||||
filename = PySide.QtGui.QFileDialog.getSaveFileName(
|
||||
self.form,
|
||||
translate("Path_ToolBit", "Save toolbit library", None),
|
||||
PathPreferences.lastPathToolLibrary(),
|
||||
"{};;{}".format(TooltableTypeJSON, TooltableTypeLinuxCNC),
|
||||
)
|
||||
if filename and filename[0]:
|
||||
if filename[1] == TooltableTypeLinuxCNC:
|
||||
path = filename[0] if filename[0].endswith('.tbl') else "{}.tbl".format(filename[0])
|
||||
path = (
|
||||
filename[0]
|
||||
if filename[0].endswith(".tbl")
|
||||
else "{}.tbl".format(filename[0])
|
||||
)
|
||||
self.libararySaveLinuxCNC(path)
|
||||
else:
|
||||
path = filename[0] if filename[0].endswith('.fctl') else "{}.fctl".format(filename[0])
|
||||
path = (
|
||||
filename[0]
|
||||
if filename[0].endswith(".fctl")
|
||||
else "{}.fctl".format(filename[0])
|
||||
)
|
||||
self.path = path
|
||||
self.librarySave()
|
||||
self.updateToolbar()
|
||||
@@ -743,11 +819,13 @@ class ToolBitLibrary(object):
|
||||
def libararySaveLinuxCNC(self, path):
|
||||
# linuxcnc line template
|
||||
LIN = "T{} P{} X{} Y{} Z{} A{} B{} C{} U{} V{} W{} D{} I{} J{} Q{}; {}"
|
||||
with open(path, 'w') as fp:
|
||||
with open(path, "w") as fp:
|
||||
fp.write(";\n")
|
||||
|
||||
for row in range(self.toolModel.rowCount()):
|
||||
toolNr = self.toolModel.data(self.toolModel.index(row, 0), PySide.QtCore.Qt.EditRole)
|
||||
toolNr = self.toolModel.data(
|
||||
self.toolModel.index(row, 0), PySide.QtCore.Qt.EditRole
|
||||
)
|
||||
toolPath = self.toolModel.data(self.toolModel.index(row, 0), _PathRole)
|
||||
|
||||
bit = PathToolBit.Factory.CreateFrom(toolPath)
|
||||
@@ -765,16 +843,39 @@ class ToolBitLibrary(object):
|
||||
voffset = bit.Voffset if hasattr(bit, "Voffset") else "0"
|
||||
woffset = bit.Woffset if hasattr(bit, "Woffset") else "0"
|
||||
|
||||
diameter = bit.Diameter.getUserPreferred()[0].split()[0] if hasattr(bit, "Diameter") else "0"
|
||||
diameter = (
|
||||
bit.Diameter.getUserPreferred()[0].split()[0]
|
||||
if hasattr(bit, "Diameter")
|
||||
else "0"
|
||||
)
|
||||
frontangle = bit.FrontAngle if hasattr(bit, "FrontAngle") else "0"
|
||||
backangle = bit.BackAngle if hasattr(bit, "BackAngle") else "0"
|
||||
orientation = bit.Orientation if hasattr(bit, "Orientation") else "0"
|
||||
orientation = (
|
||||
bit.Orientation if hasattr(bit, "Orientation") else "0"
|
||||
)
|
||||
remark = bit.Label
|
||||
|
||||
fp.write(LIN.format(toolNr, pocket, xoffset, yoffset,
|
||||
zoffset, aoffset, boffset, coffset, uoffset,
|
||||
voffset, woffset, diameter, frontangle, backangle,
|
||||
orientation, remark) + "\n")
|
||||
fp.write(
|
||||
LIN.format(
|
||||
toolNr,
|
||||
pocket,
|
||||
xoffset,
|
||||
yoffset,
|
||||
zoffset,
|
||||
aoffset,
|
||||
boffset,
|
||||
coffset,
|
||||
uoffset,
|
||||
voffset,
|
||||
woffset,
|
||||
diameter,
|
||||
frontangle,
|
||||
backangle,
|
||||
orientation,
|
||||
remark,
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
FreeCAD.ActiveDocument.removeObject(bit.Name)
|
||||
|
||||
|
||||
@@ -20,78 +20,149 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
'''Tool Controller defines tool, spindle speed and feed rates for Path Operations'''
|
||||
"""Tool Controller defines tool, spindle speed and feed rates for Path Operations"""
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
# PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ToolControllerTemplate:
|
||||
'''Attribute and sub element strings for template export/import.'''
|
||||
"""Attribute and sub element strings for template export/import."""
|
||||
|
||||
Expressions = 'xengine'
|
||||
ExprExpr = 'expr'
|
||||
ExprProp = 'prop'
|
||||
HorizFeed = 'hfeed'
|
||||
HorizRapid = 'hrapid'
|
||||
Label = 'label'
|
||||
Name = 'name'
|
||||
SpindleDir = 'dir'
|
||||
SpindleSpeed = 'speed'
|
||||
ToolNumber = 'nr'
|
||||
Tool = 'tool'
|
||||
Version = 'version'
|
||||
VertFeed = 'vfeed'
|
||||
VertRapid = 'vrapid'
|
||||
Expressions = "xengine"
|
||||
ExprExpr = "expr"
|
||||
ExprProp = "prop"
|
||||
HorizFeed = "hfeed"
|
||||
HorizRapid = "hrapid"
|
||||
Label = "label"
|
||||
Name = "name"
|
||||
SpindleDir = "dir"
|
||||
SpindleSpeed = "speed"
|
||||
ToolNumber = "nr"
|
||||
Tool = "tool"
|
||||
Version = "version"
|
||||
VertFeed = "vfeed"
|
||||
VertRapid = "vrapid"
|
||||
|
||||
|
||||
class ToolController:
|
||||
|
||||
def __init__(self, obj, legacyTool=False, createTool=True):
|
||||
PathLog.track('tool: {}'.format(legacyTool))
|
||||
PathLog.track("tool: {}".format(legacyTool))
|
||||
|
||||
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The active tool"))
|
||||
obj.addProperty(
|
||||
"App::PropertyIntegerConstraint",
|
||||
"ToolNumber",
|
||||
"Tool",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The active tool"),
|
||||
)
|
||||
obj.ToolNumber = (0, 0, 10000, 1)
|
||||
obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The speed of the cutting spindle in RPM"))
|
||||
obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Direction of spindle rotation"))
|
||||
obj.SpindleDir = ['Forward', 'Reverse']
|
||||
obj.addProperty("App::PropertySpeed", "VertFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Feed rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Feed rate for horizontal moves"))
|
||||
obj.addProperty("App::PropertySpeed", "VertRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Rapid rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Rapid rate for horizontal moves"))
|
||||
obj.setEditorMode('Placement', 2)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"SpindleSpeed",
|
||||
"Tool",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The speed of the cutting spindle in RPM"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"SpindleDir",
|
||||
"Tool",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Direction of spindle rotation"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"VertFeed",
|
||||
"Feed",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Feed rate for vertical moves in Z"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"HorizFeed",
|
||||
"Feed",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Feed rate for horizontal moves"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"VertRapid",
|
||||
"Rapid",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Rapid rate for vertical moves in Z"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"HorizRapid",
|
||||
"Rapid",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Rapid rate for horizontal moves"),
|
||||
)
|
||||
obj.setEditorMode("Placement", 2)
|
||||
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
if createTool:
|
||||
self.ensureUseLegacyTool(obj, legacyTool)
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
enums = {
|
||||
"SpindleDir": [
|
||||
(translate("Path_ToolController", "Forward"), "Forward"),
|
||||
(translate("Path_ToolController", "Reverse"), "Reverse"),
|
||||
], # this is the direction that the profile runs
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
obj.setEditorMode('Placement', 2)
|
||||
obj.setEditorMode("Placement", 2)
|
||||
|
||||
def onDelete(self, obj, arg2=None):
|
||||
# pylint: disable=unused-argument
|
||||
if not self.usesLegacyTool(obj):
|
||||
if hasattr(obj.Tool, 'InList') and len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, 'onDelete'):
|
||||
if hasattr(obj.Tool, "InList") and len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, "onDelete"):
|
||||
obj.Tool.Proxy.onDelete(obj.Tool)
|
||||
|
||||
def setFromTemplate(self, obj, template):
|
||||
'''
|
||||
"""
|
||||
setFromTemplate(obj, xmlItem) ... extract properties from xmlItem
|
||||
and assign to receiver.
|
||||
'''
|
||||
"""
|
||||
PathLog.track(obj.Name, template)
|
||||
version = 0
|
||||
if template.get(ToolControllerTemplate.Version):
|
||||
@@ -108,51 +179,79 @@ class ToolController:
|
||||
if template.get(ToolControllerTemplate.HorizRapid):
|
||||
obj.HorizRapid = template.get(ToolControllerTemplate.HorizRapid)
|
||||
if template.get(ToolControllerTemplate.SpindleSpeed):
|
||||
obj.SpindleSpeed = float(template.get(ToolControllerTemplate.SpindleSpeed))
|
||||
obj.SpindleSpeed = float(
|
||||
template.get(ToolControllerTemplate.SpindleSpeed)
|
||||
)
|
||||
if template.get(ToolControllerTemplate.SpindleDir):
|
||||
obj.SpindleDir = template.get(ToolControllerTemplate.SpindleDir)
|
||||
if template.get(ToolControllerTemplate.ToolNumber):
|
||||
obj.ToolNumber = int(template.get(ToolControllerTemplate.ToolNumber))
|
||||
obj.ToolNumber = int(
|
||||
template.get(ToolControllerTemplate.ToolNumber)
|
||||
)
|
||||
if template.get(ToolControllerTemplate.Tool):
|
||||
toolVersion = template.get(ToolControllerTemplate.Tool).get(ToolControllerTemplate.Version)
|
||||
toolVersion = template.get(ToolControllerTemplate.Tool).get(
|
||||
ToolControllerTemplate.Version
|
||||
)
|
||||
if toolVersion == 1:
|
||||
self.ensureUseLegacyTool(obj, True)
|
||||
obj.Tool.setFromTemplate(template.get(ToolControllerTemplate.Tool))
|
||||
obj.Tool.setFromTemplate(
|
||||
template.get(ToolControllerTemplate.Tool)
|
||||
)
|
||||
else:
|
||||
self.ensureUseLegacyTool(obj, False)
|
||||
obj.Tool = PathToolBit.Factory.CreateFromAttrs(template.get(ToolControllerTemplate.Tool))
|
||||
if obj.Tool and obj.Tool.ViewObject and obj.Tool.ViewObject.Visibility:
|
||||
obj.Tool = PathToolBit.Factory.CreateFromAttrs(
|
||||
template.get(ToolControllerTemplate.Tool)
|
||||
)
|
||||
if (
|
||||
obj.Tool
|
||||
and obj.Tool.ViewObject
|
||||
and obj.Tool.ViewObject.Visibility
|
||||
):
|
||||
obj.Tool.ViewObject.Visibility = False
|
||||
if template.get(ToolControllerTemplate.Expressions):
|
||||
for exprDef in template.get(ToolControllerTemplate.Expressions):
|
||||
if exprDef[ToolControllerTemplate.ExprExpr]:
|
||||
obj.setExpression(exprDef[ToolControllerTemplate.ExprProp], exprDef[ToolControllerTemplate.ExprExpr])
|
||||
obj.setExpression(
|
||||
exprDef[ToolControllerTemplate.ExprProp],
|
||||
exprDef[ToolControllerTemplate.ExprExpr],
|
||||
)
|
||||
else:
|
||||
PathLog.error(translate('PathToolController', "Unsupported PathToolController template version %s") % template.get(ToolControllerTemplate.Version))
|
||||
PathLog.error(
|
||||
"Unsupported PathToolController template version {}".format(
|
||||
template.get(ToolControllerTemplate.Version)
|
||||
)
|
||||
)
|
||||
else:
|
||||
PathLog.error(translate('PathToolController', 'PathToolController template has no version - corrupted template file?'))
|
||||
PathLog.error(
|
||||
"PathToolController template has no version - corrupted template file?"
|
||||
)
|
||||
|
||||
def templateAttrs(self, obj):
|
||||
'''templateAttrs(obj) ... answer a dictionary with all properties that should be stored for a template.'''
|
||||
"""templateAttrs(obj) ... answer a dictionary with all properties that should be stored for a template."""
|
||||
attrs = {}
|
||||
attrs[ToolControllerTemplate.Version] = 1
|
||||
attrs[ToolControllerTemplate.Name] = obj.Name
|
||||
attrs[ToolControllerTemplate.Label] = obj.Label
|
||||
attrs[ToolControllerTemplate.ToolNumber] = obj.ToolNumber
|
||||
attrs[ToolControllerTemplate.VertFeed] = ("%s" % (obj.VertFeed))
|
||||
attrs[ToolControllerTemplate.HorizFeed] = ("%s" % (obj.HorizFeed))
|
||||
attrs[ToolControllerTemplate.VertRapid] = ("%s" % (obj.VertRapid))
|
||||
attrs[ToolControllerTemplate.HorizRapid] = ("%s" % (obj.HorizRapid))
|
||||
attrs[ToolControllerTemplate.Version] = 1
|
||||
attrs[ToolControllerTemplate.Name] = obj.Name
|
||||
attrs[ToolControllerTemplate.Label] = obj.Label
|
||||
attrs[ToolControllerTemplate.ToolNumber] = obj.ToolNumber
|
||||
attrs[ToolControllerTemplate.VertFeed] = "%s" % (obj.VertFeed)
|
||||
attrs[ToolControllerTemplate.HorizFeed] = "%s" % (obj.HorizFeed)
|
||||
attrs[ToolControllerTemplate.VertRapid] = "%s" % (obj.VertRapid)
|
||||
attrs[ToolControllerTemplate.HorizRapid] = "%s" % (obj.HorizRapid)
|
||||
attrs[ToolControllerTemplate.SpindleSpeed] = obj.SpindleSpeed
|
||||
attrs[ToolControllerTemplate.SpindleDir] = obj.SpindleDir
|
||||
attrs[ToolControllerTemplate.SpindleDir] = obj.SpindleDir
|
||||
if self.usesLegacyTool(obj):
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.templateAttrs()
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.templateAttrs()
|
||||
else:
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.Proxy.templateAttrs(obj.Tool)
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.Proxy.templateAttrs(obj.Tool)
|
||||
expressions = []
|
||||
for expr in obj.ExpressionEngine:
|
||||
PathLog.debug('%s: %s' % (expr[0], expr[1]))
|
||||
expressions.append({ToolControllerTemplate.ExprProp: expr[0], ToolControllerTemplate.ExprExpr: expr[1]})
|
||||
PathLog.debug("%s: %s" % (expr[0], expr[1]))
|
||||
expressions.append(
|
||||
{
|
||||
ToolControllerTemplate.ExprProp: expr[0],
|
||||
ToolControllerTemplate.ExprExpr: expr[1],
|
||||
}
|
||||
)
|
||||
if expressions:
|
||||
attrs[ToolControllerTemplate.Expressions] = expressions
|
||||
return attrs
|
||||
@@ -161,24 +260,23 @@ class ToolController:
|
||||
PathLog.track()
|
||||
|
||||
commands = ""
|
||||
commands += "(" + obj.Label + ")"+'\n'
|
||||
commands += 'M6 T'+str(obj.ToolNumber)+'\n'
|
||||
commands += "(" + obj.Label + ")" + "\n"
|
||||
commands += "M6 T" + str(obj.ToolNumber) + "\n"
|
||||
|
||||
# If a toolbit is used, check to see if spindlepower is allowed.
|
||||
# This is to prevent accidentally spinning the spindle with an
|
||||
# unpowered tool like probe or dragknife
|
||||
|
||||
allowSpindlePower = True
|
||||
if (not isinstance(obj.Tool, Path.Tool) and
|
||||
hasattr(obj.Tool, "SpindlePower")):
|
||||
allowSpindlePower = obj.Tool.SpindlePower
|
||||
if not isinstance(obj.Tool, Path.Tool) and hasattr(obj.Tool, "SpindlePower"):
|
||||
allowSpindlePower = obj.Tool.SpindlePower
|
||||
|
||||
if allowSpindlePower:
|
||||
PathLog.debug('selected tool preventing spindle power')
|
||||
if obj.SpindleDir == 'Forward':
|
||||
commands += 'M3 S' + str(obj.SpindleSpeed) + '\n'
|
||||
PathLog.debug("selected tool preventing spindle power")
|
||||
if obj.SpindleDir == "Forward":
|
||||
commands += "M3 S" + str(obj.SpindleSpeed) + "\n"
|
||||
else:
|
||||
commands += 'M4 S' + str(obj.SpindleSpeed) + '\n'
|
||||
commands += "M4 S" + str(obj.SpindleSpeed) + "\n"
|
||||
|
||||
if commands == "":
|
||||
commands += "(No commands processed)"
|
||||
@@ -189,32 +287,56 @@ class ToolController:
|
||||
obj.ViewObject.Visibility = True
|
||||
|
||||
def getTool(self, obj):
|
||||
'''returns the tool associated with this tool controller'''
|
||||
"""returns the tool associated with this tool controller"""
|
||||
PathLog.track()
|
||||
return obj.Tool
|
||||
|
||||
def usesLegacyTool(self, obj):
|
||||
'''returns True if the tool being controlled is a legacy tool'''
|
||||
"""returns True if the tool being controlled is a legacy tool"""
|
||||
return isinstance(obj.Tool, Path.Tool)
|
||||
|
||||
def ensureUseLegacyTool(self, obj, legacy):
|
||||
if not hasattr(obj, 'Tool') or (legacy != self.usesLegacyTool(obj)):
|
||||
if legacy and hasattr(obj, 'Tool') and len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, 'onDelete'):
|
||||
if not hasattr(obj, "Tool") or (legacy != self.usesLegacyTool(obj)):
|
||||
if legacy and hasattr(obj, "Tool") and len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, "onDelete"):
|
||||
obj.Tool.Proxy.onDelete(obj.Tool)
|
||||
obj.Document.removeObject(obj.Tool.Name)
|
||||
|
||||
if hasattr(obj, 'Tool'):
|
||||
obj.removeProperty('Tool')
|
||||
if hasattr(obj, "Tool"):
|
||||
obj.removeProperty("Tool")
|
||||
|
||||
if legacy:
|
||||
obj.addProperty("Path::PropertyTool", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The tool used by this controller"))
|
||||
obj.addProperty(
|
||||
"Path::PropertyTool",
|
||||
"Tool",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The tool used by this controller"
|
||||
),
|
||||
)
|
||||
else:
|
||||
obj.addProperty("App::PropertyLink", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The tool used by this controller"))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Tool",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The tool used by this controller"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def Create(name='TC: Default Tool', tool=None, toolNumber=1, assignViewProvider=True, assignTool=True):
|
||||
legacyTool = PathPreferences.toolsUseLegacyTools() if tool is None else isinstance(tool, Path.Tool)
|
||||
def Create(
|
||||
name="TC: Default Tool",
|
||||
tool=None,
|
||||
toolNumber=1,
|
||||
assignViewProvider=True,
|
||||
assignTool=True,
|
||||
):
|
||||
legacyTool = (
|
||||
PathPreferences.toolsUseLegacyTools()
|
||||
if tool is None
|
||||
else isinstance(tool, Path.Tool)
|
||||
)
|
||||
|
||||
PathLog.track(tool, toolNumber, legacyTool)
|
||||
|
||||
|
||||
@@ -20,45 +20,50 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathToolBitGui as PathToolBitGui
|
||||
import PathScripts.PathToolEdit as PathToolEdit
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import PathScripts.PathToolController as PathToolController
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ViewProvider:
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
self.vobj = vobj
|
||||
|
||||
def attach(self, vobj):
|
||||
mode = 2
|
||||
vobj.setEditorMode('LineWidth', mode)
|
||||
vobj.setEditorMode('MarkerColor', mode)
|
||||
vobj.setEditorMode('NormalColor', mode)
|
||||
vobj.setEditorMode('DisplayMode', mode)
|
||||
vobj.setEditorMode('BoundingBox', mode)
|
||||
vobj.setEditorMode('Selectable', mode)
|
||||
vobj.setEditorMode('ShapeColor', mode)
|
||||
vobj.setEditorMode('Transparency', mode)
|
||||
vobj.setEditorMode('Visibility', mode)
|
||||
vobj.setEditorMode("LineWidth", mode)
|
||||
vobj.setEditorMode("MarkerColor", mode)
|
||||
vobj.setEditorMode("NormalColor", mode)
|
||||
vobj.setEditorMode("DisplayMode", mode)
|
||||
vobj.setEditorMode("BoundingBox", mode)
|
||||
vobj.setEditorMode("Selectable", mode)
|
||||
vobj.setEditorMode("ShapeColor", mode)
|
||||
vobj.setEditorMode("Transparency", mode)
|
||||
vobj.setEditorMode("Visibility", mode)
|
||||
self.vobj = vobj
|
||||
|
||||
def __getstate__(self):
|
||||
@@ -73,12 +78,12 @@ class ViewProvider:
|
||||
def onChanged(self, vobj, prop):
|
||||
# pylint: disable=unused-argument
|
||||
mode = 2
|
||||
vobj.setEditorMode('LineWidth', mode)
|
||||
vobj.setEditorMode('MarkerColor', mode)
|
||||
vobj.setEditorMode('NormalColor', mode)
|
||||
vobj.setEditorMode('DisplayMode', mode)
|
||||
vobj.setEditorMode('BoundingBox', mode)
|
||||
vobj.setEditorMode('Selectable', mode)
|
||||
vobj.setEditorMode("LineWidth", mode)
|
||||
vobj.setEditorMode("MarkerColor", mode)
|
||||
vobj.setEditorMode("NormalColor", mode)
|
||||
vobj.setEditorMode("DisplayMode", mode)
|
||||
vobj.setEditorMode("BoundingBox", mode)
|
||||
vobj.setEditorMode("Selectable", mode)
|
||||
|
||||
def onDelete(self, vobj, args=None):
|
||||
# pylint: disable=unused-argument
|
||||
@@ -115,7 +120,7 @@ class ViewProvider:
|
||||
PathLog.track()
|
||||
for action in menu.actions():
|
||||
menu.removeAction(action)
|
||||
action = QtGui.QAction(translate('Path', 'Edit'), menu)
|
||||
action = QtGui.QAction(translate("Path", "Edit"), menu)
|
||||
action.triggered.connect(self.setEdit)
|
||||
menu.addAction(action)
|
||||
|
||||
@@ -126,7 +131,7 @@ class ViewProvider:
|
||||
return []
|
||||
|
||||
|
||||
def Create(name='Default Tool', tool=None, toolNumber=1):
|
||||
def Create(name="Default Tool", tool=None, toolNumber=1):
|
||||
PathLog.track(tool, toolNumber)
|
||||
|
||||
obj = PathScripts.PathToolController.Create(name, tool, toolNumber)
|
||||
@@ -142,16 +147,20 @@ class CommandPathToolController(object):
|
||||
# pylint: disable=no-init
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_LengthOffset',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_ToolController", "Add Tool Controller to the Job"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_ToolController", "Add Tool Controller")}
|
||||
return {
|
||||
"Pixmap": "Path_LengthOffset",
|
||||
"MenuText": QT_TRANSLATE_NOOP(
|
||||
"Path_ToolController", "Add Tool Controller to the Job"
|
||||
),
|
||||
"ToolTip": QT_TRANSLATE_NOOP("Path_ToolController", "Add Tool Controller"),
|
||||
}
|
||||
|
||||
def selectedJob(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if sel and sel[0].Object.Name[:3] == 'Job':
|
||||
if sel and sel[0].Object.Name[:3] == "Job":
|
||||
return sel[0].Object
|
||||
jobs = [o for o in FreeCAD.ActiveDocument.Objects if o.Name[:3] == 'Job']
|
||||
jobs = [o for o in FreeCAD.ActiveDocument.Objects if o.Name[:3] == "Job"]
|
||||
if 1 == len(jobs):
|
||||
return jobs[0]
|
||||
return None
|
||||
@@ -178,30 +187,69 @@ class CommandPathToolController(object):
|
||||
|
||||
|
||||
class ToolControllerEditor(object):
|
||||
|
||||
def __init__(self, obj, asDialog):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/DlgToolControllerEdit.ui")
|
||||
if not asDialog:
|
||||
self.form.buttonBox.hide()
|
||||
self.obj = obj
|
||||
|
||||
self.vertFeed = PathGui.QuantitySpinBox(self.form.vertFeed, obj,
|
||||
'VertFeed')
|
||||
self.horizFeed = PathGui.QuantitySpinBox(self.form.horizFeed, obj,
|
||||
'HorizFeed')
|
||||
self.vertRapid = PathGui.QuantitySpinBox(self.form.vertRapid, obj,
|
||||
'VertRapid')
|
||||
self.horizRapid = PathGui.QuantitySpinBox(self.form.horizRapid, obj,
|
||||
'HorizRapid')
|
||||
comboToPropertyMap = [("spindleDirection", "SpindleDir")]
|
||||
enumTups = PathToolController.ToolController.propertyEnumerations(
|
||||
dataType="raw"
|
||||
)
|
||||
|
||||
self.populateCombobox(self.form, enumTups, comboToPropertyMap)
|
||||
self.vertFeed = PathGui.QuantitySpinBox(self.form.vertFeed, obj, "VertFeed")
|
||||
self.horizFeed = PathGui.QuantitySpinBox(self.form.horizFeed, obj, "HorizFeed")
|
||||
self.vertRapid = PathGui.QuantitySpinBox(self.form.vertRapid, obj, "VertRapid")
|
||||
self.horizRapid = PathGui.QuantitySpinBox(
|
||||
self.form.horizRapid, obj, "HorizRapid"
|
||||
)
|
||||
|
||||
if obj.Proxy.usesLegacyTool(obj):
|
||||
self.editor = PathToolEdit.ToolEditor(obj.Tool,
|
||||
self.form.toolEditor)
|
||||
self.editor = PathToolEdit.ToolEditor(obj.Tool, self.form.toolEditor)
|
||||
else:
|
||||
self.editor = None
|
||||
self.form.toolBox.widget(1).hide()
|
||||
self.form.toolBox.removeItem(1)
|
||||
|
||||
def selectInComboBox(self, name, combo):
|
||||
"""selectInComboBox(name, combo) ...
|
||||
helper function to select a specific value in a combo box."""
|
||||
blocker = QtCore.QSignalBlocker(combo)
|
||||
index = combo.currentIndex() # Save initial index
|
||||
|
||||
# Search using currentData and return if found
|
||||
newindex = combo.findData(name)
|
||||
if newindex >= 0:
|
||||
combo.setCurrentIndex(newindex)
|
||||
return
|
||||
|
||||
# if not found, search using current text
|
||||
newindex = combo.findText(name, QtCore.Qt.MatchFixedString)
|
||||
if newindex >= 0:
|
||||
combo.setCurrentIndex(newindex)
|
||||
return
|
||||
|
||||
# not found, return unchanged
|
||||
combo.setCurrentIndex(index)
|
||||
return
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def updateUi(self):
|
||||
tc = self.obj
|
||||
self.form.tcName.setText(tc.Label)
|
||||
@@ -211,10 +259,14 @@ class ToolControllerEditor(object):
|
||||
self.vertFeed.updateSpinBox()
|
||||
self.vertRapid.updateSpinBox()
|
||||
self.form.spindleSpeed.setValue(tc.SpindleSpeed)
|
||||
index = self.form.spindleDirection.findText(tc.SpindleDir,
|
||||
QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.spindleDirection.setCurrentIndex(index)
|
||||
|
||||
self.selectInComboBox(tc.SpindleDir, self.form.spindleDirection)
|
||||
|
||||
# index = self.form.spindleDirection.findText(
|
||||
# tc.SpindleDir, QtCore.Qt.MatchFixedString
|
||||
# )
|
||||
# if index >= 0:
|
||||
# self.form.spindleDirection.setCurrentIndex(index)
|
||||
|
||||
if self.editor:
|
||||
self.editor.updateUI()
|
||||
@@ -229,15 +281,14 @@ class ToolControllerEditor(object):
|
||||
self.horizRapid.updateProperty()
|
||||
self.vertRapid.updateProperty()
|
||||
tc.SpindleSpeed = self.form.spindleSpeed.value()
|
||||
tc.SpindleDir = self.form.spindleDirection.currentText()
|
||||
tc.SpindleDir = self.form.spindleDirection.currentData()
|
||||
|
||||
if self.editor:
|
||||
self.editor.updateTool()
|
||||
tc.Tool = self.editor.tool
|
||||
|
||||
except Exception as e:
|
||||
PathLog.error(translate("PathToolController",
|
||||
"Error updating TC: %s") % e)
|
||||
PathLog.error("Error updating TC: {}".format(e))
|
||||
|
||||
def refresh(self):
|
||||
self.form.blockSignals(True)
|
||||
@@ -259,7 +310,6 @@ class ToolControllerEditor(object):
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
|
||||
def __init__(self, obj):
|
||||
self.editor = ToolControllerEditor(obj, False)
|
||||
self.form = self.editor.form
|
||||
@@ -309,8 +359,7 @@ class TaskPanel:
|
||||
def setupUi(self):
|
||||
if self.editor.editor:
|
||||
t = Part.makeCylinder(1, 1)
|
||||
self.toolrep = FreeCAD.ActiveDocument.addObject("Part::Feature",
|
||||
"tool")
|
||||
self.toolrep = FreeCAD.ActiveDocument.addObject("Part::Feature", "tool")
|
||||
self.toolrep.Shape = t
|
||||
|
||||
self.setFields()
|
||||
@@ -337,6 +386,6 @@ class DlgToolControllerEdit:
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_ToolController', CommandPathToolController())
|
||||
FreeCADGui.addCommand("Path_ToolController", CommandPathToolController())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathToolControllerGui... done\n")
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
# ***************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
@@ -30,19 +31,20 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBitLibraryCmd as PathToolBitLibraryCmd
|
||||
import PathScripts.PathToolEdit as PathToolEdit
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathToolLibraryManager as ToolLibraryManager
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
class EditorPanel():
|
||||
|
||||
class EditorPanel:
|
||||
def __init__(self, job, cb):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolLibraryEditor.ui")
|
||||
self.TLM = ToolLibraryManager.ToolLibraryManager()
|
||||
@@ -88,7 +90,7 @@ class EditorPanel():
|
||||
return toolslist[tooltype]
|
||||
|
||||
def getMaterial(self, material):
|
||||
'''gets a combobox index number for a given material or vice versa'''
|
||||
"""gets a combobox index number for a given material or vice versa"""
|
||||
matslist = Path.Tool.getToolMaterials(Path.Tool())
|
||||
if isinstance(material, str):
|
||||
if material in matslist:
|
||||
@@ -99,7 +101,7 @@ class EditorPanel():
|
||||
return matslist[material]
|
||||
|
||||
def addTool(self):
|
||||
'''adds new tool to the current tool table'''
|
||||
"""adds new tool to the current tool table"""
|
||||
tool = Path.Tool()
|
||||
editor = self.toolEditor(tool)
|
||||
|
||||
@@ -111,19 +113,19 @@ class EditorPanel():
|
||||
self.loadTable(listname)
|
||||
|
||||
def delete(self):
|
||||
'''deletes the selected tool'''
|
||||
"""deletes the selected tool"""
|
||||
listname = self.TLM.getCurrentTableName()
|
||||
model = self.form.ToolsList.model()
|
||||
for i in range(model.rowCount()):
|
||||
item = model.item(i, 0)
|
||||
if item.checkState():
|
||||
t = model.index(i, 1)
|
||||
self.TLM.delete(int(t.data()) ,listname)
|
||||
self.TLM.delete(int(t.data()), listname)
|
||||
self.loadTable(listname)
|
||||
self.toolSelectionChanged()
|
||||
|
||||
def editTool(self, currItem):
|
||||
'''load the tool edit dialog'''
|
||||
"""load the tool edit dialog"""
|
||||
if not currItem:
|
||||
currItem = self.form.ToolsList.selectedIndexes()[1]
|
||||
|
||||
@@ -141,7 +143,7 @@ class EditorPanel():
|
||||
self.loadTable(listname)
|
||||
|
||||
def moveUp(self):
|
||||
'''moves a tool to a lower number, if possible'''
|
||||
"""moves a tool to a lower number, if possible"""
|
||||
item = self.form.ToolsList.selectedIndexes()[1].data()
|
||||
if item:
|
||||
number = int(item)
|
||||
@@ -152,7 +154,7 @@ class EditorPanel():
|
||||
self.updateSelection(newNum)
|
||||
|
||||
def moveDown(self):
|
||||
'''moves a tool to a higher number, if possible'''
|
||||
"""moves a tool to a higher number, if possible"""
|
||||
item = self.form.ToolsList.selectedIndexes()[1].data()
|
||||
if item:
|
||||
number = int(item)
|
||||
@@ -163,7 +165,7 @@ class EditorPanel():
|
||||
self.updateSelection(newNum)
|
||||
|
||||
def duplicate(self):
|
||||
'''duplicated the selected tool in the current tool table'''
|
||||
"""duplicated the selected tool in the current tool table"""
|
||||
item = self.form.ToolsList.selectedIndexes()[1].data()
|
||||
if item:
|
||||
number = int(item)
|
||||
@@ -174,7 +176,7 @@ class EditorPanel():
|
||||
self.updateSelection(newNum)
|
||||
|
||||
def updateSelection(self, number):
|
||||
'''update the tool list selection to track moves'''
|
||||
"""update the tool list selection to track moves"""
|
||||
model = self.form.ToolsList.model()
|
||||
for i in range(model.rowCount()):
|
||||
if int(model.index(i, 1).data()) == number:
|
||||
@@ -182,24 +184,41 @@ class EditorPanel():
|
||||
self.form.ToolsList.model().item(i, 0).setCheckState(QtCore.Qt.Checked)
|
||||
return
|
||||
|
||||
|
||||
def importFile(self):
|
||||
'''imports a tooltable from a file'''
|
||||
filename = QtGui.QFileDialog.getOpenFileName(self.form, translate( "TooltableEditor", "Open tooltable", None), None, "{};;{};;{}".format(self.TLM.TooltableTypeJSON, self.TLM.TooltableTypeXML, self.TLM.TooltableTypeHeekscad))
|
||||
"""imports a tooltable from a file"""
|
||||
filename = QtGui.QFileDialog.getOpenFileName(
|
||||
self.form,
|
||||
translate("Path_ToolTable", "Open tooltable", None),
|
||||
None,
|
||||
"{};;{};;{}".format(
|
||||
self.TLM.TooltableTypeJSON,
|
||||
self.TLM.TooltableTypeXML,
|
||||
self.TLM.TooltableTypeHeekscad,
|
||||
),
|
||||
)
|
||||
if filename[0]:
|
||||
listname = self.TLM.getNextToolTableName()
|
||||
if self.TLM.read(filename, listname):
|
||||
self.loadToolTables()
|
||||
|
||||
def exportFile(self):
|
||||
'''export a tooltable to a file'''
|
||||
filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("TooltableEditor", "Save tooltable", None), None, "{};;{};;{}".format(self.TLM.TooltableTypeJSON, self.TLM.TooltableTypeXML, self.TLM.TooltableTypeLinuxCNC))
|
||||
"""export a tooltable to a file"""
|
||||
filename = QtGui.QFileDialog.getSaveFileName(
|
||||
self.form,
|
||||
translate("Path_ToolTable", "Save tooltable", None),
|
||||
None,
|
||||
"{};;{};;{}".format(
|
||||
self.TLM.TooltableTypeJSON,
|
||||
self.TLM.TooltableTypeXML,
|
||||
self.TLM.TooltableTypeLinuxCNC,
|
||||
),
|
||||
)
|
||||
if filename[0]:
|
||||
listname = self.TLM.getCurrentTableName()
|
||||
self.TLM.write(filename, listname)
|
||||
|
||||
def toolSelectionChanged(self, index=None):
|
||||
''' updates the ui when tools are selected'''
|
||||
"""updates the ui when tools are selected"""
|
||||
if index:
|
||||
self.form.ToolsList.selectRow(index.row())
|
||||
|
||||
@@ -222,7 +241,7 @@ class EditorPanel():
|
||||
|
||||
# only allow moving or deleting a single tool at a time.
|
||||
if checkCount == 1:
|
||||
#make sure the row is highlighted when the check box gets ticked
|
||||
# make sure the row is highlighted when the check box gets ticked
|
||||
self.form.ToolsList.selectRow(checkList[0])
|
||||
self.form.ButtonDelete.setEnabled(True)
|
||||
self.form.ButtonUp.setEnabled(True)
|
||||
@@ -234,7 +253,7 @@ class EditorPanel():
|
||||
self.form.btnCopyTools.setEnabled(False)
|
||||
|
||||
def copyTools(self):
|
||||
''' copy selected tool '''
|
||||
"""copy selected tool"""
|
||||
tools = []
|
||||
model = self.form.ToolsList.model()
|
||||
for i in range(model.rowCount()):
|
||||
@@ -268,43 +287,54 @@ class EditorPanel():
|
||||
|
||||
for toolnum in tools:
|
||||
tool = self.TLM.getTool(currList, int(toolnum))
|
||||
PathLog.debug('tool: {}, toolnum: {}'.format(tool, toolnum))
|
||||
PathLog.debug("tool: {}, toolnum: {}".format(tool, toolnum))
|
||||
if self.job:
|
||||
label = "T{}: {}".format(toolnum, tool.Name)
|
||||
tc = PathScripts.PathToolController.Create(label, tool=tool, toolNumber=int(toolnum))
|
||||
tc = PathScripts.PathToolController.Create(
|
||||
label, tool=tool, toolNumber=int(toolnum)
|
||||
)
|
||||
self.job.Proxy.addToolController(tc)
|
||||
else:
|
||||
for job in FreeCAD.ActiveDocument.findObjects("Path::Feature"):
|
||||
if isinstance(job.Proxy, PathScripts.PathJob.ObjectJob) and job.Label == targetlist:
|
||||
if (
|
||||
isinstance(job.Proxy, PathScripts.PathJob.ObjectJob)
|
||||
and job.Label == targetlist
|
||||
):
|
||||
label = "T{}: {}".format(toolnum, tool.Name)
|
||||
tc = PathScripts.PathToolController.Create(label, tool=tool, toolNumber=int(toolnum))
|
||||
tc = PathScripts.PathToolController.Create(
|
||||
label, tool=tool, toolNumber=int(toolnum)
|
||||
)
|
||||
job.Proxy.addToolController(tc)
|
||||
if self.cb:
|
||||
self.cb()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def tableSelected(self, index):
|
||||
''' loads the tools for the selected tool table '''
|
||||
name = self.form.TableList.itemWidget(self.form.TableList.itemFromIndex(index)).getTableName()
|
||||
"""loads the tools for the selected tool table"""
|
||||
name = self.form.TableList.itemWidget(
|
||||
self.form.TableList.itemFromIndex(index)
|
||||
).getTableName()
|
||||
self.loadTable(name)
|
||||
|
||||
def loadTable(self, name):
|
||||
''' loads the tools for the selected tool table '''
|
||||
"""loads the tools for the selected tool table"""
|
||||
tooldata = self.TLM.getTools(name)
|
||||
if tooldata:
|
||||
self.form.ToolsList.setModel(tooldata)
|
||||
self.form.ToolsList.resizeColumnsToContents()
|
||||
self.form.ToolsList.horizontalHeader().setResizeMode(self.form.ToolsList.model().columnCount() - 1, QtGui.QHeaderView.Stretch)
|
||||
self.form.ToolsList.horizontalHeader().setResizeMode(
|
||||
self.form.ToolsList.model().columnCount() - 1, QtGui.QHeaderView.Stretch
|
||||
)
|
||||
self.setCurrentToolTableByName(name)
|
||||
|
||||
def addNewToolTable(self):
|
||||
''' adds new tool to selected tool table '''
|
||||
"""adds new tool to selected tool table"""
|
||||
name = self.TLM.addNewToolTable()
|
||||
self.loadToolTables()
|
||||
self.loadTable(name)
|
||||
|
||||
def loadToolTables(self):
|
||||
''' Load list of available tool tables '''
|
||||
"""Load list of available tool tables"""
|
||||
self.form.TableList.clear()
|
||||
model = self.form.ToolsList.model()
|
||||
if model:
|
||||
@@ -314,44 +344,54 @@ class EditorPanel():
|
||||
listWidgetItem = QtGui.QListWidgetItem()
|
||||
listItem = ToolTableListWidgetItem(self.TLM)
|
||||
listItem.setTableName(table.Name)
|
||||
listItem.setIcon(QtGui.QPixmap(':/icons/Path_ToolTable.svg'))
|
||||
listItem.setIcon(QtGui.QPixmap(":/icons/Path_ToolTable.svg"))
|
||||
listItem.toolMoved.connect(self.reloadReset)
|
||||
listWidgetItem.setSizeHint(QtCore.QSize(0,40))
|
||||
listWidgetItem.setSizeHint(QtCore.QSize(0, 40))
|
||||
self.form.TableList.addItem(listWidgetItem)
|
||||
self.form.TableList.setItemWidget(listWidgetItem, listItem)
|
||||
#Load the first tooltable
|
||||
# Load the first tooltable
|
||||
self.loadTable(self.TLM.getCurrentTableName())
|
||||
|
||||
def reloadReset(self):
|
||||
''' reloads the current tooltable'''
|
||||
"""reloads the current tooltable"""
|
||||
name = self.TLM.getCurrentTableName()
|
||||
self.loadTable(name)
|
||||
|
||||
def setCurrentToolTableByName(self, name):
|
||||
''' get the current tool table '''
|
||||
"""get the current tool table"""
|
||||
item = self.getToolTableByName(name)
|
||||
if item:
|
||||
self.form.TableList.setCurrentItem(item)
|
||||
|
||||
def getToolTableByName(self, name):
|
||||
''' returns the listWidgetItem for the selected name'''
|
||||
"""returns the listWidgetItem for the selected name"""
|
||||
for i in range(self.form.TableList.count()):
|
||||
tableName = self.form.TableList.itemWidget(self.form.TableList.item(i)).getTableName()
|
||||
tableName = self.form.TableList.itemWidget(
|
||||
self.form.TableList.item(i)
|
||||
).getTableName()
|
||||
if tableName == name:
|
||||
return self.form.TableList.item(i)
|
||||
return False
|
||||
|
||||
def removeToolTable(self):
|
||||
''' delete the selected tool table '''
|
||||
"""delete the selected tool table"""
|
||||
self.TLM.deleteToolTable()
|
||||
self.loadToolTables()
|
||||
|
||||
def renameTable(self):
|
||||
''' provides dialog for new tablename and renames the selected tool table'''
|
||||
"""provides dialog for new tablename and renames the selected tool table"""
|
||||
name = self.TLM.getCurrentTableName()
|
||||
newName, ok = QtGui.QInputDialog.getText(None, translate("TooltableEditor","Rename Tooltable"),translate("TooltableEditor","Enter Name:"),QtGui.QLineEdit.Normal,name)
|
||||
newName, ok = QtGui.QInputDialog.getText(
|
||||
None,
|
||||
translate("Path_ToolTable", "Rename Tooltable"),
|
||||
translate("Path_ToolTable", "Enter Name:"),
|
||||
QtGui.QLineEdit.Normal,
|
||||
name,
|
||||
)
|
||||
if ok and newName:
|
||||
index = self.form.TableList.indexFromItem(self.getToolTableByName(name)).row()
|
||||
index = self.form.TableList.indexFromItem(
|
||||
self.getToolTableByName(name)
|
||||
).row()
|
||||
reloadTables = self.TLM.renameToolTable(newName, index)
|
||||
if reloadTables:
|
||||
self.loadToolTables()
|
||||
@@ -380,14 +420,21 @@ class EditorPanel():
|
||||
self.form.TableList.itemChanged.connect(self.renameTable)
|
||||
|
||||
self.form.ButtonAddToolTable.clicked.connect(self.addNewToolTable)
|
||||
self.form.ButtonAddToolTable.setToolTip(translate("TooltableEditor","Add New Tool Table"))
|
||||
self.form.ButtonAddToolTable.setToolTip(
|
||||
translate("Path_ToolTable", "Add New Tool Table")
|
||||
)
|
||||
self.form.ButtonRemoveToolTable.clicked.connect(self.removeToolTable)
|
||||
self.form.ButtonRemoveToolTable.setToolTip(translate("TooltableEditor","Delete Selected Tool Table"))
|
||||
self.form.ButtonRemoveToolTable.setToolTip(
|
||||
translate("Path_ToolTable", "Delete Selected Tool Table")
|
||||
)
|
||||
self.form.ButtonRenameToolTable.clicked.connect(self.renameTable)
|
||||
self.form.ButtonRenameToolTable.setToolTip(translate("TooltableEditor","Rename Selected Tool Table"))
|
||||
self.form.ButtonRenameToolTable.setToolTip(
|
||||
translate("Path_ToolTable", "Rename Selected Tool Table")
|
||||
)
|
||||
|
||||
self.setFields()
|
||||
|
||||
|
||||
class ToolTableListWidgetItem(QtGui.QWidget):
|
||||
|
||||
toolMoved = QtCore.Signal()
|
||||
@@ -405,13 +452,13 @@ class ToolTableListWidgetItem(QtGui.QWidget):
|
||||
self.mainLayout.addWidget(self.tableNameLabel, 1)
|
||||
self.setLayout(self.mainLayout)
|
||||
|
||||
def setTableName (self, text):
|
||||
def setTableName(self, text):
|
||||
self.tableNameLabel.setText(text)
|
||||
|
||||
def getTableName(self):
|
||||
return self.tableNameLabel.text()
|
||||
|
||||
def setIcon (self, icon):
|
||||
def setIcon(self, icon):
|
||||
icon = icon.scaled(24, 24)
|
||||
self.iconQLabel.setPixmap(icon)
|
||||
|
||||
@@ -434,7 +481,7 @@ class ToolTableListWidgetItem(QtGui.QWidget):
|
||||
self.toolMoved.emit()
|
||||
|
||||
|
||||
class CommandToolLibraryEdit():
|
||||
class CommandToolLibraryEdit:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@@ -449,10 +496,12 @@ class CommandToolLibraryEdit():
|
||||
cb()
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'Path_ToolTable',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_ToolTable","Tool Manager"),
|
||||
'Accel': "P, T",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_ToolTable","Tool Manager")}
|
||||
return {
|
||||
"Pixmap": "Path_ToolTable",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_ToolTable", "Tool Manager"),
|
||||
"Accel": "P, T",
|
||||
"ToolTip": QT_TRANSLATE_NOOP("Path_ToolTable", "Tool Manager"),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
@@ -460,6 +509,7 @@ class CommandToolLibraryEdit():
|
||||
def Activated(self):
|
||||
self.edit()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_ToolLibraryEdit',CommandToolLibraryEdit())
|
||||
FreeCADGui.addCommand("Path_ToolLibraryEdit", CommandToolLibraryEdit())
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import PathScripts
|
||||
import PathScripts.PathLog as PathLog
|
||||
@@ -31,14 +30,15 @@ import PathScripts.PathUtil as PathUtil
|
||||
import json
|
||||
import os
|
||||
import xml.sax
|
||||
from PySide import QtGui
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# Tooltable XML readers
|
||||
class FreeCADTooltableHandler(xml.sax.ContentHandler):
|
||||
@@ -62,7 +62,7 @@ class FreeCADTooltableHandler(xml.sax.ContentHandler):
|
||||
self.tool.ToolType = str(attrs["type"])
|
||||
self.tool.Material = str(attrs["mat"])
|
||||
# for some reason without the following line I get an error
|
||||
#print attrs["diameter"]
|
||||
# print attrs["diameter"]
|
||||
self.tool.Diameter = float(attrs["diameter"])
|
||||
self.tool.LengthOffset = float(attrs["length"])
|
||||
self.tool.FlatRadius = float(attrs["flat"])
|
||||
@@ -80,7 +80,6 @@ class FreeCADTooltableHandler(xml.sax.ContentHandler):
|
||||
|
||||
|
||||
class HeeksTooltableHandler(xml.sax.ContentHandler):
|
||||
|
||||
def __init__(self):
|
||||
xml.sax.ContentHandler.__init__(self)
|
||||
self.tooltable = Path.Tooltable()
|
||||
@@ -115,15 +114,13 @@ class HeeksTooltableHandler(xml.sax.ContentHandler):
|
||||
elif m == "1":
|
||||
self.tool.Material = "Carbide"
|
||||
# for some reason without the following line I get an error
|
||||
#print attrs["diameter"]
|
||||
# print attrs["diameter"]
|
||||
self.tool.Diameter = float(attrs["diameter"])
|
||||
self.tool.LengthOffset = float(attrs["tool_length_offset"])
|
||||
self.tool.FlatRadius = float(attrs["flat_radius"])
|
||||
self.tool.CornerRadius = float(attrs["corner_radius"])
|
||||
self.tool.CuttingEdgeAngle = float(
|
||||
attrs["cutting_edge_angle"])
|
||||
self.tool.CuttingEdgeHeight = float(
|
||||
attrs["cutting_edge_height"])
|
||||
self.tool.CuttingEdgeAngle = float(attrs["cutting_edge_angle"])
|
||||
self.tool.CuttingEdgeHeight = float(attrs["cutting_edge_height"])
|
||||
|
||||
# Call when an elements ends
|
||||
def endElement(self, name):
|
||||
@@ -134,17 +131,21 @@ class HeeksTooltableHandler(xml.sax.ContentHandler):
|
||||
self.tool = None
|
||||
|
||||
|
||||
class ToolLibraryManager():
|
||||
'''
|
||||
class ToolLibraryManager:
|
||||
"""
|
||||
The Tool Library is a list of individual tool tables. Each
|
||||
Tool Table can contain n tools. The tool library will be persisted to user
|
||||
preferences and all or part of the library can be exported to other formats
|
||||
'''
|
||||
"""
|
||||
|
||||
TooltableTypeJSON = translate("PathToolLibraryManager", "Tooltable JSON (*.json)")
|
||||
TooltableTypeXML = translate("PathToolLibraryManager", "Tooltable XML (*.xml)")
|
||||
TooltableTypeHeekscad = translate("PathToolLibraryManager", "HeeksCAD tooltable (*.tooltable)")
|
||||
TooltableTypeLinuxCNC = translate("PathToolLibraryManager", "LinuxCNC tooltable (*.tbl)")
|
||||
TooltableTypeJSON = translate("PathToolLibraryManager", "Tooltable JSON (*.json)")
|
||||
TooltableTypeXML = translate("PathToolLibraryManager", "Tooltable XML (*.xml)")
|
||||
TooltableTypeHeekscad = translate(
|
||||
"PathToolLibraryManager", "HeeksCAD tooltable (*.tooltable)"
|
||||
)
|
||||
TooltableTypeLinuxCNC = translate(
|
||||
"PathToolLibraryManager", "LinuxCNC tooltable (*.tbl)"
|
||||
)
|
||||
|
||||
PreferenceMainLibraryXML = "ToolLibrary"
|
||||
PreferenceMainLibraryJSON = "ToolLibrary-Main"
|
||||
@@ -156,38 +157,40 @@ class ToolLibraryManager():
|
||||
self.loadToolTables()
|
||||
|
||||
def getToolTables(self):
|
||||
''' Return tool table list '''
|
||||
"""Return tool table list"""
|
||||
return self.toolTables
|
||||
|
||||
def getCurrentTableName(self):
|
||||
''' return the name of the currently loaded tool table '''
|
||||
"""return the name of the currently loaded tool table"""
|
||||
return self.currentTableName
|
||||
|
||||
def getCurrentTable(self):
|
||||
''' returns an object of the current tool table '''
|
||||
"""returns an object of the current tool table"""
|
||||
return self.getTableFromName(self.currentTableName)
|
||||
|
||||
def getTableFromName(self, name):
|
||||
''' get the tool table object from the name '''
|
||||
"""get the tool table object from the name"""
|
||||
for table in self.toolTables:
|
||||
if table.Name == name:
|
||||
return table
|
||||
|
||||
def getNextToolTableName(self, tableName='Tool Table'):
|
||||
''' get a unique name for a new tool table '''
|
||||
def getNextToolTableName(self, tableName="Tool Table"):
|
||||
"""get a unique name for a new tool table"""
|
||||
iter = 1
|
||||
tempName = tableName[-2:]
|
||||
|
||||
if tempName[0] == '-' and tempName[-1].isdigit():
|
||||
if tempName[0] == "-" and tempName[-1].isdigit():
|
||||
tableName = tableName[:-2]
|
||||
|
||||
while any(table.Name == tableName + '-' + str(iter) for table in self.toolTables):
|
||||
while any(
|
||||
table.Name == tableName + "-" + str(iter) for table in self.toolTables
|
||||
):
|
||||
iter += 1
|
||||
|
||||
return tableName + '-' + str(iter)
|
||||
return tableName + "-" + str(iter)
|
||||
|
||||
def addNewToolTable(self):
|
||||
''' creates a new tool table '''
|
||||
"""creates a new tool table"""
|
||||
tt = Path.Tooltable()
|
||||
tt.Version = 1
|
||||
name = self.getNextToolTableName()
|
||||
@@ -197,20 +200,27 @@ class ToolLibraryManager():
|
||||
return name
|
||||
|
||||
def deleteToolTable(self):
|
||||
''' deletes the selected tool table '''
|
||||
"""deletes the selected tool table"""
|
||||
if len(self.toolTables):
|
||||
index = next((index for (index, d) in enumerate(self.toolTables) if d.Name == self.currentTableName), None)
|
||||
index = next(
|
||||
(
|
||||
index
|
||||
for (index, d) in enumerate(self.toolTables)
|
||||
if d.Name == self.currentTableName
|
||||
),
|
||||
None,
|
||||
)
|
||||
self.toolTables.pop(index)
|
||||
self.saveMainLibrary()
|
||||
|
||||
def renameToolTable(self, newName, index):
|
||||
''' renames a tool table with the new name'''
|
||||
"""renames a tool table with the new name"""
|
||||
currentTableName = self.toolTables[index].Name
|
||||
if newName == currentTableName:
|
||||
PathLog.error(translate('PathToolLibraryManager', "Tool Table Same Name"))
|
||||
PathLog.error(translate("PathToolLibraryManager", "Tool Table Same Name"))
|
||||
return False
|
||||
if newName in self.toolTables:
|
||||
PathLog.error(translate('PathToolLibraryManager', "Tool Table Name Exists"))
|
||||
PathLog.error(translate("PathToolLibraryManager", "Tool Table Name Exists"))
|
||||
return False
|
||||
tt = self.getTableFromName(currentTableName)
|
||||
if tt:
|
||||
@@ -218,67 +228,74 @@ class ToolLibraryManager():
|
||||
self.saveMainLibrary()
|
||||
return True
|
||||
|
||||
|
||||
def templateAttrs(self):
|
||||
''' gets the tool table arributes '''
|
||||
"""gets the tool table arributes"""
|
||||
toolTables = []
|
||||
for tt in self.toolTables:
|
||||
tableData = {}
|
||||
tableData['Version'] = 1
|
||||
tableData['TableName'] = tt.Name
|
||||
tableData["Version"] = 1
|
||||
tableData["TableName"] = tt.Name
|
||||
|
||||
toolData = {}
|
||||
for tool in tt.Tools:
|
||||
toolData[tool] = tt.Tools[tool].templateAttrs()
|
||||
|
||||
tableData['Tools'] = toolData
|
||||
tableData["Tools"] = toolData
|
||||
toolTables.append(tableData)
|
||||
|
||||
return toolTables
|
||||
|
||||
def tooltableFromAttrs(self, stringattrs):
|
||||
if stringattrs.get('Version') and 1 == int(stringattrs['Version']):
|
||||
if stringattrs.get("Version") and 1 == int(stringattrs["Version"]):
|
||||
|
||||
tt = Path.Tooltable()
|
||||
tt.Version = 1
|
||||
tt.Name = self.getNextToolTableName()
|
||||
|
||||
if stringattrs.get('Version'):
|
||||
tt.Version = stringattrs.get('Version')
|
||||
if stringattrs.get("Version"):
|
||||
tt.Version = stringattrs.get("Version")
|
||||
|
||||
if stringattrs.get('TableName'):
|
||||
tt.Name = stringattrs.get('TableName')
|
||||
if stringattrs.get("TableName"):
|
||||
tt.Name = stringattrs.get("TableName")
|
||||
if any(table.Name == tt.Name for table in self.toolTables):
|
||||
tt.Name = self.getNextToolTableName(tt.Name)
|
||||
|
||||
for key, attrs in PathUtil.keyValueIter(stringattrs['Tools']):
|
||||
tool = Path.Tool()
|
||||
tool.Name = str(attrs["name"])
|
||||
tool.ToolType = str(attrs["tooltype"])
|
||||
tool.Material = str(attrs["material"])
|
||||
tool.Diameter = float(attrs["diameter"])
|
||||
tool.LengthOffset = float(attrs["lengthOffset"])
|
||||
tool.FlatRadius = float(attrs["flatRadius"])
|
||||
tool.CornerRadius = float(attrs["cornerRadius"])
|
||||
tool.CuttingEdgeAngle = float(attrs["cuttingEdgeAngle"])
|
||||
tool.CuttingEdgeHeight = float(attrs["cuttingEdgeHeight"])
|
||||
tt.setTool(int(key), tool)
|
||||
for key, attrs in PathUtil.keyValueIter(stringattrs["Tools"]):
|
||||
tool = Path.Tool()
|
||||
tool.Name = str(attrs["name"])
|
||||
tool.ToolType = str(attrs["tooltype"])
|
||||
tool.Material = str(attrs["material"])
|
||||
tool.Diameter = float(attrs["diameter"])
|
||||
tool.LengthOffset = float(attrs["lengthOffset"])
|
||||
tool.FlatRadius = float(attrs["flatRadius"])
|
||||
tool.CornerRadius = float(attrs["cornerRadius"])
|
||||
tool.CuttingEdgeAngle = float(attrs["cuttingEdgeAngle"])
|
||||
tool.CuttingEdgeHeight = float(attrs["cuttingEdgeHeight"])
|
||||
tt.setTool(int(key), tool)
|
||||
|
||||
return tt
|
||||
else:
|
||||
PathLog.error(translate('PathToolLibraryManager', "Unsupported Path tooltable template version %s") % stringattrs.get('Version'))
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathToolLibraryManager",
|
||||
"Unsupported Path tooltable template version %s",
|
||||
)
|
||||
% stringattrs.get("Version")
|
||||
)
|
||||
return None
|
||||
|
||||
def loadToolTables(self):
|
||||
''' loads the tool tables from the stored data '''
|
||||
"""loads the tool tables from the stored data"""
|
||||
self.toolTables = []
|
||||
self.currentTableName = ''
|
||||
self.currentTableName = ""
|
||||
|
||||
def addTable(tt):
|
||||
if tt:
|
||||
self.toolTables.append(tt)
|
||||
else:
|
||||
PathLog.error(translate('PathToolLibraryManager', "Unsupported Path tooltable"))
|
||||
PathLog.error(
|
||||
translate("PathToolLibraryManager", "Unsupported Path tooltable")
|
||||
)
|
||||
|
||||
prefString = self.prefs.GetString(self.PreferenceMainLibraryJSON, "")
|
||||
|
||||
@@ -300,14 +317,14 @@ class ToolLibraryManager():
|
||||
self.currentTableName = self.toolTables[0].Name
|
||||
|
||||
def saveMainLibrary(self):
|
||||
'''Persists the permanent library to FreeCAD user preferences'''
|
||||
"""Persists the permanent library to FreeCAD user preferences"""
|
||||
tmpstring = json.dumps(self.templateAttrs())
|
||||
self.prefs.SetString(self.PreferenceMainLibraryJSON, tmpstring)
|
||||
self.loadToolTables()
|
||||
return True
|
||||
|
||||
def getJobList(self):
|
||||
'''Builds the list of all Tool Table lists'''
|
||||
"""Builds the list of all Tool Table lists"""
|
||||
tablelist = []
|
||||
|
||||
for o in FreeCAD.ActiveDocument.Objects:
|
||||
@@ -318,27 +335,29 @@ class ToolLibraryManager():
|
||||
return tablelist
|
||||
|
||||
def getTool(self, listname, toolnum):
|
||||
''' gets the tool object '''
|
||||
"""gets the tool object"""
|
||||
tt = self.getTableFromName(listname)
|
||||
return tt.getTool(toolnum)
|
||||
|
||||
def getTools(self, tablename):
|
||||
'''returns the tool data for a given table'''
|
||||
"""returns the tool data for a given table"""
|
||||
tooldata = []
|
||||
tableExists = any(table.Name == tablename for table in self.toolTables)
|
||||
tableExists = any(table.Name == tablename for table in self.toolTables)
|
||||
if tableExists:
|
||||
self.currentTableName = tablename
|
||||
else:
|
||||
return None
|
||||
|
||||
tt = self.getTableFromName(tablename)
|
||||
headers = ["","Tool Num.","Name","Tool Type","Diameter"]
|
||||
headers = ["", "Tool Num.", "Name", "Tool Type", "Diameter"]
|
||||
model = QtGui.QStandardItemModel()
|
||||
model.setHorizontalHeaderLabels(headers)
|
||||
|
||||
def unitconv(ivalue):
|
||||
val = FreeCAD.Units.Quantity(ivalue, FreeCAD.Units.Length)
|
||||
displayed_val = val.UserString #just the displayed value-not the internal one
|
||||
displayed_val = (
|
||||
val.UserString
|
||||
) # just the displayed value-not the internal one
|
||||
return displayed_val
|
||||
|
||||
if tt:
|
||||
@@ -348,10 +367,10 @@ class ToolLibraryManager():
|
||||
|
||||
itemcheck = QtGui.QStandardItem()
|
||||
itemcheck.setCheckable(True)
|
||||
itemNumber = QtGui.QStandardItem(str(number))
|
||||
itemName = QtGui.QStandardItem(t.Name)
|
||||
itemToolType = QtGui.QStandardItem(t.ToolType)
|
||||
itemDiameter = QtGui.QStandardItem(unitconv(t.Diameter))
|
||||
itemNumber = QtGui.QStandardItem(str(number))
|
||||
itemName = QtGui.QStandardItem(t.Name)
|
||||
itemToolType = QtGui.QStandardItem(t.ToolType)
|
||||
itemDiameter = QtGui.QStandardItem(unitconv(t.Diameter))
|
||||
|
||||
row = [itemcheck, itemNumber, itemName, itemToolType, itemDiameter]
|
||||
model.appendRow(row)
|
||||
@@ -367,9 +386,9 @@ class ToolLibraryManager():
|
||||
try:
|
||||
fileExtension = os.path.splitext(filename[0])[1].lower()
|
||||
xmlHandler = None
|
||||
if fileExtension == '.tooltable':
|
||||
if fileExtension == ".tooltable":
|
||||
xmlHandler = HeeksTooltableHandler()
|
||||
if fileExtension == '.xml':
|
||||
if fileExtension == ".xml":
|
||||
xmlHandler = FreeCADTooltableHandler()
|
||||
|
||||
if xmlHandler:
|
||||
@@ -405,41 +424,58 @@ class ToolLibraryManager():
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
print("could not parse file", e)
|
||||
|
||||
|
||||
def write(self, filename, listname):
|
||||
"exports the tooltable to a file"
|
||||
tt = self.getTableFromName(listname)
|
||||
if tt:
|
||||
try:
|
||||
|
||||
def openFileWithExtension(name, ext):
|
||||
fext = os.path.splitext(name)[1].lower()
|
||||
if fext != ext:
|
||||
name = "{}{}".format(name, ext)
|
||||
return (open(PathUtil.toUnicode(name), 'w'), name)
|
||||
return (open(PathUtil.toUnicode(name), "w"), name)
|
||||
|
||||
if filename[1] == self.TooltableTypeXML:
|
||||
fp,fname = openFileWithExtension(filename[0], '.xml')
|
||||
fp, fname = openFileWithExtension(filename[0], ".xml")
|
||||
fp.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||
fp.write(tt.Content)
|
||||
elif filename[1] == self.TooltableTypeLinuxCNC:
|
||||
fp,fname = openFileWithExtension(filename[0], '.tbl')
|
||||
fp, fname = openFileWithExtension(filename[0], ".tbl")
|
||||
for key in tt.Tools:
|
||||
t = tt.Tools[key]
|
||||
fp.write("T{0} P{0} Y{1} Z{2} A{3} B{4} C{5} U{6} V{7} W{8} D{9} I{10} J{11} Q{12} ;{13}\n".format(key,0,t.LengthOffset,0,0,0,0,0,0,t.Diameter,0,0,0,t.Name))
|
||||
fp.write(
|
||||
"T{0} P{0} Y{1} Z{2} A{3} B{4} C{5} U{6} V{7} W{8} D{9} I{10} J{11} Q{12} ;{13}\n".format(
|
||||
key,
|
||||
0,
|
||||
t.LengthOffset,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
t.Diameter,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
t.Name,
|
||||
)
|
||||
)
|
||||
else:
|
||||
fp,fname = openFileWithExtension(filename[0], '.json')
|
||||
fp, fname = openFileWithExtension(filename[0], ".json")
|
||||
json.dump(self.templateAttrs(), fp, sort_keys=True, indent=2)
|
||||
|
||||
fp.close()
|
||||
print("Written ", PathUtil.toUnicode(fname))
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
print("Could not write file:", e)
|
||||
|
||||
def addnew(self, listname, tool, position = None):
|
||||
def addnew(self, listname, tool, position=None):
|
||||
"adds a new tool at the end of the table"
|
||||
tt = self.getTableFromName(listname)
|
||||
if not tt:
|
||||
@@ -456,7 +492,7 @@ class ToolLibraryManager():
|
||||
return newID
|
||||
|
||||
def updateTool(self, listname, toolnum, tool):
|
||||
'''updates tool data'''
|
||||
"""updates tool data"""
|
||||
tt = self.getTableFromName(listname)
|
||||
tt.deleteTool(toolnum)
|
||||
tt.setTool(toolnum, tool)
|
||||
@@ -498,7 +534,7 @@ class ToolLibraryManager():
|
||||
return True, target
|
||||
|
||||
def duplicate(self, number, listname):
|
||||
''' duplicates the selected tool in the selected tool table '''
|
||||
"""duplicates the selected tool in the selected tool table"""
|
||||
tt = self.getTableFromName(listname)
|
||||
tool = tt.getTool(number).copy()
|
||||
tt.addTools(tool)
|
||||
@@ -510,7 +546,7 @@ class ToolLibraryManager():
|
||||
return True, newID
|
||||
|
||||
def moveToTable(self, number, listname):
|
||||
''' Moves the tool to selected tool table '''
|
||||
"""Moves the tool to selected tool table"""
|
||||
fromTable = self.getTableFromName(self.getCurrentTableName())
|
||||
toTable = self.getTableFromName(listname)
|
||||
tool = fromTable.getTool(number).copy()
|
||||
@@ -518,9 +554,9 @@ class ToolLibraryManager():
|
||||
fromTable.deleteTool(number)
|
||||
|
||||
def delete(self, number, listname):
|
||||
'''deletes a tool from the current list'''
|
||||
"""deletes a tool from the current list"""
|
||||
tt = self.getTableFromName(listname)
|
||||
tt.deleteTool(number)
|
||||
if listname == self.getCurrentTableName():
|
||||
self.saveMainLibrary()
|
||||
return True
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user