2009-02-26 6 views

Répondre

4

Afin de faire ce que vous voulez, c'est-à-dire d'avoir une couleur de sélection différente lorsque certains éléments sont sélectionnés, vous devrez passer dans win32. Heureusement, ce n'est pas trop difficile de le faire en python. Cela rend toutefois votre plateforme de code dépendante. Je l'ai essayé aujourd'hui dans un petit programme. Si le genre n'est pas "Rock", je rends la sélection orange. Voici quelques captures d'écran.

Articles Rock sélectionnés alt text http://img26.imageshack.us/img26/981/soshot1.jpg

Articles mixtes sélectionnés. Notez que RnB et Blues sont sélectionnés avec Orange alt text http://img258.imageshack.us/img258/1307/soshot2.jpg

Voici le code. Il semble effrayant au début, mais si vous connaissez un win32, ce n'est pas si mal. J'utilise le package pywin32 et les bibliothèques std ctypes. J'ai dû définir certaines des constantes du SDK car elles n'étaient pas disponibles dans le module win32con.

import sys 
import wx 
import wx.lib.mixins.listctrl as listmix 

import win32api 
import win32gui 
import win32con 
import win32gui_struct 
import commctrl 
import ctypes 
from ctypes.wintypes import BOOL, HWND, RECT, UINT, DWORD, HDC, DWORD, LPARAM, COLORREF 

LVM_FIRST = 0x1000 
LVM_GETSUBITEMRECT=(LVM_FIRST + 56) 
LVIR_BOUNDS    =0 
LVIR_ICON    =1 
LVIR_LABEL    =2 
LVIR_SELECTBOUNDS  =3 
DEFAULT_GUI_FONT =17 

#LPNMHDR 
class NMHDR(ctypes.Structure): 
    pass 
INT = ctypes.c_int 
NMHDR._fields_ = [('hwndFrom', HWND), ('idFrom', UINT), ('code', INT)] 
LPNMHDR = ctypes.POINTER(NMHDR) 

#LPNMCUSTOMDRAW 
class NMCUSTOMDRAW(ctypes.Structure): 
    pass 
NMCUSTOMDRAW._fields_ = [('hdr', NMHDR), ('dwDrawStage', DWORD), ('hdc', ctypes.c_int), 
         ('rc', RECT), ('dwItemSpec', DWORD), ('uItemState', UINT), 
         ('lItemlParam', LPARAM)] 
LPNMCUSTOMDRAW = ctypes.POINTER(NMCUSTOMDRAW) 

#LPNMLVCUSTOMDRAW 
class NMLVCUSTOMDRAW(ctypes.Structure): 
    pass 
NMLVCUSTOMDRAW._fields_ = [('nmcd', NMCUSTOMDRAW), 
          ('clrText', COLORREF), 
          ('clrTextBk', COLORREF), 
          ('iSubItem', ctypes.c_int), 
          ('dwItemType', DWORD), 
          ('clrFace', COLORREF), 
          ('iIconEffect', ctypes.c_int), 
          ('iIconPhase', ctypes.c_int), 
          ('iPartId', ctypes.c_int), 
          ('iStateId', ctypes.c_int),       
          ('rcText', RECT), 
          ('uAlign', UINT) 
          ] 
LPNMLVCUSTOMDRAW = ctypes.POINTER(NMLVCUSTOMDRAW) 


musicdata = { 
1 : ("Bad English", "The Price Of Love", "Rock"), 
2 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"), 
3 : ("George Michael", "Praying For Time", "Rock"), 
4 : ("Gloria Estefan", "Here We Are", "Rock"), 
5 : ("Linda Ronstadt", "Don't Know Much", "Rock"), 
6 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"), 
7 : ("Paul Young", "Oh Girl", "Rock"), 
8 : ("Paula Abdul", "Opposites Attract", "Rock"), 
9 : ("Richard Marx", "Should've Known Better", "Rock"), 
10 : ("Bobby Brown", "My Prerogative", "RnB"), 
} 




class MyListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): 
    def __init__(self, parent, ID, pos=wx.DefaultPosition, 
       size=wx.DefaultSize, style=0): 
     wx.ListCtrl.__init__(self, parent, ID, pos, size, style) 
     listmix.ListCtrlAutoWidthMixin.__init__(self) 

    def ShouldCustomDraw(self, row): 
     if self.IsSelected(row): 
      listitem = self.GetItem(row, 2) 
      genre = listitem.GetText() 

      return genre != "Rock" 


    def CustomDraw(self, lpcd):   
     if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_PREPAINT: 
      return (True, commctrl.CDRF_NOTIFYITEMDRAW) 

     if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_ITEMPREPAINT:     
      if self.ShouldCustomDraw(lpcd.contents.nmcd.dwItemSpec): 
       #do custom drawing for non Rock selected rows 
       #paint the selection background 
       color = win32api.RGB(255, 127, 0) #orange 
       brush = win32gui.CreateSolidBrush(color) 
       r = lpcd.contents.nmcd.rc 
       win32gui.FillRect(int(lpcd.contents.nmcd.hdc), (r.left+4, r.top, r.right, r.bottom), brush)     
       win32gui.DeleteObject(brush) 
       return (True, commctrl.CDRF_NOTIFYSUBITEMDRAW)      

     if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_ITEMPREPAINT|commctrl.CDDS_SUBITEM:     
      row = lpcd.contents.nmcd.dwItemSpec 
      col = lpcd.contents.iSubItem 
      item = self.GetItem(row, col) 
      text = item.GetText() 
      #paint the text 
      rc = RECT() 
      rc.top = col 
      if col > 0: 
       rc.left = LVIR_BOUNDS 
      else: 
       rc.left = LVIR_LABEL 
      success = win32api.SendMessage(self.Handle, LVM_GETSUBITEMRECT, row, ctypes.addressof(rc)) 
      if col > 0: 
       rc.left += 5 
      else: 
       rc.left += 2 
      rc.top += 2 

      if success:     
       oldColor = win32gui.SetTextColor(lpcd.contents.nmcd.hdc, win32gui.GetSysColor(win32con.COLOR_HIGHLIGHTTEXT))     
       win32gui.DrawText(lpcd.contents.nmcd.hdc, text, len(text), (rc.left, rc.top, rc.right, rc.bottom), win32con.DT_LEFT|win32con.DT_VCENTER) 
       win32gui.SetTextColor(lpcd.contents.nmcd.hdc, oldColor)         

      return (True, commctrl.CDRF_SKIPDEFAULT) 


     # don't need custom drawing 
     return (True, commctrl.CDRF_DODEFAULT) 


class MyFrame(wx.Frame): 
    def __init__(self, *args, **kwds): 
     wx.Frame.__init__(self, *args, **kwds) 
     self._sizer = wx.BoxSizer(wx.VERTICAL) 
     tID = wx.NewId() 
     self._ctl = MyListCtrl(self, tID, 
           style=wx.LC_REPORT 
           #| wx.BORDER_SUNKEN 
           | wx.BORDER_NONE 
           | wx.LC_EDIT_LABELS 
           | wx.LC_SORT_ASCENDING 
           #| wx.LC_NO_HEADER 
           #| wx.LC_VRULES 
           #| wx.LC_HRULES 
           #| wx.LC_SINGLE_SEL 
           ) 
     self._sizer.Add(self._ctl, 1, wx.EXPAND, 3) 
     self.PopulateList() 

     self.oldWndProc = win32gui.SetWindowLong(self.GetHandle(), win32con.GWL_WNDPROC, self.MyWndProc) 


    def MyWndProc(self, hWnd, msg, wParam, lParam): 

     if msg == win32con.WM_NOTIFY: 
      hwndFrom, idFrom, code = win32gui_struct.UnpackWMNOTIFY(lParam) 
      if code == commctrl.NM_CUSTOMDRAW and hwndFrom == self._ctl.Handle:     
       lpcd = ctypes.cast(lParam, LPNMLVCUSTOMDRAW) 
       retProc, retCode = self._ctl.CustomDraw(lpcd) 

       if retProc: 
        return retCode 


     # Restore the old WndProc. Notice the use of wxin32api 
     # instead of win32gui here. This is to avoid an error due to 
     # not passing a callable object. 
     if msg == win32con.WM_DESTROY: 
      win32api.SetWindowLong(self.GetHandle(), 
           win32con.GWL_WNDPROC, 
           self.oldWndProc) 

     # Pass all messages (in this case, yours may be different) on 
     # to the original WndProc 
     return win32gui.CallWindowProc(self.oldWndProc, 
            hWnd, msg, wParam, lParam) 

    def PopulateList(self): 
     self._ctl.InsertColumn(0, "Artist") 
     self._ctl.InsertColumn(1, "Title") 
     self._ctl.InsertColumn(2, "Genre") 

     items = musicdata.items() 

     for key, data in items:    
      index = self._ctl.InsertStringItem(sys.maxint, data[0]) 
      self._ctl.SetStringItem(index, 1, data[1]) 
      self._ctl.SetStringItem(index, 2, data[2]) 
      self._ctl.SetItemData(index, key) 


     self._ctl.SetColumnWidth(0, wx.LIST_AUTOSIZE) 
     self._ctl.SetColumnWidth(1, wx.LIST_AUTOSIZE) 
     self._ctl.SetColumnWidth(2, 100) 

     self.currentItem = 0 

class MyApp(wx.App): 
    def OnInit(self): 
     frame = MyFrame(None, -1, 'wxListCtrl StackOverflow') 
     frame.Show() 
     self.SetTopWindow(frame) 
     return 1 

if __name__ == "__main__": 
    app = MyApp(0) 
    app.MainLoop() 
7

Dans votre classe que vous tirez de wx.ListCtrl, jetez un oeil à dominante

def OnGetItemAttr(self, item): 
    return self.normalAttr[item % 2] 
# 

Lorsque les attributs d'un élément sont initialisés à l'avance en utilisant:

self.normalAttr = [] 
    self.normalAttr.append(wx.ListItemAttr()) 
    grayAttr = wx.ListItemAttr() 
    grayAttr.SetBackgroundColour(lightGray) 
    self.normalAttr.append(grayAttr) 

Donc dans ce cas, J'alterne les couleurs d'arrière-plan par défaut, et un attribut gris clair.

Cette fonction est appelée pour chaque ligne telle qu'elle est peinte, vous pouvez donc l'utiliser pour indiquer toutes sortes de statuts. Si la ligne est sélectionnée devrait être un cas facile.

Questions connexes