VectorWidgets.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. from direct.showbase.TkGlobal import *
  2. import Valuator
  3. import Floater
  4. import Slider
  5. import string
  6. import tkColorChooser
  7. class VectorEntry(Pmw.MegaWidget):
  8. def __init__(self, parent = None, **kw):
  9. # Default vector size
  10. DEFAULT_DIM = 3
  11. # Default value depends on *actual* vector size, test for user input
  12. DEFAULT_VALUE = [0.0] * kw.get('dim', DEFAULT_DIM)
  13. DEFAULT_LABELS = map(lambda x: 'v[%d]' % x,
  14. range(kw.get('dim', DEFAULT_DIM)))
  15. # Process options
  16. INITOPT = Pmw.INITOPT
  17. optiondefs = (
  18. ('dim', DEFAULT_DIM, INITOPT),
  19. ('value', DEFAULT_VALUE, INITOPT),
  20. ('resetValue', DEFAULT_VALUE, None),
  21. ('label_width', 12, None),
  22. ('labelIpadx', 2, None),
  23. ('command', None, None),
  24. ('entryWidth', 8, self._updateEntryWidth),
  25. ('relief', GROOVE, self._updateRelief),
  26. ('bd', 2, self._updateBorderWidth),
  27. ('text', 'Vector:', self._updateText),
  28. ('min', None, self._updateValidate),
  29. ('max', None, self._updateValidate),
  30. ('numDigits', 2, self._setSigDigits),
  31. ('type', 'floater', None),
  32. ('state', 'normal', self._setState),
  33. )
  34. self.defineoptions(kw, optiondefs)
  35. # Initialize superclass
  36. Pmw.MegaWidget.__init__(self, parent)
  37. # Initialize value
  38. # Make sure its a list (and as a byproduct, make a distinct copy)
  39. self._value = list(self['value'])
  40. self['resetValue'] = self['value']
  41. self._floaters = None
  42. self.entryFormat = '%.2f'
  43. # Get a handle on the parent container
  44. interior = self.interior()
  45. # This does double duty as a menu button
  46. self._label = self.createcomponent('label', (), None,
  47. Menubutton, (interior,),
  48. text = self['text'],
  49. activebackground = '#909090')
  50. self.menu = self._label['menu'] = Menu(self._label)
  51. self.menu.add_command(label = 'Reset', command = self.reset)
  52. self.menu.add_command(label = 'Popup sliders', command = self.popupSliders)
  53. self._label.pack(side = LEFT, fill = X, ipadx = self['labelIpadx'])
  54. self.variableList = []
  55. self.entryList = []
  56. for index in range(self['dim']):
  57. var = StringVar()
  58. self.variableList.append(var)
  59. # To set the configuration of all entrys in a vector use:
  60. # ve.configure(Entry_XXX = YYY)
  61. # To configure an individual entryfield's entry use:
  62. # ve.configure(entry0_XXX = YYY)
  63. entry = self.createcomponent(
  64. 'entryField%d' % index,
  65. (('entry%d' % index,
  66. 'entryField%d_entry' % index),),
  67. 'Entry',
  68. Pmw.EntryField, (interior,),
  69. entry_justify = RIGHT,
  70. entry_textvariable = var,
  71. command = lambda s = self, i = index: s._entryUpdateAt(i))
  72. entry.pack(side = LEFT, expand = 1, fill = X)
  73. self.entryList.append(entry)
  74. # To configure the floaterGroup use:
  75. # ve.configure(floaterGroup_XXX = YYY)
  76. # ve.configure(fGroup_XXX = YYY) or
  77. # To set the configuration all floaters in a group use:
  78. # ve.configure(Valuator_XXX = YYY)
  79. # To configure an individual floater in a group use:
  80. # ve.configure(floaterGroup_floater0_XXX = YYY) or
  81. # ve.configure(fGroup_floater0_XXX = YYY)
  82. self._floaters = self.createcomponent(
  83. 'floaterGroup',
  84. (('fGroup', 'floaterGroup'),
  85. ('valuator', 'floaterGroup_valuator'),), None,
  86. Valuator.ValuatorGroupPanel, (self.interior(),),
  87. dim = self['dim'],
  88. #title = self['text'],
  89. type = self['type'],
  90. command = self.set)
  91. # Note: This means the 'X' on the menu bar doesn't really destroy
  92. # the panel, just withdraws it. This is to avoid problems which occur
  93. # if the user kills the floaterGroup and then tries to pop it open again
  94. self._floaters.userdeletefunc(self._floaters.withdraw)
  95. self._floaters.withdraw()
  96. # Make sure entries are updated
  97. self.set(self['value'])
  98. # Record entry color
  99. self.entryBackground = self.cget('Entry_entry_background')
  100. # Make sure input variables processed
  101. self.initialiseoptions(VectorEntry)
  102. def menu(self):
  103. return self.menu
  104. def label(self):
  105. return self._label
  106. def entry(self, index):
  107. return self.entryList[index]
  108. def entryList(self):
  109. return self.entryList
  110. def floaters(self):
  111. return self._floaters
  112. def _clearFloaters(self):
  113. self._floaters.withdraw()
  114. def _updateText(self):
  115. self._label['text'] = self['text']
  116. def _updateRelief(self):
  117. self.interior()['relief'] = self['relief']
  118. def _updateBorderWidth(self):
  119. self.interior()['bd'] = self['bd']
  120. def _updateEntryWidth(self):
  121. self['Entry_entry_width'] = self['entryWidth']
  122. def _setSigDigits(self):
  123. sd = self['numDigits']
  124. self.entryFormat = '%.' + '%d' % sd + 'f'
  125. self.configure(valuator_numDigits = sd)
  126. # And refresh value to reflect change
  127. for index in range(self['dim']):
  128. self._refreshEntry(index)
  129. def _updateValidate(self):
  130. # Update entry field to respect new limits
  131. self.configure(Entry_validate = {
  132. 'validator' : 'real',
  133. 'min' : self['min'],
  134. 'max' : self['max'],
  135. 'minstrict' : 0,
  136. 'maxstrict' : 0})
  137. # Reflect changes in floaters
  138. self.configure(valuator_min = self['min'],
  139. valuator_max = self['max'])
  140. def get(self):
  141. return self._value
  142. def getAt(self,index):
  143. return self._value[index]
  144. def set(self, value, fCommand = 1):
  145. for i in range(self['dim']):
  146. self._value[i] = value[i]
  147. self.variableList[i].set(self.entryFormat % value[i])
  148. self.action(fCommand)
  149. def setAt(self, index, value, fCommand = 1):
  150. self.variableList[index].set(self.entryFormat % value)
  151. self._value[index] = value
  152. self.action(fCommand)
  153. def _entryUpdateAt(self, index):
  154. entryVar = self.variableList[index]
  155. # Did we get a valid float?
  156. try:
  157. newVal = string.atof(entryVar.get())
  158. except ValueError:
  159. return
  160. # Clamp value
  161. if self['min'] is not None:
  162. if newVal < self['min']:
  163. newVal = self['min']
  164. if self['max'] is not None:
  165. if newVal > self['max']:
  166. newVal = self['max']
  167. # Update vector's value
  168. self._value[index] = newVal
  169. # refresh entry to reflect formatted value
  170. self._refreshEntry(index)
  171. # Update the floaters and call the command
  172. self.action()
  173. def _refreshEntry(self,index):
  174. self.variableList[index].set( self.entryFormat % self._value[index] )
  175. self.entryList[index].checkentry()
  176. def _refreshFloaters(self):
  177. if self._floaters:
  178. self._floaters.set(self._value, 0)
  179. def action(self, fCommand = 1):
  180. self._refreshFloaters()
  181. if fCommand and (self['command'] != None):
  182. self['command'](self._value)
  183. def reset(self):
  184. self.set(self['resetValue'])
  185. def addMenuItem(self, label = '', command = None):
  186. self.menu.add_command(label = label, command = command)
  187. def popupSliders(self):
  188. self._floaters.set(self.get()[:])
  189. self._floaters.show()
  190. def _setState(self):
  191. if self['state'] == 'disabled':
  192. # Disable entry
  193. self.configure(Entry_entry_state = 'disabled')
  194. self.configure(Entry_entry_background = '#C0C0C0')
  195. # Disable floater Group scale
  196. self.component('fGroup').configure(
  197. valuator_state = 'disabled')
  198. # Disable floater group entry
  199. self.component('fGroup').configure(
  200. valuator_entry_state = 'disabled')
  201. self.component('fGroup').configure(
  202. valuator_entry_background = '#C0C0C0')
  203. else:
  204. # Disable entry
  205. self.configure(Entry_entry_state = 'normal')
  206. self.configure(Entry_entry_background = self.entryBackground)
  207. # Disable floater Group scale
  208. self.component('fGroup').configure(
  209. valuator_state = 'normal')
  210. # Disable floater group entry
  211. self.component('fGroup').configure(
  212. valuator_entry_state = 'normal')
  213. self.component('fGroup').configure(
  214. valuator_entry_background = self.entryBackground)
  215. class Vector2Entry(VectorEntry):
  216. def __init__(self, parent = None, **kw):
  217. # Initialize options for the class
  218. optiondefs = (
  219. ('dim', 2, Pmw.INITOPT),
  220. ('fGroup_labels', ('X','Y','Z'), None),
  221. )
  222. self.defineoptions(kw, optiondefs)
  223. # Initialize the superclass, make sure dim makes it to superclass
  224. VectorEntry.__init__(self, parent, dim = self['dim'])
  225. # Needed because this method checks if self.__class__ is myClass
  226. # where myClass is the argument passed into inialiseoptions
  227. self.initialiseoptions(Vector2Entry)
  228. class Vector3Entry(VectorEntry):
  229. def __init__(self, parent = None, **kw):
  230. # Initialize options for the class
  231. optiondefs = (
  232. ('dim', 3, Pmw.INITOPT),
  233. ('fGroup_labels', ('X','Y','Z'), None),
  234. )
  235. self.defineoptions(kw, optiondefs)
  236. # Initialize the superclass, make sure dim makes it to superclass
  237. VectorEntry.__init__(self, parent, dim = self['dim'])
  238. # Needed because this method checks if self.__class__ is myClass
  239. # where myClass is the argument passed into inialiseoptions
  240. self.initialiseoptions(Vector3Entry)
  241. class Vector4Entry(VectorEntry):
  242. def __init__(self, parent = None, **kw):
  243. # Initialize options for the class
  244. optiondefs = (
  245. ('dim', 4, Pmw.INITOPT),
  246. ('fGroup_labels', ('X','Y','Z','W'), None),
  247. )
  248. self.defineoptions(kw, optiondefs)
  249. # Initialize the superclass, make sure dim makes it to superclass
  250. VectorEntry.__init__(self, parent, dim = self['dim'])
  251. # Needed because this method checks if self.__class__ is myClass
  252. # where myClass is the argument passed into inialiseoptions
  253. self.initialiseoptions(Vector4Entry)
  254. class ColorEntry(VectorEntry):
  255. def __init__(self, parent = None, **kw):
  256. # Initialize options for the class (overriding some superclass options)
  257. optiondefs = (
  258. ('dim', 4, Pmw.INITOPT),
  259. ('type', 'slider', Pmw.INITOPT),
  260. ('fGroup_labels', ('R','G','B','A'), None),
  261. ('min', 0.0, None),
  262. ('max', 255.0, None),
  263. ('nuDigits', 0, None),
  264. ('valuator_resolution', 1.0, None),
  265. )
  266. self.defineoptions(kw, optiondefs)
  267. # Initialize the superclass, make sure dim makes it to superclass
  268. VectorEntry.__init__(self, parent, dim = self['dim'])
  269. # Add menu item to popup color picker
  270. self.addMenuItem(
  271. 'Popup color picker',
  272. command = lambda s = self: s.popupColorPicker())
  273. # Needed because this method checks if self.__class__ is myClass
  274. # where myClass is the argument passed into inialiseoptions
  275. self.initialiseoptions(ColorEntry)
  276. def popupColorPicker(self):
  277. # Can pass in current color with: color = (255, 0, 0)
  278. color = tkColorChooser.askcolor(
  279. parent = self.interior(),
  280. # Initialize it to current color
  281. initialcolor = tuple(self.get()[:3]))[0]
  282. if color:
  283. self.set((color[0], color[1], color[2], self.getAt(3)))
  284. if __name__ == '__main__':
  285. root = Toplevel()
  286. root.title('Vector Widget demo')
  287. ve = VectorEntry(root); ve.pack()
  288. v3e = Vector3Entry(root); v3e.pack()
  289. v4e = Vector4Entry(root); v4e.pack()
  290. ce = ColorEntry(root); ce.pack()