Grafische Seekbar



  • Ich versuche derzeit einen MP3-Player mit GUI zu proggn, aber folgendes Problem ist noch ungelöst:

    Die MP3-Player-Klasse hat folgende Schnittstellen:
    get_length() - Liefert die Spielzeit in Millisekunden
    get_pos() - Liefert die aktuelle Position im Lied
    seek(ms) - Seekt zu ms-Millisekunden im Lied

    Nun soll der graphische Teil eine 'Seekbar' (bzw. Scale) erhalten, mit folgenden Eigenschaften:
    Die Seekbar wandert immer mit den Millisekunden des Liedes mit.
    Mit der selben Seekbar kann man gleichzeitig im Lied seeken.

    Das Problem liegt nun bei der Umsetzung.

    Die Seekbar mitwandern lassen könnte man ganz einfach, indem man der Skala als min-Wert 0 und max-Wert get_length() gibt und dann den aktuellen Wert alle 100ms abfrägt und aktualisiert.

    Im Lied seeken wäre auf die selbe weiße möglich, nur das die Seekbar nen Handler bekommt, falls sich ihr Wert ändert. Dann könnte man den nun aktuellen Wert mit set_pos() der MP3-Klasse übergeben.

    Aber wie lassen sich die beiden Funktionen gleichzeitig implementieren? Sie überschneiden sich. Man kann die Seekbar nicht mitwandern lassen, wenn dann immer der Signal-Handler reagiert und an die selbe Position seekt... Was kann man da machen?



  • Wenn es keinen Weg für dein GUI Toolkit gibt, die Seekbar zu verändern ohne ein Signal auszulösen, könntest du einfach über die Mausknöpfe arbeiten. Wird der Mausknopf losgelassen, dann veränderst du die Position oä



  • Welches GUI Toolkit verwendest du?



  • was mir noch einfallen wuerde:

    Mp3Player::refreshSeekbar()
    {
        seekBar->setPosition(getActualPosition(), false);
    }
    
    Seekbar::setPosition(int position, boolean doSetMp3PlayerPosition)
    {
        this->doSetMp3PlayerPosition = doSetMp3PlayerPosition;
        this->handlePositionChangeEvent(position);
        this->doSetMp3PlayerPosition = false;
    }
    
    // der eigentliche Eventhandler
    Seekbar::handlePositionChangeEvent(int newPosition)
    {
        // ... die neue position irgendwie setzen
    
        if (doSetMp3PlayerPosition)
            mpPlayer->set_pos(newPosition);
    }
    

    Du "merkst" dir also quasi, ob du die Seekbar jetzt aenderst, weil der MP3-Player das veranlasst hat, oder ob der User manuell die Seekbar veraendert hat.

    EDIT: dabei koennte logisch evtl. ein Fehler auftreten wenn der User und der Mp3-Player zeitgleich die Seekbar aendern. Davor musst du dich dann wohl per Mutex schuetzen.


  • Mod

    Hm, ich würde es so machen, das der Player unabhängig von der GUI ist,
    und die GUI dann nur die entsprechenden Schnittstellen zum Player bedient.

    Du müsstest dann im GUI Toolkit deiner Wahl halt beim Seekbar die entsprechenden
    Events regestrieren, oder ihn evtl. Subclassen, je nach Toolkit und Umfang.

    phlox



  • Toolkit: GTKmm

    Ich werde eure Lösungsvorschläge mal ausprobieren. 🙂

    @phlox:
    Verstehe ich nicht ganz, für mich bleibt das Problem trotzdem bestehen?!



  • Steven Seek-al schrieb:

    Toolkit: GTKmm

    👍

    @phlox:
    Verstehe ich nicht ganz, für mich bleibt das Problem trotzdem bestehen?!

    Der Vorteil an phloxs Vorschlag ist es, dass man aus der Klasse eine logische Klasse macht, die unabhängig von GUI-Toolkits arbeitet. Daher kann man sie flexibler einsetzen, als wenn man die Funktionalität direkt in die GUI reincodet.

    MfG

    GPC



  • GPC schrieb:

    Der Vorteil an phloxs Vorschlag ist es, dass man aus der Klasse eine logische Klasse macht, die unabhängig von GUI-Toolkits arbeitet. Daher kann man sie flexibler einsetzen, als wenn man die Funktionalität direkt in die GUI reincodet.

    MfG

    GPC

    Könnt ihr das mal in Code zeigen?
    Also ich habe ja die Klasse MP3, die völlig GUI-unabhängig arbeitet, die GUI holt sich die Daten wie derzeitige Position im Lied ja auch über die Schnittstellen der Klasse MP3, aber das Problem liegt ja darin, die GUI so hinzubiegen, dass sie sich nicht überschneidet. Weil die seekBar mit der Position im Lied syncronisieren macht ja die GUI, bzw. der entsprechende Eventhandler selbst. Und auf Änderungen der seekBar reagiert ja auch die GUI, das Problem liegt ja überhaupt auch da.

    Aber vielleicht meinst du auch was anderes. 🤡


  • Mod

    Also, ich hab ja auch mal nen mp3 Player geschrieben,
    und heute würde ich es so machen:

    • Player auf Konsolenbasis entwickeln, zum Abspielen etc. brauchts keine GUI (das ganze dann in einen Namespace rein)
    • Die GUI so gestalten, das sie nur über klare Schnittstellen (Singelton, Threads) auf den mp3 Player zugreift.
    • Den Player selber in einem eigenen Thread laufen lassen


  • Das Problem mit der GUI behebt das aber trotzdem nicht. 🤡


  • Mod

    Steven Seek-al schrieb:

    Das Problem mit der GUI behebt das aber trotzdem nicht. 🤡

    Welches Problem? :p



  • Angenommen der MP3-Player wäre so wie von dir genannt umgesetzt worden.
    Trotzdem muss im Code der GUI ein Code stecken, der die Seekbar mit der Position im Lied syncronisiert und auf Änderungen reagiert.



  • Ich hätte jetzt mal folgenden Pseudocode:

    if(cur_seekbar_pos > mp3_pos()+200 || cur_seekbar_pos < mp3_pos-200)
       {
    	mp3_seek(seekbar_cur_pos);
       }
       else
       {
    	seekbar_pos = cur_mp3_pos;
       }
    

    Dieser Code wird alle 100ms aufgerufen. Wenn sich die aktuelle Position der Seekbar um mehr als 200ms verändert hat, wird von einer Userinteraktion ausgegangen und die MP3-Klasse fährt zu der momentanen Position. Wenn die aktuelle Position innerhalb der Toleranz liegt (100ms, die die MP3-Klasse während dieser Zeit gespiel that + weitere 100ms falls der Host-Rechner grad am abkacken ist und eine Verzögerung verursacht), dann wird die Seekbar-Position mit der MP3-Position syncronisiert.

    Das der Code aber verhindert:
    Ein Abspielen, wenn die Seekbar auf 0 steht. Wenn das Programm läuft, kann man der Seekbar nen kleinen Stups geben, sie also nen Millimeter nach vorne ziehen, dann wird gespielt (aber warum??).

    Wenn das Lied dann zu Ende ist bleibt die Seekbar am Ende embenfalls hängen.

    Diese beiden Phänomene kann ich mir aber nicht wirklich erklären.
    Zum Start sieht die Funktion so aus:

    if(0 > 200 || 0 < -200)
       mp3_seek(seekbar_cur_pos);
    else
       seekbar_pos = 0;
    

    Nach 100ms:

    if(100 > 300 || 100 < -100)
       mp3_seek(seekbar_cur_pos);
    else
       seekbar_pos = 100;
    

    Nach 200ms:

    if(200 > 400 || 200 < 0)
       mp3_seek(seekbar_cur_pos);
    else
       seekbar_pos = 200;
    

    Nach 300ms:

    if(300 > 500 || 300 < 100)
       mp3_seek(seekbar_cur_pos);
    else
       seekbar_pos = 300;
    

    ...also so weit wie ich das Blicke, sollte es eigentlich funktionieren. 😕

    @rüdiger: deinen ansatz mit den mausknöpfen hab ich mal versucht umzusetzen, aber Gtk::HScale kennt kein entsprechendes Signal, das beim loslassen eines Mausbuttons ausgelöst werden würde.


  • Mod

    Steven Seek-al schrieb:

    Angenommen der MP3-Player wäre so wie von dir genannt umgesetzt worden.
    Trotzdem muss im Code der GUI ein Code stecken, der die Seekbar mit der Position im Lied syncronisiert und auf Änderungen reagiert.

    In der GUI gibts nen Timer, der alle x ms den mp3 player fragt, wo gerade abgespielt wird.
    In dem Timerevent wird dann der Seekbar gesetzt.

    Wenn der User nun auf den Bar klickt, gibt das wieder nen Event, dort musst du
    dann halt entsprechend von seekbar die Position auslesen, es für das Lied umrechnen,
    und dem MP3 Player dorthin vorspulen lassen. Währenddessen solltest du aber den Timer Event unterdrücken.


Anmelden zum Antworten