Advertisement
Guest User

Untitled

a guest
Jul 5th, 2015
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.51 KB | None | 0 0
  1. import os
  2. from traits.trait_base import ETSConfig
  3. _tk = ETSConfig.toolkit
  4.  
  5. from functools import partial
  6. from traits.api import (Property, Instance, Bool, Str, BaseTraitHandler,
  7. Range)
  8. from traitsui.api import CustomEditor
  9.  
  10. if _tk in ('null','',None):
  11. _tk = os.environ['ETS_TOOLKIT']
  12. #there's no situation other than debugging this file directly
  13. #where this won't be set. To debug this file, set the environment var.
  14. else:
  15. print _tk
  16.  
  17. CustomEditorKlass = __import__('traitsui.%s.custom_editor'%_tk,
  18. fromlist=['CustomEditor'], ).CustomEditor
  19.  
  20. def mkeditor(*args, **kwargs):
  21. return (getattr(__import__('custom_list_editor'),'%s_editor_factory'%_tk)
  22. (*args,**kwargs))
  23.  
  24. def wx_editor_factory(*args, **kwargs):
  25. pass
  26.  
  27. def qt4_editor_factory(parent, editor, *args, **kwargs):
  28. from pyface.qt import QtCore, QtGui
  29. trait_handler = editor.factory.trait_handler
  30. if trait_handler is None:
  31. trait_handler = editor.object.base_trait( editor.name ).handler
  32. editor._trait_handler = trait_handler
  33.  
  34. editor.control = panel = QtGui.QScrollArea()
  35. editor.control.setFrameShape(QtGui.QFrame.NoFrame)
  36. editor.control.setWidgetResizable(True)
  37.  
  38. editor.mapper = QtCore.QSignalMapper(panel)
  39.  
  40. editor._list_pane = QtGui.QWidget()
  41. editor._list_pane.setSizePolicy(QtGui.QSizePolicy.Expanding,
  42. QtGui.QSizePolicy.Expanding)
  43. layout = QtGui.QGridLayout(editor._list_pane)
  44. layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
  45. layout.setContentsMargins(0, 0, 0, 0)
  46.  
  47. _editor = editor.factory.editor
  48. if _editor is None:
  49. _editor = trait_handler.item_trait.get_editor()
  50. editor._editor = getattr( _editor, editor.kind )
  51.  
  52. extended_name = editor.extended_name.replace('.', ':')
  53. editor.context_object.on_trait_change( editor.update_editor_item,
  54. extended_name + '_items?', dispatch = 'ui' )
  55. editor.set_tooltip()
  56.  
  57. return panel
  58.  
  59. #custom editor on toolkit level
  60. class CustomQtListEditorKlass(CustomEditorKlass):
  61.  
  62. from pyface.qt import QtCore, QtGui
  63.  
  64. #trait definitions copied from file
  65. mapper = Instance(QtCore.QSignalMapper)
  66. mutable = Bool(True)
  67. kind = Str
  68.  
  69. single_row = True
  70.  
  71. list_menu = """
  72. Add &Before [_menu_before]: self.add_before()
  73. Add &After [_menu_after]: self.add_after()
  74. ---
  75. &Delete [_menu_delete]: self.delete_item()
  76. ---
  77. Move &Up [_menu_up]: self.move_up()
  78. Move &Down [_menu_down]: self.move_down()
  79. Move to &Top [_menu_top]: self.move_top()
  80. Move to &Bottom [_menu_bottom]: self.move_bottom()
  81. """
  82.  
  83. empty_list_menu = """
  84. Add: self.add_empty()
  85. """
  86.  
  87. def update_editor(self):
  88. from pyface.qt import QtCore, QtGui
  89. self.mapper = QtCore.QSignalMapper(self.control)
  90. # Disconnect the editor from any control about to be destroyed:
  91. self._dispose_items()
  92.  
  93. list_pane = self._list_pane
  94. layout = list_pane.layout()
  95.  
  96. # Create all of the list item trait editors:
  97. trait_handler = self._trait_handler
  98. resizable = ((trait_handler.minlen != trait_handler.maxlen) and
  99. self.mutable)
  100. item_trait = trait_handler.item_trait
  101.  
  102. is_fake = (resizable and (len( self.value ) == 0))
  103. if is_fake:
  104. self.empty_list()
  105. else:
  106. # Asking the mapper to send the sender to the callback method
  107. self.mapper.mapped[QtCore.QObject].connect(self.popup_menu)
  108.  
  109. editor = self._editor
  110. for index, value in enumerate(self.value):
  111. row, column = divmod(index, self.factory.columns)
  112.  
  113. # Account for the fact that we have <columns> number of
  114. # pairs
  115. column = column * 2
  116.  
  117. if resizable:
  118. # Connecting the new button to the mapper
  119. from traitsui.qt4.helper import IconButton
  120.  
  121. from pyface.qt import qt_api
  122.  
  123. # fix bug in pyqt, reverts back to traitsui 4.3 version
  124. if qt_api == 'pyside':
  125. control = IconButton('list_editor.png', self.mapper.map)
  126. elif qt_api == 'pyqt':
  127. control = IconButton('list_editor.png',
  128. lambda : self.popup_menu(
  129. self._list_pane.layout().sender() ))
  130.  
  131. # Setting the mapping and asking it to send the index of the
  132. # sender to the callback method
  133. self.mapper.setMapping(control, control)
  134.  
  135. layout.addWidget(control, row, column)
  136.  
  137. from traitsui.editors.list_editor import ListItemProxy
  138. proxy = ListItemProxy( self.object, self.name, index, item_trait,
  139. value )
  140. if resizable:
  141. control.proxy = proxy
  142. peditor = editor( self.ui, proxy, 'value', self.description,
  143. list_pane ).set( object_name = '' )
  144. peditor.prepare( list_pane )
  145. pcontrol = peditor.control
  146. pcontrol.proxy = proxy
  147.  
  148. if isinstance(pcontrol, QtGui.QWidget):
  149. layout.addWidget(pcontrol, row, column+1)
  150. else:
  151. layout.addLayout(pcontrol, row, column+1)
  152.  
  153. # QScrollArea can have problems if the widget being scrolled is set too
  154. # early (ie. before it contains something).
  155. if self.control.widget() is None:
  156. self.control.setWidget(list_pane)
  157.  
  158. def update_editor_item ( self, event ):
  159. """ Updates the editor when an item in the object trait changes
  160. externally to the editor.
  161. """
  162. # If this is not a simple, single item update, rebuild entire editor:
  163. from pyface.qt import QtCore, QtGui
  164. if (len( event.removed ) != 1) or (len( event.added ) != 1):
  165. self.update_editor()
  166. return
  167.  
  168. # Otherwise, find the proxy for this index and update it with the
  169. # changed value:
  170. for control in self.control.widget().children():
  171. if isinstance(control, QtGui.QLayout):
  172. continue
  173.  
  174. proxy = control.proxy
  175. if proxy.index == event.index:
  176. proxy.value = event.added[0]
  177. break
  178.  
  179. def dispose ( self ):
  180. """ Disposes of the contents of an editor.
  181. """
  182. self._dispose_items()
  183.  
  184. extended_name = self.extended_name.replace('.', ':')
  185. self.context_object.on_trait_change( self.update_editor_item,
  186. extended_name + '_items?', remove = True )
  187.  
  188. super( CustomQtListEditorKlass, self ).dispose()
  189.  
  190. def empty_list ( self ):
  191. """ Creates an empty list entry (so the user can add a new item).
  192. """
  193. # Connecting the new button to the mapper
  194. from pyface.qt import QtCore, QtGui
  195. from pyface.qt import qt_api
  196. from traitsui.qt4.helper import IconButton
  197.  
  198. #control = IconButton('list_editor.png', self.mapper.map)
  199. if qt_api == 'pyside':
  200. control = IconButton('list_editor.png', self.mapper.map)
  201. elif qt_api == 'pyqt':
  202. control = IconButton('list_editor.png',
  203. lambda : self.popup_empty_menu(
  204. self._list_pane.layout().sender() ))
  205.  
  206. # Setting the mapping and asking it to send the sender to the
  207. # callback method
  208. self.mapper.setMapping(control, control)
  209. self.mapper.mapped[QtCore.QObject].connect(self.popup_empty_menu)
  210. control.is_empty = True
  211. self._cur_control = control
  212.  
  213. from traitsui.editors.list_editor import ListItemProxy
  214. proxy = ListItemProxy( self.object, self.name, -1, None, None )
  215. pcontrol = QtGui.QLabel(' (Empty List)')
  216. pcontrol.proxy = control.proxy = proxy
  217.  
  218. layout = self._list_pane.layout()
  219. layout.addWidget(control, 0, 0)
  220. layout.addWidget(pcontrol, 0, 1)
  221.  
  222. def get_info ( self ):
  223. """ Returns the associated object list and current item index.
  224. """
  225. proxy = self._cur_control.proxy
  226. return ( proxy.list, proxy.index )
  227.  
  228. def popup_empty_menu ( self , sender):
  229. """ Displays the empty list editor popup menu.
  230. """
  231. from traitsui.qt4.menu import MakeMenu
  232. from pyface.qt import QtCore, QtGui
  233. self._cur_control = control = sender
  234. menu = MakeMenu( self.empty_list_menu, self, True, control ).menu
  235. menu.exec_(control.mapToGlobal(QtCore.QPoint(0, 0)))
  236.  
  237. def popup_menu ( self , sender):
  238. """ Displays the list editor popup menu.
  239. """
  240. from traitsui.qt4.menu import MakeMenu
  241. from pyface.qt import QtCore, QtGui
  242. self._cur_control = sender
  243.  
  244. proxy = sender.proxy
  245. index = proxy.index
  246. menu = MakeMenu( self.list_menu, self, True, sender ).menu
  247. len_list = len( proxy.list )
  248. not_full = (len_list < self._trait_handler.maxlen)
  249.  
  250. self._menu_before.enabled( not_full )
  251. self._menu_after.enabled( not_full )
  252. self._menu_delete.enabled( len_list > self._trait_handler.minlen )
  253. self._menu_up.enabled( index > 0 )
  254. self._menu_top.enabled( index > 0 )
  255. self._menu_down.enabled( index < (len_list - 1) )
  256. self._menu_bottom.enabled( index < (len_list - 1) )
  257.  
  258. menu.exec_(sender.mapToGlobal(QtCore.QPoint(0, 0)))
  259.  
  260. def add_item ( self, offset ):
  261. """ Adds a new value at the specified list index.
  262. """
  263. list, index = self.get_info()
  264. index += offset
  265. item_trait = self._trait_handler.item_trait
  266. value = item_trait.default_value_for( self.object, self.name )
  267. self.value = list[:index] + [ value ] + list[index:]
  268. self.update_editor()
  269.  
  270. def add_before ( self ):
  271. """ Inserts a new item before the current item.
  272. """
  273. self.add_item( 0 )
  274.  
  275. def add_after ( self ):
  276. """ Inserts a new item after the current item.
  277. """
  278. self.add_item( 1 )
  279.  
  280. def add_empty ( self ):
  281. """ Adds a new item when the list is empty.
  282. """
  283. list, index = self.get_info()
  284. self.add_item( 0 )
  285.  
  286. def delete_item ( self ):
  287. """ Delete the current item.
  288. """
  289. list, index = self.get_info()
  290. self.value = list[:index] + list[index+1:]
  291. self.update_editor()
  292.  
  293. def move_up ( self ):
  294. """ Move the current item up one in the list.
  295. """
  296. list, index = self.get_info()
  297. self.value = (list[:index-1] + [ list[index], list[index-1] ] +
  298. list[index+1:])
  299. self.update_editor()
  300.  
  301. def move_down ( self ):
  302. """ Moves the current item down one in the list.
  303. """
  304. list, index = self.get_info()
  305. self.value = (list[:index] + [ list[index+1], list[index] ] +
  306. list[index+2:])
  307. self.update_editor()
  308.  
  309. def move_top ( self ):
  310. """ Moves the current item to the top of the list.
  311. """
  312. list, index = self.get_info()
  313. self.value = [ list[index] ] + list[:index] + list[index+1:]
  314. self.update_editor()
  315.  
  316. def move_bottom ( self ):
  317. """ Moves the current item to the bottom of the list.
  318. """
  319. list, index = self.get_info()
  320. self.value = list[:index] + list[index+1:] + [ list[index] ]
  321. self.update_editor()
  322.  
  323. def _dispose_items ( self ):
  324. """ Disposes of each current list item.
  325. """
  326. layout = self._list_pane.layout()
  327. child = layout.takeAt(0)
  328. while child is not None:
  329. control = child.widget()
  330. if control is not None:
  331. editor = getattr( control, '_editor', None )
  332. if editor is not None:
  333. editor.dispose()
  334. editor.control = None
  335. control.deleteLater()
  336. child = layout.takeAt(0)
  337. del child
  338.  
  339. def _kind_default(self):
  340. """ Returns a default value for the 'kind' trait.
  341. """
  342. return self.factory.style + '_editor'
  343.  
  344. def _mutable_default(self):
  345. """ Trait handler to set the mutable trait from the factory.
  346. """
  347. return self.factory.mutable
  348.  
  349. if _tk == 'wx':
  350. from traitsui.editors.list_editor import ListEditor as CustomListEditor
  351. elif _tk == 'qt4':
  352.  
  353. from traitsui.editor_factory import EditorFactory
  354. from traitsui.ui_traits import style_trait
  355.  
  356. #custom edior on traitsui level
  357. class CustomListEditor(CustomEditor):
  358. '''
  359. Traitsui level abstraction layer for the Custom List Editor
  360. '''
  361. factory = Property #Callable
  362.  
  363. editor = Instance( EditorFactory )
  364.  
  365. mutable = Bool(True)
  366.  
  367. style = style_trait
  368.  
  369. trait_handler = Instance( BaseTraitHandler )
  370.  
  371. rows = Range(1, 50, 5, desc = 'the number of list rows to display')
  372. columns = Range(1, 10, 1, dec = 'the number of list cols to display')
  373.  
  374. use_notebook = Bool(False)
  375. show_notebook_menu = Bool(False)
  376.  
  377. def _get_klass(self):
  378. #tell the editor to use the klass defined in this file
  379. return CustomQtListEditorKlass
  380.  
  381. def _get_factory(self):
  382. return partial(mkeditor)
  383.  
  384. if __name__=='__main__':
  385. import signal
  386. signal.signal(signal.SIGINT, signal.SIG_DFL)
  387.  
  388. from traits.api import List, HasTraits
  389. from traitsui.api import View, Item
  390.  
  391. class Dog(HasTraits):
  392. dog = List
  393.  
  394. def _dog_default(self):
  395. return [4,5,7]
  396.  
  397. view = View(Item('dog', editor=CustomListEditor()))
  398.  
  399. Dog().configure_traits()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement