This commit is contained in:
billypom on debian 2025-04-24 19:22:49 -04:00
commit 3d16882185
6 changed files with 98 additions and 430 deletions

View File

@ -70,6 +70,7 @@ class MusicTable(QTableView):
refreshMusicTableSignal = pyqtSignal() refreshMusicTableSignal = pyqtSignal()
handleProgressSignal = pyqtSignal(str) handleProgressSignal = pyqtSignal(str)
getThreadPoolSignal = pyqtSignal() getThreadPoolSignal = pyqtSignal()
searchBoxSignal = pyqtSignal()
def __init__(self, parent=None, application_window=None): def __init__(self, parent=None, application_window=None):
super().__init__(parent) super().__init__(parent)
@ -91,6 +92,7 @@ class MusicTable(QTableView):
self.proxymodel.setSourceModel(self.model2) self.proxymodel.setSourceModel(self.model2)
self.setModel(self.proxymodel) self.setModel(self.proxymodel)
self.setSortingEnabled(True) self.setSortingEnabled(True)
self.search_string = None
# Config # Config
cfg_file = ( cfg_file = (
@ -582,6 +584,12 @@ class MusicTable(QTableView):
# Delete key? # Delete key?
shortcut = QShortcut(QKeySequence("Delete"), self) shortcut = QShortcut(QKeySequence("Delete"), self)
shortcut.activated.connect(self.delete_songs) 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: def confirm_reorganize_files(self) -> None:
""" """
@ -669,14 +677,27 @@ class MusicTable(QTableView):
self.model2.clear() self.model2.clear()
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers()) self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
fields = ", ".join(self.headers.user_fields) fields = ", ".join(self.headers.user_fields)
search_clause = (
"title LIKE %?% AND artist LIKE %?% and album LIKE %?%"
if self.search_string
else ""
)
params = ""
if playlist_id: # Load a playlist if playlist_id: # Load a playlist
# Fetch playlist data
selected_playlist_id = playlist_id[0] selected_playlist_id = playlist_id[0]
try: try:
with DBA.DBAccess() as db: 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( data = db.query(
f"SELECT id, {fields} FROM song JOIN song_playlist sp ON id = sp.song_id WHERE sp.playlist_id = ?", query,
(selected_playlist_id,), (selected_playlist_id, params),
) )
except Exception as e: except Exception as e:
error(f"load_music_table() | Unhandled exception: {e}") error(f"load_music_table() | Unhandled exception: {e}")
@ -684,9 +705,17 @@ class MusicTable(QTableView):
else: # Load the library else: # Load the library
try: try:
with DBA.DBAccess() as db: 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( data = db.query(
f"SELECT id, {fields} FROM song;", query,
(), (params),
) )
except Exception as e: except Exception as e:
error(f"load_music_table() | Unhandled exception: {e}") error(f"load_music_table() | Unhandled exception: {e}")
@ -915,6 +944,10 @@ class MusicTable(QTableView):
real_index: QModelIndex = self.proxymodel.mapToSource(index) real_index: QModelIndex = self.proxymodel.mapToSource(index)
self.selected_song_qmodel_index: QModelIndex = real_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: 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"""
self.qapp = qapp self.qapp = qapp

View 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

View File

@ -12,3 +12,4 @@ from .ExportPlaylistWindow import ExportPlaylistWindow
from .QuestionBoxDetails import QuestionBoxDetails from .QuestionBoxDetails import QuestionBoxDetails
from .HeaderTags import HeaderTags from .HeaderTags import HeaderTags
from .MediaPlayer import MediaPlayer from .MediaPlayer import MediaPlayer
from .SearchLineEdit import SearchLineEdit

15
main.py
View File

@ -18,6 +18,7 @@ from ui import Ui_MainWindow
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QFileDialog, QFileDialog,
QLabel, QLabel,
QLineEdit,
QMainWindow, QMainWindow,
QApplication, QApplication,
QGraphicsScene, QGraphicsScene,
@ -261,13 +262,18 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
# for drag & drop functionality # for drag & drop functionality
self.tableView.viewport().installEventFilter(self) self.tableView.viewport().installEventFilter(self)
# Search box
self.lineEditSearch: QLineEdit
## CONNECTIONS ## CONNECTIONS
self.lineEditSearch.textChanged.connect(self.handle_search_box_text)
# tableView # tableView
self.tableView.playSignal.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
self.tableView.handleProgressSignal.connect(self.handle_progress) self.tableView.handleProgressSignal.connect(self.handle_progress)
self.tableView.searchBoxSignal.connect(self.handle_search_box)
self.tableView.playlistStatsSignal.connect( self.tableView.playlistStatsSignal.connect(
self.set_permanent_status_bar_message self.set_permanent_status_bar_message
) )
@ -483,6 +489,15 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
else: else:
self.status_bar.showMessage(message) 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: def play_audio_file(self, filepath=None) -> None:
""" """
Start playback of filepath & moves playback slider Start playback of filepath & moves playback slider

15
ui.py
View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'ui.ui' # 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 # 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. # 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.setContentsMargins(-1, -1, 0, -1)
self.verticalLayout.setSpacing(6) self.verticalLayout.setSpacing(6)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.lineEditSearch = SearchLineEdit(self.centralwidget)
self.lineEditSearch.setObjectName("lineEditSearch")
self.verticalLayout.addWidget(self.lineEditSearch)
self.hLayoutHead = QtWidgets.QHBoxLayout() self.hLayoutHead = QtWidgets.QHBoxLayout()
self.hLayoutHead.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.hLayoutHead.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.hLayoutHead.setObjectName("hLayoutHead") self.hLayoutHead.setObjectName("hLayoutHead")
@ -57,13 +60,15 @@ class Ui_MainWindow(object):
self.hLayoutMusicTable.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) self.hLayoutMusicTable.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
self.hLayoutMusicTable.setContentsMargins(0, -1, 0, -1) self.hLayoutMusicTable.setContentsMargins(0, -1, 0, -1)
self.hLayoutMusicTable.setObjectName("hLayoutMusicTable") self.hLayoutMusicTable.setObjectName("hLayoutMusicTable")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.playlistTreeView = PlaylistsPane(self.centralwidget) self.playlistTreeView = PlaylistsPane(self.centralwidget)
self.playlistTreeView.setObjectName("playlistTreeView") 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 = MusicTable(self.centralwidget)
self.tableView.setObjectName("tableView") self.tableView.setObjectName("tableView")
self.hLayoutMusicTable.addWidget(self.tableView) self.hLayoutMusicTable.addWidget(self.tableView)
self.hLayoutMusicTable.setStretch(0, 2)
self.hLayoutMusicTable.setStretch(1, 10) self.hLayoutMusicTable.setStretch(1, 10)
self.verticalLayout.addLayout(self.hLayoutMusicTable) self.verticalLayout.addLayout(self.hLayoutMusicTable)
self.hLayoutCurrentSongDetails = QtWidgets.QHBoxLayout() self.hLayoutCurrentSongDetails = QtWidgets.QHBoxLayout()
@ -185,7 +190,7 @@ class Ui_MainWindow(object):
self.verticalLayout_3.setStretch(0, 20) self.verticalLayout_3.setStretch(0, 20)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) 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.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
@ -252,5 +257,5 @@ class Ui_MainWindow(object):
self.actionDeleteDatabase.setText(_translate("MainWindow", "Delete Database")) self.actionDeleteDatabase.setText(_translate("MainWindow", "Delete Database"))
self.actionNewPlaylist.setText(_translate("MainWindow", "New playlist")) self.actionNewPlaylist.setText(_translate("MainWindow", "New playlist"))
self.actionExportPlaylist.setText(_translate("MainWindow", "Export 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 from pyqtgraph import PlotWidget

420
ui.ui
View File

@ -1,420 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>953</width>
<height>739</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="statusTip">
<string/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,0,6">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="vlayoutAlbumArt">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="AlbumArtGraphicsView" name="albumGraphicsView">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutSongDetails"/>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutPlaybackVisuals">
<item>
<widget class="PlotWidget" name="PlotWidget" native="true"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutMusicTable" stretch="2,10">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="PlaylistsPane" name="playlistTreeView"/>
</item>
<item>
<widget class="MusicTable" name="tableView"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutCurrentSongDetails" stretch="0">
<property name="spacing">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutSongDetails" stretch="0,0,0">
<item>
<widget class="QLabel" name="artistLabel"/>
</item>
<item>
<widget class="QLabel" name="titleLabel"/>
</item>
<item>
<widget class="QLabel" name="albumLabel"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutPlayback" stretch="4,0">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QSlider" name="playbackSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="timeHorizontalLayout2">
<item>
<widget class="QLabel" name="startTimeLabel">
<property name="font">
<font>
<family>Monospace</family>
<italic>false</italic>
</font>
</property>
<property name="text">
<string>00:00</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="slashLabel">
<property name="font">
<font>
<family>Monospace</family>
<italic>false</italic>
</font>
</property>
<property name="text">
<string>/</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="endTimeLabel">
<property name="font">
<font>
<family>Monospace</family>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>00:00</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,0,1,1,1,0,1">
<property name="spacing">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutVolume">
<item>
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>50</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="volumeSlider">
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>101</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="previousButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSlider" name="speedSlider">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="speedLabel">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="text">
<string>1.00</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>953</width>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpenFiles"/>
<addaction name="actionNewPlaylist"/>
<addaction name="actionExportPlaylist"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionPreferences"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
</widget>
<widget class="QMenu" name="menuQuick_Actions">
<property name="title">
<string>Quick-Actions</string>
</property>
<addaction name="actionScanLibraries"/>
<addaction name="actionDeleteLibrary"/>
<addaction name="actionDeleteDatabase"/>
<addaction name="actionSortColumns"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menuQuick_Actions"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionPreferences">
<property name="text">
<string>Preferences</string>
</property>
<property name="statusTip">
<string>Open preferences</string>
</property>
</action>
<action name="actionScanLibraries">
<property name="text">
<string>Scan libraries</string>
</property>
</action>
<action name="actionDeleteLibrary">
<property name="text">
<string>Delete Library</string>
</property>
</action>
<action name="actionSortColumns">
<property name="text">
<string>Sort Columns</string>
</property>
</action>
<action name="actionOpenFiles">
<property name="text">
<string>Open file(s)</string>
</property>
</action>
<action name="actionDeleteDatabase">
<property name="text">
<string>Delete Database</string>
</property>
</action>
<action name="actionNewPlaylist">
<property name="text">
<string>New playlist</string>
</property>
</action>
<action name="actionExportPlaylist">
<property name="text">
<string>Export playlist</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>PlotWidget</class>
<extends>QWidget</extends>
<header>pyqtgraph</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MusicTable</class>
<extends>QTableView</extends>
<header>components</header>
</customwidget>
<customwidget>
<class>AlbumArtGraphicsView</class>
<extends>QGraphicsView</extends>
<header>components</header>
</customwidget>
<customwidget>
<class>PlaylistsPane</class>
<extends>QTreeView</extends>
<header>components</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>