kde-plasmoid · v2.0.0 · 2026-10-28 · sha256 8e29b903005ec9f4

kde-plasmoid v2.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/8e29b903005ec9f4.

---
name: kde-plasmoid
description: Use when building KDE Plasma 6 widgets with a Python backend - plasmoid structure, metadata.json, QML UI, configuration system, plasmapkg2 packaging, KDE Store submission, or plasmoid testing and debugging
metadata:
  author: mte90
  version: 2.0.0
  tags:
    - kde
    - plasma
    - plasmoid
    - widget
    - qml
    - qt
    - desktop
---

> **Objective:** Use this skill to reduce plasmoid-load failures, metadata-schema rejections, QML-binding-loop freezes, and packaging errors by enforcing metadata-discipline, QML-lifecycle rules, and plasmapkg2-verification patterns below.

# KDE Plasmoid Development with Python

Build Plasma 6 widgets with Python backend + QML UI.

**Important**: Native Python Plasmoids (PyKDE4/PyKDE5) are deprecated in Plasma 6. Use QML UI + Python backend via QObject.

See `references/plasmoid-structure.md` for complete structure, metadata.json schema, and dependency listings.

## Python Backend Rules

**Do subclass QObject** because QML requires QObject-derived types for property/signal exposure.

**Do emit signals after property changes** because QML bindings only update on signal emission:
```python
dataUpdated = Signal()

@Property(str, notify=dataUpdated)
def data(self):
    return self._data

@data.setter
def data(self, value):
    if self._data != value:
        self._data = value
        self.dataUpdated.emit()  # ← Required for QML update
```

**Do use @Slot for QML-callable methods** because unmarked methods may fail cross-language calls.

**Do initialize all properties in __init__** because QML accesses properties before any other initialization.

See `references/python-backend.md` for PySide6/PyQt6 registration syntax and signal/slot patterns.

## QML UI Rules

**Do use Kirigami units for all sizing** because hardcoded values break HiDPI scaling:
```qml
// ✅ GOOD
width: Kirigami.Units.gridUnit * 10
spacing: Kirigami.Units.smallSpacing

// ❌ BAD - Won't scale on HiDPI
width: 320
```

**Do provide fallback values for configuration** because missing config keys return undefined:
```qml
text: plasmoid.configuration.customLabel || "Default"
```

**Do use `Plasmoid.fullRepresentation` for expanded view** because Plasma routes expanded clicks there.

**Do use `Plasmoid.compactRepresentation` for panel mode** because panel widgets need compact UI.

**Do use Plasma 6 imports (no version for plasmoid/core)** because versioned imports break on Plasma 6:
```qml
import org.kde.plasma.plasmoid           // ✅ No version
import org.kde.plasma.components 3.0     // ✅ Components need version
import org.kde.plasma.core 2.0           // ✅ Core needs version
import org.kde.kirigami 2.0              // ✅ Kirigami needs version
```

See `references/qml-ui.md` for complete main.qml template and representation patterns.

## Best Practices

**Do use signal-based updates** because polling wastes CPU cycles:
```python
class Backend(QObject):
    dataChanged = Signal()
    
    def updateData(self):
        self._data = compute()
        self.dataChanged.emit()  # ← Notify QML only when changed
```

**Do use lazy initialization for expensive data** because repeated computation freezes the UI:
```python
@Slot(result=str)
def expensiveData(self):
    if not hasattr(self, '_cached'):
        self._cached = self._computeExpensive()
    return self._cached
```

**Do use QTimer for periodic updates** because manual loops block the event loop:
```python
self._timer = QTimer()
self._timer.timeout.connect(self.refresh)
self._timer.start(60000)  # 60 seconds
```

**Do avoid blocking the main thread** because QML waits for slot completion:
```python
# ❌ BAD - Blocks UI for 5 seconds
@Slot(result=str)
def slowOperation(self):
    time.sleep(5)  # Freezes widget!
```

**Do avoid implicit QML bindings on heavy properties** because binding loops freeze the widget:
```qml
// ❌ BAD - Re-evaluates on every frame
width: computeExpensiveWidth()  // Called constantly!

// ✅ GOOD - Evaluates once, updates on signal
width: backend.calculatedWidth
```

## Deep Dives

Refer to these on-demand reference files for detailed topics:

- **Configuration System** — `references/configuration.md`: metadata.json schema, KCFG main.xml, config.qml, configGeneral.qml, property aliases with cfg_ prefix
- **Packaging & Publishing** — `references/packaging-publishing.md`: plasmapkg2 CLI, .plasmoid package creation, local/system deployment, KDE Store submission workflow
- **Testing & Debugging** — `references/testing-debugging.md`: plasmoidtest tool, exit codes, troubleshooting guide, unit/integration tests, debug logging

## References

- [Plasma Widget Tutorial](https://develop.kde.org/docs/plasma/widget/)
- [Porting to KF6](https://develop.kde.org/docs/plasma/widget/porting_kf6/)
- [Python + Kirigami](https://develop.kde.org/docs/getting-started/python/)
- [QML API Reference](https://develop.kde.org/docs/plasma/widget/plasma-qml-api/)
- [KDE Store](https://store.kde.org/)
- [Kirigami Documentation](https://develop.kde.org/docs/kirigami/)
- [plasmapkg2 Tool](https://apps.kde.org/plasmapkg2/)