diff --git a/app/infotabs.py b/app/infotabs.py
new file mode 100644
index 0000000..04bbc07
--- /dev/null
+++ b/app/infotabs.py
@@ -0,0 +1,49 @@
+import urllib.parse
+
+from datetime import datetime
+from typing import Dict, Optional
+from PyQt5.QtCore import QUrl
+from PyQt5.QtWebEngineWidgets import QWebEngineView
+from PyQt5.QtWidgets import QTabWidget
+from config import Config
+
+
+class InfoTabs(QTabWidget):
+ """
+ Class to manage info tabs
+ """
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+
+ # Dictionary to record when tabs were last updated (so we can
+ # re-use the oldest one later)
+ self.last_update: Dict[QWebEngineView, datetime] = {}
+
+ def open_tab(self, title: str) -> None:
+ """
+ Open passed URL. Create new tab if we're below the maximum
+ number otherwise reuse oldest content tab.
+ """
+
+ short_title = title[:Config.INFO_TAB_TITLE_LENGTH]
+
+ if self.count() < Config.MAX_INFO_TABS:
+ # Create a new tab
+ widget = QWebEngineView()
+ widget.setZoomFactor(Config.WEB_ZOOM_FACTOR)
+ tab_index = self.addTab(widget, short_title)
+
+ else:
+ # Reuse oldest widget
+ widget = min(self.last_update, key=self.last_update.get)
+ tab_index = self.indexOf(widget)
+ self.setTabText(tab_index, short_title)
+
+ txt = urllib.parse.quote_plus(title)
+ url = Config.INFO_TAB_URL % txt
+ widget.setUrl(QUrl(url))
+ self.last_update[widget] = datetime.now()
+
+ # Show newly updated tab
+ self.setCurrentIndex(tab_index)
diff --git a/app/musicmuster.py b/app/musicmuster.py
index 747e1cd..92d09dd 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -5,7 +5,6 @@ from log import log
# import psutil
import sys
# import threading
-# import urllib.parse
# import webbrowser
#
#
@@ -15,7 +14,6 @@ import sys
# from PyQt5.QtCore import QDate, QEvent, QProcess, Qt, QTime, QTimer, QUrl
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
-# from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
from PyQt5.QtWidgets import (
QApplication,
QDialog,
@@ -32,7 +30,6 @@ from dbconfig import engine, Session
# import helpers
# import music
#
-# from config import Config
from models import (
Base,
# Playdates,
@@ -78,7 +75,6 @@ class Window(QMainWindow, Ui_MainWindow):
# self.music: music.Music = music.Music()
self.current_track: Optional[TrackData] = None
self.current_track_playlist_tab: Optional[PlaylistTab] = None
- self.info_tabs: Optional[Dict[str, QWebView]] = {}
self.next_track: Optional[TrackData] = None
self.next_track_playlist_tab: Optional[PlaylistTab] = None
self.previous_track: Optional[TrackData] = None
@@ -92,6 +88,7 @@ class Window(QMainWindow, Ui_MainWindow):
# self.txtSearch.setHidden(True)
# self.hide_played_tracks = False
#
+ self.splitter.setSizes([200, 200])
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
self.tabPlaylist.currentWidget
#
@@ -557,52 +554,6 @@ class Window(QMainWindow, Ui_MainWindow):
destination_visible_playlist_tab.populate(
session, dlg.playlist.id)
#
-# def open_info_tabs(self) -> None:
-# """
-# Ensure we have info tabs for next and current track titles
-# """
-#
-# title_list: List[str] = []
-#
-# if self.previous_track:
-# title_list.append(self.previous_track.title)
-# if self.current_track:
-# title_list.append(self.current_track.title)
-# if self.next_track:
-# title_list.append(self.next_track.title)
-#
-# for title in title_list:
-# if title in self.info_tabs.keys():
-# # We already have a tab for this track
-# continue
-# if len(self.info_tabs) >= Config.MAX_log.info_TABS:
-# # Find an unneeded info tab
-# try:
-# old_title = list(
-# set(self.info_tabs.keys()) - set(title_list)
-# )[0]
-# except IndexError:
-# log.debug(
-# f"ensure_info_tabs({title_list}): unable to add "
-# f"{title=}"
-# )
-# return
-# # Assign redundant widget a new title
-# widget = self.info_tabs[title] = self.info_tabs[old_title]
-# idx = self.tabPlaylist.indexOf(widget)
-# self.tabPlaylist.setTabText(
-# idx, title[:Config.log.info_TAB_TITLE_LENGTH])
-# del self.info_tabs[old_title]
-# else:
-# # Create a new tab for this title
-# widget = self.info_tabs[title] = QWebView()
-# widget.setZoomFactor(Config.WEB_ZOOM_FACTOR)
-# self.tabPlaylist.addTab(
-# widget, title[:Config.log.info_TAB_TITLE_LENGTH])
-# txt = urllib.parse.quote_plus(title)
-# url = Config.log.info_TAB_URL % txt
-# widget.setUrl(QUrl(url))
-#
# def play_next(self) -> None:
# """
# Play next track.
@@ -783,7 +734,7 @@ class Window(QMainWindow, Ui_MainWindow):
# title = self.current_track.title
# if title:
# txt = urllib.parse.quote_plus(title)
-# url = Config.log.info_TAB_URL % txt
+# url = Config.TAB_URL % txt
# webbrowser.open(url, new=2)
#
# def stop(self) -> None:
@@ -879,8 +830,8 @@ class Window(QMainWindow, Ui_MainWindow):
self.update_headers()
# Populate 'info' tabs
- self.open_info_tabs()
-#
+ self.tabInfolist.open_tab(track.title)
+
# def tick(self) -> None:
# """
# Carry out clock tick actions.
diff --git a/app/playlists.py b/app/playlists.py
index e5377f5..1427d89 100644
--- a/app/playlists.py
+++ b/app/playlists.py
@@ -4,6 +4,7 @@ from typing import List, Optional
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
+ QBrush,
QColor,
QFont,
QDropEvent
@@ -364,12 +365,12 @@ class PlaylistTab(QTableWidget):
# playlist.close(session)
#
# event.accept()
-#
-# def clear_next(self, session) -> None:
-# """Clear next track"""
-#
-# self._meta_clear_next()
-# self.update_display(session)
+
+ def clear_next(self, session) -> None:
+ """Clear next track marker"""
+
+ self._meta_clear_next()
+ self.update_display(session)
#
# def create_note(self) -> None:
# """
@@ -1044,6 +1045,9 @@ class PlaylistTab(QTableWidget):
continue
# This is a track row other than next or current
+ # Reset colour in case it was current/next
+ self._set_row_colour(row, None)
+
if row in played:
# Played today, so update last played column
last_playedtime = track.lastplayed
@@ -1277,7 +1281,7 @@ class PlaylistTab(QTableWidget):
# Fix up row numbers left in this playlist
PlaylistRows.fixup_rownumbers(session, self.playlist_id)
- #Remove selected rows from display
+ # Remove selected rows from display
self.remove_selected_rows()
def _drop_on(self, event):
@@ -1852,14 +1856,22 @@ class PlaylistTab(QTableWidget):
if self.item(row, j):
self.item(row, j).setFont(boldfont)
- def _set_row_colour(self, row: int, colour: QColor) -> None:
- """Set row background colour"""
+ def _set_row_colour(self, row: int,
+ colour: Optional[QColor] = None) -> None:
+ """
+ Set or reset row background colour
+ """
j: int
+ if colour:
+ brush = QBrush(colour)
+ else:
+ brush = QBrush()
+
for j in range(1, self.columnCount()):
if self.item(row, j):
- self.item(row, j).setBackground(colour)
+ self.item(row, j).setBackground(brush)
#
# def _set_row_content(self, row: int, object_id: int) -> None:
# """Set content associated with this row"""
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index 97bc222..0417f05 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -282,19 +282,38 @@ border: 1px solid rgb(85, 87, 83);
-
-
-
- -1
-
-
- false
-
-
- true
-
-
- true
+
+
+ Qt::Vertical
+
+
+ -1
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+
+
+ -1
+
+
+ false
+
+
+ true
+
+
+ true
+
+
-
@@ -1022,6 +1041,14 @@ border: 1px solid rgb(85, 87, 83);
+
+
+ InfoTabs
+ QTabWidget
+
+ 1
+
+
diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py
index 8424f10..c62e097 100644
--- a/app/ui/main_window_ui.py
+++ b/app/ui/main_window_ui.py
@@ -142,12 +142,20 @@ class Ui_MainWindow(object):
self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_4.setObjectName("frame_4")
self.gridLayout_4.addWidget(self.frame_4, 1, 0, 1, 1)
- self.tabPlaylist = QtWidgets.QTabWidget(self.centralwidget)
+ self.splitter = QtWidgets.QSplitter(self.centralwidget)
+ self.splitter.setOrientation(QtCore.Qt.Vertical)
+ self.splitter.setObjectName("splitter")
+ self.tabPlaylist = QtWidgets.QTabWidget(self.splitter)
self.tabPlaylist.setDocumentMode(False)
self.tabPlaylist.setTabsClosable(True)
self.tabPlaylist.setMovable(True)
self.tabPlaylist.setObjectName("tabPlaylist")
- self.gridLayout_4.addWidget(self.tabPlaylist, 2, 0, 1, 1)
+ self.tabInfolist = InfoTabs(self.splitter)
+ self.tabInfolist.setDocumentMode(False)
+ self.tabInfolist.setTabsClosable(True)
+ self.tabInfolist.setMovable(True)
+ self.tabInfolist.setObjectName("tabInfolist")
+ self.gridLayout_4.addWidget(self.splitter, 2, 0, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.frame = QtWidgets.QFrame(self.centralwidget)
@@ -473,6 +481,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
QtCore.QMetaObject.connectSlotsByName(MainWindow)
@@ -550,4 +559,5 @@ class Ui_MainWindow(object):
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
self.actionInsert_section_header.setText(_translate("MainWindow", "Insert §ion header..."))
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
+from infotabs import InfoTabs
import icons_rc
diff --git a/¡ b/¡
new file mode 100644
index 0000000..81916a7
--- /dev/null
+++ b/¡
@@ -0,0 +1,1192 @@
+#!/usr/bin/env python
+
+from log import log
+# import argparse
+# import psutil
+import sys
+# import threading
+# import urllib.parse
+# import webbrowser
+#
+#
+# from datetime import datetime, timedelta
+# from typing import Callable, Dict, List, Optional, Tuple
+#
+# from PyQt5.QtCore import QDate, QEvent, QProcess, Qt, QTime, QTimer, QUrl
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QColor
+# from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
+from PyQt5.QtWidgets import (
+ QApplication,
+ QDialog,
+ # QFileDialog,
+ # QInputDialog,
+ QLabel,
+ # QLineEdit,
+ QListWidgetItem,
+ QMainWindow,
+ # QMessageBox,
+)
+#
+from dbconfig import engine, Session
+# import helpers
+# import music
+#
+# from config import Config
+from models import (
+ Base,
+ # Playdates,
+ PlaylistRows,
+ Playlists,
+ Settings,
+ Tracks
+)
+from playlists import PlaylistTab
+from sqlalchemy.orm.exc import DetachedInstanceError
+# from ui.dlg_search_database_ui import Ui_Dialog
+from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
+# from ui.downloadcsv_ui import Ui_DateSelect
+from config import Config
+from ui.main_window_ui import Ui_MainWindow
+# from utilities import create_track_from_file, update_db
+#
+#
+# log = logging.getLogger(Config.LOG_NAME)
+class TrackData:
+ def __init__(self, track):
+ self.id = track.id
+ self.title = track.title
+ self.artist = track.artist
+ self.duration = track.duration
+ self.start_gap = track.start_gap
+ self.fade_at = track.fade_at
+ self.silence_at = track.silence_at
+ self.path = track.path
+ self.mtime = track.mtime
+
+
+# class InfoTab(QWebView):
+# """Subclass QWebView to show info about tracks"""
+#
+# def __init__(self, parent=None) -> None:
+# super().__init__(parent)
+# self.title = None
+
+class Window(QMainWindow, Ui_MainWindow):
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.setupUi(self)
+
+# self.timer: QTimer = QTimer()
+# self.even_tick: bool = True
+# self.playing: bool = False
+# self.disable_play_next_controls()
+#
+# self.music: music.Music = music.Music()
+ self.current_track: Optional[TrackData] = None
+ self.current_track_playlist_tab: Optional[PlaylistTab] = None
+ self.info_tabs: Optional[Dict[str, QWebView]] = {}
+ self.next_track: Optional[TrackData] = None
+ self.next_track_playlist_tab: Optional[PlaylistTab] = None
+ self.previous_track: Optional[TrackData] = None
+ self.previous_track_position: Optional[int] = None
+
+# self.set_main_window_size()
+ self.lblSumPlaytime = QLabel("")
+ self.statusbar.addPermanentWidget(self.lblSumPlaytime)
+# self.txtSearch = QLineEdit()
+# self.statusbar.addWidget(self.txtSearch)
+# self.txtSearch.setHidden(True)
+# self.hide_played_tracks = False
+#
+ self.splitter.setStretchSizes[200,200]
+ self.visible_playlist_tab: Callable[[], PlaylistTab] = \
+ self.tabPlaylist.currentWidget
+#
+ self._load_last_playlists()
+# self.enable_play_next_controls()
+# self.check_audacity()
+# self.timer.start(Config.TIMER_MS)
+ self.connect_signals_slots()
+#
+# def set_main_window_size(self) -> None:
+# """Set size of window from database"""
+#
+# with Session() as session:
+# record = Settings.get_int_settings(session, "mainwindow_x")
+# x = record.f_int or 1
+# record = Settings.get_int_settings(session, "mainwindow_y")
+# y = record.f_int or 1
+# record = Settings.get_int_settings(session, "mainwindow_width")
+# width = record.f_int or 1599
+# record = Settings.get_int_settings(session, "mainwindow_height")
+# height = record.f_int or 981
+# self.setGeometry(x, y, width, height)
+# return
+#
+# @staticmethod
+# def print_current_database():
+# with Session() as session:
+# db = session.bind.engine.url.database
+# print(f"{db=}")
+#
+# @staticmethod
+# def check_audacity() -> None:
+# """Offer to run Audacity if not running"""
+#
+# if not Config.CHECK_AUDACITY_AT_STARTUP:
+# return
+#
+# if "audacity" in [i.name() for i in psutil.process_iter()]:
+# return
+#
+# if helpers.ask_yes_no("Audacity not running", "Start Audacity?"):
+# QProcess.startDetached(Config.AUDACITY_COMMAND, [])
+
+ def clear_selection(self) -> None:
+ """ Clear selected row"""
+
+ if self.visible_playlist_tab():
+ self.visible_playlist_tab().clear_selection()
+#
+# def closeEvent(self, event: QEvent) -> None:
+# """Don't allow window to close when a track is playing"""
+#
+# if self.music.playing():
+# log.debug("closeEvent() ignored as music is playing")
+# event.ignore()
+# helpers.show_warning(
+# "Track playing",
+# "Can't close application while track is playing")
+# else:
+# log.debug("closeEvent() accepted")
+#
+# with Session() as session:
+# record = Settings.get_int_settings(
+# session, "mainwindow_height")
+# if record.f_int != self.height():
+# record.update(session, {'f_int': self.height()})
+#
+# record = Settings.get_int_settings(session, "mainwindow_width")
+# if record.f_int != self.width():
+# record.update(session, {'f_int': self.width()})
+#
+# record = Settings.get_int_settings(session, "mainwindow_x")
+# if record.f_int != self.x():
+# record.update(session, {'f_int': self.x()})
+#
+# record = Settings.get_int_settings(session, "mainwindow_y")
+# if record.f_int != self.y():
+# record.update(session, {'f_int': self.y()})
+#
+# # Find a playlist tab (as opposed to an info tab) and
+# # save column widths
+# if self.current_track_playlist_tab:
+# self.current_track_playlist_tab.close()
+# elif self.next_track_playlist_tab:
+# self.next_track_playlist_tab.close()
+#
+# event.accept()
+
+ def connect_signals_slots(self) -> None:
+# self.actionAdd_note.triggered.connect(self.create_note)
+ self.action_Clear_selection.triggered.connect(self.clear_selection)
+# self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
+# self.actionDownload_CSV_of_played_tracks.triggered.connect(
+# self.download_played_tracks)
+# self.actionEnable_controls.triggered.connect(
+# self.enable_play_next_controls)
+# self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
+# self.actionImport.triggered.connect(self.import_track)
+# self.actionFade.triggered.connect(self.fade)
+# self.actionMoveSelected.triggered.connect(self.move_selected)
+# self.actionNewPlaylist.triggered.connect(self.create_playlist)
+# self.actionOpenPlaylist.triggered.connect(self.open_playlist)
+# self.actionPlay_next.triggered.connect(self.play_next)
+# self.actionSearch.triggered.connect(self.search_playlist)
+# self.actionSearch_database.triggered.connect(self.search_database)
+# self.actionSelect_next_track.triggered.connect(self.select_next_row)
+# self.actionSelect_played_tracks.triggered.connect(self.select_played)
+# self.actionSelect_previous_track.triggered.connect(
+# self.select_previous_row)
+# self.actionSelect_unplayed_tracks.triggered.connect(
+# self.select_unplayed)
+# self.actionSetNext.triggered.connect(
+# lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
+# self.actionSkip_next.triggered.connect(self.play_next)
+# self.actionStop.triggered.connect(self.stop)
+# # self.btnAddNote.clicked.connect(self.create_note)
+# # self.btnDatabase.clicked.connect(self.search_database)
+# self.btnDrop3db.clicked.connect(self.drop3db)
+# self.btnHidePlayed.clicked.connect(self.hide_played)
+# self.btnFade.clicked.connect(self.fade)
+# # self.btnPlay.clicked.connect(self.play_next)
+# # self.btnSetNext.clicked.connect(
+# # lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
+# # self.btnSongInfo.clicked.connect(self.song_info_search)
+# self.btnStop.clicked.connect(self.stop)
+# self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
+# self.txtSearch.returnPressed.connect(self.search_playlist_return)
+# self.txtSearch.textChanged.connect(self.search_playlist_update)
+#
+# self.timer.timeout.connect(self.tick)
+#
+# def create_playlist(self) -> None:
+# """Create new playlist"""
+#
+# dlg = QInputDialog(self)
+# dlg.setInputMode(QInputDialog.TextInput)
+# dlg.setLabelText("Playlist name:")
+# dlg.resize(500, 100)
+# ok = dlg.exec()
+# if ok:
+# with Session() as session:
+# playlist = Playlists(session, dlg.textValue())
+# self.create_playlist_tab(session, playlist)
+#
+# def close_playlist_tab(self) -> None:
+# """Close active playlist tab"""
+#
+# self.close_tab(self.tabPlaylist.currentIndex())
+#
+# def close_tab(self, index: int) -> None:
+# """
+# Close tab unless it holds the curren or next track
+# """
+#
+# if hasattr(self.tabPlaylist.widget(index), 'playlist_id'):
+# if self.tabPlaylist.widget(index) == (
+# self.current_track_playlist_tab):
+# self.statusbar.showMessage(
+# "Can't close current track playlist", 5000)
+# return
+# if self.tabPlaylist.widget(index) == self.next_track_playlist_tab:
+# self.statusbar.showMessage(
+# "Can't close next track playlist", 5000)
+# return
+# # It's OK to close this playlist so remove from open playlist list
+# self.tabPlaylist.widget(index).close()
+#
+# # Close regardless of tab type
+# self.tabPlaylist.removeTab(index)
+#
+# def create_note(self) -> None:
+# """Call playlist to create note"""
+#
+# try:
+# self.visible_playlist_tab().create_note()
+# except AttributeError:
+# # Just return if there's no visible playlist tab
+# return
+
+ def create_playlist_tab(self, session: Session,
+ playlist: Playlists) -> None:
+ """
+ Take the passed playlist database object, create a playlist tab and
+ add tab to display.
+ """
+
+ playlist_tab: PlaylistTab = PlaylistTab(
+ musicmuster=self, session=session, playlist_id=playlist.id)
+ idx: int = self.tabPlaylist.addTab(playlist_tab, playlist.name)
+ self.tabPlaylist.setCurrentIndex(idx)
+#
+# def disable_play_next_controls(self) -> None:
+# """
+# Disable "play next" keyboard controls
+# """
+#
+# log.debug("disable_play_next_controls()")
+# self.actionPlay_next.setEnabled(False)
+# self.statusbar.showMessage("Play controls: Disabled", 0)
+#
+# def download_played_tracks(self) -> None:
+# """Download a CSV of played tracks"""
+#
+# dlg = DownloadCSV(self)
+# if dlg.exec():
+# start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime()
+# # Get output filename
+# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
+# self, 'Save CSV of tracks played',
+# directory="/tmp/playlist.csv",
+# filter="CSV files (*.csv)"
+# )
+# if not pathspec:
+# return
+#
+# path: str = pathspec[0]
+# if not path.endswith(".csv"):
+# path += ".csv"
+#
+# with open(path, "w") as f:
+# with Session() as session:
+# for playdate in Playdates.played_after(session, start_dt):
+# f.write(
+# f"{playdate.track.artist},{playdate.track.title}\n"
+# )
+#
+# def drop3db(self) -> None:
+# """Drop music level by 3db if button checked"""
+#
+# if self.btnDrop3db.isChecked():
+# self.music.set_volume(Config.VOLUME_VLC_DROP3db, set_default=False)
+# else:
+# self.music.set_volume(Config.VOLUME_VLC_DEFAULT, set_default=False)
+#
+# def enable_play_next_controls(self) -> None:
+# """
+# Enable "play next" keyboard controls
+# """
+#
+# log.debug("enable_play_next_controls()")
+# self.actionPlay_next.setEnabled(True)
+# self.statusbar.showMessage("Play controls: Enabled", 0)
+#
+# def end_of_track_actions(self) -> None:
+# """
+# Clean up after track played
+#
+# Actions required:
+# - Set flag to say we're not playing a track
+# - Reset current track
+# - Tell playlist_tab track has finished
+# - Reset current playlist_tab
+# - Reset clocks
+# - Update headers
+# - Enable controls
+# """
+#
+# # Set flag to say we're not playing a track so that tick()
+# # doesn't see player=None and kick off end-of-track actions
+# self.playing = False
+#
+# # Reset current track
+# if self.current_track:
+# self.previous_track = self.current_track
+# self.current_track = None
+#
+# # Tell playlist_tab track has finished and
+# # reset current playlist_tab
+# if self.current_track_playlist_tab:
+# self.current_track_playlist_tab.play_stopped()
+# self.current_track_playlist_tab = None
+#
+# # Reset clocks
+# self.frame_fade.setStyleSheet("")
+# self.frame_silent.setStyleSheet("")
+# self.label_elapsed_timer.setText("00:00")
+# self.label_end_timer.setText("00:00")
+# self.label_fade_length.setText("0:00")
+# self.label_fade_timer.setText("00:00")
+# self.label_silent_timer.setText("00:00")
+# self.label_track_length.setText("0:00")
+# self.label_start_time.setText("00:00:00")
+# self.label_end_time.setText("00:00:00")
+#
+# # Update headers
+# self.update_headers()
+#
+# # Enable controls
+# self.enable_play_next_controls()
+#
+# def export_playlist_tab(self) -> None:
+# """Export the current playlist to an m3u file"""
+#
+# if not self.visible_playlist_tab():
+# return
+#
+# with Session() as session:
+# playlist = Playlists.get_by_id(
+# session, self.visible_playlist_tab().playlist_id)
+# # Get output filename
+# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
+# self, 'Save Playlist',
+# directory=f"{playlist.name}.m3u",
+# filter="M3U files (*.m3u);;All files (*.*)"
+# )
+# if not pathspec:
+# return
+#
+# path: str = pathspec[0]
+# if not path.endswith(".m3u"):
+# path += ".m3u"
+#
+# with open(path, "w") as f:
+# # Required directive on first line
+# f.write("#EXTM3U\n")
+# for _, track in playlist.tracks.items():
+# f.write(
+# "#EXTINF:"
+# f"{int(track.duration / 1000)},"
+# f"{track.title} - "
+# f"{track.artist}"
+# "\n"
+# f"{track.path}"
+# "\n"
+# )
+#
+# def fade(self) -> None:
+# """Fade currently playing track"""
+#
+# log.debug("musicmuster:fade()", True)
+#
+# self.stop_playing(fade=True)
+#
+# def hide_played(self):
+# """Toggle hide played tracks"""
+#
+# if self.hide_played_tracks:
+# self.hide_played_tracks = False
+# self.btnHidePlayed.setText("Hide played")
+# else:
+# self.hide_played_tracks = True
+# self.btnHidePlayed.setText("Show played")
+# if self.current_track_playlist_tab:
+# with Session() as session:
+# self.current_track_playlist_tab.update_display(session)
+#
+# def import_track(self) -> None:
+# """Import track file"""
+#
+# dlg = QFileDialog()
+# dlg.setFileMode(QFileDialog.ExistingFiles)
+# dlg.setViewMode(QFileDialog.Detail)
+# dlg.setDirectory(Config.IMPORT_DESTINATION)
+# dlg.setNameFilter("Music files (*.flac *.mp3)")
+#
+# if dlg.exec_():
+# with Session() as session:
+# txt: str = ""
+# new_tracks = []
+# for fname in dlg.selectedFiles():
+# tags = helpers.get_tags(fname)
+# new_tracks.append((fname, tags))
+# title = tags['title']
+# artist = tags['artist']
+# possible_matches = Tracks.search_titles(session, title)
+# if possible_matches:
+# txt += 'Similar to new track '
+# txt += f'"{title}" by "{artist} ({fname})":\n\n'
+# for track in possible_matches:
+# txt += f' "{track.title}" by {track.artist}'
+# txt += f' ({track.path})\n'
+# txt += "\n"
+# # Check whether to proceed if there were potential matches
+# if txt:
+# txt += "Proceed with import?"
+# result = QMessageBox.question(self,
+# "Possible duplicates",
+# txt,
+# QMessageBox.Ok,
+# QMessageBox.Cancel
+# )
+# if result == QMessageBox.Cancel:
+# return
+#
+# # Import in separate thread
+# thread = threading.Thread(target=self._import_tracks,
+# args=(new_tracks,))
+# thread.start()
+#
+# def _import_tracks(self, tracks: list):
+# """
+# Import passed files. Don't use parent session as that may be invalid
+# by the time we need it.
+# """
+#
+# with Session() as session:
+# for (fname, tags) in tracks:
+# track = create_track_from_file(session, fname, tags=tags)
+# # Add to playlist on screen
+# # If we don't specify "repaint=False", playlist will
+# # also be saved to database
+# self.visible_playlist_tab().insert_track(session, track)
+
+ def _load_last_playlists(self) -> None:
+ """Load the playlists that were open when the last session closed"""
+
+ with Session() as session:
+ for playlist in Playlists.get_open(session):
+ self.create_playlist_tab(session, playlist)
+ playlist.mark_open(session)
+
+ def move_selected(self) -> None:
+ """
+ Move selected rows to another playlist
+
+ Actions required:
+ - identify destination playlist
+ - update playlist for the rows in the database
+ - remove them from the display
+ - update destination playlist display if loaded
+ """
+
+ # Identify destination playlist
+ with Session() as session:
+ visible_tab = self.visible_playlist_tab()
+ source_playlist = visible_tab.playlist_id
+ playlists = []
+ for playlist in Playlists.get_all(session):
+ if playlist.id == source_playlist:
+ continue
+ else:
+ playlists.append(playlist)
+
+ # Get destination playlist id
+ dlg = SelectPlaylistDialog(self, playlists=playlists,
+ session=session)
+ dlg.exec()
+ if not dlg.playlist:
+ return
+ destination_playlist = dlg.playlist
+
+ # Update playlist for the rows in the database
+ plr_ids = visible_tab.get_selected_playlistrow_ids()
+ PlaylistRows.move_to_playlist(
+ session, plr_ids, destination_playlist.id
+ )
+
+ # Remove moved rows from display
+ visible_tab.remove_selected_rows()
+
+ # Update destination playlist_tab if visible (if not visible, it
+ # will be re-populated when it is opened)
+ destination_visible_playlist_tab = None
+ for tab in range(self.tabPlaylist.count()):
+ # Non-playlist tabs won't have a 'playlist_id' attribute
+ if not hasattr(self.tabPlaylist.widget(tab), 'playlist_id'):
+ continue
+ if self.tabPlaylist.widget(tab).playlist_id == dlg.playlist.id:
+ destination_visible_playlist_tab = (
+ self.tabPlaylist.widget(tab))
+ break
+ if destination_visible_playlist_tab:
+ destination_visible_playlist_tab.populate(
+ session, dlg.playlist.id)
+
+ def open_info_tabs(self) -> None:
+ """
+ Ensure we have info tabs for next and current track titles
+ """
+
+ title_list: List[str] = []
+
+ if self.previous_track:
+ title_list.append(self.previous_track.title)
+ if self.current_track:
+ title_list.append(self.current_track.title)
+ if self.next_track:
+ title_list.append(self.next_track.title)
+
+ for title in title_list:
+ if title in self.info_tabs.keys():
+ # We already have a tab for this track
+ continue
+ if len(self.info_tabs) >= Config.MAX_log.info_TABS:
+ # Find an unneeded info tab
+ try:
+ old_title = list(
+ set(self.info_tabs.keys()) - set(title_list)
+ )[0]
+ except IndexError:
+ log.debug(
+ f"ensure_info_tabs({title_list}): unable to add "
+ f"{title=}"
+ )
+ return
+ # Assign redundant widget a new title
+ widget = self.info_tabs[title] = self.info_tabs[old_title]
+ idx = self.tabPlaylist.indexOf(widget)
+ self.tabPlaylist.setTabText(
+ idx, title[:Config.log.info_TAB_TITLE_LENGTH])
+ del self.info_tabs[old_title]
+ else:
+ # Create a new tab for this title
+ widget = self.info_tabs[title] = QWebView()
+ widget.setZoomFactor(Config.WEB_ZOOM_FACTOR)
+ self.tabPlaylist.addTab(
+ widget, title[:Config.log.info_TAB_TITLE_LENGTH])
+ txt = urllib.parse.quote_plus(title)
+ url = Config.log.info_TAB_URL % txt
+ widget.setUrl(QUrl(url))
+#
+# def play_next(self) -> None:
+# """
+# Play next track.
+#
+# Actions required:
+# - If there is no next track set, return.
+# - If there's currently a track playing, fade it.
+# - Move next track to current track.
+# - Update record of current track playlist_tab
+# - If current track on different playlist_tab to last, reset
+# last track playlist_tab colour
+# - Set current track playlist_tab colour
+# - Restore volume if -3dB active
+# - Play (new) current track.
+# - Tell database to record it as played
+# - Tell playlist track is now playing
+# - Disable play next controls
+# - Update headers
+# - Update clocks
+# """
+#
+# log.debug(
+# "musicmuster.play_next(), "
+# f"next_track={self.next_track.title if self.next_track else None} "
+# "current_track="
+# f"{self.current_track.title if self.current_track else None}",
+# True
+# )
+#
+# # If there is no next track set, return.
+# if not self.next_track:
+# log.debug("musicmuster.play_next(): no next track selected", True)
+# return
+#
+# with Session() as session:
+# # If there's currently a track playing, fade it.
+# self.stop_playing(fade=True)
+#
+# # Move next track to current track.
+# self.current_track = self.next_track
+# self.next_track = None
+#
+# # If current track on different playlist_tab to last, reset
+# # last track playlist_tab colour
+# # Set current track playlist_tab colour
+# if self.current_track_playlist_tab != self.next_track_playlist_tab:
+# self.set_tab_colour(self.current_track_playlist_tab,
+# QColor(Config.COLOUR_NORMAL_TAB))
+#
+# # Update record of current track playlist_tab
+# self.current_track_playlist_tab = self.next_track_playlist_tab
+# self.next_track_playlist_tab = None
+#
+# # Set current track playlist_tab colour
+# self.set_tab_colour(self.current_track_playlist_tab,
+# QColor(Config.COLOUR_CURRENT_TAB))
+#
+# # Restore volume if -3dB active
+# if self.btnDrop3db.isChecked():
+# self.btnDrop3db.setChecked(False)
+#
+# # Play (new) current track
+# start_at = datetime.now()
+# self.music.play(self.current_track.path)
+#
+# # Tell database to record it as played
+# Playdates(session, self.current_track.id)
+#
+# # Set last_played date
+# Tracks.update_lastplayed(session, self.current_track.id)
+#
+# # Tell playlist track is now playing
+# self.current_track_playlist_tab.play_started(session)
+#
+# # Disable play next controls
+# self.disable_play_next_controls()
+#
+# # Update headers
+# self.update_headers()
+#
+# # Update clocks
+# self.label_track_length.setText(
+# helpers.ms_to_mmss(self.current_track.duration)
+# )
+# fade_at = self.current_track.fade_at
+# silence_at = self.current_track.silence_at
+# length = self.current_track.duration
+# self.label_fade_length.setText(
+# helpers.ms_to_mmss(silence_at - fade_at))
+# self.label_start_time.setText(
+# start_at.strftime(Config.TRACK_TIME_FORMAT))
+# end_at = start_at + timedelta(
+# milliseconds=self.current_track.duration)
+# self.label_end_time.setText(
+# end_at.strftime(Config.TRACK_TIME_FORMAT))
+#
+# def search_database(self) -> None:
+# """Show dialog box to select and cue track from database"""
+#
+# with Session() as session:
+# dlg = DbDialog(self, session)
+# dlg.exec()
+#
+# def search_playlist(self):
+# """Show text box to search playlist"""
+#
+# self.disable_play_next_controls()
+# self.txtSearch.setHidden(False)
+# self.txtSearch.setFocus()
+#
+# def search_playlist_return(self):
+# """Close off search box when return pressed"""
+#
+# self.txtSearch.setText("")
+# self.txtSearch.setHidden(True)
+# self.enable_play_next_controls()
+# self.visible_playlist_tab().set_filter("")
+#
+# def search_playlist_update(self):
+# """Update search when search string changes"""
+#
+# self.visible_playlist_tab().set_filter(self.txtSearch.text())
+#
+# def open_playlist(self):
+# with Session() as session:
+# playlists = Playlists.get_closed(session)
+# dlg = SelectPlaylistDialog(self, playlists=playlists,
+# session=session)
+# dlg.exec()
+# playlist = dlg.playlist
+# if playlist:
+# playlist.mark_open(session)
+# self.create_playlist_tab(session, playlist)
+#
+# def select_next_row(self) -> None:
+# """Select next or first row in playlist"""
+#
+# self.visible_playlist_tab().select_next_row()
+#
+# def select_played(self) -> None:
+# """Select all played tracks in playlist"""
+#
+# self.visible_playlist_tab().select_played_tracks()
+#
+# def select_previous_row(self) -> None:
+# """Select previous or first row in playlist"""
+#
+# self.visible_playlist_tab().select_previous_row()
+#
+# def select_unplayed(self) -> None:
+# """Select all unplayed tracks in playlist"""
+#
+# self.visible_playlist_tab().select_unplayed_tracks()
+
+ def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
+ """
+ Find the tab containing the widget and set the text colour
+ """
+
+ idx = self.tabPlaylist.indexOf(widget)
+ self.tabPlaylist.tabBar().setTabTextColor(idx, colour)
+#
+# def song_info_search(self) -> None:
+# """
+# Open browser tab for Wikipedia, searching for
+# the first that exists of:
+# - selected track
+# - next track
+# - current track
+# """
+#
+# title: Optional[str] = self.visible_playlist_tab().get_selected_title()
+# if not title:
+# if self.next_track:
+# title = self.next_track.title
+# if not title:
+# if self.current_track:
+# title = self.current_track.title
+# if title:
+# txt = urllib.parse.quote_plus(title)
+# url = Config.log.info_TAB_URL % txt
+# webbrowser.open(url, new=2)
+#
+# def stop(self) -> None:
+# """Stop playing immediately"""
+#
+# log.debug("musicmuster.stop()")
+#
+# self.stop_playing(fade=False)
+#
+# def stop_playing(self, fade=True) -> None:
+# """
+# Stop playing current track
+#
+# Actions required:
+# - Return if not playing
+# - Stop/fade track
+# - Reset playlist_tab colour
+# - Run end-of-track actions
+# """
+#
+# log.debug(f"musicmuster.stop_playing({fade=})", True)
+#
+# # Return if not playing
+# if not self.playing:
+# log.debug("musicmuster.stop_playing(): not playing", True)
+# return
+#
+# # Stop/fade track
+# self.previous_track_position = self.music.get_position()
+# if fade:
+# log.debug("musicmuster.stop_playing(): fading music", True)
+# self.music.fade()
+# else:
+# log.debug("musicmuster.stop_playing(): stopping music", True)
+# self.music.stop()
+#
+# # Reset playlist_tab colour
+# if self.current_track_playlist_tab == self.next_track_playlist_tab:
+# self.set_tab_colour(self.current_track_playlist_tab,
+# QColor(Config.COLOUR_NEXT_TAB))
+# else:
+# self.set_tab_colour(self.current_track_playlist_tab,
+# QColor(Config.COLOUR_NORMAL_TAB))
+#
+# # Run end-of-track actions
+# self.end_of_track_actions()
+
+ def this_is_the_next_track(self, playlist_tab: PlaylistTab,
+ track: Tracks, session) -> None:
+ """
+ This is notification from a playlist tab that it holds the next
+ track to be played.
+
+ Actions required:
+ - Clear next track if on other tab
+ - Reset tab colour if on other tab
+ - Note next playlist tab
+ - Set next playlist_tab tab colour
+ - Note next track
+ - Update headers
+ - Populate ‘info’ tabs
+
+ """
+
+ # Clear next track if on another tab
+ if self.next_track_playlist_tab != playlist_tab:
+ # We need to reset the ex-next-track playlist
+ if self.next_track_playlist_tab:
+ self.next_track_playlist_tab.clear_next(session)
+
+ # Reset tab colour if on other tab
+ if (self.next_track_playlist_tab !=
+ self.current_track_playlist_tab):
+ self.set_tab_colour(
+ self.next_track_playlist_tab,
+ QColor(Config.COLOUR_NORMAL_TAB))
+
+ # Note next playlist tab
+ self.next_track_playlist_tab = playlist_tab
+
+ # Set next playlist_tab tab colour if it isn't the
+ # currently-playing tab
+ if (self.next_track_playlist_tab !=
+ self.current_track_playlist_tab):
+ self.set_tab_colour(
+ self.next_track_playlist_tab,
+ QColor(Config.COLOUR_NEXT_TAB))
+
+ # Note next track
+ self.next_track = TrackData(track)
+
+ # Update headers
+ self.update_headers()
+
+ # Populate 'info' tabs
+ self.open_info_tabs()
+#
+# def tick(self) -> None:
+# """
+# Carry out clock tick actions.
+#
+# The Time of Day clock is updated every tick (500ms).
+#
+# All other timers are updated every second. As the timers have a
+# one-second resolution, updating every 500ms can result in some
+# timers updating and then, 500ms later, other timers updating. That
+# looks odd.
+#
+# Actions required:
+# - Update TOD clock
+# - If track is playing, update track clocks time and colours
+# - Else: run stop_track
+# """
+#
+# # Update TOD clock
+# self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
+#
+# self.even_tick = not self.even_tick
+# if not self.even_tick:
+# return
+#
+# # If track is playing, update track clocks time and colours
+# if self.music.player and self.music.playing():
+# self.playing = True
+# playtime: int = self.music.get_playtime()
+# time_to_fade: int = (self.current_track.fade_at - playtime)
+# time_to_silence: int = (
+# self.current_track.silence_at - playtime)
+# time_to_end: int = (self.current_track.duration - playtime)
+#
+# # Elapsed time
+# if time_to_end < 500:
+# self.label_elapsed_timer.setText(
+# helpers.ms_to_mmss(playtime)
+# )
+# else:
+# self.label_elapsed_timer.setText(
+# helpers.ms_to_mmss(playtime)
+# )
+#
+# # Time to fade
+# self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
+#
+# # If silent in the next 5 seconds, put warning colour on
+# # time to silence box and enable play controls
+# if time_to_silence <= 5500:
+# self.frame_silent.setStyleSheet(
+# f"background: {Config.COLOUR_ENDING_TIMER}"
+# )
+# self.enable_play_next_controls()
+# # Set warning colour on time to silence box when fade starts
+# elif time_to_fade <= 500:
+# self.frame_silent.setStyleSheet(
+# f"background: {Config.COLOUR_WARNING_TIMER}"
+# )
+# # Five seconds before fade starts, set warning colour on
+# # time to silence box and enable play controls
+# elif time_to_fade <= 5500:
+# self.frame_fade.setStyleSheet(
+# f"background: {Config.COLOUR_WARNING_TIMER}"
+# )
+# self.enable_play_next_controls()
+# else:
+# self.frame_silent.setStyleSheet("")
+# self.frame_fade.setStyleSheet("")
+#
+# self.label_silent_timer.setText(
+# helpers.ms_to_mmss(time_to_silence)
+# )
+#
+# # Time to end
+# self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
+#
+# else:
+# if self.playing:
+# self.stop_playing()
+
+ def update_headers(self) -> None:
+ """
+ Update last / current / next track headers
+ """
+
+ try:
+ self.hdrPreviousTrack.setText(
+ f"{self.previous_track.title} - {self.previous_track.artist}"
+ )
+ except (AttributeError, DetachedInstanceError):
+ self.hdrPreviousTrack.setText("")
+
+ try:
+ self.hdrCurrentTrack.setText(
+ f"{self.current_track.title} - {self.current_track.artist}"
+ )
+ except (AttributeError, DetachedInstanceError):
+ self.hdrCurrentTrack.setText("")
+
+ try:
+ self.hdrNextTrack.setText(
+ f"{self.next_track.title} - {self.next_track.artist}"
+ )
+ except (AttributeError, DetachedInstanceError):
+ self.hdrNextTrack.setText("")
+#
+#
+# class DbDialog(QDialog):
+# """Select track from database"""
+#
+# def __init__(self, parent, session): # review
+# super().__init__(parent)
+# self.session = session
+# self.ui = Ui_Dialog()
+# self.ui.setupUi(self)
+# self.ui.btnAdd.clicked.connect(self.add_selected)
+# self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
+# self.ui.btnClose.clicked.connect(self.close)
+# self.ui.matchList.itemDoubleClicked.connect(self.double_click)
+# self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
+# self.ui.radioTitle.toggled.connect(self.title_artist_toggle)
+# self.ui.searchString.textEdited.connect(self.chars_typed)
+#
+# record = Settings.get_int_settings(self.session, "dbdialog_width")
+# width = record.f_int or 800
+# record = Settings.get_int_settings(self.session, "dbdialog_height")
+# height = record.f_int or 600
+# self.resize(width, height)
+#
+# def __del__(self): # review
+# record = Settings.get_int_settings(self.session, "dbdialog_height")
+# if record.f_int != self.height():
+# record.update(self.session, {'f_int': self.height()})
+#
+# record = Settings.get_int_settings(self.session, "dbdialog_width")
+# if record.f_int != self.width():
+# record.update(self.session, {'f_int': self.width()})
+#
+# def add_selected(self): # review
+# if not self.ui.matchList.selectedItems():
+# return
+#
+# item = self.ui.matchList.currentItem()
+# track = item.data(Qt.UserRole)
+# self.add_track(track)
+#
+# def add_selected_and_close(self): # review
+# self.add_selected()
+# self.close()
+#
+# def title_artist_toggle(self): # review
+# """
+# Handle switching between searching for artists and searching for
+# titles
+# """
+#
+# # Logic is handled already in chars_typed(), so just call that.
+# self.chars_typed(self.ui.searchString.text())
+#
+# def chars_typed(self, s): # review
+# if len(s) > 0:
+# if self.ui.radioTitle.isChecked():
+# matches = Tracks.search_titles(self.session, s)
+# else:
+# matches = Tracks.search_artists(self.session, s)
+# self.ui.matchList.clear()
+# if matches:
+# for track in matches:
+# t = QListWidgetItem()
+# t.setText(
+# f"{track.title} - {track.artist} "
+# f"[{helpers.ms_to_mmss(track.duration)}] "
+# f"({helpers.get_relative_date(track.lastplayed)})"
+# )
+# t.setData(Qt.UserRole, track)
+# self.ui.matchList.addItem(t)
+#
+# def double_click(self, entry): # review
+# track = entry.data(Qt.UserRole)
+# self.add_track(track)
+# # Select search text to make it easier for next search
+# self.select_searchtext()
+#
+# def add_track(self, track): # review
+# # Add to playlist on screen
+# self.parent().visible_playlist_tab().insert_track(
+# self.session, track)
+# # Commit session to get correct row numbers if more tracks added
+# self.session.commit()
+# # Select search text to make it easier for next search
+# self.select_searchtext()
+#
+# def select_searchtext(self): # review
+# self.ui.searchString.selectAll()
+# self.ui.searchString.setFocus()
+#
+# def selection_changed(self): # review
+# if not self.ui.matchList.selectedItems():
+# return
+#
+# item = self.ui.matchList.currentItem()
+# track = item.data(Qt.UserRole)
+# self.ui.dbPath.setText(track.path)
+#
+#
+# class DownloadCSV(QDialog):
+# def __init__(self, parent=None):
+# super().__init__(parent)
+#
+# self.ui = Ui_DateSelect()
+# self.ui.setupUi(self)
+# self.ui.dateTimeEdit.setDate(QDate.currentDate())
+# self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
+# self.ui.buttonBox.accepted.connect(self.accept)
+# self.ui.buttonBox.rejected.connect(self.reject)
+
+
+class SelectPlaylistDialog(QDialog):
+ def __init__(self, parent=None, playlists=None, session=None):
+ super().__init__(parent)
+
+ if playlists is None:
+ return
+ self.ui = Ui_dlgSelectPlaylist()
+ self.ui.setupUi(self)
+ self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
+ self.ui.buttonBox.accepted.connect(self.open)
+ self.ui.buttonBox.rejected.connect(self.close)
+ self.session = session
+ self.playlist = None
+ self.plid = None
+
+ record = Settings.get_int_settings(
+ self.session, "select_playlist_dialog_width")
+ width = record.f_int or 800
+ record = Settings.get_int_settings(
+ self.session, "select_playlist_dialog_height")
+ height = record.f_int or 600
+ self.resize(width, height)
+
+ for playlist in playlists:
+ p = QListWidgetItem()
+ p.setText(playlist.name)
+ p.setData(Qt.UserRole, playlist)
+ self.ui.lstPlaylists.addItem(p)
+
+ def __del__(self): # review
+ record = Settings.get_int_settings(
+ self.session, "select_playlist_dialog_height")
+ if record.f_int != self.height():
+ record.update(self.session, {'f_int': self.height()})
+
+ record = Settings.get_int_settings(
+ self.session, "select_playlist_dialog_width")
+ if record.f_int != self.width():
+ record.update(self.session, {'f_int': self.width()})
+
+ def list_doubleclick(self, entry): # review
+ self.playlist = entry.data(Qt.UserRole)
+ self.accept()
+
+ def open(self): # review
+ if self.ui.lstPlaylists.selectedItems():
+ item = self.ui.lstPlaylists.currentItem()
+ self.playlist = item.data(Qt.UserRole)
+ self.accept()
+
+
+if __name__ == "__main__":
+ # p = argparse.ArgumentParser()
+ # # Only allow at most one option to be specified
+ # group = p.add_mutually_exclusive_group()
+ # group.add_argument('-u', '--update',
+ # action="store_true", dest="update",
+ # default=False, help="Update database")
+ # # group.add_argument('-f', '--full-update',
+ # # action="store_true", dest="full_update",
+ # # default=False, help="Update database")
+ # # group.add_argument('-i', '--import', dest="fname", help="Input file")
+ # args = p.parse_args()
+ #
+ # # Run as required
+ # if args.update:
+ # log.debug("Updating database")
+ # with Session() as session:
+ # update_db(session)
+ # # elif args.full_update:
+ # # log.debug("Full update of database")
+ # # with Session() as session:
+ # # full_update_db(session)
+ # else:
+ # # Normal run
+ try:
+ Base.metadata.create_all(engine)
+ app = QApplication(sys.argv)
+ win = Window()
+ win.show()
+ sys.exit(app.exec())
+ except Exception:
+ msg = "Unhandled Exception caught by musicmuster.main()"
+ log.exception(msg, exc_info=True, stack_info=True)