SaltyCrane Blog — Notes on JavaScript and web development

Scripting wmii column widths with Python

I mentioned in my previous post on using wmii with Gnome that I had written a script for resizing the column widths in wmii. This is the followup post. Note, I am using the 20080520 snapshot of wmii. This doesn't work with wmii 3.6 (as Marco commented below).

To incrementally change window sizes, I use the following in my ~/.wmii-3.5/wmiirc file:

	Key $MODKEY-y
		# shrink horizontally
		wmiir xwrite /tag/sel/ctl grow sel sel right -10
		wmiir xwrite /tag/sel/ctl grow sel sel left -10
	Key $MODKEY-u
		# grow horizontally
		wmiir xwrite /tag/sel/ctl grow sel sel right 10
		wmiir xwrite /tag/sel/ctl grow sel sel left 10
	Key $MODKEY-i
		# shrink vertically
		wmiir xwrite /tag/sel/ctl grow sel sel down -10
		wmiir xwrite /tag/sel/ctl grow sel sel up -10
	Key $MODKEY-o
		# grow vertically
		wmiir xwrite /tag/sel/ctl grow sel sel down 10
		wmiir xwrite /tag/sel/ctl grow sel sel up 10
	Key $MODKEY-Shift-y
		# shrink horizontally
		wmiir xwrite /tag/sel/ctl grow sel sel right -2
		wmiir xwrite /tag/sel/ctl grow sel sel left -2
	Key $MODKEY-Shift-u
		# grow horizontally
		wmiir xwrite /tag/sel/ctl grow sel sel right 2
		wmiir xwrite /tag/sel/ctl grow sel sel left 2
	Key $MODKEY-Shift-i
		# shrink vertically
		wmiir xwrite /tag/sel/ctl grow sel sel down -2
		wmiir xwrite /tag/sel/ctl grow sel sel up -2
	Key $MODKEY-Shift-o
		# grow vertically
		wmiir xwrite /tag/sel/ctl grow sel sel down 2
		wmiir xwrite /tag/sel/ctl grow sel sel up 2

In addition to incrementally changing column widths, I wanted to be able to switch to predetermined column width ratios with a keyboard shortcut. For example, I wanted to be able to set the column widths at a 20/80 ratio, a 40/60 ratio, a 50/50 ratio, a 60/40 ratio, and so on. So I hacked a Python script to do this. It is pretty ugly because I first grow the window by a set amount, measure the change in size, then grow it again to the correct width. If anyone knows of a better way to do this, please let me know. I'm posting my solution here in case anyone else wanted to do the same thing and got stuck. (Note, this script only works with two columns)

UPDATE 2009-12-21: I just learned from the new wmii documentation that I can specify a grow amount in pixels by suffixing it with "px". This means I no longer have to perform the ugly, extra grow-then-measure step in my script. I'm not sure if this is a newly added change or if it is just newly documented. I am now using wmii 3.9b1. I have updated the script below to use the new method. Also, the script now works with more than two columns. I kept the old method for reference.

#!/usr/bin/env python

import os
import re
import sys

class Wmii:
    """
    wmiir xwrite /tag/sel/ctl grow col row side increment
    col: column number of the window to grow
    row: row number of the window to grow
    side: the side to grow. one of left, right, up, or down
    increment: the number of pixels to grow. use a positive number to grow larger
    and a negative number to grow smaller
    """
    def set_column_widths(self, width_list):
        """Use the 'grow' command to set the column widths to those specified.
        Widths are specified in percentages.
        """
        total_width_perc = sum([float(width) for width in width_list])
        for i, width_perc in enumerate(width_list[:-1]):
            self.read_current_col_widths()
            total_width_px = float(sum(self.curr_colwidths))
            new_width_px =  float(width_perc) / total_width_perc * total_width_px
            grow_amount_px = int(round(new_width_px - self.curr_colwidths[i]))
            self.xwrite("/tag/sel/ctl grow %d 1 right %dpx" % (i+1, grow_amount_px))

    def read_current_col_widths(self):
        """'wmiir read /tag/sel/index' and set the attribute, self.curr_colwidths.
        self.curr_colwidths is a list of the width (ints) (in pixels) of each
        column in the view.
        """
        lines = self.read("/tag/sel/index")
        self.curr_colwidths = []
        for line in lines:
            match = re.search(r"# [^~]+ \d+ (\d+)", line)
            if match:
                self.curr_colwidths.append(int(match.group(1)))
        print self.curr_colwidths

    def xwrite(self, path_and_value):
        """Use the xwrite form."""
        cmd = "wmiir xwrite %s" % path_and_value
        print cmd
        os.system(cmd)

    def read(self, path):
        """Return a list of the lines returned by "wmii read path" """
        return os.popen4("wmiir read " + path)[1].readlines()

if __name__ == "__main__":
    w = Wmii()
    w.set_column_widths(sys.argv[1:])
Old method (for reference):
#!/usr/bin/env python

import os
import re
import sys

class Wmii:
    """
    wmiir xwrite /tag/sel/ctl grow col row side increment
    col: column number of the window to grow
    row: row number of the window to grow
    side: the side to grow. one of left, right, up, or down
    increment: the number of pixels to grow. use a positive number to grow larger
    and a negative number to grow smaller
    """
    def __init__(self):
        pass

    def set_column_widths(self, width0, width1):
        """Use the 'grow' command to set the column widths to those specified.
        Widths are specified in percentages.
        Currently only works with 2 columns.
        """
        self.determine_pixels_per_grow_horiz()
        new_width0 = sum(self.curr_colwidths) * (float(width0) /
                                                 (float(width0)+float(width1)))
        grow_amount = int(round((new_width0-self.curr_colwidths[0]) /
                                self.pixels_per_grow_increment))
        self.xwrite("/tag/sel/ctl grow 1 1 right %d" % grow_amount)

    def determine_pixels_per_grow_horiz(self):
        """Try growing by an increment of 1 and record the number of pixels changed.
        """
        self.read_current_col_widths()
        prev_colwidth0 = self.curr_colwidths[0]
        self.xwrite("/tag/sel/ctl grow 1 1 right 1")
        self.read_current_col_widths()
        self.pixels_per_grow_increment = self.curr_colwidths[0] - prev_colwidth0

    def read_current_col_widths(self):
        """'wmiir read /tag/sel/index' and set the attribute, self.curr_colwidths.
        self.curr_colwidths is a list of the width (ints) (in pixels) of each
        column in the view.
        """
        lines = self.read("/tag/sel/index")
        self.curr_colwidths = []
        for line in lines:
            match = re.search(r"# [^~]+ \d+ (\d+)", line)
            if match:
                self.curr_colwidths.append(int(match.group(1)))
        print self.curr_colwidths

    def read_current_column_number(self):
        """'wmiir read /tag/sel/ctl' and set the attribute, self.curr_col."""
        lines = self.read("/tag/sel/ctl")
        self.curr_col = re.split(" ", lines[1])[1]
        print "curr_col = %s" % self.curr_col

    def xwrite(self, path_and_value):
        """Use the xwrite form."""
        cmd = "wmiir xwrite %s" % path_and_value
        print cmd
        os.system(cmd)

    def read(self, path):
        """Return a list of the lines returned by "wmii read path" """
        return os.popen4("wmiir read " + path)[1].readlines()

if __name__ == "__main__":
    w = Wmii()
    w.set_column_widths(sys.argv[1], sys.argv[2])

I named the script wmii.py, made it executable, and put it on my path. Then I modified my wmiirc:

	Key $MODKEY-q
		wmii.py 20 80
	Key $MODKEY-w
		wmii.py 40 60
	Key $MODKEY-e
		wmii.py 50 50
	Key $MODKEY-r
		wmii.py 60 40
	Key $MODKEY-t
		wmii.py 80 20

Hope that's helpful to someone. Let me know if you've written some cool python wmii scripts.

Comments


#1 Marco R. commented on :

Hi sofeng, thanks for the post! Unfortunately the stable wmii-3.6 (dated 2007-11-16) doesn't have the grow command, as confirmed here.

The idea of fixed column width ratios is good, but I would have used just one key shortcut to cycle between the predefined widths. I will experiment with that code after a wmii upgrade...

Bye, Marco


#3 Repolho commented on :

This is exactly what I was looking for! Thanks :)