]>
Commit | Line | Data |
---|---|---|
1 | # 12/09/2003 - Jeff Grimmett (grimmtooth@softhome.net) | |
2 | # | |
3 | # o Updated for wx namespace | |
4 | # | |
5 | # 12/18/2003 - Jeff Grimmett (grimmtooth@softhome.net) | |
6 | # | |
7 | # o wxScrolledMessageDialog -> ScrolledMessageDialog | |
8 | # | |
9 | ||
10 | import re | |
11 | import wx | |
12 | ||
13 | class Layoutf(wx.LayoutConstraints): | |
14 | """ | |
15 | The class Layoutf(wxLayoutConstraints) presents a simplification | |
16 | of the wxLayoutConstraints syntax. The name Layoutf is choosen | |
17 | because of the similarity with C's printf function. | |
18 | ||
19 | Quick Example: | |
20 | ||
21 | lc = Layoutf('t=t#1;l=r10#2;r!100;h%h50#1', (self, self.panel)) | |
22 | ||
23 | is equivalent to | |
24 | ||
25 | lc = wxLayoutContraints() | |
26 | lc.top.SameAs(self, wxTop) | |
27 | lc.left.SameAs(self.panel, wxRight, 10) | |
28 | lc.right.Absolute(100) | |
29 | lc.height.PercentOf(self, wxHeight, 50) | |
30 | ||
31 | Usage: | |
32 | ||
33 | You can give a constraint string to the Layoutf constructor, | |
34 | or use the 'pack' method. The following are equivalent: | |
35 | ||
36 | lc = Layoutf('t=t#1;l=r#2;r!100;h%h50#1', (self, self.panel)) | |
37 | ||
38 | and | |
39 | ||
40 | lc = Layoutf() | |
41 | lc.pack('t=t#1;l=r#2;r!100;h%h50#1', (self, self.panel)) | |
42 | ||
43 | Besides 'pack' there's also 'debug_pack' which does not set | |
44 | constraints, but prints traditional wxLayoutConstraint calls to | |
45 | stdout. | |
46 | ||
47 | The calls to the Layoutf constructor and pack methods have | |
48 | the following argument list: | |
49 | ||
50 | (constraint_string, objects_tuple) | |
51 | ||
52 | Constraint String syntax: | |
53 | ||
54 | Constraint directives are separated by semi-colons. You | |
55 | generally (always?) need four directives to completely describe a | |
56 | subwindow's location. | |
57 | ||
58 | A single directive has either of the following forms: | |
59 | ||
60 | 1. <own attribute><compare operation>[numerical argument] | |
61 | for example r!100 -> lc.right.Absolute(100) ) | |
62 | and w* -> lc.width.AsIs() | |
63 | ||
64 | 2. <own attribute><compare operation>[numerical argument] | |
65 | #<compare object nr.> | |
66 | for example t_10#2 (lc.top.Below(<second obj>, 10) | |
67 | ||
68 | 3. <own attribute><compare operation><compare attribute> | |
69 | [numerical argument]#<compare object nr.> | |
70 | for example w%h50#2 ( lc.width.PercentOf(<second obj>, | |
71 | wxHeight, 50) and t=b#1 ( lc.top.SameAs(<first obj>, | |
72 | wxBottom) ) | |
73 | ||
74 | Which one you need is defined by the <compare operation> | |
75 | type. The following take type 1 (no object to compare with): | |
76 | ||
77 | '!': 'Absolute', '?': 'Unconstrained', '*': 'AsIs' | |
78 | ||
79 | These take type 2 (need to be compared with another object) | |
80 | ||
81 | '<': 'LeftOf', '>': 'RightOf', '^': 'Above', '_': 'Below' | |
82 | ||
83 | These take type 3 (need to be compared to another object | |
84 | attribute) | |
85 | ||
86 | '=': 'SameAs', '%': 'PercentOf' | |
87 | ||
88 | For all types, the <own attribute> letter can be any of | |
89 | ||
90 | 't': 'top', 'l': 'left', 'b': 'bottom', | |
91 | 'r': 'right', 'h': 'height', 'w': 'width', | |
92 | 'x': 'centreX', 'y': 'centreY' | |
93 | ||
94 | If the operation takes an (optional) numerical argument, place it | |
95 | in [numerical argument]. For type 3 directives, the <compare | |
96 | attribute> letter can be any of | |
97 | ||
98 | 't': 'wxTop', 'l': 'wxLeft', 'b': 'wxBottom' | |
99 | 'r': 'wxRight', 'h': 'wxHeight', 'w': 'wxWidth', | |
100 | 'x': 'wxCentreX', 'y': 'wxCentreY' | |
101 | ||
102 | Note that these are the same letters as used for <own attribute>, | |
103 | so you'll only need to remember one set. Finally, the object | |
104 | whose attribute is refered to, is specified by #<compare object | |
105 | nr>, where <compare object nr> is the 1-based (stupid, I know, | |
106 | but I've gotten used to it) index of the object in the | |
107 | objects_tuple argument. | |
108 | ||
109 | Bugs: | |
110 | ||
111 | Not entirely happy about the logic in the order of arguments | |
112 | after the <compare operation> character. | |
113 | ||
114 | Not all wxLayoutConstraint methods are included in the | |
115 | syntax. However, the type 3 directives are generally the most | |
116 | used. Further excuse: wxWindows layout constraints are at the | |
117 | time of this writing not documented. | |
118 | ||
119 | """ | |
120 | ||
121 | attr_d = { 't': 'top', 'l': 'left', 'b': 'bottom', | |
122 | 'r': 'right', 'h': 'height', 'w': 'width', | |
123 | 'x': 'centreX', 'y': 'centreY' } | |
124 | op_d = { '=': 'SameAs', '%': 'PercentOf', '<': 'LeftOf', | |
125 | '>': 'RightOf', '^': 'Above', '_': 'Below', | |
126 | '!': 'Absolute', '?': 'Unconstrained', '*': 'AsIs' } | |
127 | cmp_d = { 't': 'wx.Top', 'l': 'wx.Left', 'b': 'wx.Bottom', | |
128 | 'r': 'wx.Right', 'h': 'wx.Height', 'w': 'wx.Width', | |
129 | 'x': 'wx.CentreX', 'y': 'wx.CentreY' } | |
130 | ||
131 | rexp1 = re.compile('^\s*([tlrbhwxy])\s*([!\?\*])\s*(\d*)\s*$') | |
132 | rexp2 = re.compile('^\s*([tlrbhwxy])\s*([=%<>^_])\s*([tlrbhwxy]?)\s*(\d*)\s*#(\d+)\s*$') | |
133 | ||
134 | def __init__(self,pstr=None,winlist=None): | |
135 | wx.LayoutConstraints.__init__(self) | |
136 | if pstr: | |
137 | self.pack(pstr,winlist) | |
138 | ||
139 | def pack(self, pstr, winlist): | |
140 | pstr = pstr.lower() | |
141 | for item in pstr.split(';'): | |
142 | m = self.rexp1.match(item) | |
143 | if m: | |
144 | g = list(m.groups()) | |
145 | attr = getattr(self, self.attr_d[g[0]]) | |
146 | func = getattr(attr, self.op_d[g[1]]) | |
147 | if g[1] == '!': | |
148 | func(int(g[2])) | |
149 | else: | |
150 | func() | |
151 | continue | |
152 | m = self.rexp2.match(item) | |
153 | if not m: raise ValueError | |
154 | g = list(m.groups()) | |
155 | attr = getattr(self, self.attr_d[g[0]]) | |
156 | func = getattr(attr, self.op_d[g[1]]) | |
157 | if g[3]: g[3] = int(g[3]) | |
158 | else: g[3] = None; | |
159 | g[4] = int(g[4]) - 1 | |
160 | if g[1] in '<>^_': | |
161 | if g[3]: func(winlist[g[4]], g[3]) | |
162 | else: func(winlist[g[4]]) | |
163 | else: | |
164 | cmp = eval(self.cmp_d[g[2]]) | |
165 | if g[3]: func(winlist[g[4]], cmp, g[3]) | |
166 | else: func(winlist[g[4]], cmp) | |
167 | ||
168 | def debug_pack(self, pstr, winlist): | |
169 | pstr = pstr.lower() | |
170 | for item in pstr.split(';'): | |
171 | m = self.rexp1.match(item) | |
172 | if m: | |
173 | g = list(m.groups()) | |
174 | attr = getattr(self, self.attr_d[g[0]]) | |
175 | func = getattr(attr, self.op_d[g[1]]) | |
176 | if g[1] == '!': | |
177 | print "%s.%s.%s(%s)" % \ | |
178 | ('self',self.attr_d[g[0]],self.op_d[g[1]],g[2]) | |
179 | else: | |
180 | print "%s.%s.%s()" % \ | |
181 | ('self',self.attr_d[g[0]],self.op_d[g[1]]) | |
182 | continue | |
183 | m = self.rexp2.match(item) | |
184 | if not m: raise ValueError | |
185 | g = list(m.groups()) | |
186 | if g[3]: g[3] = int(g[3]) | |
187 | else: g[3] = 0; | |
188 | g[4] = int(g[4]) - 1 | |
189 | if g[1] in '<>^_': | |
190 | if g[3]: print "%s.%s.%s(%s,%d)" % \ | |
191 | ('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]], | |
192 | g[3]) | |
193 | else: print "%s.%s.%s(%s)" % \ | |
194 | ('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]]) | |
195 | else: | |
196 | if g[3]: print "%s.%s.%s(%s,%s,%d)" % \ | |
197 | ('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]], | |
198 | self.cmp_d[g[2]],g[3]) | |
199 | else: print "%s.%s.%s(%s,%s)" % \ | |
200 | ('self',self.attr_d[g[0]],self.op_d[g[1]],winlist[g[4]], | |
201 | self.cmp_d[g[2]]) | |
202 | ||
203 | if __name__=='__main__': | |
204 | ||
205 | class TestLayoutf(wx.Frame): | |
206 | def __init__(self, parent): | |
207 | wx.Frame.__init__(self, parent, -1, 'Test Layout Constraints', | |
208 | wx.DefaultPosition, (500, 300)) | |
209 | self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) | |
210 | ||
211 | self.SetAutoLayout(True) | |
212 | ||
213 | self.panelA = wx.Window(self, -1, style=wx.SIMPLE_BORDER) | |
214 | self.panelA.SetBackgroundColour(wx.BLUE) | |
215 | self.panelA.SetConstraints(Layoutf('t=t10#1;l=l10#1;b=b10#1;r%r50#1',(self,))) | |
216 | ||
217 | self.panelB = wx.Window(self, -1, style=wx.SIMPLE_BORDER) | |
218 | self.panelB.SetBackgroundColour(wx.RED) | |
219 | self.panelB.SetConstraints(Layoutf('t=t10#1;r=r10#1;b%b30#1;l>10#2', (self,self.panelA))) | |
220 | ||
221 | self.panelC = wx.Window(self, -1, style=wx.SIMPLE_BORDER) | |
222 | self.panelC.SetBackgroundColour(wx.WHITE) | |
223 | self.panelC.SetConstraints(Layoutf('t_10#3;r=r10#1;b=b10#1;l>10#2', (self,self.panelA,self.panelB))) | |
224 | ||
225 | b = wx.Button(self.panelA, -1, ' About: ') | |
226 | b.SetConstraints(Layoutf('X=X#1;Y=Y#1;h*;w%w50#1', (self.panelA,))) | |
227 | self.Bind(wx.EVT_BUTTON, self.OnAbout, b) | |
228 | ||
229 | b = wx.Button(self.panelB, 100, ' Panel B ') | |
230 | b.SetConstraints(Layoutf('t=t2#1;r=r4#1;h*;w*', (self.panelB,))) | |
231 | ||
232 | self.panelD = wx.Window(self.panelC, -1, style=wx.SIMPLE_BORDER) | |
233 | self.panelD.SetBackgroundColour(wx.GREEN) | |
234 | self.panelD.SetConstraints(Layoutf('b%h50#1;r%w50#1;h=h#2;w=w#2', (self.panelC, b))) | |
235 | ||
236 | b = wx.Button(self.panelC, -1, ' Panel C ') | |
237 | b.SetConstraints(Layoutf('t_#1;l>#1;h*;w*', (self.panelD,))) | |
238 | self.Bind(wx.EVT_BUTTON, self.OnButton, b) | |
239 | ||
240 | wx.StaticText(self.panelD, -1, "Panel D", (4, 4)).SetBackgroundColour(wx.GREEN) | |
241 | ||
242 | def OnButton(self, event): | |
243 | self.Close(True) | |
244 | ||
245 | def OnAbout(self, event): | |
246 | import wx.lib.dialogs | |
247 | msg = wx.lib.dialogs.ScrolledMessageDialog(self, Layoutf.__doc__, "about") | |
248 | msg.ShowModal() | |
249 | msg.Destroy() | |
250 | ||
251 | def OnCloseWindow(self, event): | |
252 | self.Destroy() | |
253 | ||
254 | class TestApp(wx.App): | |
255 | def OnInit(self): | |
256 | frame = TestLayoutf(None) | |
257 | frame.Show(1) | |
258 | self.SetTopWindow(frame) | |
259 | return 1 | |
260 | ||
261 | app = TestApp(0) | |
262 | app.MainLoop() | |
263 | ||
264 | ||
265 | ||
266 | ||
267 |