Python Forum
[Tkinter] Load image and show in label
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Load image and show in label
#1
Hello,
I'm new to Tkinter and Python and trying to display an image in a label by selecting from a folder.

from asyncore import read
from email.mime import image
import tkinter as tk
from tkinter import filedialog
from turtle import width
import cv2 as cv
from PIL import Image
from PIL import ImageTk
from io import BytesIO

class Application:
    def __init__(root, window, window_title, image_path):
        root.window = window
        root.window.title(window_title)
        root.image_path = image_path

        root.cv_img = cv.cvtColor(cv.imread(image_path), cv.COLOR_BGR2RGB)

        root.height, root.width, no_channels = root.cv_img.shape

        root.canvas = tk.Canvas(window, width=500, height=700, bg='azure3', relief='raised')
        
        root.img_show = ImageTk.PhotoImage(image=Image.fromarray(root.cv_img))

        root.headline_label = tk.Label(text="IMAGE PROCESSING", bg='azure3')
        root.up_window_label = tk.Label(image=root.img_show)
        root.down_window_label = tk.Label(bg='white')
        root.load_button = tk.Button(text="LOAD IMAGE", command=root.load_image)
        root.save_button = tk.Button(text="SAVE FILE", command=root.save_image)
        root.close_button = tk.Button(text="CLOSE", command=root.window.destroy)

        root.canvas.create_window(80, 20, window=root.headline_label)
        root.canvas.create_window(root.width*0.7, root.height*0.7, window=root.up_window_label)
        
        root.canvas.create_window(100, 600, window=root.load_button)
        root.canvas.create_window(250, 600, window=root.save_button)
        root.canvas.create_window(400, 600, window=root.close_button)
        root.canvas.pack()

        root.window.mainloop()


    def load_image(root):
        global img_show
        root.import_file_path = filedialog.askopenfilename()
        root.new_img = Image.open(root.import_file_path)
        root.pic = ImageTk.PhotoImage(image=Image.fromarray(root.new_img))

        root.down_window_label = tk.Label(image=root.pic)
        root.canvas.create_window(root.width*0.7, root.height*2, window=root.down_window_label)


    def save_image(root):
        global img_show
        root.export_file_path = filedialog.asksaveasfilename(defaultextension='.jpg')
        root.img_show.save(root.export_file_path)

Application(tk.Tk(), "IMAGE_PROCESSING_APPLICATION", "OpenCV_Logo.jpeg")
After loading the image by clicking the button, this error is given:
Error:
TypeError: a bytes-like object is required, not 'JpegImageFile'
It may has to be solved with BytesIO, but I can't find something what helps me.

I'm really thankful for any suggestions!
Reply
#2
Please show complete unaltered error traceback, it contains valuable debugging information.
Reply
#3
(Oct-23-2022, 11:47 AM)Larz60+ Wrote: Please show complete unaltered error traceback, it contains valuable debugging information.

Sorry for the missing error traceback and thanks for your reply. The problem got solved now.
Reply
#4
Hey,
a couple comments on your code:
In python classes use the self instance, using self instead of root will allow you to eliminate the global variables. the use of self in classes
when you created the label in the init:
root.up_window_label = tk.Label(image=root.img_show)
you recreate it in your function: def load_image(root):
it can be configured-
self.up_window_label.config(image= self.new_img)
finally instead of creating canvas windows for each button and label you can use place for exact placement.
self.headline_label = tk.Label(self.canvas,text="IMAGE PROCESSING", bg='azure3')
self.headline_label.place(x=80,y=20)
Reply
#5
(Oct-23-2022, 06:59 PM)joe_momma Wrote: Hey,
a couple comments on your code:
In python classes use the self instance, using self instead of root will allow you to eliminate the global variables. the use of self in classes
when you created the label in the init:
root.up_window_label = tk.Label(image=root.img_show)
you recreate it in your function: def load_image(root):
it can be configured-
self.up_window_label.config(image= self.new_img)
finally instead of creating canvas windows for each button and label you can use place for exact placement.
self.headline_label = tk.Label(self.canvas,text="IMAGE PROCESSING", bg='azure3')
self.headline_label.place(x=80,y=20)

This is really helpful for me and makes everything a bit easier! Thank you!
Reply
#6
If you just want to display images, you can use tkinter.PhotoImage() to load the image.
import tkinter as tk
from tkinter import filedialog
 
class Application(tk.Tk):
    def __init__(self, image_path=None):
        super().__init__()
        self.title("Image Processing")
        self.geometry("400x300")
        self.display = tk.Label(self)
        self.display.pack(expand=True, fill=tk.BOTH)

        # Make a frame to pack the buttons horizontally
        buttons = tk.Frame(self)
        buttons.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)
    
        # Make buttons to load image, save image and quit
        button = tk.Button(buttons, text="LOAD IMAGE", command=self.load_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)
        button = tk.Button(buttons, text="SAVE FILE", command=self.save_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5)
        button = tk.Button(buttons, text="CLOSE", command=self.destroy)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)
 
    def load_image(self):
        """Select an image to display"""
        filename = filedialog.askopenfilename()
        if filename:
            self.image = tk.PhotoImage(file=filename)
            self.display["image"] = self.image
 
    def save_image(self):
        """Save image to a file"""
        filename = filedialog.asksaveasfilename(defaultextension='.jpg')
        if filename:
            self.image.write(filename)
 
Application().mainloop()
If you add image processing to your image processing application, you'll probably want to use PIL. You'll save the image as a PIL image for doing manipulations. Each time you modify the image you'll need to convert it to a tkImage and display.

Do not create a label each time you change the image. Use the same label over and over. Each time you change the image, change the image attribute for the label.

Not everything needs to be an attribute of your class. For example, I create three buttons, but my application class doesn't save handles to the buttons. If you don't need to keep it, don't keep it. I save the display label because that is used later in the program. I keep the image, because you need to do that to have the image display. I do not keep the buttons or the button form. I never refence those objects after they are created.

There are a lot of people who write tkinter programs as you did, with a class that implements the logic, but is not a tkinter object. I think that is a poor design. In the example above, I made Application a subclass of tk.Tk, the root window. This makes Applicaltion automatically know how to do everything that root knows how to do. It also cleans up all the "self.window.blah-blah-blah".

Do not use canvas unless you want to draw shapes or make a scrolled window. In tkinter you should make widgets that are children of a window and use a layout manager (pack or grid) to position the controls in the window. Place is hardly ever used, except for windows created using a designer tool. Properly used, pack and grid make windows that resize nicely. It is hard to do that using place.
Reply
#7
(Oct-24-2022, 03:20 AM)deanhystad Wrote: If you just want to display images, you can use tkinter.PhotoImage() to load the image.
import tkinter as tk
from tkinter import filedialog
 
class Application(tk.Tk):
    def __init__(self, image_path=None):
        super().__init__()
        self.title("Image Processing")
        self.geometry("400x300")
        self.display = tk.Label(self)
        self.display.pack(expand=True, fill=tk.BOTH)

        # Make a frame to pack the buttons horizontally
        buttons = tk.Frame(self)
        buttons.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)
    
        # Make buttons to load image, save image and quit
        button = tk.Button(buttons, text="LOAD IMAGE", command=self.load_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)
        button = tk.Button(buttons, text="SAVE FILE", command=self.save_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5)
        button = tk.Button(buttons, text="CLOSE", command=self.destroy)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)
 
    def load_image(self):
        """Select an image to display"""
        filename = filedialog.askopenfilename()
        if filename:
            self.image = tk.PhotoImage(file=filename)
            self.display["image"] = self.image
 
    def save_image(self):
        """Save image to a file"""
        filename = filedialog.asksaveasfilename(defaultextension='.jpg')
        if filename:
            self.image.write(filename)
 
Application().mainloop()
If you add image processing to your image processing application, you'll probably want to use PIL. You'll save the image as a PIL image for doing manipulations. Each time you modify the image you'll need to convert it to a tkImage and display.

Do not create a label each time you change the image. Use the same label over and over. Each time you change the image, change the image attribute for the label.

Not everything needs to be an attribute of your class. For example, I create three buttons, but my application class doesn't save handles to the buttons. If you don't need to keep it, don't keep it. I save the display label because that is used later in the program. I keep the image, because you need to do that to have the image display. I do not keep the buttons or the button form. I never refence those objects after they are created.

There are a lot of people who write tkinter programs as you did, with a class that implements the logic, but is not a tkinter object. I think that is a poor design. In the example above, I made Application a subclass of tk.Tk, the root window. This makes Applicaltion automatically know how to do everything that root knows how to do. It also cleans up all the "self.window.blah-blah-blah".

Do not use canvas unless you want to draw shapes or make a scrolled window. In tkinter you should make widgets that are children of a window and use a layout manager (pack or grid) to position the controls in the window. Place is hardly ever used, except for windows created using a designer tool. Properly used, pack and grid make windows that resize nicely. It is hard to do that using place.

Wow, thank you so much! This really helped me a lot and gave me good understanding about Tkinter.
I managed to integrate different image processing functions using OpenCv and they work out well together. My problem is now to fix the "save function", but it should be only a converting problem between the different image formats, which I will be have fixed soon.
Reply
#8
A program that reads and writes image files using opencv and displays them in tkinter.
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
import cv2

class Application(tk.Tk):
    def __init__(self, image_path=None):
        super().__init__()
        self.title("Image Processing")
        self.geometry("400x300")
    
        self.image = None   # Image used for image processing
        self.tkimage = None # Image used to display in tkinter label
    
        self.display = tk.Label(self)
        self.display.pack(expand=True, fill=tk.BOTH)

        # Make a frame to pack the buttons horizontally
        buttons = tk.Frame(self)
        buttons.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)

        # Make buttons to load image, save image and quit
        button = tk.Button(buttons, text="LOAD IMAGE", command=self.load_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)
        button = tk.Button(buttons, text="SAVE FILE", command=self.save_image)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5)
        button = tk.Button(buttons, text="CLOSE", command=self.destroy)
        button.pack(side=tk.LEFT, expand=True, fill=tk.X)

    def load_image(self):
        """Select an image to display"""
        filename = filedialog.askopenfilename()
        if filename:
            # Read image and convert to RGB
            self.image = cv2.imread(filename)
            self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)

            # Convert image to tkinter image format and display
            self.tkimage = ImageTk.PhotoImage(Image.fromarray(self.image))
            self.display["image"] = self.tkimage

    def save_image(self):
        """Save image to a file"""
        filename = filedialog.asksaveasfilename(defaultextension='.jpg')
        if filename:
            # Convert image to BGR and write to file.
            cv2.imwrite(filename, cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR))

Application().mainloop()
Reply
#9
I already solved my problem. Thanks for your tips! You helped me a lot.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Tkinter: An image and label are not appearing. emont 7 405 Mar-21-2024, 03:00 PM
Last Post: deanhystad
  [Tkinter] image in label not showing? rwahdan 2 7,956 Jun-25-2021, 10:27 AM
Last Post: rwahdan
  tkinter: Image to Label Maryan 10 5,117 Oct-29-2020, 01:48 PM
Last Post: joe_momma
  [Tkinter] Returning always the last image into Label Lucas_Ribeiro 1 2,054 May-08-2020, 05:56 PM
Last Post: deanhystad
  Refresh image in label after every 1s using simple function jenkins43 1 5,444 Jul-28-2019, 02:49 PM
Last Post: Larz60+
  [Tkinter] Please help, my Label does not show up on my gui ? robertinoc 2 2,642 Jun-12-2019, 05:58 PM
Last Post: robertinoc
  [Tkinter] Image does not show in treeview. KevinBrown 3 7,455 May-05-2019, 11:47 PM
Last Post: KevinBrown
  Can't load a png image tkinter Pythenx 2 9,998 May-04-2019, 05:43 PM
Last Post: woooee

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020