TKinter Update Image list - python

I am using TKinter to display some images of a folder. Those are loaded from a list:
image_list = [os.path.join("/home/pi/fotos/previews",fn) for fn in next(os.walk("/home/pi/fotos/previews"))[2]]
But the folder is beeing updated with new photos from time to time, so TKinter has to be refreshed somehow, to show those new images as well.
How could I refresh TKinter with a new image list?
Here's the full code:
#!/usr/bin/python
import Image
import ImageTk
import Tkinter
import glob
import sys
import os.path
import os
image_list = [os.path.join("/home/pi/fotos/previews",fn) for fn in next(os.walk("/home/pi/fotos/previews"))[2]]
sorted_imagelist = sorted(image_list, key=str.swapcase, reverse=True)
current = 0
def move(delta):
global current, sorted_imagelist
if not (0 <= current - delta < len(sorted_imagelist)):
tkMessageBox.showinfo('End', 'No more image.')
return
current -= delta
image = Image.open(sorted_imagelist[current])
photo = ImageTk.PhotoImage(image)
label['image'] = photo
label.photo = photo
root = Tkinter.Tk()
root.configure(background="#eee")
label = Tkinter.Label(root, compound=Tkinter.TOP, bg="#eee")
label.pack()
label.place(x=90, y=30)
frame = Tkinter.Frame(root, bg="#eee")
frame.pack()
Tkinter.Button(frame, text='Refresh', height=10, width=25, command=root.update).pack(side=Tkinter.LEFT)
Tkinter.Button(frame, text='Previous picture', height=10, width=25, command=lambda: move(-1)).pack(side=Tkinter.LEFT)
Tkinter.Button(frame, text='Next picture', height=10, width=25, command=lambda: move(+1)).pack(side=Tkinter.LEFT)
Tkinter.Button(frame, text='Quit', height=10, width=25, command=root.quit).pack(side=Tkinter.LEFT)
move(0)
root.attributes('-fullscreen', True)
root.mainloop()
Thanks for help!

I found a solution and will answer my own question to help others:
Actually I created a function that will simply check the list again.
def refresh(delta):
global current, sorted_imagelist
text_list = next(os.walk("/home/pi/fotos/previews"))[2]
image_list = [os.path.join("/home/pi/fotos/previews",fn) for fn in next(os.walk("/home/pi/fotos/previews"))[2]]
sorted_textlist = sorted(text_list, key=str.swapcase, reverse=True)
sorted_imagelist = sorted(image_list, key=str.swapcase, reverse=True)
print (sorted_imagelist)
print ('Refreshed')
This function is called every second, so everything works fast.
def updater():
threading.Timer(1.0, updater).start()
refresh(0)
updater()
Only problem I am having so far is, that when the current viewed image is the last one of the old list, the 'new' last photo is not being loaded correctly into TKinter. I have to navigate back to the previous and I'm then able to move forward to see the new one.
I am working on a solution to solve that last one.

If you store the list of filenames at time A and then later get the new list of filenames at time B then you can use the set operation symmetric_difference to find the items in B that are not in A (i.e: new files). The Tk after method can let you schedule a function to do this at intervals. You can then create the new Tk images for the new files and add those to your list of Tk images.
Each platform has some API for requesting your application be notified when changes occur to the filesystem. On Windows that's the FindFirstChangeNotification function and its friends or inotify on Linux. However, the simplest cross-platform method is to poll the contents of the directory on a timer.

Related

Image viewer app: function to display next picture not working

I've been watching this youtube tutorial and had (I thought) a way better idea of solving the build an image viewer app.
But nothing happens on input, and I don't understand why, I think I might have totally misunderstood list as it doesn't start with the first picture (index 0).
The code is here
from tkinter import *
from PIL import ImageTk,Image
i = 0
root = Tk()
root.title("Learning to code")
root.iconbitmap('blue.PNG')
my_img1 = ImageTk.PhotoImage(Image.open("pics\me1.JFIF"))
my_img2 = ImageTk.PhotoImage(Image.open("pics\me2.JPG"))
my_img3 = ImageTk.PhotoImage(Image.open("pics\me3.JPG"))
my_img4 = ImageTk.PhotoImage(Image.open("pics\me4.JPG"))
my_img5 = ImageTk.PhotoImage(Image.open("pics\pop.JPG"))
img_list = [my_img1,my_img2,my_img3,my_img4,my_img5]
my_label = Label(image=img_list[i])
my_label.grid(row=0,column=0,columnspan=3)
def f_fwd():
global i
global my_label
if i < 4 :
my_label.grid_forget()
i =+1
my_label = Label(image=img_list[i])
my_label.grid(row=0,column=0,columnspan=3)
def f_bwd():
return
button_bwd = Button(root, text= "<<")
button_bwd.grid(row=1,column=0)
button_quit =Button(root, text="Exit", command= root.quit)
button_quit.grid(row=1,column=1)
button_fwd = Button(root, text=">>", command=f_fwd())
button_fwd.grid(row=1,column=2)
root.mainloop()
Sorry if I worded the problem poorly.
I expected the index to go up and display the next picture in the list when the f_fwd function is called from the button command, and I don't understand why its not working.

GUI for On / Off Display using Tkinter Python

I am trying to create a function where if I press "1" my image changes, but if I press 1 again the image reverts back to how it was. This process should go on indefinitely.
I am able to do this if the user clicks on a button. For example, the below code simply uses config to change the function that should be run each time button is clicked.
# run this "thing" each time seat is activated
def activate(name):
name.config(image=active)
name.config(command=lambda: deactivate(name))
# run this "thing" each time seat is deactivated
def deactivate(name):
name.config(image=deactive)
name.config(command=lambda: activate(name))
x = Button(root, image=deactive, command=lambda: thing(x))
But I am unable to achieve the same if I bind a key instead:
from tkinter import *
# window config
root = Tk()
root.title("Example - on / off")
root.geometry("1080x600")
# on and off images
inactive = PhotoImage(file='green.png')
active = PhotoImage(file='red.png')
# functions to change the image using config
def something(event):
my_label.config(image=active)
def something2():
my_label.config(image=inactive)
# label which stores the image
my_label = Label(root, image=inactive)
my_label.pack()
# invisible button bind to key "1"
my_button = Button(root)
my_button.bind_all(1, something)
my_button.pack()
root.mainloop()
I would welcome any thoughts on this or an indication if I am approaching this completely wrong.
Thanks
You can simplify the logic by using itertools.cycle() and next() functions:
from tkinter import *
from itertools import cycle
root = Tk()
root.title("Example - on / off")
root.geometry("1080x600")
# on and off images
images = cycle((PhotoImage(file='green.png'), PhotoImage(file='red.png')))
def something(event):
my_label.config(image=next(images))
my_label = Label(root, image=next(images))
my_label.pack()
root.bind('1', something)
root.mainloop()

Blank images when attempting to load image into tkinter window

So what's happening is that I have a PNG file saved in my directory. I am trying to create a program that loads this picture onto a window 3 times. I am using tkinter for the UI and its PhotoImage class to do this. To load a picture I normally create a class and then load put a button in it with the 'image parameter'. However, when I try to run this program, it only loads the 3rd picture. The 1st and 2nd one just appear as blank boxes. Can somebody help me?
The code is below:
from tkinter import *
def add():
imageClass = PhotoImage(file="updated.png")
button = Button(root, compound=TOP, image=imageClass, pady=20, bd=0, highlightthickness=0)
button.pack()
root = Tk()
root.config(bg="white")
for i in range(3):
root.update()
imageClass = PhotoImage(file="updated.png")
button = Button(root, compound=TOP, image=imageClass, pady=20, bd=0, highlightthickness=0)
button.pack()
root.mainloop()
What's happening is your images are being stored on every iteration of the for loop as imageClass = PhotoImage(file="updated.png")
imageClass gets its value overriden for every i, and when the loop finally exits only one reference in imageClass remains, hence why the 3rd image appers and others do not.
Thus you need to store every PhotoImage "reference" somewhere, for example in a global dictionary
root = Tk()
# Modification 1
myImageClasses = dict()
root.config(bg="white")
for i in range(3):
root.update()
# Modification 2
myImageClasses[i] = PhotoImage(file="updated.png")
button = Button(root, compound=TOP, image=myImageClasses[i], pady=20, bd=0, highlightthickness=0)
button.pack()
root.mainloop()

How to get and save checkboxes names into a list with tkinter, python 3.6.5

I would like to to use tkinter and checkboxes to make a selection of files in a directory and save those files names in a list when I press a button:
import speech_recognition as sr
import playsound
import os
import glob
import unidecode
import pickle
import random
import tkinter
from tkinter.constants import *
ldv = os.listdir("D:/FFOutput/")
i = 0
ldv1 = []
while i < len(ldv):
ldv1.append(unidecode.unidecode(ldv[i]))
i += 1
print(ldv1)
root = tkinter.Tk()
frame = tkinter.Frame(root, relief=RIDGE, borderwidth=10)
frame.pack(fill=BOTH, expand=1)
label = tkinter.Label(frame, text="choose file(s)")
label.pack(fill=X, expand=1)
a = 0
while a < len(ldv1):
bouton = tkinter.Checkbutton(root, text=ldv1[a], command=print(ldv1[a]))
a += 1
bouton.pack()
button = tkinter.Button(frame, text="Exit", command=root.destroy)
button.pack(side=BOTTOM)
lr = []
buttonregister = tkinter.Button(root, text="Register checked files names in list lr and close tk")
buttonregister.pack(side=BOTTOM)
print(lr)
root.mainloop()
When I click on buttonregister, I would like to append the files names into the list lr and close the frame. Example
In that example, I wish to print(lr) "['alors soyez pret.mp3','c'est bien.mp3']" in the shell when I click the button.
For a Checkbutton to hold a value, a BoolVar (or any other var) from tkinter must be used. This is usually quite tedious as you would have to make a variable for every Checkbutton. This can be avoided by sub-classing the Checkbutton and adding storage for a variable. Since you require the text as well, we can also use the class to store the text value.
Replacing the Checkbuttons with the class below will do this.
class CheckBox(tkinter.Checkbutton):
boxes = [] # Storage for all buttons
def __init__(self, master=None, **options):
tkinter.Checkbutton.__init__(self, master, options) # Subclass checkbutton to keep other methods
self.boxes.append(self)
self.var = tkinter.BooleanVar() # var used to store checkbox state (on/off)
self.text = self.cget('text') # store the text for later
self.configure(variable=self.var) # set the checkbox to use our var
We would then use the class to make the buttons as follows:
a=0
while a<len(ldv1):
bouton=CheckBox(tk, text=ldv1[a], command=print(ldv1[a])) # Replace Checkbutton
a=a+1
bouton.pack()
And finally, to get the values when the window is closed, you can loop through CheckBox.buttons for each button's value. You would need to add this after the mainloop or add it to a function.
for box in CheckBox.boxes:
if box.var.get(): # Checks if the button is ticked
lr.append(box.text)
print(lr)

Toplevel() and Label out put from For loop - Python 2

I want to create a gui sub window that could look like this, which lists a live feedback for the top teams and also giving them a numbering.
So I have done for loop function to number them and Label them out, which works fine when run with nothing but the code but I'm having trouble making it run on the new window that is opened from main window.
import io
import base64
import Tkinter as tk
from Tkinter import *
from re import findall
from urllib2 import urlopen
def epl_Window():
epl = tk.Toplevel()
epl.title('Top Ten EPL Team 2016')
url = "http://i.imgur.com/3znCRqH.gif"
image_byte1 = urlopen(url).read()
data_stream1 = io.BytesIO(image_byte1)
pil_image1 = Image.open(data_stream1)
w, h = pil_image.size
tk_image1 = ImageTk.PhotoImage(pil_image1)
label1 = tk.Label(epl, image=tk_image1, bg='black')
label1.pack(padx=5, pady=5)
epl.geometry("500x700")
for index, value in enumerate(EPL_10, start=1):
Label(epl, text=str(index)).grid(row=index, column=1, sticky='W')
Label(epl, text=value).grid(row=index, column=2, sticky='W')
epl.mainloop()
root = tk.Tk()
root.title("Top Ten Lists")
url = "http://i.imgur.com/H1sURuR.gif"
image_bytes = urlopen(url).read()
# internal data file
data_stream = io.BytesIO(image_bytes)
# open as a PIL image object
pil_image = Image.open(data_stream)
# optionally show image info
# get the size of the image
w, h = pil_image.size
### convert PIL image object to Tkinter PhotoImage object
tk_image = ImageTk.PhotoImage(pil_image)
# put the image on a typical widget
label = tk.Label(root, image=tk_image, bg='brown')
label.pack(padx=5, pady=5)
#BUTTON
EPL_B = Button(root, text='EPL Teams', width=10, command=epl_Window)
EPL_B.pack(anchor = "w", side = LEFT)
#EPL RANK
url = 'http://www.bbc.com/sport/football/premier-league/table'
EPL_contents = urlopen(url).read()
EPL_Ranking = findall("'>(\w* ?\w*)</a>", EPL_contents)
EPL_10= EPL_Ranking[:10]
root.mainloop()
I think there is problem with def new window toplevel() but not sure how to change it to load. Running it make program crash
So it crashes when you click on the button which opens the TopLevel?
Without a traceback this is very difficult to answer, but my guess is that the line
epl.mainloop()
causes the crash. Even if it doesn't, you don't need to call it. There is usually only one mainloop at a time on your root widget.

Categories