tkinter: Moving objects to random places using a for loop - python

I use python tkinter and I am trying to move my ark/sun (ONLY 1 onject) to random places across (0,500) to (800,500) using a for loop so every time I run it will be in a new place but i keep failing to do so. If someone could help me it would mean a lot.
from tkinter import *
from random import *
myInterface = Tk()
screen = Canvas( myInterface, width=800, height=800, background="white" )
screen.pack()
#sky
##Sky
y = 0
y2 = 22
skyOptions = ["#4C1D6D","#53236E","#5A2970","#623072","#693674","#703D75",\
"#784377","#7F4979","#86507B","#8E567C","#955D7E","#9C6380",\
"#A46A82","#AB7083","#B27685","#BA7D87","#C18389","#C88A8A",\
"#D0908C","#D7968E","#DE9D90","#E6A391", "#EDAA93","#F4B095"]
for sky in range (1,24):
skyColour = (skyOptions[sky%24])
screen.create_rectangle (0,y,1000,y2, fill = skyColour, outline = skyColour)
y = y + 22
y2 = y2 + 22
#sun (Make it randomly move plz)
screen.create_arc(150, 250, 500, 800 ,start=0, extent=180, fill= "#fd8953", outline = "#fd8953")
screen.update
spacing = 50
for x in range(0, 1000, spacing):
screen.create_line(x, 25, x, 1000, fill="red")
screen.create_text(x, 5, text=str(x), font="Times 9", anchor = N)
for y in range(0, 1000, spacing):
screen.create_line(25, y, 1000, y, fill="blue")
screen.create_text(5, y, text=str(y), font="Times 9", anchor = W)
screen.update()

A simple example of moving a widget on a canvas using the coords() function:
from tkinter import *
from random import *
myInterface = Tk()
screen = Canvas( myInterface, width=800, height=800, background="white" )
screen.pack()
#sun (Make it randomly move plz)
arc = screen.create_arc(150, 250, 500, 800 , start=0, extent=180,
fill= "#fd8953", outline = "#fd8953")
def random_move(event):
# Generate random numbers for x and y between 0 and 99
x = randrange(0, 100)
y = randrange(0, 100)
# Move arc to original + random x, y position
screen.coords(arc, [150+x, 250+y, 500+x, 800+y])
# Create binding so random_move() is easy to invoke.
myInterface.bind('<space>', random_move)
# start mainloop() which runs the application
myInterface.mainloop()
You need to start the application mainloop or the program will just stop after creating the window and widgets. The mainloop listens for events (mouse, keyboard ect).
Also: that was a lot of code, most of which was not relevant to your problem. Try to minimize the code in your questions.
I'm binding <space> to the random_move() function so just hit space for each move.
And; check out The Tkinter Canvas Widget.

Related

How to use Tkinter after method to give the impression of a wheel spinning?

I'm trying to create three separate 'wheels' that change the starting position of the first arc in order to give the impression of the wheel spinning. Tkinter will draw the first wheel on the canvas and delete it after 1 second, but the subsequent wheels are never drawn.
from tkinter import Canvas, Tk
tk = Tk()
tk.geometry('500x500')
canvas = Canvas(tk)
canvas.pack(expand=True, fill='both')
in_list = [1,1,1,1,1,1,1]
arc_length = 360 / len(in_list)
first_mvmt = arc_length / 3
second_mvmt = arc_length * 2 / 3
mvmt_list = [0, first_mvmt, second_mvmt]
for mvmt in mvmt_list:
for i in range(len(in_list)):
start = i * arc_length
extent = (i + 1) * arc_length
arc = canvas.create_arc(5, 5, 495, 495, outline='white', start=start + mvmt, extent=extent)
canvas.after(1000)
canvas.delete('all')
tk.mainloop()
Also please excuse my poor formatting, this is my first post on stackoverflow
Your outer loop has only 3 items in it, that is not going give the impression of a wheel spinning...
Take a look a the code below:
I changed your loop to 100
Added a tk.update() after inner loop
Added a sleep(0.05) instead of your after(1000)
import time
from tkinter import Canvas, Tk
tk = Tk()
tk.geometry('500x500')
canvas = Canvas(tk)
canvas.pack(expand=True, fill='both')
in_list = [1,1,1,1,1,1,1]
arc_length = 360 / len(in_list)
for mvmt in range(100):
canvas.delete('all')
for i in range(len(in_list)):
start = i * arc_length
extent = (i + 1) * arc_length
arc = canvas.create_arc(5, 5, 495, 495, outline='white', start=start + mvmt, extent=extent)
tk.update()
time.sleep(0.05)
tk.mainloop()
it should look like this:

Why doesn't this image move when the key is pressed?

Python and Tkinter nebwie. I tried making an image in tkinter and have it move using the arrows. The image shows just it should, but when I try to move it using the arrows, it doesn't work at all. Any idea why? I use python 2.7.18 and I'm on the latest version of Ubuntu
from Tkinter import *
############
#things
w = 500
h = 500
width=w
height=h
#############
######################################################################
#window
window = Tk()
window.title("Moving image")
canvas = Canvas(window, width = 500, height = 500)
canvas.pack()
my_image = PhotoImage(file="/home/user/Documents/ddd.png")
canvas.create_image(260, 125, anchor = NW, image=my_image)
#######################################################################
################################
#var
def up(event):
x = 0
y = -10
canvas.move(my_image, x, y)
def down(event):
x = 0
y = 10
canvas.move(my_image, x, y)
def left(event):
x = -10
y = 0
canvas.move(my_image, x, y)
def right(event):
x = 10
y = 0
canvas.move(my_image, x, y)
###############################
###############################
#binds
window.bind("<Up>", up)
window.bind("<Down>", down)
window.bind("<Left>", left)
window.bind("<Right>", right)
window.mainloop()
##############################
You are trying to move the wrong object. Use the object, which is returned by canvas.create_image:
image_id = canvas.create_image(260, 125, anchor = NW, image=my_image)
...
canvas.move(image_id, x, y)
Alternatively, you can attach a tag to the image:
canvas.create_image(260, 125, anchor=NW, image=my_image, tag="move")
...
canvas.move("move", x, y)
This will move all objects, which have this specific tag attached.

Tkinter Key Binding in Different Regions of Window

I am trying to make a Tic Tac Toe program for 2 players, and so I need to be able to have mouse clicks in certain areas of the window do different things. How do I do that? This is what I have so far.
from tkinter import *
# Creates Window
tk = Tk()
canvas = Canvas(tk, width=600, height=600)
tk.title('Tic Tac Toe')
canvas.pack
# Creates Board
line1 = canvas.create_line(200, 0, 200, 600)
line2 = canvas.create_line(400, 0, 400, 600)
line3 = canvas.create_line(0, 200, 600, 200)
line4 = canvas.create_line(0, 400, 600, 400)
# Creates Functions for Xs being placed on board
def x1(event):
canvas.create_line(0, 0, 200, 200)
canvas.create_line(200, 0, 0, 200)
def x2(event):
canvas.create_line(200, 0, 400, 200)
canvas.create_line(400, 0, 200, 200)
# Creates the buttons to put the Xs on the board when clicked DOESN'T WORK
canvas.pack()
canvas.bind("<Button-1>", x1)
canvas.mainloop()
Sorry if I formatted the code wrong. The second to last line is the line I am having trouble with. I want button-1 (mouse click) to be able to do x1 and x2 (and eventually other functions) depending on the region of the window it is on. Please help.
Here is how you can use the event co-ordinates to identify which square of the tic-tac-toe board the user clicked on:
from tkinter import *
# Creates Window
tk = Tk()
width = 600
third = width / 3
canvas = Canvas(tk, width=width, height=width)
tk.title('Tic Tac Toe')
canvas.pack
# Creates Board
canvas.create_line(third, 0, third, width)
canvas.create_line(third*2, 0, third*2, width)
canvas.create_line(0, third, width, third)
canvas.create_line(0, third*2, width, third*2)
def draw_cross(row,col):
canvas.create_line(col * third, row * third, (col + 1) * third, (row + 1) * third)
canvas.create_line((col + 1) * third, row * third, col * third, (row + 1) * third)
def mouse_click(event):
col = int(event.x / third)
row = int(event.y / third)
draw_cross(row,col)
canvas.pack()
canvas.bind("<Button-1>", mouse_click)
canvas.mainloop()
First of all I parametrized the board dimensions using variables width and third - just change width, and everything will resize correctly.
Second, clicking a mouse button on the canvas calls the mouse_click event handler, which obtains the co-ordinates of the point in the canvas on which the mouse was clicked (event.x and event.y), and computes the corresponding row (0, 1 or 2) and column (0, 1 or 2) of the square on the tic-tac-tow board. These are then passed as parameters to the function draw_cross which draws the two diagonals for that square.
Hope that helps.
When function x1 is called in response to a mouse click, the event object has the mouse click x and y co-ordinates (event.x and event.y). Use those to detect which part of the canvas was clicked on and act accordingly.

Tkinter - bouncing icon script

I'm writing a simple bouncing icon program (Python 3.7, Windows 10 x64) to get the feel for Tkinter and canvases. I've posted my code below. My problem with the program is that it clips the edges of the icon (in the direction of motion). If I slow the motion down a bit (by increasing the value in the after method) it no longer clips, but the motion is choppy. Maybe I'm overthinking this, it basically does what I've aimed for. But if this were a game or other project that mattered, how would this be prevented?
from tkinter import *
import os
from PIL import Image, ImageTk
xinc, yinc = 5, 5
def load_image(width, height, imgpath):
loadimg = Image.open(imgpath)
pwid, phi = loadimg.size
pf1, pf2 = 1.0*width/pwid, 1.0*height/phi
pfactor = min([pf1, pf2])
pwidth, pheight = int(pwid*pfactor), int(phi*pfactor)
loaded = loadimg.resize((pwidth, pheight), Image.ANTIALIAS)
loaded = ImageTk.PhotoImage(loaded)
return loaded
def bounce():
global xinc
global yinc
cwid = int(dash.cget('width'))
chi = int(dash.cget('height'))
x = dash.coords(dashposition)[0]
y = dash.coords(dashposition)[1]
if x > cwid-10 or x < 10:
xinc = -xinc
if y > chi-10 or y < 10:
yinc = -yinc
dash.move(dashposition, xinc, yinc)
dash.after(15, bounce)
root = Tk()
root.configure(bg='black')
dash = Canvas(root, bg='black', highlightthickness=0, width=400, height=300)
dash.grid(row=0, column=0, padx=2, pady=2)
imagepath = os.getcwd() + '/img/cloudy.png'
image = load_image(20, 20, imagepath)
x, y = 10, 10
dashposition = dash.create_image(x, y, anchor=CENTER, image=image, tags=('current'))
bounce()
root.mainloop()
cloudy.png
There are two contributing factors to the clipping. The main problem is that load_image(20, 20, imagepath) will only result in a 20x20 object if the original image is square. But your cloud object isn't square. And your border collision calculations will only work if the rescaled cloud object is 20x20. So we need to modify that. The other issue is that you aren't compensating for the Canvas's border. The easy way to do that is to set it to zero with bd=0.
Its usually a good idea during GUI development to use various colors so you know exactly where your widgets are. So that we can more easily see when the cloud hits the Canvas border I've set the root window color to red. I also increased the .after delay, because I just couldn't see what was happening at the speed you set it at. ;) And I made the cloud a bit bigger.
I've made a couple of other minor changes to your code, the main one being that I got rid of that "star" import, which dumps over 100 Tkinter names into your namespace.
Update
I've reduced xinc & yinc to 1, and improved the bounce bounds calculation. (And incorporated jasonharper's suggestion re cwid & chi). I'm no longer seeing any clipping on my machine, and the motion is smoother. I also reduced the Canvas padding to 1 pixel, but that should have no effect on clipping. I just tried it with padx=10, pady=10 and it works as expected.
import tkinter as tk
import os
from PIL import Image, ImageTk
def load_image(width, height, imgpath):
loadimg = Image.open(imgpath)
pwid, phi = loadimg.size
pf1, pf2 = width / pwid, height / phi
pfactor = min(pf1, pf2)
pwidth, pheight = int(pwid * pfactor), int(phi * pfactor)
loaded = loadimg.resize((pwidth, pheight), Image.ANTIALIAS)
loaded = ImageTk.PhotoImage(loaded)
return loaded, pwidth // 2, pheight // 2
xinc = yinc = 1
def bounce():
global xinc, yinc
x, y = dash.coords(dashposition)
if not bx <= x < cwid-bx:
xinc = -xinc
if not by <= y < chi-by:
yinc = -yinc
dash.move(dashposition, xinc, yinc)
dash.after(5, bounce)
root = tk.Tk()
root.configure(bg='red')
dash = tk.Canvas(root, bg='black',
highlightthickness=0, width=800, height=600, bd=0)
dash.grid(row=0, column=0, padx=1, pady=1)
cwid = int(dash.cget('width'))
chi = int(dash.cget('height'))
imagepath = 'cloudy.png'
size = 50
image, bx, by = load_image(size, size, imagepath)
# print(bx, by)
dashposition = dash.create_image(bx * 2, by * 2,
anchor=tk.CENTER, image=image, tags=('current'))
bounce()
root.mainloop()

How to move a Canvas image using Tkinter?

Guys I am working on a code which needs to move an image in python using Tkinter(canvas)
This is creating problems for me. The image is being displayed but it is not moving.
from Tkinter import *
root = Tk()
root.title("Click me!")
def next_image(event):
global toggle_flag
global x, y, photo1
# display photo2, move to right, y stays same
canvas1.create_image(x+10, y, image=photo1)
canvas1.create_image(x+20, y, image=photo1)
canvas1.create_image(x+30, y, image=photo1)
canvas1.create_image(x+40, y, image=photo1)
canvas1.create_image(x+50, y, image=photo1)
canvas1.create_image(x+60, y, image=photo1)
canvas1.create_image(x+70, y, image=photo1)
canvas1.create_image(x+100, y, image=photo1)
image1 = "C:\Python26\Lib\site-packages\pygame\examples\data\ADN_animation.gif" #use some random gif
photo1 = PhotoImage(file=image1)
# make canvas the size of image1/photo1
width1 = photo1.width()
height1 = photo1.height()
canvas1 = Canvas(width=width1, height=height1)
canvas1.pack()
# display photo1, x, y is center (anchor=CENTER is default)
x = (width1)/2.0
y = (height1)/2.0
canvas1.create_image(x, y, image=photo1)
canvas1.bind('<Button-1>', next_image) # bind left mouse click
root.mainloop()
Canvas provides move method. Arguments are item you want to move, relative x offset from the previous position, y offset.
You need to save the return value of the create_image to pass it to the move method.
Also make sure the canvas is expandable (pack(expand=1, fill=BOTH) in the following code)
from Tkinter import *
root = Tk()
def next_image(event):
canvas1.move(item, 10, 0) # <--- Use Canvas.move method.
image1 = r"C:\Python26\Lib\site-packages\pygame\examples\data\ADN_animation.gif"
photo1 = PhotoImage(file=image1)
width1 = photo1.width()
height1 = photo1.height()
canvas1 = Canvas(width=width1, height=height1)
canvas1.pack(expand=1, fill=BOTH) # <--- Make your canvas expandable.
x = (width1)/2.0
y = (height1)/2.0
item = canvas1.create_image(x, y, image=photo1) # <--- Save the return value of the create_* method.
canvas1.bind('<Button-1>', next_image)
root.mainloop()
UPDATE according to the comment
Using after, you can schedule the function to be called after given time.
def next_image(event=None):
canvas1.move(item, 10, 0)
canvas1.after(100, next_image) # Call this function after 100 ms.

Categories

Resources