[MultiGraph UDF] - dynamischer GDI+ LineGraph (als Software-Oszilloskop geeignet)

  • Hi Leute,

    ich stelle euch meine kleine UDF vor. "MultiGraph" ist wie der Name schon sagt ein Graph, mit dem sich Abläufe zeitlich darstellen lassen. Zum Plotten der Werte wird GDI+ verwendet, wobei auch hohe FPS-Raten erreicht werden können. Gute CPUs (Intel i7) können bis zu 15000 Werte pro Sekunde plotten (bei 660 Werten/Oszillogramm -> ca. 22 FPS), Leistungsschwache CPUs (z.B. Intel CoreDuo) schaffen immerhin noch >5500 Werte pro Sekunde (ca. 8-9 FPS). Damit wäre es von der Darstellungsgeschwindigkeit als Osziloskop ausreichend geeignet.

    Der Graph bietet verschiedene Darstellungsmodis und die Achsbeschriftungen werden dynamisch angepasst.

    sonstige Funktionen:

    - aktivierbares Antialiasing
    - Interpolation der Darstellung
    - Anpassung der Auflösung
    - 10 verschiedene Graphen mit jeweils 10 Kanälen erstellbar (notfalls kann man die Anzahl auch in der UDF einfach erhöhen)
    - Anpassung der Linienbreite/farbe/Tranzparenz


    Aktuelle UDF-Version (Autoit): 1.0.0.3

    16.03.2013 - 1.0.0.3 - behoben: die Hintergrundfarbe der Achsenbeschriftung wurde nicht komplett übernommen
    07.03.2013 - 1.0.0.2 - neue Funktion: _MG_Graph_optionen_position () -> verschiebt einen Graphen in der GUI oder passt die Größe neu an
    06.03.2013 - 1.0.0.1 - behoben: geplottete Werte an der ersten Position des Graphen waren fehlerhaft


    Aktuelle UDF-Version (PureBasic): 1.0.0.0

    23.05.2013 - 1.0.0.0 - Fertigstellung, verbesserte Funktionalität (gegenüber AutoIt)

    hier gibt es noch die Performancestarke PureBasic-Version: HIER

    --------------------------------------------------------------------------------------------------
    zur PureBasic-Version: die PureBasic-Version bietet mehr Optionen und ist um das 80-100 fache Leistungsstärker.
    Die AutoIt-Version schafft ca. 5.000 Werte/sek (bei ca. 7-10 FPS)
    Die PureBasic-Version schafft ca. 500.000 Werte/sek (bei ca. 300-900 FPS) und eignet sich daher zur Echtzeitdarstellung von Zeitverläufen.
    zusätzliche Funktionen: 4 Quadranten-Ansicht, Hilfslinien zum Messen, dynamische Berechnung der Achsenbeschriftung
    --------------------------------------------------------------------------------------------------


    aktuelle Funktionen der AutoIt-Version:

    _MG_Graph_erstellen
    _MG_Graph_optionen_position
    _MG_Graph_optionen_allgemein
    _MG_Graph_optionen_Rahmen
    _MG_Graph_optionen_Hauptgitterlinien
    _MG_Graph_optionen_Hilfsgitterlinien
    _MG_Graph_optionen_Plottmodus
    _MG_Kanal_optionen
    _MG_Graph_initialisieren
    _MG_Wert_setzen
    _MG_Graph_updaten
    _MG_Graph_clear
    _MG_Graph_Achse_links
    _MG_Graph_Achse_rechts
    _MG_Graph_Achse_unten
    _MG_Graph_Achse_unten_update
    _MG_Graph_Achse_links_update
    _MG_Graph_Achse_rechts_update
    _MG_Graph_plotten
    _MG_Graph_entfernen


    Beispiel 1:

    oben: 2 Kanäle (sin und tan-Funktion) mit Antialiasing und Interpolation
    unten: scrolling-Graph ohne Antialiasing und Interpolation
    [Blockierte Grafik: http://s14.directupload.net/images/130202/fgl6c9zt.jpg]


    Beispiel 2:

    Achtung bunt!! :D
    Dies soll eigentlich nur die Einstellungsmöglichkeiten der einzelnen Elemente darstellen
    [Blockierte Grafik: http://s14.directupload.net/images/130202/b3fsgrvv.jpg]


    Beispiel 3:
    ...leicht erweiterter Graph. Hier kann man mal "Live" die einige Einstellungen ändern um zu schauen wie die UDF arbeitet.
    Die GUI ist skallierbar! d.h. Der Graph ändert die größe dynamisch zur GUI-Größe. Die Achsen werden dynamisch angepasst.
    [Blockierte Grafik: http://s14.directupload.net/images/130202/3dy3zy2s.jpg]


    ich hoffe, dass es eventuell ein paar User glücklich macht.

    lg
    SBond

  • Also für ein AutoIt Neuling ist das Resultat schon beachtlich! :thumbup:

    Beispiel3 zeigt bei mir um die 11 FPS an.

    Wenn du das Flimmern beim Ändern der Slider Position noch wegbekommen würdest, wäre das perfekt! ;)

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • ...ja das muss ich noch ändern. Der Rahmen ist nämlich ein Label :D
    Das bringt das ganze zum Flackern. Ich wusste schon irgendwie am Anfang, dass es eine dumme Idee war. :D

    ich werde es demnächst mal korrigieren ^^

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

    • Offizieller Beitrag

    Erst mal Willkommen "Machdeborjer" :D ,wohne ca. 50 km von dir entfernt.

    Ich bin eher der "Non"-Grafik Typ, hatte mich aber mal an was Ähnlichem versucht. Daher kann ich aus Erfahrung sagen: :thumbup:
    Dein Werk gefällt mir und ich hoffe, dass wir noch weitere schöne Skripte von dir sehen werden.

  • omg :D

    naja... ich wohne wegen meinem Studium seit 3 Jahren in Magdeburg :) Akzent habe ich noch nicht... hoffe ich... obwohl :D

    Dein Living Graph hatte ich auch schon mal zerpflückt. Ich musste mir einfach alles anschauen, was ich so im Netz gefunden hatte. Nun ist es ja doch eine eigene UDF geworden.
    Ich werde die aber noch erweitern, so dass man z.B. auch mit vier Quadranten gut arbeiten kann.

    ..da habe ich gleich noch eine Frage...

    Ich wollte eigentlich mit GUIRegisterMsg() prüfen, ob das Mausrad verwendet wurde, um so z.B. den Graphen zu zoomen. Das würde prinzipiell auch gehen, aber ein Nachrichtencode kann nur eine Funktion ansprechen. Wenn jemand die UDF verwenden würde und für sein Programm auch diesen Nachrichtencode benötigt, dann würde es zum Konflikt kommen.

    also das...

    [autoit]


    GUIRegisterMsg(WM_MOUSEWHEEL, "Funktion_1")
    GUIRegisterMsg(WM_MOUSEWHEEL, "Funktion_2")

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

    Func Funktion_1()
    ...
    EndFunc

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

    Func Funktion_2()
    ...
    EndFunc

    [/autoit]

    ...würde nicht funktionieren. In diesem Fall wird nur Funktion_2() ausgeführt. Gibt es da eine andere Möglichkeit?

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

    • Offizieller Beitrag

    Gibt es da eine andere Möglichkeit?


    Ja, das ist machbar. Dazu fängst du mit einer eigenen Prozedur die Nachricht ab, verarbeitest diese und reichst sie dann an das System weiter (oder verwirfst sie).
    Knackpunkt ist hier die Verarbeitungsdauer. Wenn es zu lange dauert entstehen unvorhersehbare Systemzustände. Also immer so schnell, wie möglich sein - User-Interaktionen oder Messageboxen sind absolut tabu.
    Hier mal aus meiner Bsp.-Sammlung:

    Spoiler anzeigen
    [autoit]

    #Region - TimeStamp
    ; 2012-05-08 17:52:07
    #EndRegion - TimeStamp

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

    #cs
    WM_ -Message Hook
    - Nachricht wird erst in einer eigenen Funktion ausgewertet
    - dann an das System zurückgegeben (auswertbar mit GUIRegisterMsg)
    - oder verworfen ("Return 0" nach Msg-Auswertung in _WinProc, s. Zeile 78)
    #ce

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

    #include <GuiImageList.au3>
    #include <GuiListView.au3>
    #include <ListViewConstants.au3>
    #include <StructureConstants.au3>
    #include <WinAPI.au3>
    #include <WindowsConstants.au3>

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

    Local Const $GWL_WNDPROC = -4

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

    $hGui = GUICreate("Test", 400, 400)
    $hListView = _GUICtrlListView_Create($hGui, "Listview", 10, 10, 380, 380)
    _GUICtrlListView_SetExtendedListViewStyle($hListView, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT, $LVS_EX_SUBITEMIMAGES))

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

    ; Load images
    $hImage = _GUIImageList_Create()
    _GUIImageList_Add($hImage, _GUICtrlListView_CreateSolidBitMap($hListView, 0xFF0000, 16, 16))
    _GUIImageList_Add($hImage, _GUICtrlListView_CreateSolidBitMap($hListView, 0x00FF00, 16, 16))
    _GUIImageList_Add($hImage, _GUICtrlListView_CreateSolidBitMap($hListView, 0x0000FF, 16, 16))
    _GUICtrlListView_SetImageList($hListView, $hImage, 1)

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

    ; Add columns
    _GUICtrlListView_InsertColumn($hListView, 0, "Column 1", 100)
    _GUICtrlListView_InsertColumn($hListView, 1, "Column 2", 100)
    _GUICtrlListView_InsertColumn($hListView, 2, "Column 3", 100)

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

    ; Add items
    _GUICtrlListView_AddItem($hListView, "Row 1: Col 1", 0)
    _GUICtrlListView_AddSubItem($hListView, 0, "Row 1: Col 2", 1)
    _GUICtrlListView_AddSubItem($hListView, 0, "Row 1: Col 3", 2)
    _GUICtrlListView_AddItem($hListView, "Row 2: Col 1", 1)
    _GUICtrlListView_AddSubItem($hListView, 1, "Row 2: Col 2", 1)
    _GUICtrlListView_AddItem($hListView, "Row 3: Col 1", 2)
    GUISetState()

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

    ;
    $hProc = DllCallbackRegister('_WinProc', 'ptr', 'hwnd;uint;wparam;lparam') ; == hier registriere ich die Funktion "_WinProc" als Callback, Returntype ist "ptr", Parametertypen
    $hHook = _WinAPI_SetWindowLong($hGui, $GWL_WNDPROC, DllCallbackGetPtr($hProc)) ; == hier wird dem Fenster die Adresse der Callback-Prozedur übergeben, diese ersetzt jetzt die Standard-Window-Prozedur

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

    GUIRegisterMsg($WM_NOTIFY, 'WM_NOTIFY')

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

    While GUIGetMsg() <> -3
    Sleep(10)
    WEnd

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

    _WinAPI_SetWindowLong($hGui, $GWL_WNDPROC, $hHook) ; == nach Programmende: originale WinProc wiederherstellen
    DllCallbackFree($hProc)

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

    Func _WinProc($hWnd, $iMsg, $iwParam, $ilParam) ; == die Prozedur soll ausschließlich WM_NOTIFY auswerten
    ; wenn nicht die gewünschte Msg kommt - Rückgabe an System ; == andere Messages werden gleich durchgereicht ans System zur Auswertung
    If $iMsg <> $WM_NOTIFY Then Return _WinAPI_CallWindowProc($hHook, $hWnd, $iMsg, $iwParam, $ilParam)

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

    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView, $tInfo
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)

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

    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
    Case $hWndListView
    Switch $iCode
    Case $LVN_COLUMNCLICK ; A column was clicked ; == Ich möchte nicht, dass diese Msg verwertet werden kann und verwerfe sie
    $tInfo = DllStructCreate($tagNMLISTVIEW, $ilParam)
    ConsoleWrite('MyProc: $LVN_COLUMNCLICK -->Column: ' & DllStructGetData($tInfo, "SubItem") & @CRLF)
    Return 0 ; <== Nachricht wird jetzt verworfen und kann über GUIRegisterMsg nicht verwertet werden

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

    Case $NM_CLICK ; == Diese Msg lasse ich durch ans System
    $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam)
    ConsoleWrite("MyProc: $NM_CLICK " & "-->Index: " & DllStructGetData($tInfo, "Index") & _
    " -->SubItem: " & DllStructGetData($tInfo, "SubItem") & @CRLF)
    EndSwitch
    EndSwitch
    ; jetzt wird die normale WinProc aufgerufen, die man mit GuiRegisterMsg auswerten kann
    Return _WinAPI_CallWindowProc($hHook, $hWnd, $iMsg, $iwParam, $ilParam)
    EndFunc ;==>_WinProc

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

    Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
    Case $hWndListView
    Switch $iCode
    Case $LVN_COLUMNCLICK ; A column was clicked <== Diese Nachricht erreicht die Funktion nicht mehr
    Local $tInfo = DllStructCreate($tagNMLISTVIEW, $ilParam)
    ConsoleWrite('GUIRegisterMsg: $LVN_COLUMNCLICK -->Column: ' & DllStructGetData($tInfo, "SubItem") & @CRLF)
    Case $NM_CLICK ; Sent by a list-view control when the user clicks an item with the left mouse button
    Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam)
    ConsoleWrite("GUIRegisterMsg: $NM_CLICK " & "-->Index: " & DllStructGetData($tInfo, "Index") & _
    " -->SubItem: " & DllStructGetData($tInfo, "SubItem") & @CRLF)
    EndSwitch
    EndSwitch
    Return 0
    EndFunc ;==>WM_NOTIFY

    [/autoit]
  • geil :D

    das werde ich mir nachher mal näher anschauen :)

    vielen Dank

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

  • ..auch etwas späte Antwort....

    Leerzeilen, wie meinst du das?

    Im Code?

    ..im Code lasse ich öfters größere Abstände, damit ich beim Scrollen leichter bestimmte Blöcke oder Bereiche finden kann. Für mich ist es übersichtlicher.

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

  • haha... jo

    ..nutze ich aber seltener ^^

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

  • Update 1.0.0.3

    16.03.2013 - 1.0.0.3 - behoben: die Hintergrundfarbe der Achsenbeschriftung wurde nicht komplett übernommen
    07.03.2013 - 1.0.0.2 - neue Funktion: _MG_Graph_optionen_position () -> verschiebt einen Graphen in der GUI oder passt die Größe neu an
    06.03.2013 - 1.0.0.1 - behoben: geplottete Werte an der ersten Position des Graphen waren fehlerhaft

    und die PureBasic-Version hinzugefügt

    ich bin ein Raspberry Pi, Arduino und ATMega-Fan :D

    Nicht vergessen:
    41 6c 73 6f 20 77 65 6e 6e 20 64 75 20 73 6f 20 76 69 65 6c 20 4c 61 6e 67 65 77 65 69 6c 65 20 68 61 73 74 2c 20 64 61 6e 6e 20 6b 61 6e 6e 73 74 20 64 75 20 61 75 63 68 20 67 6c 65 69 63 68 20 7a 75 20 6d 69 72 20 6b 6f 6d 6d 65 6e 20 75 6e 64 20 61 62 77 61 73 63 68 65 6e 2e

    :thumbup:


    lg
    SBond

  • Bei der Nutzung der UDF V1.0.0.3 in einer eigenen Anwendung, bin ich darauf gestoßen, das der Graph im nur bei jedem 2. Schritt in der GUI aktualisiert (dann aber beide neuen Werte korrekt anzeigt).

    Meiner Meinung nach müssten in der UDF in den Zeilen 311 & 1740, die Variable "iPlotCounter" jeweils mit "1" vorbesetzt werden.

    Damit würde dann bei der Abfrage (in der Funktion "_MG_Graph_plotten", Zeile 1731) zum Plotten, der Graph bei "$iPlotfrequenz = 1" auch in jedem Durchgang gezeichnet.


    Ggf. stellt ja SBond (nach Prüfung) nochmal eine aktualisierte Version hier ein, sonst würde ich meine hier auf Wunsch nochmal anhängen.

    Zur Nutzung dieses Forum's, ist ein Übersetzer für folgende Begriffe unerlässlich:

    "On-Bort, weier, verscheiden, schädliges, Butten steyling, näckstet, Parr, Porblem, scripe, Kompletenz, harken, manuel zu extramieren, geckukt, würglich, excell, acces oder Compilevorgeng"