multithreading chronometer in python

2

I am doing a project for the school in which I have to program software that helps children improve their spelling:

#-*- coding: utf-8 -*-
import random  
import subprocess
def corrector(pbien, tup):
    if tup == pbien:
        return 1
    else:
        return -1
mw = ("mucica", "presio", "vanda", "kansiones","yenar","majia","chiko","embidia","cinfonia")
gw = ("musica","precio","banda","canciones", "llenar", "magia", "chico", "envidia", "sinfonia")
count = 0
if __name__ == '__main__':
    wt = True
    while wt:
        subprocess.call(["cmd.exe","/c","cls"])
        randint = random.randint(0, len(mw) - 1)
        print('''corrige: {}
puntos: {}'''.format(mw[randint], count))
        yourw = str(raw_input("> "))
        check = corrector(gw[randint], yourw)
        count += check
        if count >= 5:
            wt = False
            print("ganaste")
        elif count <= -3:
            wt = False
            print("perdiste")

I feel that the program is very simple, so I want to add a stopwatch to make it a little more challenging, the stopwatch would go before the variable 'yourw' and there would have to be an if something like this:

if cronometro < 0:
    wt = False
    print("perdiste, se te acabo el tiempo")
    
asked by binario_newbie 27.03.2018 в 03:09
source

1 answer

2

Update

  

Actually the version with select() that is explained in this answer only works in Unix, because in Windows this function only supports sockets descriptors and not files (nor standard input).

     

Therefore this answer is not useful for the user who apparently works in Windows. However, I decided to leave it in case it was useful to other users.

     

A solution that does work in Windows appears at the end of the original response, in the Windows solution

section

Original answer

It is not necessary to enter multi-threaded programming to do what you want. You can use the function select() .

This function allows you to wait in a list of "objects" by which you could receive data (they can be files, sockets or, in your case, the keyboard) until either one of those objects has received data, or Expend the waiting time. In either case select() ends its execution and returns a list containing the objects for which data has been received (if the list is empty is that it was not received by any, and therefore the waiting time it ran out).

In your case, you could pass to select() a list with a single object: the standard input (the keyboard). If the return of select() that object is in the list returned, is that there are data waiting to be read from the keyboard, and simply call raw_input() to read them (which will not be blocked waiting for anything, because there is something). Otherwise, the time has run out.

The following is a function that implements these ideas:

def espera_respuesta(tiempo_max=5):
    print "> ",
    sys.stdout.flush()

    entrada = sys.stdin.fileno()
    listo, _ ,_ = select.select([entrada], [], [], tiempo_max)
    if entrada in listo:
        respuesta = raw_input()
        return respuesta
    else:
        return None

The first two lines are to print the "prompt" inviting the user to write something. Then we prepare the object "input" and we pass it to select() (this function actually expects three separate lists, but the other two do not use them and they are empty). The last parameter is the maximum time that select will wait.

select() returns three lists, of which again I am only interested in the first one (the other two will come empty). If entrada is in the returned list, there is data. I read them. If not, it is that time has run out. Return None to indicate that the user did not write anything.

You would use this function from your program, for example:

if __name__ == '__main__':
    wt = True
    while wt:
        cls()
        randint = random.randint(0, len(mw) - 1)
        print('corrige: {}\npuntos: {}'.format(mw[randint], count))
        yourw = espera_respuesta()
        if yourw is None:
            print("\n¡Tiempo agotado!")
            time.sleep(2)
            continue
        check = corrector(gw[randint], yourw)
        count += check
        if count >= 5:
            wt = False
            print("ganaste")
        elif count <= -3:
            wt = False
            print("perdiste")

Notice how instead of your previous raw_input() there is now a call to espera_respuesta() . When it returns, if you returned None , you print a time-out message (we paused the program a couple of seconds so that the user has time to read it before you clear the screen again) and return to the beginning of the loop with continue . Note that in this case we do not penalize the player, because we did not pass the corrector to his response. Remove the continue if you want the answer to be verified (and since this was None , it will not be the same as expected and you would be penalized).

Another improvement

Instead of storing the list of words well and badly in two separate variables, you could have a dictionary whose keys were the wrong words and the values the corrected version. I think that it is easier to add vocabulary to the game. The variable would be for example like this:

vocabulario = {
 'chiko': 'chico',
 'cinfonia': 'sinfonia',
 'embidia': 'envidia',
 'kansiones': 'canciones',
 'majia': 'magia',
 'mucica': 'musica',
 'presio': 'precio',
 'vanda': 'banda',
 'yenar': 'llenar'
}

To choose one at random, you can use random.choice(list(vocabulario)) . The expression list(vocabulario) converts the dictionary into a list, keeping only the keys (the wrong words), and random.choice() returns a random element from that list. Once you have the misspelled word, your good version would be vocabulario[mal] . That is:

    mal = random.choice(list(vocabulario))
    bien = vocabulario[mal]

Solution in Windows

In windows the solution with select() is not valid. A multi-threaded solution would also be quite complex since, although it is easy to make another thread wait a while and print "Time out", it is not so easy to interrupt the main thread that will still be waiting in raw_input() for the user to enter something.

It is easier to make a function that reads from the keyboard "key to key", in a loop, timing while reading, and ending the reading if the available time runs out.

This would be a possible implementation (specific to Windows, since it uses the msvcrt module):

import msvcrt
def espera_respuesta(tiempo_max=5):
    print "> ",
    sys.stdout.flush()

    horafinal = time.time() + tiempo_max
    resultado = []   # Iremos almacenando aqui las teclas pulsadas
    while True:
        if msvcrt.kbhit():           # Si hay tecla pulsada
            tecla = msvcrt.getche()  # la leemos
            if tecla == '\r':        # si es retorno de carro
                print()
                return ''.join(resultado)  # Retornamos la cadena leida
            resultado.append(tecla)  # Si no, metemos la tecla a la lista
        time.sleep(0.1)              # breve pausa para no saturar la CPU
        if time.time() > horafinal:  # Si se agotó el tiempo, volvemos
            return None
    
answered by 27.03.2018 / 10:03
source