full text search testing
This commit is contained in:
parent
491e134d94
commit
4b440378a5
@ -75,6 +75,7 @@ class MusicTable(QTableView):
|
||||
refreshMusicTableSignal = pyqtSignal()
|
||||
handleProgressSignal = pyqtSignal(str)
|
||||
getThreadPoolSignal = pyqtSignal()
|
||||
searchBoxSignal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, application_window=None):
|
||||
super().__init__(parent)
|
||||
@ -96,6 +97,7 @@ class MusicTable(QTableView):
|
||||
self.proxymodel.setSourceModel(self.model2)
|
||||
self.setModel(self.proxymodel)
|
||||
self.setSortingEnabled(True)
|
||||
self.search_string = None
|
||||
|
||||
# Config
|
||||
cfg_file = (
|
||||
@ -591,6 +593,12 @@ class MusicTable(QTableView):
|
||||
# Delete key?
|
||||
shortcut = QShortcut(QKeySequence("Delete"), self)
|
||||
shortcut.activated.connect(self.delete_songs)
|
||||
# Search box
|
||||
shortcut = QShortcut(QKeySequence("Ctrl+F"), self)
|
||||
shortcut.activated.connect(self.emit_search_box)
|
||||
|
||||
def emit_search_box(self):
|
||||
self.searchBoxSignal.emit()
|
||||
|
||||
def confirm_reorganize_files(self) -> None:
|
||||
"""
|
||||
@ -677,15 +685,28 @@ class MusicTable(QTableView):
|
||||
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
||||
self.model2.clear()
|
||||
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
|
||||
search_clause = (
|
||||
"title LIKE %?% AND artist LIKE %?% and album LIKE %?%"
|
||||
if self.search_string
|
||||
else ""
|
||||
)
|
||||
fields = ", ".join(self.headers.user_headers)
|
||||
params = ""
|
||||
if playlist_id: # Load a playlist
|
||||
# Fetch playlist data
|
||||
selected_playlist_id = playlist_id[0]
|
||||
try:
|
||||
with DBA.DBAccess() as db:
|
||||
query = f"SELECT id, {fields} FROM song JOIN song_playlist sp ON id = sp.song_id WHERE sp.playlist_id = ?"
|
||||
# fulltext search
|
||||
if self.search_string:
|
||||
params = 3 * [self.search_string]
|
||||
if query.find("WHERE") == -1:
|
||||
query = f"{query} WHERE {search_clause};"
|
||||
else:
|
||||
query = f"{query} AND {search_clause};"
|
||||
data = db.query(
|
||||
f"SELECT id, {fields} FROM song JOIN song_playlist sp ON id = sp.song_id WHERE sp.playlist_id = ?",
|
||||
(selected_playlist_id,),
|
||||
query,
|
||||
(selected_playlist_id, params),
|
||||
)
|
||||
except Exception as e:
|
||||
error(f"load_music_table() | Unhandled exception: {e}")
|
||||
@ -693,9 +714,17 @@ class MusicTable(QTableView):
|
||||
else: # Load the library
|
||||
try:
|
||||
with DBA.DBAccess() as db:
|
||||
query = f"SELECT id, {fields} FROM song"
|
||||
# fulltext search
|
||||
if self.search_string:
|
||||
params = 3 * [self.search_string]
|
||||
if query.find("WHERE") == -1:
|
||||
query = f"{query} WHERE {search_clause};"
|
||||
else:
|
||||
query = f"{query} AND {search_clause};"
|
||||
data = db.query(
|
||||
f"SELECT id, {fields} FROM song;",
|
||||
(),
|
||||
query,
|
||||
(params),
|
||||
)
|
||||
except Exception as e:
|
||||
error(f"load_music_table() | Unhandled exception: {e}")
|
||||
@ -730,13 +759,15 @@ class MusicTable(QTableView):
|
||||
# reloading the model destroys and makes new indexes
|
||||
# so we look for the new index of the current song on load
|
||||
current_song_filepath = self.get_current_song_filepath()
|
||||
print(f'load music table current filepath: {current_song_filepath}')
|
||||
print(f"load music table current filepath: {current_song_filepath}")
|
||||
for row in range(self.model2.rowCount()):
|
||||
real_index = self.model2.index(row, self.headers.user_headers.index("filepath"))
|
||||
real_index = self.model2.index(
|
||||
row, self.headers.user_headers.index("filepath")
|
||||
)
|
||||
if real_index.data() == current_song_filepath:
|
||||
print('is it true?')
|
||||
print(f'{real_index.data()} == {current_song_filepath}')
|
||||
print('load music table real index:')
|
||||
print("is it true?")
|
||||
print(f"{real_index.data()} == {current_song_filepath}")
|
||||
print("load music table real index:")
|
||||
print(real_index)
|
||||
self.current_song_qmodel_index = real_index
|
||||
self.model2.layoutChanged.emit() # emits a signal that the view should be updated
|
||||
@ -850,7 +881,9 @@ class MusicTable(QTableView):
|
||||
return []
|
||||
selected_rows = set(index.row() for index in indexes)
|
||||
id_list = [
|
||||
self.proxymodel.data(self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole)
|
||||
self.proxymodel.data(
|
||||
self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole
|
||||
)
|
||||
for row in selected_rows
|
||||
]
|
||||
return id_list
|
||||
@ -875,9 +908,13 @@ class MusicTable(QTableView):
|
||||
except ValueError:
|
||||
# if the user doesnt have filepath selected as a header, retrieve the file from db
|
||||
row = self.currentIndex().row()
|
||||
id = self.proxymodel.data(self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole)
|
||||
id = self.proxymodel.data(
|
||||
self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole
|
||||
)
|
||||
with DBA.DBAccess() as db:
|
||||
filepath = db.query('SELECT filepath FROM song WHERE id = ?', (id,))[0][0]
|
||||
filepath = db.query("SELECT filepath FROM song WHERE id = ?", (id,))[0][
|
||||
0
|
||||
]
|
||||
self.selected_song_filepath = filepath
|
||||
|
||||
def set_current_song_filepath(self, filepath=None) -> None:
|
||||
@ -887,7 +924,9 @@ class MusicTable(QTableView):
|
||||
"""
|
||||
# update the filepath
|
||||
if not filepath:
|
||||
path = self.current_song_qmodel_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
|
||||
path = self.current_song_qmodel_index.siblingAtColumn(
|
||||
self.headers.user_headers.index("filepath")
|
||||
).data()
|
||||
self.current_song_filepath: str = path
|
||||
else:
|
||||
self.current_song_filepath = filepath
|
||||
@ -916,6 +955,10 @@ class MusicTable(QTableView):
|
||||
real_index: QModelIndex = self.proxymodel.mapToSource(index)
|
||||
self.selected_song_qmodel_index: QModelIndex = real_index
|
||||
|
||||
def set_search_string(self, text: str):
|
||||
"""set the search string"""
|
||||
self.search_string = text
|
||||
|
||||
def load_qapp(self, qapp) -> None:
|
||||
"""Necessary for using members and methods of main application window"""
|
||||
self.qapp = qapp
|
||||
|
||||
34
components/SearchLineEdit.py
Normal file
34
components/SearchLineEdit.py
Normal file
@ -0,0 +1,34 @@
|
||||
from PyQt5.QtWidgets import QLineEdit
|
||||
|
||||
"""
|
||||
MusicTable.py holds a variable called self.search_string
|
||||
MusicTable.py had a function called load_music_table(), which loads data
|
||||
from the SQLite database to the QTableView. load_music_table()
|
||||
checks for the self.search_string
|
||||
|
||||
in main.py, on self.lineEditSearch.textChanged(),
|
||||
this updates the self.search_string in MusicTable.py
|
||||
|
||||
in MusicTable.py, when Ctrl+F is pressed, the line edit gets hidden or visible
|
||||
"""
|
||||
|
||||
|
||||
class SearchLineEdit(QLineEdit):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setVisible(False)
|
||||
|
||||
def toggle_visibility(self):
|
||||
if self.isHidden():
|
||||
self.setHidden(False)
|
||||
else:
|
||||
self.setHidden(True)
|
||||
self.setText(None)
|
||||
|
||||
# def toggle_visibility(self):
|
||||
# if self.is_hidden:
|
||||
# self.button.setVisible(True)
|
||||
# self.is_hidden = False
|
||||
# else:
|
||||
# self.button.setVisible(False)
|
||||
# self.is_hidden = True
|
||||
@ -12,3 +12,4 @@ from .ExportPlaylistWindow import ExportPlaylistWindow
|
||||
from .QuestionBoxDetails import QuestionBoxDetails
|
||||
from .HeaderTags import HeaderTags
|
||||
from .MediaPlayer import MediaPlayer
|
||||
from .SearchLineEdit import SearchLineEdit
|
||||
|
||||
51
main.py
51
main.py
@ -18,6 +18,7 @@ from ui import Ui_MainWindow
|
||||
from PyQt5.QtWidgets import (
|
||||
QFileDialog,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QMainWindow,
|
||||
QApplication,
|
||||
QGraphicsScene,
|
||||
@ -41,7 +42,13 @@ from PyQt5.QtCore import (
|
||||
QThreadPool,
|
||||
QRunnable,
|
||||
)
|
||||
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe, QMediaPlaylist, QMultimedia
|
||||
from PyQt5.QtMultimedia import (
|
||||
QMediaPlayer,
|
||||
QMediaContent,
|
||||
QAudioProbe,
|
||||
QMediaPlaylist,
|
||||
QMultimedia,
|
||||
)
|
||||
from PyQt5.QtGui import QClipboard, QCloseEvent, QFont, QPixmap, QResizeEvent
|
||||
from utils import (
|
||||
delete_album_art,
|
||||
@ -50,7 +57,7 @@ from utils import (
|
||||
initialize_db,
|
||||
add_files_to_database,
|
||||
set_album_art,
|
||||
id3_remap
|
||||
id3_remap,
|
||||
)
|
||||
from components import (
|
||||
HeaderTags,
|
||||
@ -253,13 +260,18 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
# for drag & drop functionality
|
||||
self.tableView.viewport().installEventFilter(self)
|
||||
|
||||
# Search box
|
||||
self.lineEditSearch: QLineEdit
|
||||
|
||||
## CONNECTIONS
|
||||
self.lineEditSearch.textChanged.connect(self.handle_search_box_text)
|
||||
# tableView
|
||||
self.tableView.playSignal.connect(self.play_audio_file)
|
||||
self.tableView.playPauseSignal.connect(
|
||||
self.on_play_clicked
|
||||
) # Spacebar toggle play/pause signal
|
||||
self.tableView.handleProgressSignal.connect(self.handle_progress)
|
||||
self.tableView.searchBoxSignal.connect(self.handle_search_box)
|
||||
self.tableView.playlistStatsSignal.connect(
|
||||
self.set_permanent_status_bar_message
|
||||
)
|
||||
@ -328,7 +340,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
file_url = media.canonicalUrl().toLocalFile()
|
||||
metadata = id3_remap(get_tags(file_url)[0])
|
||||
if metadata is not None:
|
||||
self.set_ui_metadata(metadata["title"], metadata["artist"], metadata["album"], file_url)
|
||||
self.set_ui_metadata(
|
||||
metadata["title"], metadata["artist"], metadata["album"], file_url
|
||||
)
|
||||
|
||||
def on_volume_changed(self) -> None:
|
||||
"""Handles volume changes"""
|
||||
@ -374,8 +388,12 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
return
|
||||
row: int = index.row()
|
||||
prev_row: int = row - 1
|
||||
prev_index: QModelIndex = self.tableView.proxymodel.index(prev_row, index.column())
|
||||
prev_filepath = prev_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
|
||||
prev_index: QModelIndex = self.tableView.proxymodel.index(
|
||||
prev_row, index.column()
|
||||
)
|
||||
prev_filepath = prev_index.siblingAtColumn(
|
||||
self.headers.user_headers.index("filepath")
|
||||
).data()
|
||||
if prev_filepath is None:
|
||||
return
|
||||
|
||||
@ -394,8 +412,12 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
return
|
||||
row: int = index.row()
|
||||
next_row: int = row + 1
|
||||
next_index: QModelIndex = self.tableView.proxymodel.index(next_row, index.column())
|
||||
next_filepath = next_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
|
||||
next_index: QModelIndex = self.tableView.proxymodel.index(
|
||||
next_row, index.column()
|
||||
)
|
||||
next_filepath = next_index.siblingAtColumn(
|
||||
self.headers.user_headers.index("filepath")
|
||||
).data()
|
||||
if next_filepath is None:
|
||||
return
|
||||
self.play_audio_file(next_filepath)
|
||||
@ -465,13 +487,22 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
else:
|
||||
self.status_bar.showMessage(message)
|
||||
|
||||
def handle_search_box(self):
|
||||
"""show or hide the searchbox"""
|
||||
self.lineEditSearch.toggle_visibility()
|
||||
|
||||
def handle_search_box_text(self, text: str):
|
||||
"""when text changes, update the music table thingie"""
|
||||
self.tableView.set_search_string(text)
|
||||
self.tableView.load_music_table(text)
|
||||
|
||||
def play_audio_file(self, filepath=None) -> None:
|
||||
"""
|
||||
Start playback of filepath & moves playback slider
|
||||
|
||||
filepath default value = `tableView.current_song_filepath`
|
||||
"""
|
||||
print('play audio file')
|
||||
print("play audio file")
|
||||
if not filepath:
|
||||
filepath = self.tableView.get_selected_song_filepath()
|
||||
metadata = id3_remap(get_tags(filepath)[0])
|
||||
@ -487,7 +518,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
|
||||
# assign "now playing" labels & album artwork
|
||||
if metadata is not None:
|
||||
self.set_ui_metadata(metadata["title"], metadata["artist"], metadata["album"], filepath)
|
||||
self.set_ui_metadata(
|
||||
metadata["title"], metadata["artist"], metadata["album"], filepath
|
||||
)
|
||||
|
||||
def set_ui_metadata(self, title, artist, album, filepath):
|
||||
"""
|
||||
|
||||
15
ui.py
15
ui.py
@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.10
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@ -24,6 +24,9 @@ class Ui_MainWindow(object):
|
||||
self.verticalLayout.setContentsMargins(-1, -1, 0, -1)
|
||||
self.verticalLayout.setSpacing(6)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.lineEditSearch = SearchLineEdit(self.centralwidget)
|
||||
self.lineEditSearch.setObjectName("lineEditSearch")
|
||||
self.verticalLayout.addWidget(self.lineEditSearch)
|
||||
self.hLayoutHead = QtWidgets.QHBoxLayout()
|
||||
self.hLayoutHead.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.hLayoutHead.setObjectName("hLayoutHead")
|
||||
@ -57,13 +60,15 @@ class Ui_MainWindow(object):
|
||||
self.hLayoutMusicTable.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
|
||||
self.hLayoutMusicTable.setContentsMargins(0, -1, 0, -1)
|
||||
self.hLayoutMusicTable.setObjectName("hLayoutMusicTable")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.playlistTreeView = PlaylistsPane(self.centralwidget)
|
||||
self.playlistTreeView.setObjectName("playlistTreeView")
|
||||
self.hLayoutMusicTable.addWidget(self.playlistTreeView)
|
||||
self.verticalLayout_2.addWidget(self.playlistTreeView)
|
||||
self.hLayoutMusicTable.addLayout(self.verticalLayout_2)
|
||||
self.tableView = MusicTable(self.centralwidget)
|
||||
self.tableView.setObjectName("tableView")
|
||||
self.hLayoutMusicTable.addWidget(self.tableView)
|
||||
self.hLayoutMusicTable.setStretch(0, 2)
|
||||
self.hLayoutMusicTable.setStretch(1, 10)
|
||||
self.verticalLayout.addLayout(self.hLayoutMusicTable)
|
||||
self.hLayoutCurrentSongDetails = QtWidgets.QHBoxLayout()
|
||||
@ -185,7 +190,7 @@ class Ui_MainWindow(object):
|
||||
self.verticalLayout_3.setStretch(0, 20)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1152, 24))
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1152, 21))
|
||||
self.menubar.setObjectName("menubar")
|
||||
self.menuFile = QtWidgets.QMenu(self.menubar)
|
||||
self.menuFile.setObjectName("menuFile")
|
||||
@ -252,5 +257,5 @@ class Ui_MainWindow(object):
|
||||
self.actionDeleteDatabase.setText(_translate("MainWindow", "Delete Database"))
|
||||
self.actionNewPlaylist.setText(_translate("MainWindow", "New playlist"))
|
||||
self.actionExportPlaylist.setText(_translate("MainWindow", "Export playlist"))
|
||||
from components import AlbumArtGraphicsView, MusicTable, PlaylistsPane
|
||||
from components import AlbumArtGraphicsView, MusicTable, PlaylistsPane, SearchLineEdit
|
||||
from pyqtgraph import PlotWidget
|
||||
|
||||
18
ui.ui
18
ui.ui
@ -26,6 +26,9 @@
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="SearchLineEdit" name="lineEditSearch"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,0,6">
|
||||
<property name="sizeConstraint">
|
||||
@ -73,7 +76,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hLayoutMusicTable" stretch="2,10">
|
||||
<layout class="QHBoxLayout" name="hLayoutMusicTable" stretch="0,10">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
@ -84,7 +87,11 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="PlaylistsPane" name="playlistTreeView"/>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="PlaylistsPane" name="playlistTreeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MusicTable" name="tableView"/>
|
||||
@ -311,7 +318,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1152</width>
|
||||
<height>24</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@ -414,6 +421,11 @@
|
||||
<extends>QTreeView</extends>
|
||||
<header>components</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SearchLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>components</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user