QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ParameterEditor.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5
6import QGroundControl
7import QGroundControl.Controls
8import QGroundControl.FactControls
9
10Item {
11 id: _root
12
13 property Fact _editorDialogFact: Fact { }
14 property int _rowHeight: ScreenTools.defaultFontPixelHeight * 2
15 property int _rowWidth: 10 // Dynamic adjusted at runtime
16 property bool _searchFilter: searchText.text.trim() != "" || controller.showModifiedOnly || controller.showFavoritesOnly ///< true: showing results of search
17 property var _searchResults ///< List of parameter names from search results
18 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
19 property bool _showRCToParam: _activeVehicle.px4Firmware
20 property var _appSettings: QGroundControl.settingsManager.appSettings
21 property var _controller: controller
22 property var _favorites: controller.favoriteParameterNames
23 property real _margins: ScreenTools.defaultFontPixelHeight / 2
24
25 ParameterEditorController {
26 id: controller
27 }
28
29 Timer {
30 id: clearTimer
31 interval: 100;
32 running: false;
33 repeat: false
34 onTriggered: {
35 searchText.text = ""
36 controller.searchText = ""
37 }
38 }
39
40 QGCMenu {
41 id: toolsMenu
42 QGCMenuItem {
43 text: qsTr("Refresh")
44 onTriggered: controller.refresh()
45 }
46 QGCMenuItem {
47 text: qsTr("Reset all to firmware's defaults")
48 onTriggered: QGroundControl.showMessageDialog(_root, qsTr("Reset All"),
49 qsTr("Select Reset to reset all parameters to their defaults.\n\nNote that this will also completely reset everything, including UAVCAN nodes, all vehicle settings, setup and calibrations."),
50 Dialog.Cancel | Dialog.Reset,
51 function() { controller.resetAllToDefaults() })
52 }
53 QGCMenuItem {
54 text: qsTr("Reset to vehicle's configuration defaults")
55 visible: !_activeVehicle.apmFirmware
56 onTriggered: QGroundControl.showMessageDialog(_root, qsTr("Reset All"),
57 qsTr("Select Reset to reset all parameters to the vehicle's configuration defaults."),
58 Dialog.Cancel | Dialog.Reset,
59 function() { controller.resetAllToVehicleConfiguration() })
60 }
61 QGCMenuSeparator { }
62 QGCMenuItem {
63 text: qsTr("Load from file for review...")
64 onTriggered: {
65 fileDialog.title = qsTr("Load Parameters")
66 fileDialog.openForLoad()
67 }
68 }
69 QGCMenuItem {
70 text: qsTr("Save to file...")
71 onTriggered: {
72 fileDialog.title = qsTr("Save Parameters")
73 fileDialog.openForSave()
74 }
75 }
76 QGCMenuSeparator { }
77 QGCMenuItem {
78 text: qsTr("Clear all favorites")
79 onTriggered: controller.clearAllFavorites()
80 }
81 QGCMenuSeparator { visible: _showRCToParam }
82 QGCMenuItem {
83 text: qsTr("Clear all RC to Param")
84 onTriggered: _activeVehicle.clearAllParamMapRC()
85 visible: _showRCToParam
86 }
87 QGCMenuSeparator { }
88 QGCMenuItem {
89 text: qsTr("Reboot Vehicle")
90 onTriggered: QGroundControl.showMessageDialog(_root, qsTr("Reboot Vehicle"),
91 qsTr("Select Ok to reboot vehicle."),
92 Dialog.Cancel | Dialog.Ok,
93 function() { _activeVehicle.rebootVehicle() })
94 }
95 }
96
97
98 QGCFileDialog {
99 id: fileDialog
100 folder: _appSettings.parameterSavePath
101 nameFilters: [ qsTr("Parameter Files (*.%1)").arg(_appSettings.parameterFileExtension) , qsTr("All Files (*)") ]
102
103 onAcceptedForSave: (file) => {
104 controller.saveToFile(file)
105 close()
106 }
107
108 onAcceptedForLoad: (file) => {
109 close()
110 if (controller.buildDiffFromFile(file)) {
111 parameterDiffDialogFactory.open()
112 }
113 }
114 }
115
116 QGCPopupDialogFactory {
117 id: editorDialogFactory
118
119 dialogComponent: editorDialogComponent
120 }
121
122 Component {
123 id: editorDialogComponent
124
125 ParameterEditorDialog {
126 fact: _editorDialogFact
127 showRCToParam: _showRCToParam
128 }
129 }
130
131 QGCPopupDialogFactory {
132 id: parameterDiffDialogFactory
133
134 dialogComponent: parameterDiffDialog
135 }
136
137 Component {
138 id: parameterDiffDialog
139
140 ParameterDiffDialog {
141 paramController: _controller
142 }
143 }
144
145 RowLayout {
146 id: header
147 anchors.left: parent.left
148 anchors.right: parent.right
149
150 RowLayout {
151 Layout.alignment: Qt.AlignLeft
152 spacing: ScreenTools.defaultFontPixelWidth
153
154 QGCTextField {
155 id: searchText
156 placeholderText: qsTr("Search")
157 onDisplayTextChanged: controller.searchText = displayText
158 }
159
160 QGCButton {
161 text: qsTr("Clear")
162 onClicked: {
163 if(ScreenTools.isMobile) {
164 Qt.inputMethod.hide();
165 }
166 clearTimer.start()
167 }
168 }
169
170 QGCCheckBox {
171 text: qsTr("Hide read-only")
172 checked: controller.hideReadOnly
173 onClicked: controller.hideReadOnly = checked
174 }
175 }
176
177 QGCButton {
178 Layout.alignment: Qt.AlignRight
179 text: qsTr("Tools")
180 onClicked: toolsMenu.popup()
181 }
182 }
183
184 QGCTabBar {
185 id: tabBar
186 anchors.left: parent.left
187 anchors.right: parent.right
188 anchors.top: header.bottom
189 anchors.topMargin: _margins
190
191 QGCTabButton { text: qsTr("Full List") }
192 QGCTabButton { text: qsTr("Modified") }
193 QGCTabButton { text: qsTr("Favorites") }
194
195 onCurrentIndexChanged: {
196 controller.showModifiedOnly = (currentIndex === 1)
197 controller.showFavoritesOnly = (currentIndex === 2)
198 }
199 }
200
201 /// Group buttons
202 QGCFlickable {
203 id : groupScroll
204 width: ScreenTools.defaultFontPixelWidth * 25
205 anchors.top: tabBar.bottom
206 anchors.topMargin: _margins
207 anchors.bottom: parent.bottom
208 clip: true
209 pixelAligned: true
210 contentHeight: groupedViewCategoryColumn.height
211 flickableDirection: Flickable.VerticalFlick
212 visible: !_searchFilter
213
214 ColumnLayout {
215 id: groupedViewCategoryColumn
216 anchors.left: parent.left
217 anchors.right: parent.right
218 spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25)
219
220 Repeater {
221 model: controller.categories
222
223 Column {
224 Layout.fillWidth: true
225 spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25)
226
227
228 SectionHeader {
229 id: categoryHeader
230 anchors.left: parent.left
231 anchors.right: parent.right
232 text: object.name
233 checked: object == controller.currentCategory
234
235 onCheckedChanged: {
236 if (checked) {
237 controller.currentCategory = object
238 }
239 }
240 }
241
242 Repeater {
243 model: categoryHeader.checked ? object.groups : 0
244
245 QGCButton {
246 width: ScreenTools.defaultFontPixelWidth * 25
247 text: object.name
248 height: _rowHeight
249 checked: object == controller.currentGroup
250 autoExclusive: true
251
252 onClicked: {
253 if (!checked) _rowWidth = 10
254 checked = true
255 controller.currentGroup = object
256 }
257 }
258 }
259 }
260 }
261 }
262 }
263
264 HorizontalHeaderView {
265 id: headerView
266 anchors.left: tableView.left
267 anchors.right: tableView.right
268 anchors.top: tabBar.bottom
269 anchors.topMargin: _margins
270 syncView: tableView
271 clip: true
272
273 delegate: Rectangle {
274 implicitWidth: column === 0 ? ScreenTools.implicitCheckBoxHeight + ScreenTools.defaultFontPixelWidth
275 : headerLabel.contentWidth + ScreenTools.defaultFontPixelWidth
276 implicitHeight: headerLabel.contentHeight + ScreenTools.defaultFontPixelHeight * 0.5
277 color: qgcPal.windowShade
278
279 QGCLabel {
280 id: headerLabel
281 anchors.left: parent.left
282 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
283 anchors.verticalCenter: parent.verticalCenter
284 text: display
285 font.bold: true
286 }
287
288 // Top border
289 Rectangle {
290 anchors.top: parent.top
291 width: parent.width
292 height: 1
293 color: qgcPal.groupBorder
294 }
295
296 // Left border
297 Rectangle {
298 anchors.left: parent.left
299 height: parent.height
300 width: 1
301 color: qgcPal.groupBorder
302 }
303
304 // Right border (last column only)
305 Rectangle {
306 anchors.right: parent.right
307 height: parent.height
308 width: 1
309 color: qgcPal.groupBorder
310 visible: column == 3
311 }
312
313 // Bottom border
314 Rectangle {
315 anchors.bottom: parent.bottom
316 width: parent.width
317 height: 1
318 color: qgcPal.groupBorder
319 }
320 }
321 }
322
323 TableView {
324 id: tableView
325 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
326 anchors.top: headerView.bottom
327 anchors.bottom: parent.bottom
328 anchors.left: _searchFilter ? parent.left : groupScroll.right
329 anchors.right: parent.right
330 columnSpacing: 0
331 rowSpacing: 0
332 model: controller.parameters
333 contentWidth: width
334 clip: true
335
336 // Qt is supposed to adjust column widths automatically when larger widths come into view.
337 // But it doesn't work. So we have to do it force a layout manually when we scroll.
338 Timer {
339 id: forceLayoutTimer
340 interval: 500
341 repeat: false
342 onTriggered: tableView.forceLayout()
343 }
344
345 onTopRowChanged: forceLayoutTimer.start()
346 onModelChanged: {
347 positionViewAtRow(0, TableView.AlignLeft | TableView.AlignTop)
348 forceLayoutTimer.start()
349 }
350
351 delegate: Rectangle {
352 implicitWidth: column === 0 ? ScreenTools.implicitCheckBoxHeight + ScreenTools.defaultFontPixelWidth
353 : column === 1 ? nameRow.implicitWidth + ScreenTools.defaultFontPixelWidth
354 : column === 2 ? ScreenTools.defaultFontPixelWidth * 16
355 : label.contentWidth + ScreenTools.defaultFontPixelWidth
356 implicitHeight: label.contentHeight + ScreenTools.defaultFontPixelHeight * 0.5
357 color: row % 2 === 0 ? "transparent" : qgcPal.windowShade
358 clip: true
359
360 // Bottom grid line
361 Rectangle {
362 anchors.bottom: parent.bottom
363 width: parent.width
364 height: 1
365 color: qgcPal.groupBorder
366 }
367
368 // Left grid line
369 Rectangle {
370 anchors.left: parent.left
371 height: parent.height
372 width: 1
373 color: qgcPal.groupBorder
374 }
375
376 // Right grid line (last column only)
377 Rectangle {
378 anchors.right: parent.right
379 height: parent.height
380 width: 1
381 color: qgcPal.groupBorder
382 visible: column == 3
383 }
384
385 QGCCheckBox {
386 visible: column === 0
387 anchors.centerIn: parent
388 checked: _root._favorites.indexOf(fact.name) >= 0
389 z: 1
390 onClicked: controller.toggleFavorite(fact.name)
391 }
392
393 Row {
394 id: nameRow
395 visible: column === 1
396 anchors.left: parent.left
397 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
398 anchors.verticalCenter: parent.verticalCenter
399 spacing: lockIcon.visible ? ScreenTools.defaultFontPixelWidth / 3 : 0
400
401 QGCLabel {
402 text: column === 1 ? display : ""
403 anchors.verticalCenter: parent.verticalCenter
404 }
405
406 QGCColoredImage {
407 id: lockIcon
408 visible: fact.readOnly
409 source: "qrc:/InstrumentValueIcons/lock-closed.svg"
410 color: qgcPal.text
411 width: ScreenTools.defaultFontPixelHeight * 0.8
412 height: width
413 sourceSize.width: width
414 anchors.verticalCenter: parent.verticalCenter
415 }
416 }
417
418 QGCLabel {
419 id: label
420 visible: column !== 0 && column !== 1
421 anchors.left: parent.left
422 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
423 anchors.verticalCenter: parent.verticalCenter
424 width: column == 2 ? ScreenTools.defaultFontPixelWidth * 15 : implicitWidth
425 text: column == 2 ? col1String() : display
426 color: column == 2 && fact.defaultValueAvailable && !fact.valueEqualsDefault ? qgcPal.modifiedParamValue : qgcPal.text
427 font.bold: column == 2 && fact.defaultValueAvailable && !fact.valueEqualsDefault
428 maximumLineCount: 1
429 elide: column == 2 ? Text.ElideRight : Text.ElideNone
430
431 function col1String() {
432 if (fact.enumStrings.length === 0) {
433 return fact.valueString + " " + fact.units
434 }
435 if (fact.bitmaskStrings.length != 0) {
436 return fact.selectedBitmaskStrings.join(',')
437 }
438 return fact.enumStringValue
439 }
440 }
441
442 QGCMouseArea {
443 anchors.fill: parent
444 visible: column !== 0
445 onClicked: mouse => {
446 _editorDialogFact = fact
447 editorDialogFactory.open()
448 }
449 }
450 }
451 }
452}