wxPython validators not working as expected - python

I've written a dialog in wxPtyhon with two comboboxes to which I've attached a custom validator, the purpose being to insure that, if a value is typed, it is an numeric string. Problem is, the validators aren't being invoked. What am I doing wrong?
import wx
# my custom validator class
class NumericObjectValidator(wx.Validator):
def __init__(self):
wx.Validator.__init__(self)
def Clone(self):
return NumericObjectValidator()
# Why isn't this method being called when the user types in the CB field?
def Validate(self, win):
cbCtrl = self.GetWindow()
text = cbCtrl.GetValue()
print 'control value=',text
return True
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Select Size', size=(200,135))
panel = wx.Panel(self, -1, size=self.GetClientSize())
sizes = map(str, range(3,21))
wx.StaticText(panel, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(panel, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80,15), validator=NumericObjectValidator())
wx.StaticText(panel, -1, 'Columns:', pos=(10,40))
self.cols = wx.ComboBox(panel, -1, choices=sizes, style=wx.CB_DROPDOWN,
pos=(80,40), value='8', validator=NumericObjectValidator())
cancel = wx.Button(panel,wx.ID_CANCEL,'Cancel', pos=(20,75))
wx.Button(panel,wx.ID_OK,'OK', pos=(100,75)).SetDefault()
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c,r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()

This is a quirk in wxWidgets. If the parent is not a (subclass of) wx.Dialog, you must call the methods "TransferDataToWindow" and "TransferDataFromWindow" on the wx.Window manually. Here you are using wx.Panel as parent for the combo-boxes, therefore the data transfer is not automatically invoked.

I have no idea why, but copying the wxPython demo works okay.
import wx
import string
# my custom validator class
class NumericObjectValidator(wx.PyValidator):
def __init__(self):
wx.PyValidator.__init__(self)
self.Bind(wx.EVT_CHAR, self.OnChar)
def Clone(self):
return NumericObjectValidator()
def Validate(self, win):
tc = self.GetWindow()
val = tc.GetValue()
for x in val:
if x not in string.digits:
return False
return True
def OnChar(self, event):
key = event.GetKeyCode()
if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
event.Skip()
return
if chr(key) in string.digits:
event.Skip()
return
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Select Size', size=(200,135))
panel = wx.Panel(self, -1, size=self.GetClientSize())
sizes = map(str, range(3,21))
wx.StaticText(panel, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(panel, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80,15), validator=NumericObjectValidator())
wx.StaticText(panel, -1, 'Columns:', pos=(10,40))
self.cols = wx.ComboBox(panel, -1, style=wx.CB_DROPDOWN, validator=NumericObjectValidator())
cancel = wx.Button(panel,wx.ID_CANCEL,'Cancel', pos=(20,75))
wx.Button(panel,wx.ID_OK,'OK', pos=(100,75)).SetDefault()
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c,r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()

Use PyValidator instead of Validator
the parent of your ComboBox and Button should be Dialog rather than Panel
The following version of your code shall work fine:
import wx
# my custom validator class
class NumericObjectValidator(wx.PyValidator):
def __init__(self):
wx.PyValidator.__init__(self)
def Clone(self):
return NumericObjectValidator()
# Why isn't this method being called when the user types in the CB
# field?
def Validate(self, win):
cbCtrl = self.GetWindow()
text = cbCtrl.GetValue()
print 'control value=', text
return True
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(
self, parent, -1, 'Select Size', size=(200, 135))
sizes = map(str, range(3, 21))
wx.StaticText(self, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(self, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80, 15), validator=NumericObjectValidator())
wx.StaticText(self, -1, 'Columns:', pos=(10, 40))
self.cols = wx.ComboBox(
self, -1, choices=sizes, style=wx.CB_DROPDOWN,
pos=(80, 40), value='8', validator=NumericObjectValidator())
cancel = wx.Button(self, wx.ID_CANCEL, 'Cancel', pos=(20, 75))
wx.Button(self, wx.ID_OK, 'OK', pos=(100, 75)).SetDefault()
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c, r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()

Related

Wxpython Phoenix - Multiple OnDropFiles boxes with TABS

I have a python 3 (wxpython phoenix) app which has two TABs, each TAB has a OnDropFiles box, but I just can't get it working. I have a simple stripped down example below, which should print the file URL on each drop, but not working, if someone could show a working example please, or point me in the right direcion, I would be very gratefull.
I'm using Python 3.10.5 and wxpython 4.2.
import wx
class ScrolledWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(510, 330), style=wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BORDER |
wx.MAXIMIZE_BOX))
run_params = {}
self.tabbed = wx.Notebook(self, -1, style=(wx.NB_TOP))
self.filePrep = PrepFile(self.tabbed, self, run_params)
self.fileCheck = CheckFile(self.tabbed, self, run_params)
self.tabbed.AddPage(self.filePrep, "File Prep")
self.tabbed.AddPage(self.fileCheck, "File Check")
self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.switchSize)
self.Centre()
self.Show()
def switchSize(self, e):
page = self.tabbed.GetPageText(self.tabbed.GetSelection())
if page == 'File Prep':
self.SetSize((510, 330))
elif page == 'File Check':
self.SetSize((510, 510))
self.fileCheck.setSubmissionDrop(self)
class PrepFile(wx.Panel):
def __init__(self, parent, frame, run_params):
wx.Panel.__init__(self, parent)
self.run_params = run_params
self.parent = parent
self.frame = self
self.selectedFiles = ""
outputtxt3 = '''Drag and Drop files'''
wx.StaticText(self, -1, outputtxt3, pos=(25, 180), style=wx.ALIGN_CENTRE)
self.drop_target = MyFileDropTarget(self)
self.SetDropTarget(self.drop_target)
self.tc_files = wx.TextCtrl(self, wx.ID_ANY, pos=(28, 200), size=(360, 25))
self.tc_files.SetFocus()
self.Show()
def setSubmissionDrop(self, dropFiles):
"""Called by the FileDropTarget when files are dropped"""
print(dropFiles)
self.tc_files.SetValue(','.join(dropFiles))
self.selectedFiles = dropFiles
class CheckFile(wx.Panel):
def __init__(self, parent, frame, run_params):
wx.Panel.__init__(self, parent)
self.run_params = run_params
self.parent = parent
self.frame = self
self.selectedFiles = ""
wx.StaticText(self, -1, '''Drag and Drop files''', pos=(25, 10), style=wx.ALIGN_CENTRE)
self.drop_target = MyFileDropTarget(self)
self.SetDropTarget(self.drop_target)
self.tc_files = wx.TextCtrl(self, wx.ID_ANY, pos=(25, 30), size=(302, 25))
self.tc_files.SetFocus()
self.Show()
def setSubmissionDrop(self, dropFiles):
"""Called by the FileDropTarget when files are dropped"""
print(dropFiles)
self.selectedFiles = dropFiles
class MyFileDropTarget(wx.FileDropTarget):
""""""
def __init__(self, window):
wx.FileDropTarget.__init__(self)
self.window = window
def OnDropFiles(self, x, y, filenames):
print(filenames)
self.window.setSubmissionDrop(filenames)
return True
app = wx.App()
ScrolledWindow(None, -1, 'File Prep ')
app.MainLoop()
We want this to be event driven, so you should define an event, from wx.lib.newevent.
I assume you want the drop zone to be the textctrl not the whole panel, so it's that that needs to be set as the target.
BIND the event.
In the FileDropTarget class we define the event and fire it.
In each drop event callback, we accept the event as a parameter and unpack whatever we packed into the event.
One last thing, in this case, you are getting back a list.
It's simple but every time you decide to use it, it trips you up, I don't know why.
import wx
import wx.lib.newevent
drop_event, EVT_DROP_EVENT = wx.lib.newevent.NewEvent()
class ScrolledWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(510, 330), style=wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BORDER |
wx.MAXIMIZE_BOX))
run_params = {}
self.tabbed = wx.Notebook(self, -1, style=(wx.NB_TOP))
self.filePrep = PrepFile(self.tabbed, self, run_params)
self.fileCheck = CheckFile(self.tabbed, self, run_params)
self.tabbed.AddPage(self.filePrep, "File Prep")
self.tabbed.AddPage(self.fileCheck, "File Check")
self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.switchSize)
self.Centre()
self.Show()
def switchSize(self, e):
page = self.tabbed.GetPageText(self.tabbed.GetSelection())
if page == 'File Prep':
self.SetSize((510, 330))
elif page == 'File Check':
self.SetSize((510, 510))
class PrepFile(wx.Panel):
def __init__(self, parent, frame, run_params):
wx.Panel.__init__(self, parent)
self.run_params = run_params
self.parent = parent
self.frame = self
self.selectedFiles = ""
outputtxt3 = '''Drag and Drop files'''
wx.StaticText(self, -1, outputtxt3, pos=(25, 180), style=wx.ALIGN_CENTRE)
self.tc_files = wx.TextCtrl(self, wx.ID_ANY, pos=(28, 200), size=(360, 25))
self.drop_target = MyFileDropTarget(self)
self.tc_files.SetDropTarget(self.drop_target)
self.Bind(EVT_DROP_EVENT, self.setSubmissionDrop)
self.tc_files.SetFocus()
self.Show()
def setSubmissionDrop(self, event):
"""Called by the FileDropTarget when files are dropped"""
files = event.data
print("\nprep event", files)
self.tc_files.SetValue(','.join(files))
self.selectedFiles = files
class CheckFile(wx.Panel):
def __init__(self, parent, frame, run_params):
wx.Panel.__init__(self, parent)
self.run_params = run_params
self.parent = parent
self.frame = self
self.selectedFiles = ""
wx.StaticText(self, -1, '''Drag and Drop files''', pos=(25, 10), style=wx.ALIGN_CENTRE)
self.tc_files = wx.TextCtrl(self, wx.ID_ANY, pos=(25, 30), size=(302, 25))
self.drop_target = MyFileDropTarget(self)
self.tc_files.SetDropTarget(self.drop_target)
self.Bind(EVT_DROP_EVENT, self.setSubmissionDrop)
self.tc_files.SetFocus()
self.Show()
def setSubmissionDrop(self, event):
"""Called by the FileDropTarget when files are dropped"""
files = event.data
print("\ncheck event", files)
self.tc_files.SetValue(','.join(files))
self.selectedFiles = files
class MyFileDropTarget(wx.FileDropTarget):
""""""
def __init__(self, window):
wx.FileDropTarget.__init__(self)
self.obj = window
def OnDropFiles(self, x, y, filenames):
drp_evt = drop_event(data=filenames)
wx.PostEvent(self.obj, drp_evt)
return True
app = wx.App()
ScrolledWindow(None, -1, 'File Prep ')
app.MainLoop()

write contact form in wxPython?

I have a code. When it runs, you click on add contact button and add some contact. When you press on each contact, it just shows you the last contact information you have added. But when I press on each contact, I want it to instead show me the information for the contact I have clicked. Who can help me to solve this problem?
import wx
from wx._core import RB_GROUP, TE_READONLY
import sys
import os
from _ssl import nid2obj
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.panel = wx.Panel(self, -1)
self.contactslist =[]
self.path = os.getcwd()+'\\contacts-db'
self.nc = wx.Button(self.panel, label='new contact')
self.nc.Bind(wx.EVT_BUTTON, self.newcontact)
self.displaycontacts()
self.Show(True)
def displaycontacts(self):
for roots, dirs, files in os.walk(self.path):
for filename in files:
#self.contactslist.append(filename)
self.cntct = wx.Button(self.panel, label=filename)
self.cntct.Bind(wx.EVT_BUTTON, self.clickcontacts)
def clickcontacts(self, e):
self.oc =self.path+'\\'+self.cntct.GetLabel()
self.oc2 = open(self.oc, 'r')
prt = wx.TextCtrl(self.panel, value=self.oc2.read())
def newcontact(self, e):
self.val = ''
self.f1 = wx.TextCtrl(self.panel, value='enter your first name', pos=(0,0))
self.f1.SetFocus()
self.f2 = wx.TextCtrl(self.panel, value='enter your last name', pos=(0,50))
self.rbm = wx.RadioButton(self.panel, label='male', style=RB_GROUP, pos=(0,100))
self.rbm.Bind(wx.EVT_RADIOBUTTON, self.male)
self.rbf = wx.RadioButton(self.panel, label='female')
self.rbf.Bind(wx.EVT_RADIOBUTTON, self.female)
self.btn = wx.Button(self.panel, label='show', pos=(0,200))
self.btn.Bind(wx.EVT_BUTTON, self.showform)
self.s = wx.TextCtrl(self.panel, value=self.val, style=wx.TE_MULTILINE, size=(800,800))
self.clrbtn = wx.Button(self.panel, label='clear', pos=(800,800))
self.clrbtn.Bind(wx.EVT_BUTTON, self.onclear)
self.af = wx.Button(self.panel, label='add additional field')
self.af.Bind(wx.EVT_BUTTON, self.additionalfield)
self.sv = wx.Button(self.panel, label='save')
self.sv.Bind(wx.EVT_BUTTON, self.save)
def showform(self, e):
self.s.SetValue('first name: '+self.f1.GetValue()+'\nlast name: '+self.f2.GetValue()+self.val)
def male(self, e):
self.val=''
self.val = '\ngender: male'
def female(self, e):
self.val=''
self.val='\ngender: female'
def onclear(self, e):
self.s.Clear()
self.f1.Clear()
self.f2.Clear()
def additionalfield(self, e):
self.x = wx.TextCtrl(self.panel, value='\nenter field name: ')
self.x.SetFocus()
self.y = wx.TextCtrl(self.panel, value='enter information ')
self.add = wx.Button(self.panel, label='add')
self.add.Bind(wx.EVT_BUTTON, self.additionalfieldappend)
def additionalfieldappend(self, e):
self.s.AppendText('\n'+self.x.GetValue()+': '+self.y.GetValue())
self.x.Destroy()
self.y.Destroy()
def save(self, e):
if not os.path.exists(self.path):
os.makedirs(self.path)
cnt = open(self.path+'\\'+self.f1.GetValue()+self.f2.GetValue()+'.txt', 'x')
cnt.write(self.s.GetValue())
nid2obj
app = wx.App()
frame = MyFrame(None, 'contact form')
app.MainLoop()
Everyone will develop their own style when using wxPython. As Rolf has suggested, you do not just add widgets to a frame, but you need to add a panel. I have added a basic contact add screen below. The 'New' button creates an instance of a wx.Dialog, which captures the details of a contact. These are then available to the MainFrame where you can save them if you want.
Note that I have freely created new classes in the application to handle the different areas of concern. GUI apps can become very complex, and in my opinion, there is no substitute for factoring each bit of functionality into its own method or class.
Not everything needs to be a member of the frame class.
Note also the I have used sizers and not absolute positions.
This code does not do everything that you want to do, but it might help get you started.
import wx
BORDER = 5
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.Title = 'Contacts'
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Center()
self.Show()
def new_contact(self, event):
del event
contact_add_screen = NewContact(self)
try:
contact_add_screen.ShowModal()
if contact_add_screen.state == wx.ID_OK:
print(contact_add_screen.first_name)
print(contact_add_screen.last_name)
# This is where you can save your contacts
finally:
contact_add_screen.Destroy()
def on_quit_click(self, event):
del event
wx.CallAfter(self.Destroy)
class MainPanel(wx.Panel):
"""Create a panel class to contain screen widgets."""
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
cmd_new_contact = wx.Button(self, id= wx.ID_NEW)
cmd_new_contact.Bind(wx.EVT_BUTTON, parent.new_contact)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(cmd_new_contact, flag=wx.ALL, border = BORDER)
self.SetSizer(sizer)
class NewContact(wx.Dialog):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.state = None
self.first_name = None
self.last_name = None
self.parent = parent
self.SetTitle('Add Contact')
self.panel = ContactAddPanel(self)
sizer = wx.BoxSizer()
sizer.Add(self.panel)
self.SetSizerAndFit(sizer)
def on_cmd_save_click(self, event):
del event
self.first_name = self.panel.txt_first_name.GetValue()
self.last_name = self.panel.txt_last_name.GetValue()
self.state = wx.ID_OK
self.Destroy()
def on_quit_click(self, event):
del event # unused
self.Destroy()
class ContactAddPanel(wx.Panel):
TEXT_SIZE = (450, -1)
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
contact_sizer = self._contact_sizer()
button_panel = ButtonPanel(self, wx.ID_SAVE,
self.parent.on_cmd_save_click,
self.parent.on_quit_click)
self.txt_first_name.SetFocus()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(contact_sizer)
sizer.Add(button_panel, flag=wx.EXPAND|wx.TOP, border=BORDER)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(sizer, flag=wx.ALL, border=BORDER)
self.SetSizer(main_sizer)
def _contact_sizer(self):
lbl_first_name= wx.StaticText(self, label='First name: ')
self.txt_first_name= wx.TextCtrl(self, size=self.TEXT_SIZE)
lbl_last_name = wx.StaticText(self, label='Last name:')
self.txt_last_name = wx.TextCtrl(self, size=self.TEXT_SIZE)
sizer = wx.GridBagSizer(BORDER, BORDER)
sizer.Add(lbl_first_name, pos=(0, 0), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.txt_first_name, pos=(0, 1))
sizer.Add(lbl_last_name, pos=(1, 0), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.txt_last_name, pos=(1, 1))
return sizer
class ButtonPanel(wx.Panel):
def __init__(self, parent, ok_id, ok_bind, cancel_bind, *args, **kwargs):
super(ButtonPanel, self).__init__(parent, *args, **kwargs)
self.cmd_action = wx.Button(self, ok_id)
cmd_cancel = wx.Button(self, wx.ID_CANCEL)
self.cmd_action.Bind(wx.EVT_BUTTON, ok_bind)
cmd_cancel.Bind(wx.EVT_BUTTON, cancel_bind)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.cmd_action)
sizer.Add((0, 0), proportion=1)
sizer.Add(cmd_cancel)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()

How can I resize * wx.CheckListBox* automatically in wxPython?

If the text of label for checkboxes is more than width of a wx.CheckListBox one must change its size.
The short answer is to not set the size of the CheckListBox. This will automatically set the size to the largest item. If you do set the size parameter and the text does not fit, set the style=wx.LB_HSCROLL and a horizontal slider will be created, if needed.
Below a demonstration with both versions of self.clb
import wx
class ClbView(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, None, -1, 'CheckListBox', size=(250, 200))
def createWidgets(self, list):
self.panel = wx.Panel(self, -1)
# self.clb = wx.CheckListBox(self.panel, -1, size=(100,150),choices=list,style=wx.LB_HSCROLL)
self.clb = wx.CheckListBox(self.panel, -1, choices = list)
self.btn_exit = wx.Button(self.panel, wx.ID_ANY, 'Exit')
def sizeWidgets(self):
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.clb, 0, wx.ALL|wx.CENTER, 5)
self.vbox.Add(self.btn_exit, 0, wx.CENTER)
self.panel.SetSizer(self.vbox)
self.Centre()
class ClbControl:
def __init__(self):
self.list = ['Quite a long piece of text', 'Short text', 'X']
self.createView()
def createView(self):
self.view = ClbView(None)
self.view.createWidgets(self.list)
self.view.sizeWidgets()
self.view.Show()
self.view.btn_exit.Bind(wx.EVT_BUTTON, self.onExit)
self.view.clb.Bind(wx.EVT_CHECKLISTBOX, self.onCLB )
def onCLB(self, evt):
x = int(evt.GetSelection())
print "Box ",x," Accessed"
def onExit(self, evt):
self.view.Close()
if __name__ == '__main__':
app = wx.App()
controller = ClbControl()
app.MainLoop()

What is the difference between wx.lib.newevent.NewEvent() and wx.NewEventType()?

What is the difference between using wx.lib.newevent.NewEvent() and using wx.NewEventType() with wx.PyCommandEvent()? The code sample below uses the two different ways to create events to accomplish the same thing (passing data attached to an event). When should one be used instead of the other?
import wx
import wx.lib.newevent
MyEvent, EVT_MY_EVENT = wx.lib.newevent.NewEvent()
EVT_ANOTHER_EVENT_TYPE = wx.NewEventType()
EVT_ANOTHER_EVENT = wx.PyEventBinder(EVT_ANOTHER_EVENT_TYPE, 1)
class EventTest(wx.Dialog):
def __init__(self, parent, id, title):
wx.Dialog.__init__(self, parent, id, title, size=(210, 200))
wx.Button(self, 1, 'NewEvent', (50, 10), (110, -1))
wx.Button(self, 2, 'PyCommandEvent', (50, 60), (110, -1))
wx.Button(self, 3, 'Close', (50, 110), (110, -1))
self.Bind(wx.EVT_BUTTON, self.OnNewEvent, id=1)
self.Bind(wx.EVT_BUTTON, self.OnPyCommandEvent, id=2)
self.Bind(wx.EVT_BUTTON, self.OnClose, id=3)
self.Bind(EVT_MY_EVENT, self.NewEventHandler)
self.Bind(EVT_ANOTHER_EVENT, self.PyCommandEventHandler)
self.Centre()
self.ShowModal()
self.Destroy()
def OnNewEvent(self, event):
evt = MyEvent(SomeData=("NewEvent", 11, "aaa"))
wx.PostEvent(self, evt)
def OnPyCommandEvent(self, event):
evt = wx.PyCommandEvent(EVT_ANOTHER_EVENT_TYPE, wx.ID_ANY)
evt.SetClientData(("PyCommandEvent", 22, "bbb"))
wx.PostEvent(self, evt)
def NewEventHandler(self, evt=None):
print "NewEvent Data:", evt.SomeData
def PyCommandEventHandler(self, evt=None):
print "PyCommandEvent Data:", evt.GetClientData()
def OnClose(self, event):
self.Close(True)
if __name__ == "__main__":
app = wx.App(0)
EventTest(None, -1, 'Event Test')
app.MainLoop()
A wx.lib.newevent.NewEvent() is just an easier wxpython way thats been added to make a wx.NewEventType().
if you have a look at the code in the module newevent you will see what it does.
"""Easy generation of new events classes and binder objects"""
__author__ = "Miki Tebeka <miki.tebeka#gmail.com>"
import wx
#---------------------------------------------------------------------------
def NewEvent():
"""Generate new (Event, Binder) tuple
e.g. MooEvent, EVT_MOO = NewEvent()
"""
evttype = wx.NewEventType()
class _Event(wx.PyEvent):
def __init__(self, **kw):
wx.PyEvent.__init__(self)
self.SetEventType(evttype)
self.__dict__.update(kw)
return _Event, wx.PyEventBinder(evttype)
def NewCommandEvent():
"""Generate new (CmdEvent, Binder) tuple
e.g. MooCmdEvent, EVT_MOO = NewCommandEvent()
"""
evttype = wx.NewEventType()
class _Event(wx.PyCommandEvent):
def __init__(self, id, **kw):
wx.PyCommandEvent.__init__(self, evttype, id)
self.__dict__.update(kw)
return _Event, wx.PyEventBinder(evttype, 1)

Destroy() wxpython simple error?

i am having an interesting problem,
This program is a simple image viewer and it can contain different images in a listbox. The listbox contains the names of the images. You can load an image to an item in the listbox. You can click any item on the listbox to see its image. For some reason Destroy() is not functioning properly. Please run the following code if you are unable to understand me,
IMAGE_NAME=[]
IMAGE_DATA=[]
IMAGE_LISTSEL=[]
import sys
import wx
def deletepic(self,parent):
try:
bitmap1.Destroy()
bmp1.Destroy()
except:
print sys.exc_info()
def sendnewpic(self,parent):
global scroll_img
deletepic(self,parent)
print IMAGE_DATA[IMAGE_LISTSEL[0]]
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
try:
print IMAGE_DATA[IMAGE_LISTSEL[0]]
bmp1 = wx.Image(IMAGE_DATA[IMAGE_LISTSEL[0]], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
except:
pass
def areachange(self,pg):
print pg
try:
if IMAGE_DATA[IMAGE_LISTSEL[0]]=='':
deletepic(self,parent)
except:
pass
if pg=="Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageMax(wx.Panel):
pass
class imageTab(imageMax):
def imagesel(self,parent):
IMAGE_LISTSEL[:] = []
IMAGE_LISTSEL.append(self.listBox.GetSelection())
sendnewpic(self,parent)
def newAddImage(self,parent):
IMAGE_NAME.append('hi');
IMAGE_DATA.append('');
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(self) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
class MyNotebook(wx.Notebook):
def __init__(self, *args, **kwargs):
wx.Notebook.__init__(self, *args, **kwargs)
class MyPanel(imageTab):
def OnClickTop(self, event):
scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self,self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self,parent):
""" Open a file"""
filename = wx.FileSelector()
if (filename!=''):
global bitmap1,bmp1,scroll_img
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
deletepic(self,parent)
bmp1 = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
scroll_img.SetScrollbars(1, 1, bmp1.GetWidth(), bmp1.GetHeight())
IMAGE_DATA[IMAGE_LISTSEL[0]]=filename
print IMAGE_DATA
def __init__(self, *args, **kwargs):
global bitmap1,bmp1,scroll_img
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = MyNotebook(self, size=(225, -1))
# self.button = wx.Button(self, label="Something else here? Maybe!")
tab_images = imageTab(self.notebook)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(tab_images, "Pics",select=True)
scroll_img = wx.ScrolledWindow(self, -1)
scroll_img.SetScrollbars(1, 1, 600, 400)
#self.button = wx.Button(scroll_img, -1, "Scroll Me", pos=(50, 20))
#self.Bind(wx.EVT_BUTTON, self.OnClickTop, self.button)
#self.button2 = wx.Button(scroll_img, -1, "Scroll Back", pos=(500, 350))
#self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)
self.images_area=wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area,wx.HORIZONTAL)
#self.load_file=wx.Button(self, label='Load File')
#self.sizerBox.Add(self.load_file,0,wx.ALL,5)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
# self.sizer.Add(self.button, proportion=0)
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File",size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON,self.onOpenFile)
bmp1 = None
bitmap1 = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#self.sizerBox.Add(self.bitmap1)
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self,self.notebook.GetPageText(0))
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Try this to see what the error is,
1.you press create new (any amount of times)
2.press, load file
3. click any item on the listbox (except for the one that you are in)
4. Then go back to the orginal item that you were in,
5. Then click any item, other than the one you are currently in
There will be some sort of problem, the image does not destroy itself and returns an error like the following:
(, PyDeadObjectError('The C++ part of the StaticBitmap object has been deleted, attribute access no longer allowed.',), )
I am still able to load images but the previous images do not delete.
It is hard to word this problem, if anyone can help me with this situation, it would be greatly appreciated. If you need further explanation please comment. I thank you greatly for viewing.
Here you have your code fixed to clear your current image when loading another one.
This is done basically using self.parent.bitmap.Destroy().
I modified some few things without changing the structure of your code, in order for you to recognize changes. I eliminated globals calls. Look how I also eliminated the global IMAGE_LISTSEL variable and converted it in a class attribute. That is what Robin and Fenikso were telling you. Try to do the same with IMAGE_NAME and IMAGE_DATA.
Although the code is working, it is still far from being acceptable wxpython code. You can get many examples of correctly written wxpython code in the web. If you can afford it I recommend to you wxPython in Action from Noel Rappin and Robin Dunn.
IMAGE_NAME = []
IMAGE_DATA = []
import sys
import wx
def deletepic(self):
try:
self.parent.bitmap.Destroy()
except:
print sys.exc_info()
def sendnewpic(self):
if self.parent.bitmap: deletepic(self)
if IMAGE_DATA[self.image_listsel] != '':
try:
print IMAGE_DATA[self.image_listsel]
bmp = wx.Image(IMAGE_DATA[self.image_listsel], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.parent.scroll_img.SetScrollbars(1, 1, bmp.GetWidth(), bmp.GetHeight())
self.parent.bitmap = wx.StaticBitmap(self.parent.scroll_img, -1, bmp, (0, 0))
self.parent.Refresh()
except:
pass
def areachange(self, pg):
print pg
try:
if IMAGE_DATA[self.image_listsel] == '':
deletepic(self)
except:
pass
if pg == "Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageTab(wx.Panel):
def __init__(self, parent, grandparent):
wx.Panel.__init__(self, parent)
self.parent = grandparent
self.image_listsel = 0
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
def imagesel(self, evt):
self.image_listsel = self.listBox.GetSelection()
sendnewpic(self)
def newAddImage(self, evt):
IMAGE_NAME.append('hi')
IMAGE_DATA.append('')
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(None) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
class MyPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = wx.Notebook(self, size=(225, -1))
#
self.tab_images = imageTab(self.notebook, self)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(self.tab_images, "Pics", select=True)
self.scroll_img = wx.ScrolledWindow(self, -1)
self.scroll_img.SetScrollbars(1, 1, 600, 400)
self.images_area = wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area, wx.HORIZONTAL)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(self.scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File", size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON, self.onOpenFile)
self.bmp = None
self.bitmap = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self, self.notebook.GetPageText(0))
def OnClickTop(self, event):
self.scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
self.scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self, self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self, evt):
""" Open a file"""
filename = wx.FileSelector()
if filename != '':
IMAGE_DATA[ self.tab_images.image_listsel] = filename
self.tab_images.imagesel(None)
print IMAGE_DATA
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Sometimes you are using bmp1 and bitmap1 as local variables and sometimes as globals. Since you are making multiple instances of them without saving the prior references anywhere then you are losing your references to the already existing objects. When you Destroy() them then you are only destroying the most recently created instances.
Try adding them to some sort of collection (like a list) instead and then you can access any of the items from the list when you need them later. Also try to avoid using global variables. Store your variables in the object instances that they belong to.

Categories