6import QGroundControl.Controls
8/// Left panel for the Log Viewer charting tab.
9/// Shows log stats and the grouped fields list.
13 required property var logParser
14 required property var logViewerController
16 signal clearSelectedRequested
18 property bool xAxisShowLocalTime: QGroundControl.settingsManager.logViewerSettings.xAxisShowLocalTime.rawValue
20 color: qgcPal.windowShade
21 radius: ScreenTools.defaultFontPixelWidth * 0.5
22 Layout.preferredWidth: mainLayout.implicitWidth + (mainLayout.anchors.margins * 2)
24 // -------------------------------------------------------------------------
26 // -------------------------------------------------------------------------
27 property string _fieldSearchText: ""
28 property var _filteredFieldRows: []
29 property real _maxFieldRowWidth: _minFieldRowWidth
31 readonly property real _minFieldRowWidth: ScreenTools.defaultFontPixelWidth
32 readonly property bool _isFirmwareLog: logViewerController.sourceType === LogViewerController.Bin
33 || logViewerController.sourceType === LogViewerController.ULog
35 QGCPalette { id: qgcPal }
37 // -------------------------------------------------------------------------
39 // -------------------------------------------------------------------------
41 /// Called by the parent after a log finishes loading.
42 function rebuildGroupedFields() {
43 _maxFieldRowWidth = _minFieldRowWidth
44 logViewerController.setPlottableFields(logParser.plottableFields)
48 // -------------------------------------------------------------------------
50 // -------------------------------------------------------------------------
52 function _applyFieldFilter() {
53 const query = String(_fieldSearchText).trim().toLowerCase()
54 if (query.length === 0) {
55 _filteredFieldRows = logViewerController.fieldRows
60 const fields = logViewerController.plottableFields
61 for (let i = 0; i < fields.length; i++) {
62 const fullName = String(fields[i])
63 const splitIndex = fullName.indexOf(".")
64 const groupName = splitIndex > 0 ? fullName.substring(0, splitIndex) : qsTr("Other")
65 const shortName = splitIndex > 0 ? fullName.substring(splitIndex + 1) : fullName
66 const haystack = (fullName + " " + groupName + " " + shortName).toLowerCase()
67 if (haystack.indexOf(query) === -1) {
70 if (!groupedMap[groupName]) {
71 groupedMap[groupName] = []
73 groupedMap[groupName].push({ fullName: fullName, shortName: shortName })
76 // Insertion order follows the controller's numeric-aware field order
77 const groups = Object.keys(groupedMap)
79 for (let g = 0; g < groups.length; g++) {
80 const groupName = groups[g]
81 rows.push({ rowType: "group", group: groupName })
82 for (let s = 0; s < groupedMap[groupName].length; s++) {
86 fullName: groupedMap[groupName][s].fullName,
87 shortName: groupedMap[groupName][s].shortName
91 _filteredFieldRows = rows
94 function _isGroupExpanded(groupName) {
95 return logViewerController.isGroupExpanded(groupName)
98 function _toggleGroupExpanded(groupName) {
99 if (String(_fieldSearchText).trim().length > 0) {
102 logViewerController.toggleGroupExpanded(groupName)
106 // -------------------------------------------------------------------------
108 // -------------------------------------------------------------------------
111 target: logViewerController
112 function onFieldRowsChanged() {
113 const savedY = _fieldsListView.contentY
115 Qt.callLater(() => { _fieldsListView.contentY = savedY })
119 // -------------------------------------------------------------------------
121 // -------------------------------------------------------------------------
124 id: _groupRowComponent
127 implicitWidth: _groupLayout.implicitWidth
128 implicitHeight: _groupLayout.implicitHeight
130 Component.onCompleted: _maxFieldRowWidth = Math.max(_maxFieldRowWidth, implicitWidth)
134 spacing: ScreenTools.defaultFontPixelWidth / 2
137 Layout.preferredWidth: _groupLabel.height * 0.5
138 Layout.preferredHeight: _groupLabel.height * 0.5
139 source: "/qmlimages/arrow-down.png"
141 fillMode: Image.PreserveAspectFit
142 rotation: (String(control._fieldSearchText).trim().length > 0 || control._isGroupExpanded(rowData.group)) ? 0 : -90
154 onClicked: control._toggleGroupExpanded(rowData.group)
160 id: _fieldRowComponent
164 checked: logViewerController.selectedFields.indexOf(rowData.fullName) !== -1
165 text: rowData.shortName ? " " + String(rowData.shortName) : ""
166 onClicked: logViewerController.setFieldSelected(rowData.fullName, checked)
168 Component.onCompleted: _maxFieldRowWidth = Math.max(_maxFieldRowWidth, implicitWidth)
174 anchors.top: parent.top
175 anchors.bottom: parent.bottom
176 anchors.left: parent.left
177 anchors.margins: ScreenTools.defaultFontPixelWidth / 2
178 spacing: ScreenTools.defaultFontPixelHeight * 0.5
181 visible: _isFirmwareLog
182 text: qsTr("Fields: %1 Parameters: %2 Events: %3")
183 .arg(logParser.plottableFields.length)
184 .arg(logParser.parameters.length)
185 .arg(logParser.events.length)
189 visible: _isFirmwareLog
190 && logParser.startTime
191 && !isNaN(logParser.startTime.getTime())
192 && logParser.startTime.getTime() > 0
193 spacing: ScreenTools.defaultFontPixelWidth
195 QGCLabel { text: qsTr("X axis:") }
197 ButtonGroup { id: _xAxisButtonGroup }
200 text: qsTr("Elapsed")
201 checked: !control.xAxisShowLocalTime
202 ButtonGroup.group: _xAxisButtonGroup
203 onClicked: QGroundControl.settingsManager.logViewerSettings.xAxisShowLocalTime.rawValue = false
207 text: qsTr("Local time")
208 checked: control.xAxisShowLocalTime
209 ButtonGroup.group: _xAxisButtonGroup
210 onClicked: QGroundControl.settingsManager.logViewerSettings.xAxisShowLocalTime.rawValue = true
215 visible: _isFirmwareLog
216 spacing: ScreenTools.defaultFontPixelWidth * 0.5
224 id: _fieldSearchField
225 Layout.fillWidth: true
226 textColor: qgcPal.textFieldText
227 placeholderTextColor: Qt.rgba(qgcPal.textFieldText.r, qgcPal.textFieldText.g, qgcPal.textFieldText.b, 0.7)
228 placeholderText: qsTr("Search fields")
231 control._fieldSearchText = text
232 if (text.trim().length === 0) {
233 _fieldSearchTimer.stop()
234 control._applyFieldFilter()
236 _fieldSearchTimer.restart()
241 control._fieldSearchText = text
242 _fieldSearchTimer.stop()
243 control._applyFieldFilter()
248 text: qsTr("Clear Selected")
249 enabled: logViewerController.selectedFields.length > 0
252 logViewerController.clearSelection()
253 control._applyFieldFilter()
254 control.clearSelectedRequested()
261 Layout.fillHeight: true
262 Layout.fillWidth: true
263 Layout.preferredWidth: _maxFieldRowWidth + ScreenTools.defaultFontPixelWidth
264 visible: _isFirmwareLog
265 model: _filteredFieldRows
266 spacing: ScreenTools.defaultFontPixelHeight * 0.25
269 width: _fieldsListView.width - ScreenTools.defaultFontPixelWidth
270 sourceComponent: modelData.rowType === "group" ? _groupRowComponent : _fieldRowComponent
271 property var rowData: modelData
277 id: _fieldSearchTimer
280 onTriggered: control._applyFieldFilter()