...2 Feladat...2 Beviteli mezők ellenőrzése...2 Ellenőrző objektumok (Validátorok)...3 QValidator::State...3 QValidator::validate()...3 QValidator::fixup()...3 Az adatbevitel ellenőrzésének lépései...3 Saját dátumvalidátor létrehozása...4 A DateValidator típus definíciója...4 Az illesztő privát művelet implementációja(match())...4 Az ellenőrző művelet implementációja (validate())...5 Alapértelmezett értéket visszaadó függvény implementációja (fixup())...5 Ellenőrzések a view osztályban...6 Ellenőrző objektumok létrehozása...6 Fordítás/Futtatás...7 Ellenőrzés az adatbevitel végén...7 Dátumellenőrző slotok implementációja...8 Ellenőrzések a számlatétel dialógusban...9 A CheckedItemDia osztály definíciója és implementációja...10 Fordítás/Futtatás...10 A documentchanged()/slotdocumentchanged() módosítása...11 Mentés fájlba, olvasás fájlból...12 A dokumentum osztály fájlkezelő műveletei...13 Új adattag a dokumentum osztályban (_filename)...13 A dokumentum objektum inicializálása (newdoc())...13 A save() művelet implementációja...13 A saveas() művelet implementációja...14 A load() művelet implementációja...14 A dokumetum osztály adatellenőrző függvénye (check())...15 A nézet osztály fájlkezelő slotjainak módosítása...15 A querysave() saját függfény...15 A slotfilenew() módosítása...17 A slotfileopen() módosítása...17 A slotfilesave() módosítása...17 A slotfilesaveas() módosítása...17 A slotfilequit() módosítása...18 Ami még hátra van...18 Projekt összeállítása Qt parancsokkal...19 A projektet alkotó összetevők forrása letölthető a people.inf.elte.hu/nacsa címről. 1. oldal
"! #%$&! #%'(!*)+,-$&.,/)1032.,547682 9;:*4<=3>@? Ebben a munkafüzetben feltételezzük, hogy Ön már feldolgozta az SDI alkalmazás készítése Qt-ben I. és II, munkafüzeteket. A Qt SDI alkalmazás készítése I. és II.-ben elkészítettünk egy Qt SDI alkalmazást, felépítettük a view osztály felhasználói felületét, megvalósítottuk a számla típust, valamint a hozzátartozó számlatétel típust, megoldottuk a számla és a view összekapcsolását, és lehetővé tettük új számlatétel felvitelét, módosítását és javítását. ACBDFEHGIEKJ Biztosítsuk alkalmazásunban az ellenőrzött adatbevitelt, és tegyük lehetővé az egyes számlák mentését fájlba, illetve azok beolvasását fájlból. L BIMONPJQBDFNSRTBUWVIXYBDFDZB\[]V_^@UW`abB Az adatbevitelt az alábbiak szerint szeretnénk korlátozni: View osztály irányítószám (négy elemű, reguláris kifejezés) számlaszám (csak számjegy) dátumok (illeszkedés a megadott mintára+dátum ellenőrzés) Számlatétel dialógusablak darabszám (numerikus) egységár (numerikus) 2. oldal
c1dfdze\f]g_h@iwgkjilnmoewpiqqristjiputwvyxsdz I~KqQj_hwjIpO A Qvalidator osztállyal biztosíthatjuk alkalmazásunkban az input adatok ellenőrzött bevitelét. AQvalidator osztály egy absztrakt osztály. A belőle származtatottqintvalidator ésqdoublevalidator segítségével az egyszerűbb numerikus ellenőrzéseket oldhatjuk meg. A QRegExpValidator reguláris kifejezéseket használ, mellyel egy általánosabb adatellenőrzést valósíthatunk meg. Ha a beépített ellenőrző objektumok nem elegendőek, akkor származtatással előállíthatunk saját ellenőrző objektumokat is. Az osztálynak két virtuális függvénye van: validate() és fixup(). A validate() függvényt kötelező implementálni. A függvény visszatérési értéke Invalid (nem elfogadható), Intermediate (esetleg még jó lehet) vagy Acceptable (elfogadható), attól függően, hogy az ellenőrzött objektum milyen állapotban van. (Például, ha a megengedett szöveg 0 és 99 közé eső szám, akkor 444 státusza Intermediate, 55 státusza Acceptable, abc státusza Invalid.) A QValidator osztály publikus tagjai a következők: QValidator::State Felsorolás (enum) típusú adattag, mely jelzi, hogy az ellenőrzött input szöveg éppen milyen állapotban van. enum State Invalid, Intermediate, Valid = Intermediate, Acceptable QValidator::validate() virtual State validate ( QString & input, int & pos ) const = 0 A validate() függvény egy tiszta virtuális függvény, ezért a származtatott osztályokban kötelező definiálni. Ellenőrzi az input szöveg tartalmát, és visszaad egy State típusú értéket. Szükség esetén megváltoztathatjuk mind az input szöveg tartalmát, mind pedig a pos (a kurzor pillanatnyi helye) paraméter értékét is. QValidator::fixup() virtual void fixup ( QString & input ) const A fixup() függvény lehetővé teszi, hogy magunk is kijavíthassunk bizonyos adatbeviteli hibákat. Például a QLineEdit adatbeviteli mező esetén az Enter leütésekor a fixup() függvény kapja meg a vezérlést, ha az ellenőrző objektum State értéke nem Acceptable. i xy IxKqZl]eI ;zqqqedbewddze\f]g_h@iw Wƒy \f]e\p dz ; Wƒbez Ellenőrző objektum létrehozása Ellenőrző objektum viselkedésének beállítása Ellenőrző objektum és az adatbeviteli mező összerendelése Megjegyzés: Ellenőrző objektumok (validátorok) használatára már mutattunk egy egyszerű példát az Egyablakos alkalmazás készítése II. munkafüzet (Milliomos2) Numerikus input kezelése c. fejezetében. 3. oldal
Elemi alkalmazások fejlesztése III. ˆ%ŠŒ K bži K Q I ;ˆ F ŽI K Q _ 1 Z y Q % \ ] bš œsˆ A dátum típusú mezők ellenőrzésére létrehozunk egy saját DateValidator osztályt, melyet a a QValidator osztályból származtatunk. A DateValidator típus definíciója #include <qvalidator.h> class DateValidator : public QValidator Q_OBJECT public: DateValidator(QString pattern = "1111.11.11", QObject *parent=0,const char *name=0): QValidator(parent,name), _pattern(pattern) virtual State validate(qstring &, int &) const; virtual void fixup(qstring &) const; private: bool match(const QChar &ch, int p) const; QString _pattern; ; datevalidator.h A Q_OBJECT makrót mindig meg kell adni, ha signal/slot mechanizmust használunk. A konstruktor egy üres törzsű függvény, ezért a datevalidaor.cpp-ben nem szerepel! Érdemes a Help-et használva bepillantani a QValidator osztályba. Az illesztő privát művelet implementációja(match()) A match() privát művelet vizsgálja az adott minta szerinti illeszkedést egy adott pozíción. bool DateValidator::match(const QChar &ch, int p) const if(p>=(int)_pattern.length()) return false; if(_pattern[p]=='1') return ch.isdigit(); else return (ch == _pattern[p]); datevalidator.cpp Saját jegyzet 4. oldal
ž Elemi alkalmazások fejlesztése III. Az ellenőrző művelet implementációja (validate()) A validáló művelet a kapott szöveget az eltárolt mintához illeszti. #include <qdatetime.h> #include "datevalidator.h" QValidator::State DateValidator::validate(QString &txt, int &pos) const int l=txt.length(); int i=0; for(;i<l && match(txt[i],i);++i); //empty body if(i<l) pos=i; //txt.truncate(i); return Intermediate; else if (i==l) if(l==(int)_pattern.length() && QDate::isValid(txt.mid(0,4).toInt(), txt.mid(5,2).toint(), txt.mid(8,2).toint())) return Acceptable; else return Intermediate; else return Invalid; datevalidator.cpp Ha jó a mintaillesztés, nézzük meg, valóban dátum-e. Alapértelmezett értéket visszaadó függvény implementációja (fixup()) A fixup() művelet hibás adat esetén kicseréli a szerkesztőmező tartalmát. void DateValidator::fixup (QString &txt) const QDateTime day = QDateTime::currentDateTime(); txt = day.tostring("yyyy") + "." + day.tostring("mm") + "." + day.tostring("dd"); datevalidator.cpp Legyen a mai dátum az alapértelmezés. Saját jegyzet 5. oldal
Ÿ1FZ \ ] _ @ W b \ «ªO K b ±² ³yµ_ S A view osztályban deklarálunk egy numerikus validatort a számlaszám ellenőrzésére (invnov), egy reguláris kifejezéseket ellenőrző validátort az irányítószám ellenőrzésére (zipv), valamint egy dátumvalidátort a dátum típusú mezők (dv) ellenőrzésére, majd a számla megfelelő mezőihez hozzárendeljük ezeket a validátorokat. Ellenőrző objektumok létrehozása #include "datevalidator.h" class InvoicerView : public InvoicerViewBase private: void initvalidators(); protected: DateValidator* dv; QRegExpValidator* zipv; QIntValidator* invnov; ; invoicerview.h InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) : InvoicerViewBase(parent) initvalidators(); invoicerview.cpp A validátorokat létrehozó privát függvényt a konstruktorban hívjuk meg. void InvoicerView::initValidators() QRegExp regexp("[1-9][0-9]3,3"); zipv = new QRegExpValidator(regExp,this); zip->setvalidator(zipv); invnov=new QIntValidator(this); invno->setvalidator(invnov); dv=new DateValidator("1111.11.11",this); released->setvalidator(dv); fulfilled->setvalidator(dv); dueto->setvalidator(dv); invoicerview.cpp A regexp-ben megadott minta szerint az irányítószám első számjegye 1 és 9 közötti számjegy, melyet pontosan három darab 0 és 9 közé eső szám követ. A setvalidator() függvénnyel rendeltük hozzá az ellenőrző objektumokat az ellenőrizendő adatbeviteli mezőkhöz. 6. oldal
C _ >¹KºQ»¼S½n¾ _À]»»ÁK»¼ ½ Fordítsa le, majd futtassa a programot. Figyelje meg, hogy az ellenőrzött adatbeviteli mezőknél helyesen működik-e az adatbevitel. Mi történik, ha az adatbevitelt félbe hagyva átmegy egy másik mezőre? Â1ÃFÃZÄ\Å]Æ_Ç@ÈWÉÊ Ë È ËyÌIËKÍZÎ]ÏIÐOÑPÍQÏÒCÐÉOÓÔÉWÕ Alkalmazásunkban azt is szeretnénk biztosítani, hogy a dátumokat tartalmazó mezőkről csak akkor lehessen továbblépni, ha a mezőt teljesen és helyesen töltötték ki. Ehhez a vizsgálandó mezők lostfocus() signáljaitt összekötjük a vizsgálatot végző slotokkal, majd megadjuk a slotok implementációját. class InvoicerView : public InvoicerViewBase protected slots: void slotdocumentchanged(); void slotdocitemadded(invoiceitem*); void checkreleaseddate(); void checkfulfilleddate(); void checkduetodate(); invoicerview.h Alkalmazásunkban három dátum típusú mezőt szeretnénk ellenőrizni. ; InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) : InvoicerViewBase(parent) initvalidators(); connect(released, SIGNAL(lostFocus()), this, SLOT(checkReleasedDate())); connect(fulfilled, SIGNAL(lostFocus()), this, SLOT(checkFulfilledDate())); connect(dueto, SIGNAL(lostFocus()), this, SLOT(checkDueToDate())); invoicerview.cpp connect( doc,signal(iteminserted(invoiceitem*)), this,slot(slotdocitemadded(invoiceitem*))); Amikor egy adatbeviteli mezőt elhagyunk (Tab-bal kiléptünk, vagy egérrel kikattintottunk a mezőből), akkor az adott vezérlő lostfocus() signált küld. A view osztály konstruktorában összekapcsoljuk a dátum típusú adatbeviteli mezők lostfocus() signálját és a dátumot ellenőrző slotokat, majd a slotokban elvégezzük az adatok ellenőrzését. 7. oldal
Dátumellenőrző slotok implementációja Számla kibocsájtás dátuma A számla kibocsájtás dátuma legyen az adott napi dátum, ha a mezőt üresen hagyták. Ha a mezőt csak részben töltötték ki, akkor írjunk ki fegyelmeztetést, és ne engedjük elhagyni ezt a mezőt. void InvoicerView::checkReleasedDate() if (released->text()=="") QDateTime day = QDateTime::currentDateTime(); QString str = day.tostring("yyyy") + "." + day.tostring("mm") + "." + day.tostring("dd"); released->settext(str); return; QString str = released->text(); int p = 0; if(released->validator()->validate(str,p)!= QValidator::Acceptable) QMessageBox::information( this, "InvoicerView", "Invalid date! \ndate format: \"yyyy.mm.dd\" "); released->setfocus(); invoicerview.cpp Számla kiegyenlítés dátuma void InvoicerView::checkFulfilledDate() QString str = fulfilled->text(); int p = 0; if(fulfilled->validator()->validate(str,p)!= QValidator::Acceptable && str!= "") qdebug(qstring::number(p)); QMessageBox::information( this, "InvoicerView", "Invalid date! \ndate format: \"yyyy.mm.dd\" "); fulfilled->setfocus(); invoicerview.cpp A számla kiegyenlítésének dátuma lehet üres is. 8. oldal
Számla esedékesség dátuma Ha a számla kifizetésének határideje üres, akkor 8 napos határidőt szabunk meg a számla kifizetésére. void InvoicerView::checkDueToDate() if (dueto->text()=="") QDateTime day = QDateTime::currentDateTime().addDays(8); QString str = day.tostring("yyyy") + "." + day.tostring("mm") + "." + day.tostring("dd"); dueto->settext(str); return; else QString str = dueto->text(); int p = 0; if(dueto->validator()->validate(str,p)!= QValidator::Acceptable) QMessageBox::information( this, "InvoicerView", "Invalid date! \ndate format: \"yyyy.mm.dd\" "); dueto->setfocus(); invoicerview.cpp Mához nyolc napra kell fizetni. Ö1 F ZØ\Ù]Ú_Û@ÜWÝÞbß\à á Þ\Ü âhãkäákåqýhåqßwäyæiçásäzè é êiþsë_ásì A számlatétel dialógus egy generált osztály (invitemdia.ui). Ha új funkciót akarunk hozzárendelni, akkor a generált osztályból származtatással létre kell hoznunk egy új osztályt, és ezt tudjuk kibővíteni a kívánt új funkciókkal. Legyen az InvItemDia osztályból származtatott ellenőrzött új osztály neve CheckedItemDia. Az osztály nagyon egyszerű, ezért a konstruktor feladatát inline módon adjuk meg, így nincs is szükségünk az osztály implementációs fájljára. Illesszen be egy új fájlt a projektjébe checkeditemdia.h néven. í Saját jegyzet 9. oldal
A CheckedItemDia osztály definíciója és implementációja #include <invitemdia.h> #include <qvalidator.h> #include <qlineedit.h> class CheckedItemDia : public InvItemDia Q_OBJECT public: CheckedItemDia(QWidget *parent,const char *name): InvItemDia(parent,name,true), v(this) unitprice->setvalidator(&v); quantity->setvalidator(&v); protected: QIntValidator v; ; checkeditemdia.h Ne feledkezzen meg az include - okról! A CheckedItemDia típus beillesztése az InvoicerView osztályba Cserélje le az alkalmazás InvItemDia példányait a most létrehozott, ellenőrzött CheckedItemDia példányokra. #include "checkeditemdia.h" void Invoicer::slotItemsNew() //invocer.cpp statusbar()->message(tr("inserting a new invoice item...")); //InvItemDia dlg(this,0,true); CheckedItemDia dlg(this,0); if (dlg.exec() == QDialog::Accepted) invoicer.cpp Ügyeljen arra, hogy a paraméterek száma is megváltozott!! void Invoicer::slotItemsEdit() statusbar()->message(tr("modifying current item...")); if(view->items->currentitem()) //InvItemDia dlg(this,0,true); CheckedItemDia dlg(this,0); InvoiceItem *ii = //invoicer.cpp Ügyeljen arra, hogy a paraméterek száma is megváltozott!! Fordítás/Futtatás Ellenőrizze munkáját. Készítsen el egy számlát, vigyen fel néhány számlatételt, majd módosítsa ill. törölje azokat. 10. oldal
îðïcñcò ókôöõ\ 1øù&úKûH Hü õ ï]ýÿþ Zñ]ø ñ_òsóiôtõw øù úkûh HüÔõ\ï ý þ ô _ïcñ Qø û Projektünket úgy készítettük el, hogy adatainkat a dokumentum osztályban, a felhasználói felületeket a view osztályban kezeljük. A dokumentum osztály adatainak megváltozásakor documentchanged() szignált küld. Ez a szignál a view osztály slotdocumentchanged() slotjára van rákötve. Ahhoz, hogy a view osztály megszerezze az adatokat, biztosítanunk kell a view osztály számára, hogy meghívhassa a dokumentum osztály adatlekérdező függvényeit. Ehhez a slotdocumentchanged() függvénynek rendelkeznie kell a dokumentum osztályra mutató pointerrel. Ezt a pointert a signal/slot kapcsolat paramétereként adjuk át. A fenti megfontolásokat figyelembe véve egészítsük ki az InvoicerDoc osztály documentchanged() és a InvoicerView osztály slotdocumentchanged() függvényeit egy, a dokumentum osztályra mutató pointert tartalmazó argumentummal. class InvoicerDoc : public QObject signals: void documentchanged(invoicerdoc* doc); void iteminserted(invoiceitem *i); ; invoicerdoc.h bool InvoicerDoc::load(const QString &filename) emit documentchanged(this); return true; invoicerdoc.cpp this: a dokumentum osztályra mutató pointer. class InvoicerView : public InvoicerViewBase Q_OBJECT protected slots: void slotdocumentchanged(invoicerdoc*); void slotdocitemadded(invoiceitem*); ; invoicerview.h InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) : InvoicerViewBase(parent) initvalidators(); //connect(doc, SIGNAL(documentChanged()), // this, SLOT(slotDocumentChanged())); connect(doc, SIGNAL(documentChanged(InvoicerDoc*)), this, SLOT(slotDocumentChanged(InvoicerDoc*))); invoicerview.cpp Csak a paraméter típusát kell megadni, paramétert (változót) nem szabad!! 11. oldal
0 Elemi alkalmazások fejlesztése III. void InvoicerView::slotDocumentChanged(InvoicerDoc* doc) //TODO update the view customer->settext(doc->customer()); zip->settext(doc->zip()); city->settext(doc->city()); street->settext(doc->street()); invno->settext(doc->invno()); released->settext(doc->released()); fulfilled->settext(doc->fulfilled()); dueto->settext(doc->dueto()); QListIterator<InvoiceItem> it (doc->items()); items->clear(); for (; it.current(); ++it) InvoiceItem *ii = it.current(); new ListViewInvoiceItem(items,ii); invoicerview.cpp "!$#&%')(*!+,- /. A fájlkezelés két osztályt érint: az alkalmazás főablakát (Invoicer) és a dokumentum osztályt (InvoicerDoc). Fájlkezelő műveletek a dokumentumban (InvoicerDoc) newdoc: a dokumetum objektum kiürítése save: a dokumentum objektum elmentése az aktuális fájlba saveas: a dokumentum objektum elmentése a megadott nevű fájlba load: a dokumentum objektum beolvasása a megadott nevű fájlból Fájlkezelő slotok a főablakban (Invoicer) slotfilenew() slotfilesave() slotfilesaveas() slotfileopen() Saját jegyzet 12. oldal
13254"687"9 :;< 7'9=4>+?+<@ ACBED @FA 6G:$?:$AIHEJLKM$N$OIN8P NQ Új adattag a dokumentum osztályban (_filename) class InvoicerDoc : public QObject public: const QString dueto() const return _dueto; const QString filename() const return _filename; protected: QString _dueto; QString _filename; ; A dokumentum objektum inicializálása (newdoc()) void InvoicerDoc::newDoc() _customer = _zip = _city = _street = _invno= _released = _fulfilled = _dueto = ""; _items.clear(); _filename=""; modified = false; emit documentchanged(this); invoicerdoc.h A dokumentum osztályt kiegészítjük egy, a fájl nevét tartalmazó adattaggal és az adattag értékét visszaadó függvénnyel. invoicerdoc.cpp A save() művelet implementációja A save() függvénnyel elmentjük a számla tartalmát a tárolt fájlba szöveges (txt) formában. #include <fstream> using namespace std; bool InvoicerDoc::save() ofstream f(_filename); if (f) f << _customer.utf8() << endl << _zip.utf8() << endl << _city.utf8() << endl << _street.utf8() << endl << _invno.utf8() << endl << _released.utf8() << endl << _fulfilled.utf8() << endl << _dueto.utf8() << endl << _items.count() << endl; for (InvoiceItem *ii= _items.first(); ii; ii=_items.next()) f << ii->name().utf8() << endl << ii->unit().utf8() << endl << ii->quantity() << endl << ii->unitprice() << endl << ii->vatpercent() << endl; modified=false; return true; return false; invoicerdoc.cpp Ne feledkezzen meg az include-okról sem!!! Létrehozunk egy ofstream adatfolyamot, majd ide kiírjuk a számla fejlécét, és rendre a számlatételeket. A szöveget az ékezetek kezelése miatt utf8 típusú karakterekre konvertáljuk. 13. oldal
A saveas() művelet implementációja A saveas() művelet eltárolja a paraméterként kapott fájlnevet és meghívja a save műveletet. bool InvoicerDoc::saveAs(const QString &filename) _filename=filename; return save(); invoicerdoc.cpp A load() művelet implementációja Az adatokat betöltjük egy fájlból. A fájl nevét elmentjük a dokumentumosztály _filename adattagjába. Ezt a nevet használjuk majd annak eldöntésére, hogy mi történjen mentés, vagy mentés másként esetén az adatainkkal. bool InvoicerDoc::load(const QString & filename) ifstream f; char buf[1024], buf2[1024]; f.open(filename); if (f) f.getline(buf,1024); _customer = QString::fromUtf8(buf); f.getline(buf,1024); _zip = QString::fromUtf8(buf); f.getline(buf,1024); _city = QString::fromUtf8(buf); f.getline(buf,1024); _street = QString::fromUtf8(buf); f.getline(buf,1024); _invno = QString::fromUtf8(buf); f.getline(buf,1024); _released = QString::fromUtf8(buf); f.getline(buf,1024); _fulfilled = QString::fromUtf8(buf); f.getline(buf,1024); _dueto = QString::fromUtf8(buf); //load items _items.clear(); int ic, q, u, v; //quantity, unit price, vat f >> ic; f.getline(buf,1024); //skip rest of line while (ic>0) f.getline(buf,1024); //name f.getline(buf2,1024); //unit f >> q >> u >> v; InvoiceItem *ii=new InvoiceItem(QString::fromUtf8(buf), QString::fromUtf8(buf2), q, u, v); f.getline(buf,1024); //skip rest of line _items.append(ii); --ic; _filename=filename; emit documentchanged(this); modified=false; return true; return false; invoicerdoc.cpp 14. oldal
A dokumetum osztály adatellenőrző függvénye (check()) A dokumentum osztály ellenőrző függvénye egy hibaüzenetet ad vissza, ha hibásan töltötték ki a számlát. A nézet osztály ezt a függvényt hívja meg annak eldöntésére, hogy helyes-e a számla, azaz menthető-e. class InvoicerDoc : public QObject public: const QString filename() const return _filename; const char* check(); ; invoicerdoc.h const char* InvoicerDoc::check() if (_customer.isempty()) return "The invoice cannot be saved \n until a customer is specified."; if (_zip.isempty() _city.isempty() _street.isempty()) return "The invoice cannot be saved \n until the address is specified."; if (_invno.isempty()) return "The invoice cannot be saved \n until an invoice number is specified."; if (_items.count() ==0) return "The invoice cannot be saved \n without any item added."; return 0; invoicerdoc.cpp RTS/U$VWGX'YZ+V+X[ \C]E^ [_\ `GW$VW$\baEc dbe/fgh i j"h8kllm"n5ec o fp c h A querysave() saját függfény Ha az aktuális számlánkat még nem mentettük el, akkor új számla létrehozásakor (New), létező számla betöltésekor (Open), illetve kilépéskor (Quit) illik megkérdezni, mi legyen a nem mentett adatokkal. Ezt a kérdést a programban több helyen is fel kell tenni, ezért e kérdés felvetésére bevezetünk egy segédfüggvényt (querysave()) 15. oldal
class Invoicer : public QMainWindow Q_OBJECT invoicer.h /** overloaded for Message box on last window exit */ bool queryexit(); bool querysave(); ; bool Invoicer::querySave() if(doc->ismodified()) int r = QMessageBox::warning(0, "Warning", "There are unsaved changes\n Save them?", "Save", "Discard", "Cancel"); if (r==0) const char* err=doc->check(); if (err!=0) return false; if(doc->filename().isempty()) QString fn = QFileDialog::getSaveFileName(0, "*.inv", this); if(!fn.isempty()) return doc->saveas(fn); else return false; else return doc->save(); else if (r==1) return true; else return false; else return true; invoicer.cpp A querysave() visszatérési értéke hamis, ha nem engedélyezett a mentés. Ha a dokumentumot módosítottuk, akkor rákérdezünk, mi legyen a módosításokkal. Ha menteni szeretnénk a számlát, akkor először a dokumentum osztályban megvizsgáljuk, hogy a számla tartalma helyes-e. Hibás számla esetén nem engedélyezzük a mentést. Ha a számla hibátlan, akkor attól függően, hogy új számlát hoztunk-e létre, vagy létező számlát módosítottunk (van-e név a dokumentum osztály _filename adattagjában), meghívjuk a saveas((), vagy a save() függvények valamelyikét. A querysave() segédfüggvény elkészülte után módosítsuk a nézet osztályunkban a fájlkezelő slotokat. 16. oldal
A slotfilenew() módosítása void Invoicer::slotFileNew() if (!querysave()) return; statusbar()->message(tr("creating new file...")); doc->newdoc(); setcaption(doc->filename()); statusbar()->message(tr("ready.")); invoicer.cpp Ha a querysave() segédfüggvény értéke hamis, nem mentjük a fájlt. A főablak címe legyen az aktuális fájl neve. A slotfileopen() módosítása void Invoicer::slotFileOpen() //invoicer.cpp if (!querysave()) return; statusbar()->message(tr("opening file...")); QString filename = QFileDialog::getOpenFileName(0,0,this); if (!filename.isempty()) doc->load(filename); setcaption(filename); QString message=tr("loaded document: ")+filename; statusbar()->message(message, 2000); else statusbar()->message(tr("opening aborted"), 2000); invoicer.cpp A slotfilesave() módosítása void Invoicer::slotFileSave() if(doc->filename().isempty()) slotfilesaveas(); return; statusbar()->message(tr("saving file...")); const char* err=doc->check(); if(err==0) doc->save(); else QMessageBox::information(this,"Missing information", err, 1); statusbar()->message(tr("ready.")); invoicer.cpp Ha nincs betöltött fájl, akkor saveas(). Ha a doc nem találta hibásnak a számlát, akkor save(), egyébként hibaüzenet. A slotfilesaveas() módosítása 17. oldal
void Invoicer::slotFileSaveAs() statusbar()->message(tr("saving file under new filename...")); QString fn = QFileDialog::getSaveFileName(0, 0, this); if (!fn.isempty()) const char* err=doc->check(); if(err==0) doc->saveas(fn); setcaption(doc->filename()); else QMessageBox::information(this,"Missing information",err,1); else statusbar()->message(tr("saving aborted"), 2000); statusbar()->message(tr("ready.")); invoicer.cpp A slotfilequit() módosítása Ha az alkalmazásunk dokumentuma egy nem mentett számla, akkor mielőtt bezárnánk az alkalmazásunkat kérdezzünk rá, mi legyen ezzel a számlával. void Invoicer::slotFileQuit() statusbar()->message(tr("exiting application...")); if (!querysave()) return; statusbar()->message(tr("exiting application...")); qapp->quit(); statusbar()->message(tr("ready.")); invoicer.cpp qsrut rlv*wyx"z" ~ 8 ƒ ƒ ƒ Önálló feldogozásra javasoljuk az alábbi feladatok megoldását: számlatételek összegzése a view sum mezőjében tételműveletek engedélyezése/tiltása a listview tartalmának függvényében fájlkezelés során előforduló hibák kezelése 18. oldal
ˆ Š Œ Ž ' ' + $ + š 8œ/ ' A projektet összeállíthatja a Qt parancssori eszközeivel is. 1. Hozzon létre a projekt számára egy könyvtárat: invoicer 2. Hozza létre (vagy a honlapomról töltse le) az alábbi fájlokat: invoicer.h, invoicer.cpp, invoicerdoc.h,invoicerdoc.cpp, invoicerview.h,invoicerview.cpp, datevalidator.h, datevalidator.cpp, checkeditemdia.h, main.cpp, invitemdia.ui, invoicerviewbase.ui. 3. Hozza létre a projekt könyvtárában az images könyvtárat, és másolja oda az ikonok kép fájljait: new.png, open.png, save.png 4. Nyisson meg egy terminál ablakot. Legyen a projekt könyvtárban (invoicer). 5. Hozza létre a platformfüggetlen projekt fájlt (invoicer.pro): qmake -project 6. A képek (ikonok) alkalmazása miatt az automatikusan generált projekt fájlt (invoicer.pro) egészítse ki az alábbiak szerint: ###################################################################### # Automatically generated by qmake (1.04a) Tue Apr 13 15:56:41 2004 ###################################################################### TEMPLATE = app INCLUDEPATH +=. # Input HEADERS += checkeditemdia.h \ datevalidator.h \ invoicer.h \ invoicerdoc.h \ invoicerview.h INTERFACES += invitemdia.ui invoicerviewbase.ui SOURCES += datevalidator.cpp \ invoicer.cpp \ invoicerdoc.cpp \ invoicerview.cpp \ main.cpp IMAGES += images/new.png \ images/open.png \ images/save.png A projektet alkotó összetevők forrása letölthető a people.inf.elte.hu/nacsa honlapról. 19. oldal