ot Mihail Petrov(21-03-2002)

reiting (11)   [ dobre ]  [ zle ]

Printer Friendly Variant za otpechatvane

Blagodarenie na Valentin Vulchev mozhe da prochetete statiiata v [PDF format], [TeX format] i po-dobur [HTML format].

SUZDAVANE NA POTREBITELSKI GRAFICHEN INTERFEIS S POMOSHTTA NA QT
ILI
EDNA IDEQ ZA PROGRAMEN MODEL

 

I. Malko istoriia ili neobhodimostta ot podobni razrabotki.

 

1.Dulboko sum ubeden, che bez suzdavane na skuchni programi, ot roda na skladovi programi, schetovodstva i podobni, Linux, kato operatsionna sistema, ne bi mogul da zaeme polagoshtoto mu se miasto na pazara na informatsionni produkti i tehnologii. Spored men tova e putia za nalagane na po - dobrata operatsionna sistema i populiariziraneto i. No ot niakude vse pak triabva da se zapochne, e az zapochnah ot tuk.

2.Suzdavaneto na podobni programni modeli bi privliaklo na nasha strana prilozhnite programisti, kum koito i az se prichisliavam, koeto mnogo bi pomognalo za razrabotka na programi, orientirani kum konkretniia potrebitel, zashtoto negovo velichestvo potrebiteliat ne se interesuva chak tolkova ot tova dali programata mu e napravena pod Wndows, Linux ili QNX, a ot tova dali dadeniia produkt vurshi niakava rabota i kak ia vurshi.

3.Kakto i da go vurtim, ne mozhem da si zatvorim ochite za dulgogodishnoto prisustvie na Windows na pazara, koeto dovede do navika, potrbitelia da izpulniava opredeleni deistviia, kato shttraka po ikoni, butoni i drugi atributi na "privlekatelniia grafichen interfeis" i toi tursi tezi neshta, vupreki, che mnogo chesto grafichniia interfeis niama nishto obshto s efektivnostta na dadenoto prilozhenie, a po - skoro prechi na dobrata rabota na prilozhenieto.

4.Suzdavaneto na potrebitelski grafichen interfeis e po - skoro tutkava rabota, no bez tova niama da minem. I v tazi vruzka predlagam tozi programen model, s nadezhdata, che tova doniakude shte podpomogne suzdavaneto na prilozheniia, sudurzhashti takuv grafichen interfeis.

5.Priznavam si bez boi, che ideiata ne e iztsialo moia, a e vzeta ot ideiata na MS VC++, za "Arhitekturata Dokument/Izgled", v koeto spored men ima dostatuchno mnogo hliab. Az prosto pretvorih doniakude tazi ideia, kato sum se stremil da izbegna ot neshtata, koito ne mi haresvat. Dokolko vsichko tova e dobro ili ne chak tolkova ostavam na vas da pretsenite. Schitam, kolkoto i skandalno da prozvuchi, v neshtoto narecheno Windows ima dostatuchno mnogo idei, koito prosto plachat za po - dobra realizatsiia, koeto ot svoia strana bi dovelo do kachestveno novi idei i razrabotki, a do po - blagosklonno otnoshenie na potrebitelite kum Linux. Predpostavkite v tova otnoshenie sa dostatuchno mnogo i ostava da gi izpolzvame.

6.Tazi statiia ia razhdam v produlzhenie na okolo tri mesetsa, zashtoto se okazva, che e dosta trudno da se prevede napisanoto na S++, na normalen choveshki ezik i v tazi vruzka molia da buda izvinen za veroiatno ne dobriia stil na statiiata, no az sum vse pak prilozhen programist a ne zhurnalist ili pisatel. Niadiavam se che napisanoto e dostatuchno razbiraemo ako ne za obiknovennite potrebiteli to pone za programistite.

7.Samite source code shte mozhete da svalite, edva sled kato si napravia web stranitsa niakude i obeshtavam, ako ima interes kum tiah da gi publikuvam.

8.Ne ochakvaite da namerite v tazi statiia nachin i opisanie na klasovete na Qt, zashtoto opisanieto na tazi biblioteka e dostatuchno dobro i podrobno i niamam namerenie da go povtariam. Tuk shte opisha printsipnite postanovki na programniia model, koito mnogo mi se iska da nareka arhitektura, no ne sum tolkova velik, kato Microsoft, taka che shte go naricham programen model.

 

II.Strukturata DoraRuntimeClass i klasut DoraClassObject.

 

Za da suzdavame potrebitelski grafichen interfeis e neobhodim da osigurim, osven izchertavaneto na prozortsite, po nachin, neobhodim za konkretnoto prilozhenie, a sushto i mehanizum za vzaimodeistvie mezhdu otdelnite obekti. Tova mozhe da se osushtestvi s napravata na RUNTIME mehanizum, pozvoliavasht dostupa ot daden obekt do drugi obekti ot prilozhenieto, kakto i obratnata vruzka. Za postiganeto na tazi tsel e neobhodimo obektite da se suzdavat dinamichno po vreme na izpulnenie. V nastoiashtiiat programen model tova se postiga sus suvokupnostta ot edna struktura DoraRuntimeClass, klasut DoraClassObject makrosut RUNTIME_CLASS. Tova samo po sebe si ne e nika novo, nito neobichaino. Osven tova tuk sum dluzhen da upomena, che Qt, kato grafichen interfeis ima podoben mehanizum, makar i realiziran po po - razlichen nachi, imam predvid tehnologiiata signal/slot. Dulzha da vi uspokoia, che nastoiashtiiat programen model po nikakuv nachin ne narushavva vutreshnata logika na Qt, a po - skoro ia izpolzva, makar i neiavno.

Strukturata DoraRuntimeClass e definirana vuv faila s definitsii DoraClassObeject.h, i izglezhda po sledniia nachin:

 

class DoraClassObject;

 

struct DoraRuntimeClass {

CString class_name;

DoraClassObject* (*m_pCreateView)(QWidget *parent = 0);

DoraClassObject* CreateView(QWidget *parent = 0);

DoraClassObject* (*m_pCreateDocument)();

DoraClassObject* CreateDocument();

};

 

Kakto se vizhda v definitsiiata na strukturata niama nishto neobichaino. Promenlivata class_name e ot tip CString, koito sum napisal za da boravia po - lesno s promenlivi ot tipa char[], i toi niama nishto obshto s poznatiia do bolka CString ot MS VC++, kato dopulnitelno sum vklyuchil v nego mehanizum za konvertirane na paskalski stringove v nulevo bazirani stringove. Osven tova v strukturata ima i dve callback funktsii, kato funktsiiata CreateView sluzhi za konstruirane na obekti, koito sa prozortsi, a CreateDocument e za konstruirane na obekti, koito ne sa prozortsi.

 

Klasut DoraClassObject e definiran v sushtiiat fail po sledniia nachin:

 

class DoraClassObject {

public:

virtual DoraRuntimeClass *GetClassObject() const {

classDoraClassObject.class_name = "";

return &classDoraClassObject;

}

static DoraRuntimeClass classDoraClassObject;

public:

DoraClassObject() {}

virtual ~DoraClassObject() {}

};

 

Smiatam, che v definuitsiiata na klasa, sushto niama nishto neobichaino. Kakto se vizhda ima si konstruktor i virtualen destruktor. Funktsiiata GetClassObject sluzhi za poluchavane na imeto na klasa, koito se konstruira, s ideiata da se izpolzva po - natuk za poluchavane na ukazatel kum suiotvetniia obekt, chrez koito da polucha dostup do funktsiite i promenlivite na suotvetniia klas. Za tselta shte e neobhodima vuv vsichki naslednitsi na tozi klas da se predefinira tazi virtualna funktsiia.

Vruzkata mezhdu strukturata DoraRuntimeClass i klasut DoraClassObject, se osushtestviava s edin makros RUNTIME_CLASS, definiran po sledniia nachin:

 

#define RUNTIME_CLASS(class_name)(&class_name::class##class_name)

 

Do tuk sme izvurshili podgotovkata na neobhodimoto za dinamichnoto suzdavane na obektite v edno prilozhenie. Strukturata DoraRuntimaClass, klasut DoraClassObject i makrosut RUNTIME_CLASS, e neobhodimo da se razglezhdat zaedno, imenno tiahnata suvokupnost osiguriava neobhodimiia mehanizum za dinamichnoto suzdavane na obekti i vzaimodeistvieto mezhdu tiah.

Failut s implementatsiite e DoraClassObject.cpp, tam niama koi znae kakvo, prosto opisanieto na dvete callback funktsii zaedno sus zadavaneto na imeto na klasa:

 

DoraRuntimeClass DoraClassObject::classDoraClassObject = {"DoraClassObject", NULL, NULL};

DoraClassObject *DoraClassObject::CreateView(QWidget *parent = 0) {

return (*m_pCreateView)(parent);

}

DoraClassObject *DoraClassObject::CreateDocument() {

return (*m_pCreateDocument)();

}

 

 

III.Bazov izgleden klas i bazov dokumenten klas.

 

Neka da razdelim obektite v edno prilozhenie po sledniia nachin - neka obektite, koito pokazvat niakakvi danni v prozorets na ekrana, narechem izgledni klasove, a obektite, koito suhraniavat dannite na prilozhenieto i/ili izvurshvat obrabotkata na dannite, narechem dokumentni klasove. Togava suzdavaiki edin bazov klas DoraBaseView, sluzhesht za proizvodstvo na izgledni klasove i edin bazov dokumenten klas DoraBaseDocument, za dokumentnite klasove.

Klasut DoraBaseView e naslednik na dva klasa, QWidget, idvasht ot Qt i opisaniiat po - gore klas DoraClassObject, toi e definiran vuv faila s definitsii - DoraBaseView.h.

 

class DoraBaseView : public QWidget, public DoraClassObject

{

public:

DoraRuntimeClass *GetClassObject() const {

return &classDoraBaseView;

}

static DoraRuntimeClass classDoraBaseView;

static DoraClassObject *CreateView(QWidget *parent = 0);

static DoraClassObject *CreateDocument();

public:

DoraBaseView(QWidget *parent = 0, const char *name = 0) : QWidget(parent, name), DoraClassObject() {

}

~DoraBaseView() {}

 

private:

void paintEvent(QPaintEvent *e);

void resizeEvent(QResizeEvent *e);

void mousePressEvent(QMouseEvent *e);

void showEvent(QShowEvent *e);

void hideEvent)QHideEvent *e);

void focusInEvent(QFocusEvent *e);

void focusOutEvent(QFocusEevent *e);

 

public:

virtual void OnDraw(QPainter *p);

virtual void OnSize(int cx, int cy);

virtual void OnShowView();

virtual void OnHideView();

virtual void OnFocus();

virtual void OffFocus();

virtual void PostMouseRightButton();

virtual void PostMouseLeftButton();

virtual void PostMouseMidButton();

};

 

Kakto se vizhda ot definitsiiata na klasa, e neobhodimo da se definirat purvo statichnata promenliva classDoraBaseView, dvete statichni funktsii za dinamichnoto suzdavane na obektite - CreateView i CreateDocument i da se predifinira virtualnata funktsiia GetClassObject. Funktsiite ot sektsiia private sa zhirtualni funktsii definitrani v klasa QWidget na Qt. Virtualnite funktsii ot sledvashtata sektsiia public sa definirani ot men i sluzhat za priemane na suobshteniiata idvashti ot sredata i obrabotvani ot Qt. Te triabva da se predefinirat v naslednitsite na tozi klas. Po tozi nachin predostaviam na Qt, grizhata da ustanovi vruzkata s grafichnata sreda, koiato ot svoia strana ima grizhata da se zanimava s harduera. Tuk mu e miastoto da spomena, che izborut mi na Qt, veroiatno ne e nai - udachniia izbor na grafichna biblioteka, za razrabotka na podoben programen model, no kogato zapochvah razrabotkata mu Qt niakak mnogo stranno mi zaprilicha na neshto mnogo poznato, imam predvid MFC. Vupreki tova obache za moe uchudvane modelut proraboti. Taka, che produlzhavam s implementatsiiata na klasa. Tova e napraveno vuv faila s implementatsii DoraBaseView.cpp.

Purvo, vseki klas, razrabotvan spored tozi programen model, failut s implementatsii, triabva da zapochva sus sledniiat red - DoraRuntimeClass DoraBaseView::classDoraBaseView={"DoraBaseView", DoraBaseView::CreateView, NULL}; za izgleden klas, v sluchaia tova e bazoviiat klas DoraBaseView. Sushto taka e neobhodimo da implementira dvete funktsii - CreateView i CreateDocument. Za klasove, koito shte sa izgledi funktsiiata CreateView, vrushta ukazatel kum klas DoraClassObject a za dokumentnite klasove bi triabvalo da vrushta nulev ukazatel. No az predpochetoh da razdelia neshtata, taka che funktsiiata CreateView, da izpolzvam za izgledni klasove, a CreateDocument za dokumentnite klasove. Za izglednite klasove te izglezhdat taka:

 

DoraClassObject *DoraBaseView::CreateView(QWidget *parent = 0)

{

return new DoraBaseView(parent);

}

 

DoraClassObject *DoraBaseView::CreateDocument()

{

return NULL;

}

 

Tezi funktsii, taka napisani sa prednaznaznacheni za bazoviiat izgleden klas, taka che za proizvodnite mu klasove e neobhodimo da se prenapishat za konkretniia klas. Tuk niama do opisvam virtualnite funktsii, zashtoto te ne sa tolkova interesni, i sluzhat samo za priemane na subshteniiata, koito Qt izprashta kum prilozhenieto. Kakvo tochno izpulniavat zavisi ot konkretnoto prilozhenie. SHTe mozhete da vidite detailno kakvo predstavliavat ot source kodovete, sled kato gi publikuvam.

 

Neka sega podgotvim bazoviiat dokumenten klas - DoraBaseDocument. Toi ne e neshto slozhno. V nego sum dobavil mehanizum za upravlenie na izgledni klasove, svurzani s proizvodniia na nego dokumenten klas. Za tselta sum razrabotil edin template klas, predstavliavasht dinamichen masiv i edna struktura za elementite na masiva i ponezhe nishto drugo ne mi idvashe naum ia krustih RegistryStruct, tui kato tia shte se populva v masiva, sum deklariral promenliva ot tip dinamichen masiv, po sledniia nachin:

typedef DoraArray<RegistryStruct> TypeRegistryItem;

i sega samata promenliva

TypeRegistryItem localRegistry;

Definitsiiata typedef e hubavo da bude niakude v drug fail, kudeto da se deklarirat vsichki masivi ot tozi tip, za da ne se pretrupva izlishno failut s definitsii na bazoviiat dokumenten klas. E neka sega vidim kak e definiran tozi bazov dokumenten klas:

 

class DoraBaseDocument : public DoraClassObject

{

public:

DoraRuntimeClass *GetClassObject() const {

return &classDoraBaseDocument;

}

static DoraRuntimeClass classDoraBaseDokument;

static DoraClassObject *CreateView(QWidget *parent = 0);

static DoraClassObject *CreateDocument();

public:

DoraBaseDocument() : DoraClassObject() {}

~DoraBaseDocument() {}

public:

V tazi sektsiia sledvat niakolko funktsii za upravlenie na registura na svurzanite kum proizvodnite dokumentni klasove izgledi. Te izvurshvat slednoto - polulvat masiva sus elementite na strukturata RegistryStruct, premahvat nenuzhnite elementi pri premahvane na izgledniia obekt, obnoviavat ili po - tochno podavat signal za obnoviavane na izgleda i t.n.

private:

TypeRegistryItem localRegistry;

};

Definitsiiata na klasa e vuv faila DoraBaseDocument.h.

 

Neka sega da napishem implementatsiiata na klasa. Tova sum go napravil vuv faila DoraBaseDocument.cpp:

 

DoraRuntimeClass DoraBaseDocument::classDoraBaseDocument={"DoraBaseDocument", NULL, DoraBaseDocument::CreateDocument};

DoraClassObject *DoraBaseDocument::CreateView(QWidget *parent = 0)

{

return NULL;

}

DoraClassObject *DoraBaseDocument::CreateDocument()

{

return new DoraBaseDocument();

}

 

Sega veche sled kato imame bazov izgleden klas i bazov dokumenten klas, mozhem spokoino da pristupim kum suzdavane na konkretno prilozhenie spored tozi programen model.

 

IV.Suzdavane na prilozheniia po tozi programen model.

 

Za da suzdavame prilozheniia s tozi programen model e neobhodimo da zapochnem ot klasa na prilozhenieto - DoraMainApp, koito da formira klasa na glavniia prozorets - DoraMainFrame, purviiat ot vsichki izgledi na prilozhenieto - DoraCentralView i tsentralniia dokumenten klas - DoraCentralDocument. Znaem po definitsiia, che vsiaka programa na S/S++, zapochva da se izpulniava ot funktsiia main. Pri men tazi funktsiia ima za zadacha da konstruira ukazatel kum klasa DoraMainApp i da izvika negovata funktsiia RunApp. Eto kak izglezhda tova na praktika vuv faila main.cpp.

 

 

int main(int argc, char **argv)

{

DoraRuntimeClass *doraClassApp = RUNTIME_CLASS(DoraClassApp);

DoraClassObject *doraClassObject = doraClssApp->CreateView();

mainApp = dynamic_cast<DoraClassApp *>(doraClassObject);

int result = mainApp->RunApp(argc, argv);

delete mainApp;

return result;

}

 

Definiraiki globalnata promenliva mainApp, nie poluchavame globalen ukazatel kum glavniia klas na prilozhenieto. Kudeto e neobhodimo da poluchim dostup do funktsiite i promenlivite na glavniia klas na prilozhenieto triabva da defimirame tazi promenliva kato extern DoraMainApp *mainApp; No neka sega da vidim kakvo predstavliava glavniia klas na prilozhenieto DoraMainApp, i e definiran vuv faila s definitsiite - DoraMainApp.h po sledniia nachin:

 

class DoraMainApp : public DoraClassObject

{

public:

DoraRuntimeClass *GetRuntimeClass() const {

return &classDoraMainApp;

}

static DoraRuntimeClass classDoraMainApp;

static DoraClassObject *CreateView(QWidget *parent = 0);

static DoraClassObject *CreateDocument();

public:

int RunApp(int argc, char **argv);

public:

DoraMainFrame *mainFrame;

};

 

Kakto se vizhda ot definitsiiata na klasa, niama nishto neobichaino v tozi klas, osven mozhe bi tova, che tozi klas ne e izgleden, vupreki koeto suzdavaneto mu se izvurshva s funktsiia CreateView, no toi ne pokazva nikakuv prozorets. Purviiat prozorets na prilozhenieto e klasut DoraMainFrame. Konstruiraneto mu se izvurshva vuv funktsiia RunApp, ot faila s implementatsii - DoraMainApp:

 

DoraRuntimeClassDoraMainApp::classDoraMainApp = {"DoraMainApp",DoraMainApp:CreateView, NULL};

DoraClassObject *DoraMainApp::CreateView(QWidget *parent = 0)

{

return new DoraMainApp();

}

DoraClassObject *DoraMainApp::CreateDocument()

{

return NULL;

}

 

int DoraMainApp::RunApp(int argc, char **argv)

{

QApplication a(argc, argv);

QWidget *wid = QApplication::desktop();

 

a.setFont("times", 12, QFont::Normal, FALSE, QFont::AnyCharSet);

int wFrame = wid->width();

int hFrame = wid->height();

int w = (int)((wFrame * 2) / 3);

int h = (int)((hFrame * 2) / 3);

int x = (int)((wFrame - w) / 2);

int y = (int)((hFrame - h) / 2);

 

DoraRuntimeClass *runtimeMainFrame = RUNTIME_CLASS(DoraMainFrame);

DoraClassObject *objectMainFrame = runtimeMainFrame->CreateView(NULL);

mainFrame = dynamic_cast<DoraMainFrame *>(objectMainFrame);

mainFrame->OnCreate();

 

a.setMainFrame(mainFrame);

mainFrame->setGeometry(x, y, w, h);

mainFrame->show();

int result = a.exec();

 

return result;

}

 

SHTe se spra po - obstoino na funktsiiata RunApp, tui kato ima dosta neshta koito idvat ot bibliotekata Qt. Redut QApplication a(argc, argv), ednoznachno opredelia glavniia klas na prilozhenieto ot gledna tochka na Qt. Promenlivata *wid e ukazatel kum desktopa na grafichnata sred, v sluchaia KDE. Po natatuk zadavam obshto validen shrift za prilozhenieto s izraza a.setFont("times", 12, QFont::Normal, False, QFont::AnyCharSet). Za poveche informatsiia za promenlivite i klasovete na Qt, prochetete opisanieto na bibliotekata, to e dostatuchno dobro i razbiraemo. Sledvashtite redove opredeliat pozitsiiata na ekrana, shirinata i visochinata na prozoretsa, koito se kanim da pokazhem. V sluchaia prozoretsut ima takiva razmeri, che da pokrie dve treti ot vidimiia ekran.

Poluchavaneto na validen ukazatel na purviia prozorets na prilozhenieto se izvurshva na tri stupki. Purvo poluchavam ukazatel kum strukturata DoraRuntimeClass s pomoshtta na makrosa RUNTIME_CLASS, Tozi ukazatel se izpolzva za konstruirane na promenliva ot tip DoraClassObject, sled izvikvane na funktsiiata CreateView s nulev ukazatel, koeto zadava na Qt, che tova e purviiat prozorets. Nakraia preobrazuvam vurnatiia ukazatel kum ukazatel kum neobhodimiia mi obekt. Tuk i navsiakude v programniia model za preobrazuvne na ukazateli kum opredeleni obekti izpolzvam operatsiiata dynamic_cast<T>(v). Taka az imam RTTI (RunTime Type Information), za vseki obekt sformiran po tozi nachin. V skobi kazano takova preobrazuvane pri Microsoft izglezhda taka: mainFrame = (DoraMainFrame *)GetRuntimeClass(), no tova si e v kraina smetka tehen problem. Metodut koito izpolzvam za preobrazuvane na ukazateli e mnogo po - bezopasen i nadezhden.

No da produlzhim natatuk. S izraza a.setMainFrame(mainFrame), zadavam na Qt, glavniiat prozorets na prilozhenieto - DoraMainFrame. Sled tova zadavam nachalnata pozitsiia x, y, shirinata w i visochinata h, na prozoretsa, sled koeto go pokazam. Promenlivata rezult sudurzha rezultata ot izpulnenieto na prilozhenieto, koiato promenliva se vrushta vuv funktsiiata main.

Sega neka se vurnem kum obekta na glavniia prozorets na prilozhenieto - DoraMainFrame. Nego sum go definiral vuv faila s definitsii DoraMainFrame.h:

 

class DoraMainFrame : public QWidget, public DoraClassObject

{

public:

DoraRuntimeClass *GetClassObject() const {

return &classDoraMainFrame;

}

static DoraRuntimeClass classDoraMainFrame;

static DoraClassObject *CreateView(QWidget *parent = 0);

static DoraClassObject *CreateDocument();

public:

DoraMainFrame(QWidget *parent = 0, const char *name = 0) : QWidget(parent, name), DoraClassObject() {}

~DoraMainFrame() {}

public:

void OnDraw(QPainter *p);

void OnSize(int cx, int cy);

private:

void paintEvent(QPaintEvent *e);

void resizeEvent(QResizeEvent *e);

public:

DoraCentralDocument *GetDocument();

void OnCreate();

public:

DoraCentralView *m_pCentralView;

DoraBaseDocument *m_pBaseDocument;

QRect clientRect;

};

 

Taka definiran tozi klas ne se otlichava koi znae kolko ot bazoviiat klas DoraBaseView, s izklyuchenie na funktsiiata GetDocument i dvete promenlivi m_pCentralView i m_pBaseDocument. Funktsiiata GetDocument vrushta ukazatel kum klasa na glavniia dokument, chrez promenlivata m_pBaseDocument, a promenlivata m_pCentralView, suhraniava ukazatel kum purviiat izgleden klas na prilozhenieto. Sega neka vidim kakvo predstavliava samiiat klas. Funktsiite sa opisani vuv faila s implementatsii - DoraMainFrame.cpp.

DoraRuntimeClass DoraMainFrame::classDoraMainFrame={"DoraMainFrame", DoraMainFrame::CreateView, NULL};

DoraClassObject *DoraMainFrame::CreateView(QWidget *parent = 0)

{

return new DoraMainFrame(parent);

}

DoraClassObject *DoraMainFrame::CreateDocument()

{

return NULL;

}

 

Funktsiite OnDraw i OnSize, ne sa chak tolkova interesni, tiahnata zadacha e daopredeliat razmerite na pokazvaniia prozorets i da go izrisuvat. Taka, che az niama da se spiram na tiah, a shte opisha funktsiiata OnCreate, zashtoto tia zasluzhava po - goliamo vnimanie. Tazi funktsiia e vhodnata tochka na klasa, opredeliashta i konstruirashta obektite, koito se poratsdat ot suotvetniia klas, razbira se bih mogul da ia napravia zhirtualna v niakakuv bazov klas, obache taka bih lishil klasovete, koito shte suzdavam ot mobilnost. Po - dobre e takava funktsiia da e unikalna za vseki klas. Kakvo predstavliava funktsiia OnCreate, v klasa DoraMainFrame.

void DoraMainFrame::OnCreate()

{

DoraRuntimeClass *runtimeDocument = RUNTIME_CLASS(DoraCentralDocument);

DoraClassObject *objectDocument = runtimeDocument->CreateDocument();

DoraCentralDocument

*centtalDocument=dynamic_cast<DoraCentralDocument*>(objektDocument();

m_pBaseDocument = dynamic_cast<DoraCentralDocument *>(centralDocument);

 

DoraRuntimeClass *runtimeView = RUNTIME_CLASS(DoraCentralView);

DoraClassObject *objectView = runtimeView->CreateView(this);

m_pCentralView = dynamic_cast<DoraCentralView*>(objectView);

m_pCentralView->OnCreate(this);

}

 

Kakto se vizhda ot opisanieto na OnCreate, funktsiiata purvo konstruira obekt ot klas DoraCentralDocument, koito e i purviiat dokumenten klas na prilozhenieto, sled tova konstruira obekt ot klas DoraCentalView, koito e i purviiat izgleden klas na nasheto prilozhenie. Ne burkaite klasa DoraMainFrame s tozi klas, DoraMainFrame e ramkata na prilozhenieto, a DoraCentralView e purviiat izgled. Ponezhe zasega se ogranichavam do ramkite na edno prilozhenie, ne mislia che e neobhodimo da zapazvam ukazatelite na suzdadenite obekti v registry.

Neka vidim sega, kakvo predstavliava funktsiiata GetDocument. Kakto spomenah po gore neinata edinstvena zadacha e da vurne ukazatel kum tsentralniia dokument na prilozhenieto DoraCentralDocument.Tia e malka i zglezhda taka:

DoraCentralDocument *DoraMaunFrame::GetDocument()

{

DoraCentralDocument *centralDocument;

centralDocument = dynamic_cast<DoraCentralDocument*>(m_pBaseDcument);

return centralDocument;

}

Za da bude edin privichno izglezhdasht prozorets, ostava da zakachim, kum nego edin klas za menyu, edin klas za lenta s instrumenti - ToolBar i eventualno edin klas za StatusBar. Taka na tozi etap napravihme edin prozorets, koito mozhe da se minimizira i maksimizira, blagodarenie na Qt. Pritezhavasht menyu, lenta s instrumenti i lenta za sustoianie. Sega mozhem spokoino da go minimizirame i maksimizirame do bezkrainost, no tova prilozhenie vse oshte ne vurshi nikakva polezna rabota, ot gledna tochka na potrebitelite. Vtakuv sluchai neka se opitame da suzdadem neshto, koeto vurshi niakakva rabota. Naprimer edin klas za pokazvane i opredeliane na dati i vreme, neshto kato kalendarche, s vklyuchen v nego chasovnik.

Takuv klas se konstruira ot klasa DoraCentralView i shte se aktivira ot menyuto. Tozi klas neka se sustoi ot tri izgledni klasa, edin dokumenten klas i dva PushButtons s naimenovaniia "Promeni" i "Otkazhi". SHTe napravim edin izgleden klas, koito shte go natovarim sus zadachata da upravliava drugite dva izgleda, a imenno klasut CalendarView i ClockView, osven tova toi shte ima grizhata za osushtestviavane na komunikatsiiata mezhdu tiah i dokumentniia klas i prehvurlianeto na dannite ot lokalniia dokumenten klas i tsentralniia dokumenten klas na prilozhenieto - DoraCentralDocument. Kato nachalo neka deklarirame dve promenlivi v klasa DoraCentralDocument - CString curentDate i CString curentTime.

Sega shte deklarierame tsentralniia izgleden klas - DateAndTimeView. Tova shte napravim vuv faila s deklaratsii - DateAndTimeView.h:

 

class DateAndTimeView : public DoraBaseView

{

public:

DoraRuntimeClass *GetClassObject() const {

return &classDateAndTimeView;

}

static DoraRuntimeClass classDateAndTimeView;

static DoraClassObject *CreateView(QWidget *parent = 0);

static DoraClassObject *CreateDocument();

public:

DateAndTimeView(QWidget *parent = 0, const char *name = 0) : DoraBaseView(parent, name) { }

~DateAndTimeView() { }

public:

void OnDraw(QPainter *p);

void OnSize(int cx, int cy);

public:

DateAndTimeDocument *GetDocument();

void OnCreat(DoraClassObject *pObject, DoraBaseView *pView);

public:

DoraClassObject *m_pObject;

DoraBaseView *m_pView;

DoraBaseDocument *m_pDocument;

CalendarView *m_pCalendar;

ClockView *m_pClock;

public:

QRect clientRect;

QRect clockRect;

QRect calendarRect;

DoraPushButton *buttonApply;

DoraPushButton *buttonCancel;

};

 

Dulzha da vi predupredia, che tova ne e tseliia klas, a samo onazi chast, neobhodima za ilyustratsiia na ideiata, zaradi koiato sum sutvoril tseliiat tozi model. Neka sega da vidim i faila s impementatsii - DateAndTimeView.cpp:

 

extern DoraMainApp *mainApp;

 

DoraRuntimeClass DateAndTimeView::classDateAndTimeView = {"DateAndTimeView", DateAndTimeView::CreateView, NULL};

 

DoraClassObject *DateAndTimeView::CreateView(QWidget *parent = 0)

{

return new DateAndTimeView(parent);

}

 

DoraClassObject *DateAndTimeView::CreateDocument()

{

return NULL;

}

 

Tuk poveche vnimanie shte otdelia na funktsiite OnCreate i GetDocument, kakto i na obrabotkata na suobshteniiata ot dvata butona - buttonApply i buttonCancel.. I taka, kak izglezhda funktsiiata OnCreate, ot klas DateAndTimeView ?

void DateAndTimeView::OnCreate(DoraClassObject *pObject, DoraBaseView *pView)

{

m_pObject = pObject;

m_pView = pView;

 

DoraRuntimeClass *runtimeCalendar = RUNTIME_CLASS(CalendarView);

DoraClassObject *objectCalendar = runtimeClaendar->CreateView(this);

calendarView = dynamic_cast<ClaendarView *>(objectCalendar);

calendarView->OnCreate(m_pObject, this);

 

Taka sled konstruiraneto na obekta CalendarView, az mu predavam ukazatel kum klas DoraClassObject i ukazatel kum roditelskiia mu klas - DateAndTimeView.. Sled tova povtariam sushtata manipulatsiia s klasa ClockView, kakto i s dvata obekta na butoni. Sled tova konstruirame klas DateAndTimeDocument.

DoraRuntimeClass *runtimeDocument = RUNTIME_CLASS(DateAndTimeDocument);

DoraClassObject *objectDocument = runtimeDocument->CreateDocument();

DateAndTimeDocument *dateAndTimeDocument = dynbamic_cast<DateAndTimeDocument *>(objectDocument);

m_pDocument = dynamic_cast<DoraBaseDocument *>(dateAndTimeDocument);

 

Taka v tozi moment az veche imam validni ukazateli kum dvata izgledni klasa, kum dvata butona i kum dokumentniia klas. Sega neka napravim funktsiiata GetDocument.

DateAndTimeDocument *DateAndTimeView::GetDocument()

{

DateAndTimeDocument *dateAndTimeDocument;

dateAndTimeDocument = dynamic_cast<DateAndTimeDocument *>(m_pDocument);

return dateAndTimeDocument;

}

Sega neka da napravim vzaimodeistvieto mezhdu otdelnite klasove. Na purvo miasto neka da opishem funktsiiata, koiato shte otgovaria na butona "Promeni". Tia shte ima zadachata da prochete dannite ot klasovete CalendarView i ClockView, sled koeto da postavi prochetenite stoinosti v glavniia dokumenten klas na prilozhenieto, za da mogat te da se izpolzvat ot vsichki drugi klasove ot prilozhenieto. Za tselta v bazoviiat izgleden klas shte definirame i opishem dve funktsii, ednata ot koito e virtualna. Tezi funktsii triabva da reagirat na natiskane na edin ot dvata butona - "Promeni" ili "Otkazhi" . Eto kak shte izglezhdat te:

Vuv faila s deklaratsii na klasa DoraBaseView:

void PostMessage(int id_message, int id_control);

virtual void PostPushButton(int id_control);

 

I vuv faila s implementatsii:

void DoraBaseView::PostMessage(int id_message, int id_control)

{

switch(id_message)

{

case ID_PUSH_BUTTON:

PostPushButton(id_control);

break;

.

.

.

};

}

void DoraBaseView::PostPushButton(int id_control)

{

}

Sega shte triabva da predefinirame funktsiiata PostPushButton, v klasa DateAndTimeView. I vuv faila s deklratsii na tozi klas funktsiiata ima sledniia vid:

void PostPushButton(int id_control);

I neinoto opisanie vuv faila s implementatsii:

void DateAndTimeView::PostPushButton(int id_control)

{

loadDateAndTime();

switch(id_control)

{

case ID_PUSH_APPLY_BUTTON:

PostApplyButton();

break;

case ID_PUSH_CANCEL_BUTTON:

PostCancelButton();

break;

}

}

Sega triabva da definirme i opishem oshte tri funktsii loadDateAndTime, PostApplyButton i PostCancelButton. Funktsiiata loadDateAndTime ima zadachata da sneme dannite ot dvata izglednite klasove CalendarView i ClockView, sled koeto da gi postavi v dokumentniia klas DateAndTimeDocument. Za tselta shte definirame chetiri promenlivi ot tip CString v tozi dokumenten klas - curentDate, curentTime, oldDate i oldTime. Eto kak izglezhda funktsiiata loadDateAndTime:

void DateAndTimeView::loadDateAndTime()

{

DateAndTimeDocument *pDoc = GetDocument();

pDoc->oldDate = "";

pDoc->oldDate = pDoc->curentDate;

pDoc->oldTime = "";

pDoc->oldTime = pDoc->curentTime;

 

pDoc->curentDate = "";

pDoc->curentDate = calendarView->getData();

pDoc->curentTime = "";

pDoc->curentTime = clockView->getData();

}

Funktsiiata PostApplyButton, shte sneme dannite ot dotsumentniia klas - DateAndTimeDocument i shte gi postavi v tsentralniia dokumenten klas - DoraCentralDocument. Eto kak shte napravim tova:

void DateAndTimeView::PostApplyButton()

{

DoraCentralDocument *centralDocument = mainApp->mainFrame->getDocument();

DataAndTimeDocument *pDoc = GetDocument();

 

centralDocument->curentDate = "";

centralDocument->curentData = pDoc->curentDate;

centralDocument->curentTime = "";

centralDocument->curentTime = pDoc->curentTime;

}

Nakraia ostava da opishem i funktsiiata PostCancelButton. Eto kak izglezhda tia:

void DateAndTimeView::PostCancelButton()

{

DateAndTimeDocument *pDoc = GetDocument();

calendarView->setData(pDoc->oldDate);

clockView->setData(pDoc->oldTime);

}

Kakvo vsushttnost napravihme do momenta - na purvo miasto osigihme mehanizum za prehvurliane na danni ot edin klas v drug s pomoshtta na dokumentnite klasove i osven tova, prinudihme nashite klasove da si komunikirat posredstvom suobshteniia. Taka naprimer funktsiiata loadDateAndTime, se izpulniava pri promiana na dannite v klasovete CalendarView i ClockView, sled kato se izvika funktsiiata PostMessage ot bazoviia klas - DoraBaseView, koiato ot svoia strana izvikva predefiniranata funktsiia PostButtonMessage ot klasa DateAndTimeView. Tova stana vuzmozhno blagodarenie na tova, che obektite na prilozhenieto se konstruirat dinamichno, po vreme na izpulnenie na prilozhenieto. Osven tova dazhe ako premahna, otnovo po vreme na izpulnenie, klasa DateAndTimeView, dannite shte se zapaziat v tsentralniia dokumenten klas, ot kudeto shte mogat da se prochetat. Klasovete CalendrView i ClockView, niamat funktsiia GetDocument, koeto puk mi pozvoliava, da gi izpolzvam v drugi izgledni klasove, bez da e neobhodimo da vnasiam v tiah niakakvi korektsii. S koeto suzdadoh dve blokcheta, koito moga da izpolzvam vuv vsiako moe drugo pilozhenie.

Vurveiki po tazi logika na razsuzhdenie, neminuemo shte se doberem do ideiata za COM, samo che v ramkite na edno prilozhenie. Taka ako poglednem po - shiroko na neshtata, mozhem da tretirame suvokupnostta ot klasovete DoraMainFrame, DocraCentralView i DoraCentralDocument, kato edno konteireno prilozhenie, a klasovete DateAndTimeView, CalendarView, ClockView i DateAndTimeDocument, kato vutreshen komponent na prilozhenieto. Tova spored Microsoft e "ochevidno falshivo obstoiatelstvo", no az ne razbiram zashto da e falshivo i pri men si e suvsem normalna situatsiia. Tuk shte si pozvolia da izkazha edno moe lichno mnenie, v smisul che edna ot prichinite, koito praviat Windows vuv vsichkite mu modifikatsii e tolkova tromav, e imeno COM i OLE2, osven tova ideiata da se izpulniavat chasti ili tseli prilozheniia v ramkite na drugo prilozhenie si e otvorena zadna vrata na kushta, pulna s hubavi neshta i kudeto niama nikoi. Tolkova za Microsoft i neshtoto narecheno Windows.

 

V. Kakvo se postiga s tozi programen model.

 

1. Az tuk govoria za Qt, koiato e edna ot nai - populiarnite grafichni biblioteki v Linux, no konkretno za takuv programen model spored men ne e osobeno udachna. Tozi programen model e dostatuchno mobilen za da mozhe da se izpolzuva i s drugi grafichni biblioteki.

2.Sled kato se suzdadat "blokcheta", po tozi programen model az moga da sglobiavam dostatuchno burzo prilozheniia, pri tova shte sa dostatuchno stabilni, poradi faktaa che sa vuv vid na source code, koeto bi mi pozvolilo da dostigna 100% suvmestimost mezhdu komponentite na dadeno prilozhenie.

3.Prilagaiki podhoda na klasicheskoto pisane na programi, az pechelia otnositelna nezavisimost ot harduera na konkretnata mashina i ot grafichnata sreda s koiato rabitia. Tova e taka zashtoto sum predostavil grizhata za tezi vazhni neshta na samata grafichna biblioteka, koeto ne e malovazhno.

4. Po moe skromno mnenie Qt kato grafichna biblioteka, dostatuchno mnogo prilicha na MFC, taka che ne vizhdam zashto da niama i mehanizum podoben na arhitekturata Document/View.

 

VI.Posoka na razvitie na tozi model.

 

Tuk mnogo mi se iskashe da kazha, che shte razvivm modela v posoka na COM ili neshto podobno, no za da stane tova imam useshtaneto, che shte triabva da se usiguri sreda, koiato da se grizhi za prenasianeto na obektite ot edno prilozhenie kum drugo. Koeto direktno ni otvezhda kum veche napravenata grafichna sreda Windows, ot koiato vseki ot nas poveche ili po - malko e imal glavoboliia. Taka che zasega shte se vuzdurzha.

Smiatam da dovursha bazovite si klasove, izgradeni spored tozi model, kakto i klasove kato naprimer - dialogovi paneli, spisuchni izgledi i t.n. Sushto taka edin takuv programen model bi bil nepulen, bez da ima klasove za mnogonishkovo programirane i obrabotka na danni, Taka che shte rabotia v tazi nasoka.

 

Mihail Petrov - grad Smolian

16.03.2002 godina.



<< Kirilitsa + efax + html2ps + php HOWTO | Programirane pod Linuks. Sredi za programirane >>