Skip to content

properties

IntegerProperty #

Bases: QObject

IntegerProperty class.

Build a widget to configure a integer property.

Source code in cogip/widgets/properties.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class IntegerProperty(QtCore.QObject):
    """
    IntegerProperty class.

    Build a widget to configure a integer property.
    """

    value_updated: qtSignal = qtSignal(str, int)

    def __init__(self, name: str, props: dict[str, Any], layout: QtWidgets.QGridLayout):
        """
        Class constructor.

        Arguments:
            name: property name
            props: properties of the integer property
            layout: The parent layout
        """
        super().__init__()
        self._name = name

        row = layout.rowCount()
        minimum = props.get("minimum")
        maximum = props.get("maximum")
        step = props.get("multipleOf", 1)
        help = props.get("description")
        self._slider = None

        label = QtWidgets.QLabel(props["title"])
        label.setToolTip(help)
        layout.addWidget(label, row, 0)

        self._value = QtWidgets.QSpinBox()
        self._value.setToolTip(help)
        self._value.valueChanged.connect(self.value_changed)
        if minimum is not None:
            self._value.setMinimum(minimum)
        if maximum is not None:
            self._value.setMaximum(maximum)
        self._value.setSingleStep(step)
        self._value.setValue(props["value"])
        layout.addWidget(self._value, row, 1)

        if minimum is not None and maximum is not None:
            self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
            self._slider.setToolTip(help)
            self._slider.setMinimum(minimum)
            self._slider.setMaximum(maximum)
            self._slider.setSingleStep(step)
            self._slider.setValue(props["value"])
            self._slider.valueChanged.connect(self._value.setValue)
            layout.addWidget(self._slider, row, 2)

    def value_changed(self, value):
        if self._slider:
            self._slider.setValue(value)
        self.value_updated.emit(self._name, value)

    def update_value(self, value):
        self._value.blockSignals(True)
        self._value.setValue(value)
        self._value.blockSignals(False)
        if self._slider:
            self._slider.blockSignals(True)
            self._slider.setValue(value)
            self._slider.blockSignals(False)

__init__(name, props, layout) #

Class constructor.

Parameters:

Name Type Description Default
name str

property name

required
props dict[str, Any]

properties of the integer property

required
layout QGridLayout

The parent layout

required
Source code in cogip/widgets/properties.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def __init__(self, name: str, props: dict[str, Any], layout: QtWidgets.QGridLayout):
    """
    Class constructor.

    Arguments:
        name: property name
        props: properties of the integer property
        layout: The parent layout
    """
    super().__init__()
    self._name = name

    row = layout.rowCount()
    minimum = props.get("minimum")
    maximum = props.get("maximum")
    step = props.get("multipleOf", 1)
    help = props.get("description")
    self._slider = None

    label = QtWidgets.QLabel(props["title"])
    label.setToolTip(help)
    layout.addWidget(label, row, 0)

    self._value = QtWidgets.QSpinBox()
    self._value.setToolTip(help)
    self._value.valueChanged.connect(self.value_changed)
    if minimum is not None:
        self._value.setMinimum(minimum)
    if maximum is not None:
        self._value.setMaximum(maximum)
    self._value.setSingleStep(step)
    self._value.setValue(props["value"])
    layout.addWidget(self._value, row, 1)

    if minimum is not None and maximum is not None:
        self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self._slider.setToolTip(help)
        self._slider.setMinimum(minimum)
        self._slider.setMaximum(maximum)
        self._slider.setSingleStep(step)
        self._slider.setValue(props["value"])
        self._slider.valueChanged.connect(self._value.setValue)
        layout.addWidget(self._slider, row, 2)

NumberProperty #

Bases: QObject

NumberProperty class.

Build a widget to configure a number property.

Source code in cogip/widgets/properties.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
class NumberProperty(QtCore.QObject):
    """
    NumberProperty class.

    Build a widget to configure a number property.
    """

    value_updated: qtSignal = qtSignal(str, float)

    def __init__(self, name: str, props: dict[str, Any], layout: QtWidgets.QGridLayout):
        """
        Class constructor.

        Arguments:
            name: property name
            props: properties of the number property
            layout: The parent layout
        """
        super().__init__()
        self._name = name

        row = layout.rowCount()
        minimum = props.get("minimum")
        maximum = props.get("maximum")
        step = props.get("multipleOf", 0.1)
        self._slider = None

        label = QtWidgets.QLabel(props["title"])
        layout.addWidget(label, row, 0)

        self._value = QtWidgets.QDoubleSpinBox()
        self._value.setDecimals(4)
        self._value.valueChanged.connect(self.value_changed)
        if minimum is not None:
            self._value.setMinimum(minimum)
        if maximum is not None:
            self._value.setMaximum(maximum)
        self._value.setSingleStep(step)
        self._value.setValue(props["value"])
        layout.addWidget(self._value, row, 1)

        if minimum is not None and maximum is not None:
            self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
            self._slider.setMinimum(minimum * 100)
            self._slider.setMaximum(maximum * 100)
            self._slider.setSingleStep(step * 100)
            self._slider.setValue(props["value"] * 100)
            self._slider.valueChanged.connect(lambda v: self._value.setValue(v / 100))
            layout.addWidget(self._slider, row, 2)

    def value_changed(self, value):
        if self._slider:
            self._slider.setValue(value * 100)
        self.value_updated.emit(self._name, value)

    def update_value(self, value):
        self._value.blockSignals(True)
        self._value.setValue(value)
        self._value.blockSignals(False)
        if self._slider:
            self._slider.blockSignals(True)
            self._slider.setValue(value * 100)
            self._slider.blockSignals(False)

__init__(name, props, layout) #

Class constructor.

Parameters:

Name Type Description Default
name str

property name

required
props dict[str, Any]

properties of the number property

required
layout QGridLayout

The parent layout

required
Source code in cogip/widgets/properties.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def __init__(self, name: str, props: dict[str, Any], layout: QtWidgets.QGridLayout):
    """
    Class constructor.

    Arguments:
        name: property name
        props: properties of the number property
        layout: The parent layout
    """
    super().__init__()
    self._name = name

    row = layout.rowCount()
    minimum = props.get("minimum")
    maximum = props.get("maximum")
    step = props.get("multipleOf", 0.1)
    self._slider = None

    label = QtWidgets.QLabel(props["title"])
    layout.addWidget(label, row, 0)

    self._value = QtWidgets.QDoubleSpinBox()
    self._value.setDecimals(4)
    self._value.valueChanged.connect(self.value_changed)
    if minimum is not None:
        self._value.setMinimum(minimum)
    if maximum is not None:
        self._value.setMaximum(maximum)
    self._value.setSingleStep(step)
    self._value.setValue(props["value"])
    layout.addWidget(self._value, row, 1)

    if minimum is not None and maximum is not None:
        self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self._slider.setMinimum(minimum * 100)
        self._slider.setMaximum(maximum * 100)
        self._slider.setSingleStep(step * 100)
        self._slider.setValue(props["value"] * 100)
        self._slider.valueChanged.connect(lambda v: self._value.setValue(v / 100))
        layout.addWidget(self._slider, row, 2)

PropertiesDialog #

Bases: QDialog

PropertiesDialog class

Build a modal for properties configuration.

Attributes:

Name Type Description
property_updated Signal

Qt signal emitted when a property is updated

closed Signal

Qt signal emitted when the window is hidden

Source code in cogip/widgets/properties.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class PropertiesDialog(QtWidgets.QDialog):
    """
    PropertiesDialog class

    Build a modal for properties configuration.

    Attributes:
        property_updated: Qt signal emitted when a property is updated
        closed: Qt signal emitted when the window is hidden
    """

    property_updated: qtSignal = qtSignal(dict)
    closed: qtSignal = qtSignal()

    def __init__(self, config: dict[str, Any], parent: QtWidgets.QWidget = None):
        """
        Class constructor.

        Arguments:
            config: JSON Schema of properties with current values and namespace
            parent: The parent widget
        """
        super().__init__(parent)
        self._config = config
        self._properties: OrderedDict[str, IntegerProperty | NumberProperty] = collections.OrderedDict()
        self.setWindowTitle(config["title"])
        self.setModal(False)

        layout = QtWidgets.QGridLayout()
        self.setLayout(layout)

        self.add_properties(config["properties"], layout)

        self.readSettings()

    def add_properties(self, props: dict[str, Any], layout: QtWidgets.QGridLayout, parent: str = ""):
        """
        Add properties.

        Arguments:
            props:  properties to add
            layout: parent layout
            parent: parent property name if any
        """
        for name, props in props.items():
            if parent:
                name = f"{parent}/{name}"
            match type := props["type"]:
                case "integer":
                    self._properties[name] = IntegerProperty(name, props, layout)
                    self._properties[name].value_updated.connect(self.value_updated)
                case "number":
                    self._properties[name] = NumberProperty(name, props, layout)
                    self._properties[name].value_updated.connect(self.value_updated)
                case "array":
                    values = props["value"]
                    for value in values:
                        box = QtWidgets.QGroupBox(value["title"])
                        layout.addWidget(box, layout.rowCount(), 0, 1, -1)
                        box_layout = QtWidgets.QGridLayout()
                        box.setLayout(box_layout)
                        self.add_properties(value["properties"], box_layout, parent=value["title"])
                case _:
                    logger.error(f"Unsupported property type: {type}")

    def update_values(self, config: dict[str, Any]):
        """
        Update properties with new values.

        Arguments:
            config: JSON Schema of properties with current values and namespace
        """
        for name, props in config["properties"].items():
            if property := self._properties.get(name):
                property.update_value(props["value"])

    def value_updated(self, name: str, value: int | float):
        """
        Emit updated values with namespace, name and value.
        """
        self.property_updated.emit(
            {
                "namespace": self._config["namespace"],
                "name": name,
                "value": value,
            }
        )

    def closeEvent(self, event: QtGui.QCloseEvent):
        """
        Hide the window.

        Arguments:
            event: The close event (unused)
        """
        settings = QtCore.QSettings("COGIP", "monitor")
        settings.setValue(f"properties/{self._config['namespace']}/{self._config['title']}", self.saveGeometry())

        self.closed.emit()
        event.accept()
        super().closeEvent(event)

    def readSettings(self):
        settings = QtCore.QSettings("COGIP", "monitor")
        self.restoreGeometry(settings.value(f"properties/{self._config['namespace']}/{self._config['title']}"))

__init__(config, parent=None) #

Class constructor.

Parameters:

Name Type Description Default
config dict[str, Any]

JSON Schema of properties with current values and namespace

required
parent QWidget

The parent widget

None
Source code in cogip/widgets/properties.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def __init__(self, config: dict[str, Any], parent: QtWidgets.QWidget = None):
    """
    Class constructor.

    Arguments:
        config: JSON Schema of properties with current values and namespace
        parent: The parent widget
    """
    super().__init__(parent)
    self._config = config
    self._properties: OrderedDict[str, IntegerProperty | NumberProperty] = collections.OrderedDict()
    self.setWindowTitle(config["title"])
    self.setModal(False)

    layout = QtWidgets.QGridLayout()
    self.setLayout(layout)

    self.add_properties(config["properties"], layout)

    self.readSettings()

add_properties(props, layout, parent='') #

Add properties.

Parameters:

Name Type Description Default
props dict[str, Any]

properties to add

required
layout QGridLayout

parent layout

required
parent str

parent property name if any

''
Source code in cogip/widgets/properties.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def add_properties(self, props: dict[str, Any], layout: QtWidgets.QGridLayout, parent: str = ""):
    """
    Add properties.

    Arguments:
        props:  properties to add
        layout: parent layout
        parent: parent property name if any
    """
    for name, props in props.items():
        if parent:
            name = f"{parent}/{name}"
        match type := props["type"]:
            case "integer":
                self._properties[name] = IntegerProperty(name, props, layout)
                self._properties[name].value_updated.connect(self.value_updated)
            case "number":
                self._properties[name] = NumberProperty(name, props, layout)
                self._properties[name].value_updated.connect(self.value_updated)
            case "array":
                values = props["value"]
                for value in values:
                    box = QtWidgets.QGroupBox(value["title"])
                    layout.addWidget(box, layout.rowCount(), 0, 1, -1)
                    box_layout = QtWidgets.QGridLayout()
                    box.setLayout(box_layout)
                    self.add_properties(value["properties"], box_layout, parent=value["title"])
            case _:
                logger.error(f"Unsupported property type: {type}")

closeEvent(event) #

Hide the window.

Parameters:

Name Type Description Default
event QCloseEvent

The close event (unused)

required
Source code in cogip/widgets/properties.py
232
233
234
235
236
237
238
239
240
241
242
243
244
def closeEvent(self, event: QtGui.QCloseEvent):
    """
    Hide the window.

    Arguments:
        event: The close event (unused)
    """
    settings = QtCore.QSettings("COGIP", "monitor")
    settings.setValue(f"properties/{self._config['namespace']}/{self._config['title']}", self.saveGeometry())

    self.closed.emit()
    event.accept()
    super().closeEvent(event)

update_values(config) #

Update properties with new values.

Parameters:

Name Type Description Default
config dict[str, Any]

JSON Schema of properties with current values and namespace

required
Source code in cogip/widgets/properties.py
209
210
211
212
213
214
215
216
217
218
def update_values(self, config: dict[str, Any]):
    """
    Update properties with new values.

    Arguments:
        config: JSON Schema of properties with current values and namespace
    """
    for name, props in config["properties"].items():
        if property := self._properties.get(name):
            property.update_value(props["value"])

value_updated(name, value) #

Emit updated values with namespace, name and value.

Source code in cogip/widgets/properties.py
220
221
222
223
224
225
226
227
228
229
230
def value_updated(self, name: str, value: int | float):
    """
    Emit updated values with namespace, name and value.
    """
    self.property_updated.emit(
        {
            "namespace": self._config["namespace"],
            "name": name,
            "value": value,
        }
    )