Merge pull request #423 from jk0dick/add-modules
* Network multi color * Add scratchpad module * Add window_title module
This commit is contained in:
commit
45c10d0fe9
@ -3,3 +3,4 @@ sphinx>=1.1
|
||||
colour>=0.0.5
|
||||
mock>=1.0
|
||||
pep8>=1.5.7
|
||||
i3ipc>=1.2.0
|
||||
|
@ -1,13 +1,13 @@
|
||||
import netifaces
|
||||
|
||||
from i3pystatus import IntervalModule
|
||||
from i3pystatus import IntervalModule, formatp
|
||||
from i3pystatus.core.color import ColorRangeModule
|
||||
from i3pystatus.core.util import make_graph, round_dict, make_bar
|
||||
|
||||
|
||||
def count_bits(integer):
|
||||
bits = 0
|
||||
while (integer):
|
||||
while integer:
|
||||
integer &= integer - 1
|
||||
bits += 1
|
||||
return bits
|
||||
@ -67,7 +67,7 @@ def sysfs_interface_up(interface, unknown_up=False):
|
||||
return status == "up" or unknown_up and status == "unknown"
|
||||
|
||||
|
||||
class NetworkInfo():
|
||||
class NetworkInfo:
|
||||
"""
|
||||
Retrieve network information.
|
||||
"""
|
||||
@ -161,7 +161,7 @@ class NetworkInfo():
|
||||
return info
|
||||
|
||||
|
||||
class NetworkTraffic():
|
||||
class NetworkTraffic:
|
||||
"""
|
||||
Retrieve network traffic information
|
||||
"""
|
||||
@ -231,6 +231,8 @@ class NetworkTraffic():
|
||||
class Network(IntervalModule, ColorRangeModule):
|
||||
"""
|
||||
Displays network information for an interface.
|
||||
formatp support
|
||||
if u wanna display recv/send speed separate in dynamic color mode, please enable pango hint.
|
||||
|
||||
Requires the PyPI packages `colour`, `netifaces`, `psutil` (optional, see below)
|
||||
and `basiciw` (optional, see below).
|
||||
@ -255,10 +257,10 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
* `{quality}` — Link quality in percent
|
||||
* `{quality_bar}` —Bar graphically representing link quality
|
||||
|
||||
Network Traffic Formatters (requires PyPI pacakge `psutil`):
|
||||
Network Traffic Formatters (requires PyPI package `psutil`):
|
||||
|
||||
* `{interface}` — the configured network interface
|
||||
* `{kbs}` – Float representing kb\s
|
||||
* `{kbs}` – Float representing KiB\s corresponds to graph type
|
||||
* `{network_graph}` – Unicode graph representing network usage
|
||||
* `{bytes_sent}` — bytes sent per second (divided by divisor)
|
||||
* `{bytes_recv}` — bytes received per second (divided by divisor)
|
||||
@ -267,6 +269,7 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
* `{rx_tot_Mbytes}` — total Mbytes received
|
||||
* `{tx_tot_Mbytes}` — total Mbytes sent
|
||||
"""
|
||||
|
||||
settings = (
|
||||
("format_up", "format string"),
|
||||
("format_down", "format string"),
|
||||
@ -278,8 +281,10 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
("end_color", "Hex or English name for end of color range, eg '#FF0000' or 'red'"),
|
||||
("graph_width", "Width of the network traffic graph"),
|
||||
("graph_style", "Graph style ('blocks', 'braille-fill', 'braille-peak', or 'braille-snake')"),
|
||||
("upper_limit",
|
||||
"Expected max kb/s. This value controls how the network traffic graph is drawn and in what color"),
|
||||
("recv_limit", "Expected max KiB/s. This value controls the drawing color of receive speed"),
|
||||
("sent_limit", "Expected max KiB/s. similar with receive_limit"),
|
||||
("separate_color", "display recv/send color separate in dynamic color mode."
|
||||
"Note: only network speed formatters will display with range color "),
|
||||
("graph_type", "Whether to draw the network traffic graph for input or output. "
|
||||
"Allowed values 'input' or 'output'"),
|
||||
("divisor", "divide all byte values by this value"),
|
||||
@ -301,7 +306,9 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
graph_type = 'input'
|
||||
graph_width = 15
|
||||
graph_style = 'blocks'
|
||||
upper_limit = 150.0
|
||||
recv_limit = 2048
|
||||
sent_limit = 1024
|
||||
separate_color = False
|
||||
|
||||
# Network traffic settings
|
||||
divisor = 1024
|
||||
@ -319,7 +326,7 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
|
||||
def init(self):
|
||||
# Don't require importing basiciw unless using the functionality it offers.
|
||||
if any(s in self.format_up or s in self.format_up for s in
|
||||
if any(s in self.format_down or s in self.format_up for s in
|
||||
['essid', 'freq', 'quality', 'quality_bar']):
|
||||
get_wifi_info = True
|
||||
else:
|
||||
@ -337,9 +344,10 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
self.network_traffic = None
|
||||
|
||||
if not self.dynamic_color:
|
||||
self.end_color = self.start_color
|
||||
self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit))
|
||||
self.end_color = self.start_color = self.color_up
|
||||
self.colors = self.get_hex_color_range(self.start_color, self.end_color, 100)
|
||||
self.kbs_arr = [0.0] * self.graph_width
|
||||
self.pango_enabled = self.hints.get("markup", False) and self.hints["markup"] == "pango"
|
||||
|
||||
def cycle_interface(self, increment=1):
|
||||
"""Cycle through available interfaces in `increment` steps. Sign indicates direction."""
|
||||
@ -354,11 +362,11 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
self.network_traffic.clear_counters()
|
||||
self.kbs_arr = [0.0] * self.graph_width
|
||||
|
||||
def get_network_graph(self, kbs):
|
||||
def get_network_graph(self, kbs, limit):
|
||||
# Cycle array by inserting at the start and chopping off the last element
|
||||
self.kbs_arr.insert(0, kbs)
|
||||
self.kbs_arr = self.kbs_arr[:self.graph_width]
|
||||
return make_graph(self.kbs_arr, 0.0, self.upper_limit, self.graph_style)
|
||||
return make_graph(self.kbs_arr, 0.0, limit, self.graph_style)
|
||||
|
||||
def run(self):
|
||||
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
||||
@ -369,15 +377,35 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
network_usage = self.network_traffic.get_usage(self.interface)
|
||||
format_values.update(network_usage)
|
||||
if self.graph_type == 'input':
|
||||
kbs = network_usage['bytes_recv']
|
||||
limit = self.recv_limit
|
||||
kbs = network_usage['bytes_recv'] * self.divisor / 1024
|
||||
elif self.graph_type == 'output':
|
||||
kbs = network_usage['bytes_sent']
|
||||
limit = self.sent_limit
|
||||
kbs = network_usage['bytes_sent'] * self.divisor / 1024
|
||||
else:
|
||||
raise Exception("graph_type must be either 'input' or 'output'!")
|
||||
|
||||
format_values['network_graph'] = self.get_network_graph(kbs)
|
||||
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
||||
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
||||
format_values['network_graph'] = self.get_network_graph(kbs, limit)
|
||||
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2))
|
||||
|
||||
if self.separate_color and self.pango_enabled:
|
||||
color = self.color_up
|
||||
color_template = "<span color=\"{}\">{}</span>"
|
||||
per_recv = network_usage["bytes_recv"] * self.divisor / (self.recv_limit * 1024)
|
||||
per_sent = network_usage["bytes_sent"] * self.divisor / (self.sent_limit * 1024)
|
||||
c_recv = self.get_gradient(int(per_recv * 100), self.colors, 100)
|
||||
c_sent = self.get_gradient(int(per_sent * 100), self.colors, 100)
|
||||
format_values["bytes_recv"] = color_template.format(c_recv, network_usage["bytes_recv"])
|
||||
format_values["bytes_sent"] = color_template.format(c_sent, network_usage["bytes_sent"])
|
||||
if self.graph_type == 'output':
|
||||
c_kbs = c_sent
|
||||
else:
|
||||
c_kbs = c_recv
|
||||
format_values['network_graph'] = color_template.format(c_kbs, format_values["network_graph"])
|
||||
format_values['kbs'] = color_template.format(c_kbs, format_values["kbs"])
|
||||
else:
|
||||
percent = int(kbs * 100 / limit)
|
||||
color = self.get_gradient(percent, self.colors, 100)
|
||||
else:
|
||||
color = None
|
||||
|
||||
@ -395,6 +423,6 @@ class Network(IntervalModule, ColorRangeModule):
|
||||
|
||||
self.data = format_values
|
||||
self.output = {
|
||||
"full_text": format_str.format(**format_values),
|
||||
"full_text": formatp(format_str, **format_values).strip(),
|
||||
'color': color,
|
||||
}
|
||||
|
74
i3pystatus/scratchpad.py
Normal file
74
i3pystatus/scratchpad.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from threading import Thread
|
||||
from i3pystatus import Module
|
||||
import i3ipc
|
||||
|
||||
|
||||
class Scratchpad(Module):
|
||||
"""
|
||||
Display the amount of windows and indicate urgency hints on scratchpad (async).
|
||||
|
||||
fork from scratchpad_async of py3status by cornerman
|
||||
|
||||
Requires the PyPI package `i3ipc`.
|
||||
|
||||
.. rubric:: Available formaters
|
||||
|
||||
* `{number}` — amount of windows on scratchpad
|
||||
|
||||
@author jok
|
||||
@license BSD
|
||||
"""
|
||||
|
||||
settings = (
|
||||
("format", "format string."),
|
||||
("always_show", "whether the indicator should be shown if there are"
|
||||
" no scratchpad windows"),
|
||||
("color_urgent", "color of urgent"),
|
||||
("color", "text color"),
|
||||
)
|
||||
|
||||
format = u"{number} ⌫"
|
||||
always_show = True
|
||||
color_urgent = "#900000"
|
||||
color = "#FFFFFF"
|
||||
|
||||
def init(self):
|
||||
self.count = 0
|
||||
self.urgent = False
|
||||
|
||||
t = Thread(target=self._listen)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
def update_scratchpad_counter(self, conn, *args):
|
||||
cons = conn.get_tree().scratchpad().leaves()
|
||||
self.urgent = any(con for con in cons if con.urgent)
|
||||
self.count = len(cons)
|
||||
|
||||
# output
|
||||
if self.urgent:
|
||||
color = self.color_urgent
|
||||
else:
|
||||
color = self.color
|
||||
|
||||
if self.always_show or self.count > 0:
|
||||
full_text = self.format.format(number=self.count)
|
||||
else:
|
||||
full_text = ''
|
||||
|
||||
self.output = {
|
||||
"full_text": full_text,
|
||||
"color": color,
|
||||
}
|
||||
|
||||
def _listen(self):
|
||||
conn = i3ipc.Connection()
|
||||
self.update_scratchpad_counter(conn)
|
||||
|
||||
conn.on('window::move', self.update_scratchpad_counter)
|
||||
conn.on('window::urgent', self.update_scratchpad_counter)
|
||||
conn.on('window::new', self.update_scratchpad_counter)
|
||||
conn.on('window::close', self.update_scratchpad_counter)
|
||||
|
||||
conn.main()
|
113
i3pystatus/window_title.py
Normal file
113
i3pystatus/window_title.py
Normal file
@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from i3pystatus import Module
|
||||
from threading import Thread
|
||||
import i3ipc
|
||||
|
||||
|
||||
class WindowTitle(Module):
|
||||
"""
|
||||
Display the current window title with async update.
|
||||
Uses asynchronous update via i3 IPC events.
|
||||
Provides instant title update only when it required.
|
||||
|
||||
fork from window_tile_async of py3status by Anon1234 https://github.com/Anon1234
|
||||
|
||||
Requires the PyPI package `i3ipc`.
|
||||
|
||||
.. rubric:: Available formaters
|
||||
|
||||
* `{title}` — title of current focused window
|
||||
* `{class_name}` - name of application class
|
||||
|
||||
@author jok
|
||||
@license BSD
|
||||
"""
|
||||
|
||||
settings = (
|
||||
("format", "format string."),
|
||||
("always_show", "do not hide the title when it can be already visible"),
|
||||
("empty_title", "string that will be shown instead of the title when the title is hidden"),
|
||||
("max_width", "maximum width of title"),
|
||||
("color", "text color"),
|
||||
)
|
||||
|
||||
format = "{title}"
|
||||
always_show = False
|
||||
empty_title = ""
|
||||
max_width = 79
|
||||
color = "#FFFFFF"
|
||||
|
||||
def init(self):
|
||||
self.title = self.empty_title
|
||||
self.output = {
|
||||
"full_text": self.title,
|
||||
"color": self.color,
|
||||
}
|
||||
|
||||
# we are listening to i3 events in a separate thread
|
||||
t = Thread(target=self._loop)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
def get_title(self, conn):
|
||||
tree = conn.get_tree()
|
||||
w = tree.find_focused()
|
||||
p = w.parent
|
||||
|
||||
# don't show window title when the window already has means
|
||||
# to display it
|
||||
if (not self.always_show and
|
||||
(w.border == "normal" or w.type == "workspace" or
|
||||
(p.layout in ("stacked", "tabbed") and len(p.nodes) > 1))):
|
||||
return self.empty_title
|
||||
else:
|
||||
title = w.name
|
||||
class_name = w.window_class
|
||||
if len(title) > self.max_width:
|
||||
title = title[:self.max_width - 1] + "…"
|
||||
return self.format.format(title=title, class_name=class_name)
|
||||
|
||||
def update_title(self, conn, e):
|
||||
# catch only focused window title updates
|
||||
title_changed = hasattr(e, "container") and e.container.focused
|
||||
|
||||
# check if we need to update title due to changes
|
||||
# in the workspace layout
|
||||
layout_changed = (
|
||||
hasattr(e, "binding") and
|
||||
(e.binding.command.startswith("layout") or
|
||||
e.binding.command.startswith("move container") or
|
||||
e.binding.command.startswith("border"))
|
||||
)
|
||||
|
||||
if title_changed or layout_changed:
|
||||
self.title = self.get_title(conn)
|
||||
self.update_display()
|
||||
|
||||
def clear_title(self, *args):
|
||||
self.title = self.empty_title
|
||||
self.update_display()
|
||||
|
||||
def update_display(self):
|
||||
self.output = {
|
||||
"full_text": self.title,
|
||||
"color": self.color,
|
||||
}
|
||||
|
||||
def _loop(self):
|
||||
conn = i3ipc.Connection()
|
||||
self.title = self.get_title(conn) # set title on startup
|
||||
self.update_display()
|
||||
|
||||
# The order of following callbacks is important!
|
||||
# clears the title on empty ws
|
||||
conn.on('workspace::focus', self.clear_title)
|
||||
|
||||
# clears the title when the last window on ws was closed
|
||||
conn.on("window::close", self.clear_title)
|
||||
|
||||
# listens for events which can trigger the title update
|
||||
conn.on("window::title", self.update_title)
|
||||
conn.on("window::focus", self.update_title)
|
||||
|
||||
conn.main() # run the event loop
|
Loading…
Reference in New Issue
Block a user