Snippet: scan folders for Qtile's maildir widget

My most recent crush is Qtile. I discovered this window manager about two weeks ago, and I just can't stop messing with the config files. For my needs it's as awesome as Awesome, with the huge difference that it's Python instead of Lua, so I feel more confident about what I'm doing.

Qtile, or any window manager, is far too big for the scope of this blog, but I wanted to share something that might be useful for anybody who is familiar with Qtile and who is also using a neomutt setup like mine.

Qtile, like most window managers, lets you add widgets to your bar(s). If you use maildir for your mail you'll like to know that one of the built-in widgets can show you the number of new emails in your mailboxes. You just need to add and instance of its class to your bar:

from libqtile.config import Screen
from libqtile import bar, widget

screens = [
	Screen(
		top=bar.Bar([
			widget.Maildir(
				maildir_path = "~/Mail",
				sub_folders = [
					{"label": "Home", "path": "home"},
					{"label": "Work", "path": "work"}
					]
				)
			])
		)
	]

My neomutt setup has tons of others subfolders to both put email away and sort it on arrival. It would take too much time to type them manually, and besides I would need to remember to update Qtile's config at each change in the subfolders structure.

My archival/sorting subfolders all follow the extended Maildir format:

.Archive.Newsletters.Site1
.Archive.Newsletters.Site2
.Archive.Company1.Project1
.Archive.Company1.Project2
.Archive.Company2
...etc

Here's a little snippet to populate the subfolders list automatically:

from os import path, listdir

mail_rel_path = "~/Mail"

def get_maildir_folders():
	# expanduser converts ~/Mail to
	# /the/absolute/path/to/Mail
	fullpath = path.expanduser(mail_rel_path)
	# First some folders that don't fall in the above scheme:
	folders = [
			{"label": " Home", "path": "home"},
			{"label": " Work", "path": "work"}
		]
	# Get a list of all the folders inside ~/Mail
	# and take only the ones starting by ".Archive.":
	dirlist = listdir(fullpath)
	for item in dirlist:
		if path.isdir(fullpath + "/" + item) == True and item[:9] == ".Archive.":
			# Remove the redundant "Archive" and make some
			# cosmetic substitutions for the names;
			name = ": ".join(item[9:].split(".")).replace("_"," ").replace("Work:", "🔨")
			# Make pairs with names and paths, convert them to dict
			# and add them to the list:
			folders.append(dict([("label", "" + name), ("path", item)]))
	return folders

And then the widget itself would get the following arguments:

widget.Maildir(
		maildir_path = mail_rel_path,
		separator = "",
		sub_folders = get_maildir_folders(),
		hide_when_empty = True,
		update_interval = 3,
	),

The hide_when_empty parameter makes the widget to just show the folder when there are new messages in it.

The empty separator is needed, because the widget shows one for every folder even if they are hidden when empty. In order to compensate the lack of separators, a vertical bar is added to the folder name in the snippet above (line 24).