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

问题描述

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

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

enter image description here

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

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,255))
main_sizer.Add(right_panel,4,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))
    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()

解决方法

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

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

,

根据 VZ's 的建议,我意识到仅手动绘制可见缩略图是最佳解决方案。

在 wxpython 中有几个类可以实现这一点:wx.VListBoxwx.VScrolledWindowwx.ScrolledWindow

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

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

import wx

class ThumbnailView(wx.ScrolledWindow):
    
    def __init__(self,*args,**kwargs):
        wx.VScrolledWindow.__init__(self,**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,0)

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

eng.MainLoop()

这是原型的截图:

wxPython prototype wx.Scrolled

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