CommandOutput, DailyForecast, CondensedWeather Ported To Plasma 6
- Command Output: Plasma 6 Widget / Plasma 5 Widget
- Daily Forecast: Plasma 6 Widget / Plasma 5 Widget
- Condensed Weather: Plasma 6 Widget / Plasma 5 Widget
Also, @dhruv8sh has forked the Plasma 5 AlphaBlack Control widget and ported it to Plasma 6. It can be found at https://store.kde.org/p/2136860.
Porting to Plasma 6
After finally setting up a Virt-Manager VM with Plasma 6, I’ve been steadily porting my widget build scripts to Plasma 6.
Most of my old build scripts used kreadconfig5
to read data from the .ini
like .desktop
files to get the widget’s namespace (eg: com.github.zren.commandoutput
) and ServiceType (eg: Plasma/Applet
).
packageNamespace=`kreadconfig5 --file="$PWD/package/metadata.desktop" --group="Desktop Entry" --key="X-KDE-PluginInfo-Name"`
packageServiceType=`kreadconfig5 --file="$PWD/package/metadata.desktop" --group="Desktop Entry" --key="X-KDE-ServiceTypes"`
The ServiceType can be assumed if I only use this build script for widgets, but I still need to read the former in my ./install
script since I need to check if the namespace is already installed to switch between kpackagetool6 --install
and kpackagetool6 --upgrade
since --install
will fail if the widget already exists in ~/.local/share/plasma/plasmoids
.
isAlreadyInstalled=false
kpackagetool5 --type="Plasma/Applet" --show="com.github.zren.commandoutput" &> /dev/null
if [ $? == 0 ]; then
isAlreadyInstalled=true
fi
if $isAlreadyInstalled; then
kpackagetool5 --type "Plasma/Applet" --upgrade ./package/
restartPlasmashell=true
else
kpackagetool5 --type "Plasma/Applet" --install ./package/
fi
Since Plasma 6 now forces the use of metadata.json
, I now need a command that’ll read a JSON object key, using only the software installed by default on most distros. I need to check if jq
is installed by default on every distro since that would simplify things. That said, every distro will have python3
installed so we can just use it’s json
module.
packageNamespace=`python3 -c 'import sys, json; print(json.load(sys.stdin).get("KPlugin", {}).get("Id", ""))' < "$PWD/package/metadata.json"`
Where things become annoying is with parsing the ServiceType. By default, desktoptojson
will convert X-KDE-ServiceTypes=Plasma/Applet
in metadata.desktop
to this in metadata.json
.
{ "KPlugin": { "ServiceTypes": [ "Plasma/Applet" ] } }
However in Plasma 6, you manually need to convert it to:
{ "KPackageStructure": "Plasma/Applet" }
So we need to parse both cases in the build scripts.
packageServiceType=`python3 -c 'import sys, json; print(json.load(sys.stdin).get("KPackageStructure",""))' < "$PWD/package/metadata.json"`
if [ -z "$packageServiceType" ]; then # desktoptojson will set KPlugin.ServiceTypes[0] instead of KPackageStructure
packageServiceType=`python3 -c 'import sys, json; print((json.load(sys.stdin).get("KPlugin", {}).get("ServiceTypes", [])+[""])[0])' < "$PWD/package/metadata.json"`
echo "[warning] metadata.json needs KPackageStructure set in Plasma6"
fi
[Plasma 5] Parsing I18n in metadata.desktop
Next up was the problem I’d been dreading since metadata.desktop
was deprecated in Plasma 5. Parsing/extracting i18n translations from the new metadata.json
then modifying the JSON with the new translations.
With the old metadata.desktop
, the xgettext
and msgfmt
tools had a built in ability to edit .desktop
files.
# Note: xgettext v0.20.1 (Kubuntu 20.04) and below will attempt to translate Icon,
# so we need to specify Name, GenericName, Comment, and Keywords.
# https://github.com/Zren/plasma-applet-lib/issues/1
# https://savannah.gnu.org/support/?108887
find "${packageRoot}" -name '*.desktop' | sort > "${DIR}/infiles.list"
xgettext --files-from="${DIR}/infiles.list" --language=Desktop \
-k -kName -kGenericName -kComment -kKeywords \
-D "${packageRoot}" -D "${DIR}"
-o "template.pot.new" \
|| \
{ echoRed "[translate/merge] error while calling xgettext. aborting."; exit 1; }
touch "$DIR/LINGUAS" # List all available translation languages
for cat in `find . -name '*.po' | sort`; do
catLocale=`basename ${cat%.*}` # "fr.po" => "fr"
echo "${catLocale}" >> "$DIR/LINGUAS"
done
cp -f "$DIR/../metadata.desktop" "$DIR/template.desktop"
sed -i '/^Name\[/ d; /^GenericName\[/ d; /^Comment\[/ d; /^Keywords\[/ d' "$DIR/template.desktop"
msgfmt --desktop --template="$DIR/template.desktop" \
-d "$DIR/" \
-o "$DIR/new.desktop"
[Plasma 6] Parsing I18n in metadata.json
KDE might be using itstool
I think (they use it in org.kde.plasmashell.metainfo.po
) but plasma-desktop._json_.po
which contains the metadata.json
translations doesn’t reference it at all.
- gnu.org/software/gettext/manual/
- https://l10n.kde.org/stats/gui/trunk-kf6/package/plasma-desktop/fr/
#. (itstool) path: component/name
#: org.kde.plasmashell.metainfo.xml:9
msgid "KDE Plasma Desktop"
msgstr "Bureau Plasma de KDE"
#: applets/activitypager/metadata.json
msgctxt "Name"
msgid "Activity Pager"
msgstr "Gestionnaire d'activités"
When I realized there wasn’t a simple tool to extract update metadata.json
with translations, I realized I’d probably have to write something myself.
Extracting the Name
and Description
from metadata.json
was fairly simple. Just read the keys from JSON and create a simple template.pot
. We can just ignore filling out the .pot
header as it gets overwritten in the xgettext --join-existing
later on.
with open(self.jsonMetaFilepath, 'r') as fin:
metadata = json.load(fin)
# This template header is overwritten later so the only changes needed is
# 'charset=CHARSET' => 'charset=UTF-8' so that xgettext doesn't complain.
# POT_DEFAULT_HEADER already has the charset=UTF-8 replaced.
newTemplateText = POT_DEFAULT_HEADER
kp = metadata.get('KPlugin', {})
trKeywords = [
'Name',
'Description',
]
relativeMetadataPath = os.path.relpath(self.jsonMetaFilepath, self.translateDir)
for keyword in trKeywords:
keywordMessage = kp.get(keyword, '')
keywordMessage = keywordMessage.replace('\"', r'\"')
if keyword != "":
# keywordText = f"#: {relativeMetadataPath}\nmsgctxt \"{keyword}\"\nmsgid \"{keywordMessage}\"\nmsgstr \"\"\n\n"
keywordText = f"#: {relativeMetadataPath}\nmsgid \"{keywordMessage}\"\nmsgstr \"\"\n\n"
newTemplateText += keywordText
with open(newTemplatePath, 'w') as fout:
fout.write(newTemplateText)
Now the tricky part, parsing the translated language.po
files, then editing the metadata.json
.
Checkout the regex I used here: https://regex101.com/r/kEJCVL/5
github.com/Zren/plasma-applet-lib/…/kpac#L97
PoMessage = namedtuple('PoMessage', ['ctxt', 'id', 'str'])
class PoFile:
def __init__(self, filepath):
self.filepath = filepath
self.text = None
self.messages = []
with open(self.filepath, 'r') as fin:
self.text = fin.read()
self.parse()
@property
def msgPattern(self):
# Edit/Test: https://regex101.com/r/kEJCVL
patt = r'(msgctxt[ \t]+(".*")[ \t]*\n)?'
patt += r'((".*"[ \t]*\n)*)'
patt += r'(msgid[ \t]+(".*")[ \t]*\n)'
patt += r'((".*"[ \t]*\n)*)'
patt += r'(msgstr[ \t]+(".*")[ \t]*\n)'
patt += r'((".*"[ \t]*\n)*)'
return patt
def _joinMsgStr(self, line1, line234):
if line1 is None:
return None
elif line234 is None:
return line1.strip().strip('\"')
else:
lines = [line1] + line234.split('\n')
msgstr = ""
for line in lines:
msgstr += line.strip().strip('\"')
return msgstr
def parse(self):
for m in re.finditer(self.msgPattern, self.text):
msgCtx = self._joinMsgStr(m.group(2), m.group(3))
msgId = self._joinMsgStr(m.group(6), m.group(7))
msgStr = self._joinMsgStr(m.group(10), m.group(11))
msg = PoMessage(msgCtx, msgId, msgStr)
self.messages.append(msg)
def getMsgStr(self, msgid, msgctxt=None):
for msg in self.messages:
if msg.ctxt == msgctxt and msg.id == msgid:
return msg.str
return None
github.com/Zren/plasma-applet-lib/…/kpac#L881
with open(self.jsonMetaFilepath, 'r') as fin:
metadata = json.load(fin)
trKeywordsMap = {
'Name': '',
'Description': '',
}
kp = metadata.get('KPlugin', {})
for keyword in trKeywordsMap.keys():
trKeywordsMap[keyword] = kp.get(keyword, '')
for catFilepath in glob.glob(os.path.join(self.translateDir, '*.po')):
catFilename = os.path.basename(catFilepath)
catLocale = os.path.splitext(catFilename)[0]
catFile = PoFile(catFilepath)
for keyword, msgid in trKeywordsMap.items():
catMsgStr = catFile.getMsgStr(msgid)
catKeyword = f"{keyword}[{catLocale}]"
if kp.get(catKeyword) != catMsgStr and catMsgStr != "":
kp[catKeyword] = catMsgStr
with open(filepath, 'w') as fout:
json.dump(data, fout, ensure_ascii=False, indent='\t', sort_keys=True)
fout.write('\n') # Trailing newline at EOF
An important part was to use ensure_ascii=False
in json.dump()
to keep the unicode text.
Drawing the rest of the Owl in Plasma 6
Okay now I needed to do everything else listed on:
https://develop.kde.org/docs/plasma/widget/porting_kf6/
Well since I have like a dozen widgets, I needed to automate some of this.
Manipulating metadata.json
with python3 is easy enough as we demonstrated above.
github.com/Zren/plasma-applet-lib/…/kpac#L1055
For the rest, the easiest way would be with some simple sed
text replacements. To make life easier I just kept using python though.
github.com/Zren/plasma-applet-lib/…/kpac#L1216
Then I grepped for the new KSvg.
namespaces, and added an import org.kde.ksvg as KSvg
at the top of the file if it was missing. I may or may not have forgotten to write the rest of the file contents and deleted a few files when testing. Glory to git checkout path/to/file.txt
for saving my bacon here.
Lastly I detected if the file was main.qml
, and replaced any line starting with ^Item\s*\{
with PlasmoidItem {
since I usually keep my code properly indented.
As always, creating a list of all the various Plasmoid.___
properties to replace was a pain.
[Plasma5] Global plasmoid
property (which is also attached to the widget root item as Plasmoid
)
- https://invent.kde.org/plasma/plasma-framework/-/tree/kf5/src/scriptengines/qml/plasmoid/appletinterface.h
- https://invent.kde.org/plasma/plasma-framework/-/tree/kf5/src/plasmaquick/appletquickitem.h
- https://invent.kde.org/plasma/plasma-framework/-/tree/kf5/src/plasma/applet.h
[Plasma6] PlasmoidItem
root item
- https://invent.kde.org/plasma/plasma-framework/-/tree/master/src/plasmaquick/plasmoid/plasmoiditem.h
- https://invent.kde.org/plasma/plasma-framework/-/tree/master/src/plasmaquick/appletquickitem.h
[Plasma6] Attached Plasmoid
similar to Layout
which dynamically grabs the value from the Applet
class
- https://invent.kde.org/plasma/plasma-framework/-/blame/master/src/plasmaquick/private/plasmoidattached_p.cpp#L33
- https://invent.kde.org/plasma/plasma-framework/-/tree/master/src/plasma/applet.h
Since Plasma6 made things a little confusing as to what is in PlasmoidItem {}
and what is accessed with Plasmoid.___
I settled on the following for now:
github.com/Zren/plasma-applet-lib/…/kpac#L1185
plasmoidPropsPorted = [
# AppletQuickItem
'switchWidth', 'switchHeight',
'compactRepresentation', 'fullRepresentation',
'preloadFullRepresentation', 'preferredRepresentation',
'expanded', 'activationTogglesExpanded',
'hideOnWindowDeactivate',
# PlasmoidItem
'toolTipMainText', 'toolTipSubText', 'toolTipTextFormat', 'toolTipItem',
'hideOnWindowDeactivate',
]
for prop in plasmoidPropsPorted:
line = line.replace(f"Plasmoid.{prop}:", f"{prop}:")
The last things I had to do manually was convert the plasmoid.setAction('refresh, ...)
calls to the new Plasmoid.contextualActions: [ PlasmaCore.Action {...} ]
which would be a tad too complicated to automate.
I was able to quickly sed
replace my plasmoid.action('configure').trigger()
trick to open the config dialog automatically when testing though which I have in most of my widgets.
PlasmoidItem {
Component.onCompleted: Plasmoid.internalAction("configure").trigger()
}
In Closing
Overall, with my new script it should be as easy to do most of the dull text replacement work.
The hard part will be converting any deprecated QQC1.Button
s and other GUI elements that might have custom styling to their QQC2.Button
counterparts. TiledMenu and EventCalendar will probably be more annoying to port than just running the following:
github.com/Zren/plasma-applet-lib/…/kpac
python3 ./kpac updatelib ../plasma-applet-lib
python3 ./kpac plasma6
# Scan changes
python3 ./kpac i18n # Update metadata.json and convert .po => .mo
git commit . -m 'Update'
python3 ./kpac build # Create a zipped .plasmoid
I got too tired last night, so firsts things up is porting SimpleWeather today since it’s basically the same code as the other weather widgets.
This “widgets have been updated” blog post rambled on more than I expected, sorry bout that.
OpenSUSE Krypton Plasma 6 Setup
The last time I tried installing Plasma 6 in a VM with OpenSUSE Krypton, I failed to actually install Plasma 6 for development use. Recently I noticed a tip on the KDE Wiki and managed to get it working.
Ignoring KDE Work Branches
I regularly use git lga
(custom alias in ~/.gitconfig
) to view all branches.
QML Profiling Plasmashell
Since my last article on the subject, I’ve found out that plasmashell
has an environment variable that enables QML Profiling which makes debugging Plasma far simpler.
KDE Neon Update Bug (Feb 16) (Fixed)
TL;DR: I went to TTY2 and ran the update again in the morning (12 hours later), then went back to TTY1 and could login. Seems libkf5coreaddons5
was missing in the update last night.
Custom Weather Widgets in Plasma 5.24
For those unaware, KDE ships it’s own Weather Widget. The weather service parsers are a Plasma DataEngine shipped in plasma-workspace
while the actual visual widget is in the kdeplasma-addons
git repo. Kubuntu breaks up that repo up into several packages however, so you’ll need to install plasma-widgets-addons
.
Firefox/Chrome Profile App Launchers
If you share a computer and want separate bookmarks/extensions without multiple OS users, using multiple web browser profiles is the solution. In Windows, creating a new browser profile will create an app launcher for you, but in Linux you have to do it manually.
QML Profiling Plasmashell (Out of Date)
Update: There is now an easier way to debug plasmashell. You no longer need to compile plasma-workspace
in Qt Creator to debug plasmashell.
Developing KRunner
KRunner’s source code is split amongst many repos, so I figured I’d jot down their locations here.
Testing Wayland in a Neon LiveCD
I’d like to easily test window decoration in Wayland, which requires restarting kwin_wayland
. Unfortunately we can’t do that as kwin_wayland
is the login session.
QML Connections "onFoo warnings" will get logging category in Qt 5.15.1
After updating Manjaro recently, I noticed Qt 5.15 has a new deprecation warning:
NightColor.py for Setting Temp From Terminal
I recently looked into the nightcolor widget hoping to add a mousewheel control to temporarily force a specific color like the Redshift Control widget does. Unfortunately I realized the existing API in the NightColor QML plugin doesn’t have the function to do so. So I dived into the NightColor DBus API using qdbusviewer
, as I was hoping to run a qdbus
command. The Night Color effect is done by KWin, and is codenamed “ColorCorrect” in it’s code.
How To Change Plasma Icon Theme in the Terminal
Quickly Modify Octopi To Use Breeze Icons
Show KOrganizer Events in the Panel's Calendar
This is a video demonstration on how to install and setup kdepim-addons which binds the events shown in the KOrganizer app with the calendar in the KDE Plasma panel.
In order to install the entire KDE PIM suite, run:
If you only want to connect KOrganizer with the panel, you probably only need:
Side Calendar Work In Progress
While skimming around the net for inspiration for my Event Calendar widget, I discovered a Gnome Calendar mockup from 2016.
Quick Tile An App When It Opens Using A KWin Script
A user on reddit recently asked how to quick tile a window when it opens in a multi monitor setup. While they could easily position the app using a KWin Rule, that solution would only work for a single monitor. As if you launched the app on the 2nd monitor, the app would always show up on the 1st monitor.
Activate any widget with Meta key
A user on reddit recently asked how to trigger any widget using the Meta key.
Building Plasma's System Monitor (Ctrl+Esc) By Itself
The “System Monitor” that ships with the plasma-workspace
package is fairly useful, and is similar to KSysGuard. The System Monitor has a default shortcut (Ctrl+Esc
) so you can quickly access it on any KDE Plasma installation. I usally have System Monitor sorted by “Relative Sort Time” so that I can quickly see the newest processes and if they’re the cause of what’s slowing down my PC.
Building EnvCan Weather Ion By Itself
We first open up the plasma-workspace
repo, and navigate to dataengines/weather/ions/envcan
. This will be the folder we’ll try to compile by itself.
Building Task Manager Widget By Itself
I recently was debugging KDE Bug #401579 and wanted to build just the Task Manager QML widget and C++ plugin by itself.
Hiding Plasma Widget Global Shortcut Tab
First we’ll look a the file that wraps the Plasma5 widget’s config page. The file is called AppletConfiguration.qml in the plasma-desktop
repo.
Picture In Picture With Firefox in KDE
I used to be able to get Picture In Picture with Chrome by maximizing the video, Alt+F3+M+F
to exit fullscreen but stay in Chrome’s “fullscreen mode” with the addressbar and window frame hidden. I would then hit Alt+F3+M+A
to make the window stay on top. Finally I would use Alt+RightClick
to resize the window to be smaller, and Alt+LeftClick
to move the window into the bottom right of the screen. This worked for the most part, but was cumbersome to setup.
Exploring Plasma's System Tray Widget
A user on reddit recently asked how to resize a few widgets. With the “Notes” widget, it is pretty straight forward by changing the Layout.preferredWidth
/ Layout.preferredHeight
as outlined in KDE’s Getting Started with Plasma Widgets article on the KDE wiki. However the user wanted to resize the System Tray and had no idea how to do so.
Creating a New Plasma Look and Feel
A user on reddit recently asked how to modify the Breeze Alt+Tab
theme. Unfortunately it seems to be tied to the Look and Feel as of Plasma 5.14.
Unity7 Styled Icon For The Plasma App Launcher
Under the unity
project on Launchpad, there a resources folder which contains the launcher_bfb.png
(download). This image contains the Unity 7 swirl.
Simpler kdesrc-build Script
Using kdesrc-build
to compile kde repositories is quite verbose, and by default will build all dependencies which can take ages. So I wrote a script to quickly build a single repo and install it.
kdesrc-build in KDE Neon
I setup a KDE Neon Git-Unstable VM to try building KDE from source so I could test C++ modifications to KDE Frameworks.
KWin TabBox Keyboard Events
In case you didn’t know, TabBox is the codename for the Alt+Tab Task Switcher. There are 2 ways to create a Task Switcher skin. A simple QML skin, or a C++ “Desktop Effect”. Somehwere along the way, the QML skins were no longer able to use keyboard shortcuts.
Compiling Dolphin
First install the build dependencies with apt-get build-dep
.
Unity Ambiance Integration for the Present Windows Button
- United (Look and Feel): https://store.kde.org/p/1167950/
- Unity Ambiance (Desktop Theme): https://store.kde.org/p/998797/
- Present Windows Button Widget): https://store.kde.org/p/1181039/
AlphaBlack Control Widget
For a long while now, I’ve had a widget that simplifies changing the accent color of my Breeze AlphaBlack desktop theme. It had a few major usuability flaws so I never got around to cleaning it up for general release.
Installing KPatience Solitaire Suite
Developing KDE in a VirtualBox
First download the git-unstable KDE Neon iso while we setup the rest.
Workout Timer
Console for Plasma DevTools
2022-06-09 Update: I’ve published the code at https://github.com/Zren/QmlDevTools.
Making a DevTools Inspector for Plasma
2022-06-09 Update: I’ve published the code at https://github.com/Zren/QmlDevTools.
Patching Breeze Window Decorations
KDE ships with the Breeze window decorations. Which are drawn via a C++ KDecoration2 “plugin”. You can also download other decorations from the KDE Store (via Get Hot New Stuff) that are SVG based themes for the Aurorae KDecoration2 “plugin” (which is also preinstalled in KDE).
XBox One Controller in KDE Neon
As of Dec 9th 2016, KDE Neon (which is based on Ubuntu 16.04 LTS) is only using the Linux 4.4.0-53 kernel. Which means the XBox One controller isn’t fully supported without some running some commands manually.