Friday, January 30, 2015

Added "Book Facts", exploring PyQtDeploy

Turning away from the frustrations of bundling, I coded a feature of PPQT. Driving along a few days ago on a routine errand, the thought of book metadata came to me. Both fp and fpgen, as well as MarkDown and AsciiDoc and others, have support for "title", "author", "publication date" in some form or other. PPQT, I realized, should offer some method of entering and editing such data.

There's no standard for the format of these, but they all amount to a set of key:value pairs, in short, a Python (or JSON) dict, like {"Title":"The Telltale Hare", "Author":"Bugs Bunny", "Published":1931} and so on.

What to call them? Please, not "metadata", not where the user can see it. That term is already being overused. These are simply facts about the book.

Because the book-facts are unique to the book (duh!) they need to be maintained by the Book object. Because they need to be persistent from session to session, they need to be stored in the metadata. So it was clear the Book should have the code to keep the dict of book-facts, and the code to read it from the metadata file and save it to the metadata file. That code took about 20 minutes to write and implement.

How to present these data to the user for editing? While driving along that afternoon, I realized that it was a dict, so present it as a dict (but not called that). Just give the user a dialog box with some text lines in it, in the form "keystring :valuestring", basically a dump of the dict.

Since the user might want to add or delete lines, it would be a multiline display, and the easiest way to do that is to present a QPlainTextEdit.

When, and by what UI? Well, the Edit view already has a context menu. It currently had four choices: toggles for highlight spelling and highlight scannos, and commands to choose a scanno file and choose a spelling dictionary. These are book-level choices, and early on I decided that book-level choices (as opposed to application-level choices) would be in a context menu, not in the global menu bar.

Editing book facts is another book-level action. So I just added that as a fifth command in the context menu: Edit Book Facts... The code is in the Book, but I put the nuts and bolts of building the QDialog in utilities where all the other dialog boxes are confined. Here's the context menu in action.

Here's the dialog.

Preparing the text for display is just this simple,

        for (key, arg) in self.book_facts.items() :
            starting_text += '{} : {}\n'.format(key,arg)

And storing it after the user has clicked Ok is like this:

                    try :
                        (key, arg) = line.split(':') # exception if not exactly 1 colon
                        self.book_facts[key.strip()] = arg.strip()

So that all went in very nicely.


Then I turned to trying out pyqtdeploy. Based on its tutorial overview it does just what a bundler would want: it converts all the messy parts of a Python/PyQt5 application into a single (no doubt huge) binary executable. I started trying to set it up to bundle Cobro without actually reading the more than the first few pages of doc. And naturally ran into some issues. But it looks promising. I hope I can get it to work; I would be so pleased to be able to forget all about cxfreeze, pyinstaller, even Nuitka. (Well, I might keep Nuitka around but use it to compile sub-modules only. PyQtDeploy should be able to bundle compiled modname.so files...?)

No comments: