2013年12月19日 星期四

[紀錄] Added image items and checkboxs in a ListCtrl


一直遇到一個問題,本來不想面對的。孰不知,終究還是要還... 

之前利用了listmix的CheckListCtrlMixin在list table上增加checkbox。
不過一直遇到一個奇怪的現象,就是當我新增了一個image list後
image list的圖案會覆蓋在checkbox的位置上,導致我的checkbox被吃掉了 =口=

Google了好久,一直無法找到方法。
一直不斷的改code亂試後,猜測原因應該是CheckListCtrlMixin有使用到image list
而我在新增image list後會把原本內建的覆蓋掉,導致checkbox的圖案變成我新增的圖案。
原本想將新增的圖案放入內建的image list中,但發現沒有append之類的method可用。

無計可施之下,只好繼續Google
皇天不負苦心人,查到了一個網頁: wxPython.lib.mixins.listctrl


其中,以下這段資料,證實了我的想法是正確的。
DESCRIPTION:
    This script provide a mixin for ListCtrl which add a checkbox in the first
    column of each row. It is inspired by limodou's CheckList.py(which can be
    got from his NewEdit) and improved:
        - You can just use InsertStringItem() to insert new items;
        - Once a checkbox is checked/unchecked, the corresponding item is not
          selected;
        - You can use SetItemData() and GetItemData();
        - Interfaces are changed to OnCheckItem(), IsChecked(), CheckItem().

    You should not set a imagelist for the ListCtrl once this mixin is used.

這下該怎麼辦呢?
索性就如法炮製,建置一個新的image list,放入了checkbox以及自己所要增加的圖案。


程式說明:listctrl會顯示所有的資料,預設是將所有的checkbox打勾。
當按下Execution button後,會依序對每個item顯示打勾的圖案。

下方就是這個sample的source code。
當然,由於我沒有附上圖片檔,所以記得要自己加上一個圖片檔喔! 


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

import sys
import time
import locale
import re
import threading

from wx import ImageFromStream, BitmapFromImage
import cStringIO, zlib

# loaded the row data of checked image and unchecked image
def getUncheckData():
    return zlib.decompress("Please copy it.")

def getUncheckBitmap():
    return BitmapFromImage(getUncheckImage())

def getUncheckImage():
    stream = cStringIO.StringIO(getUncheckData())
    return ImageFromStream(stream)

def getCheckData():
    return zlib.decompress("Please copy it.")

def getCheckBitmap():
    return BitmapFromImage(getCheckImage())

def getCheckImage():
    stream = cStringIO.StringIO(getCheckData())
    return ImageFromStream(stream)

# list data
musicdata = {
1 : ("Jerry", "Man"),
2 : ("Annie", "Woman"),
3 : ("Nick", "Man"),
4 : ("Cosmo", "Man"),
5 : ("Always", "Man"),
6 : ("Jason", "Man"),
7 : ("James", "Man"),
8 : ("Andrew", "Man"),
9 : ("Ashley", "Woman"),
10: ("Stephanie", "Woman"),
11: ("Claire", "Woman"),
12: ("Fran", "Man")
}

# a thread to check the item sequentially
class ExecutionThread(threading.Thread):
    def __init__(self, parent):
        threading.Thread.__init__(self)
        self.parent = parent
    
    # --------------------------------------------------
    # run thread
    # --------------------------------------------------
    def run(self):
        for itemIdx in range(self.parent.list.GetItemCount()):
            time.sleep(1)
            self.parent.list.SetItemColumnImage(itemIdx, 3, self.parent.list.orange_image)

# added the listmix.CheckListCtrlMixin to add checkbox on eveny row
class TestVirtualList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
    def __init__(self, parent, check_image=None, uncheck_image=None):
        wx.ListCtrl.__init__( self, parent, -1, style=wx.LC_REPORT | wx.BORDER_SUNKEN | wx.LC_VRULES | wx.LC_HRULES)
        listmix.CheckListCtrlMixin.__init__(self)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        
        # overwrote the image list
        self.__imagelist_ = wx.ImageList(16, 16)
        if not check_image:
            check_image = BitmapFromImage(getCheckImage())
        if not uncheck_image:
            uncheck_image = BitmapFromImage(getUncheckImage())
        self.uncheck_image = self.__imagelist_.Add(uncheck_image)
        self.check_image = self.__imagelist_.Add(check_image)
        
        orangeBmp = wx.Bitmap('checkOrange_16.png')
        self.orange_image = self.__imagelist_.Add(orangeBmp)
        
        self.SetImageList(self.__imagelist_, wx.IMAGE_LIST_SMALL)
        self.__last_check_ = None
        
        # created the columns
        self.InsertColumn(0, "Index", width=75)
        self.InsertColumn(1, "Name", width=100)
        self.InsertColumn(2, "Sex", width=150)
        self.InsertColumn(3, "Status", width=150)
  
    # -------------------------------------------------- 
    # Print the status of the pressed checkbox
    # --------------------------------------------------
    def OnCheckItem(self, index, flag):
        print(index, flag)

# added the listmix.ColumnSorterMixin to do the sort function
class TestFrame(wx.Frame, listmix.ColumnSorterMixin):
    def __init__(self, parent, id):
        self.frame = wx.Frame.__init__(self, parent, id, 'Sample List', size=(600, 500))
        self.panel = wx.Panel(self, wx.ID_ANY)
        
        self.list = TestVirtualList(self.panel)
        self.list.foundList = {}
        self.sortList = []
        
        self.readButton = wx.Button(self.panel, -1, "Get Sort List")
        self.readButton.SetMaxSize((25,300))
        self.Bind(wx.EVT_BUTTON, self.onGetSortList, self.readButton)
        
        self.execButton = wx.Button(self.panel, -1, "Execute")
        self.execButton.SetMaxSize((25,300))
        self.Bind(wx.EVT_BUTTON, self.onExecution, self.execButton)
        
        # put the musicdata into the foundList
        # the foundlist is same with the itemDataMap
        items = musicdata.items()
        index = 0
        for key, data in items:
            self.list.InsertStringItem(index, str(key))
            self.list.SetStringItem(index, 1, str(data[0]))
            self.list.SetStringItem(index, 2, str(data[1]))
            #self.list.SetStringItem(index, 3, str(data[2]))
            self.list.SetItemData(index, key)
            self.list.CheckItem(index)
            
            tmpNode = {key:(str(index+1),str(data[0]), str(data[1]))}
            self.list.foundList.update(tmpNode)
            index += 1
          
        # need added the itemDataMap due to use the ColumnSorterMixin
        self.itemDataMap = self.list.foundList
        listmix.ColumnSorterMixin.__init__(self, 3)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
        
        buttonHbox = wx.BoxSizer(wx.HORIZONTAL)
        buttonHbox.Add(self.readButton, 1, wx.LEFT)
        buttonHbox.Add(self.execButton, 1, wx.LEFT)
        
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(self.list, 1, wx.EXPAND)
        self.vbox.Add(buttonHbox)
        self.panel.SetSizer(self.vbox)
        
    # --------------------------------------------------
    # Used by the ColumnSorterMixin
    # --------------------------------------------------
    def GetListCtrl(self):
        return self.list

    # --------------------------------------------------
    # Used by the ColumnSorterMixin
    # --------------------------------------------------
    def OnColClick(self, event):
        event.Skip()

    # --------------------------------------------------
    # Return customSorter
    # -------------------------------------------------- 
    
    def GetColumnSorter(self):
        return self.CustColumnSorter
    
    # --------------------------------------------------
    # Sorted the list
    # -------------------------------------------------- 
    
    def CustColumnSorter(self, key1, key2):
        print "==========", key1, key2, "=========="
        col = self._col
        ascending = self._colSortFlag[col]
        item1 = self.itemDataMap[key1][col]
        item2 = self.itemDataMap[key2][col]
        print "col:", col, " ascending", ascending, " item1:",item1," item2:",item2
        #if str(item1).isdigit() and str(item2).isdigit():
        if int(col) == 0:
            print "1"
            # sort digital value
            cmpVal = cmp(int(item1), int(item2))
        else:
            print "2"
            # sort string
            cmpVal = locale.strcoll(str(item1), str(item2))

        # If the items are equal then pick something else to make the sort value unique
        if cmpVal == 0:
            cmpVal = cmp(*self.GetSecondarySortValues(col, key1, key2))

        if ascending:
            print "val:",cmpVal
            return cmpVal
        else:
            print "val:",-cmpVal
            return -cmpVal
    

    # --------------------------------------------------
    # Get the sorted list
    # -------------------------------------------------- 
    def onGetSortList(self, event):
        # list table already sorted, but itemDataMap didn't sorted
        # get the item text at every row to grab the sorted list
        self.sortList = []
        
        # get the item text and compared with itemDataMap
        for idx in range(len(self.itemDataMap)):
            if self.list.IsChecked(idx):
                item = self.list.GetItem(idx, 0) 
                findIdx = item.Text
            
                for getIdx in range(len(self.itemDataMap)):
                    if self.itemDataMap[getIdx+1][0] == findIdx:
                        s = self.itemDataMap[getIdx+1][1]
                        self.sortList.append(s)
        
        print self.sortList
    
    # --------------------------------------------------
    # call a thread to check the item
    # -------------------------------------------------- 
    def onExecution(self, event):
        self.thread = ExecutionThread(self)
        self.thread.start()  
           

if __name__ == '__main__':
    app = wx.PySimpleApp()
    f = TestFrame(None, id=1)
    f.Show()
    app.MainLoop()

由於Html的編碼問題,所以source code中有兩個地方請複製下方的code覆蓋。

def getUncheckData():
    return zlib.decompress(
"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
\x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xbb{\xba8\x86X\xf4\
&\xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
\x90'\x01\x08v\xec]\xd3\xa3qvU`l\x81\xd9\xd18\t\xd3\x84+\x0cll[\xa6t\xcc9\
\xd4\xc1\xda\xc3<O\x9a1\xc3\x88\xc3j\xfa\x86_\xee@#\x19<]\xfd\\\xd69%4\x01\
\x00\xdc\x80-\x05" )

def getCheckData():

    return zlib.decompress(
'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\
\x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe47{\xba8\x86X\xf4&\
\xa7\xa4$\xa5-`1\x08\\2\xbb\xb1\xb1\x91\xf5\xd8\x84o\xeb\xff\xfaw\x1d[.=[2\
\x90\'\x01\x08v\xec\\2C\xe3\xec+\xc3\xbd\x05fG\xe3\x14n1\xcc5\xad\x8a8\x1a\
\xb9\xa1\xeb\xd1\x853-\xaa\xc76\xecb\xb8i\x16c&\\\xc2\xb8\xe9Xvbx\xa1T\xc3U\
\xd6p\'\xbd\x85\x19\xff\xbe\xbf\xd7\xe7R\xcb`\xd8\xa5\xf8\x83\xe1^\xc4\x0e\
\xa1"\xce\xc3n\x93x\x14\xd8\x16\xb0(\x15q)\x8b\x19\xf0U\xe4\xb10\x08V\xa8\
\x99\xf3\xdd\xde\xad\x06t\x0e\x83\xa7\xab\x9f\xcb:\xa7\x84&\x00\xe0HE\xab' )


希望有幫助到你們!

1 則留言: