用第二个线程的数据更新TkinterGUI中的数据

2024-09-28 15:23:06 发布

您现在位置:Python中文网/ 问答频道 /正文

问题是,我的解决方案是否是用另一个线程的数据更新tkintergui的一种保存和pythonic方法?是否需要Locks?或者Queue如何在这里提供帮助?这个例子运行得很好,但是原始应用程序要处理的数据要复杂得多。在

请关注最小工作示例中的AsyncioThread.create_dummy_data()。这个例子有两个线程。运行一次循环。asyncio循环模拟获取一些数据并用这些数据刷新一些tkinter.Label。在

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# restrict to Python3.5 or higher because of asyncio syntax

# based on <https://stackoverflow.com/a/47920128/4865723>

from tkinter import *
import asyncio
import threading
import random


class AsyncioThread(threading.Thread):
    def __init__(self, asyncio_loop, theWindow):
        self.asyncio_loop = asyncio_loop
        self.theWindow = theWindow
        self.maxData = len(theWindow.varData)
        threading.Thread.__init__(self)


    def run(self):
        self.asyncio_loop.run_until_complete(self.do_data())


    async def do_data(self):
        """ Creating and starting 'maxData' asyncio-tasks. """
        tasks = [
            self.create_dummy_data(number)
            for number in range(self.maxData)
        ]
        completed, pending = await asyncio.wait(tasks)
        results = [task.result() for task in completed]
        print('\n'.join(results))


    async def create_dummy_data(self, number):
        """ One task. """
        sec = random.randint(1, 3)
        data = '{}:{}'.format(number, random.random())
        await asyncio.sleep(sec)

        # IS THIS SAVE?
        self.theWindow.varData[number].set(data)
        print('Thread-ID: {}\tsec: {}\n\t{}' \
               .format(threading.get_ident(), sec, data))

        return data


class TheWindow:
    def __init__(self, maxData):
        # asyncio loop will run in an extra Thread
        self.asyncio_loop = asyncio.get_event_loop()

        # the GUI main object
        self.root = Tk()

        # create the data variable
        self.varData = []
        for i in range(maxData):
            self.varData.append(StringVar())
            self.varData[i].set('<default>')

        # Button to start the asyncio tasks
        Button(master=self.root,
               text='Start Asyncio Tasks',
               command=lambda:self.do_asyncio()).pack()
        # Frames to display data from the asyncio tasks
        for i in range(maxData):
            Label(master=self.root, textvariable=self.varData[i]).pack()
        # Button to check if the GUI is freezed
        Button(master=self.root,
               text='Freezed???',
               command=self.do_freezed).pack()

    def do_freezed(self):
        """ Button-Event-Handler to see if a button on GUI works.
            The GOAL of this example is to make this button clickable
            while the other thread/asyncio-tasks are working. """
        print('Tkinter is reacting. Thread-ID: {}'
              .format(threading.get_ident()))


    def do_asyncio(self):
        """ Button-Event-Handler starting the asyncio part in a separate thread. """
        thread = AsyncioThread(self.asyncio_loop, self)
        thread.start()


if __name__ == '__main__':
    window = TheWindow(5)
    window.root.mainloop()

实际应用

这个例子被简化了。真正的应用程序是从许多不同的网站下载(使用feedparser)数百个xml文件(新闻提要)。 结果显示在Tkinter.Treeview中,其中每个xml文件在TreeView中有一个条目。e、 g.xml文件中的条目数显示在TreeView(例如“时代杂志(12个条目)”)的条目中。 每次一个xml文件的下载完成,而在所有xml文件下载完成之后,不是。在


Tags: thetoinselfloopasynciodatadef
1条回答
网友
1楼 · 发布于 2024-09-28 15:23:06

此解决方案基于其他人的意见。它使用queue.Queue在两个线程之间共享数据。Tkinter GUI/线程使用1秒计时器检查队列中是否有新数据,并使用它刷新其标签。在

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# based on <https://stackoverflow.com/a/47920128/4865723>

from tkinter import *
import asyncio
import threading
import random
import queue


class AsyncioThread(threading.Thread):
    def __init__(self, the_queue, max_data):
        self.asyncio_loop = asyncio.get_event_loop()
        self.the_queue = the_queue
        self.max_data = max_data
        threading.Thread.__init__(self)

    def run(self):
        self.asyncio_loop.run_until_complete(self.do_data())

    async def do_data(self):
        """ Creating and starting 'maxData' asyncio-tasks. """
        tasks = [
            self.create_dummy_data(key)
            for key in range(self.max_data)
        ]
        await asyncio.wait(tasks)

    async def create_dummy_data(self, key):
        """ Create data and store it in the queue. """
        sec = random.randint(1, 10)
        data = '{}:{}'.format(key, random.random())
        await asyncio.sleep(sec)

        self.the_queue.put((key, data))


class TheWindow:
    def __init__(self, max_data):
        # thread-safe data storage
        self.the_queue = queue.Queue()

        # the GUI main object
        self.root = Tk()

        # create the data variable
        self.data = []
        for key in range(max_data):
            self.data.append(StringVar())
            self.data[key].set('<default>')

        # Button to start the asyncio tasks
        Button(master=self.root,
               text='Start Asyncio Tasks',
               command=lambda: self.do_asyncio()).pack()
        # Frames to display data from the asyncio tasks
        for key in range(max_data):
            Label(master=self.root, textvariable=self.data[key]).pack()
        # Button to check if the GUI is freezed
        Button(master=self.root,
               text='Freezed???',
               command=self.do_freezed).pack()

    def refresh_data(self):
        """
        """
        # do nothing if the aysyncio thread is dead
        # and no more data in the queue
        if not self.thread.is_alive() and self.the_queue.empty():
            return

        # refresh the GUI with new data from the queue
        while not self.the_queue.empty():
            key, data = self.the_queue.get()
            self.data[key].set(data)

        print('RefreshData...')

        #  timer to refresh the gui with data from the asyncio thread
        self.root.after(1000, self.refresh_data)  # called only once!

    def do_freezed(self):
        """ Button-Event-Handler to see if a button on GUI works.
            The GOAL of this example is to make this button clickable
            while the other thread/asyncio-tasks are working. """
        print('Tkinter is reacting. Thread-ID: {}'
              .format(threading.get_ident()))

    def do_asyncio(self):
        """
            Button-Event-Handler starting the asyncio part in a separate
            thread.
        """
        # create Thread object
        self.thread = AsyncioThread(self.the_queue, len(self.data))

        #  timer to refresh the gui with data from the asyncio thread
        self.root.after(1000, self.refresh_data)  # called only once!

        # start the thread
        self.thread.start()


if __name__ == '__main__':
    window = TheWindow(10)
    window.root.mainloop()

此示例基于https://stackoverflow.com/a/47920128/4865723。 不确定这是否是一个优雅的解决方案。请随意编辑。我的目标是让别人重复使用我的问题和答案。在

相关问题 更多 >