0

I'm a beginner i Python and I want to make an application that automatically updates a game, downloading the files and then installing it, using GTK 3 and Python 3.8.

I made a class with a method that automatically gets the latest release, picks the correct file for the OS Python is running on and then downloads it on another method. Here's that class:

import requests
import platform
import asyncio


class ReleaseManager:
    def __init__(self, package_name, author_name):
        self.package_name = package_name
        self.author_name = author_name

        self.selected_url = ""
        self.selected_filename = ""

    async def get_asset_download_url_and_name(self):
        url = f"https://api.github.com/repos/{self.author_name}/{self.package_name}/releases/latest"
        response = requests.get(
            url,
            headers={"User-Agent": "OpenRCT2 Silent Launcher"}
        )

        json_response = response.json()
        assets = json_response["assets"]


        for file in assets:
            if file["content_type"] == "application/x-ms-dos-executable":
            
            # ...
            # Code that outputs the correct URL and filename into the 
            # 'selected_url' and 'selected_filename' variables

    async def download_latest_asset(self, download_path, progress_bar):
        if self.selected_url == "":
            await self.get_asset_download_url_and_name()

        response = requests.get(
            self.selected_url,
            headers={"User-Agent": "OpenRCT2 Silent Launcher", "Accept": "application/octet-stream"},
            stream=True
        )
        response_size = int(response.headers['content-length'])

        if response.status_code == 200:
            with open(os.path.join(download_path, self.selected_filename), "wb") as file:
                bytes_read = 0
                print("Downloading...")

                for chunk in response.iter_content(512):
                    file.write(chunk)

                    bytes_read += 512
                    progress = bytes_read / response_size
                    # progress_bar.set_fraction(progress)
                    # ^ This is how I wanted to update the progress bar,
                    # and I commented it out to make sure this wasn't causing the problem.

        print("Successfully finished downloading")

This is my main.py file that is executed:

import asyncio
import asyncio_glib
import gi

from github import releases

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

asyncio.set_event_loop_policy(asyncio_glib.GLibEventLoopPolicy())

builder = Gtk.Builder()
builder.add_from_file("layouts/mainWindow.glade")


def call_download(button):
    loop = asyncio.get_event_loop()
    loop.run_until_complete(download_openrct2())


async def download_openrct2():
    manager = releases.ReleaseManager("OpenRCT2", "OpenRCT2")
    await manager.download_latest_asset("/home/samuel/Downloads/OpenRCT2/", builder.get_object("PgrDownload"))

handlers = {
    "onDestroy": Gtk.main_quit,
    "onDownloadClick": call_download
}
builder.connect_signals(handlers)

win = builder.get_object("MainWindow")
win.show_all()
Gtk.main()

This code works as intended. Files are downloaded properly and no errors return. However, the GUI freezes the moment I press the "Download OpenRCT2" button. You can see I tried solving this using asyncio and asyncio_glib, based on this question on StackOverflow, and since the only documentation (from its GitHub) for asyncio_glib is to insert asyncio.set_event_loop_policy(asyncio_glib.GLibEventLoopPolicy()) before asyncio.get_loop_event() I assumed this is the only thing I needed to do.

This did not solve the problem though, the UI still freezes until the last line of the download_latest_asset method is finished executing, while no errors are thrown. How to make the download method not freeze the UI while it's downloading a file?

Thanks in advance.

androidWG
  • 43
  • 1
  • 7
  • I am not familiar with asyncio, but I wonder if Threading might work better for you. Threading worked well for me in I/O bound apps. Beware though that Gtk is not thread safe. There are plenty of examples on SO. – theGtknerd May 25 '21 at 11:52
  • @theGtknerd I forgot to update this post, I ended up going with threading and using events with PyPubSub to update the UI. Then I ended up scrapping the UI entirely for now (decided it's not needed), but that solution worked out well. I think I'll respond to my question with the details later. – androidWG May 25 '21 at 15:22

0 Answers0