2012-10-19 53 views
4

我在列表控件中添加了一些行,然後相當快地更新它們 - 通常是從總線上來的數據 - 並且整個列表閃爍了很多。停止這樣做真的很不錯。wxPython listctrl快速更新閃爍

我儘可能地減少了代碼,同時仍然保持了我在下面的示例中所做的一般外觀。

似乎並不重要,如果lisctrl是在wx.Notebook或只是一個普通的wx.Panel,所以我離開了筆記本。

我已經開始看雙緩衝,但想看看是否有其他的東西要先嚐試。

使用wxPython 2.8.12.1在Windows 7上執行此操作。雖然也發生在XP上。

import sys 
import time 
import logging 
import wx 
from random import randint 

UPDATE_MS=10 

class CanMsg(object): 
    def __init__(self, ID, type, len, data=None): 
     """Represents a CAN message""" 
     self.ID=ID   # 11/29-bit message identifier 
     self.MSGTYPE=type # Type of the message 
     self.LEN=len   # Data Length Code of the message (0..8) 
     if data: 
      self.DATA=data 
     else: 
      self.DATA=[0,]*len 

class EmulatorFrame(wx.Frame): 
    def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, 
           style=wx.DEFAULT_FRAME_STYLE): 
     wx.Frame.__init__(self, parent, id, title, pos, size, style) 
     # create frame menu and status bar 
     self.status = self.CreateStatusBar() 
     # create tab panels 
     panel = wx.Panel(self) 
     nb = wx.Notebook(panel) 
     self.messages_tab = MessagesTab(nb) 
     # self.messages_tab = MessagesTab(panel) 
     # add tab pages to notebook 
     nb.AddPage(self.messages_tab, 'CAN data') 
     self._nb = nb 

     sizer = wx.BoxSizer() 
     sizer.Add(nb, 1, wx.EXPAND) 
     # sizer.Add(self.messages_tab, 1, wx.EXPAND) 
     minSize=self.ClientToWindowSize(sizer.GetMinSize())  # get this as Info tab's min size is too small 
     panel.SetSizerAndFit(sizer) 
     self.SetInitialSize(minSize) 

     self.InitialiseTimers() 

    def InitialiseTimers(self): 
     # tab updates and test comparison timer 
     self.displayTimer=wx.Timer(self) 
     self.Bind(wx.EVT_TIMER, self.OnRefresh, self.displayTimer) 
     self.displayTimer.Start(UPDATE_MS) 
     # self.Bind(wx.EVT_IDLE, self.OnRefresh) 

    def OnRefresh(self, event): 
     self.messages_tab.Update(can_send, can_recv) 

class MessagesTab(wx.Panel): 
    def __init__(self, parent): 
     msg_size=450 # width of messge windows 
     wx.Panel.__init__(self, parent, wx.ID_ANY) 

     receivedLabel=wx.StaticText(self, wx.ID_ANY, 'Messages Received') 
     receivedLabel.SetForegroundColour('blue') 
     sentLabel=wx.StaticText(self, wx.ID_ANY, 'Messages Sent') 
     sentLabel.SetForegroundColour('dark slate blue') 
     SentMsgList = MessageList(self, size=wx.Size(msg_size,150)) 
     ReceivedMsgList = MessageList(self, size=wx.Size(msg_size,150)) 

     sizer=wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(sentLabel, 0, wx.EXPAND|wx.ALL, 2) 
     sizer.Add(SentMsgList, 1, wx.EXPAND|wx.ALL, 2) 
     sizer.Add(receivedLabel, 0, wx.EXPAND|wx.ALL, 2) 
     sizer.Add(ReceivedMsgList, 1, wx.EXPAND|wx.ALL, 2) 

     b = wx.Button(self, wx.ID_ANY, 'Clear messages', name='clear_stale') 
     self.Bind(wx.EVT_BUTTON, self.OnClearMessages, b) 
     sizer.Add(b, proportion=0, flag=wx.ALL, border=4) 

     self.SetSizer(sizer) 
     self.SentMsgList = SentMsgList 
     self.ReceivedMsgList = ReceivedMsgList 

    def Update(self, can_send, can_recv): 
     self.SentMsgList.Populate(can_send) 
     self.ReceivedMsgList.Populate(can_recv) 

    def OnClearMessages(self, event): 
     self.SentMsgList.DeleteAllItems() 
     self.ReceivedMsgList.DeleteAllItems() 

class MessageList(wx.ListCtrl): 
    def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition, 
         size=wx.DefaultSize, style=wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES|wx.LC_SORT_ASCENDING): 
     wx.ListCtrl.__init__(self, parent, ID, pos, size, style) 
     self.InsertColumn(0, "COB-ID") 
     self.InsertColumn(1, "Type") 
     self.InsertColumn(2, "Len") 
     self.InsertColumn(3, "Data") 
     self.InsertColumn(4, "Cycle [ms]") 
     self.SetColumnWidth(0, 60) 
     self.SetColumnWidth(1, 40) 
     self.SetColumnWidth(2, 40) 
     self.SetColumnWidth(3, 200) 
     self.SetColumnWidth(4, 75) 

    # either add messages to the listctrl or update the existing ones if 
    def Populate(self, msg_store): 
     item=-1 
     while 1: 
      item = self.GetNextItem(item, wx.LIST_NEXT_ALL, wx.LIST_STATE_DONTCARE) 
      if item == -1: break 
      if self.GetItemText(item) not in msg_store: 
       self.DeleteItem(item) 

     for msg_id in msg_store: 
      item = self.FindItem(-1, msg_id) 
      msg = msg_store.get(msg_id) 
      interval = randint(10,1000) 
      # insert new messages 
      if item == -1: 
       item = self.InsertStringItem(sys.maxint, msg_id) 
       self.SetStringItem(item, 1, 'std') 
      # fill in other columns 
      self.SetStringItem(item, 2, '%1d'%msg.LEN) 
      self.SetStringItem(item, 3, ' '.join(['%02x'%d for d in msg.DATA[:msg.LEN]])) 
      self.SetStringItem(item, 4, '%d'%interval) 
#==================================================================== 
#==================================================================== 
if __name__=='__main__': 
    msg=(0x180, 0, 8, range(1,9)) 
    can_send={} 
    can_recv={} 
    # just make up some simple messages for listctrl to display 
    # send msgs 
    for a in range(1,7): 
     this_msg=list(msg) 
     this_msg[0] += a 
     can_send[hex(this_msg[0])] = CanMsg(*msg) 
    # receive msgs 
    for a in range(1,10): 
     this_msg=list(msg) 
     this_msg[0] += 0x100+a 
     can_recv[hex(this_msg[0])] = CanMsg(*msg) 

    app=wx.App(0) # 0 arg stops stdout/stderr text box pop-up, messages go to console 
    frame = EmulatorFrame(None, wx.ID_ANY, 'Listctrl flicker test') 
    frame.Show(True) 
    app.MainLoop() 

回答

1

好吧我想我終於有一個可行的(如果部分)回答閃爍問題。

我發現很多人都成功了,主要是圖像閃爍,通過在面板上啓用雙緩衝。 所以在MessagesTab()類中,在wx.Panel初始化之後,我插入了行;

self.SetDoubleBuffered(True) 

這使得列表控件更新順利,但調整的GUI或將鼠標懸停在listctrl的列標題將使內容閃爍了很多或完全消失。當鼠標在上面時,Windows 7的標題會按照操作系統主題進行突出顯示。由於這似乎是我的解決辦法是將wx.LC_NO_HEADER標誌添加到MessageList()樣式arg。所以listctrl沒有標題,但是現在沒有什麼會導致閃爍或消失的文本,我可以用一些靜態文本替換它。調整大小也沒有問題。

所以也許有列標題上生成的事件有一些問題?

2

我會推薦使用wxPython的Freeze和Thaw方法。基本上你凍結小部件,更新它,然後解凍它。

+0

我試過凍結/解凍幾個地方,包括MessagesTab的更新方法和EmulatorFrame的OnRefresh方法的開始/結束。他們似乎從來沒有任何效果(在Windows上),這似乎是其他人找到的。 (還是)感謝你的建議。 – Paul

+0

我發現的唯一的其他答案早在2008年,它被建議使用ListCtrl的虛擬模式:http://wxpython-users.1045709.n5.nabble.com/Screen-flicker-while-updating-a-ListCtrl- td2362816.html –