wx.media.MediaCtrl - problems with wx.media.EVT_MEDIA_STOP, wx.media.EVT_MEDIA_FINISHED and Load()

Viewed 67

I am trying to make a player which will automatically play the next song from a list when the current one ends. Whatever I do, when I add a call to Load() in the on_song_end method of my class binded both to wx.media.EVT_MEDIA_STOP or wx.media.EVT_MEDIA_FINISHED, it hangs or just iterates through all the items indefinitely. What am I doing wrong?

import os
import wx
from wx.media import MediaCtrl
from wx.adv import Sound
import wx.lib.mixins.listctrl as listmix
import eyed3
import glob


APP_TITLE = "Crazy ID3 tag editor"


class Window(wx.Frame, listmix.ColumnSorterMixin):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.InitUI()
        self.create_menu()
        self.update_mp3_listing("D:\\YT")
        self.itemDataMap = self.row_data
        listmix.ColumnSorterMixin.__init__(self, 4)

    def InitUI(self):
        self.row_data = {}
        self.row_obj_dict = {}
        self.now_playing_index = -1
        self.panel = wx.Panel(self)
        self.panel.SetLabel(APP_TITLE)
        self.mp3_list_label = wx.StaticText(self.panel, label="List of mp3's")
        self.mp3_list = MyListCtrl(self.panel, id=wx.ID_ANY, size=(-1, -1), style=wx.LC_REPORT)
        self.mp3_list.InsertColumn(0, "Artist", width=140)
        self.mp3_list.InsertColumn(1, "Title", width=140)
        self.mp3_list.InsertColumn(2, "Album", width=200)
        self.mp3_list.InsertColumn(3, "Filename", width=200)
        # self.mp3_list.InsertColumn(3, "Year", width=200)
        self.mp3_list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_list_item_activate)
        self.mp3_list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.on_item_focused)
        self.mp3_list.Bind(wx.EVT_LIST_COL_CLICK, self.on_col_click)
        self.edit_button = wx.Button(self.panel, label="&Edit")
        self.edit_button.Bind(wx.EVT_BUTTON, self.on_edit)
        self.refresh_button = wx.Button(self.panel)
        self.refresh_button.Bind(wx.EVT_BUTTON, self.refresh_mp3_listing)
        self.refresh_button.Hide()
        self.stop_button = wx.Button(self.panel)
        self.stop_button.Bind(wx.EVT_BUTTON, self.stop_song)
        self.stop_button.Hide()
        self.focus_now_playing_button = wx.Button(self.panel)
        self.focus_now_playing_button.Bind(wx.EVT_BUTTON, self.focus_now_playing)
        self.focus_now_playing_button.Hide()
        self.sizer = wx.GridBagSizer(5, 5)
        self.sizer.Add(self.mp3_list_label, pos=(0, 0), span=(1, 2))
        self.sizer.Add(self.mp3_list, pos=(1, 0), span=(1, 1), flag=wx.EXPAND|wx.ALL)
        self.sizer.Add(self.edit_button, pos=(1, 1), span=(1, 1), flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
        self.sizer.AddGrowableRow(1)
        self.sizer.AddGrowableCol(0, proportion=3)
        self.sizer.AddGrowableCol(1, proportion=1)
        self.panel.SetSizerAndFit(self.sizer)
        self.Fit()
        self.Center()
        self.accelerator_table = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_F5, self.refresh_button.GetId()), (wx.ACCEL_ALT, wx.WXK_CONTROL_S, self.stop_button.GetId()), (wx.ACCEL_NORMAL, wx.WXK_MEDIA_STOP, self.stop_button.GetId()), (wx.ACCEL_CTRL, wx.WXK_CONTROL_P, self.focus_now_playing_button.GetId())])
        self.SetAcceleratorTable(self.accelerator_table)
        self.Show(True)

    def create_menu(self):
        self.menu_bar = wx.MenuBar()
        self.file_menu = wx.Menu()
        self.open_folder_menu_item = self.file_menu.Append(wx.ID_ANY, "Op&en folder", "Open a folder with MP3s")
        self.menu_bar.Append(self.file_menu, "&File")
        self.Bind(wx.EVT_MENU, self.on_open_folder, self.open_folder_menu_item)
        self.SetMenuBar(self.menu_bar)

    def on_open_folder(self, event):
        title = "Choose a directory: "
        dlg = wx.DirDialog(self, title, style=wx.DD_DEFAULT_STYLE)
        if dlg.ShowModal() == wx.ID_OK:
            self.update_mp3_listing(dlg.GetPath())
        dlg.Destroy()

    def on_edit(self, event):
        focused_item = self.mp3_list.GetFocusedItem()
        selected_items_count = self.mp3_list.GetSelectedItemCount()
        if focused_item >= 0:
            if selected_items_count == 1:
                if focused_item == self.now_playing_index:
                    self.stop_song(event=None)
                mp3 = self.row_obj_dict[self.mp3_list.GetItemData(focused_item)]
                dlg = EditDialog(mp3)
                dlg.ShowModal()
                self.update_mp3_listing(self.current_folder_path)
                dlg.Destroy()
                self.mp3_list.Focus(focused_item)
            else:
                mp3s_to_edit = []
                item = self.mp3_list.GetFirstSelected()
                while item != -1:
                    mp3 = self.row_obj_dict[self.mp3_list.GetItemData(item)]
                    mp3s_to_edit.append(mp3)
                    item = self.mp3_list.GetNextSelected(item)
                dlg = EditArtistDialog(mp3s_to_edit)
                dlg.ShowModal()
                self.update_mp3_listing(self.current_folder_path)
                dlg.Destroy()
            self.mp3_list.Focus(focused_item)

    def update_mp3_listing(self, folder_path):
        self.current_folder_path = folder_path
        self.mp3_list.ClearAll()
        self.mp3_list.InsertColumn(0, "Artist", width=140)
        self.mp3_list.InsertColumn(1, "Title", width=200)
        self.mp3_list.InsertColumn(2, "Album", width=140)
        self.mp3_list.InsertColumn(3, "Filename", width=200)
        # self.mp3_list.InsertColumn(3, "Year", width=200)
        mp3s = glob.glob(folder_path + "/*.mp3")
        mp3_objects = []
        index = 0
        for mp3 in mp3s:
            mp3_object = eyed3.load(mp3)
            self.mp3_list.InsertItem(index, mp3_object.tag.artist or " ")
            self.mp3_list.SetItem(index, 1, mp3_object.tag.title or " ")
            self.mp3_list.SetItem(index, 2, mp3_object.tag.album or " ")
            self.mp3_list.SetItem(index, 3, mp3)
            self.mp3_list.SetItemData(index, index)
            # self.mp3_list.SetItemData(index, [mp3_object.tag.artist or " ", mp3_object.tag.title or " ", mp3_object.tag.album or " ", mp3])
            mp3_objects.append(mp3_object)
            self.row_data[index] = (mp3_object.tag.artist or " ", mp3_object.tag.title or " ", mp3_object.tag.album or " ", mp3_object.tag.file_info.name)
            self.row_obj_dict[index] = mp3_object
            index += 1

    def refresh_mp3_listing(self, event):
        self.update_mp3_listing(self.current_folder_path)

    def on_loaded(self, event):
        self.player.Play()
        # self.now_playing_index = self.mp3_list.GetItemData(self.mp3_list.GetFocusedItem()+1)

    def on_list_item_activate(self, event):
        focused_item = self.mp3_list.GetFocusedItem()
        mp3 = self.row_obj_dict[self.mp3_list.GetItemData(focused_item)]
        self.player = MediaCtrl(parent=self, szBackend=wx.media.MEDIABACKEND_WMP10)
        self.player.Load(mp3.tag.file_info.name) # tried wx.CallAfter without success
        self.player.SetVolume(0.25)
        self.now_playing_index = focused_item
        self.Bind(wx.media.EVT_MEDIA_LOADED, self.on_loaded, self.player)
        self.Bind(wx.media.EVT_MEDIA_STOP, self.on_song_end, self.player)
        if mp3.tag.artist and mp3.tag.title:
            self.SetTitle(f"{APP_TITLE + ': ' + mp3.tag.artist + ' - ' + mp3.tag.title}")
        else:
            self.SetTitle(f"{APP_TITLE + ': ' + os.path.split(os.path.splitext(mp3.tag.file_info.name)[0])[1]} (no tags)")

    def on_song_end(self, event):
        self.player.Stop()
        now = self.now_playing_index
        if now < self.mp3_list.GetItemCount()-1:
            song = self.row_obj_dict[self.mp3_list.GetItemData(now+1)]
            self.now_playing_index = now+1
        else:
            song = self.row_obj_dict[self.mp3_list.GetItemData(0)]
            self.now_playing_index = 0
        print(song.tag.title)
        #self.player.Stop()
        wx.CallAfter(self.player.Load, song.tag.file_info.name)
        """
        if song.tag.artist and song.tag.title:
            self.SetTitle(f"{APP_TITLE + ': ' + song.tag.artist + ' - ' + song.tag.title}")
        else:
            self.SetTitle(f"{APP_TITLE + ': ' + os.path.split(os.path.splitext(song.tag.file_info.name)[0])[1]} (no tags)")
        """

    def stop_song(self, event):
        self.player.Pause()
        self.now_playing_index = -1
        self.SetTitle(APP_TITLE)

    def focus_now_playing(self, event):
        if self.now_playing_index >= 0:
            self.mp3_list.Focus(self.now_playing_index)
            self.mp3_list.Select(self.now_playing_index, on=1)
        else:
            Sound().PlaySound("c:/windows/media/Windows Ding.wav", wx.adv.SOUND_ASYNC)

    # mixin methods override

    def GetListCtrl(self):
        return self.mp3_list

    def on_col_click(self, event):
        old_order = self.mp3_list.GetColumnsOrder()
        self.mp3_list.SortItems(1)
        new_order = old_order
        clicked_column = new_order.pop(self.mp3_list.GetColumnIndexFromOrder(event.GetColumn()))
        new_order.insert(0, clicked_column)
        self.mp3_list.SetColumnsOrder(new_order)
        self.mp3_list.RefreshItems(0, self.mp3_list.GetItemCount()-1)
        # event.Skip()

    def on_item_focused(self, event):
        # print(self.mp3_list.GetItemData(self.mp3_list.GetFocusedItem()))
        pass


class MyListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
    def __init__(self, parent, id=wx.ID_ANY, autoSizeColumn="LAST", itemTextCallable=None, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
        if itemTextCallable is not None:
            if not isinstance(itemTextCallable, Callable):
                raise TypeError("itemTextCallable should be None or a callable")
            self._itemTextCallable = itemTextCallable
        else:
            self._itemTextCallable = self._super_itemTextCallable
        wx.ListCtrl.__init__(self, parent, id=id, pos=pos, size=size, style=style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        self.setResizeColumn(autoSizeColumn)
        self.Bind(wx.EVT_WINDOW_DESTROY, source=self, id=self.GetId, handler=self._onDestroy)

    def _onDestroy(self, evt):
        evt.Skip()
        self._itemTextCallable = None

    def _super_itemTextCallable(self, item, column):
        return super(MyListCtrl, self).OnGetItemText(item, column)

    def OnGetItemText(self, item, column):
        return self._itemTextCallable(item, column)

    def sendListItemFocusedEvent(self, index):
        evt = wx.ListEvent(wx.wxEVT_LIST_ITEM_FOCUSED, self.Id)
        evt.EventObject = self
        evt.Index = index
        self.ProcessEvent(evt)


app = wx.App()
window = Window(None, title=APP_TITLE)
app.MainLoop()
0 Answers
Related