Delete an instance of the object - python

I'm trying to understand the class notion in Python and having decided to do a little exercise I found myself facing a problem.
What I'm trying to do is to create a circle (on a left-click) and then I expect the program to delete the circle (on a right-click).
Well, here comes the second part of my problem.
My code:
from tkinter import *
class Application:
def __init__(self):
self.fen = Tk()
self.fen.title('Rom-rom-roooooom')
self.butt1 = Button(self.fen, text = ' Quit ', command = self.fen.quit)
self.can1 = Canvas(self.fen, width = 300, height = 300, bg = 'ivory')
self.can1.grid(row = 1)
self.butt1.grid(row = 2)
self.fen.bind("<Button-1>", self.create_obj)
self.fen.bind("<Button-3>", self.delete_obj)
self.fen.mainloop()
def create_obj(self, event):
self.d = Oval()
self.can1.create_oval(self.d.x1, self.d.y1, self.d.x2, self.d.y2, fill='red', width = 2)
def delete_obj(self, event):
self.can1.delete(self.d)
class Oval:
def __init__(self):
self.x1 = 50
self.y1 = 50
self.x2 = 70
self.y2 = 70
appp = Application()
Here, the program understands that 'd' is an instance of class Oval, but it doesn't delete the object on a right click:
def delete_obj(self, event):
self.can1.delete(self.d)

From the tkinter docs, create_oval returns an object id, which is an integer. To remove the circle, use the Canvas.delete method:
from tkinter import *
import time
class Application:
def __init__(self):
self.fen = Tk()
self.fen.title('Rom-rom-roooooom')
self.butt1 = Button(self.fen, text = ' Quit ', command = self.fen.quit)
self.can1 = Canvas(self.fen, width = 300, height = 300, bg = 'ivory')
self.can1.grid(row = 1)
self.butt1.grid(row = 2)
self.fen.bind("<Button-1>", self.create_obj)
self.fen.mainloop()
def create_obj(self, event):
d = self.can1.create_oval(150,150, 170, 170, fill='red', width = 2)
time.sleep(3)
self.can1.delete(d)
appp = Application()

Related

How to set a value in the tkinter slider.set method?

I am trying to simulate a stellar system. I aim to manipulate the parameters via slider widgets, see the (reduced) code below. The slider widget in my code accepts a new value for the sun mass, which was set via a StellarSys instance. However the slider.set method fails with TypeError: 'NoneType' object is not subscriptable. Does somebody have a solution or can explain what I'm doing wrong? Many thanks.
import tkinter as tk
import math
class Space(tk.Frame):
def __init__(self, master, size, bg=None):
super().__init__(master)
frame = tk.Frame(self, border=5)
frame.pack()
self.width, self.height = size
self.canvas = tk.Canvas(frame, width=self.width,height=self.height,
borderwidth=0, highlightthickness=0, bg=bg)
self.canvas.pack()
self.bodies = None
def place_bodies(self):
for body in self.bodies:
x1, y1 = int(body.loc[0]-body.size/2.0),int(body.loc[1]-body.size/2.0)
x2, y2 = x1 + body.size, y1 + body.size
body.tk_id = self.canvas.create_oval(x1, y1, x2, y2, fill=body.color)
class SpaceBody:
def __init__(self, **kwargs):
self.name = kwargs['name']
self.size = kwargs['size']
self.mass = kwargs['mass']
self.loc = kwargs['loc']
self.speed = kwargs['speed']
self.color = kwargs['color']
self.dxdy = (0,0)
self.tk_id = None
def __repr__(self):
return f"\n{self.name} is {self.color}"
class Dashboard(tk.Frame):
def __init__(self, master, bg=None):
super().__init__(master)
frame = tk.Frame(self, border=5, bg=bg)
frame.pack()
sun_frame=tk.Frame(frame)
sun_frame.grid(row=1)
w, h = 15, 3
tk.Label(sun_frame, text = '').grid(row=0)
tk.Label(sun_frame, text = 'SUN MASS').grid(row=1)
self.sun_mass = tk.Scale(sun_frame, from_=0, to=1000, orient='horizontal')
self.sun_mass.bind("<ButtonRelease-1>", self.update)
# self.sun_mass.set(500) # this works
self.sun_mass.set(space.bodies[0].mass) # This doesn't work
self.sun_mass.grid(row=2)
def update(self, event):
space.bodies[0].mass = self.sun_mass.get()
print(space.bodies[0].mass)
class StellarSys:
def __init__(self):
sun = SpaceBody(name='Sun', size=30, mass=500, loc=(500,400),speed=(0, 0), color='yellow')
earth = SpaceBody(name='Earth',size=15, mass=1, loc=(500,200),speed=(15,0), color='green')
space.bodies = [sun, earth]
space.place_bodies()
# MAIN
root = tk.Tk()
root.title('UNIVERSE')
size = (1000, 800)
space = Space(root, size, bg='black')
space.grid(row=0, column = 0,sticky="nsew")
dashboard = Dashboard(root)
dashboard.grid(row=0, column = 1,sticky="nsew")
stellarsys = StellarSys()
root.mainloop()
You initialize self.bodies to None, and then try to subscript that value. As the error says, you can't use subscripts on a value of None.
You need to rework your logic so that self.bodies is a non-empty list before trying to reference an item in the list.

AttributeError: '' object has no attribute ''

I have a problem, I was working on a code with python 3. the code is about getting news of a website onto my canvas. however I keep getting this error which says:
AttributeError: 'NewsFeed' object has no attribute 'canvas'.
this is my code:
from tkinter import *
import feedparser
root=Tk()
class Achtergrond() :
m_Width = 0
m_Height = 0
m_BgColor = 0
def CreateCanvas(self, master, width, height) :
m_Width = width
m_Height = height
m_BgColor='#14B4E1'
self.canvas = Canvas(master, width=m_Width, height=m_Height)
self.canvas.configure(bg=m_BgColor)
self.canvas.pack()
return(self.canvas)
def create_rectangle(x0, y0 ,x1,y1, color) :
self.canvas.create_rectangle(x0, y0, x1, y1, fill=color)
return
def create_title (self, x, y, title, opmaak):
self.canvas.create_text(x,y,text=title, font= opmaak)
return
def create_newsbox(master,x0,y0,x1,y1,color):
canvas.create_rectangle(x0,y0,x1,y1, fill=color)
return
def SetBackgroundColor(self, kleurcode) :
m_BgColor = kleurcode
self.canvas.configure(bg=m_BgCode)
self.canvas.pack()
return
class NewsFeed ():
Hitem = 0
Nitem = 0
feed = 0
th = 0
rssURL = ""
def UpdateNews(self,path):
self.rssURL = path
self.feed = feedparser.parse(self.rssURL)
self.Nitem = len(self.feed["items"])
return
def ToonEerste(self):
str=""
self.Hitem = 0
for i in range(self.Hitem,self.Hitem+2,1):
if i < self.Nitem:
entry = self.feed["entries"][i]
str += entry.title + "\n\n" + entry.summary + "\n__________________________________________________\n\n"
self.canvas.delete(th)
th = self.canvas.create_text(200,300, width=300, text = str)
return
def ToonVolgende(self):
str=""
self.Hitem += 3
if self.Hitem >= self.Nitem:
Hitem = 0
for i in range(self.Hitem,self.Hitem+2,1):
if i < self.Nitem:
entry = feed["entries"][i]
str += entry.title + "\n\n" + entry.summary + "\n__________________________________________________\n\n"
self.canvas.delete(th)
th = self.canvas.create_text(200,300, width=300, text = str)
return
hoofdscherm = Achtergrond()
a = hoofdscherm.CreateCanvas(root, 1024,600)
b = hoofdscherm.canvas.create_rectangle(30, 120, 360, 500, fill= '#ffffff')
a.create_rectangle(10,10, 1014,80, fill='#ffffff')
a.create_rectangle(30, 120, 360, 500, fill= '#ffffff')
a.create_text(250, 50, text="Harens Lyceum" , font=('Calibri' , 40))
a.configure(bg='#14B4E1')
a.pack()
n = NewsFeed()
n.UpdateNews('http://www.nu.nl/rss/Algemeen')
n.ToonEerste(root)
n.ToonVolgende(root)
root.mainloop()
We would like to have some help with our problem, we don't have much experience. We need to make a screen with raspberry which displays news etc. for school. If you know how we can fix our code we would very much appreciate your guys' help.
Your NewsFeed class instance n doesn't have a Canvas attribute. If you want to pass the Canvas defined in your Achtergrond class instance hoofdscherm to n, you can define it under the class definition for NewsFeed using __init__():
class NewsFeed ():
def __init__(self, canvas):
self.canvas = canvas
...
Then when you initialize your NewsFeed object as n, you can pass the Canvas instance from your Achtergrond class instance hoofdscherm:
n = NewsFeed(hoofdscherm.canvas)
This is a solution to your current issue, but there are other errors in your code that you can see once you modify it.

How to fix key bindings in tkinter

I want to make a sprite move around on the canvas and have tried to use key bindings as controls. When I run the program it doesn't move until I try the correct key. I've tested with mouse buttons and it works fine.
adding code:
from tkinter import *
class MainGame:
def __init__(self):
self.grid = [[""]*15 for n in range(15)]
self.grid[14][3] = 1
print(self.grid)
self.canvas = Canvas(root, width = 900, height = 900)
self.canvas.pack()
self.a, self.b = 45, 175
def noreaction(self, event):
print("It clicked")
print(self.a)
self.a += 50
self.b += 50
self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p2Tag")
self.canvas.delete("p1Tag")
self.canvas.tag_bind("p2Tag", "<Key-q>", self.noreaction)
def run(self):
self.pl = PhotoImage(file = "player.png")
self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p1Tag")
self.canvas.tag_bind("p1Tag", "<Key>", self.noreaction)
self.x0, self.y0, self.x1, self.y1 = -30, 150, 20, 200
for self.row in self.grid:
for self.column in self.row:
self.x0 += 50
self.x1 += 50
self.cell = self.canvas.create_rectangle(self.x0, self.y0, self.x1, self.y1)
self.y0 += 50
self.y1 += 50
self.x0 = -30
self.x1 = 20
root = Tk()
root.focus_set()
obj = MainGame()
obj.run()
root.mainloop()
After messing with your code for a bit it would seam that tag_bind only works with mouse clicks. After reviewing all the documentation I could find in tag_bind there is nothing that says you can bind a key to a drawn object. So the solution here would be to bind either the root window to the key or to bind the frame the canvas is placed in.
I have changed up your code a little to make this work but it should help you move in the right direction. Without more information as to what you are trying to accomplish in the long run its hard to provide a good answer but I think this will help. Let me know if you have any more questions.
from tkinter import *
class MainGame:
def __init__(self, parent):
self.parent = parent
self.grid = [[""]*15 for n in range(15)]
self.grid[14][3] = 1
self.canvas = Canvas(self.parent, width = 900, height = 900)
self.canvas.pack()
self.a, self.b = 45, 175
#added an argument here so we can better control the player object.
def noreaction(self, old):
print("It clicked")
print(self.a)
self.a += 50
self.b += 50
self.canvas.delete(old)
# assigned the object to a class attribute
self.player_obj = self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p2Tag")
# you will see <Key-q> and <q> work the same here.
self.parent.bind("<Key-q>", lambda x: self.noreaction(self.player_obj))
def run(self):
self.pl = PhotoImage(file = "./Colors/blk.gif")
# assigned the object to a class attribute
self.player_obj = self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p1Tag")
# you will see <Key-q> and <q> work the same here.
self.parent.bind("<q>", lambda x: self.noreaction(self.player_obj))
self.x0, self.y0, self.x1, self.y1 = -30, 150, 20, 200
for self.row in self.grid:
for self.column in self.row:
self.x0 += 50
self.x1 += 50
self.cell = self.canvas.create_rectangle(self.x0, self.y0, self.x1, self.y1)
self.y0 += 50
self.y1 += 50
self.x0 = -30
self.x1 + 20
root = Tk()
root.focus_set()
obj = MainGame(root)
obj.run()
root.mainloop()
"<Key>" is not a valid keysym to bind an event on. Try a valid one, for a full list you can look here http://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm
edit: appears I was mistaken, didn't know it was a valid event. You learn something every day :)

Why isn'y my Tkinter window opening?

import Tkinter
import random
class die:
def __init__(self,dieNum,frame,game):
self.button = Tkinter.Button(frame,
text = ' ',
command = self.rollDie,
font = ('Garamond', 30),
bg = 'white')
self.button.pack(side = 'left')
self.game = game
self.value = ' '
self.num = dieNum
def rollDie(self):
if self.value == ' ':
self.button.config(text = self.game.Player)
self.value = self.game.Player
self.game(self.num)
class DiceRoller:
def __init__(self):
self.gameWin = Tkinter.Tk()
self.gameFrame = Tkinter.Frame(self.gameWin)
self.dice = [ ]
self.Row1 = Tkinter.Frame(self.gameWin)
for i in range(1):
self.dice.append(die(i,self.Row1,self))
self.Row2 = Tkinter.Frame(self.gameWin)
for i in range(2):
self.dice.append(die(i,self.Row2,self))
self.Row3 = Tkinter.Frame(self.gameWin)
for i in range(3):
self.dice.append(die(i,self.Row3,self))
self.topFrame = Tkinter.Frame(self.gameWin)
self.rollBtn = Tkinter.Button(self.topFrame,
text = 'Roll Again',
command = self.randomNumber,
font = ('Garamond', 30))
self.rollBtn.pack(side = 'left')
def randomNumber(self):
self.value = random.randint(1,6)
self.display.config(text = str(self.value))
self.Row1.pack()
self.Row2.pack()
self.Row3.pack()
self.gameFrame.pack()
self.topFrame.pack()
self.gameWin.mainloop()
DR = DiceRoller()
DR
This is my first time using Tkinter and my first time working with classes, so I feel a bit in over my head.
Basically, when I run the code I get a loading wheel by my pointer, but nothing opens.
I am trying to alter a similar code to create a dice roller, but I cannot tell where the error is just by comparing the two.

How to solve an issue in a simple TR Tkinter program

Major programming noob here, and my very first question in stackoverflow. I'm trying to make a time reaction tester, with a simplistic Tkinter GUI.
Here's the code
#!/usr/bin/env python
import tkinter as tk
import time
import random
class TRTest(tk.Frame):
def __init__(self, master = None):
super().__init__()
self.grid()
self.canv()
self.createWidgets()
self.Test
self.totalt = []
def createWidgets(self):
self.quitbtn = tk.Button(self, text = "Quit", command=self.master.destroy)
self.quitbtn.grid(row = 3, column = 3)
self.label = tk.Label(self, text = "TRTest")
self.label.grid(row = 1, column = 3)
self.testbtn = tk.Button(self, text = "Start test", command = self.Test)
self.testbtn.grid(row = 1, column = 2)
def canv(self):
self.scrn = tk.Canvas(self, width = 400, height = 400, bg = "#E8E8E8")
self.scrn.grid(row = 2, column = 3)
def fixate(self):
self.scrn.create_line(190, 200, 210, 200, width = 2)
self.scrn.create_line(200, 190, 200, 210, width = 2)
def createCircle(self):
self.scrn.create_oval(150, 150, 250, 250, fill = "red", tag = "circle")
def Test(self):
if len(self.totalt) == 0:
self.fixate()
self.update()
self.bind_all("<Key>", self.gettime)
self.after(random.randint(1000, 5000))
self.t0 = time.clock()
self.createCircle()
def gettime(self, event):
t = time.clock() - self.t0
self.totalt.append(t)
self.scrn.delete("circle")
if len(self.totalt) < 6:
self.Test
else:
print(self.totalt)
a = tk.Tk()
b = TRTest(a)
b.mainloop()
It should be making 5 trials and returning the list of the measured TR times, but it freezes before showing a second circle. Where's the problem?
In addition to the answer from Patrick Haugh, you aren't using the .after method correctly.
Change this line of code
self.after(random.randint(1000, 5000))
to
self.after(random.randint(1000, 5000),self.Test)
and move it to after
if len(selftotalt) < 6:
This will call the self.Test function again after a random period of time between 1 and 5 seconds.
If more 6 or more reaction times have been captures it will no longer call the function again but print out the times.
Full code that seem to work for me
import tkinter as tk
import time
import random
class TRTest(tk.Frame):
def __init__(self, master = None):
super().__init__()
self.grid()
self.canv()
self.createWidgets()
self.totalt = []
def createWidgets(self):
self.quitbtn = tk.Button(self, text = "Quit", command=self.master.destroy)
self.quitbtn.grid(row = 3, column = 3)
self.label = tk.Label(self, text = "TRTest")
self.label.grid(row = 1, column = 3)
self.testbtn = tk.Button(self, text = "Start test", command = self.Test)
self.testbtn.grid(row = 1, column = 2)
def canv(self):
self.scrn = tk.Canvas(self, width = 400, height = 400, bg = "#E8E8E8")
self.scrn.grid(row = 2, column = 3)
def fixate(self):
self.scrn.create_line(190, 200, 210, 200, width = 2)
self.scrn.create_line(200, 190, 200, 210, width = 2)
def createCircle(self):
self.scrn.create_oval(150, 150, 250, 250, fill = "red", tag = "circle")
def Test(self):
if len(self.totalt) == 0:
self.fixate()
self.update()
self.bind_all("<Key>", self.gettime)
self.t0 = time.clock()
self.createCircle()
def gettime(self, event):
t = time.clock() - self.t0
self.totalt.append(t)
self.scrn.delete("circle")
if len(self.totalt) < 6:
self.after(random.randint(1000, 5000),self.Test)
else:
print(self.totalt)
a = tk.Tk()
b = TRTest(a)
b.mainloop()
I think you are trying to run the method Test with
self.Test
Try instead
self.Test()

Categories

Resources