Create Enum referencing a list with TkImages - python

I've got a couple of images in a folder and each of these gets resized and turned into an image for Tkinter. I want to use these images in other python scripts as a enum.
This is how I create the images:
import os
from PIL import Image, ImageTk
from enum import Enum
#Resize All Images and turn them into TkImages
def ResizeImages():
PieceImages = []
for images in os.listdir('Resources'):
cImage = Image.open('Resources/' + images)
cImage = cImage.resize((80, 80), Image.ANTIALIAS)
PieceImages.append(ImageTk.PhotoImage(cImage))
return PieceImages
#Called from outside to create PNGS list AFTER Tkinter.Tk() was called
def InitializeImages():
global PNGS
PNGS = ResizeImages()
#This is the part where I'm lost
class Black(Enum):
global PNGS
Queen = PNGS[3]
King = PNGS[4]
Tower = PNGS[5]
I want to create a Tkinter Button with these images on like this:
#PieceImages is the .py file above
import PieceImages import Black
form tkinter import *
root = Tk()
root.geometry('800x800')
someButton = tk.Button(root, image=Black.Tower).place(x=100, y=100)
My Question is:
Is it even possible to create enums of this type and if so, how?

I'd recommend staying away from global variables; you can pass arguments to a class directly:
class Black():
def __init__(self, pngs):
self.name1 = pngs[0]
self.name2 = pngs[1]
Instantiate by passing pngs:
black = Black(pngs)
Then black.name1, black.name2 etc will be your desired images.

Related

Using a class constructor to handle images tkinter

Hi i'm trying to create a class constructor to handle images in tkinter, since i'll be using many images in my code so I need a way to use images without using so much line of code.
I keep getting the error:
return _modes[mode]
KeyError: 'picture.png'
Here's the code:
from tkinter import *
import tkinter
from random import randint
from PIL import Image
from PIL import ImageTk
root = tkinter.Tk()
root.geometry('700x700')
class PhotoHandler:
def __init__(self,imagepath):
self.imagepath = imagepath
self.image = Image.open(self.imagepath)
self.image = ImageTk.PhotoImage(self.imagepath)
def returnn(self):
return self.image
search = PhotoHandler('picture.png').returnn()
root.mainloop()
You passed a string to the tkinter image. It expects a PIL image or mode. Since you passed a string, it thinks you're using an invalid mode. Pass the image instead:
class PhotoHandler:
def __init__(self,imagepath):
self.imagepath = imagepath
self.image = Image.open(self.imagepath)
self.image = ImageTk.PhotoImage(self.image)
def returnn(self):
return self.image
search = PhotoHandler('picture.png').returnn()
# add label for background image
background_label = tkinter.Label(root, image=search)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
Here's the documentation:
https://pillow.readthedocs.io/en/4.2.x/reference/ImageTk.html

Use an image-button defined inside an object with TKinter

I'm trying to build a tkinter button with an image as background inside an object. It doesn't make any sense why the second implementation doesn't work !
Here are 3 very simple examples ; Who can explain the reason why the second implementation is not working?
(Python 3.6.4 :: Anaconda, Inc.)
1. Button created globally.
Works like a charm...
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
def cb():
print("Hello World")
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=cb, image=image)
b.pack()
w.mainloop()
2. Button created inside the object A with a background image
The button doesn't work when clicked and doesn't display the image :(. There's clearly a problem but I don't understand it...
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
class A():
def __init__(self, w):
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb, image=image)
b.pack()
def cb(self):
print("Hello World")
a = A(w)
w.mainloop()
3. Button created inside the object A without a background image
The button works properly, but I would like to display the image as well
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
class A():
def __init__(self, w):
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb)#, image=image)
b.pack()
def cb(self):
print("Hello World")
a = A(w)
w.mainloop()
You have 2 problems here.
The first problem is the image is not being saved after __init__. You probably know you need to save a reference to the image for it to be used in tkinter. You may not know that in a class if you do not assign the image to a class attribute it will not save the image after __init__.
So to fix the first issue you need change this:
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
To this:
# add self. to make it a class attribute and keep the reference alive for the image.
self.image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
The 2nd issue you may not notice here is that your text will not display while loading an image. This is because you need to add the argument compound in order for tkinter to display both an image and text in the button. That said you also need to update the image argument to include the new self.image.
So change this:
b = Button(w, text="text", command=self.cb, image=image)
To this:
# added compound and change text color so you can see it.
b = Button(w, compound="center" , text="text", fg="white", command=self.cb, image=self.image)
Results:
I think I understood what happened. Thanks to the linked question what's happening in the second case is that your image gets garbage collected once the __init__ method is finished. As a result your image is not available anymore to the root application, so it cannot be binded to it.
The way to solve it is to make it a class attribute:
class A():
def __init__(self, w):
self.image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb, image=self.image)
b.pack()
def cb(self):
print("Hello World")

Class for picture view that change pic on mouse click

I want to make a class that has a picture and it is changed to the next one by mouse click.I'm new to oop, my idea here was to make class similar to real life where there is new class instance for every new picture, is it possible to do it this way? Here is my code
import tkinter as tk
from PIL import Image,ImageTk
class Picture():
_count=1
def __init__(self,window):
self.id=Picture._count
Picture._count+=1
self.img=Image.open(r'C:\ImgArchive\img%s.png' % self.id)
self.pimg = ImageTk.PhotoImage(self.img)
self.lab=tk.Label(window,image=self.pimg)
self.lab.pack()
self.lab.bind('<1>',self.click)
def click(self,event):
self.lab.destroy()
self=self.__init__(window)
window = tk.Tk()
window.title('Album')
window.geometry('1200x900')
pic=Picture(window)
window.mainloop()
It works fine, but i'm not sure that old instances of my class is deleted, are they? And i use self.lab.destroy() because if i dont new picture appears down, like this
instead of this
So why it happens?What is elegant way for it?
Below example produces a simple image viewer tested with path of C:\Users\Public\Pictures\Sample Pictures, let me know if anything's unclear:
import tkinter as tk
from PIL import Image, ImageTk
#required for getting files in a path
import os
class ImageViewer(tk.Label):
def __init__(self, master, path):
super().__init__(master)
self.path = path
self.image_index = 0
self.list_image_files()
self.show_image()
self.bind('<Button-1>', self.show_next_image)
def list_files(self):
(_, _, filenames) = next(os.walk(self.path))
return filenames
def list_image_files(self):
self.image_files = list()
for a_file in self.list_files():
if a_file.lower().endswith(('.jpg', '.png', '.jpeg')):
self.image_files.append(a_file)
def show_image(self):
img = Image.open(self.path + "\\" + self.image_files[self.image_index])
self.img = ImageTk.PhotoImage(img)
self['image'] = self.img
def show_next_image(self, *args):
self.image_index = (self.image_index + 1) % len(self.image_files)
self.show_image()
root = tk.Tk()
mypath = r"C:\Users\Public\Pictures\Sample Pictures"
a = ImageViewer(root, mypath)
a.pack()
root.mainloop()

Python Pillow Tkinter, destroy image,

So ive been tinkering with Tkinter and Pillow for a project of mine and have so far come up with a clickable image. When i click the image it prints image has been clicked. However i want the image to get destroyed before the text is displayed. I have tried img.destroy() but that throws and error saying img is not defined so i suspect the problem is that i dont understand where things have been renamed and such. any and all help is greatly appreciated :)
from tkinter import *
from PIL import Image, ImageTk
SWH = Tk()
SWH.geometry("1024x950+130+0")
SWH.title("ServiceWhiz.")
def printimage():
load = Image.open("hello.gif")
render = ImageTk.PhotoImage(load)
img = Button(SWH, image=render,command=imgpress)
img.image = render
img.place(x=0,y=0)
return;
def imgpress():
img.destroy()
Label1 = Label(SWH, text="Image has been clicked",fg="#0094FF",font=('Arial',20)).pack()
return;
SWTitle = Label(SWH, text="ServiceWhiz.",fg="#0094FF",font=('Arial',20)).pack()
MyButtonTest = Button(SWH, text="Click Me.",fg="White",bg="#0094FF",command=printimage).pack()
You need to define "img" variable outside of your printimage function (on the same layer as for SWH). If you defined img inside of the function it will be accessible only there. By adding global img we specify that img inside of function should refer to that global-level value. It's a generally bad idea to have it like this so think about moving your handlers into class which will retain state and store img for you.
Try to make like this:
from tkinter import *
from PIL import Image, ImageTk
SWH = Tk()
SWH.geometry("1024x950+130+0")
SWH.title("ServiceWhiz.")
img = None
def printimage():
global img
load = Image.open("hello.gif")
render = ImageTk.PhotoImage(load)
img = Button(SWH, image=render,command=imgpress)
img.image = render
img.place(x=0,y=0)
return;
def imgpress():
global img
img.destroy()
Label1 = Label(SWH, text="Image has been clicked",fg="#0094FF",font=('Arial',20)).pack()
return;
SWTitle = Label(SWH, text="ServiceWhiz.",fg="#0094FF",font=('Arial',20)).pack()
MyButtonTest = Button(SWH, text="Click Me.",fg="White",bg="#0094FF",command=printimage).pack()

How to maintain multiple references for PhotoImage objects

I am trying to display two image objects in my Text widget. Initially, both objects showed up as empty images. After some research, I learned that I need to keep a reference to the image. I added an extra line to my code. I now see the 2nd image displayed correctly, however the 1st image is still blank.
I suspect my "reference" is being over written during the for loop. Is this the case? And if so, how would I maintain multiple references if I need to display multiple image objects inside a Text widget?
Here is my code so far:
from Tkinter import *
from PIL import Image, ImageTk
import os
class TestApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent)
self.main_container.pack()
self.text_box = Text(self.main_container, height=50, width=80)
self.text_box.pack()
image_names = ['img1.jpg', 'img2.jpg']
for name in image_names:
img = Image.open(os.getcwd() + '\\' + name)
img_obj = ImageTk.PhotoImage(img)
self.text_box.image_create(INSERT, image=img_obj)
self.text_box.insert(END, "\n")
# Added this extra reference
self.text_box.image = img_obj
root = Tk()
root.title("This is a test app")
mainapp = TestApp(root)
root.mainloop()
Yes, your reference is being overwritten.
The simplest might be to just add the reference to a list. For example:
class TestApp:
def __init__(...):
...
self.images = []
...
for name in image_names:
...
img_obj = ImageTk.PhotoImage(img)
self.images.append(img_obj)

Categories

Resources