-
-
Save Zren/764f17c26be4ea0e088f4a6a1871f528 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
""" | |
Usage: | |
plasmasetconfig # List all widget namespaces | |
plasmasetconfig org.kde.plasma.digitalclock # List all config groups+keys | |
plasmasetconfig org.kde.plasma.digitalclock Appearance showSeconds true | |
Install: | |
chmod +x ~/Downloads/plasmasetconfig.py | |
sudo cp ~/Downloads/plasmasetconfig.py /usr/local/bin/plasmasetconfig | |
Uninstall: | |
sudo rm /usr/local/bin/plasmasetconfig | |
""" | |
import argparse | |
import dbus | |
import os | |
import re | |
import subprocess | |
import sys | |
def writeConfigKey(args): | |
widgetType = args.widget or "" | |
configGroup = args.group or "" | |
configKey = args.key or "" | |
configValue = args.value or "" | |
# print("widgetType", widgetType) | |
# print("configGroup", configGroup) | |
# print("configKey", configKey) | |
# print("configValue", configValue) | |
# https://userbase.kde.org/KDE_System_Administration/PlasmaDesktopScripting | |
plasmaScript = """ | |
function forEachWidgetInContainment(containment, callback) { | |
var widgets = containment.widgets(); | |
for (var widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++) { | |
var widget = widgets[widgetIndex]; | |
callback(widget, containment); | |
if (widget.type == "org.kde.plasma.systemtray") { | |
var childContainmentId = widget.readConfig("SystrayContainmentId"); | |
if (typeof childContainmentId !== "undefined") { | |
var childContainment = desktopById(childContainmentId); | |
if (typeof childContainment !== "undefined" && childContainment.type == "org.kde.plasma.private.systemtray") { | |
forEachWidgetInContainment(childContainment, callback); | |
} | |
} | |
} | |
} | |
} | |
function forEachWidgetInContainmentList(containmentList, callback) { | |
for (var containmentIndex = 0; containmentIndex < containmentList.length; containmentIndex++) { | |
var containment = containmentList[containmentIndex]; | |
forEachWidgetInContainment(containment, callback); | |
} | |
} | |
function forEachWidget(callback) { | |
forEachWidgetInContainmentList(desktops(), callback); | |
forEachWidgetInContainmentList(panels(), callback); | |
} | |
function forEachWidgetByType(type, callback) { | |
forEachWidget(function(widget, containment) { | |
if (widget.type == type) { | |
callback(widget, containment); | |
} | |
}); | |
} | |
function widgetSetProperty(args) { | |
if (!(args.widgetType && args.configGroup && args.configKey)) { | |
return; | |
} | |
forEachWidgetByType(args.widgetType, function(widget){ | |
widget.currentConfigGroup = [args.configGroup]; | |
widget.writeConfig(args.configKey, args.configValue); | |
var newValue = widget.readConfig(args.configKey); | |
}); | |
} | |
var args = { | |
widgetType: "{{widgetType}}", | |
configGroup: "{{configGroup}}", | |
configKey: "{{configKey}}", | |
configValue: "{{configValue}}", | |
} | |
widgetSetProperty(args); | |
""" | |
plasmaScript = plasmaScript.replace('\n', ' ') | |
plasmaScript = plasmaScript.replace("{{widgetType}}", widgetType) | |
plasmaScript = plasmaScript.replace("{{configGroup}}", configGroup) | |
plasmaScript = plasmaScript.replace("{{configKey}}", configKey) | |
plasmaScript = plasmaScript.replace("{{configValue}}", configValue) | |
# print(plasmaScript) | |
# https://dbus.freedesktop.org/doc/dbus-python/tutorial.html | |
# qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "" | |
session_bus = dbus.SessionBus() | |
plasmashell_obj = session_bus.get_object('org.kde.plasmashell', '/PlasmaShell') | |
plasmashell = dbus.Interface(plasmashell_obj, dbus_interface='org.kde.PlasmaShell') | |
plasmashell.evaluateScript(plasmaScript) | |
#--- Package config/main.xml Parser | |
packageDirList = [ | |
os.path.expanduser('~/.local/share/plasma/plasmoids'), | |
'/usr/share/plasma/plasmoids', | |
os.path.expanduser('~/.local/share/plasma/wallpapers'), | |
'/usr/share/plasma/wallpapers', | |
] | |
def findWidgetDir(namespace): | |
for packageDir in packageDirList: | |
filepath = os.path.join(packageDir, namespace) | |
if os.path.isdir(filepath): | |
return filepath | |
return None | |
def getEntryLabel(text): | |
pattern = r'<label>(.+?)<\/label>' | |
m = re.search(pattern, text) | |
if m: | |
return m.group(1) | |
else: | |
return None | |
def getEntryDefault(text): | |
pattern = r'<default>(.+?)<\/default>' | |
m = re.search(pattern, text) | |
if m: | |
return m.group(1) | |
else: | |
return None | |
def getChoiceList(text): | |
pattern = r'<choice ([^>]+)(\/>|>(.+?)<\/choice>)' | |
choiceList = [] | |
for m in re.finditer(pattern, text, flags=re.DOTALL): | |
# print(m) | |
attrXml = m.group(1) | |
choiceName = getXmlAttr(attrXml, 'name') | |
choiceList.append(choiceName) | |
return choiceList | |
def getEntryChoices(text): | |
pattern = r'<choices>(.+?)<\/choices>' | |
m = re.search(pattern, text, flags=re.DOTALL) | |
if m: | |
return getChoiceList(m.group(1)) | |
else: | |
return None | |
def getXmlAttr(text, key): | |
pattern = key + r'\s*=\s*\"([^\">]+)\"' # There's unlikely to be escaped quotes | |
m = re.search(pattern, text) | |
if m: | |
return m.group(1) | |
else: | |
return None | |
def iterGroupEntry(text): | |
pattern = r'<entry ([^>]+)>(.+?)<\/entry>' | |
for m in re.finditer(pattern, text, flags=re.DOTALL): | |
# print(m) | |
attrXml = m.group(1) | |
innerXml = m.group(2) | |
# print('entry', attrXml) | |
entry = { | |
'name': getXmlAttr(attrXml, 'name'), | |
'type': getXmlAttr(attrXml, 'type'), | |
'default': getEntryDefault(innerXml), | |
'label': getEntryLabel(innerXml) or '', | |
'choices': getEntryChoices(innerXml), | |
} | |
yield entry | |
def iterGroup(text): | |
pattern = r'<group ([^>]+)>(.+?)<\/group>' | |
for m in re.finditer(pattern, text, flags=re.DOTALL): | |
# print(m) | |
attrXml = m.group(1) | |
innerXml = m.group(2) | |
# print('group', attrXml) | |
group = { | |
'name': getXmlAttr(attrXml, 'name'), | |
'entries': [], | |
} | |
for entry in iterGroupEntry(innerXml): | |
group['entries'].append(entry) | |
yield group | |
#--- Terminal Colors | |
class TC: | |
RESET = '\033[0m' | |
FG_BLACK='\033[30m' | |
FG_RED='\033[31m' | |
FG_GREEN='\033[32m' | |
FG_ORANGE='\033[33m' | |
FG_BLUE='\033[34m' | |
FG_PURPLE='\033[35m' | |
FG_CYAN='\033[36m' | |
FG_LIGHTGREY='\033[37m' | |
FG_DARKGREY='\033[90m' | |
FG_LIGHTRED='\033[91m' | |
FG_LIGHTGREEN='\033[92m' | |
FG_YELLOW='\033[93m' | |
FG_LIGHTBLUE='\033[94m' | |
FG_PINK='\033[95m' | |
FG_LIGHTCYAN='\033[96m' | |
def prettyValue(value): | |
if value is None: | |
return '\"\"' | |
if ' ' in value: | |
return '\"' + value.replace('\"', '\\\"') + '\"' | |
else: | |
return value.replace('\"', '\\\"') | |
def formatEntryType(entry): | |
if entry['choices'] is not None: | |
return '{} {}'.format( | |
entry['type'], | |
', '.join('{}={}'.format(i, key) for i,key in enumerate(entry['choices'])), | |
) | |
else: | |
return entry['type'] | |
def printConfigKey(namespace, group, entry, showLabel=False): | |
line = '' | |
if showLabel: | |
line += TC.FG_DARKGREY + '# ' + entry['label'] + TC.RESET + '\n' | |
line += 'plasmasetconfig' | |
line += ' ' + TC.FG_PINK + namespace | |
line += ' ' + TC.FG_LIGHTBLUE + prettyValue(group['name']) | |
line += ' ' + TC.FG_LIGHTGREEN + prettyValue(entry['name']) | |
line += ' ' + TC.FG_YELLOW + prettyValue(entry['default']) | |
line += ' ' + TC.FG_DARKGREY + '# ' + formatEntryType(entry) | |
line += TC.RESET | |
print(line) | |
def printPackageConfigKeys(namespace, showLabels=False): | |
packageDir = findWidgetDir(namespace) | |
if packageDir is None: | |
print('Could not find a package with the namespace "{}"'.format(namespace)) | |
sys.exit(1) | |
configPath = os.path.join(packageDir, 'contents/config/main.xml') | |
if not os.path.isfile(configPath): | |
print('Package at "{}" does not contain "contents/config/main.xml"'.format(packageDir)) | |
sys.exit(1) | |
with open(configPath, 'r') as fin: | |
text = fin.read() | |
for group in iterGroup(text): | |
for entry in group['entries']: | |
printConfigKey(namespace, group, entry, showLabel=showLabels) | |
def printPackage(namespace, dirpath): | |
line = 'plasmasetconfig' | |
line += ' ' + TC.FG_PINK + namespace | |
line += ' ' + TC.FG_DARKGREY + '# ' + dirpath | |
line += TC.RESET | |
print(line) | |
def printNamespaceList(): | |
namespaceList = set() | |
for packageDir in packageDirList: | |
if os.path.isdir(packageDir): | |
for filename in sorted(os.listdir(packageDir)): | |
filepath = os.path.join(packageDir, filename) | |
if os.path.isdir(filepath): | |
if filename not in namespaceList: | |
namespaceList.add(filename) | |
printPackage(filename, packageDir) | |
#--- Main | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-v", "--verbose", action='store_true', help="print config key labels") | |
parser.add_argument("widget", type=str, help="widget namespace eg: 'org.kde.plasma.digitalclock'") | |
parser.add_argument("group", type=str, help="config group") | |
parser.add_argument("key", type=str, help="config key to modify") | |
parser.add_argument("value", type=str, help="new value to store in config key") | |
# Note: "plasmasetconfig.py" is first "arg" | |
flagArgs = list(filter(lambda s: s.startswith('-'), sys.argv)) | |
posArgs = list(filter(lambda s: not s.startswith('-'), sys.argv)) | |
numPosArgs = len(posArgs) | |
if numPosArgs == 1: | |
# plasmasetconfig | |
parser.print_usage() | |
print() | |
printNamespaceList() | |
sys.exit(1) | |
elif numPosArgs == 2 or numPosArgs == 3: | |
# plasmasetconfig [widget] | |
# plasmasetconfig [widget] [group] | |
widget = posArgs[1] | |
verbose = '-v' in flagArgs or '--verbose' in flagArgs | |
parser.print_usage() | |
print() | |
printPackageConfigKeys(widget, showLabels=verbose) | |
sys.exit(1) | |
args = parser.parse_args() | |
writeConfigKey(args) |
Hi,
I was happy to found a script who can help me to manage and prepare a plasma desktop.
I have problem to make it work on 5.92 plasma version on Ubuntu 22.04.
For example I want to setup org.kde.plasma.icontask with custom launchers, I done like :
plasmasetconfig org.kde.plasma.icontask General launchers "file:///usr/share/applications/firefox-esr.desktop,file:///usr/share/applications/libreoffice-startcenter.desktop,applications:xivo-desktop-assistant.desktop"
Same if I want to make change on kickoff favorites.
Did this script is up to date ?
Is not working
Hmmm, the following works
plasmasetconfig org.kde.plasma.taskmanager General launchers 'applications:org.kde.dolphin.desktop'
but this just creates a single broken launcher:
plasmasetconfig org.kde.plasma.taskmanager General launchers 'applications:org.kde.dolphin.desktop,applications:firefox.desktop'
So there's an issue with either parsing or casting a comma separated list to an array of strings.
So there's an issue with either parsing or casting a comma separated list to an array of strings.
If I am understanding correctly, and with what happens here, it works, but one needs to restart plasmashell (then all icons appear correctly)
Love it. works perfectly. Thanks