Alexays / Waybar

Highly customizable Wayland bar for Sway and Wlroots based compositors. :v: :tada:
MIT License
6.67k stars 708 forks source link

Network module: compact bandwidth formatting #2162

Open YodaEmbedding opened 1 year ago

YodaEmbedding commented 1 year ago

For the network module, it would be nice to format the bandwidth speeds in a more compact, and also non width-varying manner.

Current      Desired
420.1MB/s    420 M
420.1kB/s    420 K
420.1B/s     420 B
42.0B/s       42 B
4.2B/s         4 B

This shaves off a few unimportant characters for valuable waybar real estate, and prevents the module from varying in width (when wrapped in a monospace font, e.g. <span font='NotoSansMono-Regular'>{bandwidthDownBytes}</span>).

YodaEmbedding commented 1 year ago

I ended up writing my own custom module:

#!/usr/bin/env python

import subprocess
from time import sleep

def default_interface():
    process = subprocess.run(
        ["ip", "route"], check=True, text=True, capture_output=True
    )
    for line in process.stdout.splitlines():
        if line.startswith("default via"):
            return line.split()[4]
    raise RuntimeError("No default interface found")

def get_rx_tx_bytes(iface):
    with open("/proc/net/dev") as f:
        for line in f:
            if not line.startswith(f"{iface}:"):
                continue
            rx_bytes = int(line.split()[1])
            tx_bytes = int(line.split()[9])
            return rx_bytes, tx_bytes
    raise RuntimeError("Interface not found")

def format_size(size):
    power_labels = {0: "B", 1: "K", 2: "M", 3: "G", 4: "T"}
    kilo = 2**10
    power = 0
    while size > kilo:
        size /= kilo
        power += 1
    return f"{size:3.0f} {power_labels[power]}"

def main():
    refresh_interval = 2
    rx_icon = " "
    tx_icon = " "
    num_left = "<span font='NotoSansMono'>"
    num_right = "</span>"
    fmt_str = (
        f"{rx_icon}{num_left}{{rx}}{{unit_suffix}}{num_right}  "
        f"{tx_icon}{num_left}{{tx}}{{unit_suffix}}{num_right}"
    )
    unit_suffix = ""
    iface = default_interface()

    rx_bytes, tx_bytes = get_rx_tx_bytes(iface)

    while True:
        prev_rx_bytes, prev_tx_bytes = rx_bytes, tx_bytes
        rx_bytes, tx_bytes = get_rx_tx_bytes(iface)
        drx = format_size((rx_bytes - prev_rx_bytes) / refresh_interval)
        dtx = format_size((tx_bytes - prev_tx_bytes) / refresh_interval)
        line = fmt_str.format(rx=drx, tx=dtx, unit_suffix=unit_suffix)
        print(line, flush=True)
        sleep(refresh_interval)

if __name__ == "__main__":
    main()

config:

    "custom/bandwidth": {
        "exec": "$HOME/.config/waybar/scripts/bandwidth"
    },
yangrq1018 commented 7 months ago

Agree. The network module should provide a constant width solution, to prevent a group of labels from getting shifted. Adjusting the CSS can do, but far from elegant.

John-Dennehy commented 7 months ago

Agreed. Module is currently unusable on vertical bars due to this. I know I can rotate, but that's just as bad making everything else jump around.

Would also love to see a way to fix it to MB/s etc

csskevin commented 3 months ago

Constant width is already possible by using specifications like <, >, =. e.g. for bandwidthDownBytes:

jcbevns commented 13 hours ago

I ended up writing my own custom module:

#!/usr/bin/env python

import subprocess
from time import sleep

def default_interface():
    process = subprocess.run(
        ["ip", "route"], check=True, text=True, capture_output=True
    )
    for line in process.stdout.splitlines():
        if line.startswith("default via"):
            return line.split()[4]
    raise RuntimeError("No default interface found")

def get_rx_tx_bytes(iface):
    with open("/proc/net/dev") as f:
        for line in f:
            if not line.startswith(f"{iface}:"):
                continue
            rx_bytes = int(line.split()[1])
            tx_bytes = int(line.split()[9])
            return rx_bytes, tx_bytes
    raise RuntimeError("Interface not found")

def format_size(size):
    power_labels = {0: "B", 1: "K", 2: "M", 3: "G", 4: "T"}
    kilo = 2**10
    power = 0
    while size > kilo:
        size /= kilo
        power += 1
    return f"{size:3.0f} {power_labels[power]}"

def main():
    refresh_interval = 2
    rx_icon = " "
    tx_icon = " "
    num_left = "<span font='NotoSansMono'>"
    num_right = "</span>"
    fmt_str = (
        f"{rx_icon}{num_left}{{rx}}{{unit_suffix}}{num_right}  "
        f"{tx_icon}{num_left}{{tx}}{{unit_suffix}}{num_right}"
    )
    unit_suffix = ""
    iface = default_interface()

    rx_bytes, tx_bytes = get_rx_tx_bytes(iface)

    while True:
        prev_rx_bytes, prev_tx_bytes = rx_bytes, tx_bytes
        rx_bytes, tx_bytes = get_rx_tx_bytes(iface)
        drx = format_size((rx_bytes - prev_rx_bytes) / refresh_interval)
        dtx = format_size((tx_bytes - prev_tx_bytes) / refresh_interval)
        line = fmt_str.format(rx=drx, tx=dtx, unit_suffix=unit_suffix)
        print(line, flush=True)
        sleep(refresh_interval)

if __name__ == "__main__":
    main()

config:

    "custom/bandwidth": {
        "exec": "$HOME/.config/waybar/scripts/bandwidth"
    },

Does this solve the variable width issue? @YodaEmbedding ?