21. Properties

Qt provides a property system that allows classes derived from QObject to declare properties using the QtCore.Property class. It enables dynamic access, introspection, and binding through the Qt’s meta-object system. Qt’s properties are similar to Python’s native properties, making them less important in Python than in C++, but there are still situations where using Qt properties in PySide6 is necessary or recommended:

  • When exposing PySide6 objects to QML, only Qt properties are visible.
  • To animate custom widget attributes using QpropertyAnimation, the target must be defined as a Qt property.
  • They are useful for conditional styling with Qt style sheets.

Properties can include read/write methods, notifications via signals and other options1:

 1 Property(self, type: type,
 2          fget: Optional[Callable] = None,
 3          fset: Optional[Callable] = None,
 4          freset: Optional[Callable] = None,
 5          fdel: Optional[Callable] = None,
 6          doc: str = '',
 7          notify: Optional[PySide6.QtCore.Signal] = None,
 8          designable: bool = True,
 9          scriptable: bool = True,
10          stored: bool = True, user: bool = False,
11          constant: bool = False,
12          final: bool = False) -> PySide6.QtCore.Property

Although only the type parameter is not marked as optional, you should at minimum set type and fget to create a functional read-only property.

21.1 Declaring Basic Properties

PySide6 provides two ways of declaring properties:

  • Passing all the arguments to Property.
  • Using the @Property decorator, which lets you split the property declaration into multiple parts.
An icon of a clipboard-list1

You are building a custom dashboard widget for a fitness tracking app that displays current user name, steps taken and a configurable daily steps goal. To provide clean access to this data, you create a subclass of QLabel with properties: user name as read-only (get value from the OS), steps taken from sensor data and daily goal as read-write (user-configurable).

To create Qt properties in PySide6:

  1. Create a QObject subclass. Subclass QLabel, letting the caller optionally initialize it with the daily_goal parameter.

  2. Declare the backing attributes. We need three fields:

    • self._user_name. Simulate getting the currently logged-in user name and assign it to this field.
    • self._steps. This field is initialized with zero.
    • self._daily_goal, initialized with the daily_goal parameter.
  3. Declare the properties. Declare a Qt property for each of the backing attributes:

    • Use the @Property decorator to declare userName as a read-only string property, returning self._user_name. Client code will not be able to modify it.
    • Declare a getter (steps()) and setter (getSteps()) for the second property and use Property to create it. The property has the same name as the getter, i.e. steps. Note that steps is declared as a class-level attribute.
    • Use the @Property decorator to declare the dailyGoal getter and dailyGoal setter separately.

Now, in the main window code, you can access all three properties using the familiar Python syntax.

1     def set_daily_goal(self, goal):
2      self.steps_label.dailyGoal = goal

21.2 Properties Notification and Reset

Beyond basic getters and setters, the Qt property system has built-in support for resetting properties - this lets you declare a method that restores a property to its default value with a QMetaProperty.reset() call. It also supports automatic property change notifications via signals, allowing client code to receive signals whenever the value of a property changes.

An icon of a clipboard-list1

You are creating a theme selector widget for an application. The widget stores the current theme as a property (“Light”, “Dark”, or “System”). Other UI components should be notified of theme changes, and user should be able to restore the default theme.

To create a resettable property that supports change notifications:

  1. Create a QObject subclass and add a custom signal to it. We create a class for selecting the application theme - it contains a single combobox where each item represents an available theme. The property backing attribute is _theme, initially set to None. The signal, themeChanged, is emitted whenever the property value changes.

  2. Declare the property getter and setter. The property is named theme and we declare its getter and setter the same way as in the previous section.

  3. Declare the property reset method. resetTheme() sets theme to its default value, contained in _default_theme.

  4. Declare the property itself, providing the getter, setter and reset methods, and the notify signal. The theme property is a string and its setter emits the notify signal only when the new value is different from the property current value. The reset method simply calls the setter with the default theme name as the argument.

In the main window code, add the theme selector widget the layout, providing it the available themes (‘Light’, ‘Dark’ and ‘System’), We also add a push button to reset the theme, and five push buttons to demonstrate the theme change effects. When the user selects a theme, themeChanged is emitted and the theme colors are applied using a stylesheet.

When the user presses the Reset theme button, the theme property is set to its default value (‘System’). Instead of calling the reset method directly, this is done by calling QMetaProperty.reset().

21.3 Constant and Non-Stored Properties

The Qt property system supports both constant and non-stored properties. A constant property cannot have a getter method or a change notify signal. Marking a property as stored indicates that it doesn’t exist on its own but depends on other values and also indicates that it needs to be saved when storing the object’s state.

An icon of a clipboard-list1

You are building a user signup widget for a company portal. The widget uses a fixed email domain (@company.com) that applies to all users and never changes. Based on the user’s input, the widget automatically generates a username from the first and last name, and then forms the email address by combining that username with the company domain.

To create constant and non-stored properties:

  1. Subclass QWidget to create a custom widget. The company domain (‘company.com’) is constant so we just assign the string literal to a field. First and last name need to be provided by the user so we provide two line edits for their entry. Declare two properties, firstname and lastname with getters and setters.

  2. Declare a constant property. The domain property is declared as constant by setting the @Property decorator constant argument to false (the default is true).

  3. Declare non-stored properties. The username and email properties are declared as non-stored by setting the stored aragument to false and their value is provided based on firstname, lastname and domain values. Note that neither username nor email have setters declared.

Now, when you enter the user first and last name, the email field is updated automatically:

21.4 Dynamic Properties for Validation-Based Styling

Qt allows you to attach dynamic properties to any object using QObject.setProperty() by specifying a property name and value. Python natively supports dynamic properties too, which might make this Qt feature seem redundant in PySide6 applications. However, Qt style sheets support conditional widget styling based on Qt property values2 and this is where dynamic properties become useful, allowing you to flexibly (re)style widgets by setting or updating their custom properties.

An icon of a clipboard-list1

You are designing a user registration form featuring a QLineEdit field for email. To provide instant visual feedback, you set a dynamic property based on input validation (i.e., checking for valid email formats), and use Qt Style Sheets to apply the field borders, highlighting invalid entries in real-time.

To use dynamic properties for conditional widget styling:

  1. Add the widget that will be styled dynamically. Here, we need to style a line edit based on its text whenever it changes. The style changes are applied based on a simple regular expressions pattern that checks if a string is a valid email address. Validation is performed when the text changes so we connect textChanged() to a slot.

  2. Set the style sheet for it. This is where the conditional styling happens based on a custom property named isValid:

    • If the line edit does not have a dynamic property with that name (QLineEdit ) no custom styling is applied and it is styled using the application style sheet.
    • If isValid exists and is set to true (QLineEdit[isValid='true']) its border is set to color green.
    • If isValid exists and its value is false (QLineEdit[isValid='false']) its border is set to red.
  3. Set a dynamic property value to trigger conditional widget styling. In the validate_email() slot check if the line edit contains empty text and if it is, remove the isValid property from the line edit object by setting it to None. If the line edit contains text, validate it using Python’s regular expressions, and set isValid accordingly. Finally, re-apply the style sheet by updating the line edit:

1 self.email_edit.style().unpolish(self.email_edit)
2 self.email_edit.style().polish(self.email_edit)
3 self.email_edit.update()

While the entered email address is invalid, the line edit has a red border:

21.5 Animating Custom Properties

The QPropertyAnimation class interpolates over Qt properties3, making it possible to animate Qt widgets.

An icon of a clipboard-list1

You are building an animated system monitor widget for a desktop app. The widget needs to be updated periodically and reusable across several indicators (CPU, RAM).

To animate a custom Qt property:

  1. Create a custom Qt widget and add a property to it. Here, the custom AnimatedBar widget is capable of displaying any indicator in the range from 0 to 100 percents using a progress bar. It also contains a label to display the current indicator value in percents. Add a custom property named value to the widget and in its setter method update the inner progress bar value and the label text. AnimatedBar needs to be reusable so external code passes a function that provides indicator values to it (data_function).

  2. Add a QPropertyAnimation object to the widget. QProperyAnimation.__init__() accepts the target object and the target property’s name as parameters. Set the animation’s duration and easing curve type as well.

  3. Animate the property. The property is animated periodically so we use a QTimer to call the refresh() slot, in which we set the animation start and end values and start it.

Now, when you start the application, it shows two bars that animate the machine CPU and memory values and are updated each second. Note that you don’t set the property value explicitly but with calling QPropertyAnimation.setEndValue().

21.6 Inspecting Properties with QMetaObject

Qt’s meta-object system lets you inspect both static and dynamic properties of any QObject at runtime.

An icon of a clipboard-list1

You are building a property inspector widget for a desktop app. Given a target widget, the inspector lists all its static properties via QMetaObject followed by any dynamic properties.

To inspect properties with QMetaObject:

  1. Create a widget that inspects and displays an object’s properties. The widget name is PropertyInspector and its __init__() accepts the target widget as a parameter. It has a button to inspect the target object properties and a read-only text edit to display the results.

  2. Implement a method to inspect the target object’s properties. The inspect() slot retrieves the targets metaObject(), loops from propertyOffset() to propertyCount() toread static properties, then loops over dynamicProeprtyNames() to read dynamic properties. Use a try/except block when reading values as some of them may not be readable from Python.

  3. Create a property inspector object and add it to the main window. The example uses a QlineEdit as the target widget, setting its object name and adding one dynamic property with setProperty() so both static and dynamic properties are shown.

Now, when you start the application and click the Inspect Properties button, the text edit shows every static proeprty of the QlineEdit (objectName, text, placeholderText, etc.) followed by the dynamic property extraData that you added at runtime.


  1. If you don’t implement __init__() in your class at all, the parent class gets initalized automatically.↩︎

  2. https://doc.qt.io/qtforpython-6/↩︎

  3. https://doc.qt.io/↩︎