I want to make a circle move and avoid obstacles with collision detection. Here's the code I have.
from Tkinter import *
window = Tk()
canvas = Canvas(window,width=800, height=500, bg='pink')
canvas.pack()
finishline = canvas.create_oval(700, 300, 700+50, 300+50,fill='green')
robot = canvas.create_oval(20,200,20+45,200+45,fill='yellow')#(x1, y1, x2, y2)
ob1 = canvas.create_rectangle(200,400,200+50,200+1,fill='black')
canvas.update()
ob1 = canvas.create_rectangle(500,200,150+400,300+100,fill='blue')
canvas.update()
You could always use the canvas.find_overlapping method (found here: http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.find_overlapping-method), and if it ever returns a value other than your own little circle, you can make the circle go in the other direction or something.
It's kind of hard to give a specific answer with such unspecific requirements.
Related
I have created an image on a canvas in tkinter that responds to a button event. And, the object is created on position x and position y where that event took place. But the object changes shape constantly.
def leftclick(event):
canvas1=Canvas(play, height=hei, width=wid)
canvas1.grid(row=0, column=0, sticky=W)
canvas1.delete("all")
x=event.x
y=event.y
print(event.x, event.y)
bullet = canvas1.create_oval(x,y, 100,100, fill="red")
xspeed=random.randint(0, 50)
yspeed=random.randint(0,50)
This just draws ovals which are randomly shaped. Why is this happening and how do I fix it?
You should only create your canvas once, but that's not a problem.
The problem is that the tkinter tries to create an oval inside the rectangle. You've specified the 2 points of the rectangle: x,y and 100,100. Just use bullet = canvas1.create_oval(x-50,y-50, x+50,y+50, fill="red") or whatever number you pick instead of 50. Hope that's helpful!
I've tried several ways to change the width of the blue rectangle in this example code. Nothing seems to work. In the code, "a" represents a float variable between 1.00, and 0.00. That value is used to calculate "b," which is the desired width of the blue rectangle in pixels. I have some fairly complicated code that generates that value, and at least that works. In order for the code to work, the width of the blue rectangle must rely on "b." I've tried "Canvas.itemconfig()," and it didn't work.
import tkinter
from tkinter import *
root = Tk()
root.maxsize(320,240) # Sets max size of window
root.minsize(320,240)
canvas_height = 23
canvas_width = 315
w = Canvas(root, width=canvas_width, height=canvas_height)
w.pack()
w.create_rectangle(5, canvas_height, canvas_width, 2, fill="yellow")
w.create_rectangle(5, canvas_height, canvas_width, 2, fill="blue")
a = 1.0 # More complicated code creates this float between 0.00 and 1.00. It is a percentage of the desired 'blue rectangle' width
b = int(a * canvas_width)
root.mainloop()
If anyone could help, I would greatly appreciate it!
P.s. I'm new to the Stackoverflow community, so please let me know if there's anything I can do to make my questions easier to answer.
The rectangle is defined by a couple of coordinates for opposite corners. Get the coordinates of the left edge, add the width to the x coordinate, and use that to set the coordinates of the right edge.
First, keep track of the object id so you can change it later:
blue = w.create_rectangle(5, canvas_height, canvas_width, 2, fill="blue")
To resize, get the coordinates...
x0, y0, x1, y1 = w.coords(blue)
Do some math...
x1 = x0 + b
And reset the coordinates
w.coords(blue, x0, y0, x1, y1)
I'm drawing the Syrian flag with Python turtle. I left out the two green stars for now, as they require an external dependency (oh wow, how's that for an accidental political pun? lol). Here's the code:
from turtle import *
# Set up the drawing environment
length = 600
height = 400
setup(length, height)
setworldcoordinates(0,0,length,height)
goto(0,0)
colours = ["red","black","white","Darkgreen"]
bgcolor(colours[0])
speed('fastest')
## Black Stripe ##
pencolor(colours[1])
pendown()
fillcolor(colours[1])
begin_fill()
goto(length,0)
goto(length,height/3)
goto(0,height/3)
goto(0,0)
end_fill()
penup()
# White Stripe
goto(0,height/3)
pencolor(colours[2])
pendown()
fillcolor(colours[2])
begin_fill()
goto(length,height/3)
goto(length,(height/3)*2)
goto(0,(height/3)*2)
goto(0,height/3)
end_fill()
penup()
ht()
done()
Why is there a red border around the image? I have set the canvas size and the coordinate system to equal each other so that 1 pixel equals 1 pixel. The black box, for example, should start at the bottom-left edge of the canvas as I have told it to, but in it's stead lies a red border. If you resize the window manually after running the code, no matter how small you make it in an attempt to overlap the red border, there will always be a red border. I haven't even written a border into the code.
I think it might have something to do with the innards of Python turtle, that it just allows a small border in order to see what lies on the coordinates (0,0), but I can't find any documentation that actually shows how turtle works, they all just say things like "setup(x,y) = set canvas size" or bgcolor("enter colour here") and don't explain any of the definitions that make up the functions.
Does anyone know how to either:
Remove the border?
Make window size equal to canvas size, hence covering the red border?
Cheers!
(edit: By the way, screensize(length,height) gives me red borders and scrollbars.)
Not sure if i can alter it unless i make a tk window from scratch
which defeats the purpose of using turtle in the first place.
You can create your window structure with tkinter and then embed a turtle canvas within it. Below is an example of your program done this way, in which you can switch between turtle's ScrolledCanvas and tkiner's plain `Canvas' to see the difference:
import tkinter as tk
from turtle import TurtleScreen, RawTurtle, ScrolledCanvas
WIDTH, HEIGHT = 640, 480
root = tk.Tk()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
# canvas = ScrolledCanvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
screen = TurtleScreen(canvas)
screen.setworldcoordinates(0, 0, WIDTH, HEIGHT)
screen.bgcolor('red')
turtle = RawTurtle(screen, visible=False)
turtle.speed('fastest')
## Black Stripe ##
turtle.color('black')
turtle.begin_fill()
turtle.setx(WIDTH)
turtle.sety(HEIGHT / 3)
turtle.setx(0)
turtle.sety(0)
turtle.end_fill()
## White Stripe ##
turtle.penup()
turtle.sety(HEIGHT / 3)
turtle.pendown()
turtle.color('white')
turtle.begin_fill()
turtle.setx(WIDTH)
turtle.sety(2 * HEIGHT / 3)
turtle.setx(0)
turtle.sety(HEIGHT / 3)
turtle.end_fill()
screen.mainloop()
The ScrolledCanvas introduces more chrome (borders and other overhead) which shifts things around, sometimes in undesireable ways. Here's a vertical slice showing the difference between plain Canvas on the left and ScrolledCanvas on the right:
as more chrome is added to the window, the drawing area moves slightly. Though the large amount of offset you're seeing in your output that partially disappears when you move the window certainly seems a bug.
I am drawing a lot of moving particles in a stationary box with Tkinter. My box is always there and does not change as time goes by, whereas the particles need to be updated.
My first intuition is to delete ALL the things (both particles and the box) and then redraw everything.
canvas.delete(ALL)
It indeed works, but the frame updates get extremely slow. This is because my box is of an irregular shape, which implies that I have to draw the box dot by dot. So this delete-everything-and-redraw-everything method is unsatisfactory.
I wish that the box is drawn only once, and only the particles get deleted and redrawn (updated). How should I do this?
Suppose you have a rectangle on canvas:
canvas.create_rectangle(x0, y0, x1, y1)
This would return a handle, so if you keep track of it,
myRectangle = canvas.create_rectangle(x0, y0, x1, y1)
canvas.delete(myRectangle)
This will delete only the myRectangle object.
Another way of doing it is to use tags.
canvas.create_rectangle(x0, y0, x1, y1, tags="myRectangle")
canvas.delete("myRectangle")
What you need to do is assign the drawings to variables, and then delete those. The below script demonstrates this:
from Tkinter import Button, Canvas, Tk
root = Tk()
canvas = Canvas()
canvas.grid()
drawing1 = canvas.create_oval((10,50,20,60), fill="red")
drawing2 = canvas.create_oval((30,70,40,80), fill="blue")
Button(text="Kill 1", command=lambda: canvas.delete(drawing1)).grid()
Button(text="Kill 2", command=lambda: canvas.delete(drawing2)).grid()
root.mainloop()
In addition to ALL, the delete method can also accept a specific drawing.
I am currently writing a fsm editor with tkinter. But, I stuck on connecting two states. I have two questions:
1) How can make the transition arrow growable according to mouse movement?
2) How can I stick the starting point of the arrow on a state and the end point of the arrow on another state?
PS. Do you think the documentation of tkinter is good enough?
Here's an example that shows the concept. In a nutshell, use tags to associate lines with boxes, and simply adjust the coordinates appropriately when the user moves the mouse.
Run the example, then click and drag from within the beige box.
Of course, for production code you need to make a more general solution, but hopefully this shows you how easy it is to create a box with arrows that adjust as you move the box around.
from Tkinter import *
class CanvasDemo(Frame):
def __init__(self, width=200, height=200):
Frame.__init__(self, root)
self.canvas = Canvas(self)
self.canvas.pack(fill="both", expand="1")
self.canvas.create_rectangle(50, 25, 150, 75, fill="bisque", tags="r1")
self.canvas.create_line(0,0, 50, 25, arrow="last", tags="to_r1")
self.canvas.bind("<B1-Motion>", self.move_box)
self.canvas.bind("<ButtonPress-1>", self.start_move)
def move_box(self, event):
deltax = event.x - self.x
deltay = event.y - self.y
self.canvas.move("r1", deltax, deltay)
coords = self.canvas.coords("to_r1")
coords[2] += deltax
coords[3] += deltay
self.canvas.coords("to_r1", *coords)
self.x = event.x
self.y = event.y
def start_move(self, event):
self.x = event.x
self.y = event.y
root = Tk()
canvas = CanvasDemo(root)
canvas.pack()
mainloop()
Tkinter is perfectly fine for this sort of application. In the past I've worked on tools that were boxes connected with arrows that stayed connected as you move the boxes around (which is what I think you are asking about). Don't let people who don't know much about Tkinter sway you -- it's a perfectly fine toolkit and the canvas is very flexible.
The solution to your problem is simple math. You merely need to compute the coordinates of the edges or corners of boxes to know where to anchor your arrows. To make it "grow" as you say, simply make a binding on mouse movements and update the coordinates appropriately.
To make the line growable all you have to do is adjust the coordinates of the line each time the mouse moves. The easiest thing to do is make liberal use of canvas tags. With the tags you can know which arrows connect to which boxes so that when you move the box you adjust the coordinates of any arrows that point to or from it.