Python Tkinter GUI - Cari File Dalam Direktori Untuk Instance A String
Skrip ini membuka formulir dasar yang memungkinkan seseorang mencari semua file dalam direktori untuk contoh string, sebelum mengeluarkan hasil ke kotak teks dan CSV (ditemukan di lokasi skrip). Setiap baris yang dihasilkan memiliki format:
Kata {string} di {file} pada baris {line number} : {full line}
Langkah:
- Tentukan Direktori
- String masukan yang ingin Anda cari
- Aktifkan atau nonaktifkan abaikan kasus
- Klik Go.
Catatan:
Ini belum sepenuhnya selesai. Script hanya akan mencari file TXT dasar. Saya juga seorang pemula dengan Tkinter / GUI, jadi belum memindahkan tombol, dll. Saya baru saja menambahkan tombol & label satu per satu. Ini pada akhirnya akan diperbaiki. Ada juga masalah di mana jika saya mencari direktori file yang besar, formulir akan terlihat seperti dibekukan sampai pencarian selesai. Akhirnya, saya mungkin berlebihan dalam beberapa penanganan kesalahan. Saya juga baru dalam hal ini.
Saya mencoba menjadi programmer yang lebih baik, terutama dengan penataan dan keterbacaan, jadi kritik yang membangun akan sangat dihargai.
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()
Jawaban
PEP-8
Untuk menjaga konsistensi saat menulis kode, kode Python mengikuti konvensi penamaan PEP-8 . Sebagai contoh
Sumber
Nama fungsi harus huruf kecil, dengan kata-kata dipisahkan oleh garis bawah seperlunya untuk meningkatkan keterbacaan.
Nama variabel mengikuti konvensi yang sama dengan nama fungsi.
Pustaka yang Anda gunakan saat ini, yang tkinterjuga mengikuti konvensi penamaan yang sama. Anda dapat melihat bahwa kelas seperti Button, Canvas, dan StringVar(). Semua mengikuti CamelCasekarena mereka kelas.
Widget pemosisian di aplikasi Anda
Meskipun .pack()berfungsi, Anda akan segera melihat batasannya ketika Anda mulai mencoba memposisikan widget di tempat tertentu, seperti tombol keluar yang biasanya tetap di pojok, Judul biasanya ditempatkan di bagian atas. Dalam skenario .pack()ini sangat terbatas.
Cara yang umum adalah menggunakan .place()untuk memposisikan widget di aplikasi Anda. Ini memiliki banyak argumen seperti bordermodedan anchoruntuk menyesuaikan tugas Anda, dua yang utama xdan yyang pada dasarnya hanya titik horizontal dan vertikal di mana widget Anda akan ditempatkan.
Berikut adalah jendela Tkinter sederhana yang geometry("500x500")telah saya buat untuk menunjukkan penggunaan place(). Ini juga memiliki Labelwidget sebagai sederhana.
Contoh: widget.place(x = 300,y = 50)
Anda dapat memutuskan di mana Anda ingin menempatkan widget Anda secara akurat.
Penataan Tkaplikasi
@Reinderien menyarankan Anda TKINTER_SCRIPTke dalam suatu fungsi. Ini masuk akal karena Anda sudah memiliki fungsi yang bagus seperti search_files(), mengapa Anda memiliki aplikasi Tkinter di lingkup utama?
Sementara beberapa mungkin tidak setuju, saran saya adalah Anda memilih pendekatan berorientasi objek , yang akan membantu menjaga kode Anda tetap bersih. Dalam situasi Anda, masuk akal untuk memiliki satu MainApplication()kelas. Berikut adalah contoh sederhana tentang tampilannya
class MainApplication:
def __init__(self,window,size = "500x500"):
self.window = window
window.geometry(size)
self.entry_box = # Entry box widget
self.title = # Title label
# More widgets that are relevant to MainApplication
def close_application():
# print any messages on window like "Thank you for using..."
self.window.close()
root = tk.Tk()
window = MainApplication(root)
root.mainloop()
Menyusun aplikasi Tkinter
Saran kecil
- Gunakan
root.iconbitmap( #icon image )untuk menyetel16x16ikon untuk aplikasi tkinter Anda, ini akan muncul alih-alih bulu kecil yang datang TkinterMemiliki variasi widget yang sangat banyak, Anda mungkin akan menemukan widget baru dari daftar widget utama ini yang mungkin cocok untuk aplikasi Anda.- kotak pesan di tkinter akan cocok untuk saat pengguna mungkin melakukan sesuatu yang salah, atau mungkin Anda ingin mendapatkan konfirmasi.
Contoh kotak pesan
Pertimbangkan untuk menggunakan petunjuk jenis PEP-484 untuk tanda tangan fungsi Anda.
Gunakan sejenis linter pyflakes, flake8atau blackyang akan memberi Anda sejumlah saran tentang format kode Anda.
Pindahkan Anda TKINTER SCRIPTke satu atau beberapa fungsi, bukan dalam lingkup global.
Gunakan pathlib, sehingga ini:
script_directory+"/mycsv.csv
dapat
Path(script_directory) / 'mycsv.csv'
Jika var1.get()mengembalikan 0/1, maka Anda cukup menulis
IGNORE_WORD_CASE = bool(var1.get())
meskipun Anda harus memberi var1nama yang lebih bermakna.