Hi guys,
I'm working on a simple drawing application which is supposed to demonstrate all the main aspects of a typical wxPython app. Unfortunately, I'm stuck with on particular thing: I want to have a dialog box with a validated wxTextCtrl in it...but I've had no luck getting it to work.
It seems that wxPyValidators work fine if you don't use a wxDialog as your topmost window, and similarly wxDialogs work fine if you don't try to associate a wxPyValidator with an input field -- but when you try and mix the two you get all sorts of problems.
I've searched the mailing list archives, and found several references to people experiencing the same problem (either wxWindows crashes completely, or else you get a "Could not transfer data to window" alert box). Based on something Robin said in January of last year, I tried adding dummy TransferToWindow() and TransferFromWindow() methods to my wxPyValidator subclass, so that these methods return TRUE rather than the default value of FALSE -- but this doesn't seem to help at all. With these methods commented out or set to return FALSE, the "Could not transfer data to window" alert pops up; with them set to return TRUE, I get a hard crash in PYTHON15.DLL (I'm currently running Python 1.5.2 and wxPython 2.2.5 on a Windows ME machine -- though ideally any solution should work on all platforms and versions of Python). In desperation, I tried overriding wxWindow.TransferDataToWindow and wxWindow.TransferDataFromWindow, but it seems that these are C++ methods and can't be overridden at the Python level -- and I can't think of anything else to try.
I've copied out the relevant bits of the application and put them into a dummy standalone program, which I'll paste in below. If anyone can please explain how to get this going -- and more generally, how to implement a wxDialog subclass containing validated input fields, I'd be really grateful. The whole point of the app I'm writing is to demonstrate how to write a non-trivial wxPython application (we'll be using this program as the showcase app for the Getting Started guide), so I really need to get this working properly -- obviously, I could get around these problems by using a modal wxFrame as the topmost window rather than a wxDialog, but I want to demonstrate dialogs and validated input fields, which is why I'm persisting. Have I, perhaps, overlooked something obvious?
Any clues would be most welcome!
Thanks in advance,
- Erik.
=========================== Python Code Follows =============================
from wxPython.wx import *
···
#----------------------------------------------------------------------------
class EditTextObjectDialog(wxDialog):
""" Dialog box used to edit the properties of a text object.
The user can edit the object's text, font, size, and text style.
"""
def __init__(self, parent, title):
""" Standard constructor.
"""
wxDialog.__init__(self, parent, -1, title)
self.textCtrl = wxTextCtrl(self, -1, "", style=wxTE_MULTILINE,
validator=TextObjectValidator())
extent = self.textCtrl.GetFullTextExtent("Hy")
lineHeight = extent[1] + extent[3]
self.textCtrl.SetSize(wxSize(-1, lineHeight * 4))
fonts = wxFontEnumerator()
fonts.EnumerateFacenames()
fontList = fonts.GetFacenames()
fontList.sort()
self.fontCombo = wxComboBox(self, -1, "", wxDefaultPosition,
wxDefaultSize, fontList,
style = wxCB_READONLY)
sizeList = ["8", "9", "10", "12", "14", "16", "18",
"20", "24", "32", "48", "72"]
self.sizeCombo = wxComboBox(self, -1, "", wxDefaultPosition,
wxDefaultSize, sizeList,
style=wxCB_READONLY)
comboSizer = wxBoxSizer(wxHORIZONTAL)
comboSizer.Add(wxStaticText(self, -1, "Font:"), 0)
comboSizer.Add(self.fontCombo)
comboSizer.Add(5, 5)
comboSizer.Add(wxStaticText(self, -1, "Size:"), 0)
comboSizer.Add(self.sizeCombo)
self.boldCheckbox = wxCheckBox(self, -1, "Bold")
self.italicCheckbox = wxCheckBox(self, -1, "Italic")
self.underlineCheckbox = wxCheckBox(self, -1, "Underline")
styleSizer = wxBoxSizer(wxHORIZONTAL)
styleSizer.Add(self.boldCheckbox, 0)
styleSizer.Add(self.italicCheckbox, 0)
styleSizer.Add(self.underlineCheckbox, 0)
self.okButton = wxButton(self, wxID_OK, "OK")
self.cancelButton = wxButton(self, wxID_CANCEL, "Cancel")
btnSizer = wxBoxSizer(wxHORIZONTAL)
btnSizer.Add(self.okButton)
btnSizer.Add(self.cancelButton)
sizer = wxBoxSizer(wxVERTICAL)
sizer.Add(self.textCtrl, 1, wxEXPAND | wxALL, 5)
sizer.Add(comboSizer, 0, wxALIGN_CENTRE | wxALL, 5)
sizer.Add(styleSizer, 0, wxALIGN_CENTRE | wxALL, 5)
sizer.Add(btnSizer, 0, wxALIGN_CENTRE | wxALL, 5)
self.SetAutoLayout(true)
self.SetSizer(sizer)
sizer.Fit(self)
sizer.SetSizeHints(self)
#----------------------------------------------------------------------------
class TextObjectValidator(wxPyValidator):
""" This validator is used to ensure that the user has entered something
into the text object editor dialog's text field.
"""
def __init__(self):
""" Standard constructor.
"""
wxPyValidator.__init__(self)
def Clone(self):
""" Standard cloner.
Note that every validator must implement the Clone() method.
"""
return TextObjectValidator()
def Validate(self, win):
""" Validate the contents of the given text control.
"""
print "Calling validate..."
textCtrl = wxPyTypeCast(win, "wxTextCtrl")
text = textCtrl.GetValue()
if len(text) == 0:
wxMessageBox("A text object must contain some text!", "Error")
return false
else:
return true
def TransferToWindow(self):
""" Transfer data from validator to window.
The default implementation returns false, indicating that an error
occurred. We simply return true, as we don't do any data transfer.
"""
return false # Prevent wxDialog from complaining.
def TransferFromWindow(self):
""" Transfer data from window to validator.
The default implementation returns false, indicating that an error
occurred. We simply return true, as we don't do any data transfer.
"""
return false # Prevent wxDialog from complaining.
#----------------------------------------------------------------------------
class TestApp(wxApp):
def OnInit(self):
testFrame = wxFrame(NULL, -1, "Test")
menu = wxMenu()
menu.Append(1001, "Open Dialog")
menuBar = wxMenuBar()
menuBar.Append(menu, "Test")
EVT_MENU(self, 1001, self.doShowDialog)
testFrame.SetMenuBar(menuBar)
testFrame.Centre()
testFrame.Show(TRUE)
return true
def doShowDialog(self, event):
editor = EditTextObjectDialog(NULL, "Edit Text Object")
result = editor.ShowModal()
#----------------------------------------------------------------------------
def main():
app = TestApp(0)
app.MainLoop()
if __name__ == "__main__":
main()
=========================== End of Python Code =============================