calling listbox from different function in python with tkinter - python

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).

Related

How can I avoid repeating a for loop in two different functions

I am writing an ImageCollection class in python that should hold a dictionary with a name and the image-object (pygame.image object).
In one case I want to load all images inside a folder to the dictionary and in another case just specific files, for example only button-files.
What I have written so far is this:
class ImageCollection:
def __init__(self):
self.dict = {}
def load_images(self, path):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})
def load_specific_images(self, path, contains_str):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
if file_name.rfind(contains_str):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})
The only problem is that this is probably bad programming pattern, right? In this case it probably doesnt matter but I would like to know what the best-practice in this case would be.
How can I avoid repeating myself in two different functions when the only difference is just a single if condition?
I have tried creating a "dict_add" function that creates the entry.
Then I was thinking I could create two different functions, one which directly calls "dict_add" and the other one checks for the specific condition and then calls "dict_add".
Then I thought I could add create just a single function with the for-loop but pass a function as an argument (which would be a callback I assume?). But one callback would need an additional argument so thats where I got stuck and wondered if my approach was correct.
You could make the contains_str an optional argument.
In cases where you want to load_images - you just provide the path
In cases where you want to load specific images - you provide the path and the contains_str argument
In both cases you call load_images(...)
Code:
class ImageCollection:
def __init__(self):
self.dict = {}
def load_images(self, path, contains_str=""):
directory = os.fsencode(path)
for file in os.listdir(directory):
file_name = os.fsdecode(file)
img_path = path + "/" + file_name
if file_name.endswith(".jpg") or file_name.endswith(".png"):
if contains_str == "" or (contains_str != "" and file_name.rfind(contains_str)):
# Remove extension for dictionary entry name and add image to dictionary
#-----------------------------------------------------------------------
dict_entry_name = file_name.removesuffix(".jpg").removesuffix(".png")
self.dict.update({dict_entry_name: image.Image(img_path, 0)})

Python - tkinter askopenfilename function somehow stays active while selecting the file "directly" doesn't

I'm not a programmer so admittingly my title might be a bit off.
I have a program where I select an IFC-file (used in the construction industry, which contains data about the geometry and data connected to the objects in the 3D drawing) and then a dialog opens where I can add a GUID (an identifier) and search for all the information related to that object in the IFC-file, which I then print in the command prompt.
A minor issue which annoys me a bit is that the when is select the file in a more flexible way, using askopenfilename, the function seems to stay active or in a loop after I close the later opening dialog, so that I have to end the process using CTRL+C.
I post the entire code as I don't know if there is something else which causes it:
#imports to search for files
import os
# imports tkinter for selection purposes, so that one can select the files instead of reading in them automaticall from a directory
import tkinter as tk
from tkinter.filedialog import askopenfilename
#importing the counter to counte the occurences in a list
from collections import Counter
#importing regex functionality to check values
import re
#this is the numbering, for example #433, #4781 etc, that we either want to look for or change - now it looks for up to a number of length 7
regexIFCnumbers = '#\d{1,7}'
tk.Tk().withdraw() # we don't want a full GUI, so keep the root window from appearing
file1 = askopenfilename(initialdir=os.getcwd()) # show an "Open" dialog box and return the path to the selected file - this in the current directory where we'll start looking
file1Name = (os.path.basename(file1)) #only the file's name
#read files
def readFile(file):
x = []
f = open(file, 'r')
x = f.readlines()
f.close()
return(x)
x1 = readFile(file1)
#checks the GUIDs
def checkGUID(GUID, IFC):
A = []
for row in IFC:
if GUID in row:
#print(re.findall(regexIFCnumbers, row))
A.extend(re.findall(regexIFCnumbers, row))
return(A)
#the numbering is not perfectly ordered and varies in some places, so need to index it all to find the correct lines
def indexIFC(IFC):
A = []
for index, row in enumerate(IFC):
if re.findall('^#', row): #starts with a hash #
B = re.findall(r'\#\w+', row)[0]
A.append([B, index])
return(A)
def recurseThrough(inputList, IFC, checkedList, fullList, indexednumbersList):
for item in inputList:
for hashvalueList in indexednumbersList:
if item == hashvalueList[0]:
positionInIFC = hashvalueList[1]
if re.search('^'+item, IFC[positionInIFC]) and item not in checkedList: #if the row begins with the number item in the list
checkedList.append(item)
fullList.append(IFC[positionInIFC])
recurseThrough(re.findall(regexIFCnumbers, IFC[positionInIFC])[1:], IFC, checkedList, fullList, indexednumbersList) #recurses through the next list
return(fullList)
from os import system, name
def clear():
if name == 'nt':
_ = system('cls')
def runTheGUIDCheck(setValue):
inputValue = str(setValue)
print(inputValue)
clear()
try:
B1 = checkGUID(inputValue, x1) #This returns a list with for example [#122, #5, #7889]
checkedList = [] #the list of already checked items
fullList = []
indexedIFClist1 = indexIFC(x1)
#run the function with the initial input, sending in empty array as well as they should be empty/none at the start
outList1 = recurseThrough(B1, x1, [], [], indexedIFClist1)
for index, item in enumerate(outList1):
print(index, item.strip())
return None
except:
print("inserted GUID not found or didn't work")
#dialog
dialog = tk.Tk()
dialog.geometry('500x200')
t1 = tk.Label(dialog, text = 'Check GUID')
t1.grid(row = 0, column = 0, sticky = 'w')
testGUIDAttributes = tk.Entry(dialog, width = 40)
testGUIDAttributes.grid(row = 0, column = 1, columnspan = 50)
button = tk.Button(dialog, text='Run GUID', command = lambda: runTheGUIDCheck(testGUIDAttributes.get()))
button.grid(row = 5, column = 0, sticky = 'w')
dialog.mainloop()
If I select the file directly like this...
file1 = 'thefile.ifc'
instead of with the above this...
tk.Tk().withdraw() # we don't want a full GUI, so keep the root window from appearing
file1 = askopenfilename(initialdir=os.getcwd()) # show an "Open" dialog box and return the path to the selected file - this in the current directory where we'll start looking
then this issue doesn't come up and the command prompt will accept new commands after I close the dialog which is "created" at the end.
Since you have created two instances of Tk(): one in the line tk.Tk().withdraw() and one in the line dialog = tk.Tk(), so the line dialog.mainloop() will not return until both windows are closed. However you cannot close the withdrawn window because it is invisible.
You should create only one instance of Tk(), hide it and then show the file dialog. Then show it back and proceed as normal:
...
# create the main window and hide it initially
dialog = tk.Tk()
dialog.withdraw()
file1 = askopenfilename(initialdir=os.getcwd()) # show an "Open" dialog box and return the path to the selected file - this in the current directory where we'll start looking
...
# show back the main window
dialog.deiconify()
dialog.geometry('500x200')
...
dialog.mainloop()

Using SHUTIL to copy a file into a directory with a space in its name

I'm trying to build a program that will read in a list of files, add a prefix and a suffix to the filename, and then copy the file to a new folder with the new file name. So, for example, a file named "Reports.pdf" would become "PBC_Reports_V1.pdf"
The problem I have is when either the source or destination directory have a space in their name, the shutil module can't find the directory. I'm not sure what I need to do to have shutil recognize the directory, and could use some help.
code is as follows:
## This program is intended to allow a batch of files to have their name changed to standard IA formats
import os, shutil, datetime
from tkinter import *
strFolderName = "\Test"
#strNewPath = basePath+strFolderName
root = Tk()
root.title("Bulk File Renaming")
#root.iconbitmap(r"C:\Coding\FlagIcon.bmp")
############################################################################################################################################
## This section sets up the program's GUI. The buttons have to come after the modules, but this has to come at the beginning of the code
## because, of course, putting all the GUI stuff together would make too much sense. Oh well. GUI until the next line of hashtags.
strF = os.getcwd() #Finds the current file path
strFileIn = StringVar() #This is where we'll store the input path
strFileIn.set(strF) #Puts the current directory in the box
strFileOut = StringVar() #This variable is the output path
strFileOut.set(strF+r"\ConvertedFiles")
str1 = StringVar() #This variable holds the radiobutton output, which represents the file prefix
str1.set("\PBC_") #For some reason, if you try to set a variable in the same line you declare it, it stops working.
strPrefixes = [("PBC (Prepared by Client)", "\PBC_"), #A list (tuple?) of the different prefixes and their meaning.
("WP (Working Paper)", "\WP_"), #To be called futher down when we create the radiobuttons
("COM (Communications)", "\COM_"),
("MIN (Minutes)", "\MIN_"),
("PM (Project Management)", "\PM_"),
("DOC (Anything not created by IA or the OPI)", "\DOC_")]
inRow = 5
inCol = 0
for strPref, val in strPrefixes:
Radiobutton(root, text=strPref, variable=str1, value=val).grid(row =inRow, column=inCol, sticky=(W))
inCol=inCol+1
if inCol>2:
inCol=0
inRow=6
#First, labels
myLabelSource = Label(root, text="Source Folder").grid(row=0, column=0, sticky=(E))
myLabelDestn = Label(root, text="Destination Folder").grid(row=1, column=0, sticky=(E))
myLabelPrefix = Label(root, text="Select Desired Prefix").grid(row=3, column=0, sticky=(W))
#Next, input boxes
#Source files' location
enSource = Entry(root, textvariable=strFileIn).grid(row=0, column = 1, columnspan=3, sticky=(W,E))
#Destination files' location
enDestn = Entry(root, textvariable=strFileOut).grid(row=1, column = 1, columnspan =3, sticky=(W,E))
#################################################################################################################################################
#This module shuts down the program
def progExit():
root.quit() #Closes all the program's stuff
root.destroy() #Actually shuts things down.
#This module gets the date the file was last modified
def getDate(strA):
# Depending on how the file was created/modified, the creation date may be placed in the date modified field and vice versa.
# Here, we take both the modified and created date, see which one is earlier, format it in a YYYY-MM-DD format, and
# return it to the user.
strC = os.path.getctime(strA) #Gets the created date, as time since the epoch
strB = os.path.getmtime(strA) #Gets the modified date, as time since the epoch
if strB<strC:
strB = datetime.datetime.fromtimestamp(strB) #Converts the date into a readable string
strB = str(strB)
strB=strB[0:10] #Leaves just the YYYY-MM-DD fields
return strB
else:
strC = datetime.datetime.fromtimestamp(strC)
strC=str(strC)
strC=strC[0:10]
return strC
#This module determines the location of the '.' at the end of the filename, before the file type indicator.
def fileTypeLength(strName):
inI = len(strName)-1 #Since the first letter in the string is in the 0 position, we subtract 1 from the length to make the loop work
while inI>-1:
if strName[inI] == chr(46): #The '.' char has an ascii value of 46. It's easier for python to understand what we're looking for this way.
return inI
else:
inI=inI-1 #We go from the end towards the front so that if there are any '.' in the file name it won't screw up the program
if inI<0:
outputLabel=Label(root, text="Error finding file suffix for file "+strName).grid(row=8, column = 1)
root.quit()
#This module is where the program actually copies the file, and pastes together everything else.
def copyAllFiles():
inA=0
strPrefix=str1.get()
strNewPath=os.path.abspath(strFileOut.get())
strEntry=os.path.abspath(strFileIn.get())
listOfFiles = os.listdir(strFileIn.get())
if os.path.isdir(strNewPath) == False:
os.mkdir(strNewPath)
for entry in listOfFiles:
if os.path.isfile(os.path.join(strEntry, entry)): # Verify that file in question is a file, not a folder
entryEd=entry # entryEd holds the file name
entry=(os.path.join(strEntry, entry)) # entry holds the file location
strDate = getDate(entry) # Gets the file's creation date
inL = len(entryEd)
inS = fileTypeLength(entryEd) #Finds the position of the '.' at the end of the file name
strFT = entryEd[inS:inL] #Here we remove the file type idenifier, so that it can be moved to the end of the copied filename
entryEd=entryEd[0:inS]
strOutputFile = strNewPath+strPrefix+entryEd+"_"+str(strDate)+"_V1"+str(strFT)
shutil.copy2(entry, strOutputFile) #This copies the file, and all its attributes
inA = inA+1
outputLabel=Label(root, text=str(inA)+" files successfully processed").grid(row=8, column = 1)
##### These are the buttons that the GUI uses to launch the program or shut it down.
buttonExit=Button(root, text="Click here to exit", padx=50, command=progExit).grid(row=7, column=2)
buttonProcess=Button(root,text="Rename Files", padx=50, command=copyAllFiles).grid(row=7, column=1)
#The last thing we have to do is name the GUI that we started building on line 9. Tkinter is a funny module.
root.mainloop()
I can verify that the variables have two slashes for all the folder dividers, so `strOutputFile = C:\\Example Folder\\Example Output\\PBC_Reports_V1.pdf' .
Anybody have a suggestion?

Pypdf2 merger function and startswith

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)

How do I get my tkinter picture viewer working?

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

Categories