4

我将一些行放在列表控件中,然后相当快地更新它们 - 通常数据来自总线 - 整个列表闪烁很多。阻止它这样做真的很好。

我已经尽可能地减少了代码,同时仍然保持我在下面的示例中所做的事情的一般外观。

lisctrl 是在 wx.Notebook 中还是在普通的 wx.Panel 中似乎并不重要,所以我把笔记本放在那里。

我已经开始研究双缓冲,但想看看是否有其他东西可以先尝试。

在 Windows 7 上使用 wxPython 2.8.12.1 执行此操作。不过也发生在 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()
4

2 回答 2

3

我会推荐使用 wxPython 的 Freeze and Thaw 方法。基本上你冻结小部件,更新它,然后你解冻它。

于 2012-10-19T13:17:02.283 回答
3

好的,我想我终于有一个可行的(如果部分)回答闪烁问题。

我发现很多人通过在面板上启用双缓冲取得了成功,主要是在图像闪烁方面。所以在 MessagesTab() 类中,在 wx.Panel init 之后,我插入了这一行;

self.SetDoubleBuffered(True)

这使得列表控件更新平滑,但调整 GUI 大小或将鼠标悬停在 listctrl 列标题上会使内容闪烁很多或完全消失。作为 Windows 7,当鼠标在其上时,标题会根据操作系统主题突出显示。由于这似乎是原因,我的解决方法是将 wx.LC_NO_HEADER 标志添加到 MessageList() 样式参数。所以 listctrl 没有标题,但现在没有任何原因导致闪烁或消失的文本,我可以用一些静态文本替换它。调整大小也没有问题。

那么,列标题上生成的事件可能存在一些问题?

于 2013-05-28T06:07:54.697 回答