Проблемы изменения размера изображения холста в tkinter - PullRequest
0 голосов
/ 09 апреля 2020

Я создаю собственный класс фрейма tkinter для использования в большом проекте. Я получил его в работоспособном состоянии сейчас, но он глючит. При вызове класс должен создать виджет, который создает флажки над и слева от объекта холста с элементом изображения на нем. 2 проблемы, которые у меня возникают с этим:

1) При первом выполнении код создает весь виджет почти так, как хотел, за исключением того, что изображение на холсте не появляется, пока не произойдет событие изменения размера, скажем, я беру сторона окна и измените его размер.

Нет изображения на холсте

2) Затем, когда происходит событие изменения размера, изображение не всегда изменяется точно точно , Очень часто, особенно если изменение размера выполняется быстрым движением курсора, холст может оказаться больше, чем изображение, или изображение может быть немного обрезано по краю окна. В изображении с плохим изменением размера, которое я привел ниже, холст - это черный пиковый из-под изображения. Почему изображение не соответствует холсту по размеру каждый раз?

Хорошее изменение размера

Плохое изменение размера

Обратите внимание, когда я использую полноэкранную кнопку, изображение не изменяется вообще. Когда я затем использую оконную кнопку, изображение изменяется до полноэкранного размера, в результате чего внутри маленького окна получается огромное изображение. Событие 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 маленьких изображений. Не думайте, что это поможет мне решить проблему, с которой я столкнулся сейчас.

1 Ответ

0 голосов
/ 10 апреля 2020

Поскольку событие <Configure> привязано к self (фрейму), при выполнении обратного вызова холст еще не изменяется. Добавление self.canvas.update() в начале _resize_callback() может решить проблему.

Лучше вместо этого связать событие <Configure> с холстом. Затем, используя атрибуты ширины и высоты объекта события:

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

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