AttributeError: 'PhotoImage' object has no attribute 'resize'

Merhaba iyi geceler, yeniden boyutlandırma da problem yaşıyorum sorunu nasıl çözebilirim ? Yardımcı olursanız sevinirim.

import random
import tkinter as tk
from PIL import ImageTk, Image


def get_random_string(length):
    sample_letters = 'abcdefghi'
    result_str = ''.join((random.choice(sample_letters) for i in range(length)))
    print("String:", result_str)
    return result_str


def size_change(name):
    name.resize((20,20))


def img_creator(file):
    string = get_random_string(length=10)
    globals()[string] = ImageTk.PhotoImage(Image.open(file))
    canvas.create_image(0, 0, image=globals()[string], anchor="nw")
    canvas.bind("<Button-1>",lambda event: size_change(globals()[string]))
#

root = tk.Tk()
root.geometry("400x400")
canvas = tk.Canvas(master=root,width=500,height=500,bg="green")
canvas.pack()

img_creator(file="resimler\\arkaplan.png")
img_creator(file="resimler\\kapat.png")


root.mainloop()

Hatayı ImageTk.PhotoImage'in resize fonksiyonu olmadığı için alıyorsunuz. Image.open(file) nesnesinin resize fonksiyonu var.

Sizinle paylaşılan kodu okuyarak hangi satırda ne var anlayarak yazmanız lazım. Aldığınız hatayı neden aldığınızı fark etmiyor olmanız, yazılan kodu gerçekte anlamadığınız anlamına geliyor. O yüzden daha fazla ileri gitmeden önce şu ana kadar geldiğiniz yere kadar olan kısımları sindirmeye çalışın bence.

1 Beğeni

ImageTk kütüphanesi tam anlamıyla bilmiyorum konuyu açtıktan sonra araştırıp sonucu buldum.

import random
import tkinter as tk
from PIL import ImageTk, Image


def get_random_string(length):
    sample_letters = 'abcdefghi'
    result_str = ''.join((random.choice(sample_letters) for i in range(length)))
    print("String:", result_str)
    return result_str


def image_change(size,tag,file):
    globals()[tag] = ImageTk.PhotoImage(Image.open(file).resize((size,size)))
    canvas.itemconfig(tag, image=globals()[tag])



def img_creator(file):
    string = get_random_string(length=10)
    globals()[string] = ImageTk.PhotoImage(Image.open(file))
    canvas.create_image(0, 0, image=globals()[string], anchor="nw",tag=globals()[string])
    for seq in ["<Button-1>"]:
        canvas.tag_bind(
            tagOrId=globals()[string],
            sequence=seq,
            func= lambda event: image_change(tag=globals()[string],size=30,file=file))

#

root = tk.Tk()
root.geometry("400x400")
canvas = tk.Canvas(master=root,width=500,height=500,bg="green")
canvas.pack()

img_creator(file="resimler\\arkaplan.png")
img_creator(file="resimler\\kapat.png")


root.mainloop()

Aslında random string üretme bölümünü de kaldırıp daha da kısalttım

import random
import tkinter as tk
from PIL import ImageTk, Image



def image_change(size,tag,file):
    globals()[tag] = ImageTk.PhotoImage(Image.open(file).resize((size,size)))
    canvas.itemconfig(tag, image=globals()[tag])

def img_creator(file):
    globals()[file] = ImageTk.PhotoImage(Image.open(file))
    canvas.create_image(0, 0, image=globals()[file], anchor="nw",tag=globals()[file])
    for seq in ["<Button-1>"]:
        canvas.tag_bind(
            tagOrId=globals()[file],
            sequence=seq,
            func= lambda event: image_change(tag=globals()[file],size=30,file=file))

root = tk.Tk()
root.geometry("400x400")
canvas = tk.Canvas(master=root,width=500,height=500,bg="green")
canvas.pack()

img_creator(file="resimler\\arkaplan.png")
img_creator(file="resimler\\kapat.png")


root.mainloop()

Bakın yukarıdaki koddan vazgeçin bence. Aşağıdaki kodu bir inceleyin.

import tkinter as tk
from PIL import ImageTk, Image


class PhotoImage:
    def __init__(self, master, image, container, coordinates, bind=False):
        self.master = master
        self._image = Image.open(image)
        self.image = self._image
        self.photo_image = ImageTk.PhotoImage(self.image)
        container += [self.photo_image]
        self.master.create_image(
            coordinates,
            image=self.photo_image,
            tag=image
        )
        if bind:
            self.master.tag_bind(
                tagOrId=image,
                sequence="<ButtonPress-1>",
                func=lambda event: self.change_image_size(
                    tag=image,
                    size=tuple(i // 2 for i in self._image.size)
                )
            )
            self.master.tag_bind(
                tagOrId=image,
                sequence="<ButtonRelease-1>",
                func=lambda event: self.change_image_size(
                    tag=image
                )
            )

    def change_image_size(self, tag, size=()):
        if not size:
            size = self._image.size
        self.image = self._image.resize(size)
        self.photo_image = ImageTk.PhotoImage(self.image)
        self.master.itemconfigure(
            tagOrId=tag,
            image=self.photo_image
        )


def main():
    root = tk.Tk()
    canvas = tk.Canvas(master=root)
    canvas.pack(expand=True, fill="both")
    image_container = []
    PhotoImage(
        master=canvas,
        image="arkaplan.jpeg",
        container=image_container,
        coordinates=(0, 0)
    )
    PhotoImage(
        master=canvas,
        image="test.png",
        container=image_container,
        bind=True,
        coordinates=(50, 50)
    )
    root.mainloop()
    
    
if __name__ == "__main__":
    main()
1 Beğeni

Ama izlenen yol daha kısa değil mi ?

Değil, aynı işlemler yapılıyor. Hatta benim yazdığım sınıf, canvas nesnesinin daha fazla özelleştirilmesini sağlıyor. Özelleştirmekten kastım, resim küçültülecek mi yoksa olduğu gibi mi duracak, bunlara imkan veriyor. Kod kısaltma hadisesini bir zorunluluk gibi görmeyin. Kısaltabiliyorsanız kodu kısaltın ancak neyi kısalttığınızı ve işlemlere olan etkisini de bilin. Yukarıdaki sınıfı kaldırın, isterseniz fonksiyon kullanın, ama istediğiniz gibi çalışması için yazmanız gereken kodu fonksiyonda da yazacaksınız, sınıfta da yazacaksınız. Değişen bir şey olmayacak.

Önermemenizin sebebi global sınıfı kirletmek mi ?

@EkremDincel orada yanlış bir şey söylemedi. Siz rastgele değişkenler oluşturmak isteyince ben de size başka bir yaklaşım daha gösterdim. Normalde global alanda bir liste tanımlayıp, PhotoImage nesnelerini bu listede tutabilirdiniz. Bu örnekte de PhotoImage nesnelerini tutacak bir liste kullanılıyor. Ayrıca ben böyle bir örnek yapmak istesem, fonksiyon yerine sınıf kullanırım. Siz istiyorsanız fonksiyon kullanın. Bir şey diyemiyorum.

Anladım hocam peki sağ olun. Ben kesinlikle kendi bildiğim en doğrudur diyemem, sonuç olarak amatör kümede top koşturuyorum. Projemi zaten bir kaç ay önce tamamlamıştım fakat ben kodları kısaltıp daha iyi bir noktaya nasıl getirebilirim gayreti içerisindeyim ve bunu yaparken de kendi yorumumu katarak geliştirmek eğlenceli oluyor. :slight_smile: ( Verilen örnekler için teşekkürler değerlendireceğim. )

Aslında takıldığım nokta: izlenilen yolun yanlış olmadığı konusunda hem fikir olmamız hatta kullanılabilir olarak isimlendirip sonrasında “Bakın yukarıdaki koddan vazgeçin bence” yorumu oldu.

Ayrıca belirtmek isterim ki fonksiyon yerine class yapısı çok daha mantıklı katılıyorum bu sadece bir ön izleme geliştirmeye açık bir fikir üzerine düşülse çok daha iyi bir temel olabilir.

Bir nesne oluşturmak ve bu nesnelerin parametrelere göre özelleşmesini sağlamak istediğimde, aklıma direk sınıf kullanmak gelir.

Sınıf kullanma sebeplerimi biraz açayım isterseniz.

  1. Sınıf, nesne ile alakalı olan bütün işlemlerin, değişkenlerin sınıfın içerisinde düzgün bir şekilde yer almasını sağlar. Bir bakıma kodun okunurluğunu artırır.

  2. Sınıf, değişkenlere sınıfın her yerinden erişebiliyor olmamızı sağlar. Eğer bir değişkenin değeri değiştirilecekse, değişkeni global hale getirmeden değerini değiştirebilirsiniz.

  3. Sınıf, kendisinden türetilecek sınıflara bir temel sağlar. Bir kaç projemde daha sonra farklılaşacak olan ama belirli ortak yerleri olan bir çok alt sınıfın miras aldığı ebeveyn sınıflar tasarlamıştım. Alt sınıfları farklılaştırmak, onlara yeni özellikler eklemek böylece çok daha kolay ve işlevsel oluyor. Zaten bizim tk widgetlerini miras alan sınıflar tasarlamamızın ana nedenlerinden birisi de sınıfı özelleştirmek.

Bunun haricinde globals()[isim] ile sürekli değişken oluşturmak da aslında gereksiz bir karmaşıklığa neden oluyor.
Yani şunu mu tercih edersiniz?

for i in range(10):
    globals()[f"entry_{i}"] = tk.Entry(master=root)
    globals()[f"entry_{i}"].pack()

Yoksa şunu mu?

entries = []
for i in range(10):
    entry = tk.Entry(master=root)
    entry.pack()
    entries += [entry]

Tamam ilk kısımda 3 satır kod yazdık, ikincisinde 5 satır. Ama bir de bu entrilere ulaşmak söz konusu olduğunda, birinde globals’de entry arayacaksınız, diğerinde entries listesini kullanacaksınız.

Diyelim bu entrilere ulaşmak için şöyle bir fonksiyon yazdınız:

def get_entries():
    return [
        v for k, v in globals().items() 
        if isinstance(v, tk.Entry) and k.startswith("entry")
    ]

Bu fonksiyondan dönen değeri de entries isimli bir değişkene atadınız diyelim. E peki bunca zahmete girmeden, widgetler oluşturulurken entries tanımlansaydı daha az işlem yapılmış olmaz mıydı? Gördüğünüz gibi entrilerin hepsine ulaşmak istediğimiz zaman ikinci bir for döngüsü kurmak, globals()'i yani entries’den daha büyük bir veriyi taramak zorunda kaldık.

3 Beğeni

Peki diyelim ki liste yapısını kullanıyoruz en son attığım örnekten yola çıkarak baktığımızda change_image fonksiyonu içerisine bir önceki resmi listeden silerek yerine değiştirilecek resmi ekleyerek mi ilerlemek doğru olur.

Eğer bir resim değiştirilecek yerine yenisi gelecek ve eski resim bir daha kullanılmayacak ise değiştirme işleminde eski resmi silmek gerekir. Ama bunu yaparken bir koşul da tanımlamalısınız, çünkü zaten listeden silinmiş olan bir değişkeni tekrar silmeye kalkarsanız hata alırsınız. Ama eğer resim değiştikten sonra eski resim tekrar kullanılacaksa silmenin bir anlamı yok. Resimlere ulaşmak için listenin indislerini kullanabilirsiniz. İndis ile uğraşmak istemiyorum, anahtar sözcükler kullanarak resimlere ulaşmak istiyorum diyorsanız, bu kez de sözlük kullanabilirsiniz.

yeniden boyutlandırmak ve 0.5 saniye kadar sonra tekrar eski boyutlarına getirmek için bu dediğiniz şekilde yapabilir miyiz.

Yeniden boyutlandırma için resmi silmenize gerek yok. Paylaştığım kodu bir inceleyin isterseniz.

1 Beğeni

Mesela, resim değiştirmek mi istiyorsunuz, o zaman şöyle bir kod kullanabilirsiniz.

import tkinter as tk
from PIL import ImageTk, Image


class PhotoImage:
    def __init__(
            self,
            master,
            images,
            container,
            coordinates,
            bind=False
    ):
        self.master = master
        self.create_images(images, container)
        self.master.create_image(
            coordinates,
            image=container[images[0]],
            tag=container[images[0]]
        )
        if bind:
            self.bind(container=container, images=images)

    def bind(self, container, images):
        for sequence in ["<ButtonPress-1>", "<ButtonRelease-1>"]:
            self.master.tag_bind(
                tagOrId=container[images[0]],
                sequence=sequence,
                func=lambda event: self.change_image(
                    event=event,
                    tag=container[images[0]],
                    container=container,
                    images=images
                )
            )

    @staticmethod
    def create_images(images, container):
        for image in images:
            img = Image.open(image)
            photo_image = ImageTk.PhotoImage(img)
            container[image] = photo_image

    def change_image(self, event, tag, images, container):
        if event.type.__str__() == "ButtonPress":
            index = -1
        else:
            index = 0
        self.master.itemconfigure(
            tagOrId=tag,
            image=container[images[index]]
        )


def main():
    root = tk.Tk()
    canvas = tk.Canvas(master=root)
    canvas.pack(expand=True, fill="both")
    image_container = {}
    PhotoImage(
        master=canvas,
        images=["arkaplan.jpeg"],
        container=image_container,
        coordinates=(0, 0)
    )
    PhotoImage(
        master=canvas,
        images=["warning.png", "info.png"],
        container=image_container,
        bind=True,
        coordinates=(50, 50)
    )
    root.mainloop()


if __name__ == "__main__":
    main()
1 Beğeni

image bölümünde dizin kullandığımızda tıklama işlemlerini algılamıyor bunu nasıl sağlayabilirim.

Örn: image=r"resimler\başlat_aktif.png"

Ben de algılıyor. Kodda değişiklik yapmış olabilir misiniz acaba?

import tkinter as tk
from PIL import ImageTk, Image


class PhotoImage:
    def __init__(
            self,
            master,
            images,
            container,
            coordinates,
            bind=False
    ):
        self.master = master
        self.create_images(images, container)
        self.master.create_image(
            coordinates,
            image=container[images[0]],
            tag=container[images[0]]
        )
        if bind:
            self.bind(container=container, images=images)

    def bind(self, container, images):
        for sequence in ["<ButtonPress-1>", "<ButtonRelease-1>"]:
            self.master.tag_bind(
                tagOrId=container[images[0]],
                sequence=sequence,
                func=lambda event: self.change_image(
                    event=event,
                    tag=container[images[0]],
                    container=container,
                    images=images
                )
            )

    @staticmethod
    def create_images(images, container):
        for image in images:
            img = Image.open(image)
            photo_image = ImageTk.PhotoImage(img)
            container[image] = photo_image

    def change_image(self, event, tag, images, container):
        if event.type.__str__() == "ButtonPress":
            index = -1
        else:
            index = 0
        self.master.itemconfigure(
            tagOrId=tag,
            image=container[images[index]]
        )


def main():
    root = tk.Tk()
    canvas = tk.Canvas(master=root)
    canvas.pack(expand=True, fill="both")
    image_container = {}
    PhotoImage(
        master=canvas,
        images=["arkaplan.jpeg"],
        container=image_container,
        coordinates=(0, 0)
    )
    PhotoImage(
        master=canvas,
        images=["./resimler/warning.png", "./resimler/info.png"],
        container=image_container,
        bind=True,
        coordinates=(50, 50)
    )
    root.mainloop()


if __name__ == "__main__":
    main()

Ben attığınız ilk örneği deniyordum ve dizin belirttiğimde sonuç alamıyorum.

import tkinter as tk
from PIL import ImageTk, Image


class PhotoImage:
    def __init__(self, master, image, container, coordinates, bind=False):
        self.master = master
        self._image = Image.open(image)
        self.image = self._image
        self.photo_image = ImageTk.PhotoImage(self.image)
        container += [self.photo_image]
        self.master.create_image(
            coordinates,
            image=self.photo_image,
            tag=image
        )
        if bind:
            self.master.tag_bind(
                tagOrId=image,
                sequence="<ButtonPress-1>",
                func=lambda event: self.change_image_size(
                    tag=image,
                    size=tuple(i // 2 for i in self._image.size)
                )
            )
            self.master.tag_bind(
                tagOrId=image,
                sequence="<ButtonRelease-1>",
                func=lambda event: self.change_image_size(
                    tag=image
                )
            )

    def change_image_size(self, tag, size=()):
        if not size:
            size = self._image.size
        self.image = self._image.resize(size)
        self.photo_image = ImageTk.PhotoImage(self.image)
        self.master.itemconfigure(
            tagOrId=tag,
            image=self.photo_image
        )


def main():
    root = tk.Tk()
    canvas = tk.Canvas(master=root)
    canvas.pack(expand=True, fill="both")
    image_container = []
    PhotoImage(
        master=canvas,
        image=r"resimler\arkaplan.png",
        container=image_container,
        coordinates=(0, 0)
    )
    PhotoImage(
        master=canvas,
        image=r"resimler\başlat_aktif.png",
        container=image_container,
        bind=True,
        coordinates=(50, 50)
    )
    root.mainloop()


if __name__ == "__main__":
    main()

Yoo, ben yine sorun almıyorum. Dosya yolunu farklı bir şekilde belirtmeniz gerekiyor olabilir. Ben şu şekilde yazıyorum.

    PhotoImage(
        master=canvas,
        image="./resimler/arkaplan.jpeg",
        container=image_container,
        coordinates=(0, 0)
    )
    PhotoImage(
        master=canvas,
        image="./resimler/info.png",
        container=image_container,
        bind=True,
        coordinates=(50, 50)
    )

image="./resimler/başlat_aktif.png" bu şekilde çalışıyor, image=r"resimler\başlat_aktif.png" ama böyle çalışmıyor :slight_smile: