Revise menu, selected tracks duration summing OK

This commit is contained in:
Keith Edmunds 2022-08-07 16:15:11 +01:00
parent 91841cfc18
commit 89781c0a94
5 changed files with 364 additions and 309 deletions

View File

@ -36,7 +36,7 @@ class Config(object):
DEFAULT_COLUMN_WIDTH = 200
DEFAULT_IMPORT_DIRECTORY = "/home/kae/Nextcloud/tmp"
DEFAULT_OUTPUT_DIRECTORY = "/home/kae/music/Singles"
DISPLAY_SQL = True
DISPLAY_SQL = False
ERRORS_TO = ['kae@midnighthax.com']
FADE_STEPS = 20
FADE_TIME = 3000

View File

@ -20,7 +20,7 @@ from PyQt5.QtWidgets import (
# QDialog,
# QFileDialog,
# QInputDialog,
# QLabel,
QLabel,
# QLineEdit,
# QListWidgetItem,
QMainWindow,
@ -84,8 +84,8 @@ class Window(QMainWindow, Ui_MainWindow):
# self.previous_track_position: Optional[int] = None
#
# self.set_main_window_size()
# self.lblSumPlaytime: QLabel = QLabel("")
# self.statusbar.addPermanentWidget(self.lblSumPlaytime)
self.lblSumPlaytime = QLabel("")
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
# self.txtSearch = QLineEdit()
# self.statusbar.addWidget(self.txtSearch)
# self.txtSearch.setHidden(True)

View File

@ -1,7 +1,7 @@
from collections import namedtuple
from typing import List, Optional
# from PyQt5 import QtCore
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
QColor,
@ -75,7 +75,6 @@ columns["row_notes"] = Column(idx=8, heading=Config.COLUMN_NAME_NOTES)
# """https://stackoverflow.com/questions/72790705/dont-select-text-in-qtablewidget-cell-when-editing/72792962#72792962"""
#
# def createEditor(self, parent, option, index):
# import ipdb; ipdb.set_trace()
# editor = super().createEditor(parent, option, index)
# if isinstance(editor, QLineEdit):
# def deselect():
@ -92,9 +91,10 @@ class PlaylistTab(QTableWidget):
# cellEditingEnded = QtCore.pyqtSignal()
# Qt.UserRoles
ROW_METADATA = Qt.UserRole
ROW_FLAGS = Qt.UserRole
ROW_TRACK_ID = Qt.UserRole + 1
PLAYLISTROW_ID = Qt.UserRole + 2
ROW_DURATION = Qt.UserRole + 2
PLAYLISTROW_ID = Qt.UserRole + 3
def __init__(self, musicmuster: QMainWindow, session: Session,
playlist_id: int, *args, **kwargs) -> None:
@ -135,20 +135,20 @@ class PlaylistTab(QTableWidget):
self.setDropIndicatorShown(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragEnabled(False)
#
# # This property defines how the widget shows a context menu
# self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
# # This signal is emitted when the widget's contextMenuPolicy is
# # Qt::CustomContextMenu, and the user has requested a context
# # menu on the widget.
# self.customContextMenuRequested.connect(self._context_menu)
# self.viewport().installEventFilter(self)
#
# self.itemSelectionChanged.connect(self._select_event)
#
# This property defines how the widget shows a context menu
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
# This signal is emitted when the widget's contextMenuPolicy is
# Qt::CustomContextMenu, and the user has requested a context
# menu on the widget.
self.customContextMenuRequested.connect(self._context_menu)
self.viewport().installEventFilter(self)
self.itemSelectionChanged.connect(self._select_event)
self.row_filter: Optional[str] = None
# self.editing_cell: bool = False
# self.selecting_in_progress = False
self.selecting_in_progress = False
# Connect signals
# self.cellChanged.connect(self._cell_changed)
# self.cellClicked.connect(self._edit_note_cell)
@ -156,7 +156,7 @@ class PlaylistTab(QTableWidget):
# self.cellEditingStarted.connect(self._cell_edit_started)
# self.doubleClicked.connect(self._edit_cell)
self.horizontalHeader().sectionResized.connect(self._column_resize)
#
# Now load our tracks and notes
self.populate(session, self.playlist_id)
@ -236,58 +236,90 @@ class PlaylistTab(QTableWidget):
with Session() as session: # checked
self.save_playlist(session)
self.update_display(session)
#
# def edit(self, index, trigger, event): # review
# def edit(self, index, trigger, event):
# result = super(PlaylistTab, self).edit(index, trigger, event)
# if result:
# self.cellEditingStarted.emit(index.row(), index.column())
# return result
#
# def eventFilter(self, source, event): # review
# """Used to process context (right-click) menu, which is defined here"""
#
# if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504
# event.buttons() == QtCore.Qt.RightButton and # noqa W504
# source is self.viewport()):
# item = self.itemAt(event.pos())
# if item is not None:
# row = item.row()
# log.debug(f"playlist.eventFilter(): Right-click on row {row}")
# current = row == self._get_current_track_row()
# next_row = row == self._get_next_track_row()
# self.menu = QMenu(self)
# act_info = self.menu.addAction('Info')
# act_info.triggered.connect(lambda: self._info_row(row))
# self.menu.addSeparator()
# if row not in self._get_notes_rows():
# act_mplayer = self.menu.addAction(
# "Play track with mplayer")
# act_mplayer.triggered.connect(
# lambda: self._mplayer(row))
# self.menu.addSeparator()
# if not current and not next_row:
# act_setnext = self.menu.addAction("Set next")
# with Session() as session:
# act_setnext.triggered.connect(
# lambda: self._set_next(row, session))
# act_copypath = self.menu.addAction("Copy track path")
# act_copypath.triggered.connect(
# lambda: self._copy_path(row))
# if not current:
# act_rescan = self.menu.addAction("Rescan track")
# act_rescan.triggered.connect(lambda: self._rescan(row))
# act_audacity = self.menu.addAction(
# "Open track in Audacity")
# act_audacity.triggered.connect(
# lambda: self._audacity(row))
# if not current and not next_row:
# act_move = self.menu.addAction('Move to playlist...')
# act_move.triggered.connect(self.musicmuster.move_selected)
# self.menu.addSeparator()
# act_delete = self.menu.addAction('Delete')
# act_delete.triggered.connect(self._delete_rows)
#
# return super(PlaylistTab, self).eventFilter(source, event)
def eventFilter(self, source, event):
"""Used to process context (right-click) menu, which is defined here"""
if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504
event.buttons() == QtCore.Qt.RightButton and # noqa W504
source is self.viewport()):
item = self.itemAt(event.pos())
if item is not None:
row_number = item.row()
track_id = self._get_row_track_id(row_number)
if track_id:
current = row_number == self._get_current_track_row()
next_row = row_number == self._get_next_track_row()
else:
current = next_row = False
self.menu = QMenu(self)
if track_id:
# Info
act_info = self.menu.addAction('Info')
act_info.triggered.connect(
lambda: self._info_row(track_id)
)
self.menu.addSeparator()
# Play with mplayer
act_mplayer = self.menu.addAction(
"Play with mplayer")
act_mplayer.triggered.connect(
lambda: self._mplayer_play(track_id))
# Set next
if not current and not next_row:
act_setnext = self.menu.addAction("Set next")
with Session() as session:
act_setnext.triggered.connect(
lambda: self._set_next(session, row_number))
# Open in Audacity
if not current:
act_audacity = self.menu.addAction(
"Open in Audacity")
act_audacity.triggered.connect(
lambda: self._open_in_audacity(track_id))
# Rescan
act_rescan = self.menu.addAction("Rescan")
act_rescan.triggered.connect(
lambda: self._rescan(track_id)
)
self.menu.addSeparator()
# Remove track
act_remove_track = self.menu.addAction('Remove track')
act_remove_track.triggered.connect(
lambda: self._remove_track(row_number)
)
else:
# Add track to section header (ie, make this a track
# row)
act_add_track = self.menu.addAction('Add track')
act_add_track.triggered.connect(self._add_track)
if not current and not next_row:
act_move = self.menu.addAction('Move to playlist...')
act_move.triggered.connect(self.musicmuster.move_selected)
self.menu.addSeparator()
# Remove row
act_delete = self.menu.addAction('Remove row')
act_delete.triggered.connect(self._delete_rows)
return super(PlaylistTab, self).eventFilter(source, event)
def mouseReleaseEvent(self, event):
"""
@ -301,7 +333,7 @@ class PlaylistTab(QTableWidget):
super().mouseReleaseEvent(event)
# # ########## Externally called functions ##########
#
def clear_selection(self) -> None:
"""Unselect all tracks and reset drag mode"""
@ -399,8 +431,9 @@ class PlaylistTab(QTableWidget):
# Add row metadata to userdata column
userdata_item = QTableWidgetItem()
userdata_item.setData(self.ROW_METADATA, 0)
userdata_item.setData(self.ROW_FLAGS, 0)
userdata_item.setData(self.PLAYLISTROW_ID, row_data.id)
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
self.setItem(row, columns['userdata'].idx, userdata_item)
if row_data.track_id:
@ -420,6 +453,7 @@ class PlaylistTab(QTableWidget):
duration_item = QTableWidgetItem(
helpers.ms_to_mmss(row_data.track.duration))
self.setItem(row, columns['duration'].idx, duration_item)
self._set_row_duration(row, row_data.track.duration)
start_item = QTableWidgetItem()
self.setItem(row, columns['start_time'].idx, start_item)
@ -441,9 +475,6 @@ class PlaylistTab(QTableWidget):
if not helpers.file_is_readable(row_data.track.path):
self._set_unreadable_row(row)
# Save track_id
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
else:
# This is a section header so make empty items (row
# background won't be coloured without items present). Any
@ -484,7 +515,7 @@ class PlaylistTab(QTableWidget):
# # Put an item in COL_USERDATA for later
# item: QTableWidgetItem = QTableWidgetItem()
# # Add row metadata
# item.setData(self.ROW_METADATA, 0)
# item.setData(self.ROW_FLAGS, 0)
# self.setItem(row, self.COL_USERDATA, item)
#
# # Add track details to columns
@ -618,7 +649,7 @@ class PlaylistTab(QTableWidget):
# search_from = current_row + 1
# next_row = self._find_next_track_row(search_from)
# if next_row:
# self._set_next(next_row, session)
# self._set_next(session, next_row)
#
# # Update display
# self.update_display(session)
@ -863,7 +894,7 @@ class PlaylistTab(QTableWidget):
# return None
#
# with Session() as session:
# self._set_next(row, session)
# self._set_next(session, row)
def update_display(self, session, clear_selection: bool = True) -> None:
"""
@ -1059,18 +1090,25 @@ class PlaylistTab(QTableWidget):
session, section_start_row, section_time, no_end=True)
#
# # ########## Internally called functions ##########
#
# def _audacity(self, row: int) -> None:
# """Open track in Audacity. Audacity must be already running"""
#
# log.debug(f"_audacity({row})")
#
# if row in self._get_notes_rows():
# return None
#
# with Session() as session:
# track: Tracks = self._get_row_track_object(row, session)
# open_in_audacity(track.path)
def _add_track(self, row: int) -> None:
"""Add a track to a section header making it a normal track row"""
print("playlists._add_track() not yet implemented")
def _open_in_audacity(self, track_id: int) -> None:
"""Open track in Audacity. Audacity must be already running"""
with Session() as session:
track = session.get(Tracks, track_id)
if not track:
log.error(
f"playlists._open_in_audacity({track_id=}): "
"Track not found"
)
return
open_in_audacity(track.path)
def _calculate_end_time(self, start: Optional[datetime],
duration: int) -> Optional[datetime]:
@ -1080,11 +1118,11 @@ class PlaylistTab(QTableWidget):
return None
return start + timedelta(milliseconds=duration)
#
# def _context_menu(self, pos): # review
#
# assert self.menu
# self.menu.exec_(self.mapToGlobal(pos))
def _context_menu(self, pos):
assert self.menu
self.menu.exec_(self.mapToGlobal(pos))
#
# def _copy_path(self, row: int) -> None:
# """
@ -1364,14 +1402,16 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as played, or None"""
return self._meta_search(RowMeta.PLAYED, one=False)
#
# def _get_row_duration(self, row: int) -> int:
# """Return duration associated with this row"""
#
# try:
# return self.item(row, self.COL_USERDATA).data(self.ROW_DURATION)
# except:
# return 0
def _get_row_duration(self, row: int) -> int:
"""Return duration associated with this row"""
duration = (self.item(row, columns['userdata'].idx)
.data(self.ROW_DURATION))
if duration:
return duration
else:
return 0
#
# def _get_row_end_time(self, row) -> Optional[datetime]:
# """
@ -1434,34 +1474,32 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as unreadable, or None"""
return self._meta_search(RowMeta.UNREADABLE, one=False)
#
# def _info_row(self, row: int) -> None:
# """Display popup with info re row"""
#
# txt: str
#
# with Session() as session:
# if row in self._get_notes_rows():
# note: Notes = self._get_row_notes_object(row, session)
# txt = note.note
# else:
# track: Tracks = self._get_row_track_object(row, session)
# txt = (
# f"Title: {track.title}\n"
# f"Artist: {track.artist}\n"
# f"Track ID: {track.id}\n"
# f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
# f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
# f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
# "\n\n"
# f"Path: {track.path}\n"
# )
# info: QMessageBox = QMessageBox(self)
# info.setIcon(QMessageBox.Information)
# info.setText(txt)
# info.setStandardButtons(QMessageBox.Ok)
# info.setDefaultButton(QMessageBox.Cancel)
# info.exec()
def _info_row(self, track_id: int) -> None:
"""Display popup with info re row"""
with Session() as session:
track = session.get(Tracks, track_id)
if track:
txt = (
f"Title: {track.title}\n"
f"Artist: {track.artist}\n"
f"Track ID: {track.id}\n"
f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
"\n\n"
f"Path: {track.path}\n"
)
else:
txt = f"Can't find {track_id=}"
info: QMessageBox = QMessageBox(self)
info.setIcon(QMessageBox.Information)
info.setText(txt)
info.setStandardButtons(QMessageBox.Ok)
info.setDefaultButton(QMessageBox.Cancel)
info.exec()
#
# def _insert_note(self, session: Session, note: Notes,
# row: Optional[int] = None, repaint: bool = True) -> None:
@ -1539,7 +1577,7 @@ class PlaylistTab(QTableWidget):
#
# new_metadata: int = self._meta_get(row) & ~(1 << attribute)
# self.item(row, self.COL_USERDATA).setData(
# self.ROW_METADATA, new_metadata)
# self.ROW_FLAGS, new_metadata)
#
# def _meta_clear_next(self) -> None:
# """
@ -1554,7 +1592,7 @@ class PlaylistTab(QTableWidget):
"""Return row metadata"""
return (self.item(row, columns['userdata'].idx)
.data(self.ROW_METADATA))
.data(self.ROW_FLAGS))
#
# def _meta_notset(self, metadata: int) -> List[int]:
# """
@ -1612,38 +1650,58 @@ class PlaylistTab(QTableWidget):
# else:
# new_metadata = self._meta_get(row) | (1 << attribute)
# self.item(row, self.COL_USERDATA).setData(
# self.ROW_METADATA, new_metadata)
#
# def _mplayer(self, row: int) -> None:
# """Play track with mplayer"""
#
# log.debug(f"_mplayer({row})")
#
# if row in self._get_notes_rows():
# return None
#
# with Session() as session:
# track: Tracks = self._get_row_track_object(row, session)
# cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path]
# thread = threading.Thread(
# target=self._run_subprocess, args=(cmd_list,))
# thread.start()
#
# def _rescan(self, row: int) -> None:
# """
# If passed row is track row, rescan it.
# Otherwise, return None.
# """
#
# log.debug(f"_rescan({row=})")
#
# with Session() as session:
# if row in self._get_track_rows():
# track: Tracks = self._get_row_track_object(row, session)
# if track:
# track.rescan(session)
# self._update_row(session, row, track)
#
# self.ROW_FLAGS, new_metadata)
def _mplayer_play(self, track_id: int) -> None:
"""Play track with mplayer"""
with Session() as session:
track = session.get(Tracks, track_id)
if not track:
log.error(
f"playlists._mplayer_play({track_id=}): "
"Track not found"
)
return
cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path]
thread = threading.Thread(
target=self._run_subprocess, args=(cmd_list,))
thread.start()
def _remove_track(self, row: int) -> None:
"""Remove track from row, making it a section header"""
# Update playlist_rows record
plr = session.get(PlaylistRows, self._get_playlistrow_id(row))
plr.track_id = None
plr.save()
# Clear track text items
for i in range(2, len(columns) - 1):
self.item(row, i).setText("")
# Set note text in correct column for section head
self.item(row, 1).setText(plr.note)
# Remove row duration
self._set_row_duration(row, 0)
# And refresh display
self.update_display()
def _rescan(self, track_id: int) -> None:
"""Rescan track"""
with Session() as session:
track = session.get(Tracks, track_id)
if not track:
log.error(
f"playlists._open_in_audacity({track_id=}): "
"Track not found"
)
return
track.rescan(session)
self._update_row(session, row, track)
# def _run_subprocess(self, args):
# """Run args in subprocess"""
#
@ -1654,12 +1712,12 @@ class PlaylistTab(QTableWidget):
#
# self._clear_current_track_row()
# self._meta_set_attribute(row, RowMeta.CURRENT)
#
# def _set_next_track_row(self, row: int) -> None:
# """Mark this row as next track"""
#
# self._meta_clear_next()
# self._meta_set_attribute(row, RowMeta.NEXT)
def _set_next_track_row(self, row: int) -> None:
"""Mark this row as next track"""
self._meta_clear_next()
self._meta_set_attribute(row, RowMeta.NEXT)
#
# def _set_note_row(self, row: int) -> None:
# """Mark this row as a note"""
@ -1676,36 +1734,34 @@ class PlaylistTab(QTableWidget):
self._meta_set_attribute(row, RowMeta.UNREADABLE)
# def _select_event(self) -> None:
# """
# Called when item selection changes.
# If multiple rows are selected, display sum of durations in status bar.
# """
#
# # If we are in the process of selecting multiple tracks, no-op here
# if self.selecting_in_progress:
# return
#
# # Get the row number of all selected items and put into a set
# # to deduplicate
# sel_rows: Set[int] = set([item.row() for item in self.selectedItems()])
# # If no rows are selected, we have nothing to do
# if len(sel_rows) == 0:
# self.musicmuster.lblSumPlaytime.setText("")
# return
#
# notes_rows: Set[int] = set(self._get_notes_rows())
# ms: int = 0
# with Session() as session:
# for row in (sel_rows - notes_rows):
# ms += self._get_row_track_object(row, session).duration or 0
#
# # Only paint message if there are selected track rows
# if ms > 0:
# self.musicmuster.lblSumPlaytime.setText(
# f"Selected duration: {helpers.ms_to_mmss(ms)}")
# else:
# self.musicmuster.lblSumPlaytime.setText("")
def _select_event(self) -> None:
"""
Called when item selection changes.
If multiple rows are selected, display sum of durations in status bar.
"""
# If we are in the process of selecting multiple tracks, no-op here
if self.selecting_in_progress:
return
# Get the row number of all selected items and put into a set
# to deduplicate
selected_rows = set([item.row() for item in self.selectedItems()])
# If no rows are selected, we have nothing to do
if len(selected_rows) == 0:
self.musicmuster.lblSumPlaytime.setText("")
return
ms = 0
for row in selected_rows:
ms += self._get_row_duration(row)
# Only paint message if there are selected track rows
if ms > 0:
self.musicmuster.lblSumPlaytime.setText(
f"Selected duration: {helpers.ms_to_mmss(ms)}")
else:
self.musicmuster.lblSumPlaytime.setText("")
#
# def _select_tracks(self, played: bool) -> None:
# """
@ -1739,41 +1795,42 @@ class PlaylistTab(QTableWidget):
self.setColumnWidth(idx, record.f_int)
else:
self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH)
#
# def _set_next(self, row: int, session: Session) -> None:
# """
# Set passed row as next track to play.
#
# Actions required:
# - Check row is a track row
# - Check track is readable
# - Mark as next track
# - Update display
# - Notify musicmuster
# """
#
# log.debug(f"_set_next({row=})")
#
# # Check row is a track row
# if row in self._get_notes_rows():
# return None
# track: Tracks = self._get_row_track_object(row, session)
# if not track:
# return None
#
# # Check track is readable
# if not self._file_is_readable(track.path):
# self._set_unreadable_row(row)
# return None
#
# # Mark as next track
# self._set_next_track_row(row)
#
# # Update display
# self.update_display(session)
#
# # Notify musicmuster
# self.musicmuster.this_is_the_next_track(self, track, session)
def _set_next(self, session: Session, row: int) -> None:
"""
Set passed row as next track to play.
Actions required:
- Check row has a track
- Check track is readable
- Mark as next track
- Update display
- Notify musicmuster
"""
track_id = self._get_row_track_id(row_number)
if not track_id:
log.error(f"playlists._set_next({row=}) has no track associated")
return
track = session.get(Tracks, track_id)
if not track:
log.error(f"playlists._set_next({row=}): Track not found")
return
# Check track is readable
if not self._file_is_readable(track.path):
self._set_unreadable_row(row)
return None
# Mark as next track
self._set_next_track_row(row)
# Update display
self.update_display(session)
# Notify musicmuster
self.musicmuster.this_is_the_next_track(self, track, session)
def _set_row_bold(self, row: int, bold: bool = True) -> None:
"""Make row bold (bold=True) or not bold"""
@ -1802,13 +1859,11 @@ class PlaylistTab(QTableWidget):
#
# self.item(row, self.COL_USERDATA).setData(
# self.CONTENT_OBJECT, object_id)
#
# def _set_row_duration(self, row: int, ms: int) -> None:
# """Set duration of this row in row metadata"""
#
# assert self.item(row, columns['userdata'].idx)
#
# self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
def _set_row_duration(self, row: int, ms: int) -> None:
"""Set duration of this row in row metadata"""
self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
"""Set passed row end time to passed time"""

View File

@ -734,57 +734,48 @@ border: 1px solid rgb(85, 87, 83);</string>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>Fi&amp;le</string>
<string>P&amp;laylists</string>
</property>
<addaction name="actionNewPlaylist"/>
<addaction name="actionOpenPlaylist"/>
<addaction name="actionClosePlaylist"/>
<addaction name="actionRenamePlaylist"/>
<addaction name="actionExport_playlist"/>
<addaction name="actionDeletePlaylist"/>
<addaction name="separator"/>
<addaction name="actionMoveSelected"/>
<addaction name="actionMove_unplayed"/>
<addaction name="actionDownload_CSV_of_played_tracks"/>
<addaction name="actionExport_playlist"/>
<addaction name="separator"/>
<addaction name="actionE_xit"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuPlaylist">
<property name="title">
<string>&amp;Tracks</string>
<string>Sho&amp;wtime</string>
</property>
<addaction name="separator"/>
<addaction name="actionSearch_database"/>
<addaction name="actionAdd_note"/>
<addaction name="actionImport"/>
<addaction name="action_Clear_selection"/>
<addaction name="separator"/>
<addaction name="actionSetNext"/>
<addaction name="separator"/>
<addaction name="actionSelect_unplayed_tracks"/>
<addaction name="actionSelect_played_tracks"/>
<addaction name="actionMoveSelected"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionSelect_next_track"/>
<addaction name="actionSelect_previous_track"/>
<addaction name="separator"/>
<addaction name="actionSearch"/>
</widget>
<widget class="QMenu" name="menu_Music">
<property name="title">
<string>&amp;Music</string>
</property>
<addaction name="actionPlay_next"/>
<addaction name="actionSkip_next"/>
<addaction name="actionFade"/>
<addaction name="actionStop"/>
<addaction name="action_Resume_previous"/>
<addaction name="separator"/>
<addaction name="actionSkip_next"/>
<addaction name="separator"/>
<addaction name="actionInsert"/>
<addaction name="actionRemove"/>
<addaction name="actionImport"/>
<addaction name="actionSetNext"/>
<addaction name="action_Clear_selection"/>
<addaction name="actionInsert_section_header"/>
<addaction name="separator"/>
<addaction name="actionSearch"/>
<addaction name="actionSelect_next_track"/>
<addaction name="actionSelect_previous_track"/>
<addaction name="separator"/>
<addaction name="actionEnable_controls"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuPlaylist"/>
<addaction name="menu_Music"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="enabled">
@ -818,13 +809,13 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Ctrl+Alt+Return</string>
</property>
</action>
<action name="actionSearch_database">
<action name="actionInsert">
<property name="icon">
<iconset>
<normaloff>../../../../.designer/backup/icon_search_database.png</normaloff>../../../../.designer/backup/icon_search_database.png</iconset>
</property>
<property name="text">
<string>Search &amp;database</string>
<string>Insert &amp;track...</string>
</property>
<property name="shortcut">
<string>Ctrl+D</string>
@ -949,7 +940,7 @@ border: 1px solid rgb(85, 87, 83);</string>
</action>
<action name="actionExport_playlist">
<property name="text">
<string>E&amp;xport playlist...</string>
<string>E&amp;xport...</string>
</property>
</action>
<action name="actionSetNext">
@ -981,9 +972,9 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Select played tracks</string>
</property>
</action>
<action name="actionSelect_unplayed_tracks">
<action name="actionMove_unplayed">
<property name="text">
<string>Select unplayed tracks</string>
<string>Move &amp;unplayed tracks to...</string>
</property>
</action>
<action name="actionAdd_note">
@ -1001,7 +992,7 @@ border: 1px solid rgb(85, 87, 83);</string>
</action>
<action name="actionImport">
<property name="text">
<string>Import...</string>
<string>Import track...</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+I</string>
@ -1020,6 +1011,16 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>/</string>
</property>
</action>
<action name="actionInsert_section_header">
<property name="text">
<string>Insert &amp;section header...</string>
</property>
</action>
<action name="actionRemove">
<property name="text">
<string>&amp;Remove track</string>
</property>
</action>
</widget>
<resources>
<include location="icons.qrc"/>

View File

@ -342,8 +342,6 @@ class Ui_MainWindow(object):
self.menuFile.setObjectName("menuFile")
self.menuPlaylist = QtWidgets.QMenu(self.menubar)
self.menuPlaylist.setObjectName("menuPlaylist")
self.menu_Music = QtWidgets.QMenu(self.menubar)
self.menu_Music.setObjectName("menu_Music")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setEnabled(True)
@ -360,11 +358,11 @@ class Ui_MainWindow(object):
icon4.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actionSkip_next.setIcon(icon4)
self.actionSkip_next.setObjectName("actionSkip_next")
self.actionSearch_database = QtWidgets.QAction(MainWindow)
self.actionInsert = QtWidgets.QAction(MainWindow)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actionSearch_database.setIcon(icon5)
self.actionSearch_database.setObjectName("actionSearch_database")
self.actionInsert.setIcon(icon5)
self.actionInsert.setObjectName("actionInsert")
self.actionAdd_file = QtWidgets.QAction(MainWindow)
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
@ -422,8 +420,8 @@ class Ui_MainWindow(object):
self.actionSelect_previous_track.setObjectName("actionSelect_previous_track")
self.actionSelect_played_tracks = QtWidgets.QAction(MainWindow)
self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks")
self.actionSelect_unplayed_tracks = QtWidgets.QAction(MainWindow)
self.actionSelect_unplayed_tracks.setObjectName("actionSelect_unplayed_tracks")
self.actionMove_unplayed = QtWidgets.QAction(MainWindow)
self.actionMove_unplayed.setObjectName("actionMove_unplayed")
self.actionAdd_note = QtWidgets.QAction(MainWindow)
self.actionAdd_note.setObjectName("actionAdd_note")
self.actionEnable_controls = QtWidgets.QAction(MainWindow)
@ -434,44 +432,44 @@ class Ui_MainWindow(object):
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
self.actionSearch = QtWidgets.QAction(MainWindow)
self.actionSearch.setObjectName("actionSearch")
self.actionInsert_section_header = QtWidgets.QAction(MainWindow)
self.actionInsert_section_header.setObjectName("actionInsert_section_header")
self.actionRemove = QtWidgets.QAction(MainWindow)
self.actionRemove.setObjectName("actionRemove")
self.menuFile.addAction(self.actionNewPlaylist)
self.menuFile.addAction(self.actionOpenPlaylist)
self.menuFile.addAction(self.actionClosePlaylist)
self.menuFile.addAction(self.actionRenamePlaylist)
self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addAction(self.actionDeletePlaylist)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionMoveSelected)
self.menuFile.addAction(self.actionMove_unplayed)
self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks)
self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionE_xit)
self.menuFile.addSeparator()
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSearch_database)
self.menuPlaylist.addAction(self.actionAdd_note)
self.menuPlaylist.addAction(self.actionPlay_next)
self.menuPlaylist.addAction(self.actionFade)
self.menuPlaylist.addAction(self.actionStop)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSkip_next)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionInsert)
self.menuPlaylist.addAction(self.actionRemove)
self.menuPlaylist.addAction(self.actionImport)
self.menuPlaylist.addAction(self.action_Clear_selection)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSetNext)
self.menuPlaylist.addAction(self.action_Clear_selection)
self.menuPlaylist.addAction(self.actionInsert_section_header)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSelect_unplayed_tracks)
self.menuPlaylist.addAction(self.actionSelect_played_tracks)
self.menuPlaylist.addAction(self.actionMoveSelected)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSearch)
self.menuPlaylist.addAction(self.actionSelect_next_track)
self.menuPlaylist.addAction(self.actionSelect_previous_track)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSearch)
self.menu_Music.addAction(self.actionPlay_next)
self.menu_Music.addAction(self.actionSkip_next)
self.menu_Music.addAction(self.actionFade)
self.menu_Music.addAction(self.actionStop)
self.menu_Music.addAction(self.action_Resume_previous)
self.menu_Music.addSeparator()
self.menu_Music.addAction(self.actionEnable_controls)
self.menuPlaylist.addAction(self.actionEnable_controls)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuPlaylist.menuAction())
self.menubar.addAction(self.menu_Music.menuAction())
self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1)
@ -505,15 +503,14 @@ class Ui_MainWindow(object):
self.label_end_timer.setText(_translate("MainWindow", "00:00"))
self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk"))
self.btnHidePlayed.setText(_translate("MainWindow", "Hide played"))
self.menuFile.setTitle(_translate("MainWindow", "Fi&le"))
self.menuPlaylist.setTitle(_translate("MainWindow", "&Tracks"))
self.menu_Music.setTitle(_translate("MainWindow", "&Music"))
self.menuFile.setTitle(_translate("MainWindow", "P&laylists"))
self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
self.actionPlay_next.setText(_translate("MainWindow", "&Play next"))
self.actionPlay_next.setShortcut(_translate("MainWindow", "Return"))
self.actionSkip_next.setText(_translate("MainWindow", "Skip to &next"))
self.actionSkip_next.setShortcut(_translate("MainWindow", "Ctrl+Alt+Return"))
self.actionSearch_database.setText(_translate("MainWindow", "Search &database"))
self.actionSearch_database.setShortcut(_translate("MainWindow", "Ctrl+D"))
self.actionInsert.setText(_translate("MainWindow", "Insert &track..."))
self.actionInsert.setShortcut(_translate("MainWindow", "Ctrl+D"))
self.actionAdd_file.setText(_translate("MainWindow", "Add &file"))
self.actionAdd_file.setShortcut(_translate("MainWindow", "Ctrl+F"))
self.actionFade.setText(_translate("MainWindow", "F&ade"))
@ -534,7 +531,7 @@ class Ui_MainWindow(object):
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to..."))
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport playlist..."))
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"))
@ -542,13 +539,15 @@ class Ui_MainWindow(object):
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.actionSelect_unplayed_tracks.setText(_translate("MainWindow", "Select unplayed tracks"))
self.actionMove_unplayed.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..."))
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.actionSearch.setText(_translate("MainWindow", "Search..."))
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
self.actionInsert_section_header.setText(_translate("MainWindow", "Insert &section header..."))
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
import icons_rc