Skip to content

settings_window

Dynamic Settings Window for pyRevit Creates a reusable WPF window for managing script configurations.

Usage

from pyrevit import script from pyrevit.forms import settings_window

settings = [ {"name": "scope", "type": "choice", "label": "Scope", "options": ["Visibility", "Active State"], "default": "Visibility"}, {"name": "set_workset", "type": "bool", "label": "Set Workset", "default": True}, {"name": "tolerance", "type": "int", "label": "Tolerance (mm)", "default": 10, "min": 0, "max": 1000}, {"name": "prefix", "type": "string", "label": "Prefix", "default": ""}, {"name": "highlight_color", "type": "color", "label": "Highlight Color", "default": "#ffff0000"}, {"name": "export_folder", "type": "folder", "label": "Export Folder", "default": ""}, {"name": "template_file", "type": "file", "label": "Template File", "default": "", "file_ext": "rvt", "files_filter": "Revit Files (.rvt)|.rvt"}, ]

if settings_window.show_settings(settings, title="My Tool Settings"): print("Settings saved!")

Classes

SettingsWindow(settings_schema, section=None, title='Settings', width=450)

Bases: WPFWindow

Dynamic settings window that generates UI from schema.

Initialize the settings window.

Parameters:

Name Type Description Default
settings_schema

List of setting definitions

required
section

Config section name

None
title

Window title

'Settings'
width

Window width in pixels

450
Source code in pyrevitlib/pyrevit/forms/settings_window.py
def __init__(
    self, settings_schema, section=None, title="Settings", width=450,
):
    """Initialize the settings window.

    Args:
        settings_schema: List of setting definitions
        section: Config section name
        title: Window title
        width: Window width in pixels
    """
    self.config = script.get_config(section)
    self.settings_schema = settings_schema
    self.window_title = title
    self.window_width = width
    self.config_section = section
    self.result = False
    self.controls = {}

    # Generate XAML
    xaml_string = self._generate_xaml()

    # Initialize WPF window with generated XAML
    forms.WPFWindow.__init__(self, xaml_string, literal_string=True)

    # Set window title
    self.Title = self.window_title

    # Populate controls with current values
    self._populate_values()

    # Wire up button events
    self.save_button.Click += self.save_clicked
    self.cancel_button.Click += self.cancel_clicked
    self.reset_button.Click += self.reset_clicked

Attributes

config = script.get_config(section) instance-attribute
settings_schema = settings_schema instance-attribute
window_title = title instance-attribute
window_width = width instance-attribute
config_section = section instance-attribute
result = False instance-attribute
controls = {} instance-attribute
Title = self.window_title instance-attribute
pyrevit_version property

Active pyRevit formatted version e.g. '4.9-beta'.

Functions

save_clicked(sender, args)

Handle save button click.

Source code in pyrevitlib/pyrevit/forms/settings_window.py
def save_clicked(self, sender, args):
    """Handle save button click."""
    errors = []
    validated_values = {}

    # Validate all values first
    for setting in self.settings_schema:
        name = setting.get("name")
        setting_type = setting.get("type", "string")
        control = self.controls.get(name)

        if not control:
            continue

        # Get value from control
        if setting_type == "bool":
            value = control.IsChecked
        elif setting_type == "choice":
            value = control.SelectedItem
        else:
            value = control.Text

        # Validate
        is_valid, validated_value, error_msg = self._validate_setting(
            setting, value
        )

        if not is_valid:
            errors.append(error_msg)
        else:
            validated_values[name] = validated_value

    # Show errors if any
    if errors:
        forms.alert("\n".join(errors), title="Validation Error", warn_icon=True)
        return

    # Only save if all validations passed
    for setting in self.settings_schema:
        name = setting.get("name")
        if name in validated_values:
            self.config.set_option(name, validated_values[name])

    # Save config
    script.save_config()

    self.result = True
    self.Close()
cancel_clicked(sender, args)

Handle cancel button click.

Source code in pyrevitlib/pyrevit/forms/settings_window.py
def cancel_clicked(self, sender, args):
    """Handle cancel button click."""
    self.result = False
    self.Close()
reset_clicked(sender, args)

Handle reset button click.

Source code in pyrevitlib/pyrevit/forms/settings_window.py
def reset_clicked(self, sender, args):
    """Handle reset button click."""
    # Confirm with user
    if forms.alert(
        "Are you sure you want to reset all settings, removing it from the .ini?",
        title="Reset Settings",
        yes=True,
        no=True,
    ):
        script.reset_config(self.config_section)
        self.result = False
        self.Close()
load_xaml(xaml_source, literal_string=False, handle_esc=True, set_owner=True)

Load the window XAML file.

Parameters:

Name Type Description Default
xaml_source str

The XAML content or file path to load.

required
literal_string bool

True if xaml_source is content, False if it is a path. Defaults to False.

False
handle_esc bool

Whether the ESC key should be handled. Defaults to True.

True
set_owner bool

Whether to se the window owner. Defaults to True.

True
Source code in pyrevitlib/pyrevit/forms/__init__.py
def load_xaml(
    self, xaml_source, literal_string=False, handle_esc=True, set_owner=True
):
    """Load the window XAML file.

    Args:
        xaml_source (str): The XAML content or file path to load.
        literal_string (bool, optional): True if `xaml_source` is content,
            False if it is a path. Defaults to False.
        handle_esc (bool, optional): Whether the ESC key should be handled.
            Defaults to True.
        set_owner (bool, optional): Whether to se the window owner.
            Defaults to True.
    """
    # create new id for this window
    self.window_id = coreutils.new_uuid()

    if not literal_string:
        wpf.LoadComponent(self, self._determine_xaml(xaml_source))
    else:
        wpf.LoadComponent(self, framework.StringReader(xaml_source))

    # set properties
    self.thread_id = framework.get_current_thread_id()
    if set_owner:
        self.setup_owner()
    self.setup_icon()
    WPFWindow.setup_resources(self)
    if handle_esc:
        self.setup_default_handlers()
merge_resource_dict(xaml_source)

Merge a ResourceDictionary xaml file with this window.

Parameters:

Name Type Description Default
xaml_source str

xaml file with the resource dictionary

required
Source code in pyrevitlib/pyrevit/forms/__init__.py
def merge_resource_dict(self, xaml_source):
    """Merge a ResourceDictionary xaml file with this window.

    Args:
        xaml_source (str): xaml file with the resource dictionary
    """
    lang_dictionary = ResourceDictionary()
    lang_dictionary.Source = Uri(xaml_source, UriKind.Absolute)
    self.Resources.MergedDictionaries.Add(lang_dictionary)
get_locale_string(string_name)

Get localized string.

Parameters:

Name Type Description Default
string_name str

string name

required

Returns:

Type Description
str

localized string

Source code in pyrevitlib/pyrevit/forms/__init__.py
def get_locale_string(self, string_name):
    """Get localized string.

    Args:
        string_name (str): string name

    Returns:
        (str): localized string
    """
    return self.FindResource(string_name)
setup_owner()

Set the window owner.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def setup_owner(self):
    """Set the window owner."""
    wih = Interop.WindowInteropHelper(self)
    wih.Owner = AdWindows.ComponentManager.ApplicationWindow
setup_resources(wpf_ctrl) staticmethod

Sets the WPF resources.

Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def setup_resources(wpf_ctrl):
    """Sets the WPF resources."""
    # 2c3e50
    wpf_ctrl.Resources["pyRevitDarkColor"] = Media.Color.FromArgb(
        0xFF, 0x2C, 0x3E, 0x50
    )

    # 23303d
    wpf_ctrl.Resources["pyRevitDarkerDarkColor"] = Media.Color.FromArgb(
        0xFF, 0x23, 0x30, 0x3D
    )

    # ffffff
    wpf_ctrl.Resources["pyRevitButtonColor"] = Media.Color.FromArgb(
        0xFF, 0xFF, 0xFF, 0xFF
    )

    # f39c12
    wpf_ctrl.Resources["pyRevitAccentColor"] = Media.Color.FromArgb(
        0xFF, 0xF3, 0x9C, 0x12
    )

    wpf_ctrl.Resources["pyRevitDarkBrush"] = Media.SolidColorBrush(
        wpf_ctrl.Resources["pyRevitDarkColor"]
    )
    wpf_ctrl.Resources["pyRevitAccentBrush"] = Media.SolidColorBrush(
        wpf_ctrl.Resources["pyRevitAccentColor"]
    )

    wpf_ctrl.Resources["pyRevitDarkerDarkBrush"] = Media.SolidColorBrush(
        wpf_ctrl.Resources["pyRevitDarkerDarkColor"]
    )

    wpf_ctrl.Resources["pyRevitButtonForgroundBrush"] = Media.SolidColorBrush(
        wpf_ctrl.Resources["pyRevitButtonColor"]
    )

    wpf_ctrl.Resources["pyRevitRecognizesAccessKey"] = DEFAULT_RECOGNIZE_ACCESS_KEY
setup_default_handlers()

Set the default handlers.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def setup_default_handlers(self):
    """Set the default handlers."""
    self.PreviewKeyDown += self.handle_input_key  # pylint: disable=E1101
handle_input_key(sender, args)

Handle keyboard input and close the window on Escape.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def handle_input_key(self, sender, args):  # pylint: disable=W0613
    """Handle keyboard input and close the window on Escape."""
    if args.Key == Input.Key.Escape:
        self.Close()
set_icon(icon_path)

Set window icon to given icon path.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def set_icon(self, icon_path):
    """Set window icon to given icon path."""
    self.Icon = utils.bitmap_from_file(icon_path)
setup_icon()

Setup default window icon.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def setup_icon(self):
    """Setup default window icon."""
    self.set_icon(op.join(BIN_DIR, "pyrevit_settings.png"))
hide()

Hide window.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def hide(self):
    """Hide window."""
    self.Hide()
show(modal=False)

Show window.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def show(self, modal=False):
    """Show window."""
    if modal:
        return self.ShowDialog()
    # else open non-modal
    self.Show()
show_dialog()

Show modal window.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def show_dialog(self):
    """Show modal window."""
    return self.ShowDialog()
set_image_source_file(wpf_element, image_file) staticmethod

Set source file for image element.

Parameters:

Name Type Description Default
wpf_element Image

xaml image element

required
image_file str

image file path

required
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def set_image_source_file(wpf_element, image_file):
    """Set source file for image element.

    Args:
        wpf_element (System.Windows.Controls.Image): xaml image element
        image_file (str): image file path
    """
    if not op.exists(image_file):
        wpf_element.Source = utils.bitmap_from_file(
            os.path.join(EXEC_PARAMS.command_path, image_file)
        )
    else:
        wpf_element.Source = utils.bitmap_from_file(image_file)
set_image_source(wpf_element, image_file)

Set source file for image element.

Parameters:

Name Type Description Default
wpf_element Image

xaml image element

required
image_file str

image file path

required
Source code in pyrevitlib/pyrevit/forms/__init__.py
def set_image_source(self, wpf_element, image_file):
    """Set source file for image element.

    Args:
        wpf_element (System.Windows.Controls.Image): xaml image element
        image_file (str): image file path
    """
    WPFWindow.set_image_source_file(wpf_element, image_file)
dispatch(func, *args, **kwargs)

Runs the function in a new thread.

Parameters:

Name Type Description Default
func Callable

function to run

required
*args Any

positional arguments to pass to func

()
**kwargs Any

keyword arguments to pass to func

{}
Source code in pyrevitlib/pyrevit/forms/__init__.py
def dispatch(self, func, *args, **kwargs):
    """Runs the function in a new thread.

    Args:
        func (Callable): function to run
        *args (Any): positional arguments to pass to func
        **kwargs (Any): keyword arguments to pass to func
    """
    if framework.get_current_thread_id() == self.thread_id:
        t = threading.Thread(target=func, args=args, kwargs=kwargs)
        t.start()
    else:
        # ask ui thread to call the func with args and kwargs
        self.Dispatcher.Invoke(
            System.Action(lambda: func(*args, **kwargs)),
            Threading.DispatcherPriority.Background,
        )
conceal()

Conceal window.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def conceal(self):
    """Conceal window."""
    return WindowToggler(self)
hide_element(*wpf_elements) staticmethod

Collapse elements.

Parameters:

Name Type Description Default
*wpf_elements list[UIElement]

WPF framework elements to be collaped

()
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def hide_element(*wpf_elements):
    """Collapse elements.

    Args:
        *wpf_elements (list[UIElement]): WPF framework elements to be collaped
    """
    for wpfel in wpf_elements:
        wpfel.Visibility = WPF_COLLAPSED
show_element(*wpf_elements) staticmethod

Show collapsed elements.

Parameters:

Name Type Description Default
*wpf_elements list[UIElement]

WPF framework elements to be set to visible.

()
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def show_element(*wpf_elements):
    """Show collapsed elements.

    Args:
        *wpf_elements (list[UIElement]): WPF framework elements to be set to visible.
    """
    for wpfel in wpf_elements:
        wpfel.Visibility = WPF_VISIBLE
toggle_element(*wpf_elements) staticmethod

Toggle visibility of elements.

Parameters:

Name Type Description Default
*wpf_elements list[UIElement]

WPF framework elements to be toggled.

()
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def toggle_element(*wpf_elements):
    """Toggle visibility of elements.

    Args:
        *wpf_elements (list[UIElement]): WPF framework elements to be toggled.
    """
    for wpfel in wpf_elements:
        if wpfel.Visibility == WPF_VISIBLE:
            WPFWindow.hide_element(wpfel)
        elif wpfel.Visibility == WPF_COLLAPSED:
            WPFWindow.show_element(wpfel)
disable_element(*wpf_elements) staticmethod

Enable elements.

Parameters:

Name Type Description Default
*wpf_elements list[UIElement]

WPF framework elements to be enabled

()
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def disable_element(*wpf_elements):
    """Enable elements.

    Args:
        *wpf_elements (list[UIElement]): WPF framework elements to be enabled
    """
    for wpfel in wpf_elements:
        wpfel.IsEnabled = False
enable_element(*wpf_elements) staticmethod

Enable elements.

Parameters:

Name Type Description Default
*wpf_elements list[UIElement]

WPF framework elements to be enabled

()
Source code in pyrevitlib/pyrevit/forms/__init__.py
@staticmethod
def enable_element(*wpf_elements):
    """Enable elements.

    Args:
        *wpf_elements (list[UIElement]): WPF framework elements to be enabled
    """
    for wpfel in wpf_elements:
        wpfel.IsEnabled = True
handle_url_click(sender, args)

Callback for handling click on package website url.

Source code in pyrevitlib/pyrevit/forms/__init__.py
def handle_url_click(self, sender, args):  # pylint: disable=unused-argument
    """Callback for handling click on package website url."""
    return webbrowser.open_new_tab(sender.NavigateUri.AbsoluteUri)

Functions

show_settings(settings_schema, section=None, title='Settings', width=450)

Show settings window and return True if saved.

Parameters:

Name Type Description Default
settings_schema

List of setting definitions. Each setting is a dict with: - name (str): Setting key name for config - type (str): "bool", "choice", "int", "float", "string", "color", "folder", or "file" - label (str): Display label for the setting - default: Default value if not in config - options (list): For "choice" type, list of options - min (int/float): For "int"/"float" type, minimum value - max (int/float): For "int"/"float" type, maximum value - required (bool): For "string" type, whether field is required - file_ext (str): For "file" type, file extension filter (e.g., "rvt") - files_filter (str): For "file" type, files filter (e.g., "Revit Files (.rvt)|.rvt") - init_dir (str): For "file" type, initial directory - multi_file (bool): For "file" type, allow multiple file selection

required
section str

Config section name (default: None)

None
title str

Window title (default: "Settings")

'Settings'
width int

Window width in pixels (default: 450)

450

Returns:

Name Type Description
bool

True if settings were saved, False if canceled

Example
settings_schema = [
    {"name": "scope", "type": "choice", "label": "Scope",
     "options": ["Visibility", "Active State"], "default": "Visibility"},
    {"name": "set_workset", "type": "bool", "label": "Set Workset", "default": True},
    {"name": "tolerance", "type": "int", "label": "Tolerance (mm)",
     "default": 10, "min": 0, "max": 1000},
    {"name": "highlight_color", "type": "color", "label": "Highlight Color",
     "default": "#ffff0000"},
    {"name": "export_folder", "type": "folder", "label": "Export Folder", "default": ""},
    {"name": "template_file", "type": "file", "label": "Template File",
     "default": "", "file_ext": "rvt", "files_filter": "Revit Files (*.rvt)|*.rvt"},
]

if show_settings(settings_schema, section="MyToolSection", title="My Tool Settings"):
    print("Settings saved!")
Source code in pyrevitlib/pyrevit/forms/settings_window.py
def show_settings(settings_schema, section=None, title="Settings", width=450):
    """Show settings window and return True if saved.

    Args:
        settings_schema: List of setting definitions. Each setting is a dict with:
            - name (str): Setting key name for config
            - type (str): "bool", "choice", "int", "float", "string", "color", "folder", or "file"
            - label (str): Display label for the setting
            - default: Default value if not in config
            - options (list): For "choice" type, list of options
            - min (int/float): For "int"/"float" type, minimum value
            - max (int/float): For "int"/"float" type, maximum value
            - required (bool): For "string" type, whether field is required
            - file_ext (str): For "file" type, file extension filter (e.g., "rvt")
            - files_filter (str): For "file" type, files filter (e.g., "Revit Files (*.rvt)|*.rvt")
            - init_dir (str): For "file" type, initial directory
            - multi_file (bool): For "file" type, allow multiple file selection
        section (str): Config section name (default: None)
        title (str): Window title (default: "Settings")
        width (int): Window width in pixels (default: 450)

    Returns:
        bool: True if settings were saved, False if canceled

    Example:
        ```python
        settings_schema = [
            {"name": "scope", "type": "choice", "label": "Scope",
             "options": ["Visibility", "Active State"], "default": "Visibility"},
            {"name": "set_workset", "type": "bool", "label": "Set Workset", "default": True},
            {"name": "tolerance", "type": "int", "label": "Tolerance (mm)",
             "default": 10, "min": 0, "max": 1000},
            {"name": "highlight_color", "type": "color", "label": "Highlight Color",
             "default": "#ffff0000"},
            {"name": "export_folder", "type": "folder", "label": "Export Folder", "default": ""},
            {"name": "template_file", "type": "file", "label": "Template File",
             "default": "", "file_ext": "rvt", "files_filter": "Revit Files (*.rvt)|*.rvt"},
        ]

        if show_settings(settings_schema, section="MyToolSection", title="My Tool Settings"):
            print("Settings saved!")
        ```
    """
    window = SettingsWindow(settings_schema, section, title, width)
    window.ShowDialog()
    return window.result