wxpython线程在加载图像时显示图像 [英] wxpython threading display images as they are loaded

查看:107
本文介绍了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()

解决方案

也可以一次剥离所有线程,就像在问题代码示例中所尝试的那样.原始代码中的问题是,由于在处理位图请求时存在时间延迟,因此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()

I am writing a GUI to display a few images (book covers from amazon) from a defined list of URLs, I have been able to load them in a panel successfully, but in spite of using threads, the GUI seems to wait till all the loop is over and then all the images show up at once, how can I achieve the GUI to display each images as they are fetched run time..the GUI is basically frozen till the images are fetched...Thanks!

the question again is to ensure I get to display each image on the GUI as they are pulled and not all at once...

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()

解决方案

It would also be possible to spin off all threads at once, as has been tried in the code sample in the question. The problem in the original code was that the GUI was blocking due to the time delay in processing the request for the bitmaps.

Bear in mind that in the sample below the order of the bitmaps will depend on the order the threads are finishing (which is random in this case).

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()

EDIT: Rewrote the example to generate the empty buttons first, then apply the bitmap as soon as they arrive. This is uglier now and it would be time to refactor to have a more safe mapping button to url than (mis)using the label.

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()

这篇关于wxpython线程在加载图像时显示图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆