Inhaltsverzeichnis

12.2.9 Datenaustausch zwischen Anwendungsfenstern

Spätestens dann, wenn Sie aus einem (Haupt-)Fenster FMain heraus ein weiteres Fenster F1 starten, könnte die Frage zu beantworten sein, nach welchem Konzept der Daten-Transfer zwischen den zwei Anwendungsfenstern erfolgen soll. In das Konzept werden sicher die folgenden Überlegungen eingehen:

In den folgenden Abschnitten werden Ihnen jeweils Kapitel genannt, in denen der Datentransfer für die o.a. Überlegungen schon beschrieben wurde.

12.2.9.1 Start-Werte F1

Start-Werte können Sie dem Fenster F1 mit einem entsprechend gestalteten Konstruktor über obligatorische und optionale Parameter in der _new-Methode mitgeben → Kapitel 12.2.7.1 Projekt 1. Eine andere Möglichkeit bieten selbst entwickelte Dialoge → Kapitel 24.3.4 Account.Manager.

12.2.9.2 Unidirektionaler oder bidirektionaler Daten-Austausch

Sie können auch die Verschiebung von Daten aus Steuer-Elementen auf FMain und F1 über die Methoden Drag&Drop als Daten-Transfer auffassen.

12.2.9.3 Daten-Austausch – Projekt 1

In einem realen, erprobten Projekt wird die Temperatur über einen Temperatursensor auf einer Platine über eine RS232-Schnittstelle in einem Anwendungsfenster FSensor erfasst. Von einem Haupt-Fenster FMain aus wird auf den Temperatur-Messwert auf FSensor zugegriffen und die Temperatur in FMain angezeigt:

Abbildung 12.2.9.3.1: Temperaturanzeige auf FMain – daneben FSensor

Damit Sie das Projekt nachvollziehen können, werden die Temperatur-Werte über einen Zufallsgenerator in einem bestimmten Temperatur-Bereich erzeugt. Damit entfallen der Einsatz einer realen Platine und der Klasse SerialPort (gb.net).

Wenn Sie wechselseitig auf Eigenschaften und Methoden von Steuer-Elementen auf FMain und FSensor zugeifen wollen, dann müssen Sie neben der Deklaration projekt-weit geltender Variablen auch die Eigenschaft Public der betreffenden Steuer-Elemente bereits zur Entwicklungszeit auf True setzen. Zur Laufzeit ist das nicht möglich, weil die Eigenschaft Public virtuell ist. Wie bereits im → Kapitel 12.2.0 'Form – Klasse Window und Klasse Form' angemerkt gilt: So lange es keinen zwingenden Grund gibt, die Public-Eigenschaft von False auf True zu ändern, sollten Sie der vollständigen Kapselung des Zustandes und des Verhaltens von Objekten den Vorzug geben.

Andererseits gestaltet sich der bidirektionale Daten-Austausch zwischen FMain und FSensor über die öffentlichen Steuer-Elemente und Variablen sehr einfach. Der Quelltext für die Klassen FMain und FSensor in den Projekten 1 und 2 ist hinreichend kurz und wird deshalb vollständig angegeben. FMain.class

' Gambas class file
 
Public Sub Form_Open()
  Application.MainWindow = FMain
  FMain.Center()
  FMain.Resizable = False  
  piboxOn.Picture = Picture["LED/led_yellow.svg"] 
  ValueBox1.Value = 0.2
  btnStartSensor1.Text = "Temperaturmessung Sensor 1 starten"
 End
 
Public Sub btnStartSensor1_Click()  
  piboxOn.Picture = Picture["LED/led_green.svg"] 
  FSensor.fTIBegin = 22.9
  FSensor.fTIEnd = 24
  FSensor.fTITrigger = ValueBox1.Value  
  FSensor.Show()
  FSensor.X = FMain.X + FMain.W + 20
  FSensor.Y = FMain.Y + FMain.H - FSensor.H
  btnStartSensor1.Enabled = False
End
 
Public Sub ValueBox1_Change()
  If ValueBox1.Value >= 0 And If ValueBox1.Value <= 0.9 Then FSensor.fTITrigger = ValueBox1.Value
End
 
Public Sub Form_Close()
  FMain.Close()
End 
</close>
 
FSensor.class
 
<code gambas>
' Gambas class file
 
Public fCurTemperature As Float
Public fTIBegin As Float
Public fTIEnd As Float
Public fTITrigger As Float
 
Private fSavedTemperature As Float
 
Public Sub Form_Open()  
  FSensor.Center()
  FSensor.Resizable = False
  TimerSimulation.Trigger()
End
 
Public Sub TimerSimulation_Timer()
 
  Dim bChanged As Boolean
 
  Randomize
  fCurTemperature = Rnd(fTIBegin, fTIEnd)
  bChanged = Abs(fCurTemperature - fSavedTemperature) > fTITrigger
  fSavedTemperature = fCurTemperature 
  If bChanged Then 
     FMain.lblTemperaturAnzeige.Text = Format(FSensor.fCurTemperature, "##.0 °C")
  Endif  
End
 
Public Sub Form_Close()
  FMain.piboxOn.Picture = Picture["LED/led_yellow.svg"] ' piboxOn.Public = True
  FMain.btnStartSensor1.Enabled = True                  ' btnStartSensor1.Public = True
  FMain.lblTemperaturAnzeige.Text = "--- °C"            ' lblTemperaturAnzeige.Public = True
  FMain.ValueBox1.Value = 0.2                           ' ValueBox1.Public = True
End

Kommentar:

12.2.9.4 Daten-Austausch – Projekt 2

Im zweiten Projekt erfolgt der Daten-Austausch zwischen den Anwendungsfenstern FMain und FSensor nur über öffentliche Schnittstellen, deren Signaturen bekannt sind. Jede Klasse kapselt somit ihre Daten vollständig gegenüber der anderen Klasse. Das wird auch im Quelltext für die beiden Klassen FMain und FSensor deutlich: Es gibt keine öffentlichen Steuer-Elemente und auch keine öffentlichen Variablen!

FMain.class
 
' Gambas class file
 
Private fTITrigger As Float = 0.2
Public hSensor As FSensor
 
Public Sub Form_Open()
  Application.MainWindow = FMain
  FMain.Center()
  FMain.Resizable = False  
  piboxOn.Picture = Picture["LED/led_yellow.svg"] 
  ValueBox1.Value = fTITrigger
 End
 
 Public Sub btnStartSensor1_Click()
  CreateSensor1()
  piboxOn.Picture = Picture["LED/led_green.svg"] 
  btnStartSensor1.Enabled = False
End
 
Public Sub Sensor1_Close()
  piboxOn.Picture = Picture["LED/led_yellow.svg"]
  btnStartSensor1.Enabled = True
  lblTemperature1.Text = "--- °C"  
  ValueBox1.Value = fTITrigger
End
 
Public Sub Sensor1_Change()
  lblTemperature1.Text = Format(hSensor.Temperature, "##.0 °C")
End
 
Public Sub ValueBox1_Change()
  If hSensor Then 
     If ValueBox1.Value >= 0 And If ValueBox1.Value <= 0.9 Then
        hSensor.TriggerValue = ValueBox1.Value
     Endif
  Endif
End
 
Public Sub CreateSensor1()
 
  Dim hObserver As Observer
  Dim aRanges As Float[] = [19.9, 21]
 
  hSensor = New FSensor(aRanges[0], aRanges[1])
  hSensor.Caption = "TEMPERATUR-SENSOR 1"
  hSensor.TriggerValue = fTITrigger
  hSensor.Show()
  hSensor.X = FMain.X + FMain.W + 20
  hSensor.Y = FMain.Y + FMain.H - hSensor.H
  hObserver = New Observer(hSensor) As "Sensor1"
End
 
Public Sub Form_Close()
  FMain.Close()
End 

FSensor.class

' Gambas class file
 
Private fSavedTemperature As Float = 0.0
Private $fIBegin As Float
Private $fIEnd As Float
'--------------------------------------------------------------------
'' Eigenschaft: Temperature, Zugriffsrecht: public, Modus: read-only
Property Read Temperature As Float 
'' Eigenschaft: TriggerValue, Zugriffsrecht: public
Property TriggerValue As Float
'---------------------------------------------------------------------
Private $fTemperature As Float     ' Lokale Variable ➘ Schnitt-Stelle
Private $fTriggerValue As Float    ' Lokale Variable ➘ Schnitt-Stelle
'---------------------------------------------------------------------
'' Ereignis: Change
Event Change
'---------------------------------------------------------------------
 
Public Sub _new(IBegin As Float, IEnd As Float)
  If Not TypeOf(IBegin) = gb.Float Then Error.Raise("Typ-Fehler: Startwert von Temperatur-Intervall.")
  If Not TypeOf(IEnd) = gb.Float Then Error.Raise("Typ-Fehler: Endwert von Temperatur-Intervall.")
  If IBegin >= IEnd Then Error.Raise("Der Startwert ist größer oder gleich dem Endwert vom Intervall.")
  $fIBegin = IBegin
  $fIEnd = IEnd
End
 
Public Sub Form_Open()
  Me.Center()
  Me.Resizable = False
  TimerSimulation.Delay = 1000
  TimerSimulation.Start()
  TimerSimulation.Trigger()
End ' Form_Open()
 
Public Sub TimerSimulation_Timer()
 
  Dim bChanged As Boolean
 
  Randomize
  $fTemperature = Rnd($fIBegin, $fIEnd)
  bChanged = Abs($fTemperature - fSavedTemperature) > $fTriggerValue
  fSavedTemperature = $fTemperature
 
  If bChanged Then Raise Change
 
End
 
' Zusätzlicher Dienst oder Schnittstelle (öffentliche Methode): Temperatur_lesen
Private Function Temperature_Read() As Float
  Return $fTemperature
End
 
' Zusätzlicher Dienst oder Schnittstelle (öffentliche Methode): Trigger_lesen
Private Function TriggerValue_Read() As Float
  Return $fTriggerValue
End
 
' Zusätzlicher Dienst oder Schnittstelle (öffentliche Methode): Trigger_schreiben
Private Sub TriggerValue_Write(Value As Float)
  If Value < 0 Or Value > 1 Then 
     Error.Raise("Der Trigger-Wert liegt nicht im Intervall [0|1]")
  Endif
 
  $fTriggerValue = Value
 
End

Kommentar:

Public Sub TimerSimulation_Timer()
  Dim bChanged As Boolean
 
  Randomize
  $fTemperature = Rnd($fIBegin, $fIEnd)
  bChanged = Abs($fTemperature - fSavedTemperature) > $fTriggerValue
  fSavedTemperature = $fTemperature
  If bChanged Then Raise Change
 
End
 
Public Sub Sensor1_Change()
  lblTemperature1.Text = Format(hSensor.Temperature, "##.0 °C")
End
Public Sub Sensor1_Close()
  piboxOn.Picture = Picture["LED/led_yellow.svg"]
  btnStartSensor1.Enabled = True
  lblTemperature1.Text = "--- °C"  
  ValueBox1.Value = fTITrigger
End

FSensor.class:

Public Sub _new(IBegin As Float, IEnd As Float)
  If Not TypeOf(IBegin) = gb.Float Then Error.Raise("Typ-Fehler: Startwert.")
  If Not TypeOf(IEnd) = gb.Float Then Error.Raise("Typ-Fehler: Endwert von Temperatur-Intervall.")
  If IBegin >= IEnd Then Error.Raise("Der Startwert ist größer oder gleich dem Endwert).")
  $fIBegin = IBegin
  $fIEnd = IEnd
End

FMain.class:

Public Sub CreateSensor1()  
  Dim hObserver As Observer
  Dim aRanges As Float[] = [19.9, 21]
 
  hSensor = New FSensor(aRanges[0], aRanges[1])
  hSensor.Caption = "TEMPERATUR-SENSOR 1"
  hSensor.TriggerValue = fTITrigger
  hSensor.Show()
  hSensor.X = FMain.X + FMain.W + 20
  hSensor.Y = FMain.Y + FMain.H - hSensor.H
  hObserver = New Observer(hSensor) As "Sensor1"
End

Der Observer muss erzeugt werden, da die Klasse FSensor zwar von der Klasse Form erbt, aber nicht die Erweiterungen (2 Eigenschaften und ein Ereignis) der Klasse 'im Blick' hat.

Das Projekt bietet m.E. mehrere Vorteile gegenüber Projekt 1:

Die Vorteile klingen überzeugend. Nur werden Sie recht bald feststellen, dass diese Vorteile bei größeren Projekten schnell dahin schmelzen, denn eine Klasse konsequent und ordentlich zu kapseln kann im Design sehr schwierig und langwierig sein. Gelegentlich wird der Code sogar zu umständlich. Nur das sind die Fälle für die sorgfältig abgewogene Verwendung öffentlicher Steuerelemente! So ein Fall liegt für das Projekt 1 nicht vor, so dass dem Projekt 2 der Vorzug gegeben werden sollte.

12.2.9.5 Daten-Austausch – Projekt 3

Das 3. Projekt arbeitet mit 3 Sensor-Anwendungsfenstern. Alle drei Anwendungsfenster sind Instanzen der Klasse FSensor, die aus dem Projekt 2 übernommen wird. Es wird deshalb nur der Quelltext der Klasse FMain vorgestellt und kommentiert:

[1] ' Gambas class file
[2] 
[3] Private $iSensors As Integer = 1
[4] Private aSensors As New FSensor[]
[5] Private aTrigger As Float[] = [0.1, 0.3, 0.5]
[6] 
[7] Public Sub Form_Open()
[8]   Application.MainWindow = FMain
[9]   FMain.Center
[10]   FMain.Resizable = False
[11]   piboxOn.Picture = Picture["LED/led_green.svg"]
[12]   CreateSensors()  
[13]   ValueBox1.Value = aTrigger[0]
[14]   ValueBox2.Value = aTrigger[1]
[15]   ValueBox3.Value = aTrigger[2]
[16] End
[17] 
[18] Private Sub CreateSensors()
[19]   Dim hSensor As FSensor, hObserver As Observer
[20]   Dim i As Integer 
[21]   Dim aRanges As Float[][] = [[20, 21], [40.0, 42], [33, 35.8]]
[22]   
[23]   For i = 1 To aRanges.Count
[24]     hSensor = New FSensor(aRanges[i - 1][0], aRanges[i - 1][1])
[25]     hSensor.Caption = "TEMPERATUR-SENSOR " & Str($iSensors)
[26]     hSensor.Tag = $iSensors ' Die Tag-Eigenschaft speichert die Nummer des Sensors
[27]     hSensor.TriggerValue = aTrigger[i - 1]
[28]     hSensor.Show()
[29]     aSensors.Add(hSensor)
[30]     hObserver = New Observer(aSensors[i - 1]) As "Sensor"
[31]     hObserver.Tag = $iSensors ' Die Tag-Eigenschaft speichert die Nummer des Observers
[32]     Inc $iSensors    
[33]   Next
[34] End
[35] 
[36] Public Sub Sensor_Change()
[37]   Select Case Last.Tag
[38]     Case 1
[39]       lblTemperature1.Text = Format(Last.Temperature, "##.0 °C")
[40]     Case 2
[41]       lblTemperature2.Text = Format(Last.Temperature, "##.0 °C")
[42]     Case 3
[43]       lblTemperature3.Text = Format(Last.Temperature, "##.0 °C")
[44]   End Select
[45] End
[46] 
[47] Public Sub Sensor_Close()
[48]   Select Case Last.Tag
[49]     Case 1
[50]       lblTemperature1.Text = "--- °C"
[51]     Case 2
[52]       lblTemperature2.Text = "--- °C"
[53]     Case 3
[54]       lblTemperature3.Text = "--- °C"
[55]   End Select
[56] End
[57] 
[58] Public Sub ValueBox1_Change()
[59]   aSensors[0].TriggerValue = ValueBox1.Value
[60] End
[61] 
[62] Public Sub ValueBox2_Change()
[63]   aSensors[1].TriggerValue = ValueBox2.Value
[64] End
[65] 
[66] Public Sub ValueBox3_Change()
[67]   aSensors[2].TriggerValue = ValueBox3.Value
[68] End

Kommentar:

Hinweis: Eine Alternative bietet sich, wenn Sie die Zeile 30 durch diesen Quelltext ersetzen:

hObserver = New Observer(aSensors[i - 1]) As "Sensor" & Str($iSensors)

Nun erhalten Sie drei Observer mit jeweils eigenem Event-Namen und können aus diesem Grund das Change()-Ereignis und das Close()-Ereignis separat auswerten, wenn Sie die Zeilen 36 bis 56 durch die folgenden beiden Quelltext-Abschnitte ersetzen:

Public Sub Sensor1_Change()
  lblTemperature1.Text = Format(aSensors[0].Temperature, "##.0 °C")
End

Public Sub Sensor2_Change()
  lblTemperature2.Text = Format(aSensors[1].Temperature, "##.0 °C")
End

Public Sub Sensor3_Change()
  lblTemperature3.Text = Format(aSensors[2].Temperature, "##.0 °C")
End
Public Sub Sensor1_Close()
  lblTemperature1.Text = "--- °C"
  ValueBox1.Enabled = False
End

Public Sub Sensor2_Close()
  lblTemperature2.Text = "--- °C"
  ValueBox2.Enabled = False
End

Public Sub Sensor3_Close()
  lblTemperature3.Text = "--- °C"
  ValueBox3.Enabled = False
End

Download