How to add widgets dynamically?
Closed this issue · 13 comments
Hello,
trying sfwbar with labwc
I have been 'playing' around with sfwbar, to try and put together a satisfying config. and its mostly going good.
I find it powerful but also compicated with the configuration.
The idea i have is because there is still no (stable wayland protocoles) support for workspaces in a lot of bars and taskbars with labwc. So the only way is to manually add them to the config like:
`layout {
grid {
style = "desktops"
css = "* { -GtkWidget-direction: right; }"
label {
style = "desk"
value = "1"
action = "wtype -M win -P F1"
}
label {
style = "desk"
value = "2"
action = "wtype -M win -P F2"
}
label {
style = "desk"
value = "3"
action = "wtype -M win -P F3"
}
label {
style = "desk"
value = "4"
action = "wtype -M win -P F4"
}
}
}`
Then I got the idea to write a python script to find the workspaces in .config/labwc/rc.xml and the switching keybinds and add them to the bar like so with wtype:
`import xml.etree.ElementTree as et
from os.path import join, expanduser
rcfile = join( expanduser("~"), ".config/labwc/rc.xml" )
rctree = et.parse(rcfile)
rcroot = rctree.getroot()
meta_keys = {
"A": "alt",
"C": "ctrl",
"M": "altgr",
"S": "shift",
"W": "win"
}
def get_wtype(keyb):
l = keyb.split("-")
cmd = "wtype "
for i in l:
if i in meta_keys:
cmd += "-M " + meta_keys[i] + " "
else:
cmd += "-P " + i + " "
return cmd
keybinds = rcroot.findall("{}keyboard/{}keybind")
if keybinds:
for key in keybinds:
actions = key.findall("{}action")
if actions:
for action in actions:
if action.get("name") == "GoToDesktop":
tos = action.findall("{}to")
if tos:
for ato in tos:
if ato.text.isdigit():
k = key.get("key")
print("label {\n style = "desk"\n value = "" + ato.text + "" \n action = "" + get_wtype(k) + ""\n}")`
this script then returns:
label { style = "desk" value = "1" action = "wtype -M win -P F1 " } label { style = "desk" value = "2" action = "wtype -M win -P F2 " } label { style = "desk" value = "3" action = "wtype -M win -P F3 " } label { style = "desk" value = "4" action = "wtype -M win -P F4 " }
Is there a way to add that in the config file?
Thanks,
This is some amazingly hacky solution, I love it!
Technically you could also add something like <action name="Execute" command="sh -c 'echo 2 > /tmp/current_workspace'" />
to each of the workspace switching keybinds and then have sfwbar read that file and have a (mostly synced) current-workspace indicator.
Edit:
AFAIR you need to encode the >
in the command, e.g. >
.
Thanks for your your suggestion, I have seen this 'kind' of method in other cases but never thought of using it in this.
Thanks a bunch.
And that hacky :) method above, I got it when I was experimenting with eww bar for workspaces, taskbar and active window (eww doesn't have a taskbar widget yet) using tools like wlrctl and lswt, but didn't like it much.
Can PipeRead()
also be used in SfwBarInit()
? That would allow to have the initial state without sending a signal.
Hello,
Yeah I saw those actions, and a python script in config/wifi.widget, but I couldn't wrap my head around it (that's where I got these idea).
Sorry if I sound rude, but i couldn't find more examples in the documentation.
Thanks a lot
@Consolatis , Sure, you can use PipeRead in SfwbarInit
@salahoued , not at all. Sfwbar configuration is complex, unfortunately I haven't figured out a way to make it simpler without sacrificing configurability.
@LBCrion , Sorry for the trouble, but how can I add the output of the script the layout using Config.
labwc_workspaces.txt
@LBCrion , @Consolatis
Sorry I was about to report my results, when I saw your last comment.
I have successfully added the pager, without using Config (I still don't know how to properly use it) and here is my solution:
file : ~/.config/sfwbar/widgets/labwc_workspaces.widget
to include in sfwbar.config I put this config (inspiration from config/wifi.widget
) :
Scanner {
file("/tmp/current_ws.txt", CheckTime) {
CurrentWS = Grab()
}
}
layout {
action[0] = PipeRead "~/.local/bin/labwc-workspaces.py --ws-sfwbar"
grid "labwc_ws" {
style = "desktops"
css = "* { -GtkWidget-direction: right; }"
}
}
#CSS
label#desk, label#desk_active {
padding: 0 5px;
}
#desktops {
padding: 0 5px;
margin: 0 3px;
color: alpha(black, 0.80);
background-color: alpha(white, 0.70);
}
label#desk_active {
font-weight: bold;
color: alpha(black, 1);
}
And here is the code for the python script ~/.local/bin/labwc-workspaces.py
that generate workspace layout from ~/.config/labwc/rc.xml
:
#!/usr/bin/env python3
import xml.etree.ElementTree as et
from os.path import join, expanduser
import sys
rcfile = join( expanduser("~"), ".config/labwc/rc.xml" )
rctree = et.parse(rcfile)
rcroot = rctree.getroot()
desks = rcroot.find("{*}desktops")
num = desks.get("number")
names = desks.findall("{*}names/{*}name")
lnames = [ i.text for i in names ]
if not num:
num = len(names)
meta_keys = {
"A": "alt", # Mod1
"C": "ctrl",
"H": "", # Mod3
"M": "altgr", # Mod5
"S": "shift",
"W": "win" # Mod4, super_L
}
def get_workspace_number():
return num
def get_workspace_names():
return lnames
def get_wtype(keyb):
l = keyb.split("-")
cmd = "wtype "
for i in l:
if i in meta_keys:
cmd += "-M " + meta_keys[i] + " "
else:
cmd += "-P " + i + " "
return cmd
def get_keybinds(action_name):
keybinds = rcroot.findall("{*}keyboard/{*}keybind")
action_binds = {}
if keybinds:
for key in keybinds:
k = key.get("key")
wtype = get_wtype(k)
actions = key.findall("{*}action" )
if actions:
for action in actions:
if action.get("name") == action_name:
if action_name == "Execute":
cmd = action.get("command")
cmds = action.findall("{*}command")
if cmd:
action_binds[cmd] = wtype
else:
if cmds:
for cm in cmds:
action_binds[cm.text] = wtype
else:
tos = action.findall("{*}to")
if tos:
for ato in tos:
if ato.text not in ["current", "left", "right", "last"]:
action_binds[ato.text] = wtype
return action_binds
def usage():
print( "Usage: " + sys.argv[0] + " --ws-num | --ws-names | --ws-names-lines | --ws-json | --ws-keys ActionName | --ws-sfwbar" )
if __name__ == "__main__":
if len( sys.argv ) <= 1:
usage()
else:
if sys.argv[1] == "--ws-num":
print(num)
elif sys.argv[1] == "--ws-names":
print(lnames)
elif sys.argv[1] == "--ws-names-lines":
for i in lnames:
print(i)
elif sys.argv[1] == "--ws-json":
json_str = '{ "number": ' + str(num) + ', "names": ['
json_names = ""
for i in lnames:
json_names += f'"{i}", '
json_str += json_names.rstrip(", ") + "] }"
print(json_str)
elif sys.argv[1] == "--ws-keys":
if sys.argv[2]:
actions = get_keybinds(sys.argv[2])
#print(json.dumps(actions))
else:
usage()
elif sys.argv[1] == "--ws-sfwbar":
with open("/tmp/number_ws.txt", "w") as fout:
fout.write( str(num) + "\n" )
actions = get_keybinds("GoToDesktop")
print('grid "labwc_ws" { \n style = "desktops" \n css = "* { -GtkWidget-direction: right; }"\n')
#print('grid "labwc_ws" {\n')
for i in actions:
print('label "lwc_' + i + '" {\n style = If(CurrentWS = ' + i + ', "desk_active", "desk")\n value = "' + i + '" \n action = "' + actions[i] + '"\n}')
print('}')
else:
usage()
Script to get the current workspace ~/.local/bin/current_ws.sh
for current workspace indicator
It should be Known that this script should be executed every time the user switchs to a diffrent workspace using the keybinds in $HOME_CONFIG/labwc/rc.xml
, or sends a window to diffrent workspace with wraping set to yes (following the window to the new workspace)
#!/usr/bin/env bash
CURRENT_WS_FILE="/tmp/current_ws.txt"
NUMBER_WS_FILE="/tmp/number_ws.txt"
num_ws=1
curr_ws=1
new_ws=1
if [ ! -f "$NUMBER_WS_FILE" ]; then
labwc-workspaces.py --ws-num > $NUMBER_WS_FILE
else
num_ws=$(cat $NUMBER_WS_FILE)
fi
if [ ! -f "$CURRENT_WS_FILE" ]; then
echo "$curr_ws" > "$CURRENT_WS_FILE"
else
curr_ws=$(cat $CURRENT_WS_FILE)
fi
case $1 in
left)
if [ $curr_ws -gt 1 ]; then
new_ws=$(($curr_ws - 1))
echo "$new_ws" > $CURRENT_WS_FILE
fi
;;
right)
if [ $curr_ws -lt $num_ws ]; then
new_ws=$(($curr_ws + 1))
echo "$new_ws" > "$CURRENT_WS_FILE"
fi
;;
current)
echo "current"
;;
*)
echo "$1" > $CURRENT_WS_FILE
;;
esac
Because the taskbar shows all windows from every workspace, the indicator won't be updated if a window from a diffrent workspace got activated (needs a workaround)
You can use ``` for code blocks, otherwise its quite hard to read. Something like
***python
words=("some", "python", "code", "with", "multiple")
print(' '.join(words) + " lines")
***
The python
keyword is optional and just improves the highlighting. Replace ***
in the example with ```
@Consolatis , thanks I was working on better writing it again, so this is a quick solution for the time being
Sorry for the trouble
Nice. Hacks like this remind me why maintaining the flexibility in sfwbar is worth it.
One change I would recommend is adding a CheckTime to the scanner, to avoid constantly opening the file, I.e.:
Scanner {
file("/tmp/current_ws.txt", CheckTime) {
CurrentWS = Grab()
}
}
Credit to @Consolatis , for that idea.
Thanks for the 'tip'.