0

I have a list of 100 images that are displayed in labels and when I press a button in the main window, these images that are displayed in the top-level window, they change to another set of images, so I use the FRAME class to switch to another FRAME while pressing a button. The whole system to change images so that they do not repeat, I have already done it, but repeating a method to resize each image 100 times is not appropriate, I try with a for loop but I do not get it. Please help me I'm stuck

    class Example(Frame):
    def __init__(self, master, array, *args):
        Frame.__init__(self, master, *args)

        self.image = Image.fromarray(array) ####---> ERROR
        self.img_copy = self.image.copy()

        self.background_image = ImageTk.PhotoImage(self.image)

        self.background = Label(self, image=self.background_image)
        self.background.pack(fill='both', expand=True)
        self.background.bind('<Configure>', self._resize_image)

    def _resize_image(self, event):
        new_width = event.width
        new_height = event.height

        self.image = self.img_copy.resize((new_width, new_height))

        self.background_image = ImageTk.PhotoImage(self.image)
        self.background.configure(image=self.background_image)


class Applications(Tk):  
    def __init__(self):
        Tk. __init__(self)  
        self.path = "E:/1-RICHI/MovilesDB"      
        self.Images = self.files(self.path, "ever")   
        self.Images_copy = self.files(self.path, "partial") 

        self.example = Creator() ####---> ERROR
        self.example .pack()
        
    def files(self, path, option): # Generate list

        images = os.listdir(path)

        self.list_partial= []  
        self.list_ever= []   

        if option == "ever":
            for i in images:

                if ".jpg" in i:
                    route= path + "/" + i

                    open = cv2.imread (route)
                    RGB = cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
                    objet = Image.fromarray(RGB)
                    #photo = ImageTk.PhotoImage(objet)

                    self.list_ever.append(objet)

            return self.list_ever

        if option == "partial" :
            for i in images:

                if ".jpg" in i:        
                    route = path + "/" + i   

                    open = cv2.imread(route)
                    RGB = cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
                    objet = Image.fromarray(RGB)

                    self.list_partial.append(objet)

            return self.list_partial  

class Creator(Frame):

    def __init__(self,*args, **kwargs): 
        Frame.__init__(self,*args, **kwargs)

        self.LIST=[]   

        for i in self.master.Images:
            Example(self, i) .place(x=0, y=0, relwidth=1, relheight=1) ####---> ERROR

if __name__=="__main__":
    app_1 = Applications()
    app_1.mainloop()
Benjamin
  • 47
  • 5
  • `ImageTk.PhotoImage()` returns a `PhotoImage`, you are not doing anything with the returned value (in the `resize()` method) – Matiiss Aug 14 '21 at 08:15
  • I am telling you that the `resize()` method currently does absolutely nothing except waste resources also `Creator` class doesn't have `list_copy` attribute, please provide a [mre] – Matiiss Aug 14 '21 at 14:45
  • ok, my question is do you want to resize the images only before putting them on the screen or do you want them to resize whenever you resize the window? – Matiiss Aug 14 '21 at 16:08
  • 1
    Does this answer your question? [Tkinter resize background image to window size](https://stackoverflow.com/questions/24061099/tkinter-resize-background-image-to-window-size) – Matiiss Aug 14 '21 at 17:22
  • Although the publication you are referring to satisfies the need to resize the image in a label as the window changes size, it is not possible to repeat it, eg. 100 times as is my case. I have a list of 100 images that are displayed in labels and as I press a button in the main window, these images that are displayed in the upper window (toplevel) change for another set of images, so I use the FRAME class to change to another FRAME as I press a button. The whole system to change images so that they are not repeated, I have already done it, but repeating a method 100 times is not appropriate – Benjamin Aug 14 '21 at 17:50
  • you get an error don't you? here: `ImageTk.PhotoImage(iter.resize((new_width,new_height)))`: sth around the lines of `Label doesn't have attribute named "resize"` or sth? if you want to resize images you need to save a reference to `Image` object – Matiiss Aug 14 '21 at 18:54
  • Yes, it is the same error that I have and the reference to which it refers is to the image copy list or to ImageTk? – Benjamin Aug 14 '21 at 19:59
  • well first of all you should have put that error in your question, you need a reference list to all the `Image` objects so whenever you use `Image.open()` save the returned value to a list and then use that list in the subclass to resize the image, then I can easier help you too (when you make that list) – Matiiss Aug 14 '21 at 20:03
  • Does the list not work the same: (self.Imagenes and self.Imagenes_copy), ready to be displayed? – Benjamin Aug 14 '21 at 20:21

1 Answers1

1

You can try adding these changes to your Creator class (first just try copy-pasting and see if this works, then if you have questions, ask them):

from tkinter import Tk, Frame, Label
from PIL import Image, ImageTk


class Example(Frame):
    def __init__(self, master, path, *args):
        Frame.__init__(self, master, *args)

        self.image = Image.open(path)
        self.img_copy = self.image.copy()

        self.background_image = ImageTk.PhotoImage(self.image)

        self.background = Label(self, image=self.background_image)
        self.background.pack(fill='both', expand=True)
        self.background.bind('<Configure>', self._resize_image)

    def _resize_image(self, event):
        new_width = event.width
        new_height = event.height

        self.image = self.img_copy.resize((new_width, new_height))

        self.background_image = ImageTk.PhotoImage(self.image)
        self.background.configure(image=self.background_image)


def change_images(index):
    if index >= len(img_lst):
        index = 0
    img_lst[index].lift()
    root.after(1000, change_images, index + 1)


path_lst = ['pause_btn.png', 'play_btn.png', 'space.jpg']  # change/add paths here
img_lst = []

root = Tk()

for path in path_lst:
    e = Example(root, path)
    e.place(x=0, y=0, relwidth=1, relheight=1)
    img_lst.append(e)

change_images(0)

root.mainloop()

In your code (remember to import other stuff too):

from tkinter import Tk, Frame, Label
from PIL import Image, ImageTk


class Example(Frame):
    def __init__(self, master, array, *args):
        Frame.__init__(self, master, *args)

        self.image = Image.fromarray(array)
        self.img_copy = self.image.copy()

        self.background_image = ImageTk.PhotoImage(self.image)

        self.background = Label(self, image=self.background_image)
        self.background.pack(fill='both', expand=True)
        self.background.bind('<Configure>', self._resize_image)

    def _resize_image(self, event):
        new_width = event.width
        new_height = event.height

        self.image = self.img_copy.resize((new_width, new_height))

        self.background_image = ImageTk.PhotoImage(self.image)
        self.background.configure(image=self.background_image)



class Applications(Tk):  
    def __init__(self):
        Tk. __init__(self)  
        self.path="E:/1-RICHI/MovilesDB"      
        self.Images=self.files(self.path, "ever")   # list one
        self.Images_copy=self.files(self.path, "partial")  # list two
        self._toplevel()

    def files(self, path, option): # here I generate the lists

        images=os.listdir(path). # route
        self.list_partial=[]  # internal list
        self.list_ever=[]      # internal list
        if option == "ever":
            for i in images:
                if ".jpg" in i:
                    route=path + "/" + i
                    open=cv2.imread (route)
                    if open is None:                 
                        continue
                    RGB=cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
                    self.list_ever.append(RGB)
            return self.list_ever

        if option == "partial" :
            for i in images:
                if ".jpg" in i:        
                    route=path + "/" + i               
                    open=cv2.imread(route)
                    if open is None:                                     
                        continue
                    RGB=cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
                    objet=Image.fromarray(RGB)
                    self.list_partial.append(objet)
            return self.list_partial  

    def _toplevel(self):
        top1=Toplevel()
        frame=Creator()
        frame.pack()  

class Creator(Frame):

    def __init__(self,*args, **kwargs): 
        Frame.__init__(self,*args, **kwargs)

        self.LIST=[]          # img_lst
        for i in self.master.Images:
            Example(self, i).place(x=0, y=0, relwidth=1, relheight=1)
       
if __name__=="__main__":
    app_1 =Applications()
    app_1.mainloop()
Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • It does not throw any error but in execution the upper window appears blank and its dimension increases to the right – Benjamin Aug 14 '21 at 21:56
  • @Benjamin no idea why that happens as I cannot test this but I can only suggest that you copy paste the class from the other question I asked if it answers you question, modify so that you can specify your won image and create those class instances in a loop – Matiiss Aug 14 '21 at 22:02
  • Thank you very much for the help, I will have to break my head to find a solution or repeat code. One last question does the code you gave me work for you? – Benjamin Aug 15 '21 at 00:41
  • As I said, I can't test it because I don't have the images but I can try using my own – Matiiss Aug 15 '21 at 07:33
  • If you please try yours so you give me lights, I have given a thousand turns to the code and I can not decipher it. – Benjamin Aug 15 '21 at 08:36
  • @Benjamin added two codes, the bottom one is just your code, just copy-paste and see if it works (I only tested the top code because I had images for that but it should work) – Matiiss Aug 15 '21 at 09:06
  • Sorry for the inconvenience, I have problems with the laptop, as soon as I have the code I will let you know, I reiterate my thanks – Benjamin Aug 15 '21 at 17:07
  • It throws me this error: TypeError: an object of type bytes is required, not 'Image', the error is in my list? .Regarding the first script, that if it was executed well, but I cannot replicate it in my code. – Benjamin Aug 15 '21 at 22:09
  • @Benjamin because the first script uses path to open images not bytearrays (I needed paths for testing), you can either replace the `Example` class in the first script with the `Example` class in the second, or just simply run the second script (tho add the other necessary imports), also on which line was that error? or at least what code is on that line exactly and I guess that happened in the `Example` class? – Matiiss Aug 15 '21 at 22:46
  • Run the second script, and the error is this: https://i.imgur.com/3UimY6W.jpg – Benjamin Aug 15 '21 at 23:33
  • I modified my script that I posted at the beginning and I put ### ERROR to the lines that give error – Benjamin Aug 15 '21 at 23:59
  • @Benjamin I had overlooked an important detail, in the `Applications` class you are supposed to do `self.list_ever.append(RGB)` and then you can also comment out `objet=Image.fromarray(RGB)` – Matiiss Aug 16 '21 at 07:33
  • I already commented on them, it does not give an error but the window comes out occupying the minimum size available and of course it does not show anything. I am creating the (Frame) object of the Creator class and positioning with pack (). – Benjamin Aug 16 '21 at 09:02
  • @Benjamin you sure you don't need to do this: `frame=Creator(top)`, otherwise by default the parent/master is the first `Tk` instance – Matiiss Aug 16 '21 at 09:14
  • Now that I see it I should not replace instead of commenting the line self.list_ever.append (objet) by: self.list_ever.append (RGB)?. – Benjamin Aug 16 '21 at 09:16
  • @Benjamin the `Example` class in the second example is expecting a bytearray so you should use `self.list_ever.append(RGB)` so that the list contains those arrays and when you iterate over the list in `Creator` class you should pass those arrays to the `Example` instance as shown already, reason why you not see the `Creator` appear in the `Toplevel` is because you haven't specified the master argument so it should be `frame = Creator(top)` so that it gets packed on that toplevel, what is important tho is that it will show only the last image at that point – Matiiss Aug 16 '21 at 09:20
  • I think that now it will work, but ironically my laptop won't turn on again. I will have to wait a few hours for him to react again, I regret not being able to confirm what you tell me, but I think that this time everything will work out. I hope to return in a few hours and reiterate my sincere thanks. – Benjamin Aug 16 '21 at 09:38
  • I tried the changes you gave me and it continues without giving me an error but with the window taking up as little space as possible. check the list: self.Images (class apps) (and if it contains content), also check: self.LIST (class Creator) (it's empty), I don't know if that's the error or self.LIST has nothing to do – Benjamin Aug 16 '21 at 21:33
  • I add to avoid problems create the Creator instance directly in the main class like this: `self. creator = Creator (self)` `self.creator .pack ()` – Benjamin Aug 16 '21 at 21:42
  • @Benjamin well nothing gets appended to `self.LIST` so it should be empty, at this point I don't even know why it doesn't appear, I am pretty sure that the first example worked for me and so should the second snippet (haven't tested because that I can't but it should work) – Matiiss Aug 16 '21 at 21:42
  • The list: "self.Images_copy", also does nothing then. – Benjamin Aug 16 '21 at 21:53
  • If I am running the second script – Benjamin Aug 16 '21 at 22:02
  • @Benjamin yes it really has no use in either scripts, can you also show how exactly you run it? also how do you plan to view them? because currently you could only see the last image as I have mentioned multiple times – Matiiss Aug 16 '21 at 22:04
  • It only shows me the window manager (minimize, maximize and close) – Benjamin Aug 16 '21 at 22:08
  • @Benjamin does `self.master.Images` contain anything? if it does contain something I literally have no idea, maybe instead of `.place` try using `.pack` or sth in that case – Matiiss Aug 16 '21 at 22:12
  • "self.master.Images", contains: "[array([[[151, 151, 151], [150, 150, 150], [150, 150, 150],............[ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0]]], dtype=uint8), array([[[150, 150, 150]," – Benjamin Aug 16 '21 at 22:21
  • I just did what you said `place ()` by `pack (),` I got the first image in the list, but it happened the same as the first time, the image expands from left to right by itself. – Benjamin Aug 16 '21 at 22:25
  • @Benjamin could be because a lot of images are packed, try using `.grid(row=0, column=0)` in that loop (the same row and column) – Matiiss Aug 16 '21 at 22:32
  • With place it doesn't work, with pack it works fine but the image expands by itself as I told you and with grid the program flickers and falls. – Benjamin Aug 16 '21 at 22:40
  • @Benjamin I am actually confused, no idea why that happens, try using `.pack()` but set `.geometry()` of the master to some value so that it doesn't automatically expand – Matiiss Aug 16 '21 at 22:42
  • You are a genius. It worked fine when I gave the window a dimension. But when expanding the window. the image expands slowly. I read in a post about this, and they recommended using this to make the image expand faster: `self.master.winfo_width ()`, `self.master.winfo_height ()` instead of 'event'. But not how to implement it – Benjamin Aug 16 '21 at 22:55
  • @Benjamin I think you can simply replace `event.width` with `self.master.winfo_width()` and the same with height, in the `Example` class, I don't know if it will work because it may not be updated but try out and see – Matiiss Aug 16 '21 at 23:01
  • I will try to implement it, another thing that I do not understand is because when I create a method in the parent class (Tk) and thus create a top-level window and add the object (frame) to the window (Toplevel) I have a recognition error of the list.Images like this: `AttributeError: 'Toplevel' object has no attribute 'Images'` – Benjamin Aug 16 '21 at 23:02
  • @Benjamin because you access `self.master.Images` but if you pass in master as the toplevel then obviously that Toplevel window won't have `.Images` – Matiiss Aug 16 '21 at 23:06
  • You have helped me a lot, I will study the code you gave me, and I hope to return the gesture with another person as you have done with me. Thank you very much – Benjamin Aug 16 '21 at 23:28
  • @Benjamin you mean [pay it forward](https://en.wikipedia.org/wiki/Pay_it_forward)? nice to hear that, glad I could help, good luck with coding – Matiiss Aug 16 '21 at 23:33