From 9a88e9ff0385f66e7c565a394908503dc6e916ad Mon Sep 17 00:00:00 2001 From: neodarz Date: Fri, 28 Apr 2017 00:30:19 +0200 Subject: Site updated at 2017-04-28T00:29:42+02:00 source branch was at: f1965c50670f611ef54f9471490d45a554f7d866 Correct a link --- ...5-05-22-using-a-command-table-as-wallpaper.html | 194 +++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 build/blog/2015-05-22-using-a-command-table-as-wallpaper.html (limited to 'build/blog/2015-05-22-using-a-command-table-as-wallpaper.html') diff --git a/build/blog/2015-05-22-using-a-command-table-as-wallpaper.html b/build/blog/2015-05-22-using-a-command-table-as-wallpaper.html new file mode 100644 index 00000000..64821bb9 --- /dev/null +++ b/build/blog/2015-05-22-using-a-command-table-as-wallpaper.html @@ -0,0 +1,194 @@ + + + + + + + +Using a command table as wallpaper + + + + + + + + + +
This blog has been archived.
Visit my home page at zhimingwang.org.
+ +
+
+

Using a command table as wallpaper

+ +
+

Recently I cleaned up my source code directory, removed a lot of rarely-used, dated scripts, and grouped the remaining standalone scripts into a central place (~/dev/scripts)1. One thing I learned in this process is that I tend to write a reusable script but rarely actually reuse it (even if it sits on PATH), sometimes implementing the same functionality twice or typing a long command line over and over again.

+

To remind myself of which scripts are at my fingertip, I decided to use a command table as wallpaper on my secondary display. So I wrote a shitty Python script2 (depending on XeLaTeX and ImageMagick) to automate the generation of such a wallpaper. It's pretty customizable, and anyone may grab it and do whatever they want to with it (also available as a gist):

+
#!/usr/bin/env python3
+
+"""Generate command table."""
+
+import argparse
+import os
+import shlex
+import subprocess
+import sys
+import tempfile
+
+# pylint: disable=wildcard-import,unused-wildcard-import
+
+from zmwangx.colorout import *
+
+DEFAULT_COLUMN_WIDTH = 120
+DEFAULT_FOREGROUND_COLOR = "white"
+DEFAULT_BACKGROUND_COLOR = "black"
+DEFAULT_FONT = "Consolas"
+DEFAULT_BORDER = 20
+DEFAULT_DENSITY = 300
+DEFAULT_SIZE = "1280x800"
+
+HERE = os.path.dirname(os.path.realpath(sys.argv[0]))
+XELATEX_PROGRAM = (r"""
+\documentclass[varwidth=\maxdimen,border={border}pt]{{standalone}}
+\usepackage{{color}}
+\pagecolor{{{background}}}
+\color{{{foreground}}}
+\usepackage{{fontspec}}
+\setmonofont{{{font}}}
+
+\begin{{document}}
+\begin{{verbatim}}
+{table}
+\end{{verbatim}}
+\end{{document}}
+""")
+
+def text_table(**kwargs):
+    """Generate the text version of the table."""
+    width = kwargs["width"] if "width" in kwargs else DEFAULT_COLUMN_WIDTH
+    directory = kwargs["directory"] if "directory" in kwargs else HERE
+    command_line = (r"find {directory} -maxdepth 1 -type f -perm -u=x -exec basename {{}} \; "
+                    "| column -c {width} | expand".format(
+                        directory=shlex.quote(directory), width=width))
+    ccommand(command_line)
+    return subprocess.check_output(command_line, shell=True).decode("utf-8")
+
+def pdf_table(**kwargs):
+    """Generate the PDF version of the table.
+
+    Returns 0 on success or 1 on failure. Generated PDF is "table.pdf"
+    in the current working directory.
+
+    """
+    border = kwargs["border"] if "border" in kwargs else DEFAULT_BORDER
+    foreground = kwargs["foreground"] if "foreground" in kwargs else DEFAULT_FOREGROUND_COLOR
+    background = kwargs["background"] if "background" in kwargs else DEFAULT_BACKGROUND_COLOR
+    font = kwargs["font"] if "font" in kwargs else DEFAULT_FONT
+    program = XELATEX_PROGRAM.format(table=text_table(**kwargs).strip(),
+                                     font=font, border=border,
+                                     foreground=foreground, background=background)
+    with open("table.tex", "w") as texfileobj:
+        texfileobj.write(program)
+    try:
+        ccommand("xelatex table.tex")
+        subprocess.check_call(["xelatex", "table.tex"],
+                              stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+        return 0
+    except subprocess.CalledProcessError:
+        cerror("xelatex failed on the following program:")
+        cerrnewline()
+        cerrwrite("default", program)
+        return 1
+
+def png_table(**kwargs):
+    """Generate the PNG version of the table.
+
+    Returns 0 on success or 1 on failure. Generated PNG is "table.png"
+    in the current working directory.
+
+    """
+    if pdf_table(**kwargs) == 1:
+        return 1
+    density = kwargs["density"] if "density" in kwargs else DEFAULT_DENSITY
+    size = kwargs["size"] if "size" in kwargs else DEFAULT_SIZE
+    background = kwargs["background"] if "background" in kwargs else DEFAULT_BACKGROUND_COLOR
+    command_line = ("convert -density {density} table.pdf -resize {size} -size {size} "
+                    "xc:{background} +swap -gravity center -composite table.png".format(
+                        density=density, size=size, background=background))
+    try:
+        ccommand(command_line)
+        subprocess.check_call(shlex.split(command_line))
+        return 0
+    except subprocess.CalledProcessError:
+        cerror("the following ImageMagick command failed:")
+        cerrprint("default", command_line)
+        return 1
+
+def main():
+    """CLI."""
+    description = "Generate a PNG table of all executable commands in a directory."
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument("--width", type=int, default=DEFAULT_COLUMN_WIDTH,
+                        help="""line width, default is 120""")
+    parser.add_argument("--directory",
+                        help="""directory containing executables, default is
+                        the directory containing this command""")
+    parser.add_argument("--border", type=int, default=DEFAULT_BORDER,
+                        help="""default is 20pt""")
+    parser.add_argument("--foreground", default=DEFAULT_FOREGROUND_COLOR,
+                        help="""foreground color, default is white""")
+    parser.add_argument("--background", default=DEFAULT_BACKGROUND_COLOR,
+                        help="""background color, default is black""")
+    parser.add_argument("--font", default=DEFAULT_FONT,
+                        help="""default is Consolas""")
+    parser.add_argument("--density", default=DEFAULT_DENSITY,
+                        help="""used for the -density argument of convert,
+                        default is 300""")
+    parser.add_argument("--size", default=DEFAULT_SIZE,
+                        help="""size of image, default is 1280x800""")
+    args = parser.parse_args()
+    kwargs = {k: v for (k, v) in args.__dict__.items() if v is not None}
+
+    fd, tmpfilepath = tempfile.mkstemp(suffix=".png", prefix="table-")
+    os.close(fd)
+    with tempfile.TemporaryDirectory(prefix="table-") as working_directory:
+        os.chdir(working_directory)
+        if png_table(**kwargs) == 1:
+            cerror("execution failed")
+            os.remove(tmpfilepath)
+        else:
+            os.rename("table.png", tmpfilepath)
+            cprogress("saved to:")
+            print(tmpfilepath)
+
+if __name__ == "__main__":
+    main()
+

By the way, the zmwangx.colorout module is here, just to ease the printing of progress and errors to tty. You may safely remove all the ccommand, cerr* and cprogress calls.

+

Here is an example wallpaper reflecting my current ~/dev/scripts:

+
+Command table wallpaper for my secondary display (MBP 13'' builtin display). +

Command table wallpaper for my secondary display (MBP 13'' builtin display).

+
+
+
+
    +
  1. The ~/dev directory stands for development, and contains all my source code and almost all local builds. The point is by having a ~/dev directory, I no longer need to have bin, include, lib, and share in my HOME, thus saving a few slots. Backing up and restoring is also slightly easier.↩︎

  2. +
  3. Yeah, I know it's a shitty script, so don't nitpick on style problems.↩︎

  4. +
+
+
+
+ + + -- cgit v1.2.1