FreeCAD Property System Implementation

Object-Oriented Design in CAx Systems

CAx applications require robust management of domain-specific data and associated business logic. Following object-oriented principles, data is modeled as objects with properties, organized hierarchically through a data object model. User interfaces interact with these objects by presenting them and responding to user events.

Core Property Management Components

Property Abstraction

FreeCAD implements property management using the Observer patttern as part of its reflection mechanism. The base property class notifies containers of value changes:

void DataProperty::updateValue()
{
    PropertyLock guard(this);
    if (container) {
        container->handleChange(this);
        if (!isUpdating()) {
            UpdateLock updateLock(statusFlags);
            emitChangeSignal(*this);
        }
    }
    markAsModified();
}

void DataProperty::prepareValueChange()
{
    if (container)
        container->preChangeHandler(this);
}

Property Implementations

Concrete properties encapsulate primitive types using proxy patterns. For example, the integer property implementation:

void IntProperty::assign(int newValue)
{
    prepareValueChange();
    storedValue = newValue;
    updateValue();
}

int IntProperty::value() const
{
    return storedValue;
}

Property Container Interface

The container interface provides methods to respond to property change notifications.

Selection Handling

SelectionSingleton provides a unified interface for selection operations. SelectionObserver monitors changes through the observer pattern to synchronize with UI elements.

Property Display Architecture

View Components

The property view contains editors for both model data (App::DocumentObject) and display data (Gui::ViewProviderDocumentObject).

Tree View Implementation

The property editor extends QTreeView to display properties hierarchically:

void PropertyBrowser::refresh(PropertyModel::ItemList &&items, bool verifyDoc)
{
    documentCheck = verifyDoc;

    if (inTransaction) {
        qWarning("Selection changed during property commit");
        refreshPending = true;
        return;
    }

    QModelIndex current = currentIndex();
    QStringList path = model->getPathFromIndex(current);
    if (!path.isEmpty())
        currentSelection = path;
    
    model->rebuild(std::move(items));
    
    if (!currentSelection.isEmpty()) {
        QModelIndex idx = model->getIndexFromPath(currentSelection);
        setCurrentIndex(idx);
    }

    displayedItems = std::move(items);
    propertyOwners.clear();
    for (auto &entry : displayedItems) {
        for (auto prop : entry.properties) {
            auto owner = prop->owner();
            if (!owner) continue;
            if (documentCheck && owner->isDocumentObjectType())
                propertyOwners.insert(static_cast<:documentobject>(owner)->document());
            propertyOwners.insert(owner);
        }
    }

    if (autoExpand)
        expandAll();
}</:documentobject>

Model-Item Mapping

The property model maps application properties to view items:

PropertyItem* createViewItem(App::Property* property)
{
    const char* editorType = property->editorType();
    if (!editorType || !*editorType) {
        if (PropertyView::displayAll())
            editorType = "Gui::PropertyEditor::PropertyItem";
        else
            return nullptr;
    }
    auto item = static_cast<propertyitem>(
        PropertyItemFactory::instance().createItem(editorType));
    if (!item) {
        qWarning("Missing property editor for type %s", editorType);
    }
    return item;
}</propertyitem>

Item Factory Initialization

Property items are registered through factory initialization:

void initializePropertyItems()
{
    PropertyItem::registerClass();
    PropertySeparatorItem::registerClass();
    PropertyStringItem::registerClass();
    PropertyIntegerItem::registerClass();
    // Additional item registrations...
}

Factory Registration Macros

#define PROPERTY_ITEM_DECLARE \
public: \
    static void* createInstance(); \
    static void registerClass();

#define PROPERTY_ITEM_DEFINE(_ClassName_) \
void* _ClassName_::createInstance() { \
    return new _ClassName_(); \
} \
void _ClassName_::registerClass() { \
    new PropertyItemFactory::Producer<_ClassName_>(#_ClassName_); \
}

Property Interaction Flow

When objects are selected in tree or 3D views, property editors display associated attributes. Property modifications in editors trigger value updates through property setters, which notify containers of changes. Property changes propagate through the system to update visual representations.

Tags: FreeCAD PropertySystem ObserverPattern FactoryPattern QtFramework

Posted on Wed, 20 May 2026 05:50:35 +0000 by j007w