No module named 'termios' when using module "wget" in Android - Ren'Py

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
CharlieFuu69
Regular
Posts: 30
Joined: Mon Nov 11, 2019 10:32 pm
Projects: Current projects using Ren'Py : "Tears: The First Love!" (Visual Novel at 90%) , "ElectroBasics Electronic Quiz!" (Educative game at 45%).
Location: Chile
Contact:

No module named 'termios' when using module "wget" in Android - Ren'Py

#1 Post by CharlieFuu69 » Sat Jan 23, 2021 10:13 pm

Hello!!

In advance I apologize for my level of English. Unfortunately it is not my native language.

Some time ago I asked for help in this forum regarding the Python "wget" module, which is used to execute downloads and show the download progress of files.
Since then I have been doing several experiments generating compilations of a game until the Android compilation stopped me.

When starting the download in Android, "wget" requests to import the module "termios", which returns the exception :

Code: Select all

No module named 'termios'.
I took a walk all over Google looking for a solution, but as you can already deduce, I was not successful.

I have Python v3.8 installed on my computer. I proceeded to import "termios" into the interpreter, which worked without a hitch. It is possible that this problem involves the engine over my project.

Regarding downloading files on Android, I'm using the "ANDROID _PUBLIC" (/0/Android/data/com.package.name/files/) path, and it works great when manipulating files from the game via the public path, so I completely rule out that the problem is due to the nature of Android.
NOTE: I am using Ren'Py version v7.4.0. I tested on v7.3.5 and v7.3.2 and the error persists.
I appreciate the time you give to read this problem =D

User avatar
PyTom
Ren'Py Creator
Posts: 15893
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: No module named 'termios' when using module "wget" in Android - Ren'Py

#2 Post by PyTom » Sat Jan 23, 2021 11:24 pm

I'd suggest using requests rather than wget. It's included with 7.4 and 7.4.1.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
CharlieFuu69
Regular
Posts: 30
Joined: Mon Nov 11, 2019 10:32 pm
Projects: Current projects using Ren'Py : "Tears: The First Love!" (Visual Novel at 90%) , "ElectroBasics Electronic Quiz!" (Educative game at 45%).
Location: Chile
Contact:

Re: No module named 'termios' when using module "wget" in Android - Ren'Py

#3 Post by CharlieFuu69 » Sun Jan 24, 2021 12:12 am

I also considered using the "requests" module as an option, but the issue of adding a progress bar for the download is complicated.
Since I have little experience in programming, some time ago I asked for help to create the download progress bar with "wget", in which I was given a base to implement this in Ren'Py.
This code is a reference to what I currently have implemented for downloads:

Code: Select all

init python:

    import threading
    import wget
    from os import path

    class DownloadControl(threading.Thread):

        def __init__(self, url, out=None):

            super(DownloadControl, self).__init__()
            self.daemon = True

            self.__url = url
            self.__out = out

            self.__download_status = None
            self.__is_finished = False

            self.__exception = None
            self.__result_filename = None

        @property
        def status(self):
            return (self.__download_status or .0)

        @property
        def _filename(self):
            return self.__result_filename

        def download_is_finished(self):
            return self.__is_finished

        def _has_exception(self):
            if isinstance(self.__exception, Exception):
                return True
            return False

        def _raise_from_thread(self):
            if self._has_exception():
                raise self.__exception

        def _callback_func(self, current, total, *args, **kwargs):
            current, total = map(float, (current, total))
            if total > .0:
                self.__download_status = (current / total)
                renpy.restart_interaction()

        def run(self):

            try:
                _result_fn = wget.download(
                    self.__url,
                    self.__out,
                    bar=self._callback_func
                )
            except Exception as ex:
                self.__exception = ex
                self.__download_status = .0
            else:
                self.__result_filename = path.abspath(_result_fn)
            finally:
                self.__is_finished = True
                renpy.restart_interaction()


screen download_screen(url, out=None):

    default download_thread = DownloadControl(url, out)
    default _download_is_started = False

    showif _download_is_started:
        showif download_thread.download_is_finished():
            showif download_thread._has_exception():
                vbox:
                    text "Error have occurred."
                    textbutton "Raise traceback." action Function(
                        download_thread._raise_from_thread
                    )
            else:
                vbox:
                    text "Download completed."
                    textbutton "Return" action Return()
        else:
            vbox:
                text "Download {0:.1%}.".format(download_thread.status)
                bar value AnimatedValue(download_thread.status, 1.)
    else:
        textbutton "Start download" action (
            Function(download_thread.start),
            SetScreenVariable("_download_is_started", True)
        )


label start:
    call screen download_screen("https://www.renpy.org/dl/7.3.5/renpy-7.3.5-sdk.7z.exe")
    return
As wget offers in advance the possibility to show the download progress, this would make things much easier, but I don't know exactly how to implement the same progress bar using "requests".
How could I show a download progress bar (indicating the total percentage downloaded) in the Ren'Py environment?

User avatar
PyTom
Ren'Py Creator
Posts: 15893
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: No module named 'termios' when using module "wget" in Android - Ren'Py

#4 Post by PyTom » Sun Jan 24, 2021 1:30 am

You probably want requests.get(url, stream=True), and then response.iter_content().
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
CharlieFuu69
Regular
Posts: 30
Joined: Mon Nov 11, 2019 10:32 pm
Projects: Current projects using Ren'Py : "Tears: The First Love!" (Visual Novel at 90%) , "ElectroBasics Electronic Quiz!" (Educative game at 45%).
Location: Chile
Contact:

Re: No module named 'termios' when using module "wget" in Android - Ren'Py

#5 Post by CharlieFuu69 » Wed Jan 27, 2021 5:37 am

I appreciate your help PyTom!!! I implemented both systems in my game (using "wget" for Windows version and "requests" for Android.

For those who dare to use "wget" on Android, I have found the solution to prevent the module from throwing the exception no module named 'termios'.
I started looking closely at the wget code (wget.py) until I found the exact point that asks to import "termios". You can definitely use wget without the need to have "termios".
All of this leads me to this function:

Code: Select all

def get_console_width():
    """Return width of available window area. Autodetection works for
       Windows and POSIX platforms. Returns 80 for others

       Code from http://bitbucket.org/techtonik/python-pager
    """

    if os.name == 'nt':
        STD_INPUT_HANDLE  = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE  = -12

        # get console handle
        from ctypes import windll, Structure, byref
        try:
            from ctypes.wintypes import SHORT, WORD, DWORD
        except ImportError:
            # workaround for missing types in Python 2.5
            from ctypes import (
                c_short as SHORT, c_ushort as WORD, c_ulong as DWORD)
        console_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)

        # CONSOLE_SCREEN_BUFFER_INFO Structure
        class COORD(Structure):
            _fields_ = [("X", SHORT), ("Y", SHORT)]

        class SMALL_RECT(Structure):
            _fields_ = [("Left", SHORT), ("Top", SHORT),
                        ("Right", SHORT), ("Bottom", SHORT)]

        class CONSOLE_SCREEN_BUFFER_INFO(Structure):
            _fields_ = [("dwSize", COORD),
                        ("dwCursorPosition", COORD),
                        ("wAttributes", WORD),
                        ("srWindow", SMALL_RECT),
                        ("dwMaximumWindowSize", DWORD)]

        sbi = CONSOLE_SCREEN_BUFFER_INFO()
        ret = windll.kernel32.GetConsoleScreenBufferInfo(
            console_handle, byref(sbi))
        if ret == 0:
            return 0
        return sbi.srWindow.Right+1

    elif os.name == 'posix':
        from fcntl import ioctl
        from termios import TIOCGWINSZ
        from array import array

        winsize = array("H", [0] * 4)
        try:
            ioctl(sys.stdout.fileno(), TIOCGWINSZ, winsize)
        except IOError:
            pass
        return (winsize[1], winsize[0])[0]

    return 80
This function returns a value to define the width of the window, in order to show a progress bar in the console / terminal / interpreter.
This is somewhat irrelevant to the Ren'Py code that I am using at the moment.
Just pay attention to what the text at the beginning of the function tells us:

Code: Select all

"""Return width of available window area. Autodetection works for
   Windows and POSIX platforms. Returns 80 for others

   Code from http://bitbucket.org/techtonik/python-pager
"""
When using "wget" in Android, it detects Android as a POSIX platform, which is completely natural if you understand the architecture of Android.
This function returns 80 when neither platform (NT or POSIX) is detected.
A simple change that will make "wget" work, would be to eliminate much of the code in this function, making it always return the value 80 when it is called. The function that I showed previously, should simply look like this:

Code: Select all

def get_console_width():
    """Return width of available window area. Autodetection works for
       Windows and POSIX platforms. Returns 80 for others

       Code from http://bitbucket.org/techtonik/python-pager
    """

    return 80
This does not affect the main operation of the module or the code that I implemented in Ren'Py (with the help of more people). Downloads are successful and the progress bar in Ren'Py continues to work as expected.
Hope this helps other folks experimenting with Android downloads using "wget". My main intention to implement the downloads on Android was so that the player, at the end of the game, can obtain DLCs and "patch" the game through the Android public path.

Post Reply

Who is online

Users browsing this forum: span4ev