0

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()

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I would just pass what's needed, i.e., just a reference to the canvas, rather than to the entire app object.
How would I go about doing that? I'm kind of new to Tkinter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.