I am trying to write a python app that will switch between two views when I click on the canvas in Tkinter. I have written the views and main app as seperate objects with binds linked to the opposing view through the left mousebutton. When I run the app in Pyscripter I exceed my maximum recursion depth but am not sure why this is happening. I am relatively new to Tkinter so please forgive if this is a really ignorant question. Here is the code:

class App(object):

    def __init__(self, width=256, height=256):
        self.width = width
        self.height = height

        self.root = Tk()
        self.root.title("tkinter_test01")
        self.root.geometry("%sx%s"%(self.width, self.height))

        self.canvas = Canvas(self.root, width=self.width, height=self.height)
        self.canvas.pack()

        win1(self.canvas)

class win1(object):
    def __init__(self,canvas):
        self.canvas=canvas
        self.canvas.delete(ALL)
        self.canvas.bind("<Button1>", win2(self.canvas))
        self.canvas.create_line(10,10,100,100)

class win2(object):
    def __init__(self,canvas):
        self.canvas=canvas
        self.canvas.delete(ALL)
        self.canvas.bind("<Button1>", win1(self.canvas))
        self.canvas.create_line(30,30,100,40)

App()

oops i noticed that the event loop is missing from the main app but that dint fix the problem. Line 14 is still where its failing...

Upon debugging i notice that the bound function is calling itself upon definition event though the trigger has not been activated. Anyone help me figure out why this is happening?

Here is a code that works for me

from Tkinter import *

class App(object):

    def __init__(self, width=256, height=256):
        self.width = width
        self.height = height

        self.root = Tk()
        self.root.title("tkinter_test01")
        self.root.geometry("%sx%s"%(self.width, self.height))

        self.canvas = Canvas(self.root, width=self.width, height=self.height)
        self.canvas.pack()

        self.views = [win1(self.canvas), win2(self.canvas)]
        self.view_idx = 0
        self.canvas.bind("<Button-1>", self.switch_view)
        self.current_view.repaint_canvas()

    def switch_view(self, event):
        self.view_idx = (self.view_idx + 1) % len(self.views)
        self.current_view.repaint_canvas()

    @property
    def current_view(self):
        return self.views[self.view_idx]

class View(object):
    def __init__(self, canvas):
        self.canvas = canvas

class win1(View):
    def repaint_canvas(self):
        self.canvas.delete(ALL)
        self.canvas.create_line(10,10,100,100)

class win2(View):
    def repaint_canvas(self):
        self.canvas.delete(ALL)
        self.canvas.create_line(30,30,100,40)

app = App()
app.root.mainloop()

Edited 3 Years Ago by Gribouillis

Thanks, this works for me though I'll have to get my head around it a bit. However, one question: are win1 win2 objects being called each time you switch_view and being handled by garbage collection or are they only being called in the views list. I wasn't aware you could construct objects from inside a list....

These objects are only created once and stored in the views list, but their repaint_canvas() method is called every time we switch the views. You could change this and always create new objects if you want, but you'd need a good reason to do so. These objects only hold a pointer to the application's canvas, there is no memory leak to worry about.

Edited 3 Years Ago by Gribouillis

Instead of calling the same class instance every time, you are creating a new class instance

 self.canvas.bind("<Button1>", win2(self.canvas))

As stated above, you should create one instance of each class in init() and use those.

This question has already been answered. Start a new discussion instead.