Saturday, June 7, 2014

Plugging along

In two marathon sessions — well, marathon for me, about four consecutive hours each — I completed functional coding of the mainwindow, all the elements of the File menu (Open, Recent->list of recent, Save, Save As, Close). This entailed major and frequent revisions and additions to the "utilities" module, where I have corralled all uses of QFile, QFileDialog, QMessageBox and the like. And some changes to the Book object, trying to get its relationship to the main window just right, efficient and also clear.

But that's pretty well done now. Next big task is to write unit test drivers for both utilities.py and mainwindow.py to ensure every branch is exercised. For the main window that will mean using Sikuli to do visual testing of the GUI, and that's time-consuming (but mega-fun to watch it run when finished). This will take much of next week.

Edit Menu(s)

Meanwhile I've been mulling how to handle the Edit menu. It was a problem with V.1; I never could, for example, get the Edit menu to work right when one of the panels, like the Word panel, had the focus.

Working on the File menu I realized that the menu actions are key. Each menu consists of a list of QActions; and each QAction has a "triggered" signal that is bound to a slot in some QWidget derivative. Well, what happens when the widget to which, say, the Edit>Cut action is bound, is not the focused widget?

This is particularly important when contrasting the Edit menu when a document (QPlainTextEdit) is focused, versus when, say, the Word table (QAbstractTableModel) has the focus. Edit>Copy in the first case means, the current selection of text to the clipboard. In the second, the current selection is some number of cells available as a list, and there may well be processing needed to format them before their value(s) go to the clipboard.

For the editor, there are Edit menu actions I want to support like to-lowercase, to-uppercase that are not appropriate for other panels, and shouldn't even appear (or at least, not be enabled).

And then there's the issue of having multiple books open (which changes everything, as I've lamented frequently before). So let us say that the Edit>To &Uppercase menu action is bound to a slot in the editview object for Book 1. And now the user clicks in the edit tab bar to make Book B the focus of his typing. Different editor contents, different selection, all handled in a different goddam object. Choice of Edit>To &Uppercase now should affect the current selection in Book B, but how is Qt to know that? How to keep it from sending the signal to the editview object representing Book 1?

So I've about concluded that every widget that supports Edit actions needs its own unique Edit menu. And somehow (!) when any such widget gets the focus (focus-in event), it puts its own Edit menu into the application's menu bar; and when it loses the focus, it removes it again.

This takes care of the problem of signal-binding. Each widget that wants an Edit menu, creates its own Edit menu and populates it with such actions as it supports, binding each to the slots in its own code where it does things its way, upon its own data.

But how to swap Edit menus in the menu bar, quickly and simply? I note that what the QMenuBar supports is not menus per se but menu Actions. And I note that QAction supports a property "visible" with a method "setVisible(bool)". So tentatively I am thinking I will have any Edit-supporting widget, upon creation, add its own Edit menu to the app's menu bar. There might be a dozen Edit menus in the menu bar! But it adds it with visible False, and sets visible True on focus-in and False on focus-out. So hopefully only one (or possibly zero) Edit menus will actually be visible in the menu bar.

Does QMenuBar actually support such shenanigans? Damn if I know! If it doesn't, plan B is to have each widget add its menu on focus-in, and remove it again on focus-out, which seems uglier.

Stay tuned, it could be a rocky night...

No comments: