Change family and type of an element with Revit API - python-3.x

I'm trying to build a script that changes the family and the type of an element from a selected element in the model in Revit. I've tried what I've been doing for the rest of the paramaters of the element:
from Autodesk.Revit.DB import Transaction
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
transaction = Transaction(doc, "Modify element")
transaction.Start()
gdict = globals()
max_elements = 100
if uidoc:
selection = [doc.GetElement(x) for x in uidoc.Selection.GetElementIds()]
for idx, el in enumerate(selection):
if idx < max_elements:
gdict['e{}'.format(idx+1)] = el
print(el.LookupParameter('Type').AsValueString())
update = el.LookupParameter('Type').SetValueString('ANG')
transaction.Commit()
This way I can access to the type parameter, but it's not changing at all (it's the same for the family parameter, but again, nothing changes after running the code).
I'm assuming this must be because of the relation between Type and Family. Do you have any idea how to change this?
Thank you in advance!

Yes.
Use the Element.ChangeTypeId method.

Related

Can I dynamically set a PXDataFieldAssign parameter of a PXDataFieldParam object?

I have code that sets the PXDataFieldAssign value as follows:
pf = new PXDataFieldAssign<xTACProjectTask.dueDate>(someValue);
I also have a table, holding the DAC field names, such as "xTACProjectTask.dueDate". This table also has a checkbox field to determine whether to use this DAC field as a parameter.
Is there a way to not have the DAC fieldname hard-coded, and instead (maybe using a 'typeof' call?) use the results of the table query to set that field name - like the following?
pf = new PXDataFieldAssign<typeof("xTACProjectTask.dueDate")>(someValue);
or, using my query result:
pf = new PXDataFieldAssign<typeof(query.value)>(someValue);
with query.value being the value in the table holding the DAC field name?
You can create it using Type.GetType and Activator.CreateInstance. Please see the example below:
string typeName = "PX.Objects.IN.InventoryItem+descr,PX.Objects";
Type typeArgument = Type.GetType(typeName);
Type genericClass = typeof(PXDataFieldAssign<>);
Type constructedClass = genericClass.MakeGenericType(typeArgument);
object created = Activator.CreateInstance(constructedClass,new object[] { "Test Description" });
You will get the below wrapped into object in the created

exiting lot_id in stock.move.line, showing same value from compute field

I got some issue that lot_name is not showing same value from another compute field in same recordset, stock.move.line. below is my code:
class StockMoveLine(models.Model):
_name = 'stock.move.line'
_inherit = 'stock.move.line'
lotter = fields.Char(string="Supplier Lot", compute='_compute_partner')
def _compute_partner(self):
if not self.lotter:
partner_id = self.env['stock.picking'].search([('name','=',self.reference)]).partner_id.id
self.lotter = str(partner_id)
if self.lot_name == "":
self.lot_name = self.lotter
else:
self.lot_name = "blank"
the lot_name had been already existed in base module field. So I would like to show partner_id in lot_name field as well. now I only see it in my new compute fieldl. I tried using #api.onchange but it is only work when the textfield of lotter is lost focus. how can I do to show the same value on both lotter and lot_name fields if there is no value earlier?
Add store=True in your field.Because compute field is not stored in your database.
lotter = fields.Char(string="Supplier Lot", compute='_compute_partner', store=True)

"TypeError: product_show() missing 1 required positional argument: 'Option'"

I have build up a menu with numbers, and my selection variable is Option with type int. 5-Hoodie will show all the stuff with category is Hoodie in my SQL database. I have created a module and I am using the class named LunaPy
Option = int(input("Option: "))
LunaPy.product_show(Option)
I am using SQL library in Python
def product_show(self,Option):
product_dict ={1:"Belt",2:"Blazer",3:"Coat",4:"Dress",5:"Hoodie",6:"Jacket",7:"Jeans",8:"Pants",9:"Shoes",10:"Shorts",11:"Sunglasses",12:"Sweater",13:"SweatShirt",14:"TShirt",15:"Underwear"}
query = "SELECT * FROM LunaPyDataBase WHERE Category = ?"
self.cursor.execute(query,(product_dict[Option],))
I expected the Option variable would return the value to the function so the function can use that to select the category in dictionary. And prints the items in that chosen category.
Change your method to this:
#staticmethod
def product_show(Option):
product_dict ={1:"Belt",2:"Blazer",3:"Coat",4:"Dress",5:"Hoodie",6:"Jacket",7:"Jeans",8:"Pants",9:"Shoes",10:"Shorts",11:"Sunglasses",12:"Sweater",13:"SweatShirt",14:"TShirt",15:"Underwear"}
query = "SELECT * FROM LunaPyDataBase WHERE Category = ?"
self.cursor.execute(query,(product_dict[Option],))
Or do this:
option = int(input("Option: "))
lunaPy = LunaPy()
lunaPy.product_show(option)
The self in your function definition points to the object instance of your LunaPy class. If your method does not require an instance of LunaPy, you can mark it as static... and then you can use it like this Class.method(), but won't be able to use any instance variables or methods of the class.
The other option is to just create the instance and call the method using that.
EDIT:
Didn't notice the self inside the function. The first option won't work, because object instance is required. The second option with lunaPy = LunaPy() should work though.

Modify selection to first element by Selection.SetElementIds

I'm having trouble writing a script that lets med select the first element in my selection. This is useful for me because i select my correct Air Terminal from a schedule (where I can see the similar air-flow which I want to use) and use the command Create Similar from the selection. The trouble is that this command does not work when multiple elements are selected. Therefore, I want the first object from the list.
This is the code which I'm trying:
from Autodesk.Revit.UI.Selection.Selection import SetElementIds
from System.Collections.Generic import List
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]
sel=[]
for i in selection:
sel.append(i.Id)
uidoc.Selection.SetElementIds(List[ElementId](sel[0]))
That will return the following error message:
Exception : Microsoft.Scripting.ArgumentTypeException: expected int, got ElementId
OK, then I'll try to replace
uidoc.Selection.SetElementIds(List[ElementId](sel[0]))
with
uidoc.Selection.SetElementIds(List[ElementId](sel[0].IntegerValue))
This seems to work, but selection is not modified
I am just starting to write RPS-scripts, but I'm hoping someone will show me what am I doing wrong here even if it is very obvious.
Thank you.
Kyrre
EDIT:
Thank you Jeremy, for solving this for me! The trick was to generate a List, not a python list. .Add method is what I did not get.
Final code if someone is interested:
from Autodesk.Revit.UI.Selection.Selection import SetElementIds
from System.Collections.Generic import List
from Autodesk.Revit.DB import ElementId
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]
sel=[]
for i in selection:
sel.append(i.Id)
ids=List[ElementId](1)
ids.Add(sel[0])
uidoc.Selection.SetElementIds(ids)
SetElementIds takes a .NET ICollection<ElementId> argument, as you can see from the Revit API documentation.
Your statement calls the .NET List constructor that expects an integrer argument specifying the number N of elements to allovate space for: List[ElementId](N).
sel[0] is an ElementId, not an integer, which causes the first error.
sel[0].IntegerValue is a (very large and semi-arbitrary) integer number, so that causes no error. However, you are still leaving the List empty, unpopulated.
You should initialise the List for one single element and add that:
ids = List[ElementId](1)
ids.Add(sel[0])

How to convert a Hit into a Document with elasticsearch-dsl?

Consider the following mapping for a document in ES.
class MyDoc(elasticseach_dsl.Document):
id_info = Object(IdInfo)
class IdInfo(elasticseach_dsl.InnerDoc):
id = Keyword()
type = Keyword()
Using elasticsearch-dsl, there are 2 ways of retrieving a document (that I am interested in):
Using MyDoc.search().query().execute(), that yields Hit objects
Using MyDoc.get(), that yields a MyDoc object
Here is the issue I am experiencing:
When I retrieve the same document from ES, and that document is missing, for example, the type field, I get different behaviours:
When using search(): doc being a Hit object, accessing doc.type raises a KeyError
When using get(): doc being a MyDoc object, accessing doc.type simply returns None
To workaround this discrepancy, I would like to convert a Hit instance to a MyDoc instance, so that I can always use the doc.type syntax without any errors being raised.
How can I do that?
Alternatively, is there a way that I could access Hit instances with the same behaviour as MyDoc instances?
dict_hit = hit.to_dict()
doc = YourDocument(**dict_hit)
doc.property1 # you can access the property here
I know it is a bit awkward and annoying, it used to work with versions below 6.
I found a workaround, if you take the dictionary coming out from elasticsearch response you can then ask the document class to interpret it like the following.
query = MyDoc.search()
response = query.execute()
my_doc = MyDoc.from_es(response.hits.hits[0])
We were facing this situation. In our case, is was due to the index name in the Index subclass to configure Document indices. Our model looked more or les like this:
class MyDoc(Document):
my_field = Keyword()
class Index:
name = "my-doc-v1-*"
This way, when querying for documents in indexes that match that name (for example "my-doc-v1-2022-07"), hits are automatically instantianted as MyDoc objects.
Now we have started to generate 'v2' indices, named like "my-doc-v2--000001", and then hits were not being populated as MyDoc objects.
For that to happen, we had to change Index.name to my-doc-*. That way, documents from both 'v1' and 'v2' indices are always populated automatically by the library, since they match the Index.name expression.

Resources