freecad-cam/Mod/Draft/DraftGui.py
2026-02-01 01:59:24 +01:00

1820 lines
76 KiB
Python

# -*- coding: utf8 -*-
#***************************************************************************
#* Copyright (c) 2009 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__ = "FreeCAD Draft Workbench - GUI part"
__author__ = "Yorik van Havre <yorik@uncreated.net>"
__url__ = "https://www.freecad.org"
## @package DraftGui
# \ingroup DRAFT
# \brief GUI elements and utilities of the Draft workbench
#
# This module provides GUI tools for the Draft workbench, such as
# toolbars and task panels, and Qt-dependent utilities such as
# a delayed (todo) commit system
"""This is the GUI part of the Draft module.
Report to Draft.py for info
"""
import os
import sys
import math
import PySide.QtCore as QtCore
import PySide.QtGui as QtGui
import PySide.QtWidgets as QtWidgets
import FreeCAD
import FreeCADGui
import Draft
import DraftVecUtils
import WorkingPlane
from draftutils import params
from draftutils import utils
from draftutils.todo import todo
from draftutils.translate import translate
from draftutils.units import display_external
translate("draft", "Relative")
translate("draft", "Global")
translate("draft", "Continue")
translate("draft", "Close")
translate("draft", "Copy")
translate("draft", "Subelement mode")
translate("draft", "Fill")
translate("draft", "Exit")
translate("draft", "Snap On/Off")
translate("draft", "Increase snap radius")
translate("draft", "Decrease snap radius")
translate("draft", "Restrict X")
translate("draft", "Restrict Y")
translate("draft", "Restrict Z")
translate("draft", "Select edge")
translate("draft", "Add custom snap point")
translate("draft", "Length mode")
translate("draft", "Wipe")
translate("draft", "Set Working Plane")
translate("draft", "Cycle snap object")
translate("draft", "Undo last segment")
def _get_incmd_shortcut(itm):
return params.get_param("inCommandShortcut" + itm).upper()
#---------------------------------------------------------------------------
# Customized widgets
#---------------------------------------------------------------------------
class DraftBaseWidget(QtWidgets.QWidget):
def __init__(self,parent = None):
super().__init__(parent)
def eventFilter(self, widget, event):
if (event.type() == QtCore.QEvent.KeyPress
and event.text().upper() == _get_incmd_shortcut("CycleSnap")):
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.cycleSnapObject()
return True
return super().eventFilter(widget, event)
class DraftDockWidget(DraftBaseWidget):
"""custom Widget that emits a resized() signal when resized"""
def __init__(self,parent = None):
super().__init__(parent)
def resizeEvent(self,event):
self.emit(QtCore.SIGNAL("resized()"))
def changeEvent(self, event):
if event.type() == QtCore.QEvent.LanguageChange:
self.emit(QtCore.SIGNAL("retranslate()"))
else:
super().changeEvent(event)
class DraftLineEdit(QtWidgets.QLineEdit):
"""custom QLineEdit widget that has the power to catch Escape keypress"""
def __init__(self, parent=None):
super().__init__(parent)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.emit(QtCore.SIGNAL("escaped()"))
elif event.key() == QtCore.Qt.Key_Up:
self.emit(QtCore.SIGNAL("up()"))
elif event.key() == QtCore.Qt.Key_Down:
self.emit(QtCore.SIGNAL("down()"))
else:
super().keyPressEvent(event)
class DraftTaskPanel:
def __init__(self,widget,extra=None):
if extra:
if isinstance(extra,list):
self.form = [widget] + extra
else:
self.form = [widget,extra]
else:
self.form = widget
def getStandardButtons(self):
return QtWidgets.QDialogButtonBox.Close
def accept(self):
if hasattr(FreeCADGui,"draftToolBar"):
return FreeCADGui.draftToolBar.validatePoint()
else:
if FreeCADGui.ActiveDocument:
FreeCADGui.ActiveDocument.resetEdit()
return True
def reject(self):
# https://github.com/FreeCAD/FreeCAD/issues/17027
# Function can be called multiple times if Esc is pressed during mouse
# move. We need to prevent multiple calls to draftToolBar.escape():
if not FreeCADGui.draftToolBar.isTaskOn:
return
FreeCADGui.draftToolBar.isTaskOn = False
FreeCADGui.draftToolBar.escape()
if FreeCADGui.ActiveDocument:
FreeCADGui.ActiveDocument.resetEdit()
return True
def isAllowedAlterDocument(self):
return False
class DraftToolBar:
"""The Draft Task panel UI
Draft Toolbar is the main ui of the Draft Module. Once displayed as a
toolbar, now it define the ui of the Task Panel.
Toolbar become obsolete due to lack of manteinence and was disabled
by default in February 2020.
Draft Ui Commands call and get information such as point coordinates,
subcommands activation, continue mode, etc. from Task Panel Ui
"""
def __init__(self):
self.tray = None
self.sourceCmd = None
self.mouse = True
self.cancel = None
self.pointcallback = None
# OBSOLETE BUT STILL USED BY SOME ADDONS AND MACROS
self.color = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeLineColor")))
self.facecolor = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeColor")))
self.linewidth = params.get_param_view("DefaultShapeLineWidth")
self.fontsize = params.get_param("textheight")
self.paramconstr = utils.rgba_to_argb(params.get_param("constructioncolor"))
self.constrMode = False
self.continueMode = False
self.relativeMode = True
self.globalMode = False
self.state = None
self.textbuffer = []
self.crossedViews = []
self.isTaskOn = False
self.fillmode = True
self.mask = None
self.alock = False
self.x = 0
self.y = 0
self.z = 0
self.lvalue = 0
self.pvalue = 90
self.avalue = 0
self.angle = None
self.radius = 0
self.offset = 0
self.uiloader = FreeCADGui.UiLoader()
self.autogroup = None
self.isCenterPlane = False
self.input_fields = {
"xValue":{"value":"x","unit":"Length"},
"yValue":{"value":"y","unit":"Length"},
"zValue":{"value":"z","unit":"Length"},
"lengthValue":{"value":"lvalue","unit":"Length"},
"radiusValue":{"value":"radius","unit":"Length"},
"angleValue":{"value":"avalue","unit":"Angle"}
}
# add only a dummy widget, since widgets are created on demand
self.baseWidget = DraftBaseWidget()
self.tray = FreeCADGui.UiLoader().createWidget("Gui::ToolBar")
self.tray.setObjectName("Draft tray")
self.tray.setWindowTitle("Draft tray")
self.toptray = self.tray
self.bottomtray = self.tray
self.setupTray()
self.setupStyle()
mw = FreeCADGui.getMainWindow()
mw.addToolBar(self.tray)
self.tray.setParent(mw)
self.tray.hide()
#---------------------------------------------------------------------------
# General UI setup
#---------------------------------------------------------------------------
def _pushbutton(self,name, layout, hide=True, icon=None,
width=None, checkable=False, square=False):
if square:
button = QtWidgets.QToolButton(self.baseWidget)
if width is not None:
button.setFixedHeight(width)
button.setFixedWidth(width)
else:
button = QtWidgets.QPushButton(self.baseWidget)
button.setObjectName(name)
if hide:
button.hide()
if icon:
if icon.endswith(".svg"):
button.setIcon(QtGui.QIcon(icon))
else:
button.setIcon(QtGui.QIcon.fromTheme(
icon, QtGui.QIcon(':/icons/'+icon+'.svg')))
if checkable:
button.setCheckable(True)
button.setChecked(False)
layout.addWidget(button)
return button
def _label (self,name, layout, hide=True, wrap=False):
label = QtWidgets.QLabel(self.baseWidget)
label.setObjectName(name)
if wrap:
label.setWordWrap(True)
if hide: label.hide()
layout.addWidget(label)
return label
def _lineedit (self,name, layout, hide=True, width=None):
bsize = params.get_param("ToolbarIconSize", path="General") - 2
lineedit = DraftLineEdit(self.baseWidget)
lineedit.setObjectName(name)
if hide: lineedit.hide()
#if not width: width = 800
#lineedit.setMaximumSize(QtCore.QSize(width,bsize))
layout.addWidget(lineedit)
return lineedit
def _inputfield (self,name, layout, hide=True, width=None):
inputfield = self.uiloader.createWidget("Gui::InputField")
inputfield.setObjectName(name)
if hide: inputfield.hide()
if not width:
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
inputfield.setSizePolicy(sizePolicy)
inputfield.setMinimumWidth(110)
else:
inputfield.setMaximumWidth(width)
layout.addWidget(inputfield)
return inputfield
def _spinbox (self,name, layout, val=None, vmax=None,
hide=True, double=False, size=None):
if double:
sbox = QtWidgets.QDoubleSpinBox(self.baseWidget)
sbox.setDecimals(params.get_param("Decimals", path="Units"))
else:
sbox = QtWidgets.QSpinBox(self.baseWidget)
sbox.setObjectName(name)
if vmax: sbox.setMaximum(vmax)
if val: sbox.setValue(val)
#if size: sbox.setMaximumSize(QtCore.QSize(size[0],size[1]))
if hide: sbox.hide()
layout.addWidget(sbox)
return sbox
def _checkbox (self,name, layout, checked=True, hide=True):
chk = QtWidgets.QCheckBox(self.baseWidget)
chk.setChecked(checked)
chk.setObjectName(name)
if hide: chk.hide()
layout.addWidget(chk)
return chk
def _combo (self,name,layout,hide=True):
cb = QtWidgets.QComboBox(self.baseWidget)
cb.setObjectName(name)
if hide: cb.hide()
layout.addWidget(cb)
def setupToolBar(self,task=False):
"""sets the draft toolbar up"""
# command
self.promptlabel = self._label("promptlabel", self.layout, hide=task)
self.cmdlabel = self._label("cmdlabel", self.layout, hide=task)
boldtxt = QtGui.QFont()
boldtxt.setBold(True)
self.cmdlabel.setFont(boldtxt)
# point
xl = QtWidgets.QHBoxLayout()
yl = QtWidgets.QHBoxLayout()
zl = QtWidgets.QHBoxLayout()
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(xl)
self.layout.addLayout(yl)
self.layout.addLayout(zl)
self.layout.addLayout(bl)
self.labelx = self._label("labelx", xl)
self.xValue = self._inputfield("xValue", xl) #width=60
self.xValue.installEventFilter(self.baseWidget)
self.xValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.labely = self._label("labely", yl)
self.yValue = self._inputfield("yValue", yl)
self.yValue.installEventFilter(self.baseWidget) # Required to detect snap cycling in case of Y constraining.
self.yValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.labelz = self._label("labelz", zl)
self.zValue = self._inputfield("zValue", zl)
self.zValue.installEventFilter(self.baseWidget) # Required to detect snap cycling in case of Z constraining.
self.zValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.pointButton = self._pushbutton("addButton", bl, icon="Draft_AddPoint")
# text
self.textValue = QtWidgets.QTextEdit(self.baseWidget)
self.textValue.setObjectName("textValue")
self.textValue.setTabChangesFocus(True)
self.layout.addWidget(self.textValue)
self.textValue.hide()
tl = QtWidgets.QHBoxLayout()
self.layout.addLayout(tl)
self.textOkButton = self._pushbutton("textButton", tl, icon="button_valid")
# additional line controls
ll = QtWidgets.QHBoxLayout()
al = QtWidgets.QHBoxLayout()
self.layout.addLayout(ll)
self.layout.addLayout(al)
self.labellength = self._label("labellength", ll)
self.lengthValue = self._inputfield("lengthValue", ll)
self.lengthValue.installEventFilter(self.baseWidget) # Required to detect snap cycling if focusOnLength is True.
self.lengthValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.labelangle = self._label("labelangle", al)
self.angleLock = self._checkbox("angleLock",al,checked=self.alock)
self.angleValue = self._inputfield("angleValue", al)
self.angleValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Angle).UserString)
# options
fl = QtWidgets.QHBoxLayout()
self.layout.addLayout(fl)
self.numFacesLabel = self._label("numfaceslabel", fl)
self.numFaces = self._spinbox("numFaces", fl, 3)
ol = QtWidgets.QHBoxLayout()
self.layout.addLayout(ol)
rl = QtWidgets.QHBoxLayout()
self.layout.addLayout(rl)
self.labelRadius = self._label("labelRadius", rl)
self.radiusValue = self._inputfield("radiusValue", rl)
self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.undoButton = self._pushbutton("undoButton", bl, icon='Draft_Rotate')
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.finishButton = self._pushbutton("finishButton", bl, icon='Draft_Finish')
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.closeButton = self._pushbutton("closeButton", bl, icon='Draft_Lock')
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.wipeButton = self._pushbutton("wipeButton", bl, icon='Draft_Wipe')
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.orientWPButton = self._pushbutton("orientWPButton", bl, icon='Draft_SelectPlane')
bl = QtWidgets.QHBoxLayout()
self.layout.addLayout(bl)
self.selectButton = self._pushbutton("selectButton", bl, icon='view-select')
# update modes from parameters:
self.relativeMode = params.get_param("RelativeMode")
self.globalMode = params.get_param("GlobalMode")
self.fillmode = params.get_param("fillmode")
self.continueMode = params.get_param("ContinueMode")
# Note: The order of the calls to self._checkbox() below controls
# the position of the checkboxes in the task panel.
# update checkboxes with parameters and internal modes:
self.isRelative = self._checkbox("isRelative", self.layout, checked=self.relativeMode)
self.isGlobal = self._checkbox("isGlobal", self.layout, checked=self.globalMode)
self.hasFill = self._checkbox("hasFill", self.layout, checked=self.fillmode)
self.continueCmd = self._checkbox("continueCmd", self.layout, checked=self.continueMode)
# update checkboxes without parameters and without internal modes:
self.occOffset = self._checkbox("occOffset", self.layout, checked=False)
# update checkboxes with parameters but without internal modes:
# self.isCopy is also updated in modUi ("CopyMode") and offsetUi ("OffsetCopyMode")
self.isCopy = self._checkbox("isCopy",
self.layout,
checked=params.get_param("CopyMode"))
self.isSubelementMode = self._checkbox("isSubelementMode",
self.layout,
checked=params.get_param("SubelementMode"))
# spacer
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding)
self.layout.addItem(spacerItem)
QtCore.QObject.connect(self.xValue,QtCore.SIGNAL("valueChanged(double)"),self.changeXValue)
QtCore.QObject.connect(self.yValue,QtCore.SIGNAL("valueChanged(double)"),self.changeYValue)
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("valueChanged(double)"),self.changeZValue)
QtCore.QObject.connect(self.lengthValue,QtCore.SIGNAL("valueChanged(double)"),self.changeLengthValue)
QtCore.QObject.connect(self.angleValue,QtCore.SIGNAL("valueChanged(double)"),self.changeAngleValue)
QtCore.QObject.connect(self.angleLock,QtCore.SIGNAL("stateChanged(int)"),self.toggleAngle)
QtCore.QObject.connect(self.radiusValue,QtCore.SIGNAL("valueChanged(double)"),self.changeRadiusValue)
QtCore.QObject.connect(self.xValue,QtCore.SIGNAL("returnPressed()"),self.checkx)
QtCore.QObject.connect(self.yValue,QtCore.SIGNAL("returnPressed()"),self.checky)
QtCore.QObject.connect(self.lengthValue,QtCore.SIGNAL("returnPressed()"),self.checklength)
QtCore.QObject.connect(self.xValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.yValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.lengthValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.radiusValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.angleValue,QtCore.SIGNAL("textEdited(QString)"),self.checkSpecialChars)
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("returnPressed()"),self.validatePoint)
QtCore.QObject.connect(self.pointButton,QtCore.SIGNAL("clicked()"),self.validatePoint)
QtCore.QObject.connect(self.radiusValue,QtCore.SIGNAL("returnPressed()"),self.validatePoint)
QtCore.QObject.connect(self.angleValue,QtCore.SIGNAL("returnPressed()"),self.validatePoint)
QtCore.QObject.connect(self.textValue,QtCore.SIGNAL("textChanged()"),self.checkEnterText)
QtCore.QObject.connect(self.textOkButton,QtCore.SIGNAL("clicked()"),self.sendText)
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("returnPressed()"),self.setFocus)
QtCore.QObject.connect(self.finishButton,QtCore.SIGNAL("pressed()"),self.finish)
QtCore.QObject.connect(self.closeButton,QtCore.SIGNAL("pressed()"),self.closeLine)
QtCore.QObject.connect(self.wipeButton,QtCore.SIGNAL("pressed()"),self.wipeLine)
QtCore.QObject.connect(self.orientWPButton,QtCore.SIGNAL("pressed()"),self.orientWP)
QtCore.QObject.connect(self.undoButton,QtCore.SIGNAL("pressed()"),self.undoSegment)
QtCore.QObject.connect(self.selectButton,QtCore.SIGNAL("pressed()"),self.selectEdge)
QtCore.QObject.connect(self.continueCmd,QtCore.SIGNAL("stateChanged(int)"),self.setContinue)
QtCore.QObject.connect(self.isCopy,QtCore.SIGNAL("stateChanged(int)"),self.setCopymode)
QtCore.QObject.connect(self.isSubelementMode, QtCore.SIGNAL("stateChanged(int)"), self.setSubelementMode)
QtCore.QObject.connect(self.isRelative,QtCore.SIGNAL("stateChanged(int)"),self.setRelative)
QtCore.QObject.connect(self.isGlobal,QtCore.SIGNAL("stateChanged(int)"),self.setGlobal)
QtCore.QObject.connect(self.hasFill,QtCore.SIGNAL("stateChanged(int)"),self.setFill)
QtCore.QObject.connect(self.baseWidget,QtCore.SIGNAL("resized()"),self.relocate)
QtCore.QObject.connect(self.baseWidget,QtCore.SIGNAL("retranslate()"),self.retranslateUi)
def setupTray(self):
"""sets draft tray buttons up"""
self.wplabel = self._pushbutton(
"wplabel", self.toptray, icon='Draft_SelectPlane',
hide=False,width=120)
self.styleButton = self._pushbutton(
"stylebutton", self.toptray, icon='Draft_Apply', hide=False,
width=120)
self.setStyleButton()
self.constrButton = self._pushbutton(
"constrButton", self.toptray, hide=False, icon='Draft_Construction',
width=self.styleButton.sizeHint().height(), checkable=True, square=True)
self.constrColor = QtGui.QColor(self.paramconstr)
self.autoGroupButton = self._pushbutton(
"autoGroup", self.bottomtray,icon=":/icons/button_invalid.svg",
hide=False, width=120)
self.autoGroupButton.setText(translate("draft", "None"))
self.autoGroupButton.setFlat(True)
QtCore.QObject.connect(self.wplabel,QtCore.SIGNAL("pressed()"),self.selectplane)
QtCore.QObject.connect(self.styleButton,QtCore.SIGNAL("pressed()"),self.setstyle)
QtCore.QObject.connect(self.constrButton,QtCore.SIGNAL("toggled(bool)"),self.toggleConstrMode)
QtCore.QObject.connect(self.autoGroupButton,QtCore.SIGNAL("pressed()"),self.runAutoGroup)
QtCore.QTimer.singleShot(2000,self.retranslateTray) # delay so translations get a chance to load
def setupStyle(self):
style = "#constrButton:Checked {background-color: "
style += self.getDefaultColor("constr",rgb=True)+" } "
style += "#addButton:Checked, #delButton:checked, "
style += "#sharpButton:Checked, "
style += "#tangentButton:Checked, #symmetricButton:checked {"
style += "background-color: rgb(20,100,250) }"
self.baseWidget.setStyleSheet(style)
#if hasattr(self,"tray"):
# self.tray.setStyleSheet(style)
#---------------------------------------------------------------------------
# language tools
#---------------------------------------------------------------------------
def retranslateUi(self, widget=None):
self.promptlabel.setText(translate("draft", "active command:"))
self.cmdlabel.setText(translate("draft", "None"))
self.cmdlabel.setToolTip(translate("draft", "Active Draft command"))
self.xValue.setToolTip(translate("draft", "X coordinate of next point"))
self.labelx.setText(translate("draft", "X"))
self.labely.setText(translate("draft", "Y"))
self.labelz.setText(translate("draft", "Z"))
self.yValue.setToolTip(translate("draft", "Y coordinate of next point"))
self.zValue.setToolTip(translate("draft", "Z coordinate of next point"))
self.pointButton.setText(translate("draft", "Enter point"))
self.pointButton.setToolTip(translate(
"draft","Enter a new point with the given coordinates"))
self.labellength.setText(translate("draft", "Length"))
self.labelangle.setText(translate("draft", "Angle"))
self.lengthValue.setToolTip(translate("draft", "Length of current segment"))
self.angleValue.setToolTip(translate("draft", "Angle of current segment"))
self.angleLock.setToolTip(translate(
"draft", "Check this to lock the current angle")\
+ " (" + _get_incmd_shortcut("Length") + ")")
self.labelRadius.setText(translate("draft", "Radius"))
self.radiusValue.setToolTip(translate("draft", "Radius of Circle"))
self.isRelative.setText(translate(
"draft", "Relative") + " (" + _get_incmd_shortcut("Relative") + ")")
self.isRelative.setToolTip(translate(
"draft", "Coordinates relative to last point or to coordinate system "
+ "origin\nif is the first point to set"))
self.isGlobal.setText(translate(
"draft", "Global") + " (" + _get_incmd_shortcut("Global") + ")")
self.isGlobal.setToolTip(translate(
"draft", "Coordinates relative to global coordinate system."
+ "\nUncheck to use working plane coordinate system"))
self.hasFill.setText(translate(
"draft", "Filled") + " (" + _get_incmd_shortcut("Fill") + ")")
self.hasFill.setToolTip(translate(
"draft", "Check this if the object should appear as filled, "
+ "otherwise it will appear as wireframe.\nNot available if "
+ "Draft preference option 'Use Part Primitives' is enabled"))
self.finishButton.setText(translate(
"draft", "Finish") + " (" + _get_incmd_shortcut("Exit") + ")")
self.finishButton.setToolTip(translate(
"draft", "Finishes the current drawing or editing operation"))
self.continueCmd.setToolTip(translate(
"draft", "If checked, command will not finish until you press "
+ "the command button again"))
self.continueCmd.setText(translate(
"draft", "Continue") + " (" + _get_incmd_shortcut("Continue") + ")")
self.occOffset.setToolTip(translate(
"draft", "If checked, an OCC-style offset will be performed"
+ " instead of the classic offset"))
self.occOffset.setText(translate("draft", "OCC-style offset"))
self.undoButton.setText(translate("draft", "Undo") + " (" + _get_incmd_shortcut("Undo") + ")")
self.undoButton.setToolTip(translate("draft", "Undo the last segment"))
self.closeButton.setText(translate("draft", "Close") + " (" + _get_incmd_shortcut("Close") + ")")
self.closeButton.setToolTip(translate("draft", "Finishes and closes the current line"))
self.wipeButton.setText(translate("draft", "Wipe") + " (" + _get_incmd_shortcut("Wipe") + ")")
self.wipeButton.setToolTip(translate("draft", "Wipes the existing segments of this line and starts again from the last point"))
self.orientWPButton.setText(translate("draft", "Set WP") + " (" + _get_incmd_shortcut("SetWP") + ")")
self.orientWPButton.setToolTip(translate("draft", "Reorients the working plane on the last segment"))
self.selectButton.setText(translate("draft", "Select edge") + " (" + _get_incmd_shortcut("SelectEdge") + ")")
self.selectButton.setToolTip(translate("draft", "Selects an existing edge to be measured by this dimension"))
self.numFacesLabel.setText(translate("draft", "Sides"))
self.numFaces.setToolTip(translate("draft", "Number of sides"))
self.isCopy.setText(translate("draft", "Copy") + " (" + _get_incmd_shortcut("Copy") + ")")
self.isCopy.setToolTip(translate("draft", "If checked, objects will be copied instead of moved"))
self.isSubelementMode.setText(translate("draft", "Modify subelements") + " (" + _get_incmd_shortcut("SubelementMode") + ")")
self.isSubelementMode.setToolTip(translate("draft", "If checked, subelements will be modified instead of entire objects"))
self.textOkButton.setText(translate("draft", "Create text"))
self.textOkButton.setToolTip(translate("draft", "Press this button to create the text object, or finish your text with two blank lines"))
self.retranslateTray(widget)
# Update the maximum width of the push buttons
maxwidth = 66 # that's the default
pb = []
for i in range(self.layout.count()):
w = self.layout.itemAt(i).widget()
if w is not None and w.inherits('QPushButton'):
pb.append(w)
for i in pb:
fm = QtGui.QFontMetrics(i.font())
fw = fm.width(i.text())
fw = max(fw, maxwidth)
maxwidth = maxwidth + 16 +10 # add icon width and a margin
for i in pb:
i.setMaximumWidth(maxwidth)
def retranslateTray(self,widget=None):
self.styleButton.setToolTip(translate("draft", "Change default style for new objects"))
self.constrButton.setToolTip(translate("draft", "Toggle construction mode"))
self.autoGroupButton.setToolTip(translate("draft", "Autogroup off"))
#---------------------------------------------------------------------------
# Interface modes
#---------------------------------------------------------------------------
def taskUi(self,title="Draft",extra=None,icon="Draft_Draft"):
# reset InputField values
self.reset_ui_values()
self.isTaskOn = True
todo.delay(FreeCADGui.Control.closeDialog,None)
self.baseWidget = DraftBaseWidget()
self.layout = QtWidgets.QVBoxLayout(self.baseWidget)
self.setupToolBar(task=True)
self.retranslateUi(self.baseWidget)
self.panel = DraftTaskPanel(self.baseWidget,extra)
todo.delay(FreeCADGui.Control.showDialog,self.panel)
self.setTitle(title,icon)
def redraw(self):
"""utility function that is performed after each clicked point"""
self.checkLocal()
def setFocus(self,f=None):
if params.get_param("focusOnLength") and self.lengthValue.isVisible():
self.lengthValue.setFocus()
self.lengthValue.setSelection(0,self.number_length(self.lengthValue.text()))
elif self.angleLock.isVisible() and self.angleLock.isChecked():
self.lengthValue.setFocus()
self.lengthValue.setSelection(0,self.number_length(self.lengthValue.text()))
elif (f is None) or (f == "x"):
self.xValue.setFocus()
self.xValue.setSelection(0,self.number_length(self.xValue.text()))
elif f == "y":
self.yValue.setFocus()
self.yValue.setSelection(0,self.number_length(self.yValue.text()))
elif f == "z":
self.zValue.setFocus()
self.zValue.setSelection(0,self.number_length(self.zValue.text()))
elif f == "radius":
self.radiusValue.setFocus()
self.radiusValue.setSelection(0,self.number_length(self.radiusValue.text()))
def number_length(self, str):
nl = 0
for char in str:
if char in "0123456789.,-":
nl += 1
return nl
def extraLineUi(self):
'''shows length and angle controls'''
self.labellength.show()
self.lengthValue.show()
self.labelangle.show()
self.angleValue.show()
self.angleLock.show()
self.angleLock.setChecked(False)
def hideXYZ(self):
''' turn off all the point entry widgets '''
self.labelx.hide()
self.labely.hide()
self.labelz.hide()
self.labellength.hide()
self.labelangle.hide()
self.xValue.hide()
self.yValue.hide()
self.zValue.hide()
self.pointButton.hide()
self.lengthValue.hide()
self.angleValue.hide()
self.angleLock.hide()
self.isRelative.hide()
self.isGlobal.hide()
def lineUi(self, title=translate("draft", "Line"), cancel=None, extra=None,
getcoords=None,rel=False,icon="Draft_Line"):
self.pointUi(title, cancel, extra, getcoords, rel, icon)
self.extraLineUi()
self.xValue.setEnabled(True)
self.yValue.setEnabled(True)
self.continueCmd.show()
def wireUi(self, title=translate("draft", "DWire"), cancel=None, extra=None,
getcoords=None, rel=False, icon="Draft_Wire"):
self.pointUi(title, cancel, extra, getcoords, rel, icon)
self.xValue.setEnabled(True)
self.yValue.setEnabled(True)
if params.get_param("UsePartPrimitives"):
self.hasFill.setEnabled(False)
else:
self.hasFill.setEnabled(True)
self.hasFill.show()
self.finishButton.show()
self.closeButton.show()
self.wipeButton.show()
self.orientWPButton.show()
self.undoButton.show()
self.continueCmd.show()
def circleUi(self):
self.pointUi(translate("draft", "Circle"),icon="Draft_Circle")
self.extUi()
self.isRelative.hide()
def arcUi(self):
self.pointUi(translate("draft", "Arc"),icon="Draft_Arc")
self.continueCmd.show()
self.isRelative.hide()
def rotateSetCenterUi(self):
self.pointUi(translate("draft", "Rotate"),icon="Draft_Rotate")
self.modUi()
self.isRelative.hide()
def pointUi(self, title=translate("draft","Point"), cancel=None, extra=None,
getcoords=None, rel=False, icon="Draft_Draft"):
if cancel: self.cancel = cancel
if getcoords: self.pointcallback = getcoords
self.taskUi(title,extra,icon)
self.xValue.setEnabled(True)
self.yValue.setEnabled(True)
self.isRelative.show()
self.isGlobal.show()
self.checkLocal()
self.labelx.show()
self.labely.show()
self.labelz.show()
self.xValue.show()
self.yValue.show()
self.zValue.show()
# reset UI to (0,0,0) on start
self.xValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.yValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.zValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.x = 0
self.y = 0
self.z = 0
self.pointButton.show()
if rel: self.isRelative.show()
todo.delay(self.setFocus,None)
self.xValue.selectAll()
def labelUi(self,title=translate("draft","Label"),callback=None):
w = QtWidgets.QWidget()
w.setWindowTitle(translate("draft","Label type"))
l = QtWidgets.QVBoxLayout(w)
combo = QtWidgets.QComboBox()
from draftobjects.label import get_label_types
types = get_label_types()
for s in types:
combo.addItem(translate("Draft", s), userData=s)
combo.setCurrentIndex(types.index(params.get_param("labeltype")))
l.addWidget(combo)
QtCore.QObject.connect(combo,QtCore.SIGNAL("currentIndexChanged(int)"),callback)
self.pointUi(title=title, extra=w, icon="Draft_Label")
def extraUi(self):
pass
def offsetUi(self):
self.taskUi(translate("draft","Offset"), icon="Draft_Offset")
self.radiusUi()
self.isCopy.show()
self.isCopy.setChecked(params.get_param("OffsetCopyMode"))
self.occOffset.show()
self.labelRadius.setText(translate("draft","Distance"))
self.radiusValue.setToolTip(translate("draft", "Offset distance"))
self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
todo.delay(self.radiusValue.setFocus,None)
self.radiusValue.selectAll()
def offUi(self):
todo.delay(FreeCADGui.Control.closeDialog,None)
self.cancel = None
self.sourceCmd = None
self.pointcallback = None
self.mask = None
self.isTaskOn = False
self.baseWidget = QtWidgets.QWidget()
def trimUi(self,title=translate("draft","Trimex")):
self.taskUi(title, icon="Draft_Trimex")
self.radiusUi()
self.labelRadius.setText(translate("draft","Distance"))
self.radiusValue.setToolTip(translate("draft", "Offset distance"))
self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
todo.delay(self.radiusValue.setFocus,None)
self.radiusValue.selectAll()
def radiusUi(self):
self.hideXYZ()
self.labelRadius.setText(translate("draft", "Radius"))
self.radiusValue.setToolTip(translate("draft", "Radius of Circle"))
self.labelRadius.show()
self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString)
self.radiusValue.show()
todo.delay(self.radiusValue.setFocus,None)
self.radiusValue.selectAll()
def textUi(self):
self.hideXYZ()
self.textValue.show()
self.textOkButton.show()
self.textValue.setText('')
todo.delay(self.textValue.setFocus,None)
self.textbuffer=[]
self.textline=0
self.continueCmd.show()
# Change the checkbox label as the in-command shortcut cannot be used:
self.continueCmd.setText(translate("draft", "Continue"))
def switchUi(self,store=True):
if store:
self.state = []
self.state.append(self.labelx.isVisible())
self.state.append(self.labely.isVisible())
self.state.append(self.labelz.isVisible())
self.state.append(self.xValue.isVisible())
self.state.append(self.yValue.isVisible())
self.state.append(self.zValue.isVisible())
self.hideXYZ()
else:
if self.state:
if self.state[0]:self.labelx.show()
if self.state[1]:self.labely.show()
if self.state[2]:self.labelz.show()
if self.state[3]:self.xValue.show()
if self.state[4]:self.yValue.show()
if self.state[5]:self.zValue.show()
self.state = None
def setTitle(self,title,icon="Draft_Draft"):
self.baseWidget.setWindowTitle(title)
self.baseWidget.setWindowIcon(QtGui.QIcon(":/icons/"+icon+".svg"))
def selectUi(self,extra=None, on_close_call=None):
self.makeDumbTask(extra, on_close_call)
def editUi(self):
self.makeDumbTask(on_close_call=self.finish)
def extUi(self):
if params.get_param("UsePartPrimitives"):
self.hasFill.setEnabled(False)
else:
self.hasFill.setEnabled(True)
self.hasFill.show()
self.continueCmd.show()
def modUi(self):
self.isCopy.show()
self.isSubelementMode.show()
self.isCopy.setChecked(params.get_param("CopyMode"))
self.continueCmd.show()
def checkLocal(self):
"""checks if x,y,z coords must be displayed as local or global"""
if not self.globalMode and self.relativeMode:
self.labelx.setText(translate("draft", "Local {}").format("\u0394X")) # \u0394 = ∆ (Greek delta)
self.labely.setText(translate("draft", "Local {}").format("\u0394Y"))
self.labelz.setText(translate("draft", "Local {}").format("\u0394Z"))
elif not self.globalMode and not self.relativeMode:
self.labelx.setText(translate("draft", "Local {}").format("X"))
self.labely.setText(translate("draft", "Local {}").format("Y"))
self.labelz.setText(translate("draft", "Local {}").format("Z"))
elif self.globalMode and self.relativeMode:
self.labelx.setText(translate("draft", "Global {}").format("\u0394X"))
self.labely.setText(translate("draft", "Global {}").format("\u0394Y"))
self.labelz.setText(translate("draft", "Global {}").format("\u0394Z"))
else:
self.labelx.setText(translate("draft", "Global {}").format("X"))
self.labely.setText(translate("draft", "Global {}").format("Y"))
self.labelz.setText(translate("draft", "Global {}").format("Z"))
def setNextFocus(self):
def isThere(widget):
if widget.isEnabled() and widget.isVisible():
return True
else:
return False
if self.isTaskOn:
if isThere(self.xValue):
self.setFocus()
elif isThere(self.yValue):
self.yValue.setFocus()
self.yValue.selectAll()
elif isThere(self.zValue):
self.zValue.setFocus()
self.zValue.selectAll()
elif isThere(self.radiusValue):
self.radiusValue.setFocus()
self.radiusValue.selectAll()
def relocate(self):
"""relocates the right-aligned buttons depending on the toolbar size"""
if self.baseWidget.geometry().width() < 400:
self.layout.setDirection(QtWidgets.QBoxLayout.TopToBottom)
else:
self.layout.setDirection(QtWidgets.QBoxLayout.LeftToRight)
def makeDumbTask(self, extra=None, on_close_call=None):
"""create a dumb taskdialog to prevent deleting the temp object"""
class TaskPanel:
def __init__(self, extra=None, callback=None):
if extra:
self.form = [extra]
self.callback = callback
def getStandardButtons(self):
return QtWidgets.QDialogButtonBox.Close
def reject(self):
if self.callback:
self.callback()
return True
todo.delay(FreeCADGui.Control.closeDialog,None)
panel = TaskPanel(extra, on_close_call)
todo.delay(FreeCADGui.Control.showDialog,panel)
#---------------------------------------------------------------------------
# Processing functions
#---------------------------------------------------------------------------
def setContinue(self, val):
params.set_param("ContinueMode", bool(val))
self.continueMode = bool(val)
# val=-1 is used to temporarily switch to relativeMode and disable the checkbox.
# val=-2 is used to switch back.
# Used by:
# gui_ellipses.py
# gui_rectangles.py
# gui_stretch.py
def setRelative(self, val=-1):
if val < 0:
QtCore.QObject.disconnect(self.isRelative,
QtCore.SIGNAL("stateChanged(int)"),
self.setRelative)
if val == -1:
self.isRelative.setChecked(True)
self.relativeMode = True
elif val == -2:
val = params.get_param("RelativeMode")
self.isRelative.setChecked(val)
self.relativeMode = val
QtCore.QObject.connect(self.isRelative,
QtCore.SIGNAL("stateChanged(int)"),
self.setRelative)
else:
params.set_param("RelativeMode", bool(val))
self.relativeMode = bool(val)
self.checkLocal()
def setGlobal(self, val):
params.set_param("GlobalMode", bool(val))
self.globalMode = bool(val)
self.checkLocal()
def setFill(self, val):
params.set_param("fillmode", bool(val))
self.fillmode = bool(val)
def setCopymode(self, val):
# special value for offset command
if self.sourceCmd and self.sourceCmd.featureName == "Offset":
params.set_param("OffsetCopyMode", bool(val))
else:
params.set_param("CopyMode", bool(val))
def setSubelementMode(self, val):
params.set_param("SubelementMode", bool(val))
self.sourceCmd.set_ghosts()
def checkx(self):
if self.yValue.isEnabled():
self.yValue.setFocus()
self.yValue.setSelection(0,self.number_length(self.yValue.text()))
self.updateSnapper()
else:
self.checky()
def checky(self):
if self.zValue.isEnabled():
self.zValue.setFocus()
self.zValue.setSelection(0,self.number_length(self.zValue.text()))
self.updateSnapper()
else:
self.validatePoint()
def checklength(self):
if self.angleValue.isEnabled():
self.angleValue.setFocus()
self.angleValue.setSelection(0,self.number_length(self.angleValue.text()))
self.updateSnapper()
else:
self.validatePoint()
def validatePoint(self):
"""function for checking and sending numbers entered manually"""
if self.sourceCmd or self.pointcallback:
if (self.labelRadius.isVisible()):
try:
#rad=float(self.radiusValue.text())
rad = self.radius
except (ValueError, AttributeError):
print("debug: DraftGui.validatePoint: AttributeError")
else:
self.sourceCmd.numericRadius(rad)
elif (self.labelx.isVisible()):
try:
#numx=float(self.xValue.text())
numx = self.x
#numy=float(self.yValue.text())
numy = self.y
#numz=float(self.zValue.text())
numz = self.z
except (ValueError, AttributeError):
print("debug: DraftGui.validatePoint: AttributeError")
else:
num_vec = FreeCAD.Vector(numx, numy, numz)
if self.pointcallback:
self.pointcallback(num_vec, self.globalMode, self.relativeMode)
else:
plane = WorkingPlane.get_working_plane(update=False)
ref_vec = FreeCAD.Vector(0, 0, 0)
if plane and not self.globalMode:
num_vec = plane.get_global_coords(num_vec, as_vector=True)
ref_vec = plane.get_global_coords(ref_vec)
if self.relativeMode and self.sourceCmd.node:
ref_vec = self.sourceCmd.node[-1]
numx, numy, numz = num_vec + ref_vec
self.sourceCmd.numericInput(numx, numy, numz)
elif self.textValue.isVisible():
return False
else:
FreeCADGui.ActiveDocument.resetEdit()
return True
def finish(self, cont=None):
"""finish button action"""
if self.sourceCmd:
if cont is None:
cont = self.continueMode
self.sourceCmd.finish(cont=cont)
if self.cancel:
self.cancel()
self.cancel = None
if FreeCADGui.ActiveDocument:
FreeCADGui.ActiveDocument.resetEdit()
def escape(self):
"""escapes the current command"""
self.finish(cont=False)
def closeLine(self):
"""close button action"""
self.sourceCmd.finish(cont=self.continueMode, closed=True)
FreeCADGui.ActiveDocument.resetEdit()
def wipeLine(self):
"""wipes existing segments of a line"""
self.sourceCmd.wipe()
def orientWP(self):
"""reorients the current working plane"""
self.sourceCmd.orientWP()
def selectEdge(self):
"""allows the dimension command to select an edge"""
if hasattr(self.sourceCmd,"selectEdge"):
self.sourceCmd.selectEdge()
def undoSegment(self):
"""undo last line segment"""
if hasattr(self.sourceCmd,"undolast"):
self.sourceCmd.undolast()
def checkSpecialChars(self,txt):
"""checks for special characters in the entered coords that must be
treated as shortcuts
"""
if txt == "":
self.updateSnapper()
return
if txt[0] in "0123456789.,-":
self.updateSnapper()
self.setMouseMode(False)
return
txt = txt[0].upper()
spec = False
# Most frequently used shortcuts first:
if txt == _get_incmd_shortcut("Relative"):
if self.isRelative.isVisible():
self.isRelative.setChecked(not self.isRelative.isChecked())
self.relativeMode = self.isRelative.isChecked()
spec = True
elif txt == _get_incmd_shortcut("Global"):
if self.isGlobal.isVisible():
self.isGlobal.setChecked(not self.isGlobal.isChecked())
self.globalMode = self.isGlobal.isChecked()
spec = True
elif txt == _get_incmd_shortcut("Length"):
if self.lengthValue.isVisible():
self.constrain("angle")
self.displayPoint()
spec = True
elif txt == _get_incmd_shortcut("RestrictX"):
self.constrain("x")
self.displayPoint()
spec = True
elif txt == _get_incmd_shortcut("RestrictY"):
self.constrain("y")
self.displayPoint()
spec = True
elif txt == _get_incmd_shortcut("RestrictZ"):
self.constrain("z")
self.displayPoint()
spec = True
elif txt == _get_incmd_shortcut("Copy"):
if self.isCopy.isVisible():
self.isCopy.setChecked(not self.isCopy.isChecked())
spec = True
elif txt == _get_incmd_shortcut("Exit"):
if self.finishButton.isVisible():
self.finish()
elif txt == _get_incmd_shortcut("Close"):
if self.closeButton.isVisible():
self.closeLine()
elif txt == _get_incmd_shortcut("AddHold"):
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.addHoldPoint()
spec = True
elif txt == _get_incmd_shortcut("Snap"):
self.togglesnap()
spec = True
elif txt == _get_incmd_shortcut("Fill"):
if self.hasFill.isVisible():
self.hasFill.setChecked(not self.hasFill.isChecked())
spec = True
elif txt == _get_incmd_shortcut("Continue"):
if self.continueCmd.isVisible():
self.toggleContinue()
spec = True
elif txt == _get_incmd_shortcut("SetWP"):
if self.orientWPButton.isVisible():
self.orientWP()
spec = True
elif txt == _get_incmd_shortcut("SelectEdge"):
self.selectEdge()
spec = True
elif txt == _get_incmd_shortcut("SubelementMode"):
if self.isSubelementMode.isVisible():
self.isSubelementMode.setChecked(not self.isSubelementMode.isChecked())
spec = True
elif txt == _get_incmd_shortcut("Wipe"):
if self.wipeButton.isVisible():
self.wipeLine()
spec = True
elif txt == _get_incmd_shortcut("Undo"):
self.undoSegment()
spec = True
elif txt == _get_incmd_shortcut("IncreaseRadius"):
self.toggleradius(1)
spec = True
elif txt == _get_incmd_shortcut("DecreaseRadius"):
self.toggleradius(-1)
spec = True
if spec:
widget = self.baseWidget.focusWidget()
field = self.input_fields[widget.objectName()]
value = getattr(self, field["value"])
unit = getattr(FreeCAD.Units, field["unit"])
v = FreeCAD.Units.Quantity(value, unit).getUserPreferred()[0]
widget.setProperty("text",v)
widget.setFocus()
widget.selectAll()
self.updateSnapper()
def updateSnapper(self):
"""updates the snapper track line if applicable"""
if hasattr(FreeCADGui,"Snapper"):
if FreeCADGui.Snapper.trackLine:
if FreeCADGui.Snapper.trackLine.Visible:
last = FreeCAD.Vector(0,0,0)
if not self.xValue.isVisible():
return
if self.isRelative.isChecked():
if self.sourceCmd:
if hasattr(self.sourceCmd,"node"):
if self.sourceCmd.node:
last = self.sourceCmd.node[-1]
plane = WorkingPlane.get_working_plane(update=False)
delta = plane.get_global_coords(
FreeCAD.Vector(self.x,self.y,self.z))
FreeCADGui.Snapper.trackLine.p2(last.add(delta))
def setMouseMode(self, mode=True):
"""Sets self.mouse True (default) or False and sets a timer
to set it back to True if applicable. self.mouse is then
used by gui_tools_utils.get_point() to know if the mouse can
update field values and point position or not."""
if mode == True:
self.mouse = True
else:
delay = params.get_param("MouseDelay")
if delay:
if self.mouse is True:
self.mouse = False
QtCore.QTimer.singleShot(delay*1000, self.setMouseMode)
def checkEnterText(self):
"""this function checks if the entered text ends with two blank lines"""
t = self.textValue.toPlainText()
if t.endswith("\n\n"):
self.sendText()
def sendText(self):
"""this function sends the entered text to the active draft command
if enter has been pressed twice. Otherwise it blanks the line.
"""
self.sourceCmd.text = self.textValue.toPlainText()\
.replace("\\","\\\\")\
.replace("\"","\\\"")\
.replace("\'","\\\'")\
.splitlines()
self.sourceCmd.createObject()
def displayPoint(self, point=None, last=None, plane=None, mask=None):
"""this function displays the passed coords in the x, y, and z widgets"""
if not self.isTaskOn:
return
if not plane:
plane = WorkingPlane.get_working_plane(update=False)
# get coords to display
if not last:
if self.globalMode:
last = FreeCAD.Vector(0,0,0)
else:
last = plane.position
dp = None
if point:
dp = point
if self.relativeMode: # and (last is not None):
if self.globalMode:
dp = point - last
else:
dp = plane.get_local_coords(point - last, as_vector=True)
else:
if self.globalMode:
dp = point
else:
dp = plane.get_local_coords(point)
# set widgets
if dp:
if self.mask in ['y','z']:
self.xValue.setText(display_external(dp.x,None,'Length'))
else:
self.xValue.setText(display_external(dp.x,None,'Length'))
if self.mask in ['x','z']:
self.yValue.setText(display_external(dp.y,None,'Length'))
else:
self.yValue.setText(display_external(dp.y,None,'Length'))
if self.mask in ['x','y']:
self.zValue.setText(display_external(dp.z,None,'Length'))
else:
self.zValue.setText(display_external(dp.z,None,'Length'))
# set length and angle
if last and dp and plane:
length, theta, phi = DraftVecUtils.get_spherical_coords(*dp)
theta = math.degrees(theta)
phi = math.degrees(phi)
self.lengthValue.setText(display_external(length,None,'Length'))
#if not self.angleLock.isChecked():
self.angleValue.setText(display_external(phi,None,'Angle'))
if not mask:
# automask, phi is rounded to identify one of the below cases
phi = round(phi, Draft.precision())
if phi in [0,180,-180]:
mask = "x"
elif phi in [90,270,-90,-270]:
mask = "y"
# set masks
if (mask == "x") or (self.mask == "x"):
self.xValue.setEnabled(True)
self.yValue.setEnabled(False)
self.zValue.setEnabled(False)
self.yValue.setText("0")
self.zValue.setText("0")
self.angleValue.setEnabled(False)
self.setFocus()
elif (mask == "y") or (self.mask == "y"):
self.xValue.setEnabled(False)
self.yValue.setEnabled(True)
self.zValue.setEnabled(False)
self.xValue.setText("0")
self.zValue.setText("0")
self.angleValue.setEnabled(False)
self.setFocus("y")
elif (mask == "z") or (self.mask == "z"):
self.xValue.setEnabled(False)
self.yValue.setEnabled(False)
self.zValue.setEnabled(True)
self.xValue.setText("0")
self.yValue.setText("0")
self.angleValue.setEnabled(False)
self.setFocus("z")
else:
self.xValue.setEnabled(True)
self.yValue.setEnabled(True)
self.zValue.setEnabled(True)
self.angleValue.setEnabled(True)
self.setFocus()
def getDefaultColor(self, typ, rgb=False):
"""gets color from the preferences or toolbar"""
r = 0
g = 0
b = 0
if typ == "snap":
r, g, b, _ = utils.get_rgba_tuple(params.get_param("snapcolor"))
elif typ == "ui":
print("draft: deprecation warning: Do not use getDefaultColor(\"ui\") anymore - use getDefaultColor(\"line\") instead.")
r = float(self.color.red() / 255.0)
g = float(self.color.green() / 255.0)
b = float(self.color.blue() / 255.0)
elif typ == "line":
r, g, b, _ = utils.get_rgba_tuple(params.get_param_view("DefaultShapeLineColor"))
elif typ == "text":
r, g, b, _ = utils.get_rgba_tuple(params.get_param("DefaultTextColor"))
elif typ == "face":
r, g, b, _ = utils.get_rgba_tuple(params.get_param_view("DefaultShapeColor"))
elif typ == "constr":
r, g, b, _ = utils.get_rgba_tuple(params.get_param("constructioncolor"))
else:
print("draft: error: couldn't get a color for ", typ, " typ.")
if rgb:
return("rgb(" + str(int(r * 255)) + "," + str(int(g * 255)) + "," + str(int(b * 255)) + ")")
else:
return (r,g,b)
def cross(self,on=True):
"""deprecated"""
pass
def toggleConstrMode(self,checked):
self.baseWidget.setStyleSheet(
"#constrButton:Checked {background-color: "
+ self.getDefaultColor("constr",rgb=True)+" }")
self.constrMode = checked
def toggleContinue(self):
FreeCAD.Console.PrintMessage("toggle continue\n")
self.continueMode = not self.continueMode
try:
if hasattr(self,"continueCmd"):
if self.continueCmd.isVisible():
self.continueCmd.toggle()
if hasattr(self,"panel"):
if hasattr(self.panel,"form"):
if isinstance(self.panel.form,list):
for w in self.panel.form:
c = w.findChild(QtWidgets.QCheckBox,"ContinueCmd")
if c:
c.toggle()
else:
c = self.panel.form.findChild(QtWidgets.QCheckBox,"ContinueCmd")
if c:
c.toggle()
except Exception:
pass
def isConstructionMode(self):
return self.tray is not None and self.constrButton.isChecked()
def selectplane(self):
FreeCADGui.runCommand("Draft_SelectPlane")
def setstyle(self):
FreeCADGui.runCommand("Draft_SetStyle")
def setStyleButton(self):
"sets icon and text on the style button"
linecolor = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeLineColor")))
facecolor = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeColor")))
im = QtGui.QImage(32,32,QtGui.QImage.Format_ARGB32)
im.fill(QtCore.Qt.transparent)
pt = QtGui.QPainter(im)
pt.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap))
pt.setBrush(QtGui.QBrush(linecolor, QtCore.Qt.SolidPattern))
pts = [QtCore.QPointF(4.0,4.0), QtCore.QPointF(4.0,26.0), QtCore.QPointF(26.0,4.0)]
pt.drawPolygon(pts, QtCore.Qt.OddEvenFill)
pt.setBrush(QtGui.QBrush(facecolor, QtCore.Qt.SolidPattern))
pts = [QtCore.QPointF(28.0,28.0),QtCore.QPointF(8.0,28.0),QtCore.QPointF(28.0,8.0)]
pt.drawPolygon(pts,QtCore.Qt.OddEvenFill)
pt.end()
icon = QtGui.QIcon(QtGui.QPixmap.fromImage(im))
linewidth = params.get_param_view("DefaultShapeLineWidth")
fontsize = params.get_param("textheight")
txt = str(linewidth) + "px | "\
+ FreeCAD.Units.Quantity(fontsize,FreeCAD.Units.Length).UserString
self.styleButton.setIcon(icon)
self.styleButton.setText(txt)
# FOR BACKWARDS COMPATIBILITY
self.color = linecolor
self.facecolor = facecolor
self.linewidth = linewidth
self.fontsize = fontsize
def popupMenu(self,llist,ilist=None,pos=None):
"""pops up a menu filled with the given list"""
self.groupmenu = QtWidgets.QMenu()
for i,l in enumerate(llist):
if ilist:
self.groupmenu.addAction(ilist[i],l)
else:
self.groupmenu.addAction(l)
if not pos:
pos = FreeCADGui.getMainWindow().cursor().pos()
self.groupmenu.popup(pos)
QtCore.QObject.connect(self.groupmenu,QtCore.SIGNAL("triggered(QAction *)"),self.popupTriggered)
def getIcon(self,iconpath):
return QtGui.QIcon(iconpath)
def popupTriggered(self,action):
self.sourceCmd.proceed(str(action.text()))
def setRadiusValue(self,val,unit=None):
# print("DEBUG: setRadiusValue val: ", val, " unit: ", unit)
if not isinstance(val, (int, float)): # some code passes strings
t = val
elif unit:
t = display_external(val, None, unit)
else:
t = display_external(val, None, None)
self.radiusValue.setText(t)
self.radiusValue.setFocus()
def runAutoGroup(self):
FreeCADGui.runCommand("Draft_AutoGroup")
def setAutoGroup(self,value=None):
if value is None:
self.autogroup = None
self.autoGroupButton.setText(translate("draft", "None"))
self.autoGroupButton.setIcon(QtGui.QIcon.fromTheme('Draft_AutoGroup_off',
QtGui.QIcon(':/icons/button_invalid.svg')))
self.autoGroupButton.setToolTip(translate("draft", "Autogroup off"))
self.autoGroupButton.setDown(False)
else:
obj = FreeCAD.ActiveDocument.getObject(value)
if obj:
self.autogroup = value
self.autoGroupButton.setText(obj.Label)
self.autoGroupButton.setIcon(obj.ViewObject.Icon)
self.autoGroupButton.setToolTip(translate("draft", "Autogroup:") + " " + obj.Label)
self.autoGroupButton.setDown(False)
else:
self.autogroup = None
self.autoGroupButton.setText(translate("draft", "None"))
self.autoGroupButton.setIcon(QtGui.QIcon.fromTheme('Draft_AutoGroup_off',
QtGui.QIcon(':/icons/button_invalid.svg')))
self.autoGroupButton.setToolTip(translate("draft", "Autogroup off"))
self.autoGroupButton.setDown(False)
def getXPM(self,iconname,size=16):
i = QtGui.QIcon(":/icons/"+iconname+".svg")
p = i.pixmap(size,size)
a = QtCore.QByteArray()
b = QtCore.QBuffer(a)
b.open(QtCore.QIODevice.WriteOnly)
p.save(b,"XPM")
b.close()
return str(a)
def togglesnap(self):
FreeCADGui.doCommand('FreeCADGui.runCommand("Draft_Snap_Lock")')
def toggleradius(self,val):
if hasattr(FreeCADGui,"Snapper"):
par = params.get_param("snapRange")
params.set_param("snapRange", max(0, par+val))
FreeCADGui.Snapper.showradius()
def constrain(self,val):
if val == "angle":
self.alock = not(self.alock)
self.angleLock.setChecked(self.alock)
elif self.mask == val:
self.mask = None
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.mask = None
else:
self.mask = val
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.mask = val
def changeXValue(self,d):
self.x = d
if not self.xValue.hasFocus():
return None
self.update_spherical_coords()
def changeYValue(self,d):
self.y = d
if not self.yValue.hasFocus():
return None
self.update_spherical_coords()
def changeZValue(self,d):
self.z = d
if not self.zValue.hasFocus():
return None
self.update_spherical_coords()
def changeRadiusValue(self,d):
self.radius = d
def changeLengthValue(self,d):
self.lvalue = d
if not self.lengthValue.hasFocus():
return None
self.update_cartesian_coords()
def changeAngleValue(self,d):
self.avalue = d
if not self.angleValue.hasFocus():
return None
self.update_cartesian_coords()
if self.angleLock.isChecked():
if not self.globalMode:
plane = WorkingPlane.get_working_plane(update=False)
angle_vec = plane.get_global_coords(self.angle, as_vector=True)
else:
angle_vec = self.angle
FreeCADGui.Snapper.setAngle(angle_vec)
def toggleAngle(self,b):
self.alock = self.angleLock.isChecked()
self.update_cartesian_coords()
if self.alock:
if not self.globalMode:
plane = WorkingPlane.get_working_plane(update=False)
angle_vec = plane.get_global_coords(self.angle, as_vector=True)
else:
angle_vec = self.angle
FreeCADGui.Snapper.setAngle(angle_vec)
else:
FreeCADGui.Snapper.setAngle()
self.angle = None
def update_spherical_coords(self):
length, theta, phi = DraftVecUtils.get_spherical_coords(
self.x,self.y,self.z)
self.lvalue = length
self.pvalue = math.degrees(theta)
self.avalue = math.degrees(phi)
self.angle = FreeCAD.Vector(DraftVecUtils.get_cartesian_coords(
1, theta, phi))
self.lengthValue.setText(display_external(self.lvalue,None,'Length'))
self.angleValue.setText(display_external(self.avalue,None,'Angle'))
def update_cartesian_coords(self):
self.x, self.y, self.z = DraftVecUtils.get_cartesian_coords(
self.lvalue,math.radians(self.pvalue),math.radians(self.avalue))
self.angle = FreeCAD.Vector(DraftVecUtils.get_cartesian_coords(
1, math.radians(self.pvalue), math.radians(self.avalue)))
self.xValue.setText(display_external(self.x,None,'Length'))
self.yValue.setText(display_external(self.y,None,'Length'))
self.zValue.setText(display_external(self.z,None,'Length'))
#---------------------------------------------------------------------------
# TaskView operations
#---------------------------------------------------------------------------
def setWatchers(self):
class DraftCreateWatcher:
def __init__(self):
self.commands = ["Draft_Line","Draft_Wire",
"Draft_Rectangle","Draft_Arc",
"Draft_Circle","Draft_BSpline",
"Draft_Text","Draft_Dimension",
"Draft_ShapeString","Draft_BezCurve"]
self.title = "Create objects"
def shouldShow(self):
return (FreeCAD.ActiveDocument is not None) and (not FreeCADGui.Selection.getSelection())
class DraftModifyWatcher:
def __init__(self):
self.commands = ["Draft_Move","Draft_Rotate",
"Draft_Scale","Draft_Offset",
"Draft_Trimex","Draft_Upgrade",
"Draft_Downgrade","Draft_Edit"]
self.title = translate("draft", "Modify objects")
def shouldShow(self):
return (FreeCAD.ActiveDocument is not None) and (FreeCADGui.Selection.getSelection() != [])
FreeCADGui.Control.addTaskWatcher([DraftCreateWatcher(),DraftModifyWatcher()])
def changeEvent(self, event):
if event.type() == QtCore.QEvent.LanguageChange:
#print("Language changed!")
self.ui.retranslateUi(self)
def Activated(self):
self.setWatchers()
if hasattr(self,"tray"):
todo.delay(self.tray.show, None)
def Deactivated(self):
if (FreeCAD.activeDraftCommand is not None):
self.continueMode = False
FreeCAD.activeDraftCommand.finish()
FreeCADGui.Control.clearTaskWatcher()
#self.tray = None
if hasattr(self,"tray"):
todo.delay(self.tray.hide, None)
def reset_ui_values(self):
"""Method to reset task panel values"""
self.x = 0
self.y = 0
self.z = 0
self.lvalue = 0
self.pvalue = 90
self.avalue = 0
self.angle = None
self.radius = 0
self.offset = 0
class FacebinderTaskPanel:
'''A TaskPanel for the facebinder'''
def __init__(self):
self.obj = None
self.form = QtWidgets.QWidget()
self.form.setObjectName("FacebinderTaskPanel")
self.grid = QtWidgets.QGridLayout(self.form)
self.grid.setObjectName("grid")
self.title = QtWidgets.QLabel(self.form)
self.grid.addWidget(self.title, 0, 0, 1, 2)
# tree
self.tree = QtWidgets.QTreeWidget(self.form)
self.grid.addWidget(self.tree, 1, 0, 1, 2)
self.tree.setColumnCount(2)
self.tree.setHeaderLabels(["Name","Subelement"])
# buttons
self.addButton = QtWidgets.QPushButton(self.form)
self.addButton.setObjectName("addButton")
self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg"))
self.grid.addWidget(self.addButton, 3, 0, 1, 1)
self.delButton = QtWidgets.QPushButton(self.form)
self.delButton.setObjectName("delButton")
self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg"))
self.grid.addWidget(self.delButton, 3, 1, 1, 1)
QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement)
QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement)
self.update()
def isAllowedAlterSelection(self):
return True
def isAllowedAlterView(self):
return True
def getStandardButtons(self):
return QtWidgets.QDialogButtonBox.Ok
def update(self):
"""fills the treewidget"""
self.tree.clear()
if self.obj:
for f in self.obj.Faces:
if isinstance(f[1],tuple):
for subf in f[1]:
item = QtWidgets.QTreeWidgetItem(self.tree)
item.setText(0,f[0].Name)
item.setIcon(0, QtGui.QIcon(":/icons/Part_3D_object.svg"))
item.setText(1,subf)
else:
item = QtWidgets.QTreeWidgetItem(self.tree)
item.setText(0,f[0].Name)
item.setIcon(0, QtGui.QIcon(":/icons/Part_3D_object.svg"))
item.setText(1,f[1])
self.retranslateUi(self.form)
def addElement(self):
if self.obj:
for sel in FreeCADGui.Selection.getSelectionEx("", 0):
if sel.HasSubObjects:
obj = sel.Object
for elt in sel.SubElementNames:
if "Face" in elt:
flist = self.obj.Faces
found = False
for face in flist:
if (face[0] == obj.Name):
if isinstance(face[1],tuple):
for subf in face[1]:
if subf == elt:
found = True
else:
if (face[1] == elt):
found = True
if not found:
flist.append((obj,elt))
self.obj.Faces = flist
FreeCAD.ActiveDocument.recompute()
self.update()
def removeElement(self):
if self.obj:
it = self.tree.currentItem()
if it:
obj = FreeCAD.ActiveDocument.getObject(str(it.text(0)))
elt = str(it.text(1))
flist = []
for face in self.obj.Faces:
if (face[0].Name != obj.Name):
flist.append(face)
else:
if isinstance(face[1],tuple):
for subf in face[1]:
if subf != elt:
flist.append((obj,subf))
else:
if (face[1] != elt):
flist.append(face)
self.obj.Faces = flist
FreeCAD.ActiveDocument.recompute()
self.update()
def accept(self):
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
return True
def retranslateUi(self, TaskPanel):
TaskPanel.setWindowTitle(QtWidgets.QApplication.translate("draft", "Faces", None))
self.delButton.setText(QtWidgets.QApplication.translate("draft", "Remove", None))
self.addButton.setText(QtWidgets.QApplication.translate("draft", "Add", None))
self.title.setText(QtWidgets.QApplication.translate("draft", "Facebinder elements", None))
#def translateWidget(w, context=None, disAmb=None):
# '''translator for items where retranslateUi() is unavailable.
# translates widget w and children.'''
# #handle w itself
# if w.metaObject().className() == "QWidget":
# origText = None
# origText = w.windowTitle()
# if origText:
# newText = translate(context, str(origText))
# if newText:
# w.setWindowTitle(newText)
# #handle children
# wKids = w.findChildren(QtWidgets.QWidget)
# for i in wKids:
# className = i.metaObject().className()
# if hasattr(i,"text") and hasattr(i,"setText"):
# origText = i.text()
# newText = translate(context, str(origText))
# if newText:
# i.setText(newText)
# elif hasattr(i,"title") and hasattr(i,"setTitle"):
# origText = i.title()
# newText = translate(context, str(origText))
# if newText:
# i.setTitle(newText)
# elif hasattr(i,"itemText") and hasattr(i,"setItemText"):
# for item in range(i.count()):
# oldText = i.itemText(item)
# newText = translate(context, str(origText))
# if newText:
# i.setItemText(item,newText)
##for debugging:
## else:
## msg = "TranslateWidget: Can not translate widget: {0} type: {1}\n".format(w.objectName(),w.metaObject().className())
## FreeCAD.Console.PrintMessage(msg)
if not hasattr(FreeCADGui,"draftToolBar"):
FreeCADGui.draftToolBar = DraftToolBar()
#----End of Python Features Definitions----#