WidgetPropertiesDialog.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. """Undocumented Module"""
  2. __all__ = ['WidgetPropertiesDialog']
  3. from direct.showbase.TkGlobal import *
  4. from Tkinter import *
  5. import types, string, Pmw
  6. """
  7. TODO:
  8. Checkboxes for None?
  9. Floaters to adjust float values
  10. OK and Cancel to allow changes to be delayed
  11. Something other than Return to accept a new value
  12. """
  13. class WidgetPropertiesDialog(Toplevel):
  14. """Class to open dialogs to adjust widget properties."""
  15. def __init__(self, propertyDict, propertyList = None, parent = None,
  16. title = 'Widget Properties'):
  17. """Initialize a dialog.
  18. Arguments:
  19. propertyDict -- a dictionary of properties to be edited
  20. parent -- a parent window (the application window)
  21. title -- the dialog title
  22. """
  23. # Record property list
  24. self.propertyDict = propertyDict
  25. self.propertyList = propertyList
  26. if self.propertyList is None:
  27. self.propertyList = self.propertyDict.keys()
  28. self.propertyList.sort()
  29. # Use default parent if none specified
  30. if not parent:
  31. import Tkinter
  32. parent = Tkinter._default_root
  33. # Create toplevel window
  34. Toplevel.__init__(self, parent)
  35. self.transient(parent)
  36. # Set title
  37. if title:
  38. self.title(title)
  39. # Record parent
  40. self.parent = parent
  41. # Initialize modifications
  42. self.modifiedDict = {}
  43. # Create body
  44. body = Frame(self)
  45. self.initial_focus = self.body(body)
  46. body.pack(padx=5, pady=5)
  47. # Create OK Cancel button
  48. self.buttonbox()
  49. # Initialize window state
  50. self.grab_set()
  51. self.protocol("WM_DELETE_WINDOW", self.cancel)
  52. self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
  53. parent.winfo_rooty()+50))
  54. self.initial_focus.focus_set()
  55. self.wait_window(self)
  56. def destroy(self):
  57. """Destroy the window"""
  58. self.propertyDict = {}
  59. self.initial_focus = None
  60. # Clean up balloons!
  61. for balloon in self.balloonList:
  62. balloon.withdraw()
  63. Toplevel.destroy(self)
  64. #
  65. # construction hooks
  66. def body(self, master):
  67. """create dialog body.
  68. return entry that should have initial focus.
  69. This method should be overridden, and is called
  70. by the __init__ method.
  71. """
  72. count = 0
  73. entryList = []
  74. self.balloonList = []
  75. for property in self.propertyList:
  76. propertySet = self.propertyDict[property]
  77. # Widget
  78. widget = propertySet.get('widget', None)
  79. # Get initial value
  80. initialvalue = widget[property]
  81. # Type of entry
  82. entryType = propertySet.get('type', 'real')
  83. # Is None an allowable value?
  84. fAllowNone = propertySet.get('fNone', 0)
  85. # Help string specified?
  86. helpString = propertySet.get('help', None)
  87. # Create label
  88. label = Label(master, text=property, justify=LEFT)
  89. label.grid(row=count, column = 0, padx=5, sticky=W)
  90. # Create entry
  91. entry = Pmw.EntryField(master, entry_justify = 'right')
  92. entry.grid(row=count, column = 1, padx=5, sticky=W+E)
  93. if initialvalue is None:
  94. entry.insert(0, 'None')
  95. else:
  96. entry.insert(0, initialvalue)
  97. # Create balloon for help
  98. balloon = Pmw.Balloon(state = 'balloon')
  99. self.balloonList.append(balloon)
  100. # extra info if None is allowed value
  101. if helpString is None:
  102. if fAllowNone:
  103. extra = ' or None'
  104. else:
  105. extra = ''
  106. # Set up help string and validator based upon type
  107. if entryType == 'real':
  108. # Only allow real numbers
  109. if fAllowNone:
  110. entry['validate'] = { 'validator': self.realOrNone }
  111. else:
  112. entry['validate'] = { 'validator': 'real' }
  113. if helpString is None:
  114. helpString = 'Enter a floating point number' + extra + '.'
  115. elif entryType == 'integer':
  116. # Only allow integer values
  117. if fAllowNone:
  118. entry['validate'] = { 'validator': self.intOrNone }
  119. else:
  120. entry['validate'] = { 'validator': 'integer' }
  121. if helpString is None:
  122. helpString = 'Enter an integer' + extra + '.'
  123. else:
  124. # Anything goes with a string widget
  125. if helpString is None:
  126. helpString = 'Enter a string' + extra + '.'
  127. # Bind balloon with help string to entry
  128. balloon.bind(entry, helpString)
  129. # Create callback to execute whenever a value is changed
  130. modifiedCallback = (lambda f=self.modified, w=widget, e=entry,
  131. p=property, t=entryType, fn=fAllowNone:
  132. f(w, e, p, t, fn))
  133. entry['modifiedcommand'] = modifiedCallback
  134. # Keep track of the entrys
  135. entryList.append(entry)
  136. count += 1
  137. # Set initial focus
  138. if len(entryList) > 0:
  139. entry = entryList[0]
  140. entry.select_range(0, END)
  141. # Set initial focus to first entry in the list
  142. return entryList[0]
  143. else:
  144. # Just set initial focus to self
  145. return self
  146. def modified(self, widget, entry, property, type, fNone):
  147. self.modifiedDict[property] = (widget, entry, type, fNone)
  148. def buttonbox(self):
  149. """add standard button box buttons.
  150. """
  151. box = Frame(self)
  152. # Create buttons
  153. w = Button(box, text="OK", width=10, command=self.ok)
  154. w.pack(side=LEFT, padx=5, pady=5)
  155. # Create buttons
  156. w = Button(box, text="Cancel", width=10, command=self.cancel)
  157. w.pack(side=LEFT, padx=5, pady=5)
  158. # Bind commands
  159. self.bind("<Return>", self.ok)
  160. self.bind("<Escape>", self.cancel)
  161. # Pack
  162. box.pack()
  163. def realOrNone(self, val):
  164. val = string.lower(val)
  165. if string.find('none', val) != -1:
  166. if val == 'none':
  167. return Pmw.OK
  168. else:
  169. return Pmw.PARTIAL
  170. return Pmw.realvalidator(val)
  171. def intOrNone(self, val):
  172. val = string.lower(val)
  173. if string.find('none', val) != -1:
  174. if val == 'none':
  175. return Pmw.OK
  176. else:
  177. return Pmw.PARTIAL
  178. return Pmw.integervalidator(val)
  179. #
  180. # standard button semantics
  181. def ok(self, event=None):
  182. self.withdraw()
  183. self.update_idletasks()
  184. self.validateChanges()
  185. self.apply()
  186. self.cancel()
  187. def cancel(self, event=None):
  188. # put focus back to the parent window
  189. self.parent.focus_set()
  190. self.destroy()
  191. def validateChanges(self):
  192. for property in self.modifiedDict.keys():
  193. tuple = self.modifiedDict[property]
  194. widget = tuple[0]
  195. entry = tuple[1]
  196. type = tuple[2]
  197. fNone = tuple[3]
  198. value = entry.get()
  199. lValue = string.lower(value)
  200. if (string.find('none', lValue) != -1):
  201. if fNone and (lValue == 'none'):
  202. widget[property] = None
  203. else:
  204. if type == 'real':
  205. value = string.atof(value)
  206. elif type == 'integer':
  207. value = string.atoi(value)
  208. widget[property] = value
  209. def apply(self):
  210. """process the data
  211. This method is called automatically to process the data, *after*
  212. the dialog is destroyed. By default, it does nothing.
  213. """
  214. pass # override