placing class defined tkinter object on an individual frame when many are defined using tk.Tk

558 views Asked by At

Following a suggestion that I got from Sentdex I have coded a multiple page Python/Tkinter application which among other things provides a time-moving graph on a single frame of a suite of many frames that live under tk.Tk. The coding of a moving graph was slightly complicated so I chose to define the canvas in a class: GrpCanvas(tk.Canvas).

My problem is that this program structure seems to cause the canvas object to appear on all 21 of my page-frames! How can I manage the code so that the graphcanvas=GrpCanvas(HomePage) only appears on that page? I have commented out some parent definitions to show what I have tried to do (and failed). I am using Python 3.4.4.

I show the code (cut down as much as I can to show the problem) below:

#Avoiding canvas on all pages when pages are managed using tk.Tk

import tkinter as tk

sinewave_points=[]  #Generated by a sin function of time.
#class GrpCanvas(self, parent):
class GrpCanvas(tk.Canvas):    
    #def __init__(self, parent):
    def __init__(self, parent, controller):    
        tk.Canvas.__init__(self, height=340, width=594, bg='white')#,      x=pos_x, y=pos_y):
        self.place(x=180, y=80)

    def set_y_scale(self, sinewave_points):
        self.scale=100 #actually calculated from a scaling algorithm (adapting to amplitude of sinewave_points)
        return self.scale

    def define_graph(self, scale, sinewave_points):
        # create x-axis
        self.horizontal=self.create_line(0, 170, 594, 170, width=2) 
        for i in range(13): #used to be 26
            x = 20 + (i * 48)
            self.x_scale=self.create_text(x, 175, font=("", 6),\
                                      anchor='n', text='{}'.format(((12/3) * i)-24))

        # y-axis
        self.vertical=self.create_line(20, 330, 20, 10, width=2)
        self.y_scale=self.set_y_scale(sinewave_points)

        if self.y_scale == 100:
            for i in range(21):
                self.y = int(330 - (i * (320/20))) #In fact there is an slgorithm to scale the y-axis
                #print(i, self.y)
                self.y_axis=self.create_text(17, (self.y), font=("", 6), anchor='e',\
                                text='{}'.format(int((((200/320)*(320/20)) * i)-\
                                100)))


        for i in range(len(sinewave_points)):                    
            self.x, self.y = (i+20) , int(-1*self.scale*sinewave_points[i])+ 170
            self.history=self.create_oval(self.x - 1, self.y - 1, self.x + 1,\
                                      self.y + 1, width=0, fill='purple')



class Moving_Sinewave(tk.Tk):
    def __init__(self, *args, **kwargs):        
        #Initialising Tkinter    

        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, 'Sinewave Moving Plotter')
        tk.Tk.geometry(self, '800x480')#This is the size of the screen (in pixels)       

        container = tk.Frame(self)
        container.pack(fill='both', expand= True)#(side="top", fill="both", expand = True)
        container.grid_rowconfigure (0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (HomePage,                 
              SystemConfigPage,                   
              ConfigAlarmsPage):

            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
            frame.configure(background= 'ivory2'),
        self.show_frame(HomePage)

    def show_frame(self, cont):       
        frame=self.frames[cont]
        frame.tkraise()


class HomePage(tk.Frame):

    def __init__(self, parent, controller):
        self.controller=controller
        tk.Frame.__init__(self, parent)
        global time1, time2, time4, time5

        sysconfigbutton=tk.Button(self, text= 'System\nConfiguration',        
                              command=lambda: controller.show_frame(SystemConfigPage),
                              height=2, width=12)
        sysconfigbutton.place(x=20, y=80)

        #graphcanvas=GrpCanvas(tk.Frame)            #works with:    class GrpCanvas(tk.Canvas):    
                                                            #def __init__(self, parent):
        #graphcanvas=GrpCanvas(HomePage)            #works with:    class GrpCanvas(tk.Canvas):    
                                                            #def    __init__(self, parent):
        #graphcanvas=GrpCanvas(HomePage(tk.Frame))

        graphcanvas=GrpCanvas(HomePage, controller.tk)# works with: class GrpCanvas(tk.Canvas):    
                                                    #def __init__(self, parent, controller):

        graphcanvas.define_graph(graphcanvas.set_y_scale(sinewave_points), sinewave_points)
    # This actually plots the points

class SystemConfigPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        configalarmsbutton=tk.Button(self, text= 'Configure\nAlarms',
                            command=lambda: controller.show_frame(ConfigAlarmsPage),                                 
                            height=2, width=12)
        configalarmsbutton.place(x=20, y=180)

class ConfigAlarmsPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        backbutton=tk.Button(self, text= 'Back',
                         command=lambda: controller.show_frame(HomePage),
                               height=2, width=12)
        backbutton.place(x=20, y=380)


app = Moving_Sinewave()             
app.mainloop()
1

There are 1 answers

1
Bryan Oakley On BEST ANSWER

The first argument to GrpCanvas needs to be the parent widget in which the canvas is to appear. In this specific case you should use self, since you want it to be in the home page and you are creating it as part of creating HomePage:

graphcanvas=GrpCanvas(self, controller.tk)

You also need to pass this argument on to the __init__ of the parent class, which you are neglecting to do. To fix that, change this:

tk.Canvas.__init__(self, height=340, ...)

... to this:

tk.Canvas.__init__(self, parent, height=340, ...)

What was happening is that because you didn't pass in the parent, the widget was using the root window as the default. And because you were using place, and because this widget was getting created last (and thus was at the top of the stacking order) it was appearing on top of all of the pages.