.DOCX in .TXT umwandeln

Code-Stücke können hier veröffentlicht werden.
Antworten
malakai22
User
Beiträge: 2
Registriert: Freitag 18. August 2023, 16:17

Liebes Team,

mein Name ist Mischel und ich arbeite gerade zum ersten mal mit Python.
Gleich vorweg, ich nutze Chat GPT 4 und kam bisher sehr weit. Ich besitze keine fundamentalen Kenntnisse in Python, da ich Motion Designer bin.
Ich möchte aber in Zukunft mehr darüber lernen und probiere gerade eine .EXE zu schreiben.

Folgendes ist mein Ziel, ich habe eine sogenannte "Insertliste" in der 3 Spalte vorhanden sind.

Die erste Spalte ist ein Timecode.
Die zweite Spalte ein Hinweis wie "BB" oder "BB rechts" oder "BB links" oder auch anderes wie Karte, Grafik, Abspann, Infopanel, Insert, UT, Quelle. Das sind die gängigsten Infos.
Manchmal steht aber dort auch noch Zusatz Hinweis, die nicht mit in meine .TXT übernommen werden sollen.

Mein Ziel ist es alle Infos aus der .DOCX die relevant für mich sind, also BB, Inserts und Infopanel auszugeben und so zu formatieren Bspw:

BB,Dr. Andrew Birley,Leiter Ausgrabungsstätte Vindolanda
Infopanel,*BrigantenBriganten*,loser Zusammenschluss keltischer Volksgruppen in Nordost-Britannien
Insert, 1955


Was hier mit Sternchen markiert ist, wird fett geschriebene später in After Effects.
Mein Ziel ist es diesen Vorgang zu automatisieren. Ich möchte die TXT mit Java script laden und mir automatisch Bauchbinden, Inserts und Infopanles erstellen lassen, anhand von dafür vorgesehene Vorlage in einem After Effects Projekt.

Das klappt auch alles soweit, wenn in der .DOCX nur BB steht, aber sobald dort mehr als BB steht Bspw. "BB rechts" oder "BB links" wird es zwar gezählt, aber nicht in meiner TXT ausgegeben.
Was kann ich tun? Das wäre für die Funktion des Skriptes essenziel. Vielleicht kann mir jemand helfen, der mehr Ahnung hat?
Über eine Hilfe wäre ich sehr dankbar :-)

Code: Alles auswählen

[b]
Das ist mein bisheriger code:[/b]

import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from docx import Document
results = {}

def extract_info_from_docx(docx_file, progress_var, seen_entries, counts):
    doc = Document(docx_file)
    table = doc.tables[0]
    print(results)

    total_rows = len(table.rows)
    for i, row in enumerate(table.rows):
        cell_content = row.cells[0].text.strip()
        name = row.cells[1].text.strip()
        subtitle = row.cells[2].text.strip()
        try:
            parts = []  # parts should be reset for every row.
            if len(row.cells) > 2:
                cell_content = row.cells[1].text.strip()
                print(f"Processing cell_content: '{cell_content}'")

                if "BB" in cell_content and cell_content != "":
                    counts["BB"] += 1
                    print(f"Updated count for BB: {counts['BB']}")  # Dieser Befehl gibt jedes Mal eine Nachricht aus, wenn "BB" gefunden und gezählt wird


                elif cell_content in ["Infopanel", "Insert"]:
                    counts[cell_content] += 1

                # ... (anderer Code in der Funktion bleibt unverändert)

                for paragraph in row.cells[2].paragraphs:
                    formatted_text = ""
                    for run in paragraph.runs:
                        if run.bold:
                            formatted_text += run.text + " "
                            update_output_with_tags(cell_content, run.text, "")
                        elif run.italic:
                            formatted_text += run.text + " "
                            update_output_with_tags(cell_content, "", run.text)
                        else:
                            formatted_text += run.text + " "
                            update_output_with_tags(cell_content, "", "")
                    
                    parts.append(formatted_text.strip())


                name = parts[0] if parts else ""
                subtitle = ' '.join(parts[1:]) if len(parts) > 1 else ""
                unique_id = f"{name}_{subtitle}"

                if unique_id not in seen_entries:
                    results[i] = {'marker': cell_content, 'name': name, 'subtitle': subtitle}
                    seen_entries.add(unique_id)

                progress_percentage = (i + 1) / total_rows * 50
                progress_var.set(progress_percentage)
                progress_label["text"] = f"{int(progress_percentage)}%"
                update_output_with_tags(cell_content, name, subtitle)
                real_time_output.yview(tk.END)  # Scroll zum letzten Eintrag
                app.update() 
            else:
                print(f"Skipping row {i} due to insufficient cells. Cells found: {len(row.cells)}, Content: {[cell.text for cell in row.cells]}")
        except Exception as e:
            print(f"Error processing row {i}: {e}")

    return results, counts


def save_to_txt(file_path, info):
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            for line in info.values():
                f.write(f"{line['marker']},{line['name']},{line['subtitle']}\n")
    except Exception as e:
        messagebox.showerror("Error", f"Error saving to TXT file: {str(e)}")


def select_docx():
    try:
        file_paths = filedialog.askopenfilenames(filetypes=[("DOCX files", "*.docx")])
        if file_paths:
            docx_var.set(";".join(file_paths))
            base_name = os.path.splitext(os.path.basename(file_paths[0]))[0]
            txt_var.set(os.path.join(os.path.dirname(file_paths[0]), base_name + ".txt"))
    except Exception as e:
        messagebox.showerror("Error", f"Error reading DOCX file: {str(e)}")
        
def select_txt():
    file_path = filedialog.asksaveasfilename(filetypes=[("TXT files", "*.txt")], defaultextension=".txt")
    if file_path:
        txt_var.set(file_path)

def convert():
    seen_entries = set()
    docx_paths = docx_var.get().split(";")
    counts = {"BB": 0, "Infopanel": 0, "Insert": 0}
    
    if not docx_paths:
        messagebox.showerror("Error", "Please select DOCX paths!")
        return

    try:
        progress_var.set(0)
        progress_label["text"] = "0%"
        

        total_docs = len(docx_paths)
        for idx, docx_path in enumerate(docx_paths):
            txt_path = os.path.splitext(docx_path)[0] + ".txt"
            info, counters = extract_info_from_docx(docx_path, progress_var, seen_entries, counts)
            bb_label["text"] = f"BB: {counters['BB']}"
            infopanel_label["text"] = f"Infopanel: {counters['Infopanel']}"
            insert_label["text"] = f"Insert: {counters['Insert']}"
            info = {k: v for k, v in info.items() if v['marker'] in ['Insert', 'BB', 'Infopanel']}
            print(info)
            save_to_txt(txt_path, info)
            progress_var.set(int((idx + 1) / total_docs * 100))
            progress_label["text"] = f"{int(progress_var.get())}%"
            app.update()

        messagebox.showinfo("Success", "Conversion completed successfully!")

    except Exception as e:
        messagebox.showerror("Error", f"An error occurred: {str(e)}")

app = tk.Tk()
app.title("DOCX to TXT Converter for Kelvinfilm")

# Zeilen und Spalten für das Hauptfenster `app` anpassen
app.grid_rowconfigure(0, weight=1)
app.grid_columnconfigure(0, weight=1)

style = ttk.Style()
style.configure("TButton", background="#448ce6")
style.configure("TProgressbar", troughcolor='white', background='#448ce6')

docx_var = tk.StringVar()
txt_var = tk.StringVar()
progress_var = tk.IntVar()

frame = ttk.Frame(app, padding="10")
frame.grid(sticky=(tk.W, tk.E, tk.N, tk.S))

# Label und Progressbar Initialisierung
progress_label = ttk.Label(frame, text="0%")

# Definiere bb_label, infopanel_label und insert_label hier vorab
bb_label = ttk.Label(frame, text="BB: 0")
infopanel_label = ttk.Label(frame, text="Infopanel: 0")
insert_label = ttk.Label(frame, text="Insert: 0")

# Zeilen und Spalten für das `frame` anpassen
progress_label = ttk.Label(frame, text="0%")
progress_label.grid(row=3, column=0, columnspan=3, pady=5)
ttk.Button(frame, text="Convert", command=convert).grid(row=4, column=0, columnspan=3, pady=10)
frame.grid_rowconfigure(0, weight=1)
frame.grid_rowconfigure(1, weight=1)
frame.grid_rowconfigure(2, weight=1)
frame.grid_rowconfigure(3, weight=1)
frame.grid_rowconfigure(4, weight=1)  # Für die Convert-Taste
frame.grid_rowconfigure(5, weight=1)  # Für das Logo
frame.grid_columnconfigure(1, weight=1)

# Alle Widgets werden unverändert hinzugefügt. Ich habe die `sticky`-Attribute nur dort gesetzt, wo es nötig war.
ttk.Label(frame, text="Open DOCX:").grid(row=0, column=0, pady=5, sticky=tk.W)
ttk.Label(frame, text="Save TXT:").grid(row=1, column=0, pady=5, sticky=tk.W)
ttk.Entry(frame, textvariable=docx_var, width=40).grid(row=0, column=1, pady=5, sticky=(tk.W, tk.E))
ttk.Button(frame, text="Browse", command=select_docx).grid(row=0, column=2, pady=5, padx=5)
ttk.Entry(frame, textvariable=txt_var, width=40).grid(row=1, column=1, pady=5, sticky=(tk.W, tk.E))
ttk.Button(frame, text="Browse", command=select_txt).grid(row=1, column=2, pady=5, padx=5)
progress_bar = ttk.Progressbar(frame, orient="horizontal", mode="determinate", variable=progress_var)
progress_bar.grid(row=2, column=0, columnspan=3, pady=10, padx=5, sticky=(tk.W, tk.E))
progress_label.grid(row=3, column=0, columnspan=3, pady=5)
ttk.Button(frame, text="Convert", command=convert).grid(row=4, column=0, columnspan=3, pady=10)

# Logo-Teil
try:
    logo_path = r"\\nexis\Grafik_TEMP\_Kelvinfilm-Druck\_LOGO_KELVINFILM\Export\Logo_Kelvinfilm_CMYK_blau.png"
    logo_image = tk.PhotoImage(file=logo_path)
    logo_image = logo_image.subsample(7)
    ttk.Label(frame, image=logo_image).grid(row=5, column=0, columnspan=3, pady=5)
except Exception as e:
    print(f"Error loading logo: {e}")
    ttk.Label(frame, text="Kelvinfilm").grid(row=5, column=0, columnspan=3, pady=5)

# Hinzufügen des scrolledtext-Widgets zur GUI, direkt zwischen dem Convert-Button und dem Fortschrittslabel
real_time_output = scrolledtext.ScrolledText(frame, wrap=tk.WORD, width=50, height=10)
real_time_output.grid(row=5, column=0, columnspan=3, pady=10, padx=5, sticky=(tk.W, tk.E))

# Formatierungstags definieren
real_time_output.tag_configure("bold", font=("Arial", 10, "bold"))
real_time_output.tag_configure("italic", font=("Arial", 10, "italic"))
real_time_output.tag_configure("normal", font=("Arial", 10))  # für dünnen Text
real_time_output.tag_configure("other", foreground="dark green", font=("Arial", 10, "bold"))
real_time_output.tag_configure("BB", foreground="blue", font=("Arial", 10, "bold"))
real_time_output.tag_configure("Insert", foreground="green", font=("Arial", 10, "bold"))
real_time_output.tag_configure("Infopanel", foreground="purple", font=("Arial", 10, "bold"))

def update_output_with_tags(marker, name, subtitle):
    formatted_name = name
    formatted_subtitle = subtitle
    
    # Standardtext
    real_time_output.insert(tk.END, f"Verarbeite: ", "bold")
    # Text mit spezifischem Tag je nach Markierungstyp
    if marker in ["BB", "Insert", "Infopanel"]:
        real_time_output.insert(tk.END, f"{marker} ", marker)
    else:
        real_time_output.insert(tk.END, f"{marker} ", "other")
    
    # Formatierter Name und Untertitel
    for part, tag in [(name, "bold"), (subtitle, "italic")]:
        real_time_output.insert(tk.END, f"{part} ", tag)
    
    real_time_output.insert(tk.END, "\n")
    real_time_output.yview(tk.END)  # Scroll zum letzten Eintrag

# Setze das Grid für die vorher definierten Labels
bb_label.grid(row=6, column=0, pady=5)
infopanel_label.grid(row=6, column=1, pady=5)
insert_label.grid(row=6, column=2, pady=5)

# Die von Ihnen bereitgestellte Funktion, um den Code in einem neuen Fenster anzuzeigen
def display_code_in_window():
    code_window = tk.Toplevel(app)
    code_window.title("Code Viewer")
    
    # Hinzufügen eines Scrollbaren Textfeldes
    text_widget = scrolledtext.ScrolledText(code_window, wrap=tk.WORD, width=80, height=40)
    text_widget.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
    
    # Den gesamten Code im Textfeld anzeigen
    with open(__file__, "r", encoding="utf-8") as file:
        code_content = file.read()
        text_widget.insert(tk.END, code_content)
        text_widget.config(state=tk.DISABLED)  # Schreibschutz aktivieren, damit der Benutzer den Code nicht bearbeiten kann

def display_log_in_window():
    log_window = tk.Toplevel(app)
    log_window.title("Log Viewer")
    
    # Hinzufügen eines Scrollbaren Textfeldes
    text_widget = scrolledtext.ScrolledText(log_window, wrap=tk.WORD, width=80, height=40)
    text_widget.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
    
    # Für diesen Beispielcode gehe ich davon aus, dass Sie die Terminalausgabe in eine Datei mit dem Namen "log.txt" im aktuellen Verzeichnis umleiten. 
    # Wenn Sie den Pfad oder den Dateinamen ändern möchten, aktualisieren Sie einfach den folgenden Pfad.
    log_file_path = "log.txt"
    
    if os.path.exists(log_file_path):
        with open(log_file_path, "r", encoding="utf-8") as file:
            log_content = file.read()
            text_widget.insert(tk.END, log_content)
            text_widget.config(state=tk.DISABLED)  # Schreibschutz aktivieren, damit der Benutzer den Inhalt nicht bearbeiten kann
    else:
        text_widget.insert(tk.END, "No log file found!")
        text_widget.config(state=tk.DISABLED)

ttk.Button(frame, text="Show Log file", command=display_log_in_window).grid(row=7, column=0, columnspan=3, pady=10)

app.mainloop()
nezzcarth
User
Beiträge: 1636
Registriert: Samstag 16. April 2011, 12:47

Ich weiß, dass das vermutlich nicht die Antwort ist, die du erwartest, aber ich möchte dazu sagen, dass meine Motivation eher gering ist, mir Code anzuschauen, der von Einsteigenden mit geringen Vorkenntnissen per ChatGPT generiert wurde. Insbesondere, wenn der so lang ist. Inzwischen kommt das hier häufiger vor. Mein Tipp wäre: nimm dir ein paar Nachmittage Zeit und arbeite das offizielle Python Tutorial durch: https://docs.python.org/3/tutorial/index.html Dann kannst du besser verstehen, was ChatGPT da tut oder den Code vielleicht sogar ohne Hilfe selbst schreiben. Bei einem Menschen kann ich zumindest Rückfragen zum Code und der Motivation dahinter stellen (und wenn die Antwort nur ist "Von stackoverflow" kopiert) bzw unterstellen, dass es eine benennbare Motivation gibt; bei ChatGPT weiß ich nur, dass das letztendlich lediglich das Ergebnis statistischer Analysen ist.
malakai22
User
Beiträge: 2
Registriert: Freitag 18. August 2023, 16:17

nezzcarth hat geschrieben: Freitag 18. August 2023, 18:08 Ich weiß, dass das vermutlich nicht die Antwort ist, die du erwartest, aber ich möchte dazu sagen, dass meine Motivation eher gering ist, mir Code anzuschauen, der von Einsteigenden mit geringen Vorkenntnissen per ChatGPT generiert wurde. Insbesondere, wenn der so lang ist. Inzwischen kommt das hier häufiger vor. Mein Tipp wäre: nimm dir ein paar Nachmittage Zeit und arbeite das offizielle Python Tutorial durch: https://docs.python.org/3/tutorial/index.html Dann kannst du besser verstehen, was ChatGPT da tut oder den Code vielleicht sogar ohne Hilfe selbst schreiben. Bei einem Menschen kann ich zumindest Rückfragen zum Code und der Motivation dahinter stellen (und wenn die Antwort nur ist "Von stackoverflow" kopiert) bzw unterstellen, dass es eine benennbare Motivation gibt; bei ChatGPT weiß ich nur, dass das letztendlich lediglich das Ergebnis statistischer Analysen ist.
Vielen dank für deine Antwort!
Danke auch, für den Hinweis mit dem Python Tutorial. Wenn du eine geringe Motivation verspürst, dann ist das ok.
Ich glaube, dass es auch in Zukunft öfter vorkommen wird, ob von mir oder jemand anderen :) Es ist ja zugänglicher geworden, was ganz cool ist und neue Möglichkeiten bietet für Einsteiger, aber es braucht bei manchen Lösung eben doch noch einen Menschen, der den Bereich kennt.
Kenne das ja auch mit den ganzen Bildern/Grafiken die erstellt werden können über Midjourney oder Stable Diffusion.
Als schnell Entwurf ganz nett, aber um daraus dann eine funktionierend Grafik zu machen, die in einer Doku/Film oder mehreren Folgen nutzbar sein kann, braucht es dann doch mehr Custom Arbeit von einem Designer.

Liebe Grüße
Mischel
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@malakai22: Nicht bei manchen Lösungen, sondern eigentlich bei jeder Lösung. Das erste Problem taucht schon dabei auf, dass man es selbst schreiben können muss um genug wissen zu haben um beurteilen zu können ob die Lösung überhaupt gut ist. Und dann macht ChatGPT so viel schlecht, dass man eigentlich sehr viel neu/umschreiben muss.

Der vorliegende Code funktioniert auch gar nicht mit dem beschriebenen Tabellenformat. Den Fehler muss man also erst einmal finden und beheben können.

Dann wird hier GUI-Code und Programmlogik vermischt, Code auf Modulebene ausgeführt der nicht nur Konstanten, Funktionen, und Klassen definiert, und der ist dann auch noch zwischen Funktionsdefinitionen verteilt. Trotzdem werden globale Variablen auch mal als Argumente übergeben oder als Rückgabewert an den Aufrufer zurückgegeben. Also wenn man das absichtlich unübersichtlich und schwer nachvollziehbar schreiben wollte, könnte man es kaum besser machen.

Und bei der Komplexität kann man auch nicht helfen wenn der ”Autor” nicht versteht was da vor sich geht. Bei jedem nicht-trivialen GUI-Programm muss man Python bis einschliesslich objektorientierter Programmierung (OOP) drauf haben, denn das braucht man hier eigentlich.

Ich persönlich war raus hier nachdem ich Tk, also GUI, und globale Variablen gesehen habe und gesehen habe das es ein globales `result` gibt, dass nicht an die erste Funktion übergeben wird, aber von der ersten Funktion an deren Aufrufer zurückgegeben wird, und das dort `counts` übergeben, verändert, und zurückgegeben wird. 260 Zeilen in der Qualität — nein Danke. Das ist nicht wirklich reparierbar, das muss man neu schreiben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten