Changeset 1186

Show
Ignore:
Timestamp:
03/13/10 06:31:24 (5 months ago)
Author:
erikj
Message:

prepare expanded search and sorting of query proxy models

Location:
trunk/camelot
Files:
9 modified

Legend:

Unmodified
Added
Removed
  • trunk/camelot/admin/validator/entity_validator.py

    r987 r1186  
    3737    """ 
    3838 
    39     def objectValidity(self, entity_instance): 
    40         """:return: list of messages explaining invalid data 
    41         empty list if object is valid 
    42         """ 
    43         from camelot.view.controls import delegates 
    44         messages = [] 
    45         fields_and_attributes = dict(self.admin.get_columns()) 
    46         fields_and_attributes.update(dict(self.admin.get_fields())) 
    47         for field, attributes in fields_and_attributes.items(): 
    48             # if the field was not editable, don't waste any time 
    49             if attributes['editable']: 
    50               value = getattr(entity_instance, field) 
    51               #@todo: check if field is a primary key instead of checking  
    52               # whether the name is id 
    53               if attributes['nullable']!=True and field!='id': 
    54                   logger.debug('column %s is required'%(field)) 
    55                   if 'delegate' not in attributes: 
    56                       raise Exception('no delegate specified for %s'%(field)) 
    57                   is_null = False 
    58                   if value==None: 
    59                       is_null = True 
    60                   elif (attributes['delegate'] == delegates.CodeDelegate) and \ 
    61                        (sum(len(c) for c in value) == 0): 
    62                       is_null = True 
    63                   elif (attributes['delegate'] == delegates.PlainTextDelegate) and (len(value) == 0): 
    64                       is_null = True 
    65                   elif (attributes['delegate'] == delegates.Many2OneDelegate) and (not value.id): 
    66                       is_null = True 
    67                   elif (attributes['delegate'] == delegates.VirtualAddressDelegate) and (not value[1]): 
    68                       is_null = True                     
    69                   if is_null: 
    70                       messages.append(u'%s is a required field' % (attributes['name'])) 
    71         logger.debug(u'messages : %s'%(u','.join(messages))) 
    72         return messages 
     39 
  • trunk/camelot/admin/validator/object_validator.py

    r1060 r1186  
    4646        empty list if object is valid 
    4747        """ 
    48         return [] 
     48        from camelot.view.controls import delegates 
     49        messages = [] 
     50        fields_and_attributes = dict(self.admin.get_columns()) 
     51        fields_and_attributes.update(dict(self.admin.get_fields())) 
     52        for field, attributes in fields_and_attributes.items(): 
     53            # if the field was not editable, don't waste any time 
     54            if attributes['editable']: 
     55              value = getattr(entity_instance, field) 
     56              #@todo: check if field is a primary key instead of checking  
     57              # whether the name is id, but this should only happen in the entity validator 
     58              if attributes['nullable']!=True and field!='id': 
     59                  logger.debug('column %s is required'%(field)) 
     60                  if 'delegate' not in attributes: 
     61                      raise Exception('no delegate specified for %s'%(field)) 
     62                  is_null = False 
     63                  if value==None: 
     64                      is_null = True 
     65                  elif (attributes['delegate'] == delegates.CodeDelegate) and \ 
     66                       (sum(len(c) for c in value) == 0): 
     67                      is_null = True 
     68                  elif (attributes['delegate'] == delegates.PlainTextDelegate) and (len(value) == 0): 
     69                      is_null = True 
     70                  elif (attributes['delegate'] == delegates.Many2OneDelegate) and (not value.id): 
     71                      is_null = True 
     72                  elif (attributes['delegate'] == delegates.VirtualAddressDelegate) and (not value[1]): 
     73                      is_null = True                     
     74                  if is_null: 
     75                      messages.append(u'%s is a required field' % (attributes['name'])) 
     76        logger.debug(u'messages : %s'%(u','.join(messages))) 
     77        return messages 
    4978 
    5079    def isValid(self, row): 
  • trunk/camelot/view/controls/formview.py

    r1185 r1186  
    5555        self.setLayout( self._widget_layout ) 
    5656                 
     57    def get_model(self): 
     58        return self._model 
     59     
    5760    def set_model(self, model): 
    5861        self._model = model 
  • trunk/camelot/view/controls/tableview.py

    r1184 r1186  
    127127            self.number_of_rows = None 
    128128        layout.addLayout( widget_layout ) 
    129         self._expanded_search = QtGui.QLabel('Expanded') 
     129        self._expanded_search = QtGui.QWidget() 
    130130        self._expanded_search.hide() 
    131131        layout.addWidget(self._expanded_search) 
     
    133133        self.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) 
    134134        self.setNumberOfRows( 0 ) 
    135      
     135        post( admin.get_columns, self._fill_expanded_search_options ) 
     136     
     137    def _fill_expanded_search_options(self, columns): 
     138        layout = QtGui.QHBoxLayout() 
     139        for field, attributes in columns: 
     140            if 'operators' in attributes: 
     141                widget = QtGui.QLabel(field) 
     142                layout.addWidget( widget ) 
     143        self._expanded_search.setLayout( layout ) 
     144         
     145         
    136146    def expand_search_options(self): 
    137147        if self._expanded_search.isHidden(): 
  • trunk/camelot/view/elixir_admin.py

    r1146 r1186  
    169169                from elixir import entities 
    170170                mapped_entities = [str(e) for e in entities] 
    171                 logger.error(u'%s is not a mapped class, mapped classes include %s'%(self.entity, u','.join(mapped_entities)), 
     171                logger.error(u'%s is not a mapped class, mapped classes include %s'%(self.entity, u','.join([unicode(me) for me in mapped_entities])), 
    172172                             exc_info=exception) 
    173173                raise exception 
     
    401401                model = QueryTableProxy( 
    402402                    tableview.admin, 
    403                     tableview._table_model._query_getter, 
     403                    tableview._table_model.get_query_getter(), 
    404404                    tableview.admin.get_fields, 
    405405                    max_number_of_rows=1 
  • trunk/camelot/view/field_attributes.py

    r1179 r1186  
    3131import camelot.types 
    3232import datetime 
     33import operator 
    3334 
    3435from controls import delegates 
     
    4546) 
    4647 
     48_numerical_operators = (operator.eq, operator.lt, operator.le, operator.gt, operator.ge) 
    4749 
    4850_sqlalchemy_to_python_type_ = { 
     
    5355        'nullable': True, 
    5456        'delegate': delegates.BoolDelegate, 
    55         'from_string': bool_from_string 
     57        'from_string': bool_from_string, 
     58        'operators' : (operator.eq,), 
    5659    }, 
    5760 
     
    6164        'nullable': True, 
    6265        'delegate': delegates.BoolDelegate, 
    63         'from_string': bool_from_string 
     66        'from_string': bool_from_string, 
     67        'operators' : (operator.eq,), 
    6468    }, 
    6569 
     
    7276        'nullable': True, 
    7377        'delegate': delegates.DateDelegate, 
    74         'from_string': date_from_string 
     78        'from_string': date_from_string, 
     79        'operators' : _numerical_operators, 
    7580    }, 
    7681 
     
    8388            'format': constants.camelot_time_format, 
    8489            'nullable': True, 
    85             'from_string': time_from_string 
     90            'from_string': time_from_string, 
     91            'operators': _numerical_operators, 
    8692    }, 
    8793 
     
    94100        'nullable': True, 
    95101        'delegate': delegates.DateTimeDelegate, 
    96         'from_string': datetime_from_string 
     102        'from_string': datetime_from_string, 
     103        'operators': _numerical_operators, 
    97104    }, 
    98105 
     
    105112        'nullable': True, 
    106113        'delegate': delegates.FloatDelegate, 
    107         'from_string': float_from_string 
     114        'from_string': float_from_string, 
     115        'operators': _numerical_operators, 
    108116    }, 
    109117 
     
    117125        'from_string': int_from_string, 
    118126        'to_string': unicode, 
    119         'widget': 'int' 
     127        'widget': 'int', 
     128        'operators': _numerical_operators, 
    120129    }, 
    121130 
     
    128137        'delegate': delegates.IntegerDelegate, 
    129138        'from_string': int_from_string, 
    130         'widget': 'int' 
     139        'widget': 'int', 
     140        'operators': _numerical_operators, 
    131141    }, 
    132142 
  • trunk/camelot/view/proxy/collection_proxy.py

    r1174 r1186  
    259259        self.logger.debug( 'initialization finished' ) 
    260260 
     261    def get_validator(self): 
     262        return self.validator 
     263     
    261264    def map_to_source(self, sorted_row_number): 
    262265        """Converts a sorted row number to a row number of the source 
  • trunk/camelot/view/proxy/queryproxy.py

    r1170 r1186  
    3333logger = logging.getLogger('camelot.view.proxy.queryproxy') 
    3434 
    35 from collection_proxy import CollectionProxy, stripped_data_to_unicode, \ 
    36                              strip_data_from_object, tool_tips_from_object, \ 
    37                              background_colors_from_object 
    38 from camelot.view.model_thread import model_function, gui_function 
     35from collection_proxy import CollectionProxy, strip_data_from_object 
     36from camelot.view.model_thread import model_function, gui_function, post 
    3937 
    4038 
     
    5048        logger.debug('initialize query table') 
    5149        self._query_getter = query_getter 
     50        self._sort_decorator = None 
    5251        #rows appended to the table which have not yet been flushed to the 
    5352        #database, and as such cannot be a result of the query 
     
    5655                                 columns_getter, max_number_of_rows=10, edits=None) 
    5756 
     57    def get_query_getter(self): 
     58        if not self._sort_decorator: 
     59            return self._query_getter 
     60        else: 
     61             
     62            def sorted_query_getter(): 
     63                return self._sort_decorator(self._query_getter()) 
     64             
     65            return sorted_query_getter 
     66     
    5867    @model_function 
    5968    def _clean_appended_rows(self): 
     
    6978    def getRowCount(self): 
    7079        self._clean_appended_rows() 
    71         return self._query_getter().count() + len(self._appended_rows) 
     80        return self.get_query_getter()().count() + len(self._appended_rows) 
    7281 
    7382    @gui_function 
     
    8089         
    8190        def collection_getter(): 
    82             return self._query_getter().all() 
     91            return self.get_query_getter()().all() 
    8392         
    8493        return collection_getter 
     
    8695    @gui_function 
    8796    def sort( self, column, order ): 
    88         pass    
     97         
     98        def create_set_sort_decorator(column, order): 
     99 
     100            def set_sort_decorator(): 
     101                from sqlalchemy import orm 
     102                from sqlalchemy.exceptions import InvalidRequestError 
     103                field_name = self._columns[column][0] 
     104                class_attribute = getattr(self.admin.entity, field_name) 
     105                mapper = orm.class_mapper(self.admin.entity) 
     106                try: 
     107                    property = mapper.get_property( 
     108                        field_name, 
     109                        resolve_synonyms=True 
     110                    ) 
     111                except InvalidRequestError: 
     112                    # 
     113                    # If the field name is not a property of the mapper, we cannot 
     114                    # sort it using sql 
     115                    # 
     116                    return self.rows 
     117                 
     118                def create_sort_decorator(class_attribute, order): 
     119                     
     120                    def sort_decorator(query): 
     121                        if order: 
     122                            return query.order_by(class_attribute.desc()) 
     123                        else: 
     124                            return query.order_by(class_attribute) 
     125                     
     126                    return sort_decorator 
     127                 
     128                 
     129                self._sort_decorator = create_sort_decorator(class_attribute, order) 
     130                return self.rows 
     131                     
     132            return set_sort_decorator 
     133             
     134        post( create_set_sort_decorator(column, order), self._refresh_content ) 
    89135 
    90136    def append(self, o): 
     
    103149    def getData(self): 
    104150        """Generator for all the data queried by this proxy""" 
    105         for _i,o in enumerate(self._query_getter().all()): 
     151        for _i,o in enumerate(self.get_query_getter()().all()): 
    106152            yield strip_data_from_object(o, self.getColumns()) 
    107153 
     
    109155    def _extend_cache(self, offset, limit): 
    110156        """Extend the cache around row""" 
    111         q = self._query_getter().offset(offset).limit(limit) 
     157        q = self.get_query_getter()().offset(offset).limit(limit) 
    112158        columns = self.getColumns() 
    113159        for i, o in enumerate(q.all()): 
     
    136182                pass 
    137183            # momentary hack for list error that prevents forms to be closed 
    138             res = self._query_getter().offset(row) 
     184            res = self.get_query_getter()().offset(row) 
    139185            if isinstance(res, list): 
    140186                res = res[0] 
  • trunk/camelot/view/wizard/pages/form_page.py

    r1185 r1186  
    2626#  ============================================================================ 
    2727 
    28 from PyQt4 import QtGui 
     28from PyQt4 import QtGui, QtCore 
    2929 
    3030from camelot.core.utils import ugettext as _ 
    3131from camelot.view.art import Icon 
     32from camelot.view.model_thread import post 
    3233 
    3334class FormPage(QtGui.QWizardPage): 
     
    3738     
    3839    To access the data stored by the wizard form into a data object, use its 
    39     get_data method . 
     40    get_data method. 
     41     
     42    The 'Next' button will only be activated when the form is complete. 
    4043    """ 
    4144 
     
    5760        self.setPixmap(QtGui.QWizard.LogoPixmap, self.get_icon().getQPixmap()) 
    5861        self._data = self.Data() 
     62        self._complete = False 
    5963         
    6064        admin = self.get_admin() 
    61         collection_proxy = CollectionProxy(admin, lambda:[self._data], admin.get_fields) 
     65        self._model = CollectionProxy(admin, lambda:[self._data], admin.get_fields) 
    6266         
    6367        layout = QtGui.QVBoxLayout() 
    6468        form = FormWidget(admin) 
    65         form.set_model(collection_proxy) 
     69        self.connect(form, FormWidget.changed_signal, self._form_changed) 
     70        form.set_model(self._model) 
    6671        layout.addWidget(form) 
    6772        self.setLayout(layout) 
     73     
     74    def _form_changed(self): 
     75         
     76        def is_valid(): 
     77            return self._model.get_validator().isValid(0) 
     78         
     79        post(is_valid, self._change_complete) 
     80         
     81    def _change_complete(self, complete): 
     82        self._complete = complete 
     83        self.emit(QtCore.SIGNAL('completeChanged ()')) 
    6884             
     85    def isComplete(self): 
     86        return self._complete 
     87     
    6988    def get_admin(self): 
    7089        from camelot.view.application_admin import get_application_admin