Python Tkinter coordinate canvas error - python

I am a total beginner here. I would like to know the coordinates of x1 while it is moving, so it will be keep updating.
Here is my code.
from tkinter import *
import tkinter as tk
import time
import random
class Example(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self)`
#create a canvas
self.canvas = tk.Canvas(width=600, height=250)
self.canvas.pack()
self.road()
self.crossing()
def road(self):
Line1 = self.canvas.create_line(50, 50, 450, 50)
Line2 = self.canvas.create_line(50, 100, 450, 100)
def crossing(self):
CLine1 = self.canvas.create_line(350, 50, 350, 100)
CLine2 = self.canvas.create_line(375, 50, 375, 100)
class Car:
def __init__(self,x1,y1,x2,y2,vx,vy,color,Example):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.vx = vx
self.vy = vy
self.color=color
self.Example = Example
def drawit(self):
self.Example.canvas.create_rectangle(x1,y1,x2,y2,color)
def moveit(self,vx,vy):
self.Example.canvas.move(vx,vy)
if __name__ == "__main__":
root = tk.Tk()
my_canvas = Example(root).pack(fill="both", expand=True)
mycar = Car(60, 60, 125, 90,3,0,"red",Example)
mycar.drawit()
mycar.moveit()
print (mycar.x1)
root.mainloop()
Here is the error message:
AttributeError: type object 'Example' has no attribute 'canvas'
Process finished with exit code 1
Any help would be much appreciated.

You have some basic misunderstandings of how classes and objects work. Instead of doing this:
my_canvas = Example(root)
my_canvas.pack(fill="both", expand=True)
mycar = Car(60, 60, 125, 90,3,0,"red",Example)
(note: you also need to call pack on a separate line from where the widget is created and assigned to a variable. See https://stackoverflow.com/a/1101765/7432)
You need to do this:
my_canvas = Example(root).pack(fill="both", expand=True)
mycar = Car(60, 60, 125, 90,3,0,"red", my_canvas)
You need to pass in the instance of Example (eg: my_canvas), not the class (eg: Example). Also, Car needs to use the example like this:
class Car:
def __init__(self,x1,y1,x2,y2,vx,vy,color,example):
...
self.example=example
def drawit(self):
self.example.canvas.create_rectangle(x1,y1,x2,y2,color)
def moveit(self,vx,vy):
self.example.canvas.move(vx,vy)

Your Car doesn't have access to the Example object. I would pass my Example to my Car on init, so that it can access its context. e.g.
class Car:
def __init__(self, x, y, example):
...
self.example=example
def do_stuff(self):
self.example.canvas.draw(whatever)
example = Example(args)
car = Car(3,4,example)
car.do_stuff()
In this example, Car objects have access to the canvas that you create in your Example class, so they can draw themselves, etc.

Related

How to find which Canvas item is closer to the point clicked in tkinter?

I want to find which Canvas item is closer to the point I clicked. I am trying to use find the closest but I can't use it efficiently. I am trying to make an itemlist and then find which item is the closest in the itemlist. I also have self keywords since I'm trying to do all this using classes
def initUI(self):
self.canvas = Canvas(self.parent)
self.item1 = self.canvas.create_oval(50, 50, 60, 60, fill="red")
self.item2 = self.canvas.create_oval(100, 100, 110, 110, fill="red")
self.item3 = self.canvas.create_oval(150, 150, 160, 160, fill="red")
self.itemlist = []
self.itemlist.append(self.item1)
self.itemlist.append(self.item2)
self.itemlist.append(self.item3)
print(self.itemlist)
self.canvas.pack()
self.canvas.bind("<Button-1>", self.find_closest)
def find_closest(self, event):
""" """
As pointed out in the comments you can use the Canvas method find_closest() to do what's needed. Below is a runnable example of doing that based on the code in your question. It uses an auxilary dictionary to map the ids of the canvas object to arbitrary strings (referred to as "names" in the example).
import tkinter as tk
from tkinter.constants import *
class MyClass:
def __init__(self, parent):
self.parent = parent
self.initUI()
def initUI(self):
self.canvas = tk.Canvas(self.parent)
self.canvas.pack()
self.canvas.bind("<Button-1>", self.find_closest)
# Create dictionary mapping canvas object ids to a name.
self.object_names = {}
id = self.canvas.create_oval(50, 50, 60, 60, fill="Red")
self.object_names[id] = 'Red object'
id = self.canvas.create_oval(100, 100, 110, 110, fill="Blue")
self.object_names[id] = 'Blue object'
id = self.canvas.create_oval(150, 150, 160, 160, fill="Green")
self.object_names[id] = 'Green object'
self.name_lbl1 = tk.Label(self.parent, text='Closest object:')
self.name_lbl1.pack(side=LEFT)
self.name_var = tk.StringVar(value='')
self.name_lbl2 = tk.Label(self.parent, textvariable=self.name_var)
self.name_lbl2.pack(side=LEFT)
def find_closest(self, event):
if (closest := self.canvas.find_closest(event.x, event.y)):
obj_id = closest[0]
self.name_var.set(self.object_names[obj_id]) # Updates lbl2.
if __name__ == '__main__':
root = tk.Tk()
instance = MyClass(root)
root.mainloop()
Screenshot of it running:

Python tkinter image not displaying

so i am trying to make a 2d "minecraft" game in python, but i just cant get an image to show up
I have this code now:
from tkinter import *
import time
class Block:
def __init__(self, posx, posy, game):
self.x = posx
self.y = posy
self.game = game
self.grass_block = PhotoImage(file="grass_block.gif")
print("place")
self.image = self.game.canvas.create_image(posx, posy, image=self.grass_block, anchor="nw")
self.hoi = self.game.canvas.create_oval(100,100,100,100, fill = "red")
def get_image(self):
return self.image
class Game:
def __init__(self):
self.tk = Tk()
self.tk.title("MijnCraft")
self.tk.resizable(0, 0)
self.canvas = Canvas(self.tk, width=1000, height=1000, highlightthickness=0)
self.canvas.pack()
self.bg = PhotoImage(file="nacht.gif")
self.canvas.create_image(0, 0, image=self.bg, anchor="nw")
self.aan = True
self.genworld()
def genworld(self):
print("make")
Block(100, 100, self)
def gameloop(self):
while self.aan:
self.tk.update_idletasks()
self.tk.update()
time.sleep(0.01)
spel = Game()
spel.gameloop()
But i just cant get a block image to show up, i just get the backgound, does someone know what i am doing wrong, (i dont get any errors)(it does print the 2 debug messages)
i hope you can help!
Your instance of Block is a local object that is getting destroyed when genworld returns. To prevent this, save a reference to the block. Assuming you're going to want to create more than one block, you can use a list or dictionary.
class Game:
def __init__(self):
self.blocks = []
...
def genworld(self):
self.blocks.append(Block(100, 100, self))

Create an image in a canvas inside a class [duplicate]

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 1 year ago.
I wanted to create a chess program using OOP. So I made a superclass Pieces, a subclass Bishop, and a UI class GameUI. I created a canvas in the class GameUI. I wanted, that when I instantiate an object bishop in the class GameUI, it shows an Image from a bishop, on the canvas.
The problem is, when I instantiate the Bishop, I don't see any image. So I tried to do the same with a text : instead of using the method create_image from the class Canvas, I used the method create_text, and it worked : I saw a text on the canvas. That means, the problem comes from the method create_image, and I don't understand it.
If I create an Image directly in the class GameUi, it works! but that's not what I want...
So I don't have any error message. I see the canvas (with a blue background), but no image on it.
Here's the code :
from tkinter import PhotoImage, Tk, Canvas
class Pieces:
def __init__(self, can, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
class Bishop(Pieces):
def __init__(self, can, color, x_position, y_position):
super().__init__(can, color, x_position, y_position)
if color == "black":
icon_path = 'black_bishop.png'
elif color == "white":
icon_path = 'white_bishop.png'
icon = PhotoImage(file=icon_path) # doesn't see the image
can.create_image(x, y, image=icon)
class GameUI:
def __init__(self):
self.windows = Tk()
self.windows.title("My chess game")
self.windows.geometry("1080x720")
self.windows.minsize(300, 420)
self.can = Canvas(self.windows, width=1000, height=600, bg='skyblue')
icon = PhotoImage(file=icon_path) # here I create the image in this class, and
can.create_image(x, y, image=icon) # we can see it very well
self.bishop = Bishop(self.can, "black", 50, 50)
self.can.pack()
self.windows.mainloop()
app = GameUI()
To make your code work, I decided to sort of rewrite it based on this answer. It works now, but really the only thing that you needed to add was self.icon instead of icon. icon gets garbage collected since there is no further reference to it, while self.icon remains. Also, it's not entirely the same as yours was, so it probably needs a bit of rewriting too.
from tkinter import *
from random import randint
class Piece:
def __init__(self, canvas, x1, y1):
self.x1 = x1
self.y1 = y1
self.canvas = canvas
class Bishop(Piece):
def __init__(self, canvas, x1, y1, color):
super().__init__(canvas, x1, y1)
if color == "black":
icon_path = 'black_bishop.png'
elif color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
self.ball = canvas.create_image(self.x1, self.y1, image=self.icon)
def move_piece(self):
deltax = randint(0,5)
deltay = randint(0,5)
self.canvas.move(self.ball, deltax, deltay)
self.canvas.after(50, self.move_piece)
class GameUI:
def __init__(self):
# initialize root Window and canvas
root = Tk()
root.title("Chess")
root.resizable(False,False)
canvas = Canvas(root, width = 300, height = 300)
canvas.pack()
# create two ball objects and animate them
bishop1 = Bishop(canvas, 10, 10, 'white')
bishop2 = Bishop(canvas, 60, 60, 'black')
bishop1.move_piece()
bishop2.move_piece()
root.mainloop()
app = GameUI()

How would make it so I can apply a button to any window I want?

I had trouble wording the question so let me explain. I am learning to use Object Oriented Programming as part of a project, so I decided to start off by creating classes for creating windows and buttons to place on the windows (Labels ect. will come later too). In other words, I want to be able to place button on a "Window1" without specifying "Window1" in the class (and instead pass it through with the CreateButton(values). Here is my code so far, and help is appreaciated. PS. It functioned how it should before I tried the windowname parameter, and I set the window manually to home_window, but I cant seem to get the windowname paramater to pass through properly.
import tkinter as tk
from tkinter import ttk
home_window = tk.Tk()
home_window.title("Rota System")
home_window.geometry("600x600")
def thankyou():
print ("Thank you very much")
class CreateWindow():
def __init__(self, name, title, geometry):
self.name = name
self.title = title
self.geometry = geometry
name = tk.Tk()
name.title(title)
name.geometry(geometry)
class CreateButton():
def __init__(self, name, width, height, x, y, font, size, text, command, windowname):
#I want to be able to place the button on any window I want, not just home_window
self.name = name
self.width = width
self.height = height
self.x = x
self.y = y
self.font = font
self.size = size
self.text = text
self.command = command
self.windowname = windowname
name = tk.Button(windowname, text=text, command=command)
name.config(font=(font, size))
name.place(x=x, y=y, height=height, width=width)
CreateWindow("Test", "Test", "900x600")
CreateButton("Test","200","200","0","0","Courier",10,"Test",thankyou, "Test")
First: tkinter needs object's ID returned by Tk(), Button(), etc. - not names (strings) which you created.
With string names you would have to keep global dictionary {name: object_id} to convert name to object_id.
BTW:
Tk() should be used only to create main window. For other windows you should use Toplevel()
Your code in classes (and they names) rather fit to functions.
Function creates object and use return to return this object so it can be used in other function.
import tkinter as tk
from tkinter import ttk
# --- functions ---
def thank_you():
print ("Thank you very much")
def create_window(title, geometry):
window = tk.Tk()
window.title(title)
window.geometry(geometry)
return window
def create_button(parent, width, height, x, y, font, size, text, command):
button = tk.Button(parent, text=text, command=command)
button.config(font=(font, size))
button.place(x=x, y=y, height=height, width=width)
return button
# --- main ---
window = create_window("Test", "900x600")
button = create_button(window, 200, 200, 0, 0, "Courier", 10, "Test", thank_you)
window.mainloop()
Using classes I would inherit it. So I can assing instance to variable and use in other class as parent.
And I would use nouns as names for classes.
import tkinter as tk
from tkinter import ttk
# --- classes ---
class MainWindow(tk.Tk):
def __init__(self, name, title, geometry):
super().__init__()
self.name = name
self.title(title)
self.geometry(geometry)
#self._title = title
#seff._geometry = geometry
class MyButton(tk.Button):
def __init__(self, parent, name, width, height, x, y, font, size, text, command):
super().__init__(parent, text=text, command=command)
self.name = name
self.config(font=(font, size))
self.place(x=x, y=y, height=height, width=width)
#self._width = width
#self._height = height
#self._x = x
#self._y = y
#self._font = font
#self._size = size
#self._text = text
#self._command = command
# --- functions ---
def thank_you():
print("Thank you very much")
# --- main ---
window = MainWindow("WindowName", "Test", "900x600")
button = MyButton(window, "ButtonName", 200, 200, 0, 0, "Courier", 10, "Test", thank_you)
window.mainloop()
See: PEP 8 -- Style Guide for Python Code -
I don't know if there is about nouns for class names and verbs for functions (or maybe it was in book Clean Code, Robert C. Martin) but usually people use this rule.

multiplying moving rectangles in python (tkinter)

I have two questions:
I want to make several rectangles, moving randomly. I am at a point where i
can do it with one rectangle but i don't get it how to multiply them.
I am a beginner so i have copied this example and modified it in my favor but i don't know exactly why i have to write everytime the "self" and the "init". It seems to be common to name those parameters in this manner.
I looked both questions up several times but didn't find a satisfying answer.
here the code:
from tkinter import *
from tkinter.ttk import *
from random import *
class simulation:
def __init__(self, anzahl, master = None):
self.master = master
self.canvas = Canvas(master, width= 2736, height= 1824)
self.rectangle = self.canvas.create_rectangle(500, 380, 515, 395, fill = "black")
self.canvas.pack()
self.movement()
def movement(self):
self.canvas.move(self.rectangle, randint(-10,10), randint(-10,10))
self.canvas.after(100, self.movement)
if __name__ == "__main__":
master = Tk()
master.title("Simulation")
simulation = simulation(master)
mainloop()
maybe this will help you, make an object for each player and the canvas packed ones in order not to hide other players ...
from tkinter import *
from random import *
class simulation:
def __init__(self, master , canvas , color):
self.master = master
self.canvas = canvas
self.rectangle = canvas.create_rectangle(500, 380, 515, 395, fill=color)
def movement(self):
canvas.move(self.rectangle, randint(-10,10), randint(-10,10))
self.canvas.after(100, self.movement)
if __name__ == "__main__":
master = Tk()
canvas = Canvas(master, width=2736, height=1824)
canvas.pack()
master.title("Simulation")
player1 = simulation(master, canvas,"red")
player2 = simulation(master,canvas, "black")
player1.movement()
player2.movement()
mainloop()

Categories

Resources