GUI Browser(client) - Server (03-2008/12-2009/12-2010)
Aanleiding .
In de huidige tijd neemt een gebruiker geen genoegen meer met een simple commandbox .
Het moet muis gestuurd zijn en mogelijk in kleur en voorzien zijn van duidelijke iconen .
Daar bijna ieder window-systeem weer zijn eigen , volstrekt incompatibel , aansturing kent
betekend dit dat je een GUI voor bijna ieder verschillend systeem moet maken .
Zo is MSWindows weer anders dan GTK , welk weer verschilt van QT .
Er zijn , mij , een paar systemen bekent die OS en Window onafhangkelijk zijn .
Een is JAPI . Een windowsysteem dat voor veel toepassingen een wrapper kent naar Java .
De andere is GraphAppl wel een window systeem is gebouwd op MSWindows en XWindows .
Een andere benadering is gebruikt te maken van een Browser welke als besturing het HTML protocol
gebruiken . Dit is geheel OS onafhankelijk . Ofschoon er wel verschil zit in de uitvoering van de
browsers . Mozilla (en wat daar vanaf stamt) houd zich goed aan de W2C afspraken .
IE houd er zo af en toe wel een zeer speciaal idee opna hoe de W2C afspraken worden uitgevoerd .
Safira (Mac) heb ik geen ervaring mee .
Een mooie desktop-toepassing van zo'n Browser interface is die zoals gebruikt door eyeOS .
Mijn idee is dat je een GUI zou moeten kunnen maken door een Browser als uitvoer tegebruiken .
Dit betekend dat je programma een server met HTTP protocol moet gaan bevatten .
Dan kan je programma de uitvoer maken in HTML/CSS en via de server sturen naar de Browser .
De Browser stuurd dan de formulieren terug naar de server . Deze herformateerd deze zodanig
dat je programma ze kan gebruiken als invoer .
!--------------------------------!
!---------! ! !--------! !------------! !
! Browser ! ... < ---- > ... ! ! server ! <--> ! Toepassing ! !
!---------! ! !--------! !------------! !
!--------------------------------!
je in/uit voer je applicatie
Wensen :
1) Eigenlijk wil ik dat we meerdere zelfde applicatie kunnen starten .
2) Tevens wil ik dat als de gebruiker het window waarin de applicatie getoond wordt sluit
dit resulteerd in het gedrag dat de applicatie zich ook afsluit .
3) Eigenlijk wil ik dat het beeindigen van de applicatie ook het window in de browser zich sluit .
--------------------------------------------------------------------------------------------------
Probleem 1) is bedacht dat de gestarte applicatie zoekt naar een vrije poort .
Voor XP vond ik dat 4990-5000 vrij zijn . Waardat gedeffineerd wordt , geen idee .
Want vanaf 5001 worden de aanvragen door XP geweigerd .
Voorbeeld :
data serveradres/'127.0.0.1:'/
serverpoort = 4990
server = 0
c zoek een niet gebruikte poort
do while((server.ge.0).and.(serverpoort.le.5001))
serverpoort=serverpoort+1
write(serveradres(11:14),'(I4,$)')serverpoort
write(6,*)serveradres
c mode = client
mode='c'
server=netwerk_open(serveradres,mode)
c indien gevonden geeft dit fouten aan de server site
c daar er geen HTTP is . Dit wordt gezien als een EOF
if(server.ge.0)call netwerk_close(server)
end do
c mode = server
mode='s'
server=netwerk_open(serveradres,mode)
if(server.lt.0)then
write(6,*)'error cann''t open server'
stop
end if
c start browser
call system('defaultbrowser http://'//serveradres)
c return to application
Dus de browser wordt gestart zodra de server is gestart .
Dit houdt wel in dat de browser als backgroundjob c.q. parallel process moet kunnen worden gestart .
In Linux start je een backgroundjob door achter de commandline een & te zetten .
In Windows start je een backgroundjob door de commandline te beginnen met de opdracht 'start' .
In XP windows geld voor defaultbrowser :
@echo off
start c:\"Program Files\Mozilla Firefox"\firefox.exe %1
In Linux :
#!/bin/sh
firefox @1 &
------------------------------------------------------------------------------------------------------
Het probleem voor 2) is wat ingewikkelder .
Er wordt heel wat over geschreven als je wat zoekt op Internet .
Maar geen van al de aangedragen oplossing werkt echt bevredigend .
Het basis probleem is dat een Browser geen informatie terug geeft als een gebruiker de browser sluit .
Opzich logisch als je bedenkt dat de gebruiker feitelijk de browser stopt , hoe kan deze dan wat doen .
Omdit niet alte ingewikkeld te krijgen heb ik teruggegrepen op een heel oud principe n.l. clock-tick .
De server staat de heletijd te kijken of er een aanvraag komt van de Browser .
(Hiertoe moet de onderliggende ACCEPT() functie niet geblokeerd is !
Je kunt de socket openen met een UNBLOCK of de SELECT() functie gebruiken met een time-out .
Het openen van een UNBLOCK socket geeft 100% cpu bezetting in XP .
Daarom gebruik ik de select() met een time-out van 1 sec .
)
Laat de browser , als er een tijd niets gebeurd zelf , een (klok)tick naar de server sturen .
Dit geeft de server de informatie dat de gebruiker het window nog aktief heeft (127.0.0.1:4992/tick) .
De server kan opgrond van deze aanvraag de time-out weer op 0 zetten en een antwoord sturen .
Voor dit antwoord heeft het HTTP protocol een dummy antwoord wat de aanwezige pagina niet wijzigd .
Hiervoor zorg de response header HTTP/1.1 204 OKE .
Komt er gedurende enige tijd geen tick dan start de server een process om de applicatie te sluiten .
Voorbeeld server accept :
client=0
timeout=time()+75
do while(client.eq.0)
c mode = accept
client=netwerk_open(server,mode)
if (timeout.le.time())then
c time-out error : close server and dump program
call netwerk_close(server)
call netwerk_end()
write(6,*)'Progam abort due Time-Out at '
stop
end if
end do
De server heeft een time-out van 60+15 sec dit daar de browser een tick geeft om de 60 sec .
Ieder antwoord wat gestuurd wordt naar de browser met HTTP1.1/ 200 OKE moet nu een script meekrijgen
dat om de 60 sec een tick stuurd als er niet anders gebeurd is .
Dit wordt gedaan door in het antwoord onderstaand java-script meetesturen .
<script Language=JavaScript>
function tik() {setTimeout("tik()",60000);window.location.pathname="/tick"}
setTimeout("tik()",60000)
</script>
-----------------------------------------------------------------------------------------------------
Probleem 3) is niet in alle browser optelossen .
In IE kun je een window.opener="onzin";window.close() het venster sluiten .
In de Mozilla versie is dit na versie 2.x en hoger niet meer mogelijk .
Het argument van de Mozilla groep is dat alleen de gebruiker een venster kan sluiten .
Behalve als een venster is gemaakt door een script dan kan deze ook door een script worden gesloten .
-----------------------------------------------------------------------------------------------------
Subroutine fserver.f
Allereerst moet je de server initieeren d.m.v. call netserver .
Dit opend een Webbrowser met http://ipadres:poort .
De kommunikatie gaat via een commonblok /serverdata/cmd,cmdbuf
cmd geeft de opdracht weer en cmdbuf de data .
Je hoofd programma is een lus welke begin met :
call command_from_client
Op grond van cmd en/of cmdbuf moet je nu een antwoord geven .
Dit kan zijn :
1 regel text call responsetext(text-line)
1 bestand call responseclient(file-name)
of een intern gemaakt html antwoord
call write_open_client(aanhef)
gevolgd door de teschrijven text via het common cmdbuf
call write_client_buf
of d.m.v. een text regel
call write_client(text-line)
afsluiten met (wat ook een </body></html> geeft)
call write_close_client
De subroutne fserver.f heeft tevens nodig het programma netlib.c deze bevat de interface naar de
netwerkroutines voor Linux en MSWindows en de wrappers naar Fortran .
-------------------------------------------------------------------------------------------------------
Pagina opbouw
Ofschoon er vele mogelijkheden zijn heb ik gekozen om de pagina optebouwen m.b.v.
construktie .
Als eerste wordt een routine new_page(titel,style-file,achtergrond-kleur) aangroepen .
Deze zorgt voor de de opbouw van een HTML aanhef met style-file als style-sheet .
Nu wordt een table gedefinieerd .
De routine programtitle() vult de eerste rij van de tabel met een aanhef van iets als :
naam van het programma , versie nummer , datum , tijd .
De routine menutab(menu) vult de tweede rij met een menu selectie .
open , bewerk , info .
De hierop volgende rijen kunnen gevuld worden met datgene wat het antwoord is op een menu-selectie .
Afgesloten wordt het geheel met een write_close_client(client) welke tevens een </body></html> zend .
-------------------------------------------------------------------------------------------------------
Procedure's pagina opbouw
add_table(class,align,width,border)
add_row(class,align,width,colspan)
add_col(class,align,width,colspan)
add_text(class,align,text-string)
add_form(form-name)
add_text_input(class,accesskey,size,name,value)
add_href(class,accesskey,href,text)
add_radio(class,accesskey,name,value,default,text)
add_check(class,accesskey,name,value,default,text)
add_reset(class,accesskey,name,value)
add_submit(class,accesskey,name,value)
een form moet je zelf afsluiten met </form>
een tabel moet je zelf afsluiten met </table>
De class is het nummer uit de CSS file die de afbeelding verfraaid .
accesskey mag leeg zijn maar anders moet het een character bevatten .
default is 1 als dit de vooringestelde selctie is .
width is in procenten .
Voorbeeld :
subroutine zoekOrg()
implicit integer *2 (a-z)
c parameter voor align
parameter (left=1,center=2,right=3)
c parameter voor valign
parameter (top=10,middle=20,bottom=30)
call add_form('zo')
call add_table(104,center,70,0)
c defineer kopregel
c lege regel
call add_row(0,0,0,4,0)
call add_text(0,0,' ')
c title
call add_row(109,0,0,4,0)
call add_text(0,center,"Een organisatie zoeken")
call add_row(0,0,0,4,0)
call add_text(0,0," ")
c linker helft
call add_row(107,0,25,1,0)
call add_col(107,0,25,1,0)
call add_text(0,0,'Zoek naar')
c rechter helft
call add_col(107,0,0,2,0)
call add_input_text(1000,ichar('z'),20,100,'')
c commentaar
call add_row(108,0,0,4,0)
call add_text(0,0,'Geef het aansluitnummer of (deel van) de naam')
c opdrachten
call add_row(0,0,0,4,0)
call d_oke_reset_niets()
c uitleg sneltoetsen
call add_row(0,0,0,4,0)
call d_sneltoetsen
call write_client('</table>')
call write_client('</form>')
end
Dit geeft als beeld :
Judo Tournooi Programma | versie Nov 30 2010 | Vandaag is : | dinsdag 30 november 2010 14:06:28 | |
|
Waardit over gaat is weer heel iets anders , het gaat om het voorbeeld c.q. idee er achter .
En niet of je de gekozen kleuren en of lettertypes mooi vind .
Als je nu een "Oke" klik dan stuur de browser een vraag met http://server/zo?100='ingevuld' naar de server .
Dit omdat de form 'zo' heet en de naam van het inputveld 100 is .
-------------------------------------------------------------------------------------------------------
Toegangstijd
Op de voorgestelde manier wordt iedere klik op het menu of submit beantwoord door een gehele nieuwe pagina
over te sturen . Dit is redelijk snel zolang de script , css e.d. inline in de HTML text staan .
Bij het laden van externe scripts en css neemt de toegangstijd (turn around time) beduidend af .
Een mogelijke oplossing is gebruik te maken van dynamische pagina loading (XMLHttpRequest) ook wel AJAX gemeomd .
Hiervoor gebruikt men dit javascript .
function createRequestObject() {
var ro;
var browser = navigator.appName;
if(browser == "Microsoft Internet Explorer"){
ro = new ActiveXObject("Microsoft.XMLHTTP");
}else{
ro = new XMLHttpRequest();
/* only firefox 3 to get ride of the syntax error */
if (ro.overrideMimeType) ro.overrideMimeType("text/plain");
}
return ro;
}
var http = createRequestObject();
function sndReq(action) {
var noHttp = new Array();
if (action.indexOf('_') != -1) {
noHttp = action.split('_');
/* standaard HTTP call */
location.href=location.protocol+'//'+location.host+'/'+noHttp[1];
} else {
/* standaard AJAX call */
http.open('get', action );
http.onreadystatechange = handleResponse;
http.send(null);
}
}
function handleResponse() {
if(http.readyState == 4){
var response = http.responseText;
var update = new Array();
if(response.indexOf('|') != -1) {
update = response.split('|');
document.getElementById(update[0]).innerHTML = update[1];
}
}
}
Alle aanroepen zoals href en submit gaan via sndReq(action) .
De action is de opdracht aan de server b.v. no_zo?100='ingevuld' of zo?100='ingevuld' .
De eerste geeft aan de server een HTTP oproep die beantwoord moet worden met een HTTP header gevolgt door text(data) .
De tweede geeft aan de server een AJAX oproep die beantwoord moet worden met text string ID(naam)|text(data) .
Het antwoord van de server schrijft in dat deel van de pagina dat omvat is door <div ID=foo></div> .
Het wijzigd de inhoud maar laat de rest van de pagina ongemoeit , dus geen exstra laad tijd .
vb :
<div ID=FOO>Hier staat mijn onzinnige text</div>
.
.
<a href=javascript:sndReq(newtext)>Haal nieuwe text op . </a>
Nu stuurd de server terug : FOO|Dit is <b>nieuwe</b> text . ( het | is een scheidings teken , mag ook iets anders zijn .)
Nu wordt de text "Hier staat mijn onzinnige text" vervangen door "Dit is nieuwe text" .
In praktijk :Hier staat mijn onzinnige text
Haal nieuwe text op .
(wil je de oude text weer zien doe een reload van de browser)
-------------------------------------------------------------------------------------------------------
Submit
Als je in een normale (HTTP oproep) een submit doet wordt het formulier keurig vertaald in b.v. http//:server/zo?100='ingevuld'
Maar als je gebruikt maakt van de AJAX methode moet je deze vertaalslag zelf doen . Er is mij geen interne procedure bekent die dit doet .
// Serializes a form
// Will return a string which is a vald HTTP POST body for the given form
// If no form is given, it will search for the _FIRST_ form on the page
function serializeForm(level) {
// Return value
var retVal = '';
// Getting ALL elements inside of form element
var els = document.forms[level].getElementsByTagName('*');
// Looping through all elements inside of form and checking to see if they're "form elements"
for( var idx = 0; idx < els.length; idx++ ) {
var el = els[idx];
// According to the HTTP/HTML specs we shouldn't serialize disabled controls
// Notice also that according to the HTTP/HTML standards we should also serialize the
// name/value pair meaning that the name attribute are being used as the ID of the control
// Though for Ra controls the name attribute will have the same value as the ID attribute
if( !el.disabled && el.name && el.name.length > 0 ) {
switch(el.tagName.toLowerCase()) {
case 'input':
switch( el.type ) {
// Note we SKIP Buttons and Submits since there are no reasons as to why we
// should submit those anyway
case 'checkbox':
case 'radio':
if( el.checked ) {
retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
}
break;
case 'hidden':
case 'password':
case 'text':
retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
break;
}
break;
case 'select':
case 'textarea':
retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
break;
}
}
}
if( retVal.length > 0 ) { retVal='?'+retVal.substring(1); }
return retVal;
}
De form moet nu luiden : <form method=get action=javascript:sndReg(form-naam+serielForm(0))> .
form-naam is de (action)naam die je aan de form geeft , de (0) slaat er op dat je maar 1 form hebt die heeft dan index 0 .
-------------------------------------------------------------------------------------------------------
Slot opmerking
Denk niet dat ik zo goed ben in Java , het meeste vindt je door rond te snuffellen op het Internet .
Het menu werkt niet in IE .
FileBrowsing geeft in IE het komplete pad doch in Firefox(3 en hoger) alleen de file naam .
-------------------------------------------------------------------------------------------------------
Verbeteringen altijd welkom eMail mij dan of Skype mij .