In this recipe, we extended the Thread class to implement the logic that must run in a separate thread using a more object-oriented approach. This is done by overriding its run() method, which will be responsible for performing the HTTP request to the local server:
class AsyncAction(threading.Thread):
def run(self):
# ...
There are lots of HTTP client libraries, but here we will simply use the urllib.request module from the standard library. This module contains the urlopen() function, which can take a URL string and return an HTTP response that can work as a context manager—that is, it can be safely read and closed using the with statement.
The server returns a JSON document like the following one (you can check it by opening the http://localhost:8080 URL in your browser):
{"random": 0.0915826359180778}
To decode the string to a object, we pass the response contents to the loads() function from the json module. Thanks to this, we can access the random value like using a dictionary, and store it in the result attribute, which is initialized to None to prevent the main thread from reading a field that is not set in case an error occurs:
def run(self):
self.result = None
url = "http://localhost:8080"
with urllib.request.urlopen(url) as f:
obj = json.loads(f.read().decode("utf-8"))
self.result = obj["random"]
Then, the GUI periodically polls the thread status, as we saw in the preceding recipe:
def check_thread(self, thread):
if thread.is_alive():
self.after(100, lambda: self.check_thread(thread))
else:
text = "Random value: {}".format(thread.result)
self.label.config(text=text)
self.button.config(state=tk.NORMAL)
Here, the main difference is that once the thread is not alive, we can retrieve the value of the result attribute because it has been set before finishing its execution.