Как обновить виджет wxPython из потока? - PullRequest
0 голосов
/ 27 октября 2019

У меня есть фрейм wxPython, и я хочу периодически получать доступ к веб-странице и обновлять виджет на фрейме со значением.

Я знаю, что есть wx.Timer, но я уже использую его дляобновить другой виджет.

Моя попытка (ниже) завершается с помощью:

Pango:ERROR:/build/pango1.0-Ne8X8r/pango1.0-1.40.1/./pango/pango-layout.c:3925:pango_layout_check_lines: assertion failed: (!layout->log_attrs)

Это код, который я использую:

import datetime
import wx
import requests
from bs4 import BeautifulSoup
from threading import Thread, Event


class MainFrame(wx.Frame):
    def __init__(self):
        super(MainFrame, self).__init__(None)
        self.Bind(wx.EVT_CLOSE, self.on_quit_click)
        self.Size = (400, 200)

        self.panel = MainPanel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel)
        self.SetSizer(sizer)
        self.Center()
        self.Show()

        self._on_clock_timer(None)
        self.clock_timer = wx.Timer(self, -1)
        self.clock_timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self._on_clock_timer)

        self.stop_flag = Event()
        thread = ScraperThread(self, 1, self.stop_flag)
        thread.start()

    def _on_clock_timer(self, event):
        date_text = datetime.datetime.now().strftime('%a, %d %b %Y, %H:%M:%S')
        self.SetTitle(date_text)

    def on_quit_click(self, event):
        del event
        self.stop_flag.set()
        wx.CallAfter(self.Destroy)


class ScraperThread(Thread):
    def __init__(self, parent, period, event):
        super(ScraperThread, self).__init__()
        self.stopped = event
        self.period = period
        self.parent = parent

    def run(self):
        while not self.stopped.wait(self.period):
            response = requests.get('http://google.com')
            soup = BeautifulSoup(response.text, 'html.parser')
            all_tags = soup.find_all('span')
            for tag in all_tags:
                if 'Advertising' in tag.text:
                    print(tag.text[:50])
                    self.parent.panel.lbl_value.SetLabel(tag.text[:50])


class MainPanel(wx.Panel):
    def __init__(self, parent):
        super(MainPanel, self).__init__(parent)
        self.lbl_value = wx.StaticText(self, label='value')
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.lbl_value, flag=wx.ALL|wx.ALIGN_CENTER, border=5)
        self.SetSizer(sizer)


if __name__ == '__main__':
    wx_app = wx.App()
    MainFrame()
    wx_app.MainLoop()

1 Ответ

1 голос
/ 27 октября 2019

Полагаю, вам понадобится что-то вроде этого:

class MainFrame(wx.Frame):
    def __init__(self):
        # previous code up to self.Show()

        self._on_clock_timer(None)
        self.clock_timer = wx.Timer(self, -1)
        self.clock_timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self._on_clock_timer, self.clock_timer)

        self.scrape_timer = wx.Timer(self, -1)
        self.scrape_timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self._on_scrape, self.scrape_timer)

    def _on_scrape(self, event):
        response = requests.get('http://google.com')
        soup = BeautifulSoup(response.text, 'html.parser')
        all_tags = soup.find_all('span')
        for tag in all_tags:
            if 'Advertising' in tag.text:
                print(tag.text[:50])
                self.panel.lbl_value.SetLabel(tag.text[:50])

Затем вы можете удалить ScraperThread

Теперь единственное, что вам нужно быть уверенным, что _on_scrape() быстро завершается, иначе вы создадите резерв событий таймера.

В качестве альтернативы вы можете сделать: self.scrape_timer.Start(1000, oneShot = True) и _on_scrape() также закончить с этой строкой, так что между каждым http будет задержка в одну секундузапрос.

...