Component in Gambas2

Basis-uitleg:

  • hoe componenten in Gambas werken
  • hoe ze zelf te maken
  • hoe ze te installeren.

De originele documentatie over componenten vermeldt uitdrukkelijk dat alle “onderstaande tekst” voor Gambas3 is, dus ik doorloop het even in en voor Gambas2:

Componenten

De componenten die je in de Gambas IDE ziet (project, properties), zijn mee geïnstalleerd met de Gambas IDE. (Kijk in je Linux pakket manager om bij te installeren).

In bv OpenSUSE vind je ze na installatie in: /usr/lib64/gambas2/
Bv de gb.chart.component en gb.chart.gambas

De .component is een tekstbestand, en bevat voor gb.chart:

[Component]
Key=gb.chart
State=2
Authors=Fabien Bodard
Needs=Form
Requires=gb.form

en voor gb.settings:

[Component]
Key=gb.settings
Authors=Benoît Minisini

In de IDE zijn de componenten uit te kiezen bij de projecteigenschappen, en tonen bovenstaande zich als:

gb.chart
Chart drawing (Beta version)
Authors: Fabien Bodard
Requires: Graphical form management, gb.form.

en

gb.settings
Application settings managament
Authors: Benoit Minisini

Bij het gebruik van Settings in je broncode worden volgende methodes en eigenschappen zichtbaar in de editor:

De broncode, van bv Settings, vind je na het afhalen en uitpakken van de broncode:
gambas2-2.23.1/comp/src/gb.settings

Een uittreksel (… is overgeslagen code):

' Gambas class file
'
EXPORT
CREATE STATIC
'
CLASS Window
'
STATIC PROPERTY READ Path AS String
'
PRIVATE $sPath AS String
PRIVATE $sTitle AS String
PRIVATE $cSlot AS Collection
PRIVATE $bModify AS Boolean
'
PRIVATE SUB Load()
'  
  DIM hFile AS File
  DIM iLine AS Integer
  DIM sLine AS String
  DIM sSlot AS String
  DIM iPos AS Integer
'
  $cSlot = NEW Collection
  IF NOT Exist($sPath) THEN RETURN
' ...
PUBLIC SUB _new(OPTIONAL Path AS String, OPTIONAL Title AS String)
'
  DIM sDir AS String
  DIM sPath AS String
  DIM sElt AS String
'
  IF Left(Path) <> "/" THEN 
    IF NOT Path THEN
' ...
PUBLIC SUB _free()
'
  TRY ME.Save
'
END
'
PUBLIC SUB Save()
'
  DIM aKey AS NEW String[]
  DIM cSlot AS Collection
  DIM sKey AS String
  DIM hFile AS File
  DIM vVal AS Variant
'
  IF NOT $bModify THEN RETURN
' ....
PRIVATE FUNCTION GetSlot(sKey AS String) AS String
' ...
PUBLIC FUNCTION _get(Key AS String, OPTIONAL {Default} AS Variant) AS Variant
' ...
PUBLIC SUB _put(Value AS Variant, Key AS String)
' ...
PUBLIC SUB Clear(ParentKey AS String)
' ...
PUBLIC SUB Read(hObject AS Object, OPTIONAL sKey AS String, OPTIONAL vDefault AS Variant)
  '
  DIM sVal AS String
  DIM aVal AS String[]
  '
  IF Object.Is(hObject, "Window") THEN
    sKey = Object.Type(hObject) &/ sKey
    LoadWindow(hObject, sKey)
  ELSE
    IF NOT sKey THEN TRY sKey = hObject.Name
    sKey = GetTopLevel(hObject) &/ sKey
    'DEBUG sKey
    TRY hObject.Settings = ME[sKey, vDefault]
  ENDIF
  '
END
'
PUBLIC SUB Write(hObject AS Object, OPTIONAL sKey AS String)
  '
  IF Object.Is(hObject, "Window") THEN
    sKey = Object.Type(hObject) &/ sKey
    SaveWindow(hObject, sKey)
  ELSE 'IF Object.Is(hObject, "SidePanel") THEN
    IF NOT sKey THEN TRY sKey = hObject.Name
    sKey = GetTopLevel(hObject) &/ sKey
    'DEBUG sKey
    TRY ME[sKey] = hObject.Settings
  ENDIF
  '
END
'
STATIC PRIVATE FUNCTION Path_Read() AS String
  '
  DIM sPath AS String = Application.Env["XDG_CONFIG_HOME"]
  IF NOT sPath THEN sPath = System.User.Home &/ ".config"
  RETURN sPath &/ "gambas"
  '
END
'
STATIC PUBLIC FUNCTION Array(...) AS String[]
  '  
  DIM aVal AS NEW String[]
  DIM iInd AS Integer
  DIM sVal AS String
  DIM vVal AS Variant
  '
  FOR iInd = 0 TO Param.Max
    vVal = Param[iInd]
    IF IsBoolean(vVal) THEN 
      sVal = IIf(vVal, "1", "0")
    ELSE
      sVal = CStr(vVal)
    ENDIF
    aVal.Add(sVal)
  NEXT
  '
  RETURN aVal
  '
END
' 
PUBLIC SUB Reload()
  '
  Load
  '
END

Conclusies:

Classe begint met:
EXPORT
CREATE STATIC

De bruikbare, dwz van buitenaf zichtbare elementen zijn vet weergegeven in de broncode hierboven.

Het path is een eigenschap die van buitenaf (enkel) leesbaar moet zijn.

Dat wordt gerealiseerd met een interne variabele die in de (automatisch bestaande) functie _Read() wordt gebruikt om de externe alleen-lezen variabele af te schermen. Als gevolg komt die eigenlijk twee keer voor, waarbij je onderscheid moet kunnen maken tussen de namen, wat hier gedaan wordt door de private variabele als $sPath te definieren, en de externe gewoon als Path:

 
STATIC PROPERTY READ Path AS String

PRIVATE $sPath AS String
'
STATIC PRIVATE FUNCTION Path_Read() AS String
  '
  DIM sPath AS String = Application.Env["XDG_CONFIG_HOME"]
  IF NOT sPath THEN sPath = System.User.Home &/ ".config"
  RETURN sPath &/ "gambas"
  '
END

Allerlei procedures (groene bol) zonder of met (één of meer optionele) parameters:

PUBLIC SUB Save()
PUBLIC SUB Reload()
PUBLIC SUB Clear(ParentKey AS String)
PUBLIC SUB Write(hObject AS Object, OPTIONAL sKey AS String)
PUBLIC SUB Read(hObject AS Object, OPTIONAL sKey AS String, OPTIONAL vDefault AS Variant)

De array ziet er zo uit (groene kubus):

STATIC PUBLIC FUNCTION Array(...) AS String[]

Zelf Component maken

Zelf maak je in de IDE een nieuw project, of wijzigt* een bestaand via menu:

Project, Properties, Options

kruis aan:

Options, Component: Yes. (Advancement: Beta)

Dat voegt onmiddellijk de tabbladen “Provides” en “Requieres” toe:

Schermafbeelding project venster gambas

Component in Gambas2

* Wijzigen van een bestaand, niet als component gestart project naar “component” leverde me een crash op bij een klik in het tabblad “Provides” als daar nog niets aanwezig is.

Bij een nieuw project krijg je dan
Classes:

  • CContainer
  • CControl

En onder Data de Controls:

  • ccontainer.png
  • ccontrol.png

Class CContainer bevat:

' Gambas class file

EXPORT

INHERITS UserContainer

PUBLIC SUB _new()

END

En Class CControl bevat:

' Gambas class file

EXPORT

INHERITS UserControl

PUBLIC SUB _new()

  DIM hLabel AS Label
  
  hLabel = NEW Label(ME) AS "MyLabel"
  hLabel.Text = "Gambas!"
  hLabel.Font = Font["+4,Bold,Italic"]
  hLabel.Border = Border.Plain
  
END

PUBLIC SUB MyLabel_Enter()

  LAST.Text = "Gambas Almost Means BASIC!"

END

PUBLIC SUB MyLabel_Leave()

  LAST.Text = "Gambas!"

END

De IDE maakte deze bestanden aan, samen met 2 grafische bestanden, en ik veronderstel dat de code een soort voorbeeldcode is die je zelf kan aanpassen of weggooien als je ze niet nodig hebt.

Bij een bestaand project met classen die je algemeen wil hergebruiken: Begin van je project en pas aan zoals hierboven uitgelegd. Ter vergelijking kan je de broncode van Gambas2 gebruiken als voorbeeld.

In de broncode van je class zelf moet je volgens de documentatie met sleutelwoord “EXPORT” aangeven dat ze geëxporteerd moet worden.

Je kan er een gewoon source archive van maken (tar.gz) om te verspreiden naar de gebruikers.

Je kan als gebruiker het project “compileren”; met “make executable”. Daar krijg je een extra optie:

“Install in the user component directory”

Daardoor wordt de component bruikbaar vanuit je Gambas2 IDE, menu Project, Properties, Components.
Hij komt onderaan in de lijst van de componenten, onder de extra titel “User Components”. Geniaal eenvoudig!

De bestanden .gambas en .component worden geïnstalleerd(*) in
~/.local/lib/gambas2
waardoor ze bruikbaar worden in al de gebruiker’s gambas2 projecten.

* Opgelet!
Ze worden daar niet naartoe gekopieerd, maar er wordt slechts een link, een verwijzing gemaakt naar de gecompileerde versie (i.e. de locatie waar je ze gecompileerd hebt). Gooi het originele project daar weg, en je hebt een probleem!

Nota: virtuele class
In geval van een hierarchie van klassen, waarbij de hoogste een virtuele klasse is, die nooit rechtstreeks gebruikt wordt, moet je ze benoemen met een begin-underscore “_”, zodat ze onzichtbaar wordt. De verwijzing INHERITS hoofdklasse in de subklassen ook aanpassen naar INHERITS _hoofdklasse natuurlijk.

Toetsencombinaties met control: Ctrl-s

Om te bewaren wordt in programma’s standaard toetsencombinatie Ctrl-s gebruikt, dat kan je ook in je Gambas programma’s.

Voorbeeldsituatie:

  • een scherm FMain met een paar tekstvelden “TextLabel1”, “TextLabel2″…
  • de toetsen worden opgevangen in het aktieve scherm “FMain”.

Dat kan in Gambas2 zo:

PUBLIC SUB _new()

END

PUBLIC SUB Form_Open()
  
END

PUBLIC SUB Form_KeyPress()

  IF Key.Control AND (Key.Code = Key["A"] OR Key.Code = Key["a"])
    TextLabel1.Text = "CTRL-"
    TextLabel2.Text = "Aa"
  ENDIF 

  IF Key.Control AND Key.Code = Key["z"]
    TextLabel1.Text = "CTRL-"
    TextLabel2.Text = "z"
  ENDIF 

END

Snel data ingeven met een tekstgebied (TextArea)

Gambas2 TextArea Input, leeg (screenshot)

Tekstgebied voor invoer

Als je op een scherm (Form) data laat ingeven moet je altijd een compromis zoeken tussen

  1. efficiëntie van de input
  2. controle van de input

Door punt “controle” zou je geneigd kunnen zijn meer aparte tekstvelden te gaan gebruiken, waarvan je telkens de invoer controleert. Waarbij de gebruiker, om zijn hele invoer te kunnen doen, dan wel telkens van veld moet veranderen (indien: implementeer TAB mogelijkheid!).

Een tekstgebied (TextArea), dat je eventueel “in de hoogte” vormgeeft, laat toe snel opeenvolgende woorden (zinnen) in te geven, hier vooral een reeks onder elkaar dus.

Ik laat de gebruiker achtereenvolgens de invoer doen, en op het moment dat hij het tekstgebied verlaat, doe ik de controle en geef feedback. Daarvoor worden de regels van het tekstgebied gesplitst in “zinnen”, die elk als één invoer beschouwd worden.

Gambas2 TextArea Input End (screenshot)

Na drie lijnen invoer, een enter, en nog een…

PUBLIC SUB doImportClassValues()

  DIM sLine AS String
  DIM hArrStr AS NEW String[]
  hArrStr = Split(txaeClassNames.Text, gb.NewLine) 
  
  setState(cAdd)
  
  FOR EACH sLine IN hArrStr
    doCheckSave(sLine)
  NEXT 
  

Om het toetsenbord niet te moeten verlaten bij het einde van de invoer, tel ik twee achtereenvolgende “volgende regel” (enter/return) als teken om te stoppen: gebruiker heeft een lege regel ingevoerd, en daarna nog één. Dat is het teken voor “klaar”, de verwerking van de invoer wordt gestart.

Om gemakkelijk toegang te geven tot de invoer in het tekstgebied gebruik ik bv een dubbelklik op het tekstgebied.

De teller voor het aantal enters is vooraf hoger gedefinieerd als
PRIVATE $iLastEnter AS integer

De toets voor enter/return wordt zo getest en geteld:

PUBLIC SUB txaeClassNames_KeyPress()
'
  IF Key.Code = Key.Enter OR Key.Code = Key.Return
    INC $iLastEnter 
    IF $iLastEnter = 2
      doCheckSaveValues(txaeClassNames.Text)
      $iLastEnter = 0
    ENDIF 
  ELSE 
    $iLastEnter = 0
  ENDIF 
'
END

Parameters doorgeven

Parameters tussen haakjes

Parameter passing of parameters doorgeven naar een procedure, functie (of een klasse) gaat door ze tussen gewone ronde haakjes te zetten:

procedure1(string1, string2)

resultaat = functie1(parameter1, parameter2)

De parameters kunnen getallen, tekst enz. zijn, afhankelijk hoe ze gedeclareerd zijn in de procedure.

Bv in de FMain form toon je het resultaat in een TextLabel1:

TextLabel1.Text = produkt(15, 3)

De procedure kan er zo uitzien:

PUBLIC SUB produkt(i AS INTEGER, j AS INTEGER)
   RETURN i * j
END

Een array als parameter

Het wordt een beetje ingewikkelder als je geen gewone parameters (integer, float, string..) wil geven, maar een “hoger” type, als een array.

Stel dat de klasse er zo uitziet:

PUBLIC SUB _new(ArrayOfParameters AS VARIANT)
   iNumberOfParameters = ArrayOfParameters.Count
   myParameters = ArrayOfParameters
END

En volgende methodes bevat:

PUBLIC SUB getParameters() AS Integer
   RETURN myParameters.Count
END

PUBLIC SUB getParameter(i AS Integer) AS Float
   RETURN myParameters[i]
END

De aanroep kan dan zo gebeuren (code van FMain):

PUBLIC arrFloats AS NEW FLOAT[]
PUBLIC hClass AS CReceiver


PUBLIC SUB _new()
   arrFloats.Add(1.1)
   arrFloats.Add(1.3)
   arrFloats.Add(2.4)
'
   hClass = NEW CReceiver(arrFloats)
END

PUBLIC SUB Form_Open()
   SpinBox1.MaxValue = arrFloats.Count - 1
   SpinBox1.Value = SpinBox1.MaxValue
END

PUBLIC SUB Button1_Click()
   TextLabel1.Text = hClass.getParameters()
END

PUBLIC SUB Button2_Click()
   TextLabel2.Text = hClass.getParameter(SpinBox1.Value)
END

Code voor groep van controls: group en tag

Controls op het scherm (en sommige andere objecten) hebben een “tag” eigenschap, die je zelf kan gebruiken naar eigen goeddunken. Een aantal controls in een container of group kunnen herkend worden aan de waarde van de tag property. Zo kan je alle “child” objecten van een container doorlopen tot je de gewenset “tag” inhoud vindt. Dat kan zowel een string als een getal of eender welke ” variant” zijn. Ps: Een timer heeft geen “tag”, een Form wel.

Bij een Form kan je de tag gebruiken om een toestand bij te houden, bv add/edit/saved mode.

Wil je dezelfde “event handlers” gebruiken voor een aantal controls, dan kan je ze in een group opnemen. In de eigenschappen staat de group bovenaan, onder de control (Class) en (Name): (Group).

Stel dat je 12 knoppen op je scherm hebt, die je allemaal als Group “grpKnoppen” hebt gegeven. Dan kan je:

PUBLIC SUB grpKnoppen_Click()
  
  Message.Info("Knop geklikt: " & LAST.Name)
  
END

Je kan verschillende objecten in dezelfde groep steken, en dan kan je voor alle de Tag eigenschap (of Name, Text, …) gebruiken.
Maar je moet er wel op letten dat de eigenschap die je aanspreekt bestaat (desnoods op object type controleren in je code).
bv grpObjecten:

PUBLIC SUB grpObjecten_Click()
  
  Message.Info(LAST.Name & "; " & LAST.Text & "; Tag:" & LAST.Tag)
  
END

In de Tag kan je een verwijzing bewaren, bv

  • index van een Array met andere waarden
  • record id van een record uit een database (via result set bv)

Je kan ook gebruik maken van een container waarin de objecten zitten (bv Frame1 dat Buttons bevat), en die je kan aflopen als “enumerated type”:
Als je een ListBox1 op je Form hebt kan je zo de objecten oplijsten:

PUBLIC SUB showAllChildren()
  
  DIM hObject AS Object
  
  FOR EACH hObject IN Frame1.Children
    ListBox1.Add(hObject.Name & ": " & hObject.Tag, 0)
  NEXT 
  
END

Klik en sleep

Dit is het meest minimale voorbeeld van klik en sleep of drag’n drop: je sleept een tekst van de linkse tekstbox naar het rechtse tekstlabel

Schermafbeelding Kleinste klik en sleep voorbeeld

Schermafbeelding Kleinste klik en sleep voorbeeld

Je hebt twee objecten nodig (zie schermafbeelding), en de volgende code in FMain.Form :

Van tekstbox …

PUBLIC SUB TextBox1_MouseDrag()
  IF Mouse.Left
    TextBox1.Drag(TextBox1.Text)
  ENDIF 
END

naar tekstlabel:

PUBLIC SUB TextLabel1_Drop()
  TextLabel1.Text = Drag.Data
END

Het voorwerp dat ontvangt moet de eigenschap voor ontvangen van “drop” ingesteld hebben.
Dat kan zowel in de schermontwerper bij eigenschappen, als in code (bv bij openen venster):

PUBLIC SUB Form_Open()
  TextLabel1.Drop = TRUE
END

Wat je nog extra kan doen:

  • Oorspronkelijke tekst laten verdwijnen bij slepen van ..
  • Icoon van het gesleepte tonen terwijl het slepen doorgaat (meestal als je een beeldje sleept).
  • Andere dingen (als waarde opslaan in buffer, teller verhogen enz)
  • Na “drop” het object afsluiten*: in PUBLIC SUB TextLabel1_Drop() regel toevoegen met TextLabel1.Drop = FALSE

* (26/6/2014 correctie: afsluiten voor nieuwe drop natuurlijk door de waarde FALSE, niet TRUE zoals er eerst stond).

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