Ich melde mich noch mal. Mir ist eingefallen, das es ja sein könnte, das man während des Auslesens das Programm abbrechen will. Damit das Auslesen auch aufhört, muss ich den Thread beenden, was haltet ihr von der folgenden Lösung? Ich gehe davon aus, das bis man nach dem Start-Button auf den Abbrechen-Button geklickt hat, die 'for'-Schleife bereits läuft.
Die Messagebox, die erscheint wenn man keinen Ordner ausgewählt hat habe ich auch noch eingebaut.
Dann habe ich noch eine tkinter-Frage. Ich weis man gibt keine festen Größen an, damit das GUI auf jeder Bildschirmauflösung bedienbar ist. Bei mir ist jetzt alles noch etwas arg zusammengedrückt. 'ttk.Label' bietet 'width' an, das ist standardmäßig auf 20 eingestellt. Kann ich an der Schraube drehen oder verwende ich eher 'columnspan' in 'grid'? Ich hätte zwischen den Labels gerne etwas mehr Platz und das Entry-Feld würde ich gerne länger machen, damit der Benutzer nicht das Gefühl hat, da wäre kein Platz für seine Eingaben.
Code: Alles auswählen
#!/usr/bin/env python3
import re
import tkinter as tk
from datetime import datetime
from pathlib import Path
from queue import Empty, Queue
from threading import Event, Thread
from tkinter import messagebox, ttk
from tkinter.filedialog import askdirectory
from openpyxl import Workbook, load_workbook
WORKBOOK_WITH_READ_DATA = "Ausgelesene Werte.xlsx"
INFO_FILE = "Nicht gelesene Dateien.txt"
DEFAULT_CELL_NAMES = "Volumen, Drehzahl, Gas"
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
master.columnconfigure(0, weight=100)
self.user_select = tk.StringVar()
self.user_select.set("<Kein Oderne ausgewählt>")
ttk.Label(self, text="Ausgewählter Ordner:").grid(row=0, column=0, sticky=tk.W)
self.selected_folder = tk.Label(self, textvariable=self.user_select)
self.selected_folder.grid(row=1, column=0, sticky=tk.W)
ttk.Button(self, text="Ordner auswählen", command=self.open_dialog).grid(
row=2, column=0, sticky=tk.W
)
ttk.Label(self, text="Zellennamen die ausgelesen werden sollen").grid(
row=0, column=4, sticky=tk.W
)
ttk.Label(self, text="getrennt mit Komma eingeben:").grid(
row=1, column=4, sticky=tk.W
)
self.cells_to_read = tk.StringVar()
self.cells_to_read.set(DEFAULT_CELL_NAMES)
ttk.Entry(self, textvariable=self.cells_to_read).grid(
row=2, column=4, sticky=tk.W
)
ttk.Button(self, text="Auslesen", command=self.transfer_gui_data).grid(
row=5, column=5
)
ttk.Button(self, text="Beenden", command=self.stop_program).grid(
row=5, column=4, sticky=tk.E
)
self.process_in_percent = tk.StringVar()
ttk.Label(self, textvariable=self.process_in_percent).grid(row=4, column=4)
self.cancel = Event()
self.process = Queue()
self.folder_path = None
def open_dialog(self):
self.folder_path = Path(askdirectory(mustexist=True))
self.user_select.set(self.folder_path.name)
def on_progress(self, percentage):
self.process.put(percentage)
@staticmethod
def show_info_file_message(status):
if status:
messagebox.showinfo(
"Info Datei",
"Nicht alle gefundenen Dateien konnten gelesen werden, bitte Info-Datei beachten.",
)
def stop_program(self):
self.cancel.set()
self.quit()
def update_process(self):
try:
percent = self.process.get(block=False)
except Empty:
pass
else:
if percent == 100:
self.process_in_percent.set("Fertig!")
return
self.process_in_percent.set(f"{percent:.0f} %")
self.after(10, self.update_process)
def transfer_gui_data(self):
Thread(
target=control_program_sequence,
args=[
self.cells_to_read.get(),
self.folder_path,
self.cancel,
self.on_progress,
self.show_info_file_message,
],
).start()
self.update_process()
def control_program_sequence(
cells_to_read,
folder_path,
cancel,
on_progress=lambda percentage: None,
show_info_file_message=lambda status: False,
):
info_file = False
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
keywords = keywords_to_list(cells_to_read)
workbook, output_file = open_output_file(timestamp, folder_path)
worksheet = workbook.active
files = collect_files(folder_path)
for number, filename in enumerate(files, 1):
if cancel.is_set():
break
on_progress(number / len(files) * 100)
keyword_to_row_content = read_excel_file(filename, keywords)
not_found_keywords = set(name.lower() for name in keywords) - set(
name.lower() for name in keyword_to_row_content
)
if not not_found_keywords:
write_into_output_file(worksheet, keyword_to_row_content)
else:
write_info_file(timestamp, folder_path, filename, not_found_keywords)
info_file = True
workbook.save(output_file)
if info_file:
show_info_file_message(True)
else:
show_info_file_message(False)
def collect_files(folder_path):
files = []
for file in folder_path.glob("**/*.xlsx"):
if file.name.split("_")[-1] != WORKBOOK_WITH_READ_DATA:
files.append(file)
return files
def find_keyword(description, keywords):
match = re.search(rf"\b({'|'.join(keywords)})\b", description, re.IGNORECASE)
if match:
return match.group(1)
return None
def keywords_to_list(cells_to_read):
return [keyword.strip() for keyword in cells_to_read.split(",")]
def open_output_file(timestamp, folder_path):
output_file = folder_path / f"{timestamp}_{WORKBOOK_WITH_READ_DATA}"
if output_file.exists():
workbook = load_workbook(output_file)
else:
workbook = Workbook()
return workbook, output_file
def read_excel_file(filename, keywords):
keyword_to_row_content = {}
worksheet = load_workbook(filename)["Tabelle1"]
for description, content in worksheet.iter_rows(min_col=1, max_col=2):
keyword = find_keyword(description.value, keywords)
if keyword is not None:
keyword_to_row_content[keyword] = content.value
return keyword_to_row_content
def write_info_file(timestamp, folder_path, file, not_found_keywords):
with (folder_path / f"{timestamp}_{INFO_FILE}").open(
"a", encoding="UTF-8"
) as info_file:
info_file.write(
f'-> In der Datei "{file}" wurden folgende Suchwörter nicht gefunden: {not_found_keywords}'
)
def write_into_output_file(worksheet, keyword_to_row_content):
for row in keyword_to_row_content.items():
worksheet.append(row)
def main():
root = tk.Tk()
root.title("Auswertung")
app = App(root)
app.pack()
app.mainloop()
if __name__ == "__main__":
main()
Danke und Grüße
Dennis