Mit for-Schleife in Excel Tabelle schreiben

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Vielen Dank für deine weitere Hilfe.

Was passiert bei dir in folgender Zeile?

Code: Alles auswählen

SOURCE_PATH = Path("test.xlsx")
Legst du hier einen neuen Namen fest? Zusammen mit dieser Zeile bekomme ich eine Fehlermeldung, die ich leider nicht verstehe.

Redeclared 'SOURCE_PATH' defined above without usage

File "C:\Users\t3wa893\PycharmProjects\TestProject\main.py", line 48, in <module>
main()
File "C:\Users\t3wa893\PycharmProjects\TestProject\main.py", line 42, in main
for signal_name, test_name_to_id in process_tests(iter_tests(SOURCE_PATH)):
File "C:\Users\t3wa893\PycharmProjects\TestProject\main.py", line 17, in iter_tests
workbook = load_workbook(file_path)
File "C:\Users\t3wa893\AppData\Local\Programs\Python\Python310\lib\site-packages\openpyxl\reader\excel.py", line 315, in load_workbook
reader = ExcelReader(filename, read_only, keep_vba,
File "C:\Users\t3wa893\AppData\Local\Programs\Python\Python310\lib\site-packages\openpyxl\reader\excel.py", line 124, in __init__
self.archive = _validate_archive(fn)
File "C:\Users\t3wa893\AppData\Local\Programs\Python\Python310\lib\site-packages\openpyxl\reader\excel.py", line 96, in _validate_archive
archive = ZipFile(filename, 'r')
File "C:\Users\t3wa893\AppData\Local\Programs\Python\Python310\lib\zipfile.py", line 1240, in __init__
self.fp = io.open(file, filemode)
FileNotFoundError: [Errno 2] No such file or directory: 'test.xlsx'



Ohne die oben genannte Zeile sieht es wie folgt aus:

> Signal_1
{'Test A': 'Test_ID_1',
'Test B': 'Test_ID_2',
'Test C': 'Test_ID_3',
'Test D': 'Test_ID_4',
'Test E': 'Test_ID_5',
'Test F': 'Test_ID_6',
'Test G': 'Test_ID_7'}
> Signal_2
{'Test A': 'Test_ID_12',
'Test B': 'Test_ID_23',
'Test C': 'Test_ID_33',
'Test D': 'Test_ID_47',
'Test E': 'Test_ID_53',
'Test F': 'Test_ID_69',
'Test G': 'Test_ID_71'}
> Signal_3
{'Test A': 'Test_ID_14',
'Test B': 'Test_ID_22',
'Test C': 'Test_ID_38',
'Test D': 'Test_ID_47',
'Test E': 'Test_ID_54',
'Test F': 'Test_ID_60',
'Test G': 'Test_ID_74'}
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Krystek: Na ich habe halt Deinen Pfad nicht, also hatte ich mir eine Testdatei im aktuellen Arbeitsverzeichnis angelegt. Und dann halt vergessen das aus dem Programm wieder raus zu nehmen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

na logisch.... :oops:
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Moin,

ich bräuchte da noch einmal ein wenig Hilfe :oops:

Deine Lösung gefällt mir wirklich gut. Allerdings habe ich jetzt das Problem, dass ich die IDs in die entsprechenden Zeilen schreiben muss.
Ich habe in def main() ein for-Schleife hinzugefügt. Das ist mein Ansatz um das Problem zu lösen aber ich weiß nicht, ob mit dieser Schleife eine Lösung meines Problems möglich ist

Code: Alles auswählen

 
 
#!/usr/bin/env python3
from collections import namedtuple
from itertools import groupby
from pathlib import Path
from pprint import pprint

from openpyxl import Workbook, load_workbook
from openpyxl.utils import get_column_letter, column_index_from_string, coordinate_to_tuple

BASE_PATH = Path("C:/Users/t3wa893/PycharmProjects/TestProject/venv")
SOURCE_PATH = BASE_PATH / "tab_1.xlsx"

Test = namedtuple("Test", "id name signal_name")
print("ANFANG")
print(Test)
print("ENDE")
def iter_tests(file_path):
    workbook = load_workbook(file_path)
    print(workbook.sheetnames)
    sheet = workbook["boolean"]
    return (
        Test(test_id_cell.value, test_name_cell.value, signal_name_cell.value)
        for (
            test_id_cell,
            _,
            signal_name_cell,
            _,
            _,
            _,
            test_name_cell,
        ) in sheet.rows
    )


def process_tests(tests):
    return (
        (signal_name, {test.name: test.id for test in group})
        for signal_name, group in groupby(tests, lambda test: test.signal_name)
    )


def main():
    tab1_workbook = load_workbook('C:/tab_1.xlsx')
    tab_1_sheet = tab1_workbook.active

    template_workbook = load_workbook('C:/template.xlsx')
    template_workbook_sheet = template_workbook.active
    #template_workbook['D9'] = test_name_to_id["Test A"]


    for signal_name, test_name_to_id in process_tests(iter_tests(SOURCE_PATH)):
        print(">", signal_name)
        pprint(test_name_to_id)
        print(test_name_to_id["Test A"])
    

    
    for row in template_workbook_sheet.iter_rows(min_row=8, min_col=4, max_row=12, max_col=4):
        for cell in row:
            #print(cell.value, end=" ")
             template_workbook_sheet[D9] = test_name_to_id["Test A"]    	#Wie kann hier D9 als erste Zelle festgelegt werden? Im nächsten Schritt muss dann die ID von Test A und Signal 2 in D10 geschrieben werden usw.
        

    template_workbook.save('C:/tab_2.xlsx')

if __name__ == "__main__":
    main()
    
    
    
VG
Krystek
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Ah und mir fällt ein, dass ich in Abhängigkeit der Anzahl der Signal neue Zeilen in die Vorlage laden muss.
Dementsprechend muss folgendes wohl auch in irgendeiner Form in die Schleife mit eingebunden werden.
Bzw. hast du mir beigebracht die Probleme einzeln zu betrachten und zu lösen. Daher sollte das vielleicht separat betrachtet werden

template_workbook.insert_rows(9)
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Hi,

ich hätte da noch eine Frage zu deinem Code.

Code: Alles auswählen

def iter_tests(file_path):
    workbook = load_workbook(file_path)
    print(workbook.sheetnames)
    sheet = workbook["boolean"]
    return (
        Test(test_id_cell.value, test_name_cell.value, signal_name_cell.value)
        for (
            test_id_cell,
            _,
            signal_name_cell,
            _,
            _,
            _,
            test_name_cell,
        ) in sheet.rows
    )


def process_tests(tests):
    return (
        (signal_name, {test.name: test.id for test in group})
        for signal_name, group in groupby(tests, lambda test: test.signal_name)
    )

ich habe in meiner Quelldatei weitere Reiter, aus denen ich in der gleichen Form das "Wörterbuch" erstellen möchte. Ich habe verschiedene Variante ausprobiert aber es hat nichts funktioniert.

In deiner Version liegen die Daten am Ende in test_name_to_id vor. Ich hätte gerne, dass dann weitere Daten in test_name_to_id_sheet_2, test_name_to_id_sheet_3 usw. vorliegen. Ich hoffe, dass man verstehen kann, was ich meine.

VG
Krystek
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Krystek: Warum willst Du da nummerierte Namen haben? Brauchst Du denn diese Werte alle gleichzeitig, oder werden die nicht unabhängig voneinander verarbeitet, Arbeitsblatt für Arbeitsblatt? Wenn die die gleiche Struktur haben, müsste man bei `iter_tests()` doch nur den Blattnamen als Argument übergeben können. Und dann kann man eine weitere Schleife über die Blattnamen hinzufügen. Oder eine Datenstruktur die Blattnamen und eventuell weitere Informationen enthält durch die sich die Arbeitsblätter unterscheiden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Ich habe in meinem ersten sheet Signale vom Typ boolean. Diese sollen in ein Wörterbuch, da diese später als ein gemeinsamer Block in das template geladen werden soll.

Im nächsten sheet habe ich Signale vom Typ int8, die auch in ein eigenes Wörterbuch sollen. Das liegt daran, dass ich diese Daten im weiteren Verlauf in folgender Form aufrufe bzw. ins template lade:

Code: Alles auswählen

template_workbook_sheet.cell(row=j, column=4).value = test_name_to_id_["Testen des Max. Wertebereichs"]
Hier suche ich nach der ID des Max-Tests. Wenn die Signale vom Typ boolean und int8 sich in einem Wörterbuch befinden würden und ich den gezeigten Code verwende, würde ich die ID von allen Signalen erhalten.
Allerdings möchte ich einmal auf die ID des Max-Test von zB Signal_1 (Datentyp: boolean, erstes sheet) zugreifen können ohne dabei gleichzeitig auf die ID des Max-Test von zB Signal_8 (Datentyp:int8, zweites sheet) zuzugreifen.

Der folgende Code funktioniert so nicht aber verdeutlicht vielleicht, wie das Ergebnis aussehen sollte

Code: Alles auswählen

def iter_tests(file_path):
    workbook = load_workbook(file_path)
    print(workbook.sheetnames)
    sheet_bool = workbook["boolean"]
    sheet_int8 = workbook["int8"]
    sheet_uint8 = workbook["uint8"]
    sheet_int16 = workbook["int16"]
    sheet_uint16 = workbook["uint16"]
    sheet_int32 = workbook["int32"]
    sheet_uint32 = workbook["uint32"]

    return (
        Test(test_id_cell.value, test_name_cell.value, signal_name_cell.value)
        for (
            test_id_cell_bool,
            _,
            signal_name_cell_bool,
            _,
            _,
            _,
            test_name_cell_bool,
            _,
            _,
            _,
            _,
            _,
            _,
            _,
            _,
            _,
            _,
        ) in sheet_bool.rows


        for (
        test_id_cell_int8,
        _,
        signal_name_cell_int8,
        _,
        _,
        _,
        test_name_cell_int8,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        ) in sheet_int8.rows
    )
    
    def main():

    template_workbook = load_workbook('C:/.../template.xlsx')
    template_workbook_sheet = template_workbook.active

    

    j = 9
    for signal_name, test_name_to_id_bool in process_tests(iter_tests(SOURCE_PATH)):
        print(">", signal_name)
        pprint(test_name_to_id_bool)


        template_workbook_sheet.cell(row=j, column=3).value = signal_name


      
            template_workbook_sheet.cell(row=j, column=4).value = test_name_to_id_bool["Testen des Max. Wertebereichs"]
            template_workbook_sheet.cell(row=j, column=5).value = test_name_to_id_bool["Timeout"]
            template_workbook_sheet.cell(row=j, column=6).value = test_name_to_id_bool["Testen des Min. Wertebereichs"]
            template_workbook_sheet.cell(row=j, column=7).value = test_name_to_id_bool["Init+Timeout"]
            template_workbook_sheet.cell(row=j, column=8).value = test_name_to_id_bool["Init"]
            template_workbook_sheet.cell(row=j, column=9).value = test_name_to_id_bool["Init+Timeout (Busruhe) "]
            template_workbook_sheet.cell(row=j, column=10).value = test_name_to_id_bool["Init (Busruhe)"]

        j += 1
        
    k = 15
    for signal_name, test_name_to_id_int8 in process_tests(iter_tests(SOURCE_PATH)):
        print(">", signal_name)
        pprint(test_name_to_id_int8)


        template_workbook_sheet.cell(row=j, column=3).value = signal_name


      
            template_workbook_sheet.cell(row=k, column=4).value = test_name_to_id_int8["Testen des Max. Wertebereichs"]
            template_workbook_sheet.cell(row=k, column=5).value = test_name_to_id_int8["Timeout"]
            template_workbook_sheet.cell(row=k, column=6).value = test_name_to_id_int8["Testen des Min. Wertebereichs"]
            template_workbook_sheet.cell(row=k, column=7).value = test_name_to_id_int8["Init+Timeout"]
            template_workbook_sheet.cell(row=k, column=8).value = test_name_to_id_int8["Init"]
            template_workbook_sheet.cell(row=k, column=9).value = test_name_to_id_int8["Init+Timeout (Busruhe) "]
            template_workbook_sheet.cell(row=k, column=10).value = test_name_to_id_int8["Init (Busruhe)"]

       k += 1
    
    template_workbook.save('C:/.../tab_2.xlsx')

if __name__ == "__main__":
    main()
    
    
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Hallo nochmal,

könntest du mir vielleicht noch kurz erklären wie du das gemeint hattest.
Wenn die die gleiche Struktur haben, müsste man bei `iter_tests()` doch nur den Blattnamen als Argument übergeben können. Und dann kann man eine weitere Schleife über die Blattnamen hinzufügen.
So wie ich das verstanden habe, müsste ich das wie folgt machen.

Code: Alles auswählen

def iter_tests(file_path):
    workbook = load_workbook(file_path)
    print(workbook.sheetnames)
    sheet_bool = workbook["boolean"]
    sheet_int8 = workbook["int8"]
    return (
        Test(test_id_cell.value, test_name_cell.value, signal_name_cell.value)
        for (
        test_id_cell,
        _,
        signal_name_cell,
        _,
        _,
        _,
        test_name_cell,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        ) in sheet_bool.rows

        for (
        test_id_cell_int8,
        _,
        signal_name_cell_int8,
        _,
        _,
        _,
        test_name_cell_int8,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
        _,
    ) in sheet_int8.rows
    )
Was muss ich nun in def main() ändern bzw. hinzufügen, damit ich ein weiteres test_name_to_id erhalte und es nicht einfach überschrieben wird.

Vielen Dank!

Gruß
Krystek
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Krystek: Nein, man muss da anders parametrisieren. Wenn man mehrere Arbeitsblätter aus der gleichen Exceldatei verarbeiten will, dann macht der Dateiname/-pfad als Argument keinen Sinn, denn man will die Datei ja nicht für jedes Arbeitsblatt neu laden. Und der Name des Arbeitsblattes müsste dann auch ein Argument sein. Und falls die Arbeitsblätter nicht ähnlich strukturiert sind, aber die Spalten anders angeordnet sind, dann wäre neben dem Arbeitsblattnamen auch diese Spaltenanordnung ein Argument damit der Code für jedes Arbeitsblatt gleich bleiben kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Vielen Dank für deine Antwort.

Die einzelnen Blätter haben die gleiche Struktur, sodass die Spaltenordnung kein Argument sein muss.

Was meinst du exakt mit dem Begriff "Argument" bezogen auf den Namen des Arbeitsblattes?
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Krystek: Mit Argument meine ich Argumente von Funktionen. Wenn das einzige was sich für die Verarbeitung eines Arbeitsblattes ändert der Name des Arbeitsblattes ist, dann muss man ja nur *den* im Code ändern, beziehungsweise eben als Argument übergeben. Das mit dem Dateinamen kann man so lassen, aber es ist halt ineffizienter die gleiche Exceldatei jedes mal zu laden um ein Arbeitsblatt zu verarbeiten, statt die auch als Argument zu übergeben und vorher beim Aufrufer *einmal* zu laden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Krystek
User
Beiträge: 23
Registriert: Mittwoch 10. November 2021, 10:12

Ok! Super! Vielen Dank für den Tipp. Ich habe es so umgesetzt und das funktioniert auch :)
Antworten