Reputation: 1543
I am trying to create a program that tracks bike intervals and creates a map of a course to use while biking. I want to be able to havbe a red line track the line while the stopwatch is counting. However, I am not able to pass the canvas between the class for the main App and the Stopwatch.
from Tkinter import *
import random
import time
from itertools import product
class App():
def __init__(self,master):
menubar = Menu(master)
filemenu = Menu(menubar,tearoff=0)
filemenu.add_command(label="Quit",command=root.destroy)
filemenu.add_command(label="Interval",command=self.Interval)
coursemenu = Menu(menubar,tearoff=0)
coursemenu.add_command(label="New Random Course",command=self.regenerateTerrain)
menubar.add_cascade(label="File",menu=filemenu)
menubar.add_cascade(label="Course",menu=coursemenu)
master.config(menu=menubar)
self.statusbar = Frame(master)
self.statusbar.pack(side=BOTTOM, fill=X)
self.infobar = Frame(master)
self.infobar.pack(side=TOP,fill=X)
self.course = Label(self.infobar, text="Welcome!")
self.course.pack(side=LEFT)
self.action = Label(self.infobar, text="")
self.action.pack(side=RIGHT,fill=X)
#Stopwatch
stop = StopWatch(self.infobar)
stop.pack(side=LEFT)
#Stopwatch buttons
self.button = Button(self.infobar, text="Start", command=stop.Start)
self.button2 = Button(self.infobar, text="Stop", command=stop.Stop)
self.button3 = Button(self.infobar, text="Reset", command=stop.Reset)
self.button4 = Button(self.infobar, text="Quit", command=root.quit)
self.button.pack(side=LEFT)
self.button2.pack(side=LEFT)
self.button3.pack(side=LEFT)
self.button4.pack(side=LEFT)
#Constants for program
#distance is in miles
#height is in feet
self.totalDistance = 25
self.heightInterval = 10
self.canvasHeight = 300
self.canvasWidth = 500
self.c=Canvas(root,width=self.canvasWidth,height=self.canvasHeight,background="white")
self.c.pack(side=TOP,fill=BOTH,expand=YES)
#Call regenerate an initial time, so that terrain gets generated on
#initial creation
self.regenerateTerrain()
def buildTerrain(self,distance=25,topBound=0,
bottomBound=300,width=500):
options=['up','down','flat','flat']
y = (bottomBound-topBound)/2
map = [y]
changer =0
for i in xrange(distance*10):
direction = random.choice(options)
options.pop()
if direction=='up' and y>10:
options.append('up')
map.append(map[len(map)-1]-self.heightInterval)
changer=-self.heightInterval
elif direction=='down' and y<bottomBound-10:
options.append('down')
map.append(map[len(map)-1]+self.heightInterval)
changer=self.heightInterval
else:
options.append('flat')
map.append(map[len(map)-1])
changer=0
y+=changer
return map
def regenerateTerrain(self,distance=25,topBound=0,bottomBound=300,width=500):
self.c.delete(ALL)
x = 0
y = (bottomBound+topBound)/2
self.build = self.buildTerrain()
for i in xrange(1,len(self.build)-1):
self.c.create_line(x,y,x+(self.canvasWidth/(self.totalDistance*10)),self.build[i],fill="black")
x+=(self.canvasWidth/(self.totalDistance*10))
y=self.build[i]
self.c.create_oval(0,self.build[0]-1,4,self.build[0]-5,fill="red")
def Interval(self):
self.clock = StopWatch()
top = Toplevel()
top.title("Interval Mode")
entLabelLow = Label(top)
entLabelLow["text"] = "# of minutes at low interval: "
entLabelHigh = Label(top)
entLabelHigh["text"] = "# of minutes at high interval: "
entLabelTotal = Label(top)
entLabelTotal["text"] = "Total # of minutes"
entWidgeTotal = Entry(top)
entWidgeTotal["width"] = 5
entWidgeLow = Entry(top)
entWidgeLow["width"] = 5
entWidgeHigh = Entry(top)
entWidgeHigh["width"] = 5
entLabelTotal.pack(side=LEFT)
entWidgeTotal.pack(side=LEFT)
entLabelLow.pack(side=LEFT)
entWidgeLow.pack(side=LEFT)
entLabelHigh.pack(side=LEFT)
entWidgeHigh.pack(side=LEFT)
self.linesDist = 0
self.minutes = 0.0
self.timeatHL = 0
self.timeatLL = 0
self.currentPos = 0
def drawGraph():
if entWidgeLow.get().strip() == "" or entWidgeHigh.get().strip() == "":
print"Enter a value please"
pass
top.destroy()
elif int(entWidgeLow.get().strip()) not in range(1,11) or int(entWidgeHigh.get().strip()) not in range(1,11):
print"Please enter a number between 1 and 10"
pass
top.destroy()
else: #Get the values
self.LLength = int(entWidgeLow.get().strip())
self.HLength = int(entWidgeHigh.get().strip())
self.TLength = int(entWidgeTotal.get().strip())
top.destroy()
while self.linesDist < self.canvasWidth - 50: #Create the vertical lines
self.c.create_line(10,195,10,205,fill="red")
self.linesDist += 50
self.intervalLength = self.TLength / 10.0
self.minutes += float(self.intervalLength)
self.c.create_line((self.linesDist, 0, self.linesDist, 300), fill="gray")
self.c.create_text(self.linesDist, 290, text=str(self.minutes))
#Now to draw the graph
while self.currentPos < 500:
self.c.create_line(self.currentPos, 200, (((500/self.TLength)*self.LLength)+self.currentPos), 200)
self.currentPos += (float(self.LLength)/self.TLength) * 500
self.c.create_line(self.currentPos, 200, self.currentPos, 100)
self.c.create_line(self.currentPos, 100, (((500/self.TLength)*self.HLength)+self.currentPos), 100)
self.currentPos += (float(self.HLength)/self.TLength) * 500
self.c.create_line(self.currentPos, 100, self.currentPos, 200)
self.submit = Button(top, text="Submit", command = drawGraph)
self.submit.pack(side=BOTTOM)
self.c.delete(ALL)
class StopWatch(Frame):
def __init__(self, parent=None, **kw):
"""Creates the watch widget"""
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsed = 0.0
self._running = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
"""Make the label"""
l = Label(self, textvariable=self.timestr)
self._setTime(self._elapsed)
l.pack(fill=X, expand=NO, padx=2, pady=2)
def _update(self):
"""Update the label with the correct time"""
self._elapsed=time.time() - self._start
self._setTime(self._elapsed)
self._timer = self.after(50, self._update)
App.self.c.create_line(95,1,105,1)
def _setTime(self, elap):
"""Set time string"""
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hundreths = int(((elap - minutes*60.0 - seconds)*100))
self.timestr.set("%02d:%02d:%02d" % (minutes, seconds, hundreths))
def Start(self):
if not self._running:
self._start = time.time() - self._elapsed
self._update()
self._running = 1
def Stop(self):
"""To stop it, DUH"""
if self._running:
self.after_cancel(self._timer)
self._elapsed = time.time() - self._start
self._setTime(self._elapsed)
self._running = 0
def Reset(self):
"""Think about it"""
if self._running:
self.Stop()
self._start = time.time()
self._elapsed = 0.0
self._setTime(self._elapsed)
root=Tk()
root.title("Bike Computer")
myapp=App(root)
root.mainloop()
Upvotes: 0
Views: 481
Reputation: 526613
Variables declared as part of self
are instance variables - they're tied to a particular instance of the class, not the class itself - hence why you can't access them via App.
, since that's looking for them in the class, when c
is actually a member of myapp
.
What you need to do is pass the App's self.c
to the StopWatch constructor (you'll need to add an argument to the constructor to accept it), and then store it locally in the StopWatch.
self
is a special name in Python classes (technically you can call it whatever you want, but the standard name is self
) that always refers to the current class instance. So for example, the following code:
class A:
def __init__(self, foo):
self.bar = foo
def echo(self):
print self.bar
one = A(1)
one.echo() # prints '1'
two = A(2)
two.echo() # prints '2'
one.echo() # still prints '1'
If self
were shared by all instances of a class, the above wouldn't work.
Upvotes: 3