【问题标题】:Issues resizing a canvas image in tkinter在 tkinter 中调整画布图像大小的问题
【发布时间】:2020-04-09 17:56:46
【问题描述】:

我正在创建一个自定义 tkinter Frame 类以用于更大的项目。我现在可以使用它了,但是它有问题。 调用时,该类应创建一个小部件,该小部件在带有图像项的画布对象的上方和左侧创建复选按钮。我遇到了 2 个问题:

1) 首次执行时,代码几乎按照需要创建了整个小部件,除了画布上的图像在调整大小事件发生之前不会出现,比如我抓住窗口的一侧并调整它的大小。

No image on canvas

2) 然后,当发生调整大小事件时,图像并不总是完全正确地调整大小。很多时候,特别是如果通过快速移动光标来调整大小,画布最终可能会比图像大,或者图像可能会被窗口边缘切掉一点。在我下面链接的错误调整大小图像中,画布是图像下方的黑色峰值。为什么每次图片大小都不匹配画布?

Good resize

Bad resize

值得注意的是,当我使用全屏按钮时,图像根本不会调整大小。然后当我使用窗口按钮时,图像会调整为全屏大小,从而在小窗口内产生巨大的图像。调整大小事件似乎落后了一步,导致我认为我以某种方式滥用了事件队列,但我不确定这是否是我的两个问题的根源......

有什么想法吗?

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

我考虑将我正在使用的图像分割成更小的方块,所以我最终会得到 80 个小图像,我可以将它们与我的小部件网格中的每个单元格匹配,但我假设一个大图像调整大小比 80 个小图像更有效。无论如何,不​​要认为这会绕过我现在遇到的问题。

【问题讨论】:

  • 可能是在执行resize回调时画布还没有更新。在_resize_callback()函数的开头添加self.update()。最好将&lt;Configure&gt; 绑定到self.canvas 上,然后使用event.widthevent.height 调整图像大小。

标签: python tkinter tkinter-canvas


【解决方案1】:

由于&lt;Configure&gt; 事件绑定在self(框架)上,执行回调时画布尚未调整大小。在_resize_callback() 开头添加self.canvas.update() 可能会解决问题。

最好在画布上绑定&lt;Configure&gt; 事件。然后使用事件对象的宽高属性:

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)

同时更新绑定:

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

顺便说一句,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))

应该是:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 2021-11-02
    相关资源
    最近更新 更多