【问题标题】:How can I increase Tkinter Maximum Canvas size for extremely large images?如何为超大图像增加 Tkinter 最大画布大小?
【发布时间】:2018-05-09 01:17:48
【问题描述】:

我无法显示长度(高度)超过 30612 像素高的图像。我读过画布有最大高度。我想获取源文件并将其扩展到 90 或 100k 像素的高度。相反,我看到有人建议画布可能会被缓冲,如果这是真的,我不知道如何实现它。感谢任何帮助!

我正在使用我在 Stack 中找到的用于处理大图像的代码,它确实可以,但最终达到了 cavas 高度限制。 Canvas Limit

from tkinter import *
from PIL import ImageTk
from PIL import *

Image.MAX_IMAGE_PIXELS = None


class ScrolledCanvas(Frame):
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.master.title("Spectrogram Viewer")
        self.pack(expand=YES, fill=BOTH)
        canv = Canvas(self, relief=SUNKEN)
        canv.config(width=400, height=500)
        # canv.config(scrollregion=(0,0,1000, 1000))
        # canv.configure(scrollregion=canv.bbox('all'))
        canv.config(highlightthickness=0)

        sbarV = Scrollbar(self, orient=VERTICAL)
        sbarH = Scrollbar(self, orient=HORIZONTAL)

        sbarV.config(command=canv.yview)
        sbarH.config(command=canv.xview)

        canv.config(yscrollcommand=sbarV.set)
        canv.config(xscrollcommand=sbarH.set)

        sbarV.pack(side=RIGHT, fill=Y)
        sbarH.pack(side=BOTTOM, fill=X)

        canv.pack(side=LEFT, expand=YES, fill=BOTH)
        self.im = Image.open("Test_3.tif")
        width, height = self.im.size
        canv.config(scrollregion=(0, 0, width, height))
        self.im2 = ImageTk.PhotoImage(self.im)
        self.imgtag = canv.create_image(0, 0, anchor="nw", image=self.im2)
ScrolledCanvas().mainloop()

【问题讨论】:

  • 是否可以使用多个画布,每个画布都包含一个子图像?只是一个想法......
  • 如果可能的话,我愿意尝试一下,看起来好像有人在下面尝试过。

标签: python image tkinter buffer


【解决方案1】:

我试图从画布网格的显示中拼凑出更大的图像。这看起来可能有效,至少如果您只想显示大图像。我刚刚测试了一张小图像,并没有注意内存或速度或任何东西......

from tkinter import *
from scrframe import VerticalScrolledFrame

root = Tk()
tiles = VerticalScrolledFrame(root)    # Scrolled frame
tiles.grid()

tw = 90     # Tile width
th = 110    # Tile height
rows = 4    # Number of tiles/row
cols = 4    # Number of tiles/column

tile_list = []      # List of image tiles
img = PhotoImage(file='pilner.png')

for r in range(rows):
    col_list = []
    for c in range(cols):
        tile = Canvas(tiles.interior, highlightthickness=0, bg='tan1', 
                      width=tw, height=th)
        tile.create_image(-c*tw, -r*th, image=img, anchor ='nw')
        tile.grid(row=r, column=c)
        col_list.append(tile)
    tile_list.append(col_list)

root.mainloop()

现在,滚动框架似乎会引发一些问题,但似乎也有解决方案。我尝试按照Python Tkinter scrollbar for frame 中的描述使用VerticalScrolledFrame,它工作正常。由于它只提供垂直滚动条,您必须自己实现水平滚动条。也许使用鼠标滚轮、键盘快捷键或其他一些附加功能会很有用。

我从TKinter scrollable frame 获得了VerticalScrolledFrame,并针对 Python 3 进行了修改。

【讨论】:

  • 这似乎不起作用。我只能显示“第一个”图像片段。我无法向下滚动。当我尝试添加滚动条时,它会返回错误。不能在同一个类或def中使用.pack和.grid
  • 我使用VerticalScrolledFrame 将我的示例更新为我的确切代码。这适用于 Windows 10 上的 Python 3.6.5。
  • 直到现在才考虑,但VerticalScrolledFrame 使用画布作为小部件的容器。毕竟这可能不适用于大型画布。
  • 只是另一个想法; Label 有同样的尺寸限制吗?
  • 我不知道,但我会调查一下。但是,我愿意尝试修改和重新编译(但我不知道该怎么做......)。我发现另一个人有同样的问题stackoverflow.com/questions/42830037/…
【解决方案2】:

这是我从多个来源获得的代码 - 感谢 figbeam 提供的所有帮助。还有,这不好看!!!!该按钮显示在 Tkinter 窗口的中心。如果你想修改这个,请做。

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)

        # 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.button = Button(main, text="DOWN", command=self.onDownButton)
        self.button.grid(row=0, 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 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()

【讨论】:

    猜你喜欢
    • 2017-08-07
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    • 2019-04-26
    相关资源
    最近更新 更多