wxPython/wxWidgets 带有缩略图的超长滚动区域

wxPython/wxWidgets very long scroll area with thumbnails

我正在尝试创建一个垂直的图像缩略图列表,可能会达到 hundreds/thousands 个图像。 wxPython/wxWidgets 中是否有推荐的策略允许高效加载列表,以便不需要立即加载所有图像。在 iOS 上,在 UITableView 中有单元重用的概念,我不确定这里是否有类似的东西,或者推荐的解决方案是否只是预加载所有这些。我还考虑加载默认图像,然后在单独的线程中加载图像。

这是一个原型,展示了我正在努力完成的事情(红色框替换为图像缩略图)

这是我用来生成原型的代码:

import wx
from wx.lib.scrolledpanel import ScrolledPanel

eng = wx.App()
frame = wx.Frame(parent=None, title='wxPython')

main_panel = wx.Panel(frame)
main_sizer = wx.BoxSizer(wx.HORIZONTAL)
main_panel.SetSizer(main_sizer)

left_panel = ScrolledPanel(main_panel)
left_panel.SetupScrolling()
left_panel.SetBackgroundColour(wx.Colour(0,255,0))
left_panel_sizer = wx.BoxSizer(wx.VERTICAL)
left_panel.SetSizer(left_panel_sizer)
main_sizer.Add(left_panel, 1, wx.ALL | wx.EXPAND, 0)

right_panel = wx.Panel(main_panel)
right_panel.SetBackgroundColour(wx.Colour(0,0,255))
main_sizer.Add(right_panel, 4, wx.ALL | wx.EXPAND, 0)

for i in range(1000):
    left_panel_sizer.AddSpacer(10)
    thumbnail = wx.Panel(left_panel)
    thumbnail.SetMinSize((175,240))
    thumbnail.SetBackgroundColour(wx.Colour(255,0,0))
    left_panel_sizer.Add(thumbnail, 3, wx.ALL | wx.CENTER)
    left_panel_sizer.AddSpacer(10)


frame.Show()
frame.SetSize(1440,875)
frame.CenterOnScreen()

eng.MainLoop()

您应该避免创建数千个 windows。相反,您可以自己绘制图像——这样您也可以按需加载它们。

然后您可以轻松地在列表中拥有数百万(或更多)项目,例如参见wxHtmlListBox 使用这种方法。

按照的建议,我意识到只手动绘制可见的缩略图是最好的解决方案。

有几个 类 可以在 wxpython 中完成此操作:wx.VListBoxwx.VScrolledWindowwx.ScrolledWindow

wx.VListBox提供了最简单的API。您创建了一个自定义子类并实现了 OnMeasureItem 来指定单元格高度并 OnDrawItem 指定 仅适用于您的项目的绘图代码。这种方法的缺点是滚动会 'lock' 在每个项目的顶部。 wx.VScrolledWindow 与锁定滚动位置有同样的问题,但略有不同 API 这可能在其他情况下有用。

wx.ScrolledWindow 是最有用的,因为它解决了锁定滚动位置的问题。但是只绘制可见的单元格需要一些计算。下面是实现这个的原型:

import wx

class ThumbnailView(wx.ScrolledWindow):
    
    def __init__(self, *args, **kwargs):
        wx.VScrolledWindow.__init__(self, *args, **kwargs)

        self.ScrollRate = 10
        self.ThumbnailHeight = 100
        self.NumThumbnails = 100000

        self.SetVirtualSize(-1, self.ThumbnailHeight*self.NumThumbnails)
        self.SetScrollRate(0, self.ScrollRate)

    def OnDraw(self, dc):
        width, height = self.GetSize()

        # Calculate min and max thumbnails in view
        min_visible_thumb = int((self.GetViewStart()[1]*self.ScrollRate) / self.ThumbnailHeight)
        max_visible_thumb = min_visible_thumb + int(height / self.ThumbnailHeight) + 2

        # TEMP - Print min/max visible thumbnails
        print(f"min_visible_thumb = {min_visible_thumb}")
        print(f"max_visible_thumb = {max_visible_thumb}")
        print(f"height = {height}")

        for n in range(min_visible_thumb, max_visible_thumb):
            # Define the thumnail rectangle
            r = wx.Rect(0, n*100, width, 100)
            
            # Set the color alternate between red, green, or blue
            dc.SetBrush(wx.Brush(wx.Colour(255 * (n % 3 == 0), 255 * (n % 3 == 1), 255 * (n % 3 == 2))))
            
            # Draw the background
            dc.DrawRectangle(r)

            # Draw the text label
            dc.SetTextForeground(wx.WHITE)
            dc.DrawLabel(str(n), r, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)

eng = wx.App()
frame = wx.Frame(parent=None, title='wxPython')

main_panel = wx.Panel(frame)
main_sizer = wx.BoxSizer(wx.HORIZONTAL)
main_panel.SetSizer(main_sizer)

left_panel = ThumbnailView(main_panel)
main_sizer.Add(left_panel, 1, wx.ALL | wx.EXPAND, 0)

right_panel = wx.Panel(main_panel)
main_sizer.Add(right_panel, 4, wx.ALL | wx.EXPAND, 0)

frame.Show()
frame.SetSize(1440,875)
frame.CenterOnScreen()

eng.MainLoop()

原型截图如下:

这种方法可以很好地扩展到数千、数万或十万行,而只需要绘制显示的行。