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