When use image in tk Button, Button disappear - python

Here is my code, you can ignore most of them but only see the last part which have #
import tkinter as tk
from PIL import ImageTk, Image
def bresize_and_load(path):
global bwidth, bheight
im = Image.open(path)
bwidth,bheight = im.size
resized = bresizemode(im, bwidth, bheight)
width,height = resized.size
return ImageTk.PhotoImage(resized)
def bresizemode(im, width, height):
if height / width >= ratio:
return im.resize((int(round((width / height) * usable_height)), usable_height),
Image.ANTIALIAS)
if height / width < ratio:
return im.resize((usable_width, (int(round((height / width) * usable_width)))),
Image.ANTIALIAS)
root = tk.Tk()
root.state("zoomed")
root.resizable(width=False, height=False)
frame = tk.Frame(root)
frame.grid(row = 0, column = 0, sticky = 'nsew')
tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
row = 4
column = 5
for ro in range(row):
tk.Grid.rowconfigure(frame, ro, weight=1)
for co in range(column):
tk.Grid.columnconfigure(frame, co, weight=1)
root.update()
f_width = frame.winfo_width()
f_height = frame.winfo_height()
booklistbutton = []
for i in range(row):
for e in range(column):
bbutton = tk.Button(frame, height = int(f_height / row),
width = int(f_width / column))
bbutton.grid(row = i, column = e)
booklistbutton.append(bbutton)
root.update()
usable_width = booklistbutton[0].winfo_width()
usable_height = booklistbutton[0].winfo_height()
ratio = usable_height / usable_width
#here is image path
path = 'sample.jpg'
imm = []
#if it is range(20)(just = row * column) or less than column(here is 5), it work fine
for i in range(20):
imm.append(bresize_and_load(path))
booklistbutton[i].config(image = imm[i])
root.mainloop()
My question is, if you load image in button, but the number of imaged buttons is not less than column or equal row * column, the imaged buttons will disappear.
When range equal row * column(20):
When range is 6:
This is weird for me, does anyone have any idea?
Also, if you do not set button's width and height, they won't disappear. But buttons will little bigger than images.

(Posted solution on behalf of the OP).
I find the problem by myself, the problem is when I set the Buttons' size, it is chr size, but when I load a image, it change to pixel size, and at the same size number, chr size is bigger and bigger than pixel size, so the imaged button become too small to show.

Related

get certain cell in tkinter grid

This will draw a grid with same picture in every field. If I want to input row=x and col=y ex. x=3 y=2 and change picture only for that cell, how to do it?
Also if, I move that picture one or two fields how to get coordinates from cell where is it?
from tkinter import *
from PIL import Image, ImageTk
#main window
root = Tk()
root.title("toys ")
root.geometry("1000x800")
root.iconbitmap('Images/python.ico')
img1_temp = Image.open("Images/Spikes.png")
img1resized= img1_temp.resize((100,100))
img1 = ImageTk.PhotoImage(img1resized)
img2 = ImageTk.PhotoImage(Image.open("Images/Empty.png"))
imgr_temp = Image.open("Images/robot.png")
imgr_resized = imgr_temp.resize((100,100))
img_robot = ImageTk.PhotoImage(imgr_resized)
main_canvas = Canvas(root, width=600, height=600,scrollregion = "0 -200 200 0")
main_canvas.pack(pady=70)
cells = []
for r in range(6):
for c in range(6):
cells.append(Label(main_canvas, bd=5, height=80, width=80,
relief=RIDGE,padx=3, pady=3, image=img2).grid(row=r,column=c))
I tried this, but... not good
for cell in cells:
if type(cell) is Label:
row = 0
column = 0
image = img_robot

Python Tkinter:Frames are not going under each otheer

I was making a simple tkinter GUI which has scrolling frames in it but it is not being so perfect for me.At first I used only one frame, but however I found out that frames have a limit on the no. of widgets they can hold,so I started using multiple frames. However I got this problem:the frames are not going in the direction I wanted.I wanted the frames to go under the previous frame ,however the frames are going one above the other here is a screenshot of the same:
(Frame 0 is the first Frame)
Here is the code(I have only kept the important portion)
i=0
image_no=0
for video in videos:
u = urllib.request.urlopen(video["thumbnail"]["thumbnails"][0]["url"])
raw_data = u.read()
u.close()
im = Image.open(BytesIO(raw_data))
image = ImageTk.PhotoImage(im.resize((470,210)))
a.append(image)
tk.Label(fr[i], image=a[image_no]).pack()
image_no+=1
tk.Label(fr[i], text=("Video:"+str(image_no)+" frame:"+str(i)),wraplength=470,font=("ariel",11,"bold"),bg="white").pack()
tk.Label(fr[i], text=video["title"]["accessibility"]["accessibilityData"]['label'].replace(video["title"]["runs"][0]["text"],""),wraplength=470,font=("ariel",10),bg="white",fg="grey").pack(anchor="w")
canvas.configure(yscrollcommand=scroll_y.set)
canvas.configure(scrollregion=canvas.bbox("all"))
print(image_no)
print(video["title"]["runs"][0]["text"])
if image_no%10==0:
time.sleep(3)
if image_no%110==0:
i+=1
if image_no%440==0:
break
Is there any way I could make it go downwards?
Edit:
Here is the screenshot of the frame limit
the black area is of the canvas
Here is a link to question tkinter maximum canvas size?
I've modified the code due to the possibility that the number of images may be responsible or the canvas may have some maximum height.
This will now fill the entire canvas height with images, each with a 10 point space between.
I've updated it for python 3.x and increased height to 100000!
Works without problems.
Choose your own image (gif or png)
import tkinter as tk
from tkinter import filedialog as fido
root = tk.Tk()
root.rowconfigure(0, weight = 1)
root.columnconfigure(0, weight = 1)
picture = fido.askopenfilename(title = "Pick a pic")
iconimage = tk.PhotoImage(file = picture)
wide, high = iconimage.width(), iconimage.height()
frame = tk.LabelFrame(root, labelanchor = "s", text = "0|0")
frame.grid(row = 0, column = 0, sticky = "nsew")
frame.rowconfigure(0, weight = 1)
frame.columnconfigure(0, weight = 1)
cv = tk.Canvas(
frame, width = 1200, height = 700,
scrollregion = "0 0 2000 100000")
cv.grid(row = 0, column = 0, sticky = "nsew")
vscrollbar = tk.Scrollbar(
frame, orient = "vertical", command = cv.yview)
vscrollbar.grid(row = 0, column = 1, sticky = "ns")
cv.config(yscrollcommand = vscrollbar.set)
def rowcol(ev):
frame["text"] = f"{cv.canvasx(ev.x)} | {cv.canvasy(ev.y)}"
cv.bind("<Motion>", rowcol)
root.update()
testimage = []
for pos in range( 0, 100000 - high - 10, high + 10):
testimage.append(cv.create_image(100, pos, anchor = "nw", image = iconimage))
print(f"Number of images = {len(testimage)}, width = {wide}, height = {high}")
root.mainloop()
So it doesn't seem to be a limitation of Canvas height or number of images displayed.

Resizing and Redrawing Canvas in tkinter

I am currently trying to display images on a canvas. More specifically, I would like to have the images that are drawn on the canvas to be resized based on the size of the window (this way the images are always going to fit on the canvas).
I start off with a simple canvas that fills the entire screen.
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
WIDTH, HEIGHT = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry('%dx%d+0+0' % (WIDTH, HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
I then get set up with my background images and images that will be able to get resized on the background.
backgroundImage = ImageTk.PhotoImage(Image.open("filepath"))
image1 = Image.open("filepath")
image2 = ...
....
....
I then created a method for resizing the images.
"""
This method resizes a image so that all the images fits on the GUI. This first creates an Image object,
but since the Image object does not allow access to the width and height of the Image object, a ImageTk
object needs to be created from the Image object. The ImageTk object cannot resize, but the Image object
can. So using ImageTk object to get the height and width and the Image object to resize, a new Image object
that is resized to fit the GUI is created.
#imageFile- the image file that is being resized
#windowMeasure- the measurement of the window to proportionally resize the image
#useHeight- determine the measurement being proportioned
"""
def resizedImageTk(image, windowMeasure, useHeight):
imageTk = ImageTk.PhotoImage(image)
area = windowMeasure * 4/5
tileSize = area / 4
if useHeight:
proportion = tileSize / imageTk.height()
else:
proportion = tileSize / imageTk.width()
resizedImage = image.resize((int(imageTk.width()*proportion), int(imageTk.height()*proportion)))
resizedImageTk = ImageTk.PhotoImage(resizedImage)
return resizedImageTk
I then use a method for redrawing the resized images when there is a change to the size of the window and bind it to the root. Note: I know that this can be extremely computational and so I have reduced the number of times this occurs
numResizes = 0
def handle_configure(event):
if numResizes % 5 == 0:
geometry = root.geometry()
width = int(geometry[0:geometry.index("x")])
height = int(geometry[geometry.index("x")+1:geometry.index("+")])
canvas.create_image((0,0), image=backgroundImageTk, anchor="nw")
if height < width:
measurement = height
else:
measurement = width
resizedImage1 = resizedImageTk(image1, measurement, height < width)
resizedImage2 = ....
....
....
images = [resizedImage1, resizedImage2, ...]
imageWidth = resizedImage1.width()
imageHeight = resizedImage1.height()
i = 0
for row in range(0, int(len(images) / 4)):
for column in range(0, int(len(images) / 5):
x = imageWidth*column + int(width/2) - imageWidth * 2
y = imageHeight*row + int(height/2) - int(imageHeight*2.5)
canvas.create_image((x, y), image=images[i])
i=i+1
numResizes = numResizes + 1
root.bind("<Configure>", handle_configure)
root.mainloop()
I have run this with my images and have had some success, however, it does not work completely. I have my background image get displayed but my other images do not. I do not know why since when I use the create_line function for the canvas in the nested for loop (where the images are not being shown), I do get the line showing.
If someone could give some advice on this, I would appreciate it!
Thanks
Update:
Here is a simple sample of what I am trying to do. You can use any sample image to test this.
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
WIDTH, HEIGHT = int(root.winfo_screenwidth() * 103/104), int(root.winfo_screenheight() * 11/12)
root.geometry('%dx%d+0+0' % (WIDTH, HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
testImage = Image.open("enter file path here!")
testImageTk = ImageTk.PhotoImage(testImage)
resizedTestImage = None
resizedTestImageTk = None
def handle_configure(event):
geometry = root.geometry()
width = int(geometry[0:geometry.index("x")])
height = int(geometry[geometry.index("x")+1:geometry.index("+")])
useHeight = height < width
if useHeight:
measurement = height
else:
measurement = width
if useHeight:
proportion = measurement / testImageTk.height()
else:
proportion = measurement / testImageTk.width()
resizedTestImage = testImage.resize((int(testImageTk.width()*proportion), int(testImageTk.height()*proportion)))
resizedTestImageTk = ImageTk.PhotoImage(resizedTestImage)
canvas.create_image((0,0), image=resizedTestImageTk, anchor="nw")
print("(image width, image height): (" + str(resizedTestImageTk.width()) + " " + str(resizedTestImageTk.height()) + ")")
root.bind("<Configure>", handle_configure)
root.mainloop()
Your new code has problem with bug in PhotoImage.
Inside handle_configure you assign to local variable resizedTestImageTk even if you already created external/global variable resizedTestImageTk
It works for me if I inform function that it has to use global to inform function that it has to assign it to external variable instead of local one
def handle_configure(event):
global resizedTestImageTk # <-- inform function to assign image to global variable instead of using local one
or I assign local resizedTestImageTk to global class.
canvas.resizedTestImageTk = resizedTestImageTk # <-- assign local `resizedTestImageTk` to global class.
Minimal working code.
I changed some calculations to make it more readable.
import tkinter as tk
from PIL import Image, ImageTk
# --- functions ---
def handle_configure(event):
global resizedTestImageTk # <-- inform function to assign image to global variable instead of using local one
geometry = root.geometry()
window_width = int(geometry[0:geometry.index("x")])
window_height = int(geometry[geometry.index("x")+1:geometry.index("+")])
image_width = testImageTk.height()
image_height = testImageTk.width()
if window_height < window_width:
proportion = window_height / image_height
else:
proportion = window_width / image_width
image_width = int(image_width * proportion)
image_height = int(image_height * proportion)
resizedTestImage = testImage.resize((image_width, image_height))
resizedTestImageTk = ImageTk.PhotoImage(resizedTestImage)
canvas.create_image((0,0), image=resizedTestImageTk, anchor="nw")
#canvas.resizedTestImageTk = resizedTestImageTk # <-- assign local `resizedTestImageTk` to global class.
print(f"(image width, image height): ({image_width} {image_height})")
# --- main ---
root = tk.Tk()
WIDTH = int(root.winfo_screenwidth() * 103/104)
HEIGHT = int(root.winfo_screenheight() * 11/12)
root.geometry('%dx%d+0+0' % (WIDTH, HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
testImage = Image.open("lenna.png")
testImageTk = ImageTk.PhotoImage(testImage)
resizedTestImage = None
resizedTestImageTk = None
root.bind("<Configure>", handle_configure)
root.mainloop()
And image for test
Wikipedia: Lenna
EDIT:
I see other problem - not so visible. You always put new image on but you don't remove old image - so finally it may use more memory.
You could put image on canvas at start - and get its ID
image_id = canvas.create_image((0,0), image=testImageTk, anchor="nw")
and later replace image using ID
canvas.itemconfig(image_id, image=resizedTestImageTk)
Minimale working code:
import tkinter as tk
from PIL import Image, ImageTk
# --- functions ---
def handle_configure(event):
global resizedTestImageTk # <-- inform function to assign image to global variable instead of using local one
geometry = root.geometry()
window_width = int(geometry[0:geometry.index("x")])
window_height = int(geometry[geometry.index("x")+1:geometry.index("+")])
image_width = testImageTk.width()
image_height = testImageTk.height()
if window_height < window_width:
proportion = window_height / image_width
else:
proportion = window_width / image_height
image_width = int(image_width * proportion)
image_height = int(image_height * proportion)
resizedTestImage = testImage.resize((image_width, image_height))
resizedTestImageTk = ImageTk.PhotoImage(resizedTestImage)
canvas.itemconfig(image_id, image=resizedTestImageTk)
#canvas.resizedTestImageTk = resizedTestImageTk # <-- assign local `resizedTestImageTk` to global class.
print(f"(image width, image height): ({image_width} {image_height})")
# --- main ---
image_id = None # default value at start
root = tk.Tk()
WIDTH = int(root.winfo_screenwidth() * 103/104)
HEIGHT = int(root.winfo_screenheight() * 11/12)
root.geometry('%dx%d+0+0' % (WIDTH, HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both", expand=True)
testImage = Image.open("lenna.png")
testImageTk = ImageTk.PhotoImage(testImage)
resizedTestImage = None
resizedTestImageTk = None
image_id = canvas.create_image((0,0), image=testImageTk, anchor="nw")
root.bind("<Configure>", handle_configure)
root.mainloop()

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]).

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()

Categories

Resources