Repository: ultrabug/py3status

Issue:
Three more modules for my older request
Added not only current title, but else - battery level, current keyboard layout and scratchpad window counter.

Documentation, dependencies and config included inside files.



Discussion:
Wow that's great mate !

Do you have tested them with python3 ?

I think the battery-level.py does need an encofing header because of the special chars in it 

```
# -*- coding: utf-8 -*-
```

I'd love to test them and make some little enhancements maybe, would you care giving me write access to your fork or do you prefer me to list the things here ? (I could also merge and change after, as you wish)

Thanks

Yes, I use python 3 as primary python on my laptop. Py3status and this modules ran with it.

Added header, granted access - you may go on.

And I added mpd module, also.

Thanks @ShadowPrince 

I've reworked a bit the battery and keyboard layout modules so far. It's better to make sure your code works for both python2 and python3 while checking also for PEP8 (and Flake8) coding conventions.

I'll go into the next modules soon and let you know. Feel free to comment on my commits on your fork.

Cheers

Ok cleanup done, please tell me if you're okay with all this before I merge :)

Thanks again @ShadowPrince 

Sorry about PEP8, its been a long time a commit a public python code, and yes, I'm totally ok with changes, except for one:

Its about current keyboard layout. When I was searching how to do that, I figure out that, it seems, there is no command-line way to get layout with standart Xorg components. `setxkbmap -query` only shows all layouts, and don't show any information about a current one, so, that code is broken and 'll not work.

Depending on uncommon program is not a good way, I know, but it seems that there is no other obvious way.

Great.

As for the `setxkbmap -query`, it doesn't show all the layouts but the currently active one as per the man (and tested behavior)

```
-query  With this option setxkbmap just prints the current rules, model, layout, variant, and options, then exits.
```

Doesn't it for you ? make sure we're talking about setxkbmap v1.3.0 as commented on the code.

I have 1.3.0 and that what I got:

```
~ » /usr/bin/setxkbmap -layout us,ru -variant -option grp:caps_toggle,grp_led:scroll
~ » current layout - US 
~ » setxkbmap -query
rules:      evdev
model:      pc104
layout:     us,ru
variant:    -option
options:    grp:caps_toggle,grp_led:scroll,grp:caps_toggle,grp_led:scroll
~ » current layout - russian
~ » setxkbmap -query                                                                
rules:      evdev
model:      pc104
layout:     us,ru
variant:    -option
options:    grp:caps_toggle,grp_led:scroll,grp:caps_toggle,grp_led:scroll
~ » /usr/bin/setxkbmap -layout us,ru,ua -variant -option grp:caps_toggle,grp_led:scroll
~ » current layout - ukrainian
~ » setxkbmap -query                                                                   
rules:      evdev
model:      pc104
layout:     us,ru,ua
variant:    -option
options:    grp:caps_toggle,grp_led:scroll,grp:caps_toggle,grp_led:scroll,grp:caps_toggle,grp_led:scroll
```

And, speaking literally - it does'nt show all layouts, only enabled ones.

gah :( what about `setxkbmap -print` if I parse the `xkb_symbols` value ?

Same thing, no difference between layout changes. Do something of this works to you?

When using a binding to change your current layout on i3 it works correctly, yes.

I think your output is showed because you use setxkbmap with multiple layout and not an i3 binding. As for most users it won't be the case and as the default behavior of the module is to use xkblayout-state if present, I suggest we stick with the proposed solution.

Is that okay ?

I think so. But, maybe, you'll put a warning about that in the module?

Sure as a comment in the code, be my guest.

Tell me when done, then I'll merge :)

Thanks a lot for all those additions @ShadowPrince ! :)


Resolving patch:
diff --git a/py3status/modules/keyboard_layout.py b/py3status/modules/keyboard_layout.py
--- a/py3status/modules/keyboard_layout.py
+++ b/py3status/modules/keyboard_layout.py
@@ -2,88 +2,97 @@
 """
 Display the current keyboard layout.
 
-Configuration parameters:
-    - cache_timeout: check for keyboard layout change every seconds
-
+This module displays the current active keyboard layout.
 Requires:
     - xkblayout-state
     or
-    - setxkbmap
+    - setxkbmap and xset (Works for the first two predefined layouts.)
 
-@author shadowprince
+Configuration parameters:
+    - cache_timeout: check for keyboard layout change every seconds
+    - colors: a comma separated string of color values for each layout,
+              eg: "us=#FCE94F, fr=#729FCF".
+    - color: a single color value for all layouts. eg: "#FCE94F"
+
+@author shadowprince, tuxitop
 @license Eclipse Public License
 """
 
 from subprocess import check_output
 from time import time
-import shlex
 import re
 
-# colors of layouts, check your command's output to match keys
-LANG_COLORS = {
-    'fr': '#268BD2',  # solarized blue
-    'ru': '#F75252',  # red
-    'ua': '#FCE94F',  # yellow
-    'us': '#729FCF',  # light blue
-}
-
-LAYOUT_RE = re.compile(r".*layout:\s*(\w+).*", flags=re.DOTALL)
-
-
-def xkblayout():
-    """
-    check using xkblayout-state (preferred method)
-    """
-    return check_output(
-        ["xkblayout-state", "print", "%s"]
-    ).decode('utf-8')
-
-
-def setxkbmap():
-    """
-    check using setxkbmap >= 1.3.0
-
-    Please read issue 33 for more information :
-        https://github.com/ultrabug/py3status/pull/33
-    """
-    out = check_output(shlex.split("setxkbmap -query")).decode("utf-8")
-
-    return re.match(LAYOUT_RE, out).group(1)
-
 
 class Py3status:
     """
     """
     # available configuration parameters
     cache_timeout = 10
-    color = ''
+    colors = 'us=#729FCF, fr=#268BD2, ua=#FCE94F, ru=#F75252'
+    color = None
 
     def __init__(self):
         """
         find the best implementation to get the keyboard's layout
         """
         try:
-            xkblayout()
+            self._xkblayout()
+            self._command = self._xkblayout
         except:
-            self.command = setxkbmap
-        else:
-            self.command = xkblayout
+            self._command = self._xset
 
     def keyboard_layout(self, i3s_output_list, i3s_config):
         response = {
             'cached_until': time() + self.cache_timeout,
             'full_text': ''
         }
-
-        lang = self.command().strip()
-        lang_color = self.color if self.color else LANG_COLORS.get(lang)
-
-        response['full_text'] = lang or '??'
+        if not self.color:
+            self.colors_dict = dict((k.strip(), v.strip()) for k, v in
+                                    (layout.split('=') for layout in
+                                     self.colors.split(',')))
+        lang = self._command().strip()
+        lang_color = self.color if self.color else self.colors_dict.get(lang)
         if lang_color:
             response['color'] = lang_color
 
+        response['full_text'] = lang or '??'
         return response
 
+    def _get_layouts(self):
+        """
+        Returns a list of predefined keyboard layouts
+        """
+        layouts_re = re.compile(r".*layout:\s*((\w+,?)+).*", flags=re.DOTALL)
+        out = check_output(["setxkbmap", "-query"]).decode("utf-8")
+        layouts = re.match(layouts_re, out).group(1).split(",")
+        return layouts
+
+    def _xkblayout(self):
+        """
+        check using xkblayout-state
+        """
+        return check_output(
+            ["xkblayout-state", "print", "%s"]
+        ).decode('utf-8')
+
+    def _xset(self):
+        """
+        Check using setxkbmap >= 1.3.0 and xset
+        This method works only for the first two predefined layouts.
+        """
+        ledmask_re = re.compile(r".*LED\smask:\s*(\d+).*", flags=re.DOTALL)
+        layouts = self._get_layouts()
+        if len(layouts) == 1:
+            return layouts[0]
+        xset_output = check_output(["xset", "-q"]).decode("utf-8")
+        led_mask = re.match(ledmask_re, xset_output).group(1)
+        if led_mask == "00000000":
+            return layouts[0]
+        elif led_mask == "00001000":
+            return layouts[1]
+        return "Err"
+
+
 if __name__ == "__main__":
     """
     Test this module by calling it directly.
@@ -92,8 +101,10 @@ def keyboard_layout(self, i3s_output_list, i3s_config):
     x = Py3status()
     config = {
         'color_good': '#00FF00',
+        'color_degraded': '#FFFF00',
         'color_bad': '#FF0000',
     }
     while True:
         print(x.keyboard_layout([], config))
         sleep(1)
+


Test patch: