【问题标题】:wxpython threading display images as they are loadedwxpython 线程在加载时显示图像
【发布时间】:2016-12-27 05:07:27
【问题描述】:

我正在编写一个 GUI 来显示定义的 URL 列表中的一些图像(来自亚马逊的书籍封面),我已经能够成功地将它们加载到面板中,但是尽管使用了线程,但 GUI 似乎在等待直到所有循环结束,然后所有图像一次显示,我怎样才能实现 GUI 来显示每个图像,因为它们在运行时被提取..GUI 基本上被冻结,直到图像被提取......谢谢!

问题再次是确保我能够在 GUI 上显示每个图像,因为它们被拉出,而不是一次全部显示......

import wx
import os
import sys
import urllib2
import cStringIO
import threading
import time

urls = ['https://images-na.ssl-images-amazon.com/images/I/51-u3J3mtTL._AC_US100_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51cRqX8DTgL._AC_US100_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/515iBchIIzL._AC_US100_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/511MaP7GeJL._AC_US100_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
    'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
    'https://images-na.ssl-images-amazon.com/images/I/31Pw7voGDFL._AC_US160_.jpg',
    'https://images-na.ssl-images-amazon.com/images/I/51g30m1xpPL._AC_US160_.jpg',
    'https://images-na.ssl-images-amazon.com/images/I/51qx+6aQUnL._AC_US160_.jpg']

class Example(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(Example, self).__init__(*args, **kwargs) 
        self.InitUI()
        self.Ctrls()
        self.makeButtons()

    def makeButtons(self):

        def _update_data(data):
            time.sleep(2)
            stream = cStringIO.StringIO(data)
            bmp = wx.BitmapFromImage( wx.ImageFromStream( stream ) )
            button = wx.Button(self.panel, -1, "Book cover", style=wx.ALIGN_CENTER, size=wx.Size(100,100))
            button.SetToolTipString("wx.Button can how have an icon on the left, right,\n"
                           "above or below the label.")
            button.SetBitmap(bmp,
                    wx.LEFT    # Left is the default, the image can be on the other sides too
                    #wx.RIGHT
                    #wx.TOP
                    #wx.BOTTOM
                    )
            button.SetBitmapMargins((4,4)) 
            button.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, False))
            self.wrapSizer.Add(button, 1, wx.EXPAND)
            self.Show(True)
            self.panel.Layout()

        def f():
            f = urllib2.urlopen(url)
            data = f.read()
            wx.CallAfter(_update_data, data)

        for url in urls:
            threading.Thread(target=f).start()

    def InitUI(self):
        self.SetSize((800, 400))
        self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
        self.Centre()


    def Sizers(self):
        self.wrapSizer = wx.WrapSizer()
        self.panel.SetSizer(self.wrapSizer)

    def Ctrls(self):
        self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
        self.Sizers()

def main():

    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()

【问题讨论】:

    标签: multithreading python-2.7 wxpython


    【解决方案1】:

    也可以一次分拆所有线程,正如在问题的代码示例中所尝试的那样。原始代码中的问题是,由于处理位图请求的时间延迟,GUI 被阻塞。

    请记住,在下面的示例中,位图的顺序将取决于线程完成的顺序(在这种情况下是随机的)。

    import wx
    import urllib2
    import cStringIO
    import threading
    import time
    from random import random
    
    urls = ['https://images-na.ssl-images-amazon.com/images/I/51-u3J3mtTL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51cRqX8DTgL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/515iBchIIzL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/511MaP7GeJL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/31Pw7voGDFL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51g30m1xpPL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51qx+6aQUnL._AC_US160_.jpg']
    
    def url2bmp(url, callback):
        f = urllib2.urlopen(url)
        data = f.read()
    
        # to simulate random read delay
        time.sleep(2 * random())
    
        stream = cStringIO.StringIO(data)
        bmp = wx.BitmapFromImage(wx.ImageFromStream(stream))
        wx.CallAfter(callback, bmp)
    
    class Example(wx.Frame):
    
        def __init__(self, *args, **kwargs):
            super(Example, self).__init__(*args, **kwargs) 
            self.InitUI()
            self.Ctrls()
            self.Show(True)
            self.makeButtons()
    
        def makeButtons(self):
            for url in urls:
                threading.Thread(target=url2bmp, args=(url, self.update_ui)).start()
    
        def update_ui(self, bmp):
            button = wx.Button(self.panel, -1, "Book cover", style=wx.ALIGN_CENTER, size=wx.Size(100,100))
            button.SetToolTipString("wx.Button can how have an icon on the left, right,\n"
                           "above or below the label.")
            button.SetBitmap(bmp,
                    wx.LEFT    # Left is the default, the image can be on the other sides too
                    #wx.RIGHT
                    #wx.TOP
                    #wx.BOTTOM
                    )
            self.wrapSizer.Add(button, 1, wx.EXPAND)
            self.panel.Layout()
    
        def InitUI(self):
            self.SetSize((800, 400))
            self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
            self.Centre()
    
        def Sizers(self):
            self.wrapSizer = wx.WrapSizer()
            self.panel.SetSizer(self.wrapSizer)
    
        def Ctrls(self):
            self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
            self.Sizers()
    
    def main():
    
        ex = wx.App()
        Example(None)
        ex.MainLoop()
    
    if __name__ == '__main__':
        main()
    

    编辑:重写示例以首先生成空按钮,然后在它们到达时立即应用位图。现在这更难看,是时候重构一个比(错误)使用标签更安全的映射按钮到 url

    import wx
    import urllib2
    import cStringIO
    import threading
    import time
    from random import random
    
    urls = ['https://images-na.ssl-images-amazon.com/images/I/51-u3J3mtTL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51cRqX8DTgL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/515iBchIIzL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/511MaP7GeJL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/31Pw7voGDFL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51g30m1xpPL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51qx+6aQUnL._AC_US160_.jpg']
    
    def url2bmp(url, callback):
        f = urllib2.urlopen(url)
        data = f.read()
    
        # to simulate random read delay
        time.sleep(2 * random())
    
        stream = cStringIO.StringIO(data)
        bmp = wx.BitmapFromImage(wx.ImageFromStream(stream))
        wx.CallAfter(callback, url, bmp)
    
    class Example(wx.Frame):
    
        def __init__(self, *args, **kwargs):
            super(Example, self).__init__(*args, **kwargs) 
            self.InitUI()
            self.Ctrls()
            self.Show(True)
            self.makeButtons()
    
        def makeButtons(self):
            for url in urls:
                self.update_ui(url)
                threading.Thread(target=url2bmp, args=(url, self.update_ui)).start()
    
        def update_ui(self, url, bmp=None):
            if bmp is None:
                # create button, but not bitmap
                button = wx.Button(self.panel, -1, url, style=wx.ALIGN_CENTER, size=wx.Size(100,100))
                button.SetToolTipString("wx.Button can how have an icon on the left, right,\n"
                               "above or below the label.")
                self.wrapSizer.Add(button, 1, wx.EXPAND)
            else:
                children = self.wrapSizer.GetChildren()
                # http://www.blog.pythonlibrary.org/2012/08/24/wxpython-how-to-get-children-widgets-from-a-sizer/
                for widget in children:
                    button = widget.GetWindow()
                    if isinstance(button, wx.Button):
                        if button.GetLabel() == url:
                            button.SetBitmap(bmp,
                                    wx.LEFT    # Left is the default, the image can be on the other sides too
                                    #wx.RIGHT
                                    #wx.TOP
                                    #wx.BOTTOM
                                    )
                            button.SetLabel('')
            self.panel.Layout()
    
        def InitUI(self):
            self.SetSize((800, 400))
            self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
            self.Centre()
    
        def Sizers(self):
            self.wrapSizer = wx.WrapSizer()
            self.panel.SetSizer(self.wrapSizer)
    
        def Ctrls(self):
            self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
            self.Sizers()
    
    def main():
    
        ex = wx.App()
        Example(None)
        ex.MainLoop()
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 哦,谢谢你,我学到了很多东西。这是我一直在寻找的结果,因为我将在获取后完成排序..
    • 我在考虑队列和线程,希望它会产生多个线程,然后队列会处理顺序?不确定这个结果是否等同于您发布的第一个答案..
    • 修改了代码以显示如何首先生成按钮并稍后应用位图。 Queue 不会帮助您,因为在第一个示例中,线程将按顺序处理,而不是同时处理。
    【解决方案2】:

    允许自己重写。重要的是尽快退出__init__ 方法(在启动线程之前调用Show)并让线程异步工作。您还同时启动了所有线程,而本示例使用一个线程获取一个接一个的位图。

    import wx
    import os
    import sys
    import urllib2
    import cStringIO
    import threading
    import time
    
    urls = ['https://images-na.ssl-images-amazon.com/images/I/51-u3J3mtTL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51cRqX8DTgL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/515iBchIIzL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/511MaP7GeJL._AC_US100_.jpg',
            'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51jizRmRYYL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/31Pw7voGDFL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51g30m1xpPL._AC_US160_.jpg',
        'https://images-na.ssl-images-amazon.com/images/I/51qx+6aQUnL._AC_US160_.jpg']
    
    def gen_urls():
        """Gives back one bitmap after another."""
        for url in urls:
            f = urllib2.urlopen(url)
            data = f.read()
            time.sleep(2)
            stream = cStringIO.StringIO(data)
            bmp = wx.BitmapFromImage( wx.ImageFromStream( stream ) )
            yield bmp
    
    class Example(wx.Frame):
    
        def __init__(self, *args, **kwargs):
            super(Example, self).__init__(*args, **kwargs) 
            self.InitUI()
            self.Ctrls()
            self.Show(True)
            threading.Thread(target=self.makeButtons).start()
    
        def makeButtons(self):
            for bmp in gen_urls():
                wx.CallAfter(self.update_ui, bmp)
    
        def update_ui(self, bmp):
            button = wx.Button(self.panel, -1, "Book cover", style=wx.ALIGN_CENTER, size=wx.Size(100,100))
            button.SetToolTipString("wx.Button can how have an icon on the left, right,\n"
                           "above or below the label.")
            button.SetBitmap(bmp,
                    wx.LEFT    # Left is the default, the image can be on the other sides too
                    #wx.RIGHT
                    #wx.TOP
                    #wx.BOTTOM
                    )
            button.SetBitmapMargins((4,4)) 
            button.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, False))
            self.wrapSizer.Add(button, 1, wx.EXPAND)
            self.panel.Layout()
    
        def InitUI(self):
            self.SetSize((800, 400))
            self.SetTitle('Dynamically Flow Buttons to Next Row on Window-Resize')
            self.Centre()
    
        def Sizers(self):
            self.wrapSizer = wx.WrapSizer()
            self.panel.SetSizer(self.wrapSizer)
    
        def Ctrls(self):
            self.panel = wx.Panel(parent=self,pos=wx.Point(0,0), size=wx.Size(750,550), style=wx.TAB_TRAVERSAL)
            self.Sizers()
    
    def main():
    
        ex = wx.App()
        Example(None)
        ex.MainLoop()
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 太棒了!就像我想要的那样工作,非常感谢,正如你所知道的,我还在学习 wxpython,并且在线程等方面有点脱离了我的联盟。
    • 是否可以让线程执行多次提取并在完成时显示项目,而不是一次提取一个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 2015-06-26
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多