Matt Habel
Matt Habel

Reputation: 1543

How to pass self declared variables between different classes in python

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

Answers (1)

Amber
Amber

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

Related Questions