wxpython线程在加载时显示图像

时间:2016-12-27 05:07:27

标签: multithreading python-2.7 wxpython

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

问题再次是确保我在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()

2 个答案:

答案 0 :(得分:2)

也可以一次性剥离所有线程,正如问题中的代码示例中所尝试的那样。原始代码中的问题是由于处理位图请求的时间延迟而导致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()

答案 1 :(得分:1)

允许自己重写。重要的是尽快退出__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()