Can’t Print with Input and Help With Threading

Question:
So I am making a module/template to easily make an Idle Game. I am trying to use threading to reprint while input’s are trying to accomplish player actions. It immediately times out and doesn’t do anything. Should I be using threading or should I use curses for this.

Repl link:
https://replit.com/@SalladShooter/IdleGameMaker?v=1

__init__.py

from termcolor import colored
from replit import clear
import time
import threading

class Game:
    def __init__(self, gameName, author: str, value=None, collectSpeed=1, rebirths=False, rebirthName="Rebirth"):
        self.gameName = gameName
        self.author = author
        self.actions = ["Shop"]
        self.currency = value['symbol'] if value else None
        self.rebirths = rebirths

        if value and value.get('rebirths', False):
            self.rebirths = True
            self.rebirthName = value.get('rebirthName', 'Rebirth')
            self.actions.append(self.rebirthName)

        self.currencyColor = value.get('color', 'white')
        self.collectSpeed = value.get('collectSpeed', 1)
        self.title = colored(f"{self.gameName}\n", self.currencyColor)

        self.items = []
        self._money = 0
        self._perSecond = 0
        self.moneyAdd = 1
        self.type = None
        self.color = None
        self.shop = "closed"
        self.actions.append("Credits")
        self.item_name = []
        self.item_cost = []
        self.item_gives_type = []
        self.item_amount = []
        self.item_color = []
        self.running = True

        self._last_money_update = time.time()
        self.stop_event = threading.Event()

    @property
    def money(self):
        elapsed_time = time.time() - getattr(self, '_last_money_update', time.time())
        self._money += elapsed_time * self._perSecond
        self._last_money_update = time.time()
        return round(self._money)

    @money.setter
    def money(self, value):
        self._last_money_update = time.time()
        self._money = value

    @property
    def perSecond(self):
        return self._perSecond

    @perSecond.setter
    def perSecond(self, value):
        self.money
        self._perSecond = value

    def add(self, type: str, value: dict):
        if type.lower() == "buy":
            item_name = value['name']
            self.item_name.append(item_name)
            item_cost = value['cost']
            self.item_cost.append(item_cost)
            gives_type = value.get('givesType', 'perSecond')
            self.item_color.append(value['color'])
            self.item_gives_type.append(gives_type)
            amount = value.get('amount', 1)
            self.item_amount.append(amount)

    def click(self):
        for i in range(len(self.item_name)):
            if self.item_gives_type[i] == 'perClick':
                self.money += self.moneyAdd

    def buy(self, item):
        item_index = int(item) - 1
        if 0 <= item_index < len(self.item_name):
            item_cost = self.item_cost[item_index]
            item_name = self.item_name[item_index]
            gives_type = self.item_gives_type[item_index]
            amount = self.item_amount[item_index]
            item_color = self.item_color[item_index]
            if self.money >= item_cost:
                self.money -= item_cost
                self.items.append((item_name, item_cost))

                if gives_type == 'perSecond':
                    self.perSecond += amount
                elif gives_type == 'perClick':
                    self.moneyAdd += amount
                clear()
                print(self.title)
                print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            else:
                print(f"You don't have enough {colored(self.currency, self.currencyColor)}'s to buy {colored(item_name, item_color)}.")
                input("Press [" + colored("ENTER", "cyan") + "] to continue...")
        else:
            print("Invalid item selection.")
            input("Press [" + colored("ENTER", "cyan") + "] to continue...")

    def input_thread(self):
       while not self.stop_event.is_set():
           print("stop_event is not set")
           choice = input()
           if choice:
               self.handle_input(choice)
                   
    def handle_input(self, choice):
        if choice.isdigit():
            choice = int(choice)
            if 1 <= choice <= len(self.actions):
                action = self.actions[choice - 1]
                if action == "Shop" and self.shop == "closed":
                    clear()
                    print(self.title)
                    print(f"{round(self.money)} - {colored(self.currency, self.currencyColor)}\n")
                    self.display_items()
                    print(f"{len(self.item_name) + 1}. Exit Shop")
                    player_action = input("> ")
                    if player_action == str(len(self.item_name) + 1):
                        self.shop = "closed"
                        clear()
                    elif player_action.isdigit() and 1 <= int(player_action) <= len(self.item_name):
                        self.buy(player_action)
                elif action == "Credits":
                    clear()
                    print(
                        f"""
    Game Made By - {self.author}
    Made With {colored("Idle Game Maker", "red")} - SalladShooter""")
                    input(f"Press [" + colored("ENTER", "cyan") + "] to continue...")

    def game_loop(self):
           threading.Thread(target=self.input_thread, daemon=True).start()
           
    def display_items(self):
        for i, item in enumerate(self.item_name, start=1):
            print(
                f"{i}. {colored(item, self.item_color[i - 1])} {colored(self.currency, self.currencyColor)} {self.item_cost[i - 1]} ({self.item_gives_type[i - 1]} {self.item_amount[i - 1]} {colored(self.currency, self.currencyColor)})")

main.py

from idlegamemaker import idlegamemaker

# Example usage
game = idlegamemaker.Game(
    "Idle Apple Collector",
    value={
        "symbol": "",
        "color": "green",
        "collectSpeed": 0,
        "rebirths": True,
        "rebirthName": "Ascend",
    },
    author="SalladShooter",
)

game.add(
    "buy",
    {
        "name": "Item1",
        "cost": 10,
        "color": "blue",
        "givesType": "perSecond",
        "amount": 1,
    },
)
game.add(
    "buy",
    {
        "name": "Item2",
        "cost": 20,
        "color": "cyan",
        "givesType": "perSecond",
        "amount": 5,
    },
)
game.add(
    "buy",
    {
        "name": "Item3", "cost": 50, "color": "red", "givesType": "perClick", "amount": 1
    },
)

game.game_loop()
1 Like

You can use Queue to communicate between the input thread and the main thread. The input thread can place user input into the queue, and the main thread can process the input accordingly.

self.input_queue = Queue()

def input_thread(self):
    while not self.stop_event.is_set():
        print("stop_event is not set")
        choice = input()
        if choice:
            self.input_queue.put(choice)

def game_loop(self):
    threading.Thread(target=self.input_thread, daemon=True).start()
    while not self.stop_event.is_set():
        try:
            choice = self.input_queue.get(timeout=0.1) 
            self.handle_input(choice)
        except Empty:
            pass

You need to set a class variable self.input_queue as Queue().

1 Like

@Sky I tried using Queue but it didn’t seem to work →

__init__.py

from termcolor import colored
from replit import clear
import time
import threading
from threading import Event
from queue import Queue, Empty

class Game:
    def __init__(self, gameName, author: str, value=None, collectSpeed=1, rebirths=False, rebirthName="Rebirth"):
        self.gameName = gameName
        self.author = author
        self.actions = ["Shop"]
        self.currency = value['symbol'] if value else None
        self.rebirths = rebirths

        if value and value.get('rebirths', False):
            self.rebirths = True
            self.rebirthName = value.get('rebirthName', 'Rebirth')
            self.actions.append(self.rebirthName)

        self.currencyColor = value.get('color', 'white')
        self.collectSpeed = value.get('collectSpeed', 1)
        self.title = colored(f"{self.gameName}\n", self.currencyColor)

        self.items = []
        self._money = 0
        self._perSecond = 0
        self.moneyAdd = 1
        self.type = None
        self.color = None
        self.shop = "closed"
        self.actions.append("Credits")
        self.item_name = []
        self.item_cost = []
        self.item_gives_type = []
        self.item_amount = []
        self.item_color = []
        self.running = True

        self._last_money_update = time.time()
        self.stop_event = threading.Event()
        self.input_queue = Queue()

    @property
    def money(self):
        elapsed_time = time.time() - getattr(self, '_last_money_update', time.time())
        self._money += elapsed_time * self._perSecond
        self._last_money_update = time.time()
        return round(self._money)

    @money.setter
    def money(self, value):
        self._last_money_update = time.time()
        self._money = value

    @property
    def perSecond(self):
        return self._perSecond

    @perSecond.setter
    def perSecond(self, value):
        self.money
        self._perSecond = value

    def add(self, type: str, value: dict):
        if type.lower() == "buy":
            item_name = value['name']
            self.item_name.append(item_name)
            item_cost = value['cost']
            self.item_cost.append(item_cost)
            gives_type = value.get('givesType', 'perSecond')
            self.item_color.append(value['color'])
            self.item_gives_type.append(gives_type)
            amount = value.get('amount', 1)
            self.item_amount.append(amount)

    def click(self):
        for i in range(len(self.item_name)):
            if self.item_gives_type[i] == 'perClick':
                self.money += self.moneyAdd

    def buy(self, item):
        item_index = int(item) - 1
        if 0 <= item_index < len(self.item_name):
            item_cost = self.item_cost[item_index]
            item_name = self.item_name[item_index]
            gives_type = self.item_gives_type[item_index]
            amount = self.item_amount[item_index]
            item_color = self.item_color[item_index]
            if self.money >= item_cost:
                self.money -= item_cost
                self.items.append((item_name, item_cost))

                if gives_type == 'perSecond':
                    self.perSecond += amount
                elif gives_type == 'perClick':
                    self.moneyAdd += amount
                clear()
                print(self.title)
                print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            else:
                print(f"You don't have enough {colored(self.currency, self.currencyColor)}'s to buy {colored(item_name, item_color)}.")
                input("Press [" + colored("ENTER", "cyan") + "] to continue...")
        else:
            print("Invalid item selection.")
            input("Press [" + colored("ENTER", "cyan") + "] to continue...")

    def input_thread(self):
        while not self.stop_event.is_set():
            print("stop_event is not set")
            choice = input()
            if choice:
                self.input_queue.put(choice)
                               
    def handle_input(self, choice):
        if choice.isdigit():
            choice = int(choice)
            if 1 <= choice <= len(self.actions):
                action = self.actions[choice - 1]
                if action == "Shop" and self.shop == "closed":
                    clear()
                    print(self.title)
                    print(f"{round(self.money)} - {colored(self.currency, self.currencyColor)}\n")
                    self.display_items()
                    print(f"{len(self.item_name) + 1}. Exit Shop")
                    player_action = input("> ")
                    if player_action == str(len(self.item_name) + 1):
                        self.shop = "closed"
                        clear()
                    elif player_action.isdigit() and 1 <= int(player_action) <= len(self.item_name):
                        self.buy(player_action)
                elif action == "Credits":
                    clear()
                    print(
                        f"""
    Game Made By - {self.author}
    Made With {colored("Idle Game Maker", "red")} - SalladShooter""")
                    input(f"Press [" + colored("ENTER", "cyan") + "] to continue...")

    def game_loop(self):
        threading.Thread(target=self.input_thread, daemon=True).start()
        while not self.stop_event.is_set():
            try:
                choice = self.input_queue.get(timeout=0.1) 
                self.handle_input(choice)
            except Empty:
                pass
                       
    def display_items(self):
        for i, item in enumerate(self.item_name, start=1):
            print(
                f"{i}. {colored(item, self.item_color[i - 1])} {colored(self.currency, self.currencyColor)} {self.item_cost[i - 1]} ({self.item_gives_type[i - 1]} {self.item_amount[i - 1]} {colored(self.currency, self.currencyColor)})") 
1 Like

Does it return any errors?

@Sky it just says stop_event is not set

1 Like

Oh, my bad you have to add a class variable self.stop_event and set it as threading.Event(). You also have to add a function:

def stop(self):
    self.stop_event.set()

Make sure to call this when you want to exit the program, it will exit the threads smoothly.

@Sky I tried what you said, it did not work. So I asked AI to see if I did something wrong, and it could not fix it. →

from termcolor import colored
from replit import clear
import time
import threading
from threading import Event, Lock
from queue import Queue, Empty

class Game:
    def __init__(self, gameName, author: str, value=None, collectSpeed=1, rebirths=False, rebirthName="Rebirth"):
        self.gameName = gameName
        self.author = author
        self.actions = ["Shop"]
        self.currency = value['symbol'] if value else None
        self.rebirths = rebirths

        if value and value.get('rebirths', False):
            self.rebirths = True
            self.rebirthName = value.get('rebirthName', 'Rebirth')
            self.actions.append(self.rebirthName)

        self.currencyColor = value.get('color', 'white')
        self.collectSpeed = value.get('collectSpeed', 1)
        self.title = colored(f"{self.gameName}\n", self.currencyColor)

        self.items = []
        self._money = 0
        self._perSecond = 0
        self.moneyAdd = 1
        self.type = None
        self.color = None
        self.shop = "closed"
        self.actions.append("Credits")
        self.item_name = []
        self.item_cost = []
        self.item_gives_type = []
        self.item_amount = []
        self.item_color = []
        self.running = True
        self.lock = Lock()

        self._last_money_update = time.time()
        self.stop_event = Event()
        self.input_queue = Queue()
        
    @property
    def money(self):
        with self.lock:
            elapsed_time = time.time() - getattr(self, '_last_money_update', time.time())
            self._money += elapsed_time * self._perSecond
            self._last_money_update = time.time()
            return round(self._money)

    @money.setter
    def money(self, value):
        with self.lock:
            self._last_money_update = time.time()
            self._money = value

    @property
    def perSecond(self):
        with self.lock:
            return self._perSecond

    @perSecond.setter
    def perSecond(self, value):
        with self.lock:
            self.money
            self._perSecond = value
            
    def stop(self):
        self.stop_event.set()
        
    def add(self, type: str, value: dict):
        if type.lower() == "buy":
            item_name = value['name']
            self.item_name.append(item_name)
            item_cost = value['cost']
            self.item_cost.append(item_cost)
            gives_type = value.get('givesType', 'perSecond')
            self.item_color.append(value['color'])
            self.item_gives_type.append(gives_type)
            amount = value.get('amount', 1)
            self.item_amount.append(amount)

    def click(self):
        for i in range(len(self.item_name)):
            if self.item_gives_type[i] == 'perClick':
                self.money += self.moneyAdd

    def buy(self, item):
        item_index = int(item) - 1
        if 0 <= item_index < len(self.item_name):
            item_cost = self.item_cost[item_index]
            item_name = self.item_name[item_index]
            gives_type = self.item_gives_type[item_index]
            amount = self.item_amount[item_index]
            item_color = self.item_color[item_index]
            if self.money >= item_cost:
                self.money -= item_cost
                self.items.append((item_name, item_cost))

                if gives_type == 'perSecond':
                    self.perSecond += amount
                elif gives_type == 'perClick':
                    self.moneyAdd += amount
                clear()
                print(self.title)
                print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            else:
                print(f"You don't have enough {colored(self.currency, self.currencyColor)}'s to buy {colored(item_name, item_color)}.")
                input("Press [" + colored("ENTER", "cyan") + "] to continue...")
        else:
            print("Invalid item selection.")
            input("Press [" + colored("ENTER", "cyan") + "] to continue...")

    def input_thread(self):
        while not self.stop_event.is_set():
            print("stop_event is not set")
            choice = input()
            if choice:
                self.input_queue.put(choice)
                               
    def handle_input(self, choice):
        if choice.isdigit():
            choice = int(choice)
            if 1 <= choice <= len(self.actions):
                action = self.actions[choice - 1]
                if action == "Shop" and self.shop == "closed":
                    clear()
                    print(self.title)
                    print(f"{round(self.money)} - {colored(self.currency, self.currencyColor)}\n")
                    self.display_items()
                    print(f"{len(self.item_name) + 1}. Exit Shop")
                    player_action = input("> ")
                    if player_action == str(len(self.item_name) + 1):
                        self.shop = "closed"
                        clear()
                    elif player_action.isdigit() and 1 <= int(player_action) <= len(self.item_name):
                        self.buy(player_action)
                elif action == "Credits":
                    clear()
                    print(
                        f"""
    Game Made By - {self.author}
    Made With {colored("Idle Game Maker", "red")} - SalladShooter""")
                    input(f"Press [" + colored("ENTER", "cyan") + "] to continue...")

    def game_loop(self):
        threading.Thread(target=self.input_thread, daemon=True).start()
        while not self.stop_event.is_set():
            try:
                choice = self.input_queue.get(timeout=0.1) 
                self.handle_input(choice)
            except Empty:
                pass
                               
    def display_items(self):
        for i, item in enumerate(self.item_name, start=1):
            print(
                f"{i}. {colored(item, self.item_color[i - 1])} {colored(self.currency, self.currencyColor)} {self.item_cost[i - 1]} ({self.item_gives_type[i - 1]} {self.item_amount[i - 1]} {colored(self.currency, self.currencyColor)})") 
1 Like

Like what exactly is wrong, and what do you need. I may have misread or something, can you tell me what you expect?

1 Like

@Sky so normally (when it works) I should be able to go to the shop and buy something, if that something is a perSecond item I should get that amount plus the other perSecond amounts I have bought every second added to my money. It works in adding money every second but I can’t reprint because of the input(), so I am trying to do some threading to be able to reprint with the input()’s.

Right now it’s timing out and says: stop_event is not set.


I hope this explains it well.

1 Like

Where are you handling the stop function in your code?

@Sky I use it in the game_loop() function.

def game_loop(self):
        threading.Thread(target=self.input_thread, daemon=True).start()
        while not self.stop_event.is_set():
            try:
                choice = self.input_queue.get(timeout=0.1) 
                self.handle_input(choice)
            except Empty:
                self.stop()