Permalink
Browse files

DGroups really is first class now

Groups configuration is now done entirely by DGroups. The default behavior is
the same as before, but the DGroups configuration API is now much simpler.
  • Loading branch information...
1 parent 69dd351 commit e1e9b2397c5c1ef48fb40aa46fa9e265632172a4 @tych0 tych0 committed Dec 10, 2012
Showing with 137 additions and 34 deletions.
  1. +3 −1 docs/conf.py
  2. +22 −5 docs/manual/config/groups.rst
  3. +2 −0 libqtile/confreader.py
  4. +103 −27 libqtile/dgroups.py
  5. +4 −1 libqtile/manager.py
  6. +3 −0 libqtile/resources/default_config.py
View
@@ -17,6 +17,8 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('_themes'))
+sys.path.insert(0, os.path.abspath('../'))
+# sys.path.insert(0, os.path.abspath('../libqtile'))
# -- General configuration -----------------------------------------------------
@@ -25,7 +27,7 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -5,16 +5,33 @@ A group is a container for a bunch of windows,
analogous to workspaces in other window managers. Each
client window managed by the window manager belongs to
exactly one group. The ``groups`` config file variable
-should be initialized to a list of Group objects.
+should be initialized to a list of ``DGroup`` objects.
+
+``DGroup`` objects provide several options for group configuration. Groups can
+be configured to show and hide themselves when they're not empty, spawn
+applications for them when they start, automatically acquire certain groups,
+and various other options.
+
+.. autoclass:: libqtile.dgroups.Match
+ :members: __init__
+
+.. autoclass:: libqtile.dgroups.DGroup
+ :members: __init__
+
+.. autofunction:: libqtile.dgroups.simple_key_binder
Example
~~~~~~~
::
- from libqtile.manager import Group
+ from libqtile.dgroups import DGroup, simple_key_binder
groups = [
- Group("a"),
- Group("b"),
- Group("c"),
+ DGroup("a"),
+ DGroup("b"),
+ DGroup("c", match=Match(wm_title=["Firefox"])),
]
+
+ # allow mod3+1 through mod3+0 to bind to groups
+ dgroups_key_binder = simple_key_binder("mod3")
+
View
@@ -67,6 +67,8 @@ def __init__(self, fname=None, is_restart=False):
"keys",
"mouse",
"groups",
+ "dynamic_groups",
+ "dgroups_key_binder",
"follow_mouse_focus",
"cursor_warp",
"layouts",
View
@@ -1,14 +1,26 @@
+import itertools
import gobject
import libqtile.hook
from libqtile.manager import Key
from libqtile.command import lazy
-
class Match(object):
''' Match for dynamic groups
it can match by title, class or role '''
def __init__(self, title=[], wm_class=[], role=[], wm_type=[]):
+ """
+
+ ``Match`` supports both regular expression objects (i.e. the result of
+ ``re.compile()``) or strings (match as a "include" match). If a window
+ matches any of the things in any of the lists, it is considered a
+ match.
+
+ :param title: things to match against the title
+ :param wm_classes: things to match against the WM_CLASS atom
+ :param role: things to match against the WM_ROLE atom
+ :param wm_type: things to match against the WM_TYPE atom
+ """
self._rules = [('title', t) for t in title]
self._rules += [('wm_class', w) for w in wm_class]
self._rules += [('role', r) for r in role]
@@ -70,14 +82,80 @@ def func(dgroup):
return func
+class Rule(object):
+ """ A Rule contains a Match object, and a specification about what to do
+ when that object is matched. """
+ def __init__(self, match, group=None, float=False, intrusive=False):
+ """
+ :param match: ``Match`` object associated with this ``Rule``
+ :param float: auto float this window?
+ :param intrusive: override the group's exclusive setting?
+ """
+ self.match = match
+ self.group = group
+ self.float = float
+ self.intrusive = intrusive
+
+ def matches(self, w):
+ return self.match.compare(w)
+
+class DGroup(object):
+ """
+ Represents a "dynamic" group. These groups can spawn apps, only allow
+ certain Matched windows to be on them, hide when they're not in use, etc.
+ """
+ def __init__(self, name, matches=None, rules=None, exclusive=False,
+ spawn=None, layout=None, persist=True, init=True,):
+ """
+ :param name: the name of this group
+ :type name: string
+ :param matches: list of ``Match`` objects whose windows will be assigned to this group
+ :type matches: default ``None``
+ :param rules: list of ``Rule`` objectes wich are applied to this group
+ :type rules: default ``None``
+ :param exclusive: when other apps are started in this group, should we allow them here or not?
+ :type exclusive: boolean
+ :param spawn: this will be ``exec()`` d when the group is created
+ :type spawn: string
+ :param layout: the default layout for this group (e.g. 'max' or 'stack')
+ :type layout: string
+ :param persist: should this group stay alive with no member windows?
+ :type persist: boolean
+ :param init: is this group alive when qtile starts?
+ :type init: boolean
+
+ """
+ self.name = name
+ self.exclusive = exclusive
+ self.spawn = spawn
+ self.layout = layout
+ self.persist = persist
+ self.init = init
+ self.rules = rules
+ if self.rules is None:
+ self.rules = []
+ for rule in self.rules:
+ rule.group = self.name
+ if matches is None:
+ matches = []
+ self.rules.extend([Rule(match, group=self.name) for match in matches])
+
+ # undocumented, any idea what these do?
+ self.master = None
@Roger

Roger Dec 18, 2012

Contributor

master sets the window that is master in groups like Tile, it's useful to set the roster gajim, as master, for example

+ self.ratio = None
@Roger

Roger Dec 18, 2012

Contributor

set the radio of the layout in this group

+
class DGroups(object):
''' Dynamic Groups '''
- def __init__(self, qtile, groups, apps, key_binder=None, delay=1):
+ def __init__(self, qtile, dgroups, key_binder=None, delay=1):
self.qtile = qtile
- self.groups = groups
- self.apps = apps
+ self.groups = dgroups
+ self.groupMap = {}
+ for group in self.groups:
+ self.groupMap[group.name] = group
+
+ self.rules = itertools.chain.from_iterable([g.rules for g in dgroups])
self.keys = []
self.key_binder = key_binder
@@ -90,13 +168,12 @@ def __init__(self, qtile, groups, apps, key_binder=None, delay=1):
self.timeout = {}
def _setup_groups(self):
- for name, tag in self.groups.iteritems():
- if tag.get('init') == True:
- self.qtile.addGroup(name)
+ for group in self.groups:
+ if group.init:
+ self.qtile.addGroup(group.name)
- spawn_cmd = tag.get('spawn')
- if spawn_cmd and not self.qtile.no_spawn:
- self.qtile.cmd_spawn(spawn_cmd)
+ if group.spawn and not self.qtile.no_spawn:
+ self.qtile.cmd_spawn(group.spawn)
def _setup_hooks(self):
libqtile.hook.subscribe.client_new(self._add)
@@ -131,43 +208,42 @@ def _add(self, client):
group_set = False
intrusive = False
- for app in self.apps:
+ for rule in self.rules:
# Matching Rules
- if app['match'].compare(client):
- if 'group' in app:
- group = app['group']
- group_added = self.qtile.addGroup(group)
- client.togroup(group)
+ if rule.matches(client):
+ if rule.group:
+ group_added = self.qtile.addGroup(rule.group)
+ client.togroup(rule.group)
group_set = True
- group_obj = self.qtile.groupMap[group]
- group_opts = self.groups.get(group)
- if group_opts:
+ group_obj = self.qtile.groupMap[rule.group]
+ group = self.groupMap.get(rule.group)
+ if group:
if group_added:
- layout = group_opts.get('layout')
- ratio = group_opts.get('ratio')
+ layout = group.layout
+ ratio = group.ratio
if layout:
group_obj.layout = layout
if ratio:
group_obj.ratio = ratio
- master = group_opts.get('master')
+ master = group.master
if master:
group_obj.layout.shuffle(
lambda lst: self.shuffle_groups(
lst, master))
- if 'float' in app and app['float']:
+ if rule.float:
client.enablefloating()
- if 'intrusive' in app:
- intrusive = app['intrusive']
+ if rule.intrusive:
+ intrusive = group.intrusive
# If app doesn't have a group
if not group_set:
current_group = self.qtile.currentGroup.name
if current_group in self.groups and\
- self.groups[current_group].get('exclusive') and\
+ self.groupMap[current_group].exclusive and\
not intrusive:
wm_class = client.window.get_wm_class()
@@ -193,7 +269,7 @@ def _del(self, client):
def delete_client():
# Delete group if empty and dont persist
if group and group.name in self.groups and\
- self.groups[group.name].get('persist') and\
+ self.groupMap[group.name].persist and\
len(group.windows) <= 0:
self.qtile.delGroup(group.name)
View
@@ -758,7 +758,10 @@ def __init__(self, config,
if config.main:
config.main(self)
- self.groups += self.config.groups[:]
+ if self.config.groups:
+ # TODO: break some dependencies out of this file (i.e. break more configs)
+ from libqtile.dgroups import DGroups
+ DGroups(self, self.config.groups, self.config.dgroups_key_binder)
for i in self.groups:
i._configure(config.layouts, config.floating_layout, self)
@@ -74,6 +74,9 @@
Key(["mod1", "shift"], i.name, lazy.window.togroup(i.name))
)
+dynamic_groups = {}
+dgroups_key_binder = None
+
layouts = [
layout.Max(),
layout.Stack(stacks=2)

0 comments on commit e1e9b23

Please sign in to comment.