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