]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | import wx | |
3 | import wx.html as wxhtml | |
4 | ||
5 | #---------------------------------------------------------------------- | |
6 | ||
7 | BTN1 = wx.NewId() | |
8 | BTN2 = wx.NewId() | |
9 | ||
10 | class TestPanel(wx.Panel): | |
11 | def __init__(self, parent, log): | |
12 | wx.Panel.__init__(self, parent, -1) | |
13 | self.log = log | |
14 | ||
15 | sizer = wx.BoxSizer(wx.VERTICAL) | |
16 | html = wxhtml.HtmlWindow(self, -1) | |
17 | html.SetPage(overview) | |
18 | sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5) | |
19 | ||
20 | btns = wx.BoxSizer(wx.HORIZONTAL) | |
21 | btns.Add((50, -1), 1, wx.EXPAND) | |
22 | btn1 = wx.Button(self, BTN1, "Find My Alter-ego") # don't save a ref to this one | |
23 | btns.Add(btn1) | |
24 | btns.Add((50, -1), 1, wx.EXPAND) | |
25 | self.btn2 = wx.Button(self, BTN2, "Find Myself") | |
26 | btns.Add(self.btn2) | |
27 | btns.Add((50, -1), 1, wx.EXPAND) | |
28 | ||
29 | sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 15) | |
30 | ||
31 | self.SetSizer(sizer) | |
32 | self.SetAutoLayout(True) | |
33 | ||
34 | self.sizer = sizer # save it for testing later | |
35 | ||
36 | self.Bind(wx.EVT_BUTTON, self.OnFindButton1, id=BTN1) | |
37 | self.Bind(wx.EVT_BUTTON, self.OnFindButton2, id=BTN2) | |
38 | ||
39 | ||
40 | def OnFindButton1(self, evt): | |
41 | win = self.FindWindowById(BTN1) | |
42 | ||
43 | if win is None: | |
44 | self.log.write("***** OOPS! None returned...\n") | |
45 | return | |
46 | ||
47 | className = win.__class__.__name__ | |
48 | ||
49 | if className in ["Button", "ButtonPtr"]: | |
50 | self.log.write("The types are the same! <grin>\n") | |
51 | else: | |
52 | self.log.write("Got %s, expected wxButton or wxButtonPtr\n" % className) | |
53 | ||
54 | ||
55 | ||
56 | def OnFindButton2(self, evt): | |
57 | win = self.FindWindowById(BTN2) | |
58 | ||
59 | if win is None: | |
60 | self.log.write("***** OOPS! None returned...\n") | |
61 | return | |
62 | ||
63 | if win is self.btn2: | |
64 | self.log.write("The objects are the same! <grin>\n") | |
65 | else: | |
66 | self.log.write("The objects are NOT the same! <frown>\n") | |
67 | ||
68 | win = evt.GetEventObject() | |
69 | ||
70 | if win is None: | |
71 | self.log.write("***** OOPS! None returned...\n") | |
72 | return | |
73 | ||
74 | if win is self.btn2: | |
75 | self.log.write("The objects are the same! <grin>\n") | |
76 | else: | |
77 | self.log.write("The objects are NOT the same! <frown>\n") | |
78 | ||
79 | sizer = self.GetSizer() | |
80 | ||
81 | if sizer is None: | |
82 | self.log.write("***** OOPS! None returned...\n") | |
83 | return | |
84 | ||
85 | if sizer is self.sizer: | |
86 | self.log.write("The objects are the same! <grin>\n") | |
87 | else: | |
88 | self.log.write("The objects are NOT the same! <frown>\n") | |
89 | ||
90 | ||
91 | #---------------------------------------------------------------------- | |
92 | ||
93 | def runTest(frame, nb, log): | |
94 | win = TestPanel(nb, log) | |
95 | return win | |
96 | ||
97 | #---------------------------------------------------------------------- | |
98 | ||
99 | ||
100 | overview = """\ | |
101 | <html><body> | |
102 | <h2>Original Object Return</h2> | |
103 | ||
104 | <p>Several methods in wxWindows return pointers to base class objects, | |
105 | when in fact the actual object pointed to is of a derived type. Since | |
106 | SWIG isn't able to tell the actual type it just creates a new Python | |
107 | shadow object of the base type to wrap around the base type pointer | |
108 | and returns it. | |
109 | ||
110 | <p>In wxPython prior to 2.3.0 this could cause annoying issues. For | |
111 | example if you called: | |
112 | ||
113 | <pre> | |
114 | ||
115 | myText = someWindow.FindWindowById(txtID) | |
116 | </pre> | |
117 | ||
118 | expecting to get a wxTextCtrl you would actually get a wxWindow object | |
119 | instead. If you then try to call SetValue on that object you'll get | |
120 | an exception since there is no such method. This is the reason for | |
121 | the wxPyTypeCast hack that has been in wxPython for so long. | |
122 | ||
123 | <p>Even with wxPyTypeCast there was the issue that the object returned | |
124 | was not the same one that was created in Python originally, but a new | |
125 | object of the same type that wraps the same C++ pointer. If the | |
126 | programmer has set additional attributes of that original object they | |
127 | will not exist in the new object. | |
128 | ||
129 | <p>For a long time now I have wanted to do away with wxPyTypeCast and | |
130 | also find a way to return the original Python object from methods like | |
131 | FindWindowById. This project naturally divides into two phases: | |
132 | ||
133 | <p><ol> | |
134 | ||
135 | <li>Teach the wrapper methods how to return objects of the right type, | |
136 | and be able to then turn wxPyTypeCast in to a no-op. | |
137 | ||
138 | <li>Be able to return the original Python shadow object if it still exists. | |
139 | ||
140 | </ol> | |
141 | ||
142 | <p>The first button below shows the first of these phases (<i>working</i>) | |
143 | and the second will show #2 (<i>working as of Python 2.3.2</i>) | |
144 | ||
145 | </body></html> | |
146 | """ | |
147 | ||
148 | ||
149 | ||
150 | ||
151 | if __name__ == '__main__': | |
152 | import sys,os | |
153 | import run | |
154 | run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) | |
155 |