Dr. Vermes Mátyás1
1999. február 13.
A ddict2 program és a táblaobjektumok részletes ismertetésével másik dokumentum foglalkozik, pillanatnyi célunk csak annyi, hogy a kezdő minél hamarabb példát lásson CCC-beli adatbázis kezelésre. Hangsúlyozzuk, hogy a bemutatott programok színtiszta Clipperben vannak2, amit egyszerűen bizonyít, hogy Clipperrel fordítva is működnek. A Clipper nyelv ismeretében, a preprocesszált és generált kódok (esetleg a könyvtárak forrásának) tanulmányozásával teljes mélységben megérthetők a programok.
A DATIDX és Btrieve formátumokhoz létezik saját adatbázis struktúra editor, ami megtalálható (forrásban is) a ccctab könyvtárban. DBF formátumhoz sem volna nehéz ilyen programot készíteni, de felesleges, mert bővében vagyunk a megfelelő eszközöknek. Mellesleg a célalkalmazásban használt adatbázisformátumtól függetlenül az adatbázisok prototípusa és az adatszótár mindig lehet DBF-ben.3
Az adatbáziskezelés CCC-ben az ún. táblaobjektum interfésszel történik. A táblaobjektumok kódját a ddict2 programmal generáljuk, amit a d.bat filével indítunk el. A Karbantartás:Tábla definíció bevitele menüpontban előjövő maszkban az alábbi adatokat vittük be:
| Adatbázis | Logikai név | Index filé | Oszlopok |
| tesztdb | name | tesztdb1 | name |
| tesztdb | size | tesztdb2 | size |
| tesztdb | time | tesztdb3 | date,time |
A program megkérdezi az adatbázis módosítójának nevét, amire megfelel a guest válasz. Az azonosítás célja, hogy a programozó közösség tagjai tájékozódni tudjanak egymás munkájában. Jelenleg a ddict2 program a ComFirm programozóinak nevét és a guest nevet fogadja el. Ha más közösség akar dolgozni ddict2-vel, akkor legegyszerűbb a megengedett nevek halmazát átírni ddict2 forrásában (ccctools\ddict2). A név megadása után az adatszótárba bekerül a TESZTDB adatbázis három indexszel.
Mielőtt a ddict2 programból kilépnénk, a Program:#ifdef ARROW... menüponttal legeneráljuk az objektumok kódját, jelen esetben a _tesztdb.ch és _tesztdb.prg filéket (amikre később érdemes egy pillantást vetni).
A ddict2 programból kilépve folytatódik a d.bat script futása, és létrejön xddict.lib. Ezt a lib-et a demonstrációs programok be fogják linkelni. Az adatszótár linkelésével CCC-ben a programok mindig apriori információval rendelkeznek az adatbázisok struktúrájáról.
A teszt adatbázison dolgozó programok a ccctutor\xtesztdb directoryban találhatók. A programokat mint általában, most is egy m.bat nevű scripttel fordítjuk le, jelenleg ennek tartalma:
bapp_w32c_datidx -bxddict -i..\xddict -p..\xddict\$(build_obj)
A script minden main-t tartalmazó PRG-ből EXE-t készít, úgy hogy lefordítja és linkeli az összes main-t nem tartalmazó modult is (most éppen egy ilyen sincs).
Az include filéket az aktuális directoryn kívül keresi ..\xddict-ben is (-i kapcsoló). Az EXE-khez hozzálinkeli az xddict.lib-et (-b kapcsoló), amit az ..\xddict\$(build_obj) könyvtárban is keres (-p kapcsoló).
A program Win32 konzolos lesz és DATIDX adatbáziskezeléssel fog működni. Ha a bapp_w32c_datidx script helyett a bapp_w32g_dbfctx scripttel fordítanánk, akkor GUI-s, DBFCTX adatbáziskezelésű programot kapnánk. Mindkét script a BUILD program megfelelő felparaméterezésével és futtatásával működik. A BUILD-re épülő make rendszer használatát a Programkészítés BUILD-del dokumentum ismerteti részletesen.
Az xtesztdb példaprogram a tesztdb adatbázist feltölti az aktuális directory filébejegyzéseinek adataival.
#include "directry.ch"
#include "table.ch"
#include "_tesztdb.ch"
function main()
local dir:=directory("*.*"), n
TESZTDB:open
for n:=1 to len(dir)
TESZTDB:append
TESZTDB_NAME:=dir[n][F_NAME]
TESZTDB_SIZE:=dir[n][F_SIZE]
TESZTDB_DATE:=dir[n][F_DATE]
TESZTDB_TIME:=dir[n][F_TIME]
TESZTDB_ATTR:=dir[n][F_ATTR]
next
TESZTDB:close
return NIL
A program elején inkludáljuk table.ch-t, ami a táblaobjektumok metódushívásainak preprocesszálására tartalmaz direktívákat. A _tesztdb.ch filéből veszi a program a TESZTDB_NAME,... mezőhivatkozások fordításához szükséges makrókat. A TESZTDB:open utasítás megnyitja az adatbázist, sőt, ha az nem létezik még azt is felajánlja, hogy üresen létrehozza. Ezt megteheti, mert a program ismeri az adatbázis struktúráját, hiszen a belinkelt _tesztdb.prg tartalmazza annak leírását. Minden bejegyzéshez egy új rekordot teszünk az adatbázisba, amit kitöltünk, végül lezárjuk az adatbázist.
A fenti xtesztdb program minden platformon linkelhető.
| Script | Op. rendszer | Adatformátum | Megjelenítési mód |
| bapp_clp_dbfntx | DOS | DBFNTX | karakteres |
| bapp_w32c_dbfctx | Win32 | DBFCTX | karakteres |
| bapp_w32g_dbfctx | Win32 | DBFCTX | GUI |
| bapp_w32c_btrieve | Win32 | Btrieve | karakteres |
| bapp_w32g_btrieve | Win32 | Btrieve | GUI |
| bapp_w32c_datidx | Win32 | DATIDX | karakteres |
| bapp_w32g_datidx | Win32 | DATIDX | GUI |
Az alábbi program kilistázza tesztdb tartalmát dátum/idő szerinti sorrendben. A program forrása a ccctutor\xtesztdb\xnavig.prg-ben található.
#include "table.ch" #include "_tesztdb.ch" function main() TESZTDB:open TESZTDB:control:="time" TESZTDB:gotop while( !TESZTDB:eof ) ? TESZTDB_NAME,TESZTDB_SIZE,TESZTDB_DATE,TESZTDB_TIME TESZTDB:skip end TESZTDB:close return NIL
Az alábbi program (melynek forrása a ccctutor\xtesztdb könyvtárban taláható) az előző pontokban elkészült tesztdb adatbázist browseolja. A program nagyon hasonlít az array-t browseoló példához. A sorrend beállítása itt nem a tömb rendezésével, hanem a vezérlő index cseréjével történik.
#include "table.ch"
#include "_tesztdb.ch"
function main()
local brw, smenu:={}
setcursor(0)
set date format to "yyyy.mm.dd"
TESZTDB:open
TESZTDB:control:="name"
brw:=TESZTDB:browse(10,10,maxrow()-2,maxcol()-10)
brwColumn(brw,"Name",{||TESZTDB_NAME},"XXXXXXXXXXXXX")
brwColumn(brw,"Size",{||TESZTDB_SIZE},"99999999")
brwColumn(brw,"Date",{||TESZTDB_DATE})
brwColumn(brw,"Time",{||TESZTDB_TIME})
brwColumn(brw,"Attr",{||TESZTDB_ATTR},1)
aadd(smenu,{"By name",{|b|sortbyname(b)}})
aadd(smenu,{"By time",{|b|sortbytime(b)}})
aadd(smenu,{"By size",{|b|sortbysize(b)}})
brwMenu(brw,"Sort","Sort by name/time/size",smenu)
brwMenu(brw,"Alert","Make an alert",;
{||2!=alert("Are you sure?",{"Continue","Quit"})})
brwMenuName(brw,"[Directory]")
brwCaption(brw,"Database Browse Example")
brwSetFocus(brw)
brwShow(brw)
brwLoop(brw)
brwHide(brw)
return NIL
static function sortbyname(b)
TESZTDB:control:="name"
b:refreshall()
return NIL
static function sortbytime(b)
TESZTDB:control:="time"
b:refreshall()
return NIL
static function sortbysize(b)
TESZTDB:control:="size"
b:refreshall()
return NIL
A browse objektum a brwShow() függvényhívásnál jelenik meg a képernyőn. Az interaktív programhasználat közben a vezérlés végig a brwLoop() eseménykezelő függvényben van. Világos, hogy brwShow(), brwLoop(), ... függvények implementációja karakteres és GUI-s környezetben nem lehet egyforma, ezért a programok platformfüggetlensége csak akkor tartható fenn, ha megelégszünk a példában bemutatott magas szintű interfész nyújtotta lehetőségekkel.
Az aktuális directory bejegyzéseinek példáján mutatjuk be egy kétdimenziós array browseolását. A program a ccctutor\xbrwarr directoryban található. A browse objektum felszerelésének több (bár közel sem összes) lehetőségét is bemutatjuk. Érdemes a programot karakteres és GUI-s módban is kipróbálni.
#include "directry.ch"
function main()
local dir:=directory("*.*","D")
local brw:=brwCreate(10,10,maxrow()-2,maxcol()-10)
local smenu:={}
setcursor(0)
set date format to "yyyy.mm.dd"
brwArray(brw,dir)
brwColumn(brw,"Name",brwABlock(brw,F_NAME),"XXXXXXXXXXXXX")
brwColumn(brw,"Size",brwABlock(brw,F_SIZE),"99999999")
brwColumn(brw,"Date",brwABlock(brw,F_DATE))
brwColumn(brw,"Time",brwABlock(brw,F_TIME))
brwColumn(brw,"Attr",brwABlock(brw,F_ATTR),1)
aadd(smenu,{"By name",{|b|sortbyname(b)}})
aadd(smenu,{"By time",{|b|sortbytime(b)}})
aadd(smenu,{"By size",{|b|sortbysize(b)}})
brwMenu(brw,"Sort","Sort by name/time/size",smenu)
brwMenu(brw,"Alert","Make an alert",;
{||2!=alert("Are you sure?",{"Continue","Quit"})})
brwMenuName(brw,"[Directory]")
brwCaption(brw,"Array Browse Example")
brwShow(brw)
brwLoop(brw)
brwHide(brw)
return NIL
static function sortbyname(b)
local a:=brwArray(b)
asort(a,,,{|x,y| x[F_NAME]<=y[F_NAME]})
b:refreshall()
return NIL
static function sortbytime(b)
local a:=brwArray(b)
asort(a,,,{|x,y| dtos(x[F_DATE])+x[F_TIME]<=dtos(y[F_DATE])+y[F_TIME]})
b:refreshall()
return NIL
static function sortbysize(b)
local a:=brwArray(b)
asort(a,,,{|x,y| x[F_SIZE]<=y[F_SIZE]})
b:refreshall()
return NIL
A browse objektum a brwShow() függvényhívásnál jelenik meg a képernyőn. Az interaktív programhasználat közben a vezérlés végig a brwLoop() eseménykezelő függvényben van. Világos, hogy brwShow(), brwLoop(), ... függvények implementációja karakteres és GUI-s környezetben nem lehet egyforma, ezért a programok platformfüggetlensége csak akkor tartható fenn, ha megelégszünk a példában bemutatott magas szintű interfész nyújtotta lehetőségekkel.
Egy új osztályhoz mindig meg kell írni a Class, New és Ini függvényeket. Az alábbi mintában _classname_ helyére mindenhol az osztály tényleges nevét kell írni. Teljes példa található a ccctutor\xclass könyvtárban. Érdemes tanulmányozni a runtime library beépített osztályainak forrását.
function _classname_Class()
static clid
if( clid==NIL )
clid:=classRegister("_classname_",{objectClass()})
classMethod(clid,"initialize",{|this|_classname_Ini(this)})
classAttrib(clid,"cargo")
end
return clid
function _classname_New()
local clid:=_classname_Class()
return objectNew(clid):initialize()
function _classname_Ini(this)
objectIni(this)
return this
A ccctutor\xmask könyvtárban található az alábbi példaprogram. A program karakteres és GUI-s módban is működik. Clipper program készíthető az bapp_clp paranccsal, Win32 karakteres program készül az bapp_w32c utasítás, GUI-s az bapp_w32g utasítás hatására.
function main()
cls
set date format to "yyyy/mm/dd"
demo( {|g|load(g)},{|g|readmodal(g)},{|g|store(g)})
return NIL
#include "demo.say"
static function load(getlist)
//string demo
g_s:picture:=replicate("X",len(g_s:varget()) )
g_s:varput("QWERTY")
g_s:postblock:={|g| 1==alert("Ezt akartad: "+;
alltrim(g:varget())+"?",{"Igen","Nem"})}
//numeric demo
g_n:picture:="@R 99.9999"
g_n:varput(3.1415)
//date demo
g_d:varput(date())
//logical demo
g_l:picture:="L"
g_l:varput(.t.)
aeval(getlist,{|g|g:display()})
return NIL
static function store(getlist)
local result:=.f.
if( 1==alert("Minden rendben?",{"Igen","Nem"}) )
? "String :", g_s:varget()
? "Numeric :", g_n:varget()
? "Date :", g_d:varget()
? "Logical :", g_l:varget()
result:=.t.
end
return result
A demo() függvényt a mask.exe programgenerátorral csináltuk. A mask programmal lerajzoljuk a képernyőt, és a rajzot elmentjük (demo.msk). A rajzból a make rendszer előállítja a demo.say programot, amit a program inkludál (kivételesen nem a forrásprogam elején, azért, hogy a main legyen az első függvény). A mask által generált függvényeket mindig három kódblokk paraméterrel hívjuk meg.
A példában szereplő demo.msk-t úgy lehet módosítani, hogy elindítjuk a mask programot, majd F3-at ütve választunk a felsorolt msk filékből. A program F1-re ad helpet.
A mask kétféle kód generálására képes. Alapállapotban egy magasabb szintű interfészre készít kódot, ami karakteres és GUI-s környezetben egyformán működik. A generált kód működésének megértéséhez azonban hasznos a csak karakteres képernyővel működő hagyományos kód tanulmányozása. Ezt így lehet előállítani: mask demo -gTRAD.
1ComFirm BT.
2 Ez alól csak a kibővített objektumrendszer demója kivétel, ami nem vihető vissza Clipperre.
3 Azóta az XSTRU (datidx) és BSTRU (Btrieve) mintájára elkészült a DSTRU (dbf) struktúra editor.