![]() |
Excel-Funktion im Hintergrund ablaufen lassen
Hallo,
ich habe ein Programm geschrieben, welches eigentlich aus nur 4 Elementen besteht: 1.: Datumsauswahlfeld 2.: Auswahlfeld mit Währungen 3.: Eingabefeld für Betrag 4.: Button "OK" Es rechnet den Betrag, den man in das Eingabefeld (3) eingegeben hat, von der Währung aus (2) in Euro um und gibt ihn aus. Das Datum, welches man ausgewählt hat, wird in die Berechnung mit einbezogen, da der zum angegebenen Datum aktuelle Kurs benutzt wird. Klappt bisher wunderbar... Jetzt möchte ich aber gerne noch eine "Updatefunktion" haben. Vom Prinzip her ist es nicht schwer: Ich lade die Datei runter und ersetze die alte. Nur arbeitet mein Programm mit .txt Dateien, nicht mir XML. Diese XML Dateien müssen vor der Verwendung erst noch mit Excel in eine Tabelle umgewandelt werden, dann müssen die ersten beiden Spalten komplett gelöscht werden und das ganze muss als Textdatei gespeichert werden, die durch Tabs getrennt ist (statt neuer Spalten). Ich würd das ganze ja immer von Hand machen, doch da dieses Programm auch von anderen benutzt werden soll, die das nicht unbedingt können, bräuchte ich da Hilfe. Kann man diese Befehle irgendwie an Excel senden oder das in Delphi selbst machen? Die weiteren Formatierungen bekommen ich dann selber hin... Hoffe auf eure Hilfe! Liebe Grüße! |
Re: Excel-Funktion im Hintergrund ablaufen lassen
|
Re: Excel-Funktion im Hintergrund ablaufen lassen
In der XML-Datei liegen die Daten doch in ungefähr folgender Form vor:
XML-Code:
Da ist es doch beinahe weniger aufwendig hier Daten auszulesen als in einer CSV-Datei?
<Satz id=xxx1>
... <datum>20090101</datum> <kurs>2.34</kurs> ... </Satz> <Satz id=xxx2> ... <datum>20090102</datum> <kurs>2.30</kurs> ... </satz> Warum da den Umweg über Excel? Und es soll vorkommen, das die verschiedenen Excel-Versionen zueinander leicht inkompatibel sind, da ist Ärger durch die Hintertür eigentlich vorprogrammiert. Gruß K-H |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Zitat:
Du brauchst dazu übrigens keinen eigenen Server, denn die Europäische Zentralbank bietet die täglichen Wechselkurse als download an. ![]() Excel ist ebenfalls überflüssig, denn die XML-Dateien lassen sich doch prima parsen. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Also ich hab die Dateien schon von einer Bank (weiß nicht mehr genau welche).
Ich mach das auch gerne mit XML, doch wie lade ich eine XML-Datei in Delphi rein? Edit: Das mit den Makros interessiert mich jetzt schon... Also hier ist er: Sub Makro1() ' ' Makro1 Makro ' Makro ' ' ChDir "C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Dat afiles" Workbooks.OpenXML Filename:= _ "C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Dat afiles\eurofxref-hist.xml" _ , LoadOption:=xlXmlLoadImportToList Columns("A:B").Select Selection.Delete Shift:=xlToLeft ActiveWorkbook.SaveAs Filename:= _ "C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Dat afiles\Mappe2.txt" _ , FileFormat:=xlText, CreateBackup:=False End Sub Wie muss ich den jetzt in Delphi einbinden? Also ich habe bisher:
Delphi-Quellcode:
Ist jetzt ja nicht so ein Monsterquellcode, der würde doch bestimmt auch bei Excel 2003 funktionieren...
procedure TForm1.Button1Click(Sender: TObject);
var Excel : Variant; begin Excel := CreateOleObject('Excel.Application'); |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Keiner weiß was?
Hier ist meine Variante, klappt nur leider nicht...
Delphi-Quellcode:
excel.Application.SheetsInNewWorkBook := 1; Excel.Workbooks.Add; //Der Befehl hier existiert in Excel angeblich nicht: Excel.ChDir('C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles'); Excel.Workbooks.OpenXML; Excel.Filename:='C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles\eurofxref-hist.xml'; Excel.LoadOption:=Excel.xlXmlLoadImportToList; Excel.Columns('A:B').Select; Excel.Selection.Delete; Excel.Shift:=Excel.xlToLeft; Excel.ActiveWorkbook.SaveAs; Excel.Filename:='C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles\Newdata.txt'; Excel.FileFormat:=Excel.xlText; Excel.CreateBackup:=False; Komplett also so:
Delphi-Quellcode:
Wenn ich das CHDir weglasse, da es ja eigentlich nicht gebraucht wird, da es nur im Dialog ausgewählt wurde, Wird die Zeile darauf markiert und es kommt die Meldung: Ungültige Parameteranzahl
procedure TForm1.Button1Click(Sender: TObject);
var Excel : Variant; begin Excel := CreateOleObject('Excel.Application'); excel.Application.SheetsInNewWorkBook := 1; Excel.Workbooks.Add; Excel.ChDir('C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles'); Excel.Workbooks.OpenXML; Excel.Filename:='C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles\eurofxref-hist.xml'; Excel.LoadOption:=Excel.xlXmlLoadImportToList; Excel.Columns('A:B').Select; Excel.Selection.Delete; Excel.Shift:=Excel.xlToLeft; Excel.ActiveWorkbook.SaveAs; Excel.Filename:='C:\Program Files\Borland\Delphi5\Projects\Währungsrechner\Datafiles\Newdata.txt'; Excel.FileFormat:=Excel.xlText; Excel.CreateBackup:=False; end; Hab das noch nie gemacht, wenn mir also einer sagen kann, wie man das macht, dann kann ich das sicher auf weitere Makros übertragen... [edit=mkinzler]Code-Tag durch Delphi-Tag ersetzt Mfg, mkinzler[/edit] |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Ich hab es mal eben zusammengeschustert:
Delphi-Quellcode:
Aber leider habe ich keine Möglichkeit gefunden die Nachfrage beim Speichern zu umgehen.
excel.visible:=true; { visible nur für test-zwecke }
excel.workbooks.Open(Filename:='c:\temp\testdatei0.xml'); //,LoadOption:=xlXmlLoadImportToList); excel.activeworkbook.sheets[1].activate; { 1. Worksheet -- wahrscheinlich nicht notwendig} excel.activeworkbook.sheets[1].columns[1].delete; excel.activeworkbook.sheets[1].columns[1].delete; excel.ActiveWorkbook.SaveAs(Filename:='c:\temp\testdatei_stripped.txt', FileFormat:=$FFFFEFC2, CreateBackup:=False); excel.ActiveWorkbook.Close; excel.quit; Aber wenn Du schon kein originäres XML lesen willst, und Du schon Excel offen hast, warum dann nicht gleich im "Original" lesen?
Delphi-Quellcode:
Bei einigen Sheets könnten Excel und der User unterschiedlicher Meinung sein welche Zellen genutzt werden!
{---- Auslesen von Excel sheets mit StartSpalte/StartZeile bis Endspalte/EndZeile ----}
maxZeil:=excel.activesheet.usedrange.rows.count; maxSpal:=excel.activesheet.usedrange.columns.count; if StartZeile=0 then Startzeile:=1; if StartSpalte=0 then StartSpalte:=1; {---------------------} if endZeile>maxzeil then endZeile:=maxZeil; if endSpalte>maxspal then endSpalte:=maxSpal; for i:=startZeile to endZeile do begin for j:=startSpalte to endSpalte do begin zelle:=excel.activesheet.cells[i,j]; { Lese Wert } ll.Add(zelle); { Wert ausgeben } Gruß K-H Nachtrag: xlToLeft u.ä. sind Konstanten die delphi nicht kennt. Die muß man sich selbst heraus suchen. z.B. FileFormat:=$FFFFEFC2 = FileFormat:=xlText 2.Nachtrag:
Delphi-Quellcode:
Da ist ll ein Tlist oder TSringlist oder irgend so etwas....
ll.Add(zelle); { Wert ausgeben }
|
Re: Excel-Funktion im Hintergrund ablaufen lassen
Erstmal danke für die Mühe! Also Excel geht auf, wird sichtbar und lädt die Liste. Da die LoadOption nicht erkannt wird, musste ich die zu löschenden Spalten etwas ändern, da sie anders geladen wurden. Soweit aber kein Problem.
Jetzt soll Excel nur noch wieder geschlossen werden. Die Arbeitsmappe selber schließt sich, das Programm jedoch nicht. Ansonsten ist es perfekt, danke! Zu 2: Ich hab Excel nicht offen und es soll auch nicht geöffnet sein. Ich will es praktisch nur zum umkodieren benutzen - fast ohne dass der Nutzer es mitkriegt. Die Voraussetzung für das Update ist natürlich Excel... Das haben die dafür vorgesehenen Nutzer aber. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Zitat:
Was ich meinte, wenn Du Excel bemühst um die beiden ersten Spalten zu löschen, dann kannst Du auch gleich die Spalte 3 und 4 und .... auslesen. Und da keine Datei gespeichert wird, bekommt der Benutzer noch weniger mit. Gruß K-H (und ich bin immer noch der Meinung XML direkt zu lesen ist einfacher..) |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Ich versteh schon, was du meinst, nur brauch es immer, bis Excel geöffnet ist und bis ein Befehl ausgeführt ist - dann würde erstens eine längere Wartezeit entstehen und wenn der User gleichzeitig mit Excel arbeiten will, dann sieht er auf einmal eine für ihn völlig sinnlose Tabelle.
Es gibt noch ein Problem: Bevor die Datei codiert werden kann, muss ich die ja erst runterladen. Also: uses URLMon; URLDownloadToFile(nil, PChar('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml'),PChar('C:\Programme\Währungsrechner\Dat afiles\eurofxref-hist.xml'),0,nil); Die Datei ist vorhanden und wird täglich aktualisiert. Ich hab das ganze in einen Try - Except - Block gesetzt, angeblich wurde die Datei runtergeladen... Nur ist später keine Datei vorhanden. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Ok, das Ganze liegt an Schreibrechten... Zu anderen Verzeichnissen kann ich laden. Kann ich das irgendwie abstellen?
|
Re: Excel-Funktion im Hintergrund ablaufen lassen
Liste der Anhänge anzeigen (Anzahl: 2)
"Delphi-Narr", du machst deinem Namen alle Ehre!
Mein Rat war doch kein Excel zu benützen. Die Anwendung im Anhang zeigt wie einfach man die Umrechnungskurse ohne Excel einlesen kann. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Das "Narr" bezieht sich eigentlich nicht auf "ein Narr sein"->nichts können sondern "vernarrt"->mögen...
Naja, Dieses Programm benutzt nur die täglichen Daten. Ich habe mal versucht, die gesamte Datenbank seit 1999 reinzuladen, er lädt nur die ersten beiden Daten. Außerdem fehlt das Datum und mir fehlt die MSXML2_TLB.pas Ich hatte bisher noch keine Erfahrungen mit XML und auch keine Ahnung von ihm im Zusammenhang mit Delphi. Ich würde mir das ganze gerne erklären lassen :D Da ich Delphi nur in der Schule lerne, ist das, was ich hier mache eigentlich "über meinen Fähigkeiten", also kann ich sowas nicht wissen. Natürlich habe ich schon gegooglet nach XML in Delphi etc. und auch nach der MSXML2_TLB.pas, doch die gefundene Datei hat sich hinterher als HTML-Datei entpuppt und zu XML hab ich nur Beschreibungen ab Delphi 7 gefunden. Wenn du mir das ganze erklären könntest und die MSXML2_TLB.pas anhängen könntest (sofern erlaubt), wäre ich dir sehr dankbar! Ich weiß, ich bin manchmal ein bisschen umständlich, liegt aber dadran, dass ich das ganze erst seit August lerne... Liebe Grüße |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Die Datei MSXML2_TLB lässt man sich am Besten von Delphi erzeugen.
Je nach Delphi Version unterscheiden sich die Schritte etwas. * Projekt schliesen * im Menü entweder (D5-D7)Projekt->Typbibliothek importieren oder (D2007)Komponente->Komponente importieren... wählen. * man bekommt eine sehr umfangreiche Liste von Typbiliotheken auf dem Rechner * daraus "Microsoft XML, v4.0" auswählen * keinen Komponentenwrapper erzeugen lassen * das Defaultverzeichnis ...\Imports nicht ändern * Unit anlegen => es wird MSXML2_TLB.pas in dem Imports-Verzeichnis erzeugt. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Ah, vielen Dank!
Ich arbeite mich dann mal in XML ein... Gute Nacht allerseits |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Alternativ kann ich himXML empfehlen.
MfG Fabian |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Delphi-Quellcode:
procedure TForm1.UpdateFromXML(const data: string; dataIsFile:Boolean);
var document : IXMLDOMDocument2; node, node2 : IXMLDOMNode; ElementCube : IXMLDOMElement; i : Integer; currency : string; rate : Double; begin document := CoDOMDocument40.Create; if dataIsFile then document.load(data) else document.loadXML(data); // CheckDocumentError(document); node2 := document.documentElement; node := node2.lastChild; // node := document.documentElement.selectSingleNode('//Cube'); node := node.firstChild; // Schleife über alle vorhandenen Währungen for i := 0 to node.childNodes.length-1 do begin ElementCube := node.childnodes.item[i] as IXMLDOMElement; currency := ElementCube.getAttribute('currency'); rate := ElementCube.getAttribute('rate'); UpdateCurrency(currency, rate); end; end; Dies ist ja dein Code, data muss also der Dateipfad+Name sein. Also praktisch Ordner/dateiname.xml So, ich hab das auch mal gemacht. Bei mir kommt allerdings eine Zugriffsverletzung:
Delphi-Quellcode:
variable i ist für spätere Schleife(n)...
procedure TForm1.Button1Click(Sender: TObject);
var i:integer; node:IXMLDOMNode; Element:IXMLDOMELEMENT; begin XMLDokument:=CoDOMDocument40.Create; XMLDokument.loadXML('datafiles/euroxref-hist.xml'); node := XMLdokument.documentElement.selectSingleNode('//Cube'); Element:=node.childnodes.item[1] as IXMLDOMElement; end; Wenn ich dann auf besagten Button klicke, wird die 3. Zeile nach Begin (node := ...) markiert und die Zugriffsverletzungsnachricht Modul 00442242 Adresse 00000000 erscheint. Ich seh den Unterschied nicht genau... Ok, habe andere Bezeichnungen aber sonst müsste alles gleich sein, oder? Habe das Tutorial ![]() |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Zitat:
Load() holt die Daten aus einer Datei oder URL. Nach dem Laden muss überprüft werden, ob XMLdokument.documentElement = NIL ist. Falls ja, ist beim Laden ein Fehler aufgetreten. Bei deinem Code liegt das daran, dass du fälschlicherweise LoadXML() statt Load() verwendet. |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Achso, ok. Jetzt habe ich die Datei eurofxref-hist.xml
Die ist so aufgebaut:
XML-Code:
Nicht zu jedem Datum ist jede Währung vorhanden. Im Optimalfall sollten es (Stand wie oben) 2802 Einträge sein. 20 Währungen aus dem ersten Cube sind in dieser Zahl vorhanden. Ich habe auch nicht nachgeprüft, ob bei älteren Daten andere Währungen sind. Ist auch ersteinmal unwichtig.
<?xml version="1.0" encoding="UTF-8" ?>
- <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"> <gesmes:subject>Reference rates</gesmes:subject> - <gesmes:Sender> <gesmes:name>European Central Bank</gesmes:name> </gesmes:Sender> - <Cube> - <Cube time="2009-12-10"> <Cube currency="USD" rate="1.473" /> <Cube currency="JPY" rate="130.03" /> <Cube currency="BGN" rate="1.9558" /> <Cube currency="CZK" rate="25.705" /> <Cube currency="DKK" rate="7.4417" /> <Cube currency="EEK" rate="15.6466" /> <Cube currency="GBP" rate="0.9043" /> <Cube currency="HUF" rate="272.05" /> <Cube currency="LTL" rate="3.4528" /> <Cube currency="LVL" rate="0.7072" /> <Cube currency="PLN" rate="4.1405" /> <Cube currency="RON" rate="4.2382" /> <Cube currency="SEK" rate="10.4367" /> <Cube currency="CHF" rate="1.5113" /> <Cube currency="NOK" rate="8.4355" /> <Cube currency="HRK" rate="7.2668" /> <Cube currency="RUB" rate="44.8505" /> <Cube currency="TRY" rate="2.202" /> <Cube currency="AUD" rate="1.6074" /> <Cube currency="BRL" rate="2.5904" /> <Cube currency="CAD" rate="1.5472" /> <Cube currency="CNY" rate="10.0556" /> <Cube currency="HKD" rate="11.4168" /> <Cube currency="IDR" rate="13905.61" /> <Cube currency="INR" rate="68.7" /> <Cube currency="KRW" rate="1716.4" /> <Cube currency="MXN" rate="19.0312" /> <Cube currency="MYR" rate="5.0045" /> <Cube currency="NZD" rate="2.0236" /> <Cube currency="PHP" rate="68.075" /> <Cube currency="SGD" rate="2.0473" /> <Cube currency="THB" rate="48.793" /> <Cube currency="ZAR" rate="11.0565" /> </Cube> - <Cube time="2009-12-09"> <Cube currency="USD" rate="1.4768" /> <Cube currency="JPY" rate="129.91" /> <Cube currency="BGN" rate="1.9558" /> <Cube currency="CZK" rate="25.749" /> <Cube currency="DKK" rate="7.4416" /> ... </Cube> </cube> Also zur Auswahl stehen die aufgelisteten Währungen. Erst muss also das Datum gesucht werden. Hatte das bisher so, dass alle Daten durchsucht werden, solange das Datum2 (das aus der Datei) > ist als das aus der Datumsauswahl. Dann wird die Variable i auf die Stelle gesetzt, ab wo der Kurs des eingegebenen Datums gilt. Also Eingabe: 27.10.2009 Datei: 0 -> 12.12.2009 1 -> 11.12.2009 2 -> 30.11.2009 3 -> 15.11.2009 4 -> 28.10.2009 5 -> 26.10.2009 als i erhalte ich also 5. Jetzt müssen alle Unterpunkte von Cube[i] durchsucht werden, bis der Wert von currency gleich die angegebene Währung ist und dann muss die Variable Kurs auf den Wert vom Attribut "rate" gesetzt werden. Wenn die Währung nicht existiert, soll der Anwender dies mitgeteilt bekommen. Könnte sich jemand die Zeit nehmen, mir mal zu erklären, wie ich das jetzt realisieren kann? Liebe Grüße! |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Also ich habe es jetzt so versucht:
Delphi-Quellcode:
Ich erhalte jedoch immer eine Zugriffsverletzung bei Adresse 00000000 und die Zeile node := XMLdokument... wird markiert. Ich versteh leider nicht, was ich falsch mache... Den Text aus manchen Nodes kann ich anzeigen lassen, aber ich brauche ja die Attribute. Und an die komm ich nicht ran.
procedure TForm1.Button1Click(Sender: TObject);
var i:integer; node:IXMLDOMNode; Element:IXMLDOMELEMENT; begin XMLDokument:=CoDOMDocument40.Create; XMLDokument.loadXML('datafiles/xref-hist.xml'); node := XMLdokument.documentElement.lastChild; node:=node.firstChild; Element:=node.childnodes.item[1] as IXMLDOMElement; ... end; |
Re: Excel-Funktion im Hintergrund ablaufen lassen
Delphi-Quellcode:
"datafiles/xref-hist.xml" - soll das ein Dateiname sein?
XMLDokument.loadXML('datafiles/xref-hist.xml');
Problem1: Unter Windows ist der Pfadtrenner ein Backslash (\) und nicht ein (/). Problem2: der Dateiname ist weder Fisch noch Fleisch. Entweder gibts du nur den Dateinamen an (dann muss die Datei im aktuellen Verzeichnis liegen) oder den Dateinamen inklusive den kompletten Pfad. Problem3: die Funktion heisst Load() nicht LoadXML()!!!! Ein kleine Änderung im Code schafft Klarheit:
Delphi-Quellcode:
PS: du musst die Antworten hier schon sorgfältig lesen; beim Programieren kommt es auf jedes Zeichen an.
dateiname := 'C:\datafiles\xref-hist.xml';
if FileExists(dateiname) then XMLDokument.Load(datei) else raise Exception.CreateFmt('Datei %s nicht vorhanden', [dateiname]); Ein Zeichen falsch z.B. / statt \ und es funkt nicht. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:54 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz