TL;DR: Czas w Pythonie liczy się jako liczbę sekund od epoch, wystarczy więc, żeby program przechowywał liczbę sekund i na nich wykonywał obliczenia a potem konwertował wynik w zależności od potrzeb.

Program Tacho będzie wykonywał wszystkie obliczenia na czasie. Aby jak najbardziej uprościć mu to zadanie powinien on przechowywać dane jako liczba sekund. Dlaczego?

Kocepcja czasu w Pythonie (i nie tylko) jest dość prosta. Python liczy sekundy od 01.01.1970 00:00:00 (UTC) czyli od unix epoch. Czyli bieżący czas to liczba sekund, która upłyneła od epoch, którą potem możemy przekonwertować na datę i godzinę.

Na szczęście nie trzeba tego konwertować samemu, bo jest od tego gotowy moduł o nazwie datetime lub time (moduły te należy importować: import datetime).

Wiadomo, że najlepiej poczytać o tych modułach w dokumentacji, ale na początek można też obejrzeć na szybko filmik po polsku o jego najważniejszych koncepcjach a przy okazji można dowiedzieć się jak zmusić Pythona do wykonywania czynności po upływie określonego czasu.

Jak wykonywać działania na dacie i czasie w Pythonie?

Ja na początek potrzebuję robić proste dodawanie czasu do siebie. Dzieki temu, że czas mierzony jest w sekundach od epoch, wszystkie dane wprowadzone przez użytkownika będą przechowywane w sekundach i na nich będą wykonywane wszystkie potrzebne obliczenia. Na koniec przekonwertowane dane program będzie ładnie wyświetlał użytkownikowi. Ma to dwie podstawowe zalety. Po pierwsze matematyka będzie wykonywana tylko na pojedynczych liczbach co uprości mocno zadanie, natomiast wynik będzie mógł być łatwo przekonwertowany w zależności od potrzeb.

Jak konwertować sekundy na HH:MM:SS?

Bardzo łatwo, wystarczy:

>>> import datetime
>>> str(datetime.timedelta(seconds=666))
'0:11:06'

Fajną opcją jest fakt, że jeśli liczba sekund wynosi więcej niż jeden dzień to jest to słownie zapisywane czyli 666666 da nam „7 days, 17:11:06”.

Ja na początek będę potrzebować, żeby program pokazywał więcej niż 24 godziny a do tego minuty i sekundy. Chcę więc, aby 666666 sekund było wyświetlane jako „185:11:06”. Można to osiągnąć na piechotę:

>>> seconds = 666666
>>> result = '%d:%02d:%02d' % (seconds / 3600, seconds / 60 % 60, seconds % 60)
>>> print(result)
'185:11:06'

Screenshot from 2017-04-20 09-37-39croped

Oto jak wygląda program Tacho w tej chwili. Dane na liście są przeformatowane na HH:MM:SS natomiast w statusie total dodawane są po prostu sekundy do siebie, bo na tym etapie użytkownik wprowadza z klawiatury liczbę sekund.

Oczywiście to się zmieni, bo zaraz zmusimy go do tego, żeby wprowadzał czas w formacie HH:MM:SS. Wtedy program będzie to konwertował na sekundy i tak zapisywał a potem spowrotem przerabiał to na miły dla oka format. Ale po kolei.

Podsumowanie

Wydaje się, że nie wiele się zmieniło, ale najważniejsza jest koncepcja w głowie, która mówi w jaki sposób przechowywać dane a potem wykonywać na nich operacje. W przyszłości będzie trzeba porówynywać do siebie nie tylko godziny, ale również i daty. Program musi wiedzieć np. czy w przeciągu dwutygodniowego okresu został wykorzystany dostępny czas jazdy. Dzięki temu, że czas liczony jest w sekundach od epoch i dane są przechowywane jako liczba sekund wszystko staje się dużo prostrze.

Nie chcę, żeby użytkownik musiał mozolnie wprowadzać całą datę, żeby program mógł coś obliczyć. Zamiast wprowadzać dane na zasadzie od 20 kwietnia 2017 od godziny 00:00 do 20 kwietnia 2017 do godziny 10:00 był odpoczynek, użytkownik ustali datę (np. przejdzie na zakładkę konkretnego dnia) a potem wprowadzi np. 10h odpoczynku a program sam ogarnie resztę na podstawie epoch. Wydaje się to sensowne.

Na tym etapie już mocno zaczyna być widać, że muszę wprowadzić klasy do kodu.  Sama konwersja sekund na inne formaty oraz obliczenia daty na podstawie epoch muszą być ujęte w klasie, bo zacznie się robić bałagan. Zanim użytkownik będzie musiał wprowadzać czas w formacie HH:MM:SS, który potem będzie przerabiany na sekundy i tak zapisywany trzeba ogarnąć klasy. To będzie moje kolejne zadanie.

Jeśli czytasz to i chcesz podpowiedzieć jak przerobić poniższy kod na kod z klasami to koniecznie daj znać w komentarzu.

from tkinter import *

entries = []
total_info = ""

def add_entry(evt):  # For calculation button
    global total_info, entries
    entry = top_frame_input.get()
    entry = entry.replace("+", "")

    try:
        int(entry)
        # entries_list.insert(0,entry)
        entries.append(int(entry))
        total_info = "Total: " + str(sum(entries))
        status.set(total_info)
        entries_update()
        top_frame_input.delete(0, END)

    except:
        top_frame_input.focus()

def clear_all(evt):  # clear top_frame_input
    top_frame_input.delete(0, END)
    top_frame_input.focus()

def clear_one(evt):  # clear last digit from top_frame_input
    entry = top_frame_input.get()[:-1]
    top_frame_input.delete(0, END)
    top_frame_input.insert(0, entry)

def num_press(num):  # num pad button action
    if num == "C":
        clear_all("C")
    elif num == ",":  # yeah, I found it quite useful for my keyboard
        top_frame_input.insert(END, ".")
    else:

        top_frame_input.insert(END, num)
        top_frame_input.focus()

def entries_update():
    entries_list.delete(0, END)
    for sec in entries:
        # convert to HH:MM:SS and add it to listbox
        converted_seconds = "%d:%02d:%02d" % (sec / 3600, sec / 60 % 60, sec % 60)
        entries_list.insert(0, converted_seconds)
    # print(entries_list.get(0, END))

# =======tkinter window===========
win = Tk()
# win.geometry("775x325") # Force window size
win.wm_title("Tacho 0.0.2")
win.resizable(width=FALSE, height=FALSE)

topFrame = Frame(win)
# topFrame.pack(fill=BOTH)
topFrame.grid(row=0, column=0, columnspan=2)

rightFrame = Frame(win)
rightFrame.grid(row=1, column=1)

leftFrame = Frame(win)
# middleFrame.pack(fill=BOTH)
leftFrame.grid(row=1, column=0)

bottomFrame = Frame(win)
bottomFrame.grid(row=2, columnspan=2)

# -------keys actions-----
win.bind("<Return>", add_entry)
win.bind("<KP_Enter>", add_entry)
win.bind("<KP_Add>", add_entry)

# ==========input entry===========
top_frame_input = Entry(topFrame, textvariable=StringVar(),
                        font="Helvetica 20 bold", width=18, justify=RIGHT)
top_frame_input.grid(column=8)
# top_frame_input.pack(fill=X, expand=True, side=RIGHT, ipady=10)
top_frame_input.bind('<Button-1>', clear_all)
top_frame_input.focus()

# ======top icons==========
top_frame_icons = []

for i in range(0, 7):

    top_frame_icons.append(

        Button(topFrame, text='X',

               font="Helvetica 15 bold", height=1, width=2)

    )

    top_frame_icons[i].grid(row=0, column=i)

# ===========Listbox with scrollbar=================
entries_list = Listbox(leftFrame, exportselection=0, height=15, width=40)
entries_list.pack(side=LEFT, fill=BOTH, expand=True)

entries_list_scrollbar = Scrollbar(leftFrame, orient=VERTICAL)  # scrollbar
entries_list_scrollbar.pack(side=LEFT, fill=BOTH)

entries_list_scrollbar.configure(command=entries_list.yview)
entries_list.configure(yscrollcommand=entries_list_scrollbar.set)

# entries_list.bind('<<ListboxSelect>>', select) #action for selected line

# =======num pad==========
keyboard = []
keys = "HMS789456123C0:"
i = 0
for j in range(1, 6):
    for k in range(3):
        keyboard.append(Button(rightFrame, text=keys[i], font="Helvetica 15 bold", height=1, width=2))
        keyboard[i].grid(row=j, column=k, pady=2, padx=2)
        keyboard[i]["command"] = lambda x=keys[i]: num_press(x)
        i += 1

# --------other buttons---------
add_entry_button = Button(rightFrame, text="+", font="Helvetica 15 bold", height=6, width=7)
add_entry_button.grid(row=1, column=4, rowspan=4, columnspan=2, pady=2, padx=2)
add_entry_button.bind('<Button-1>', add_entry)

clear_one_button = Button(rightFrame, text="←", font="Helvetica 15 bold", height=1, width=2)
clear_one_button.grid(row=5, column=4, pady=2, padx=2)
clear_one_button.bind('<Button-1>', clear_one)

modulo_button = Button(rightFrame, text="M24", font="Helvetica 15 bold", height=1, width=2)
modulo_button.grid(row=5, column=5, pady=2, padx=2)
modulo_button.bind('<Button-1>', add_entry)

# =====bottom status=============
status = StringVar()
bottom_status_total = Label(bottomFrame, textvariable=status, bd=1, relief=SUNKEN,
                            font="Helvetica 15 bold", width=54)
status.set(total_info)
bottom_status_total.pack(fill=X, expand=True, side=TOP, ipady=10, ipadx=10)

# ============================================

win.mainloop()

3 uwagi do wpisu “Ten czas – praca z datą i godziną w Pythonie

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s