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 :)
Related
I am trying to have the rectangle I create spawn at the bottom of my tkinter canvas instead of spawning at the top without affecting its size. I have only found this post and this doesn't help me as it does not describe how to move the shape to the bottom of the canvas
Here's my code:
from tkinter import *
from tkinter.ttk import *
#Temporary
width = 1024
height=768
class GFG:
def __init__(self, master = None):
self.master = master
self.master.configure(bg="black") #Apart from the canvas colour
self.x = 0
self.y = 0
self.canvas = Canvas(master, background="black", highlightthickness=0, width=width, height=height) #Canvas colour
self.rectangle = self.canvas.create_rectangle(5, 5, 25, 25, fill='white')
self.canvas.pack()
self.movement()
def movement(self):
self.canvas.move(self.rectangle, self.x, self.y)
self.canvas.after(100, self.movement)
def left(self, event):
print(event.keysym)
self.x = -5
self.y = 0
def right(self, event):
print(event.keysym)
self.x = 5
self.y = 0
if __name__ == "__main__":
master = Tk()
master.title("Black Hole Settlers")
gfg = GFG(master)
master.bind("<KeyPress-Left>", lambda e: gfg.left(e))
master.bind("<KeyPress-Right>", lambda e: gfg.right(e))
mainloop()
Pastebin
Thanks,
swanserquack
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.
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.
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()
I can track where the user clicks and where they release but I want to track distance traveled.
from Tkinter import *
root = Tk()
class DragCursor():
def __init__(self, location):
self.label = location
location.bind('<ButtonPress-1>', self.StartMove)
location.bind('<ButtonRelease-1>', self.StopMove)
def StartMove(self, event):
startx = event.x
starty = event.y
print [startx, starty]
def StopMove(self, event):
self.StartMove
stopx = event.x
stopy = event.y
print [stopx, stopy]
location = Canvas(root, width = 300, height = 300)
DragCursor(location)
location.pack()
root.mainloop()
You just need to use the distance formula for determining the distance between two points in an xy-plane,
Also, you need to include some kind of instance variable that will save the coordinates for the start and end points so that you can compute it after the mouse release.
This is pretty much your code just with a new distancetraveled function that is printed at the end of StopMove using self.positions.
from Tkinter import *
root = Tk()
class DragCursor():
def __init__(self, location):
self.label = location
location.bind('<ButtonPress-1>', self.StartMove)
location.bind('<ButtonRelease-1>', self.StopMove)
self.positions = {}
def StartMove(self, event):
startx = event.x
starty = event.y
self.positions['start'] = (startx, starty)
def StopMove(self, event):
stopx = event.x
stopy = event.y
self.positions['stop'] = (stopx, stopy)
print self.distancetraveled()
def distancetraveled(self):
x1 = self.positions['start'][0]
x2 = self.positions['stop'][0]
y1 = self.positions['start'][1]
y2 = self.positions['stop'][1]
return ((x2-x1)**2 + (y2-y1)**2)**0.5
location = Canvas(root, width = 300, height = 300)
DragCursor(location)
location.pack()
root.mainloop()