[Solved] Units in python

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

[Solved] Units in python

Post by jfc4120 »

I spent now over 2 hours on google and the forum trying to figure out how to do this in inch instead of mm, please help.

Code: Select all

TR, trflag = getDouble(None, 'example', 'throat radius', 5)
Refer to https://forum.freecadweb.org/viewtopic.php?f=22&t=70260
Last edited by jfc4120 on Tue Jul 19, 2022 6:24 am, edited 1 time in total.
heda
Veteran
Posts: 1348
Joined: Sat Dec 12, 2015 5:49 pm

Re: Units in python

Post by heda »

don't think you can, you would have to roll that on your own afaik.
could be that there is a fc-unique spinner that handles units, but dunno...

fc exclusively uses mm internally, it can however "fool" you with showing other units.
that is however just in the presentation of things, geometrically in the file it is still mm's though.

so, just put the expected unit in the dialogue description, and then convert before you use the number in any fc command.

there is a unit-handler in fc, Units, any unit-free number in fc is assumed to be in mm (or degrees, I suppose).

hang in there on the python part, it is a language made to be easy to learn and understand, although when working with libraries like qt and fc, there is not much python about it, since python is just an api-wrapper around c-libs, so how it works then is a matter of how the underlying c-libs works, which is not always straight forward.
edwilliams16
Veteran
Posts: 3179
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Units in python

Post by edwilliams16 »

The getDouble routine just returns a number. It has no units associated with it. FreeCAD internally use mm and the low-level routines for drawing lines and arcs assume mm units.
If you always want to use inches, by far the simplest thing to do is just do the conversion right after the numbers are read in.

Code: Select all

INCH_IN_MM = 25.4
CW, cwflag = getDouble(None, 'example', 'side view cheek width', 5)
CW *= INCH_IN_MM

TR, trflag = getDouble(None, 'example', 'throat radius', 5)
TR *= INCH_IN_MM
 
My version of make_line requires Vector inputs

Code: Select all

# -*- coding: utf-8 -*-
"""
example, only runs in gui (implicit imports)
if no vertex selected as start_point
(0,0,0) is assumed
makes shape in xy-plane
could as well be done with Part or Sketcher in similar ways
"""

from PySide import QtGui
import Draft


WIRE = True
INPUTININCHES = True
if INPUTININCHES:
    convert = 25.4
else:
    convert =1.0

getDouble = QtGui.QInputDialog.getDouble
Vector, Placement = App.Vector, App.Placement
doc = App.ActiveDocument

sel = Gui.Selection.getSelectionEx()
start_point = Vector(0, 0, 0)
if sel:
    sel, = sel
    if sel.PickedPoints:
        start_point = sel.PickedPoints[-1]    

CW, cwflag = getDouble(None, 'example', 'side view cheek width', 5)
CW *= convert
TR, trflag = getDouble(None, 'example', 'throat radius', 5)
TR *= convert
ANG1, ang1flag = getDouble(None, 'example', 'angle of elbow', 45)

def make_wedge(sp, cw, tr, ang):
    hline = Draft.make_line(Vector(tr, 0, 0), Vector(tr + cw, 0, 0))
    arci = Draft.make_circle(tr, startangle=0, endangle=ang)
    arco = Draft.make_circle(tr + cw, startangle=0, endangle=ang)
    doc.recompute()
    # otherwise the objects are not properly populated
    ai1, ai2 = arci.Shape.Vertexes
    ao1, ao2 = arco.Shape.Vertexes
    v2c = lambda vt: Vector(vt.X, vt.Y, vt.Z)
    aline = Draft.make_line(v2c(ai2), v2c(ao2))
    doc.recompute()
    if WIRE:
        [[wire], _] = Draft.upgrade([hline, arci, aline, arco], delete=True)
        wire.Placement.Base = sp
    else:
        for obj in (hline, arci, arco, aline):
            opl = getattr(obj, 'Placement')
            setattr(opl, 'Base', sp)
    doc.recompute()


if cwflag and trflag and ang1flag:
    make_wedge(start_point, CW, TR, ANG1)
else:
    print('bummer, not valid input')



jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Units in python

Post by jfc4120 »

In lines like this:

Code: Select all

CW, cwflag = getDouble(None, 'example', 'side view cheek width', 5)
What is cwflag? I have searched the freecad docs on python, somehow I am missing the usage of flag. And thank yo so much for replying.
edwilliams16
Veteran
Posts: 3179
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Units in python

Post by edwilliams16 »

cwFlag returns True or False depending on whether input was received. You might have pressed Cancel
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Units in python

Post by jfc4120 »

What I don't understand is these lines:

Code: Select all

CW, cwflag = getDouble(None, 'example', 'side view cheek width', 5)

but a small cw is used in setting the line:

hline = Draft.make_line(Vector(tr, 0, 0), Vector(tr + cw, 0, 0))
Same with TR and tr. Why wouldn't the capitals be use, TR and CW. Also where is the usage of flag documented

Edit:

Okay I see you are passing parameters. Is the word "flag" required?
heda
Veteran
Posts: 1348
Joined: Sat Dec 12, 2015 5:49 pm

Re: Units in python

Post by heda »

function call:

Code: Select all

make_wedge(start_point, CW, TR, ANG1)
function definition:

Code: Select all

def make_wedge(sp, cw, tr, ang):
variables are scoped (locally) in python, so in the function, due to the definition small caps are used, you could use large caps in the definition, but in reality you would then have 2 different TR without being aware of that fact... (or you could skip those completely in the definition since python if it does not find anything in the local scope takes a glance in the global if something fits - which in this case is little harm, but it is an avenue for shooting oneself in the foot with hard to find unexpected results - so i make it a habit to avoid that).

the

Code: Select all

getDouble
has nothing to do with fc, it is qt - more specifically PySide, a wrapper for qt, so...
https://pyside.github.io/docs/pyside/Py ... ialog.html
which to further complicate things is not really true, since it is really PySide2 that is used in fc, but it has been aliased to be PySide (it has to do with qt going from 4 to 5...)

before one resorts to googling anything, first give it a go to explore in the python console of fc, at minimum you can make more targeted searches on the net.
as example...

Code: Select all

>>> from PySide import QtGui
>>> getDouble = QtGui.QInputDialog.getDouble
>>> getDouble(None, 'example', 'throat radius', 5)
(5.0, True)
>>> help(getDouble)
Help on built-in function getDouble:

getDouble(...)
    getDouble(parent: PySide2.QtWidgets.QWidget, title: str, label: str, value: float, minValue: float, maxValue: float, decimals: int, ok: bool, flags: PySide2.QtCore.Qt.WindowFlags, step: float) -> float
    getDouble(parent: PySide2.QtWidgets.QWidget, title: str, label: str, value: float, minValue: float = 0, maxValue: float = -2147483647, decimals: int = 2147483647, ok: bool = 1, flags: PySide2.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> float

>>> 
the help indicates that it returns a float, which clearly is not true, since it returns a tuple, the float and a ok/cancel flag.
Last edited by heda on Mon Jul 18, 2022 9:17 pm, edited 1 time in total.
edwilliams16
Veteran
Posts: 3179
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Units in python

Post by edwilliams16 »

jfc4120 wrote: Mon Jul 18, 2022 8:47 pm What I don't understand is these lines:

Code: Select all

CW, cwflag = getDouble(None, 'example', 'side view cheek width', 5)

but a small cw is used in setting the line:

hline = Draft.make_line(Vector(tr, 0, 0), Vector(tr + cw, 0, 0))
Same with TR and tr. Why wouldn't the capitals be use, TR and CW. Also where is the usage of flag documented

Code: Select all

def make_wedge(sp, cw, tr, ang)
defines a function. Inside the function the arguments are sp, cw, tr, ang

The function was called with start_point, CW, TR, ANG1 as arguments.

Code: Select all

if cwflag and trflag and ang1flag:
    make_wedge(start_point, CW, TR, ANG1)
else:
    print('bummer, not valid input')
You'll need to learn at least the rudiments of python if you are going to go this route.
getDouble is an I/O routine handled by the QT library. Googling QT getDouble finds it: https://doc.qt.io/qt-6/qinputdialog.html#getDouble

Let us suppose you create the geometric objects defined by your Basic Script in FreeCAD - then what? What you intend to do with them might affect the route you take. There are almost certainly much simpler and more flexible ways of getting to your goal than doing an as-literal-as-possible translation of a Basic script.

EDIT as @heda points out, getDouble() is a python wrapper around the QT function. The documentation of the original function may or may not be illuminating. Use python's help facility.
openBrain
Veteran
Posts: 9041
Joined: Fri Nov 09, 2018 5:38 pm
Contact:

Re: [Solved] Units in python

Post by openBrain »

jfc4120 wrote: Mon Jul 18, 2022 4:28 pm I spent now over 2 hours on google and the forum trying to figure out how to do this in inch instead of mm, please help.

Code: Select all

TR, trflag = getDouble(None, 'example', 'throat radius', 5)
Refer to https://forum.freecadweb.org/viewtopic.php?f=22&t=70260
Sorry, have this tab open for a couple of days.
Probably the simpler is to define your own dialog using QuantitySpinBox, so that it will adapt automatically to user unit settings, and allow to deal with all kind of units.

Code: Select all

from PySide2 import QtWidgets

class myQuantityDlg(QtWidgets.QDialog):

	def __init__(self,parent, title, label, min, max):
		super().__init__(parent)
		self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
		self.setAttribute(QtCore.Qt.WA_WindowPropagation, True)
		self.quantity = 0
		self.setWindowTitle(title)
		lay = QtWidgets.QVBoxLayout(self)
		lay.addWidget(QtWidgets.QLabel(label, self))
		self.qsb = FreeCADGui.UiLoader().createWidget('Gui::QuantitySpinBox')
		self.qsb.setParent(self)
		self.qsb.setProperty('unit', 'mm')
		lay.addWidget(self.qsb)
		self.qsb.valueChanged.connect(self.updateValue)
		bbox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
		lay.addWidget(bbox)
		bbox.accepted.connect(self.accept)
		bbox.rejected.connect(self.reject)
		self.setLayout(lay)

	def updateValue(self, value):
		self.quantity = self.qsb.property('rawValue')

	def exec_(self):
		ret = super().exec_()
		return self.quantity, ret

# from there just code to demonstrate how to use
wid = myQuantityDlg(FreeCADGui.getMainWindow(), 'Example', 'Radius', 0, 300)
qty, ret = wid.exec_()
if ret:
	print(f"Dialog was accepted with quantity value of {qty} [raw value in mm]")
else:
	print("Dialog was rejected")
Post Reply