1

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
martineau
  • 119,623
  • 25
  • 170
  • 301
imxitiz
  • 3,920
  • 3
  • 9
  • 33

4 Answers4

1

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

Jason Yang
  • 11,284
  • 2
  • 9
  • 23
  • **But** What about `Label` in question there is asked about `Label` as well. :D I believe you can add that `Label` part too. – imxitiz Aug 08 '21 at 08:08
  • If you mean the name label, also create only a name_label, and update option `text` of name_label with labels[index] at the same time in callback function `shift`. – Jason Yang Aug 08 '21 at 08:12
  • I don't want answer, I already added my answer and I know how to add label but I am asking you to add label part too in your answer because in question I had actually asked for label and links. – imxitiz Aug 08 '21 at 08:14
  • Jason: FYI, the `grid()` method always returns `None`, so that's the value being assigned to both `button1` and `button2`. See [Tkinter: AttributeError: NoneType object has no attribute ](https://stackoverflow.com/questions/1101750/tkinter-attributeerror-nonetype-object-has-no-attribute-attribute-name). – martineau Aug 12 '21 at 19:14
  • Update as above. – Jason Yang Aug 13 '21 at 05:51
1

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()
Derek
  • 1,916
  • 2
  • 5
  • 15
1

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

martineau
  • 119,623
  • 25
  • 170
  • 301
  • Actually unique approach. :) but is this really a better approach then other? – imxitiz Aug 08 '21 at 14:19
  • 1
    Whether it's better depends on what qualities you consider important of course. The good things about it are it's largely data-driven and once everything's set up, showing different image and associated name label-pairs is simply a matter of hiding and unhiding a couple of them. – martineau Aug 08 '21 at 18:28
0

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()
imxitiz
  • 3,920
  • 3
  • 9
  • 33
  • @Art If I want then I can don't provide any code but I had provided code just to show a simple example like... Should I delete code from question? – imxitiz Aug 08 '21 at 07:37
  • I am sorry I didn't get your last comment. You should at least explain what you changed *you can try this* doesn't explain anything. – Art Aug 08 '21 at 07:41
  • I mean Do I have to remove the code from question? If I want then I don't have to provide any code in question, I can just right answer and that is okay! I hadn't explained it because one thing code is self explanatory and second code in question I had already explain what I am trying to do! – imxitiz Aug 08 '21 at 07:43
  • Maybe, I don't know. Showing an IndexError is what's making this post ambiguous. Besides, all codes are self-explanatory once you know what it is. Beginners might find it hard to understand. – Art Aug 08 '21 at 07:45
  • Have you read _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 :_ explanation? And if beginners wants explanation then they have to look at the question also, now looking at answer only help them to solve their problem. And in question I am clearly telling what i am trying to do! I gave that error message in question just to show this is also an issue for me. – imxitiz Aug 08 '21 at 07:46
  • 1
    Are you answering your own question? – Delrius Euphoria Aug 08 '21 at 09:14
  • @CoolCloud Exactly, I had actually posted answer, but we need question so question with answer. :) – imxitiz Aug 08 '21 at 14:10