The Problem
Your shell crashes, a widget shows wrong data, a popup doesn't appear, or the panel renders at the wrong size. Without debugging tools, you're guessing at the cause. QML's dynamic nature makes errors silent — a binding that fails just shows nothing.
The Naive Approach
Add random console.log() statements:
function refresh() {
console.log("refresh called")
console.log("cpu = " + cpuValue)
console.log("about to call Qt.process...")
Qt.process(["..."]).onStdout.connect(function(data) {
console.log("got data: " + data)
})
}console.log() works, but it's noisy, easy to forget, and doesn't help with rendering issues or binding failures.
Think of debugging like detective work. You have witnesses (logs), forensic evidence (crash dumps), surveillance cameras (profiler), and interrogation (interactive shell). Each tool reveals a different angle. Using only one tool misses crucial clues.
Essential Debugging Tools
1. The QML Console
Quickshell prints QML errors to stderr. Run with visible stderr:
quickshell 2>&1 | tee quickshell.logLook for:
TypeError: Cannot read property 'x' of null— null referenceReferenceError: foo is not defined— typo or missing importQML PanelWindow: Cannot anchor to an item that is a sibling or ancestor— layout issue
2. console.log() and Friends
console.log("simple string")
console.debug("debug: " + value)
console.warn("warning: " + value)
console.error("error: " + value)
console.trace() // Print stack trace
console.time("label") / console.timeEnd("label") // Timing
console.count("label") // Count calls
console.profile("label") / console.profileEnd("label") // Profile3. Visual Debug Overlays
Add a debug mode that shows bounding boxes:
// shell.qml
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "red"
border.width: debugMode ? 1 : 0
}4. Property Dump
Create a debug panel that shows all service properties:
// DebugOverlay.qml
PopupWindow {
visible: debugMode
ListView {
model: [
"CPU: " + CpuService.usagePercent.toFixed(1) + "%",
"RAM: " + MemoryService.usedPercent.toFixed(1) + "%",
"Battery: " + BatteryService.percent + "%",
"Time: " + TimeService.timeString
]
delegate: Text { text: modelData; color: "lime"; font.pixelSize: 10 }
}
}5. QML Inspector
Quickshell may support the Qt QML Inspector (port 3768 by default):
QML_DEBUG=1 quickshell
# Connect with Qt Creator or qmldbgCommon Bugs and Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Panel not visible | Missing anchors | Add at least 2 opposite anchors |
| Popup shows at (0,0) | No anchor set | Set anchor.window and anchor.rect |
| Property shows "undefined" | Binding failed, property doesn't exist | Check spelling and import |
| Timer not firing | repeat: false or wrong interval | Set repeat: true and check interval |
| Component not loading | Wrong path or circular import | Check file path and dependency chain |
| Crash on startup | Missing import or bad QML syntax | Run qmllint on your files |
| Wrong colors | Theme not applied | Check import "Theme.qml" as Theme syntax |
| Layout clipping | No clip: true on container | Add clip: true to ListView/Grid |
Debugging Bindings
Enable binding removal warnings to find accidental overwrites:
export QT_LOGGING_RULES="qt.qml.binding.removal.info=true"
quickshellWatch for: QML Binding: Binding loop detected for property "height".
Debugging D-Bus
Monitor D-Bus traffic:
dbus-monitor --session # Session bus
dbus-monitor --system # System busFilter for specific services:
dbus-monitor "interface=org.mpris.MediaPlayer2.Player"Exercises
- Use console.log, console.warn, console.error, and console.trace for logging
- Enable QML debug mode with QML_DEBUG=1 for the QML Inspector
- Add visual debug overlays for real-time property monitoring
- Monitor D-Bus traffic with dbus-monitor for integration debugging