The question is if my solution is a save and pythonic way to update a Tkinter-GUI with data from another thread? Are Locks required? Or how could a Queue help here? This example is working fine but the original application has much more complex data to deal with.
Please focus on AsyncioThread.create_dummy_data() in the minimal working example. The example has two threads. One run the Tkinter-mainloop and the second thread run a asyncio-loop. The asyncio-loop simulates getting some data and refreshing some tkinter.Label with this data.
#!/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()
The real application
This example is simplified. The real application is downloading (with feedparser) hundreds of xml-files (Newsfeeds) from just as many different websites.
The results are displayed in a Tkinter.Treeview where each xml-file has one entry in the TreeView. e. g. the count of entries in the xml-files is shown in the entries of the TreeView (e. g. "Time Magazine (12 entries)").
This should be done everytime one download of an xml-file has finished and not after all xml-file downloads are finished.
 
     
    