Merge pull request #19477 from Roy-043/Draft-Introduce-Edit-option-for-Draft-Clone-scaling
Draft: Introduce Edit option for Draft Clone scaling
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# ***************************************************************************
|
||||
# * (c) 2009 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
|
||||
# * Copyright (c) 2009 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
|
||||
# * Copyright (c) 2025 FreeCAD Project Association *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
@@ -36,7 +37,9 @@ import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
import Draft
|
||||
import Draft_rc
|
||||
from draftguitools import gui_trackers as trackers
|
||||
from draftutils import params
|
||||
from draftutils import utils
|
||||
from draftutils.translate import translate
|
||||
|
||||
# So the resource file doesn't trigger errors from code checkers (flake8)
|
||||
@@ -50,9 +53,11 @@ class ScaleTaskPanel:
|
||||
decimals = max(6, params.get_param("Decimals", path="Units"))
|
||||
self.sourceCmd = None
|
||||
self.form = QtWidgets.QWidget()
|
||||
self.form.setWindowTitle(translate("Draft", "Scale"))
|
||||
self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_Scale.svg"))
|
||||
layout = QtWidgets.QGridLayout(self.form)
|
||||
self.xLabel = QtWidgets.QLabel()
|
||||
self.xLabel.setText(translate("Draft", "X factor"))
|
||||
layout.addWidget(self.xLabel, 0, 0, 1, 1)
|
||||
self.xValue = QtWidgets.QDoubleSpinBox()
|
||||
self.xValue.setRange(-1000000.0, 1000000.0)
|
||||
@@ -60,6 +65,7 @@ class ScaleTaskPanel:
|
||||
self.xValue.setValue(1)
|
||||
layout.addWidget(self.xValue,0,1,1,1)
|
||||
self.yLabel = QtWidgets.QLabel()
|
||||
self.yLabel.setText(translate("Draft", "Y factor"))
|
||||
layout.addWidget(self.yLabel,1,0,1,1)
|
||||
self.yValue = QtWidgets.QDoubleSpinBox()
|
||||
self.yValue.setRange(-1000000.0, 1000000.0)
|
||||
@@ -67,6 +73,7 @@ class ScaleTaskPanel:
|
||||
self.yValue.setValue(1)
|
||||
layout.addWidget(self.yValue,1,1,1,1)
|
||||
self.zLabel = QtWidgets.QLabel()
|
||||
self.zLabel.setText(translate("Draft", "Z factor"))
|
||||
layout.addWidget(self.zLabel,2,0,1,1)
|
||||
self.zValue = QtWidgets.QDoubleSpinBox()
|
||||
self.zValue.setRange(-1000000.0, 1000000.0)
|
||||
@@ -74,36 +81,60 @@ class ScaleTaskPanel:
|
||||
self.zValue.setValue(1)
|
||||
layout.addWidget(self.zValue,2,1,1,1)
|
||||
self.lock = QtWidgets.QCheckBox()
|
||||
self.lock.setText(translate("Draft", "Uniform scaling"))
|
||||
self.lock.setChecked(params.get_param("ScaleUniform"))
|
||||
layout.addWidget(self.lock,3,0,1,2)
|
||||
self.relative = QtWidgets.QCheckBox()
|
||||
self.relative.setChecked(params.get_param("ScaleRelative"))
|
||||
layout.addWidget(self.relative,4,0,1,2)
|
||||
self.isCopy = QtWidgets.QCheckBox()
|
||||
self.isCopy.setChecked(params.get_param("ScaleCopy"))
|
||||
layout.addWidget(self.isCopy,5,0,1,2)
|
||||
self.isSubelementMode = QtWidgets.QCheckBox()
|
||||
self.isSubelementMode.setChecked(params.get_param("SubelementMode"))
|
||||
layout.addWidget(self.isSubelementMode,6,0,1,2)
|
||||
self.isClone = QtWidgets.QCheckBox()
|
||||
layout.addWidget(self.isClone,7,0,1,2)
|
||||
self.isClone.setChecked(params.get_param("ScaleClone"))
|
||||
self.pickrefButton = QtWidgets.QPushButton()
|
||||
layout.addWidget(self.pickrefButton,8,0,1,2)
|
||||
|
||||
QtCore.QObject.connect(self.xValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
|
||||
QtCore.QObject.connect(self.yValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
|
||||
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
|
||||
QtCore.QObject.connect(self.pickrefButton,QtCore.SIGNAL("clicked()"),self.pickRef)
|
||||
QtCore.QObject.connect(self.lock,QtCore.SIGNAL("toggled(bool)"),self.setLock)
|
||||
QtCore.QObject.connect(self.relative,QtCore.SIGNAL("toggled(bool)"),self.setRelative)
|
||||
QtCore.QObject.connect(self.isCopy,QtCore.SIGNAL("toggled(bool)"),self.setCopy)
|
||||
QtCore.QObject.connect(self.isSubelementMode,QtCore.SIGNAL("toggled(bool)"),self.setSubelementMode)
|
||||
QtCore.QObject.connect(self.isClone,QtCore.SIGNAL("toggled(bool)"),self.setClone)
|
||||
self.retranslateUi()
|
||||
|
||||
if self.__class__.__name__ != "ScaleTaskPanelEdit":
|
||||
self.relative = QtWidgets.QCheckBox()
|
||||
self.relative.setText(translate("Draft", "Working plane orientation"))
|
||||
self.relative.setChecked(params.get_param("ScaleRelative"))
|
||||
layout.addWidget(self.relative,4,0,1,2)
|
||||
self.isCopy = QtWidgets.QCheckBox()
|
||||
self.isCopy.setText(translate("Draft", "Copy"))
|
||||
self.isCopy.setChecked(params.get_param("ScaleCopy"))
|
||||
layout.addWidget(self.isCopy,5,0,1,2)
|
||||
self.isSubelementMode = QtWidgets.QCheckBox()
|
||||
self.isSubelementMode.setText(translate("Draft", "Modify subelements"))
|
||||
self.isSubelementMode.setChecked(params.get_param("SubelementMode"))
|
||||
layout.addWidget(self.isSubelementMode,6,0,1,2)
|
||||
self.isClone = QtWidgets.QCheckBox()
|
||||
self.isClone.setText(translate("Draft", "Create a clone"))
|
||||
self.isClone.setChecked(params.get_param("ScaleClone"))
|
||||
layout.addWidget(self.isClone,7,0,1,2)
|
||||
self.pickrefButton = QtWidgets.QPushButton()
|
||||
self.pickrefButton.setText(translate("Draft", "Pick from/to points"))
|
||||
layout.addWidget(self.pickrefButton,8,0,1,2)
|
||||
|
||||
QtCore.QObject.connect(self.relative,QtCore.SIGNAL("toggled(bool)"),self.setRelative)
|
||||
QtCore.QObject.connect(self.isCopy,QtCore.SIGNAL("toggled(bool)"),self.setCopy)
|
||||
QtCore.QObject.connect(self.isSubelementMode,QtCore.SIGNAL("toggled(bool)"),self.setSubelementMode)
|
||||
QtCore.QObject.connect(self.isClone,QtCore.SIGNAL("toggled(bool)"),self.setClone)
|
||||
QtCore.QObject.connect(self.pickrefButton,QtCore.SIGNAL("clicked()"),self.pickRef)
|
||||
|
||||
def setValue(self, val=None):
|
||||
"""Set the value of the scale factors."""
|
||||
if self.lock.isChecked():
|
||||
if not self.xValue.hasFocus():
|
||||
self.xValue.setValue(val)
|
||||
if not self.yValue.hasFocus():
|
||||
self.yValue.setValue(val)
|
||||
if not self.zValue.hasFocus():
|
||||
self.zValue.setValue(val)
|
||||
if self.sourceCmd:
|
||||
# self.sourceCmd is always None for ScaleTaskPanelEdit
|
||||
self.sourceCmd.scale_ghosts(self.xValue.value(),self.yValue.value(),self.zValue.value(),self.relative.isChecked())
|
||||
|
||||
def setLock(self, state):
|
||||
"""Set the uniform scaling."""
|
||||
params.set_param("ScaleUniform", state)
|
||||
if self.sourceCmd:
|
||||
# self.sourceCmd is always None for ScaleTaskPanelEdit
|
||||
params.set_param("ScaleUniform", state)
|
||||
if state:
|
||||
val = self.xValue.value()
|
||||
self.yValue.setValue(val)
|
||||
@@ -116,12 +147,13 @@ class ScaleTaskPanel:
|
||||
self.sourceCmd.scale_ghosts(self.xValue.value(),self.yValue.value(),self.zValue.value(),self.relative.isChecked())
|
||||
|
||||
def setCopy(self, state):
|
||||
"""Set the scale and copy option."""
|
||||
"""Set the copy option."""
|
||||
params.set_param("ScaleCopy", state)
|
||||
if state and self.isClone.isChecked():
|
||||
self.isClone.setChecked(False)
|
||||
|
||||
def setSubelementMode(self, state):
|
||||
"""Set the subelement option."""
|
||||
params.set_param("SubelementMode", state)
|
||||
if state and self.isClone.isChecked():
|
||||
self.isClone.setChecked(False)
|
||||
@@ -130,38 +162,13 @@ class ScaleTaskPanel:
|
||||
self.sourceCmd.scale_ghosts(self.xValue.value(),self.yValue.value(),self.zValue.value(),self.relative.isChecked())
|
||||
|
||||
def setClone(self, state):
|
||||
"""Set the clone and scale option."""
|
||||
"""Set the clone option."""
|
||||
params.set_param("ScaleClone", state)
|
||||
if state and self.isCopy.isChecked():
|
||||
self.isCopy.setChecked(False)
|
||||
if state and self.isSubelementMode.isChecked():
|
||||
self.isSubelementMode.setChecked(False)
|
||||
|
||||
def setValue(self, val=None):
|
||||
"""Set the value of the points."""
|
||||
if self.lock.isChecked():
|
||||
if not self.xValue.hasFocus():
|
||||
self.xValue.setValue(val)
|
||||
if not self.yValue.hasFocus():
|
||||
self.yValue.setValue(val)
|
||||
if not self.zValue.hasFocus():
|
||||
self.zValue.setValue(val)
|
||||
if self.sourceCmd:
|
||||
self.sourceCmd.scale_ghosts(self.xValue.value(),self.yValue.value(),self.zValue.value(),self.relative.isChecked())
|
||||
|
||||
def retranslateUi(self, widget=None):
|
||||
"""Translate the various widgets"""
|
||||
self.form.setWindowTitle(translate("Draft", "Scale"))
|
||||
self.xLabel.setText(translate("Draft", "X factor"))
|
||||
self.yLabel.setText(translate("Draft", "Y factor"))
|
||||
self.zLabel.setText(translate("Draft", "Z factor"))
|
||||
self.lock.setText(translate("Draft", "Uniform scaling"))
|
||||
self.relative.setText(translate("Draft", "Working plane orientation"))
|
||||
self.isCopy.setText(translate("Draft", "Copy"))
|
||||
self.isSubelementMode.setText(translate("Draft", "Modify subelements"))
|
||||
self.pickrefButton.setText(translate("Draft", "Pick from/to points"))
|
||||
self.isClone.setText(translate("Draft", "Create a clone"))
|
||||
|
||||
def pickRef(self):
|
||||
"""Pick a reference point from the calling class."""
|
||||
if self.sourceCmd:
|
||||
@@ -181,4 +188,78 @@ class ScaleTaskPanel:
|
||||
Gui.ActiveDocument.resetEdit()
|
||||
return True
|
||||
|
||||
|
||||
class ScaleTaskPanelEdit(ScaleTaskPanel):
|
||||
"""The task panel to edit the scale of Draft Clones."""
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__()
|
||||
self.ghost = None
|
||||
self.selection = Gui.Selection.getSelectionEx("", 0)
|
||||
self.obj = obj
|
||||
self.obj_x, self.obj_y, self.obj_z = self.obj.Scale
|
||||
self.form.setWindowTitle(translate("Draft", "Edit scale"))
|
||||
self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_Clone.svg"))
|
||||
self.xValue.setValue(self.obj_x)
|
||||
self.yValue.setValue(self.obj_y)
|
||||
self.zValue.setValue(self.obj_z)
|
||||
self.lock.setChecked(self.obj_x == self.obj_y == self.obj_z)
|
||||
|
||||
def setValue(self, val=None):
|
||||
"""Set the value of the scale factors."""
|
||||
super().setValue(val)
|
||||
self.scale_ghost(self.xValue.value(), self.yValue.value(), self.zValue.value())
|
||||
|
||||
def scale_ghost(self, x, y, z):
|
||||
"""Scale the preview of the object."""
|
||||
x = x / (self.obj_x if abs(self.obj_x) > 1e-7 else 1e-7)
|
||||
y = y / (self.obj_y if abs(self.obj_y) > 1e-7 else 1e-7)
|
||||
z = z / (self.obj_z if abs(self.obj_z) > 1e-7 else 1e-7)
|
||||
|
||||
if self.ghost is None:
|
||||
self.set_ghost()
|
||||
|
||||
mtx_scale = App.Matrix()
|
||||
mtx_scale.scale(x, y, z)
|
||||
mtx = self.global_place.Matrix * mtx_scale
|
||||
mtx = mtx * self.global_place.Matrix.inverse()
|
||||
|
||||
delta = self.global_place.inverse().Rotation.multVec(self.global_place.Base)
|
||||
delta = -App.Vector(delta.x*x, delta.y*y, delta.z*z)
|
||||
delta = self.global_place.multVec(delta)
|
||||
|
||||
self.ghost.setMatrix(mtx)
|
||||
self.ghost.move(delta)
|
||||
# self.ghost.flip_normals(x * y * z < 0) # Does not work properly for Draft_Circles for example.
|
||||
self.ghost.on()
|
||||
|
||||
def set_ghost(self):
|
||||
"""Set the ghost to display."""
|
||||
if self.ghost is not None:
|
||||
self.ghost.remove()
|
||||
objs, places, _ = utils._modifiers_process_selection(self.selection, copy=False, scale=True)
|
||||
self.ghost = trackers.ghostTracker(objs, parent_places=places)
|
||||
self.global_place = places[0] * self.obj.Placement
|
||||
|
||||
def accept(self):
|
||||
"""Execute when clicking the OK button."""
|
||||
self.obj.Scale = (self.xValue.value(), self.yValue.value(), self.zValue.value())
|
||||
App.ActiveDocument.recompute()
|
||||
if self.ghost is not None:
|
||||
self.ghost.finalize()
|
||||
Gui.ActiveDocument.resetEdit()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
"""Execute when clicking the Cancel button."""
|
||||
if self.ghost is not None:
|
||||
self.ghost.finalize()
|
||||
Gui.ActiveDocument.resetEdit()
|
||||
return True
|
||||
|
||||
def finish(self):
|
||||
"""Called by unsetEdit in view_clone.py."""
|
||||
Gui.Control.closeDialog()
|
||||
return None
|
||||
|
||||
## @}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
|
||||
# * Copyright (c) 2020 FreeCAD Developers *
|
||||
# * Copyright (c) 2025 FreeCAD Project Association *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
@@ -27,7 +28,12 @@
|
||||
|
||||
## \addtogroup draftviewproviders
|
||||
# @{
|
||||
from PySide import QtCore
|
||||
from PySide import QtGui
|
||||
|
||||
import FreeCADGui as Gui
|
||||
from drafttaskpanels import task_scale
|
||||
from draftutils.translate import translate
|
||||
|
||||
class ViewProviderClone:
|
||||
"""a view provider that displays a Clone icon instead of a Draft icon"""
|
||||
@@ -35,9 +41,38 @@ class ViewProviderClone:
|
||||
def __init__(self,vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
return
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/Draft_Clone.svg"
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
if mode != 0:
|
||||
return None
|
||||
|
||||
self.task = task_scale.ScaleTaskPanelEdit(self.Object)
|
||||
Gui.Control.showDialog(self.task)
|
||||
return True
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
if mode != 0:
|
||||
return None
|
||||
|
||||
self.task.finish()
|
||||
return True
|
||||
|
||||
def setupContextMenu(self, vobj, menu):
|
||||
action_edit = QtGui.QAction(translate("draft", "Edit"), menu)
|
||||
QtCore.QObject.connect(action_edit,
|
||||
QtCore.SIGNAL("triggered()"),
|
||||
self.edit)
|
||||
menu.addAction(action_edit)
|
||||
|
||||
def edit(self):
|
||||
Gui.ActiveDocument.setEdit(self.Object, 0)
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user