14 Replies - 638 Views - Last Post: 05 February 2019 - 08:11 PM Rate Topic: -----

#1 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Python Chronometer

Posted 25 January 2019 - 08:35 AM

Hello,

I'm trying to build a chronometer as my first Python project, and I got it working to the point where when I press STOP, the time that has passed since i pressed the START button is displayed both on terminal and on the main window.

What I'm having trouble to do right now is constantly displaying the time that has passed since i pressed the START button. I'm not even sure if it's possible due to the way my program is designed (I'm not counting the time, I'm getting a start_time when i press the START button and a stop_time when i press the STOP button, and then calculating the delta_time)

Here is my code so far:
from tkinter import *
import datetime

def start_fun():
	global start_time
	start_time = datetime.datetime.now()
	print(start_time)

def stop_fun():
	global stop_time
	stop_time = datetime.datetime.now()
	print(stop_time)
	delta()
	update_label()

def delta():
	global delta_time
	delta_time = stop_time - start_time
	print(delta_time)

def update_label():
	tempo = delta_time
	timer.config(text=str(tempo))

root = Tk()

root.geometry("300x50")
root.title("vChronometer")

timer = Label(root, text="Hello, World!", font=(None, 14))
timer.place(x=100, y=15)

start = Button(root, width=8, text="START", command=start_fun)
start.place(x=0,y=0)

stop = Button(root, width=8, text="STOP", command=stop_fun)
stop.place(x=0,y=25)

root.mainloop()





Is it even possible to display the delta_time every millisecond or so?
Any hints on how to proceed?

Is This A Good Question/Topic? 0
  • +

Replies To: Python Chronometer

#2 andrewsw   User is offline

  • awks lol ffs
  • member icon

Reputation: 6693
  • View blog
  • Posts: 27,471
  • Joined: 12-December 12

Re: Python Chronometer

Posted 25 January 2019 - 08:48 AM

Basically.. display a clock that updates?

If you search "tkinter clock" you find lots of stuff, such as this.

I would first rename your label to something other than timer, I thought you were already using a timer :whistling:

Essentially, you get a timer running to certain intervals, and at each interval update the label back on the UI. Don't attempt 1ms, 200ms is more like it.
Was This Post Helpful? 0
  • +
  • -

#3 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 28 January 2019 - 06:33 AM

I found a solution. Fully working chronometer code:

# Import the required modules
import threading
import datetime
from tkinter import *

# global variables
i = 0
x = False

# Function that atualizes the label every 0.1 seconds
def printit():
	global i
	global x

	# If x is true, update the label every 0.1 seconds
	if x:
		threading.Timer(0.1, printit).start() # Calls itself every 0.1 seconds
		i = delta() # i = time that has passed since you pressed the start button
		label.config(text=str(i)) # updates the label

# Function that starts the clock
def start():
	global start_time
	global x
	start_time = datetime.datetime.now() # Get current time, and save to start_time
	x = True # Sets x to True
	printit() # Calls the printit function

# Function that calculates the time that has passed since you pressed the start button
def delta():
	global stop_time, delta_time
	stop_time = datetime.datetime.now() # Get current time
	delta_time = stop_time - start_time # Get the time that has passed since you pressed the start button
	return delta_time # Returns delta_time

# Function that makes the clock stop
def stop():
	global x
	x = False # Sets x to false

# Create the main window
root = Tk()

# Set the main window properties
root.geometry("300x60")
root.title("Python Tracker")
root.resizable(0,0)

# Creates the START button
start = Button(root, width=8, text="START", command=start)
start.grid(column=0, row=0)

# Creates the STOP button
stop = Button(root, width=8, text="STOP", command=stop)
stop.grid(column=0, row=1)

# Creates the label that will display time passed
label = Label(root, text="0:00:00.000000", font=("FreeMono", 24))
label.place(x=85, y=14)

# Executes
root.mainloop()


Was This Post Helpful? 1
  • +
  • -

#4 andrewsw   User is offline

  • awks lol ffs
  • member icon

Reputation: 6693
  • View blog
  • Posts: 27,471
  • Joined: 12-December 12

Re: Python Chronometer

Posted 28 January 2019 - 07:00 AM

Did you find that code? Or did you find something that you were able to follow and modify to your needs?

I don't like all those globals, they are a red flag.
Was This Post Helpful? 0
  • +
  • -

#5 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 30 January 2019 - 01:34 PM

View Postandrewsw, on 28 January 2019 - 07:00 AM, said:

Did you find that code? Or did you find something that you were able to follow and modify to your needs?

I don't like all those globals, they are a red flag.


I saw a stackoverflow post about calling a function every second. Modified that to call the function every 0.1 seconds, only if x is True.
Was This Post Helpful? 0
  • +
  • -

#6 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7378
  • View blog
  • Posts: 15,309
  • Joined: 16-October 07

Re: Python Chronometer

Posted 31 January 2019 - 05:16 AM

You know, the more I look at this code, the more I hate it.

Any reason you're avoiding classes? Even if you didn't want to extend frame, you'd still do better with a class for state rather than all those globals.

Note, Tkinter actually has a method you can use without the need for the thread thing. Check out after
Was This Post Helpful? 1
  • +
  • -

#7 ndc85430   User is offline

  • I think you'll find it's "Dr"
  • member icon

Reputation: 954
  • View blog
  • Posts: 3,782
  • Joined: 13-June 14

Re: Python Chronometer

Posted 31 January 2019 - 01:34 PM

View PostAnon962245, on 28 January 2019 - 01:33 PM, said:

i = 0
x = False



Besides what others have said about classes and globals, these variables have horrible names, since they convey no meaning whatsoever about what they're for. i would be reasonable for an index and x for a co-ordinate in the Cartesian plane, but these things aren't that. Please choose meaningful names for your variables because it helps you and others read and understand the code. The fact you had to write a comment on line 18 to tell you what i was for should have suggested it needed a better name.
Was This Post Helpful? 1
  • +
  • -

#8 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 31 January 2019 - 04:50 PM

View Postbaavgai, on 31 January 2019 - 05:16 AM, said:

You know, the more I look at this code, the more I hate it.

Any reason you're avoiding classes? Even if you didn't want to extend frame, you'd still do better with a class for state rather than all those globals.

Note, Tkinter actually has a method you can use without the need for the thread thing. Check out after


I'm pretty new to programming, I don't know how to use classes yet, but I'm learning. I'll definitely adapt my code to OOP when I can.

Appreciate the feedback!



View Postndc85430, on 31 January 2019 - 01:34 PM, said:

View PostAnon962245, on 28 January 2019 - 01:33 PM, said:

i = 0
x = False



Besides what others have said about classes and globals, these variables have horrible names, since they convey no meaning whatsoever about what they're for. i would be reasonable for an index and x for a co-ordinate in the Cartesian plane, but these things aren't that. Please choose meaningful names for your variables because it helps you and others read and understand the code. The fact you had to write a comment on line 18 to tell you what i was for should have suggested it needed a better name.


Thanks for the tip. I have renamed them :)

Here is the updated code.

from tkinter import *
import datetime
import threading

is_true = False

def start_python():
	global start_time, is_true
	start_time = datetime.datetime.now()
	is_true = True
	update_label()

def get_time_passed():
	global start_time, stop_time, delta_time
	stop_time = datetime.datetime.now()
	delta_time = stop_time - start_time
	return delta_time

def update_label():
	global is_true
	if is_true:
		threading.Timer(0.1, update_label).start()
		y = get_time_passed()
		label.config(text=str(y))

def stop_timer():
	global is_true
	is_true = False



root = Tk()

root.geometry("300x60")
root.title("Real Life RPG v1.0")
root.resizable(0,0)

python = Button(root, width=8, text="Python", command=start_python)
python.grid(column=0,row=0)

stop = Button(root, width=8, text="Stop", command=stop_timer)
stop.grid(column=0,row=1)

label = Label(root, text="0:00:00.000000", font=("FreeMono", 24))
label.place(x=85, y=14)

root.mainloop()


Was This Post Helpful? 0
  • +
  • -

#9 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7378
  • View blog
  • Posts: 15,309
  • Joined: 16-October 07

Re: Python Chronometer

Posted 03 February 2019 - 03:55 PM

Since you've got the code...

So, here's basically what you have, with a state object to wrangle those globals.

from tkinter import *
import datetime

class State(object):
    def __init__(self):
        # the moving parts
        self.running = False
        self.start_time = None
        self.label = None # we'll set this later
    # def start_python():
    #     global start_time, is_true
    #     start_time = datetime.datetime.now()
    #     is_true = True
    #     update_label()
    def start(self):
        self.start_time = datetime.datetime.now()
        self.running = True
        self.update_label()
    # def get_time_passed():
    #     global start_time, stop_time, delta_time
    #     stop_time = datetime.datetime.now()
    #     delta_time = stop_time - start_time
    #     return delta_time
    def current_timespan(self):
        return datetime.datetime.now() - self.start_time
    # def update_label():
    #     global is_true
    #     if is_true:
    #         threading.Timer(0.1, update_label).start()
    #         y = get_time_passed()
    #         label.config(text=str(y))
    def update_label(self):
        if self.running:
            self.label.config(text=str(self.current_timespan()))
            self.label.after(100, self.update_label)
    # def stop_timer():
    #     global is_true
    #     is_true = False
    def stop(self):
        self.running = False



def main():
    state = State()

    root = Tk()

    root.geometry("300x60")
    root.title("Real Life RPG v1.0")
    # root.resizable(0,0)

    python = Button(root, width=8, text="Python", command=state.start)
    python.grid(column=0,row=0)

    stop = Button(root, width=8, text="Stop", command=state.stop)
    stop.grid(column=0,row=1)

    state.label = Label(root, text="0:00:00.000000", font=("FreeMono", 24))
    state.label.place(x=85, y=14)

    root.mainloop()

main()



Now, you might be asking what moving those globals into an object buys you. Usually, you'll roll that kind of object up with the gui bits as well. More modular than separation of concerns, at this point. So, for your consideration, the same idea in a reusable object:
import tkinter as tk
import datetime

class TimerWidget(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.running = False
        self.start_time = None
        self.button_start = tk.Button(self, width=8, text="Start", command=self.start)
        self.button_stop = tk.Button(self, width=8, text="Stop", command=self.stop)
        self.time_readout = tk.Label(self, text="0:00:00.000000", font=("FreeMono", 24))
        self.button_start.pack(side=tk.LEFT)
        self.button_stop.pack(side=tk.LEFT)
        self.time_readout.pack(fill=tk.X)
        self.pack()
    def start(self):
        self.start_time = datetime.datetime.now()
        self.running = True
        self.update_label()
    def current_timespan(self):
        return datetime.datetime.now() - self.start_time
    def update_label(self):
        if self.running:
            self.time_readout.config(text=str(self.current_timespan()))
            self.time_readout.after(100, self.update_label)
    def stop(self):
        self.running = False

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        master.title("Kronos")

        ele = TimerWidget(self)
        ele.pack(side=tk.TOP)
        ele = TimerWidget(self)
        ele.pack(side=tk.BOTTOM)
        self.pack()

def main():
    root = tk.Tk()
    app = App(root)
    app.mainloop()

main()


This is a pretty standard way to roll a Tk app. It is certainly not the only way, and there really is no canonical way to go, but it's not a bad start.
Was This Post Helpful? 1
  • +
  • -

#10 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 04 February 2019 - 03:05 PM

@baavgai I couldn't thank you enough for this!
Was This Post Helpful? 0
  • +
  • -

#11 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 05 February 2019 - 09:18 AM

So, here is the code I have:

import tkinter as tk
import datetime

class TimerWidget(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.running = False
        self.start_time = None
        self.button_start = tk.Button(self, width=8, text="Start", command=self.start)
        self.button_stop = tk.Button(self, width=8, text="Stop", command=self.stop)
        self.time_readout = tk.Label(self, text="0:00:00.000000", font=("FreeMono", 24))
        self.button_start.pack(side=tk.LEFT)
        self.button_stop.pack(side=tk.LEFT)
        self.time_readout.pack(fill=tk.X)
        self.pack()

    def start(self):
        self.start_time = datetime.datetime.now()
        self.running = True
        self.update_label()

    def current_timespan(self):
        return datetime.datetime.now() - self.start_time

    def update_label(self):
        if self.running:
            self.time_readout.config(text=str(self.current_timespan()))
            self.time_readout.after(100, self.update_label)

    def stop(self):
        self.running = False
        self.write_to_file()

    def write_to_file(self):
        file = open("python.txt", "a")
        message = str(self.start_time) + ", " + str(datetime.datetime.now()) + ", " + str(self.current_timespan()) + "\n"
        file.write(message)
        file.close()

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        master.title("Kronos")

        ele = TimerWidget(self)
        ele.button_start.config(text="Python")
        ele.pack(side=tk.TOP)

        ele = TimerWidget(self)
        ele.button_start.config(text="C")
        ele.pack(side=tk.BOTTOM)

        ele = TimerWidget(self)
        ele.button_start.config(text="Java")
        ele.pack(side=tk.BOTTOM)
        self.pack()

def main():
    root = tk.Tk()
    app = App(root)
    app.mainloop()

main()




What I'm trying to do now is make each stop button save to the appropriate text file, instead of all stop buttons saving to the same file.

What I thought about doing is making three different functions, one to write to each file, but I feel like there should be an easier way.
Was This Post Helpful? 0
  • +
  • -

#12 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7378
  • View blog
  • Posts: 15,309
  • Joined: 16-October 07

Re: Python Chronometer

Posted 05 February 2019 - 09:49 AM

Pass the values that are unique to that instance of the TimerWidget. e.g.
class TimerWidget(tk.Frame):
    def __init__(self, parent, start_label, filename):
    ...
    def write_to_file(self):
        file = open(self.filename, "a")
...
class App(tk.Frame):
...    
        TimerWidget(self, "Python", "foo.txt").pack(side=tk.TOP)
        TimerWidget(self, "C", "bar.txt").pack(side=tk.TOP)


Was This Post Helpful? 1
  • +
  • -

#13 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 05 February 2019 - 12:05 PM

Here is my code:
import tkinter as tk
import datetime

class TimerWidget(tk.Frame):
    def __init__(self, parent, start_label, filename):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.running = False
        self.start_time = None
        self.button_start = tk.Button(self, width=8, text=start_label, command=self.start)
        self.button_stop = tk.Button(self, width=8, text="Stop", command=self.stop)
        self.time_readout = tk.Label(self, text="0:00:00.000000", font=("FreeMono", 24))
        self.button_start.pack(side=tk.LEFT)
        self.button_stop.pack(side=tk.LEFT)
        self.time_readout.pack(fill=tk.X)
        self.pack()

    def start(self):
        self.start_time = datetime.datetime.now()
        self.running = True
        self.update_label()

    def current_timespan(self):
        return datetime.datetime.now() - self.start_time

    def update_label(self):
        if self.running:
            self.time_readout.config(text=str(self.current_timespan()))
            self.time_readout.after(100, self.update_label)

    def stop(self):
        self.running = False
        self.write_to_file()

    def write_to_file(self):
        file = open(self.filename, "a")
        message = str(self.start_time) + ", " + str(datetime.datetime.now()) + ", " + str(self.current_timespan()) + "\n"
        file.write(message)
        file.close()

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        master.title("Kronos")

        ele = TimerWidget(self, "Python", "python.txt")
        ele.pack(side=tk.TOP)

        ele = TimerWidget(self, "C", "c.txt")
        ele.pack(side=tk.BOTTOM)

        ele = TimerWidget(self, "Java", "java.txt")
        ele.pack(side=tk.BOTTOM)
        self.pack()

def main():
    root = tk.Tk()
    app = App(root)
    app.mainloop()

main()




And here is the error I'm getting when i click the STOP button:
Exception in Tkinter callback
Traceback (most recent call last):
  File "/home/victor/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "rlrpg2.py", line 33, in stop
    self.write_to_file()
  File "rlrpg2.py", line 36, in write_to_file
    file = open(self.filename, "a")
AttributeError: 'TimerWidget' object has no attribute 'filename'



What does it mean "'TimerWidget' object has no attribute 'filename'"?
Was This Post Helpful? 0
  • +
  • -

#14 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7378
  • View blog
  • Posts: 15,309
  • Joined: 16-October 07

Re: Python Chronometer

Posted 05 February 2019 - 03:52 PM

Probably that you are calling something like self.filename but nowhere in your object was there an assignment of that attribute. e.g. self.filename = ...

I applaud your effort to take the code in front of you and attempt to pound it into shape, even if you only half understand it. It's a good way to learn and what I do myself. However, it feels like you've gotten to the point where you might want to read up on some fundamentals. e.g. https://docs.python....al/classes.html
Was This Post Helpful? 1
  • +
  • -

#15 Anon962245   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 9
  • Joined: 25-January 19

Re: Python Chronometer

Posted 05 February 2019 - 08:11 PM

View Postbaavgai, on 05 February 2019 - 03:52 PM, said:

Probably that you are calling something like self.filename but nowhere in your object was there an assignment of that attribute. e.g. self.filename = ...

I applaud your effort to take the code in front of you and attempt to pound it into shape, even if you only half understand it. It's a good way to learn and what I do myself. However, it feels like you've gotten to the point where you might want to read up on some fundamentals. e.g. https://docs.python....al/classes.html


Thanks once again. All I had to do is add this line self.filename = filename to the TimerWidget class, and now everything works exactly like I wanted to. I can't wait till I have enough data on my studying habits to analyze, lol!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1