With DB.Transaction()

Hello pyRevit Community :slight_smile:

I want to change my code to a “with transaction” instead of starting and commiting myself.
As my code works with start/commit, it does not work with the “with” method because I´m changing the model without a transaction, on the DuplicateViews() function. Am I doing something wrong? Happy about any advice :slight_smile:

not working:

if viewports:
    with DB.Transaction(doc, 'Create Hidden View'):
        AllViewports_DetailNumbers = AllViewports_DetailNumbers(viewports)
        ViewFromViewports = ViewFromViewports()
        DuplicatedViews = DuplicateViews(ViewFromViewports)
        NewViewports = CreateViewport(DuplicatedViews, viewports)
        ViewportTypeId = GetViewportTypeNoTitle()
        SetTypeId(NewViewports)
        ViewTemplate(ViewFromViewports,DuplicatedViews)
        SetDetailNumbers = SetDetailNumbers()

working :

t = DB.Transaction(doc, 'Create Hidden View')

t.Start()

if viewports:

    AllViewports_DetailNumbers = AllViewports_DetailNumbers(viewports)
    ViewFromViewports = ViewFromViewports()
    DuplicatedViews = DuplicateViews(ViewFromViewports)
    NewViewports = CreateViewport(DuplicatedViews, viewports)
    ViewportTypeId = GetViewportTypeNoTitle()
    SetTypeId(NewViewports)
    ViewTemplate(ViewFromViewports,DuplicatedViews)
    SetDetailNumbers = SetDetailNumbers()

if not viewports:
    UserMessage = UserMessage()

t.Commit()

@Gerhard.P
Can you share the whole code but before that rename your variable with a name the is different from your definitions.
It is general good practice to have a name for a variable that is different from what the definition results it stores.
Have a look at Pep8 python formatting convention

1 Like

Hello @Jean-Marc

Here you are:

# -*- coding: utf-8 -*-

# import from AFRY library
import Log

import clr
import sys
import time

from os.path import exists

start_time = time.time()

from pyrevit import script
from pyrevit import forms
from pyrevit import EXEC_PARAMS

from System import Array
from System.Collections.Generic import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices

from Autodesk.Revit.UI import TaskDialog, TaskDialogCommonButtons, TaskDialogResult, TaskDialogCommandLinkId, TaskDialogIcon

from RevitServices.Persistence import DocumentManager 
from RevitServices.Transactions import TransactionManager 
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

from Autodesk.Revit import DB
from Autodesk.Revit.DB import (
    ParameterFilterRuleFactory, Viewport, ViewDuplicateOption, SpecTypeId,
    FilteredElementCollector, ParameterValueProvider, Element, View, ViewType,
    ElementId, ElementParameterFilter, FilterElementIdRule, BuiltInParameter, BuiltInCategory
)

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application

def get_current_selection():
    selection_ids = uidoc.Selection.GetElementIds()
    return [doc.GetElement(id) for id in selection_ids]

def filter_viewports(elements):
    viewport_list = []
    for element in elements:
        if element.Category.Id == ElementId(BuiltInCategory.OST_Viewports):
            viewport_list.append(element)
    return viewport_list

def get_views_from_viewports(viewports):
    views_from_viewport_list = []
    for viewport in viewports:
        view_id = viewport.ViewId
        views_from_viewport_list.append(doc.GetElement(view_id))
    return views_from_viewport_list

def duplicate_views(views):
    new_views = []
    for view in views:
        duplicate_option = ViewDuplicateOption.Duplicate 
        new_view_id = view.Duplicate(duplicate_option)
        new_view = doc.GetElement(new_view_id)
        new_views.append(new_view)
    return new_views

def create_viewport(views, viewports):
    new_viewports = []
    sheets = [doc.GetElement(viewport.SheetId) for viewport in viewports]
    points = [viewport.GetBoxCenter() for viewport in viewports]
    for sheet, view, point in zip(sheets, views, points):
        try:
            new_viewport = Viewport.Create(doc, sheet.Id, view.Id, point)
        except:
            new_viewport = "View is already placed"
        new_viewports.append(new_viewport)
    return new_viewports

def get_viewport_type_no_title():
    built_in_parameter = BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM
    rule = ParameterFilterRuleFactory.CreateEqualsRule(ElementId(int(built_in_parameter)), "Viewport", False)
    filter = ElementParameterFilter(rule)
    
    viewport_types = FilteredElementCollector(doc).WhereElementIsElementType().WherePasses(filter)
    viewport_type_id = None
    for viewport_type in viewport_types:
        name_parameter = viewport_type.get_Parameter(BuiltInParameter.SYMBOL_NAME_PARAM)
        name = name_parameter.AsString()
        if "kein Titel" in name:
            viewport_type_id = viewport_type.Id
    return viewport_type_id
    
def set_type_id(viewports):
    if viewport_type_id:
        for viewport in viewports:
            viewport.ChangeTypeId(viewport_type_id)
    else:
        user_message("View Type missing", "The View Type -kein Titel- does not exist.")

def user_message(title, message):
    task_dialog = TaskDialog("AFRY")
    task_dialog.MainInstruction = title
    task_dialog.MainContent = message
    task_dialog.TitleAutoPrefix = False  
    task_dialog.MainIcon = TaskDialogIcon.TaskDialogIconInformation  
    task_dialog.CommonButtons = TaskDialogCommonButtons.Close
    user_input = task_dialog.Show()
    return user_input

def get_all_viewports_detail_numbers(viewports):
    all_viewports_detail_numbers = []
    for viewport in viewports:
        sheet_id = viewport.SheetId
        sheet = doc.GetElement(sheet_id)
        current_viewport_ids = sheet.GetAllViewports()
        current_viewports = [doc.GetElement(current_viewport_id) for current_viewport_id in current_viewport_ids]
        for current_viewport in current_viewports:
            current_viewport_detail_number = current_viewport.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER).AsString()
            all_viewports_detail_numbers.append(current_viewport_detail_number)
    return all_viewports_detail_numbers

def create_detail_numbers():
    new_detail_numbers = []
    for number in selected_viewports_detail_numbers:
        new_number = number + "h"
        new_detail_numbers.append(new_number)
    return new_detail_numbers

def set_detail_numbers():
    for viewport, new_viewport in zip(viewports, new_viewports):
        new_detail_number = viewport.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER).AsString() + "h"
        if new_detail_number not in all_viewports_detail_numbers:
            set_value = new_viewport.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER).Set(new_detail_number)
        else:
            pass

def view_template(views, new_views):

    def get_view_template_names(views):
        template_names = []
        for view in views:
            template_name = None
            template_id = view.ViewTemplateId
            if template_id != ElementId.InvalidElementId:
                template = doc.GetElement(template_id)
                template_name = template.Name
                template_names.append(template_name)
        return template_names

    def set_view_template_by_name(views, template_names):
        template = None
        collector = FilteredElementCollector(doc)
        template_collector = collector.OfClass(View)
        for view, template_name in zip(views, template_names):
            for t in template_collector:
                if template_name in t.Name and "idden" in t.Name.lower():
                    template = t
                    break
            if template is None:
                user_message("View Template missing", "The -hidden- View Template does not exist.")
            view.ViewTemplateId = template.Id
            doc.Regenerate()
    
    template_names = get_view_template_names(views)
    if template_names:
        set_view_template_by_name(new_views, template_names)
    else:
        user_message("No View Template", "Some selected views have no View Template applied.")

def main():
    elements = get_current_selection()
    viewports = filter_viewports(elements)

    if viewports:
        with DB.Transaction(doc, 'Create Hidden View'):
            all_viewports_detail_numbers = get_all_viewports_detail_numbers(viewports)
            views_from_viewports = get_views_from_viewports(viewports)
            duplicated_views = duplicate_views(views_from_viewports)
            new_viewports = create_viewport(duplicated_views, viewports)
            viewport_type_id = get_viewport_type_no_title()
            set_type_id(new_viewports)
            view_template(views_from_viewports, duplicated_views)
            set_detail_numbers()

    if not viewports:
        user_message("No View selected", "Select a View on a Sheet before running")

    Log.LOG_Entry()
    Log.Read_LOG()
    Log.Write_LOG()

    print(time.time() - start_time)

main()
IronPython Traceback:
Traceback (most recent call last):
 File , line 201, in <module>
 File , line 185, in main
 File , line 69, in duplicate_views
Exception: Attempt to modify the model outside of transaction.

The problem is that your importing DB directly from Revit instead of importing it from pyrevit. Using contextual transactions (I.E. with Transaction(doc, ‘Command Name’)) only works through pyrevits implementation of Transaction. You need to either: import DB from pyrevit and use the Transaction class defined there, or create your own Transaction class that supports context management similar to pyrevit.
https://github.com/eirannejad/pyRevit/blob/master/pyrevitlib/pyrevit/revit/db/transaction.py#L18

2 Likes

Hello @Nicholas.Miles,

But why is

t = DB.Transaction(doc, 'Create Hidden View')

t.Start()

working properly then?

@Gerhard.P

I’m not sure what your implying. That works, because that is how transactions work. You are using the Transaction class native to the Revit API, so of course it works that way.

You need to import the class from pyrevit to use it contextually. Like so.

from pyrevit.revit.db.transaction import Transaction

with Transaction('CommandName') as rvtxn:
    # do stuff
1 Like

I see, so a “with” statement is a standard python thing.
But handling a transaction with a with statement is a pyrevit thing :bulb:

Thanks for your replies :slight_smile:

1 Like

Interesting that ChatGPT is using the with “with Transaction” as a standard method but without importing from pyRevit.

Only after asking for it i get a explanation.

@Gerhard.P

“with Transaction” also “works” with the default Transaction class from the Revit API. However, it does not work the same way as pyrevits implementation. You would still need to call Start() within the context of the Transaction. I’m also not sure if it automatically Commits or handles expections with RollBack.

Basically it would work like this, but I’m not sure about RollBack on exceptions. Ultimately, it would be identical to the default behaviour of Transaction.

from Autodesk.Revit.DB import Transaction

with Transaction(doc, 'CommandName') as rvtxn:
    rvtxn.Start()
    # do stuff
    rvtxn.Commit()
1 Like