jump to current song after sorting, model2, proxymodel
This commit is contained in:
parent
171583b2f7
commit
31f74f7276
@ -59,9 +59,10 @@ from configparser import ConfigParser
|
|||||||
|
|
||||||
class MusicTable(QTableView):
|
class MusicTable(QTableView):
|
||||||
playPauseSignal = pyqtSignal()
|
playPauseSignal = pyqtSignal()
|
||||||
|
playSignal = pyqtSignal(str)
|
||||||
enterKey = pyqtSignal()
|
enterKey = pyqtSignal()
|
||||||
deleteKey = pyqtSignal()
|
deleteKey = pyqtSignal()
|
||||||
refreshMusicTable = pyqtSignal()
|
refreshMusicTableSignal = pyqtSignal()
|
||||||
handleProgressSignal = pyqtSignal(str)
|
handleProgressSignal = pyqtSignal(str)
|
||||||
getThreadPoolSignal = pyqtSignal()
|
getThreadPoolSignal = pyqtSignal()
|
||||||
|
|
||||||
@ -81,9 +82,9 @@ class MusicTable(QTableView):
|
|||||||
# need a standard item model to do actions on cells
|
# need a standard item model to do actions on cells
|
||||||
self.model2: QStandardItemModel = QStandardItemModel()
|
self.model2: QStandardItemModel = QStandardItemModel()
|
||||||
# proxy model for sorting i guess?
|
# proxy model for sorting i guess?
|
||||||
proxymodel = QSortFilterProxyModel()
|
self.proxymodel = QSortFilterProxyModel()
|
||||||
proxymodel.setSourceModel(self.model2)
|
self.proxymodel.setSourceModel(self.model2)
|
||||||
self.setModel(proxymodel)
|
self.setModel(self.proxymodel)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
@ -124,7 +125,7 @@ class MusicTable(QTableView):
|
|||||||
self.selected_song_filepath = ""
|
self.selected_song_filepath = ""
|
||||||
self.current_song_filepath = ""
|
self.current_song_filepath = ""
|
||||||
self.current_song_db_id = None
|
self.current_song_db_id = None
|
||||||
self.current_song_qmodelindex: QModelIndex
|
self.current_song_qmodel_index: QModelIndex
|
||||||
|
|
||||||
# Properties
|
# Properties
|
||||||
self.setAcceptDrops(True)
|
self.setAcceptDrops(True)
|
||||||
@ -140,6 +141,7 @@ class MusicTable(QTableView):
|
|||||||
assert self.horizontal_header is not None # i hate look at linting errors
|
assert self.horizontal_header is not None # i hate look at linting errors
|
||||||
self.horizontal_header.setStretchLastSection(True)
|
self.horizontal_header.setStretchLastSection(True)
|
||||||
self.horizontal_header.setSectionResizeMode(QHeaderView.Interactive)
|
self.horizontal_header.setSectionResizeMode(QHeaderView.Interactive)
|
||||||
|
self.horizontal_header.sortIndicatorChanged.connect(self.on_sort)
|
||||||
# dumb vertical estupido
|
# dumb vertical estupido
|
||||||
self.vertical_header: QHeaderView = self.verticalHeader()
|
self.vertical_header: QHeaderView = self.verticalHeader()
|
||||||
assert self.vertical_header is not None
|
assert self.vertical_header is not None
|
||||||
@ -148,8 +150,8 @@ class MusicTable(QTableView):
|
|||||||
# CONNECTIONS
|
# CONNECTIONS
|
||||||
self.clicked.connect(self.on_cell_clicked)
|
self.clicked.connect(self.on_cell_clicked)
|
||||||
self.deleteKey.connect(self.delete_songs)
|
self.deleteKey.connect(self.delete_songs)
|
||||||
# self.doubleClicked.connect(self.set_current_song_filepath)
|
self.doubleClicked.connect(self.play_audio_file)
|
||||||
# self.enterKey.connect(self.set_current_song_filepath)
|
self.enterKey.connect(self.play_audio_file)
|
||||||
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
||||||
self.model2.layoutChanged.connect(self.restore_scroll_position)
|
self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||||
self.horizontal_header.sectionResized.connect(self.on_header_resized)
|
self.horizontal_header.sectionResized.connect(self.on_header_resized)
|
||||||
@ -265,7 +267,9 @@ class MusicTable(QTableView):
|
|||||||
if directories:
|
if directories:
|
||||||
worker = Worker(self.get_audio_files_recursively, directories)
|
worker = Worker(self.get_audio_files_recursively, directories)
|
||||||
worker.signals.signal_progress.connect(self.handle_progress)
|
worker.signals.signal_progress.connect(self.handle_progress)
|
||||||
worker.signals.signal_result.connect(self.on_get_audio_files_recursively_finished)
|
worker.signals.signal_result.connect(
|
||||||
|
self.on_get_audio_files_recursively_finished
|
||||||
|
)
|
||||||
worker.signals.signal_finished.connect(self.load_music_table)
|
worker.signals.signal_finished.connect(self.load_music_table)
|
||||||
if self.qapp:
|
if self.qapp:
|
||||||
threadpool = self.qapp.threadpool
|
threadpool = self.qapp.threadpool
|
||||||
@ -339,14 +343,38 @@ class MusicTable(QTableView):
|
|||||||
# | |
|
# | |
|
||||||
# |____________________|
|
# |____________________|
|
||||||
|
|
||||||
|
def find_qmodel_index_by_value(self, model, column: int, value) -> QModelIndex:
|
||||||
|
for row in range(model.rowCount()):
|
||||||
|
index = model.index(row, column)
|
||||||
|
if index.data() == value:
|
||||||
|
return index
|
||||||
|
return QModelIndex() # Invalid index if not found
|
||||||
|
|
||||||
|
def on_sort(self):
|
||||||
|
debug("on_sort")
|
||||||
|
search_col_num = self.table_headers.index("path")
|
||||||
|
qmodel_index = self.find_qmodel_index_by_value(
|
||||||
|
self.model2, search_col_num, self.current_song_filepath
|
||||||
|
)
|
||||||
|
self.set_qmodel_index(qmodel_index)
|
||||||
|
self.jump_to_current_song()
|
||||||
|
|
||||||
|
# ```python
|
||||||
|
# (method) def match(
|
||||||
|
# start: QModelIndex,
|
||||||
|
# role: int,
|
||||||
|
# value: Any,
|
||||||
|
# hits: int = ...,
|
||||||
|
# flags: MatchFlags | MatchFlag = ...
|
||||||
|
# ) -> List[QModelIndex]
|
||||||
|
# ```
|
||||||
|
|
||||||
def on_cell_clicked(self, index):
|
def on_cell_clicked(self, index):
|
||||||
"""
|
"""
|
||||||
When a cell is clicked, do some stuff :)
|
When a cell is clicked, do some stuff :)
|
||||||
|
- this func also runs when double click happens, fyi
|
||||||
"""
|
"""
|
||||||
current_index = self.currentIndex()
|
debug("on_cell_clicked")
|
||||||
if index == current_index:
|
|
||||||
return
|
|
||||||
self.setCurrentIndex(index)
|
|
||||||
self.set_selected_song_filepath()
|
self.set_selected_song_filepath()
|
||||||
self.viewport().update() # type: ignore
|
self.viewport().update() # type: ignore
|
||||||
|
|
||||||
@ -431,6 +459,17 @@ class MusicTable(QTableView):
|
|||||||
# | |
|
# | |
|
||||||
# |____________________|
|
# |____________________|
|
||||||
|
|
||||||
|
def set_qmodel_index(self, index: QModelIndex):
|
||||||
|
self.current_song_qmodel_index = index
|
||||||
|
|
||||||
|
def play_audio_file(self):
|
||||||
|
"""
|
||||||
|
Sets the current song filepath
|
||||||
|
Emits a signal that the current song should start playback
|
||||||
|
"""
|
||||||
|
self.set_current_song_filepath()
|
||||||
|
self.playSignal.emit(self.current_song_filepath)
|
||||||
|
|
||||||
def add_files_to_library(self, files: list[str]) -> None:
|
def add_files_to_library(self, files: list[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Spawns a worker thread - adds a list of filepaths to the library
|
Spawns a worker thread - adds a list of filepaths to the library
|
||||||
@ -495,14 +534,26 @@ class MusicTable(QTableView):
|
|||||||
|
|
||||||
def jump_to_current_song(self):
|
def jump_to_current_song(self):
|
||||||
"""Moves screen to the currently playing song, and selects the row"""
|
"""Moves screen to the currently playing song, and selects the row"""
|
||||||
|
debug("jump_to_current_song")
|
||||||
|
print(self.current_song_qmodel_index.model() == self.model2)
|
||||||
|
print("is it?")
|
||||||
|
print("current song qmodel index")
|
||||||
|
print(self.current_song_qmodel_index)
|
||||||
|
proxy_index = self.proxymodel.mapFromSource(self.current_song_qmodel_index)
|
||||||
|
print(f"proxy index: {proxy_index}")
|
||||||
|
self.scrollTo(proxy_index)
|
||||||
|
row = proxy_index.row()
|
||||||
|
print(f"proxy index row: {row}")
|
||||||
|
self.selectRow(proxy_index.row())
|
||||||
|
|
||||||
|
# self.scrollTo(self.current_song_qmodel_index)
|
||||||
|
# row = self.current_song_qmodel_index.row()
|
||||||
|
# self.selectRow(row)
|
||||||
|
|
||||||
# FIXME: this doesn't work regardless of sorting
|
# FIXME: this doesn't work regardless of sorting
|
||||||
# 1. play song 2. sort columns differently 3. jump to current song
|
# 1. play song 2. sort columns differently 3. jump to current song
|
||||||
# this will jump to table index of where the song was when it started playing (its index was set)
|
# this will jump to table index of where the song was when it started playing (its index was set)
|
||||||
|
|
||||||
self.scrollTo(self.current_song_qmodelindex)
|
|
||||||
row = self.current_song_qmodelindex.row()
|
|
||||||
self.selectRow(row)
|
|
||||||
|
|
||||||
def open_directory(self):
|
def open_directory(self):
|
||||||
"""Opens the currently selected song in the system file manager"""
|
"""Opens the currently selected song in the system file manager"""
|
||||||
if self.get_selected_song_filepath() is None:
|
if self.get_selected_song_filepath() is None:
|
||||||
@ -580,7 +631,7 @@ class MusicTable(QTableView):
|
|||||||
debug("reorganizing files")
|
debug("reorganizing files")
|
||||||
# FIXME: batch update, instead of doing 1 file at a time
|
# FIXME: batch update, instead of doing 1 file at a time
|
||||||
# DBAccess is being instantiated for every file, boo
|
# DBAccess is being instantiated for every file, boo
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# is that even possible with move file function?
|
# is that even possible with move file function?
|
||||||
|
|
||||||
# Get target directory
|
# Get target directory
|
||||||
@ -810,15 +861,17 @@ class MusicTable(QTableView):
|
|||||||
def set_current_song_filepath(self) -> None:
|
def set_current_song_filepath(self) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the current song filepath to the value in column 'path' with current selected row index
|
Sets the current song filepath to the value in column 'path' with current selected row index
|
||||||
also stores the QModelIndex for some useful other stuff
|
also stores the QModelIndex for some useful navigation stuff
|
||||||
"""
|
"""
|
||||||
# NOTE:
|
# Get
|
||||||
# Setting the current song filepath automatically plays that song
|
source_index = self.proxymodel.mapToSource(self.currentIndex())
|
||||||
# self.tableView listens to this function and plays the audio file located at self.current_song_filepath
|
self.current_song_qmodel_index: QModelIndex = source_index
|
||||||
self.current_song_qmodelindex: QModelIndex = self.currentIndex()
|
|
||||||
self.current_song_filepath: str = self.current_song_qmodelindex.siblingAtColumn(
|
self.current_song_filepath: str = (
|
||||||
self.table_headers.index("path")
|
self.current_song_qmodel_index.siblingAtColumn(
|
||||||
).data()
|
self.table_headers.index("path")
|
||||||
|
).data()
|
||||||
|
)
|
||||||
|
|
||||||
def load_qapp(self, qapp) -> None:
|
def load_qapp(self, qapp) -> None:
|
||||||
"""Necessary for using members and methods of main application window"""
|
"""Necessary for using members and methods of main application window"""
|
||||||
@ -859,6 +912,7 @@ class MusicTable(QTableView):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# QT Roles
|
# QT Roles
|
||||||
|
|
||||||
# In Qt, roles are used to specify different aspects or types of data associated with each item in a model. The roles are defined in the Qt.ItemDataRole enum. The three roles you asked about - DisplayRole, EditRole, and UserRole - are particularly important. Let's break them down:
|
# In Qt, roles are used to specify different aspects or types of data associated with each item in a model. The roles are defined in the Qt.ItemDataRole enum. The three roles you asked about - DisplayRole, EditRole, and UserRole - are particularly important. Let's break them down:
|
||||||
|
|||||||
16
main.py
16
main.py
@ -232,6 +232,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
# tableView
|
# tableView
|
||||||
self.tableView.doubleClicked.connect(self.play_audio_file)
|
self.tableView.doubleClicked.connect(self.play_audio_file)
|
||||||
self.tableView.enterKey.connect(self.play_audio_file)
|
self.tableView.enterKey.connect(self.play_audio_file)
|
||||||
|
self.tableView.playSignal.connect(self.play_audio_file)
|
||||||
self.tableView.playPauseSignal.connect(
|
self.tableView.playPauseSignal.connect(
|
||||||
self.on_play_clicked
|
self.on_play_clicked
|
||||||
) # Spacebar toggle play/pause signal
|
) # Spacebar toggle play/pause signal
|
||||||
@ -390,11 +391,11 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
else:
|
else:
|
||||||
self.status_bar.showMessage(message)
|
self.status_bar.showMessage(message)
|
||||||
|
|
||||||
def play_audio_file(self) -> None:
|
def play_audio_file(self, filepath: str) -> None:
|
||||||
"""
|
"""
|
||||||
Start playback of `tableView.current_song_filepath` & moves playback slider
|
Start playback of `tableView.current_song_filepath` & moves playback slider
|
||||||
"""
|
"""
|
||||||
self.tableView.set_current_song_filepath()
|
# self.tableView.set_current_song_filepath()
|
||||||
# get metadata
|
# get metadata
|
||||||
self.current_song_metadata = self.tableView.get_current_song_metadata()
|
self.current_song_metadata = self.tableView.get_current_song_metadata()
|
||||||
# read the file
|
# read the file
|
||||||
@ -568,6 +569,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
initialize_db()
|
initialize_db()
|
||||||
self.tableView.load_music_table()
|
self.tableView.load_music_table()
|
||||||
|
|
||||||
|
|
||||||
def update_database_file() -> bool:
|
def update_database_file() -> bool:
|
||||||
"""
|
"""
|
||||||
Reads the database file (specified by config file)
|
Reads the database file (specified by config file)
|
||||||
@ -583,7 +585,9 @@ def update_database_file() -> bool:
|
|||||||
# If the database location isnt set at the config location, move it
|
# If the database location isnt set at the config location, move it
|
||||||
if not db_filepath.startswith(cfg_path):
|
if not db_filepath.startswith(cfg_path):
|
||||||
new_path = f"{cfg_path}/{db_filepath}"
|
new_path = f"{cfg_path}/{db_filepath}"
|
||||||
debug(f"Set new config [db] database path: \n> Current: {db_filepath}\n> New:{new_path}")
|
debug(
|
||||||
|
f"Set new config [db] database path: \n> Current: {db_filepath}\n> New:{new_path}"
|
||||||
|
)
|
||||||
config["db"]["database"] = new_path
|
config["db"]["database"] = new_path
|
||||||
# Save the config
|
# Save the config
|
||||||
with open(cfg_file, "w") as configfile:
|
with open(cfg_file, "w") as configfile:
|
||||||
@ -595,12 +599,11 @@ def update_database_file() -> bool:
|
|||||||
db_path.pop()
|
db_path.pop()
|
||||||
db_path = "/".join(db_path)
|
db_path = "/".join(db_path)
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(db_filepath):
|
if os.path.exists(db_filepath):
|
||||||
try:
|
try:
|
||||||
size = os.path.getsize(db_filepath)
|
size = os.path.getsize(db_filepath)
|
||||||
except OSError:
|
except OSError:
|
||||||
error('Database file exists but could not read.')
|
error("Database file exists but could not read.")
|
||||||
return False
|
return False
|
||||||
if size == 0:
|
if size == 0:
|
||||||
initialize_db()
|
initialize_db()
|
||||||
@ -611,6 +614,7 @@ def update_database_file() -> bool:
|
|||||||
initialize_db()
|
initialize_db()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def update_config_file() -> ConfigParser:
|
def update_config_file() -> ConfigParser:
|
||||||
"""
|
"""
|
||||||
If the user config file is not up to date, update it with examples from sample config
|
If the user config file is not up to date, update it with examples from sample config
|
||||||
@ -655,8 +659,6 @@ def update_config_file() -> ConfigParser:
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# logging setup
|
# logging setup
|
||||||
file_handler = logging.FileHandler(filename="log", encoding="utf-8")
|
file_handler = logging.FileHandler(filename="log", encoding="utf-8")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user