Mein Projekt: Formelparser und Funktionsplotter

  • VB.NET
  • .NET 4.0

SSL ist deaktiviert! Aktivieren Sie SSL für diese Sitzung, um eine sichere Verbindung herzustellen.

Es gibt 52 Antworten in diesem Thema. Der letzte Beitrag () ist von Carbonunit.

    Richtig, erstmal mach ich das nur für mich, will dabei natürlich sinnvollen Code bauen, der auch unter anderen Bedingungen nicht sofort auf die Nase fällt. Aber eine Vermarktung oder Weitergabe ist vorerst nicht geplant.
    Also ich werde da mal ein bisschen herumprobieren. Erstmal die Daten eines Graphen berechnen und in einer Tabelle speichern, das kann man ja immer brauchen. Dann aus dieser Tabelle im Paint-Event zeichnen, ausprobieren wie das mit der Backgound-Image-Eigenschaft funktioniert, wenn ich zuerst in eine Datei zeichne und die dann lade oder eben das MS-Chart-Control nehmen, wobei das meiner Faulheit wohl am ehesten entgegen kommt. Eigentlich will ich die Zeichnung machen und gestalten, aber nicht dauernd den technischen Kram, dass die auch nicht plötzlich wieder verschwindet, richtig skaliert beim Resize etc. Wenn sich darum jemand im Hintergrund kümmert, ist mir das schon recht.

    Carbonunit schrieb:

    ...aber nicht dauernd den technischen Kram, dass die auch nicht plötzlich wieder verschwindet, richtig skaliert beim Resize etc. Wenn sich darum jemand im Hintergrund kümmert, ist mir das schon recht.
    dann nimm Ms-Chart.
    Das ist ein Standard, und ist äusserst leistungsfähig.

    Wenn du allerdings dich in OwnerDrawing-Grundlagen vertiefen willst, dann bastel halt selbst was. Ist auch schön, sich mit Matrix und GraphicsPath auszukennen.
    Macht aber Arbeit, und wird immer nur ein Spezialfall sein (und evtl. sogar punktuelle Vorteile gegenüber MsChart rausholen können).

    Ach guck - hier hab ich sogar ein': activevb.de/cgi-bin/tippupload…4/Ownerdrawn_ChartControl

    Es zeichnet nicht...

    Hallo, ich will zunächst die drei Möglichkeiten, also Paint-Event, in Datei zeichnen und als Backgroundimage laden und MS-Chart Control mal ausprobieren. Nur dafür müßte es wenigstens irgendwas zeichnen. Das tuts aber nicht. Im Folgenden der Code, von dem ich denke, dass er fürs Zeichnen im Paint-Event relevant ist. Sicher fehlt noch irgendwas, nur ich weiß nicht was.

    Dieser Code wird aufgerufen, wenn der Menuepunkt Funtion in neuem Fenster zeichnen gewählt wird:

    VB.NET-Quellcode

    1. Private Sub mnuDrawNewWindow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuDrawNewWindow.Click
    2. Dim blnGetOn As Boolean = False
    3. Dim frmNewChild As frmDraw
    4. blnGetOn = OpenScaleEdit() ' Dialog zur Definition des Koordinatenkreuzes
    5. If blnGetOn Then
    6. ' Neues Fenster anzeigen
    7. frmNewChild = AddNewWindow(gobjFunctions.GetName)
    8. With frmNewChild
    9. ' Koordinatenkreuz festlegen und Zeichnen
    10. .DefineXAxis(frmDefineScale.X_AxisStart, frmDefineScale.X_AxisEnd, frmDefineScale.X_AxisTicks)
    11. .DefineYAxis(frmDefineScale.Y_AxisStart, frmDefineScale.Y_AxisEnd, frmDefineScale.Y_AxisTicks)
    12. .SetScale() ' Maßstabsfaktoren berechnen
    13. .DrawScale() ' Koordinazenkreuz zeichnen
    14. blnGetOn = .CalcNewGraph() ' Graphen berechnen
    15. End With
    16. gobjFunctions.ShowFunction()
    17. End If
    18. End Sub

    frmDraw ist eine Form ohne Rahmen, die als eiziges Steuerelement eine Picturebox hat (picDraw), welche die komplette Fläche der Form einnimmt und auf der eigentlich die Zeichnung zu sehen sein soll.

    Dieser Code berechnet und zeichnet das Koordinatenkreuz:

    VB.NET-Quellcode

    1. Friend Sub DrawScale()
    2. Dim ptStart_XAxis As System.Drawing.Point
    3. Dim ptEnd_XAxis As System.Drawing.Point
    4. Dim ptStart_YAxis As System.Drawing.Point
    5. Dim ptEnd_YAxis As System.Drawing.Point
    6. Dim penScale As System.Drawing.Pen = Pens.Black
    7. Dim intXAxisY As Integer = 0
    8. Dim intYAxisX As Integer
    9. ' Wo liegt die X-Achse?
    10. With mtypAxisX
    11. If CBool(.dblStart < 0.0) And CBool(.dblEnd > 0.0) Then ' Von Minus bis Plus
    12. ' Y-Achse irgendwo in der Mitte
    13. intYAxisX = Math.Abs(CInt(.dblStart * mdblFactorX))
    14. ElseIf CBool(.dblStart < 0.0) And CBool(.dblEnd <= 0.0) Then ' Nur negagtive Seite
    15. ' Y-Achse liegt ganz rechts
    16. intYAxisX = picDraw.Width
    17. ElseIf CBool(.dblStart >= 0.0) And CBool(.dblEnd > 0.0) Then ' Nur positive Seite
    18. ' Y-Achse liegt ganz links
    19. intYAxisX = 0
    20. End If
    21. End With
    22. ' Wo liegt die Y-Achse?
    23. With mtypAxisY
    24. If CBool(.dblStart < 0.0) And CBool(.dblEnd > 0.0) Then ' Von Minus bis Plus
    25. ' X-Achse irgendwo in der Mitte
    26. intXAxisY = Math.Abs(CInt(.dblStart * mdblFactorY))
    27. ElseIf CBool(.dblStart < 0.0) And CBool(.dblEnd <= 0.0) Then ' Nur negagtive Seite
    28. ' X-Achse liegt ganz oben
    29. intXAxisY = 0
    30. ElseIf CBool(.dblStart >= 0.0) And CBool(.dblEnd > 0.0) Then ' Nur positive Seite
    31. ' X-Achse liegt ganz unten
    32. intXAxisY = picDraw.Height
    33. End If
    34. End With
    35. ' X-Achse definieren
    36. ptStart_XAxis.X = 0
    37. ptStart_XAxis.Y = intXAxisY
    38. ptEnd_XAxis.X = picDraw.Width
    39. ptEnd_XAxis.Y = intXAxisY
    40. ' Y-Achse definieren
    41. ptStart_YAxis.X = intYAxisX
    42. ptStart_YAxis.Y = 0
    43. ptEnd_YAxis.X = intYAxisX
    44. ptEnd_YAxis.Y = picDraw.Height
    45. With Me.picDraw.CreateGraphics
    46. .DrawLine(penScale, ptStart_XAxis, ptEnd_XAxis)
    47. .DrawLine(penScale, ptStart_YAxis, ptEnd_YAxis)
    48. End With
    49. End Sub

    Wenn ich unmittelbar nach dessen Ausführung einen Breakpoint setze und die VB-IDE minimiere, so daß mein Programm im Vordergrund ist, liegt das Koordinatenkreuz auch genau da, wo es hin soll.

    Dieser Code berechnet den Graphen für die vorgegebene X-Achse und ruft Invalidate auf, um das Paint-Event auszulösen:

    VB.NET-Quellcode

    1. Friend Function CalcNewGraph() As Boolean
    2. Dim blnSuccess As Boolean = False
    3. Dim dblStart As Double = mtypAxisX.dblStart ' Gleichzeitig auch X-Offset
    4. Dim dblEnd As Double = mtypAxisX.dblEnd
    5. Dim dblYOffset As Double = mtypAxisY.dblEnd
    6. Dim dblX As Double = dblStart
    7. Dim dblY As Double = 0.0
    8. Dim intSteps As Integer = GetSteps()
    9. Dim dblAxisLen As Double = Math.Abs(dblEnd - dblStart)
    10. Dim dblStep As Double = dblAxisLen / intSteps
    11. Dim ptResult As System.Drawing.Point
    12. Dim intError As Integer = ValfDeclares.ERR_SUCCESS
    13. Dim intProgressBarLen As Integer = FPlotMain.gobjFunctions.ProgressbarMax - FPlotMain.gobjFunctions.ProgressbarMin
    14. Dim dblProgressFactor As Double = CDbl(dblAxisLen / intProgressBarLen)
    15. Do
    16. ' Berechnen
    17. intError = FPlotMain.gobjFunctions.ActiveFunction.Calculate(dblY, dblX)
    18. blnSuccess = CBool(intError < ValfDeclares.ERR_CRITICAL) ' Keine kritischen Fehler aufgetreten?
    19. If blnSuccess Then
    20. If CBool(intError = ValfDeclares.ERR_SUCCESS) Then
    21. ' Nur, wenn auch keine mathematischen Fehler aufgetreten sind,
    22. ' wie Funktion nicht definiert oder Disision durch Null, dann zeichnen
    23. ' Punkt in der Grafik berechnen und speichern
    24. ' Offset berechnen, Nullpunkt oben links
    25. ptResult.X = CInt((dblX - dblStart) * mdblFactorX)
    26. ptResult.Y = CInt((dblY + dblYOffset) * mdblFactorY)
    27. mlistGraph.Add(ptResult)
    28. End If
    29. dblX += dblStep ' Nächster X-Wert
    30. FPlotMain.gobjFunctions.SetProgressbar(CInt(Math.Abs(dblX / intProgressBarLen * dblProgressFactor)))
    31. End If
    32. Loop Until CBool(dblX > dblEnd) Or Not blnSuccess
    33. Me.picDraw.Invalidate()
    34. Return blnSuccess
    35. End Function


    Das Paint-Event wird auch gefeuert. Darin wird das Koordinatenkreuz nochmal gezeichnet und die berechneten Punkte sollen als Graph ausgegeben werden:

    VB.NET-Quellcode

    1. Private Sub picDraw_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles picDraw.Paint
    2. ' Hier wird gezeichnet
    3. Dim intIdx As Integer = 0
    4. Dim penGraph As System.Drawing.Pen = Pens.Black
    5. ' Koordinatenkreuz
    6. DrawScale()
    7. ' Graphen zeichnen
    8. For intIdx = 1 To mlistGraph.Count - 1
    9. Me.picDraw.CreateGraphics.DrawLine(penGraph, mlistGraph(intIdx - 1), mlistGraph(intIdx))
    10. Next intIdx
    11. End Sub


    Wenn ich unmittelbar danach einen Breakpoint setze und die IDE minimiere, ist das Koordinatenkreuz immer noch da, aber vom Graphen ist nichts zu sehen. Läuft das Programm normal weiter, ist die ganze Fläche weiß, also auch das Koordinatenkreuz verschwindet wieder.

    Was genau fehlt da noch? Welche Informationen braucht ihr ggf. noch?

    Gruß
    Carbonunit
    wie ich sehe - mein gegebener Link hat dich ja nicht weiter beeinflusst.

    versuch 2 Dinge zu beherzigen:
    1. Vergiss Control.CreateGraphics()! nur mit dem Graphics, was dir im Paint-Event gegeben wird zeichnen!!
    2. Nur im Paint-Event zeichnen und evtl. in Methoden, die du von da aus aufrufst
      Alles woanders gezeichnete Zeug ist nicht persistent und verschwindet bei Gelegenheit wieder - haste nun ja gesehen.
    Ich hab auch ein OwnerDrawing-Tut gemacht, wo die ganze Bandbreite erläutert wird (und bei einem Chart ist die ganze Bandbreite zu kennen leider erforderlich):
    OwnerDrawing



    Wie gesagt: Die Thematik ist sehr schwierig, und an die Qualität des ChartControls (was Beschriftung, Skalierung, Maus-Interaktion etc angeht) ist von uns Hobby-Programmierern eh nicht heranzukommen.

    Es mag unkreativ erscheinen, aber besseres Programmieren, und auch besseres Programmieren Lernen ist, professionelle Infrastruktur richtig benutzen zu lernen, anstatt Selbstgebasteltes in die Welt zu setzen.

    ErfinderDesRades schrieb:

    wie ich sehe - mein gegebener Link hat dich ja nicht weiter beeinflusst.

    Der Link aus #43? Hab ich ehrlich gesagt nicht so richtig verstanden...

    ErfinderDesRades schrieb:

    Ich hab auch ein OwnerDrawing-Tut gemacht

    Das sieht imteressant aus, damit komme ich wohl weiter.

    ErfinderDesRades schrieb:

    Es mag unkreativ erscheinen, aber besseres Programmieren, und auch besseres Programmieren Lernen ist, professionelle Infrastruktur richtig benutzen zu lernen, anstatt Selbstgebasteltes in die Welt zu setzen.

    Das ist natürlich richtig, aber ich will wenigstens einmal sehen wie das geht mit dem Ownerdrawing. Dann kann ich mich besser entscheiden, welchen Weg es weiter geht.

    faxe1008 schrieb:

    Öhm das CBool kannste dir sparen, ein Vergleich erzeugt immer einen boolean als Resultat.

    Auch richtig, aber das wird doch sicher wegoptimiert? Oder nicht?
    Ist so ein bisschen wie das Blinken beim Autofahren. Das habe ich mir bei jedem Richtungswechsel angewöhnt, egal ob auch jemand da ist, der es sieht oder nicht.

    Ergänzung: Mein Koordinatenkreuz bleibt schon mal erhalten, Graph erscheint gar nicht, da stimmt möglicherweise auch was mit den Koordinaten nicht... Es geht voran, muss erstmal etwas die Architektur umbauen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Carbonunit“ ()

    Carbonunit schrieb:

    Der Link aus #43? Hab ich ehrlich gesagt nicht so richtig verstanden...
    ah - ok - hihi.
    Das ist in diesem Fall garnet so falsch, weil dann hast du verstanden, dasses verdammt kompliziert werden wird.

    Carbonunit schrieb:

    aber ich will wenigstens einmal sehen wie das geht mit dem Ownerdrawing.
    ok - dagegen ist nichts einzuwenden - ging mir ja nicht anders.



    Carbonunit schrieb:

    ...CBool ...
    Auch richtig, aber das wird doch sicher wegoptimiert? Oder nicht?
    Ist so ein bisschen wie das Blinken beim Autofahren. Das habe ich mir bei jedem Richtungswechsel angewöhnt, egal ob auch jemand da ist, der es sieht oder nicht.
    Nein, hier kann Autofahrer-Sicherheits-Denke nicht auf Programmierer-Denke übertragen werden.
    Beim Autofahren darf kein Unfall passieren, aber beim Proggen sollen Exceptions auftreten!
    Wenn Exceptions auftreten dürfen, wo immer Mist gecodet ist, dann ist die Wahrscheinlichkeit exorbitant höher, dass kein Mist gecodet ist - wenn sie nicht auftreten.
    An einer Exception stirbt niemand - sie tut nicht einmal weh. Sondern daran verbesserst du dein Programm.

    Und Typumwandlungen jeder Art sind sowieso mit äusserster Vorsicht zu behandeln - und wo immer geht zu vermeiden. Weil jede Typumwandlung deaktiviert lokal die Compiler-Typ-Kontrolle.

    VB.NET-Quellcode

    1. 'Also an dieser Stelle:
    2. Loop Until CBool(dblX > dblEnd)
    3. 'könnte auch stehen:
    4. Loop Until CBool("dblX > dblEnd")
    5. 'und der Compiler würde es durchlassen
    6. 'aber das hier:
    7. Loop Until "dblX > dblEnd"
    8. 'lässt er nicht durch, sondern sagt dir gleich, dasses Unfug ist.
    9. '(Option Strict vorrausgesetzt)

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Hallöchen,

    hier das erste Beweisfoto:


    Da ist natürlich noch längst nichts in einem vorzeigbaren Zustand, aber die Sache mit dem Ownerdrawing funktioniert schon mal.
    Jeder Speicherplatz meines Funktionsspeichers enthält jetzt noch zwei Listen. Einmal die Rohdaten mit den x- und y-Werten, die sich aus der Berechnung ergeben.
    Dann eine zweite, gleichgroße Liste mit System.Drawing.Point-Objekten. Aus dieser Liste zeichne ich im Paint-Event der Picturebox meine Kurve. Die wird jedes Mal neu berechnet, wenn sich die Größe des Fenster geändert hat. Die Faktoren und Offset-Werte für x und y errechne ich im Resize-Event.
    Das hat den schönen Nebeneffekt, dass sich mein Bild immer schön der Fenstergröße anpaast. Noch geht das alles auch sehr schnell, mal sehen, was passiert, wenn mehrere Kurven auf einer Picturebox sind.
    Eine Baustelle sind natürlich noch die Skalenstriche für das Koordinatenkreuz und so einige Menuepunkte, bei denen noch gar nichts passiert. Aber eine wichtige Klippe ist umschifft.

    Gelernt habe ich heute auch wieder was. So ein Konstrukt ist Mist (Pseudocode, VB-ähnlich):

    VB.NET-Quellcode

    1. With MyListOfObjects(Index)
    2. for Index = 0 to MyListOfObjects.count-1.
    3. If IsNumeric(.SomeTextFromMyObject) Then
    4. DoSomething()
    5. End If
    6. next Index
    7. end with

    Da hat mein Code bestimmte Dinge einfach nicht gemacht, obwohl der Debugger angezeigt hat, dass SomeTextFromMyObject den zum Index passenden Wert hatte. Tatsächlich übergeben wurde aber ein anderer Wert.
    Beginn und Ende des With-Blocks gehören IN die For-Schleife, dann gehts. Das IsNumeric kein .NET ist, weiß ich jetzt auch, wird noch angepasst.

    Zu den CBool-Funktionen beim If: Ja, ihr habt recht, da bemerke ich Fehler sicher schneller, wenn es dann kracht. Schmeiß ich wieder aus, wo ich das sehe.

    Gruß
    Carbonunit

    ErfinderDesRades schrieb:

    deaktiviere den MVB-Namespace-GeneralImport!

    Ja, muss ich noch machen, das gibt im Moment über 80 Fehler, meistens bei so Sachen wie Mid, Len, Instr oder den nicht sichtbaren Zeichen wie vbCrLf. Wenn ich mal wieder Lust zur öden Überarbeitung habe, wird das erledigt. Oder erstmal Dateiweise. Also Generalimport deaktivieren und überall Import Microsoft.VisualBasic drüberschreiben. Sind zum Glück nicht mal alle Dateien des Projekts, wo das drinsteckt.
    So habe ich das mit Option Strict On gemacht, da gibt es noch zwei Stellen, wo es ohne die Einstellung hakt. Ich schäm mich ja auch... ;)
    Das hat aber alles einen TODO-Kommentar und steht damit in der Überarbeitungsliste.

    CKextreme schrieb:

    Und ich dachte immer als "erfahrener" Informatiker macht man gleich alles richtig..
    Ehrlich gesagt hätte ich mehr nach der Vorstellung deiner Person erwartet.


    Niemand macht immer alles richtig.
    Ich bin seit sieben Jahren aus dem Job raus und VB.NET war in den letzten Jahren bis 2010 auch nicht mehr meine Hauptaufgabe. Dafür klappt es aber immer noch ganz ordentlich.
    Im Support, dazu noch in der Höhle des Löwen (Microsoft) ging es vor allem darum, dass die Leute ihr Zeug ans Fliegen bekommen haben, da war es erstmal egal, ob sie das mit .NET oder dem alten Zeug gemacht haben. Schön programmiert wurde da nicht, eher effizient im Sinne von "Schnell das Problem lösen".
    Das war in den letzten Jahren nur noch Geschäft, da musstest du so viele Cases (Anfragen) wie möglich wegschaffen.
    Jetzt kann ich endlich wieder mal pfriemeln und basteln.