QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
AppLogging.qml
Go to the documentation of this file.
1import QGroundControl
2import QGroundControl.Controls
3import QGroundControl.Logging
4import QtQuick
5import QtQuick.Layouts
6
7Item {
8 id: root
9
10 readonly property real _indicatorWidth: ScreenTools.defaultFontPixelWidth * 0.4
11 readonly property real _margin: ScreenTools.defaultFontPixelWidth
12 readonly property real _rowHeight: ScreenTools.defaultFontPixelHeight * 1.6
13
14 function _levelColor(lvl) {
15 switch (lvl) {
16 case LogEntry.Debug: return qgcPal.colorGrey
17 case LogEntry.Info: return qgcPal.text
18 case LogEntry.Warning: return qgcPal.colorOrange
19 case LogEntry.Critical: return qgcPal.colorRed
20 case LogEntry.Fatal: return qgcPal.colorRed
21 default: return qgcPal.text
22 }
23 }
24
25 QGCPalette {
26 id: qgcPal
27
28 colorGroupEnabled: enabled
29 }
30
31 ColumnLayout {
32 anchors.fill: parent
33 spacing: 0
34
35 // ── Column header ────────────────────────────────────────
36 HorizontalHeaderView {
37 id: headerView
38
39 Layout.fillWidth: true
40 clip: true
41 syncView: tableView
42
43 delegate: Rectangle {
44 required property var display
45
46 color: qgcPal.windowShade
47 implicitHeight: headerLabel.contentHeight + ScreenTools.defaultFontPixelHeight * 0.4
48 implicitWidth: headerLabel.contentWidth + ScreenTools.defaultFontPixelWidth * 2
49
50 QGCLabel {
51 id: headerLabel
52
53 anchors.left: parent.left
54 anchors.leftMargin: _margin * 0.5
55 anchors.verticalCenter: parent.verticalCenter
56 font.bold: true
57 font.family: ScreenTools.fixedFontFamily
58 font.pointSize: ScreenTools.defaultFontPointSize
59 text: parent.display ?? ""
60 }
61
62 Rectangle {
63 anchors.bottom: parent.bottom
64 color: qgcPal.groupBorder
65 height: 1
66 width: parent.width
67 }
68
69 Rectangle {
70 anchors.right: parent.right
71 color: qgcPal.groupBorder
72 height: parent.height
73 width: 1
74 }
75 }
76 }
77
78 // ── Log table ────────────────────────────────────────────
79 TableView {
80 id: tableView
81
82 property bool _loadCompleted: false
83 property real _cachedWidth: 0
84
85 Layout.fillHeight: true
86 Layout.fillWidth: true
87 boundsBehavior: Flickable.StopAtBounds
88 clip: true
89 columnSpacing: 0
90 columnWidthProvider: function (col) {
91 const explicit_ = tableView.explicitColumnWidth(col)
92 if (explicit_ > 0) {
93 return explicit_;
94 }
95 const cw = ScreenTools.defaultFontPixelWidth
96 const w = _cachedWidth
97 const compact = w < cw * 60
98 switch (col) {
99 case LogEntry.TimestampColumn:
100 return compact ? cw * 9 : cw * 12;
101 case LogEntry.LevelColumn:
102 return compact ? cw * 5 : cw * 7;
103 case LogEntry.CategoryColumn:
104 return compact ? cw * 14 : cw * 22;
105 case LogEntry.SourceColumn:
106 return compact ? cw * 16 : cw * 22;
107 case LogEntry.MessageColumn: {
108 const fixedWidth = compact ? cw * 44 : cw * 63
109 return Math.max(cw * 30, w - fixedWidth)
110 }
111 }
112 return -1;
113 }
114 model: LogManager.model
115 resizableColumns: true
116 rowSpacing: 0
117 reuseItems: true
118
119 onWidthChanged: {
120 _cachedWidth = width;
121 _layoutTimer.restart();
122 }
123
124 Timer {
125 id: _layoutTimer
126
127 interval: 0
128
129 onTriggered: tableView.forceLayout()
130 }
131
132 delegate: Rectangle {
133 required property int column
134 required property var display
135 required property int level
136 required property int row
137
138 color: row % 2 === 0 ? qgcPal.window : qgcPal.windowShade
139 implicitHeight: _rowHeight
140
141 Rectangle {
142 anchors.bottom: parent.bottom
143 anchors.left: parent.left
144 anchors.top: parent.top
145 color: root._levelColor(parent.level)
146 visible: parent.column === 0 && parent.level >= LogEntry.Warning
147 width: _indicatorWidth
148 }
149
150 QGCLabel {
151 anchors.left: parent.left
152 anchors.leftMargin: parent.column === 0 ? _indicatorWidth + _margin * 0.3 : _margin * 0.3
153 anchors.right: parent.right
154 anchors.rightMargin: _margin * 0.3
155 anchors.verticalCenter: parent.verticalCenter
156 color: root._levelColor(parent.level)
157 elide: Text.ElideRight
158 font.family: ScreenTools.fixedFontFamily
159 font.pointSize: ScreenTools.defaultFontPointSize
160 text: parent.display ?? ""
161 }
162 }
163
164 QGCLabel {
165 color: qgcPal.colorGrey
166 text: qsTr("No log entries")
167 visible: tableView.rows === 0
168 x: tableView.contentX + (tableView.width - width) / 2
169 y: tableView.contentY + (tableView.height - height) / 2
170 }
171
172 Component.onCompleted: {
173 _cachedWidth = width;
174 _loadCompleted = true;
175 if (rows > 0)
176 positionViewAtRow(rows - 1, TableView.AlignBottom);
177 }
178
179 Timer {
180 id: scrollTimer
181
182 interval: 50
183
184 onTriggered: {
185 if (tableView.rows > 0)
186 tableView.positionViewAtRow(tableView.rows - 1, TableView.AlignBottom);
187 }
188 }
189
190 Connections {
191 function onRowsInserted() {
192 if (tableView._loadCompleted && followTail.checked)
193 scrollTimer.restart();
194 }
195
196 target: LogManager.model
197 }
198 }
199
200 // ── Separator ─────────────────────────────────────────────
201 Rectangle {
202 Layout.fillWidth: true
203 Layout.preferredHeight: 1
204 color: qgcPal.groupBorder
205 }
206
207 // ── Filter bar ────────────────────────────────────────────
208 Rectangle {
209 Layout.fillWidth: true
210 Layout.preferredHeight: filterRow.implicitHeight + _margin
211 color: qgcPal.windowShade
212
213 RowLayout {
214 id: filterRow
215
216 anchors.fill: parent
217 anchors.leftMargin: _margin
218 anchors.rightMargin: _margin
219 spacing: _margin * 0.75
220
221 QGCComboBox {
222 id: levelCombo
223
224 model: [qsTr("All Levels"), qsTr("Debug"), qsTr("Info"), qsTr("Warning"), qsTr("Critical"), qsTr("Fatal")]
225 sizeToContents: true
226
227 Component.onCompleted: currentIndex = LogManager.model.filterLevel + 1
228 onActivated: index => {
229 LogManager.model.filterLevel = index - 1;
230 }
231 }
232
233 QGCComboBox {
234 id: categoryCombo
235
236 model: [qsTr("All Categories")].concat(LogManager.model.categoriesList)
237 sizeToContents: true
238
239 onActivated: index => {
240 LogManager.model.filterCategory = index === 0 ? "" : model[index];
241 }
242 }
243
244 QGCTextField {
245 id: searchField
246
247 Layout.fillWidth: true
248 Layout.minimumWidth: _margin * 10
249 placeholderText: qsTr("Search…")
250
251 onTextChanged: LogManager.model.setFilterTextDeferred(text)
252 }
253
254 QGCButton {
255 ToolTip.text: qsTr("Regex search")
256 ToolTip.visible: hovered
257 checkable: true
258 checked: LogManager.model.filterRegex
259 text: qsTr(".*")
260
261 onClicked: LogManager.model.filterRegex = checked
262 }
263
264 QGCLabel {
265 color: qgcPal.colorRed
266 font.bold: true
267 text: qsTr("\u26A0 Disk Error")
268 visible: LogManager.hasError
269
270 QGCMouseArea {
271 anchors.fill: parent
272
273 onClicked: LogManager.clearError()
274 }
275 }
276
277 QGCButton {
278 id: followTail
279
280 checkable: true
281 checked: true
282 text: qsTr("Follow")
283
284 onCheckedChanged: {
285 if (checked && tableView._loadCompleted && tableView.rows > 0)
286 tableView.positionViewAtRow(tableView.rows - 1, TableView.AlignBottom);
287 }
288 }
289
290 QGCButton {
291 text: qsTr("Categories")
292
293 onClicked: filtersDialogFactory.open()
294 }
295
296 QGCButton {
297 text: qsTr("Settings")
298
299 onClicked: settingsDialogFactory.open()
300 }
301 }
302 }
303 }
304
305 QGCPopupDialogFactory {
306 id: filtersDialogFactory
307
308 dialogComponent: Component {
309 LoggingCategoriesDialog {
310 }
311 }
312 }
313
314 QGCPopupDialogFactory {
315 id: settingsDialogFactory
316
317 dialogComponent: Component {
318 LoggingSettingsDialog {
319 }
320 }
321 }
322}