wxPython wxListCtrl 选定行颜色

发布于 2024-07-14 02:31:40 字数 70 浏览 4 评论 0原文

我希望将某些行选择为红色而不是标准颜色(窗口上为蓝色),以便我可以指示状态。 有人知道这在 wxPython 中是否可行吗?

I want to have certain rows selected color be red instead of the standard color (blue on windows) so that I can indicate status. Anyone know if this is possible in wxPython?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你爱我像她 2024-07-21 02:31:40

在从 wx.ListCtrl 派生的类中,查看一下重写

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)

因此,在本例中,我将在默认属性和浅灰色属性之间交替背景颜色。

每行绘制时都会调用此函数,因此您可以使用它来指示各种状态。 如果选择行应该是一个简单的情况。

In your class that you derive from wx.ListCtrl, take a look at overriding

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

Where the item attributes are initialized ahead of time using:

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

So in this case, I'm alternating background colors between the default, and a light Gray attribute.

This function is called for each row as its painted, so you can use it to indicate all sorts of status. If row is selected should be an easy case.

绅士风度i 2024-07-21 02:31:40

为了做你想做的事,即在选择某些项目时有不同的选择颜色,你需要进入 win32。 幸运的是,在 python 中做到这一点并不难。 然而,它确实使您的代码依赖于平台。 今天在一个小程序中尝试了一下。 如果流派不是“摇滚”,我会将选择设置为橙色。 这是一些屏幕截图。

选定的摇滚项目
替代文本

已选择混合项目。 请注意,RnB 和 Blues 是用 Orange 选择的
替代文本 http://img258.imageshack.us/img258/1307/soshot2.jpg< /a>

这是代码。 乍一看很吓人,但如果你了解任何 win32,那就没那么糟糕了。 我使用 pywin32 包和 std ctypes 库。 我必须定义一些 SDK 常量,因为它们在 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()

In order to do what you want, i.e. have a different selection color when certain items are selected, you will need to drop into win32. Fortunately, it is not too hard to do that in python. It does however make your code platform dependent. I tried it out today in a small program. If the Genre is not "Rock" I make the selection orange. Here are some screenshots.

Rock Items Selected
alt text

Mixed Items Selected. Notice the RnB and Blues are selected with Orange
alt text http://img258.imageshack.us/img258/1307/soshot2.jpg

Here is the code. It looks scary at first but if you know any win32, its not that bad. I make use of the pywin32 package and the std ctypes libraries. I had to define some of the SDK constants as they were not available in the win32con module.

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文