mirror of
https://github.com/SoongNoonien/mpdevil.git
synced 2023-08-10 21:12:44 +03:00
reworked "SearchWindow" to use threading
This commit is contained in:
parent
3bb968a772
commit
11e3699d28
265
bin/mpdevil
265
bin/mpdevil
@ -1793,75 +1793,127 @@ class ArtistPopover(Gtk.Popover):
|
||||
# browser #
|
||||
###########
|
||||
|
||||
class SearchThread(threading.Thread):
|
||||
def __init__(self, client, search_entry, songs_window, hits_label, search_tag):
|
||||
super().__init__(daemon=True)
|
||||
self._client=client
|
||||
self._search_entry=search_entry
|
||||
self._songs_view=songs_window.get_treeview()
|
||||
self._store=self._songs_view.get_model()
|
||||
self._action_bar=songs_window.get_action_bar()
|
||||
self._hits_label=hits_label
|
||||
self._search_tag=search_tag
|
||||
self._stop_flag=False
|
||||
self._callback=None
|
||||
|
||||
def set_callback(self, callback):
|
||||
self._callback=callback
|
||||
|
||||
def stop(self):
|
||||
self._stop_flag=True
|
||||
|
||||
def start(self):
|
||||
self._songs_view.clear()
|
||||
self._hits_label.set_text("")
|
||||
self._action_bar.set_sensitive(False)
|
||||
self._search_text=self._search_entry.get_text()
|
||||
if self._search_text:
|
||||
super().start()
|
||||
else:
|
||||
self._exit()
|
||||
|
||||
def run(self):
|
||||
hits=0
|
||||
stripe_size=1000
|
||||
songs=self._get_songs(0, stripe_size)
|
||||
stripe_start=stripe_size
|
||||
while songs:
|
||||
hits+=len(songs)
|
||||
if not self._append_songs(songs):
|
||||
self._exit()
|
||||
return
|
||||
GLib.idle_add(self._search_entry.progress_pulse)
|
||||
GLib.idle_add(self._hits_label.set_text, ngettext("{hits} hit", "{hits} hits", hits).format(hits=hits))
|
||||
stripe_end=stripe_start+stripe_size
|
||||
songs=self._get_songs(stripe_start, stripe_end)
|
||||
stripe_start=stripe_end
|
||||
if hits > 0:
|
||||
GLib.idle_add(self._action_bar.set_sensitive, True)
|
||||
self._exit()
|
||||
|
||||
def _exit(self):
|
||||
def callback():
|
||||
self._search_entry.set_progress_fraction(0.0)
|
||||
if self._callback is not None:
|
||||
self._callback()
|
||||
return False
|
||||
GLib.idle_add(callback)
|
||||
|
||||
@main_thread_function
|
||||
def _get_songs(self, start, end):
|
||||
if self._stop_flag:
|
||||
return []
|
||||
else:
|
||||
self._client.restrict_tagtypes("track", "title", "artist", "album")
|
||||
songs=self._client.search(self._search_tag, self._search_text, "window", f"{start}:{end}")
|
||||
self._client.tagtypes("all")
|
||||
return songs
|
||||
|
||||
@main_thread_function
|
||||
def _append_songs(self, songs):
|
||||
for song in songs:
|
||||
if self._stop_flag:
|
||||
return False
|
||||
try:
|
||||
int_track=int(song["track"][0])
|
||||
except ValueError:
|
||||
int_track=0
|
||||
self._store.insert_with_valuesv(-1, range(7), [
|
||||
str(song["track"]), str(song["title"]),
|
||||
str(song["artist"]), str(song["album"]),
|
||||
str(song["duration"]), song["file"],
|
||||
int_track
|
||||
])
|
||||
return True
|
||||
|
||||
class SearchWindow(Gtk.Box):
|
||||
def __init__(self, client):
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
self._client=client
|
||||
self._stop_flag=False
|
||||
self._done=True
|
||||
self._pending=[]
|
||||
|
||||
# tag switcher
|
||||
# widgets
|
||||
self._tag_combo_box=Gtk.ComboBoxText()
|
||||
|
||||
# search entry
|
||||
self.search_entry=Gtk.SearchEntry()
|
||||
|
||||
# label
|
||||
self._hits_label=Gtk.Label(xalign=1)
|
||||
|
||||
# store
|
||||
# songs window
|
||||
# (track, title, artist, album, duration, file, sort track)
|
||||
self._store=Gtk.ListStore(str, str, str, str, str, str, int)
|
||||
self._store.set_default_sort_func(lambda *args: 0)
|
||||
|
||||
# songs window
|
||||
self._songs_window=SongsWindow(self._client, self._store, 5)
|
||||
|
||||
# action bar
|
||||
self._action_bar=self._songs_window.get_action_bar()
|
||||
self._action_bar.set_sensitive(False)
|
||||
|
||||
# songs view
|
||||
self._songs_view=self._songs_window.get_treeview()
|
||||
|
||||
# columns
|
||||
renderer_text=Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END, ellipsize_set=True)
|
||||
renderer_text_ralign=Gtk.CellRendererText(xalign=1.0)
|
||||
column_data=(
|
||||
(_("No"), renderer_text_ralign, False, 0, 6),
|
||||
(_("Title"), renderer_text, True, 1, 1),
|
||||
(_("Artist"), renderer_text, True, 2, 2),
|
||||
(_("Album"), renderer_text, True, 3, 3),
|
||||
(_("Length"), renderer_text_ralign, False, 4, 4),
|
||||
)
|
||||
for title, renderer, expand, text, sort in column_data:
|
||||
column=Gtk.TreeViewColumn(title, renderer, text=text)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column.set_property("resizable", False)
|
||||
column.set_property("expand", expand)
|
||||
column.set_sort_column_id(sort)
|
||||
self._songs_view.append_column(column)
|
||||
|
||||
column_track=Gtk.TreeViewColumn(_("No"), renderer_text_ralign, text=0)
|
||||
column_track.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column_track.set_property("resizable", False)
|
||||
self._songs_view.append_column(column_track)
|
||||
|
||||
column_title=Gtk.TreeViewColumn(_("Title"), renderer_text, text=1)
|
||||
column_title.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column_title.set_property("resizable", False)
|
||||
column_title.set_property("expand", True)
|
||||
self._songs_view.append_column(column_title)
|
||||
|
||||
column_artist=Gtk.TreeViewColumn(_("Artist"), renderer_text, text=2)
|
||||
column_artist.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column_artist.set_property("resizable", False)
|
||||
column_artist.set_property("expand", True)
|
||||
self._songs_view.append_column(column_artist)
|
||||
|
||||
column_album=Gtk.TreeViewColumn(_("Album"), renderer_text, text=3)
|
||||
column_album.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column_album.set_property("resizable", False)
|
||||
column_album.set_property("expand", True)
|
||||
self._songs_view.append_column(column_album)
|
||||
|
||||
column_time=Gtk.TreeViewColumn(_("Length"), renderer_text, text=4)
|
||||
column_time.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
column_time.set_property("resizable", False)
|
||||
self._songs_view.append_column(column_time)
|
||||
|
||||
column_track.set_sort_column_id(6)
|
||||
column_title.set_sort_column_id(1)
|
||||
column_artist.set_sort_column_id(2)
|
||||
column_album.set_sort_column_id(3)
|
||||
column_time.set_sort_column_id(4)
|
||||
# search thread
|
||||
self._search_thread=SearchThread(self._client, self.search_entry, self._songs_window, self._hits_label, "any")
|
||||
|
||||
# connect
|
||||
self.search_entry.connect("activate", self._search)
|
||||
@ -1883,107 +1935,44 @@ class SearchWindow(Gtk.Box):
|
||||
self.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0)
|
||||
self.pack_start(self._songs_window, True, True, 0)
|
||||
|
||||
def _clear(self, *args):
|
||||
if self._done:
|
||||
self.search_entry.handler_block(self._search_entry_changed)
|
||||
self._tag_combo_box.handler_block(self._tag_combo_box_changed)
|
||||
self._songs_view.clear()
|
||||
self.search_entry.set_text("")
|
||||
self._tag_combo_box.remove_all()
|
||||
self.search_entry.handler_unblock(self._search_entry_changed)
|
||||
self._tag_combo_box.handler_unblock(self._tag_combo_box_changed)
|
||||
elif not self._clear in self._pending:
|
||||
self._stop_flag=True
|
||||
self._pending.append(self._clear)
|
||||
|
||||
def _on_disconnected(self, *args):
|
||||
self._tag_combo_box.set_sensitive(False)
|
||||
self.search_entry.set_sensitive(False)
|
||||
self._clear()
|
||||
self._search_thread.stop()
|
||||
|
||||
def _on_reconnected(self, *args):
|
||||
if self._done:
|
||||
def callback():
|
||||
self._action_bar.set_sensitive(False)
|
||||
self._songs_view.clear()
|
||||
self._hits_label.set_text("")
|
||||
self.search_entry.handler_block(self._search_entry_changed)
|
||||
self.search_entry.set_text("")
|
||||
self.search_entry.handler_unblock(self._search_entry_changed)
|
||||
self._tag_combo_box.handler_block(self._tag_combo_box_changed)
|
||||
self._tag_combo_box.remove_all()
|
||||
self._tag_combo_box.append_text(_("all tags"))
|
||||
for tag in self._client.tagtypes():
|
||||
if not tag.startswith("MUSICBRAINZ"):
|
||||
self._tag_combo_box.append_text(tag)
|
||||
self._tag_combo_box.set_active(0)
|
||||
self._tag_combo_box.set_sensitive(True)
|
||||
self.search_entry.set_sensitive(True)
|
||||
self._tag_combo_box.handler_unblock(self._tag_combo_box_changed)
|
||||
elif not self._on_reconnected in self._pending:
|
||||
self._stop_flag=True
|
||||
self._pending.append(self._on_reconnected)
|
||||
if self._search_thread.is_alive():
|
||||
self._search_thread.set_callback(callback)
|
||||
self._search_thread.stop()
|
||||
else:
|
||||
callback()
|
||||
|
||||
def _search(self, *args):
|
||||
if self._done:
|
||||
self._done=False
|
||||
self._songs_view.clear()
|
||||
self._hits_label.set_text("")
|
||||
self._action_bar.set_sensitive(False)
|
||||
hits=0
|
||||
if self.search_entry.get_text():
|
||||
if self._tag_combo_box.get_active() == 0:
|
||||
search_tag="any"
|
||||
else:
|
||||
search_tag=self._tag_combo_box.get_active_text()
|
||||
search_text=self.search_entry.get_text()
|
||||
stripe_size=1000
|
||||
self._client.restrict_tagtypes("track", "title", "artist", "album")
|
||||
try: # client cloud meanwhile disconnect
|
||||
songs=self._client.search(search_tag, search_text, "window", f"0:{stripe_size}")
|
||||
except MPDBase.ConnectionError:
|
||||
self._done_callback()
|
||||
return
|
||||
stripe_start=stripe_size
|
||||
while songs:
|
||||
hits+=len(songs)
|
||||
for song in songs:
|
||||
if self._stop_flag:
|
||||
self._done_callback()
|
||||
return
|
||||
try:
|
||||
int_track=int(song["track"][0])
|
||||
except ValueError:
|
||||
int_track=0
|
||||
self._store.insert_with_valuesv(-1, range(7), [
|
||||
str(song["track"]), str(song["title"]),
|
||||
str(song["artist"]), str(song["album"]),
|
||||
str(song["duration"]), song["file"],
|
||||
int_track
|
||||
])
|
||||
self.search_entry.progress_pulse()
|
||||
self._hits_label.set_text(ngettext("{hits} hit", "{hits} hits", hits).format(hits=hits))
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration_do(True)
|
||||
stripe_end=stripe_start+stripe_size
|
||||
try: # client cloud meanwhile disconnect
|
||||
songs=self._client.search(search_tag, search_text, "window", f"{stripe_start}:{stripe_end}")
|
||||
except MPDBase.ConnectionError:
|
||||
self._done_callback()
|
||||
return
|
||||
stripe_start+=stripe_size
|
||||
self._client.tagtypes("all")
|
||||
if hits > 0:
|
||||
self._action_bar.set_sensitive(True)
|
||||
self._done_callback()
|
||||
elif not self._search in self._pending:
|
||||
self._stop_flag=True
|
||||
self._pending.append(self._search)
|
||||
|
||||
def _done_callback(self, *args):
|
||||
self.search_entry.set_progress_fraction(0.0)
|
||||
self._stop_flag=False
|
||||
self._done=True
|
||||
pending=self._pending
|
||||
self._pending=[]
|
||||
for p in pending:
|
||||
try:
|
||||
p()
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
def callback():
|
||||
if self._tag_combo_box.get_active() == 0:
|
||||
search_tag="any"
|
||||
else:
|
||||
search_tag=self._tag_combo_box.get_active_text()
|
||||
self._search_thread=SearchThread(self._client, self.search_entry, self._songs_window, self._hits_label, search_tag)
|
||||
self._search_thread.start()
|
||||
if self._search_thread.is_alive():
|
||||
self._search_thread.set_callback(callback)
|
||||
self._search_thread.stop()
|
||||
else:
|
||||
callback()
|
||||
|
||||
def _on_search_entry_focus_event(self, widget, event, focus):
|
||||
app=self.get_toplevel().get_application()
|
||||
|
Loading…
Reference in New Issue
Block a user