tkinter + pillow. Проблема при изменении размеров окна
При изменении размеров окна иногда случается такое, что параметры аргумента event функции on_resize малы, относительно действительного размера окна, поэтому бывает так, что в функции get_gradient_image (которая вызывается из refresh_image) возникает исключение библиотеки PIL ValueError: Width and height must be >= 0 за счет чего программа сильно "пролагивает" в такие моменты.
Пока я использовал pack и place для менеджмента виджетов все было нормально.
Как это можно исправить?
P.S.: Прошу прощения за огромную часть кода, но я учусь программированию совсем недавно.
import os
import tkinter as tk
from PIL import Image, ImageDraw, ImageColor, ImageTk
DLM = ">"
DEFAULT_SETTINGS = {"index": 0, "width": 800, "height": 450}
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
top = self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
self.settings = self.load_settings()
self.gradients = self.get_gradients("gradients.db", "utf-8")
self.img_gradient = tk.Label(parent, image=None)
self.img_gradient.grid(row=0, column=0, columnspan=2, sticky="nswe")
self.lbl_score = tk.Label(parent, text="")
self.lbl_score.grid(row=1, column=1, sticky="e")
self.lbl_index = tk.Label(parent, text="")
self.lbl_index.grid(row=1, column=0, sticky="w")
parent.bind("<Up>", self.inc_score)
parent.bind("<Down>", self.dec_score)
parent.bind("<Right>", self.inc_index)
parent.bind("<Left>", self.dec_index)
parent.bind("<Configure>", self.on_resize)
parent.protocol("WM_DELETE_WINDOW", self.close_window)
parent.title("Training data generator")
self.refresh_image()
self.refresh_index()
self.refresh_score()
self.lbl_index.update()
def load_settings(self):
settings = dict()
if not os.path.exists("options.cfg"):
self.save_settings(DEFAULT_SETTINGS)
with open("options.cfg", "r", encoding="utf-8") as file:
for line in file:
param, value = [part.strip() for part in line.split(": ")]
if param in ("index", "width", "height"):
value = int(value)
settings[param] = value
return settings
def save_settings(self, settings=None):
if settings is None:
settings = self.settings
with open("options.cfg", "w", encoding="utf-8") as file:
for param, value in settings.items():
print(f"{param}: {value}", file=file)
def get_gradients(self, filepath, encoding):
gradients = list()
with open(filepath, "r", encoding=encoding) as file:
for line in file:
gradient = line.strip().split(DLM)
gradients.append({"from": gradient[0],
"to": gradient[1],
"score": int(gradient[2])})
return gradients
def get_gradient_image(self, clr_from, clr_to):
print(str(self.settings["width"]) + "x" + str(self.settings["height"]))
im_gradient = Image.new("RGB", (self.settings["width"] -4,
self.settings["height"]-25))
draw = ImageDraw.Draw(im_gradient)
clr_from = ImageColor.getrgb(self.gradients[self.settings["index"]]["from"])
clr_to = ImageColor.getrgb(self.gradients[self.settings["index"]]["to"])
for x in range(self.settings["width"]-4):
clr_current = list()
for i in range(3):
lclr = (1 - x/(self.settings["width"]-1))*clr_from[i]
rclr = x/(self.settings["width"]-1)*clr_to[i]
clr_current.append(round(lclr) + round(rclr))
draw.line([(x, 0), (x, self.settings["height"]-1)], fill=tuple(clr_current))
del draw
return im_gradient
def save_gradients(self, filepath, encoding):
with open(filepath, "w", encoding=encoding) as file:
for gradient in self.gradients:
print(f"{gradient['from']}{DLM}{gradient['to']}{DLM}{gradient['score']}", file=file)
def close_window(self):
self.save_gradients("gradients.db", "utf-8")
self.save_settings()
self.parent.destroy()
def on_resize(self, event):
self.settings["width"] = event.width
self.settings["height"] = event.height
self.refresh_image()
def refresh_image(self):
img = ImageTk.PhotoImage(self.get_gradient_image(self.gradients[self.settings["index"]]["from"],
self.gradients[self.settings["index"]]["to"]))
self.img_gradient.configure(image=img)
self.img_gradient.img_ref = img
def refresh_score(self):
text = {-2: "CRAPPY",
-1: "BAD",
0: "NEUTRAL",
1: "GOOD",
2: "BEAUTIFUL"}.get(self.gradients[self.settings["index"]]["score"])
self.lbl_score.config(text=text)
def refresh_index(self):
self.lbl_index.config(text=f"{self.settings['index']+1} / {len(self.gradients)}")
def inc_score(self, event):
if self.gradients[self.settings["index"]]["score"] < 2:
self.gradients[self.settings["index"]]["score"] += 1
self.refresh_score()
def dec_score(self, event):
if self.gradients[self.settings["index"]]["score"] > -2:
self.gradients[self.settings["index"]]["score"] -= 1
self.refresh_score()
def inc_index(self, event):
if self.settings["index"] < len(self.gradients) - 1:
self.settings["index"] += 1
self.refresh_image()
self.refresh_index()
self.refresh_score()
def dec_index(self, event):
if self.settings["index"] > 0:
self.settings["index"] -= 1
self.refresh_image()
self.refresh_index()
self.refresh_score()
def main():
root = tk.Tk()
Application(root)
root.mainloop()
if __name__ == "__main__":
main()