Elemi alkalmazások fejlesztése III SDI alkalmazás készítése Qt-ben III. Készítette: Szabóné Nacsa Rozália Steingart Ferenc
Ami már kész van: Készítettünk egy Qt SDI alkalmazást.
Ami már kész van: Megterveztük a view osztály felhasználói felületét.
Ami már kész van: Megvalósítottuk a számla típust.
Ami már kész van: Megvalósítottuk a számlatétel típust.
Ami már kész van: #include "invoicerview.h" #include <qlineedit.h> #include <qlistview.h> InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) : InvoicerViewBase(parent) { /** connect doc with the view*/ connect(doc, SIGNAL(documentChanged()), this, SLOT(slotDocumentChanged())); connect(customer, SIGNAL(textChanged(const QString&)), doc, SLOT(setCustomer(const QString&))); connect(zip, SIGNAL(textChanged(const QString&)), doc, SLOT(setZip(const QString&))); connect(city, SIGNAL(textChanged(const QString&)), doc, SLOT(setCity(const QString&))); connect(street, SIGNAL(textChanged(const QString&)), Összekapcsoltuk számlát (doc) doc, SLOT(setStreet(const QString&))); és a view osztályt. connect(invno, SIGNAL(textChanged(const QString&)), doc, SLOT(setInvNo(const QString&))); connect(released, SIGNAL(textChanged(const QString&)), doc, SLOT(setReleased(const QString&))); connect(fulfilled, SIGNAL(textChanged(const QString&)), doc, SLOT(setFulfilled(const QString&))); connect(dueto, SIGNAL(textChanged(const QString&)), doc, SLOT(setDueTo(const QString&)));
Ami már kész van: Megvalósítottuk a számlatétel felvitele, módosítása, törlése funkciókat.
Az alkalmazás osztálydiagramja QListViewItem InvoicItem InvoicerDoc ListViewInvoice Item InvoiceItem* _i; QDialog InvoicerView InvItemDia Invoicer
Ami még hátra van: (Számlafejléc tartalmának megadása) (Számlatétel hozzáadása) (Számlatétel módosítása) (Számlatétel törlése) Beviteli mezők ellenőrzése Számla mentése fájlba Számla betöltése fájlból
Beviteli mez ő k ellen ő rzése View osztály irányítószám (négyelem ű, reguláris) számlaszám (csak számjegy) dátumok (illeszkedés egy mintára+dátum ellenő rzés) Számlatétel dialógus darabszám (numerikus) egységár (numerikus)
Adatellen ő rzés lépései 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 Ehhez a QValidator absztrakt osztályt használjuk.
A QValidator osztály Az ellen ő rz ő objektumok a QValidator osztály leszármazottai.
QValidator: Public Members Public Members QValidator ( QObject * parent, const char * name = 0 ) ~QValidator () enum State { Invalid, Intermediate, Valid = Intermediate, Acceptable virtual State validate ( QString & input, int & pos ) const = 0 virtual void fixup ( QString & input ) const
QValidator osztály: State minta: 1111.11.11 abc 2003.04.06: 2003.04.06 Invalid Intermadiate Acceptable State
QValidator: validate(), fixup() pure virtual virtual State validate (QString &txt, int &pos) const = 0; A txt szöveget ellenőrzi. Visszatérési értéke lehet: Invalid: a szöveg nem elfogadható Intermediate: a szöveg még jó lehet (közbüls ő állapot) Acceptable: a szöveg elfogadható virtual void fixup (QString &txt) const; Kijavítja a nem elfogadható szöveget.
Ellen ő rz ő objektumok: validátorok Cél: adatbevitel korlátozása QObject QValidator QIntValidator QDoubleValidator Beépített validátorok.
Adatellen ő rzés lépései 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 Példa (vázlat) invoicerviw.h :... QIntvalidator* invnov;... //deklaráció invoicerviw.cpp:... invnov = new QIntValidator(this)... //létrehozás... invno->setvalidator(invnov)... //összerendelés
Dátum ellen ő rzéséhez nincs beépített osztály.
Ellen ő rz ő objektumok: validátorok Cél: adatbevitel korlátozása QObject QValidator QIntValidator QDoubleValidator DateValidator Beépített validátorok. Saját validátor
Az alkalmazás osztálydiagramja QListViewItem QValidator InvoicItem InvoicerDoc ListViewInvoice Item InvoiceItem* _i; DatumValidator QDialog InvoicerView InvItemDia Invoicer
Dátum ellen ő rz ő osztály létrehozása
A DateValidator osztály deklaráció datevalidator.h A szül ő konstruktor hívása #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) { //üres törzs A dátum validátort a QValidator osztályból származtatjuk. A konstruktor argumentumai Adattag inicializálása virtual State validate(qstring &, int &) const; virtual void fixup(qstring &) const; private: bool match(const QChar &ch, int p) const; QString _pattern; Adattag ; Saját függvény
A match() privát függvény implementációja bool DateValidator::match(const QChar &ch, int p) const { if(p>=_pattern.length()) { return false; if(_pattern[p]=='1') { return ch.isdigit(); else { return (ch == _pattern[p]); datevalidator.cpp minta: 1111.11.11 2003.04.06:
Avalidate() implementációja #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; datevalidator.cpp _pattern: 1111.11 11.11 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; 2004.04 04.20
A fixup() m ű velet implementációja void DateValidator::fixup (QString &txt) const { QDateTime day = QDateTime::currentDateTime(); txt = day.tostring("yyyy") + "." + day.tostring("mm") + "." + day.tostring("dd"); datevalidator.cpp
Ellen ő rz ő objektumok létrehozása A view osztályban létrehozunk két numerikus validátor adattagot az irányítószám és a számlaszám mezőkhöz, valamint egy dátum validátor adattagot a dátum típusú mezők ellenőrzésére. irányítószám: egy QRegExpValidator objektum az 1000..9999 értékek elfogadására (zipv) számlaszám: egy QIntValidator objektum tetszőleges egész értékek fogadására (invnov) dátumok: egy közös DateValidator objektum a default mintával és dátum ellenőrzéssel ( dv)
A módosított view osztály #include "datevalidator.h" invoicerview.h class InvoicerView : public InvoicerViewBase { private: void initvalidators(); protected: DateValidator* dv; QRegExpValidator* zipv; QIntValidator* invnov; ; InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) : InvoicerViewBase(parent) { initvalidators();... invoicerview.cpp
Validátorok inicializálása 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); DateValidator* dv; QIntValidator* zipv; QIntValidator* invnov; dv=new DateValidator("1111.11.11",this); released->setvalidator(dv); fulfilled->setvalidator(dv); dueto->setvalidator(dv);
Ellen ő rzés adatbevitel végén class InvoicerView : public InvoicerViewBase {... protected slots: void slotdocumentchanged(); void slotdocitemadded(invoiceitem*); ; void checkreleaseddate(); void checkfulfilleddate(); void checkduetodate(); 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())); connect( doc,signal(iteminserted(invoiceitem*)), this,slot(slotdocitemadded(invoiceitem*)));
InvoicerView:: checkreleaseddate() 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:: checkfulfilleddate() 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:: checkduetodate() 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();
Számlatétel ellen ő rzése Qt designer invitemdia.ui UIC invitemdia.h Írás, olvasás Olvasás Generálás #includes Eszköz (tool) Generált forráskód Felhasználó forráskódja invitemdia. cpp A generált osztályba nem tudjuk az adatellen ő rzést betenni. invoicer.h invoicer.cpp
Modulszerkezet Qt designer invitemdia.ui UIC invitemdia.h Örökl ő dés cheked itmdia.h Írás, olvasás Olvasás Generálás #includes Eszköz (tool) Generált forráskód Felhasználó forráskódja invitemdia. cpp checked itemdia.cpp Az ellen ő rzést a származtatott osztályban valósítjuk meg. invoicer.h invoicer.cpp
Az alkalmazás osztálydiagramja QListViewItem QValidator InvoicItem ListViewInvoice Item DatumValidator QDialog InvoicerDoc InvoiceItem* _i; InvoicerView InvItemDia Invoicer Checked ItemDia
A CheckedItemDia osztály létrehozása 1
A CheckedItemDia osztály létrehozása 2
A CheckedItemDia osztály #ifndef CHECKEDITEMDIA_H #define CHECKEDITEMDIA_H checkeditemdia.h #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; ; #endif A konstruktor törzsét is itt adjuk meg, ezért a konstruktor definícióját a checkeditemdia.cpp fájlból törölni kell.
Az InvItemDia példányok lecserélése
Az InvItemDia példányok lecserélése #include "checkeditemdia.h" void InvoicerApp::slotItemsNew() { statusbar()->message(tr("inserting a new invoice item...")); //InvItemDia dlg(this,0,true); CheckedItemDia dlg(this,0); if (dlg.exec() == QDialog::Accepted) {... invoicer.cpp void InvoicerApp::slotItemsEdit() { statusbar()->message(tr("modifying current item...")); if(view->items->currentitem()) { //InvItemDia dlg(this,0,true); CheckedItemDia dlg(this,0); InvoiceItem *ii =... Ügyeljen arra, hogy a paraméterek száma is megváltozott!!
Fordítás / Futtatás
Doc/View technika: adatlekérdezés invoicerdoc.h class InvoicerDoc : public QObject {... signals: void documentchanged(invoicerdoc* doc); public: const QString customer() const { return _customer;... protected: QString _customer; ; bool InvoicerDoc::load(const QString &filename) { emit documentchanged(this); return true; Hozzáveszünk egy paramétert a documentchanged() szignálhoz. invoicerdoc.cpp Azokon a helyeken, ahol hivatkozás történik a módosított függvényre, elvégezzük a szükséges módosítást.
Doc/View technika: adatlekérdezés class InvoicerDoc : public QObject {... signals: void documentchanged(invoicerdoc* doc); public: const QString customer() const { return _customer;... protected: QString _customer; ; A dokumentumra mutató pointert a signal/slot mechanizmust kihasználva adjuk át a slotdocumentchanged() slotnak. InvoicerView::InvoicerView(QWidget *parent, InvoicerDoc *doc) :... connect(doc, SIGNAL(documentChanged(InvoicerDoc*)), this, SLOT(slotDocumentChanged(InvoicerDoc*)));...... A dokumentumra mutató void InvoicerView::slotDocumentChanged(InvoicerDoc* doc) pointer segítségével { lekérdezhetjük a számla customer->settext( doc->customer() ); (doc) adatait....
Az adatbeviteli mez ő k aktualizálása void InvoicerView::slotDocumentChanged(InvoicerDoc* doc) { 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);
Fájl m ű veletek 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 ( InvoicerApp) slotfilenew slotfilesave slotfilesaveas slotfileopen
A dokumentum osztályban már ott vannak az alapvet ő fájlkezel ő m ű veletek.
Ezeket a m ű veleteket nekünk kell implementálni.
Új adattag: _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 osztályt kib ő vítjük egy új adattaggal, amelyben az aktuális számlát tartalmazó fájl nevét tároljuk.
A newdoc() implementációja 1 2 3 void InvoicerDoc::newDoc() { _customer = _zip = _city = _street = _invno= _released = _fulfilled = _dueto = ""; _items.clear(); _filename=""; modified = false; emit documentchanged(this); Kiüríti a dokumentumot, és a módosításról értesíti a view osztályt.
A save() implementációja bool InvoicerDoc::save() { ofstream f(_filename); #include <fstream> if (f) { using namespace std; 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;
A saveas() implementációja bool InvoicerDoc::saveAs(const QString &filename) { _filename=filename; return save();
A load() függvény implementációja 1 bool InvoicerDoc::load(const QString &filename) { ifstream f; char buf[1024], buf2[1024]; f.open(filename); Fájl megnyitása 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);... invoicerdoc.cpp A számla fejléc adatainak beolvasása
... A load() függvény implementációja 2 invoicerdoc.cpp //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 Számlatételek f.getline(buf2,1024); //unit beolvasása 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; Üzenetet küldünk a külvilág számára return true; arról, hogy a számla megváltozott. return false;
Adatellen ő rzés: check() invoicerdoc.cpp 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;
A querysave() privát függvény 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) { // "Save"... Lásd következ ő dia else if (r==1) { return true; else{ return false; else{ return true; // "Discard" // "Cancel" // Nincs változás
A querysave() privát függvény bool Invoicer::querySave() { Kitöltöttük a... kötelez ő adatokat? 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) {...
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."));
A slotfileopen() módosítása void Invoicer::slotFileOpen() { 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);
A slotfilesave() módosítása void Invoicer::slotFileSave() { statusbar()->message(tr("saving file...")); doc->save(); statusbar()->message(tr("ready.")); 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."));
A slotfilesaveas() módosítása void Invoicer::slotFileSaveAs() { statusbar()->message(tr("saving file under new filename...")); QString fn = QFileDialog::getSaveFileName(0, 0, this); if (!fn.isempty()) { doc->saveas(fn); else { statusbar()->message(tr("saving aborted"), 2000); statusbar()->message(tr("ready.")); const char* err=doc->check(); if(err==0) { doc->saveas(fn); setcaption(doc->filename()); else{ QMessageBox::information(this,"Missing information",err,1);
A slotfilequit() módosítása void Invoicer::slotFileQuit() { if (!querysave()) return; statusbar()->message(tr("exiting application...")); qapp->quit(); statusbar()->message(tr("ready."));
Ami (biztosan) kimaradt... számlatételek összegzése a view sum mezőjében a tételműveletek engedélyezése/tiltása fájlkezelési hibák figyelése
Vége