1 from PyQt4 import QtCore, QtGui
2 from PyQt4.QtCore import Qt
3
4 from customeditor import CustomEditor
5 from abstractmanytooneeditor import AbstractManyToOneEditor
6 from camelot.core.utils import variant_to_pyobject, create_constant_function
7
8 from camelot.view.art import Icon
9 from camelot.view.model_thread import gui_function, model_function, post
10 from camelot.view.search import create_entity_search_query_decorator
11 from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
12 from camelot.core.utils import ugettext as _
15 """Widget for editing many 2 one relations"""
16
18
20 QtCore.QAbstractListModel.__init__( self, parent )
21 self._completions = []
22
24 self._completions = completions
25 self.emit( QtCore.SIGNAL( 'layoutChanged()' ) )
26
27 - def data( self, index, role ):
28 if role == Qt.DisplayRole:
29 return QtCore.QVariant( self._completions[index.row()][0] )
30 elif role == Qt.EditRole:
31 return QtCore.QVariant( self._completions[index.row()][1] )
32 return QtCore.QVariant()
33
35 return len( self._completions )
36
39
40 - def __init__( self, admin = None, parent = None, editable=True, **kwargs ):
41 """
42 :param entity_admin : The Admin interface for the object on the one side of
43 the relation
44 """
45
46 CustomEditor.__init__( self, parent )
47 self.admin = admin
48 self._editable = editable
49 self.entity_instance_getter = None
50 self._entity_representation = ''
51 self.entity_set = False
52 self._last_highlighted_entity_getter = None
53 self.layout = QtGui.QHBoxLayout()
54 self.layout.setSpacing( 0 )
55 self.layout.setMargin( 0 )
56
57
58 self.search_button = QtGui.QToolButton()
59 self.search_button.setFocusPolicy( Qt.ClickFocus )
60 self.search_button.setIcon( Icon( 'tango/16x16/actions/edit-clear.png' ).getQIcon() )
61 self.search_button.setToolTip(unicode(_('clear')))
62 self.search_button.setAutoRaise( True )
63 self.search_button.setFixedHeight( self.get_height() )
64 self.connect( self.search_button,
65 QtCore.SIGNAL( 'clicked()' ),
66 self.searchButtonClicked )
67
68
69 self.open_button = QtGui.QToolButton()
70 self.open_button.setFocusPolicy( Qt.ClickFocus )
71 self.open_button.setIcon( Icon( 'tango/16x16/actions/document-new.png' ).getQIcon() )
72 self.open_button.setToolTip( unicode(_('new')) )
73 self.open_button.setFixedHeight( self.get_height() )
74 self.connect( self.open_button,
75 QtCore.SIGNAL( 'clicked()' ),
76 self.openButtonClicked )
77 self.open_button.setAutoRaise( True )
78
79
80 self.search_input = DecoratedLineEdit( self )
81 self.search_input.set_background_text(_('Search...'))
82 self.setFocusProxy( self.search_input )
83
84
85
86
87 self.connect( self.search_input,
88 QtCore.SIGNAL( 'textEdited(const QString&)' ),
89 self.textEdited )
90
91 self.connect( self.search_input,
92 QtCore.SIGNAL( 'editingFinished()' ),
93 self.editingFinished )
94
95 self.completer = QtGui.QCompleter()
96 self.completions_model = self.CompletionsModel( self.completer )
97 self.completer.setModel( self.completions_model )
98 self.completer.setCaseSensitivity( Qt.CaseInsensitive )
99 self.completer.setCompletionMode( QtGui.QCompleter.UnfilteredPopupCompletion )
100 self.connect( self.completer,
101 QtCore.SIGNAL( 'activated(const QModelIndex&)' ),
102 self.completionActivated )
103 self.connect( self.completer,
104 QtCore.SIGNAL( 'highlighted (const QModelIndex&)' ),
105 self.completion_highlighted )
106 self.search_input.setCompleter( self.completer )
107
108
109 self.layout.addWidget( self.search_input )
110 self.layout.addWidget( self.search_button )
111 self.layout.addWidget( self.open_button )
112 self.setLayout( self.layout )
113 self.set_editable(editable)
114
116 self._editable = editable
117 self.search_input.setEnabled(editable)
118 self.search_button.setEnabled(editable)
119
120 - def textEdited( self, text ):
121 self._last_highlighted_entity_getter = None
122 text = self.search_input.user_input()
123
124 def create_search_completion( text ):
125 return lambda: self.search_completions( text )
126
127 post( create_search_completion( unicode( text ) ),
128 self.display_search_completions )
129 self.completer.complete()
130
131 @model_function
133 """Search for object that match text, to fill the list of completions
134
135 :return: a list of tuples of (object_representation, object_getter)
136 """
137 search_decorator = create_entity_search_query_decorator( self.admin, text )
138 if search_decorator:
139 return text, [( unicode( e ), create_constant_function( e ) )
140 for e in search_decorator( self.admin.entity.query ).limit( 20 )]
141 return text, []
142
143 @gui_function
145 prefix, completions = prefix_and_completions
146 self.completions_model.setCompletions( completions )
147 self.completer.setCompletionPrefix( prefix )
148 self.completer.complete()
149
153
155 object_getter = index.data( Qt.EditRole )
156 self._last_highlighted_entity_getter = variant_to_pyobject(object_getter)
157
163
167
173
176
177 @gui_function
183
184 post( get_has_subclasses, self.show_new_view )
185
186 @gui_function
203
211
212 post( get_admin_and_title, self.show_form_view)
213
219
220 from camelot.view.proxy.collection_proxy import CollectionProxy
221 from camelot.view.workspace import get_workspace
222
223 workspace = get_workspace()
224 model = CollectionProxy( admin,
225 create_collection_getter( self.entity_instance_getter ),
226 admin.get_fields )
227 self.connect( model,
228 QtCore.SIGNAL( 'dataChanged(const QModelIndex &, const QModelIndex &)' ),
229 self.dataChanged )
230 form = admin.create_form_view( title, model, 0, workspace )
231 workspace.addSubWindow( form )
232 form.show()
233
235 self.setEntity( self.entity_instance_getter, False )
236
238 if not self.entity_set:
239
240
241
242
243 if self._last_highlighted_entity_getter:
244 self.setEntity( self._last_highlighted_entity_getter )
245 elif not self.entity_set and self.completions_model.rowCount()==1:
246
247 enity_getter = variant_to_pyobject( self.completions_model.index(0,0).data( Qt.EditRole ) )
248 self.setEntity( enity_getter )
249 self.search_input.set_user_input( self._entity_representation )
250
252 """:param value: either ValueLoading, or a function that returns None or the entity to be shown in the editor"""
253 self._last_highlighted_entity_getter = None
254 value = CustomEditor.set_value( self, value )
255 if value:
256 self.setEntity( value, propagate = False )
257
259 """:return: a function that returns the selected entity or ValueLoading or None """
260 value = CustomEditor.get_value( self )
261 if not value:
262 value = self.entity_instance_getter
263 return value
264
266 """Update the gui"""
267 ((desc, pk), propagate) = representation_and_propagate
268 self._entity_representation = desc
269 self.search_input.set_user_input( desc )
270 if pk != False:
271 self.open_button.setIcon( Icon( 'tango/16x16/places/folder.png' ).getQIcon() )
272 self.open_button.setToolTip(unicode(_('open')))
273 self.open_button.setEnabled(True)
274 self.search_button.setIcon( Icon( 'tango/16x16/actions/edit-clear.png' ).getQIcon() )
275 self.search_button.setToolTip(unicode(_('clear')))
276 self.entity_set = True
277 else:
278 self.open_button.setIcon( Icon( 'tango/16x16/actions/document-new.png' ).getQIcon() )
279 self.open_button.setToolTip( unicode(_('new')) )
280 self.open_button.setEnabled(self._editable)
281 self.search_button.setIcon( Icon( 'tango/16x16/actions/system-search.png' ).getQIcon() )
282 self.search_button.setToolTip(_('Search'))
283 self.entity_set = False
284 if propagate:
285 self.emit( QtCore.SIGNAL( 'editingFinished()' ) )
286
287 - def setEntity( self, entity_instance_getter, propagate = True ):
288
289 def create_instance_getter( entity_instance ):
290 return lambda:entity_instance
291
292 def get_instance_represenation():
293 """Get a representation of the instance
294
295 :return: (unicode, pk) its unicode representation and its primary key
296 or ('', False) if the instance was None
297 """
298 entity = entity_instance_getter()
299 self.entity_instance_getter = create_instance_getter( entity )
300 if entity and hasattr( entity, 'id' ):
301 return (( unicode( entity ), entity.id ), propagate)
302 elif entity:
303 return (( unicode( entity ), False ), propagate)
304 return (( None, False ), propagate)
305
306 post( get_instance_represenation, self.set_instance_represenation )
307
310