Stopuhr mit Stopwatch

  • VB.NET

Es gibt 39 Antworten in diesem Thema. Der letzte Beitrag () ist von TH0R.

    Stopuhr mit Stopwatch

    Hi,

    ich bin neu eingestiegen in Visual Basic (2010 Express) und habe mal eine Stoppuhr programmiert. Allerdings bin ich mir mit der Sprache noch nicht so ganz sicher. Meine Stoppuhr funktioniert, aber ich bin mir nicht genau sicher wie das nun mit der Stopwatch Klasse ist.

    Kann ich die so ansprechen oder läuft sie in der Form nur als ungenauer Timer?

    Ich möchte das ganze dann fortsetzen indem bei einem Start einer .wav Datei die Stoppuhr startet. Allerdings will ich erstmal sichergehen, dass es sich um eine Stopwatch Anwendung handelt.

    Danke schonmal

    Hier mein Code:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stopwatch As New Stopwatch
    3. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    4. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    5. Label1.Text = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)
    6. End Sub
    7. Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    8. Me.stopwatch.Start()
    9. btReset.Enabled = False
    10. End Sub
    11. Private Sub btStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStop.Click
    12. Me.stopwatch.Stop()
    13. btReset.Enabled = True
    14. End Sub
    15. Private Sub btReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReset.Click
    16. Me.stopwatch.Reset()
    17. Label1.Text = "00:00:00:000"
    18. ListBox1.Items.Clear()
    19. End Sub
    20. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    21. ListBox1.Items.Add(ListBox1.Items.Count + 1 & " " & Label1.Text)
    22. End Sub
    23. End Class
    Praktisch gesehen funktioniert die Stopwatch so:
    Beim Start wird die aktuelle Zeit gespeichert. Wenn du nun die vergangene Zeit abrufst, wird einfach nur die aktuelle Zeit von der gespeicherten abgezogen. In meinen Programmen nutze ich übrigens auch die Methode: Stopwatch und jede Sekunde einen Refresh. Ein kleiner Tipp:

    VB.NET-Quellcode

    1. ​ListBox1.Items.Add(ListBox1.Items.Count + 1 & " " & Label1.Text)

    Es ist unüblich, Daten von einem Label anzufordern. Ein Label dient dazu, Daten für den Benutzer darzustellen und nicht, um diese zu speichern. Außerdem ist dies dann ungenau, weil die letze Aktualisierung im Worst-Case-Scenario 0,9 Sekunden zurückliegt. Ich würde mir einfach eine Methode schreiben ​GetElapsedTimeString() As String und da einfach den Code reinschreiben, den du auch zur Anzeige beim Label nimmst.
    Mfg
    Vincent

    Willkommen im Forum. :thumbup:

    TH0R schrieb:

    VB.NET-Quellcode

    1. elapsed.Milliseconds
    solltest Du 3 Stellen geben,
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi,

    Danke für die Tipps.
    1. Ich habe das ganze mal umgeschrieben und die ListBox sollte nun die Werte aus der Stopwatch nehmen. Ist das so korrekt? Beim testen sieht man auch jetzt das der Mark wert in der Listbox nicht mehr dem im Label entspricht.
    2. Kann das Label genauso genau sein wie die Stopwatch? oder liegt die Ungenauigkeit an der Aktualisierung des Labels?
    3. Wie genau kann ich den Wert der Milisekunden auf 3 Stellen setzen? Habs probiert aber dann ging die Stopuhr nicht mehr.
    4. Welchen Wert sollte man für den Timer wählen, damit er am genausten ist? (Hab aktuell nen Interval von 1, ist doch 1ms oder?)

    Anbei der Code:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stopwatch As New Stopwatch
    3. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    4. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    5. Label1.Text = String.Format("{0:00}:{1:00}:{2:00}:{3:00}",
    6. Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds,
    7. elapsed.Milliseconds)
    8. End Sub
    9. Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    10. Timer1.Start()
    11. Me.stopwatch.Start()
    12. btReset.Enabled = False
    13. End Sub
    14. Private Sub btStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStop.Click
    15. Timer1.Stop()
    16. Me.stopwatch.Stop()
    17. btReset.Enabled = True
    18. End Sub
    19. Private Sub btReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReset.Click
    20. Me.stopwatch.Reset()
    21. Label1.Text = "00:00:00:000"
    22. ListBox1.Items.Clear()
    23. End Sub
    24. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    25. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    26. Dim GetElapsedTimeString As String
    27. GetElapsedTimeString =
    28. String.Format("{0:00}:{1:00}:{2:00}:{3:00}",
    29. Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds,
    30. elapsed.Milliseconds)
    31. ListBox1.Items.Add(GetElapsedTimeString)
    32. End Sub
    33. End Class

    TH0R schrieb:

    Milisekunden auf 3 Stellen setzen?

    TH0R schrieb:

    {3:00}
    machst Du {3:000}
    Du hast da zwei Ereignisse (Timer_Tick und Button_Click), die können einfach nicht synchron laufen, da Du 2 Mal Me.stopwatch.Elapsed abrufst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    TH0R schrieb:

    dann
    musst Du beim Button_Click noch mal dasselbe machen wie beim Timer_Tick.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Kleine Anmerkung:
    Mit der Methode (eigentlich ja Funktion) meine ich sowas:

    VB.NET-Quellcode

    1. Private Function GetElapsedTimeString() As String
    2. Return String.Format("{0:00}:{1:00}:{2:00}:{3:00}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)
    3. End Function



    Dann kannst du das hier:

    VB.NET-Quellcode

    1. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    2. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    3. Dim GetElapsedTimeString As String
    4. GetElapsedTimeString =
    5. String.Format("{0:00}:{1:00}:{2:00}:{3:00}",
    6. Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds,
    7. elapsed.Milliseconds)
    8. ListBox1.Items.Add(GetElapsedTimeString)
    9. End Sub


    Zu einem Einzeiler machen:

    VB.NET-Quellcode

    1. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    2. ListBox1.Items.Add(GetElapsedTimeString())
    3. End Sub


    Gleiches gilt dann für:

    VB.NET-Quellcode

    1. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    2. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    3. Label1.Text = String.Format("{0:00}:{1:00}:{2:00}:{3:00}",
    4. Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds,
    5. elapsed.Milliseconds)
    6. End Sub


    VB.NET-Quellcode

    1. ​Label1.Text = GetElapsedTimeString()


    Das erlaubt dir, ganz einfach Änderungen vorzunehmen. Außerdem musst du dann den selben Bug nur einmal fixen, wenn einer drin sein sollte ;)
    Mfg
    Vincent

    Vielen Dank für den Tipp. Das hat es schonmal viel Übersichtlicher gemacht.

    Hab das mal umgesetzt:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stopwatch As New Stopwatch
    3. Private Function GetElapsedTimeString() As String
    4. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    5. Return String.Format("{0:00}:{1:00}:{2:00}:{3:000}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)
    6. End Function
    7. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    8. Label1.Text = GetElapsedTimeString()
    9. End Sub
    10. Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    11. Timer1.Start()
    12. Me.stopwatch.Start()
    13. btReset.Enabled = False
    14. End Sub
    15. Private Sub btStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStop.Click
    16. Timer1.Stop()
    17. Me.stopwatch.Stop()
    18. btReset.Enabled = True
    19. End Sub
    20. Private Sub btReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReset.Click
    21. Me.stopwatch.Reset()
    22. Label1.Text = "00:00:00:000"
    23. ListBox1.Items.Clear()
    24. End Sub
    25. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    26. ListBox1.Items.Add(GetElapsedTimeString)
    27. End Sub
    28. End Class


    Allerdings ist dann immer noch ein Unterschied zwischen Label und den Listboxwerten. ?(

    TH0R schrieb:


    Allerdings ist dann immer noch ein Unterschied zwischen Label und den Listboxwerten. ?(


    Nur Du weißt, wann Du die abgelaufene Zeit "wohin" schreiben willst.
    Der Timer_Tick schreibt Dir bei jedem Auftreten des Events nur das Label neu.
    Der Listbox-Eintrag wird nur geschrieben, wenn die Schaltfläche "btMark" geklickt wird.

    Frage ist doch, was willst Du denn?
    Also ich erkläre mal was ich will.

    Das der Listboxeneintrag nur gesetzt wird wenn ich drücke ist mir klar. Das soll so sein.

    Mein Problem ist, dass wenn Start drücke läuft das Label, dann drücke ich Stop und das Label stoppt. Wenn ich dann allerdings im gestoppten Zustand den "btMark" drücke um einen Listboxeintrag zu schreiben ist dieser nicht gleich dem Label.

    D.h. nachdem Stoppen ist das Label bei 00:00:02:293 und der Listeneintrag der erst wenn schon gestoppt ist geschrieben wird ist bei 00:00:02:308.
    Natürlich, die Stopwatch läuft ja weiter.

    Vielleicht hilft Dir ja die kleine Anpassung?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stopwatch As New Stopwatch
    3. Private elapsed As String
    4. Private Function GetElapsedTimeString() As String
    5. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    6. Return String.Format("{0:00}:{1:00}:{2:00}:{3:000}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)
    7. End Function
    8. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    9. Label1.Text = GetElapsedTimeString()
    10. End Sub
    11. Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    12. ListBox1.Items.Add("00:00:00:000")
    13. Timer1.Start()
    14. Me.stopwatch.Start()
    15. btReset.Enabled = False
    16. btMark.Enabled = True
    17. End Sub
    18. Private Sub btStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStop.Click
    19. elapsed = GetElapsedTimeString()
    20. Timer1.Stop()
    21. Me.stopwatch.Stop()
    22. ListBox1.Items.Add(elapsed)
    23. Label1.Text = elapsed
    24. btReset.Enabled = True
    25. btMark.Enabled = False
    26. End Sub
    27. Private Sub btReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReset.Click
    28. Me.stopwatch.Reset()
    29. Label1.Text = "00:00:00:000"
    30. ListBox1.Items.Clear()
    31. End Sub
    32. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    33. ListBox1.Items.Add(GetElapsedTimeString)
    34. End Sub
    35. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    36. Label1.Text = "00:00:00:000"
    37. ListBox1.Items.Clear()
    38. End Sub
    39. End Class



    Ich sagte schon, der Timer schreibt nur ins Label, der hat zum Zeitpunkt des Stoppens einen Wert, der notwendigerweise kleiner sein muss, weil der Timer_Tick ja bereits geschehen sein muss. Die Werte können nicht gleich sein. Das würdest Du auch gut sehen, wenn Du Dir einen Haltepunkt in die Funktion GetElapsedTimeString() setzen würdest und Dir den TimeSpan anschaust.
    Also besser beim starten und stoppen die Werte auch immer mit in die Liste zu übernehmen. Das geht, wenn man die vergangene Zeit nur einmal abholt und in einen String zwischenparkt.
    Dann aber macht es keinen Sinn mehr, den Marker zu setzen, wenn der Timer gestoppt wurde. Der Wert steht bei meiner Änderung eh bereits in der Liste.

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

    TH0R schrieb:

    Unterschied zwischen Label und den Listboxwerten

    RodFromGermany schrieb:

    Du hast da zwei Ereignisse (Timer_Tick und Button_Click), die können einfach nicht synchron laufen, da Du 2 Mal Me.stopwatch.Elapsed GetElapsedTimeString() abrufst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ok Danke. Dann sollte es so passen.

    Die Listbox Werte sind ja dann genau nach der Stopwatch und die will ich weiterverwenden. Den Wert aus dem Label brauche ich ja dann nicht beachten, wenn er ausdrücklich aus dem Timer stammt und eine Abweichung zur Stopwatch hat, solange die Listbox werte genau nach der Stopwatch sind.
    Ich werde den eindruck nicht los, Du hättest Deine eigene Programmierung nicht verstanden.
    Jeder Aufruf der Funktion GetElapsedTimeString() wird einen neuen Wert ermittlen, sofern er vor dem Me.stopwatch.Stop() erfolgt, das ist doch zwingend logisch, denn solange die Stopwatch läuft wird die Zeitspane immer größer.
    Ich hätte mir also die Stringvariable auch genausogut sparen können, nur die Reihenfolge der Arbeitsschritte im Ereignis btStop_Click sind wichtig für eine korrekte Anzeige der Werte. Und Du hast in Deinem Ereignis btStop_Click halt das Label nicht auch noch einmal aktualisiert.

    Der Wert des Labels stammt auch nicht aus dem Timer, sondern aus der Stopwatch. Der Timer sollte doch nur in Intervallen die Abfrage immer wieder neu durchführen, wieviel Zeit nun vergangen ist.
    Der Rückgabewert der Funktion (ein String) GetElapsedTimeString() wurde dann vom Timer verwendet um das Label zu aktualisieren.
    Also der Timer aktualisiert das Label ist mir klar.

    Ich meinte nur damit, da ich im Endeffekt die Werte aus der Listbox nehme ist mir die ungenauigkeit das Labels im Endeffekt egal und deswegen benöltige ich ja den Markbutton, um mehrere Werte zu setzen während es läuft. Und im Prinzip ist das Label auch nicht ungenau es ist nur nicht so aktualisiert durch die Timerschritte.

    Dennoch will ich ja, dass das Label live läuft damit man einen Überblick über den aktuellen Wert hat.

    Ich hätte mir also die Stringvariable auch genausogut sparen können, nur die Reihenfolge der Arbeitsschritte im Ereignis btStop_Click sind wichtig für eine korrekte Anzeige der Werte. Und Du hast in Deinem Ereignis btStop_Click halt das Label nicht auch noch einmal aktualisiert.


    Damit meinst du das man das Label dort auch nochmal aktualisieren müsste, wenn das Label nicht durch die Aktualisierung des Timers beeinflusst sein soll?
    Danke nochmal für die Unterstützung. Es funktioniert nun alles soweit.

    Jetzt bin ich an einer neuen Sache dran. Ich würde gerne die Werte der Listbox automatisch in eine externe Exceltabelle oder andere Art von Tabelle (welche ohne Excel) zu öffnen ist schreiben lassen.

    Hat da jmd. ein Beispiel ?
    bzw.
    kann mir bei dem untenstehendem Problem helfen?

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stopwatch As New Stopwatch
    3. Private elapsed As String
    4. Private Function GetElapsedTimeString() As String
    5. Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    6. Return String.Format("{0:00}:{1:00}:{2:00}:{3:000}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)
    7. End Function
    8. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    9. Label1.Text = GetElapsedTimeString()
    10. End Sub
    11. Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    12. Timer1.Start()
    13. Me.stopwatch.Start()
    14. btReset.Enabled = False
    15. btMark.Enabled = True
    16. End Sub
    17. Private Sub btStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStop.Click
    18. elapsed = GetElapsedTimeString()
    19. Timer1.Stop()
    20. Me.stopwatch.Stop()
    21. ListBox1.Items.Add(elapsed)
    22. Label1.Text = elapsed
    23. btReset.Enabled = True
    24. btMark.Enabled = False
    25. SaveListToFile(ListBox1, "C:\Users\Jan-Oliver.Deeg\Documents\Master Thesis\Visual Studio Projekte\ListBox1.txt")
    26. End Sub
    27. Private Sub btReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReset.Click
    28. Me.stopwatch.Reset()
    29. Label1.Text = "00:00:00:000"
    30. ListBox1.Items.Clear()
    31. End Sub
    32. Private Sub btMark_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btMark.Click
    33. ListBox1.Items.Add(GetElapsedTimeString)
    34. End Sub
    35. Public Sub SaveListToFile(ByVal ListBox1 As Object, ByVal Filename As String)
    36. Dim file As New IO.FileStream(Filename, IO.FileMode.Create)
    37. Dim writer As New IO.StreamWriter(file)
    38. If ListBox1.Items.Count > 0 Then
    39. For i As Long = 0 To ListBox1.Items.Count - 1
    40. writer.WriteLine(ListBox1.Items(i))
    41. Next i
    42. writer.Close()
    43. file.Close()
    44. End If
    45. End Sub
    46. End Class


    Das ist der Code aktuell. Meine Problem ist das er immer in das gleiche File schreibt, d.h. bei jedem nutzen der Stoppuhr überschreibt er mir die Werte bzw. das File. Ich hätte aber gerne, dass jedesmal ein neues File erstellt wird oder das man es als Tabelle setzen könnte und jedesmal eine neue Spalte gewählt wird.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „TH0R“ ()

    TH0R schrieb:

    Ich würde gerne die Werte der Listbox automatisch in eine externe Exceltabelle oder andere Art von Tabelle (welche ohne Excel) zu öffnen ist schreiben lassen.

    Hat da jmd. ein Beispiel ?
    Daten laden und speichern




    bzw. kann mir bei dem untenstehendem Problem helfen?
    Eins nachm anderen.



    Btw wo ich nu doch ein Blick in dein Code geworfen habe, ist folgendes das erste, was zu tun ist, und nix anderes: Visual Studio - Empfohlene Einstellungen
    Du proggst offsichtlich noch mit Strict Off, was bedeutet, dass du noch nicht gelernt hast, die Unterschiedlichkeit von Datentypen zu respektieren.
    Wie gesagt - Empfehlung: Bring das sofort in Ordnung.
    Je länger du das rausschiebst, desto schwieriger wird das Ausbessern der Fehler, die schon jetzt drinne sind, und gleichzeitig gewöhnst du dir immer mehr Mist an, der in der einen oder anderen Weise gegen Grundregeln ordentlicher Programmierung verstößt.

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