Friday, December 12, 2014

Power of missing parentheses

I've completed the Footnotes panel. Its testing was rather minimal, although I believe sufficient. The footnote "model" did receive a rather thorough fnotdata_test.py module that exercises every branch and error condition. With a solid model, the view/controller piece goes together very quickly. I set up a fnotview_runner.py that starts up the app, loads a document full of footnotes of various types, and sits waiting for interaction. I used this to exercise all the functions manually, and quickly ran into a vexing problem that took a couple of hours to track down. When I knew the cause, I was even more vexed. Look at the following code fragment. Hands up all those who see the glaring, obvious, stupid error?

        self.model.beginResetModel()
        worktc = self.edit_view.get_cursor()
        worktc.beginEditBlock()
        try:
            for j in range(self.data.count()):
                # ...twenty lines of code computing a new key value
                # for footnote j...
                if new_key is not None :
                    self.data.set_key(j, new_key, worktc)
            # end of for j in range of keys
        except Exception as whatever:
            fnotview_logger.error(
                'Unexpected error renumbering footnotes: {}'.format(whatever.args)
                )
        worktc.endEditBlock
        self.model.endResetModel()

Here's what's going on. The user has clicked Renumber. The view/controller tells its QTableModel that data is changing. It obtains a QTextCursor on the document and starts an "edit macro" on it, so that all changes made using it will be a single Undo. For each existing footnote key it computes a new renumbered value, and calls the data model to set that as the new Key value in both the Anchor and Note of footnote j.

The model gets passed the working text cursor and uses it when it updates the Key value in the Anchor ([key]) and the Note ([Footnote key:...). At the end of the loop, even if there were errors, the edit macro is closed and the table model is told it can refresh itself.

When executed, this caused all sorts of flaky behavior in the editor. The new Key values would not appear unless I made the page scroll. Undo did not always undo the changes. Doing new changes compounded the problems.

I spent quite a bit of time over two days inserting print statements and tracing and... before I finally noticed that glaring error that you, dear reader, spotted five minutes ago. Fix that and suddenly everything worked "just swellegant" as my late father liked to say.

Well, live and learn.

Or not.

Looking ahead, again

Anyway, that's done, and also I fixed a serious, if obscure, bug in Version 1 and released new packages for it. A few posts back I put up the current to-do list. A month has passed; time to revisit it.

  • When Qt5.4 and the matching PyQt are available, install those on the new iMac and move development to there.
    • Qt5.4 is out, PyQt5.4 is expected any minute. So this should happen next week I hope.
  • Then, bring CoBro up to the Qt5.4 level and replace the execrable WebKit browser with the new WebEngine one.
  • Then, use Cobro as a test-bed for learning how to use pyqtdeploy to bundle an app. I am eager to find out if this is truly a way to make a self-contained executable on all 3 platforms, in place of pyinstaller.
  • Presuming that works (and that the new web engine fixes the frequent crashes induced by webkit), release CoBro on all three platforms.
  • Then, or right now to pass the time waiting for Qt5.4, code the footnotes module. That will go fast; most of the code can be lifted out of version 1...
    • It took a bit more work than that, but it's done.
  • Then, or right now if PyQt5.4 is delayed, implement the Preferences dialog.

At that point — which will in no way be reached in calendar 2014 — PPQT2 will be at what might be called an alpha state, that is, with adequate function that an experienced user could post-process a book with it. That user would have to run from source, however, until the pyqtdeploy work is complete.

The work to be done after that includes:

  • Writing the translation interface module, which includes figuring out how to dynamically load translator modules.
  • Writing the plain-ascii example translator
  • Writing the HTML example translator
  • Bringing the "keyboard palettes" of V1 forward to V2 and making them load dynamically (using the same scheme as the translators?)
  • Finally going back into the UI and make panels drag-out-able, applying the drag-drop research with which I began this series of posts many months ago.
  • Writing the Help file and adding the Help panel
  • Rewriting the "suggested workflow" document to reflect all the changes; for this I will want to actually post-process a book myself to make sure I know the best way to use the app.
  • Make some screencasts to explain PPQT and show its features.

No comments: