Door de bestanden in een map lopen (files in directory)

Hier een stukje code waar door een map (directory) wordt gelopen en alle bestanden uit die directory worden weergegeven.

  • De directory komt hier uit een object ($hCardGame.sLocation), maar het is een gewone string met het volledige pad in, je kan het als test vervangen door de home directory zoals in de commentaar van de code wordt gesuggereerd.
  • Set1 is de subdirectory die doorlopen wordt.
  • De selectie van de files is hier een aantal grafische bestanden: png, jpg, jpeg; die worden eerst in een array gestoken.
  • Dan wordt de array uitgelezen met een FOR EACH / NEXT lus
  • Binnen die lus gebeurt een test, hier om te kijken of de lijst met namen niet te lang wordt
  • De uitvoer gaat naar een listbox, hier lsbxLog genaamd.

Dus behalve onderstaande code heb je daarbuiten de listbox en het object met eigenschap sLocation nodig (of vervang het door een parameter in de procedure).

PUBLIC SUB listImages()
  
  DIM sDirectory AS String
  DIM aFiles AS String[]
  DIM sFileName AS String
  DIM sList AS String 
  
'  Directory = System.User.Home
  sDirectory = $hCardGame.sLocation &/ "Set1"
  lsbxLog.Add(sDirectory, 0)
  aFiles = Dir(sDirectory, "*.png")
  aFiles.Insert(Dir(sDirectory, "*.jpg"))
  aFiles.Insert(Dir(sDirectory, "*.jpeg"))
  
  sList = ""
  FOR EACH sFileName IN aFiles
    sList &= ";" & sFileName
    IF Len(sList) > 80
      lsbxLog.Add(sList, 0) ' buffer full, show current part
      sList = ""
    ENDIF 
  NEXT
  lsbxLog.Add(sList, 0) ' final part
  
END

Versie van Gambas vanuit programma opvragen

De versie van je eigen programma kan je in een tekstvariabele steken met:

sMijnVersie = Application.version

Maar als je daarnaast de versie van Gambas zelf nodig hebt?
Binnen Gambas is er geen commando om dat op te vragen.

In de IDE is die natuurlijk zichtbaar in het menu: ? , About Gambas (bv: 2.21).

Maar als je de gambas versie wil weergeven vanuit je draaiende gambas programma?

Meestal zal je dan de runtime bedoelen; die heet gbx (van gambas execute). In gambas 2 is dat gbx2, in gambas 3 vermoedelijk gbx3.

In je besturingssysteem kan je meestal de versie van een programma opvragen met
programmanaam --version
Wat ook geldt voor Gambas:

gbx2 –version
2.21.0

gba2 –version
2.21.0

gbc2 –version
2.21.0

gbi2 –version
2.21.0

gbr2 –version
ERROR: #35: Cannot open file ‘–version’: No such file or directory

Zie ook: archiver, compiler, informer.

Linux command prompt is bruikbaar vanuit Gambas:

EXEC ["gbx2", "--version"] TO sRuntimeVersie

Error management – foutbeheer

Bij gebruik van een databank lijkt de eenvoudigste manier om de fouten op te slaan in de databank; tenzij de fouten juist gaan over de databankverbinding.

Een alternatief is ze op te slaan naar een tekstbestand “error.log”, zoals de meeste programma’s. Het is bedrijfszekerder en de fouten zijn gemakkelijk te raadplegen zonder dat de applicatie draait, of zonder zelfs de databank draait.

Wat moet er in de foutlijn in de logfile staan? Minstens:
Foutmelding, datum, tijd, systeeminformatie als user@host

Een paar voorbeelden uit /var/log:

Zypper:mail
2010-07-27 11:32:25 <1> mybox(17593) [zypp] RpmDb.cc(~RpmDb):310 ~RpmDb()
2010-07-27 11:32:25 <1> mybox(17593) [zypp] RpmDb.cc(closeDatabase):805 closeDatabase: RpmDb[NO_INIT]
2010-07-27 11:32:25 <1> mybox(17593) [zypp] TargetImpl.cc(~TargetImpl):302 Targets closed

X.org:
(II) intel(0): Output VGA is connected to pipe A
(II) intel(0): [drm] dma control initialized, using IRQ 16
(II) Mouse[1]: ps2EnableDataReporting: succeeded
(II) AIGLX: Suspending AIGLX clients for VT switch
(II) intel(0): xf86UnbindGARTMemory: unbind key 0

mail.err
Aug 25 10:24:01 linux postfix/postfix-script[3310]: fatal: the Postfix mail system is not running

Moeten er alleen fouten in staan, of ook meldingen van bv een toestand? Dan wordt het meer een soort “message log”. Cups lijkt letters te gebruiken als I = info, E = Error, W = warning:

/var/log/cups/error_log

I [25/Aug/2010:15:15:49 +0200] Started “/usr/lib/cups/cgi-bin/admin.cgi” (pid=1119)
E [25/Aug/2010:15:15:49 +0200] CUPS-Add-Modify-Printer: Unauthorized
W [10/Sep/2010:15:19:40 +0200] [Job 1886] Unknown choice “Auto” for option “InputSlot”!

Of moet je het scheiden zoals bij “mail“, in verschillende bestanden:

mail
mail.err
mail.info
mail.warn

Kan bv de “debug” omgeleid worden naar een bestand? Dan heb je de debug meldingen bij het programmeren+draaien van de code, en vergelijkbare data in de log.

Log snoeien: hoe lang mag log worden? FIFO die oudste data verliest, of telkens bij nieuwe start de vorige log leegmaken?

Afgeleide klasse van Listbox, vanuit code het object maken/instantiëren en gebruiken in een VBox

Doel: weergave van de dagen van de week, waarbij iedere dag een reeks waarden bevat (orders). De dagen van een week zijn vertikaal gegroepeerd.

De dag wordt voorgesteld als een soort listbox, met op iedere lijn de weergave van een ordernummer. Maar ik wil niet alle Dag-Listboxen, met steeds dezelfde eigenschappen, op het scherm tekenen.

De orders worden er naartoe “gesleept” (vierkantje op schermafbeelding) en komen in de lijst als je ze loslaat boven de dag (maandag op schermafbeelding).

Als er meer orders in de listbox komen, moet hij “groeien”, dwz de hoogte van de listbox moet groter worden; en omgekeerd moet hij ook kunnen krimpen.

Bovendien: de Dag-Listbox moet meer kunnen dan een listbox, ik maak dus een afgeleide klasse bv myListbox, of zo. Beter: ik noemt die CPlanDay; het geeft de planning voor die dag weer.

VBox ordent een aantal van mijn Listboxes (objecten van CPlanDay) vertikaal tot een week.

Volgende vragen bleven me open toen dat gelukt was:

  • Hoe maak ik een klasse die afgeleid is van een bestaande (Listbox)?
  • Hoe maak ik de objecten van die klasse vanuit code; “on the run”?
  • Hoe kan ik de extra mogelijkheden programmeren?
  • Hoe bereik ik de bestaande mogelijkheden van de ouder-klasse Listbox?

Eerste poging:
Continue reading

Gordijn: Een HSplit om twee GridViews te schalen zoals in een spreadsheet

Doel:
Het scherm is in twee verdeeld: links en rechts een rooster met gegevens, ongeveer er uitziend als een rekenblad/spreadsheet.

De gegevens aan de linkse kant wil je altijd zien omdat ze vaste titels en gegevens van de rij bevatten, bv de naam van een persoon (naam, voornaam, geb.datum, in dienst datum), waarvan in de volgende kolommen gegevens te zien zijn, bv aanwezigheden in een maand.

Van het linkse rooster is de eerste kolom meestal genoeg, de andere gegevens zijn “verborgen” achter de data van het rechtse rooster. Maar af en toe wil je toch de andere gegevens raadplegen, bv even naar de geboortedatum kijken, en wil je het rooster wat breder maken. Het lijkt alsof je een gordijn opzijschuift.

Er zijn meer kolommen met gegevens dan het scherm breed is, dus het rechtse rooster kan horizontaal scrollen. Er zijn ook meer rijen dan het scherm kan weergeven, dus beide roosters scrollen ook vertikaal.

Je hebt dus twee gridviews nodig: GridView1 links en GridView2 rechts.
Met de HSplit kan je horizontaal een “schuiver” krijgen om twee elementen met een middengrip groter/kleiner te maken, horizontaal in dit geval. Maar om dat te kunnen is er een “hoger” element nodig, dat de twee gridviews omvat, en van waaruit de Split kan werken, hier een HBox.

Gebruik van HSplit met GridViews

In de IDE rechts bij eigenschappen (properties), tabblad “hierarchy” krijg je dan deze structuur (zie afbeelding).

Het vervelende is dat de twee grids hun eigen scrolbars hebben, en niet alleen horizontaal maar ook vertikaal afzonderlijk scrollen. Dan klopt de horizontale lijn van de gegevens niet meer.

Daar kan je wel manueel en mouw aan passen; bv het weergeven van log gegevens, linkse grid de titel of naam van een logitem, rechtse grid de gegevens of data die gelogd en weergegeven worden.

Als je in het linkse “names” grid naar beneden scrolt moet het rechtse data-grid meescrollen:

PUBLIC SUB grvwLogNames_Scroll()

  grvwLogData.ScrollY = grvwLogNames.ScrollY

END

En omgekeeerd ook natuurlijk; als je in de data scrolt moeten de namen meescrollen:

PUBLIC SUB grvwLogData_Scroll()

  grvwLogDataNames.ScrollY = grvwLogData.ScrollY

END

Bovendien is het handig dat een klik op het rechtse grid ook “werkt”, dwz eigenlijk hetzelfde doet als een klik op het linkse grid.

PUBLIC SUB grvwLogData_Click()

  grvwLogNames.Row = grvwLogData.Row
  grvwLogNames_Click

END

Mail sturen vanuit Gambas

MailFromGambas Kies component “gb.net.smtp” in Project/Properties/Components.
Je krijgt dan beschikking over een smtp opject met volgende eigenschappen en methodes:

gb.net.smtp
 SmtpClient
   Add
   Alternative
   Bcc
   Cc
   From
   Host
   Port
   Send
   Subject
   To

De meeste eigenschappen kan je gewoon een tekstwaarde toewijzen, bv .Host = "..."
De lijst van de bestemmelingen wordt gegeven door de string .To.
Om een bestemmeling toe te voegen moet je To.Add("..") gebruiken, bv in een loop per adres dat je wil toevoegen.
Voor de inhoud van de mail .Add("..")
Attachments zijn halvelings voorzien maar lijken in 2.x versies (<2.21) niet altijd te werken (alleen te werken voor pure tekst).
Attachments hebben een type: IANN
Zie ook de documentatie van de gb.net.smtp module.

Een voorbeeld van code (die bij het schermvoorbeeld hoort):
Continue reading

Rekenen met tijd in Gambas (uren, dagen)

Om tijd vast te leggen op het moment zelf: Now() geeft de huidige datum+tijd. Bekijk:

DEBUG Now()

Toekennen aan een variabele:

PRIVATE dStartuptime AS Date
dStartuptime = Now()
DEBUG Format$(dStartuptime, “hh:nn:ss”)

De debug regelt toont de variabele weergegeven in opgegeven patroon, waarbij “n” staat voor minuten (omdat de m al staat voor maand in het dagpatroon: yy-mm-dd).

Rekenen met tijd of opgeslagen tijd gaat niet zo:
DEBUG Now() – dStartuptime
Dat geeft immers ofwel direct 0, ofwel na acht seconden 9.055575355887E-5 en waarschijnlijk wil je een leesbaar getal, als een aantal minuten.

Hiervoor gebruik je de functie DateDiff met parameters oude tijd, nieuwe tijd, gecombineerd met uitvoerpatroon:

DEBUG DateDiff(dStartuptime, Now(), gb.Minute)
DEBUG DateDiff(dStartuptime, Now(), gb.Minute)
DEBUG DateDiff(dStartuptime, Now(), gb.Minute)

Bij start is het resultaat:

0
0
0

Na 8 seconden is het resultaat:

8
0
0

En na 121 seconden is het:

121
2
0

Dus je hoeft niet zelf (het aantal minuten x 60 ) plus de seconden te doen; de seconden tellen door en kunnen gebruikt worden, of als je slechts de nauwkeurigheid van minuten nodig hebt kan je rechtstreeks met minuten werken.
Na meer dan een hele dag wordt het, uitgedrukt in gb.Minute, gb.Hour, gb.Day:

1547
25
2

De eerste twee, minuten in uren:
Van minuten naar uren: 1547/60 is 25.7833.. dus de uren worden “afgekapt”
Maar de laatste in “dagen” telt een begonnen dag als een dag: hoewel het maar goed 25 uren zijn wordt 2 dagen aangegeven!

Hoe data in een cel steken van een TableView

Zet op een Form bv Form1 een tabel met de eigenschap TableView, bv tbvwFill.
Het gemakkelijkst is waarschijnlijk om de tabel te initialiseren door code die je oproept bij het openen van de Form waar ze in staat:

PUBLIC SUB Form_Open()

  initTableView()

END

Je moet dan een procedure daarvoor maken:

PUBLIC SUB initTableView()

Daarin moet je zeker bepalen wat de grootte van de tabel is, m.a.w. hoeveel cellen horizontaal en hoeveel vertikaal. Daarvoor “zet” je de eigenschap van de kolom en rij-teller door er een waarde of de waarde van een bestaande variabele aan toe te kennen:

  tbvwFill.Columns.Count = 3
  tbvwFill.Rows.Count = iDefinedRows

Je kan het aantal natuurlijk laten bepalen door een globale variabele die je elders reeds gevuld had:

  tbvwFill.Rows.Count = FMain.$hMonth.GrdTotRows()
  • FMain: het hoofdscherm van waaruit het huidig scherm (Form1) wordt opgeroepen
  • $hMonth is een object dat een maand voorstelt, daar een aantal eigenschappen over heeft, en gedefinieerd werd in FMain.
  • GrTotRows is een eigenschap van object $hMonth, de waarde is al toegekend in FMain; of hier is het eigenlijk een functie van het object $hMonth die deze waarde berekent op basis van andere eigenschappen en teruggeeft als waarde.

Op deze manier kan het aantal rijen van tabellen in verschillende forms gestuurd worden uit 1 variabele.

Een waarde toekennen aan de twee cellen of velden van de bovenste rij in de tabel:

  tbvwFill[0, 0].Text = "ID"
  tbvwFill[0, 1].Text = "Naam"
  iRowStartEmployees = 1

Procedure natuurlijk altijd afsluiten met

END

Het vullen van de tabel kan o.a. met een FOR EACH loop, bv met waarden uit een resultset – hier $hResEmployees – die op voorhand gevuld werd met waarden uit een databank.

  FOR EACH $hResEmployees
    tbvwFill[$hResEmployees.Index + iRowStartEmployees, 0].Text = $hResEmployees!emp_id
    tbvwFill[$hResEmployees.Index + iRowStartEmployees, 1].Text = $hResEmployees!emp_name
  NEXT 

De eerste twee kolommen (kolom 0 en kolom 1) worden gevuld met een de velden id en name uit de database.
De eerste rij wordt hier niet gebruikt (rij 0) omdat daar de titel in staat.

Dan moet ervoor gezorgd worden dat als je op de tabel klikt, je de waarde van de cel kan wijzigen:

PUBLIC SUB tbvwFill_Click()

  DEBUG TableView1.Column
  DEBUG TableView1.Row

  tbvwFill.Edit()
  
END

De DEBUG lijnen tonen de rij en de kolom-waard; de cel linksboven is (0,0); de headers reageert niet op deze klik om coördinaten.

En tenslotte moet je ervoor zorgen dat die ingevulde waarde ook behouden blijft in de cel:
Daarvoor moet je de methode _Save van de tabel zelf aanvullen met code:

PUBLIC SUB tbvwFill_Save(x AS Integer, y AS Integer, sText AS String)
  
  tbvwFill[x, y].Text = sText
  
END

Je kan hier natuurlijk binnen deze code controles op uitvoeren enz.

– NB: – – Continue reading

Standaard leeg project

Project Type: Graphical application.
Options:

  • Internationalisation
  • Database access
  • Settings files management

Name: EmptyProject
Title: Empty Project

FMain: Menu met

  • File: Quit
  • Help: About, Changes, License

Form_Open: zet programma titel en versie.
doClose(): aangeroepen bij Menu/Quit (of scherm “Exit” button); kan afsluitende code aanroepen (wegschrijven toestand)

FAbout: About logo en tekst: naam plus versie.

FLicense: Toont License.txt
FChanges: Toont Changes.txt

Misschien nog een “test” menu-item dat alleen verschijnt als de conf file de verwijzing bevat:

[Mode]
Developer=”TRUE”

Op basis van die verwijzing zet je bij het starten van het programma het menu op visible.
Daaronder kunnen dat stukjes code getest worden die nog niet zijn vrijgegeven voor gebruik.
Opgelet: de gebruiker kan door het (buiten het programma om) wijzigen van zijn settings file dit menu dus aanzetten!

Logs en foutmeldingen op scherm

– Een eenvoudige weergave van log-meldingen en foutmeldingen kan in het (hoofd-)venster van het programma FMain in een ListBoxLog en ListBoxError. Door ListBoxLog.Add(“Opmerking”, 0) kan je de meldingen bovenaan toevoegen, de oude rollen weg naar beneden.

Fouten met Error Class

Een foutklasse CError kan vanuit het programma geinitieerd worden en de foutafhandeling waarnemen; bv kunnen de fouten naar een bestand gelogd worden, waarin elke regel voorafgegaan wordt door datum en tijd, en de foutmeldingslijn aangevuld wordt met de gebruiker System.User.Name

(laatste aanpassing: 08-2014)