سؤال

أريد أن يكون هناك معينة من الصفوف المختارة يكون اللون الأحمر بدلا من اللون القياسي (الأزرق على النوافذ) حتى أتمكن من الإشارة إلى الحالة. أي شخص يعرف إذا كان هذا ممكن في Wxpython؟

هل كانت مفيدة؟

المحلول

من أجل القيام بما تريد، فإن أي لون اختيار مختلف عند تحديد عناصر معينة، ستحتاج إلى الانتقال إلى Win32. لحسن الحظ، ليس من الصعب القيام بذلك في بيثون. ومع ذلك، فإنه يجعل منصة التعليمات البرمجية تعتمد. جربت ذلك اليوم في برنامج صغير. إذا كان هذا النوع ليس "صخور"، فأنا أقوم باختيار Orange. وهنا بعض لقطات الشاشة.

العناصر الصخرية المحددةalt text

العناصر المختلطة المحددة. لاحظ اختيار RNB والبلوز مع البرتقالنص Alt http://img258.imageshack.us/img258/1307/soshot2.jpg.

هنا هو الرمز. يبدو مخيفا في البداية ولكن إذا كنت تعرف أي Win32، فإنه ليس سيئا. أنا الاستفادة من pywin32. حزمة ومكتبات STD CTTYPES. اضطررت إلى تحديد بعض ثوابت 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()

نصائح أخرى

في صفك الذي تستمد من 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)

لذلك في هذه الحالة، أنا بالتناوب ألوان الخلفية بين الافتراضي، وسمة رمادية فاتحة.

يتم استدعاء هذه الوظيفة لكل صف حسب رسمها، حتى تتمكن من استخدامها للإشارة إلى جميع أنواع الحالة. إذا تم تحديد الصف يجب أن يكون حالة سهلة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top