play audio from stop position in kivy

524 views
Skip to first unread message

Raja Ramesh

unread,
Apr 11, 2020, 11:32:18 PM4/11/20
to Kivy users support
Hi ,

i am trying to play a song from stop position, but with below code it's playing from begin. could some one provide the details how to achieve this.


song = SoundLoader.load(path of .mp3 file)
cur_pos = song.get_pos()
if song.state == 'play':
    song.stop()
elif song.state == 'stop' and str(cur_pos):
    song.play() 


Thanks,
raja

Elliot Garbus

unread,
Apr 12, 2020, 7:00:53 AM4/12/20
to kivy-...@googlegroups.com

After the song is playing use seek to position the song to cur_pos.

 

A small example:

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.properties import BooleanProperty

kv=
"""
SoundTestBoxLayout:
    orientation: 'vertical'
    Label:
        text: 'Audio test'
        font_size: 20
    BoxLayout:
        size_hint_y: None
        height: dp(48)
        Button:
            text: 'Play'
            on_release: root.play()
        Button:
            text: 'Pause'
            on_release: root.pause()
            disabled: root.paused
        Button:
            text: 'Stop'
            on_release: root.stop()
    Label:
        text:
"""


class SoundTestBoxLayout(BoxLayout):
    paused = BooleanProperty(
False)

   
def __init__(self, **kwargs):
       
self.sound = SoundLoader.load('music/PinkPanther60.wav')
       
self.song_position = None
       
super().__init__(**kwargs)

   
def play(self):
       
self.sound.play()
       
if self.paused:
           
self.sound.seek(self.song_position)
           
self.paused = False

    def
pause(self):
       
self.song_position = self.sound.get_pos()
       
self.sound.stop()
       
self.paused = True

    def
stop(self):
       
self.sound.stop()
       
self.paused = False


class
SoundTestApp(App):
   
def build(self):
       
return Builder.load_string(kv)


SoundTestApp().run()

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/5c4065bd-ee6b-474e-8719-f2cbc09aa186%40googlegroups.com.

 

Raja Ramesh

unread,
Apr 12, 2020, 12:30:56 PM4/12/20
to kivy-...@googlegroups.com
Thank you very much Elliot. Now i am able to play the song from stop position. 

Elliot


Elliot Garbus

unread,
Apr 12, 2020, 12:33:09 PM4/12/20
to kivy-...@googlegroups.com

Raja Ramesh

unread,
Apr 14, 2020, 5:16:13 AM4/14/20
to kivy-...@googlegroups.com
Hi Elliot,

Now i am trying to play songs present in a directory one by one with below code, but the app is not displaying the screen while playing. kindly share details how to play the song one by one in a directory.  Also, i tried with loading the files in one method and calling that in play method but no luck.

import os
import time
from kivy.core.audio import SoundLoader

path = r'path to directory'
songs_list = [ ]
for  song in os.listdir(path):
    if song.endswith('.mp3'):
        songs_list.append(os.path.join(path,song))
        sound = SoundLoader.load(songs_list)
        sound.play()
        time.sleep(sound._get_length())
         


Thanks
Raja

Elliot Garbus

unread,
Apr 14, 2020, 10:32:27 AM4/14/20
to kivy-...@googlegroups.com

Here are a few points to consider.

  1. Do not use sleep in the UI thread.  Any GUI is an event driven system, using sleep will lock up the system.  This is why you are never seeing the screen.  Your code is running in a loop, never returning to the kivy event loop.
  2. Look at the documentation for Audio in kivy, there is an event for when the audio has stopped,  on_stop.  Use the event to know when the song has ended.  See bind.  Bind a function you want to run when the song has ended.
  3. There is a module in the standard library called glob that returns lists of files.  A very easy way to get all of the mp3 files from a directory.

Elliot Garbus

unread,
Apr 14, 2020, 1:00:22 PM4/14/20
to kivy-...@googlegroups.com

Here is an example:

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.core.audio import SoundLoader

from glob import glob

kv =
"""
<AudioPlayer>:
    orientation: 'vertical'
    BoxLayout:
        size_hint_y: None
        height: 48
        Button:
            text: 'prev'
            on_release: root.previous()
        Button:
            text: 'next'
            on_release: root.next()
        Button:
            text: 'play'
            on_release: root.play()
        Button:
            text: 'stop'
            on_release: root.stop()
    Label:
        text: root.song_name
        font_size:30

BoxLayout:
    orientation: 'vertical'
    Label:
        text: 'Audio File Player'
        font_size: 30
    AudioPlayer:
"""


class AudioPlayer(BoxLayout):
    song_name = StringProperty(
'No Song Selected')

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.song = None
       
self.songs = glob('music/*.wav'# change to appropriate directory and *.mp3
       
self.song_index = 0
       
self.stop_pressed = False

    def
play(self):
       
if self.song:
           
if self.song.state == 'play':
               
self.stop_pressed = True  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end
       
self.song.play()
       
self.song_name = self.songs[self.song_index]

   
def next(self):
       
self.song_index = min(self.song_index + 1, len(self.songs) -1)
       
self.play()

   
def previous(self):
       
self.song_index = max(self.song_index -1, 0)
       
self.play()

   
def stop(self):
       
if self.song:
           
self.stop_pressed = True
           
self.song.stop()
           
self.song = None

    def
song_end(self, *args):
       
print('song end')
       
if not self.stop_pressed:
           
self.next()
       
self.stop_pressed = False


class
AudioPlayerApp(App):
   
def build(self):
       
return Builder.load_string(kv)


AudioPlayerApp().run()

Raja Ramesh

unread,
Apr 15, 2020, 1:30:11 AM4/15/20
to kivy-...@googlegroups.com
Thank you Elliot for your help.... AS the world is suffering from COVID-19...i want people like you to be safe.

Elliot Garbus

unread,
Apr 15, 2020, 2:00:35 AM4/15/20
to kivy-...@googlegroups.com

Thanks for your kinds words.   Best of health to you and your family… and to us all.

Raja Ramesh

unread,
Apr 18, 2020, 9:56:08 AM4/18/20
to kivy-...@googlegroups.com
Hi Elliot, 

i am trying to append pause logic to the code you shared with me. But when i press pause button  next song is playing instead of pausing the song with below modified code .
i noticed because of on_stop event  next song is playing. if i comment on_stop event pause logic is working but next song is not playing until i press next  button. please let me know how to implement pause logic.


from kivy.app import App
from kivy.lang import Builder
from kivy.properties import BooleanProperty
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from mutagen.id3 import ID3, APIC
from mutagen import File
from mutagen.mp3 import MP3
from glob import glob

Builder.load_string('''
<MyWidget>:
Label:
text: 'Image_Display'
pos_hint: {'center_x':.5, 'center_y':.9}

<MyImage>:
source: root.img()
size: self.size
pos: self.pos
Button:
text: 'previous'
size_hint: .2, .1
pos_hint:{'center_x':.1, 'center_y': .1}
on_press: root.previous()
Button:
text: 'play'
size_hint: .2, .1
pos_hint:{'center_x':.3, 'center_y': .1}
on_press: root.play()
Button:
text: 'pause'
size_hint: .2, .1
pos_hint:{'center_x':.5, 'center_y': .1}
on_press: root.pause()
disabled: root.paused
Button:
text: 'next'
size_hint: .2, .1
pos_hint:{'center_x':.7, 'center_y': .1}
on_press: root.next()
Button:
text: 'stop'
size_hint: .2, .1
pos_hint:{'center_x':.9, 'center_y': .1}
on_press: root.stop()

MyWidget:

''')

class MyImage(FloatLayout):
#pass
    paused = BooleanProperty(False)
def __init__(self, **kwargs):
        super(MyImage, self).__init__(**kwargs)
self.song = None
#self.songs = glob('music/*.wav') # change to appropriate directory and *.mp3
self.songs = glob(path to file)

self.song_index = 0
self.stop_pressed = False
        self.paused = False

def img(self):
self.album = File(path to file)
self.destination = path to file # for image saving
self.cover = self.album['APIC:'].data
with open(self.destination, 'wb') as img:
img.write(self.cover)
self.mp3_img = Image(source = self.destination)
self.add_widget(self.mp3_img)


def play(self):
if self.song:
if self.song.state == 'play':
self.stop_pressed = True # pressing play while playing... is hitting stop then play
self.song.stop()
self.song = SoundLoader.load(self.songs[self.song_index])
self.song.bind(on_stop=self.song_end) # when the on_stop event fires, execute song_end
self.song.play()
        #self.song.loop = True # it loops current song which is playing
self.song_name = self.songs[self.song_index]
if self.paused:
self.song.seek(self.song_position)

self.paused = False

def pause(self):
        self.song_position = self.song.get_pos()
self.song.stop()
self.paused = True
       
def next(self):
self.song_index = min(self.song_index + 1, len(self.songs) - 1)
self.play()

def previous(self):
        self.song_index = max(self.song_index - 1, 0)

self.play()

def stop(self):
if self.song:
self.stop_pressed = True
self.song.stop()
self.song = None

def song_end(self, *args):
print('song end')
if not self.stop_pressed:
self.next()

self.stop_pressed = False

class MyWidget(FloatLayout):
pass

class ImageDisplay(App):
def build(self):
return MyImage()

ImageDisplay().run()


Thanks,
Raja

Elliot Garbus

unread,
Apr 18, 2020, 10:51:13 AM4/18/20
to kivy-...@googlegroups.com

I added pause to the original example I created:

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.core.audio import SoundLoader

from glob import glob

kv =
"""

<AudioPlayer>:
    orientation: 'vertical'
    BoxLayout:
        size_hint_y: None
        height: 48
        Button:
            text: 'prev'
            on_release: root.previous()
        Button:
            text: 'next'
            on_release: root.next()
        Button:
            text: 'play'
            on_release: root.play()
        Button:
            text: 'pause'
            on_release: root.pause()   

        Button:
            text: 'stop'
            on_release: root.stop()
    Label:
        text: root.song_name
        font_size:30

BoxLayout:
    orientation: 'vertical'
    Label:
        text: 'Audio File Player'
        font_size: 30
    AudioPlayer:
"""


class AudioPlayer(BoxLayout):
    song_name = StringProperty(
'No Song Selected')

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.song = None
       
self.songs = glob('music/*.wav'# change to appropriate directory and *.mp3
       
self.song_index = 0
       
self.stop_pressed =
False
       
self.song_position = 0
       
self.paused = False

    def
play(self):
       
if self.paused:
           
self.song.play()
           
self.song.seek(self.song_position)
           
self.paused = False
            return
        if
self.song:
           
if self.song.state == 'play':
                
self.stop_pressed = True  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end
       
self.song.play()
       
self.song_name = self.songs[self.song_index]

   
def next(self):
       
self.song_index = min(self.song_index + 1, len(self.songs) -1)
       
self.play()

   
def previous(self):
       
self.song_index = max(self.song_index -1, 0)
       
self.play()

   
def stop(self):
       
if self.song:
           
self.stop_pressed = True
           
self.paused = False
           
self.song.stop()
           
self.song = None

    def
pause(self):
       
if self.paused:
           
return
        if
self.song:
           
self.paused = True
           
self.song_position = self.song.get_pos()
           
self.song.stop()
           
self.paused = True

    def
song_end(self, *args):
       
print('song end')
       
if self.paused:
           
return
        if not
self.stop_pressed:
           
self.next()
       
self.stop_pressed = False


class
AudioPlayerApp(App):
   
def build(self):
       
return Builder.load_string(kv)


AudioPlayerApp().run()

Raja Ramesh

unread,
Apr 19, 2020, 5:17:43 AM4/19/20
to kivy-...@googlegroups.com
Hi Elliot,

i tried your code, pause is working but when i press play button it is not resuming from stop position. i tried to modify the code but i couldn't fix. i need you help.


Elliot Garbus

unread,
Apr 19, 2020, 10:52:06 AM4/19/20
to kivy-...@googlegroups.com

I tried it again with .mp3, it is all working.

Here it is again, all I changed was using mp3 files, instead of wave files.

 

None
       
self.songs = glob('music/*.mp3'# change to appropriate directory and *.mp3
       
self.song_index = 0
       
self.stop_pressed = False
       
self.song_position = 0
       
self.paused = False

    def
play(self):
       
if self.paused:
           
self.song.play()
           
self.song.seek(self.song_position)
           
self.paused = False
            return
        if
self.song:
           
if self.song.state == 'play':
                
self.stop_pressed = True  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end
       
self.song.play()
       
self.song_name = self.songs[self.song_index]

   
def next(self):
       
self.song_index = min(self.song_index + 1, len(self.songs) -1)
       
self.play()

   
def previous(self):
       
self.song_index = max(self.song_index -1, 0)
       
self.play()

   
def stop(self):
       
if self.song:
           
self.stop_pressed = True
           
self.paused = False
           
self.song.stop()
           
self.song = None

    def
pause(self):
       
if self.paused:
           
return
        if
self.song:
           
self.paused = True
           
self.song_position = self.song.get_pos()
           
self.song.stop()
           
self.paused = True

    def
song_end(self, *args):
       
print('song end event')
       
if self.paused:
           
return
        if not
self.stop_pressed:
           
self.next()
       
self.stop_pressed = False


class
AudioPlayerApp(App):
   
def build(self):
       
return Builder.load_string(kv)


AudioPlayerApp().run()

Elliot Garbus

unread,
Apr 19, 2020, 11:46:16 AM4/19/20
to kivy-...@googlegroups.com

Here is a slightly modified version.  My original version did not have pause.  As a result there were 2 boolean variables stop_pressed and pause_pressed used to manage state.  I combined them here in to a kivy property called player_state.  This state is used in the stop event handler, song_end(), to determine the appropriate action.

The player_state can be:

stop – the stop button has been pressed.

pause - the pause button has been pressed

ready – not paused or stopped.

 

The method on_player_state will print the state when it changes, this is for debug and will let you see what is going on.  player_state needs to be a kivy property to create this event.

 

You can also use player_state to control the buttons in UI.

 

)
    player_state = StringProperty(
'ready') # can be stop, pause, or ready (ready is not stop or pause)

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.song = None
       
self.songs = glob('music/*.mp3'# change to appropriate directory and *.mp3
        
self.song_index = 0
       
self.song_position = 0
       
self.player_state = 'ready'

   
def play(self):
       
if self.player_state == 'pause':
           
self.song.play()
           
self.song.seek(self.song_position)
           
self.player_state = 'ready'
           
return
        if
self.song:
           
if self.song.state == 'play':
               
self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end
       
self.song.play()
       
self.song_name = self.songs[self.song_index]

   
def next(self):
       
self.player_state = 'ready'
       
self.song_index = min(self.song_index + 1, len(self.songs) -1)
       
self.play()

   
def previous(self):
       
self.player_state = 'ready'
       
self.song_index = max(self.song_index -1, 0)
       
self.play()

   
def stop(self):
       
if self.song:
           
self.player_state = 'stop'
           
self.song.stop()
           
self.song = None

    def
pause(self):
       
if self.player_state == 'pause':
           
return
        if
self.song:
           
self.player_state = 'pause'
           
self.song_position = self.song.get_pos()
           
self.song.stop()

   
def song_end(self, *args):
       
print(f'song end event, player_state: {self.player_state}')
       
if self.player_state == 'pause':
           
return
        if not
self.player_state == 'stop':
           
self.next()
       
self.player_state = 'ready'

   
def on_player_state(self, *args):          # this is for debug - it prints out the state when it changes
       
print(f'state: {self.player_state}')

Raja Ramesh

unread,
Apr 19, 2020, 12:06:57 PM4/19/20
to kivy-...@googlegroups.com
Hi Elliot,

For few songs app is not playing from stopped position, it's starting from beginning. Just to Know...app should work for all mp3 files, but Not sure what was wrong with few songs [ which i converted from youtube video song to mp3 songs using online converter ] app is not working. Any details from your side for these kind of mp3 files[which i converted]?

Thanks,
Raja

Elliot Garbus

unread,
Apr 19, 2020, 12:20:58 PM4/19/20
to kivy-...@googlegroups.com

Raja,

That’s interesting.  Do some tests.  For the songs that don’t property resume from pause, do they always report their position as zero?

Check the meta-data, you might be able to see if it is  some particular combination of bit-rate or compression settings that is causing this problem.

 

I have not experienced this issue.  It would be a function of the underlying audio decoder.

Raja Ramesh

unread,
Apr 19, 2020, 1:06:12 PM4/19/20
to kivy-...@googlegroups.com
Hi Elliot,

yes, position is  Zero after resuming from pause.... i have attached screenshot of log for the same. Now how to solve this.   just to clarify .... we need to use third party libraries like Mutagen, id3reader....  to check metadata of song ?
                                                                            
image.png  image.png

Elliot Garbus

unread,
Apr 19, 2020, 1:32:14 PM4/19/20
to kivy-...@googlegroups.com

It looks like the songs are properly reporting their position when paused.

Test that the seek command is working properly with these songs.

 

 

 

From: Raja Ramesh
Sent: Sunday, April 19, 2020 10:06 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

 

yes, position is  Zero after resuming from pause.... i have attached screenshot of log for the same. Now how to solve this.   just to clarify .... we need to use third party libraries like Mutagen, id3reader....  to check metadata of song ?

                                                                            

  

Raja Ramesh

unread,
Apr 19, 2020, 1:57:20 PM4/19/20
to kivy-...@googlegroups.com
Hi Elliot, value of seek is saying None for all songs which are resuming from pause also not resuming from pause. 

image.png

Elliot Garbus

unread,
Apr 19, 2020, 2:01:51 PM4/19/20
to kivy-...@googlegroups.com

Seek does not return a value. 

If you do a seek with one of your problem files, does the seek work.  To seek the song must be playing.  You can use get_pos() after a seek to see if the seek worked.

 

If you want, send me one of your problem files, I can do some tests as well.

 

From: Raja Ramesh
Sent: Sunday, April 19, 2020 10:57 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot, value of seek is saying None for all songs which are resuming from pause also not resuming from pause. 

 

Raja Ramesh

unread,
Apr 20, 2020, 1:53:48 AM4/20/20
to kivy-...@googlegroups.com

Hi Elliot,

i have attached sample file.
i am not sure how to perform seek. Also i implemented code for progress bar in the app but not sure how to implement seek. similar like below screenshot. 

image.png

Thanks,
Raja
padutha theyaga_balu.zip

Elliot Garbus

unread,
Apr 20, 2020, 11:06:22 AM4/20/20
to kivy-...@googlegroups.com

https://superuser.com/questions/893494/mp3-file-seek-is-bugged

 

It looks like a problem with the way your file is encoded.

The seek() method has no effect.

The problem can be detected by getting the length of the song.  The length is coming back as -1.  This suggests the length meta-data is not correct.

Do you have another encoder you could use?  Or perhaps find an encoder that can correct meta-data.

You could disable the pause button for content with a length of -1, or use to re-encode the data from within your app if you find a solution.

 

For a progressbar, load the song, play the song, get the length.  Set max on the progress bar to be int(length).  Use Clock.schhedule_once, the set up a schedule to update the progress bar every .1 seconds.  Cancel the schedule on pause, and resume when play is pressed.  On stop cancel the schedule and reset the progressbar.

 

Give it a shot.  Post a small example if you run into trouble

 

 

From: Raja Ramesh
Sent: Sunday, April 19, 2020 10:53 PM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

 

Hi Elliot,

 

i have attached sample file.

i am not sure how to perform seek. Also i implemented code for progress bar in the app but not sure how to implement seek. similar like below screenshot. 

 

 

Thanks,

Raja

 

 

On Sun, Apr 19, 2020 at 11:31 PM Elliot Garbus <elli...@cox.net> wrote:

Seek does not return a value. 

If you do a seek with one of your problem files, does the seek work.  To seek the song must be playing.  You can use get_pos() after a seek to see if the seek worked.

 

If you want, send me one of your problem files, I can do some tests as well.

 

 

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Raja Ramesh

unread,
Apr 20, 2020, 1:36:46 PM4/20/20
to kivy-...@googlegroups.com
Hi Elliot,

I appended progress bar and volume code. Need your inputs for below points,

1. progress bar color is not disappearing after completion of song. it's over ridding on top of it for next song.
 2. i am not able to add seek code to progress bar.(i mean play from the drag position of progress bar.) 


image.png 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout

from kivy.properties import StringProperty
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.uix.progressbar import ProgressBar
from kivy.properties import BooleanProperty

from glob import glob

kv= """
SoundTestBoxLayout:
FloatLayout:
Button:
text: 'Play'
size_hint: 0.1,0.07
pos_hint:{'center_x': 0.3, 'center_y': 0.1}
on_release: root.play()
Label:
id: val
pos_hint:{'center_x':0.07,'center_y':0.55}
Button:
text: 'Pause'
size_hint: 0.1,0.07
pos_hint:{'center_x': 0.4, 'center_y': 0.1}
            on_release: root.pause()
disabled: root.paused
Button:
text: 'Stop'
            size_hint:  0.1,0.07
pos_hint:{'center_x': 0.6, 'center_y': 0.1}
on_release: root.stop()
Button:
text: 'Prev'
size_hint: 0.1,0.07
pos_hint:{'center_x': 0.2, 'center_y': 0.1}
            on_release: root.previous()
Button:
text: 'next'
            size_hint:  0.1,0.07
pos_hint:{'center_x': 0.5, 'center_y': 0.1}
on_release: root.next()
# below code is for slider
Slider:
id: slider
min: 0.0
max: 1.0
value: 0.2
on_value: root.set_volume(self.value)
value_track: True
value_track_color: (1,0,0,1)
orientation: "vertical"
size_hint_x: None
width: "48dp"
Label:
text: str(round(slider.value * 100))
pos_hint:{'center_x':0.05, 'center_y':.9}

"""


class SoundTestBoxLayout(FloatLayout):
paused = BooleanProperty(False)

player_state = StringProperty('ready') # can be stop, pause, or ready (ready is not stop or pause)
def __init__(self, **kwargs):
        #self.song_position = None
self.song = None
        self.song_index = 0
self.stop_pressed = False
self.song_position = 0
self.paused = False
        self.player_state = 'ready'
self.songs = glob(r'C:\music\*.mp3')
super().__init__(**kwargs)


def play(self):
if self.player_state == 'pause':
self.song.play()
self.song.seek(self.song_position)
            print('seek position is:- %s' % (self.song.seek(self.song_position)))
self.player_state = 'ready'
            return
if self.song:
if self.song.state == 'play':
                self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
                self.song.stop()
self.song = SoundLoader.load(self.songs[self.song_index])
self.song.bind(on_stop=self.song_end) # when the on_stop event fires, execute song_end
        if self.song:
self.song.volume = 0.2
            self.song.play()
self.song_name = self.songs[self.song_index]
            self.event = Clock.schedule_interval(self.play_status, 0.1)

def play_status(self, _):
if self.song:
self.text = str(self.song.get_pos())
mins = self.song.get_pos() / 60
self.ids.val.text = str(round(mins,2))
self.progress = ProgressBar(max = mins)
self.progress = ProgressBar(max= self.song._get_length())
#self.progress.value = mins
self.progress.value = self.song.get_pos() if self.progress.value < self.song._get_length() else 0
self.add_widget(self.progress)
else:
self.text = "play"
Clock.unschedule(self.event)

def set_volume(self, volume):
self.volume= volume
if self.song:
self.song.volume = self.volume
    
def stop(self):
if self.song:
self.player_state = 'stop'
            self.song.stop()
self.song = None

def pause(self):
        if self.player_state == 'pause':
return
if self.song:
self.player_state = 'pause'
            print('song name paused is:- %s' %(self.song_name))
self.song_position = self.song.get_pos()
print('current position after pausing the song is:- %s' % (self.song_position))

self.song.stop()

def song_end(self, *args):
print(f'song end event, player_state: {self.player_state}')
if self.player_state == 'pause':
return
if not self.player_state == 'stop':
self.next()
self.player_state = 'ready'

def on_player_state(self, *args): # this is for debug - it prints out the state when it changes
print(f'state: {self.player_state}')


class SoundTestApp(App):
def build(self):
return Builder.load_string(kv)


SoundTestApp().run()

Elliot Garbus

unread,
Apr 20, 2020, 3:22:27 PM4/20/20
to kivy-...@googlegroups.com

Here you go…

You were creating multiple progress bars on top of each other.  You want one progress bar, and to change its value and min.

Some other small enhancements.

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.uix.progressbar import ProgressBar
from kivy.properties import BooleanProperty

from glob import glob

kv =
"""

SoundTestBoxLayout:
    FloatLayout:
        Button:
            text: 'Play'
            size_hint: 0.1,0.07
            pos_hint:{'center_x': 0.3, 'center_y': 0.1}
            on_release: root.play()
        Label:
            id: val
            text: '00:00'
            size_hint: None, None     # adding
            size: self.texture_size
            pos_hint:{'x': .1, 'center_y':0.55}

           
        Button:
            text: 'Pause'
            size_hint: 0.1,0.07
            pos_hint:{'center_x': 0.4, 'center_y': 0.1}
            on_release: root.pause()
            disabled: root.paused
        Button:
            text: 'Stop'
            size_hint:  0.1,0.07
            pos_hint:{'center_x': 0.6, 'center_y': 0.1}
            on_release: root.stop()
        Button:
            text: 'Prev'
            size_hint:  0.1,0.07
            pos_hint:{'center_x': 0.2, 'center_y': 0.1}
            on_release: root.previous()
        Button:
            text: 'next'
            size_hint:  0.1,0.07
            pos_hint:{'center_x': 0.5, 'center_y': 0.1}
            on_release: root.next()
        # below code is for slider
        BoxLayout:
            ProgressBar:
                id:pb

        Slider:
            id: slider
            min: 0.0
            max: 1.0
            value: 0.2
            on_value: root.set_volume(self.value)
            value_track: True
            value_track_color: (1,0,0,1)
            orientation: "vertical"
            size_hint_x: None
            width: "48dp"
        Label:
            text: str(round(slider.value * 100))
            pos_hint:{'center_x':0.05, 'center_y':.9}

"""


class SoundTestBoxLayout(FloatLayout):
    paused = BooleanProperty(
False)
    player_state = StringProperty(
'ready'# can be stop, pause, or ready (ready is not stop or pause)

   
def __init__(self
, **kwargs):
       
# self.song_position = None
       
self.song = None
       
self.song_index = 0
       
self.stop_pressed = False
       
self.song_position = 0
       
self.paused = False
       
self.player_state = 'ready'
       
self.songs = glob('music\*.mp3')
       
self.event = None
       
super().__init__(**kwargs)

   
def play(self):
       
if self.player_state == 'pause':
           
self.song.play()
           
self.song.seek(self.song_position)
           
print('seek position is:- %s' % (self.song.seek(self.song_position)))
           
self.player_state = 'ready'
           
return
        if
self.song:
           
if self.song.state == 'play':
               
self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)  # when the on_stop event fires, execute song_end
       
self.song.volume = 0.2
       
self.song.play()
       
self.player_state = 'ready'
       
self.song_name = self.songs[self.song_index]
       
self.event = Clock.schedule_interval(self.play_status, 0.1)

   
def play_status(self, _):
       
if self.player_state == 'pause':
           
return
        if
self.song:
            t =
int(self.song.get_pos())
            mins =
int(t / 60)
            secs = t %
60
           
self.ids.val.text = f'{mins:02}:{secs:02}'
           
self.ids.pb.max = int(self.song.length)
           
self.ids.pb.value = int(self.song.get_pos())


   
def set_volume(self, volume):
       
self.volume = volume
       
if self.song:
           
self.song.volume = self.volume

   
def stop(self):
       
if self.event:
           
self.event.cancel()  # this is the preferred method of stopping the timer
       
if self.song:
           
self.ids.pb.value = 0
           
self.ids.val.text = '00:00'
           
self.player_state = 'stop'
           
self.song.stop()
           
self.song = None

    def
pause(self):
       
if self.player_state == 'pause':
           
return
        if
self.song:
           
self.player_state = 'pause'
           
print('song name paused is:- %s' % (self.song_name))
           
self.song_position = self.song.get_pos()
           
print('current position after pausing the song is: %s' % (self.song_position))
           
self.song.stop()

   
def song_end(self, *args):
       
print(f'song end event, player_state: {self.player_state}')
       
if self.player_state == 'pause':
           
return
        if not
self.player_state == 'stop':
            
self.next()
       
self.player_state = 'ready'

   
def on_player_state(self, *args):  # this is for debug - it prints out the state when it changes
       
print(f'state: {self.player_state}')


class SoundTestApp(App):
   
def build(self):
        
return Builder.load_string(kv)


SoundTestApp().run()

 

 

 

 

From: Raja Ramesh
Sent: Monday, April 20, 2020 10:37 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

 

I appended progress bar and volume code. Need your inputs for below points,

 

1. progress bar color is not disappearing after completion of song. it's over ridding on top of it for next song.

 2. i am not able to add seek code to progress bar.(i mean play from the drag position of progress bar.) 

 

 

 

Raja Ramesh

unread,
Apr 21, 2020, 2:56:01 AM4/21/20
to kivy-...@googlegroups.com
Hi Elliot,

Now  progress bar is not overriding for next song. 

How to perform drag / seek to certain position on progress bar and continue playing from that dragged / seek position. similar to attached gif file. it is for video but i need to perform same functionality for songs.


Thanks,
Raja

playmovies.gif

Elliot Garbus

unread,
Apr 21, 2020, 9:41:20 AM4/21/20
to kivy-...@googlegroups.com

How to perform drag / seek to certain position on progress bar and continue playing from that dragged / seek position. similar to attached gif file. it is for video but i need to perform same functionality for songs.

Replace the ProgressBar with a slider.  When the value of the slider changes (on_value: ) do a seek.

I’ll take a look at the Pb behavior on next song. 

 

When the next song is detected in the end_song method, the pb needs to be reset.

Elliot Garbus

unread,
Apr 21, 2020, 12:26:21 PM4/21/20
to kivy-...@googlegroups.com

You do want to use a Slider, but I realized you can not use on_value.  The on_value event will be firing while the song in playing.  The program is using the slider value like it did the progress bar, updating during song play.  To avoid this I have created a new class called TouchSlider, that extends slide and adds an on_move event.  It only does the seek during when a touch is moving the slider.

 

There is some work to do to adjust the allow a seek from stop.

 

from kivy.app import App

from kivy.lang import Builder

from kivy.uix.boxlayout import BoxLayout

from kivy.properties import StringProperty

from kivy.core.audio import SoundLoader

from kivy.clock import Clock

from kivy.uix.slider import Slider

 

from glob import glob

 

kv = """

<AudioPlayer>:

    orientation: 'vertical'

    BoxLayout:

        size_hint_y: None

        height: 48

        Button:

            text: 'prev'

            on_release: root.previous()

        Button:

            text: 'next'

            on_release: root.next()

        Button:

            text: 'play'

            on_release: root.play()

        Button:

            text: 'pause'

            on_release: root.pause()   

        Button:

            text: 'stop'

            on_release: root.stop()

    BoxLayout:

        Label:

            size_hint_x:.1

            text: root.song_time

        TouchSlider:

            id:pos_slider

            on_move: root.go_to_pos(self.value)

        Label:

            size_hint_x:.1

            text: root.song_time_end

    Label:

        text: root.song_name

        font_size:30

 

BoxLayout:

    orientation: 'vertical'

    Label:

        text: 'Audio File Player'

        font_size: 30

    AudioPlayer:

"""

 

 

class TouchSlider(Slider):

    def __init__(self, **kwargs):

        self.register_event_type('on_release')

        self.register_event_type('on_press')

        self.register_event_type('on_move')

        super().__init__(**kwargs)

 

    def on_press(self):

        pass

 

    def on_release(self):

        pass

 

    def on_move(self):

        pass

 

    def on_touch_down(self, touch):

        super().on_touch_down(touch)

        if self.collide_point(*touch.pos) and self.disabled is False:

            self.dispatch('on_press')

            return True

 

    def on_touch_move(self, touch):

        super().on_touch_move(touch)

        if self.collide_point(*touch.pos) and self.disabled is False:

            self.dispatch('on_move')

            return True

 

    def on_touch_up(self, touch):

        super().on_touch_up(touch)

        if touch.grab_current == self:

            self.dispatch('on_release')

            return True

 

class AudioPlayer(BoxLayout):

    song_name = StringProperty('No Song Selected')

    player_state = StringProperty('ready')  # can be stop, pause, or ready (ready is not stop or pause)

    song_time_end = StringProperty('00:00')

    song_time = StringProperty('00:00')

 

    def __init__(self, **kwargs):

        super().__init__(**kwargs)

        self.song = None

        self.schedule = None

        self.songs = glob('music/*.mp3')  # change to appropriate directory and *.mp3

        self.song_index = 0

        self.song_position = 0

        self.player_state = 'ready'

        print(self.songs)

 

    def play(self):

        if self.player_state == 'pause':

            self.song.play()

            self.song.seek(self.song_position)

            self.player_state = 'ready'

            return

        if self.song:

            if self.song.state == 'play':

                self.player_state = 'stop'  # pressing play while playing... is hitting stop then play

                self.song.stop()

        self.song = SoundLoader.load(self.songs[self.song_index])

        self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end

        self.song.play()

        self.song_name = self.songs[self.song_index]

        print(f'{self.song_name} Loaded, the length is: {self.song.length}')

        self.song_time_end = self.pos_to_min_sec(self.song.length)

        self.schedule = Clock.schedule_interval(self._play_status, 0.1)

 

    def _play_status(self, _):

        if self.player_state == 'pause':

            return

        if self.song:

            self.song_time = self.pos_to_min_sec(self.song.get_pos())

            self.ids.pos_slider.max = int(self.song.length)

            self.ids.pos_slider.value = int(self.song.get_pos())

 

    def go_to_pos(self, p):

        if self.song:

            self.song.seek(p)

 

    @staticmethod

    def pos_to_min_sec(seconds):

        t = int(seconds)

        mins = int(t / 60)

        secs = t % 60

        return f'{mins:02}:{secs:02}'

 

    def next(self):

        self.player_state = 'ready'

        self.song_index = min(self.song_index + 1, len(self.songs) - 1)

        self.play()

 

    def previous(self):

        self.player_state = 'ready'

        self.song_index = max(self.song_index - 1, 0)

        self.play()

 

    def stop(self):

        if self.schedule:

            self.schedule.cancel()

        if self.song:

            self.ids.pos_slider.value = 0

            self.song_time = '00:00'

            self.player_state = 'stop'

            self.song.stop()

            self.song = None

 

    def pause(self):

        if self.player_state == 'pause':

            return

        if self.song:

            self.player_state = 'pause'

            self.song_position = self.song.get_pos()

            self.song.stop()

            print(f'The Song Paused at: {self.song_position}')

 

    def song_end(self, *args):

        print(f'song end event, player_state: {self.player_state}')

        if self.player_state == 'pause':

            return

        if not self.player_state == 'stop':

            self.next()

        self.player_state = 'ready'

 

    def on_player_state(self, *args):          # this is for debug - it prints out the state when it changes

        print(f'state: {self.player_state}')

 

 

class AudioPlayerApp(App):

    def build(self):

        return Builder.load_string(kv)

 

 

AudioPlayerApp().run()

 

From: Elliot Garbus
Sent: Tuesday, April 21, 2020 6:41 AM
To: kivy-...@googlegroups.com

Elliot Garbus

unread,
Apr 21, 2020, 1:49:28 PM4/21/20
to kivy-...@googlegroups.com

Added a few refinements:

  1. The song time on the left side of the progressbar was wiggling as the width of the field changes, I put it in a relative layout and set the position to avoid the re-centering that was causing the wiggle.
  2. The slider was not moving slowly, but clicking every second.  Increased the granularity by 10x to create smoother motion.

        RelativeLayout:  # Put the label in a relative layout to prevent wiggling, as time changes

            size_hint_x: .1

            Label:

                text: root.song_time

                pos_hint: {'x': .02, 'center_y': .5}

        TouchSlider:

            id:pos_slider

            on_move: root.go_to_pos(self.value)

            on_release: root.go_to_pos(self.value)

    def resume_from_pause(self):

        self.song.play()

        self.song.seek(self.song_position)

        self.player_state = 'ready'

        self.schedule = Clock.schedule_interval(self._play_status, 0.1)

 

    def play(self):

        if self.player_state == 'pause':

            self.resume_from_pause()

            return

        if self.song:

            if self.song.state == 'play':

                self.player_state = 'stop'  # pressing play while playing... is hitting stop then play

                self.song.stop()

        self.song = SoundLoader.load(self.songs[self.song_index])

        self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end

        self.song.play()

        self.song_name = self.songs[self.song_index]

        print(f'{self.song_name} Loaded, the length is: {self.song.length}')

        self.song_time_end = self.pos_to_min_sec(self.song.length)

        self.schedule = Clock.schedule_interval(self._play_status, 0.1)

 

    def _play_status(self, _):

        if self.player_state == 'pause':

            return

        if self.song:

            self.song_time = self.pos_to_min_sec(self.song.get_pos())

            self.ids.pos_slider.max = int(self.song.length * 10)   # Increased slider granularity for smoother movement

            self.ids.pos_slider.value = int(self.song.get_pos() * 10)

 

    def go_to_pos(self, p):

        p = p / 10           # slider value is postion * 10

        if self.song:

            self.song_time = self.pos_to_min_sec(p)

            if self.song.state == 'play':

                self.song.seek(p)

            else:

                self.player_state = 'pause'

                self.song_position = p

                self.song.play()

                self.song.seek(p)

                self.song.stop()

 

    @staticmethod

    def pos_to_min_sec(seconds):

        t = int(seconds)

        mins = int(t / 60)

        secs = t % 60

        return f'{mins:02}:{secs:02}'

 

    def next(self):

        self.player_state = 'ready'

        self.song_index = min(self.song_index + 1, len(self.songs) - 1)

        self.play()

 

    def previous(self):

        self.player_state = 'ready'

        self.song_index = max(self.song_index - 1, 0)

        self.play()

 

    def stop(self):

        if self.schedule:

            self.schedule.cancel()

        if self.song:

            self.ids.pos_slider.value = 0

            self.song_time = '00:00'

            self.player_state = 'stop'

            self.song.stop()

 

    def pause(self):

Raja Ramesh

unread,
Apr 21, 2020, 1:53:45 PM4/21/20
to kivy-...@googlegroups.com
Thank you very much Elliot. 

Raja Ramesh

unread,
Apr 22, 2020, 11:34:24 AM4/22/20
to kivy-...@googlegroups.com
Hi Elliot,

currently i am going through your code will get in touch soon.

Thanks,
Raja

Raja Ramesh

unread,
Apr 26, 2020, 8:02:29 AM4/26/20
to kivy-...@googlegroups.com
Hi Elliot,

i am trying to append album image of a song while playing with below code. but the image of first song is displaying for all songs instead of updating as per the song. Also the image is not fitting to the position which i included in kv.

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from mutagen import File
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from glob import glob
from kivy.uix.slider import Slider
import os

kv = '''

Mywidget:
    orientation: 'vertical'
   
    BoxLayout:        
        source: root.image()
        size_hint: .3,.2
        pos_hint:{'center_x':.5, 'center_y': .5}
    Label:
        text: root.song_name
        size_hint: .3, .2
        pos_hint:{'center_x': .1,'center_y':0.95}
   
    RelativeLayout:
        Label:
            #text:'00:00'
            text: root.song_time
            size_hint: .3, .2
            pos_hint: {'center_x':.082,'center_y': .14}
        TouchSlider:
            id:pos_slider
            value_track: True
            value_track_color: (1,0,0,1)
            size_hint: 0.64, 0.23
            pos_hint: {'center_x':.44, 'center_y': .14}
            on_press: root.go_to_pos(self.value)
       
        Label:
            text:'00:00'
            text: root.song_time_end
            size_hint: .3, .2
            pos_hint: {'center_x':.8,'center_y': .14}
    Button:
        text: 'prev'
        size_hint: .1, .07
        pos_hint:{'center_x':.2, 'center_y':.07}
        on_press: root.previous()
   
    Button:
        text: 'next'
        size_hint: .1, .07
        pos_hint:{'center_x':.3, 'center_y':.07}
        on_press: root.next()
       
    Button:
        text: 'play'
        size_hint: .1, .07
        pos_hint:{'center_x':.4, 'center_y':.07}

        on_press: root.play()
   
    Button:
        text: 'pause'
        size_hint: .1, .07
        pos_hint:{'center_x':.5, 'center_y':.07}
        on_press: root.pause()
   
    Button:
        text: 'stop'
        size_hint: .1, .07
        pos_hint:{'center_x':.6, 'center_y':.07}
        on_press: root.stop()
       
'''

class TouchSlider(Slider):
    def __init__(self, **kwargs):
        self.register_event_type('on_release')
        self.register_event_type('on_press')
        self.register_event_type('on_move')

        super().__init__(**kwargs)
    def on_press(self):
        pass
    def on_release(self):
        pass
    def on_move(self):
        pass
    def on_touch_down(self, touch):
        super().on_touch_down(touch)
        if self.collide_point(*touch.pos) and self.disabled is False:
            self.dispatch('on_press')
            return True

class Mywidget(FloatLayout):
    #pass

    song_name = StringProperty('No Song Selected')
    player_state = StringProperty('ready')  # can be stop, pause, or ready (ready is not stop or pause)
    song_time_end = StringProperty('00:00')
    song_time = StringProperty('00:00')
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.song = None
        self.schedule = None
        self.songs = glob(r'music\*.mp3')

        self.song_index = 0
        self.song_position = 0
        self.player_state = 'ready'
        print(self.songs)


    def play(self):
        if self.player_state == 'pause':
            self.song.play()
            self.song.seek(self.song_position)
            self.player_state = 'ready'
            return
        if self.song:
            if self.song.state == 'play':
                self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
                self.song.stop()
        self.song = SoundLoader.load(self.songs[self.song_index])
        self.song.bind(on_stop=self.song_end)            # when the on_stop event fires, execute song_end
        self.song.play()
        self.song_name = self.songs[self.song_index]
        print(f'{self.song_name} Loaded, the length is: {self.song.length}')
        self.song_time_end = self.pos_to_min_sec(self.song.length)
        self.schedule = Clock.schedule_interval(self._play_status, 0.1)

    def image(self):
        # Below code is for mp3 image
        self.destination = r'img\image_NEW.jpg'  # for image saving
        self.album = File(self.songs[self.song_index])

        self.cover = self.album['APIC:'].data
        with open(self.destination, 'wb') as img:
            img.write(self.cover)
            img.close()

        self.mp3_img = Image(source = self.destination)
        self.add_widget(self.mp3_img)
        
#class MyImage(Screen):
#    pass

class AudioImage(App):
    def build(self):
        return Builder.load_string(kv)
        #return MyGrid()
if __name__=='__main__':
    AudioImage().run()


Thanks,
raja

Elliot Garbus

unread,
Apr 26, 2020, 11:23:28 AM4/26/20
to kivy-...@googlegroups.com

I made a few changes to display the album art in the code below.

Read the comments.

 

 

 

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from mutagen import File
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from glob import glob
from kivy.uix.slider import Slider
import os

kv =
'''

Mywidget:
    orientation: 'vertical'

    Image:                                  # Used an image, not a boxlayout
        id: album_image
        source:  root.album_art             # Used a property for the file name
        allow_stretch: True
        # size_hint: .3,.2                  # Allow the art to fill the window
)
    album_art = StringProperty(
'img\image_NEW.jpg')                # Added a property *** PUT DEFAULT IMAGE HERE ****

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.song = None
       
self.schedule = None
       
self.songs = glob(r'music\*.mp3')
       
self.song_index = 0
       
self.song_position = 0
       
self.player_state = 'ready'
       
print(self.songs)

   
def play(self):
       
if self.player_state == 'pause':
           
self.song.play()
           
self.song.seek(self.song_position)
           
self.player_state = 'ready'
           
return
        if
self.song:
           
if self.song.state == 'play':
               
self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
       
self.song.bind(on_stop=self.song_end)  # when the on_stop event fires, execute song_end
        
self.song.play()
       
self.song_name = self.songs[self.song_index]
       
print(f'{self.song_name} Loaded, the length is: {self.song.length}')
       
self.song_time_end = self.pos_to_min_sec(self.song.length)
       
self.schedule = Clock.schedule_interval(self._play_status, 0.1)
       
self.get_image()
       
self.ids.album_image.reload() # reload required filename stays the same

   
def get_image(self):
       
# Below code is for mp3 image
       
self.destination = 'img\image_NEW.jpg'  # for image saving
       
self.album = File(self.songs[self.song_index])
       
self.cover = self.album['APIC:'].data
       
with open(self.destination, 'wb') as img:
            img.write(
self.cover)
           
# img.close()                             the context manager closes the file
        # self.mp3_img = Image(source=self.destination)
        # self.add_widget(self.mp3_img)
       
self.album_art = 'img\image_NEW.jpg'

   
def _play_status(self, _):
       
if self.player_state == 'pause':
           
return
        if
self.song:
           
self.song_time = self.pos_to_min_sec(self.song.get_pos())
           
self.ids.pos_slider.max = int(self.song.length)
           
self.ids.pos_slider.value = int(self.song.get_pos())

   
def go_to_pos(self, p):
       
if self.song:
           
self.song.seek(p)

   
@staticmethod
   
def pos_to_min_sec(seconds):
        t =
int(seconds)
        mins =
int(t / 60)
        secs = t %
60
       
return f'{mins:02}:{secs:02}'

   
def next(self):
        
self.player_state = 'ready'
       
self.song_index = min(self.song_index + 1, len(self.songs) - 1)
       
self.play()

   
def previous(self):
       
self.player_state = 'ready'
       
self.song_index = max(self.song_index - 1, 0)
       
self.play()

   
def stop(self):
       
if self.schedule:
           
self.schedule.cancel()
       
if self.song:
           
self.ids.pos_slider.value = 0
           
self.song_time = '00:00'
           
self.player_state = 'stop'
           
self.song.stop()
           
self.song = None

    def
pause(self):
       
if self.player_state == 'pause':
           
return
        if
self.song:
           
self.player_state = 'pause'
           
self.song_position = self.song.get_pos()
           
self.song.stop()
           
print(f'The Song Paused at: {self.song_position}')

   
def song_end(self, *args):
       
print(f'song end event, player_state: {self.player_state}')
       
if self.player_state == 'pause':
           
return
        if not
self.player_state == 'stop':
           
self.next()
       
self.player_state = 'ready'

   
def on_player_state(self, *args):  # this is for debug - it prints out the state when it changes
       
print(f'state: {self.player_state}')


# class MyImage(Screen):
#    pass

class AudioImage(App):
   
def build(self):
       
return Builder.load_string(kv)
       
# return MyGrid()


if __name__ == '__main__':
    AudioImage().run()

Raja Ramesh

unread,
Apr 26, 2020, 1:09:30 PM4/26/20
to kivy-...@googlegroups.com
Hi Elliot,

Now image is updating as per the songs. But the app is playing last song (repeating same song) after reaching end of the play count. for example, assume we have 3 mp3 files in the path(music/.*mp3), the app play all three songs and continue playing last song(i,e third song) instead of playing from beginning(i.e from first song). i append self.song.loop = True  in the play method. but it loops first song.    


   
def play(self):
       
if self.player_state == 'pause':
           
self.song.play()
           
self.song.seek(self.song_position)
           
self.player_state = 'ready'
           
return
        if
self.song:
           
if self.song.state == 'play':
               
self.player_state = 'stop'  # pressing play while playing... is hitting stop then play
               
self.song.stop()
       
self.song = SoundLoader.load(self.songs[self.song_index])
        self.song.loop = True 
       
self.song.bind(on_stop=self.song_end)  # when the on_stop event fires, execute song_end
        
self.song.play()
       


Thanks,
Raja

Elliot Garbus

unread,
Apr 26, 2020, 1:27:18 PM4/26/20
to kivy-...@googlegroups.com

Update what happens on song_end, now the song will stop after the last song.

 

def song_end(self, *args):
   
print(f'song end event, player_state: {self.player_state}')
   
if self.player_state == 'pause':
       
return
    if not
self.player_state == 'stop' and self.song_index != len(self.songs) -1:  # check if we are not at the last song…
        
self.next()
   
self.player_state = 'ready'

 

If you want the songs to play in a loop, update next() and previous() to loop instead of stopping at min or max.

Raja Ramesh

unread,
Apr 27, 2020, 6:55:03 AM4/27/20
to kivy-...@googlegroups.com
Hi Elliot,

To loop the songs i have updated as below in next() method but the songs are not looping,

self.player.state == 'Ready'
self.song_index != min(self.song_index+1, len(self.songs)-1)
 

Thanks,
raja

Elliot Garbus

unread,
Apr 27, 2020, 8:37:37 AM4/27/20
to kivy-...@googlegroups.com

The methods next() and pervious() are using min() and max() respectively as a clamp.  You can go to the end, but no higher or no lower.

Now we want to reset the index when next hits the last song.  Here iis an updated next(), use similar logic to update previous.

 

def next(self):
   
self.player_state = 'ready'
   
#self.song_index = min(self.song_index + 1, len(self.songs) - 1
   
if self.song_index < len(self.songs) -1:
       
self.song_index += 1
   
else:
       
self.song_index = 0

    self.play()

 

Alternately you can use a ternary expression.


def next(self):
   
self.player_state = 'ready'
   
#self.song_index = min(self.song_index + 1, len(self.songs) - 1)
   
self.song_index = self.song_index + 1 if self.song_index < len(self.songs) - 1 else 0   # this is the same as the if else statement
    self.play()
 
from kivy.app import App

Thanks,

Raja Ramesh

unread,
Apr 27, 2020, 10:26:03 AM4/27/20
to kivy-...@googlegroups.com
Thank you Elliot next() and prev() are working as expected. now my next step is to display the songs in playlist. i will try and get back to if am not able to achieve. 

Raja Ramesh

unread,
Apr 28, 2020, 1:48:08 PM4/28/20
to kivy-...@googlegroups.com
Hi Elliot,

i didn't get any examples how to build playlist for an audio on google. so, i guess we can use TextInput method for Playlist(correct me if i am wrong) and build below code but i am getting Attribute error :-  NoneType object has no attribute 'play'

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import StringProperty

kv = '''
mywidget:
    orientation: 'vertical'
    TextInput:
         hint_text: 'Enter Text'
    Button:
          text: 'play'
          size_hint: .3,.2
          pos_hint: {'center_x': .3, 'center_y': .1}
          on_press: root.play()

'''

class mywidget(BoxLayout):
    text = StringProperty()
    def play(self):
        self.song =  SoundLoader.load(self.text)
        self.song.play()

class Audio(App):
    def build(self):
        return Builder.load_string(kv)
Audio().run() 


Thanks,
Raja

Elliot Garbus

unread,
Apr 28, 2020, 2:49:45 PM4/28/20
to kivy-...@googlegroups.com

You could build a play list with a text input.  If you do you would need to split the text on ‘/n’ to create a list of filenames, or songs.  This would be rather error prone, so you would need some error checking (does the song exist in the library?).  If you combined the text input with autocompletion hints it might be kind of interesting.

 

Depending on what you had in mind you could collect all of the music files in a directory, put the meta-data as labels on a RecycleView, and use a text input or spinner to next to each label to determine the order of the songs (or use a next or insert button).  Or going further you could drag and drop the songs on to a playlist.

 

The issue with your code below was that in your play routine you were using self.text and expecting the text input text.  In this context self is the BoxLayout.  I gave the TextInput an id, and used that to access the text.  If you provide a valid song file in the text input this will work.

 

 

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import StringProperty

kv =
'''
MyWidget:
    orientation: 'vertical'
    TextInput:
        id: ti

        hint_text: 'Enter Text'
    Button:
        text: 'play'
        size_hint: .3,.2
        pos_hint: {'center_x': .3, 'center_y': .1}
        on_press: root.play()

'''


class MyWidget(BoxLayout):
    text = StringProperty()

   
def play(self):
       
print(self.ids.ti.text)
       
self.song =  SoundLoader.load(self.ids.ti.text)
       
self.song.play()



class Audio(App):
   
def build(self):
       
return Builder.load_string(kv)
Audio().run()

 

 

 

 

 

From: Raja Ramesh
Sent: Tuesday, April 28, 2020 10:48 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Raja Ramesh

unread,
Apr 29, 2020, 9:09:09 AM4/29/20
to kivy-...@googlegroups.com
Hi Elliot,

After reading the points shared by you....i build a code with drag and drop method with the help of google to play the song and the code is working fine for one song not for multiple songs.  But i am not sure how to keep the files in playlist and play the song one by one by clicking the song from playlist.

from kivy.app import App
from kivy.core.window import Window
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout

kv = '''
drag:
      Label:
              text: ' Music Player'
              pos_hint: { 'center_x': .5, 'center_y': .5}
      Button:
              text: 'play'
              size_hint: .3,.2
              pos_hint: { 'center_x': .4, 'center_y': .1}
              on_press: app.play()
      Button:
              text: 'stop'
              size_hint: .3,.2
              pos_hint: { 'center_x': .7, 'center_y': .1}
              on_press: app.stop()
'''

class drag(FloatLayout):
    pass

class dragdropmusic(App):
     def _on_file_drop(self, window, file_path):
         print(file_path)
         self.file_list = [ ]
         self.file_list.append(file_path.decode('utf-8')
     def play(self):
         self.song_index = 0
         self.song = SoundLoader.load(self.file_list[self.song_index])
         self.song.play()    
    def stop(self):
         self.song.stop()
     def build(self):
         Window.bind(on_dropfile= self._on_file_drop)
         return Builder.load_string(kv)
dragdropmusic().run()


Thanks,
Raja 

Elliot Garbus

unread,
Apr 29, 2020, 11:59:39 AM4/29/20
to kivy-...@googlegroups.com

You are clearing the list when you drag each file.

 

Try something like:

class DragDropMusic(App):
    file_list = ListProperty()
   
def _on_file_drop(self, window, file_path):
       
print(file_path)
       
# self.file_list = [ ]
       
self.file_list.append(file_path.decode('utf-8'))
       
print(self.file_list)

 

This will give you a list of song files.  I imagine you would want to convert the list of filenames to buttons with the song name or perhaps, the album art, artist, and song name…  you can then create the Buttons and put them in a ScrollView.

Raja Ramesh

unread,
Apr 29, 2020, 1:16:40 PM4/29/20
to kivy-...@googlegroups.com
yes Elliot, my next steps were to display the folders on app which contain mp3 files in gridlayout with album art of the songs. Now, i am able to drag and drop the song and by clicking the play button song is playing. 

Now i am trying to understand below recycleview code. but it is not displaying anything after running the code. i got this example from https://kivy.org/doc/stable/api-kivy.uix.recycleview.html  could you please let me know what was wrong. currently my aim is to display the songs which i drag and drop on to the app and play the song on clicking the song from playlist. 
image.png

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView


Builder.load_string('''
<RV>:
    viewclass: 'Label'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
''')

class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(x)} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()

if __name__ == '__main__':
    TestApp().run()

Elliot Garbus

unread,
Apr 29, 2020, 5:48:23 PM4/29/20
to kivy-...@googlegroups.com

You might find this example of a RecycleView more helpful.

Here are a few key concepts.  The RecycleView only has the number of widgets that are visible on the screen.  The widgets are recycled to create the illusion of a long list.  Create a viewclass that represents the object you want on the list.  The values for the attributes of the list come from a list of dictionaries.   You will create a view class that combines the image, artist and song data.

 

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty

kv =
"""
<ThreeLabel>:
    Label:
        text: root.year
    Label:
        text: root.club
    Label:
        text: root.apps

TestBox:
    orientation: 'vertical'
    ThreeLabel:
        year: 'Year'
        club: 'Club'
        apps: 'Apps'
        size_hint_y: None
        height: 48
    RecycleView:
        viewclass: 'ThreeLabel'
        data: root.rv_data_list
        scroll_type: ['bars','content']
        bar_width: dp(20)
        do_scroll_x: False

        RecycleBoxLayout:
            default_size: None, dp(56)
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'
"""


class ThreeLabel(BoxLayout):
    year = StringProperty()
    club = StringProperty()
    apps = StringProperty()


class TestBox(BoxLayout):
    rv_data_list = [{
'year': str(1999 + n), 'club': str(n + 100), 'apps':str(n + 200) } for n in range(100)]


class RVTestApp(App):
   
def build(self):
       
return Builder.load_string(kv)


RVTestApp().run()

 

 

From: Raja Ramesh
Sent: Wednesday, April 29, 2020 10:16 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

yes Elliot, my next steps were to display the folders on app which contain mp3 files in gridlayout with album art of the songs. Now, i am able to drag and drop the song and by clicking the play button song is playing. 

 

Now i am trying to understand below recycleview code. but it is not displaying anything after running the code. i got this example from https://kivy.org/doc/stable/api-kivy.uix.recycleview.html  could you please let me know what was wrong. currently my aim is to display the songs which i drag and drop on to the app and play the song on clicking the song from playlist. 

Raja Ramesh

unread,
Apr 30, 2020, 6:41:35 AM4/30/20
to kivy-...@googlegroups.com
Hi Elliot,

i have modified the code as below and getting Attribute Error: 'str' object has no attribute 'get'. i have tried modifying the ListProperty to StringProperty  and got TypeError: 'str' object is not callable.
 

from kivy.app import App
from kivy,lang import Builder
from kivy.uix.boxlayout import Boxlayout
from kivy.core.window import Window
from kivy.properties import ListProperty, StringProperty

kv = '''

playlist:
    orientation: 'vertical'
    Label: 
        text: 'Songslist'
    RecycleView:
        viewclass: 'playlist'
        data: root.rv_file_list
        scroll_type:  ['bars', 'content']
        bar_width: dp(15)
        do_scroll_x: False
        RecycleBoxLayout:
            default_size: None, dp(56)
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'
'''
class playlist(BoxLayout):
    rv_file_list = ListProperty()
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.bind(on_dropfile = self._on_file_drop)
    
     def _on_file_drop(self, window, file_path)
        self.rv_file_list.append(file_path.decode('utf-8')
        print(self.rv_file_list)

class RVPlayList(App):
    def build(self):
        return Builder.load_string(kv)

RVPlayList().run()

Elliot Garbus

unread,
Apr 30, 2020, 10:05:04 AM4/30/20
to kivy-...@googlegroups.com

There are a few things going on here.

  1. Define your viewclass.  Your view class needs to represent a single element of your list.  You have the view class as a boxlayout containing the Recycleview.

 

You could start simple with:

 

<SongFile>:

    Label:

        text: root.song_file

 

class SongFiile(BoxLayout):

    song_file = StringProperty()

 

and then additional attributes as you get things working.  Take a close look at the view class in the example I sent previously (ThreeLabel).

 

  1. In your on_file_drop code, you are creating a list.  You need to create a list of dicts in the form:

[{‘song_file’: filename}, {‘song_file’: filename},…]

Raja Ramesh

unread,
Apr 30, 2020, 1:19:51 PM4/30/20
to kivy-...@googlegroups.com
hi Elliot,

i did the changes as you suggested and got the songslist whenever i drag and drop on to the app. but i have a doubt on below _on_file_drop method , i come across one of my project with  same scenario  that time i take it light as i  get what i need. but now i would like to know the reason of for loop declaration.

def _on_file_drop(self, window, file_path):
    self.file_list.append(file_path.decode('utf-8')
    for filename in self.file_list:
        self.ids.rv_data_list.data = [{ 'song_file': str(filename)}]  # it gives last file

where-as 

def _on_file_drop(self, window, file_path):
    self.file_list.append(file_path.decode('utf-8')
    self.ids.rv_data_list.data = [{ 'song_file': str(filename)}  for filename in self.file_list]  # it gives all file


from kivy.app import App

from kivy,lang import Builder

from kivy.uix.boxlayout import Boxlayout

from kivy.core.window import Window

from kivy.properties import ListProperty, StringProperty

 

kv = '''

<SongFile>:

    Label:

        text: root.song_file 

playlist:

    orientation: 'vertical'

    SongFile:

        song_file: 'SongList'

        size_hint_y: None

        height: 48

    RecycleView:

        id: rv_data_list

        viewclass: 'SongFile'

        scroll_type:  ['bars', 'content']

        bar_width: dp(15)

        do_scroll_x: False

        RecycleBoxLayout:

            default_size: None, dp(56)

            default_size_hint: 1, None

            size_hint_y: None

            height: self.minimum_height

            orientation: 'vertical'

'''

class SongFile(BoxLayout):

    song_file = StringProperty()

class playlist(BoxLayout):

    file_list = ListProperty()

    

    def __init__(self, **kwargs):

        super().__init__(**kwargs)

        Window.bind(on_dropfile = self._on_file_drop)

    

   def _on_file_drop(self, window, file_path):

        self.file_list.append(file_path.decode('utf-8')
        self.ids.rv_data_list.data = [{ 'song_file': str(filename)}  for filename in self.file_list]

 

class RVPlayList(App):

    def build(self):

        return Builder.load_string(kv)

 

RVPlayList().run()


Thanks,
raja

Elliot Garbus

unread,
Apr 30, 2020, 3:37:26 PM4/30/20
to kivy-...@googlegroups.com

    for filename in self.file_list:

        self.ids.rv_data_list.data = [{ 'song_file': str(filename)}]  # it gives last file

 

In this code above you are writing one data element per iteration, one on top of the other.

 

The code below is a list comprehension.  The it is creating a list, appending more data for each iteration.

Do a search for list comprehension to learn more.

self.ids.rv_data_list.data = [{ 'song_file': str(filename)}  for filename in self.file_list]  # it gives all file

 

It would be more natural just to add to append to the list:

def _on_file_drop(self, window, file_path):
    d = {
'song_file': file_path.decode()}
   
self.rv_file_list.append(d)

 

 

 

From: Raja Ramesh
Sent: Thursday, April 30, 2020 10:19 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

hi Elliot,

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Raja Ramesh

unread,
May 1, 2020, 9:08:39 AM5/1/20
to kivy-...@googlegroups.com
Thank you very much Elliot for sharing the information. it was very helpful. 

Now i am trying to display the playlist in left side of the window in app, having some gap on top and bottom, for this i am trying below in kv, but it was not displaying properly. Also song names are displaying as center aligned in the recycleview. i tried using halign = 'left' in recycleview, but it gives an error saying 'invalid property name' .

kv = '''

<SongFile>:

    Label:

        text: root.song_file 

playlist:

    orientation: 'vertical'

    BoxLayout: 

        size_hint: .3,.2

        pos_hint: {'center_x': .8, 'center_y': .8}   

        SongFile:

            song_file: 'SongList'

            size_hint_y: None

            height: 48

    BoxLayout:

        size_hint: .3,.2

        pos_hint: {'center_x': .8, 'center_y':.8}    

        RecycleView:

            id: rv_data_list

            viewclass: 'SongFile'

            scroll_type:  ['bars', 'content']

            bar_width: dp(15)

            do_scroll_x: False

            RecycleBoxLayout:

                default_size: None, dp(56)

                default_size_hint: 1, None

                size_hint_y: None

                height: self.minimum_height

                orientation: 'vertical'

'''




Regards,
Raja

Elliot Garbus

unread,
May 1, 2020, 9:19:03 AM5/1/20
to kivy-...@googlegroups.com

To change the alignment of the song names. Make the change to the Label in SongFile.

See: https://kivy.org/doc/stable/api-kivy.uix.label.html?highlight=label#text-alignment-and-wrapping

 

To create ‘gaps’ on the top and bottom of the RecycleView, add padding to the enclosing BoxLayout.

Padding can be expressed as 4 values.  Look in the docs for BoxLayout, padding.  Add padding to the top and bottom.

Raja Ramesh

unread,
May 1, 2020, 5:20:15 PM5/1/20
to kivy-...@googlegroups.com
hi elliot,
 
with below  code i am able to manage recycleview . but when i increase padding value , recycleview is decreasing. is there any way to make the recycleview not to decrease. Also the scroll bar is not visible or highlighted  until i place cursor on scrollbar. so, i add color to view. Is there a way to make the scrollbar highlighted without placing cursor?  i like to view the slider dial / knob to appear when i place cursor on the app. could you please help how to do. 


image.png  

<SongFile>:
orientation: 'vertical'
Label:
text: root.song_file
pos_hint: {'center_x':.55, 'center_y':.4}

drag:
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
pos_hint:{'center_x': .8, 'center_y':.7}
padding: [0,200]
SongFile:
song_file: 'SongList'
RecycleView:
orientation: 'vertical'
canvas:
Color:
rgba: (0.524,0.543,.854,.53)
Rectangle:
size: self.size
pos: self.pos
size_hint: .3,.9
pos_hint: {'center_x':.55, 'center_y':.4}
id: rv_data_list
viewclass: 'SongFile'
#data: root.rv_data_list
            scroll_type: ['bars','content']
bar_width: dp(15)
do_scroll_x: False
RecycleBoxLayout:
                default_size: None, dp(36)
                default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

Thanks,
Raja

Elliot Garbus

unread,
May 2, 2020, 9:57:45 AM5/2/20
to kivy-...@googlegroups.com

when i increase padding value , recycleview is decreasing. is there any way to make the recycleview not to decrease.

 

You could give the enclosing boxlayout a size. It looks like you are putting this layout together based on a FloatLayout.  I would encourage you to use a set of nested BoxLayouts, and not use FloatLayout.  This will scale with Window size and require less manual positioning of the widgets.

 

Also the scroll bar is not visible or highlighted  until i place cursor on scrollbar. so, i add color to view. Is there a way to make the scrollbar highlighted without placing cursor? 

 

When you look at the documentation for RecycleView, you will see it is derived from ScrollView.

 

 

Inside ScrollView you will find the attributes: bar_color and bar_inactive_color.  Use those attributes to change the color of the scrollbar.

 

To change the text alignment in the long list:

 

<SongFile>:
    orientation: 'vertical'
    Label:
        text: root.song_file
        text_size: self.size
        haligh: 'left'
        valign: 'center'
        padding: 5, 0



From: Raja Ramesh
Sent: Friday, May 1, 2020 2:20 PM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

hi elliot,

 

with below  code i am able to manage recycleview . but when i increase padding value , recycleview is decreasing. is there any way to make the recycleview not to decrease. Also the scroll bar is not visible or highlighted  until i place cursor on scrollbar. so, i add color to view. Is there a way to make the scrollbar highlighted without placing cursor?  i like to view the slider dial / knob to appear when i place cursor on the app. could you please help how to do. 

 

 

  

Raja Ramesh

unread,
May 8, 2020, 11:48:08 AM5/8/20
to kivy-...@googlegroups.com
Hi Elliot,

Now i am trying to implement music player using BoxLayout as per your suggestion in previous post.  with below code i am able to implement button and slider code. But i am not able to implement the code to display image of album art and songslist side - by- side. for this we need to use orientation :'horizontal'  in BoxLayout. could you please suggest how to use.

image.png

from kivy.app import App

from kivy.core.window import Window
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.slider import Slider
from kivy.properties import ListProperty, StringProperty
from mutagen import File
import os
from glob import glob

kv = '''
    
<SongFile>:
orientation: 'vertical'
Label:
text: root.song_file
pos_hint: {'center_x':.55, 'center_y':.4}
        text_size: self.size
haligh: 'left'
valign: 'center'
padding: 5, 0

<drag>:
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
        #Image:
# id: album_image
# source: root.album_art
# allow_strech: True
# size_hint: 100,10
# pos_hint: {'center_x':.6, 'center_y': .5}
        SongFile: 
song_file: 'SongList'
RecycleView:
orientation: 'vertical'
            pos_hint: {'center_x':.55, 'center_y':.4}            
id: rv_data_list
viewclass: 'SongFile'
#data: root.rv_data_list
scroll_type: ['bars','content']
            bar_color: 1,0,0,1
bar_inactive_color: 1,0,0,0.5
            bar_width: dp(15)
do_scroll_x: False
RecycleBoxLayout:
default_size: None, dp(36)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

    BoxLayout:
size_hint_y: None
        Label:
#text: '00:00'
text: root.song_time
size_hint: .3,.2
            pos_hint: {'center_x':.1, 'center_y':.4}
        TouchSlider:
id:pos_slider
value_track: True
value_track_color: (1,0,0,1)
            size_hint: 2.8, 0.2
pos_hint: {'center_x':.01, 'center_y':.4}
on_press: root.go_to_pos(self.value)
Label:
#text:'00:00'
            text: root.song_time_end
size_hint: .3, .2
            pos_hint: {'center_x':.1,'center_y': .4}

BoxLayout:
#orientation:'vertical'
size_hint_y: None
#padding: [150,105]
height: 40
#cols:2

Button:
text: 'prev'
on_press: root.previous()
Button:
text: 'pause'
on_press: root.pause()
Button:
text: 'play'
on_press: root.play()
Button:
text: 'next'
on_press: root.next()
Button:
text: 'stop'
on_press: root.stop()
      

BoxLayout:
orientation: 'vertical'
Label:
text: 'Audio File Player'
font_size: 30
        size_hint: .3,.2
pos_hint: {'center_x':.5, 'center_y':.9}

drag:
padding:[130,70]
    
'''

class SongFile(BoxLayout):
song_file = StringProperty()

class TouchSlider(Slider):
def __init__(self, **kwargs):
self.register_event_type('on_release')
self.register_event_type('on_press')
self.register_event_type('on_move')

super().__init__(**kwargs)
def on_press(self):
pass
def on_release(self):
pass
def on_move(self):
pass
def on_touch_down(self, touch):
super().on_touch_down(touch)
if self.collide_point(*touch.pos) and self.disabled is False:
self.dispatch('on_press')
return True

class drag(BoxLayout):
#pass
songs = ListProperty() # use this when using drag and drop method
    player_state = StringProperty('ready')
    song_time_end = StringProperty('00:00')
song_time = StringProperty('00:00')
    album_art = StringProperty(r'music_logo.png')  # image_NEW.jpg # Added a property ***Put default Image Here***.
    def __init__(self, **kwargs):
super().__init__(**kwargs)
self.song = None
        self.song_index = 0
self.stop_pressed = False
self.song_position = 0
self.paused = False
self.player_state = 'ready'
        Window.bind(on_dropfile=self._on_file_drop) 
    def _on_file_drop(self, window, file_path):
self.songs.append(file_path.decode('utf-8'))
print(self.songs)
self.ids.rv_data_list.data = [{'song_file': str(os.path.basename(filename))} for filename in self.songs]



def play(self):
if self.player_state == 'pause':
self.song.play()
self.song.seek(self.song_position)
print('seek position is:- %s' % (self.song.seek(self.song_position)))
self.player_state = 'ready'
return
if self.song:
if self.song.state == 'play':
self.player_state = 'stop' # pressing play while playing... is hitting stop then play
self.song.stop()
self.song = SoundLoader.load(self.songs[self.song_index])
self.song.bind(on_stop=self.song_end) # when the on_stop event fires, execute song_end
if self.song:
            #self.song.volume = 0.2
            self.song.play()
self.song_name = self.songs[self.song_index]
            self.song_time_end = self.pos_to_min_sec(self.song.length)
self.schedule = Clock.schedule_interval(self._play_status, 0.1)
            self.image()    # calling image method to update image
self.ids.album_image.reload() # this will change the image of song when song changes. if this is not include the filename stays the same with first song.

def image(self):

self.destination = r'image_NEW.jpg' # for image saving
print(f'current song name:- {os.path.basename(self.songs[self.song_index])}')

self.album = File(self.songs[self.song_index])
self.cover = self.album['APIC:'].data
with open(self.destination, 'wb') as img:
img.write(self.cover)
        self.album_art = r'image_NEW.jpg' 

def next(self):
# related to next

def previous(self):
#related to previous

def stop(self):

def pause(self):

class dragdropmusic(App):

def build(self):
return Builder.load_string(kv)

dragdropmusic().run()  


Thanks,
Raja

Elliot Garbus

unread,
May 8, 2020, 12:20:18 PM5/8/20
to kivy-...@googlegroups.com

Do a sketch of where you want the different elements on the screen.

 

From: Raja Ramesh
Sent: Friday, May 8, 2020 8:48 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

 

Now i am trying to implement music player using BoxLayout as per your suggestion in previous post.  with below code i am able to implement button and slider code. But i am not able to implement the code to display image of album art and songslist side - by- side. for this we need to use orientation :'horizontal'  in BoxLayout. could you please suggest how to use.

 

Raja Ramesh

unread,
May 9, 2020, 2:16:51 AM5/9/20
to kivy-...@googlegroups.com
Hi Elliot,

i would like to view the app as below, 

image.png

Thanks,
Raja

Elliot Garbus

unread,
May 9, 2020, 9:30:48 AM5/9/20
to kivy-...@googlegroups.com

In the image below, Have drawn blue boxes to show the major layout elements.

Overall there is a vertical BoxLayout.

In the large panel in the middle there is a horizontal BoxLayout.  The trick is to create a blank or placeholder for the space on the left.  You could use a label with no text as a placeholder.  Something like:

 

BoxLayout:  # This is for the 3 items in the big panel

    Label: # used a placeholder

    Image:

        Source: ‘album_art.png’

    Playlist:

 

 

 

 

From: Raja Ramesh
Sent: Friday, May 8, 2020 11:16 PM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

 

i would like to view the app as below, 

 

 

Thanks,

Raja

 

On Fri, May 8, 2020 at 9:50 PM Elliot Garbus <elli...@cox.net> wrote:

Do a sketch of where you want the different elements on the screen.

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Raja Ramesh

unread,
May 9, 2020, 1:32:12 PM5/9/20
to kivy-...@googlegroups.com
Hi Elliot

i have updated the code as below and the app looks like image_1.png.
But i would like the app to look like image_2.png. for this i have
update code with Label before image and it is not working as expected.
Any suggestions.

BoxLayout:
Image:
id: album_image
source: root.album_art
allow_strech: True
# size_hint: 100,10
# pos_hint: {'center_x':.6, 'center_y': .5}

SongFile:
song_file: 'SongList'
RecycleView:
orientation: 'vertical'
#canvas:
# Color:
# rgba: (0.524,0.543,.854,.53)
# Rectangle:
# size: self.size
# pos: self.pos
#size_hint: 2,.9
pos_hint: {'center_x':.55, 'center_y':.4}
id: rv_data_list
viewclass: 'SongFile'
#data: root.rv_data_list
scroll_type: ['bars','content']
bar_color: 1,0,0,1
bar_inactive_color: 1,0,0,0.5
bar_width: dp(15)
do_scroll_x: False
RecycleBoxLayout:
default_size: None, dp(36)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

Thanks,
Raja

On 5/9/20, Elliot Garbus <elli...@cox.net> wrote:
> In the image below, Have drawn blue boxes to show the major layout
> elements.
> Overall there is a vertical BoxLayout.
> In the large panel in the middle there is a horizontal BoxLayout. The trick
> is to create a blank or placeholder for the space on the left. You could
> use a label with no text as a placeholder. Something like:
>
> BoxLayout: # This is for the 3 items in the big panel
> Label: # used a placeholder
> Image:
> Source: ‘album_art.png’
> Playlist:
>
>
>
>
>
> From: Raja Ramesh
> Sent: Friday, May 8, 2020 11:16 PM
> To: kivy-...@googlegroups.com
> Subject: Re: [kivy-users] play audio from stop position in kivy
>
> Hi Elliot,
>
> i would like to view the app as below,
>
>
>
> Thanks,
> Raja
>
> On Fri, May 8, 2020 at 9:50 PM Elliot Garbus <elli...@cox.net> wrote:
> Do a sketch of where you want the different elements on the screen.
> --
> You received this message because you are subscribed to the Google Groups
> "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to kivy-users+...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/kivy-users/CABDb-VhDrC4W7BXXbDe%2BSoYRL8gM2Dvvhtb_qx%2B0YL_mAAYMqA%40mail.gmail.com.
>
> --
> You received this message because you are subscribed to the Google Groups
> "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to kivy-users+...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/kivy-users/5eb6b07e.1c69fb81.75bc3.0e35SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>
image_2.png
image_1.png

Elliot Garbus

unread,
May 9, 2020, 1:59:00 PM5/9/20
to kivy-...@googlegroups.com

Combine the SongFile and RecycleView together in a Vertical BoxLayout, under the Horizontal BoxLayout

Raja Ramesh

unread,
May 10, 2020, 3:18:58 AM5/10/20
to kivy-...@googlegroups.com
hi Elliot,

Now i am trying to update default image with below code if the MP3 file has no image, but it is not working.
def image(self):
# Below code is for mp3 image

    self.destination = r'c:\my files\image_NEW.jpg'  # for image saving
print(f'current song has album art:- {os.path.basename(self.songs[self.song_index])}')

self.album = File(self.songs[self.song_index])
self.cover = self.album['APIC:'].data
    if self.cover:

with open(self.destination, 'wb') as img:
img.write(self.cover)
        self.album_art = r'c:\my files\image_NEW.jpg' # this will update the image of song when song changes and replace in required place to display image on app.
else:
print(f'current song has no album art:- {os.path.basename(self.songs[self.song_index])}')
self.album = File(r'C:\my files\music_logo.png')

self.cover = self.album['APIC:'].data
with open(self.destination, 'wb') as img:
img.write(self.cover)
        self.album_art = r'c:\my files\image_NEW.jpg'

Thanks,
Raja

Elliot Garbus

unread,
May 10, 2020, 11:35:28 AM5/10/20
to kivy-...@googlegroups.com

In the else clause, can you just set the Image to  r'C:\my files\music_logo.png?

 

Are you executing the else clause?

Raja Ramesh

unread,
May 10, 2020, 2:14:56 PM5/10/20
to kivy-...@googlegroups.com
Hi Elliot,

yes, i am executing the else clause, but i am getting KeyError: 'APIC:' error.
> https://groups.google.com/d/msgid/kivy-users/5eb81f36.1c69fb81.93eb.f3d1SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>

Elliot Garbus

unread,
May 10, 2020, 3:35:06 PM5/10/20
to kivy-...@googlegroups.com

Perhaps I don’t understand some of the other logic in you app, It seems you could do something like: 

 

   else:

      print(f'current song has no album art:-{os.path.basename(self.songs[self.song_index])}')

      with open(self.destination, 'wb') as img:

         img.write(r'C:\my files\music_logo.png')

      self.album_art = r'c:\my files\image_NEW.png'

 

OR

 

  else:

      print(f'current song has no album art:-{os.path.basename(self.songs[self.song_index])}')

      self.album_art = r'C:\my files\music_logo.png'

 

Also note that you were switching file extensions between png and jpg – this won’t work.

Raja Ramesh

unread,
May 11, 2020, 12:14:49 PM5/11/20
to kivy-...@googlegroups.com
Hi Elliot,

i have  modified the logic and the app is changing the image as per the song. But for few songs which i recorded from youtube, it gives an error saying 'mutagen.id3._util.ID3NoHeaderError: 'bit song.mp3' doesn't start with an ID3 tag'  . i am googling for this .Also how to make as below by right clicking an MP3 file.
image.png


Thanks,
Raja

Elliot Garbus

unread,
May 11, 2020, 12:27:47 PM5/11/20
to kivy-...@googlegroups.com

To make the right click menu will take a few steps.

Use Config.set to turn off multi-touch:

Config.set('input', 'mouse', 'mouse,disable_multitouch')

 

You will need to create an on_touch_down() and on_touch_up() method for each widget in  the song list that uses the right click.

The context menu will be a a set of labels (or buttons) in a FloatLayout.

 

 

From: Raja Ramesh
Sent: Monday, May 11, 2020 9:14 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

 

i have  modified the logic and the app is changing the image as per the song. But for few songs which i recorded from youtube, it gives an error saying 'mutagen.id3._util.ID3NoHeaderError: 'bit song.mp3' doesn't start with an ID3 tag'  . i am googling for this .Also how to make as below by right clicking an MP3 file.

 

 

Thanks,

Raja

image.png

Raja Ramesh

unread,
May 11, 2020, 12:52:30 PM5/11/20
to kivy-...@googlegroups.com
Hi Elliot,

could you please share any example.

Thanks,
Raja

Elliot Garbus

unread,
May 11, 2020, 1:26:02 PM5/11/20
to kivy-...@googlegroups.com

Here is an example of a custom widget I created that uses a right click. 

Look at the on_touch_down() method to see how the right click is captured.  You can run this file stand alone to create some experiments.

 

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty, NumericProperty, StringProperty
from kivy.lang import Builder

Builder.load_string(
'''
#-------------------------------
# Knob class
#  Properties are: text, values, value
#
#-------------------------------
#: set black [0, 0, 0, 1]
#: set white [1, 1, 1, 1]
<CircleKnob>
    orientation: 'vertical'
    opacity: .25 if root.disabled else 1
    Label:
        font_size: '25sp'
        color: root.text_color
        text: root.values[root.value]

        canvas:
            Color:
                rgba: root.arc_foreground
            Line:        
                circle: self.center_x, self.center_y, self.height/2 *.9, -140, 140
                cap: 'square'
                width: dp(3)
        canvas.after:
            Color:
                rgba: root.arc_background
            Line:        
                circle:
                    (
                    self.center_x, self.center_y,
                    self.height/2 *.9,
                    -140, -140 + root.value * 280 / ( len(root.values) - 1 )
                    )
                cap: 'square'
                width: dp(3)
    Label:
        font_size: '20sp'
        text: root.text
        size: self.texture_size
        size_hint_y: None
        color: root.text_color
#-------------------------------
'''
)


class CircleKnob(BoxLayout):
    text = StringProperty()
    arc_background = ListProperty([
1, 1, 1, 1])
    arc_foreground = ListProperty([
0, 0, 0, 1])
    text_color = ListProperty([
0, 0, 0, 1])
    values = ListProperty([
str(i) for i in range(128)])
    value = NumericProperty(
0)
    mouse_set_value = NumericProperty(
0# set when a mouse moves, triggers sending a midi msg
   
right_click_value = NumericProperty(0)
    _scroll_direction = {
'scrollup': 1, 'scrolldown': -1}

   
def on_touch_down(self, touch):
       
if self.collide_point(*touch.pos) and self.disabled is False:
            
if touch.device == 'mouse' and touch.button == 'right':
               
self.value = self.right_click_value
           
else:
                touch.grab(
self)
           
return True
        return False

    def
on_touch_move(self, touch):
        
if touch.grab_current is self and touch.dy and self.disabled is False:
           
#  sorted(min, val, max)[1] works to clamp val to floor or ceiling
           
self.value = (sorted((0, self.value + int(touch.dy), len(self.values) - 1))[1])
            
self.mouse_set_value = self.value
           
return True
        return False

    def
on_touch_up(self, touch):
       
if touch.is_mouse_scrolling and touch.grab_current is self and self.disabled is False:
           
# sorted(min, val, max)[1] works to clamp val to floor or ceiling
           
self.value = (sorted((0, self.value + self._scroll_direction.get(touch.button, 0),
                                 
len(self.values) - 1))[1])
           
self.mouse_set_value = self.value
           
return True
        elif
touch.grab_current is self:
            touch.ungrab(
self)
           
return True
        return False

   
@property
   
def knob_value(self):
       
return self.value

   
@knob_value.setter
   
def knob_value(self, v):
       
self.value = v


if __name__ == '__main__':
    kv_test =
'''
BoxLayout:
    orientation: 'vertical'
    canvas:
        Color:
            rgb: .7, .7, .7
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        size_hint_y: .1
        Button:
            text: "CircleKnob Test"

    GridLayout:
        rows: 2
        cols: 5

        CircleKnob:
            text: "Pulse Width"
            values:  [str(x) for x in range(101)]
        CircleKnob:
            values: [str(x) for x in range(-24, 25)]
            value: 24
            text: 'Pitch'
        CircleKnob:
            values: ['L'+str(-x) for x in range(-50, 0)] + ['CTR'] + ['R'+ str(x) for x in range(1, 51)]
            value: 50
            text: 'Pan'
            right_click_value: 50
        CircleKnob:
            text: "Rate"
            disabled: True
        CircleKnob:
            text: "One"
            values:  [str(x) for x in range(101)]
        CircleKnob:
            values: [str(x) for x in range(-24, 25)]
            value: 3
            text: 'Pitch Bend'
        CircleKnob:
            values: ['L'+str(-x) for x in range(-50, 0)] + ['CTR'] + ['R'+ str(x) for x in range(1, 51)]
            value: 1
            text: 'Pan'
        CircleKnob:
            text: "Default"
        CircleKnob:
            text: "Pulse Width"
            values:  [str(x) for x in range(101)]
        CircleKnob:
            values: [str(x) for x in range(-24, 25)]
            value: 3
            text: 'Pitch'
            right_click_value: 24
    '''
   
from kivy.config import Config
   
from kivy.core.window import Window

    Config.set(
'input', 'mouse', 'mouse,disable_multitouch')


   
class CircleKnobApp(App):

       
def build(self):
            Window.size =
700, 300
           
return Builder.load_string(kv_test)


    CircleKnobApp().run()

 

 

 

 

From: Raja Ramesh
Sent: Monday, May 11, 2020 9:52 AM
To: kivy-...@googlegroups.com
Subject: Re: [kivy-users] play audio from stop position in kivy

 

Hi Elliot,

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Raja Ramesh

unread,
May 13, 2020, 7:16:25 AM5/13/20
to kivy-...@googlegroups.com
Hi Elliot,

i gone through your code and understand as....when we scroll the knob
on app the values are changing and when i right click on app the
values are changing. but when i right click outside of the app nothing
happened but popup opens with the details as shown in image1.png
attachment. my point is..., we have to add a song from outside of
app( like windows media player). i have attached a file(image2.png)
how windows media player popup looks like.

Thanks,
Raja
> https://groups.google.com/d/msgid/kivy-users/5eb98aa2.1c69fb81.fbe77.53c0SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>
image1.png
image2.PNG

Elliot Garbus

unread,
May 13, 2020, 8:41:11 AM5/13/20
to kivy-...@googlegroups.com

Do you want to add items to a context menu in Windows Explorer?

I had thought the context menu was for in your own app.

 

I don’t know how to add context menus, but doing a search there are a number of examples around.

Raja Ramesh

unread,
May 13, 2020, 12:22:51 PM5/13/20
to kivy-...@googlegroups.com
ok Elliot, i will google it.

i have added below code to control volume of a song. but song starts
with max volume when song complete's . how to make the song to play
with volume code . Also i have included self.song.volume = 0.2 in play
method.... but the volume is not sync with volume code. it starts with
low volume when song completes.

code in kv:-
BoxLayout:
# below code is for slider
Slider:
id: slider
min: 0
max: 1
value: .2 # by default set volume to low
on_value: root.set_volume(self.value)
value_track: True
value_track_color: (1,0,0,1)
orientation: "horizontal"
size_hint_x: None
width: "200dp"
Label:
text: str(round(slider.value * 100))

code in py:-

def play(self):
if self.song:
self.song.volume = 0.2
self.song.play()

def set_volume(self, volume):
self.volume= volume
if self.song:
self.song.volume = self.volume
> https://groups.google.com/d/msgid/kivy-users/5ebbeade.1c69fb81.da52a.3156SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>

Elliot Garbus

unread,
May 13, 2020, 1:26:31 PM5/13/20
to kivy-...@googlegroups.com

Set the volume after play.

 

def play(self):

        if self.song:

            self.song.play()

            self.song.volume = 0.2

Raja Ramesh

unread,
May 14, 2020, 12:10:23 AM5/14/20
to kivy-...@googlegroups.com
Hi Elliot,

it was no working after modifying the code. it starts with low
volume(0.2) when the song repeated for second time.

Assume, for first time the app starts with 0.2 volume (which is low)
and we increased the volume(with the help of slider code and set the
volume to 0.7) while the song is playing. volume remains same (0.7)
until the song completes for first time and the volume is set to 0.2
for second time which was not correct and it should set to slider
volume i.e 0.7 until we decrease the volume.

Thanks,
Raja
> https://groups.google.com/d/msgid/kivy-users/5ebc2dbe.1c69fb81.ed89e.1082SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>

Elliot Garbus

unread,
May 14, 2020, 12:41:33 AM5/14/20
to kivy-...@googlegroups.com

Have you set:

 

def play(self):

>         if self.song:

>             self.song.play()

>             self.song.volume = self.volume

Raja Ramesh

unread,
May 14, 2020, 12:33:38 PM5/14/20
to kivy-...@googlegroups.com
Hi Elliot,

i have updated the code as below and song volume is in sync with
slider volume while playing the song.

self.song.volume = self.ids.slider.value

Now i would like to highlight the song in playlist when the song is
playing....any examples

Thanks,
Raja

Elliot Garbus

unread,
May 14, 2020, 2:22:48 PM5/14/20
to kivy-...@googlegroups.com

How is your playlist constructed?  What is the widget?

One simple idea, assuming you have a compound widget in a BoxLayout, you could add a color as the background, using canvas, and change the color if the widget is selected.

Raja Ramesh

unread,
May 15, 2020, 1:20:49 AM5/15/20
to kivy-...@googlegroups.com
Hi Elliot,

i have a BoxLayout with Recycleview and below is the code.if possible,
could you please update the below code to highlight the song which is
playing currently or share an example code i will modify accordingly.

BoxLayout:
Image:
id: album_image
source: root.album_art
allow_strech: True
#padding: [100,0]
#pos_hint: {'center_x':.8, 'center_y': .5}
BoxLayout:
orientation:'vertical'
SongFile:
song_file: 'SongList'
pos_hint: {'center_x':.9, 'center_y': 1}

RecycleView:
orientation: 'vertical'
canvas:
Color:
rgba: (0.14,0.37,.54,.2)
Rectangle:
size: self.size
pos: self.pos
size_hint: 1,.9
pos_hint: {'center_x':.65, 'center_y':.5}
id: rv_data_list
viewclass: 'SongFile'
#data: root.rv_data_list
scroll_type: ['bars','content']
bar_color: 1,0,0,1
bar_inactive_color: 1,0,0,0.5
bar_width: dp(15)
do_scroll_x: False
RecycleBoxLayout:
default_size: None, dp(36)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

> https://groups.google.com/d/msgid/kivy-users/5ebd8c70.1c69fb81.53994.3cdaSMTPIN_ADDED_MISSING%40gmr-mx.google.com.
>

Elliot Garbus

unread,
May 15, 2020, 12:05:18 PM5/15/20
to kivy-...@googlegroups.com

Here is an example of highlighting a song.  I added a TextInput to the top of the window.  If the text typed matches a song in the list, that song is highlighted with the highlight color.  Note

  • added to the viewclass, <ThreeLable>, the  use of the canvas to create the highlight.
  • To the rv_data_list,  highlight_color has been added to set (or clear) the highlight color.
  • See highlight song for the code that does the higlight

 

 

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty

kv =
"""
<ThreeLabel>:
    canvas:
        Color:
            rgba: root.highlight_color

        Rectangle:
            size: self.size
            pos: self.pos
    padding: 20,0
    Label:
        text: root.song
    Label:
        text: root.album
    Label:
        text: root.artist

        text_size: self.size
        haligh: 'left'
        valign: 'center'
        padding: 5, 0

TestBox:
    orientation: 'vertical'
    TextInput:
        size_hint_y: None
        height: 48
        hint_text: 'Enter Song Name'
        multiline: False
        on_text: root.highlight_song(self.text)
    Label:
        text: 'Song List'
        size_hint_y: .1
    RecycleView:
        id:rv
        viewclass: 'ThreeLabel'

        data: root.rv_data_list
        scroll_type: ['bars','content']
        bar_inactive_color: 1,1,1,1
        bar_width: dp(20)
        do_scroll_x: False
        RecycleBoxLayout:
            default_size: None, dp(56)

            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'
"""


class ThreeLabel(BoxLayout):
    song = StringProperty()
    album = StringProperty()
    artist = StringProperty()
    highlight_color = ListProperty()


class TestBox(BoxLayout):
    rv_data_list = [{
'song': f'song_{n}',
                    
'album': f'album_{n}',
                    
'artist': f'artist_{n}',
                    
'highlight_color': [0, 0, 0, 1]} for n in range(20)]

   
def highlight_song(self, song):
       
for track in self.rv_data_list:
            track[
'highlight_color'] = (.8,.8,.8,1) if song == track['song'] else (0, 0, 0, 1)
       
self.ids.rv.refresh_from_data()



class RVTestApp(App):
   
def build(self):
       
return Builder.load_string(kv)


RVTestApp().run()

Raja Ramesh

unread,
May 16, 2020, 1:38:21 PM5/16/20
to kivy-...@googlegroups.com
Hi Elliot,

i have implemented the code to highlight the song when it is playing. is it possible to perform below operations on a file within recycleview ? please share any samples...if yes.
1.  drag and drop
2.  delete

Thanks,
Raja

Elliot Garbus

unread,
May 16, 2020, 3:07:52 PM5/16/20
to kivy-...@googlegroups.com

The delete is similar to the highlight.

 

Find the item to delete (perhaps add a delete button to the viewclass).  Remove the item from the rv_data_list, and then refresh from data.  Give it a try.

 

What do you want to do with the drag and drop?  What do you want to drag from and to, and what is the action?

Raja Ramesh

unread,
May 24, 2020, 12:01:48 PM5/24/20
to kivy-...@googlegroups.com
Hi Elliot.

could you please share any example to delete a song from Recycleview. My intention is to move the selected song up and down in the Recycleview.

Thanks,
Raja

Elliot Garbus

unread,
May 24, 2020, 1:06:29 PM5/24/20
to kivy-...@googlegroups.com

My intention is to move the selected song up and down in the Recycleview.  I’m not sure what you mean here, but to change where a song appears in the RecycleView, change the order of the data list.

 

To remove an item from the recycleview, remove it’s entry from the recycleview data list.

Building on the example from highlighting a song, I’ve added a delete button to each data item.  I also added an index to each item in the list.  This make it easy to delete the song.

 

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout

from kivy.properties import StringProperty, ListProperty, NumericProperty

kv =
"""


<ThreeLabel>:
    canvas:
        Color:
            rgba: root.highlight_color
        Rectangle:
            size: self.size
            pos: self.pos
    padding: 20,0
    Label:
        text: root.song
    Label:
        text: root.album
    Label:
        text: root.artist

    Button:
        size_hint_x: None
        width: 80
        text: 'Delete'
        on_release: root.delete_song()

class ThreeLabel(BoxLayout):

    index = NumericProperty()

   
def delete_song(self):
        app = App.get_running_app()
       
del app.root.ids.rv.data[self.index]


class TestBox(BoxLayout):
    rv_data_list = [{
'song': f'song_{n}',
                    
'album': f'album_{n}',
                    
'artist': f'artist_{n}'

,
                    
'index': n,
                     
'highlight_color': [0, 0, 0, 1]} for n in range(20)]

   
def highlight_song(self, song):
       
for track in self.rv_data_list:
            track[
'highlight_color'] = (.8,.8,.8,1) if song == track['song'] else (0, 0, 0, 1)
       
self.ids.rv.refresh_from_data()


Elliot Garbus

unread,
May 24, 2020, 1:17:56 PM5/24/20
to kivy-...@googlegroups.com

Oops.. The data list indexes need to be updated after a delete.

 

def delete_song(self):
    app = App.get_running_app()
    rv = app.root.ids.rv
   
del rv.data[self.index]
   
for i, _ in enumerate(rv.data):
        rv.data[i][
'index'] = i

Raja Ramesh

unread,
Jun 8, 2020, 11:12:26 AM6/8/20
to kivy-...@googlegroups.com
HI Elliot,

could you please share example if any,  to select a song(using key-board keys up, down) from recycleview and then delete the selected song from recycelview using key-board key(Del) ?


Thanks,
Raja

Elliot Garbus

unread,
Jun 8, 2020, 10:26:46 PM6/8/20
to kivy-...@googlegroups.com

Interesting problem.  I have not done something like this before.  Perhaps some one else has an idea.  Start a new thread on this one.

There are a set of calls for selecting a node in the RecycleView.  You will also need to decide when is the RecycleView selected vs not selected. 

 

It may a while before I come up with anything.  Start with a simple example – and build from there.

Reply all
Reply to author
Forward
0 new messages