diff --git a/app/classes.py b/app/classes.py index 5399dea..c9750fc 100644 --- a/app/classes.py +++ b/app/classes.py @@ -12,6 +12,7 @@ from typing import Any, Optional, NamedTuple # Third party imports import numpy as np import pyqtgraph as pg # type: ignore +from sqlalchemy.orm.session import Session import vlc # type: ignore # PyQt imports @@ -761,6 +762,17 @@ class RowAndTrack: self.fade_graph.tick(self.time_playing()) + def update_playlist_and_row(self, session: Session) -> None: + """ + Update local playlist_id and row_number from playlistrow_id + """ + + plr = session.get(PlaylistRows, self.playlistrow_id) + if not plr: + raise ApplicationError(f"(Can't retrieve PlaylistRows entry, {self=}") + self.playlist_id = plr.playlist_id + self.row_number = plr.row_number + @dataclass class TrackFileData: diff --git a/app/config.py b/app/config.py index fa26ea5..1d7a196 100644 --- a/app/config.py +++ b/app/config.py @@ -85,6 +85,8 @@ class Config(object): OBS_PORT = 4455 PLAY_NEXT_GUARD_MS = 10000 PLAY_SETTLE = 500000 + PLAYLIST_ICON_CURRENT = ":/icons/green-circle.png" + PLAYLIST_ICON_NEXT = ":/icons/yellow-circle.png" PREVIEW_ADVANCE_MS = 5000 PREVIEW_BACK_MS = 5000 PREVIEW_END_BUFFER_MS = 1000 diff --git a/app/models.py b/app/models.py index 38ffa88..7cf73fc 100644 --- a/app/models.py +++ b/app/models.py @@ -479,9 +479,7 @@ class PlaylistRows(dbtables.PlaylistRowsTable): """ plrs = session.scalars( - select(cls) - .where(cls.playlist_id == playlist_id) - .order_by(cls.row_number) + select(cls).where(cls.playlist_id == playlist_id).order_by(cls.row_number) ).all() return plrs @@ -566,7 +564,10 @@ class PlaylistRows(dbtables.PlaylistRowsTable): @staticmethod @line_profiler.profile def update_plr_row_numbers( - session: Session, playlist_id: int, sqla_map: List[dict[str, int]], dummy_for_profiling=None + session: Session, + playlist_id: int, + sqla_map: List[dict[str, int]], + dummy_for_profiling=None, ) -> None: """ Take a {plrid: row_number} dictionary and update the row numbers accordingly diff --git a/app/musicmuster.py b/app/musicmuster.py index 055a9c2..24556e7 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -26,6 +26,7 @@ from PyQt6.QtCore import ( from PyQt6.QtGui import ( QCloseEvent, QColor, + QIcon, QKeySequence, QPalette, QShortcut, @@ -460,7 +461,7 @@ class Window(QMainWindow, Ui_MainWindow): self.actionImport.triggered.connect(self.import_track) self.actionInsertSectionHeader.triggered.connect(self.insert_header) self.actionInsertTrack.triggered.connect(self.insert_track) - self.actionMark_for_moving.triggered.connect(self.cut_rows) + self.actionMark_for_moving.triggered.connect(self.mark_rows_for_moving) self.actionMoveSelected.triggered.connect(self.move_selected) self.actionNew_from_template.triggered.connect(self.new_from_template) self.actionNewPlaylist.triggered.connect(self.create_and_show_playlist) @@ -564,18 +565,6 @@ class Window(QMainWindow, Ui_MainWindow): log.debug(f"create_playlist_tab() returned: {idx=}") return idx - def cut_rows(self) -> None: - """ - Cut rows ready for pasting. - """ - - # Save the selected PlaylistRows items ready for a later - # paste - self.move_source_rows = self.active_tab().get_selected_rows() - self.move_source_model = self.active_proxy_model() - - log.debug(f"cut_rows(): {self.move_source_rows=} {self.move_source_model=}") - def debug(self): """Invoke debugger""" @@ -929,6 +918,18 @@ class Window(QMainWindow, Ui_MainWindow): self.signals.search_wikipedia_signal.emit(track_info.title) + def mark_rows_for_moving(self) -> None: + """ + Cut rows ready for pasting. + """ + + # Save the selected PlaylistRows items ready for a later + # paste + self.move_source_rows = self.active_tab().get_selected_rows() + self.move_source_model = self.active_proxy_model() + + log.debug(f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}") + def move_playlist_rows(self, row_numbers: List[int]) -> None: """ Move passed playlist rows to another playlist @@ -964,6 +965,16 @@ class Window(QMainWindow, Ui_MainWindow): row_numbers, to_row, to_playlist_id ) + # Reset track_sequences + with db.Session() as session: + for ts in [ + track_sequence.next, + track_sequence.current, + track_sequence.previous, + ]: + if ts: + ts.update_playlist_and_row(session) + def move_selected(self) -> None: """ Move selected rows to another playlist @@ -1814,6 +1825,38 @@ class Window(QMainWindow, Ui_MainWindow): else: self.hdrNextTrack.setText("") + self.update_playlist_icons() + + def update_playlist_icons(self) -> None: + """ + Set current / next playlist tab icons + """ + + # Do we need to set a 'next' icon? + set_next = True + if ( + track_sequence.current + and track_sequence.next + and track_sequence.current.playlist_id == track_sequence.next.playlist_id + ): + set_next = False + + for idx in range(self.tabBar.count()): + widget = self.tabPlaylist.widget(idx) + if ( + track_sequence.next + and set_next + and widget.playlist_id == track_sequence.next.playlist_id + ): + self.tabPlaylist.setTabIcon(idx, QIcon(Config.PLAYLIST_ICON_NEXT)) + elif ( + track_sequence.current + and widget.playlist_id == track_sequence.current.playlist_id + ): + self.tabPlaylist.setTabIcon(idx, QIcon(Config.PLAYLIST_ICON_CURRENT)) + else: + self.tabPlaylist.setTabIcon(idx, QIcon()) + class DownloadCSV(QDialog): def __init__(self, parent=None): diff --git a/app/playlistmodel.py b/app/playlistmodel.py index f2a7066..32aa915 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -40,6 +40,7 @@ from classes import ( ) from config import Config from helpers import ( + ask_yes_no, file_is_unreadable, get_embedded_time, get_relative_date, @@ -67,7 +68,7 @@ class PlaylistModel(QAbstractTableModel): database refresh_data() will repopulate all of playlist_rows from the - database + database. """ def __init__( @@ -155,10 +156,18 @@ class PlaylistModel(QAbstractTableModel): if file_is_unreadable(rat.path): return QBrush(QColor(Config.COLOUR_UNREADABLE)) # Current track - if track_sequence.current and track_sequence.current.row_number == row: + if ( + track_sequence.current + and track_sequence.current.playlist_id == self.playlist_id + and track_sequence.current.row_number == row + ): return QBrush(QColor(Config.COLOUR_CURRENT_PLAYLIST)) # Next track - if track_sequence.next and track_sequence.next.row_number == row: + if ( + track_sequence.next + and track_sequence.next.playlist_id == self.playlist_id + and track_sequence.next.row_number == row + ): return QBrush(QColor(Config.COLOUR_NEXT_PLAYLIST)) # Individual cell colouring @@ -738,7 +747,9 @@ class PlaylistModel(QAbstractTableModel): self.invalidate_rows(row_numbers) @line_profiler.profile - def move_rows(self, from_rows: list[int], to_row_number: int, dummy_for_profiling=None) -> None: + def move_rows( + self, from_rows: list[int], to_row_number: int, dummy_for_profiling=None + ) -> None: """ Move the playlist rows given to to_row and below. """ @@ -805,8 +816,11 @@ class PlaylistModel(QAbstractTableModel): @line_profiler.profile def move_rows_between_playlists( - self, from_rows: list[int], to_row_number: int, to_playlist_id: int, - dummy_for_profiling=None + self, + from_rows: list[int], + to_row_number: int, + to_playlist_id: int, + dummy_for_profiling=None, ) -> None: """ Move the playlist rows given to to_row and below of to_playlist. @@ -826,7 +840,6 @@ class PlaylistModel(QAbstractTableModel): self.signals.begin_reset_model_signal.emit(to_playlist_id) with db.Session() as session: - for row_group in row_groups: # Make room in destination playlist max_destination_row_number = PlaylistRows.get_last_used_row( @@ -981,8 +994,43 @@ class PlaylistModel(QAbstractTableModel): @line_profiler.profile def refresh_data(self, session: db.session, dummy_for_profiling=None) -> None: + """ + Populate self.playlist_rows with playlist data + + We used to clear self.playlist_rows each time but that's + expensive and slow on big playlists. Instead we track where rows + are in database versus self.playlist_rows and fixup the latter. + This works well for news rows added and for rows moved, but + doesn't work for changed comments so they must be handled using + refresh_row(). + """ + + + # Note where each playlist_id is + plid_to_row: dict[int, int] = {} + for oldrow in self.playlist_rows: + plrdata = self.playlist_rows[oldrow] + plid_to_row[plrdata.playlistrow_id] = plrdata.row_number + + # build a new playlist_rows + new_playlist_rows: dict[int, RowAndTrack] = {} + for p in PlaylistRows.get_playlist_rows(session, self.playlist_id): + if p.id not in plid_to_row: + new_playlist_rows[p.row_number] = RowAndTrack(p) + else: + new_playlist_rows[p.row_number] = self.playlist_rows[plid_to_row[p.id]] + new_playlist_rows[p.row_number].row_number = p.row_number + + # Copy to self.playlist_rows + self.playlist_rows = new_playlist_rows + + def load_data(self, session: db.session, dummy_for_profiling=None) -> None: """Populate self.playlist_rows with playlist data""" + # Same as refresh data, but only used when creating playslit. + # Distinguishes profile time between initial load and other + # refreshes. + # We used to clear self.playlist_rows each time but that's # expensive and slow on big playlists @@ -1090,13 +1138,43 @@ class PlaylistModel(QAbstractTableModel): track_sequence.current, track_sequence.previous, ]: - if ts and ts.playlist_id == self.playlist_id and ts.row_number: - playlist_row = session.get(PlaylistRows, ts.playlistrow_id) - if playlist_row and playlist_row.row_number != ts.row_number: - ts.row_number = playlist_row.row_number + if ts: + ts.update_playlist_and_row(session) self.update_track_times() + def remove_comments(self, row_numbers: list[int]) -> None: + """ + Remove comments from passed rows + """ + + if not row_numbers: + return + + # Safety check + if not ask_yes_no( + title="Remove comments", + question=f"Remove comments from {len(row_numbers)} rows?" + ): + return + + with db.Session() as session: + for row_number in row_numbers: + playlist_row = session.get( + PlaylistRows, self.playlist_rows[row_number].playlistrow_id + ) + if playlist_row.track_id: + playlist_row.note = "" + # We can't use refresh_data() because its + # optimisations mean it won't update comments in + # self.playlist_rows + # The "correct" approach would be to re-read from the + # database but we optimise here by simply updating + # self.playlist_rows directly. + self.playlist_rows[row_number].note = "" + session.commit() + self.invalidate_rows(row_numbers) + def _reversed_contiguous_row_groups( self, row_numbers: list[int] ) -> list[list[int]]: diff --git a/app/playlists.py b/app/playlists.py index f816660..bd1a566 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -483,6 +483,13 @@ class PlaylistTab(QTableView): self._add_context_menu( "Rescan track", lambda: self._rescan(model_row_number) ) + self._add_context_menu( + "Mark for moving", lambda: self._mark_for_moving() + ) + if self.musicmuster.move_source_rows: + self._add_context_menu( + "Move selected rows here", lambda: self._move_selected_rows() + ) # ---------------------- self.menu.addSeparator() @@ -498,6 +505,9 @@ class PlaylistTab(QTableView): lambda: proxy_model.remove_track(model_row_number), ) + # Remove comments + self._add_context_menu("Remove comments", lambda: self._remove_comments()) + # Add track to section header (ie, make this a track row) if header_row: self._add_context_menu("Add a track", lambda: self._add_track()) @@ -740,6 +750,20 @@ class PlaylistTab(QTableView): self.source_model.mark_unplayed(row_numbers) self.clear_selection() + def _mark_for_moving(self) -> None: + """ + Mark selected rows for pasting + """ + + self.musicmuster.mark_rows_for_moving() + + def _move_selected_rows(self) -> None: + """ + Move selected rows here + """ + + self.musicmuster.paste_rows() + def _open_in_audacity(self, row_number: int) -> None: """ Open track in passed row in Audacity @@ -757,6 +781,17 @@ class PlaylistTab(QTableView): except ApplicationError as e: show_warning(self.musicmuster, "Audacity error", str(e)) + def _remove_comments(self) -> None: + """ + Remove comments from selected rows + """ + + row_numbers = self.selected_model_row_numbers() + if not row_numbers: + return + + self.source_model.remove_comments(row_numbers) + def _rescan(self, row_number: int) -> None: """Rescan track""" diff --git a/app/ui/green-circle.png b/app/ui/green-circle.png new file mode 100644 index 0000000..388a876 Binary files /dev/null and b/app/ui/green-circle.png differ diff --git a/app/ui/icons.qrc b/app/ui/icons.qrc index d02caf5..6e5d633 100644 --- a/app/ui/icons.qrc +++ b/app/ui/icons.qrc @@ -1,5 +1,7 @@ + yellow-circle.png + green-circle.png star.png star_empty.png record-red-button.png diff --git a/app/ui/icons_rc.py b/app/ui/icons_rc.py index b3de8d0..d9681af 100644 --- a/app/ui/icons_rc.py +++ b/app/ui/icons_rc.py @@ -5154,6 +5154,430 @@ I\x92$I\x92$I\x92$I\x92$I\x92$I\ $I\x92$I\x92$I\x92$I\x92$I\x92\xa4\ U\xfd/\xdc\xe3\x0a%XB:O\x00\x00\x00\x00I\ END\xaeB`\x82\ +\x00\x00\x1aX\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x06\x00\x00\x00\xf4x\xd4\xfa\ +\x00\x00\x00\xc5zTXtRaw prof\ +ile type exif\x00\x00x\ +\xdamP\xdb\x0d\x830\x0c\xfc\xf7\x14\x1d!~\x908\ +\xe3\x04H\xa5n\xd0\xf1\xeb`\xa7\x02\x84\xa5\x9c\xcd\x1d\ +:?\xa0\x7f?ox\x8d \x14\x90\xa5h\xae9'\ +\x0b\xa9R\xa9Y\xa1\xc9\xa3\x1d\x88I\x0e\x9cA\xc1^\ +x\xf8\x0bd\x14[f\x174{\xc6\xc9O\xa3\xc8\xd8\ +\xacZNF\xba\x85\xb0^\x85*\xd1^oF\xd1\x88\ +\xc7Dc\x84=\x8cj\x181\xb9\x80a\xd0|\xad\x94\ +\xab\x96\xf3\x0akO\xd7P\x7f0`\xeb\xbe\x1eF\xb7\ +\xfb\xb7\x14\xbb\xde\xbe\x18\xc9D\x9d\x8d6d\x16\x1f\x80\ +\xc7\x13\xe0f\x02\x19&.\xf6#r\xb1\x9a\xb8\x1e8\ +'\xb1\x83<\xddi\x06\xfc\x00<\xd0Y\xd6\xc84}\ +\xda\x00\x00\x01\x84iCCPICC pro\ +file\x00\x00x\x9c}\x91=H\xc3@\x1c\xc5\ +_S\xa5\x22\x15\x11;\x88:d\xa8Nv\xf1\x0b\xc7\ +Z\x85\x22T\x08\xb5B\xab\x0e&\x97~\x08M\x1a\x92\ +\x14\x17G\xc1\xb5\xe0\xe0\xc7b\xd5\xc1\xc5YW\x07W\ +A\x10\xfc\x00qvpRt\x91\x12\xff\x97\x14Z\xc4\ +xp\xdc\x8fw\xf7\x1ew\xef\x00\xa1^f\x9a\xd5\x11\ +\x074\xdd6\xd3\xc9\x84\x98\xcd\xad\x88\xa1W\x84\x11B\ +\x1f\x860%3\xcb\x98\x95\xa4\x14|\xc7\xd7=\x02|\ +\xbd\x8b\xf1,\xffs\x7f\x8e\x1e5o1 \x12\xc7\ +\x99a\xda\xc4\xeb\xc4\xd3\x9b\xb6\xc1y\x9f8\xc2J\xb2\ +J|NN\x1f\ +\x80\x0cu\x95\xba\x01\x0e\x0e\x81\xd1\x22e\xaf\xf9\xbc\xbb\ +\xab\xbd\xb7\x7f\xcf4\xfb\xfb\x01\xbfSr\xc5n=\xc5\ +\x13\x00\x00\x0e[iTXtXML:com\ +.adobe.xmp\x00\x00\x00\x00\x00<\ +?xpacket begin=\x22\ +\xef\xbb\xbf\x22 id=\x22W5M0MpC\ +ehiHzreSzNTczkc9\ +d\x22?>\x0a\x0a \x0a \x0a \x0a \ +\x0a <\ +rdf:li\x0a stE\ +vt:action=\x22saved\ +\x22\x0a stEvt:ch\ +anged=\x22/\x22\x0a \ +stEvt:instanceID\ +=\x22xmp.iid:0eb6ed\ +ca-2091-4bcf-976\ +3-62819213ae14\x22\x0a\ + stEvt:soft\ +wareAgent=\x22Gimp \ +2.10 (Linux)\x22\x0a \ + stEvt:when=\x22\ +2024-05-06T17:21\ +:44+01:00\x22/>\x0a \ + \x0a\ + \x0a \ + \x0a \x0a \x0a\ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ +\x0a\x99\xb0bn\x00\x00\x00\x06bKGD\ +\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pH\ +Ys\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\xa8d\x00\ +\x00\x00\x07tIME\x07\xe8\x0c\x07\x11\x1d\x1aq\x0e\ +\xba\xd6\x00\x00\x09\x1dIDATx\xda\xed\xdc\xdd\x8d\ +\xea\xca\x16\x85Q\xe8X\x9c \x91\x90\xa0s\x81\x07\x9e\ +L[\x08\xaaj\xd5\xef\x18\x09\x5ciK\xe7\xce\xcf\xcb\ +\xa6/\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xe0\xea\x9f\x00\ +\xc6\xb6\xdd\xb7G\x8b\xff\xdd\xfd\xb6\xfb\xff\x0f\x10\x00\xc0\ +L\xe3.\x12@\x00\x00\x86^\x18\x80\x00\x00\x0c\xbd0\ +\x00\x01\x00\x18|A\x00\x02\x00\x0c\xbe\xc1\x17\x04 \x00\ +\xc0\xe0#\x08@\x00\x80\xd1G\x0c\x80\x00\x00\xa3\x8f\x18\ +\x00\x01\x00F\x1f1\x00\x02\x00\x8c>b\x00\x04\x00\x18\ +~\x84\x00\x08\x000\xfa\x88\x01\x10\x00`\xf8\x11\x02 \ +\x00\xc0\xe8#\x06@\x00`\xf8A\x08\x80\x00\xc0\xf0\x83\ +\x10\x00\x01\x80\xe1\x07!\x80\x00\x00\xc3\x0fB\x00\x01\x00\ +\x86\x1f\x84\x00\x02\x00\x0c?\x08\x01\x04\x00\x18~\x10\x02\ +\x08\x000\xfe \x02\x10\x00`\xf8\x11\x02 \x00\xc0\xf0\ +#\x04@\x00\x80\xf1G\x04\x80\x00\x00\xc3\x8f\x10\x00\x01\ +\x80\xe1\x07!\x00\x02\x00\xe3\x0f\x22\x00\x04\x00\x86\x1f\x84\ +\x00\x08\x00\x8c?\x88\x00\x10\x00\x18\x7f\x10\x01 \x000\ +\xfc \x04@\x00`\xfcA\x04\x80\x00\xc0\xf0\x83\x10@\ +\x00\x80\xf1\x07\x11\x80\x00\x00\xe3\x0f\x22\x00\x01\x00\xc6\x1f\ +D\x00\x02\x00\x0c?\x08\x01\x04\x00\xc6\xdf\xf8\x83\x08@\ +\x00`\xfc\x01\x11\x80\x00\xc0\xf8\x03\x22\x00\x01\x80\xf1\x07\ +D\x00\x02\x00\xe3\x0f\x88\x00\x04\x00\x86\x1f\x10\x02\x08\x00\ +\x8c? \x02\x10\x00\x18\x7f@\x04 \x000\xfe\x80\x08\ +@\x00`\xfc\x01\x11\x80\x00\xc0\xf8\x83\x08\x10\x01\x08\x00\ +\x8c?\x88\x00\x10\x00\x18\x7f\x10\x01\x08\x000\xfe \x02\ +\x10\x00\x18\x7f@\x04 \x000\xfe\x80\x08@\x00`\xfc\ +\x01\x11\xc0\xd8\xfe\xfc\x13\x00\x80\x0b\x00\x9e\xfe\x01W\x00\ +\x04\x00\xc6\x1f\x10\x01\x08\x00\x8c? \x02\x10\x00\x18\x7f\ +@\x04 \x000\xfe\x80\x08@\x00`\xfc\x01\x11@\xaf\ +\xfc\x0c\x10\x00\x5c\x00\xf0\xf4\x0f\xb8\x02\xb8\x02\x08\x00\x8c\ +? \x02\x10\x00\x18\x7f@\x04 \x000\xfe\x80\x08`\ +P>\x02\x04\x00\x17\x00<\xfd\x03\xb8\x02\x08\x00\x8c?\ +\x80\x08\x98\x92W\x00\x00\xe0\x02\x80\xa7\x7f\x00W\x00\x01\ +\x80\xf1\x07\x10\x01\x02\x00\xe3\x0f \x02\xe6\xe0\x1b\x00\x00\ +p\x01\xc0\xd3?\x80+\x80\x00\xc0\xf8\x03\x88\x80)y\ +\x05\x00\x00.\x00x\xfa\x07p\x05p\x01\x00\x00\x5c\x00\ +\xf0\xf4\x0f\xe0\x0a \x000\xfe\x00\x22`P^\x01\x00\ +\x80\x0b\x00\x9e\xfe\x01\x5c\x01\x5c\x00\x00\x00\x17\x00<\xfd\ +\x03\xb8\x02\x08\x00\x8c?\x80\x08\x18\x94W\x00\x00\xe0\x02\ +\x80\xa7\x7f\x00W\x00\x17\x00\x00\xc0\x05\x00O\xff\x00\xae\ +\x00.\x00\x00\x80\x0b\x00\x9e\xfe\x01\x5c\x01\x5c\x00\x00\x00\ +\x17\x00<\xfd\x03\xae\x00\xb8\x00\x00\x00.\x00x\xfa\x07\ +\x5c\x01p\x01\x00\x00\x5c\x00<\xfd\x03\xb8\x02\xe0\x02\x00\ +\x00\xb8\x00x\xfa\x07p\x05\xc0\x05\x00\x00\x10\x00\x00\xc0\ +\x7fN1\x9dp\xfe\x07V\xe15\x80\x0b\x00\x00\xe0\x02\ +\xe0\xe9\x1f\xc0\x15\x00\x17\x00\x00@\x00\x00\x00\xe58\xc1\ +4\xe6\xfc\x0f\xac\xcak\x00\x17\x00\x00\xc0\x05\xc0\xd3?\ +\x80+\x00.\x00\x00\x80\x00\x00\x00\xf29\xbd4\xe2\xfc\ +\x0f\xf0\xe25\x80\x0b\x00\x00 \x00\x00\x80(\xce.\x0d\ +8\xff\x03\x1cy\x0d\xe0\x02\x00\x00\x08\x00\x00 \x82\x93\ +Ke\xce\xff\x00\xe7\xbc\x06p\x01\x00\x00\x04\x00\x00P\ +\x9asKE\xce\xff\x00\x9fy\x0d\xe0\x02\x00\x00\x08\x00\ +\x00@\x00\x00\x00Y\xbck\xa9\xc4\xfb\x7f\x80\xef\xf8\x0e\ +\xc0\x05\x00\x00\x10\x00\x00\x80\x00\x00\x00\x92y\xcfR\x81\ +\xf7\xff\x00\xbf\xf1\x1d\x80\x0b\x00\x00 \x00\x00\x00\x01\x00\ +\x00$\xf1\x8e%\x98\xf7\xff\x00i|\x07\xe0\x02\x00\x00\ +\x08\x00\x00@\x00\x00\x00\x02\x00\x00\x10\x00\x00\xc0\x09_\ +X\x06\xf2\x0b\x00\x80<~\x09\xe0\x02\x00\x00\x08\x00\x00\ +@\x00\x00\x00\x02\x00\x00\x10\x00\x00\xc0\x1b_W\x06\xf1\ +\x0b\x00\x802\xfc\x12\xc0\x05\x00\x00\x10\x00\x00\x80\x00\x00\ +\x00\x04\x00\x00 \x00\x00\x00\x01\x00\x00\x02\x00\x00\x10\x00\ +\x00\xc0\xec\xfcq\x85\x00\xfe\x08\x10@Y\xfe\x18\x90\x0b\ +\x00\x00 \x00\x00\x00\x01\x00\x00\x08\x00\x00@\x00\x00\x00\ +\x02\x00\x00\x04\x00\x00 \x00\x00\x00\x01\x00\x00\x08\x00\x00\ +@\x00\x00\x00\x02\x00\x00\x10\x00\x00\x80\x00\x00\x00\x04\x00\ +\x00 \x00\x00\x00\x01\x00\x00\x08\x00\x00@\x00\x00\x00\x02\ +\x00\x00\x10\x00\x00\x80\x00\x00\x00\x04\x00\x00\x08\x00\x00@\ +\x00\x00\x00\x02\x00\x00\x10\x00\x00\x80\x00\x00\x00\x04\x00\x00\ +0\x80\xab\x7f\x82\x18\xdb}{\xf8W\x00\xc8\xb7\xdfv\ +[\xe5\x02\x00\x00\x08\x00\x00@\x00\x00\x00\x02\x00\x00\x10\ +\x00\x00\x80\x00\x00\x00\x01\x00\x00\x08\x00\x00`j\xfe\xb8\ +B \x7f\x0c\x08 \x8f?\x02\xe4\x02\x00\x00\x08\x00\x00\ +@\x00\x00\x00\x02\x00\x00\x10\x00\x00\xc0\x1b_W\x06\xf3\ +K\x00\x804~\x01\xe0\x02\x00\x00\x08\x00\x00@\x00\x00\ +\x00\x02\x00\x00\x10\x00\x00\xc0\x09_XV\xe0\x97\x00\x00\ +\xbf\xf1\x0b\x00\x17\x00\x00@\x00\x00\x00\x02\x00\x00H\xe2\ +\x1dK%\xbe\x03\x00\xf8\x8e\xf7\xff.\x00\x00\x80\x00\x00\ +\x00\x04\x00\x00\x90\xcc{\x96\x8a|\x07\x00\xf0\x99\xf7\xff\ +.\x00\x00\x80\x00\x00\x00\x04\x00\x00\x90\xc5\xbb\x96\xca|\ +\x07\x00p\xce\xfb\x7f\x17\x00\x00@\x00\x00\x00\xa59\xb7\ +4\xe05\x00\xc0\x91\xf3\xbf\x0b\x00\x00 \x00\x00\x80\x08\ +N.\x8dx\x0d\x00\xf0\xe2\xfc\xef\x02\x00\x00\x08\x00\x00\ + \x8a\xb3KC^\x03\x00\xabs\xfew\x01\x00\x00\x04\ +\x00\x00\x10\xc9\xe9\xa51\xaf\x01\x80U9\xff\xbb\x00\x00\ +\x00.\x00\xae\x00\x00\x9e\xfeq\x01\x00\x00\x04\x00\x00\x90\ +\xcf\x09\xa6\x13^\x03\x00\xabp\xfew\x01\x00\x00\x5c\x00\ +p\x05\x00<\xfd\xe3\x02\x00\x00\x08\x00\x00\xa0\x1c\xa7\x98\ +\xcex\x0d\x00\xcc\xca\xf9\xdf\x05\x00\x00p\x01\xc0\x15\x00\ +\xf0\xf4\x8f\x0b\x00\x00\xe0\x02\xe0\x0a\x00\xe0\xe9\x1f\x17\x00\ +\x00\xc0\x05\xc0\x15\x00\xc0\xd3?.\x00\x00\x80\x0b\x80+\ +\x00\x80\xa7\x7f\x5c\x00\x00\x00\x17\x00W\x00\x00O\xff.\ +\x00\x00\x80\x0b\x00\xae\x00\x00\x9e\xfe]\x00\x00\x00\x17\x00\ +\x5c\x01\x00<\xfd\x0b\x00D\x00\x80\xf1\x1f\x8cW\x00\x00\ +\xe0\x02\x80+\x00\x80\xa7\x7f\x17\x00\x00\xc0\x05\x00W\x00\ +\x00O\xff\x02\x00\x11\x00\x18\x7f;2(\xaf\x00\x00\xc0\ +\x05\x00W\x00\x00O\xff.\x00\x00\x80\x0b\x00\xae\x00\x00\ +\x9e\xfe\x05\x00\x22\x000\xfe\x0c\xca+\x00\x00p\x01\xc0\ +\x15\x00\xc0\xd3\xbf\x00@\x04\x00\x18\x7f\x01\x80\x08\x00\x8c\ +?s\xf0\x0d\x00\x00\xb8\x00\xe0\x0a\x00\xe0\xe9_\x00 \ +\x02\x00\xe3\xcf\x94\xbc\x02\x00\xc0\xf8\x0b\x00\xfc\xc7\x0b\xc0\ +\x0a\x0c\xc4\x02\xbc\x0a\x00<@ \x00D\x00\x80\xf1G\ +\x00\x88\x00\x00\xe3\xbf\x22\xdf\x00\x00\x80\x0b\x00\xae\x00\x80\ +\xa7\x7f\x04\x00\x22\x000\xfe\x08\x00D\x00`\xfc\x11\x00\ +\x88\x00\xc0\xf8#\x00\x10\x01\x80\xf1G\x00 \x02\x00\xe3\ +O\xb7\xfc\x0c\x10\x00\x5c\x00p\x05\x00<\xfd#\x00\x10\ +\x01\x80\xf1G\x00 \x02\x00\xe3\x8f\x00@\x04\x00\xc6\x1f\ +\x01\x80\x08\x00\x8c?\x02\x00\x11\x00\x18\x7f\x04\x00\x22\x00\ +0\xfe\x08\x00D\x00`\xfc\x11\x00\x88\x00\xc0\xf8#\x00\ +\x10\x01\x80\xf1G\x00 \x02\x00\xe3\x8f\x00@\x08\x00\x86\ +\x1f\x01\x80\x08\x00\xe3\x0f\x02\x00\x11\x00\xc6\x1f\x04\x00\x22\ +\x00\x8c?\x08\x00D\x00\x18\x7f\x10\x00\x88\x000\xfe\x08\ +\x00\x10\x02`\xf8\x11\x00 \x02\xc0\xf8#\x00@\x04\x80\ +\xf1G\x00\x80\x08\x00\xc3\x8f\x00\x00!\x00\xc6\x1f\x01\x00\ +\x22\x00\x8c?\x02\x00\x84\x00\x18~\x04\x00\x88\x000\xfe\ +\x08\x00D\x00\x18\x7f\x10\x00\x08\x010\xfc \x00\x10\x01\ +`\xfcA\x00 \x04\xc0\xf0\x83\x00@\x08\x80\xe1\x07\x01\ +\x80\x08\x00\xe3\x0f\x02\x00!\x00\x86\x1f\x04\x00B\x00\x0c\ +?\x08\x00D\x00\x18\x7f\x04\x00\x08\x010\xfc\x08\x00\x10\ +\x02`\xf8\x11\x00 \x04\xc0\xf0#\x00@\x08`\xf8A\ +\x00\x80\x10\xc0\xf0\x83\x00\x00!\x80\xe1\x07\x01\x00B\x00\ +\xc3\x0f\x02\x00\xc4\x00F\x1f\x04\x00\x08\x01\x0c?\x08\x00\ +\x10\x03\x18}\x10\x00 \x040\xfc \x00@\x0c`\xf4\ +A\x00\x80\x18\xc0\xe8\x83\x00\x001`\xf4\x01\x01\x00\x82\ +\xc0\xe8\x03\x02\x00\xc4\x80\xc1\x07\x04\x00\x08\x02\x83\x0f\x02\ +\x00\x10\x04\x06\x1f\x04\x00 \x0c\x0c=\x08\x00@\x18\x18\ +z\x10\x00\x80H0\xee\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +L\xe6\x09l=\x1eh\xbb\x22\xdfY\x00\x00\x00\x00I\ +END\xaeB`\x82\ \x00\x00\x1aS\ \x89\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ @@ -6987,6 +7411,432 @@ PH\x00\x00@!\x01\x00\x00\x85\x04\x00\x00\x14\x12\x00\ \x00\x00PH\x00\x00@!\x01\x00\x00\x85\xfe\x17S\xd3\ \xbcN3\xeb\xa5U\x00\x00\x00\x00IEND\xaeB\ `\x82\ +\x00\x00\x1az\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x06\x00\x00\x00\xf4x\xd4\xfa\ +\x00\x00\x00\xc6zTXtRaw prof\ +ile type exif\x00\x00x\ +\xdamP[\x0e\xc30\x08\xfb\xe7\x14;B\x024\x8f\ +\xe3\xa4m&\xed\x06;\xfe\x9cB\xa6\xb6\xaa\xa5\x00\xb1\ +#\x03\xa1\xfe\xfd\xbc\xe95\xc0QI\x97\x5cRM)\ +\x00Z\xb5rCQ\x82\xa1\x1d1\x06=\xe2\x04;{\ +\xe1\xe9/0(A\x16\x13J\xb2\x1c'?\x8d<\xc7\ +\x86j9\x19\x95\xcd\x85\xf5*T\xf5\xf6\xe5f\xe4\x8d\ +dL4F\xd8\xdd\xa8\xba\x91\xb0\x09\xd1\x0d\x9a\xad\x15\ +R-\xf9\xbc\xc2\xda\xc3\x15\xc5\x0e\x8d\xb0u[/z\ +\xb7\xfb]3~o_@\x0as\x17\xd0\x88\x22j\x03\ +\xc88J\xd2 0b\x90\x8c\x87Q2j\xc1m\xf0\ +\xd9'\xc1\x87<\xfd\xd3\x04\xfd\x00;\xb0Y\xd0\xf2*\ +1\xb7\x00\x00\x01\x84iCCPICC pr\ +ofile\x00\x00x\x9c}\x91=H\xc3@\x1c\ +\xc5_S\xa5\x22\x15\x11;\x88:d\xa8Nv\xf1\x0b\ +\xc7Z\x85\x22T\x08\xb5B\xab\x0e&\x97~\x08M\x1a\ +\x92\x14\x17G\xc1\xb5\xe0\xe0\xc7b\xd5\xc1\xc5YW\x07\ +WA\x10\xfc\x00qvpRt\x91\x12\xff\x97\x14Z\ +\xc4xp\xdc\x8fw\xf7\x1ew\xef\x00\xa1^f\x9a\xd5\ +\x11\x074\xdd6\xd3\xc9\x84\x98\xcd\xad\x88\xa1W\x84\x11\ +B\x1f\x860%3\xcb\x98\x95\xa4\x14|\xc7\xd7=\x02\ +|\xbd\x8b\xf1,\xffs\x7f\x8e\x1e5o1 \x12\ +\xc7\x99a\xda\xc4\xeb\xc4\xd3\x9b\xb6\xc1y\x9f8\xc2J\ +\xb2J|NN\ +\x1f\x80\x0cu\x95\xba\x01\x0e\x0e\x81\xd1\x22e\xaf\xf9\xbc\ +\xbb\xab\xbd\xb7\x7f\xcf4\xfb\xfb\x01\xbfSr\xc5n=\ +\xc5\x13\x00\x00\x0e[iTXtXML:co\ +m.adobe.xmp\x00\x00\x00\x00\x00\ +\x0a\x0a \x0a <\ +rdf:Description \ +rdf:about=\x22\x22\x0a \ + xmlns:xmpMM=\x22ht\ +tp://ns.adobe.co\ +m/xap/1.0/mm/\x22\x0a \ + xmlns:stEvt=\x22\ +http://ns.adobe.\ +com/xap/1.0/sTyp\ +e/ResourceEvent#\ +\x22\x0a xmlns:dc=\x22\ +http://purl.org/\ +dc/elements/1.1/\ +\x22\x0a xmlns:GIMP\ +=\x22http://www.gim\ +p.org/xmp/\x22\x0a \ +xmlns:tiff=\x22http\ +://ns.adobe.com/\ +tiff/1.0/\x22\x0a x\ +mlns:xmp=\x22http:/\ +/ns.adobe.com/xa\ +p/1.0/\x22\x0a xmpMM\ +:DocumentID=\x22gim\ +p:docid:gimp:66a\ +dc484-6096-437b-\ +aa0d-3b0c0a65c53\ +d\x22\x0a xmpMM:Inst\ +anceID=\x22xmp.iid:\ +35e7befe-1312-42\ +39-acb8-27e7aad8\ +cbff\x22\x0a xmpMM:O\ +riginalDocumentI\ +D=\x22xmp.did:9f854\ +f62-b4a5-42e7-98\ +c2-a9cc8ee1f29f\x22\ +\x0a dc:Format=\x22i\ +mage/png\x22\x0a GIM\ +P:API=\x222.0\x22\x0a G\ +IMP:Platform=\x22Li\ +nux\x22\x0a GIMP:Tim\ +eStamp=\x2217335926\ +19389840\x22\x0a GIM\ +P:Version=\x222.10.\ +34\x22\x0a tiff:Orie\ +ntation=\x221\x22\x0a x\ +mp:CreatorTool=\x22\ +GIMP 2.10\x22\x0a xm\ +p:MetadataDate=\x22\ +2024:12:07T17:30\ +:17+00:00\x22\x0a xm\ +p:ModifyDate=\x2220\ +24:12:07T17:30:1\ +7+00:00\x22>\x0a \x0a \ + \x0a \ +\x0a \ + \ +\x0a \x0a\ + \x0a \x0a \x0a\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a\ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \ + \ + \ + \ + \ +\x0a \ + \ + \ + \ + \ + \ + \x0a \ + \ + \x0a\xc2\x0b\x10K\x00\x00\x00\x06bKG\ +D\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09p\ +HYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\xa8d\ +\x00\x00\x00\x07tIME\x07\xe8\x0c\x07\x11\x1e\x13#\ +\xffQ\xb1\x00\x00\x09>IDATx\xda\xed\xdc\xcb\ +\xb1\xe2@\x10EA`\x83Y\xd8\x8eY\xac\xc4\x86\x8d\ +P\x10@wW\x7f3Mx\x113\xf7\xa8\x04\x9cN\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xce\xd9\x9f\x00\xc6\xb6\xdd\ +\xaf[\x93\xffb\x00\x04\x00\x18}\xc4\x00\x08\x000\ +\xfa\x88\x01\x10\x00`\xf4\x11\x03 \x00\xc0\xf0#\x04@\ +\x00\x80\xd1G\x0c\x80\x00\x00\xc3\x8f\x10\x00\x01\x00F\x1f\ +1\x00\x02\x00\xc3\x0fB\x00\x04\x00\x86\x1f\x84\x00\x08\x00\ +\x0c?\x08\x01\x04\x00\x18~\x10\x02\x08\x000\xfc \x04\ +\x10\x00`\xf8A\x08 \x00\xc0\xf0\x83\x10@\x00\x80\xf1\ +\x07\x11\x80\x00\x00\xc3\x8f\x10\x00\x01\x00\x86\x1f!\x00\x02\ +\x00\x8c?\x22\x00\x04\x00\x18~\x84\x00\x08\x00\x0c?\x08\ +\x01\x10\x00\x18\x7f\x10\x01 \x000\xfc \x04@\x00`\ +\xfcA\x04\x80\x00\xc0\xf8\x83\x08\x00\x01\x80\xe1\x07!\x00\ +\x02\x00\xe3\x0f\x22\x00\x04\x00\x86\x1f\x84\x00\x02\x00\x8c?\ +\x88\x00\x04\x00\x18\x7f\x10\x01\x08\x000\xfe \x02\x10\x00\ +`\xf8A\x08 \x000\xfe\xc6\x1fD\x00\x02\x00\xe3\x0f\ +\x88\x00\x04\x00\xc6\x1f\x10\x01\x08\x00\x8c? \x02\x10\x00\ +\x18\x7f@\x04 \x000\xfc\x80\x10@\x00`\xfc\x01\x11\ +\x80\x00\xc0\xf8\x03\x22\x00\x01\x80\xf1\x07D\x00\x02\x00\xe3\ +\x0f\x88\x00\x04\x00\xc6\x1fD\x80\x08@\x00`\xfcA\x04\ +\x80\x00\xc0\xf8\x83\x08@\x00\x80\xf1\x07\x11\x80\x00\xc0\xf8\ +\x03\x22\x00\x01\x80\xf1\x07D\x00\x02\x00\xe3\x0f\x88\x00\xc6\ +v\xf1'\x00\x00\x17\x00<\xfd\x03\xae\x00\x08\x00\x8c?\ + \x02\x10\x00\x18\x7f@\x04 \x000\xfe\x80\x08@\x00\ +`\xfc\x01\x11\x80\x00\xc0\xf8\x03\x22\x80^\xf9\x1a \x00\ +\xb8\x00\xe0\xe9\x1fp\x05p\x05\x10\x00\x18\x7f@\x04 \ +\x000\xfe\x80\x08@\x00`\xfc\x01\x11\xc0\xa0|\x08\x10\ +\x00\x5c\x00\xf0\xf4\x0f\xe0\x0a \x000\xfe\x00\x22`J\ +^\x01\x00\x80\x0b\x00\x9e\xfe\x01\x5c\x01\x04\x00\xc6\x1f@\ +\x04\x08\x00\x8c?\x80\x08\x98\x83\xcf\x00\x00\x80\x0b\x00\x9e\ +\xfe\x01\x5c\x01\x04\x00\xc6\x1f@\x04L\xc9+\x00\x00p\ +\x01\xc0\xd3?\x80+\x80\x0b\x00\x00\xe0\x02\x80\xa7\x7f\x00\ +W\x00\x01\x80\xf1\x07\x10\x01\x83\xf2\x0a\x00\x00\x5c\x00\xf0\ +\xf4\x0f\xe0\x0a\xe0\x02\x00\x00\xb8\x00\xe0\xe9\x1f\xc0\x15@\ +\x00`\xfc\x01D\xc0\xa0\xbc\x02\x00\x00\x17\x00<\xfd\x03\ +\xb8\x02\xb8\x00\x00\x00.\x00x\xfa\x07p\x05p\x01\x00\ +\x00\x5c\x00\xf0\xf4\x0f\xe0\x0a\xe0\x02\x00\x00\xb8\x00\xe0\xe9\ +\x1fp\x05\xc0\x05\x00\x00p\x01\xc0\xd3?\xe0\x0a\x80\x0b\ +\x00\x00\xe0\x02\xe0\xe9\x1f\xc0\x15\x00\x17\x00\x00\xc0\x05\xc0\ +\xd3?\x80+\x00.\x00\x00\x80\x00\x00\x00\x8e\x9cb:\ +\xe1\xfc\x0f,3<^\x03\xb8\x00\x00\x00.\x00\x9e\xfe\ +\x01\x5c\x01p\x01\x00\x00\x04\x00\x00P\x8c\x13Lc\xce\ +\xff\xc0\xb2\x03\xe45\x80\x0b\x00\x00\xe0\x02\xe0\xe9\x1f\xc0\ +\x15\x00\x17\x00\x00@\x00\x00\x00\xd9\x9c^\x1aq\xfe\x07\ +x\x0d\x91\xd7\x00.\x00\x00\x80\x00\x00\x00\x828\xbb4\ +\xe0\xfc\x0f\xf06F^\x03\xb8\x00\x00\x00\x02\x00\x00\x08\ +\xe0\xe4R\x99\xf3?\xc0\x87A\xf2\x1a\xc0\x05\x00\x00\x10\ +\x00\x00@a\xce-\x159\xff\x03|\x19%\xaf\x01\x5c\ +\x00\x00\x00\x01\x00\x00\x08\x00\x00 \x87w-\x95x\xff\ +\x0f\xf0\xe30\xf9\x1c\x80\x0b\x00\x00 \x00\x00\x00\x01\x00\ +\x00\xa4\xf2\x9e\xa5\x02\xef\xff\x01\xfe\x1c'\x9f\x03p\x01\ +\x00\x00\x04\x00\x00 \x00\x00\x80\x14\xde\xb1\x04\xf3\xfe\x1f\ + q\xa0|\x0e\xc0\x05\x00\x00\x10\x00\x00\x80\x00\x00\x00\ +\x04\x00\x00 \x00\x00\x80#\x9f\xb0\x0c\xe4\x1b\x00\x00\x99\ +#\xe5\x9b\x00.\x00\x00\x80\x00\x00\x00\x04\x00\x00 \x00\ +\x00\x00\x01\x00\x00\xec\xf9te\x10\xdf\x00\x00(4T\ +\xbe\x09\xe0\x02\x00\x00\x08\x00\x00@\x00\x00\x00\x02\x00\x00\ +\x10\x00\x00\x80\x00\x00\x00\x01\x00\x00\x08\x00\x00`r~\ +\x5c!\x80\x1f\x01\x02(\x08\x00\x10\x02\x18~\x10\x00 \x06\ +0\xfa \x00@\x0c`\xf4A\x00\x80\x180\xfa\x80\x00\ +\x00A`\xf4\x01\x01\x00b\xc0\xe0\x03\x02\x00\x04\x81\xc1\ +\x07\x01\x00\x08\x02\x83\x0f\x02\x00\x10\x06\x86\x1e\x04\x00 \ +\x0c\x0c=\x08\x00@$\x18w\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00&\xf3\x042\xbc\x1c\x88\x0c\xb2\xbfh\x00\x00\x00\ +\x00IEND\xaeB`\x82\ \x00\x03\x03W\ \xff\ \xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\ @@ -21203,6 +22053,10 @@ qt_resource_name = b"\ \x02\xac\xf9g\ \x00s\ \x00t\x00a\x00r\x00_\x00e\x00m\x00p\x00t\x00y\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x02\x1c\xb4\xa7\ +\x00g\ +\x00r\x00e\x00e\x00n\x00-\x00c\x00i\x00r\x00c\x00l\x00e\x00.\x00p\x00n\x00g\ \x00\x0a\ \x0bb\xb2#\ \x00h\ @@ -21215,6 +22069,11 @@ qt_resource_name = b"\ \x00\x06\xc7\xa5\ \x00f\ \x00a\x00d\x00e\ +\x00\x11\ +\x07 \x1a\x87\ +\x00y\ +\x00e\x00l\x00l\x00o\x00w\x00-\x00c\x00i\x00r\x00c\x00l\x00e\x00.\x00p\x00n\x00g\ +\ \x00\x08\ \x0bg\x90\x9e\ \x00s\ @@ -21241,11 +22100,11 @@ qt_resource_name = b"\ qt_resource_struct = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x12\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x14\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x01*\x00\x00\x00\x00\x00\x01\x00\x01\xa2\x89\ +\x00\x00\x01P\x00\x00\x00\x00\x00\x01\x00\x01\xbc\xe5\ \x00\x00\x01xr\xe9}\xc0\ -\x00\x00\x01\xb2\x00\x00\x00\x00\x00\x01\x00\x05&\x0f\ +\x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x05Z\xe9\ \x00\x00\x01x\x84r}X\ \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x01x\xbb\xe5wh\ @@ -21253,25 +22112,29 @@ qt_resource_struct = b"\ \x00\x00\x01xJ\xff\x88\xe0\ \x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x01\x0aa\ \x00\x00\x01x\x84r}X\ -\x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x05\x11D\ +\x00\x00\x01\xe8\x00\x00\x00\x00\x00\x01\x00\x05F\x1e\ \x00\x00\x01y/9=X\ +\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x01\x00\x01@o\ +\x00\x00\x01\x93\xa2+J\x8e\ \x00\x00\x00\xd8\x00\x00\x00\x00\x00\x01\x00\x01\x12\x83\ -\x00\x00\x01\x8fTX\xa5\x95\ -\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x04\xf7\xc9\ -\x00\x00\x01\x8fN\xb6\x0a3\ +\x00\x00\x01\x90x\xb0R\x0a\ +\x00\x00\x01\xb8\x00\x00\x00\x00\x00\x01\x00\x05,\xa3\ +\x00\x00\x01\x90x\xb0R\x06\ +\x00\x00\x01^\x00\x00\x00\x00\x00\x01\x00\x01\xcc\xec\ +\x00\x00\x01\x93\xa2,\x19\x82\ \x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x04\x98\ \x00\x00\x01xr\xe5\x9d\x90\ \x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00d\xab\ \x00\x00\x01x\x84r}X\ \x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\x80\x85\ -\x00\x00\x01\x8fN\xb2\xe2%\ -\x00\x00\x01\x14\x00\x00\x00\x00\x00\x01\x00\x01Z\xc6\ -\x00\x00\x01\x8fTd\x16^\ -\x00\x00\x01N\x00\x00\x00\x00\x00\x01\x00\x04\xb5\xeb\ +\x00\x00\x01\x90x\xb0R\x06\ +\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x01u\x22\ +\x00\x00\x01\x90x\xb0R\x06\ +\x00\x00\x01\x9c\x00\x00\x00\x00\x00\x01\x00\x04\xea\xc5\ \x00\x00\x01\x80A\xd4\xb2`\ -\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x01\x00\x01@o\ +\x00\x00\x01 \x00\x00\x00\x00\x00\x01\x00\x01Z\xcb\ \x00\x00\x01\x8bRz\xee0\ -\x00\x00\x018\x00\x00\x00\x00\x00\x01\x00\x01\xb2\x90\ +\x00\x00\x01\x86\x00\x00\x00\x00\x00\x01\x00\x01\xe7j\ \x00\x00\x01x\xe8\xe8Z\xc8\ \x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\xbf\x98\ \x00\x00\x01xr\xe6\xdd\xe0\ diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py index e41eff2..7b3c1b9 100644 --- a/app/ui/main_window_ui.py +++ b/app/ui/main_window_ui.py @@ -1,6 +1,6 @@ # Form implementation generated from reading ui file 'app/ui/main_window.ui' # -# Created by: PyQt6 UI code generator 6.7.0 +# Created by: PyQt6 UI code generator 6.7.1 # # WARNING: Any manual changes made to this file will be lost when pyuic6 is # run again. Do not edit this file unless you know what you are doing. @@ -15,7 +15,11 @@ class Ui_MainWindow(object): MainWindow.resize(1280, 857) MainWindow.setMinimumSize(QtCore.QSize(1280, 0)) icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(":/icons/musicmuster"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon.addPixmap( + QtGui.QPixmap(":/icons/musicmuster"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) MainWindow.setWindowIcon(icon) MainWindow.setStyleSheet("") self.centralwidget = QtWidgets.QWidget(parent=MainWindow) @@ -27,39 +31,62 @@ class Ui_MainWindow(object): self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.previous_track_2.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.previous_track_2.sizePolicy().hasHeightForWidth() + ) self.previous_track_2.setSizePolicy(sizePolicy) self.previous_track_2.setMaximumSize(QtCore.QSize(230, 16777215)) font = QtGui.QFont() font.setFamily("Sans") font.setPointSize(20) self.previous_track_2.setFont(font) - self.previous_track_2.setStyleSheet("background-color: #f8d7da;\n" -"border: 1px solid rgb(85, 87, 83);") - self.previous_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) + self.previous_track_2.setStyleSheet( + "background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);" + ) + self.previous_track_2.setAlignment( + QtCore.Qt.AlignmentFlag.AlignRight + | QtCore.Qt.AlignmentFlag.AlignTrailing + | QtCore.Qt.AlignmentFlag.AlignVCenter + ) self.previous_track_2.setObjectName("previous_track_2") self.verticalLayout_3.addWidget(self.previous_track_2) self.current_track_2 = QtWidgets.QLabel(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.current_track_2.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.current_track_2.sizePolicy().hasHeightForWidth() + ) self.current_track_2.setSizePolicy(sizePolicy) self.current_track_2.setMaximumSize(QtCore.QSize(230, 16777215)) font = QtGui.QFont() font.setFamily("Sans") font.setPointSize(20) self.current_track_2.setFont(font) - self.current_track_2.setStyleSheet("background-color: #d4edda;\n" -"border: 1px solid rgb(85, 87, 83);") - self.current_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) + self.current_track_2.setStyleSheet( + "background-color: #d4edda;\n" "border: 1px solid rgb(85, 87, 83);" + ) + self.current_track_2.setAlignment( + QtCore.Qt.AlignmentFlag.AlignRight + | QtCore.Qt.AlignmentFlag.AlignTrailing + | QtCore.Qt.AlignmentFlag.AlignVCenter + ) self.current_track_2.setObjectName("current_track_2") self.verticalLayout_3.addWidget(self.current_track_2) self.next_track_2 = QtWidgets.QLabel(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.next_track_2.sizePolicy().hasHeightForWidth()) @@ -69,19 +96,29 @@ class Ui_MainWindow(object): font.setFamily("Sans") font.setPointSize(20) self.next_track_2.setFont(font) - self.next_track_2.setStyleSheet("background-color: #fff3cd;\n" -"border: 1px solid rgb(85, 87, 83);") - self.next_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) + self.next_track_2.setStyleSheet( + "background-color: #fff3cd;\n" "border: 1px solid rgb(85, 87, 83);" + ) + self.next_track_2.setAlignment( + QtCore.Qt.AlignmentFlag.AlignRight + | QtCore.Qt.AlignmentFlag.AlignTrailing + | QtCore.Qt.AlignmentFlag.AlignVCenter + ) self.next_track_2.setObjectName("next_track_2") self.verticalLayout_3.addWidget(self.next_track_2) self.horizontalLayout_3.addLayout(self.verticalLayout_3) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.hdrPreviousTrack.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.hdrPreviousTrack.sizePolicy().hasHeightForWidth() + ) self.hdrPreviousTrack.setSizePolicy(sizePolicy) self.hdrPreviousTrack.setMinimumSize(QtCore.QSize(0, 0)) self.hdrPreviousTrack.setMaximumSize(QtCore.QSize(16777215, 16777215)) @@ -89,32 +126,43 @@ class Ui_MainWindow(object): font.setFamily("Sans") font.setPointSize(20) self.hdrPreviousTrack.setFont(font) - self.hdrPreviousTrack.setStyleSheet("background-color: #f8d7da;\n" -"border: 1px solid rgb(85, 87, 83);") + self.hdrPreviousTrack.setStyleSheet( + "background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);" + ) self.hdrPreviousTrack.setText("") self.hdrPreviousTrack.setWordWrap(False) self.hdrPreviousTrack.setObjectName("hdrPreviousTrack") self.verticalLayout.addWidget(self.hdrPreviousTrack) self.hdrCurrentTrack = QtWidgets.QPushButton(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.hdrCurrentTrack.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.hdrCurrentTrack.sizePolicy().hasHeightForWidth() + ) self.hdrCurrentTrack.setSizePolicy(sizePolicy) font = QtGui.QFont() font.setPointSize(20) self.hdrCurrentTrack.setFont(font) - self.hdrCurrentTrack.setStyleSheet("background-color: #d4edda;\n" -"border: 1px solid rgb(85, 87, 83);\n" -"text-align: left;\n" -"padding-left: 8px;\n" -"") + self.hdrCurrentTrack.setStyleSheet( + "background-color: #d4edda;\n" + "border: 1px solid rgb(85, 87, 83);\n" + "text-align: left;\n" + "padding-left: 8px;\n" + "" + ) self.hdrCurrentTrack.setText("") self.hdrCurrentTrack.setFlat(True) self.hdrCurrentTrack.setObjectName("hdrCurrentTrack") self.verticalLayout.addWidget(self.hdrCurrentTrack) self.hdrNextTrack = QtWidgets.QPushButton(parent=self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.hdrNextTrack.sizePolicy().hasHeightForWidth()) @@ -122,10 +170,12 @@ class Ui_MainWindow(object): font = QtGui.QFont() font.setPointSize(20) self.hdrNextTrack.setFont(font) - self.hdrNextTrack.setStyleSheet("background-color: #fff3cd;\n" -"border: 1px solid rgb(85, 87, 83);\n" -"text-align: left;\n" -"padding-left: 8px;") + self.hdrNextTrack.setStyleSheet( + "background-color: #fff3cd;\n" + "border: 1px solid rgb(85, 87, 83);\n" + "text-align: left;\n" + "padding-left: 8px;" + ) self.hdrNextTrack.setText("") self.hdrNextTrack.setFlat(True) self.hdrNextTrack.setObjectName("hdrNextTrack") @@ -172,7 +222,12 @@ class Ui_MainWindow(object): self.cartsWidget.setObjectName("cartsWidget") self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget) self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + spacerItem = QtWidgets.QSpacerItem( + 40, + 20, + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Minimum, + ) self.horizontalLayout_Carts.addItem(spacerItem) self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1) self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget) @@ -217,7 +272,11 @@ class Ui_MainWindow(object): self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame) self.btnPreview.setMinimumSize(QtCore.QSize(132, 41)) icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon1.addPixmap( + QtGui.QPixmap(":/icons/headphones"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.btnPreview.setIcon(icon1) self.btnPreview.setIconSize(QtCore.QSize(30, 30)) self.btnPreview.setCheckable(True) @@ -239,8 +298,16 @@ class Ui_MainWindow(object): self.btnPreviewArm.setMaximumSize(QtCore.QSize(44, 23)) self.btnPreviewArm.setText("") icon2 = QtGui.QIcon() - icon2.addPixmap(QtGui.QPixmap(":/icons/record-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) - icon2.addPixmap(QtGui.QPixmap(":/icons/record-red-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On) + icon2.addPixmap( + QtGui.QPixmap(":/icons/record-button.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) + icon2.addPixmap( + QtGui.QPixmap(":/icons/record-red-button.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.On, + ) self.btnPreviewArm.setIcon(icon2) self.btnPreviewArm.setCheckable(True) self.btnPreviewArm.setObjectName("btnPreviewArm") @@ -261,8 +328,16 @@ class Ui_MainWindow(object): self.btnPreviewMark.setMaximumSize(QtCore.QSize(44, 23)) self.btnPreviewMark.setText("") icon3 = QtGui.QIcon() - icon3.addPixmap(QtGui.QPixmap(":/icons/star.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On) - icon3.addPixmap(QtGui.QPixmap(":/icons/star_empty.png"), QtGui.QIcon.Mode.Disabled, QtGui.QIcon.State.Off) + icon3.addPixmap( + QtGui.QPixmap(":/icons/star.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.On, + ) + icon3.addPixmap( + QtGui.QPixmap(":/icons/star_empty.png"), + QtGui.QIcon.Mode.Disabled, + QtGui.QIcon.State.Off, + ) self.btnPreviewMark.setIcon(icon3) self.btnPreviewMark.setObjectName("btnPreviewMark") self.btnPreviewFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) @@ -363,10 +438,15 @@ class Ui_MainWindow(object): self.verticalLayout_7.addWidget(self.label_silent_timer) self.horizontalLayout.addWidget(self.frame_silent) self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred, + ) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.widgetFadeVolume.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.widgetFadeVolume.sizePolicy().hasHeightForWidth() + ) self.widgetFadeVolume.setSizePolicy(sizePolicy) self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0)) self.widgetFadeVolume.setObjectName("widgetFadeVolume") @@ -383,7 +463,11 @@ class Ui_MainWindow(object): self.btnFade.setMinimumSize(QtCore.QSize(132, 32)) self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215)) icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon4.addPixmap( + QtGui.QPixmap(":/icons/fade"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.btnFade.setIcon(icon4) self.btnFade.setIconSize(QtCore.QSize(30, 30)) self.btnFade.setObjectName("btnFade") @@ -391,7 +475,11 @@ class Ui_MainWindow(object): self.btnStop = QtWidgets.QPushButton(parent=self.frame) self.btnStop.setMinimumSize(QtCore.QSize(0, 36)) icon5 = QtGui.QIcon() - icon5.addPixmap(QtGui.QPixmap(":/icons/stopsign"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon5.addPixmap( + QtGui.QPixmap(":/icons/stopsign"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.btnStop.setIcon(icon5) self.btnStop.setObjectName("btnStop") self.verticalLayout_5.addWidget(self.btnStop) @@ -417,39 +505,71 @@ class Ui_MainWindow(object): MainWindow.setStatusBar(self.statusbar) self.actionPlay_next = QtGui.QAction(parent=MainWindow) icon6 = QtGui.QIcon() - icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon6.addPixmap( + QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionPlay_next.setIcon(icon6) self.actionPlay_next.setObjectName("actionPlay_next") self.actionSkipToNext = QtGui.QAction(parent=MainWindow) icon7 = QtGui.QIcon() - icon7.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon7.addPixmap( + QtGui.QPixmap(":/icons/next"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionSkipToNext.setIcon(icon7) self.actionSkipToNext.setObjectName("actionSkipToNext") self.actionInsertTrack = QtGui.QAction(parent=MainWindow) icon8 = QtGui.QIcon() - icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon8.addPixmap( + QtGui.QPixmap( + "app/ui/../../../../../../.designer/backup/icon_search_database.png" + ), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionInsertTrack.setIcon(icon8) self.actionInsertTrack.setObjectName("actionInsertTrack") self.actionAdd_file = QtGui.QAction(parent=MainWindow) icon9 = QtGui.QIcon() - icon9.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon9.addPixmap( + QtGui.QPixmap( + "app/ui/../../../../../../.designer/backup/icon_open_file.png" + ), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionAdd_file.setIcon(icon9) self.actionAdd_file.setObjectName("actionAdd_file") self.actionFade = QtGui.QAction(parent=MainWindow) icon10 = QtGui.QIcon() - icon10.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon10.addPixmap( + QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionFade.setIcon(icon10) self.actionFade.setObjectName("actionFade") self.actionStop = QtGui.QAction(parent=MainWindow) icon11 = QtGui.QIcon() - icon11.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon11.addPixmap( + QtGui.QPixmap(":/icons/stop"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.actionStop.setIcon(icon11) self.actionStop.setObjectName("actionStop") self.action_Clear_selection = QtGui.QAction(parent=MainWindow) self.action_Clear_selection.setObjectName("action_Clear_selection") self.action_Resume_previous = QtGui.QAction(parent=MainWindow) icon12 = QtGui.QIcon() - icon12.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon12.addPixmap( + QtGui.QPixmap(":/icons/previous"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) self.action_Resume_previous.setIcon(icon12) self.action_Resume_previous.setObjectName("action_Resume_previous") self.actionE_xit = QtGui.QAction(parent=MainWindow) @@ -496,7 +616,9 @@ class Ui_MainWindow(object): self.actionImport = QtGui.QAction(parent=MainWindow) self.actionImport.setObjectName("actionImport") self.actionDownload_CSV_of_played_tracks = QtGui.QAction(parent=MainWindow) - self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks") + self.actionDownload_CSV_of_played_tracks.setObjectName( + "actionDownload_CSV_of_played_tracks" + ) self.actionSearch = QtGui.QAction(parent=MainWindow) self.actionSearch.setObjectName("actionSearch") self.actionInsertSectionHeader = QtGui.QAction(parent=MainWindow) @@ -524,9 +646,13 @@ class Ui_MainWindow(object): self.actionResume = QtGui.QAction(parent=MainWindow) self.actionResume.setObjectName("actionResume") self.actionSearch_title_in_Wikipedia = QtGui.QAction(parent=MainWindow) - self.actionSearch_title_in_Wikipedia.setObjectName("actionSearch_title_in_Wikipedia") + self.actionSearch_title_in_Wikipedia.setObjectName( + "actionSearch_title_in_Wikipedia" + ) self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow) - self.actionSearch_title_in_Songfacts.setObjectName("actionSearch_title_in_Songfacts") + self.actionSearch_title_in_Songfacts.setObjectName( + "actionSearch_title_in_Songfacts" + ) self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow) self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows") self.actionReplace_files = QtGui.QAction(parent=MainWindow) @@ -581,7 +707,7 @@ class Ui_MainWindow(object): self.retranslateUi(MainWindow) self.tabPlaylist.setCurrentIndex(-1) self.tabInfolist.setCurrentIndex(-1) - self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore + self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): @@ -623,38 +749,58 @@ class Ui_MainWindow(object): self.actionFade.setShortcut(_translate("MainWindow", "Ctrl+Z")) self.actionStop.setText(_translate("MainWindow", "S&top")) self.actionStop.setShortcut(_translate("MainWindow", "Ctrl+Alt+S")) - self.action_Clear_selection.setText(_translate("MainWindow", "Clear &selection")) + self.action_Clear_selection.setText( + _translate("MainWindow", "Clear &selection") + ) self.action_Clear_selection.setShortcut(_translate("MainWindow", "Esc")) - self.action_Resume_previous.setText(_translate("MainWindow", "&Resume previous")) + self.action_Resume_previous.setText( + _translate("MainWindow", "&Resume previous") + ) self.actionE_xit.setText(_translate("MainWindow", "E&xit")) self.actionTest.setText(_translate("MainWindow", "&Test")) self.actionOpenPlaylist.setText(_translate("MainWindow", "O&pen...")) self.actionNewPlaylist.setText(_translate("MainWindow", "&New...")) self.actionTestFunction.setText(_translate("MainWindow", "&Test function")) - self.actionSkipToFade.setText(_translate("MainWindow", "&Skip to start of fade")) + self.actionSkipToFade.setText( + _translate("MainWindow", "&Skip to start of fade") + ) self.actionSkipToEnd.setText(_translate("MainWindow", "Skip to &end of track")) self.actionClosePlaylist.setText(_translate("MainWindow", "&Close")) self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename...")) self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te...")) - self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to...")) + self.actionMoveSelected.setText( + _translate("MainWindow", "Mo&ve selected tracks to...") + ) self.actionExport_playlist.setText(_translate("MainWindow", "E&xport...")) self.actionSetNext.setText(_translate("MainWindow", "Set &next")) self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N")) - self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track")) + self.actionSelect_next_track.setText( + _translate("MainWindow", "Select next track") + ) self.actionSelect_next_track.setShortcut(_translate("MainWindow", "J")) - self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track")) + self.actionSelect_previous_track.setText( + _translate("MainWindow", "Select previous track") + ) self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K")) - self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks")) - self.actionMoveUnplayed.setText(_translate("MainWindow", "Move &unplayed tracks to...")) + self.actionSelect_played_tracks.setText( + _translate("MainWindow", "Select played tracks") + ) + self.actionMoveUnplayed.setText( + _translate("MainWindow", "Move &unplayed tracks to...") + ) self.actionAdd_note.setText(_translate("MainWindow", "Add note...")) self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T")) self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls")) self.actionImport.setText(_translate("MainWindow", "Import track...")) self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I")) - self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks...")) + self.actionDownload_CSV_of_played_tracks.setText( + _translate("MainWindow", "Download CSV of played tracks...") + ) self.actionSearch.setText(_translate("MainWindow", "Search...")) self.actionSearch.setShortcut(_translate("MainWindow", "/")) - self.actionInsertSectionHeader.setText(_translate("MainWindow", "Insert §ion header...")) + self.actionInsertSectionHeader.setText( + _translate("MainWindow", "Insert §ion header...") + ) self.actionInsertSectionHeader.setShortcut(_translate("MainWindow", "Ctrl+H")) self.actionRemove.setText(_translate("MainWindow", "&Remove track")) self.actionFind_next.setText(_translate("MainWindow", "Find next")) @@ -662,8 +808,12 @@ class Ui_MainWindow(object): self.actionFind_previous.setText(_translate("MainWindow", "Find previous")) self.actionFind_previous.setShortcut(_translate("MainWindow", "P")) self.action_About.setText(_translate("MainWindow", "&About")) - self.actionSave_as_template.setText(_translate("MainWindow", "Save as template...")) - self.actionNew_from_template.setText(_translate("MainWindow", "New from template...")) + self.actionSave_as_template.setText( + _translate("MainWindow", "Save as template...") + ) + self.actionNew_from_template.setText( + _translate("MainWindow", "New from template...") + ) self.actionDebug.setText(_translate("MainWindow", "Debug")) self.actionAdd_cart.setText(_translate("MainWindow", "Edit cart &1...")) self.actionMark_for_moving.setText(_translate("MainWindow", "Mark for moving")) @@ -672,11 +822,23 @@ class Ui_MainWindow(object): self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V")) self.actionResume.setText(_translate("MainWindow", "Resume")) self.actionResume.setShortcut(_translate("MainWindow", "Ctrl+R")) - self.actionSearch_title_in_Wikipedia.setText(_translate("MainWindow", "Search title in Wikipedia")) - self.actionSearch_title_in_Wikipedia.setShortcut(_translate("MainWindow", "Ctrl+W")) - self.actionSearch_title_in_Songfacts.setText(_translate("MainWindow", "Search title in Songfacts")) - self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S")) - self.actionSelect_duplicate_rows.setText(_translate("MainWindow", "Select duplicate rows...")) + self.actionSearch_title_in_Wikipedia.setText( + _translate("MainWindow", "Search title in Wikipedia") + ) + self.actionSearch_title_in_Wikipedia.setShortcut( + _translate("MainWindow", "Ctrl+W") + ) + self.actionSearch_title_in_Songfacts.setText( + _translate("MainWindow", "Search title in Songfacts") + ) + self.actionSearch_title_in_Songfacts.setShortcut( + _translate("MainWindow", "Ctrl+S") + ) + self.actionSelect_duplicate_rows.setText( + _translate("MainWindow", "Select duplicate rows...") + ) self.actionReplace_files.setText(_translate("MainWindow", "Import files...")) + + from infotabs import InfoTabs # type: ignore from pyqtgraph import PlotWidget # type: ignore diff --git a/app/ui/yellow-circle.png b/app/ui/yellow-circle.png new file mode 100644 index 0000000..94a4f60 Binary files /dev/null and b/app/ui/yellow-circle.png differ