Mercurial > traipse_dev
comparison orpg/tools/metamenus.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4385a7d0efd1 |
---|---|
1 __author__ = "E. A. Tacao <e.a.tacao |at| estadao.com.br>" | |
2 __date__ = "21 Apr 2006, 13:15 GMT-03:00" | |
3 __version__ = "0.07" | |
4 __doc__ = """ | |
5 metamenus: classes that aim to simplify the use of menus in wxPython. | |
6 | |
7 MenuBarEx is a wx.MenuBar derived class for wxPython; | |
8 MenuEx is a wx.Menu derived class for wxPython. | |
9 | |
10 Some features: | |
11 | |
12 - Menus are created based on the indentation of items on a list. (See 'Usage' | |
13 below.) | |
14 | |
15 - Each menu item will trigger a method on the parent. The methods names may | |
16 be explicitly defined on the constructor or generated automatically. It's | |
17 also possible to define some names and let metamenus create the remaining. | |
18 | |
19 - Allows the user to enable or disable a menu item or an entire menu given | |
20 its label. | |
21 | |
22 - Supplies EVT_BEFOREMENU and EVT_AFTERMENU, events that are triggered right | |
23 before and after, respectively, the triggering of a EVT_MENU-bound method | |
24 on selection of some menu item. | |
25 | |
26 - MenuBarEx handles accelerators for numpad keys and also supports 'invisible | |
27 menu items'. | |
28 | |
29 - If your app is already i18n'd, menu items may be translated on the fly. | |
30 All you need to do is to write somewhere a .mo file containing the menu | |
31 translations. | |
32 | |
33 | |
34 MenuEx Usage: | |
35 | |
36 The MenuEx usage is similar to MenuBarEx (please see below), except that it | |
37 has an optional kwarg named show_title (boolean; controls whether the menu | |
38 title will be shown) and doesn't have the MenuBarEx's xaccel kwarg: | |
39 | |
40 MenuEx(self, menus, margin=wx.DEFAULT, show_title=True, | |
41 font=wx.NullFont, custfunc={}, i18n=True, style=0) | |
42 | |
43 | |
44 MenuBarEx Usage: | |
45 | |
46 In order to put a MenuBarEx inside a frame it's enough to do this: | |
47 MenuBarEx(self, menus) | |
48 | |
49 or you can also use some few optional keyword arguments: | |
50 MenuBarEx(self, menus, margin=wx.DEFAULT, font=wx.NullFont, | |
51 xaccel=None, custfunc={}, i18n=True, style=0) | |
52 | |
53 Arguments: | |
54 - self: The frame in question. | |
55 | |
56 - menus: A python list of 'menus', which are python lists of | |
57 'menu_entries'. Each 'menu_entry' is a python list that needs to | |
58 be in one of the following formats: | |
59 | |
60 [label] | |
61 or [label, args] | |
62 or [label, kwargs] | |
63 or [label, args, kwargs] | |
64 or [label, kwargs, args] (but please don't do this one). | |
65 | |
66 . label: (string) The text for the menu item. | |
67 | |
68 Leading whitespaces at the beginning of a label are used to | |
69 compute the indentation level of the item, which in turn is | |
70 used to determine the grouping of items. MenuBarEx determines | |
71 one indentation level for every group of two whitespaces. | |
72 | |
73 If you want this item to be a sub-item, increase its | |
74 indentation. Top-level items must have no indentation. | |
75 | |
76 Separators are items labeled with a "-" and may not have args | |
77 and kwargs. | |
78 | |
79 Menu breaks (please see the wx.MenuItem.Break docs) are items | |
80 labeled with a "/" and may not have args and kwargs. | |
81 | |
82 Accelerators are handled as usual; MenuBarEx also supports | |
83 numpad accelerators (e.g, " &Forward\tCtrl+Num 8"). | |
84 | |
85 Please refer to the wxPython docs for wx.Menu.Append for more | |
86 information about them. | |
87 | |
88 . args: (tuple) (helpString, wxItemKind) | |
89 | |
90 - helpString is an optional help string that will be shown on | |
91 the parent's status bar. If don't pass it, no help string | |
92 for this item will appear on the statusbar. | |
93 | |
94 - wxItemKind may be one of wx.ITEM_CHECK, "check", | |
95 wx.ITEM_RADIO or "radio". It is also optional; if don't pass | |
96 one, a default wx.ITEM_NORMAL will be used. | |
97 | |
98 Note that if you have to pass only one argument, you can do | |
99 either: | |
100 | |
101 args=("", wxItemKind) | |
102 or args=(helpString,) | |
103 or helpString | |
104 or wxItemKind | |
105 or (helpString) | |
106 or (wxItemKind) | |
107 | |
108 When you pass only one item, Metamenus will check if the | |
109 thing passed can be translated as an item kind (either | |
110 wx.RADIO, "radio", etc.) or not, and so will try to guess | |
111 what to do with the thing. So that if you want a status bar | |
112 showing something that could be translated as an item kind, | |
113 say, "radio", you'll have to pass both arguments: ("radio",). | |
114 | |
115 | |
116 . kwargs: (dict) wxBitmap bmpChecked, wxBitmap bmpUnchecked, | |
117 wxFont font, int width, | |
118 wxColour fgcolour, wxColour bgcolour | |
119 | |
120 These options access wx.MenuItem methods in order to change | |
121 its appearance, and might not be present on all platforms. | |
122 They are internally handled as follows: | |
123 | |
124 key: item method: | |
125 | |
126 "bmpChecked" and "bmpUnchecked" : SetBitmaps | |
127 "font" : SetFont | |
128 "margin", : SetMarginWidth | |
129 "fgColour", : SetTextColour | |
130 "bgColour", : SetBackgroundColour | |
131 | |
132 The "bmpChecked" and "bmpUnchecked" options accept a bitmap or | |
133 a callable that returns a bitmap when called. This is useful | |
134 if you created your bitmaps with encode_bitmaps.py and want to | |
135 pass something like {"bmpChecked": my_images.getSmilesBitmap}. | |
136 | |
137 Please refer to the wxPython docs for wx.MenuItem for more | |
138 information about the item methods. | |
139 | |
140 - margin: (int) a value that will be used to do a SetMargin() for each | |
141 menubar item. Please refer to the wxPython docs for | |
142 wx.MenuItem.SetMargin for more information about this. | |
143 | |
144 - font: (wx.Font) a value that will be used to do a SetFont() for | |
145 each menu item. Please refer to the wxPython docs for | |
146 wx.MenuItem.SetFont for more information about this. | |
147 | |
148 - xaccel: (MenuBarEx only) allows one to bind events to 'items' that | |
149 are not actually menu items, rather methods or functions that | |
150 are triggered when some key or combination of keys is | |
151 pressed. | |
152 | |
153 xaccel is a list of tuples (accel, function), where accel is | |
154 a string following the accelerator syntax described in the | |
155 wx.Menu.Append docs and function is the function/method to be | |
156 executed when the accelerator is triggered. | |
157 | |
158 The events are managed in the same way as MenuBarEx events. | |
159 | |
160 - custfunc: (dict) allows one to define explicitly what will be the | |
161 parent's method called on a menu event. | |
162 | |
163 By default, all parent's methods have to start with "OnMB_" | |
164 (for menubars) or "OnM_" (for menus) plus the full menu | |
165 'path'. For a 'Save' menu item inside a 'File' top menu, e.g: | |
166 | |
167 def OnMB_FileSave(self): | |
168 self.file.save() | |
169 | |
170 However, the custfunc arg allows you to pass a dict of | |
171 | |
172 {menupath: method, menupath: method, ...} | |
173 | |
174 so that if you want your File > Save menu triggering a | |
175 'onSave' method instead, you may pass | |
176 | |
177 {"FileSave": "onSave"} | |
178 or {"FileSave": self.onSave} | |
179 | |
180 as custfunc. This way, your parent's method should look like | |
181 this instead: | |
182 | |
183 def onSave(self): | |
184 self.file.save() | |
185 | |
186 You don't have to put all menu items inside custfunc. The | |
187 menupaths not found there will still trigger automatically | |
188 an OnMB_/OnM_-prefixed method. | |
189 | |
190 - i18n: (bool) Controls whether you want the items to be translated | |
191 or not. Default is True. For more info on i18n, please see | |
192 'More about i18n' below. | |
193 | |
194 - style: Please refer to the wxPython docs for wx.MenuBar/wx.Menu for | |
195 more information about this. | |
196 | |
197 | |
198 The public methods: | |
199 | |
200 The 'menu_string' arg on some of the public methods is a string that | |
201 refers to a menu item. For a File > Save menu, e. g., it may be | |
202 "OnMB_FileSave", "FileSave" or the string you passed via the custfunc | |
203 parameter (i. e., if you passed {"FileSave": "onSave"} as custfunc, the | |
204 string may also be "onSave"). | |
205 | |
206 The 'menu_string_list' arg on some of the public methods is a python list | |
207 of 'menu_string' strings described above. Please refer to the methods | |
208 themselves for more details. | |
209 | |
210 | |
211 More about i18n: | |
212 If you want to get your menu items automatically translated, you'll need | |
213 to: | |
214 | |
215 1. Create a directory named 'locale' under your app's directory, and under | |
216 the 'locale', create subdirectories named after the canonical names of | |
217 the languages you're going to use (e. g., 'pt_BR', 'es_ES', etc.) | |
218 | |
219 2. Inside each of the subdirectories, write a gettext compiled catalog file | |
220 (e. g., "my_messages.mo") containing all of the menu labels translated | |
221 to the language represented by the subdirectory. | |
222 | |
223 4. The language can be changed on the fly. Whenever you want to change the | |
224 menu language, execute these lines somewhere in your app: | |
225 | |
226 l = wx.Locale(wx.LANGUAGE_PORTUGUESE_BRAZILIAN) | |
227 l.AddCatalogLookupPathPrefix("locale") | |
228 l.AddCatalog("my_messages.mo") | |
229 self.my_menu.UpdateMenus() | |
230 | |
231 Unless you want your menus showing up in pt_BR, replace the | |
232 wx.LANGUAGE_PORTUGUESE_BRAZILIAN above by the proper language identifier. | |
233 For a list of supported identifiers please see the wxPython docs, under the | |
234 'Constants\Language identifiers' section. | |
235 | |
236 Some items may show up in the selected language even though you didn't | |
237 create a .mo file for the translations. That's because wxPython looks for | |
238 them in the wxstd.mo file placed somewhere under the wxPython tree, and | |
239 maybe wxPython already uses some of the string you are using. | |
240 | |
241 Note that if you're to distribute a standalone app the wxPython tree may | |
242 not be present, so it's a good idea to include a specific .mo file in your | |
243 package. On the other hand, if by any reason you _don't_ want the menu | |
244 items to be translated, you may pass a i18n=False kwarg to the constructor. | |
245 | |
246 You can use metamenus itself directly from a command line to help on | |
247 creating a gettext-parseable file based on the menus you wrote. For more | |
248 info about this, please see the docs for the _mmprep class. | |
249 | |
250 For more info about i18n, .mo files and gettext, please see | |
251 <http://wiki.wxpython.org/index.cgi/Internationalization>. | |
252 | |
253 | |
254 Menu bar example: | |
255 | |
256 a = [["File"], | |
257 [" New", "Creates a new file"], | |
258 [" Save"], | |
259 [" -"], | |
260 [" Preview", "Preview Document", | |
261 {"bmpChecked": images.getSmilesBitmap(), | |
262 "fgColour": wx.RED}], | |
263 [" -"], | |
264 [" Exit"]] | |
265 | |
266 b = [["Edit"], | |
267 [" Cut"], | |
268 [" Copy"], | |
269 [" Foo", "check"], | |
270 [" Bar", "check"], | |
271 [" Paste"]] | |
272 | |
273 myMenuBar = MenuBarEx(self, [a, b]) | |
274 | |
275 | |
276 Context menu example: | |
277 | |
278 a = [["Edit"], # A 'top-level' menu item is used as title; | |
279 [" Cut"], | |
280 [" Copy"], | |
281 [" Foo", "radio"], | |
282 [" Bar", "radio"], | |
283 [" Paste"]] | |
284 | |
285 myContextMenu = MenuEx(self, a) | |
286 | |
287 | |
288 If you don't want to show the title for the context menu: | |
289 | |
290 myContextMenu = MenuEx(self, a, show_title=False) | |
291 | |
292 | |
293 A very default 'File' menu example: | |
294 | |
295 [ | |
296 ['&File'], | |
297 [' &New\tCtrl+N'], | |
298 [' &Open...\tCtrl+O'], | |
299 [' &Save\tCtrl+S'], | |
300 [' Save &As...\tCtrl+Shift+S'], | |
301 [' -'], | |
302 [' Publis&h\tCtrl+Shift+P'], | |
303 [' -'], | |
304 [' &Close\tCtrl+W'], | |
305 [' C&lose All'], | |
306 [' -'], | |
307 [' E&xit\tAlt+X'] | |
308 ] | |
309 | |
310 | |
311 Known Issues: | |
312 | |
313 These are wx.Menu issues, and since metamenus doesn't/can't work around them | |
314 it's advisable to stay away from custom menus: | |
315 | |
316 - If you try to customize an item changing either its font, margin or | |
317 colours, the following issues arise: | |
318 | |
319 1. The item will appear shifted to the right when compared to default menu | |
320 items, although a GetMarginWidth() will return a default value; | |
321 2. wx.ITEM_RADIO items won't show their bullets. | |
322 | |
323 - If you try to change the bitmaps for wx.ITEM_RADIO items, the items will | |
324 ignore the 2nd bitmap passed and will always show the checked bitmap, | |
325 regardless of their state. | |
326 | |
327 | |
328 About: | |
329 | |
330 metamenus is distributed under the wxWidgets license. | |
331 | |
332 This code should meet the wxPython Coding Guidelines | |
333 <http://www.wxpython.org/codeguidelines.php> and the wxPython Style Guide | |
334 <http://wiki.wxpython.org/index.cgi/wxPython_20Style_20Guide>. | |
335 | |
336 For all kind of problems, requests, enhancements, bug reports, etc, | |
337 please drop me an e-mail. | |
338 | |
339 For updates please visit <http://j.domaindlx.com/elements28/wxpython/>. | |
340 """ | |
341 | |
342 # History: | |
343 # | |
344 # Version 0.07: | |
345 # - Applied a patch from Michele Petrazzo which now allows the values | |
346 # passed via the custfunc dictionary to be either callable objects | |
347 # or strings representing the callable objects. | |
348 # | |
349 # Version 0.06: | |
350 # - Added i18n capabilities. Running metamenus.py from the command line | |
351 # also creates a gettext-parseable file than can in turn be used to | |
352 # create .po files. | |
353 # | |
354 # - Some minor syntax fixes so that this code hopefully should meet the | |
355 # wxPython Coding Guidelines and the wxPython Style Guide. | |
356 # | |
357 # - Changed EVT_BEFOREMENU_EVENT to EVT_BEFOREMENU and EVT_AFTERMENU_EVENT | |
358 # to EVT_AFTERMENU. If your app was using them, please update it. | |
359 # | |
360 # - Fixed a test into OnMB_ that would raise an error on unicode systems; | |
361 # thanks to Michele Petrazzo for pointing this out. | |
362 # | |
363 # - Fixed the EVT_MENU binding so that the accelerators now should work | |
364 # on Linux; thanks to Michele Petrazzo for pointing this out. | |
365 # | |
366 # - Fixed a couple of bad names in the public methods (EnableMenuTop to | |
367 # EnableTopMenu, etc.) that would prevent the methods to work. | |
368 # | |
369 # - Fixed a bug that would prevent checkable items to be created when | |
370 # only a tupleless wxItemKind was passed within a menu item. | |
371 # | |
372 # - Fixed a couple of potential unicode bugs in _adjust that could arise | |
373 # if unicode objects were passed as menu items or help strings. | |
374 # | |
375 # - Changes in _sItem: _adjust now is a method of _sItem; GetPath | |
376 # substituted _walkMenu/_walkMenuBar; _sItem now finds a translated | |
377 # label when using 18n, etc. | |
378 # | |
379 # - All of the menu item strings passed to the public methods now may be | |
380 # in one of the following forms: (1) The full menu 'path' (e. g., | |
381 # "FileSave"), (2) The prefix + the full menu 'path' (e. g., | |
382 # "OnMB_FileSave"), (3) The method name passed as custfunc (e. g., if | |
383 # you passed {"FileSave": "onSave"} as custfunc, the string may also | |
384 # be "onSave"). | |
385 # | |
386 # - "bmpChecked" and "bmpUnchecked" options now may accept a bitmap or | |
387 # a callable that returns a bitmap when called. This is useful if your | |
388 # menu 'tree' is in another file and you import it _before_ your app is | |
389 # created, since BitmapFromImage can only be used if the app is already | |
390 # out there. | |
391 # | |
392 # Version 0.05: | |
393 # - Fixed the popup menu position on MenuEx. | |
394 # | |
395 # - Applied a patch from Michele Petrazzo which implemented the custfunc | |
396 # funcionality, allowing one to choose arbitrary names for methods | |
397 # called on menu events. | |
398 # | |
399 # Version 0.04: | |
400 # - Changed the OnMB_, OnM_ code so that they won't shadow AttributeErrors | |
401 # raised on parent's code. | |
402 # | |
403 # - Add the show_title kwarg to the MenuEx constructor. | |
404 # | |
405 # Version 0.03: | |
406 # - Added support for numpad accelerators; they must be passed as "Num x", | |
407 # where x may be into a [0-9] range. | |
408 # | |
409 # - Added support for wx.MenuItem.Break(); if you want a menu break, | |
410 # now you can pass a "/" on a menu entry label. | |
411 # | |
412 # - Added the EVT_BEFOREMENU_EVENT, which will be triggered right before | |
413 # the menu event. | |
414 # | |
415 # - Those who believe that wx.UPPERCASE_STUFF_IS_UGLY 8^) now can pass | |
416 # "radio" instead of wx.ITEM_RADIO, "check" instead of wx.ITEM_CHECK, and | |
417 # "normal" (or "", or even nothing at all) instead of wx.ITEM_NORMAL. | |
418 # | |
419 # - The args syntax has been extended. The previous version allowed one to | |
420 # pass either: | |
421 # | |
422 # (helpString, wxItemKind) | |
423 # or ("", wxItemKind) | |
424 # or (helpString,) | |
425 # | |
426 # Now its also possible to pass: | |
427 # | |
428 # helpString | |
429 # or wxItemKind | |
430 # or (helpString) | |
431 # or (wxItemKind) | |
432 # | |
433 # When you use this new style, Metamenus will check if the thing passed | |
434 # can be translated as an item kind (either wx.RADIO, "radio", etc.) or | |
435 # not, and so will try to guess what to do with the thing. Note that if | |
436 # you want a status bar showing something like "radio", you'll not be | |
437 # able to use this new style, but ("radio",) will still work for such | |
438 # purposes, though. | |
439 # | |
440 # - xaccel, a new kwarg available in MenuBarEx, allows one to bind events | |
441 # to 'items' that are not actually menu items, rather methods or | |
442 # functions that are triggered when some key or combination of keys is | |
443 # pressed. | |
444 # | |
445 # xaccel is a list of tuples (accel, function), where accel is a string | |
446 # following the accelerator syntax described in wx.Menu.Append docs and | |
447 # function is the function/method to be executed when the accelerator is | |
448 # triggered. | |
449 # | |
450 # The events will be managed in the same way as MenuBarEx events. IOW, | |
451 # xaccel accelerators will provide some sort of 'invisible menu items'. | |
452 # | |
453 # Version 0.02: severe code clean-up; accelerators for submenus now work. | |
454 # | |
455 # Version 0.01: initial release. | |
456 | |
457 #---------------------------------------------------------------------------- | |
458 | |
459 import wx | |
460 from wx.lib.newevent import NewEvent | |
461 | |
462 # Events -------------------------------------------------------------------- | |
463 | |
464 (MenuExBeforeEvent, EVT_BEFOREMENU) = NewEvent() | |
465 (MenuExAfterEvent, EVT_AFTERMENU) = NewEvent() | |
466 | |
467 # Constants ----------------------------------------------------------------- | |
468 | |
469 # If you're to use a different indentation level for menus, change | |
470 # _ind here. | |
471 _ind = 2 * " " | |
472 | |
473 # _sep is used internally only and is a substring that _cannot_ | |
474 # appear on any of the regular menu labels. | |
475 _sep = " @@@ " | |
476 | |
477 # If you want to use different prefixes for methods called by this | |
478 # menubar/menu, change them here. | |
479 _prefixMB = "OnMB_" | |
480 _prefixM = "OnM_" | |
481 | |
482 #---------------------------------------------------------------------------- | |
483 | |
484 class _sItem: | |
485 """ | |
486 Internal use only. This provides a structure for parsing the 'trees' | |
487 supplied in a sane way. | |
488 """ | |
489 | |
490 def __init__(self, params): | |
491 self.parent = None | |
492 self.Id = wx.NewId() | |
493 self.params = self._adjust(params) | |
494 self.children = [] | |
495 | |
496 self.Update() | |
497 | |
498 | |
499 def _adjust(self, params): | |
500 """ | |
501 This is responsible for formatting the args and kwargs for items | |
502 supplied within the 'tree'. | |
503 """ | |
504 | |
505 args = (); kwargs = {} | |
506 params = params + [None] * (3 - len(params)) | |
507 | |
508 if type(params[1]) == tuple: | |
509 args = params[1] | |
510 elif type(params[1]) in [str, unicode, int]: | |
511 args = (params[1],) | |
512 elif type(params[1]) == dict: | |
513 kwargs = params[1] | |
514 | |
515 if type(params[2]) == tuple: | |
516 args = params[2] | |
517 elif type(params[2]) in [str, unicode, int]: | |
518 args = (params[2],) | |
519 elif type(params[2]) == dict: | |
520 kwargs = params[2] | |
521 | |
522 args = list(args) + [""] * (2 - len(args)) | |
523 | |
524 # For those who believe wx.UPPERCASE_STUFF_IS_UGLY... 8^) | |
525 kind_conv = {"radio": wx.ITEM_RADIO, | |
526 "check": wx.ITEM_CHECK, | |
527 "normal": wx.ITEM_NORMAL} | |
528 | |
529 if args[0] in kind_conv.keys() + kind_conv.values(): | |
530 args = (args[1], args[0]) | |
531 | |
532 kind_conv.update({"normal": None, "": None}) | |
533 | |
534 if type(args[1]) in [str, unicode]: | |
535 kind = kind_conv.get(args[1]) | |
536 if kind is not None: | |
537 args = (args[0], kind) | |
538 else: | |
539 args = (args[0],) | |
540 | |
541 return (params[0], tuple(args), kwargs) | |
542 | |
543 | |
544 def Update(self): | |
545 # Members created/updated here: | |
546 # | |
547 # label: "&New\tCtrl+N" | |
548 # label_text: "&New" | |
549 # tlabel: "&Novo\tCtrl+N" | |
550 # tlabel_text: "&Novo" | |
551 # acc: "Ctrl+N" | |
552 # | |
553 # I'm not actually using all of them right now, but maybe I will... | |
554 | |
555 self.label = self.params[0].strip() | |
556 self.label_text = self.label.split("\t")[0].strip() | |
557 label, acc = (self.label.split("\t") + [''])[:2] | |
558 self.tlabel_text = wx.GetTranslation(label.strip()) | |
559 self.acc = acc.strip() | |
560 if self.acc: | |
561 self.tlabel = "\t".join([self.tlabel_text, self.acc]) | |
562 else: | |
563 self.tlabel = self.tlabel_text | |
564 | |
565 | |
566 def AddChild(self, Item): | |
567 Item.parent = self | |
568 self.children.append(Item) | |
569 return Item | |
570 | |
571 | |
572 def GetRealLabel(self, i18n): | |
573 if i18n: | |
574 label = self.GetLabelTranslation() | |
575 else: | |
576 label = self.GetLabel() | |
577 return label | |
578 | |
579 | |
580 def GetLabel(self): | |
581 return self.label | |
582 | |
583 | |
584 def GetLabelText(self): | |
585 return self.label_text | |
586 | |
587 | |
588 def GetLabelTranslation(self): | |
589 return self.tlabel | |
590 | |
591 | |
592 def GetLabelTextTranslation(self): | |
593 return self.tlabel_text | |
594 | |
595 | |
596 def GetAccelerator(self): | |
597 return self.acc | |
598 | |
599 | |
600 def GetId(self): | |
601 return self.Id | |
602 | |
603 | |
604 def GetParams(self): | |
605 return self.params | |
606 | |
607 | |
608 def GetParent(self): | |
609 return self.parent | |
610 | |
611 | |
612 def GetChildren(self, recursive=False): | |
613 def _walk(Item, r): | |
614 for child in Item.GetChildren(): | |
615 r.append(child) | |
616 if child.HasChildren(): | |
617 _walk(child, r) | |
618 return r | |
619 | |
620 if not recursive: | |
621 return self.children | |
622 else: | |
623 return _walk(self, []) | |
624 | |
625 | |
626 def HasChildren(self): | |
627 return bool(self.children) | |
628 | |
629 | |
630 def GetChildWithChildren(self): | |
631 def _walk(Item, r): | |
632 for child in Item.GetChildren(): | |
633 if child.HasChildren(): | |
634 r.insert(0, child); _walk(child, r) | |
635 return r | |
636 | |
637 return _walk(self, []) | |
638 | |
639 | |
640 def GetChildWithId(self, Id): | |
641 r = None | |
642 for child in self.GetChildren(True): | |
643 if child.GetId() == Id: | |
644 r = child; break | |
645 return r | |
646 | |
647 | |
648 def GetPath(self): | |
649 this = self; path = this.GetLabelText() | |
650 | |
651 while this.GetParent() is not None: | |
652 this = this.GetParent() | |
653 path = "%s %s %s" % (this.GetLabelText(), _sep, path) | |
654 | |
655 return path | |
656 | |
657 | |
658 def SetMethod(self, prefix, custfunc): | |
659 menuName = _clean(self.GetPath()) | |
660 | |
661 method_custom = custfunc.get(menuName) | |
662 method_default = prefix + menuName | |
663 | |
664 # If a custfunc was passed here, use it; otherwise we'll use a | |
665 # default method name when this menu item is selected. | |
666 self.method = method_custom or method_default | |
667 | |
668 # We also store a reference to all method names that the public | |
669 # methods can address. | |
670 self.all_methods = {method_custom: self.GetId(), | |
671 method_default: self.GetId(), | |
672 menuName: self.GetId()} | |
673 | |
674 | |
675 def GetMethod(self): | |
676 return self.method | |
677 | |
678 | |
679 def GetAllMethods(self): | |
680 return self.all_methods | |
681 | |
682 #---------------------------------------------------------------------------- | |
683 | |
684 class _acceleratorTable: | |
685 """ | |
686 Internal use only. | |
687 | |
688 The main purposes here are to provide MenuBarEx support for accelerators | |
689 unhandled by the original wxMenu implementation (currently we only handle | |
690 numpad accelerators here) and to allow user to define accelerators | |
691 (passing the kwarg xaccel on MenuBarEx.__init__) that work even though | |
692 they're not associated to a menu item. | |
693 """ | |
694 | |
695 def __init__(self, xaccel=None): | |
696 """ | |
697 Constructor. | |
698 | |
699 xaccel is a list of tuples (accel, function), where accel is a string | |
700 following the accelerator syntax described in wx.Menu.Append docs and | |
701 function is the function/method to be executed when the | |
702 accelerator is triggered. | |
703 """ | |
704 | |
705 self.entries = [] | |
706 | |
707 self.flag_conv = {"alt" : wx.ACCEL_ALT, | |
708 "shift": wx.ACCEL_SHIFT, | |
709 "ctrl" : wx.ACCEL_CTRL, | |
710 "" : wx.ACCEL_NORMAL} | |
711 | |
712 xaccel = xaccel or (); n = [] | |
713 for acc, fctn in xaccel: | |
714 flags, keyCode = self._parseEntry(acc) | |
715 if flags != None and keyCode != None: | |
716 n.append((flags, keyCode, fctn)) | |
717 self.xaccel = n | |
718 | |
719 | |
720 def _parseEntry(self, acc): | |
721 """Support for unhandled accelerators.""" | |
722 | |
723 lacc = acc.lower() | |
724 flags, keyCode = None, None | |
725 | |
726 # Process numpad keys... | |
727 if "num" in lacc: | |
728 | |
729 # flags... | |
730 if "+" in lacc: | |
731 flag = lacc.split("+")[:-1] | |
732 elif "-" in acc: | |
733 flag = lacc.split("-")[:-1] | |
734 else: | |
735 flag = [""] | |
736 | |
737 flags = 0 | |
738 for rflag in flag: | |
739 flags |= self.flag_conv[rflag.strip()] | |
740 | |
741 # keycode... | |
742 exec("keyCode = wx.WXK_NUMPAD%s" % lacc.split("num")[1].strip()) | |
743 | |
744 return flags, keyCode | |
745 | |
746 | |
747 def Convert(self, cmd, accel): | |
748 """ | |
749 Converts id and accelerator supplied into wx.AcceleratorEntry | |
750 objects. | |
751 """ | |
752 | |
753 flags, keyCode = self._parseEntry(accel) | |
754 if flags != None and keyCode != None: | |
755 self.entries.append(wx.AcceleratorEntry(flags, keyCode, cmd)) | |
756 | |
757 | |
758 def Assemble(self, MBIds): | |
759 """Assembles the wx.AcceleratorTable.""" | |
760 | |
761 for flags, keyCode, fctn in self.xaccel: | |
762 _id = wx.NewId(); MBIds[_id] = fctn | |
763 self.entries.append(wx.AcceleratorEntry(flags, keyCode, _id)) | |
764 | |
765 return MBIds, wx.AcceleratorTable(self.entries) | |
766 | |
767 #---------------------------------------------------------------------------- | |
768 | |
769 def _process_kwargs(item, kwargs, margin, font): | |
770 """ | |
771 Internal use only. This is responsible for setting font, margin and | |
772 colour for menu items. | |
773 """ | |
774 | |
775 if kwargs.has_key("bmpChecked"): | |
776 checked = kwargs["bmpChecked"] | |
777 unchecked = kwargs.get("bmpUnchecked", wx.NullBitmap) | |
778 | |
779 if callable(checked): | |
780 checked = checked() | |
781 if callable(unchecked): | |
782 unchecked = unchecked() | |
783 | |
784 item.SetBitmaps(checked, unchecked) | |
785 | |
786 kwlist = [("font", "SetFont"), | |
787 ("margin", "SetMarginWidth"), | |
788 ("fgColour", "SetTextColour"), | |
789 ("bgColour", "SetBackgroundColour")] | |
790 | |
791 for kw, m in kwlist: | |
792 if kwargs.has_key(kw): | |
793 getattr(item, m)(kwargs[kw]) | |
794 | |
795 if margin != wx.DEFAULT: | |
796 item.SetMarginWidth(margin) | |
797 | |
798 if font != wx.NullFont: | |
799 item.SetFont(font) | |
800 | |
801 return item | |
802 | |
803 #---------------------------------------------------------------------------- | |
804 | |
805 def _evolve(a): | |
806 """Internal use only. This will parse the menu 'tree' supplied.""" | |
807 | |
808 top = _sItem(a[0]); il = 0; cur = {il: top} | |
809 | |
810 for i in range(1, len(a)): | |
811 params = a[i] | |
812 level = params[0].count(_ind) - 1 | |
813 | |
814 if level > il: | |
815 il += 1; cur[il] = new_sItem | |
816 elif level < il: | |
817 il = level | |
818 | |
819 new_sItem = cur[il].AddChild(_sItem(params)) | |
820 | |
821 return top | |
822 | |
823 #---------------------------------------------------------------------------- | |
824 | |
825 def _clean(s): | |
826 """Internal use only. Removes all non-alfanumeric chars from a string.""" | |
827 | |
828 return "".join([x for x in s if x.isalnum()]) | |
829 | |
830 #---------------------------------------------------------------------------- | |
831 | |
832 def _makeMenus(wxmenus, saccel, h, k, margin, font, i18n): | |
833 """Internal use only. Creates menu items.""" | |
834 | |
835 label = h.GetRealLabel(i18n); Id = h.GetId() | |
836 args, kwargs = h.GetParams()[1:] | |
837 | |
838 if h.HasChildren(): | |
839 args = (wxmenus[h], Id, label) + args | |
840 item = wx.MenuItem(*args, **{"subMenu": wxmenus[h]}) | |
841 item = _process_kwargs(item, kwargs, margin, font) | |
842 wxmenus[k].AppendItem(item) | |
843 if saccel is not None: | |
844 saccel.Convert(item.GetId(), h.GetAccelerator()) | |
845 | |
846 else: | |
847 if label == "-": | |
848 wxmenus[k].AppendSeparator() | |
849 | |
850 elif label == "/": | |
851 wxmenus[k].Break() | |
852 | |
853 else: | |
854 args = (wxmenus[k], Id, label) + args | |
855 item = wx.MenuItem(*args) | |
856 item = _process_kwargs(item, kwargs, margin, font) | |
857 wxmenus[k].AppendItem(item) | |
858 if saccel is not None: | |
859 saccel.Convert(item.GetId(), h.GetAccelerator()) | |
860 | |
861 #---------------------------------------------------------------------------- | |
862 | |
863 class _mmprep: | |
864 """ | |
865 Generates a temporary file that can be read by gettext utilities in order | |
866 to create a .po file with strings to be translated. This class is called | |
867 when you run metamenus from the command line. | |
868 | |
869 Usage: | |
870 1. Make sure your menus are in a separate file and that the separate | |
871 file in question contain only your menus; | |
872 | |
873 2. From a command line, type: | |
874 metamenus.py separate_file outputfile | |
875 | |
876 where 'separate_file' is the python file containing the menu 'trees', | |
877 and 'outputfile' is the python-like file generated that can be parsed | |
878 by gettext utilities. | |
879 | |
880 To get a .po file containing the translatable strings, put the | |
881 'outputfile' in the app.fil list of translatable files and run the | |
882 mki18n.py script. For more info please see | |
883 <http://wiki.wxpython.org/index.cgi/Internationalization>. | |
884 """ | |
885 | |
886 def __init__(self, filename, outputfile): | |
887 """Constructor.""" | |
888 | |
889 print "Parsing %s.py..." % filename | |
890 | |
891 exec("import %s" % filename) | |
892 mod = eval(filename) | |
893 | |
894 objs = [] | |
895 for obj in dir(mod): | |
896 if type(getattr(mod, obj)) == list: | |
897 objs.append(obj) | |
898 | |
899 all_lines = [] | |
900 for obj in objs: | |
901 gerr = False; header = ["\n# Strings for '%s':\n" % obj] | |
902 err, lines = self.parseMenu(mod, obj) | |
903 if not err: | |
904 print "OK: parsed '%s'" % obj | |
905 all_lines += header + lines | |
906 else: | |
907 err, lines = self.parseMenuBar(mod, obj) | |
908 if not err: | |
909 print "OK: parsed '%s'" % obj | |
910 all_lines += header + lines | |
911 else: | |
912 gerr = True | |
913 if gerr: | |
914 print "Warning: couldn't parse '%s'" % obj | |
915 | |
916 try: | |
917 f = file("%s.py" % outputfile, "w") | |
918 f.writelines(all_lines) | |
919 f.close() | |
920 print "File %s.py succesfully written." % outputfile | |
921 | |
922 except: | |
923 print "ERROR: File %s.py was NOT written." % outputfile | |
924 raise | |
925 | |
926 | |
927 def form(self, lines): | |
928 """Removes separators and breaks and adds gettext stuff.""" | |
929 | |
930 new_lines = [] | |
931 for line in lines: | |
932 if line not in ["-", "/"]: | |
933 new_lines.append("_(" + `line` + ")\n") | |
934 return new_lines | |
935 | |
936 | |
937 def parseMenuBar(self, mod, obj): | |
938 """Tries to parse a MenuBarEx object.""" | |
939 | |
940 err = False; lines = [] | |
941 try: | |
942 for menu in getattr(mod, obj): | |
943 top = _evolve(menu) | |
944 lines.append(top.GetLabelText()) | |
945 for child in top.GetChildren(True): | |
946 lines.append(child.GetLabelText()) | |
947 except: | |
948 err = True | |
949 | |
950 return err, self.form(lines) | |
951 | |
952 | |
953 def parseMenu(self, mod, obj): | |
954 """Tries to parse a MenuEx object.""" | |
955 | |
956 err = False; lines = [] | |
957 try: | |
958 top = _evolve(getattr(mod, obj)) | |
959 lines.append(top.GetLabelText()) | |
960 for child in top.GetChildren(True): | |
961 lines.append(child.GetLabelText()) | |
962 except: | |
963 err = True | |
964 | |
965 return err, self.form(lines) | |
966 | |
967 | |
968 # MenuBarEx Main stuff ------------------------------------------------------ | |
969 | |
970 class MenuBarEx(wx.MenuBar): | |
971 def __init__(self, *args, **kwargs): | |
972 """ | |
973 Constructor. | |
974 MenuBarEx(parent, menus, margin=wx.DEFAULT, font=wx.NullFont, | |
975 xaccel=None, custfunc={}, i18n=True, style=0) | |
976 """ | |
977 | |
978 # Initializing... | |
979 self.parent, menus = args | |
980 margin = kwargs.pop("margin", wx.DEFAULT) | |
981 font = kwargs.pop("font", wx.NullFont) | |
982 xaccel = kwargs.pop("xaccel", None) | |
983 custfunc = kwargs.pop("custfunc", {}) | |
984 i18n = self.i18n = kwargs.pop("i18n", True) | |
985 | |
986 wx.MenuBar.__init__(self, **kwargs) | |
987 | |
988 # An object to handle accelerators. | |
989 self.accel = _acceleratorTable(xaccel) | |
990 | |
991 # A reference to all of the sItems involved. | |
992 tops = [] | |
993 | |
994 # For each menu... | |
995 for a in menus: | |
996 # Parse the menu 'tree' supplied. | |
997 top = _evolve(a) | |
998 | |
999 # Create these menus first... | |
1000 wxmenus = {top: wx.Menu()} | |
1001 for k in top.GetChildWithChildren(): | |
1002 wxmenus[k] = wx.Menu() | |
1003 | |
1004 # ...and append their respective children. | |
1005 for h in k.GetChildren(): | |
1006 _makeMenus(wxmenus, self.accel, h, k, margin, font, i18n) | |
1007 | |
1008 # Now append these items to the top level menu. | |
1009 for h in top.GetChildren(): | |
1010 _makeMenus(wxmenus, self.accel, h, top, margin, font, i18n) | |
1011 | |
1012 # Now append the top menu to the menubar. | |
1013 self.Append(wxmenus[top], top.GetRealLabel(i18n)) | |
1014 | |
1015 # Store a reference of this sItem. | |
1016 tops.append(top) | |
1017 | |
1018 # Now find out what are the methods that should be called upon | |
1019 # menu items selection. | |
1020 MBIds = {}; self.MBStrings = {} | |
1021 for top in tops: | |
1022 for child in top.GetChildren(True): | |
1023 child.SetMethod(_prefixMB, custfunc) | |
1024 MBIds[child.GetId()] = child | |
1025 self.MBStrings.update(child.GetAllMethods()) | |
1026 | |
1027 # It won't hurt if we get rid of a None key, if any. | |
1028 bogus = self.MBStrings.pop(None, None) | |
1029 | |
1030 # We store the position of top-level menus rather than ids because | |
1031 # wx.Menu.EnableTop uses positions... | |
1032 for i, top in enumerate(tops): | |
1033 self.MBStrings[_clean(top.GetLabelText())] = i | |
1034 MBIds[i] = top | |
1035 | |
1036 # Nice class. 8^) Will take care of this automatically. | |
1037 self.parent.SetMenuBar(self) | |
1038 self.parent.Bind(wx.EVT_MENU, self.OnMB_) | |
1039 | |
1040 # Now do something about the accelerators... | |
1041 self.MBIds, at = self.accel.Assemble(MBIds) | |
1042 self.parent.SetAcceleratorTable(at) | |
1043 | |
1044 | |
1045 def OnMB_(self, evt): | |
1046 """ | |
1047 Called on all menu events for this menu. It will in turn call | |
1048 the related method on parent, if any. | |
1049 """ | |
1050 | |
1051 try: | |
1052 attr = self.MBIds[evt.GetId()] | |
1053 | |
1054 self.OnMB_before() | |
1055 | |
1056 # Trigger everything except stuff passed via xaccel. | |
1057 if isinstance(attr, _sItem): | |
1058 attr_name = attr.GetMethod() | |
1059 | |
1060 if callable(attr_name): | |
1061 attr_name() | |
1062 elif hasattr(self.parent, attr_name) and \ | |
1063 callable(getattr(self.parent, attr_name)): | |
1064 getattr(self.parent, attr_name)() | |
1065 else: | |
1066 print "%s not found in parent." % attr_name | |
1067 | |
1068 # Trigger something passed via xaccel. | |
1069 elif callable(attr): | |
1070 attr() | |
1071 | |
1072 self.OnMB_after() | |
1073 | |
1074 except KeyError: | |
1075 # Maybe another menu was triggered elsewhere in parent. | |
1076 pass | |
1077 | |
1078 #evt.Skip() - removed. see http://www.archivum.info/comp.soft-sys.wxwindows/2008-07/msg00027.html | |
1079 | |
1080 | |
1081 def OnMB_before(self): | |
1082 """ | |
1083 If you need to execute something right before a menu event is | |
1084 triggered, you can bind the EVT_BEFOREMENU. | |
1085 """ | |
1086 | |
1087 evt = MenuExBeforeEvent(obj=self) | |
1088 wx.PostEvent(self, evt) | |
1089 | |
1090 | |
1091 def OnMB_after(self): | |
1092 """ | |
1093 If you need to execute something right after a menu event is | |
1094 triggered, you can bind the EVT_AFTERMENU. | |
1095 """ | |
1096 | |
1097 evt = MenuExAfterEvent(obj=self) | |
1098 wx.PostEvent(self, evt) | |
1099 | |
1100 | |
1101 # Public methods -------------------------------------------------------- | |
1102 | |
1103 def UpdateMenus(self): | |
1104 """ | |
1105 Call this to update menu labels whenever the current locale | |
1106 changes. | |
1107 """ | |
1108 | |
1109 if not self.i18n: | |
1110 return | |
1111 | |
1112 for k, v in self.MBIds.items(): | |
1113 # Update top-level menus | |
1114 if not v.GetParent(): | |
1115 v.Update() | |
1116 self.SetLabelTop(k, v.GetRealLabel(self.i18n)) | |
1117 # Update other menu items | |
1118 else: | |
1119 item = self.FindItemById(k) | |
1120 if item is not None: # Skip separators | |
1121 v.Update() | |
1122 self.SetLabel(k, v.GetRealLabel(self.i18n)) | |
1123 | |
1124 | |
1125 def GetMenuState(self, menu_string): | |
1126 """Returns True if a checkable menu item is checked.""" | |
1127 | |
1128 this = self.MBStrings[menu_string] | |
1129 return self.IsChecked(this) | |
1130 | |
1131 | |
1132 def SetMenuState(self, menu_string, check=True): | |
1133 """Toggles a checkable menu item checked or unchecked.""" | |
1134 | |
1135 this = self.MBStrings[menu_string] | |
1136 self.Check(this, check) | |
1137 | |
1138 | |
1139 def EnableItem(self, menu_string, enable=True): | |
1140 """Enables or disables a menu item via its label.""" | |
1141 | |
1142 this = self.MBStrings[menu_string] | |
1143 self.Enable(this, enable) | |
1144 | |
1145 | |
1146 def EnableItems(self, menu_string_list, enable=True): | |
1147 """Enables or disables menu items via a list of labels.""" | |
1148 | |
1149 for menu_string in menu_string_list: | |
1150 self.EnableItem(menu_string, enable) | |
1151 | |
1152 | |
1153 def EnableTopMenu(self, menu_string, enable=True): | |
1154 """Enables or disables a top level menu via its label.""" | |
1155 | |
1156 this = self.MBStrings[menu_string] | |
1157 self.EnableTop(this, enable) | |
1158 | |
1159 | |
1160 def EnableTopMenus(self, menu_string_list, enable=True): | |
1161 """Enables or disables top level menus via a list of labels.""" | |
1162 | |
1163 for menu_string in menu_string_list: | |
1164 self.EnableTopMenu(menu_string, enable) | |
1165 | |
1166 | |
1167 # MenuEx Main stuff --------------------------------------------------------- | |
1168 | |
1169 class MenuEx(wx.Menu): | |
1170 def __init__(self, *args, **kwargs): | |
1171 """ | |
1172 Constructor. | |
1173 | |
1174 MenuEx(parent, menu, margin=wx.DEFAULT, font=wx.NullFont, | |
1175 show_title=True, custfunc={}, i18n=True, style=0) | |
1176 """ | |
1177 | |
1178 # Initializing... | |
1179 self.parent, menu = args | |
1180 margin = kwargs.pop("margin", wx.DEFAULT) | |
1181 font = kwargs.pop("font", wx.NullFont) | |
1182 show_title = kwargs.pop("show_title", True) | |
1183 custfunc = kwargs.pop("custfunc", {}) | |
1184 i18n = self.i18n = kwargs.pop("i18n", True) | |
1185 | |
1186 wx.Menu.__init__(self, **kwargs) | |
1187 | |
1188 self._title = menu[0][0] | |
1189 if show_title: | |
1190 if i18n: | |
1191 self.SetTitle(wx.GetTranslation(self._title)) | |
1192 else: | |
1193 self.SetTitle(self._title) | |
1194 | |
1195 # Parse the menu 'tree' supplied. | |
1196 top = _evolve(menu) | |
1197 | |
1198 # Create these menus first... | |
1199 wxmenus = {top: self} | |
1200 for k in top.GetChildWithChildren(): | |
1201 wxmenus[k] = wx.Menu() | |
1202 | |
1203 # ...and append their respective children. | |
1204 for h in k.GetChildren(): | |
1205 _makeMenus(wxmenus, None, h, k, margin, font, i18n) | |
1206 | |
1207 # Now append these items to the top level menu. | |
1208 for h in top.GetChildren(): | |
1209 _makeMenus(wxmenus, None, h, top, margin, font, i18n) | |
1210 | |
1211 # Now find out what are the methods that should be called upon | |
1212 # menu items selection. | |
1213 self.MenuIds = {}; self.MenuStrings = {}; self.MenuList = [] | |
1214 for child in top.GetChildren(True): | |
1215 Id = child.GetId(); item = self.FindItemById(Id) | |
1216 if item: | |
1217 child.SetMethod(_prefixM, custfunc) | |
1218 self.MenuIds[Id] = child | |
1219 self.MenuStrings.update(child.GetAllMethods()) | |
1220 self.MenuList.append([Id, child.GetPath()]) | |
1221 | |
1222 # Initialize menu states. | |
1223 self.MenuState = {} | |
1224 for Id in self.MenuIds.keys(): | |
1225 if self.FindItemById(Id).IsCheckable(): | |
1226 is_checked = self.IsChecked(Id) | |
1227 else: | |
1228 is_checked = False | |
1229 self.MenuState[Id] = is_checked | |
1230 | |
1231 # Nice class. 8^) Will take care of this automatically. | |
1232 self.parent.Bind(wx.EVT_MENU, self.OnM_) | |
1233 | |
1234 | |
1235 def _update(self, i): | |
1236 def _resetRadioGroup(i): | |
1237 g = []; n = [] | |
1238 | |
1239 for Id, s in self.MenuList: | |
1240 item = self.FindItemById(Id) | |
1241 if item.GetKind() == wx.ITEM_RADIO: | |
1242 g.append(Id) | |
1243 else: | |
1244 g.append(None) | |
1245 | |
1246 for x in range(g.index(i), 0, -1): | |
1247 if g[x] != None: | |
1248 n.append(g[x]) | |
1249 else: | |
1250 break | |
1251 | |
1252 for x in range(g.index(i) + 1, len(g)): | |
1253 if g[x] != None: | |
1254 n.append(g[x]) | |
1255 else: | |
1256 break | |
1257 | |
1258 for i in n: | |
1259 self.MenuState[i] = False | |
1260 | |
1261 kind = self.FindItemById(i).GetKind() | |
1262 | |
1263 if kind == wx.ITEM_CHECK: | |
1264 self.MenuState[i] = not self.IsChecked(i) | |
1265 | |
1266 elif kind == wx.ITEM_RADIO: | |
1267 _resetRadioGroup(i) | |
1268 self.MenuState[i] = True | |
1269 | |
1270 | |
1271 def OnM_(self, evt): | |
1272 """ | |
1273 Called on all menu events for this menu. It will in turn call | |
1274 the related method on parent, if any. | |
1275 """ | |
1276 | |
1277 try: | |
1278 attr = self.MenuIds[evt.GetId()] | |
1279 | |
1280 self.OnM_before() | |
1281 | |
1282 if isinstance(attr, _sItem): | |
1283 attr_name = attr.GetMethod() | |
1284 | |
1285 if callable(attr_name): | |
1286 attr_name() | |
1287 elif hasattr(self.parent, attr_name) and \ | |
1288 callable(getattr(self.parent, attr_name)): | |
1289 getattr(self.parent, attr_name)() | |
1290 else: | |
1291 print "%s not found in parent." % attr_name | |
1292 | |
1293 self.OnM_after() | |
1294 | |
1295 except KeyError: | |
1296 # Maybe another menu was triggered elsewhere in parent. | |
1297 pass | |
1298 | |
1299 #evt.Skip() - removed. see http://www.archivum.info/comp.soft-sys.wxwindows/2008-07/msg00027.html | |
1300 | |
1301 | |
1302 def OnM_before(self): | |
1303 """ | |
1304 If you need to execute something right before a menu event is | |
1305 triggered, you can bind the EVT_BEFOREMENU. | |
1306 """ | |
1307 | |
1308 evt = MenuExBeforeEvent(obj=self) | |
1309 wx.PostEvent(self, evt) | |
1310 | |
1311 | |
1312 def OnM_after(self): | |
1313 """ | |
1314 If you need to execute something right after a menu event is | |
1315 triggered, you can bind the EVT_AFTERMENU. | |
1316 """ | |
1317 | |
1318 evt = MenuExAfterEvent(obj=self) | |
1319 wx.PostEvent(self, evt) | |
1320 | |
1321 | |
1322 # Public methods -------------------------------------------------------- | |
1323 | |
1324 def UpdateMenus(self): | |
1325 """ | |
1326 Call this to update menu labels whenever the current locale | |
1327 changes. | |
1328 """ | |
1329 | |
1330 if not self.i18n: | |
1331 return | |
1332 | |
1333 for k, v in MenuIds.items(): | |
1334 item = self.FindItemById(k) | |
1335 if item is not None: # Skip separators | |
1336 v.Update() | |
1337 self.SetLabel(k, v.GetRealLabel(self.i18n)) | |
1338 | |
1339 | |
1340 def Popup(self, evt): | |
1341 """Pops this menu up.""" | |
1342 | |
1343 [self.Check(i, v) for i, v in self.MenuState.items() \ | |
1344 if self.FindItemById(i).IsCheckable()] | |
1345 | |
1346 obj = evt.GetEventObject(); pos = evt.GetPosition() | |
1347 obj.PopupMenu(self, pos) | |
1348 | |
1349 | |
1350 def GetItemState(self, menu_string): | |
1351 """Returns True if the item is checked.""" | |
1352 | |
1353 this = self.MenuStrings[menu_string] | |
1354 return self.IsChecked(this) | |
1355 | |
1356 | |
1357 def SetItemState(self, menu_string, check): | |
1358 """Toggles a checkable menu item checked or unchecked.""" | |
1359 | |
1360 this = self.MenuStrings[menu_string] | |
1361 self.MenuState[this] = check | |
1362 | |
1363 | |
1364 def EnableItem(self, menu_string, enable=True): | |
1365 """Enables or disables a menu item via its label.""" | |
1366 | |
1367 this = self.MenuStrings[menu_string] | |
1368 self.Enable(this, enable) | |
1369 | |
1370 | |
1371 def EnableItems(self, menu_string_list, enable=True): | |
1372 """Enables or disables menu items via a list of labels.""" | |
1373 | |
1374 for menu_string in menu_string_list: | |
1375 self.EnableItem(menu_string, enable) | |
1376 | |
1377 | |
1378 def EnableAllItems(self, enable=True): | |
1379 """Enables or disables all menu items.""" | |
1380 | |
1381 for Id in self.MenuIds.keys(): | |
1382 self.Enable(Id, enable) | |
1383 | |
1384 #---------------------------------------------------------------------------- | |
1385 | |
1386 if __name__ == "__main__": | |
1387 import sys, os.path | |
1388 args = sys.argv[1:] | |
1389 if len(args) == 2: | |
1390 _mmprep(*[os.path.splitext(arg)[0] for arg in args]) | |
1391 else: | |
1392 print """ | |
1393 ----------------------------------------------------------------------------- | |
1394 metamenus %s | |
1395 | |
1396 %s | |
1397 %s | |
1398 Distributed under the wxWidgets license. | |
1399 ----------------------------------------------------------------------------- | |
1400 | |
1401 Usage: | |
1402 ------ | |
1403 | |
1404 metamenus.py separate_file outputfile | |
1405 | |
1406 - 'separate_file' is the python file containing the menu 'trees'; | |
1407 - 'outputfile' is the output file generated that can be parsed by the gettext | |
1408 utilities. | |
1409 | |
1410 Please see metamenus.__doc__ (under the 'More about i18n' section) and | |
1411 metamenus._mmprep.__doc__ for more details. | |
1412 ----------------------------------------------------------------------------- | |
1413 """ % (__version__, __author__, __date__) | |
1414 | |
1415 | |
1416 # | |
1417 ## | |
1418 ### eof |