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