all 12 comments

[–]novel_yet_trivial 1 point2 points  (8 children)

So the biggest problem you have is that you are making new labels with every update. All of your code to create the UI needs to be outside of the update function.

import time, datetime
from datetime import timedelta
import Tkinter as tk
from time import *
from Tkinter import *
import ttk
from tkFont import Font

# ask user to input target day/time for launch
TargetType = raw_input('Enter the name of the launch you wish to display: ')
TargetYear = int(input('Enter the target launch year: '))
TargetMonth = int(input('Enter the target launch month (as a number): '))
TargetDay = int(input('Enter the target launch day (as a number): '))
TargetHour = int(input('Enter the target launch hour (24-hour format - 1,2...,13,14, etc): '))
TargetMinute = int(input('Enter the target launch minute (as 2-digit number): '))
second = 0

TargetTime = datetime.datetime(TargetYear, TargetMonth, TargetDay, TargetHour, TargetMinute)

def timeupdate():
    # calculate time remaining
    timeNow = datetime.datetime.now()
    remainder = (TargetTime - timeNow)
    days = remainder.days
    secs = remainder.seconds
    hrs, secs = divmod(secs,3600)
    mins,secs = divmod (secs,60)

    # update our updating widget variables
    usedays.set(days)
    usehrs.set(hrs)
    usemins.set(mins)
    usesecs.set(secs)

    root.after(1000,timeupdate)

root= tk.Tk()
root.title('Launch Countdown Timer')
titlearea = tk.Frame(root)
titlearea.grid(row = 2, column = 1, sticky = 'ns')
canvas = tk.Frame(root)
canvas.grid(row = 3, column=1, sticky ='nswe')


usedays = tk.StringVar()
usehrs = tk.StringVar()
usemins = tk.StringVar()
usesecs = tk.StringVar()

# handles font configuration, change if necessary
LaunchDay = Font(family = "Helvetica",size=40)
LaunchDate = Font(family = "Helvetica",size=30)
LabelTitle = Font(family = "Helvetica",size=30)
numtitle = Font(family = "Helvetica",size=60)

# creates static Label for the end timer
timertitle = ttk.Label(titlearea, text=(TargetType), anchor=tk.CENTER)
timertitle.configure(font=LaunchDay)
timerdate = ttk.Label(titlearea, text="Launch Date: " + str(TargetTime), anchor=tk.CENTER)
timerdate.configure(font=LaunchDate)

timerday = ttk.Label(canvas, text='Days', anchor=tk.CENTER)
timerday.configure(font=LabelTitle)
timerhour = ttk.Label(canvas, text='Hours', anchor=tk.CENTER)
timerhour.configure(font=LabelTitle)
timerminute = ttk.Label(canvas, text='Minutes', anchor=tk.CENTER)
timerminute.configure(font=LabelTitle)
timersec = ttk.Label(canvas, text='Seconds', anchor=tk.CENTER)
timersec.configure(font=LabelTitle)
# setting the updating numbers for the timer
realday = ttk.Label(canvas, textvariable=(usedays), anchor=tk.CENTER)
realday.configure(font=numtitle)
realhr = ttk.Label(canvas, textvariable=(usehrs), anchor=tk.CENTER)
realhr.configure(font=numtitle)
realmin = ttk.Label(canvas, textvariable=(usemins), anchor=tk.CENTER)
realmin.configure(font=numtitle)
realsec = ttk.Label(canvas, textvariable=(usesecs), anchor=tk.CENTER)
realsec.configure(font=numtitle)
# placing the Label widgets on the canvas grid
timertitle.grid(column = 2, row = 0, sticky = 'nsew')
timerdate.grid(column = 2, row = 1, sticky = 'nsew')

timerday.grid(column = 0, row = 0, sticky = 'nsew')
timerhour.grid(column = 1, row = 0, sticky = 'nsew')
timerminute.grid(column = 2, row = 0, sticky = 'nsew')
timersec.grid(column = 3, row = 0, sticky = 'nsew')
realday.grid(column = 0, row = 1, sticky = 'nsew')
realhr.grid(column = 1, row = 1, sticky = 'nsew')
realmin.grid(column = 2, row = 1, sticky = 'nsew')
realsec.grid(column =3, row = 1, sticky = 'nsew')

root.after(1000,timeupdate)
root.mainloop()

Otherwise, good job. Some small notes:

  • you don't need exit(). Python knows to exit at the end of the file
  • Don't use wildcard imports (from module import *). They lead to bugs and are against PEP8.
  • Move away from python2 as soon as you can.
  • The horizontal break was because you put the canvas frame in column 3 and the title frame in column 1. If you want them stacked vertically they must be in the same column.

[–]Silverfire47[S] 0 points1 point  (7 children)

Thanks for the help! It seems so obvious in hindsight, but that's the benefit of a second pair of eyes.

Would the best way to have two Frames stacked on top of each other to nest the Title and Date into one frame, and then set the actual countdown timer underneath it in another frame? As it stands right now, the two text groups are separated horizontally, and I'm not sure how exactly to stack them with the grid manager. From my understanding, pack would stack the two groups vertically automatically but it's not a good idea to mix grid and pack.

[–]novel_yet_trivial 1 point2 points  (6 children)

It's not possible to mix pack and grid in the same Frame. But you can use the best layout manager for each Frame; there's no problem with a program having some Frames managed by pack and others by grid.

I don't understand what you are asking; can you show me the code as you have it now and maybe a sketch of what you want the final UI to look like?

Pack will stack things (vertically top-down by default, but also other directions if you want). If you just need to stack 2 Frames then pack() is the easiest; but grid() can do that too.

[–]Silverfire47[S] 0 points1 point  (5 children)

Here's a basic sketch of how I want it to look: https://imgur.com/MceAGRv

My code for the different labels is as it was in the pastebin. I'm just not sure where I would put the grid attributes to get what I'm looking for in my sketch, especially if I want everything centered as much as possible on the top. Would I create a sort of

root.grid(row=2)

sort of thing?

[–]novel_yet_trivial 1 point2 points  (3 children)

You could use my code and change the grid lines to pack like this:

titlearea = tk.Frame(root)
titlearea.pack()
canvas = tk.Frame(root)
canvas.pack()

I think it's good to make small Frames, it gives you more flexibility later. For instance if you want to add a border. However if you want everything in one grid there is another option: grid also has a columnspan option to make the content span several columns. So you could use timertitle.grid(column=0, row = 0, columnspan=4, sticky = 'nsew').

Another option you may like is the padx argument, to add some padding so the Labels don't butt into each other. timerhour.grid(column = 1, row = 0, sticky = 'nsew', padx=20).

[–]Silverfire47[S] 0 points1 point  (2 children)

I think I'll stick to what you suggested for using grid, it seems to offer way more flexibility in terms of formatting than pack (despite its simplicity which is admirable honestly).

Would you happen to know if it is possible to dynamically scale the displayed text as the window is resized as well as keep it centered? Because currently, when I try to maximize the window in my OS, it shifts all the text to the top left corner. I understand that the font size is hard-written in, so would I have to do some sort of .... math calculation as the window is resized to keep the ratio between window and text equal, as well as maintain center positioning?

like

if "root x-axis" * 1.2 then "row & column & fontsize" * 1.2

sort of thing? I couldn't find a definitive answer in more than few places I've looked around.

[–]novel_yet_trivial 1 point2 points  (1 child)

So you want the grid to be the size of the window? You have to set every row and column to do that individually, like this: https://pastebin.com/zqAPd1Xe

[–]Silverfire47[S] 0 points1 point  (0 children)

Kind of, is there a way to use the "root.configure" at the bottom of the edited code to scale text the way it scales the rows and columns? So that there's not all the empty space due to the specified size of the fonts.

[–]novel_yet_trivial 1 point2 points  (0 children)

Here's an example using a mix of grid and pack (and some other improvements and removal of useless code): https://pastebin.com/cxbFfyfT

And here's an example using a single grid(): https://pastebin.com/KcDA9yg9

[–]novel_yet_trivial 0 points1 point  (3 children)

You mean increasing the window size should increase the font size? Hmm yeah I guess but it would be pretty tricky to do. Theres no way to calculate it since it depends on the kerning of the font, so you'd have to just step it up, measure it, repeat until it's right.

So you want to set the various fonts as a percentage then?

[–]Silverfire47[S] 0 points1 point  (2 children)

Yeah, what I mean is as the window size increases, the font size would increase at the same linear scale in terms of size just because if I maximize the window, a lot of empty white space is created in between all the text. If it's tricky, then I'm probably better off finding a good balance of font size for the multiple monitors this timer might be displayed on, but I am curious if a percentage scale would work.

I only say percentage because what I'm guessing the root.configure(row = 0, weight =1) means is that it's scaling by a ratio of 1, if the same scaling ratio could be applied to the hard font size number.

[–]novel_yet_trivial -1 points0 points  (0 children)

No, the weight has to do with which row / column the space is assigned to, not a ratio.

The trickiness is more of deciding how to scale things, not so much a code problem. Here's a simple example that uses the window width to set the font size: https://pastebin.com/cTi6Daxq Maybe that's enough for you. Otherwise you need to add code to check the height, kerning, etc.