I've been trying to teach myself tkinter and wanted to make a program that would find all the pictures in the directory and sub-directories of a folder and then display them one by one with a button to either save the file into the "Yes", "Maybe", or "Skip" folders or simply delete the file.
Here's what I'm trying to get it to look like:
And here is my code which tries to do just that:
# Python 3.4
import os
import tkinter as tk
from tkinter import Frame, Button
from PIL import Image, ImageTk
from send2trash import send2trash
tk_root = tk.Tk()
tk_root.title("Picture Viewer - Do I want to keep this picture?")
file_count = 0
p = path = 'C:\\Users\\MyUserName\\Desktop\\Test\\'
yes = lambda img: os.rename(img, p+'Yes\\Picture_{0}.jpg'.format(file_count))
maybe = lambda img: os.rename(img, p+'Maybe\\Picture_{0}.jpg'.format(file_count))
skip = lambda img: os.rename(img, p+'Skipped\\Picture_{0}.jpg'.format(file_count))
delete = lambda img: send2trash(img) # Note: os.remove('img.jpg') also works
def search(directory):
global file_count
for root, subdirs, files in os.walk(directory):
for file in files:
if os.path.splitext(file)[1].lower() in ('.jpg', '.jpeg'):
img = os.path.join(root, file)
file_count += 1
top_frame = Frame(tk_root)
bottom_frame = Frame(tk_root)
top_frame.pack(side='top')
bottom_frame.pack(side='bottom')
picture = ImageTk.PhotoImage(Image.open(img))
picture = tk.Label(tk_root, image=picture)
picture.pack(side='top')
button_yes = Button(top_frame, text="Yes", command=lambda x=img:yes(x))
button_maybe = Button(top_frame, text="Maybe", command=lambda x=img:maybe(x))
button_skip = Button(top_frame, text="skip", command=lambda x=img:skip(x))
button_delete = Button(bottom_frame, text="Delete", command=lambda x=img:delete(x))
button_yes.pack(side='left')
button_maybe.pack(side='left')
button_skip.pack(side='left')
button_delete.pack(side='bottom')
print('All done!')
search('Test')
However, the problem is after I launch the program it isn't working well at all. It simply moves the first picture, "1.jpg", into whatever folder I pick (or delete) and then giving the following error if I try to sort another image:
FileNotFoundError: [WinError 2] The system cannot find the file
specified: 'Test\Example.jpg' ->
'C:\Users\Vale\Desktop\Test\Maybe\1.jpg'
Perhaps most importantly the images are not displaying and cycling properly. It is just a gray box in the middle each time. How do I get my program to work? I'm aware I need to get the image to appear still and something needs to be done to get the program to move onto the next picture as well (so I don't get FileNotFoundError for trying to sort the same picture twice), but I'm unsure what to do differently after watching tutorials and reading documentation.
As mentioned by BlackJack, your code creates the GUI Widgets over and over. You'll need to move that out from the loop. Also for displaying the image in the Label, you can't use picture as the name for both the ImageTk object and Label object.
Suggestions to changes. You could use a Generator to get the image path/filename. And make regular functions instead of using lambda. I had an interest in seeing how it could work, so I made the program below based on your code. When I tested it I had different paths, working on OSX, so haven't tested it with your Windows paths (that I put into the code here).
import os
import tkinter as tk
from tkinter import Frame, Button
from PIL import Image, ImageTk
tk_root = tk.Tk()
tk_root.title("Picture Viewer - Do I want to keep this picture?")
file_count = 0
p = path = 'C:\\Users\\MyUserName\\Desktop\\Test\\'
def search(directory):
global file_count
for root, subdirs, files in os.walk(directory):
for file in files:
if os.path.splitext(file)[1].lower() in ('.jpg', '.jpeg'):
img = os.path.join(root, file)
file_count += 1
yield img
def next_image():
try:
global photo_path
photo_path = next(path_generator)
photo = ImageTk.PhotoImage(Image.open(photo_path))
picture.configure(image=photo)
picture.image = photo
except StopIteration:
picture.configure(image='', text='All done!')
def move_file(directory):
if not os.path.exists(directory):
os.makedirs(directory)
new_file = directory + 'Picture_{0}.jpg'.format(file_count)
os.rename(photo_path, new_file)
def yes():
move_file(path + 'Yes\\')
next_image()
def maybe():
move_file(path + 'Maybe\\')
next_image()
def skip():
move_file(path + 'Skipped\\')
next_image()
def delete():
# Code for deleting file here
next_image()
top_frame = Frame(tk_root)
bottom_frame = Frame(tk_root)
top_frame.pack(side='top')
bottom_frame.pack(side='bottom')
path_generator = search(p)
photo_path = next(path_generator)
photo = ImageTk.PhotoImage(Image.open(photo_path))
picture = tk.Label(tk_root, image=photo)
picture.image = photo
picture.pack(side='top')
button_yes = Button(top_frame, text="Yes", command=yes)
button_maybe = Button(top_frame, text="Maybe", command=maybe)
button_skip = Button(top_frame, text="skip", command=skip)
button_delete = Button(bottom_frame, text="Delete", command=delete)
button_yes.pack(side='left')
button_maybe.pack(side='left')
button_skip.pack(side='left')
button_delete.pack(side='bottom')
tk_root.mainloop()
EDIT:
One issue with this code seems to be that it runs through the subdirectories (Yes, Maybe, Skipped). So you'll be presented with the images twice if it is in the path and then moved.
If you don't want to traverse the Yes, Maybe and Skipped folders, you can change the search function to:
def search(directory):
global file_count
excludes = ['Yes', 'Maybe', 'Skipped']
for root, subdirs, files in os.walk(directory, topdown=True):
subdirs[:] = [d for d in subdirs if d not in excludes]
for file in files:
if os.path.splitext(file)[1].lower() in ('.jpg', '.jpeg'):
img = os.path.join(root, file)
file_count += 1
yield img
Related
first time coder here. I'm trying to create a program to help automate some of my work in the office using python.
what I'm trying to do is to merge pdf file from Folder 1, with another pdf file from folder 2 with the same name. I also would like to use Tkinter gui
this is what I get so far
from tkinter import *
from PyPDF2 import PdfFileMerger
root = Tk()
# Creating a Label Widget
MainLabel = Label(root, text="PDF Rawat Jalan")
# Shoving it onto the screen
MainLabel.pack()
#Prompt Kode
KodeLabel = Label(root, text="Masukan Kode")
KodeLabel.pack()
#Input Kode
kode = Entry(root, bg="gray",)
kode.pack()
#function of Merge Button
def mergerclick():
kode1 = kode.get()
pdflocation_1 = "C:\\Users\\User\\Desktop\\PDF\\Folder 1\\1_"+kode1+".pdf"
pdflocation_2 = "C:\\Users\\User\\Desktop\\PDF\\Folder 2\\2_"+kode1+".pdf"
Output = "C:\\Users\\User\\Desktop\\PDF\\output\\"+kode1+".pdf"
merger = PdfFileMerger()
merger.append(pdflocation_1)
merger.append(pdflocation_2)
merger.write(open(Output, 'wb'))
confirmation = kode1 +" merged"
testlabel = Label(root, text=confirmation)
testlabel.pack()
#Merge Button
mergerButton = Button(root, text= "Merge", command=mergerclick)
mergerButton.pack()
root.mainloop()
Now there is a third file i'm supposed to append, but the third file i'm supposed to append has date in its file name. for example: file 1 (010.pdf); file 2 (010.pdf); file 3 (010_2020_10_05).
There is like 9000 file per folder
How I'm supposed to do this?
I think what you need is a way to just find files prefixed with a particular string. Based on the date suffix I'm guessing the file names may not be unique so I'm writing this to find all matches. Something like this will do that:
import pathlib
def find_prefix_matches(prefix):
dir_path = pathlib.Path(directory_name)
return [str(f_name) for f_name in dir_path.iterdir()
if str(f_name).startswith(prefix)]
If you are just learning to write code, this example is relatively simple. However it is not efficient if you need to match 9,000 files at the same time. To make it run faster you'll want to load the file list once instead of per request.
import pathlib
def find_prefix_matches(prefix, file_list):
return [f for f in file_list if f.startswith(prefix)]
file_list = [str(f_name) for f_name in dir_path.iterdir()]
for file_name_prefix in your_list_of_files_to_append:
file_matches = find_prefix_matches(file_name_prefix, file_list)
I have a code which manages my file transfers. Now I want to add a tkinter to it so I can actually make a button 'run' which will let me run the code. I don't know where to implement the tkinter code as I don't know where to begin.
This is my current code:
import os
source1 = r'D:FolderX'
location2 = 'c:\data\AM\Desktop\destination'
black_list = ['folder1', 'folder2']
for root, dirs, files in os.walk(source1):
#Exclude the blacklist folders.
dirs[:] = [d for d in dirs if d not in black_list]
for file in files:
file_path = os.path.join(root, file)
if os.path.getsize(file_path) == 0:
continue
if file.endswith(".tdms"):
tdms_path = (os.path.join(root, file))
file_size = os.path.getsize(tdms_path)
if file_size == 0:
continue
else:
continue
metadata = td.read_metadata(tdms_path)
print(metadata)
dfs.append(pd.DataFrame([metadata.properties.values()], columns=metadata.properties.keys()))
df = pd.concat(dfs)
df.to_excel(locatie2 + '\\' + 'final_sheet.xlsx'
Here's an example of how to use buttons with tkinter that I think will help.
import tkinter
import tkMessageBox
top = tkinter.Tk()
def buttonPressed():
# put code for what happens when button pressed here
messagebox.showinfo("Window Title", "The code is now running")
B = tkinter.Button(top, text="Press Me", command=buttonPressed)
B.pack()
top.mainloop()
Hope this helps!
I have the following code:
import face_recognition
from PIL import Image, ImageDraw
from tkinter import Tk
from tkinter.filedialog import askopenfilename
from shutil import copyfile
#Ask user for file name
Tk().withdraw()
filename = askopenfilename()
#Add known images
image_of_person = face_recognition.load_image_file(filename)
person_face_encoding = face_recognition.face_encodings(image_of_person)[0]
for i in range (1, 8):
#Construct the picture name and print it
file_name = str(i).zfill(5) + ".jpg"
print(file_name)
#Load the file
newPic = face_recognition.load_image_file(file_name)
#Search every detected face
for face_encoding in face_recognition.face_encodings(newPic):
results = face_recognition.compare_faces([person_face_encoding], face_encoding, 0.5)
#If match, show it
if results[0] == True:
copyFile(file_name, "./img/saved" + file_name)
The intention is to use the known image (image_of_person) and search a folder of images ('./img/unknown') for a match, then show the matched photo.
I receive the error:
No such file or directory: '00001.jpg'
On the line
newPic = face_recognition.load_image_file(file_name)
How do I point the recognition to the sample of images folder?
Note: for i in range (1, 8): - 8 Images are in the sample folder.
I think your problem is you're not giving the right path when trying to load the images.
Change
file_name = str(i).zfill(5) + ".jpg"
to
file_name = f"./img/unknown/{str(i).zfill(5)}.jpg"
Note: If you're using python2, then
file_name = "./img/unknown/{}.jpg".format(str(i).zfill(5)
Another tip, if you want your code to be generic, no matter how many images there are, you can do
for i in range(1, len(os.listdir("./img/unknown"))).
Or, even better, you can simply do
for img in os.listdir("img/unknown"):
file_name = os.path.join("img/unknown", img)
... continue with the rest of the flow ...
Quite a beginner here. I have a command line script that works fine for what I do and I'm looking to move it into a GUI.
os.chdir(ImageDirST)
for f in sorted(os.listdir(ImageDirST)):
f_name,f_ext = (os.path.splitext(f))
f_sku = (f_name.split(' ')[0])
f_num = (f_name[-2:])
n_name = ('{}_{}{}'.format(f_sku,f_num,f_ext))
print(f, "-->", n_name)
I would like this to display in the same fashion within a message window in tkinter.
With some help from here, I managed to print the filenames in the directory when a button is pushed with:
filenames = sorted(os.listdir(ImageDirBT))
text = "\n".join(filenames)
print_filename_test.set(text)
I have tried to use my split code to setup a list of what the new filenames would look like, prior to setting the variable, with the following, where print_filenames() is the function triggered by the press of a button.
def print_filenames():
filenames = sorted(os.listdir(ImageDirBT))
for filenames in sorted(os.listdir(ImageDirBT)):
f_name,f_ext = (os.path.splitext(filenames))
f_sku = (f_name.split('_')[0])
f_num = (f_name[-2:])
n_name = ('{}_{}{}'.format(f_sku,f_num,f_ext))
newlist = "\n".join(n_name)
print_filename_test.set(newlist)
I don't get any errors with this code for print_filenames(), however what is displayed in the message panel is the last filename in the list, vertically, one character wide:
eg:
F
I
L
E
_
1
1
.
e
x
t
I would like to display the output as:
oldfilename_01.ext --> newfilename_csvdata_01.ext
oldfilename_02.ext --> newfilename_csvdata_02.ext
oldfilename_03.ext --> newfilename_csvdata_03.ext
oldfilename_04.ext --> newfilename_csvdata_04.ext
The command line program I have written uses numbers to chose menu options for what needs to be done, confirming before any renaming is done, hence printing the file name comparisons. My struggle is manipulating the strings in the list to be able to do the same thing.
Using messagebox:
import os
import tkinter as tk
from tkinter import messagebox
ImageDirST = r"your_path"
os.chdir(ImageDirST)
root = tk.Tk()
names = []
for f in sorted(os.listdir(ImageDirST)):
f_name,f_ext = (os.path.splitext(f))
f_sku = (f_name.split(' ')[0])
f_num = (f_name[-2:])
n_name = ('{}_{}{}'.format(f_sku,f_num,f_ext))
names.append(f"{f} --> {n_name}\n")
messagebox.showinfo(title="Something", message="".join(names))
root.mainloop()
Or using Text widget with scrollbar:
import os
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
ImageDirST = r"your_path"
os.chdir(ImageDirST)
root = tk.Tk()
txt = ScrolledText(root, font="Arial 8")
txt.pack()
for f in sorted(os.listdir(ImageDirST)):
f_name,f_ext = (os.path.splitext(f))
f_sku = (f_name.split(' ')[0])
f_num = (f_name[-2:])
n_name = ('{}_{}{}'.format(f_sku,f_num,f_ext))
txt.insert("end",f"{f} --> {n_name}\n")
root.mainloop()
I'm trying to add and remove items from a listbox but I'm getting the following error:
files = self.fileList()
TypeError: 'list' object is not callable
How can I access this list if I can't call it? I tried to use it as a global variable but maybe I was using it incorrectly. I want to be able to take items from that listbox and when a button is pressed, add them to another listbox.
class Actions:
def openfile(self): #select a directory to view files
directory = tkFileDialog.askdirectory(initialdir='.')
self.directoryContents(directory)
def filename(self):
Label (text='Please select a directory').pack(side=TOP,padx=10,pady=10)
files = []
fileListSorted = []
fileList = []
#display the contents of the directory
def directoryContents(self, directory): #displays two listBoxes containing items
scrollbar = Scrollbar() #left scrollbar - display contents in directory
scrollbar.pack(side = LEFT, fill = Y)
scrollbarSorted = Scrollbar() #right scrollbar - display sorted files
scrollbarSorted.pack(side = RIGHT, fill = Y)
#files displayed in the left listBox
global fileList
fileList = Listbox(yscrollcommand = scrollbar.set)
for filename in os.listdir(directory):
fileList.insert(END, filename)
fileList.pack(side =LEFT, fill = BOTH)
scrollbar.config(command = fileList.yview)
global fileListSorted #this is for the filelist in the right window. contains the values the user has selected
fileListSorted = Listbox(yscrollcommand = scrollbarSorted.set) #second listbox (button will send selected files to this window)
fileListSorted.pack(side=RIGHT, fill = BOTH)
scrollbarSorted.config(command = fileListSorted.yview)
selection = fileList.curselection() #select the file
b = Button(text="->", command=lambda:self.moveFile(fileList.curselection()))#send the file to moveFile to be added to fileListSorted
b.pack(pady=5, padx =20)
def moveFile(self,File):
files = self.fileList()
insertValue = int(File[0]) #convert the item to integer
insertName = self.fileList[insertValue] #get the name of the file to be inserted
fileListSorted.insert(END,str(insertName)) #insertthe value to the fileList array
I changed files to the following to see if files was setting properly and it returned an empty array
files = self.fileList
print files
#prints []
You never initialise self.fileList (nor fileListSorted).
When you write in directoryContents
global fileList
fileList = Listbox(yscrollcommand = scrollbar.set)
...
you work on a global variable called fileList. You could either use self.fileList everywhere (or add global fileList in all your function, and thus use fileList).
However, I am skeptical of your use of classes, you should try to understand object-oriented concepts and their implementation in python, or ignore these concepts for the moment.
Edit
I have tried to run your code and you might also change the line
insertName = self.fileList[insertValue]
by
insertName = self.fileList.get(insertValue)
fileList i a widget and every Tkinter widgets use dictionnary notation for properties (such as self.fileList['background']).
Note that get take either a number, or a string containing a number and thus your conversion on above line is useless. Also note that you can get the whole list through get(0,END).