Posts

Checking files on startup

A few of the files for which I'm responsible at work need to be updated regularly to ensure team members and clients always have access to the latest data. The problem is that each file has a different "expiration", and I'm quite a forgetful person. To solve this issue, I created a simple script to check the last time my files were edited. If a file is expired, I get an alert to update it. This script runs in the background every time I start my computer, so I don't notice it when I don't need it. To do this, let's create a new python file. Rather than ending our file in .py , however, we'll use .pyw . This will ensure our script runs without a console window, meaning it will go unnoticed unless it finds an issue for us to address. Next, we'll add our imports and global variables. We'll create a list of the file paths we want to check and, crucially, we'll establish the maximum age of the files. To kee...

Create Dimensions - Rooms

Image
In this post, we discussed creating dimensions with the Revit API. But we only discussed dimensioning walls. Sometimes, however, we want to dimensions to include other elements - balconies, for instance, which are not typically drawn with wall elements. In this case, we can use our room separation lines. def r2(num): return round(num, 2) doc = DocumentManager.Instance.CurrentDBDocument rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().ToElements() Once that's done, we can iterate through all these lines to find their maximum extents (for the purposes of this post, I'll assume we just want to dimension the rooms' x extents). leftmost_x = float('inf') rightmost_x = 0 line_left = None line_right = None for line in roomlines: info = line.Location.Curve direction = info.Direction origin = info.Origin if r2...

AutoHotKey

Over the weekend, I spent some time exploring the potential of AutoHotKey . This programme was not designed specifically for architects, but it has useful applications in the field nevertheless. Essentially, this is a programming language that lets users code their own keyboard shortcuts. The AHK file has to be open for it to work, but by appending a script file to the startup folder (accessable on windows by hitting Windows+R and entering "shall:startup" into the resulting pop-up window), you can open and run it in the background whenever your computer starts up. I've only spent a couple of hours playing around with it, but already, I've created a few cool shortcuts I'd like to share. Let's start with a super simple one: #q:: { Send "benjaminjamesrowley@gmail.com" } Let's go line by line: Declare a keyboard combination. I've chosen Windows (indicated by the #) + Q. The double colon indic...

Exporting Sheets as PDFs

In this post, we discussed the automatic creation and population of view sheets. We can also automate the export those sheets as PDFs - in my company, for instance, we do this every week to ensure that our clients, who do not use Revit itself, always have access to relatively up-to-date design information. To start, we'll collect all the sheets in our file and filter for the ones we want to export. There are two things to note here. Firstly, regardless of how many sheets we want to export, we need a list (if we only want one, we'll have a list containing a single item). Secondly, the list should not contain the sheet objects themselves but their IDs. doc = DocumentManager.Instance.CurrentDBDocument sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements() names_of_sheets_to_export = ["Lorem", "Ipsum"] sheets_to_export = [sheet.Id for sheet in sheets if sheet.Name in names_of_sheets_to_export] Once we have...

Using Excel in Automations

Let's say you want your non-coding coworkers to be able to control certain results of your scripts. There are several ways to do this, but by far the simplest is to create a shared Excel file. Your coworkers can input values into this file, which can then be read by your script at execution time. There are countless possible use-cases for this method, but for this example, we'll keep it simple and look at creating levels at certain elevations. Setting Up Excel Before we create any code, we need to set up our Excel file. Make sure to save it somewhere your coworkers can access. We'll create two columns, one called "LevelName" and the other "Elevation". We'll call our sheet "New Level Names". NOTE: Once the file is created, we'll want to add guards to make sure our script runs smoothly. We don't want people to be able to add non-integers to the Elevation column, for instance. For more information on protectin...

Creating and Editing Schedules

Schedules can be created and adjusted via the Revit API. In this example, we'll keep things simple and create a schedule that lists all the walls in our file and their lengths. To do this, we'll use ViewSchedule.CreateSchedule(), which takes three arguments: The document we want to add our sheet to. The Element ID of the category we want to analyse. The Element ID of the area scheme we want to use. We won't be looking at areas in this post, so we'll pass an invalid element ID to skip it. doc = DocumentManager.Instance.CurrentDBDocument wall_category_id = ElementId(BuiltInCategory.OST_Walls) TransactionManager.Instance.EnsureInTransaction(doc) new_schedule = ViewSchedule.CreateSchedule(doc, wall_category_id, ElementId(-1)) #ElementId(-1) is an invalid ID. It is used to skip the area scheme. TransactionManager.Instance.TransactionTaskDone() That should have created a new schedule. If we open that schedule, howeve...

Creating and Populating View Sheets

View sheets can be created and managed automatically using the Revit API. Creating a Sheet To start, we'll keep things simple and discuss adding a sheet to a file. To do that, we'll use the ViewSheet.Create() method, which takes two arguments: The document we want to add our sheet to. The ID of the title block we want the view sheet to use. doc = DocumentManager.Instance.CurrentDBDocument #Get the ID of the TitleBlock we want to create titleblocks = Fec(doc).OfCategory(Bic.OST_TitleBlocks).WhereElementIsElementType() for titleblock in titleblocks: name = titleblock.get_Parameter(BuiltInParameter.SYMBOL_NAME_PARAM).AsString() if name == "lorem_ipsum": target_titleblock_id = titleblock.Id break #Create a ViewSheet of the established target type TransactionManager.Instance.EnsureInTransaction(doc) new_sheet = ViewSheet.Create(doc, target_titleblock_id) TransactionMana...

Overwriting Parameters

To overwrite parameters via the Revit API, we need to get the parameter, then pass it a value of the type it expects. If you don't already have it, I would recommend installing Revit Lookup . This tool is always useful, but for what we're doing here, I'd go so far as to say it's virtually essential. In this example, let's pretend we have a big meeting in Germany, requiring us to change all our room names. We'll start by creating a simple dictionary of the room names we need to change. eng_to_de_roomnames = { "Bedroom": "Schlafzimmer", "Living Room": "Wohnzimmer", "Kitchen": "Kueche" } Then, we'll collect all the rooms in our document and iterate through them. (NOTE: When working iterating through rooms, it's always a good idea to check that the room has a location before trying to do anything to it. This is because when a room is deleted...

Manage Global Parameters

Global Parameters can be renamed, deleted, or added or changed via the Revit API. This is especially useful in office contexts, where it is sometimes necessary to update dozens of parameters across hundreds of files, a task that would take us (or a student) days to finish by hand. Let's say we want to change the global parameters in our office so they match the following: Apple: 10 mm Banana: "Example String" Cherry: Yes We'll start by converting that into a python dictionary: global_parameters_should_be = { "Apple": { "Type": "Length", "Value": 10/304.8 #Revit always works in feet, regardless of your project settings. }, "Banana": { "Type": "String", "Value": "Example String" }, "Cherry": { "Type": "Boolean"...

Change Dimension Type

Image
In this post, we discussed creating dimensions with the Revit API. But that script doesn't give us any control over dimension types, which might be an issue. The first time I ran it, for instance, it returned a dimension with nine decimal places: Today, we'll talk about creating and customising a new dimension type, so we can have our new dimensions look exactly as we'd like. For the sake of argument, we'll say we want to convert our dimension to millimetres, with one decimal place. Unfortunately, it's not possible (as far as I know) to create a new dimension type from nothing. We need to duplicate an existing type and edit its format options. (For the sake of simplicity, we'll assume that all our existing dimensions are (and should remain) of the same type. doc = DocumentManager.Instance.CurrentDBDocument dimensions = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Dimensions).WhereElementIsNotElementType()...

Create dimensions

Image
It is possible to automate the creation of dimensions in Revit. This process is highly customisable, but for the sake of argument, let's say we want to dimension the horizontal extents of a series of walls (to learn how to dimension room separation lines, click here ). To do this, we'll be using the NewDimension() method, which takes four arguments: View. In this case, we'll be using the doc.ActiveView, but you can specify any view you'd like. A line. This line will form the dimension. We will create this line before creating the dimension. A reference array. We need to tell the dimension which wall planes to base itself on. That way, if the wall moves, the dimension moves too. Dimension type. This input is optional. To keep this tutorial as simple as possible, we'll ignore this for now. Before we begin, let's set up a simple rounding function (to deal with tiny imprecisions in our model), and an instance of Opt...

Check which user owns an element in a shared model

When working with the Revit API in shared models, we are unable to execute commands on elements that are owned by other users. The following code snippet shows how to handle this issue. For the sake of arguement, let's imagine we want to perform an action on our walls. doc = DocumentManager.Instance.CurrentDBDocument walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType() owned_walls = [] unowned_walls = [] for wall in walls: wall_id = wall.Id tooltipInfo = WorksharingUtils.GetWorksharingTooltipInfo(doc, wall_id) owner = tooltipInfo.Owner #If you're interested, you can also check who created the element and who edited it last. creator = tooltipInfo.Creator lastchangedby = tooltipInfo.LastChangedBy if owner != "" and owner != "lorem.ipsum": #The element is owned (it doesn't belong to an empty string), and it...