简体   繁体   English

在 tkinter 中调整 canvas 图像大小的问题

[英]Issues resizing a canvas image in tkinter

I'm creating a custom tkinter Frame class for use in a larger project.我正在创建一个自定义 tkinter 框架 class 用于更大的项目。 I've got it in a usable state now, but it's buggy.我现在把它装在一个可用的 state 中,但它有问题。 When called, the class should create a widget that creates checkbuttons above and to the left of a canvas object with an image item on it.调用时,class 应该创建一个小部件,该小部件在 canvas object 的上方和左侧创建带有图像项的复选按钮。 2 issues I'm having with this:我遇到的2个问题:

1) When first executed, the code creates the entire widget almost as wanted, except the image on the canvas doesn't appear until a resize event occurs, say I grab a side of the window and resize it. 1) 首次执行时,代码几乎按照需要创建整个小部件,除了 canvas 上的图像直到发生调整大小事件才会出现,比如我抓住 window 的一侧并调整它的大小。

No image on canvas canvas 上没有图像

2) Then,when a resize event occurs, the image doesn't always resize exactly right. 2)然后,当发生调整大小事件时,图像并不总是完全正确地调整大小。 Very often, especially if the resize is done with a quick jerk of the cursor, the canvas can end up larger than the image, or the image can be cut off a bit by the edge of the window.很多时候,特别是如果使用 cursor 快速调整大小,canvas 最终会比图像大,或者图像可能会被 Z05B8C74CBD96FBF2DE4C1A3527 的边缘切掉一点。 In the bad resize image I linked below, the canvas is the black peaking from beneath the image.在我下面链接的错误调整大小图像中,canvas 是图像下方的黑色峰值。 Why is the image not matching the canvas in size every time?为什么每次图像大小都与 canvas 不匹配?

Good resize很好的调整大小

Bad resize错误调整大小

Of note, when I use the fullscreen button, the image doesn't resize at all.值得注意的是,当我使用全屏按钮时,图像根本不会调整大小。 When I then use the windowed button, the image resizes to the fullscreen size, resulting in a huge image inside the small window.然后当我使用窗口按钮时,图像会调整为全屏大小,从而在小 window 内产生一个巨大的图像。 The resize event seems to be executing a step behind, leading me to think I'm misusing the event queue somehow, though I'm not sure that's the source of both my issues...调整大小事件似乎落后了一步,导致我认为我以某种方式滥用了事件队列,尽管我不确定这是我的两个问题的根源......

Any ideas?有任何想法吗?

import tkinter as tk
from PIL import Image, ImageTk

KATA_CHART = "../assets/kata_chart.png"
HIRA_CHART = "../assets/hira_chart.png"

class KanaChart(tk.Frame):

    def __init__(self, master, kana_type, **kwargs):

        super().__init__(master, **kwargs)
        self.build_chart(kana_type)

    def _resize_callback(self, *args):        

        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()

        self.img = self.original.resize((width, height))
        self.img = ImageTk.PhotoImage(image=self.img)        
        self.canvas.itemconfig(self.canvas_img, image=self.img)

    def build_chart(self, kana_type):

        vowels = "AIUEO"
        consonants = " KSTNHMYRWNGZDBP"
        let_var_dict = {}
        for letter in vowels + consonants:
            var = tk.BooleanVar()
            let_var_dict[letter] = var

        for i in range(6):
            self.grid_rowconfigure(i, weight=1)
        for i in range(17):
            self.grid_columnconfigure(i, weight=1)

        # build the checkbuttons
        for i in range(5):
            letter = vowels[i]
            row = i + 1
            var = let_var_dict[letter]
            b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                command=self._checkb_wrapper("r", row, var))
            b.grid(row=row, column=0, sticky="nsew")
        for i in range(16):
            letter = consonants[i]
            column = i + 1
            var = let_var_dict[letter]
            b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                command=self._checkb_wrapper("r", column, var))
            b.grid(row=0, column=column, sticky="nsew")

        # build the canvas with the chart on it
        if kana_type == "hira":
            self.original = Image.open(HIRA_CHART)
        elif kana_type == "kata":
            self.original = Image.open(KATA_CHART)
        self.canvas = tk.Canvas(self, bg="black")
        self.canvas.grid(row=1, column=1, rowspan=5, columnspan=16,
            sticky="nsew")
        self.img = ImageTk.PhotoImage(image=self.original)        
        self.canvas_img = self.canvas.create_image(0, 0, image=self.img,
            anchor=tk.NW)
        self._resize_callback(None)
        self.bind("<Configure>", self._resize_callback)

root = tk.Tk()
chart = KanaChart(root, "kata")
chart.pack(fill=tk.BOTH, expand=1)
root.mainloop()

I've considered chopping up the image I'm using into smaller squares, so I'd end up with 80 small images that I could just match to each cell in my widget's grid, but I've assumed one large image is more efficient to resize than 80 small images.我已经考虑将我正在使用的图像分割成更小的方块,所以我最终会得到 80 个小图像,我可以匹配到我的小部件网格中的每个单元格,但我假设一个大图像更有效调整超过 80 个小图像的大小。 Don't think that would circumnavigate the issue I'm having now, anyway.无论如何,不要认为这会绕过我现在遇到的问题。

Since <Configure> event is bound on self (the Frame), the canvas is not yet resized when the callback is executed.由于<Configure>事件绑定在self (帧)上,因此执行回调时 canvas 尚未调整大小。 Adding self.canvas.update() at the beginning of _resize_callback() may solve the issue.self.canvas.update()开头添加 self.canvas.update _resize_callback()可能会解决问题。

It is better to bind <Configure> event on the canvas instead.最好在 canvas 上绑定<Configure>事件。 Then using width and height attributes of the event object:然后使用事件 object 的宽度和高度属性:

def _resize_callback(self, event):
    width, height = event.width, event.height

    self.img = self.original.resize((width, height))
    self.img = ImageTk.PhotoImage(image=self.img)
    self.canvas.itemconfig(self.canvas_img, image=self.img)

Update the binding as well:同时更新绑定:

def build_chart(self, kana_type):
    ...
    #self._resize_callback(None)  # no need to call here
    self.canvas.bind("<Configure>", self._resize_callback)

BTW, there is issue in the following line of your code in the two for loops inside build_chart() :顺便说一句,在build_chart()的两个 for 循环中,以下代码行存在问题:

b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                   command=self._checkb_wrapper("r", row, var))
...
b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                   command=self._checkb_wrapper("r", column, var))

It should be:它应该是:

b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                   command=lambda row=row, var=var: self._checkb_wrapper("r", row, var))
...
b = tk.Checkbutton(self, text=letter, relief=tk.GROOVE, var=var,
                   command=lambda column=column, var=var: self._checkb_wrapper("r", column, var))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM