Source code for libqtile.widget.currentlayout

# -*- coding: utf-8 -*-
# Copyright (c) 2011 Florian Mounier
# Copyright (c) 2011 Kenji_Takahashi
# Copyright (c) 2012 roger
# Copyright (c) 2012, 2014 Tycho Andersen
# Copyright (c) 2012 Maximilian Köhl
# Copyright (c) 2013 Craig Barnes
# Copyright (c) 2014 Sean Vig
# Copyright (c) 2014 Adi Sieker
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import inspect
import os

import cairocffi

from libqtile import bar, hook
from libqtile import layout as layout_module
from libqtile.layout.base import Layout
from libqtile.log_utils import logger
from libqtile.widget import base


[docs]class CurrentLayout(base._TextBox): """ Display the name of the current layout of the current group of the screen, the bar containing the widget, is on. """ orientations = base.ORIENTATION_HORIZONTAL def __init__(self, width=bar.CALCULATED, **config): base._TextBox.__init__(self, "", width, **config) def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.text = self.bar.screen.group.layouts[0].name self.setup_hooks() self.add_callbacks({ 'Button1': qtile.cmd_next_layout, 'Button2': qtile.cmd_prev_layout, }) def setup_hooks(self): def hook_response(layout, group): if group.screen is not None and group.screen == self.bar.screen: self.text = layout.name self.bar.draw() hook.subscribe.layout_change(hook_response)
[docs]class CurrentLayoutIcon(base._TextBox): """ Display the icon representing the current layout of the current group of the screen on which the bar containing the widget is. If you are using custom layouts, a default icon with question mark will be displayed for them. If you want to use custom icon for your own layout, for example, `FooGrid`, then create a file named "layout-foogrid.png" and place it in `~/.icons` directory. You can as well use other directories, but then you need to specify those directories in `custom_icon_paths` argument for this plugin. The order of icon search is: - dirs in `custom_icon_paths` config argument - `~/.icons` - built-in qtile icons """ orientations = base.ORIENTATION_HORIZONTAL defaults = [ ( 'scale', 1, 'Scale factor relative to the bar height. ' 'Defaults to 1' ), ( 'custom_icon_paths', [], 'List of folders where to search icons before' 'using built-in icons or icons in ~/.icons dir. ' 'This can also be used to provide' 'missing icons for custom layouts. ' 'Defaults to empty list.' ) ] def __init__(self, **config): base._TextBox.__init__(self, "", **config) self.add_defaults(CurrentLayoutIcon.defaults) self.scale = 1.0 / self.scale self.length_type = bar.STATIC self.length = 0 def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.text = self.bar.screen.group.layouts[0].name self.current_layout = self.text self.icons_loaded = False self.icon_paths = [] self.surfaces = {} self._update_icon_paths() self._setup_images() self._setup_hooks() self.add_callbacks({ 'Button1': qtile.cmd_next_layout, 'Button2': qtile.cmd_prev_layout, }) def _setup_hooks(self): """ Listens for layout change and performs a redraw when it occurs. """ def hook_response(layout, group): if group.screen is not None and group.screen == self.bar.screen: self.current_layout = layout.name self.bar.draw() hook.subscribe.layout_change(hook_response) def draw(self): if self.icons_loaded: try: surface = self.surfaces[self.current_layout] except KeyError: logger.error('No icon for layout {}'.format( self.current_layout )) else: self.drawer.clear(self.background or self.bar.background) self.drawer.ctx.set_source(surface) self.drawer.ctx.paint() self.drawer.draw(offsetx=self.offset, width=self.length) else: # Fallback to text self.text = self.current_layout[0].upper() base._TextBox.draw(self) def _get_layout_names(self): """ Returns the list of lowercased strings for each available layout name. """ return [ layout_class_name.lower() for layout_class, layout_class_name in map(lambda x: (getattr(layout_module, x), x), dir(layout_module)) if inspect.isclass(layout_class) and issubclass(layout_class, Layout) ] def _update_icon_paths(self): self.icon_paths = [] # We allow user to override icon search path self.icon_paths.extend(self.custom_icon_paths) # We also look in ~/.icons/ self.icon_paths.append(os.path.expanduser('~/.icons')) # Default icons are in libqtile/resources/layout-icons. # If using default config without any custom icons, # this path will be used. root = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-2]) self.icon_paths.append(os.path.join(root, 'resources', 'layout-icons')) def find_icon_file_path(self, layout_name): icon_filename = 'layout-{}.png'.format(layout_name) for icon_path in self.icon_paths: icon_file_path = os.path.join(icon_path, icon_filename) if os.path.isfile(icon_file_path): return icon_file_path def _setup_images(self): """ Loads layout icons. """ for layout_name in self._get_layout_names(): icon_file_path = self.find_icon_file_path(layout_name) if icon_file_path is None: logger.warning('No icon found for layout "{}"'.format(layout_name)) icon_file_path = self.find_icon_file_path('unknown') try: img = cairocffi.ImageSurface.create_from_png(icon_file_path) except (cairocffi.Error, IOError) as e: # Icon file is guaranteed to exist at this point. # If this exception happens, it means the icon file contains # an invalid image or is not readable. self.icons_loaded = False logger.exception( 'Failed to load icon from file "{}", ' 'error was: {}'.format(icon_file_path, e.message) ) return input_width = img.get_width() input_height = img.get_height() sp = input_height / (self.bar.height - 1) width = input_width / sp if width > self.length: self.length = int(width) + self.actual_padding * 2 imgpat = cairocffi.SurfacePattern(img) scaler = cairocffi.Matrix() scaler.scale(sp, sp) scaler.scale(self.scale, self.scale) factor = (1 - 1 / self.scale) / 2 scaler.translate(-width * factor, -width * factor) scaler.translate(self.actual_padding * -1, 0) imgpat.set_matrix(scaler) imgpat.set_filter(cairocffi.FILTER_BEST) self.surfaces[layout_name] = imgpat self.icons_loaded = True