Graph mit bewegter Zeitachse

  • Hallo Forum,

    ich möchte gern auf einer GUI einen Graph zeichnen, bei dem sich die x-Achse bewegt.
    Genauer gesagt will ich eine Sollgeschwindigkeit vorgeben und die Istgeschwindigkeit zeichnen. Die Sollgeschwindigkeit sind quasi Rampen die sich von rechts nach links bewegen und in der Mitte bewegt sich ein Punkt mit der aktuellen Geschindigkeit von unten nach oben und zeichnet eine Spur.
    So ähnlich wie ein Super-Mario der beim Hüpfen eine Spur zieht.

    Hat jemand so etwas schonmal gemacht? Könnte mir jemand einen Tipp geben wie man so etwas machen könnte?

    Für Hilfe bin ich sehr dankbar!

    pipo

    Einmal editiert, zuletzt von pipo28 (22. Juni 2012 um 23:28)

  • Um effektiv auf GUIs zu zeichnen benutzt du am besten die GDIPlus Funktionen.
    Wenn du damit noch nicht gearbeitet hast empfehle ich dir Tutorials anzuschauen (das von Ubuntu zum Beispiel).
    Das hier könnte möglicherweise auch interessant für dich sein: Graphen zeichnen, Unicalc Grafik.

    Ich hoffe ich konnte dir weiterhelfen ;)

    mfg
    Developer30

    "Je mehr Käse, desto mehr Löcher; je mehr Löcher, desto weniger Käse. Ergo: Je mehr Käse, desto weniger Käse. 8| "
    "Programmers never die: they just GOSUB without RETURN"
    "I tried to change the world but I couldn't find the source code."

  • Cool danke.
    Am meisten hat mir das Tutorial Snake für Anfänger von Marsi geholfen. Wenn mans mal kapiert ist es einfacher als gedacht. Ich Poste mal meinen Code bevor ich zu spezielle Sachen reinmache, vielleicht kann es jemand benutzen:

    [autoit]

    #include <GUIConstants.au3>
    #include <GDIPlus.au3>
    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    Global $FLGwidth = 800
    Global $FLGheight = 400
    Global $Hintergrundfarbe = 0xFF00CED1
    Global $Farbe_Fahrkurve = 0xFF800000
    Global $Refreshrate = 200

    [/autoit][autoit][/autoit][autoit]

    _Main()

    [/autoit][autoit][/autoit][autoit]

    Func _Main()
    Local $hGUI, $hGraphic, $hBitmap, $Vist[101][2], $hBuffer
    $hGUI = GUICreate("Bewegte x-Achse", 1000, 800)
    GUISetState()
    _GDIPlus_Startup ()
    $Pen = _GDIPlus_PenCreate($Farbe_Fahrkurve, 3)
    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hBitmap = _GDIPlus_BitmapCreateFromGraphics( $FLGwidth,$FLGheight, $hGraphic)
    $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    [/autoit][autoit][/autoit][autoit]

    $Vist[0][0] = 100
    for $i = 1 to 100
    $Vist[$i][0] = $i*$FLGwidth/(2*100)
    $Vist[$i][1] = 0.9*$FLGheight
    Next
    $zeit = 0
    $timer = TimerInit()
    while True
    Select
    case GUIGetMsg() = $GUI_EVENT_CLOSE
    ExitLoop
    case TimerDiff($timer) > $zeit * $Refreshrate
    $zeit += 1
    for $i = 1 to 99
    $Vist[$i][1] = $Vist[$i+1][1]
    Next
    $x = Random(0,$FLGheight)
    $Vist[100][1] = $x
    _GDIPlus_GraphicsClear($hBuffer, $Hintergrundfarbe)
    _GDIPlus_GraphicsDrawCurve($hBuffer, $Vist,$Pen)
    _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $FLGwidth, $FLGheight)
    EndSelect
    WEnd

    [/autoit][autoit][/autoit][autoit]

    ; Clean up resources
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_GraphicsDispose($hBuffer)
    _GDIPlus_PenDispose($Pen)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()

    [/autoit][autoit][/autoit][autoit]

    EndFunc ;==>_Main

    [/autoit]
  • Ich habe das Ganze jetzt verbessert geschrieben und mit folgenden Features versehen:

    - Freie Wählbarkeit der Frequenz mit der Werte aktualisiert werden
    - Freie Wählbarkeit der dargestellten Zeit und damit der Geschwindigkeit mit der sich die x-Achse bewegt
    - Möglichkeit eine Sollkurve mit Tolleranz vorzugeben, direkt im Programm eingeben oder aus txt Datei auslesen
    - Berechnen des akutellen Sollwertes durch Interpolation
    - Freie Wählbarkeit des Endwertes der y-Achse und Anpassung der Gitterlinien daran

    Bei mir heißen die Sollwerte Vsoll und die Istwerte Vist, weil es bei mir um Geschwindigkeiten geht. Also nicht verwirren lassen!

    Ein Problem habe ich noch wenn man die GUI mit der Maus zieht, dann bleibt das Script beim Zeichnen hängen und die Zeit bleibt mit stehen. Weiß jemand ob man das durch irgendwelche Optionen, ohne die Logik des Codes zu ändern, umgehen kann?

    Demnächst werde ich noch versuchen zusätzlich die Möglichkeit einer sich anpassenden y-Achse hinzuzufügen, so dass die Y-Achse rein und rauszoomt wenn sich die Werte im Angezeigten Bereich ändern.

    Ich hoffe es kann jemand brauchen, viel Spaß damit:


    [autoit]

    #include <GUIConstants.au3>
    #include <GDIPlus.au3>
    #include <Array.au3>
    #include <file.au3>

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Global $FLGwidth = 1200 ; Breite des Graphen
    Global $FLGheight = 400 ; Breite des Graphen
    Global $Hintergrundfarbe = 0xFF00CED1
    Global $Farbe_Fahrkurve = 0xFF800000
    Global $Farbe_Sollkurve = 0xFF006400
    Global $Farbe_Tolleranz = 0xFFFF4040
    Global $Refreshrate = 200 ; Frequenz der Datenaufnahme
    Global $Vorlauf = 20 ; Zeit in Sekunden in denen Vsoll im Voraus dargestellt wird
    Global $VMAX = 60 ; Maximalgeschwindigkeit am oberen Ende des Graphen
    Global $LostData = 0

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    $hGUI = GUICreate("bewegte x-Achse", 1200, 800)
    ; ----------------- Kontrollelemente
    GUICtrlCreateLabel("Sollgeschwindigkeit", 20,$FLGheight + 240, 270, 40)
    GUICtrlSetFont(-1,22)
    $vsoll_Label = GUICtrlCreateLabel("0", 20, $FLGheight + 340, 100, 80)
    GUICtrlSetFont(-1,28)
    GUICtrlCreateLabel("km / h ", 100, $FLGheight + 340, 180, 80)
    GUICtrlSetFont(-1,28)

    [/autoit][autoit][/autoit][autoit]

    GUICtrlCreateLabel("Debug", 350, $FLGheight +240, 270, 40)
    GUICtrlSetFont(-1,22)
    $debug = GUICtrlCreateLabel("0", 350, $FLGheight +340, 600, 80)
    GUICtrlSetFont(-1,28)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit][/autoit][autoit]

    _Main()

    [/autoit][autoit][/autoit][autoit]

    Func _Main()
    Local $hGraphic, $hBitmap, $Vist[1+$Vorlauf/($Refreshrate/1000)][2],$vsoll_norm[1000][2], $hBuffer ;$vsoll[1000][2]
    ; KOmmentar enferen wenn Sollwerte aus Datei gelesen werden sollen

    [/autoit][autoit][/autoit][autoit]

    #cs Kommentar entfernen wenn Sollwerte aus Datei gelesen werden sollen
    ; -------------- Vsoll aus Datei auslesen Mit bis zu 999 Zeilen wobei in jeder Zeile Zeit[s] - Tabulator - Sollwert stehen soll
    $file = FileOpen("vsoll.txt", 0)
    ; Check if file opened for reading OK
    If $file = -1 Then
    MsgBox(0, "Error", "Kann die Vsoll.txt nicht finden. Bitte im selben Verzeichnis wie dieses Programm ablegen.")
    Exit
    EndIf
    ; Read in lines of text until the EOF is reached
    $i = 0
    While 1
    $line = FileReadLine($file)
    If @error = -1 Then ExitLoop
    $temp = StringSplit($line,@TAB)
    if ubound($temp) = 3 Then
    $i += 1
    $vsoll[$i][0] = $temp[1]
    $vsoll_norm[$i][0] = $temp[1] * ($FLGwidth/2)/$Vorlauf
    $vsoll[$i][1] = $temp[2]
    $vsoll_norm[$i][1] = $temp[2] * 0.9 * $FLGheight / $vmax
    Else
    msgbox(0,"Fehler", "Die Vsoll.txt hat das falsche Format. In jeder Zeile muss stehen: Zeit[s] - Tabulator - Geschwindigkeit[kmh]")
    Exit
    EndIf
    Wend
    FileClose($file)
    $vsoll_norm[0][0] = $i
    $vsoll[0][0] = $i
    ; Vsoll auslesen Ende, $Vsoll enthält nun im Element [0][0] die Anzahl an Stützpunkten und in den Elementen [1][0] - [n][0] die Sollzeit und in [1][1] - [n][1] die zugehörige Sollgeschwindigkeit
    #ce
    local $vsoll[17][2] = [[16,0],[0,0],[10,10],[15,16],[24,16],[28,0],[50,0],[60,32],[85,32],[95,0],[120,0],[145,50],[155,50],[165,35],[175,35],[190,0],[195,0]]
    for $i = 1 to $vsoll[0][0]
    $vsoll_norm[$i][0] = $vsoll[$i][0] * ($FLGwidth/2)/$Vorlauf
    $vsoll_norm[$i][1] = $vsoll[$i][1] * 0.9 * $FLGheight / $vmax
    Next
    $vsoll_norm[0][0] = $vsoll[0][0]
    ; ---------------- den oberen Teil löschen wenn die Sollwerte aus einer Datei gelesen werden sollen

    [/autoit][autoit][/autoit][autoit]

    GUISetState()
    ; ---- GDI Plus Zeug initialiesieren
    _GDIPlus_Startup ()
    $Pen_Vist = _GDIPlus_PenCreate($Farbe_Fahrkurve, 3)
    $Pen_Vsoll = _GDIPlus_PenCreate($Farbe_Sollkurve, 2)
    $Pen_Vtoll = _GDIPlus_PenCreate($Farbe_Tolleranz, 2)
    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    $hBitmap = _GDIPlus_BitmapCreateFromGraphics( $FLGwidth,$FLGheight, $hGraphic)
    $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    ; -------- Array fürs Vist malen erzeugen
    for $i = 1 to $Vorlauf/($Refreshrate/1000)-1 ;Anzahl der Elemente ist abhängig von der Updatefrequenz und der dargestellten Datenmenge (Vorlaufzeit)
    $Vist[$i][0] = $i*$FLGwidth/(2*$Vorlauf/($Refreshrate/1000))
    $Vist[$i][1] = 0.9*$FLGheight
    Next
    $vist[0][0] = $Vorlauf/($Refreshrate/1000)-1

    [/autoit][autoit][/autoit][autoit]

    $zeit = 0 ; Zeit und Timer um die zyklischen Updates durchzuführen
    $timer = TimerInit()
    ; -------------------
    while True
    Select
    case GUIGetMsg() = $GUI_EVENT_CLOSE
    ExitLoop
    case TimerDiff($timer) > $zeit * $Refreshrate

    [/autoit][autoit][/autoit][autoit]

    $LostData += Floor(TimerDiff($timer)/ $Refreshrate) - ($zeit) ; überprüfen ob ich zwischen den Updates zu lange gebraucht habe für Zeichnen und berechnen
    $zeit = 1 + Floor(TimerDiff($timer)/ $Refreshrate)
    for $i = 1 to $Vorlauf/($Refreshrate/1000)-1 ; Alle Werte wandern eins weiter
    $Vist[$i][1] = $Vist[$i+1][1]
    Next
    $Vist[$Vorlauf/($Refreshrate/1000)][1] = Datenempfang($Zeit, $vsoll) ; Der höchste Wert bekommt den aktuellen Wert. Diese Funktion ist ausgelagert

    [/autoit][autoit][/autoit][autoit]

    _GDIPlus_GraphicsClear($hBuffer, $Hintergrundfarbe)
    ; Gitterlinien mit Beschriftung zeichnen
    for $i = 0 to $vmax / 10
    _GDIPlus_GraphicsDrawLine($hBuffer,0,$i*0.9*$FLGheight/($vmax/10) ,$FLGwidth,$i*0.9*$FLGheight/($vmax/10))
    _GDIPlus_GraphicsDrawString($hBuffer,Round($i*10,-1) & " km/h",$FLGwidth/20,0.9*$FLGheight-$i*0.9*$FLGheight/($vmax/10));Round($vmax/10,-1),20,$FLGwidth,$i*0.9*$FLGheight/($vmax/10)-25)
    Next
    ; Solkurve zeichnen
    for $i = 1 to $vsoll_norm[0][0]-1
    if $vsoll_norm[$i][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf < $FLGwidth and $vsoll_norm[$i+1][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf > 0 then
    ; zeichne Vsoll
    _GDIPlus_GraphicsDrawLine($hBuffer,$vsoll_norm[$i][0]+$FLGwidth/2-$zeit*($Refreshrate/1000)*($FLGwidth/2)/$Vorlauf,0.9*$FLGheight-$vsoll_norm[$i][1],$vsoll_norm[$i+1][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf, 0.9*$FLGheight-$vsoll_norm[$i+1][1],$Pen_Vsoll)
    ; zeichne untere Tolleranz
    _GDIPlus_GraphicsDrawLine($hBuffer,$vsoll_norm[$i][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf,0.9 * $FLGheight / $vmax * 5 + 0.9*$FLGheight-$vsoll_norm[$i][1],$vsoll_norm[$i+1][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf, 0.9 * $FLGheight / $vmax * 5 + 0.9*$FLGheight-$vsoll_norm[$i+1][1],$Pen_Vtoll)
    ; zeichne obere Tolleranz
    _GDIPlus_GraphicsDrawLine($hBuffer,$vsoll_norm[$i][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf,-0.9 * $FLGheight / $vmax * 5 + 0.9*$FLGheight-$vsoll_norm[$i][1],$vsoll_norm[$i+1][0]+$FLGwidth/2-$zeit/(1000/$Refreshrate)*($FLGwidth/2)/$Vorlauf, -0.9 * $FLGheight / $vmax * 5 + 0.9*$FLGheight-$vsoll_norm[$i+1][1],$Pen_Vtoll)

    [/autoit][autoit][/autoit][autoit]

    EndIf
    Next
    ; Vist zeichnen
    _GDIPlus_GraphicsDrawCurve($hBuffer, $Vist,$Pen_Vist)
    ; alles visualisieren
    _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $FLGwidth, $FLGheight)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit][/autoit][autoit]

    EndSelect
    WEnd

    [/autoit][autoit][/autoit][autoit]

    ; Clean up resources
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_GraphicsDispose($hBuffer)
    _GDIPlus_PenDispose($Pen_Vist)
    _GDIPlus_PenDispose($Pen_Vsoll)
    _GDIPlus_PenDispose($Pen_Vtoll)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()

    [/autoit][autoit][/autoit][autoit]

    EndFunc ;==>_Main

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func Datenempfang($Zeit, $vsoll)

    [/autoit][autoit][/autoit][autoit]

    for $i = $vsoll[0][0] to 1 step -1
    if $zeit / (1000/$Refreshrate) > $vsoll[$i][0] Then
    if $i = $vsoll[0][0] then msgbox(0,"","Ende")
    $a = ($vsoll[$i][1]-$vsoll[$i+1][1])/($vsoll[$i][0]-$vsoll[$i+1][0])
    $b = ($vsoll[$i][1]-$a*($vsoll[$i][0])) ;durch interpolieren wird die aktuelle Sollgeschwindigkeit berechnet
    GUICtrlSetData($vsoll_Label,Round($a*$zeit/(1000/$Refreshrate)+$b,1)) ; Sollgeschwindigkeit ausgeben
    GUICtrlSetData($debug,"Zeit[s]: " & @TAB & $zeit /(1000/$Refreshrate) & @TAB & "Verlorene Daten: " & $LostData) ; zur Info die aktuelle Zeit und die verlornen Datenpakete angeben
    ExitLoop
    EndIf
    Next

    [/autoit][autoit][/autoit][autoit]

    return Random(0,$FLGheight) ; hier muss jeder selber wissen wie er seinen IST-Wert berechnet. von mir als Beispiel Zufallszahlen

    [/autoit][autoit][/autoit][autoit]

    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit]