Python Forum
Button in one class, methods in another one - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: Button in one class, methods in another one (/thread-19693.html)



Button in one class, methods in another one - alan9979 - Jul-10-2019

Hi, I spent 2 days trying to solve this little exercice, creating a face with button open or closing his mouth.
I do have 2 questions about this code :

  1. I cannot delete the round face to create a closed mouth, line 40 and line 43, python says : AttributeError: 'Button_Can' object has no attribute 'opened_mouth'. How could I make this work ?
  2. How could I avoid to repeat the circle function on line 17 in the Button_Can Class ?

    Thanks for any help that could enlighten my poor coding skills

    from tkinter import *
    
    class Button_Can(Frame):
    	def __init__(self):
    		Frame.__init__(self)
    		self.larg,self.haut=200,200
    		self.can=Canvas(self.master,width=self.larg,height=self.haut,bg='ivory')
    		self.can.pack()
    		self.b1=Button(self.master,text='Open it !',command=self.openit)
    		self.b2=Button(self.master,text='Close it !',command=self.closeit)
    		self.b1.pack(side=RIGHT)
    		self.b2.pack(side=LEFT)
    	def openit(self):
    		Face.openMouth(self)
    	def closeit(self):
    		Face.closeMouth(self)
    	def circle(self,x, y, r, coul ='black'):
    		"Draw a circle center x,y radius r "
    		self.can.create_oval(x-r, y-r, x+r, y+r, outline=coul)
    
    class Face():
    	def __init__(self,can):
    		self.can=can
    		"Draw a face"
    		can.delete(ALL)
    		cc =[[100, 100, 80, 'red'],[70, 70, 15, 'blue'], 
    		[130, 70, 15, 'blue'],[70, 70, 5, 'black'],
    		[130, 70, 5, 'black'],[44, 115, 20, 'red'], 
    		[156, 115, 20, 'red'],[100, 95, 15, 'purple']] 
    		i =0
    		while i < len(cc):
    			el = cc[i] 
    			self.circle(el[0], el[1], el[2], el[3])
    			i += 1 
    
    	def circle(self,x, y, r, coul ='black'):
    		"Draw a circle center x,y radius r "
    		self.can.create_oval(x-r, y-r, x+r, y+r, outline=coul)
    	def openMouth(self):
    		self.can.delete(self.closed_mouth)
    		self.opened_mouth=self.circle(100, 145, 30, 'purple')
    	def closeMouth(self):
    		self.can.delete(self.opened_mouth)
    		self.closed_mouth=self.can.create_line(80,145,120,145,fill='purple')
    
    A=Button_Can()
    B=Face(A.can)



RE: Button in one class, methods in another one - metulburr - Jul-10-2019

That is not the best way to interact classes with each other. The best solution would be to make the Face object in the canvas class like this:

from tkinter import *
 
class Button_Can(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.larg,self.haut=200,200
        self.can=Canvas(self.master,width=self.larg,height=self.haut,bg='ivory')
        self.can.pack()
        self.face = Face(self.can)
        self.b1=Button(self.master,text='Open it !',command=self.face.openMouth)
        self.b2=Button(self.master,text='Close it !',command=self.face.closeMouth)
        self.b1.pack(side=RIGHT)
        self.b2.pack(side=LEFT)
 
class Face():
    def __init__(self, can):
        self.can = can
        self.setup()
        
    def setup(self):
        self.can.delete(ALL)
        cc =[[100, 100, 80, 'red'],[70, 70, 15, 'blue'], 
        [130, 70, 15, 'blue'],[70, 70, 5, 'black'],
        [130, 70, 5, 'black'],[44, 115, 20, 'red'], 
        [156, 115, 20, 'red'],[100, 95, 15, 'purple']] 
        i =0
        while i < len(cc):
            el = cc[i] 
            self.circle(el[0], el[1], el[2], el[3])
            i += 1 
            
        self.opened_mouth = None
        self.closed_mouth = None
 
    def circle(self,x, y, r, coul ='black'):
        "Draw a circle center x,y radius r "
        self.can.create_oval(x-r, y-r, x+r, y+r, outline=coul)
    def openMouth(self):
        #self.can.delete(self.closed_mouth)
        self.setup()
        self.opened_mouth=self.circle(100, 145, 30, 'purple')
    def closeMouth(self):
        #self.can.delete(self.opened_mouth)
        self.setup()
        self.closed_mouth=self.can.create_line(80,145,120,145,fill='purple')

root = Tk()
A=Button_Can()
B=Face(A.can)
root.mainloop()
NOTE: I am more use to pygame's redraw everything style... every frame, so i am not sure if tkinter has a method to just delete a portion of the canvas or not. So i just redrew everything.


RE: Button in one class, methods in another one - alan9979 - Jul-10-2019

Thanks for your very fast reply metulburr,
I am still trying to erase just the mouth. But I don't understand why my erase methode, using what you said above, doesn't work :
When I click the erase button, nothing happens, no error message either.
from tkinter import *

class C1(Frame):
	def __init__(self):
		Frame.__init__(self)
		self.larg,self.haut=100,100
		self.can=Canvas(self.master,width=self.larg,height=self.haut,bg='ivory')
		self.can.pack()
		self.c2=C2(self.can)
		self.b1=Button(self.master,text='Erase',command=self.c2.erase)
		self.b1.pack()
		
	

class C2():
	def __init__(self,can):
		self.can=can	
		self.redline=self.can.create_line(10, 10, 90, 90, fill ='red',width=5)

	def erase(self):
		self.can.delete(self.redline)


root = Tk()
A=C1()
canevas =A.can
B=C2(canevas)
root.mainloop()



RE: Button in one class, methods in another one - metulburr - Jul-11-2019

You are creaing two objects with C2 class. One inside C1 on line 9, and another at line 27. Comment out the line 27 one. What is happening is you are creating two lines overlapping each other, where one only has access to delete it.
from tkinter import *
 
class C1(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.larg,self.haut=100,100
        self.can=Canvas(self.master,width=self.larg,height=self.haut,bg='ivory')
        self.can.pack()
        self.c2=C2(self.can)
        self.b1=Button(self.master,text='Erase',command=self.c2.erase)
        self.b1.pack()
         
     
 
class C2():
    def __init__(self,can):
        self.can=can    
        self.redline=self.can.create_line(10, 10, 90, 90, fill ='red',width=5)
 
    def erase(self):
        self.can.delete(self.redline)
 
 
root = Tk()
A=C1()
canevas =A.can
#B=C2(canevas)
root.mainloop()



RE: Button in one class, methods in another one - joe_momma - Jul-11-2019

The easiest way to control or work with objects in tkinter is to use tags.
I don't see the need for 2 classes here
from tkinter import Tk,Frame,Canvas,Button,RIGHT,LEFT
  
class Button_Can(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.pack(expand='yes',fill='both')
        self.larg,self.haut=200,200
        self.can=Canvas(self.master,width=self.larg,height=self.haut,bg='ivory')
        self.can.pack()        
        self.b1=Button(self.master,text='Open it !',command=self.openMouth)
        self.b2=Button(self.master,text='Close it !',command=self.closeMouth)
        self.b3=Button(self.master,text='delete it!',command=self.delete_mouth)
        self.b1.pack(side=RIGHT)
        self.b2.pack(side=LEFT)
        self.b3.pack(side='bottom')
        self.setup()
    def setup(self):
        
        cc =[[100, 100, 80, 'red'],[70, 70, 15, 'blue'], 
        [130, 70, 15, 'blue'],[70, 70, 5, 'black'],
        [130, 70, 5, 'black'],[44, 115, 20, 'red'], 
        [156, 115, 20, 'red'],[100, 95, 15, 'purple']] 
        i =0
        while i < len(cc):
            el = cc[i] 
            self.circle(el[0], el[1], el[2], el[3])
            i += 1 
        
  
    def circle(self,x, y, r, coul ='black'):
        "Draw a circle center x,y radius r "
        self.can.create_oval(x-r, y-r, x+r, y+r, outline=coul)
    def openMouth(self):
        self.can.delete('bouche')        
        self.opened_mouth= self.can.create_oval(70, 115, 130, 175,
                                                outline='purple',tag='bouche')
    def closeMouth(self):
        self.can.delete('bouche')        
        self.closed_mouth=self.can.create_line(80,145,120,145,
                                               fill='purple', tag='bouche')
    def delete_mouth(self):
        self.can.delete('bouche')
if __name__ == '__main__': 
    root = Tk()
    root.title('la visage')
    A=Button_Can()
    root.mainloop()