Graph aus Messdaten zeichnen

Linz Erich

Grünschnabel
Hi Leute,

Ich programmiere gerade mit Visual Studio 2005 eine Meßsoftware, die Messdaten von einem Messgerät erfasst und in eine .txt Datei schreibt. In der Datei stehen dann lauter Wertepaare. Jedes Tupel würde einem Punkt im x,y - Koordinatensystem entsprechen.
Auf meiner Benutzeroberfläche existiert ein button "Graph zeichnen". Jetzt würde ich gerne diese Punkte - sobald der button geklickt wurde - in ein Koordinatensystem zeichnen und durch Geraden verbinden lassen.
Ich hab schon das Internet durchforstet und nach entsprechenden Tools gesucht... Leider ohne viel versprechende Ergebnisse.

Ich nehme an, dass ich irgendeine Bibliothek runterladen und verlinken muss... :confused:

Vielleicht hat jemand von euch eine Idee, wie sich mein Problem relativ leicht lösen lässt. Würde mich über Antworten freuen.
Vielen Dank im Voraus.

Gruß Linzer
 
Es gibt bestimmt ein paar entsprechende Bibliotheken für sowas, aalerdings kenne ich selber keine.
Wenn es nur darum geht, einen Graphen zu zeichnen, der verschiedene Koordinaten linear verbindet, dann brauchst du eigentlich keine extra Bibliothek. Das ist mit wenig Aufwand mit CDC::MoveTo(...) und CDC::LineTo(...) zu realisieren. Du mußt nur berücksichtigen, daß die Y-Koordinaten im Windows-Fenster umgekehrt laufen als in einem normalen Koordinatensystem. Wenn Du Dann noch die Achsen zeichnen willst, mußt Du die Fensterkoordinaten der Achse als Offset berücksichtigen und wenn Du die Größe des Graphen an die Fenstergröße anpassen willst, mußt du das Verhältnis zwischen dem maximalen Signalabstand (MAX-Wert - MIN-Wert) und der Fensterhöhe (abzüglich Achsen) ausrechnen und zur Berechnung der Fensterkoordinaten aus den Signalkoordinaten verwenden.
Hier ist ein kleines Beispiel, wie ich das mal gemacht habe. Es ist bei weitem nicht perfekt, aber für meine damaligen Zwecke hat's gereicht :).
C++:
    CDC *pPicDC;
    CPen *pPen = new CPen(PS_SOLID, 2, RGB(0, 0, 0));
    int iCounter;
    float fStep;
    CHANNEL_HANDLE hChannel;                // <-- das wirst Du nicht brauchen, da Deine Meßwerte woanders herkommen

    LOGBRUSH logbrush;
    CBrush brushErase;
    CBrush brushPaint;
    CRect rectPaint;
    CRgn rgnPaint;
    CRect rectWinRect, rectPictureRect, rectPicCoord;

    //... anderer Code ...

    pPicDC = m_staticGraphOut.GetDC();        // <-- hier den DC Deines Fensters ermitteln!!
    m_staticGraphOut.GetClientRect(&rectPaint);
    rgnPaint.CreateRectRgnIndirect(&rectPaint);
    pPicDC->SelectClipRgn(&rgnPaint);
    pPicDC->SetBkColor(RGB(255, 255, 255));
    pPicDC->SetBkMode(TRANSPARENT);
    pPicDC->SetTextAlign(TA_LEFT | TA_BASELINE);

    logbrush.lbStyle = BS_SOLID;
    logbrush.lbColor = RGB(255, 255, 255);
    brushErase.CreateBrushIndirect(&logbrush);
    logbrush.lbStyle = BS_HOLLOW;
    brushPaint.CreateBrushIndirect(&logbrush);

    m_staticGraphOut.GetWindowRect(&rectPictureRect);
    GetWindowRect(&rectWinRect);
    rectPicCoord.left = (rectPictureRect.left - rectWinRect.left) - 1;
    rectPicCoord.top = (rectPictureRect.top - rectWinRect.top) - 1;
    rectPicCoord.right = (rectPictureRect.right - rectWinRect.left) - 3;
    rectPicCoord.bottom = (rectPictureRect.bottom - rectWinRect.top) - 3;

    //Zeichenfläche löschen
    pPicDC->SelectObject(&brushPaint);
    pPicDC->FillRect(rectPaint, &brushErase);

    //Achsen zeichnen
    pPicDC->SelectObject(pPen);
    pPicDC->SetMapMode(MM_TEXT);

    pDC->SelectObject(pPen);
    pDC->SetMapMode(MM_TEXT);

    //Y-Achse
    fStep = (float)rectPicCoord.Height() / (float)Y_AXIS_DIFF;
    for(iCounter = 0; iCounter < Y_AXIS_DIFF; iCounter++)
    {
        pDC->MoveTo(rectPicCoord.left - 5, rectPicCoord.top + (int)((float)iCounter * fStep));
        pDC->LineTo(rectPicCoord.left, rectPicCoord.top + (int)((float)iCounter * fStep));
    }
    pDC->MoveTo(rectPicCoord.left - 5, rectPicCoord.bottom);
    pDC->LineTo(rectPicCoord.left, rectPicCoord.bottom);
    
    //X-Achse
    fStep = (float)rectPicCoord.Width() / (float)X_AXIS_DIFF;
    for(iCounter = 0; iCounter < X_AXIS_DIFF; iCounter++)
    {
        pDC->MoveTo(rectPicCoord.left + (int)((float)iCounter * fStep), rectPicCoord.bottom);
        pDC->LineTo(rectPicCoord.left + (int)((float)iCounter * fStep), rectPicCoord.bottom + 5);
    }
    pDC->MoveTo(rectPicCoord.right, rectPicCoord.bottom);
    pDC->LineTo(rectPicCoord.right, rectPicCoord.bottom + 5);
    
    //Kurven
    m_staticGraphOut.GetClientRect(&rectPictureRect);
    pPicDC->SetViewportOrg(0, rectPaint.bottom / 2);

    hChannel = m_pDoc->GetFirstChannelHandle();    // <-- jeder Channel ist ein Meßkanal
    while(hChannel != NULL)
    {
        int iVal, iMaxVal, iMinVal, iCenter, iDiff;
        float fXPos = 0.0f;
        BOOL bHasValue;
        CString Name;

        pPen->DeleteObject();
        pPen->CreatePen(PS_SOLID, 1, m_pDoc->GetChannelColor(hChannel));

        pPicDC->SelectObject(pPen);

        m_fXStep = (float)rectPaint.Width() / (float)m_pDoc->GetChannelBufferSize(hChannel);
        iMaxVal = m_pDoc->GetChannelMaxValue(hChannel);
        iMinVal = m_pDoc->GetChannelMinValue(hChannel);
        iCenter = (iMaxVal + iMinVal) / 2;
        iDiff = iMaxVal - iMinVal;
        m_fYStep = (float)rectPaint.Height() / (float)iDiff;
        if(iMinVal == 0) iMinVal = (int)(-1.0 / m_fYStep);
        pPicDC->SetViewportOrg(0, (int)((float)rectPaint.Height() + (float)iMinVal * m_fYStep));
        bHasValue = m_pDoc->GetFirstChannelValue(hChannel, iVal);
        //1. Punkt
        if(bHasValue)
        {
            pPicDC->MoveTo(0, (int)-((float)iVal * m_fYStep));
            bHasValue = m_pDoc->GetNextChannelValue(hChannel, iVal);
        }
        //weitere Punkte
        while(bHasValue)
        {
            fXPos += m_fXStep;        / <-- konstanter Abstand in X-Richtung
            pPicDC->LineTo((int)fXPos, (int)-((float)iVal * m_fYStep));
            bHasValue = m_pDoc->GetNextChannelValue(hChannel, iVal);
        }

        hChannel = m_pDoc->GetNextChannelHandle(hChannel);
    }
    
    m_staticGraphOut.ValidateRect(NULL);
    delete pPen;

Anmerkungen:
- Die Konstanten X_AXIS_DIFF und Y_AXIS_DIFF beim Zeichnen der Achsen bezeichen die Anzahlen der Achsenmarkierungen
- Ich zeichne den Graphen in einem CStatic-Fenster. Da Du wahrscheinlich einen anderen Fenstertyp verwendest, mußt Du die Zeile anpassen, in der der DC des Fensters ermittelt wird. Vielleicht zeichnest Du auch erst in einen MemDC und kopierst diesen dann in Dein Fenster.
- Meine Daten sind im CDocument der Applikation in 'Channels' gespeichert. Da das bei Dir wahrscheinlich auch anders implementiert ist, mußt Du diesen Teil auch anpassen. Ich denke aber, die Namen der funktionen zum Holen der Meßwerte im Codebeispiel sind selbsterklärend.
- Hier wird nur der Graph selber gezeichnet. Wenn Du die Meßpunkte markieren willst, dann mußt Du das noch implementieren.
- Da meine X-Abstände konstant sind, habe ich dafür keine Werte. Wenn das bei Dir anders ist, mußt du die X-Koordinaten ebenfalls berechnen.

Noch ein Tip: Du kannst eine Fensterklasse CGraphWindow von CStatic (Fensterstil SS_BITMAP) ableiten. Dann kannst Du das Zeichnen der Graphen in der (überschriebenen) OnPaint-Methode erledigen.
 
Hey, Danke für die Antwort...

Ich hab aber vorhin was viel versprechendes im Internet gefunden. Es funktioniert sogar. :) ^^
Für alle die es interessiert oder auch mal etwas ähnliches programmieren müssen: DISLIN ist dafür geeignet: http://www.dislin.de
Es gibt dort auch entsprechende Beispielcodes. Einfach installieren und die dll einbinden...

@ jokey2: Nochmals vielen Dank. Wenn ich Zeit habe, werde ich mal deine Methode ausprobieren.

Gruß Linzer
 

Neue Beiträge

Zurück