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.