Friday, October 29, 2010

Making wxWidgets do my damn bidding

In order to match the functionality of my Opal Kelly FP display using wxWidgets, I have to figure out how to make a numerical control. None of the Opal Kelly samples that used wxWidgets seem to have this capability, and I suspect that it's because this is harder than it looks, particularly with needing to do hexadecimal entry limited only to the hex characters. The PipeTest sample in particular seems to wimp out by using wxComboBoxes, which seem to be listboxes according to the wxWidgets Wiki (This may not be a correct impression, if only because the examples that I've seen so far don't show listbox initializations).

http://wiki.wxwidgets.org/WxComboBox
http://docs.wxwidgets.org/2.8/wx_wxcombobox.html

Data entry in wxWidgest seems to be only through wxTextCtrl objects. I find that hard to believe! This seems even more unbelievable if these objects are multiline???

Here is the basic argument list for wxTextCtrl, in the helpful wiki "docs" format. Oddly, I don't think that it mentions validators:
http://docs.wxwidgets.org/stable/wx_wxtextctrl.html.

Here is an example where capitalization is enforced in a wxTextCtrl object, by setting an event for any editing, reading the contents, mashing the retrieved contents into all caps, and writing them back, apparently instantaneously:

http://wiki.wxwidgets.org/WxTextCtrl#In-place_Input_Capitalization

Here is a similar example of home-grown validation which waits for the user to hit enter, and then checks the result and rejects it back to the text box:

http://www.daniweb.com/forums/thread303734.html

Earlier in the helpful wiki page, it suggests using controls that I can't find. Apparently they are "contributed" which means they probably have to be downloaded from some other site and compiled etc.

http://wiki.wxwidgets.org/WxTextCtrl#Allowing_Only_Certain_Kinds_of_Input

The following forum thread, although about Python, references the same non-canonical objects:

http://programming.itags.org/python/34536/

Finally, this thread, only two answers long, not only references the unoffical objects, but also shows code for an event handler for a TextCtrl that responds to *each keypress* and judges it.

http://stackoverflow.com/questions/1369086/is-it-possible-to-limit-textctrl-to-accept-numbers-only-in-wxpython

The big problem with these lovely custom versions of TextCtrls is that they all seem to be written in Python. Here is the only page so far that has a link to a C++ version of a masked text control. The FTP link is to a zip file that contains an older style .dsw project file, a copy of the control, and two layers of a demo application which is actually quite a nice demo. I hope that I can somehow compile this and play with it. My ambivalence about this code is that the control itself is about 20 freaking pages long!

http://ftp.sunet.se/wmirror/wxwidgets.org/contrib2.htm



It really seems like the missing piece might be "validators", but I have no idea if they work the way I want or not. The following thread seems to indicate that they might not even have worked as recently as 2008. It seems to show people writing wxWidgets classes on the spot!

http://www.blitzbasic.com/Community/posts.php?topic=79872

The above makes reference to filters that are part of wxTextCtrl, like ALPHANUMERIC. Here is more info (oddly, can't find the same page in the "stable" docs tree?):

http://docs.wxwidgets.org/2.8/wx_wxtextvalidator.html#wxtextvalidator

And here's probably the most complete example of validator setup that I've been able to find so far:

http://forum.wxformbuilder.org/index.php?topic=229.0;wap2

Here's an example of building up a validator list using entirely custom adds. The author of the post admits to his code's crudity:

http://old.nabble.com/wxTextValidator-question-td8761160.html

Finally, the validator docs guide from the stable docs tree, showing those various ready-to-use options:

http://docs.wxwidgets.org/stable/wx_wxtextvalidator.html

A realization from the Clue-by-four is to compile the TextCtrl and Validator samples from the install directory. I'll try that and see if they contain examples of getting a numerical value using a TextCtrl widget.

http://docs.wxwidgets.org/trunk/page_samples.html

So, I did that, and decided to use the SetMaxLength method of the wxTextCtrl to limit the number of characters, and use a validator with wxFILTER_INCLUDE_LIST to limit to the hex characters. Eventually I figured out that using wxFILTER_INCLUDE_LIST or'd with wx_NUMERIC doesn't work, and so I just used wxFILTER_INCLUDE_LIST by itself and added the ten number characters to the include list. The two text controls look very nice and show the default value, but their contents couldn't be edited! Eventually I figured out that the problem was that they weren't getting "focus". After a while, I discovered that I could use SetFocus() in my code to make one of them start off with focus, and then I stumbled into the realization that I could use the tab character to get the focus to each text control and was able to edit them when they had focus, but I can't figure out how to get the focus on the controls with a mouse click. You'd think this would be simple. I found a clue here, finally:

http://thedailyreviewer.com/compsys/view/click-events-for-wxtextctrl-11147227

This opened up a new avenue of investigation. Originally, my take-away from the above was that (due to the fact that I'm using a frame with text controls on it) mouse click events on the text controls are not propagating up to the frame, which I presumed was supposed to be the parent. The actual answer turned out to be simplicity itself, basically PEBWAC. Because I had coded my main program from one example, but had taken the code for the TextCtrls from a different example, the TextCtrls were "on" the wrong part of the screen. Specifically, they were members of the "frame", and not the "panel". All examples that I looked at defined an application, a frame, and a panel. My first example defined all the widget pointers as members of a frame, but when it defined them it first created a panel right in the initializer for the frame and assigned that panel as the parent of each widget. My second example defined the frame and panel as two separate objects, and made each widget pointer a member of the panel, and they were defined using "this" as their parent. Thus when I pasted the code into my code which had been derived from the first example, they used "this" rather than the panel pointer to define their parent, and consequently their parent was defined as the frame. By simply recoding the wxTextCtrls to have the panel as their parent, it restored the behavior of mouse clicking on the control to allow it to get focus. This all has to do with the fact that MouseEvents don't propagate up to the frame; I didn't have to define a whole new class derived from the wxTextCtrl and do some kind of trick to push the class's event handling to the frame, as the above forum suggests. The text controls with validators in the validator example worked because they were defined on a dialog, which is apparently like a panel.