This script opens a basic form which allows one to search all files in a directory for instances of a string, before outputting results to a textbox and CSV (found in the script location). Each line outputted is of the format:
Word {string} in {file} on line {line number}: {full line}
Steps:
- Specify Directory
- Input string you would like to search for
- Toggle ignore case on or off
- Click Go.
Notes:
This is not completely finished yet. Script will only search basic TXT files. I'm also a beginner with Tkinter/GUIs, so haven't moved buttons around, etc. I've just added the buttons & labels one after the other. This will eventually be rectified. There's also an issue where if I'm searching a large directory of files, the form will look like it's frozen until it's finished searching. Finally, I may have went overboard on some of the error handling. I'm newish to this as well.
I'm trying to be a better programmer, particularly with structuring and readability, so any constructive criticism would be much appreciated.
from tkinter import filedialog
from tkinter.scrolledtext import ScrolledText
import pandas as pd
import tkinter as tk
import os
import re
import sys
################ FUNCTIONS ################
def save_to_file(wordlist):
"""Save list to CSV format and save CSV to script directory"""
script_directory = os.path.dirname(sys.argv[0]) # Path where script is being run from
df = pd.DataFrame(data={"Results": wordlist})
df.to_csv(script_directory+"/mycsv.csv", sep=",", index=False, line_terminator='\n')
def print_to_textbox(wordlist):
"""Print all lines in wordlist to textbox"""
for lines in wordlist:
text_box.insert("end", "\n"+lines)
if len(wordlist) == 0:
text_box.insert("1.0", "\nNothing To Display")
def browse_button():
"""Button will open a window for directory selection"""
global folder_path
selected_directory = filedialog.askdirectory()
folder_path.set(selected_directory)
def search_files():
"""Search all files in specified directory"""
folderPath = folder_path.get()
searchString = string_entry.get()
text_box.delete("1.0", tk.END)
# Set word case option on/off.
if var1.get() == 1:
IGNOREWORDCASE = True
else:
IGNOREWORDCASE = False
# List to store all lines where string is found.
wordlist = []
# Loop through all files and search for string, line by line.
for (path, directories, files) in os.walk(folderPath, topdown=True):
for file in files:
filepath = os.path.join(path, file)
try:
with open(filepath, 'r') as currentfile:
for lineNum, line in enumerate(currentfile, 1):
line = line.strip()
match = re.search(searchString, line, re.IGNORECASE) if IGNOREWORDCASE else re.search(searchString, line)
if match:
word = f"Word '{searchString}' in '{file}' on line {lineNum}: {line}"
wordlist.append(word)
except IOError as ex:
words = f"Error; {file}; {ex}"
wordlist.insert(0, words)
except EnvironmentError as ex:
words = f"Error; {file}; {ex}"
wordlist.insert(0, words)
except OSError as ex:
words = f"Error; {file}; {ex}"
wordlist.insert(0, words)
except UnicodeDecodeError as ex:
words = f"Error; {file}; {ex}"
wordlist.insert(0, words)
except:
words = f"Error; {file}"
wordlist.insert(0, words)
# Print all lines to text box.
print_to_textbox(wordlist)
# Save to file.
save_to_file(wordlist)
################ TKINTER SCRIPT ################
# Setup Window.
window = tk.Tk()
window.geometry("900x500")
window.title("String Search")
# Button to select directory.
select_directory = tk.Button(window, text = "Select Directory", command=browse_button)
select_directory.pack()
# Label to store chosen directory.
folder_path = tk.StringVar()
directory_label = tk.Label(window, textvariable = folder_path, bg="#D3D3D3", width=70)
directory_label.pack()
# Entry to type search string.
string_entry = tk.Entry(window, bg="#D3D3D3")
string_entry.pack()
# Check button to turn ignore case on/off.
var1 = tk.IntVar()
stringCase_select = tk.Checkbutton(window, text='Ignore Case',variable=var1, onvalue=1, offvalue=0)
stringCase_select.pack()
# Button to run main script.
go_button = tk.Button(window, text="Go", command=search_files)
go_button.pack()
# Button to quit the app.
quit_button = tk.Button(window, text = "Quit", command=window.quit)
quit_button.pack()
# Text box to display output of main text.
text_box = ScrolledText(width=110, borderwidth=2, relief="sunken", padx=20)
text_box.pack()
# Button to clear the text box display.
clear_button = tk.Button(window, text = "Clear", command = lambda: text_box.delete("1.0", tk.END))
clear_button.pack()
# Run an event loop.
window.mainloop()