Merge pull request #366 from ncoop/iss364
Notification with update summary on_rightclick
This commit is contained in:
commit
806ebe9060
@ -2,6 +2,7 @@ import threading
|
||||
|
||||
from i3pystatus import SettingsBase, Module, formatp
|
||||
from i3pystatus.core.util import internet, require
|
||||
from i3pystatus.core.desktop import DesktopNotification
|
||||
|
||||
|
||||
class Backend(SettingsBase):
|
||||
@ -18,13 +19,17 @@ class Updates(Module):
|
||||
Left clicking on the module will refresh the count of upgradeable packages.
|
||||
This may be used to dismiss the notification after updating your system.
|
||||
|
||||
Right clicking shows a desktop notification with a summary count and a list
|
||||
of available updates.
|
||||
|
||||
.. rubric:: Available formatters
|
||||
|
||||
* `{count}` — Sum of all available updates from all backends.
|
||||
* For each backend registered there is one formatter named after the backend,
|
||||
multiple identical backends do not accumulate, but overwrite each other.
|
||||
* For example, `{Cower}` (note capitcal C) is the number of updates reported by
|
||||
the cower backend, assuming it has been registered.
|
||||
* For each backend registered there is one formatter named after the
|
||||
backend, multiple identical backends do not accumulate, but overwrite
|
||||
each other.
|
||||
* For example, `{Cower}` (note capital C) is the number of updates
|
||||
reported by the cower backend, assuming it has been registered.
|
||||
|
||||
.. rubric:: Usage example
|
||||
|
||||
@ -50,9 +55,16 @@ class Updates(Module):
|
||||
("backends", "Required list of backends used to check for updates."),
|
||||
("format", "Format used when updates are available. "
|
||||
"May contain formatters."),
|
||||
("format_no_updates", "String that is shown if no updates are available."
|
||||
" If not set the module will be hidden if no updates are available."),
|
||||
("format_working", "Format used while update queries are run. By default the same as ``format``."),
|
||||
("format_no_updates", "String that is shown if no updates are "
|
||||
"available. If not set the module will be hidden if no updates "
|
||||
"are available."),
|
||||
("format_working", "Format used while update queries are run. By "
|
||||
"default the same as ``format``."),
|
||||
("format_summary", "Format for the summary line of notifications. By "
|
||||
"default the same as ``format``."),
|
||||
("notification_icon", "Icon shown when reporting the list of updates. "
|
||||
"Default is ``software-update-available``, and can be "
|
||||
"None for no icon."),
|
||||
"color",
|
||||
"color_no_updates",
|
||||
"color_working",
|
||||
@ -64,21 +76,27 @@ class Updates(Module):
|
||||
format = "Updates: {count}"
|
||||
format_no_updates = None
|
||||
format_working = None
|
||||
format_summary = None
|
||||
notification_icon = "software-update-available"
|
||||
color = "#00DD00"
|
||||
color_no_updates = None
|
||||
color_working = None
|
||||
|
||||
on_leftclick = "run"
|
||||
on_rightclick = "report"
|
||||
|
||||
def init(self):
|
||||
if not isinstance(self.backends, list):
|
||||
self.backends = [self.backends]
|
||||
if self.format_working is None: # we want to allow an empty format
|
||||
self.format_working = self.format
|
||||
if self.format_summary is None: # we want to allow an empty format
|
||||
self.format_summary = self.format
|
||||
self.color_working = self.color_working or self.color
|
||||
self.data = {
|
||||
"count": 0
|
||||
}
|
||||
self.notif_body = {}
|
||||
self.condition = threading.Condition()
|
||||
self.thread = threading.Thread(target=self.update_thread, daemon=True)
|
||||
self.thread.start()
|
||||
@ -95,7 +113,9 @@ class Updates(Module):
|
||||
for backend in self.backends:
|
||||
key = backend.__class__.__name__
|
||||
if key not in self.data:
|
||||
self.data[key] = '?'
|
||||
self.data[key] = "?"
|
||||
if key not in self.notif_body:
|
||||
self.notif_body[key] = ""
|
||||
|
||||
self.output = {
|
||||
"full_text": formatp(self.format_working, **self.data).strip(),
|
||||
@ -104,9 +124,11 @@ class Updates(Module):
|
||||
|
||||
updates_count = 0
|
||||
for backend in self.backends:
|
||||
updates = backend.updates
|
||||
name = backend.__class__.__name__
|
||||
updates, notif_body = backend.updates
|
||||
updates_count += updates
|
||||
self.data[backend.__class__.__name__] = updates
|
||||
self.data[name] = updates
|
||||
self.notif_body[name] = notif_body or ""
|
||||
|
||||
if updates_count == 0:
|
||||
self.output = {} if not self.format_no_updates else {
|
||||
@ -124,3 +146,12 @@ class Updates(Module):
|
||||
def run(self):
|
||||
with self.condition:
|
||||
self.condition.notify()
|
||||
|
||||
def report(self):
|
||||
DesktopNotification(
|
||||
title=formatp(self.format_summary, **self.data).strip(),
|
||||
body="\n".join(self.notif_body.values()),
|
||||
icon=self.notification_icon,
|
||||
urgency=1,
|
||||
timeout=0,
|
||||
).display()
|
||||
|
@ -23,10 +23,14 @@ class AptGet(Backend):
|
||||
command = "apt-get upgrade -s -o Dir::State::Lists=" + cache_dir
|
||||
apt = run_through_shell(command.split())
|
||||
|
||||
update_count = 0
|
||||
for line in apt.out.split("\n"):
|
||||
if line.startswith("Inst"):
|
||||
update_count += 1
|
||||
return update_count
|
||||
out = apt.out.splitlines()
|
||||
out = [line[5:] for line in apt.out if line.startswith("Inst ")]
|
||||
return out.count("\n"), out
|
||||
|
||||
Backend = AptGet
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Call this module directly; Print the update count and notification body.
|
||||
"""
|
||||
print("Updates: {}\n\n{}".format(*Backend().updates))
|
||||
|
@ -13,6 +13,12 @@ class Cower(Backend):
|
||||
def updates(self):
|
||||
command = ["cower", "-u"]
|
||||
cower = run_through_shell(command)
|
||||
return cower.out.count('\n')
|
||||
return cower.out.count('\n'), cower.out
|
||||
|
||||
Backend = Cower
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Call this module directly; Print the update count and notification body.
|
||||
"""
|
||||
print("Updates: {}\n\n{}".format(*Backend().updates))
|
||||
|
@ -1,11 +1,14 @@
|
||||
from i3pystatus.core.command import run_through_shell
|
||||
from i3pystatus.updates import Backend
|
||||
from re import split
|
||||
from re import split, sub
|
||||
|
||||
|
||||
class Dnf(Backend):
|
||||
"""
|
||||
Gets update count for RPM-based distributions with dnf.
|
||||
Gets updates for RPM-based distributions with `dnf check-update`.
|
||||
|
||||
The notification body consists of the status line followed by the package
|
||||
name and version for each update.
|
||||
|
||||
https://dnf.readthedocs.org/en/latest/command_ref.html#check-update-command
|
||||
"""
|
||||
@ -14,12 +17,22 @@ class Dnf(Backend):
|
||||
def updates(self):
|
||||
command = ["dnf", "check-update"]
|
||||
dnf = run_through_shell(command)
|
||||
if dnf.err:
|
||||
return "?", dnf.err
|
||||
|
||||
raw = dnf.out
|
||||
update_count = 0
|
||||
if dnf.rc == 100:
|
||||
lines = dnf.out.splitlines()[2:]
|
||||
lines = [l for l in lines if len(split("\s{2,}", l.rstrip())) == 3]
|
||||
lines = raw.splitlines()[2:]
|
||||
lines = [l for l in lines if len(split("\s+", l.rstrip())) == 3]
|
||||
update_count = len(lines)
|
||||
return update_count
|
||||
notif_body = sub(r"(\S+)\s+(\S+)\s+\S+\s*\n", r"\1: \2\n", raw)
|
||||
return update_count, notif_body
|
||||
|
||||
Backend = Dnf
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Call this module directly; Print the update count and notification body.
|
||||
"""
|
||||
print("Updates: {}\n\n{}".format(*Backend().updates))
|
||||
|
@ -12,6 +12,12 @@ class Pacman(Backend):
|
||||
def updates(self):
|
||||
command = ["checkupdates"]
|
||||
checkupdates = run_through_shell(command)
|
||||
return checkupdates.out.count('\n')
|
||||
return checkupdates.out.count("\n"), checkupdates.out
|
||||
|
||||
Backend = Pacman
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Call this module directly; Print the update count and notification body.
|
||||
"""
|
||||
print("Updates: {}\n\n{}".format(*Backend().updates))
|
||||
|
@ -1,4 +1,3 @@
|
||||
import re
|
||||
from i3pystatus.core.command import run_through_shell
|
||||
from i3pystatus.updates import Backend
|
||||
|
||||
@ -6,16 +5,22 @@ from i3pystatus.updates import Backend
|
||||
class Yaourt(Backend):
|
||||
"""
|
||||
This module counts the available updates using yaourt.
|
||||
By default it will only count aur packages. Thus it can be used with the pacman backend like this:
|
||||
By default it will only count aur packages. Thus it can be used with the
|
||||
pacman backend like this:
|
||||
|
||||
from i3pystatus.updates import pacman, yaourt
|
||||
status.register("updates", backends = [pacman.Pacman(), yaourt.Yaourt()])
|
||||
.. code-block:: python
|
||||
|
||||
If you want to count both pacman and aur packages with this module you can set the variable
|
||||
count_only_aur = False like this:
|
||||
from i3pystatus.updates import pacman, yaourt
|
||||
status.register("updates", backends = \
|
||||
[pacman.Pacman(), yaourt.Yaourt()])
|
||||
|
||||
from i3pystatus.updates import yaourt
|
||||
status.register("updates", backends = [yaourt.Yaourt(False)])
|
||||
If you want to count both pacman and aur packages with this module you can
|
||||
set the variable count_only_aur = False like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from i3pystatus.updates import yaourt
|
||||
status.register("updates", backends = [yaourt.Yaourt(False)])
|
||||
"""
|
||||
|
||||
def __init__(self, aur_only=True):
|
||||
@ -25,8 +30,15 @@ class Yaourt(Backend):
|
||||
def updates(self):
|
||||
command = ["yaourt", "-Qua"]
|
||||
checkupdates = run_through_shell(command)
|
||||
out = checkupdates.out
|
||||
if(self.aur_only):
|
||||
return len(re.findall("^aur/", checkupdates.out, flags=re.M))
|
||||
return checkupdates.out.count("\n")
|
||||
out = [line for line in out if line.startswith("aur")]
|
||||
return out.count("\n"), out
|
||||
|
||||
Backend = Yaourt
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Call this module directly; Print the update count and notification body.
|
||||
"""
|
||||
print("Updates: {}\n\n{}".format(*Backend().updates))
|
||||
|
Loading…
Reference in New Issue
Block a user