WxPython listctrl flickers with quick updates

I put some lines in the list control and then update them pretty quickly - usually the data comes from the bus - and the whole list flickers quite a lot. It would be very nice to stop this.

I have reduced the code as much as I can, keeping the general appearance of what I am doing in the example below.

It looks like lisctrl is in wx.Notebook or just plain wx.Panel, so I left a notepad there.

I started looking at double buffering, but wanted to see if there was anything else to try first.

Doing this on Windows 7 using wxPython 2.8.12.1. It happens on XP, though.

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 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
source share
2 answers

Good. I think that finally I have a workable (if partial) answer to the flicker problem.

I found that a number of people had success, mainly with flickering images, by enabling double buffering on the panel. Therefore, in the MessagesTab () class, after initializing wx.Panel, I inserted a line;

 self.SetDoubleBuffered(True) 

This made the list management update smoother, but resizing the GUI or mousing over the column headers of the listctrl will make the content too flicker or disappear completely. Being Windows 7, the title is highlighted according to the OS theme when the mouse is on it. Since this, apparently, was the reason that my workaround was to add the wx.LC_NO_HEADER flag to the MessageList () style argument. Thus, listctrl does not have a title, but nothing causes the text to flicker or disappear, and I can replace it with some kind of static text. Resizing is also not a problem.

So maybe there is a problem with the events generated in the column headers?

+1
source

I would recommend using the wxPython Freeze and Thaw methods. Basically you lock the widget, refresh it, and then thaw.

+2
source

Source: https://habr.com/ru/post/1440695/


All Articles