Thursday, October 2, 2014

Well, that was easy...

Wrote loupeview.py from scratch in three short sessions over three days while hanging out on the farm and being sociable.

First I had to install bookloupe. This was nontrivial owing to the readme not being as specific on Mac OS as it might be. Wrote a detailed description for the pgdp forum; maybe it will get picked up and integrated someday. For the moment, bookloupe development appears to be stalled.

I looked over the source and concluded it would need major surgery to turn it into a Python lib via either Cython, SWIG or manual coding. So instead, invoke it via subprocess, and apply it to a temporary file.

The temporary file part, which I had been uncertain about, turned out to be amazingly easy. Qt has already thought of it, and the QTemporaryFile class gives you a temporary file in a platform-independent way. You create the object and open() it. That actually creates the file in some suitable place. From that point, the QTemporaryFile object is an actual QFile, with all its methods. You write to the file and close() it. Then you can use QFileInfo to get the full path to the QFile, and that's your argument to some command line program. When eventually the Q(Temporary)File object is garbage-collected, the actual file is automatically deleted.

I wanted to use subprocess.check_output(). The leading, positional argument to that is a list containing the elements of an executable command. The leading element of the list is the name of the command, or in this case the fully qualified pathname of the bookloupe executable. The user will have to provide that eventually through the still to be written Preferences dialog. But the paths module has a default that works for Mac OS and probably for Linux, so that works for now.

Last in the list comes the full path to the temporary file. Between the head and the tail come the parameters of the command. For bookloupe I wanted to pass a batch of dash-letter parameters. So this was my initial shot at coding this.

        command = [bl_path,'-e -s -l -m -v', fbts.fullpath()]
        # run it, capturing the output as a byte stream
        try:
            bytesout = subprocess.check_output( command, stderr=subprocess.STDOUT )
        except subprocess.CalledProcessError as CPE :
            # display a message with text from stderr
            return # leaving message_tuples empty

This provided a nice test of the code in the except clause, displaying "unknown option -e -s -l -m -v". Oh. Pretty clearly the underlying subprocess.popen was putting quotes around my string instead of simply substituting the string into a command. Oh sigh. With

command = [bl_path,'-e','-s','-l','-m','-v', fbts.fullpath()]

it ran perfectly. Here's a screenshot.

The user has doubleclicked on the mesage line headed "461". The doubleclick signal slot tells the editor to jump to line 461, column 48, where indeed there is a "spaced quote".

Bookloupe, like the gutcheck program it was forked from, produces a great quantity of nit-picky diagnostic messages. The way Guiguts dealt with this was with a special dialog that had one checkbox for each possible message type—40 or more of them. It would display in its report only the messages of the types you'd checked. In practice, you hit the "clear all" button and then checked one box at a time, filtering the report to one message type at a time. I think this way of handling it is simpler and just as effective. You can sort the table on either the message text column or the line number column ascending or descending. So if you prefer you can deal with the diagnostics in sequence from the last line up (preserving the lineation if you make edits). Or you can sort by message texts. Then you can deal with one group of messages at a time, just as with Guiguts but a simpler UI.

No comments: