Skip to content

Commit b549534

Browse files
committed
feat: register a D-Bus service per widget to apply presets
Should be enabled first from the widget settings General tab example usage: qdbus6 luisbocanegra.panel.colorizer.c337.w2346 /preset luisbocanegra.panel.colorizer.c337.w2346.preset /path/to/preset/dir refs: #126
1 parent 9cca3c7 commit b549534

File tree

6 files changed

+282
-13
lines changed

6 files changed

+282
-13
lines changed

package/contents/config/main.xml

+13
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,18 @@
6969
type="Int">
7070
<default>0</default>
7171
</entry>
72+
<entry name="pythonExecutable" type="String">
73+
<default>python3</default>
74+
</entry>
75+
<entry
76+
name="enableDBusService"
77+
type="Bool">
78+
<default>false</default>
79+
</entry>
80+
<entry
81+
name="dBusPollingRate"
82+
type="Int">
83+
<default>250</default>
84+
</entry>
7285
</group>
7386
</kcfg>
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import QtQuick
2+
import org.kde.plasma.plasmoid
3+
4+
5+
Item {
6+
7+
id: root
8+
property bool enabled: false
9+
property string preset: ""
10+
property bool switchIsPending: false
11+
property int poolingRate: 250
12+
13+
property string toolsDir: Qt.resolvedUrl("./tools").toString().substring(7) + "/"
14+
property string serviceUtil: toolsDir+"service.py"
15+
property string pythonExecutable: plasmoid.configuration.pythonExecutable
16+
property string serviceCmd: pythonExecutable + " '" + serviceUtil + "' " + Plasmoid.containment.id + " " + Plasmoid.id
17+
property string dbusName: Plasmoid.metaData.pluginId + ".c" + Plasmoid.containment.id + ".w" + Plasmoid.id
18+
property string gdbusPartial: "gdbus call --session --dest "+dbusName+" --object-path /preset --method "+dbusName
19+
property string pendingSwitchCmd: gdbusPartial +".pending_switch"
20+
property string switchDoneCmd: gdbusPartial +".switch_done"
21+
property string getPresetCmd: gdbusPartial +".preset"
22+
property string quitServiceCmd: gdbusPartial +".quit"
23+
24+
RunCommand {
25+
id: runCommand
26+
onExited: (cmd, exitCode, exitStatus, stdout, stderr) => {
27+
// console.error(cmd, exitCode, exitStatus, stdout, stderr)
28+
if (exitCode!==0) return
29+
stdout = stdout.trim().replace(/[()',]/g, "")
30+
// console.log("stdout parsed:", stdout)
31+
if(cmd === pendingSwitchCmd) {
32+
switchIsPending = stdout === "true"
33+
}
34+
if (cmd === getPresetCmd) {
35+
preset = stdout
36+
switchIsPending = false
37+
}
38+
}
39+
}
40+
41+
Component.onCompleted: {
42+
toggleService()
43+
}
44+
45+
function toggleService() {
46+
if (enabled) {
47+
runCommand.run(serviceCmd)
48+
} else (
49+
runCommand.run(quitServiceCmd)
50+
)
51+
}
52+
53+
onEnabledChanged: toggleService()
54+
55+
onSwitchIsPendingChanged: {
56+
if (switchIsPending) {
57+
runCommand.run(switchDoneCmd)
58+
runCommand.run(getPresetCmd)
59+
}
60+
}
61+
62+
Timer {
63+
id: updateTimer
64+
interval: poolingRate
65+
running: enabled
66+
repeat: true
67+
onTriggered: {
68+
if (switchIsPending) return
69+
runCommand.run(pendingSwitchCmd)
70+
}
71+
}
72+
}
73+

package/contents/ui/code/globals.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -260,5 +260,7 @@ const ignoredConfigs = [
260260
"configurationOverrides",
261261
"widgetClickMode",
262262
"switchPresets",
263-
"switchPresetsIndex"
263+
"switchPresetsIndex",
264+
"enableDBusService",
265+
"dBusPollingRate"
264266
]

package/contents/ui/configGeneral.qml

+84
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ KCM.SimpleKCM {
1616
property bool cfg_hideWidget: hideWidget.checked
1717
property alias cfg_isEnabled: headerComponent.isEnabled
1818
property alias cfg_enableDebug: enableDebug.checked
19+
property alias cfg_enableDBusService: enableDBusService.checked
20+
property alias cfg_pythonExecutable: pythonExecutable.text
21+
property alias cfg_dBusPollingRate: dBusPollingRate.value
22+
23+
property string presetsDir: StandardPaths.writableLocation(
24+
StandardPaths.HomeLocation).toString().substring(7) + "/.config/panel-colorizer/presets"
25+
property string presetsBuiltinDir: Qt.resolvedUrl("./presets").toString().substring(7) + "/"
26+
27+
property string dbusName: Plasmoid.metaData.pluginId + ".c" + Plasmoid.containment.id + ".w" + Plasmoid.id
1928

2029
header: ColumnLayout {
2130
Components.Header {
@@ -27,6 +36,7 @@ KCM.SimpleKCM {
2736

2837
ColumnLayout {
2938
Kirigami.FormLayout {
39+
id: form
3040
CheckBox {
3141
Kirigami.FormData.label: i18n("Hide widget:")
3242
id: hideWidget
@@ -41,6 +51,80 @@ KCM.SimpleKCM {
4151
onCheckedChanged: cfg_enableDebug = checked
4252
text: i18n("Show debugging information")
4353
}
54+
55+
Kirigami.Separator {
56+
Kirigami.FormData.isSection: true
57+
Kirigami.FormData.label: i18n("D-Bus Service")
58+
Layout.fillWidth: true
59+
}
60+
61+
CheckBox {
62+
Kirigami.FormData.label: i18n("Enabled:")
63+
id: enableDBusService
64+
checked: cfg_enableDBusService
65+
onCheckedChanged: cfg_enableDBusService = checked
66+
text: i18n("D-Bus name:") + " " + dbusName
67+
}
68+
69+
Label {
70+
text: i18n("Each Panel Colorizer instance has its D-Bus name.")
71+
wrapMode: Text.WordWrap
72+
Layout.preferredWidth: 400
73+
opacity: 0.6
74+
}
75+
76+
TextField {
77+
Kirigami.FormData.label: i18n("Python 3 executable:")
78+
id: pythonExecutable
79+
placeholderText: qsTr("Python executable e.g. python, python3")
80+
enabled: enableDBusService.checked
81+
}
82+
83+
Label {
84+
text: i18n("Required to run the D-Bus service in the background")
85+
wrapMode: Text.WordWrap
86+
Layout.preferredWidth: 400
87+
opacity: 0.6
88+
}
89+
90+
SpinBox {
91+
Kirigami.FormData.label: i18n("Polling rate:")
92+
from: 10
93+
to: 9999
94+
stepSize: 100
95+
id: dBusPollingRate
96+
}
97+
98+
Label {
99+
text: i18n("How fast the widget reacts to D-Bus changes")
100+
wrapMode: Text.WordWrap
101+
Layout.preferredWidth: 400
102+
opacity: 0.6
103+
}
104+
105+
106+
Label {
107+
Kirigami.FormData.label: i18n("Usage:")
108+
text: i18n("Apply a preset:")
109+
}
110+
111+
TextArea {
112+
text: "qdbus6 " + dbusName + " /preset preset /path/to/preset/dir/"
113+
readOnly: true
114+
wrapMode: Text.WordWrap
115+
Layout.preferredWidth: 400
116+
}
117+
118+
Label {
119+
text: i18n("Preview and switch presets using fzf + qdbus6 + jq:")
120+
}
121+
122+
TextArea {
123+
text: "find " + presetsBuiltinDir + " "+ presetsDir +" -mindepth 1 -prune -type d | fzf --preview 'qdbus6 " + dbusName + " /preset preset {} && jq --color-output . {}/settings.json'"
124+
readOnly: true
125+
wrapMode: Text.WordWrap
126+
Layout.preferredWidth: 400
127+
}
44128
}
45129
}
46130
}

package/contents/ui/main.qml

+20-12
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ PlasmoidItem {
150150

151151
onStockPanelSettingsChanged: {
152152
Qt.callLater(function() {
153-
console.error(JSON.stringify(stockPanelSettings))
153+
// console.error(JSON.stringify(stockPanelSettings))
154154
let script = Utils.setPanelModeScript(panelPosition, stockPanelSettings)
155155
Utils.evaluateScript(script)
156156
})
@@ -1340,7 +1340,7 @@ PlasmoidItem {
13401340
if (!child.applet?.plasmoid?.pluginName) continue
13411341
// if (Utils.getBgManaged(child)) continue
13421342
// console.error(child.applet?.plasmoid?.pluginName)
1343-
if (child.applet.plasmoid.pluginName !== "luisbocanegra.panel.colorizer") {
1343+
if (child.applet.plasmoid.pluginName !== Plasmoid.metaData.pluginId) {
13441344
child.applet.plasmoid.contextualActions.push(configureAction)
13451345
}
13461346
const isTray = child.applet.plasmoid.pluginName === "org.kde.plasma.systemtray"
@@ -1440,20 +1440,20 @@ PlasmoidItem {
14401440
}
14411441
}
14421442

1443+
// toolTipMainText: onDesktop ? "" :
14431444
toolTipSubText: {
1445+
let text = ""
14441446
if (onDesktop) {
1445-
return "<font color='"+Kirigami.Theme.neutralTextColor+"'>Panel not found, this widget must be child of a panel</font>"
1446-
}
1447-
if (!plasmoid.configuration.isEnabled) {
1448-
return ""
1449-
}
1450-
const name = plasmoid.configuration.lastPreset.split("/")
1451-
if (name.length) {
1452-
return i18n("Last preset loaded:") + " " + name[name.length-1]
1447+
text = "<font color='"+Kirigami.Theme.neutralTextColor+"'>Panel not found, this widget must be child of a panel</font>"
1448+
} else if (plasmoid.configuration.isEnabled) {
1449+
const name = plasmoid.configuration.lastPreset.split("/")
1450+
if (name.length) {
1451+
text = i18n("Last preset loaded:") + " " + name[name.length-1]
1452+
}
14531453
}
1454-
return ""
1454+
return text
14551455
}
1456-
toolTipTextFormat: Text.RichText
1456+
toolTipTextFormat: Text.PlainText
14571457

14581458
Plasmoid.status: (editMode || !hideWidget) ?
14591459
PlasmaCore.Types.ActiveStatus :
@@ -1595,4 +1595,12 @@ PlasmoidItem {
15951595
}
15961596

15971597
fullRepresentation: onDesktop ? desktopView : popupView
1598+
1599+
DBusServiceModel {
1600+
enabled: plasmoid.configuration.enableDBusService
1601+
poolingRate: plasmoid.configuration.dBusPollingRate
1602+
onPresetChanged: {
1603+
applyPreset(preset)
1604+
}
1605+
}
15981606
}

package/contents/ui/tools/service.py

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python
2+
"""
3+
D-Bus service to interact with the current panel
4+
"""
5+
6+
import sys
7+
import dbus
8+
import dbus.service
9+
from dbus.mainloop.glib import DBusGMainLoop
10+
from gi.repository import GLib
11+
12+
DBusGMainLoop(set_as_default=True)
13+
bus = dbus.SessionBus()
14+
15+
CONTAINMENT_ID = sys.argv[1]
16+
PANEL_ID = sys.argv[2]
17+
SERVICE_NAME = "luisbocanegra.panel.colorizer.c" + CONTAINMENT_ID + ".w" + PANEL_ID
18+
PATH = "/preset"
19+
20+
21+
class Service(dbus.service.Object):
22+
"""D-Bus service
23+
24+
Args:
25+
dbus (dbus.service.Object): D-Bus object
26+
"""
27+
28+
def __init__(self):
29+
self._loop = GLib.MainLoop()
30+
self._last_preset = ""
31+
self._pending_witch = False
32+
super().__init__()
33+
34+
def run(self):
35+
"""run"""
36+
DBusGMainLoop(set_as_default=True)
37+
bus_name = dbus.service.BusName(SERVICE_NAME, dbus.SessionBus())
38+
dbus.service.Object.__init__(self, bus_name, PATH)
39+
40+
print("Service running...")
41+
self._loop.run()
42+
print("Service stopped")
43+
44+
@dbus.service.method(SERVICE_NAME, in_signature="s", out_signature="s")
45+
def preset(self, m="") -> str:
46+
"""Set and get the last applied preset
47+
48+
Args:
49+
m (str, optional): Preset. Defaults to "".
50+
51+
Returns:
52+
str: "saved" or current Preset
53+
"""
54+
if m:
55+
if m != self._last_preset:
56+
print(f"last_last_preset: '{m}'")
57+
self._last_preset = m
58+
self._pending_witch = True
59+
return "saved"
60+
return self._last_preset
61+
62+
@dbus.service.method(SERVICE_NAME, in_signature="", out_signature="b")
63+
def pending_switch(self) -> bool:
64+
"""Wether there is a pending preset switch
65+
66+
Returns:
67+
bool: Pending
68+
"""
69+
return self._pending_witch
70+
71+
@dbus.service.method(SERVICE_NAME, in_signature="", out_signature="")
72+
def switch_done(self):
73+
"""Void the pending switch"""
74+
self._pending_witch = False
75+
76+
@dbus.service.method(SERVICE_NAME, in_signature="", out_signature="")
77+
def quit(self):
78+
"""Stop the service"""
79+
print("Shutting down")
80+
self._loop.quit()
81+
82+
83+
if __name__ == "__main__":
84+
# Keep a single instance of the service
85+
try:
86+
bus.get_object(SERVICE_NAME, PATH)
87+
print("Service is already running")
88+
except dbus.exceptions.DBusException:
89+
Service().run()

0 commit comments

Comments
 (0)