Does xlwt module support INDIRECT? - python

I used the following codes, but it doesn't work. I checked the xls. The formula is filled in correctly, but remains as a text entry. If to press ENTER to active this cell, it works.
sheet1.write(1, 1, xlwt.Formula('INDIRECT(\"\'sheet1\'!B1\")'))
I find INDIRECT is declared in xlwt\ExcelMagic.py:
all_funcs_by_name = {
# Includes Analysis ToolPak aka ATP aka add-in aka xcall functions,
# distinguished by -ve opcode.
# name: (opcode, min # args, max # args, func return type, func arg types)
# + in func arg types means more of the same.
...
'INDIRECT' : (148, 1, 2, 'R', 'VV'),
...
Can anyone suggest how to use INDIRECT formula?

xlwt has issues with compiling formulas that contain some functions that should return references. This doesn't matter in OpenOffice Calc and Gnumeric, which appear to operate on a "do what I mean" philosophy. In Excel 2003, you need to select the cell then hit F2 (forces it to decompile the formula from xlwt-generated bytecode to text) followed by Enter (makes it compile the text into bytecode that it's happy with). Working on this is very low on my list of priorities.

Related

How does vs code get intellisense hints from python comments?

I am seeing that VS Code is getting intellisense hints from the comments I put in my class functions:
def GyroDriveOnHeading(self, desiredHeading, desiredDistance):
"""
Drives the robot very straight on a given heading for a \
given distance, using the acceleration and the gyro. \
Accelerates to prevent wheel slipping. \
Gyro keeps the robot pointing on the desired heading.
Minimum distance that this will work for is about 16cm.
If you need to go a very short distance, use move_tank.
Parameters
-------------
desiredHeading: On what heading should the robot drive (float)
type: float
values: any. Best if the desired heading is close to the current heading. Unpredictable robot movement may occur for large heading differences.
default: no default value
desiredDistance: How far the robot should go in cm (float)
type: float
values: any value above 16.0. You can enter smaller numbers, but the robot will still go 16cm
default: no default value
Example
-------------
import base_robot
br = base_robot.BaseRobot()
br.GyroDriveOnHeading(90, 40) #drive on heading 90 for 40 cm
"""
Which gives me a really nice popup when I use that function:
As you can see here, since I am about to enter the first parameter, desiredHeading, the intellisense was smart enough to know that the line in the comments under "Parameters" that starts with the variable name should be the first thing displayed in the hint. And indeed, once I type the first parameter and a comma, the first line of the intellisense popup changes to show the information about desiredDistance.
But I would like to know more about how the comments should be written. I read about the numpy style guide as being close to a standard most widely adopted, but when I change the parameter documentation format to match numpy (and somethihng called Sphinx has something to do with this too, I think), the popups were not the same. Really, I just want to see the documentation on how to document (yikes!) my python code so it renders correct intellisense. For example, how can I bold a word in the middle of a sentence? Are there other formatting options available?
This is just for a middle-school robotics club, nothing like production code for real programmers. Nothing is broken, I just want to learn more about how this works.
That's it for docstrings in python, about it's introduction:
https://docs.python.org/3.10/tutorial/controlflow.html#documentation-strings
https://peps.python.org/pep-0287/
In addition, you can use the type stub of the parameter in this way.
def open(url: str, new: int = ..., autoraise: bool = ...) -> bool: ...

Dynamo Revit set formula for a parameter in a family

I am trying to add a formula to a parameter within a Revit Family.
Currently I have multiple families in a project. I run Dynamo from within that project then I extract the families that I want to modify using Dynamo standard nodes.
Then I use a python script node that goes through every selected family and find the parameter I am interested in, and assign a formula for it.
That seemed fine until I noticed that it is not assigning the formula, but it is entering it as a string — as in it is in quotes. And sure enough, the code i am using will only work with Text type parameters.
Can someone shed the light on how to assign a formula to a parameter using dynamo?
see line 32 in code below
Thanks
for family in families:
TransactionManager.Instance.ForceCloseTransaction()
famdoc = doc.EditFamily(family)
FamilyMan = famdoc.FamilyManager
found.append(family.Name)
TransactionManager.Instance.EnsureInTransaction(famdoc)
check = 0
# Loop thru the list of parameters to assign formula values to them... these are given as imput
for r in range(len(param_name_lst)):
# Loop thru the list of parameters in the current family per the families outter loop above.
for param in FamilyMan.Parameters:
#for param in FamilyMan.get_Parameter(param_name_lst[r]):
# for each of the parameters get their name and store in paramName.
paramName = param.Definition.Name
# Check if we have a match in parameter name.
if param_name_lst[r] in paramName:
if param.CanAssignFormula:
canassignformula.append(param_name_lst[r])
else:
cannotassignformula.append(param_name_lst[r])
try:
# Make sure that the parameter is not locked.
if FamilyMan.IsParameterLocked(param):
FamilyMan.SetParameterLocked(param,False)
locked.append(paraName)
# Enter formula value to parameter.
FamilyMan.SetFormula(param, param_value_lst[r])
check += 1
except:
failed.append(paramName)
else:
continue
Actually, you can access the family from the main project, and you can assign a formula automatically.... That's what i currently do, i load all the families i want in one project and run the script.
After a lot of work, i was able to figure out what i was doing wrong, and in it is not in my code... my code was fine.
The main problem is that i need to have all of my formula's dependencies lined up.... just like in manual mode.
so if my formula is:
size_lookup(MY_ID_tbl, "MY_VAR", "MY_DefaultValue", ND1,ND2)
then i need to have the following:
MY_ID_tbl should exist and be assigned a valid value, in this case it should have a csv filename. Moreover, that file should be also loaded. This is important for the next steps.
MY_VAR should be defined in that csv file, so Does ND1, ND2
The default value (My_Default_Value) should match what that csv file says about that variable...in this case, it is a text.
Needless to say, i did not have all of the above lined up as it should be, once i fixed that, my setFormula code did its job. And i had to change my process altogether, cause i have to first create the MY_ID_tbl and load the csv file which i also do using dynamo, then i go and enter the formulas using dynamo.
Revit parameters can only be assigned to a formula inside the family editor only, that is the first point, so you should run your dynamo script inside the family editor for each family which will be a waste of time and you just edit the parameter's formula manually inside each family.
and the second point, I don't even think that it is possible to set a certain parameter's formula automatically, it must be done manually ( I haven't seen anything for it in the Revit API docs).

How to handle unit conversions while interacting with FMUs?

I have a python script that filters and lists the parameters, their units and default values from a fmu using the read_model_description function from FMPy library and writes in an excel sheet (related discussion). Then using the simulate_fmu function the script simulates the fmu and writes the results with units back in the excel sheet.
In filtering the parameters and output variable, I use this line to get their units.
unit = variable.declaredType.unit if hasattr(variable.declaredType,'unit') else '-'
While interacting with the fmu, the parameter and variable values are in default SI units. I guess this is according to the FMI standard. However, in the modelDescription.xml under <UnitDefinitions> I see that there is information regarding the default SI unit to displayUnit conversion. For example:
<Unit
name="Pa">
<BaseUnit kg="1"
m="-1"
s="-2"/>
<DisplayUnit
name="bar"
factor="1E-05"/>
<DisplayUnit
name="ftH2O"
factor="0.0003345525633129686"/>
</Unit>
Is there a way to be able to get the parameter values and output variables in displayUnits if the conversion factors are already available in the modelDescription.xml?
Or is there a easier solution using python libraries like pint that can act as a wrapper around fmu to convert the units in desired unit system (i.e. SI to IP) while interacting with it?
In the FMPy source I did not find any place where unit conversion is implemented.
But all the relevant information is read in model_description.py.
The display unit information ends up in modelDescription.unitDefinitions. E.g. to convert a value val = 1.013e5 # Pa to all defined display units, the following might work:
for unit in modelDescription.unitDefinitions:
if unit.name == "Pa":
for display_unit in unit.displayUnits:
print(display_unit.name)
# not sure about the brackets here
print( (val - display_unit.offset)/display_unit.factor )
break
Take a look at the FMI Specification 2.01, chapter 2.2.2 Definition of Units (UnitDefinitions) to get the full picture.

Is there a Python equivalent to Raku's dd (i.e. "tiny data dumper")?

I would like to debug my Python code by inspecting multiple variables, dumping out their names and contents, equivalent to Raku's dd (Raku was formerly known as "Perl 6"):
The closest I've found has been mentioned in another post, which compares Python's pprint to Perl 5's Data::Dumper. However, unlike dd, neither of those outputs the name of the variable. dd in Raku is closest to the show function from the Perl 5 module Data::Show, except show additionally outputs the filename and line number.
Here is a demo of Raku's dd in action:
#!/bin/env perl6
my %a = ( :A(1), :B(2) );
my %c = ( :C(3), :D(4) );
dd %a;
dd %c;
Which, when run, results in the following :
Hash %a = {:A(1), :B(2)}
Hash %c = {:C(3), :D(4)}
(By the way, a Hash in Perl or Raku is equivalent to a dictionary in Python)
And here is the closest I have yet been able to get in Python, but it redundantly requires both the name of the variable and the variable itself:
#!/usr/bin/env python
def tiny_dd(name,x):
print(name + ' is "' + str(x) + '"')
a = { 'A':1, 'B':2}
c = { 'C':3, 'D':4}
tiny_dd('a',a)
tiny_dd('c',c)
Which, when run, results in the following:
a is "{'A': 1, 'B': 2}"
c is "{'C': 3, 'D': 4}"
Repeating a name twice when printing a value often grates people who think of variables as having unique values. In Python, however, it is often very hard to find names which reference a particular value, which makes writing a printer like the one you're looking for pretty hard, since you'd need do some very expensive looking around the name space.
That said, PySnooper has done all this heavy lifting for you and can print out a great deal of information on how a program is running, which can be very useful for debugging.
Note that in Python 3.8 you get pretty much what you're looking for with the new = syntax for f strings, which works like this (copied from the release notes):
>>> user = 'eric_idle'
>>> member_since = date(1975, 7, 31)
>>> f'{user=} {member_since=}'
"user='eric_idle' member_since=datetime.date(1975, 7, 31)"
I would like to debug my Python code by inspecting multiple variables
Then you are probably better off using the built-in debugger, pdb.
If you must fall back on debug traces (my not-so-secret vice - just please make sure you don't check them in to version control) then hard-coding the variable name in a print call is not so bad - after all, maybe you can write something more descriptive than the variable name, anyway. There is also the pprint(prettyprint) module to get nicer formatting of complex nested data structures.
But if you really want to be able to find a variable given a string with its name, the built-in locals() and globals() functions provide dicts in which you can look up local and global variables respectively. You can also find global variables (attributes) of other modules (as well as attributes of anything else that has attributes) by name using the getattr() builtin function.

xlwt can't parse IF statement in formula

i'm trying to write a formula to a series of cells by building the string. for instance i want to display the word "true" in a cell if the corresponding cell in column G contains the word "monkey" in it.
for rowi in range(totalRows):
thisRow = str(rowi)
functionString = r'IF(ISNUMBER(SEARCH("monkey",G'+thisRow+')),("true")
this will produce the string "IF(ISNUMBER(SEARCH("monkey",G1)),("true")" -- if i then pass that to xlwt.Formula...
ws.write(rowi+1, 0, xlwt.Formula(functionString))
i get this error...
File "C:\Python27\Lib\site-packages\xlwt\ExcelFormula.py", line 22, in __init__
raise ExcelFormulaParser.FormulaParseException, "can't parse formula " + s
xlwt.ExcelFormulaParser.FormulaParseException: can't parse formula IF(ISNUMBER(SEARCH("bitmap",G2)),("true")
is it possible to use this type of function with xlwt?
Ignoring the impossibility that you typed in monkey and G1 whereas the error message mentions bitmap and G2 ...
I suggest that you fire up Excel, select a cell, type = and paste your formula ... what happens? This check is always advisable before going public, and is available even if your internet connection is broken.
Notes:
(1) the parentheses around "true" are redundant
(2) simpler: ISNUMBER(SEARCH("bitmap",G2))
(3) Are you really expecting that a user will open the resulting XLS file and type text into column G?

Categories