diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt
index f9dfdd8348..ef552e7414 100644
--- a/src/Mod/Draft/CMakeLists.txt
+++ b/src/Mod/Draft/CMakeLists.txt
@@ -49,22 +49,26 @@ SET(Draft_tests
SET(Draft_objects
draftobjects/__init__.py
+ draftobjects/circulararray.py
draftobjects/polararray.py
)
SET(Draft_view_providers
draftviewproviders/__init__.py
+ draftviewproviders/view_circulararray.py
draftviewproviders/view_polararray.py
)
SET(Draft_GUI_tools
draftguitools/__init__.py
draftguitools/gui_base.py
+ draftguitools/gui_circulararray.py
draftguitools/gui_polararray.py
)
SET(Draft_task_panels
drafttaskpanels/__init__.py
+ drafttaskpanels/task_circulararray.py
drafttaskpanels/task_polararray.py
)
diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py
index a93170e05a..4ed44d0fbe 100644
--- a/src/Mod/Draft/InitGui.py
+++ b/src/Mod/Draft/InitGui.py
@@ -65,6 +65,7 @@ class DraftWorkbench(Workbench):
try:
import os, Draft_rc, DraftTools, DraftGui, DraftFillet
from DraftTools import translate
+ from draftguitools import gui_circulararray
from draftguitools import gui_polararray
FreeCADGui.addLanguagePath(":/translations")
FreeCADGui.addIconPath(":/icons")
@@ -86,7 +87,7 @@ class DraftWorkbench(Workbench):
"Draft_WireToBSpline", "Draft_AddPoint",
"Draft_DelPoint", "Draft_Shape2DView",
"Draft_Draft2Sketch", "Draft_Array", "Draft_LinkArray",
- "Draft_PolarArray",
+ "Draft_PolarArray", "Draft_CircularArray",
"Draft_PathArray", "Draft_PathLinkArray", "Draft_PointArray", "Draft_Clone",
"Draft_Drawing", "Draft_Mirror", "Draft_Stretch"]
self.treecmdList = ["Draft_ApplyStyle", "Draft_ToggleDisplayMode",
diff --git a/src/Mod/Draft/Resources/Draft.qrc b/src/Mod/Draft/Resources/Draft.qrc
index faf759e28d..4a1aaa5696 100644
--- a/src/Mod/Draft/Resources/Draft.qrc
+++ b/src/Mod/Draft/Resources/Draft.qrc
@@ -16,6 +16,7 @@
icons/Draft_BezTanNode.svgicons/Draft_BSpline.svgicons/Draft_Circle.svg
+ icons/Draft_CircularArray.svgicons/Draft_Clone.svgicons/Draft_Construction.svgicons/Draft_CubicBezCurve.svg
@@ -149,6 +150,7 @@
ui/preferences-dxf.uiui/preferences-oca.uiui/preferences-svg.ui
+ ui/TaskPanel_CircularArray.uiui/TaskPanel_PolarArray.uiui/TaskSelectPlane.uiui/TaskShapeString.ui
diff --git a/src/Mod/Draft/Resources/icons/Draft_CircularArray.svg b/src/Mod/Draft/Resources/icons/Draft_CircularArray.svg
new file mode 100644
index 0000000000..26fbea8978
--- /dev/null
+++ b/src/Mod/Draft/Resources/icons/Draft_CircularArray.svg
@@ -0,0 +1,667 @@
+
+
+
+
diff --git a/src/Mod/Draft/Resources/ui/TaskPanel_CircularArray.ui b/src/Mod/Draft/Resources/ui/TaskPanel_CircularArray.ui
new file mode 100644
index 0000000000..ece65b12ee
--- /dev/null
+++ b/src/Mod/Draft/Resources/ui/TaskPanel_CircularArray.ui
@@ -0,0 +1,292 @@
+
+
+ DraftCircularArrayTaskPanel
+
+
+
+ 0
+ 0
+ 445
+ 488
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 250
+ 0
+
+
+
+ Circular array
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+ The coordinates of the point through which the axis of rotation passes.
+
+
+ Center of rotation
+
+
+
+
+
+
+
+ Z
+
+
+
+
+
+
+ X
+
+
+
+
+
+
+ Y
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ Reset the coordinates of the center of rotation
+
+
+ Reset point
+
+
+
+
+
+
+
+
+
+
+
+ If checked, the resulting objects in the array will be fused if they touch each other
+
+
+ Fuse
+
+
+
+
+
+
+ If checked, the resulting objects in the array will be Links instead of simple copies
+
+
+ Use Links
+
+
+
+
+
+
+
+
+
+
+ Distance from one element in the array to the next element in the same layer. It cannot be zero.
+
+
+ Tangential distance
+
+
+
+
+
+
+ Distance from one element in the array to the next element in the same layer. It cannot be zero.
+
+
+
+
+
+ 100.000000000000000
+
+
+
+
+
+
+ Distance from the center of the array to the outer layers
+
+
+ Radial distance
+
+
+
+
+
+
+ Distance from the center of the array to the outer layers
+
+
+
+
+
+ 200.000000000000000
+
+
+
+
+
+
+ Number that controls how the objects will be distributed
+
+
+ 1
+
+
+
+
+
+
+ Number of circular arrays to create, including a copy of the original object. It must be at least 2.
+
+
+ 3
+
+
+
+
+
+
+ Number of circular arrays to create, including a copy of the original object. It must be at least 2.
+
+
+ Number of circular layers
+
+
+
+
+
+
+ Number that controls how the objects will be distributed
+
+
+ Symmetry
+
+
+
+
+
+
+
+
+ (Placeholder for the icon)
+
+
+
+
+
+
+
+
+
+
+ Gui::InputField
+ QLineEdit
+ Gui/InputField.h
+
+
+ Gui::QuantitySpinBox
+ QWidget
+ Gui/QuantitySpinBox.h
+
+
+
+ spinbox_r_distance
+ spinbox_tan_distance
+ spinbox_number
+ spinbox_symmetry
+ input_c_x
+ input_c_y
+ input_c_z
+ button_reset
+ checkbox_fuse
+ checkbox_link
+
+
+
+
diff --git a/src/Mod/Draft/draftguitools/gui_circulararray.py b/src/Mod/Draft/draftguitools/gui_circulararray.py
new file mode 100644
index 0000000000..e75139f35e
--- /dev/null
+++ b/src/Mod/Draft/draftguitools/gui_circulararray.py
@@ -0,0 +1,155 @@
+"""This module provides the Draft CircularArray tool.
+"""
+## @package gui_circulararray
+# \ingroup DRAFT
+# \brief This module provides the Draft CircularArray tool.
+
+# ***************************************************************************
+# * (c) 2019 Eliud Cabrera Castillo *
+# * *
+# * This file is part of the FreeCAD CAx development system. *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * FreeCAD is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with FreeCAD; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD as App
+import FreeCADGui as Gui
+import Draft
+import DraftGui
+import Draft_rc
+from . import gui_base
+from drafttaskpanels import task_circulararray
+
+
+if App.GuiUp:
+ from PySide.QtCore import QT_TRANSLATE_NOOP
+ # import DraftTools
+ from DraftGui import translate
+ # from DraftGui import displayExternal
+ from pivy import coin
+else:
+ def QT_TRANSLATE_NOOP(context, text):
+ return text
+
+ def translate(context, text):
+ return text
+
+
+def _tr(text):
+ """Function to translate with the context set"""
+ return translate("Draft", text)
+
+
+# So the resource file doesn't trigger errors from code checkers (flake8)
+True if Draft_rc.__name__ else False
+
+
+class GuiCommandCircularArray(gui_base.GuiCommandBase):
+ """Gui command for the CircularArray tool"""
+
+ def __init__(self):
+ super().__init__()
+ self.command_name = "CircularArray"
+ self.location = None
+ self.mouse_event = None
+ self.view = None
+ self.callback_move = None
+ self.callback_click = None
+ self.ui = None
+ self.point = App.Vector()
+
+ def GetResources(self):
+ _msg = ("Creates copies of a selected object, "
+ "and places the copies in a circular pattern.\n"
+ "The properties of the array can be further modified after "
+ "the new object is created, including turning it into "
+ "a different type of array.")
+ d = {'Pixmap': 'Draft_CircularArray',
+ 'MenuText': QT_TRANSLATE_NOOP("Draft", "Circular array"),
+ 'ToolTip': QT_TRANSLATE_NOOP("Draft", _msg)}
+ return d
+
+ def Activated(self):
+ """This is called when the command is executed.
+
+ We add callbacks that connect the 3D view with
+ the widgets of the task panel.
+ """
+ self.location = coin.SoLocation2Event.getClassTypeId()
+ self.mouse_event = coin.SoMouseButtonEvent.getClassTypeId()
+ self.view = Draft.get3DView()
+ self.callback_move = \
+ self.view.addEventCallbackPivy(self.location, self.move)
+ self.callback_click = \
+ self.view.addEventCallbackPivy(self.mouse_event, self.click)
+
+ self.ui = task_circulararray.TaskPanelCircularArray()
+ # The calling class (this one) is saved in the object
+ # of the interface, to be able to call a function from within it.
+ self.ui.source_command = self
+ # Gui.Control.showDialog(self.ui)
+ DraftGui.todo.delay(Gui.Control.showDialog, self.ui)
+
+ def move(self, event_cb):
+ """This is a callback for when the mouse pointer moves in the 3D view.
+
+ It should automatically update the coordinates in the widgets
+ of the task panel.
+ """
+ event = event_cb.getEvent()
+ mousepos = event.getPosition().getValue()
+ ctrl = event.wasCtrlDown()
+ self.point = Gui.Snapper.snap(mousepos, active=ctrl)
+ if self.ui:
+ self.ui.display_point(self.point)
+
+ def click(self, event_cb=None):
+ """This is a callback for when the mouse pointer clicks on the 3D view.
+
+ It should act as if the Enter key was pressed, or the OK button
+ was pressed in the task panel.
+ """
+ if event_cb:
+ event = event_cb.getEvent()
+ if (event.getState() != coin.SoMouseButtonEvent.DOWN
+ or event.getButton() != coin.SoMouseButtonEvent.BUTTON1):
+ return
+ if self.ui and self.point:
+ # The accept function of the interface
+ # should call the completed function
+ # of the calling class (this one).
+ self.ui.accept()
+
+ def completed(self):
+ """This is called when the command is terminated.
+
+ We should remove the callbacks that were added to the 3D view
+ and then close the task panel.
+ """
+ self.view.removeEventCallbackPivy(self.location,
+ self.callback_move)
+ self.view.removeEventCallbackPivy(self.mouse_event,
+ self.callback_click)
+ if Gui.Control.activeDialog():
+ Gui.Snapper.off()
+ Gui.Control.closeDialog()
+ super().finish()
+
+
+if App.GuiUp:
+ Gui.addCommand('Draft_CircularArray', GuiCommandCircularArray())
diff --git a/src/Mod/Draft/draftobjects/circulararray.py b/src/Mod/Draft/draftobjects/circulararray.py
new file mode 100644
index 0000000000..07982d16e4
--- /dev/null
+++ b/src/Mod/Draft/draftobjects/circulararray.py
@@ -0,0 +1,45 @@
+"""This module provides the object code for Draft CircularArray.
+"""
+## @package circulararray
+# \ingroup DRAFT
+# \brief This module provides the object code for Draft CircularArray.
+
+# ***************************************************************************
+# * (c) 2019 Eliud Cabrera Castillo *
+# * *
+# * This file is part of the FreeCAD CAx development system. *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * FreeCAD is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with FreeCAD; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD as App
+import Draft
+
+
+def make_circular_array(obj,
+ r_distance=100, tan_distance=100,
+ axis=App.Vector(0, 0, 1), center=App.Vector(0, 0, 0),
+ number=2, symmetry=1,
+ use_link=False):
+ """Create a circular array from the given object.
+ """
+ obj = Draft.makeArray(obj,
+ arg1=r_distance, arg2=tan_distance,
+ arg3=axis, arg4=center, arg5=number, arg6=symmetry,
+ useLink=use_link)
+ return obj
diff --git a/src/Mod/Draft/drafttaskpanels/task_circulararray.py b/src/Mod/Draft/drafttaskpanels/task_circulararray.py
new file mode 100644
index 0000000000..a0405bbf52
--- /dev/null
+++ b/src/Mod/Draft/drafttaskpanels/task_circulararray.py
@@ -0,0 +1,402 @@
+"""This module provides the task panel for the Draft CircularArray tool.
+"""
+## @package task_circulararray
+# \ingroup DRAFT
+# \brief This module provides the task panel code for the CircularArray tool.
+
+# ***************************************************************************
+# * (c) 2019 Eliud Cabrera Castillo *
+# * *
+# * This file is part of the FreeCAD CAx development system. *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * FreeCAD is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with FreeCAD; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD as App
+import FreeCADGui as Gui
+# import Draft
+import Draft_rc
+import DraftVecUtils
+
+import PySide.QtCore as QtCore
+import PySide.QtGui as QtGui
+from PySide.QtCore import QT_TRANSLATE_NOOP
+# import DraftTools
+from DraftGui import translate
+# from DraftGui import displayExternal
+
+_Quantity = App.Units.Quantity
+
+
+def _Msg(text, end="\n"):
+ """Print message with newline"""
+ App.Console.PrintMessage(text + end)
+
+
+def _Wrn(text, end="\n"):
+ """Print warning with newline"""
+ App.Console.PrintWarning(text + end)
+
+
+def _tr(text):
+ """Function to translate with the context set"""
+ return translate("Draft", text)
+
+
+# So the resource file doesn't trigger errors from code checkers (flake8)
+True if Draft_rc.__name__ else False
+
+
+class TaskPanelCircularArray:
+ """TaskPanel code for the CircularArray command.
+
+ The names of the widgets are defined in the `.ui` file.
+ In this class all those widgets are automatically created
+ under the name `self.form.`
+
+ The `.ui` file may use special FreeCAD widgets such as
+ `Gui::InputField` (based on `QLineEdit`) and
+ `Gui::QuantitySpinBox` (based on `QAbstractSpinBox`).
+ See the Doxygen documentation of the corresponding files in `src/Gui/`,
+ for example, `InputField.h` and `QuantitySpinBox.h`.
+ """
+
+ def __init__(self):
+ ui_file = ":/ui/TaskPanel_CircularArray.ui"
+ self.form = Gui.PySideUic.loadUi(ui_file)
+ self.name = self.form.windowTitle()
+
+ icon_name = "Draft_CircularArray"
+ svg = ":/icons/" + icon_name
+ pix = QtGui.QPixmap(svg)
+ icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon(svg))
+ self.form.setWindowIcon(icon)
+ self.form.label_icon.setPixmap(pix.scaled(32, 32))
+
+ start_distance = _Quantity(1000.0, App.Units.Length)
+ distance_unit = start_distance.getUserPreferred()[2]
+ self.form.spinbox_r_distance.setProperty('rawValue',
+ 2 * start_distance.Value)
+ self.form.spinbox_r_distance.setProperty('unit', distance_unit)
+ self.form.spinbox_tan_distance.setProperty('rawValue',
+ start_distance.Value)
+ self.form.spinbox_tan_distance.setProperty('unit', distance_unit)
+
+ self.r_distance = 2 * start_distance.Value
+ self.tan_distance = start_distance.Value
+
+ self.form.spinbox_number.setValue(3)
+ self.form.spinbox_symmetry.setValue(1)
+
+ self.number = self.form.spinbox_number.value()
+ self.symmetry = self.form.spinbox_symmetry.value()
+
+ self.axis = App.Vector(0, 0, 1)
+
+ start_point = _Quantity(0.0, App.Units.Length)
+ length_unit = start_point.getUserPreferred()[2]
+ self.form.input_c_x.setProperty('rawValue', start_point.Value)
+ self.form.input_c_x.setProperty('unit', length_unit)
+ self.form.input_c_y.setProperty('rawValue', start_point.Value)
+ self.form.input_c_y.setProperty('unit', length_unit)
+ self.form.input_c_z.setProperty('rawValue', start_point.Value)
+ self.form.input_c_z.setProperty('unit', length_unit)
+ self.valid_input = True
+
+ self.c_x_str = ""
+ self.c_y_str = ""
+ self.c_z_str = ""
+ self.center = App.Vector(0, 0, 0)
+
+ # Old style for Qt4
+ # QtCore.QObject.connect(self.form.button_reset,
+ # QtCore.SIGNAL("clicked()"),
+ # self.reset_point)
+ # New style for Qt5
+ self.form.button_reset.clicked.connect(self.reset_point)
+
+ # The mask is not used at the moment, but could be used in the future
+ # by a callback to restrict the coordinates of the pointer.
+ self.mask = ""
+
+ # When the checkbox changes, change the fuse value
+ self.fuse = False
+ QtCore.QObject.connect(self.form.checkbox_fuse,
+ QtCore.SIGNAL("stateChanged(int)"),
+ self.set_fuse)
+
+ self.use_link = False
+ QtCore.QObject.connect(self.form.checkbox_link,
+ QtCore.SIGNAL("stateChanged(int)"),
+ self.set_link)
+
+ def accept(self):
+ """Function that executes when clicking the OK button"""
+ selection = Gui.Selection.getSelection()
+ self.number = self.form.spinbox_number.value()
+
+ tan_d_str = self.form.spinbox_tan_distance.text()
+ self.tan_distance = _Quantity(tan_d_str).Value
+ self.valid_input = self.validate_input(selection,
+ self.number,
+ self.tan_distance)
+ if self.valid_input:
+ self.create_object(selection)
+ self.print_messages(selection)
+ self.finish()
+
+ def validate_input(self, selection, number, tan_distance):
+ """Check that the input is valid"""
+ if not selection:
+ _Wrn(_tr("At least one element must be selected"))
+ return False
+ if number < 2:
+ _Wrn(_tr("Number of elements must be at least 2"))
+ return False
+ # Todo: each of the elements of the selection could be tested,
+ # not only the first one.
+ if selection[0].isDerivedFrom("App::FeaturePython"):
+ _Wrn(_tr("Selection is not suitable for array"))
+ _Wrn(_tr("Object:") + " {}".format(selection[0].Label))
+ return False
+ if tan_distance == 0:
+ _Wrn(_tr("Tangential distance cannot be zero"))
+ return False
+ return True
+
+ def create_object(self, selection):
+ """Create the actual object"""
+ r_d_str = self.form.spinbox_r_distance.text()
+ tan_d_str = self.form.spinbox_tan_distance.text()
+ self.r_distance = _Quantity(r_d_str).Value
+ self.tan_distance = _Quantity(tan_d_str).Value
+
+ self.number = self.form.spinbox_number.value()
+ self.symmetry = self.form.spinbox_symmetry.value()
+ self.center = self.set_point()
+
+ if len(selection) == 1:
+ sel_obj = selection[0]
+ else:
+ # This can be changed so a compound of multiple
+ # selected objects is produced
+ sel_obj = selection[0]
+
+ self.fuse = self.form.checkbox_fuse.isChecked()
+ self.use_link = self.form.checkbox_link.isChecked()
+
+ # This creates the object immediately
+ # obj = Draft.makeArray(sel_obj,
+ # self.center, self.angle, self.number)
+ # if obj:
+ # obj.Fuse = self.fuse
+
+ # Instead, we build the commands to execute through the parent
+ # of this class, the GuiCommand.
+ # This is needed to schedule geometry manipulation
+ # that would crash Coin3D if done in the event callback.
+ _cmd = "obj = Draft.makeArray("
+ _cmd += "FreeCAD.ActiveDocument." + sel_obj.Name + ", "
+ _cmd += "arg1=" + str(self.r_distance) + ", "
+ _cmd += "arg2=" + str(self.tan_distance) + ", "
+ _cmd += "arg3=" + DraftVecUtils.toString(self.axis) + ", "
+ _cmd += "arg4=" + DraftVecUtils.toString(self.center) + ", "
+ _cmd += "arg5=" + str(self.number) + ", "
+ _cmd += "arg6=" + str(self.symmetry) + ", "
+ _cmd += "useLink=" + str(self.use_link) + ")"
+
+ _cmd_list = ["FreeCADGui.addModule('Draft')",
+ _cmd,
+ "obj.Fuse = " + str(self.fuse),
+ "Draft.autogroup(obj)",
+ "FreeCAD.ActiveDocument.recompute()"]
+ self.source_command.commit("Circular array", _cmd_list)
+
+ def set_point(self):
+ """Assign the values to the center"""
+ self.c_x_str = self.form.input_c_x.text()
+ self.c_y_str = self.form.input_c_y.text()
+ self.c_z_str = self.form.input_c_z.text()
+ center = App.Vector(_Quantity(self.c_x_str).Value,
+ _Quantity(self.c_y_str).Value,
+ _Quantity(self.c_z_str).Value)
+ return center
+
+ def reset_point(self):
+ """Reset the point to the original distance"""
+ self.form.input_c_x.setProperty('rawValue', 0)
+ self.form.input_c_y.setProperty('rawValue', 0)
+ self.form.input_c_z.setProperty('rawValue', 0)
+
+ self.center = self.set_point()
+ _Msg(_tr("Center reset:")
+ + " ({0}, {1}, {2})".format(self.center.x,
+ self.center.y,
+ self.center.z))
+
+ def print_fuse_state(self):
+ """Print the state translated"""
+ if self.fuse:
+ translated_state = QT_TRANSLATE_NOOP("Draft", "True")
+ else:
+ translated_state = QT_TRANSLATE_NOOP("Draft", "False")
+ _Msg(_tr("Fuse:") + " {}".format(translated_state))
+
+ def set_fuse(self):
+ """This function is called when the fuse checkbox changes"""
+ self.fuse = self.form.checkbox_fuse.isChecked()
+ self.print_fuse_state()
+
+ def print_link_state(self):
+ """Print the state translated"""
+ if self.use_link:
+ translated_state = QT_TRANSLATE_NOOP("Draft", "True")
+ else:
+ translated_state = QT_TRANSLATE_NOOP("Draft", "False")
+ _Msg(_tr("Use Link object:") + " {}".format(translated_state))
+
+ def set_link(self):
+ """This function is called when the fuse checkbox changes"""
+ self.use_link = self.form.checkbox_link.isChecked()
+ self.print_link_state()
+
+ def print_messages(self, selection):
+ """Print messages about the operation"""
+ if len(selection) == 1:
+ sel_obj = selection[0]
+ else:
+ # This can be changed so a compound of multiple
+ # selected objects is produced
+ sel_obj = selection[0]
+ _Msg("{}".format(16*"-"))
+ _Msg("{}".format(self.name))
+ _Msg(_tr("Object:") + " {}".format(sel_obj.Label))
+ _Msg(_tr("Radial distance:") + " {}".format(self.r_distance))
+ _Msg(_tr("Tangential distance:") + " {}".format(self.tan_distance))
+ _Msg(_tr("Number of circular layers:") + " {}".format(self.number))
+ _Msg(_tr("Symmetry parameter:") + " {}".format(self.symmetry))
+ _Msg(_tr("Center of rotation:")
+ + " ({0}, {1}, {2})".format(self.center.x,
+ self.center.y,
+ self.center.z))
+ self.print_fuse_state()
+ self.print_link_state()
+
+ def display_point(self, point=None, plane=None, mask=None):
+ """Displays the coordinates in the x, y, and z widgets.
+
+ This function should be used in a Coin callback so that
+ the coordinate values are automatically updated when the
+ mouse pointer moves.
+ This was copied from `DraftGui.py` but needs to be improved
+ for this particular command.
+
+ point :
+ is a vector that arrives by the callback.
+ plane :
+ is a `WorkingPlane` instance, for example,
+ `App.DraftWorkingPlane`. It is not used at the moment,
+ but could be used to set up the grid.
+ mask :
+ is a string that specifies which coordinate is being
+ edited. It is used to restrict edition of a single coordinate.
+ It is not used at the moment but could be used with a callback.
+ """
+ # Get the coordinates to display
+ dp = None
+ if point:
+ dp = point
+
+ # Set the widgets to the value of the mouse pointer.
+ #
+ # setProperty() is used if the widget is a FreeCAD widget like
+ # Gui::InputField or Gui::QuantitySpinBox, which are based on
+ # QLineEdit and QAbstractSpinBox.
+ #
+ # setText() is used to set the text inside the widget, this may be
+ # useful in some circumstances.
+ #
+ # The mask allows editing only one field, that is, only one coordinate.
+ # sbx = self.form.spinbox_c_x
+ # sby = self.form.spinbox_c_y
+ # sbz = self.form.spinbox_c_z
+ if dp:
+ if self.mask in ('y', 'z'):
+ # sbx.setText(displayExternal(dp.x, None, 'Length'))
+ self.form.input_c_x.setProperty('rawValue', dp.x)
+ else:
+ # sbx.setText(displayExternal(dp.x, None, 'Length'))
+ self.form.input_c_x.setProperty('rawValue', dp.x)
+ if self.mask in ('x', 'z'):
+ # sby.setText(displayExternal(dp.y, None, 'Length'))
+ self.form.input_c_y.setProperty('rawValue', dp.y)
+ else:
+ # sby.setText(displayExternal(dp.y, None, 'Length'))
+ self.form.input_c_y.setProperty('rawValue', dp.y)
+ if self.mask in ('x', 'y'):
+ # sbz.setText(displayExternal(dp.z, None, 'Length'))
+ self.form.input_c_z.setProperty('rawValue', dp.z)
+ else:
+ # sbz.setText(displayExternal(dp.z, None, 'Length'))
+ self.form.input_c_z.setProperty('rawValue', dp.z)
+
+ # Set masks
+ if (mask == "x") or (self.mask == "x"):
+ self.form.input_c_x.setEnabled(True)
+ self.form.input_c_y.setEnabled(False)
+ self.form.input_c_z.setEnabled(False)
+ self.set_focus("x")
+ elif (mask == "y") or (self.mask == "y"):
+ self.form.input_c_x.setEnabled(False)
+ self.form.input_c_y.setEnabled(True)
+ self.form.input_c_z.setEnabled(False)
+ self.set_focus("y")
+ elif (mask == "z") or (self.mask == "z"):
+ self.form.input_c_x.setEnabled(False)
+ self.form.input_c_y.setEnabled(False)
+ self.form.input_c_z.setEnabled(True)
+ self.set_focus("z")
+ else:
+ self.form.input_c_x.setEnabled(True)
+ self.form.input_c_y.setEnabled(True)
+ self.form.input_c_z.setEnabled(True)
+ self.set_focus()
+
+ def set_focus(self, key=None):
+ """Set the focus on the widget that receives the key signal"""
+ if key is None or key == "x":
+ self.form.input_c_x.setFocus()
+ self.form.input_c_x.selectAll()
+ elif key == "y":
+ self.form.input_c_y.setFocus()
+ self.form.input_c_y.selectAll()
+ elif key == "z":
+ self.form.input_c_z.setFocus()
+ self.form.input_c_z.selectAll()
+
+ def reject(self):
+ """Function that executes when clicking the Cancel button"""
+ _Msg(_tr("Aborted:") + " {}".format(self.name))
+ self.finish()
+
+ def finish(self):
+ """Function that runs at the end after OK or Cancel"""
+ # App.ActiveDocument.commitTransaction()
+ Gui.ActiveDocument.resetEdit()
+ # Runs the parent command to complete the call
+ self.source_command.completed()
diff --git a/src/Mod/Draft/draftviewproviders/view_circulararray.py b/src/Mod/Draft/draftviewproviders/view_circulararray.py
new file mode 100644
index 0000000000..1a6eab8b4f
--- /dev/null
+++ b/src/Mod/Draft/draftviewproviders/view_circulararray.py
@@ -0,0 +1,44 @@
+"""This module provides the view provider code for Draft CircularArray.
+"""
+## @package view_circulararray
+# \ingroup DRAFT
+# \brief This module provides the view provider code for Draft CircularArray.
+
+# ***************************************************************************
+# * (c) 2019 Eliud Cabrera Castillo *
+# * *
+# * This file is part of the FreeCAD CAx development system. *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * FreeCAD is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with FreeCAD; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import Draft
+import Draft_rc
+ViewProviderDraftArray = Draft._ViewProviderDraftArray
+
+# So the resource file doesn't trigger errors from code checkers (flake8)
+True if Draft_rc.__name__ else False
+
+
+class ViewProviderCircularArray(ViewProviderDraftArray):
+
+ def __init__(self, vobj):
+ super().__init__(self, vobj)
+
+ def getIcon(self):
+ return ":/icons/Draft_CircularArray"