[Solved]Windows - Qt alkalmazás nem talál egy input állományt

Sziasztok!

Adott egy kis alkalmazás, Qt 4.7.0-n alapul, és ezt szeretném lefordítani Linux és Windows (XP) alatt is.
Linux és Windows alatt is hibátlanul lefordul a kód, de az alábbi kódrészletre Win alatt feldobja az msgBox-ot (nem találja a fájlt):
QFile allomany("./varosok.dat"); // Bemeneti állomány (varosok.dat)
if (!allomany.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox msgBox; // Üzenetablak
msgBox.setText("IO Hiba"); // Üzenet beállítása
msgBox.setInformativeText("A varosok.dat allomany nem talalhato."); // Magyarázat beállítása
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
} else {
QTextStream be(&allomany);
. . .

A kezelendő fájlt bemásoltam a (projekt.neve)-build-desktop alá, és kínomban már a forráskód mellé is, de semmi hatása.
Qt Creator ugyanaz a verzió mindkét OS alatt.

Mi lehet a gyógyír?

(Zárójelben jegyzem meg: a windows által kezelt partíción nem volt elég hely, így egy külső meghajtóra telepítettem a Qt Creator-t, és build után a projektnek létrehozott egy D:\home\richard\... (ez a Linux-on használt felh. nevem, Winen más van) könyvtárszerkezetet, és oda került a lefordított kód.)

szerk.: Cím módosítva

Hozzászólások

Próbáld ki, ha

QFile allomany("./varosok.dat");

helyett ezt írod:

QFile allomany("varosok.dat");

Próba szerencse.
---------------------------
Oszt jónapot!

Kipróbálni most nem tudom, mert nincs winen Qtém, de ez nekem nagyon gyanús:

QFile allomany("./varosok.dat");

A "./" nekem elég unixos, próbálj ki egy windows-os abszolút útvonalat, pl: "d:/varosok.dat". Ha ez működik, akkor vagy QDir::current ()-el lehet kinyerni a program könyvtárát vagy csinálsz a kódban egy OS specifikus változatot az abszolút névvel.

Nos, az alábbi "fejlemények" vannak:

Csak most vettem észre, hogy Warning keletkezik a fordítás során: "Qmake does not support build directores below the source directory." Nem tudom ez számít-e (ez csak Winen jelentkezik)

Átírtam a fájl elérési útját relatívról teljesre, biztos ami biztos, és a statikus hibaüzenet helyett errorString() segítségével állapítom meg a hiba okát. Futtatáskor ezt dobja: "Az eszköz nem áll készen." Az állomány nincs megnyitva más programban, nem tudom hogy mire gondol.

Ezekkel mit lehet kezdeni?

Ez a warning tök lényegtelen, nincs köze a problémádhoz. A D:\varosok.dat lehet hogy nem jó, jobbra per jel elvileg a Qtén belül a szabvány, akárhol is dolgozol, tehát D:/varosok.dat. Bár nem hiszem, hogy ez gondot okoz. Ennek elég egyszerűen kellene működnie. Ha lesz egy kis időm délután feldobok winre egy Qtét és megrpóbálom reprodukálni a dolgot.

Itt a teljes forrás (az egész Qt project), gondolom így egyszerűbb:
http://dl.dropbox.com/u/14078600/Neptun-Qt.tar.bz2

Átírtam a fájl nevére vonatkozó részt erre:
QDir konyvtar(QDir::current()); //Aktualis konyvtar megallapitasa
QString fajl=konyvtar.absoluteFilePath("varosok.dat"); // varosok.dat abszolUt eleresi Utjanak megallapitasa
QFile allomany(fajl); // Bemeneti allomany (varosok.dat);

Ettől már elviekben helyesen kellene megállapítania az elérési útvonalat az összes rendszeren. Linux alatt működik, Winen változatlan a dolog.

Hmm ez a windowsos fájlnévkezelés most hirtelen nem világos, de így működik nálam:

QString fajl = "C:\\varosok.dat";
QFile allomany(fajl);

Megjegyzem, hogy a fájlt megnyitja minden alkalommal amikor a ComboBoxhoz nyúlsz, ami nem túl frankó dolog.

Na mindjárt megnézem, hogy ennek a QDir dolognak hogyan kellene működnie :)

No a megoldásoddal nincs baj, csak annyi hogy itt keresi a fájlt:

"C:/home/richard/SzoftFejlTech/Neptun-Qt/Neptun-build-desktop/varosok.dat"

A projekt beállításaiban meg tudod jelölni mi legyen a build könyvtár és akkor ott fog nézegetni. De vannak frappánsabb megoldások is, nézd meg a QSettings dolgot, platform függetlenül tudsz elrakni mindenféle infókat a programodról.

Nem az exe könyvtárába keres, hanem egyel feljebb, a build könyvtárba!

Utánna olvastam közben, a QDir::absoluteFilePath(QString) nem ellenőrzi, hogy a megadott fájl létezik e (varosok.dat). "Returns the absolute path name of a file in the directory. Does not check if the file actually exists in the directory;"
Azaz a futási könyvtárat kapod szerintem, ami a fentebb írt, ez az oka a dolognak.

Megjegyzem, hogy a fájlt megnyitja minden alkalommal amikor a ComboBoxhoz nyúlsz, ami nem túl frankó dolog.

Igen, tisztában vagyok vele. Sajnos nem találtam rá jobb megoldást, hogy egy második form által módosított varosok.dat-ot újra beolvastathassam vele. Az eredeti megoldáskor csak akkor olvasta be a fájlt, mikor indult az alkalmazás, így a lista bővítése után újra kellett indítani a programot.

VC++-ban ez szépen meg lett oldva:
if (UjVaros->ShowDialog(this)==System::Windows::Forms::DialogResult::OK) // "Település felvétele" form megjelenítése

{

try // Új település rögzítése a comboboxokban és a be/kimeneti állományban

{

KiIr=File::AppendText("varos.txt");

KiIr->WriteLine(UjVaros->textUjTelepules->Text);

KiIr->Flush();

comboLakhely->BeginUpdate();

comboLakhely->Items->Add(UjVaros->textUjTelepules->Text);

comboLakhely->EndUpdate();

comboSzulHely->BeginUpdate();

comboSzulHely->Items->Add(UjVaros->textUjTelepules->Text);

comboSzulHely->EndUpdate();

}

catch (Exception^ e)

{

MessageBox::Show(e->Message);

}

finally

{

if(KiIr)

KiIr->Close();

}

}
viszont Qt megfelelőjére még nem jöttem rá. (Ugyanez a project megy VC++-ban, amit szemináriumon fejlesztgetünk, csak én szeretnék egy platformfüggetlen megoldást "tanulás" címén.)

Ha jól értem a problémát ennél pedig nem lesz bonyolultabb.

... megnyomják az új város gombot ...

ujvarosdialog->exec();

comboLakhely->clear();
comboSzulHely->clear();

... fájl beolvas combobox feltölt ...

Gyakorlásnak megteszi, mondjuk a combo boxok ürítése elött amíg nincs beolvasva a fájl illik őket letiltani vagy valami, hogy ne legyen az, hogy véletlen akkor kattint a felhasználó amikor még nincsenek feltöltve.

Pár gondolat ...

1. file elérés: nem jó, ha OS specifikus klienseket csinálsz. Helyette sokkal egyszerűbb, ha csinálsz valamilyen QString getDataPath() függvényt, ami mindig az OS-től függően veszi fel az adatkönyvtárad elérhetőségét. Pl:

QFile f;
f.setName(getDataPath()+"varosok.dat");

2. A többszörös GUI felület + fájlba mentés nagyon nem elegáns. Szerintem amit érdemes lenne használnod, az a model-view architektúra. Ezzel éket versz a GUI elemek és az adatforrások közé. Azon a szinten a az adatok szinkronizálása megoldott. Illetve később, ha a modellben változás van (pl adatbázisból akaros venni a városokat) akkor elegendő a modellt módosítanod, a már meglévő GUI részhez nem kell nyúlnod (nem is fogja tudni, honnan jön az adat).

Nem mellesleg, ha a model-view-t később egy proxy-val kiegészíted, akkor az adatokat tudod szűrni, sorbarendezni, stb. (Pl a kombóban elkezded beírni az első pár betűt és csak azok a városok maradnak benn, amelyek azzal a kombinációval kezdődnek.

QtCreatorban az oldalsáv -> Projects -> Run settingsnél a working directory oda mutat ahol a fájlod meg a binárisod van?

Kis kiegészítés még:

- az UI classokat általában statikusan szokás megadni. Nem kell new-t hívni a member konstruktorban és delete-t a destruktorban.
- egy nyelvet használj: nem poén, hogy a föggvény ::add_city(), de benne már magyarul vannak a nevek.
- a kimenet.operator << részt nem teljesen értem. Miért kell oda az operator?
- túl sok a komment. Nem kell minden sort a végén kommentelni. Aki érti, az úgyis a forráskódot olvassa el és megérti, hogy miről szól az. Mindenki mást csak feltart a komment. Ha nagyon nem egyértelmű, hogy az adott rész mit csinál, akkor a függvény előtt vagy közben, pár sorban foglald össze a dolgot.
-
QTextStream kimenet(&allomany);
// ertekek kiirasa XML szeruen adatok.dat-ba
kimenet.operator <<("\t[textNev]\n"+ui->textNev->text()+"\n");
kimenet.operator <<("\t[textAzonosito]\n"+ui->textAzonosito->text()+"\n");
kimenet.operator <<("\t[textJelszo]\n"+ui->textJelszo->text()+"\n");

Ehhez 3 megjegyzés társul:
- ha még nem láttál XML-t, akkor ne nagyon hivatkozzál a forráskódban rá. Ennek semmi köze az XML-hez, csak frusztrációt okoz, hogy hol kellene ebbe belelátni az XML-t. Ne használj buzzwordoket, csak hogy profibbnak tűnjön.
- Ha XML-t akarsz használni, akkor a QDomDocument és a környéke a barátod.
- érdemesebb lenne itt a CSV felé menni, valamilyen "PARAM=VALUE" mentén. Első egyenlőségjelig keresel

A paraméterfájl olvasásához érdemes lenne egy QHash-et declarálnod valahol.
Így nem kellene minden egyes paramétert implementálnod a beolvasó függvényben, csak ott, ahol szükséged van rá. Pl:

QHash hash

void Class:load()
{
végigiterálsz a fájlon
minden egyes sornál megkeresed az első egyenlőségjelet
szétbontod a sort kulcsra és értékre az egyenlőségjel pozíciója alapján
berakod a hashba: hash.insert(key, value);
}

utána pl a jelszót kinyerheted a
hash.value("password", "defaultpassword").toString() hívással. Ha volt ilyen paramétered a fájlban, akkor azt adja vissza, ha nem, akkor a "defaultpassword" stringet.

Így írhatsz egy generic betöltő és paraméter mentő algoritmust, aminek nem kell tudni a paraméterekről. a paramétereket bárhol felhasználhatod, ha a hash függvényt az egyes osztályoknak átadod.

Ha nem akarsz az átadással molyolni, akkor kell egy Singleton pattern alapján létrehozott osztály, aminek egy instance-ja tartalmazza a hasht. Onnantól kezdve a program bármely pontján ez elérhető lesz.

Természetesen az adott implementáció feladatfüggő és lehetnek jobb megoldások is. Viszont mivel most kezded az ezzel való ismerkedés, a fentiek pár támpontot jelentenek a továbbhaladásra. Guglizz bátran ezekkel kapcsolatosan.

Köszi a támpontokat.

kimenet.operator << esetén a .operator-t a QT Creator egészítette ki nekem ilyenre, valamiért a sima kimenet << "szöveg"; nem volt jó neki.

Azt az XML-es bejegyzést órai anyagból vettem át, ott a tanár hivatkozott rá így (maga a Qt projekt egy VC++ 2010 projekt alapján készül, ugyanazokat a feladatokat lehető leghasonlóbban megoldva). Magát a célt nem látjuk, mert a tanár óráról órára közöl egy-egy megoldandó részfeladatot, és így szép lassan épül a dolog, usecase diagram is csak a feladat megoldása _után_ készült. Mivel úgymond elsődleges projekt most nálam a Qt megismerése, valószínűleg elviszem más irányba a dolgot, esetleg tök más feladatot találok ki magamnak, mert haladnék az anyaggal, de így, hogy hétről hétre 2 munkaórányi feladat születik elég lassú.

Az említettekből adódóan persze sok dolgot lehetne sokkal szebben, elegánsabban megoldani, de mivel a VC++-ben így oldottuk meg, itt is erre törekedtem. Azóta pl. 1 form van, és InputDialog kéri a felhasználótól a település nevét, mert ezt könnyebb megoldásnak láttam, meg minek kínlódjak én vele.

Kommentek azért vannak ilyen sűrűn, mert így könnyebben átláthatom a saját kódom holnap, amikor már nem biztos, hogy emlékszem mi miért, de legfőképp hogyan volt. Mint írtad: "Aki érti, az úgyis a forráskódot olvassa el és megérti, hogy miről szól az.", csakhogy előfordulhat, hogy pont én nem értem meg a kódot, mert mondjuk egy hete készítettem, és azóta rengeteg dolgot néztem már Qt ügyben. Zavarni nem bánom, ha mást zavar, engem segít a tanulásban/gyakorlásban, és ennek a kódnak ez a célja.

A konstruktor/destruktor függvényeket a Qt Creator generálta, nem saját.

Az egységes nyelve majd igyekszem figyelni.

Azt esetleg nem tudja még valaki, hogy a stringekben található ékezetes karaktereket hogyan lehetne szépen megjeleníttetni?

Pl. a
QString szoveg=QInputDialog::getText(this,tr("Új település felvétele"),tr("Település neve:"),QLineEdit::Normal,"Település neve", &ok);
hatására az InputDialog-ban az ékezetes karakterek helyére mindenféle "érdekes" karakter kerül.

Általában UTF8-ban kódolom a forrást, ha te is így teszel, ezt kell beszúrni a main elejefelé és jó lesz a megjelenés:

QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

BTW a kollégák meglátásaiban bőven van igazság, ha komolyan akarsz Qtét tanulni, akkor szerintem érdemes lenne valahogy a Qt módszereket és megoldásokat magadévá tenned. Egy VC projekt átírása helyett, néhány lépésről lépésre tutorialt végig csinálni, vannak nagyon jók.

Persze, meg is hajoltam ajánlásuk előtt, amiket ajánlottak elkezdtem áttanulmányozni. Az ismerkedést pedig úgy képzeltem el, hogy adott ez az alap feladat, VC++-ból átvett eljárásokkal, és szép fokozatosan a megoldásokat lecserélem a Qt szemléletének megfelelően.

Az UTF8-at köszi.