如何在 Tkinter 中显示超长图像? (如何绕过 canvas 最大限制)
How do I display an extremly long image in Tkinter? (how to get around canvas max limit)
我尝试了多种使用 tkinter 显示大图像的方法really long image 无论我尝试了什么,似乎都没有任何代码有效。主要问题是 Canvas 的最大高度限制约为 30,000 像素。
有没有办法显示整个图像?增加或绕过 canvas 限制?请参阅下面的示例图片。
没有办法绕过 canvas 的大小限制,除非修改和重新编译底层 tk 代码。这可能不是一项简单的任务。
假设您正尝试在典型的计算机屏幕上显示图像,仍然有查看图像的方法。基本上它归结为只加载用户在任何时候可以看到的图像部分。
例如,世界图像比 64k x 64k 大得多,但 google 地图可以让您随心所欲地滚动。它通过将地图显示为一系列图块来实现这一点。当您在图像中四处移动时,屏幕外的图块将被丢弃并加载新的图块。
同样的技术可以用在 tkinter 中,甚至可以用滚动条代替拖动动作。您只需要将滚动条连接到一个函数而不是直接连接到 canvas。然后,当函数被调用时,它可以计算出用户正在查看图像的哪一部分,并将其加载到内存中。
这是一个相当不吸引人的答案,但不失为一个答案。这会将非常长的图像分成 1000 像素长度的 "tiles"。它不划分宽度。我已经将来自多个来源的代码拼接在一起,直到一切正常。如果有人可以用滚动条功能来做这个,那就太棒了。
from tkinter import *
from PIL import ImageTk as itk
from PIL import Image
import math
import numpy as np
Image.MAX_IMAGE_PIXELS = None #prevents the "photo bomb" warning from popping up. Have to have this for really large images.
#----------------------------------------------------------------------
# makes a simple window with a button right in the middle that let's you go "down" an image.
class MainWindow():
#----------------
def __init__(self, main):
# canvas for image
_, th, tw, rows, cols = self.getrowsandcols()
self.canvas = Canvas(main, width=tw, height=th)
#self.canvas.grid(row=0, column=0)
self.canvas.pack()
# images
self.my_images = self.cropimages() # crop the really large image down into several smaller images and append to this list
self.my_image_number = 0 #
# set first image on canvas
self.image_on_canvas = self.canvas.create_image(0, 0, anchor = NW, image = self.my_images[self.my_image_number])
# button to change image
self.upbutton = Button(main, text="UP", command=self.onUpButton)
self.downbutton = Button(main, text="DOWN", command=self.onDownButton)
self.upbutton.pack()
self.downbutton.pack()
#self.downbutton.grid(row=1, column=0)
#self.upbutton.grid(row=1, column=0)
#----------------
def getimage(self):
im = Image.open("Test_3.png") # import the image
im = im.convert("RGBA") # convert the image to color including the alpha channel (which is the transparency best I understand)
width, height = im.size # get the width and height
return width, height, im # return relevent variables/objects
def getrowsandcols(self):
width, height, im = self.getimage()
im = np.asarray(im) # Convert image to Numpy Array
tw = width # Tile width will equal the width of the image
th = int(math.ceil(height / 100)) # Tile height
rows = int(math.ceil(height / th)) # Number of tiles/row
cols = int(math.ceil(width / tw)) # Number of tiles/column
return im, th, tw, rows, cols #return selected variables
def cropimages(self):
self.my_images = [] # initialize list to hold Tkinter "PhotoImage objects"
im, th, tw, rows, cols = self.getrowsandcols() # pull in needed variables to crop the really long image
for r in range(rows): # loop row by row to crop all of the image
crop_im =im[r * th:((r * th) + th), 0:tw] # crop the image for the current row (r). (th) stands for tile height.
crop_im = Image.fromarray(crop_im) # convert the image from an Numpy Array to a PIL image.
crop_im = itk.PhotoImage(crop_im) # convert the PIL image to a Tkinter Photo Object (whatever that is)
self.my_images.append(crop_im) # Append the photo object to the list
crop_im = None
return self.my_images
def onUpButton(self):
# next image
if self.my_image_number == 0:
self.my_image_number = len(self.my_images)-1
else:
self.my_image_number -= 1 # every button pressed will
# change image
self.canvas.itemconfig(self.image_on_canvas, image=self.my_images[self.my_image_number]) # attaches the image from the image list to the canvas
def onDownButton(self):
# next image
self.my_image_number += 1 #every button pressed will
# return to first image
if self.my_image_number == len(self.my_images):
self.my_image_number = 0
# change image
self.canvas.itemconfig(self.image_on_canvas, image = self.my_images[self.my_image_number]) #attaches the image from the image list to the canvas
#----------------------------------------------------------------------
root = Tk()
MainWindow(root)
root.mainloop()
我尝试了多种使用 tkinter 显示大图像的方法really long image 无论我尝试了什么,似乎都没有任何代码有效。主要问题是 Canvas 的最大高度限制约为 30,000 像素。
有没有办法显示整个图像?增加或绕过 canvas 限制?请参阅下面的示例图片。
没有办法绕过 canvas 的大小限制,除非修改和重新编译底层 tk 代码。这可能不是一项简单的任务。
假设您正尝试在典型的计算机屏幕上显示图像,仍然有查看图像的方法。基本上它归结为只加载用户在任何时候可以看到的图像部分。
例如,世界图像比 64k x 64k 大得多,但 google 地图可以让您随心所欲地滚动。它通过将地图显示为一系列图块来实现这一点。当您在图像中四处移动时,屏幕外的图块将被丢弃并加载新的图块。
同样的技术可以用在 tkinter 中,甚至可以用滚动条代替拖动动作。您只需要将滚动条连接到一个函数而不是直接连接到 canvas。然后,当函数被调用时,它可以计算出用户正在查看图像的哪一部分,并将其加载到内存中。
这是一个相当不吸引人的答案,但不失为一个答案。这会将非常长的图像分成 1000 像素长度的 "tiles"。它不划分宽度。我已经将来自多个来源的代码拼接在一起,直到一切正常。如果有人可以用滚动条功能来做这个,那就太棒了。
from tkinter import *
from PIL import ImageTk as itk
from PIL import Image
import math
import numpy as np
Image.MAX_IMAGE_PIXELS = None #prevents the "photo bomb" warning from popping up. Have to have this for really large images.
#----------------------------------------------------------------------
# makes a simple window with a button right in the middle that let's you go "down" an image.
class MainWindow():
#----------------
def __init__(self, main):
# canvas for image
_, th, tw, rows, cols = self.getrowsandcols()
self.canvas = Canvas(main, width=tw, height=th)
#self.canvas.grid(row=0, column=0)
self.canvas.pack()
# images
self.my_images = self.cropimages() # crop the really large image down into several smaller images and append to this list
self.my_image_number = 0 #
# set first image on canvas
self.image_on_canvas = self.canvas.create_image(0, 0, anchor = NW, image = self.my_images[self.my_image_number])
# button to change image
self.upbutton = Button(main, text="UP", command=self.onUpButton)
self.downbutton = Button(main, text="DOWN", command=self.onDownButton)
self.upbutton.pack()
self.downbutton.pack()
#self.downbutton.grid(row=1, column=0)
#self.upbutton.grid(row=1, column=0)
#----------------
def getimage(self):
im = Image.open("Test_3.png") # import the image
im = im.convert("RGBA") # convert the image to color including the alpha channel (which is the transparency best I understand)
width, height = im.size # get the width and height
return width, height, im # return relevent variables/objects
def getrowsandcols(self):
width, height, im = self.getimage()
im = np.asarray(im) # Convert image to Numpy Array
tw = width # Tile width will equal the width of the image
th = int(math.ceil(height / 100)) # Tile height
rows = int(math.ceil(height / th)) # Number of tiles/row
cols = int(math.ceil(width / tw)) # Number of tiles/column
return im, th, tw, rows, cols #return selected variables
def cropimages(self):
self.my_images = [] # initialize list to hold Tkinter "PhotoImage objects"
im, th, tw, rows, cols = self.getrowsandcols() # pull in needed variables to crop the really long image
for r in range(rows): # loop row by row to crop all of the image
crop_im =im[r * th:((r * th) + th), 0:tw] # crop the image for the current row (r). (th) stands for tile height.
crop_im = Image.fromarray(crop_im) # convert the image from an Numpy Array to a PIL image.
crop_im = itk.PhotoImage(crop_im) # convert the PIL image to a Tkinter Photo Object (whatever that is)
self.my_images.append(crop_im) # Append the photo object to the list
crop_im = None
return self.my_images
def onUpButton(self):
# next image
if self.my_image_number == 0:
self.my_image_number = len(self.my_images)-1
else:
self.my_image_number -= 1 # every button pressed will
# change image
self.canvas.itemconfig(self.image_on_canvas, image=self.my_images[self.my_image_number]) # attaches the image from the image list to the canvas
def onDownButton(self):
# next image
self.my_image_number += 1 #every button pressed will
# return to first image
if self.my_image_number == len(self.my_images):
self.my_image_number = 0
# change image
self.canvas.itemconfig(self.image_on_canvas, image = self.my_images[self.my_image_number]) #attaches the image from the image list to the canvas
#----------------------------------------------------------------------
root = Tk()
MainWindow(root)
root.mainloop()