How do I display an extremly long image in Tkinter? (how to get around canvas max limit)

陌路散爱 提交于 2021-02-11 12:33:36

问题


I've tried multiple ways of displaying large images with tkinterreally long image No matter what I've tried, there doesn't seem to be any code that works. The main issue is that Canvas has a maximum height limit of around 30,000 pixels.

Is there a way to display this whole image? increase, or get around the canvas limit? See the example image below.


回答1:


There is no way around the size limit of the canvas, short of modifying and recompiling the underlying tk code. This would likely not be a trivial task.

Assuming you are trying to display the image on a typical computer screen, there are still ways to view the image. Basically it boils down to only loading the part of the image that the user can see at any one time.

For example, an image of the world is considerably larger than 64k by 64k, yet google maps is able to let you scroll around all you want. It does this by displaying the map as a series of tiles. As you move around the image, off-screen tiles are thrown away and new tiles are loaded.

This same technique can be used in tkinter, and can even be used with scrollbars instead of a dragging motion. You just need to hook the scrollbars up to a function rather than directly to the canvas. Then, when the function is called, it can compute which part of the image that the user is looking at, and load it into memory.




回答2:


This is a rather unattractive answer, but an answer non-the-less. This divides up extremely long images into "tiles" of 1000 pixel lengths. It does not divide the width. I've spliced together code from several sources until I got it all to work. If someone could make this with a scroll-bar functionality, that would be cool.

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


来源:https://stackoverflow.com/questions/50263155/how-do-i-display-an-extremly-long-image-in-tkinter-how-to-get-around-canvas-ma

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!