2016-12-27 10 views
1

Я пишу графический интерфейс для отображения нескольких изображений (обложки книг из амазонки) из определенного списка URL-адресов, я смог загрузить их в панели успешно, но, несмотря на используя потоки, графический интерфейс, кажется, ждет, пока весь цикл не закончится, а затем все изображения появятся сразу, как я могу достичь графического интерфейса для отображения каждого изображения по мере того, как они выберут время выполнения. Графический интерфейс в основном заморожен до изображений взяты ... Спасибо!wxpython Threading отображает изображения по мере их загрузки

вопрос снова, чтобы убедиться, я получаю, чтобы отобразить каждое изображение на графическом интерфейсе, как они тянут и не все сразу ...

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

Также можно было бы открутить все потоки сразу, как это было проверено в примере кода в вопросе. Проблема в исходном коде заключалась в том, что 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() 

EDIT: Переписана пример первой генерации пустых кнопок, а затем применить растровое изображение, как только они прибывают. Сейчас это уродливо, и пришло время реорганизовать более безопасную кнопку сопоставления 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() 
+0

О, спасибо за это, я многому учусь. Это результат, который я искал, поскольку я собираюсь выполнить сортировку после того, как он был извлечен. –

+0

Я думал о очереди вместе с Threading, надеясь, что это породит несколько потоков, а затем Queue позаботится о заказе? Не уверен, что это произойдет, будет эквивалентно первому ответу, отправленному вами. –

+1

Изменен код, чтобы показать, как сгенерировать кнопки вначале и применить растровое изображение позже. '' Queue'' вам не поможет, потому что, как и в первом примере, потоки будут обрабатываться последовательно, а не в одно и то же время. – nepix32

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

Удивительный! Это сработало так, как я этого хотел, Большое вам спасибо, как вы можете сказать, я все еще изучаю wxpython и немного ушел из своей лиги с Threading и т. Д. –

+0

Возможно ли, что Threading сделает несколько выборки и отображения как они были извлечены, когда делались, а не по одному за раз? –

+0

Также это можно сделать, см. Отдельный ответ. – nepix32