win32 öffnet Excel-Workbook nicht

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.
Antworten
Amelie
User
Beiträge: 16
Registriert: Montag 7. März 2011, 14:48

Hallo,
Ich schreibe ein python plugin in 2.5 für Quantum GIS. Das Plugin soll Werte aus der QGIS Attributtabelle auslesen, in ein Excelblatt einfüttern und andere Werte dort wieder auslesen.
Ich versuche auf Excel zurückzugreifen, was vor einiger Zeit auch funktioniert hat. Zwischenzeitlich hatte ich mich anderen Problemen zugewandt und jetzt habe ich folgendes Problem mit Excel:
Zuerst las die .xls an einem Ort, wo ich wohl keine vollen Befugniss hatte, also keine Änderungen an der Datei vornehmen konnte (beim Beschreiben der Zellen kam immer ein Exception Error -2146827284). Daraufhin habe ich den Ort der .xls geändert und es auf meine vollzugreifbare Platte gelegt und die Pfade zum Aufrufend er Datei entsprechend geändert. Wenn ich jetzt versuche aus dem Skript auf Zellen zuzugreifen, scheint die Datei nicht zu laden.

Code: Alles auswählen

    def open_Excel(self) :
        self.xl = win32.gencache.EnsureDispatch('Excel.Application')
        self.xl.Visible = True
        #self.wb = self.xl.Workbooks.Open(self.Vorlage)
        self.wb = self.xl.Workbooks.Open('Z:\\GEB_files\\Master.xls')
        self.sheet = self.wb.Sheets[0] # default Blatt ist 1. Blatt
        if self.sheet is None:
          QMessageBox.warning(None, 'geb', 'Laden der Vorlage gescheitert. \nDas Programm kann nicht korrekt weiter ausgeführt werden.')
[...]    
def input_to_Excel(self) :
        try:             
            # Boolean-Werte in Excel aus technischeAngaben aus Eingabemaske und Fenster zu diesesHaus
            for KEY in self.technischeAngaben.keys():
                controller = 0
                for key in self.technischeAngaben[KEY].keys():
                    if KEY != 'Fenster':
                       if self.technischeAngaben[KEY][key]['Status'] == 'checked':
                          if self.sheet is None:
                            QMessageBox.warning(None, 'geb', 'Laden der Vorlage gescheitert. \nDas Programm kann nicht korrekt weiter ausgeführt werden.')
                          self.sheet.OLEObjects(key).Object.Value = 1
                          controller = 1
                    elif KEY == 'Fenster':
                        if key == 'optF_IWU': 
                            controller = 1 
        except:
                error = traceback.format_exc()
                QMessageBox.warning(None, 'geb', error) 
Keine der beiden QMessageBox Warnungen erscheint (ist 'None' hier etwa nicht der richtige Ausdruck?). Außerdem wird Excel trotz Visible = True nicht sichtbar. Dafür kommt aus einem try/except über den ganzen 2. Absatz:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Dokumente und Einstellungen/amelie.lesser/.qgis/python/plugins\GEB_20110414\GemeindeEnergieBeratung_run.py", line 198, in input_to_Excel
    if self.sheet is None:
AttributeError: 'GemeindeEnergieBeratungRUN' object has no attribute 'sheet'
Wenn ich das entsprechende Laden-Schnipsel in der QGIS Konsole schreiben, friert QGIS ein
und reagiert nicht mehr.
Hatte jemand schon ein ähnliches Problem? Woran könnte es liegen?
Ich habe die Pfadnamen bereits mehrmals auf genau Zeichenfolge und Richtigkeit überprüft, hieran sollte es also nciht liegen.
Zuletzt geändert von Amelie am Freitag 15. April 2011, 14:41, insgesamt 3-mal geändert.
BlackJack

@Amelie: Ob `None` zum Testen das richtige ist, müsstest Du in der entsprechenden Dokumentation nachlesen oder einfach mal ausprobieren in dem Du auf ein garantiert nicht vorhandenes Arbeitsblatt zugreifst und schaust was da zurückgegeben wird.

Die Ausnahme deutet darauf hin, dass `self.sheet` vor dem Aufruf von `input_to_Excel()` nie gebunden wurde. Dazu erst einmal der Hinweis, dass man ausserhalb von der `__init__()`-Methode keine neuen Attribute hinzufügen sollte. Das ist unübersichtlich und kann eben zu solchen Fehlern führen. Wenn man die Attribute alle in der `__init__()` setzt, kann man zum einen dort sehen welche Attribute so ein Objekt hat, ohne dass man die ganze Klasse durchlesen muss, und zum anderen braucht man sich in Methoden dann nur noch Gedanken um die Werte der Attribute machen, und keine mehr ob sie überhaupt existieren.

Also entweder läuft `open_Excel()` nicht bis zu ende durch, oder es wird gar nicht aufgerufen. Das würde dann auch erklären warum das Excel-Fenster nicht angezeigt wird.
BlackJack

Nachtrag: Die `input_to_Excel()` sieht ziemlich gruselig aus. Eine Schleife mit der Laufvariable `KEY`, die eine Schleife mit einer Laufvariable `key` enthält -- das ist verwirrend. Zum Beispiel bei ``self.technischeAngaben[KEY][key]['Status']``. Und zwei Indexoperatoren hintereinander sind IMHO schon ein Grund näher hin zu schauen ob man das nicht anders schreiben kann, aber drei IMHO sind fast immer zu viel.

Die Verschachtelungsreihenfolge erscheint mir auch ungünstig. In der inneren Schleife ändert sich KEY doch gar nicht, es wird aber bei jedem Durchlauf eine Unterscheidung gemacht. Das gezeigte müsste man auch so schreiben können, natürlich mit besseren Namen als `key_a`/`mapping_a` und `key_b`/`mapping_b`:

Code: Alles auswählen

    def input_to_Excel(self):
        try:             
            # Boolean-Werte in Excel aus technischeAngaben aus Eingabemaske und
            # Fenster zu diesesHaus
            for key_a, mapping_a in self.technischeAngaben.iteritems():
                controller = 0
                if key_a == 'Fenster':
                    if 'optF_IWU' in mapping_a:
                        controller = 1
                else:
                    for key_b, mapping_b in mapping_a.iteritems():
                       if mapping_b['Status'] == 'checked':
                          self.sheet.OLEObjects(key_b).Object.Value = 1
                          controller = 1
        except:
            QMessageBox.warning(None, 'geb', traceback.format_exc())
Ob es das `sheet` überhaupt gibt, sollte man vielleicht auch vor die Ganze Aktion ziehen und gar nicht erst mit den Schleifen anfangen die dort etwas eintragen sollen. Und eine Warnung ausgeben und trotzdem weitermachen ist keine gute Idee. Dass das Laden fehlschlug sollte man auch beim Laden feststellen und melden und nicht erst beim Eintragen.

Ist `controller` ein Wahrheitswert? Dann sollte man dafür `True` und `False` statt 1 und 0 verwenden.
Amelie
User
Beiträge: 16
Registriert: Montag 7. März 2011, 14:48

Hey BlackJack,
Vielen dank für deine hilfreichen Antworten. Auch wenn sie nicht direkt auf das Excel-Öffnenproblem abgezielt haben, haben sie mir doch sehr geholfen. Das Problem scheint jetzt gelöst zu sein, zumindest ist es nach einer kleinen Änderung Freitag Abend heute nicht mehr aufgetaucht, danke. Ich hatte eigentlich schon 2 mal auch diesen beiotrag geantwortet, wegen instabiler Internetverbindung dann aber leider aufgegeben.
Ich hoffe jetzt sehen die for-loops auch nicht mehr so gruselig aus.

Code: Alles auswählen

# Boolean-Werte in Excel aus technischeAngaben aus Eingabemaske und Fenster zu diesesHaus
for key_1, map_2  in self.technischeAngaben.iteritems(): # also: Heizung, Solar, RLT, Fenster
    checked = False
    if key_1 == 'Fenster': continue
    for key_2, map_3 in map_2.iteritems():                     if map_3['Status'] == 'checked':                
        checked = True
        self.sheet.OLEObjects(key_2).Object.Value = 1
    if checked == False:
                    QMessageBox.warning(None, 'GemeindeEnergieBeratung', "Feature enthaelt keinen gueltigen Wert \nfuer das Attribut: " + key_1)

# Fenster-Werte aus technischeAngaben
for key_2, map_3 in self.technischeAngaben['Fenster'].iteritems():
    for Himmelsrichtung in diesesHaus['Fenster'].keys():
        if map_3['Status'] == 'checked' and key_2 is not 'optF_IWU':  
            diesesHaus['Fenster'][Himmelsrichtung]['U-Wert']['Wert'] += map_3['U-Wert']
BlackJack

@Amelie: Sieht besser aus. :-) Trotzdem noch ein paar kleine Anmerkungen:

Zuerst ein echter Fehler: ``is`` bzw. ``is not`` sind keine Wert- sondern Identitätsvergleiche. Dass ``key_2 is not 'optF_IWU'`` anscheinend funktioniert ist reiner Zufall bzw. ein Implementierungsdetail von CPython. Man kann nicht davon ausgehen:

Code: Alles auswählen

In [346]: a = 'Hallo Welt'

In [347]: b = 'Hallo Welt'

In [348]: a is b
Out[348]: False

In [349]: a == b
Out[349]: True

In [350]: a = 'spam'

In [351]: b = 'spam'

In [352]: a is b
Out[352]: True

In [353]: a == b
Out[353]: True
Vergleiche auf literale Wahrheitswerte sind unnötig kompliziert. ``checked == False`` würde man eher als ``not checked`` ausdrücken.

Das ``if`` in der inneren "Fenster-Werte"-Schleife müsste man vor die Schleife ziehen können. An der Bedingung kann sich innerhalb der Schleife ja nichts ändern. Und `Himmelsrichtung` als Schlüssel wird gar nicht wirklich benötigt -- man kann da einfach über die Werte iterieren:

Code: Alles auswählen

# Fenster-Werte aus technischeAngaben
for key_2, map_3 in self.technischeAngaben['Fenster'].iteritems():
    if map_3['Status'] == 'checked' and key_2 != 'optF_IWU':
        for himmelsrichtung in diesesHaus['Fenster'].values():
            himmelsrichtung['U-Wert']['Wert'] += map_3['U-Wert']
Antworten