diff --git a/README.md b/README.md
index d082a17..7564fa5 100644
--- a/README.md
+++ b/README.md
@@ -42,9 +42,13 @@ Fully-featured widget to bring Latte-Dock and WM status bar customization featur
* [Built-in presets](https://github.com/luisbocanegra/plasma-panel-colorizer/tree/main/package/contents/ui/presets)
* Create your own presets
* Preset auto-loading
- * Floating panel
- * Maximized window shown
- * Window touching panel
+ * Fullscreen window
+ * Maximized window
+ * Window touching the panel
+ * At least one window is shown on screen
+ * Panel is floating
+ * Normal (fall-back when none of the above are meet)
+* [Switch presets from the command-line using D-Bus](#switching-presets-using-the-commandline-with-d-bus)
### Panel/Widget/System Tray elements
@@ -96,8 +100,14 @@ Overrides let you give a completely different configuration to one or more widge
### KDE Store
-1. **Right click on the Panel** > **Add or manage widgets** > **Add new...** > **Download new...**
-2. **Search** for "**Panel Colorizer**", install and add it to a Panel.
+1. Install these runtime dependencies or the equivalents for your distribution
+
+ ```txt
+ python python-dbus python-gobject
+ ```
+
+2. **Right click on the Panel** > **Add or manage widgets** > **Add new...** > **Download new...**
+3. **Search** for "**Panel Colorizer**", install and add it to a Panel.
* ~~[Plasma 5](https://store.kde.org/p/2131149) version v0.2.0~~ **[No longer maintained](https://github.com/luisbocanegra/plasma-panel-colorizer/issues/10)**
@@ -110,13 +120,13 @@ Overrides let you give a completely different configuration to one or more widge
* Arch
```txt
- git gcc cmake extra-cmake-modules libplasma spectacle
+ git gcc cmake extra-cmake-modules libplasma spectacle python python-dbus python-gobject
```
* Fedora
```txt
- git gcc-c++ cmake extra-cmake-modules libplasma-devel spectacle
+ git gcc-c++ cmake extra-cmake-modules libplasma-devel spectacle python3 python3-dbus python3-gobject
```
Spectacle is optional, will be used to create preset previews
@@ -187,6 +197,18 @@ To install the widget use one of these methods:
2. Go to the widget settings to change the current panel appearance (right click > Configure...)
3. Widget can set to only show in panel **Edit Mode** (right click > Hide widget or from the widget settings)
+### Switching presets using the commandline with D-Bus
+
+1. Go to the widget settings
+2. In General tab enable the D-Bus service
+3. To apply a preset can use qdbus6 and pass the absolute path of a preset:
+
+ ```sh
+ qdbus6 luisbocanegra.panel.colorizer.c337.w2346 /preset preset /path/to/preset/dir/
+ ```
+
+Each widget instance has its own D-bus name, you can get it from the same widget settings General tab.
+
## Adding or improving the built-in presets
Instructions to add new presets or improve the existing ones are [here](https://github.com/luisbocanegra/plasma-panel-colorizer/blob/main/package/contents/ui/presets/README.md)
@@ -243,7 +265,7 @@ Backgrounds are drawn by creating rectangle areas bellow widgets/panel, text and
**Performance**
-I tried to optimize it so CPU usage only increases around 0.5-1% on my computer, but usage could vary depending on your System or how many widgets are in your panels.
+I tried to optimize it so CPU usage only increases around 0.5-1% on my computer, usage may vary depending on your System or how many widgets are in your panels.
### Can this widget change the appearance of other parts of Plasma (e.g Desktop view, widget popups/tooltips, overview)
diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml
index f4e5b42..0c75a36 100644
--- a/package/contents/config/main.xml
+++ b/package/contents/config/main.xml
@@ -69,5 +69,18 @@
type="Int">
0
+
+ python3
+
+
+ false
+
+
+ 250
+
diff --git a/package/contents/ui/DBusServiceModel.qml b/package/contents/ui/DBusServiceModel.qml
new file mode 100644
index 0000000..6b9dfd5
--- /dev/null
+++ b/package/contents/ui/DBusServiceModel.qml
@@ -0,0 +1,73 @@
+import QtQuick
+import org.kde.plasma.plasmoid
+
+
+Item {
+
+ id: root
+ property bool enabled: false
+ property string preset: ""
+ property bool switchIsPending: false
+ property int poolingRate: 250
+
+ property string toolsDir: Qt.resolvedUrl("./tools").toString().substring(7) + "/"
+ property string serviceUtil: toolsDir+"service.py"
+ property string pythonExecutable: plasmoid.configuration.pythonExecutable
+ property string serviceCmd: pythonExecutable + " '" + serviceUtil + "' " + Plasmoid.containment.id + " " + Plasmoid.id
+ property string dbusName: Plasmoid.metaData.pluginId + ".c" + Plasmoid.containment.id + ".w" + Plasmoid.id
+ property string gdbusPartial: "gdbus call --session --dest "+dbusName+" --object-path /preset --method "+dbusName
+ property string pendingSwitchCmd: gdbusPartial +".pending_switch"
+ property string switchDoneCmd: gdbusPartial +".switch_done"
+ property string getPresetCmd: gdbusPartial +".preset"
+ property string quitServiceCmd: gdbusPartial +".quit"
+
+ RunCommand {
+ id: runCommand
+ onExited: (cmd, exitCode, exitStatus, stdout, stderr) => {
+ // console.error(cmd, exitCode, exitStatus, stdout, stderr)
+ if (exitCode!==0) return
+ stdout = stdout.trim().replace(/[()',]/g, "")
+ // console.log("stdout parsed:", stdout)
+ if(cmd === pendingSwitchCmd) {
+ switchIsPending = stdout === "true"
+ }
+ if (cmd === getPresetCmd) {
+ preset = stdout
+ switchIsPending = false
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ toggleService()
+ }
+
+ function toggleService() {
+ if (enabled) {
+ runCommand.run(serviceCmd)
+ } else (
+ runCommand.run(quitServiceCmd)
+ )
+ }
+
+ onEnabledChanged: toggleService()
+
+ onSwitchIsPendingChanged: {
+ if (switchIsPending) {
+ runCommand.run(switchDoneCmd)
+ runCommand.run(getPresetCmd)
+ }
+ }
+
+ Timer {
+ id: updateTimer
+ interval: poolingRate
+ running: enabled
+ repeat: true
+ onTriggered: {
+ if (switchIsPending) return
+ runCommand.run(pendingSwitchCmd)
+ }
+ }
+}
+
diff --git a/package/contents/ui/code/globals.js b/package/contents/ui/code/globals.js
index 5f56225..3c3d640 100644
--- a/package/contents/ui/code/globals.js
+++ b/package/contents/ui/code/globals.js
@@ -260,5 +260,8 @@ const ignoredConfigs = [
"configurationOverrides",
"widgetClickMode",
"switchPresets",
- "switchPresetsIndex"
+ "switchPresetsIndex",
+ "enableDBusService",
+ "dBusPollingRate",
+ "pythonExecutable"
]
diff --git a/package/contents/ui/configGeneral.qml b/package/contents/ui/configGeneral.qml
index d67bd95..4d2c9d2 100644
--- a/package/contents/ui/configGeneral.qml
+++ b/package/contents/ui/configGeneral.qml
@@ -16,6 +16,15 @@ KCM.SimpleKCM {
property bool cfg_hideWidget: hideWidget.checked
property alias cfg_isEnabled: headerComponent.isEnabled
property alias cfg_enableDebug: enableDebug.checked
+ property alias cfg_enableDBusService: enableDBusService.checked
+ property alias cfg_pythonExecutable: pythonExecutable.text
+ property alias cfg_dBusPollingRate: dBusPollingRate.value
+
+ property string presetsDir: StandardPaths.writableLocation(
+ StandardPaths.HomeLocation).toString().substring(7) + "/.config/panel-colorizer/presets"
+ property string presetsBuiltinDir: Qt.resolvedUrl("./presets").toString().substring(7) + "/"
+
+ property string dbusName: Plasmoid.metaData.pluginId + ".c" + Plasmoid.containment.id + ".w" + Plasmoid.id
header: ColumnLayout {
Components.Header {
@@ -27,6 +36,7 @@ KCM.SimpleKCM {
ColumnLayout {
Kirigami.FormLayout {
+ id: form
CheckBox {
Kirigami.FormData.label: i18n("Hide widget:")
id: hideWidget
@@ -41,6 +51,80 @@ KCM.SimpleKCM {
onCheckedChanged: cfg_enableDebug = checked
text: i18n("Show debugging information")
}
+
+ Kirigami.Separator {
+ Kirigami.FormData.isSection: true
+ Kirigami.FormData.label: i18n("D-Bus Service")
+ Layout.fillWidth: true
+ }
+
+ CheckBox {
+ Kirigami.FormData.label: i18n("Enabled:")
+ id: enableDBusService
+ checked: cfg_enableDBusService
+ onCheckedChanged: cfg_enableDBusService = checked
+ text: i18n("D-Bus name:") + " " + dbusName
+ }
+
+ Label {
+ text: i18n("Each Panel Colorizer instance has its D-Bus name.")
+ wrapMode: Text.WordWrap
+ Layout.preferredWidth: 400
+ opacity: 0.6
+ }
+
+ TextField {
+ Kirigami.FormData.label: i18n("Python 3 executable:")
+ id: pythonExecutable
+ placeholderText: qsTr("Python executable e.g. python, python3")
+ enabled: enableDBusService.checked
+ }
+
+ Label {
+ text: i18n("Required to run the D-Bus service in the background")
+ wrapMode: Text.WordWrap
+ Layout.preferredWidth: 400
+ opacity: 0.6
+ }
+
+ SpinBox {
+ Kirigami.FormData.label: i18n("Polling rate:")
+ from: 10
+ to: 9999
+ stepSize: 100
+ id: dBusPollingRate
+ }
+
+ Label {
+ text: i18n("How fast the widget reacts to D-Bus changes")
+ wrapMode: Text.WordWrap
+ Layout.preferredWidth: 400
+ opacity: 0.6
+ }
+
+
+ Label {
+ Kirigami.FormData.label: i18n("Usage:")
+ text: i18n("Apply a preset:")
+ }
+
+ TextArea {
+ text: "qdbus6 " + dbusName + " /preset preset /path/to/preset/dir/"
+ readOnly: true
+ wrapMode: Text.WordWrap
+ Layout.preferredWidth: 400
+ }
+
+ Label {
+ text: i18n("Preview and switch presets using fzf + qdbus6 + jq:")
+ }
+
+ TextArea {
+ text: "find " + presetsBuiltinDir + " "+ presetsDir +" -mindepth 1 -prune -type d | fzf --preview 'qdbus6 " + dbusName + " /preset preset {} && jq --color-output . {}/settings.json'"
+ readOnly: true
+ wrapMode: Text.WordWrap
+ Layout.preferredWidth: 400
+ }
}
}
}
diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml
index 101e080..c3f6ed5 100644
--- a/package/contents/ui/main.qml
+++ b/package/contents/ui/main.qml
@@ -150,7 +150,7 @@ PlasmoidItem {
onStockPanelSettingsChanged: {
Qt.callLater(function() {
- console.error(JSON.stringify(stockPanelSettings))
+ // console.error(JSON.stringify(stockPanelSettings))
let script = Utils.setPanelModeScript(panelPosition, stockPanelSettings)
Utils.evaluateScript(script)
})
@@ -1333,7 +1333,7 @@ PlasmoidItem {
if (!child.applet?.plasmoid?.pluginName) continue
// if (Utils.getBgManaged(child)) continue
// console.error(child.applet?.plasmoid?.pluginName)
- if (child.applet.plasmoid.pluginName !== "luisbocanegra.panel.colorizer") {
+ if (child.applet.plasmoid.pluginName !== Plasmoid.metaData.pluginId) {
child.applet.plasmoid.contextualActions.push(configureAction)
}
const isTray = child.applet.plasmoid.pluginName === "org.kde.plasma.systemtray"
@@ -1433,20 +1433,20 @@ PlasmoidItem {
}
}
+ // toolTipMainText: onDesktop ? "" :
toolTipSubText: {
+ let text = ""
if (onDesktop) {
- return "Panel not found, this widget must be child of a panel"
- }
- if (!plasmoid.configuration.isEnabled) {
- return ""
- }
- const name = plasmoid.configuration.lastPreset.split("/")
- if (name.length) {
- return i18n("Last preset loaded:") + " " + name[name.length-1]
+ text = "Panel not found, this widget must be child of a panel"
+ } else if (plasmoid.configuration.isEnabled) {
+ const name = plasmoid.configuration.lastPreset.split("/")
+ if (name.length) {
+ text = i18n("Last preset loaded:") + " " + name[name.length-1]
+ }
}
- return ""
+ return text
}
- toolTipTextFormat: Text.RichText
+ toolTipTextFormat: Text.PlainText
Plasmoid.status: (editMode || !hideWidget) ?
PlasmaCore.Types.ActiveStatus :
@@ -1588,4 +1588,12 @@ PlasmoidItem {
}
fullRepresentation: onDesktop ? desktopView : popupView
+
+ DBusServiceModel {
+ enabled: plasmoid.configuration.enableDBusService
+ poolingRate: plasmoid.configuration.dBusPollingRate
+ onPresetChanged: {
+ applyPreset(preset)
+ }
+ }
}
diff --git a/package/contents/ui/tools/service.py b/package/contents/ui/tools/service.py
new file mode 100644
index 0000000..7971744
--- /dev/null
+++ b/package/contents/ui/tools/service.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+"""
+D-Bus service to interact with the current panel
+"""
+
+import sys
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+from gi.repository import GLib
+
+DBusGMainLoop(set_as_default=True)
+bus = dbus.SessionBus()
+
+CONTAINMENT_ID = sys.argv[1]
+PANEL_ID = sys.argv[2]
+SERVICE_NAME = "luisbocanegra.panel.colorizer.c" + CONTAINMENT_ID + ".w" + PANEL_ID
+PATH = "/preset"
+
+
+class Service(dbus.service.Object):
+ """D-Bus service
+
+ Args:
+ dbus (dbus.service.Object): D-Bus object
+ """
+
+ def __init__(self):
+ self._loop = GLib.MainLoop()
+ self._last_preset = ""
+ self._pending_witch = False
+ super().__init__()
+
+ def run(self):
+ """run"""
+ DBusGMainLoop(set_as_default=True)
+ bus_name = dbus.service.BusName(SERVICE_NAME, dbus.SessionBus())
+ dbus.service.Object.__init__(self, bus_name, PATH)
+
+ print("Service running...")
+ self._loop.run()
+ print("Service stopped")
+
+ @dbus.service.method(SERVICE_NAME, in_signature="s", out_signature="s")
+ def preset(self, m="") -> str:
+ """Set and get the last applied preset
+
+ Args:
+ m (str, optional): Preset. Defaults to "".
+
+ Returns:
+ str: "saved" or current Preset
+ """
+ if m:
+ if m != self._last_preset:
+ print(f"last_last_preset: '{m}'")
+ self._last_preset = m
+ self._pending_witch = True
+ return "saved"
+ return self._last_preset
+
+ @dbus.service.method(SERVICE_NAME, in_signature="", out_signature="b")
+ def pending_switch(self) -> bool:
+ """Wether there is a pending preset switch
+
+ Returns:
+ bool: Pending
+ """
+ return self._pending_witch
+
+ @dbus.service.method(SERVICE_NAME, in_signature="", out_signature="")
+ def switch_done(self):
+ """Void the pending switch"""
+ self._pending_witch = False
+
+ @dbus.service.method(SERVICE_NAME, in_signature="", out_signature="")
+ def quit(self):
+ """Stop the service"""
+ print("Shutting down")
+ self._loop.quit()
+
+
+if __name__ == "__main__":
+ # Keep a single instance of the service
+ try:
+ bus.get_object(SERVICE_NAME, PATH)
+ print("Service is already running")
+ except dbus.exceptions.DBusException:
+ Service().run()