Programmeren in REXX/Een beetje OORexx
OORexx biedt toch een paar niet te versmaden extra mogelijkheden zonder dat de techniek van object georiënteerd programmeren moet aangeleerd worden.
In dit hoofdstuk behandelen we
- het maken van een extern functiepakket
- het gebruik van eenvoudige (en enigszins beperkte) grafische schermpjes.
Verdere informatie kan gevonden worden in het boek OODialog Reference dat na installatie als bestand oodialog.pdf terug te vinden is in map c:\Program Files\ooRexx\doc\.
Extern functiepakket maken
bewerkenMet OORexx is het wel mogelijk een extern functiepakket te maken waarbij het uitvoeren van een functie niet minder performant is dan indien ze intern zou geschreven zijn. Er kan wel een kleine extra tijd nodig zijn om het pakket te laden. Het grootste voordeel is echter dat we de functies niet steeds moeten toevoegen in elke programma dat er gebruik van wil maken.
Een functiepakket "MijnRoutines.rex" ziet er dan bijvoorbeeld als volgt uit:
::ROUTINE CHOP PUBLIC /*+---------------------------------------------------------------+*/ /*| CHOP(string,lengte<,string2>) |*/ /*| kapt een string af aan bepaalde lengte |*/ /*| "string2" wordt er desgevallend achtergeplakt |*/ /*+---------------------------------------------------------------+*/ call trace 'Off' if length(arg(1))<=arg(2) then return arg(1) select when arg()=2 then return left(arg(1),arg(2)) when arg()=3 then return left(arg(1),arg(2)-length(arg(3)))||arg(3) end ::ROUTINE PAD PUBLIC /*+---------------------------------------------------------------+*/ /*| PAD(string,lengte<,pad<,L|R>>) |*/ /*| maakt een string van opgegeven lengte |*/ /*+---------------------------------------------------------------+*/ call trace 'Off' if length(arg(1))>=arg(2) then return arg(1) select when arg()=2 then return left(arg(1),arg(2)) when arg()=3 then return left(arg(1),arg(2),arg(3)) when arg(4)='L' then return left(arg(1),arg(2),arg(3)) when arg(4)='R' then return right(arg(1),arg(2),arg(3)) end
Het oproepend programma kan er dan als volgt gebruik van maken:
say chop("Dit is een lange tekst",10,'...') «Dit is een...» say pad("Een tekst",18,'-') «Een tekst---------» say pad("Een tekst",18,'-','R') «---------Een tekst»
Al lijken deze functies op left en right, ze verschillen toch in die zin dat chop de string nooit zal verlengen terwijl pad de string nooit zal inkorten.
::REQUIRES "D:\RexxProgrammas\MijnRoutines.rex"
De zogenaamde directives zijn hier van belang. Het ::REQUIRES directive zal aan REXX duidelijk maken dat het desbetreffend bestand moet geladen worden. Daarin vindt hij dan ::ROUTINE directives die een naam geven aan een functie, of beter methode om de OO terminologie te gebruiken. Deze functies worden als publiek gedefiniëerd.
Grafische schermpjes
bewerkenDe nu volgende functies kunnen maken dat het programma geen opdrachtprompt meer nodig heeft. Of beter gezegd, de opdrachtprompt kan geminimaliseerd blijven. Als voor het programma een snelkoppeling wordt gemaakt kan het dus gaan lijken op een klassiek Windows programma.
Maak daarom een kopie van de opdrachtprompt. Open de eigenschappen en vul als "doel" voor de snelkoppeling het volgende in:
"C:\Program Files\ooRexx\rexx.exe" "D:\RexxProgrammas\MijnProgramma.rex"
en laat het "uitvoeren" in een geminimaliseerd venster. Op het tabblad "Algemeen" kan je een sprekende titel geven, en in de map "C:\Program Files\OORexx\" kan het icoontje OORexx.ico gekozen worden om niet dat van de opdrachtprompt te blijven behouden.
Indien OORexx niet ten volle wordt benut blijven de mogelijkheden echter beperkt zoals we zullen zien.
Functies van het OODialog pakket
bewerkenOORexx levert een aantal extra functies als toevoeging voor de standaard functies die we vroeger aanleerden. Deze functies zitten in OODialog.dll en moeten geactiveerd worden met:
call RxFuncAdd "InstMMFuncs","OODialog","InstMMFuncs" call "InstMMFuncs"
Dit is de lijst van de functies uit dit pakket:
InfoMessage
bewerkenInfoMessage(bericht)
Een pop-up schermpje verschijnt en toont de opgegeven tekst. Het schermpje heeft een OK-knop. De titel is "Information" en er is een icoontje dat duidt op een bericht.
call InfoMessage("Welkom in de wereld van REXX")
ErrorMessage
bewerkenZoals een InfoMessage functie, maar met als titel "Error" en een icoontje dat op een fout wijst.
YesNoMessage
bewerkenZoals een InfoMessage, maar nu zijn er twee knoppen, "Yes" en "No". We kunnen desgevallend ook opgeven welke knop standaard actief is (de focus heeft).
YesNoMessage(bericht[,knop])
Het antwoord is 1 als "Yes" is gekozen, anders is het 0. Bijvoorbeeld:
if \YesNoMessage("Wil je hiermee verder gaan ?","No") then exit
GetScreenSize
bewerkenGetScreenSize()
Retourneert een string waarin 4 waarden staan:
- breedte van de dialoog in dialoogeenheden
- hoogte van de dialoog in dialoogeenheden
- breedte van het fysisch scherm in pixels
- hoogte van het fysisch scherm in pixels
PlaySoundFile
bewerkenPlaySoundFile(wav-bestand[,asynchroon])
Speelt de muziek (geluid) van het wav-bestand (enkel deze vorm is toegelaten, dus geen MP3). Als de parameter asynchroon als "Yes" is opgegeven speelt de muziek verder terwijl het programma loopt.
Willen we continu de muziek herhalen, dan gebruiken we de functie PlaySoundFileInLoop. Om die muziek dan weer te stoppen gebruiken we de functie StopSoundFile().
GetFileNameWindow
bewerkenGetFileNameWindow(bestand,handle,filter,open_of_save,titel,standaardextensie,multiselectie,scheiding)
Presenteert een verkenners-venstertje waarop één of meer bestanden kunnen worden gekozen. Het resultaat komt in een string terug.
Alle parameters zijn optioneel:
- bestand: een vooraf geselecteerde bestandsnaam of map. Indien geen bestand is opgegeven, bepaalt het systeem in welke map men begint, meestal is dit de map die de laatste keer is gebruikt, of indien Windows er geen heeft onthouden vertrekt men in de map "Mijn Documenten". Jokers kunnen gebruikt worden in de bestandsnaam.
- handle: de handle van het oproepend scherm. Dit hebben we normaal gezien nergens voor nodig in een eenvoudig REXX programma.
- filter: een masker. Dit is opgebouwd uit een reeks paren die van elkaar gescheiden zijn door '00'x. Elk van de paren bestaat uit een beschrijvende tekst en een masker, weer van elkaar gescheiden door een '00'x. Standaard is de filter de volgende:
"Text files (*.txt)"'0'x"*.TXT"'0'x"All files (*.*)"'0'x"*.*"
- open_of_save: kan de waarden 1 of 0 aannemen. De waarde 1 betekent "Open" en is de standaardwaarde. De waarde 0 betekent "Save as".
- titel: titel van het schermpje
- standaardextensie: de extensie die aan de bestandsnaam moet worden toegevoegd indien er geen extensie is opgegeven.
- multiselectie: indien 1 wordt opgegeven kunnen meerdere bestanden tegelijk worden geselecteerd.
- scheiding: is het teken dat tussen de bestandsnamen in het antwoord moet worden gezet. Dit is belangrijk als men verwacht dat er spaties in de namen kunnen staan.
Voorbeeld:
bestanden=FileNameDialog("D:\RexxProgrammas\*.rex",,"REXX programma's (*.rex)"'0'x"*.REX",1,,,1,"#")
Later bespreken we een uitgebreider voorbeeld.
SleepMS
bewerkenSleepMS(milliseconden)
Deze functie stopt de uitvoering gedurende een aantal milliseconden.
WinTimer
bewerkenWinTimer(bevel,milliseconden)
Het bevel kan "Start","Stop" of "Wait" zijn. De functie activeert, stopt of wacht voor een Windows timer.
timer=WinTimer("START",300) /* 0,3 seconden */ call WinTimer "WAIT,timer /* wachten op tijdsignaal... */ stop = WinTimer("STOP",timer) /* vroegtijdig stoppen */
Standaard heeft REXX slechts één chronometer. Hiermee kunnen we meer chronometers starten, en wachten op een tijdssein.
Publieke OORexx routines
bewerkenDe functies die we net zagen bestaan ook als zogenaamde publieke OORexx routines. Om deze functies te kunnen gebruiken moet volgende directive helemaal achteraan het REXX programma worden toegevoegd om de class-library te benaderen:
::REQUIRES "OODPLAIN.CLS"
U zal merken dat de beschrijving van de functies niet zelden gelijklopend is aan diegene die we hierboven hebben gezien, doch in sommige gevallen zal het mogelijk zijn de standaardwaarden of titels van de schermpjes zelf te kiezen, zodat ze niet steeds in het Engels moeten blijven. Daarom zullen sommige parameters nu anders worden geschreven.
Daarbovenop zijn er ook nog een reeks nieuwe, zeer nuttige functies (we moeten eigenlijk spreken van methodes). Om de verwarring met de vorige functies te vermijden adviseren we enkel de hier beschreven publieke methoden te gebruiken.
Dit is de lijst van publieke routines:
PLAY - geluid afspelen
bewerkenPlay(wav-bestand[,optie])
Speelt een geluidsbestand af (enkel WAV is voorzien). Met optie yes wordt het geluid asynchroon afgespeeld - het programma gaat verder. De optie loop laat toe het geluid asynchroon en continu te herhalen.
INFODIALOG - een informatieschermpje
bewerkenInfoDialog(tekst)
Een schermpje met de opgegeven tekst verschijnt als pop-up. Het heeft een OK-knop.
ERRORDIALOG - scherm met foutbericht
bewerkenErrorDialog(foutbericht)
Het foutbericht wordt in een pop-up schermpje getoond. Er is een OK-knop en een icoontje dat duidt op een fout.
ASKDIALOG - een eenvoudige vraag stellen
bewerkenAskDialog(vraag[,standaardknop])
De vraag wordt in een pop-up schermpje getoond. Er is een YES-knop en een NO-knop. Men kan d.m.v. de opties "Yes" of "No" aanduiden welke knop standaard geselecteerd moet zijn, zodat op de Enter-toets drukken deze keuze onmiddellijk bevestigd. Het antwoord van de functie is 1 als YES-knop is ingedrukt, anders is het 0.
Voorbeeld:
if \AskDialog("Gaan we hier toch mee verder ?","Yes") then exit
FileNameDialog - selecteren van een bestand
bewerkenFileNameDialog([selbestand][,ouder][,masker][,laden_of_bewaren][,titel][,standaardextensie][,multi][,scheiding])
Deze functie opent een scherm waarop één of meer bestanden kunnen worden geselecteerd. De namen van deze bestanden vormen het antwoord van de functie.
De parameters zijn:
- selbestand - een eventueel voorgeselecteerd bestand of map. Als deze map niet is opgegeven zal het besturingssysteem bepalen in welke map men begint te zoeken (meestal de map die een vorige maal was geselecteerd).
Als wel een pad is opgegeven, maar het bestaat niet, dan is het alsof er geen pad is opgegeven. Jokers in bestandsnamen zijn toegelaten.
- ouder - een handle (identificatie) van een bovenliggend scherm. Wij hebben dit niet nodig.
- masker - paren filters die toelaten zich te beperken tot bepaalde soorten bestanden. Indien hier niets is opgegeven, is het standaardfilter gelijk aan «"Text Files (*.TXT)"'00'x"*.TXT"'00'x"All Files (*.*)"'00'x"*.*"», waarbij de gebruiker dus kan kiezen tussen tekstbestanden van het type *.txt, of alle bestanden (*.*). De scheiding tussen de verschillende paren moet met een '00'x karakter gebeuren.
- laden_of_bewaren - "LOAD" bepaalt dat een "File-Open" dialoog moet worden getoond (dit is de standaardwaarde). "SAVE" bepaalt dat een "File-Save" dialoog moet worden getoond.
- titel - vervangt de anders standaardtitels "Open a File" of "Save File as".
- standaardextensie - de extensie die aan het bestand moet worden toegevoegd als er geen is opgegeven. De standaardwaarde is "TXT".
- multi - meerdere bestanden kunnen worden geselecteerd mits "MULTI" op te geven. Het antwoord van de functie wordt dan «pad file1 file2 file3...»
- scheiding - een eventueel scheidingskarakter tussen de namen van de bestanden. Dit is nodig als men spaties in de namen verwacht. Met bijvoorbeeld "#" als scheiding krijgt het antwoord dan de vorm «pad#file1#file2 file3...»
Voorbeeld:
bestanden=FileNameDialog("D:\RexxProgrammas\*.rex",,"REXX programma's (*.rex)"'0'x"*.REX","LOAD",,,"MULTI","#") if bestanden<>"" then do if pos("#",bestanten)=0 then do bestand.0=1 ; bestand.1=bestanden end; else do parse var bestanden pad "#" bestanden do i=1 while bestanden<>'' parse var bestanden naam '#' bestanden bestand.i=pad"\"naam end bestand.0=i-1 end end; else bestand.0=0
InputBox - een waarde opvragen
bewerkenInputBox(vraag,titel[,standaardwaarde][,breedte])
Toont een schermpje met één inputveld. Het schermpje heeft de opgegeven titel en de vraag staat net boven het inputveld. Er zijn 2 knoppen: OK en Cancel. De functie geeft de tekst die de gebruiker invult terug, of een nullstring als Cancel-knop werd gebruikt. De breedte van het inputveld kan worden opgegeven, maar deze is in dialoogeenheden, niet in karakters. Het is daarom nuttig de breedte van de gebruikte font te kennen indien men het inputveld een bepaalde lengte wil geven.
naam=InputBox("Hoe heet je ?","Een antwoord graag") if naam<>"" then say "Je naam is" naam
PasswordBox - een paswoord vragen
bewerkenPasswordBox(vraag,titel[,standaardwaarde][,breedte])
Deze functie gebruikt de onderliggende InputBox methode om een paswoord te vragen. Het paswoord wordt weggestopt achter sterretjes.
IntegerBox - een geheel getal opvragen
bewerkenIntegerBox(vraag,titel[,standaardwaarde][,breedte])
Deze functie is ook een afgeleide van de InputBox methode, en aanvaardt enkel een positief of negatief geheel getal als input.
MultiInputBox - meerdere waarden opvragen
bewerkenMultiInputBox(vraag,titel,labels.,data.[,breedte])
Deze methode laat toe meer dan één waarde op te vragen. De labels van de velden worden in een stem gezet. Het eerste element is de label voor het eerste veld, enzovoort. De resultaten komen in een data. stem. Hiervan moeten de elementen beginnen aan nummer 101. De huidige waarde van de resultaatvelden worden op het schermpje getoond.
Dit is een voorbeeld:
lab.1='Type fotobestanden' ; lab.2='Te analyseren disken' lab.3='Resultaatsbestand' addr.101="JPG" ; addr.102="C: D:" ; addr.103="D:\Fotolijst.txt" dlg = .MultiInputBox~new("Geef nodige parameters",, "Parameters", lab., addr.,250) if dlg~execute = 1 then do fototype=dlg~typefotobestanden drives=dlg~teanalyserendisken ; outputfile=dlg~resultaatsbestand end; else call exit 28,'Programma onderbroken'
Merk op dat we nu de methode moeten initialiseren en daarna uitvoeren. Dit is OORexx syntaxis. De antwoorden van de gebruiker komen in velden die de genoemd zijn naar de labels, maar zonder de spaties. Ze zijn niet hoofdlettergevoelig, maar moeten wel juist geschreven zijn.
Er is een klein schoonheidsfoutje op dit schermpje. De tekst bij het tweede veld is gesplitst en gedeeltelijk onleesbaar. De oplossing is om de allereerste label een breedte te geven die groot genoeg is voor alle volgende labels. We voegen daarom voldoende spaties toe bij lab.1 en bekomen:
Een alternatieve manier om bovenstaande te schrijven is de volgende:
velden="fototype drives outputfile" res=MultiInputBox("Geef de nodige parameters",, "Parameters",, .array~of("Type fotobestanden ","Te analyseren disken","Resultaatsbestand"),, .array~of("jpg","D E","CSV"),250) if res \= .Nil then do input over res parse var velden veld velden interpret veld"=input" end
In dit geval werken we werkelijk met arrays. Ook het do bevel heeft een nieuwe vorm: "loop over alle elementen van de array". Dit zijn mogelijkheden die enkel door OORexx worden geboden. Indien uw programma op andere computers moet kunnen uitgevoerd worden is deze versie van REXX daar dus ook nodig.
ListChoice - kiezen uit een lijst
bewerkenListChoice(vraag,titel,.array~of(keuze1,keuze2,...[,breedte][,hoogte][,standaardkeuze])
Deze functie toont een selectieschermpje met de verschillende keuze-mogelijkheden. De standaardkeuze is daarbij al geselecteerd, als er een is opgegeven. Nu kunnen zowel breedte als hoogte van het selectieschermpje bepaald worden.
say ListChoice("Wanneer kan je naar de tandarts komen ?",, "Afspraakdag",, .array~off("Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag"),, 100,100,"Woensdag")
MultiListChoice - meerdere keuzes uit een lijst
bewerkenMultiListChoice(vraag,titel,.array~of(keuze1,keuze2,...[,breedte][,hoogte][,standaardkeuzes])
Dit is een variatie op de ListChoice methode. De standaardkeuzes zijn een lijst van getallen die overeenkomen met de posities in de lijst van mogelijke keuzes.
dagen=MultiListChoice("Welke dagen kan je komen ?",, "Afspraakdag",, .array~of("Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag"),, 100,100,"2 4") if dagen <> .Nil then do dag over dagen say "Mogelijke afspraakdag=" dag end
CheckList - een keuze in een lijst afvlaggen
bewerkenCheckList(vraag,titel,.array~of(keuze1,keuze2,...)[,breedte][,max_in_kolom])
Deze functie presenteert een lijst waarin één of meer elementen kunnen worden afgevlagd. De parameter max_in_kolom bepaalt hoeveel keuzes onder elkaar moeten komen alvorens over te gaan tot een nieuwe kolom.
Voorbeeld:
dagen=.array~of("Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag") afspraken=CheckList("Welke dagen kan je komen ?","Afspraken",dagen) if afspraken <> .Nil then do i = 1 to afspraken~items if afspraken[i] then say "Je kan op" dagen[i] "komen" end
Nogmaals, merk op dat we hier werkelijk met arrays werken, en niet met stems. Dit kan enkel met OORexx.
SingleSelection - unieke keuze uit een lijst
bewerkenSingleSelection(tekst,titel,array~of(keuzes),[breedte][,max_in_kolom])
Nu presenteert de functie een lijst met radio-buttons waaruit de gebruiker er slechts één kan kiezen.
maanden=.array~of("Jan","Feb","Mar","Apr","Mei","Jun",, "Jul","Aug","Sep","Oct","Nov","Dec") maand=SingleSelection("Kies je maand","Geboren in",maanden,12,,6) say "Je bent dus geboren in de maand" maanden[maand]