tkinter: Mesh distance is innacurate - python

I am trying to draw a simple mesh with tkinter:
from tkinter import *
root=Tk()
root.title('Mesh simulator')
window_identifiers = {}
frame_identifiers = {}
canvas_identifiers = {}
mesh_identifiers = []
window_identifiers["main_window"] = root
SETTINGS_canvas_total_width = 2048
SETTINGS_canvas_total_height = 1536
SETTINGS_canvas_visible_width = 800
SETTINGS_canvas_visible_height = 600
SETTINGS_canvas_color = "black"
SETTINGS_grid_color = "white"
mesh_density = 50
frame=Frame(window_identifiers["main_window"],width=SETTINGS_canvas_visible_width,height=SETTINGS_canvas_visible_height)
frame_identifiers["body_holder"] = frame
frame.grid(row=0,column=0)
canvas=Canvas(frame,bd=-2, bg=SETTINGS_canvas_color,width=SETTINGS_canvas_visible_width,height=SETTINGS_canvas_visible_height,scrollregion=(0,0,SETTINGS_canvas_total_width,SETTINGS_canvas_total_height), highlightthickness=0)
canvas_identifiers["main_canvas"] = canvas
canvas.grid(row=0, column=0)
i = 0
while(i<=SETTINGS_canvas_total_height):
l = canvas_identifiers["main_canvas"].create_line(0, i, SETTINGS_canvas_total_width, i, width=1, fill=SETTINGS_grid_color)
mesh_identifiers.append(l)
i+=mesh_density
i = 0
while(i<=SETTINGS_canvas_total_width):
l = canvas_identifiers["main_canvas"].create_line(i, 0, i, SETTINGS_canvas_total_height, width=1, fill=SETTINGS_grid_color)
mesh_identifiers.append(l)
i+=mesh_density
root.mainloop()
But on the very end, when I measure up the distance between two lines, it seems it is not 50px, but about 62-64px. I don't have a clue what adds those 12 pixels per square. Can anyone explain me the root cause of this please, based on my upper snippet?
EDIT:
Interesting fact, I've just done the measurement on 2 different monitors (laptop and 22" one), and results are interesting. On 22" monitor, everything seems perfectly fine (image 1) while on laptop monitor, there's an offset (image 2)
And since this is not an HTML and web-design issue, I am even more confused now :)

Related

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.

Button takes up full screen no matter what

Hi I am making a tkinter program but have run into this issue.
I am trying to use the grid method to center a load of buttons in the middle of the screen(The amount of buttons will vary dynamically) However I cant keep the buttons to a reasonable size I have followed the advice of someone who had a similar problem however I cant figure out what I am doing differently as it hasn't solved my issue.
Here is my code (it uses a directory pathway so it won't work for the reader but maybe you can spot what I am doing wrong)
ef open_win3():
global third_window
third_window = Toplevel()
third_window.config(height = 1800,width = 1800, bg = "chocolate1")
create_thirdwindow_button()
def create_thirdwindow_button():
bf = Frame(third_window,bg = "blue")
bf.grid(row=0, column=0,sticky="NESW")
bf.rowconfigure(0,weight = 1)
bf.columnconfigure(0,weight =1)
third_window.grid_rowconfigure(0, weight=1)
third_window.grid_columnconfigure(0, weight=1)
count = 0
x = 1
y = 0
mypath = "C:\\Users\\link\\OneDrive\\Desktop\\python stuff\\screenshot example\\Snapshots by catagory"
for g in listdir(mypath):
count += 1
for I in listdir(mypath):
btp = mypath +"\\"+str(I)
print(btp)
screenshot_snap = Button(bf,text = str(I),width = 1,height = 1, bg = "chocolate3",activebackground = "white",padx= 10,pady =10)
screenshot_snap.grid(sticky = "NESW")
screenshot_snap.grid_rowconfigure(0, weight=1)
screenshot_snap.grid_columnconfigure(0, weight=1)
x += 10
if y < count:
y += 1
Thanks a lot for any help!

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 tkinter canvas xview_moveto() does not work properly?

I'm trying to make movement of the canvas automatic, i.e. moving plot along with charts drawn on the canvas, for that I'm using xview_moveto() function, but for some reason it only moves canvas 1 time, the second time view returns to initial view, do not know why, could some one help to understand?
here is part of my code:
enter code here
x_cord = 0
y_cord = 0
x0_cord = 50
y0_cord = 850
x2_cord = 1800
trig_1 = 0
def send_com(com1):
......
........
.........
if var1.find('A54_')>=1:
first = var1.find('A54_')
a_val = var1[first+4: first+8]
alanog_val = int(a_val)*100/1023
barA0['value'] = alanog_val
b_val = int(a_val)/1.204
global x0_cord
global y0_cord
global x2_cord
x_cord = x0_cord + 100
y_cord = 850 - b_val
canv.create_line(x0_cord, y0_cord, x_cord, y_cord, width=2,
smooth = "true", activefill = "blue", fill="red")
#canv.create_text(x_cord, y_cord, text="A0", anchor=tk.SE,
font='bold', activefill = "blue", fill="red")
#print(x0_cord, y0_cord, x_cord, y_cord)
x0_cord = x_cord
y0_cord = y_cord
if x_cord > x2_cord:
canv.config(scrollregion=( x2_cord , 0,1800,0))
canv.create_line(50,850,x2_cord+1800,850,width=2,arrow=LAST)
canv.after(500, canv.xview_moveto,x2_cord)
x2_cord = x2_cord + 1800
The method xview_moveto takes a fraction between zero and one. Anything larger than one will be treated as one. From the canonical tcl/tk documentation:
Adjusts the view in the window so that fraction of the total width of the canvas is off-screen to the left. Fraction must be a fraction between 0 and 1.

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