Package Camelot :: Package camelot :: Package view :: Package controls :: Package editors :: Module many2oneeditor
[frames] | no frames]

Source Code for Module Camelot.camelot.view.controls.editors.many2oneeditor

  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 _ 
13 14 -class Many2OneEditor( CustomEditor, AbstractManyToOneEditor ):
15 """Widget for editing many 2 one relations""" 16
17 - class CompletionsModel( QtCore.QAbstractListModel ):
18
19 - def __init__( self, parent = None ):
20 QtCore.QAbstractListModel.__init__( self, parent ) 21 self._completions = []
22
23 - def setCompletions( self, completions ):
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
34 - def rowCount( self, index = None ):
35 return len( self._completions )
36
37 - def columnCount( self, index = None ):
38 return 1
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 # Search button 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 # Open button 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 # Search input 80 self.search_input = DecoratedLineEdit( self ) 81 self.search_input.set_background_text(_('Search...')) 82 self.setFocusProxy( self.search_input ) 83 #self.search_input.setReadOnly(True) 84 #self.connect(self.search_input, 85 # QtCore.SIGNAL('returnPressed()'), 86 # self.returnPressed) 87 self.connect( self.search_input, 88 QtCore.SIGNAL( 'textEdited(const QString&)' ), 89 self.textEdited ) 90 # suppose garbage was entered, we need to refresh the content 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 # Setup layout 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
115 - def set_editable(self, editable):
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
132 - def search_completions( self, text ):
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
144 - def display_search_completions( self, prefix_and_completions ):
145 prefix, completions = prefix_and_completions 146 self.completions_model.setCompletions( completions ) 147 self.completer.setCompletionPrefix( prefix ) 148 self.completer.complete()
149
150 - def completionActivated( self, index ):
151 object_getter = index.data( Qt.EditRole ) 152 self.setEntity( variant_to_pyobject(object_getter) )
153
154 - def completion_highlighted(self, index ):
155 object_getter = index.data( Qt.EditRole ) 156 self._last_highlighted_entity_getter = variant_to_pyobject(object_getter)
157
158 - def openButtonClicked( self ):
159 if self.entity_set: 160 return self.createFormView() 161 else: 162 return self.createNew()
163
164 - def returnPressed( self ):
165 if not self.entity_set: 166 self.createSelectView()
167
168 - def searchButtonClicked( self ):
169 if self.entity_set: 170 self.setEntity( lambda:None ) 171 else: 172 self.createSelectView()
173
174 - def trashButtonClicked( self ):
175 self.setEntity( lambda:None )
176 177 @gui_function
178 - def createNew( self ):
179 180 @model_function 181 def get_has_subclasses(): 182 return len( self.admin.get_subclass_tree() )
183 184 post( get_has_subclasses, self.show_new_view ) 185 186 @gui_function
187 - def show_new_view( self, has_subclasses ):
188 selected = QtGui.QDialog.Accepted 189 admin = self.admin 190 if has_subclasses: 191 from camelot.view.controls.inheritance import SubclassDialog 192 select_subclass = SubclassDialog( self, self.admin ) 193 select_subclass.setWindowTitle(_('select')) 194 selected = select_subclass.exec_() 195 admin = select_subclass.selected_subclass 196 if selected: 197 from camelot.view.workspace import get_workspace 198 workspace = get_workspace() 199 form = admin.create_new_view( workspace ) 200 self.connect( form, form.entity_created_signal, self.selectEntity ) 201 sub_window = workspace.addSubWindow( form ) 202 sub_window.show()
203
204 - def createFormView( self ):
205 if self.entity_instance_getter: 206 207 def get_admin_and_title(): 208 object = self.entity_instance_getter() 209 admin = self.admin.get_subclass_entity_admin( object.__class__ ) 210 return admin, ''
211 212 post( get_admin_and_title, self.show_form_view) 213
214 - def show_form_view( self, admin_and_title ):
215 admin, title = admin_and_title 216 217 def create_collection_getter( instance_getter ): 218 return lambda:[instance_getter()]
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
234 - def dataChanged( self, index1, index2 ):
235 self.setEntity( self.entity_instance_getter, False )
236
237 - def editingFinished( self ):
238 if not self.entity_set: 239 # Only try to 'guess' what the user meant when no entity is set 240 # to avoid inappropriate removal of data, (eg when the user presses 241 # Esc, editingfinished will be called as well, and we should not 242 # overwrite the current entity set) 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 # There is only one possible option 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
251 - def set_value( self, value ):
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
258 - def get_value(self):
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
265 - def set_instance_represenation( self, representation_and_propagate ):
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
308 - def selectEntity( self, entity_instance_getter ):
309 self.setEntity( entity_instance_getter )
310