• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

time.sleep() z możliwością przerwania komendą

Object Storage Arubacloud
+2 głosów
279 wizyt
pytanie zadane 16 października 2021 w Python przez Maflex24 Nowicjusz (240 p.)

Witam!

W ramach treningu piszę w pythonie konsolową aplikację, która podlicza mój czas pracy, eksportując dane do pliku .json. 

Chciałbym aby np. po wpisaniu 'work' program między innymi odświeżał i wyświetlał mi informację ile już pracuję, co powiedzmy 30 sekund. Można użyć funkcji time.sleep(30), zgadza się. Problemem jest jednak to, że wtedy nie mam możliwości, aby to odliczanie przerwać, gdy powiedzmy chcę wpisać "break", aby pójść na przerwę i zmienić tym samym status programu. 

Gdy zrobię, by program np. co 10 sekund pytał mnie, czy chcę kontynuować, to jest to bez sensu, bo ma sobie działać w tle, a nie przerywać mi pracy, czekając ma moją decyzję. 

Próbowałem na szybko rozwiązania typu:

for i in range(60):
    try:
        debugInfo("counting, number:", i)
        time.sleep(5)
    except KeyboardInterrupt:
        debugInfo("counting interupted")

ale to w moim przypadku nie działa. Program odlicza dalej, niezależnie, co wcisnę. Jeśli ktoś ma pomysł, jak mogę to zrobić, co użyć, bardzo proszę o podpowiedź. Oczywiście mogę się bez tego obejść, gdy będę chciał sprawdzić czas, to po prostu wpisać "status", ale to nie do końca to, czego oczekuję :)

komentarz 16 października 2021 przez Oscar Nałogowiec (29,290 p.)
To zwykle robi się odwrotnie. Nie przerywa się sleepa, tylko używa się operacji na coś czekającej z timeoutem. Np. na dane po sieci, jakiś sygnał, itp.Wybierająć odpowiednią funkcję określasz w jaki sposób kończy się czekanie.

2 odpowiedzi

+1 głos
odpowiedź 18 października 2021 przez VBService Ekspert (253,280 p.)
edycja 19 października 2021 przez VBService
 
Najlepsza

Proponuję wykorzystać moduł keyboard ( keyboard.is_pressed ), a do zliczania czasu datetime ( datetime, timedelta )

przykład

[ edit ]

import sys, os, keyboard, time
from datetime import datetime, timedelta

clearConsole = lambda: os.system('cls' if os.name in ('nt', 'dos') else 'clear')

def menu(cmd):
    clearConsole()
    if cmd == 'all':
        print(
              'w - Start work time\n'
              'b - Start break time\n'
              'n - Stop work time\n'
              'e - Exit\n'          
             )
        
    if cmd == 'break':
        print('w - Stop break, continue work time\n')
        
    if cmd == 'stop':
        print(
              'w - Continue work time\n'
              'e - Exit\n'          
             )


menu('all')
time_break_array = []
time_break_array_length = 0
time_stop_work = datetime.strptime('00:00:00.000000', '%H:%M:%S.%f')

time_loop = False
time_break_loop = False

while True:
    if keyboard.is_pressed('w'):
        if not time_loop:
            menu('all')
            time_start_work = datetime.now()
            time_stop_work_td = timedelta(hours=time_stop_work.hour, minutes=time_stop_work.minute, seconds=time_stop_work.second)
            time_loop = True
            
        if time_break_loop:
            time_break_loop = False
            time_stop_break = datetime.strptime(str(calculate_time_break), "%H:%M:%S.%f")
            time_break_array.append(timedelta(hours=time_stop_break.hour, minutes=time_stop_break.minute, seconds=time_stop_break.second))
            time_break_array_length = len(time_break_array)
            

    if keyboard.is_pressed('n'):
        if time_loop:
            menu('stop')
            time_loop = False
            time_stop_work = datetime.strptime(str(calculate_time_work), "%H:%M:%S.%f")

            sys.stdout.write("\rTime work: " + str(calculate_time_work))
            sys.stdout.flush()


    if keyboard.is_pressed('b'):
        if time_loop and not time_break_loop:
            menu('break')
            time_start_break = datetime.now()
            time_stop_break = datetime.strptime('00:00:00.000000', '%H:%M:%S.%f')
            time_stop_break_td = timedelta(hours=time_stop_break.hour, minutes=time_stop_break.minute, seconds=time_stop_break.second)            
            time_stop_work = datetime.strptime(str(calculate_time_work), "%H:%M:%S.%f")
            
            if time_break_array_length > 0:
                for i in range(time_break_array_length):
                    print(f"Time break [{i + 1}]: {time_break_array[i]}")
            
            time_loop = False
            time_break_loop = True


    if keyboard.is_pressed('e'):
        if not time_break_loop:
            try:
                clearConsole()
                print(f"Time work: {calculate_time_work}")

                if time_break_array_length > 0:
                    for i in range(time_break_array_length):
                        print(f"Time break [{i + 1}]: {time_break_array[i]}")
                else:
                    print('The break was not used!')
            except:
                clearConsole()
            finally:
                exit()

    if time_loop:
        calculate_time_work = datetime.now() - time_start_work
        calculate_time_work += time_stop_work_td
        
        sys.stdout.write("\rTime work: " + str(calculate_time_work))
        sys.stdout.flush()

    if time_break_loop:
        calculate_time_break = datetime.now() - time_start_break
        calculate_time_break += time_stop_break_td
        
        sys.stdout.write("\rTime break [" + str(time_break_array_length + 1) + "]: " + str(calculate_time_break))
        sys.stdout.flush()

    time.sleep(0.05)

 

 

Clear Console in Python ]  [ Timedelta in Python ]
How do I write output in same place on the console? ]

1
komentarz 18 października 2021 przez Maflex24 Nowicjusz (240 p.)

Po zrobieniu kiedyś podobnego skryptu w javascript nauczyłem się, aby wykorzystywać do tego obiekty typu 'date', na samym początku użyłem wtedy 

setTimeout()

i szybko przekonałem się o tym, jakie miałem rozbieżności między realnym czasem, a pokazywanym przez mój arcyskrypt. Przy okazji nauczyłem się operować czasem (jakkolwiek to brzmi), a to myślę, że duży plus. 

Dzięki bardzo za odpowiedzi, nie analizowałem jeszcze dokładnie twojego kodu @VBService, ale widzę, że twój pomysł nieco rozszerzyłem. Mój skrypt regularnie zapisuje dane do jsona, na tym mi bardzo zależało, by nawet w jakieś sytuacji awaryjnej, te dane były regularnie aktualizowane i zapisywane, bez mojej zbytniej ingerencji. Jeśli masz/macie ochotę, zapraszam na githuba, zerknąć jak to wygląda: work-time-watcher

Tutaj jest główny plik 'work-time-watcher.py':

# imports
from datetime import datetime, time, date
from useful_functions import debugInfo, info, error, function_test, clearConsole, clear
from os.path import exists
import json
import time

# definitions:
def create_json_file(path, file_name):
    with open(path + file_name, 'w+') as created_file:
        debugInfo('json file created')
        function_test(today_file_is_created())     
        
def update_json_file(path, file_name, element):
    with open(path + file_name, 'w') as created_file:
        json.dump(element, created_file)
        debugInfo('updated json file')

def read_json_file(path, file_name):
    with open(path + file_name, 'r') as readed_file:
        global times
        times = json.load(readed_file)
        debugInfo('readed json file')

def show_json_file_content(path):
    file_name = input("input file date in format: YYYY-MM-DD: ")
    with open(path + file_name + '_worktime.json', 'r') as readed_file:
        json_content = json.load(readed_file)
        info("json file content: ", json_content)

def data():
    show_json_file_content(path_of_file)

existing_commands = ["help", "start", "stop", "exit", "work", "w", "break", "b", "status", "clear", "data"]
today_file_name = f'{date.today()}_worktime.json'
path_of_file = 'worktime_jsons/'

def is_command_valid(user_command): 
    for command in existing_commands:
        if user_command == command:
            debugInfo("you choose existed command", user_command)
            return True
    return False

def execute_command(command):
    if command == 'break':
        break_in_work()
    else:
        globals()[command]()

def today_file_is_created():
    is_file_created = exists(path_of_file + today_file_name)
    return is_file_created

def help():
    info("possible commands:", existing_commands)

def start():
    if not today_file_is_created():
        create_json_file(path_of_file, today_file_name)
        times['today_date'] = str(date.today().year) + '-' + str(date.today().month) + '-' + str(date.today().day) 
        debugInfo(times["today_date"])
        update_json_file(path_of_file, today_file_name, times)
    else:
        print("Session was already started")
    

def stop():
    pass
    # TODO

counting = {
    "work_id": 0,
    "break_id": 0,
}

def work():
    if today_file_is_created():
        times["works"].append({'start': datetime.now().timestamp()})
        update_json_file(path_of_file, today_file_name, times)

        work_in_progress = True
        refresh_loop_id = 0
        while work_in_progress:
            try:
                current_work = times['works'][counting["work_id"]]
                current_work['stop'] = datetime.now().timestamp()
                current_work['total'] = current_work['stop'] - current_work['start']
                print_work_time()
                time.sleep(10)

                if refresh_loop_id % 10 == 0: 
                    info("Type:'Ctrl + C to stop refreshing")
                if refresh_loop_id % 5 == 0: 
                    update_json_file(path_of_file, today_file_name, times)

                refresh_loop_id += 1

            except KeyboardInterrupt:
                info("waiting loop was stopped")
                update_json_file(path_of_file, today_file_name, times)
                work_in_progress = False

        counting["work_id"] += 1
    else:
        error("first you need to use command 'start' to create today .json file")

salary_per_minute = 7 / 60

def print_work_time():
    total = 0
    global times
    for work_value in times['works']:
        try:
            total += work_value['total']
        except:
            error(work_value, 'has no argument "total"')

    t1 = datetime.fromtimestamp(total)
    salary = ((t1.hour - 1) * salary_per_minute * 60) + (salary_per_minute * t1.minute)
    print()
    info("Today total worktime is:", f'{t1.hour-1} hours and {t1.minute} minutes')
    info(f"You earned today: {round(salary, 2)} Eur")

def w(): work()

def break_in_work():
    if today_file_is_created():
        # TODO
        pass
    else:
        error("first you need to use command 'start' to create today .json file")

def b(): break_in_work()

def status():
    pass
    # TODO

# Dates
today = date.today()
times = {
        'today_date': '',
        'worktime_hours': None,
        'worktime_minutes': None,
        'total_worktime': None,
        'total_breaktime': None,
        'works': [],
        'breaks': []
    }

user_command = "not started"
def main():
    if today_file_is_created(): #FIXME if file is created, but it's empty, json have error
        read_json_file(path_of_file, today_file_name)
        counting["work_id"] = len(times["works"])
        counting["break_id"] = len(times["breaks"])

    global user_command
    while user_command != 'exit':
        print()
        user_command = input("Write command, type 'help' if you don't know what to choose: ")
        
        if is_command_valid(user_command) and user_command != 'exit':
            execute_command(user_command)
        elif user_command == 'exit':
                debugInfo("exit")
                clearConsole()
                pass
        else:
            error("Choose valid command!")
            pass   
    
main()

 

komentarz 18 października 2021 przez mokrowski Mędrzec (155,460 p.)
Ale dlaczego pooling? Przecież to "przepala" energię systemu! Nie lepiej asychronicznie to zrobić? Proces powinien zasnąć i nie konsumować energii a nie kręcić się "jak chomik" w pętli while.
komentarz 19 października 2021 przez VBService Ekspert (253,280 p.)
edycja 19 października 2021 przez VBService

Dodanie time.sleep(0.05) w pętli while poprawia nieco "przepał". Można zamiast moduł keyboard użyć np. keyboard-listener 0.0.7 lub Pynput

+1 głos
odpowiedź 16 października 2021 przez mokrowski Mędrzec (155,460 p.)
Zainteresuj się modułem signal

Podobne pytania

+1 głos
1 odpowiedź 298 wizyt
pytanie zadane 30 maja 2021 w PHP przez KFC Użytkownik (610 p.)
0 głosów
2 odpowiedzi 392 wizyt
0 głosów
1 odpowiedź 1,133 wizyt
pytanie zadane 4 lipca 2019 w JavaScript przez czarli.w Nowicjusz (150 p.)

92,565 zapytań

141,418 odpowiedzi

319,602 komentarzy

61,951 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...