Changeset 3219

Show
Ignore:
Timestamp:
06/29/2007 10:45:45 AM (2 years ago)
Author:
ed
Message:

Implemented sorting in dListControl by clicking on the column headers. Added the ListHeaderMouseLeftClick? and ListHeaderMouseRightClick? events to dEvents; these correspond to left and right clicks on the headers of dListControl. Modified the sort routines in dControlItemMixin to take into account the fact that dListControl doesn't have a '_choices' attribute.

Added the AutoConvertToString? property to dListControl. When True (default), adding any non-string item, such as a number, will auto-convert it to a unicode string. When False, trying to insert a non-string item will raise an exception.

Updated init.py to retrieve the event column for list events if available.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/dabo/dEvents.py

    r3199 r3219  
    716716    This event is not implemented for other grid cell editors, yet. 
    717717    """ 
     718    pass 
    718719 
    719720 
     
    730731class GridAfterSort(GridEvent): 
    731732    """Occurs after the grid is sorted""" 
     733    pass 
     734 
     735 
     736class ListHeaderMouseLeftClick(GridEvent, MouseEvent): 
     737    """Occurs when the left mouse button is clicked in the header region of dListControl.""" 
     738    pass 
     739 
     740 
     741class ListHeaderMouseRightClick(GridEvent, MouseEvent): 
     742    """Occurs when the right mouse button is clicked in the header region of dListControl.""" 
    732743    pass 
    733744 
  • trunk/dabo/ui/uiwx/__init__.py

    r3208 r3219  
    347347        if isinstance(wxEvt, wx.MouseEvent): 
    348348            ed["mouseDown"] = ed["dragging"] = wxEvt.Dragging() 
    349  
     349             
    350350    if isinstance(wxEvt, wx.ListEvent): 
    351351        pos = wxEvt.GetPosition() 
     
    353353        idx, flg = obj.HitTest(pos) 
    354354        ed["listIndex"] = idx 
     355        try: 
     356            ed["col"] = wxEvt.GetColumn() 
     357        except: 
     358            pass 
    355359 
    356360    if isinstance(wxEvt, wx.MenuEvent): 
  • trunk/dabo/ui/uiwx/dControlItemMixin.py

    r3199 r3219  
    9393        return ms 
    9494 
     95 
     96    def sort(self, sortFunction=None): 
     97        """Sorts the list items. By default, the Python 'cmp' function is  
     98        used, but this can be overridden with a custom sortFunction. 
     99        """ 
     100        if sortFunction is None: 
     101            sortFunction = self._sortFunction 
     102        self._choices.sort(sortFunction) 
     103 
    95104         
    96105    # Property get/set/del methods follow. Scroll to bottom to see the property 
     
    111120            self._choices = list(choices) 
    112121            if self._sorted: 
    113                 self._choices.sort(self._sortFunction
     122                self.sort(
    114123            self.AppendItems(self._choices) 
    115124            if oldVal is not None: 
     
    268277                if val: 
    269278                    # Force a re-ordering 
    270                     self._setChoices(self._choices
     279                    self.sort(
    271280        else: 
    272281            self._properties["Sorted"] = val 
     
    281290                self._sortFunction = val 
    282291                # Force a re-ordering 
    283                 self._setChoices(self._choices
     292                self.sort(
    284293            else: 
    285294                raise TypeError, _("SortFunction must be callable") 
  • trunk/dabo/ui/uiwx/dListControl.py

    r3199 r3219  
    3030        self._hitIndex = None 
    3131        self._valCol = 0 
     32        self._sortOrder = 0 
     33        self._sortColumn = -1 
     34        self._sortOnHeaderClick = True 
     35        # Do we auto-convert all entries to strings? 
     36        self._autoConvertToString = True 
    3237 
    3338        try: 
     
    4146        # Dictionary for tracking images by key value 
    4247        self.__imageList = {}    
     48        # Need to set this after the superclass call in order to override the default for 
     49        # a control with items 
     50        self.SortFunction = self._listControlSort 
    4351 
    4452 
     
    5058        self.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.__onFocus) 
    5159        self.Bind(wx.EVT_LIST_KEY_DOWN, self.__onWxKeyDown) 
    52      
    53      
     60        self.Bind(wx.EVT_LIST_COL_CLICK, self.__onWxHeaderClick) 
     61        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.__onWxHeaderRightClick) 
     62 
     63        self.bindEvent(dEvents.ListHeaderMouseLeftClick, self.__onHeaderMouseLeftClick) 
     64        self.bindEvent(dEvents.ListHeaderMouseRightClick, self.__onHeaderMouseRightClick) 
     65         
     66 
    5467    def addColumn(self, caption): 
    5568        """ Add a column with the selected caption. """ 
     
    170183            currCol = col 
    171184            for itm in tx: 
     185                if not isinstance(itm, basestring) and self.AutoConvertToString: 
     186                    tx = u"%s" % itm 
    172187                new_item = self.append(itm, currCol, row) 
    173188                currCol += 1 
     
    176191                if insert: 
    177192                    new_item = self.InsertStringItem(row, "") 
     193                if not isinstance(tx, basestring) and self.AutoConvertToString: 
     194                    tx = u"%s" % tx 
    178195                self.SetStringItem(row, col, tx) 
    179196            else: 
     
    334351         
    335352         
     353    def __onWxHeaderClick(self, evt): 
     354        self.raiseEvent(dEvents.ListHeaderMouseLeftClick, evt) 
     355         
     356         
     357    def __onWxHeaderRightClick(self, evt): 
     358        self.raiseEvent(dEvents.ListHeaderMouseRightClick, evt) 
     359 
     360 
     361    def __onHeaderMouseLeftClick(self, evt): 
     362        if self.SortOnHeaderClick: 
     363            if self._sortColumn != evt.col: 
     364                self._sortColumn = evt.col 
     365                self._sortOrder = 0 
     366            else: 
     367                self._sortOrder += 1 
     368            self.sort() 
     369         
     370 
     371    def __onHeaderMouseRightClick(self, evt): 
     372        pass 
     373     
     374     
     375    def _listControlSort(self, x, y): 
     376        # Default to standard Python comparison 
     377        return cmp(x, y) 
     378 
     379         
     380    def sort(self, sortFunction=None): 
     381        # Sorts the control based on the current sort column. 
     382        if sortFunction is None: 
     383            sortFunction = self.SortFunction 
     384        itemData = self._getItemDataDict() 
     385        self._fillItemData(self._sortColumn) 
     386        self.SortItems(sortFunction) 
     387        self._restoreItemData(itemData) 
     388     
     389     
     390    def _getItemDataDict(self): 
     391        """Return a dict with the items as keys, and the ItemData as values.""" 
     392        ret = {} 
     393        for row in range(self.RowCount): 
     394            ret[row] = self.GetItemData(row) 
     395        return ret 
     396 
     397 
     398    def _fillItemData(self, col): 
     399        """Sets the Item Data for each row to be the value corresponding to the order 
     400        for each column. 
     401        """ 
     402        data = [] 
     403        col = self._sortColumn 
     404        for row in range(self.RowCount): 
     405            try:     
     406                itm = self.GetItem(row, col) 
     407                data.append((itm.GetText(), row)) 
     408            except: 
     409                pass 
     410        data.sort() 
     411        if (self._sortOrder % 2): 
     412            # Odd number of sorts 
     413            data.reverse() 
     414        for pos, elem in enumerate(data): 
     415            self.SetItemData(elem[1], pos) 
     416     
     417     
     418    def _restoreItemData(self, dct): 
     419        """After a sort, returns the original item data values.""" 
     420        for row, val in dct.items(): 
     421            self.SetItemData(row, val) 
     422         
     423     
     424    # Property get/set/del methods follow. Scroll to bottom to see the property 
     425    # definitions themselves. 
     426    def _getAutoConvertToString(self): 
     427        return self._autoConvertToString 
     428 
     429    def _setAutoConvertToString(self, val): 
     430        if self._constructed(): 
     431            self._autoConvertToString = val 
     432        else: 
     433            self._properties["AutoConvertToString"] = val 
     434 
     435 
     436    def _getChoices(self): 
     437        dabo.errorLog(_("'Choices' is not a valid property for a dListControl.")) 
     438        return [] 
     439 
     440         
     441    def _getColumnCount(self): 
     442        return self.GetColumnCount() 
     443 
     444    def _setColumnCount(self, val): 
     445        if self._constructed(): 
     446            cc = self.GetColumnCount() 
     447            if val < cc: 
     448                # Remove rightmost columns 
     449                while val < self.GetColumnCount(): 
     450                    self.removeColumn() 
     451            elif val > cc: 
     452                while val > self.GetColumnCount(): 
     453                    self.addColumn(_("Column %s") % self.GetColumnCount())               
     454        else: 
     455            self._properties["ColumnCount"] = val 
     456         
     457         
     458    def _getHeaderVisible(self): 
     459        return not self._hasWindowStyleFlag(wx.LC_NO_HEADER) 
     460         
     461    def _setHeaderVisible(self, val): 
     462        if bool(val): 
     463            self._delWindowStyleFlag(wx.LC_NO_HEADER) 
     464        else: 
     465            self._addWindowStyleFlag(wx.LC_NO_HEADER) 
     466 
     467 
     468    def _getHitIndex(self): 
     469        return self._hitIndex 
     470         
     471 
     472    def _getHorizontalRules(self, val): 
     473        return self._hasWindowStyleFlag(wx.LC_HRULES) 
     474         
     475 
     476    def _setHorizontalRules(self, val): 
     477        if bool(val): 
     478            self._addWindowStyleFlag(wx.LC_HRULES) 
     479        else: 
     480            self._delWindowStyleFlag(wx.LC_HRULES) 
     481             
     482 
     483    def _getLastSelectedIndex(self): 
     484        return self._lastSelectedIndex 
     485         
     486 
     487    def _getMultipleSelect(self): 
     488        return not self._hasWindowStyleFlag(wx.LC_SINGLE_SEL) 
     489 
     490    def _setMultipleSelect(self, val): 
     491        if bool(val): 
     492            self._delWindowStyleFlag(wx.LC_SINGLE_SEL) 
     493        else: 
     494            self._addWindowStyleFlag(wx.LC_SINGLE_SEL) 
     495             
     496 
     497    def _getRowCount(self): 
     498        return self.GetItemCount() 
     499         
     500         
    336501    def _getSelectedIndices(self): 
    337502        ret = [] 
     
    345510        return ret 
    346511         
    347          
    348512    def _setSelectedIndices(self, selList): 
    349513        if self._constructed(): 
     
    356520             
    357521 
    358     def _getColumnCount(self): 
    359         return self.GetColumnCount() 
    360  
    361     def _setColumnCount(self, val): 
     522    def _getSortOnHeaderClick(self): 
     523        return self._sortOnHeaderClick 
     524 
     525    def _setSortOnHeaderClick(self, val): 
    362526        if self._constructed(): 
    363             cc = self.GetColumnCount() 
    364             if val < cc: 
    365                 # Remove rightmost columns 
    366                 while val < self.GetColumnCount(): 
    367                     self.removeColumn() 
    368             elif val > cc: 
    369                 while val > self.GetColumnCount(): 
    370                     self.addColumn(_("Column %s") % self.GetColumnCount())               
    371         else: 
    372             self._properties["ColumnCount"] = val 
    373          
    374          
    375     def _getRowCount(self): 
    376         return self.GetItemCount() 
    377          
    378          
    379     def _getHitIndex(self): 
    380         return self._hitIndex 
    381          
    382  
    383     def _getLastSelectedIndex(self): 
    384         return self._lastSelectedIndex 
    385          
    386  
    387     def _getMultipleSelect(self): 
    388         return not self._hasWindowStyleFlag(wx.LC_SINGLE_SEL) 
    389          
    390  
    391     def _setMultipleSelect(self, val): 
    392         if bool(val): 
    393             self._delWindowStyleFlag(wx.LC_SINGLE_SEL) 
    394         else: 
    395             self._addWindowStyleFlag(wx.LC_SINGLE_SEL) 
    396              
    397  
    398     def _getHeaderVisible(self): 
    399         return not self._hasWindowStyleFlag(wx.LC_NO_HEADER) 
    400          
    401  
    402     def _setHeaderVisible(self, val): 
    403         if bool(val): 
    404             self._delWindowStyleFlag(wx.LC_NO_HEADER) 
    405         else: 
    406             self._addWindowStyleFlag(wx.LC_NO_HEADER) 
    407              
    408  
    409     def _getHorizontalRules(self, val): 
    410         return self._hasWindowStyleFlag(wx.LC_HRULES) 
    411          
    412  
    413     def _setHorizontalRules(self, val): 
    414         if bool(val): 
    415             self._addWindowStyleFlag(wx.LC_HRULES) 
    416         else: 
    417             self._delWindowStyleFlag(wx.LC_HRULES) 
    418              
     527            self._sortOnHeaderClick = val 
     528        else: 
     529            self._properties["SortOnHeaderClick"] = val 
     530 
    419531 
    420532    def _getValue(self): 
     
    425537        colcnt = self.ColumnCount 
    426538        vc = self.ValueColumn 
    427          
    428539        if idx is not None: 
    429540            if 0 <= vc <= colcnt: 
     
    434545            return item.GetText() 
    435546             
    436  
    437547    def _setValue(self, val): 
    438548        if self._constructed(): 
     
    457567        return ret 
    458568         
    459      
     569 
    460570    def _getValCol(self): 
    461571        return self._valCol 
     572 
    462573    def _setValCol(self, val): 
    463574        self._valCol = val 
     
    466577    def _getVerticalRules(self, val): 
    467578        return self._hasWindowStyleFlag(wx.LC_VRULES) 
    468          
    469579 
    470580    def _setVerticalRules(self, val): 
     
    475585 
    476586 
     587    AutoConvertToString = property(_getAutoConvertToString, _setAutoConvertToString, None, 
     588            _("""When True (default), all non-string values are forced to strings. When False,  
     589            attempting to use a non-string value will throw an error.  (bool)""")) 
     590     
     591    Choices = property(_getChoices, None, None, 
     592            _("""Since dListControl doesn't have the equivalent to 'Choices' as the  
     593            other item controls do, this will return an empty list and print a warning  
     594            message. (read-only) (list)""")) 
     595     
    477596    ColumnCount = property(_getColumnCount, _setColumnCount, None, 
    478597            _("Number of columns in the control  (int)")) 
     
    501620    SelectedIndices = property(_getSelectedIndices, _setSelectedIndices, None,  
    502621            _("Returns a list of selected row indices.  (list of int)") ) 
     622     
     623    SortOnHeaderClick = property(_getSortOnHeaderClick, _setSortOnHeaderClick, None, 
     624            _("When True (default), clicking a column header cycles the sorting on that column.  (bool)")) 
    503625     
    504626    Value = property(_getValue, _setValue, None, 
     
    522644    DynamicVerticalRules = makeDynamicProperty(VerticalRules) 
    523645     
    524  
     646     
     647     
    525648class _dListControl_test(dListControl): 
    526649    def afterInit(self): 
    527         self.setColumns( ("Main Column", "Another Column") ) 
     650        self.setColumns( ("Title", "Subtitle", "Release Year") ) 
    528651        self.setColumnWidth(0, 150) 
    529         self.append( ("The Phantom Menace", "Episode 1") ) 
    530         self.append( ("Attack of the Clones", "Episode 2") ) 
    531         self.append( ("Revenge of the Sith", "Episode 3") ) 
    532         self.insert( ("A New Hope", "Episode 4") ) 
     652        self.setColumnWidth(1, 100) 
     653        self.setColumnWidth(2, 200) 
     654        self.append( ("The Phantom Menace", "Episode 1", 1999) ) 
     655        self.append( ("Attack of the Clones", "Episode 2", 2002) ) 
     656        self.append( ("Revenge of the Sith", "Episode 3", 2005) ) 
     657        self.append( ("A New Hope", "Episode 4", 1977) ) 
     658        self.append( ("The Empire Strikes Back", "Episode 5", 1980) ) 
     659        self.append( ("Return of the Jedi", "Episode 6", 1983) ) 
     660 
    533661 
    534662    def initProperties(self): 
    535         self.Width = 275 
    536         self.Height = 200 
    537663        self.MultipleSelect = True 
    538664        self.HorizontalRules = True 
     
    554680 
    555681 
    556 if __name__ == '__main__': 
    557     class TestForm(dabo.ui.dForm): pass 
    558     app = dabo.dApp(MainFormClass=TestForm) 
    559     app.setup() 
    560      
    561     mf = app.MainForm 
    562     mf.testListControl = _dListControl_test(mf) 
    563  
    564     app.start() 
    565  
     682if __name__ == "__main__": 
     683    import test 
     684    test.Test().runTest(_dListControl_test)