簡體   English   中英

當OnGetItemText耗時太長時,wx.ListCtrl閃爍

[英]wx.ListCtrl flickers when OnGetItemText takes too long

我使用LC_VIRTUAL遇到了wx.ListCtrl的問題,並且無法找到解決問題的正確方法。

問題是如果OnGetItemText()花費太長時間來返回它的結果,控件的內容會閃爍。

到目前為止我嘗試過的解決方案:

  • SetDoubleBuffered - 如果在ListCtrl本身或父控件上完成,則會導致內容根本不顯示
  • 禁用后台擦除可修復閃爍但會產生重繪問題,尤其是在調整控件大小時。
  • Freeze()和Thaw()沒有任何效果,因為控件已經在重繪過程​​中。

到目前為止,我提出的唯一解決方案是在調用Refresh()之前緩存新數據。 但是,在某些情況下我可能需要顯示100,000條或更多記錄,因此提前緩存它們將無法正常工作。

下面是我構建的示例,用於演示此問題,但您可能需要根據CPU速度調整計數:

from __future__ import print_function, unicode_literals

import wx

class MyListCtrl ( wx.ListCtrl ):
    def OnGetItemText ( self, item, column ):
        ar = []
        for i in range ( 200000 ):
            ar.append ( i )
        return '{}'.format ( len ( ar ) )

class MyFrame ( wx.Frame ):
    lc = None
    timer = None

    def __init__ ( self, *args, **kwargs ):
        super ( MyFrame, self ).__init__ ( *args, **kwargs )
        self.timer = wx.Timer ( self )
        self.lc = MyListCtrl ( self, style=wx.LC_VIRTUAL|wx.LC_REPORT )
        self.lc.InsertColumn ( 0, 'Count1', width=75 )
        self.lc.InsertColumn ( 1, 'Count2', width=75 )
        self.lc.InsertColumn ( 2, 'Count3', width=75 )
        self.lc.InsertColumn ( 3, 'Count4', width=75 )
        self.lc.SetItemCount ( 1 )
        self.Bind ( wx.EVT_TIMER, self.on_timer, self.timer )
        self.Bind ( wx.EVT_CLOSE, self.on_close )
        self.timer.Start ( 1000 )

    def on_timer ( self, event ):
        self.lc.Refresh()

    def on_close ( self, event ):
        self.timer.Stop()
        event.Skip()

if __name__=='__main__':
    app = wx.App ( False )
    frame = MyFrame ( None )

    frame.Show()
    app.MainLoop()

我在我的工作環境中遇到了這個問題:

Python 2.7.12 (v2.7.12:d33e0cf91556, Jun 27 2016, 15:19:22) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
>>> wx.version()
'3.0.2.0 msw (classic)'

Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
>>> wx.version()
'3.0.3.dev2680+55dda48 msw (phoenix)'

如果您在面板上繪制所有內容並在面板上設置雙重緩沖,則將消除閃爍

from __future__ import print_function, unicode_literals

import wx

class MyListCtrl ( wx.ListCtrl ):
    def OnGetItemText ( self, item, column ):
        ar = []
        for i in range ( 200000 ):
            ar.append ( i )
        return '{}'.format ( len ( ar ) )

class MyFrame ( wx.Frame ):
    lc = None
    timer = None

    def __init__ ( self, *args, **kwargs ):
        super ( MyFrame, self ).__init__ ( *args, **kwargs )
        self.timer = wx.Timer ( self )

        self.panel = wx.Panel(self, -1)
        self.panel.SetDoubleBuffered(True)

        self.lc = MyListCtrl ( self.panel, style=wx.LC_VIRTUAL|wx.LC_REPORT )
        self.lc.InsertColumn ( 0, 'Count1', width=75 )
        self.lc.InsertColumn ( 1, 'Count2', width=75 )
        self.lc.InsertColumn ( 2, 'Count3', width=75 )
        self.lc.InsertColumn ( 3, 'Count4', width=75 )
        self.lc.SetItemCount ( 1 )
        self.Bind ( wx.EVT_TIMER, self.on_timer, self.timer )
        self.Bind ( wx.EVT_CLOSE, self.on_close )

        self.Show(True)
        self.timer.Start ( 1000 )

    def on_timer ( self, event ):
        self.Freeze()
        self.lc.Refresh()
        self.Thaw()

    def on_close ( self, event ):
        self.timer.Stop()
        event.Skip()

if __name__=='__main__':
    app = wx.App ( False )
    frame = MyFrame ( None )

    frame.Show()
    app.MainLoop()

通常不需要Freeze()和Thaw(),但在這種情況下,lc的行為有點奇怪。

我在另一個項目上遇到了同樣的問題,我終於提出了一個可行的解決方案。 回顧過去,我的解決方案正是Mike Driscoll上面的評論所暗示的。 我會在這里提供更多詳細信息,以防有人幫忙。

技巧是ListCtrl的GetTopItem()和GetCountPerPage()方法。 使用這兩個函數,您可以計算頁面刷新時可見的索引范圍。 這些是准備刷新頁面時需要緩存的唯一索引。

這是我如何實現它的簡化示例。

get_row_count()只是從表中選擇“select count(*)”

如果光標位於右側偏移量,則get_row()返回光標中的下一條記錄,否則執行新查詢以將光標重新定位在正確的位置。 根據我的經驗,ListCtrl按順序刷新項目,因此這非常有效。

python def on_list_update ( self, event ): self.on_begin_update() # notify subclasses that a refresh is coming count = self.get_row_count() # how many records will be visible? top = self.GetTopItem() bottom = min ( top + self.GetCountPerPage(), count ) new_item_cache = {} for item in range ( top, bottom ): new_item_cache[item] = self.get_row ( item ) self.item_cache = new_item_cache if count != self.GetItemCount(): self.SetItemCount ( count ) else: self.RefreshItems ( 0, count-1 )

上述模式通常導致在刷新開始之前僅向數據庫服務器發送2個sql查詢並消除閃爍。

重要的是要注意,如果將數據用於其他事件,則要將項目緩存存儲在臨時變量中,直到重建為止,否則您將遇到由於刷新發生而無法使用緩存的情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM