Saving and loading script data

I’m trying to create a button to save my favorite views in the project with Shift-click and open them with a regular click. I’m struggling to use get_document_data_file though. Does anyone have any code examples for writing and reading data using this tool?

I would rather store document specific data in the document itself, preferrably in ExtensibleStorage

1 Like

Thank you, I didn’t even realize that was a thing! So would you add a boolean schema set to True for the favorited views? And then just search all the views for that schema when you want to open the favorites? I’m worried that will reduce the responsiveness of the primary command. I guess I’d prefer to save the favorite views as a string of element ids, but I’m not sure what element I’d save it on.

I’ve done something similar and stored the data with Extensible Storage using a schema that contains an array field of ElementId type. This data can then be saved as an entity to a DataStorage Element. That data can be recalled and overwritten to load and save views.

The extensible storage process is a little complex and convoluted in my opinion, but it works well when you get it working.

Yeah, that is one way of doing it, however my solution does it using a DataStorage element that has a simple string field in the ExtensibleStorage schema, where I store the list of ElementId’s as a json array. Using a datastorage object is also better in workshared environment, because you can’t edit the schema of elements borrowed by someone else. The best way is to use multiple DataStorage elements per different users.

I’ve created a free addin for this as an exercise: Favourite Views | Revit | Autodesk App Store
It is slower (the json string method) in theory, but never head performance issues with it yet.

1 Like

Ok so this may be a bit of a novice question, but schemas are basically hidden parameters (or rather, one schema can contain many sets of hidden variables), right? So what Guid do you provide when setting up the schema? Do I just make a random one and use it for anything else in my toolbar as well? How do I know it’s not already taken?

Yes they are hardcoded sets of some specific hidden parameters, with much less options than normal parameters. Their main problem is once you create the schema you can’t edit it, thats why I use a string field, with json values, it is much more flexible, but slightly slower.
Regarding the guid, yes, you have to make up a random one, you have almost no chance making up something that is already used by someone else, especially in your session.

Thanks for the advice! Only now is it occurring to me that schemas and extensible storage may not be the best option since I’ll want different users to have their own list of favorites. I suppose I could save them in a JSON format as you have suggested, but that’s a bit outside my wheelhouse. I was able to find some examples of the pyRevit get_document_data_file by searching the pyRevit GitHub though. I’ll try to remember to update here if/when I find the time to build this.

Some great points thumDer. As an alternative to json, I like using pickle to dump data to a string.

1 Like

@Archibum / @thumDer - Ok, I finally made some time to implement this using Ehsan’s examples of script.store_data and script.load_data in the pyRevit API. I’m still a little confused about how to import functions from my script.py into my config.py file though, so I was forced to duplicate the CustomData class :sweat_smile: Tips would be appreciated there…

Anyway, here’s what I’ve got. script.py:

"""Open Favorite Views
Opens your favorite views in this project, if they are available.

    Shift-Click:

    Choose your favorite views.

Author: Perry Lackowski
Tested: 2021.1
"""

from pyrevit import script, revit, forms
import sys

doc = revit.doc
uidoc = revit.uidoc

class CustomData(object):
    def __init__(self, count, element_ids):
        self._count = count
        # serializes the Revit native objects
        self._elmnt_ids = [revit.serialize(x) for x in element_ids]

    @property
    def count(self):
        return self._count

    @property
    def element_ids(self):
        # de-serializes the Revit native objects
        return [x.deserialize() for x in self._elmnt_ids]

if not script.data_exists("Favorite Views"):
    forms.alert(msg='Favorite Views not found.',
                sub_msg='Shift + Click on the button to set favorites.',
                warn_icon=False,
                exitscript=True)

saved_data = script.load_data("Favorite Views")
favorite_view_ids = saved_data.element_ids

for view_id in favorite_view_ids:
    view = doc.GetElement(view_id)
    if view:
        uidoc.RequestViewChange(view)

…and config.py:

"""Record Favorite Views
Choose views from a list to set as favorites.

Author: Perry Lackowski
Tested: 2021.1
"""

from pyrevit import script, revit, forms
import sys

favorite_views = forms.select_views(title='Select Favorite Views',
                                    button_name='Save Settings')

if not favorite_views:
    sys.exit()

if len(favorite_views) > 10:
    forms.alert(msg='You have selected more than ten views.',
                title='Warning',
                sub_msg='Opening this many favorited views at once may take some time. Do you still wish to save these settings?',
                ok=False,
                yes=True,
                no=True,
                exitscript=True)

class CustomData(object):
    def __init__(self, count, element_ids):
        self._count = count
        # serializes the Revit native objects
        self._elmnt_ids = [revit.serialize(x) for x in element_ids]

    @property
    def count(self):
        return self._count

    @property
    def element_ids(self):
        # de-serializes the Revit native objects
        return [x.deserialize() for x in self._elmnt_ids]

save_data = CustomData(
    count=len(favorite_views),
    element_ids=[view.Id for view in favorite_views]
)
        
script.store_data("Favorite Views", save_data)

I would look at not using a config file and instead keeping it all in one script. You can use something like:

if __shiftclick__:
     set_favorites()
else:
     load_favorites()
1 Like

Oh, that’s clever; I didn’t know you could do that. It looks like it doesn’t put the dot on the UI though (that indicates there is shift-click functionality). The more I think about it, I may make them separate buttons in a drop-down, that way I can add a third button which will clear the saved favorites for all your projects.