Making a 5x5 Board using a grid in Tkinter - python

Im making one of my first programs, using python, tkinter and pillow.
This program is supposed to randomly select one of four tile images, create an image and repeat 25 times
Making a 5x5 Board using a grid to position the images in a square.
This is not what happened when i ran my code though
One or two columns and 4 rows are usually generated and not every coordinate is filled with an image.
And only the corner of the images are visible for some reason?
The images center seems to be placed in the top left corner of their grid.
A output might look something like this
X = coordinate filled with an image
O = coordinate filled with bg colour
X X
O X
O X
When i want it to look like this
X X X X X
X X X X X
X X X X X
X X X X X
X X X X X
The code thats supposed to place the images looks like this
while n < 25:
n = n + 1
number = random.randint(1,4)
if number == 1:
TILE_FLOWER.grid(row = x, column = y)
TILE_FLOWER.create_image(16,16, image = TILE_FLOWER_IMG)
elif number == 2:
TILE_GRASS.grid(row = x, column = y)
TILE_GRASS.create_image(16,16, image = TILE_GRASS_IMG)
elif number == 3:
TILE_STONE.grid(row = x, column = y)
TILE_STONE.create_image(16,16, image = TILE_STONE_IMG)
elif number == 4:
TILE_WATER.grid(row = x, column = y,)
TILE_WATER.create_image(16,16, image = TILE_WATER_IMG)
if x == 5:
x = 0
y = y + 1
else:
x = x + 1
win.mainloop()
The code defining my canvases looks like this
They're the same size as the images i want to draw on them.
TILE_FLOWER = Canvas(win, height = 80, width = 80)
TILE_GRASS = Canvas(win, height = 80, width = 80)
TILE_STONE = Canvas(win, height = 80, width = 80)
TILE_WATER = Canvas(win, height = 80, width = 80)
And lastly the code defining the images looks like this
TILE_FLOWER_IMG = PhotoImage(file = 'Path1.png')
TILE_GRASS_IMG = PhotoImage(file = 'Path2.png')
TILE_STONE_IMG = PhotoImage(file = 'Path3.png')
TILE_WATER_IMG = PhotoImage(file = 'Path4.png')
Hope what i've written makes sense!
And i would be super thankful if someone could help me fix this mess :)

You need to create new Label or Canvas in each cell of the 5x5 grid:
import tkinter as tk
import random
root = tk.Tk()
imagelist = [
tk.PhotoImage(file='Path1.png'),
tk.PhotoImage(file='Path2.png'),
tk.PhotoImage(file='Path3.png'),
tk.PhotoImage(file='Path4.png'),
]
for i in range(25):
image = random.choice(imagelist)
tk.Label(root, image=image, width=80, height=80, bg='white').grid(row=i//5, column=i%5)
'''
# or using Canvas
canvas = tk.Canvas(root, width=80, height=80, bg='white')
canvas.create_image(40, 40, image=image)
canvas.grid(row=i//5, column=i%5)
'''
root.mainloop()
Or using single Canvas:
canvas = tk.Canvas(root, width=400, height=400, bg='white')
canvas.pack()
for i in range(25):
image = random.choice(imagelist)
row = i // 5
col = i % 5
canvas.create_image(40+col*80, 40+row*80, image=image)

Related

Cannot get initial coordinate to work on TkInter Canvas

Subsequent to an earlier question (Scrolling canvas doesn't scroll) that I eventually managed to answer myself, I have hit upon another issue. I am drawing rectangles and placing images on a canvas. Everything works ok, except that the initial values for the x and y position seem to be ignored. It almost as if the canvas doesn't exist at that point until something has been drawn on i suspect I am misunderstanding how the canvas works.
Id be very grateful if someone could point out what i am doing wrong.
Everything gets drawn as it should but the origin of the whole thing seems to be at 0,0 not 95, 100 as specified.
thanks
class MapElements(Canvas):
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.scroll_y = CTkScrollbar(master, orientation="vertical", command=self.yview)
self.scroll_y.grid(row=0, column=1, sticky="ns")
self.configure(yscrollcommand=self.scroll_y.set)
files = os.listdir(os.path.join(os.getcwd(), "uf_split", "uf_terrain"))
files.sort()
margin_x = 95
x = margin_x
y = 100
self.images = []
self.buttons = []
for i in range(0, len(files)):
self.create_rectangle(x, y, x + 50, y + 50, fill=None, outline="BLACK")
image = Image.open(
os.path.join(os.getcwd(), "uf_split", "uf_terrain", files[i])
)
self.images.append(ImageTk.PhotoImage(image))
self.create_image(x + 1, y + 1, image=self.images[i], anchor="nw")
x += 50
if x >= (50 * 8) + margin_x:
y += 50
x = margin_x
self.configure(scrollregion=self.bbox("all"))

Tkinter canvas image transition

I want to have some transition to show the images in the canvas, I'm using Tkinter and I'm looping through the images to show them on the canvas but I need to have some transition while switching among the images.
I'm using canvasName.create_image method for showing the images. Need a way to show them smoothly.
Here is my code:
def Multi_view_rotate():
window.geometry(str(scr_w)+"x"+str(scr_h)+"+0+0")
z_out = 20
global timeSleep
timeSleepVal = int(timeSleep.get())
global footerPath
footerPath = footerPath.get()
#geting director from entry boxes
global portDirEntry
portDirEntry = portDirEntry.get()
global colorEntry
bgcolor = colorEntry.get()
allPaths = getPaths(portDirEntry)
#directory = r"C:\Users\DotNet\Desktop\Ragazinana Data reduced\diashow\4 Random\Landschaft"
#Get paths
pathsPrt = allPaths[0]
pathsLand = allPaths[1]
#read the image
#call the function to get the picture object with new size
global numOfImagesPort
global numOfImagesLand
#footer path
#footerPath = "C:/Users/DotNet/Desktop/Ragazinana Data reduced/diashow/ragaziana_s.jpg"
#Footer will take 8% of the screen width
per_w_footer = cal_per_num(8, scr_w)
# Footer Image operations
canvasFoot = Canvas(window,width=per_w_footer, height=scr_h, bg=bgcolor, highlightthickness=1, highlightbackground=bgcolor)
canvasFoot.grid(row=0, column=0)
#footerImg = get_img_fit_size(footerPath, scr_h, per_w_footer, True)
footerImg1 = Image.open(footerPath)
footerImg2 = footerImg1.transpose(Image.ROTATE_270)
footerImg3 = footerImg2.resize((int(per_w_footer),int(scr_h)), Image.ANTIALIAS)
footerImg = ImageTk.PhotoImage(footerImg3)
footer = canvasFoot.create_image(per_w_footer/2,scr_h/2,anchor=CENTER, image=footerImg)
while(numOfImagesPort<=len(pathsPrt)-1 or numOfImagesLand<=len(pathsLand)-1 ):
pathPort = pathsPrt[numOfImagesPort]
#increase the index to get the next file in the next loop
numOfImagesPort=numOfImagesPort+1
#if the next photo is out of bound then assign it to the first index
if(numOfImagesPort >= len(pathsPrt)):# if total is 5 pic, 1st loop 0 > 6 /reset the loop
numOfImagesPort=0
# each image will take as following in percentage
per_w_imgs_portriate = cal_per_num(42, scr_w)
per_w_imgs_landscape= cal_per_num(50, scr_w)
#Create the canvases
canvasPort = Canvas(window,width=per_w_imgs_portriate, height=scr_h, bg=bgcolor, highlightthickness=10, highlightbackground=bgcolor)
#gird plays the canvas without it the canvas will not work
canvasPort.grid(row=0, column=1)
#in order to make the picture fit in the rotated state in the half of the screen
# we make the get_img_fit_size adjust it to us to that size by providing
# screen hight as a width and half of the screen with as a height
imgPort = get_img_fit_size(pathPort, scr_h, per_w_imgs_landscape, True)
portImgCanvas = canvasPort.create_image(int(scr_w/4.3),int(scr_h/2),anchor=CENTER, image=imgPort)**
window.update()
time.sleep(timeSleepVal/2)
# Landscape image
pathLand = pathsLand[numOfImagesLand]
numOfImagesLand = numOfImagesLand+1
if(numOfImagesLand >= len(pathsLand)):
numOfImagesLand=0
canvasLand = Canvas(window,width=per_w_imgs_landscape, height=scr_h, bg=bgcolor, highlightthickness=10, highlightbackground=bgcolor)
canvasLand.grid(row=0, column=2)
imgLand = get_img_fit_size(pathLand, scr_h, per_w_imgs_portriate, True)
landImgCanvas = canvasLand.create_image(int(scr_w/4.5),int(scr_h/2),anchor=CENTER, image=imgLand)
window.update()
time.sleep(timeSleepVal/2)
window.mainloop()
I don't think there is something like this built into Tkinter.PhotoImage, but you could manually create a "fade" transition by randomly selecting pixels and setting them to the color values of the next image:
import tkinter, random
root = tkinter.Tk()
c = tkinter.Canvas(root, width=800, height=400)
c.pack()
img_a = tkinter.PhotoImage(file="a.gif")
img_b = tkinter.PhotoImage(file="b.gif")
i = c.create_image(0, 0, image=img_a, anchor="nw")
pixels = [(x, y) for x in range(img_a.width()) for y in range(img_a.height())]
random.shuffle(pixels)
def fade(n=1000):
global pixels, i
for _ in range(min(n, len(pixels))):
x, y = pixels.pop()
col = "#%02x%02x%02x" % img_b.get(x,y)
img_a.put(col, (x, y))
c.delete(i)
i = c.create_image(0, 0, image=img_a, anchor="nw")
if pixels:
c.after(1, fade)
fade()
root.mainloop()
This is slow, though. The after with 1 ms is only to keep the UI from freezing (don't use while with time.sleep in Tkinter!). For a smoother transition, instead of replacing pixel values you might gradually shift all pixels towards the values in the next image, but that will be even slower since you'd change all pixels in each step.
Instead of pure tkinter, we can try it wit PIL and numpy, but it is not noticeably faster, and least not the way I did it:
import numpy as np
from PIL import Image, ImageTk
from itertools import islice
...
arr_a = np.array(Image.open("a.gif").convert("RGB"))
arr_b = np.array(Image.open("b.gif").convert("RGB"))
img = ImageTk.PhotoImage(Image.fromarray(arr_a, mode="RGB"))
i = c.create_image(0, 0, image=img, anchor="nw")
h, w, _ = arr_a.shape
pixels = [(x, y) for x in range(w) for y in range(h)]
random.shuffle(pixels)
def fade(k=0, n=1000):
global i, img
X, Y = zip(*islice(pixels, k, k+n))
arr_a[Y,X] = arr_b[Y,X]
c.delete(i)
img = ImageTk.PhotoImage(Image.fromarray(arr_a, mode="RGB"))
i = c.create_image(0, 0, image=img, anchor="nw")
if k + n < w * h:
c.after(1, fade, k+n, n)
fade()
root.mainloop()
However, this also allows us to replace entire lines at once. The effect is not quite as nice, but it is much faster (also note changed n and if condition).
...
h, w, _ = arr_a.shape
lines = list(range(h))
random.shuffle(lines)
def fade(k=0, n=10):
global i, img
Y = lines[k:k+n]
arr_a[Y] = arr_b[Y]
...
if k + n < h:
c.after(1, fade, k+n, n)
...
This can also easily be transformed to a vertical or horizontal slide transition by simply not shuffling the lines (for columns, use arr_a[:,X] = arr_b[:,X]).

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: Moving objects to random places using a for loop

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.

How to have different actions happen every second and then repeat every four seconds

My title may be kind of confusing, but I essentially want to draw a different set of shapes every four seconds then start over. For example, second one I want to draw triangles, second two I want to draw lines, second three I want to draws rectangles, and second four I want to draw ovals. The for second five I want this process to repeat. My Idea was to use the remainder which works up to second seven. Also is this possible to do without importing any additional modules. Any help would be great and if something was not clear please ask.
Thanks,
Scott
Here is my current code to run if you want:
from tkinter import *
from random import *
#creates a class
class mainWindow():
def __init__(self,theWindow,winName):
#Sets values for variable
self.running = 0
#makes theWindow and instnace var
self.theWindow = theWindow
#creates the main frames
self.mainFrame = Frame(theWindow,width=600,height=200,bg="light gray")
self.secondframe = Frame(theWindow,width=600, height =287, bg = "green")
self.mainFrame.grid()
self.secondframe.grid()
# gives the window a title
theWindow.title(winName)
# do not resize window based on widgets
self.mainFrame.grid_propagate(False)
# window location
theWindow.geometry('+500+300')
#Makes it so the window wont resize
self.theWindow.resizable(0,0)
#
self.theWindow.wm_attributes("-topmost",1)
`enter code here`#Creates the three frames that will be used
#Inerts a blank label to help with formating
self.blank1 = Label(self.mainFrame, bg= "light gray", height =3, width=19)
self.blank1.grid(row=0, column=0, rowspan =1)
#Creates and places the start button
self.startbutton = Label(self.mainFrame,text = "Start", bg= "green", height =1, width=10,relief=RAISED)
self.startbutton.bind("<Button-1>", self.startTimer)
self.startbutton.grid(row=0, column=2, rowspan =1, sticky = E)
#Creates and places the stop button
self.stopbutton = Label(self.mainFrame,text = "Stop", bg= "red", height =1, width=10,relief=RAISED)
self.stopbutton.bind("<Button-1>", self.endTimer)
self.stopbutton.grid(row=0, column=3, rowspan =1, sticky =W)
#Creates and places the timer
self.timerLabel = Label(self.mainFrame,text = "Time: 0", bg= "white", height =2, width=14,relief=SUNKEN)
self.timerLabel.bind("<Button-1>",)
self.timerLabel.grid(row=3, column=2, columnspan =2, pady =25)
#draws the canvas
self.drawCanvas = Canvas(self.secondframe,width=598,height=285, bg= "light green")
self.drawCanvas.grid()
#Function for strting the timer
def startTimer(self,event):
if self.running == 0:
self.running = 1
self.countElapse(0)
#function for ening the timer
def endTimer(self,event):
if self.running == 1:
self.running = 0
self.timecount = 0
self.drawCanvas.delete(ALL)
self.timerLabel.configure(text = "Time: %d" %0)
#function for showing the time
def countElapse(self,localTime = NONE):
#self.timerLabel.configure()
if self.running:
if localTime is not NONE:
self.timecount = localTime
self.timerLabel.configure(text ="Time: "+str(self.timecount+1))
self.timecount = self.timecount+1
self.drawshapes(self.timecount)
self.mainFrame.after(1000,self.countElapse)
#function for drawing the shpaes
def drawshapes(self,timecount):
self.drawCanvas.delete(ALL)
color = ["red","white","purple","green","lime green", "cyan", "black","light blue","blue","pink","brown","gray"]
numshapes = 100
counter = 0
print(self.timecount)
var = self.timecount
#draws ovals
if var % 4 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
#draws the shape
self.drawCanvas.create_oval(x,y,x1,y1,fill=choice(color))
#draws rectangles
elif var % 3 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
self.drawCanvas.create_rectangle(x,y,x1,y1,fill= choice(color))
#draws Lines
elif var % 2 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
self.drawCanvas.create_line(x,y,x1,y1,fill= choice(color))
#draws triangles
elif var % 1 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
x2 = randint(10,600)
y2 = randint(10,600)
self.drawCanvas.create_polygon(x,y,x1,y1,x2,y2,fill= choice(color))
def main():
# create a tkinter object
mainWin = Tk()
# create a mainWindow object
theMainWin = mainWindow(mainWin,"Scott Rodgers")
# keep the window displaying
mainWin.mainloop()
#calls main
main()
What you are looking for is how to use the .after method.
Here is how it works.
Lbl = Label()
Lbl.pack()
Lbl.after([Amount of milliseconds], lamba: [Function])
For more advanced notation,
after(delay_ms, callback=None, *args)
What this does is that it registers an alarm callback that is called after a given time

Categories

Resources