Skill v1.0.1
currentAutomated scan100/100+1 new
version: "1.0.1" name: kde-plasmoid description: "Build KDE Plasma 6 widgets with Python backend and QML UI, including metadata, deployment, and KDE Store distribution" metadata: author: mte90 version: "1.0.0" tags:
- kde
- plasma
- plasmoid
- widget
- qml
- qt
- desktop
KDE Plasmoid Development with Python
Complete guide for developing Plasma widgets (Plasmoids) using Python backend with QML UI layer.
Overview
Important: Native Python Plasmoids (PyKDE4/PyKDE5) are deprecated in Plasma 6. Modern Plasmoids must use:
- UI Layer: QML with Kirigami components
- Backend Logic: Python (PySide6 or PyQt6) via QObject subclasses
Architecture
┌─────────────────────────────────────┐│ QML UI Layer ││ (PlasmoidItem + Kirigami) │└──────────────┬──────────────────────┘│▼┌─────────────────────────────────────┐│ Python Backend Logic ││ (QObject-derived classes) │└─────────────────────────────────────┘
Version Requirements
| Component | Version | |
|---|---|---|
| Plasma | 6.x | |
| Qt | 6.x | |
| Python | 3.8+ | |
| PySide6/PyQt6 | 6.x |
Dependencies
System Packages
# Arch/Manjarosudo pacman -S python-pyqt6 pyside6 kirigami plasma-framework plasma-sdk# Fedorasudo dnf install python3-pyqt6 python3-pyside6 kf6-kirigami-devel plasma-framework plasma-sdk# Debian/Ubuntusudo apt install python3-pyqt6 python3-pyside6 kirigami-devel plasma-framework plasma-sdk# openSUSEsudo zypper install python3-qt6 python3-pyside6 kf6-kirigami-devel plasma-framework
Python Packages
pip install psutil requests pydbus
Plasmoid Structure
my-plasmoid/├── package/│ ├── contents/│ │ ├── config/│ │ │ ├── config.qml│ │ │ └── main.xml│ │ ├── ui/│ │ │ ├── main.qml│ │ │ └── configGeneral.qml│ │ └── main.xml│ └── metadata.json├── src/│ ├── __init__.py│ └── backend.py├── README.md└── LICENSE
Configuration Files
metadata.json
{"KPlugin": {"Authors": [{"Email": "your.email@example.com","Name": "Your Name"}],"Category": "System Information","Description": "A Python-powered Plasma widget with system monitoring capabilities","Icon": "utilities-system-monitor","Id": "com.example.my-plasmoid","License": "LGPL-2.1-or-later","Name": "My Plasmoid","Version": "1.0.0","Website": "https://github.com/youruser/my-plasmoid","Keywords": ["system","monitor","cpu","memory","disk"],"X-KDE-PluginInfo-Name": "com.example.my-plasmoid"},"X-Plasma-API-Minimum-Version": "6.0","X-Plasma-API-Extensions-Required": [],"X-Plasma-Check-Compatibility": "true","KPackageStructure": "Plasma/Applet"}
Critical Fields:
KPlugin.Category: Required widget category (see below)KPlugin.License: Must be valid SPDX identifier (e.g., "LGPL-2.1-or-later", "MIT", "GPL-2.0-or-later")X-Plasma-API-Minimum-Version: Must be"6.0"for Plasma 6X-Plasma-Check-Compatibility: Set to"true"to enable compatibility checkingKPackageStructure: Must be"Plasma/Applet"Id: Unique identifier, must match folder name exactly
Categories
| Category | Description | |
|---|---|---|
System Information | System monitors, stats, sensors | |
Utility | General tools and helpers | |
Date and Time | Clocks, calendars, timers | |
Environment and Weather | Weather widgets, climate data | |
Miscellaneous | Other widgets not fitting other categories | |
Application Launchers | App menus, launchers, shortcuts | |
Windows and Tasks | Task managers, window controls |
Python Backend
Basic Backend Class
#!/usr/bin/env python3"""Python backend for Plasma widget"""from PySide6.QtCore import QObject, Signal, Slot, Property# OR PyQt6:# from PyQt6.QtCore import QObject, pyqtSignal as Signal, pyqtSlot as Slot, pyqtProperty as Propertyclass WidgetBackend(QObject):"""Backend logic exposed to QML"""# SignalsdataUpdated = Signal()def __init__(self, parent=None):super().__init__(parent)self._data = "Initial Value"self._count = 0# Properties (exposed to QML)@Property(str, notify=dataUpdated)def data(self):return self._data@data.setterdef data(self, value):if self._data != value:self._data = valueself.dataUpdated.emit()@Property(int, notify=dataUpdated)def count(self):return self._count# Slots (callable from QML)@Slot(result=str)def getData(self):return self._data@Slot(str)def setData(self, value):self.data = value@Slot(str, result=str)def processData(self, inputText):"""Process input and return result"""return f"Processed: {inputText}"@Slot()def refresh(self):"""Refresh data"""self._count += 1self._data = f"Updated #{self._count}"self.dataUpdated.emit()@Slot(str, result=str)def getSystemInfo(self, category):"""Get system information"""import psutilif category == "cpu":return f"{psutil.cpu_percent():.1f}%"elif category == "memory":mem = psutil.virtual_memory()return f"{mem.percent:.1f}%"elif category == "disk":disk = psutil.disk_usage('/')return f"{disk.percent:.1f}%"return "Unknown"
PySide6 vs PyQt6
| Feature | PySide6 | PyQt6 | |
|---|---|---|---|
| Signal | Signal | pyqtSignal | |
| Slot | Slot | pyqtSlot | |
| Property | Property | pyqtProperty | |
| License | LGPL | GPL | |
| QML Registration | @QmlElement decorator | qmlRegisterType() |
PySide6 Registration:
from PySide6.QtQml import QmlElementQML_IMPORT_NAME = "com.example.widget"QML_IMPORT_MAJOR_VERSION = 1@QmlElementclass WidgetBackend(QObject):pass
PyQt6 Registration:
from PyQt6.QtQml import qmlRegisterTypeqmlRegisterType(WidgetBackend, "com.example.widget", 1, 0, "WidgetBackend")
QML UI
main.qml
import QtQuickimport QtQuick.Layoutsimport org.kde.plasma.plasmoidimport org.kde.plasma.components 3.0 as PlasmaComponents3import org.kde.plasma.core 2.0 as PlasmaCoreimport org.kde.kirigami 2.0 as Kirigamiimport com.example.widget 1.0PlasmoidItem {id: root// Backend instanceWidgetBackend {id: backend}// Full representation (expanded widget)Plasmoid.fullRepresentation: Kirigami.Card {implicitWidth: Kirigami.Units.gridUnit * 20implicitHeight: Kirigami.Units.gridUnit * 15ColumnLayout {anchors.fill: parentanchors.margins: Kirigami.Units.smallSpacingspacing: Kirigami.Units.smallSpacing// TitlePlasmaComponents3.Label {text: Plasmoid.configuration.customLabel || "My Widget"font.bold: truefont.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.2Layout.fillWidth: true}// Data displayPlasmaComponents3.Label {text: backend.dataLayout.fillWidth: truewrapMode: Text.WordWrap}// System infoRowLayout {Layout.fillWidth: truePlasmaComponents3.Label {text: "CPU: " + backend.getSystemInfo("cpu")}PlasmaComponents3.Label {text: "RAM: " + backend.getSystemInfo("memory")}}// Input fieldPlasmaComponents3.TextField {id: inputFieldplaceholderText: "Enter text..."Layout.fillWidth: true}// ButtonsRowLayout {Layout.fillWidth: truePlasmaComponents3.Button {text: "Process"onClicked: backend.processData(inputField.text)}PlasmaComponents3.Button {text: "Refresh"icon.name: "view-refresh"onClicked: backend.refresh()}}}}// Compact representation (panel icon)Plasmoid.compactRepresentation: PlasmaCore.IconItem {source: Plasmoid.iconanchors.centerIn: parentimplicitWidth: {if (Plasmoid.location === PlasmaCore.Types.HorizontalPanel ||Plasmoid.location === PlasmaCore.Types.VerticalPanel) {return Kirigami.Units.iconSizes.medium}return Kirigami.Units.iconSizes.large}implicitHeight: implicitWidthMouseArea {anchors.fill: parentonClicked: Plasmoid.expanded = !Plasmoid.expanded}}// TooltipPlasmoid.toolTipMainText: "My Widget"Plasmoid.toolTipSubText: backend.data// IconPlasmoid.icon: "utilities-system-monitor"}
Plasma 6 QML Imports
// Correct Plasma 6 imports (no version numbers for most)import QtQuickimport QtQuick.Layoutsimport org.kde.plasma.plasmoidimport org.kde.plasma.components 3.0 as PlasmaComponents3import org.kde.plasma.core 2.0 as PlasmaCoreimport org.kde.kirigami 2.0 as Kirigami
Configuration System
contents/config/main.xml
<?xml version="1.0" encoding="UTF-8"?><kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0http://www.kde.org/standards/kcfg/1.0/kcfg.xsd"><kcfgfile name=""/><group name="General"><entry name="enabled" type="Bool"><default>true</default><label>Enable widget</label></entry><entry name="refreshInterval" type="Int"><default>60</default><min>5</min><max>3600</max><label>Refresh interval in seconds</label></entry><entry name="customLabel" type="String"><default>My Widget</default><label>Custom label</label></entry><entry name="showNotifications" type="Bool"><default>false</default><label>Show notifications</label></entry></group></kcfg>
contents/config/config.qml
import QtQuick 2.0import org.kde.plasma.configuration 2.0ConfigModel {ConfigCategory {name: i18n("General")icon: "configure"source: "configGeneral.qml"}}
contents/ui/configGeneral.qml
import QtQuick 2.0import QtQuick.Controls 2.5 as QQC2import org.kde.kirigami 2.4 as KirigamiKirigami.FormLayout {id: page// Property aliases MUST use cfg_ prefixproperty alias cfg_enabled: enabledCheck.checkedproperty alias cfg_refreshInterval: intervalSpin.valueproperty alias cfg_customLabel: labelField.textproperty alias cfg_showNotifications: notifyCheck.checkedQQC2.CheckBox {id: enabledChecktext: i18n("Enable widget")Kirigami.FormData.label: i18n("Status:")}QQC2.SpinBox {id: intervalSpinfrom: 5to: 3600editable: trueKirigami.FormData.label: i18n("Refresh interval (seconds):")}QQC2.TextField {id: labelFieldplaceholderText: i18n("Enter custom label")Kirigami.FormData.label: i18n("Label:")}QQC2.CheckBox {id: notifyChecktext: i18n("Show notifications")}}
Accessing Configuration in QML
// Read configurationtext: plasmoid.configuration.customLabel || "Default"checked: plasmoid.configuration.enabled// Write configurationplasmoid.configuration.customLabel = "New Label"
Installation & Testing
Development Commands
# Package the plasmoidcd my-plasmoid/packagezip -r ../my-plasmoid.plasmoid .# Install locallyplasmapkg2 -i my-plasmoid.plasmoid# Test in window (recommended for development)plasmapkg2 -l com.example.my-plasmoidplasmoidtest com.example.my-plasmoid# Test directly from sourceplasmoidtest /path/to/my-plasmoid/package# Uninstallplasmapkg2 -r com.example.my-plasmoid# Upgrade existing installationplasmapkg2 -u my-plasmoid.plasmoid# List installed plasmoidsplasmapkg2 -t Plasma/Applet --list
Reload Plasma Shell
# Plasma 6kquitapp6 plasmashell && kstart6 plasmashell# Plasma 5 (legacy)kquitapp5 plasmashell && kstart5 plasmashell
Testing with plasmoidtest
The plasmoidtest command is the recommended testing tool for Plasma 6. It provides:
- Interactive widget preview in a resizable window
- Real-time configuration testing
- Console output for debugging
# Test in window mode (interactive)plasmoidtest com.example.my-plasmoid# Test with debug outputplasmapkg2 -l com.example.my-plasmoidplasmoidtest --debug com.example.my-plasmoid# Test from file pathplasmoidtest /path/to/my-plasmoid/package
Debugging
# View logsjournalctl -f | grep -i plasma# Run with verbose outputplasmoidtest com.example.my-plasmoid 2>&1 | tee debug.log# Enable debug loggingexport QT_LOGGING_RULES="*.debug=true"export QML_DEBUGGING_ENABLED=1# Check QML errorsplasmoidtest com.example.my-plasmoid 2>&1 | grep -i "qml\|error"# Force validation on loadplasmoidtest --validate com.example.my-plasmoid
Packaging & Distribution
Create Release Package
# Clean packagecd my-plasmoidrm -f ../my-plasmoid.plasmoidcd package && zip -r ../../my-plasmoid-1.0.0.plasmoid . && cd ..# Verify package structureunzip -l my-plasmoid-1.0.0.plasmoid
Build .plasmoid with plasmapkg2
# Package creationcd my-plasmoid/packageplasmapkg2 -c my-plasmoid.plasmoid# List contentsplasmapkg2 -t Plasma/Applet --list# Package with compressionplasmapkg2 -c -o my-plasmoid-compressed.plasmoid package/
Install/Update/Remove
# Install from packageplasmapkg2 -i my-plasmoid.plasmoid# Install from directory (auto-packages)plasmapkg2 -i package/# Upgrade existingplasmapkg2 -u my-plasmoid.plasmoid# Removeplasmapkg2 -r com.example.my-plasmoid# List all installed widgetsplasmapkg2 -t Plasma/Applet --list# List with detailsplasmapkg2 -t Plasma/Applet --list --verbose
KDE Store Submission
- Prepare files:
my-plasmoid-1.0.0.plasmoid- Screenshots (PNG, 1920x1080 recommended)
- README.md with description
- LICENSE file (compatible with specified license)
- KDE Store requirements:
- Minimum plasma version compatibility declared
- Complete metadata in metadata.json
- Valid SPDX license identifier
- At least 3 screenshots showing widget in use
- Upload to KDE Store:
- Visit https://store.kde.org/
- Create account
- Submit to "Plasma Desktop Applets" category
- Fill description, screenshots, changelog
- Undergo review process (typically 1-2 weeks)
- KDE Store policy compliance:
- No promotional content
- No aggressive marketing in description
- Open-source license only (no proprietary components)
- Privacy policy for widgets accessing user data
GitHub Release
# Create release archivetar -czf my-plasmoid-1.0.0.tar.gz my-plasmoid/# Installation scriptcat > install.sh << 'EOF'#!/bin/bashplasmapkg2 -i my-plasmoid-1.0.0.plasmoidecho "Widget installed. Reload plasma shell: kquitapp6 plasmashell && kstart6 plasmashell"EOFchmod +x install.sh# Verify installation./install.shplasmapkg2 -t Plasma/Applet --list | grep my-plasmoid
Deployment/Publishing
Local Deployment
# Install to user Plasma (persistent across sessions)plasmapkg2 -i my-plasmoid-1.0.0.plasmoid# Verify installation locationfind ~/.local/share/plasma-appletsrc -name "*my-plasmoid*" 2>/dev/null || \find ~/.local/share/plasma-appletsrc -name "*com.example.my-plasmoid*" 2>/dev/null# List all installed appletsplasmapkg2 -t Plasma/Applet --list# Check widget configuration file locationcat ~/.local/share/plasma-appletsrc/com.example.kde.desktop-appletsrc
System-Wide Deployment (requires root)
# Copy to system locationsudo cp my-plasmoid-1.0.0.plasmoid /usr/share/plasma/plasmoids/com.example.my-plasmoid/# Or copy folder structuresudo mkdir -p /usr/share/plasma/plasmoids/com.example.my-plasmoidsudo cp -r my-plasmoid/package/* /usr/share/plasma/plasmoids/com.example.my-plasmoid/# Reload plasma shell to applykquitapp6 plasmashell && kstart6 plasmashell# Verify installationsudo plasmapkg2 -l com.example.my-plasmoid
KDE Store Distribution
Step 1: Prepare package
# Create release packagecd my-plasmoidVERSION="1.0.0"plasmapkg2 -c -o my-plasmoid-${VERSION}.plasmoid package/# Verify package contentsunzip -l my-plasmoid-${VERSION}.plasmoid# Check metadataunzip -p my-plasmoid-${VERSION}.plasmoid metadata.json | jq .
Step 2: Prepare screenshots
Screenshots should:
- Be high-resolution (1920x1080 minimum)
- Show widget in actual Plasma session
- Include at least 3 variations:
- Widget in desktop mode
- Widget in panel (if applicable)
- Configuration dialog
# Create screenshots directorymkdir -p screenshots# Capture widget screenshotsplasmoidtest com.example.my-plasmoid# (Take screenshots manually or use automation)
Step 3: Submit to KDE Store
- Account setup:
- Visit https://store.kde.org/
- Register with KDE account (GitHub, KDE Accounts, or email)
- New submission:
- Click "Submit New Applet"
- Fill metadata form:
- Name, description, category
- License (must match metadata.json)
- Screenshots (upload PNG files)
- GitHub repository URL (optional but recommended)
- Review process:
- KDE maintainers review (typically 1-2 weeks)
- Common rejection reasons:
- Missing required metadata fields
- Non-compliant license
- Promotional/proprietary content
- Broken QML syntax
- Missing plasma compatibility declaration
- Approved applet:
- Appears in Plasma Add-Widgets panel
- Visible to all KDE users
- Automated updates available
Publishing Timeline
| Stage | Duration | Notes | |
|---|---|---|---|
| Development | Variable | Depends on feature complexity | |
| Testing | 1-2 days | Manual testing, regression checks | |
| Package creation | 30 minutes | Build .plasmoid package | |
| KDE Store review | 1-2 weeks | Varies by workload | |
| User availability | Immediate | Once approved |
Troubleshooting
plasmapkg2 Exit Codes
| Exit Code | Meaning | Solution | |
|---|---|---|---|
0 | Success | Package created/installed correctly | |
1 | Invalid source | Check folder structure, verify contents/ exists | |
2 | Missing metadata.json | Add metadata.json to package root | |
3 | Invalid KPackageStructure | Set to "Plasma/Applet" in metadata.json | |
4 | QML compilation error | Fix QML syntax errors in main.qml | |
5 | Python backend error | Check backend.py for syntax/runtime errors | |
6 | Permission denied | Run with sudo or check file permissions | |
7 | Invalid KPluginMetaData | Verify all required fields in metadata.json | |
8 | Config file error | Check main.xml and config.qml syntax | |
9 | Package too large | Remove unused files, compress properly |
plasmapkg2 Non-Zero Exit Codes
# Common installation failures# Exit code 1: Invalid source directory# Solution: Verify structurels my-plasmoid/package/contents/# Expected: config/, ui/, main.xml# Exit code 2: Missing metadata.json# Solution: Check metadata.json exists in package rootls my-plasmoid/package/metadata.json# Exit code 4: QML compilation error# Solution: Fix QML syntax errorsplasmapkg2 -c -v my-plasmoid.plasmoid 2>&1 | grep -i "qml\|error"# Exit code 7: Invalid KPluginMetaData# Solution: Add all required fieldsjq '.KPlugin' metadata.json# Required: Name, Id, Category, Description, License, Version
Missing KPluginMetaData Errors
Error: Missing required KPluginMetaData field
Solutions:
- Missing Category:
{"KPlugin": {"Category": "System Information", // ← Required"Name": "My Widget","Id": "com.example.my-widget"}}
- Missing License:
{"KPlugin": {"License": "LGPL-2.1-or-later", // ← Required"Name": "My Widget","Id": "com.example.my-widget"}}
- Missing Version:
{"KPlugin": {"Version": "1.0.0", // ← Required"Name": "My Widget","Id": "com.example.my-widget"}}
QML Compilation Errors
// Error: "Object 'backend' not found"// Solution: Ensure backend is instantiated before referenceWidgetBackend {id: backend}PlasmaComponents3.Label {text: backend.data // ← Only valid after backend initialization}
Common QML Issues:
- Import errors:
// Error: "Import 'org.kde.plasma.plasmoid 2.0' is not allowed"// Solution: Use Plasma 6 import (no version number)import org.kde.plasma.plasmoid // ← No version
- Property binding errors:
// Error: "Cannot assign to const property"// Solution: Use cfg_ prefix for config propertiesproperty alias cfg_customLabel: label.text // ← Correct
- Undefined signals:
// Error: "Signal 'dataChanged' is not defined"// Solution: Ensure backend has matching signalclass WidgetBackend(QObject):dataChanged = Signal() // ← Must match QML
KPackageStructure Errors
Error: KPackageStructure must be "Plasma/Applet"
Fix in metadata.json:
{"KPackageStructure": "Plasma/Applet", // ← Required value"X-Plasma-API-Minimum-Version": "6.0"}
Widget Not Appearing
| Issue | Solution | |
|---|---|---|
Missing X-Plasma-API-Minimum-Version | Add "X-Plasma-API-Minimum-Version": "6.0" to metadata.json | |
Wrong KPackageStructure | Set to "Plasma/Applet" | |
| Missing main.qml | Ensure contents/ui/main.qml exists | |
| Wrong Id format | Use reverse domain: com.example.widget | |
| Permission denied | Check file permissions on widget directory | |
| Category not found | Verify category matches predefined list |
Python Backend Not Loading
# Check Python pathplasmapkg2 -l com.example.my-plasmoid 2>&1 | grep -i python# Verify importspython3 -c "from src.backend import WidgetBackend"# Check Qt versionpython3 -c "from PySide6 import QtCore; print(QtCore.__version__)"
Common Backend Issues:
- Import errors:
# Error: "No module named 'src.backend'"# Solution: Update PySide6.QtQml importsfrom PySide6.QtQml import qmlRegisterTypeqmlRegisterType(WidgetBackend, "com.example.widget", 1, 0, "WidgetBackend")
- Signal/slot mismatch:
# Error: "Signal not emitted or not connected"# Solution: Use correct Signal typedataUpdated = Signal() # ← Must match QML signal name
- Attribute errors:
# Error: "'WidgetBackend' object has no attribute '_data'"# Solution: Initialize in __init__def __init__(self, parent=None):super().__init__(parent)self._data = "Initial" # ← Initialize before use
Configuration Not Saving
- Check
main.xmluses correct types - Property aliases use
cfg_prefix - Config file:
~/.config/plasma-org.kde.plasma.desktop-appletsrc
Debug config saving:
# View config filecat ~/.config/plasma-org.kde.plasma.desktop-appletsrc# Reload shell after changeskquitapp6 plasmashell && kstart6 plasmashell# Verify config persistedplasmapkg2 -l com.example.my-plasmoid
Script Installation
systemd service (optional, for testing):
# /etc/systemd/system/plasmoid-test.service[Unit]Description=Plasmoid Test ServiceAfter=plasma-org.kde.plasma.desktop-appletsrc.service[Service]Type=simpleExecStart=/usr/bin/plasmoidtest com.example.my-plasmoidRestart=always[Install]WantedBy=multi-user.target
Advanced Debugging
# Full widget debug modeexport QT_LOGGING_RULES="*.debug=true"export KWIN_DEBUG_LOGGING=1plasmapkg2 -l com.example.my-plasmoid 2>&1 | tee widget-debug.log# Capture widget runtime errorsjournalctl -u plasma-org.kde.plasma.desktop-appletsrc -f | grep -i error# Check widget manifestunzip -p my-plasmoid.plasmoid metadata.json | jq .KPlugin# Validate QML syntaxqmlcppcheck main.qml
Best Practices
Python Backend
# ✅ GOOD: Signal-based updatesclass Backend(QObject):dataChanged = Signal()def updateData(self):self._data = compute()self.dataChanged.emit()# ✅ GOOD: Lazy initialization@Slot(result=str)def expensiveData(self):if not hasattr(self, '_cached'):self._cached = self._computeExpensive()return self._cached# ❌ BAD: Blocking main thread@Slot(result=str)def slowOperation(self):time.sleep(5) # Blocks UI
QML UI
// ✅ GOOD: Use Kirigami units for scalingwidth: Kirigami.Units.gridUnit * 10spacing: Kirigami.Units.smallSpacing// ✅ GOOD: Handle configuration defaultstext: plasmoid.configuration.label || i18n("Default")// ❌ BAD: Hardcoded valueswidth: 320 // Won't scale on HiDPI
Performance
# Use Timer for periodic updatesfrom PySide6.QtCore import QTimerclass Backend(QObject):def __init__(self):self._timer = QTimer()self._timer.timeout.connect(self.refresh)self._timer.start(60000) # 60 seconds
Testing/Debugging
Unit Testing Backend
#!/usr/bin/env python3"""Unit tests for WidgetBackend"""import unittestfrom src.backend import WidgetBackendclass TestWidgetBackend(unittest.TestCase):def setUp(self):self.backend = WidgetBackend()def test_initial_data(self):self.assertEqual(self.backend.getData(), "Initial Value")def test_set_data(self):self.backend.setData("New Value")self.assertEqual(self.backend.getData(), "New Value")def test_process_data(self):result = self.backend.processData("test")self.assertEqual(result, "Processed: test")def test_refresh(self):self.backend.refresh()self.assertIn("Updated", self.backend.getData())if __name__ == "__main__":unittest.main()
Runtime Testing
# Test widget in isolationplasmoidtest com.example.my-plasmoid# Test with specific plasma locationkrun "plasma-shell --test"# Monitor widget eventsjournalctl -u plasma-org.kde.plasma.desktop-appletsrc -f
Automated Testing
#!/usr/bin/env python3"""Integration tests for complete plasmoid"""import subprocessimport sysimport tempfileimport osdef test_plasmoid_package():"""Test plasmoid package creation"""with tempfile.TemporaryDirectory() as tmpdir:# Test packagingresult = subprocess.run(["plasmapkg2", "-c", "-o", "test.plasmoid", "package/"],capture_output=True,text=True)assert result.returncode == 0, f"Packages failed: {result.stderr}"# Verify packageresult = subprocess.run(["unzip", "-l", "test.plasmoid"],capture_output=True,text=True)assert "metadata.json" in result.stdout, "Missing metadata.json"assert "main.qml" in result.stdout, "Missing main.qml"if __name__ == "__main__":test_plasmoid_package()print("All tests passed!")
Manual Testing Checklist
- [ ] Widget appears in Add Widgets panel
- [ ] Widget expands/collapses correctly
- [ ] Configuration dialog opens and saves
- [ ] Data updates reflect in UI
- [ ] System info displays correctly
- [ ] Buttons respond to clicks
- [ ] Compact representation shows in panel
- [ ] Tooltip displays properly
- [ ] No console errors when running
Debug Tools
# Full widget debug modeexport QT_LOGGING_RULES="*.debug=true"export KWIN_DEBUG_LOGGING=1plasmapkg2 -l com.example.my-plasmoid 2>&1 | tee widget-debug.log# Monitor widget eventsjournalctl -u plasma-org.kde.plasma.desktop-appletsrc -f | grep -i error# Check widget manifestunzip -p my-plasmoid.plasmoid metadata.json | jq .KPlugin# Validate QML syntaxqmlcppcheck main.qml