How to make a image viewer with left right functionality in tkinter?

626 Views Asked by At

I have bunch of links and labels in list to view those I want to make a image viewer with next, previous functionality. My links and labels for respective images are in list. Up to now I had tried this :

urls=[url,url1,url2,url3]
labels=["label 1","label 2","label 3","label 4"]

images=[]
for ur in urls:
    raw_data = urllib.request.urlopen(ur).read()
    im = Image.open(io.BytesIO(raw_data))
    image = ImageTk.PhotoImage(im)
    images.append(image)

Now I have images ready in images and now I want to display it in image viewer but only last image is visible in image viewer.

Label(root).grid(row=1,column=1)

for i in range(len(images)):
    image_label=Label(root,image=images[i])
    image_label.grid(row=1,column=2)
    
    name=Label(root,text=labels[i])
    name.grid(row=2,column=2)

def left():
    image_label=Label(root,image=images[i-1])
    image_label.grid(row=1,column=2)

def right():
    image_label=Label(root,image=images[i+1])
    image_label.grid(row=1,column=2)
    
left_button=Button(root,text="Left",command=left)
left_button.grid(row=2,column=1)

right_button=Button(root,text="Right",command=right)
right_button.grid(row=2,column=3)

Right button is not working and left button is working but for one time only. When I click left button second time then nothing is working.
Error while clicking in right button :

line 45, in right
    image_label=Label(root,image=images[i+1])
IndexError: list index out of range
4

There are 4 best solutions below

7
imxitiz On

You can try this :

from tkinter import Tk,Label,Button
from PIL import ImageTk,Image
import io
import requests
import urllib 

root=Tk()

count=0 # To know the current image and label
urls=[url,url1,url2,url3]
labels=["label 1","label 2","label 3","label 4"]

images=[]
for ur in urls:
    raw_data = urllib.request.urlopen(ur).read()
    im = Image.open(io.BytesIO(raw_data))
    image = ImageTk.PhotoImage(im)
    images.append(image)

def change(direction): # Function to change image
    global count
    if direction=="left":
        if count<=0:
            count=len(urls)-1
        else:
            count-=1
    else:
        if count>=len(urls)-1:
            count=0
        else:
            count+=1
    name.config(text=labels[count])
    image_label.config(image=images[count])

Label(root).grid(row=1,column=1)
image_label=Label(root,image=images[count])
image_label.grid(row=1,column=2)

left_button=Button(root,text="Left",command=lambda : change("left"))
left_button.grid(row=2,column=1)

name=Label(root,text=labels[count])
name.grid(row=2,column=2)

right_button=Button(root,text="Right",command=lambda : change("right"))
right_button.grid(row=2,column=3)

root.mainloop()
5
Jason Yang On

Should just update a image label when shift left or right, not create a new label. Using modulus % to find which one next turn.

To reduce code and executable, I use the image data in PySimpleGUI and simple name for image here.

import tkinter as tk
import PySimpleGUI as sg

def shift(num):
    global index
    index = (index + num) % size
    label1.configure(image=images[index])
    label2.configure(text =labels[index])

root = tk.Tk()

images = [tk.PhotoImage(data=image) for image in sg.EMOJI_BASE64_HAPPY_LIST]
size = len(images)
labels = [f'Image {i:0>2d}' for i in range(size)]
index = 0

label1 = tk.Label(root, image=images[index])
label1.grid(row=1, column=2)
label2 = tk.Label(root, text=labels[index])
label2.grid(row=2, column=2)

button1 = tk.Button(root, text="<LEFT",  width=6, anchor='center', command=lambda num=-1:shift(num))
button1.grid(row=1, column=1)
button2 = tk.Button(root, text="RIGHT>", width=6, anchor='center', command=lambda num=+1:shift(num))
button2.grid(row=1, column=3)

root.mainloop()

enter image description here

2
Derek On

The code is pretty close to original posted example.

There is no lambda in Button, just straight forward command that gives access to left and right functions.

The name label is unnecessary as label objects already have a text attribute so I've integrated image and text into single label and increased the font size.

label compound is used to control where the image is display relative to the text, I've chosen top. That is, the image will be place above the text.

The use of global in functions keeps track of count


from tkinter import Tk, Label, Frame, Button
from PIL import ImageTk, Image
import io
import requests
import urllib 

root = Tk()
urls = [url, url1, url2, url3]
labels = ["label 1","label 2","label 3","label 4"]
count = -1
images = []
for ur in urls:
    raw_data = urllib.request.urlopen( ur ).read()
    im = Image.open(io.BytesIO( raw_data ))
    image = ImageTk.PhotoImage(im)
    images.append(image)

def left( ):
    global count
    count = (count - 1) % len(images)
    image_label.config(image = images[count], text = labels[count])

def right( ):
    global count
    count = (count + 1) % len(images)
    image_label.config(image = images[count], text = labels[count])

image_label = Label(
    root, image = images[count], font = "Helvetica 20 normal",
    text = labels[count], compound = "top")
image_label.grid(row = 0, column = 0, columnspan = 2, sticky = "nsew")

Button(
    root, text = "<< LEFT", command = left).grid( row = 1, column = 0, sticky = "ew")
Button(
    root, text = "RIGHT >>", command = right).grid(row = 1, column = 1, sticky = "ew")

right( ) # display first image
root.mainloop()
2
martineau On

Here's a way to do it by showing and hiding pairs of Labels with the image and it name on them. For development and testing purposes, it loads the images from disk instead of downloading them, but you can easily replace that with what you have in your question.

from pathlib import Path
from PIL import Image, ImageTk
from tkinter import Button, Tk, Label


root = Tk()
root.title('Image Viewer')

# Create thumbnails of all the images.
image_folder = '/path/to/image/folder'
images = []
for filepath in Path(image_folder).iterdir():
    img = Image.open(filepath)
    w, h = img.size
    if w > h:
        f = 100 / w
        nw, nh = 100, int(f*h)
    else:
        f = 100 / h
        nw, nh = int(f*w), 100
    # Offsets to center thumbnail within (100, 100).
    woff, hoff = (100-nw) // 2, (100-nh) // 2
    img = img.resize((nw, nh), Image.BICUBIC)  # Shrink so largest dimen is 100.
    thumbnail = Image.new('RGBA', (100, 100), (255, 255, 255, 0))  # Blank.
    thumbnail.paste(img, (woff, hoff))  # Offset keep it centered.
    images.append(ImageTk.PhotoImage(thumbnail))

names = ['Name 1', 'Name 2', 'Name 3', 'Name 4']

def get_images():
    """Create all image and name Label pairs but initially  hidden."""
    global image_labels, name_labels

    image_labels, name_labels  = [], []
    for i, (img, name) in enumerate(zip(images, names)):
        image_label = Label(root, image=img)
        image_label.grid(row=1, column=2)
        image_labels.append(image_label)

        name_label = Label(root, text=name)
        name_label.grid(row=2, column=2)
        name_labels.append(name_label)

        hide(i)

def hide(i):
    """Hide specified image and name pair."""
    image_labels[i].grid_remove()
    name_labels[i].grid_remove()

def show(i):
    """Unhide specified image and name pair."""
    image_labels[i].grid()
    name_labels[i].grid()

def shift(direction):
    """Display the image and pair before or after the current one that is
    `direction` steps away (usually +/-1).
    """
    global current
    hide(current)
    current = (current + direction) % len(images)
    show(current)


left_button = Button(root, text="Left", command=lambda: shift(-1))
left_button.grid(row=2, column=1)

right_button = Button(root, text="Right", command=lambda: shift(+1))
right_button.grid(row=2, column=3)

get_images()
current = 0  # Currently displayed image.
show(current)

root.mainloop()

Screenshot of it running:

screenshot