2009-02-26 128 views
4

ステータスを示すことができるように、選択された特定の行を標準色(ウィンドウ上の青色)の代わりに赤色にしたい。これがwxPythonで可能かどうかは誰にでも分かりますか?あなたはwx.ListCtrlから派生クラスでwxPython wxListCtrl選択された行の色

答えて

4

特定のアイテムが選択されているときに異なる選択色を表示するには、win32にドロップする必要があります。幸いにも、それはあまりにも難しいことではないpythonでそれを行う。ただし、コード・プラットフォームに依存します。私は今日、小さなプログラムで試してみました。ジャンルが「ロック」でない場合は、選択をオレンジ色にします。ここにいくつかのスクリーンショットがあります。

ロックアイテムが選択された alt text http://img26.imageshack.us/img26/981/soshot1.jpg

ミックスアイテムを選択しました。 RnBとブルースがオレンジで選択されていることに注意してください。 alt text http://img258.imageshack.us/img258/1307/soshot2.jpg

ここにコードがあります。最初は怖かったですが、win32を知っていれば、それほど悪くはありません。私はpywin32パッケージとstd ctypesライブラリを利用しています。 win32conモジュールでは利用できなかったSDK定数のいくつかを定義しなければなりませんでした。

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

、アイテム属性を使用して事前に初期化され

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

オーバーライドを見てみましょう。この場合にはそう

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

を、私はデフォルトと明るいグレーの属性の間で背景色を交互にしています。

この関数は、描画された行ごとに呼び出されるため、すべての種類のステータスを示すために使用できます。行が選択されている場合は、簡単なケースです。

関連する問題