WIP: carts

This commit is contained in:
Keith Edmunds 2022-10-15 17:42:37 +01:00
parent 4f3769ae38
commit 0b91cf7da4
11 changed files with 516 additions and 32 deletions

3
.envrc
View File

@ -6,6 +6,9 @@ if on_git_branch master; then
elif on_git_branch carts; then elif on_git_branch carts; then
export MM_ENV="DEVELOPMENT" export MM_ENV="DEVELOPMENT"
export MM_DB="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster_carts" export MM_DB="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster_carts"
elif on_git_branch newcarts; then
export MM_ENV="DEVELOPMENT"
export MM_DB="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster_carts"
else else
export MM_ENV="DEVELOPMENT" export MM_ENV="DEVELOPMENT"
export MM_DB="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster" export MM_DB="mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster"

View File

@ -42,6 +42,7 @@ prepend_sys_path = .
sqlalchemy.url = SET sqlalchemy.url = SET
# sqlalchemy.url = mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod # sqlalchemy.url = mysql+mysqldb://musicmuster:musicmuster@localhost/musicmuster_prod
# sqlalchemy.url = mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster # sqlalchemy.url = mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster
# sqlalchemy.url = mysql+mysqldb://dev_musicmuster:dev_musicmuster@localhost/dev_musicmuster_carts
[post_write_hooks] [post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run # post_write_hooks defines scripts or Python functions that are run

View File

@ -9,9 +9,13 @@ class Config(object):
BITRATE_LOW_THRESHOLD = 192 BITRATE_LOW_THRESHOLD = 192
BITRATE_OK_THRESHOLD = 300 BITRATE_OK_THRESHOLD = 300
CHECK_AUDACITY_AT_STARTUP = True CHECK_AUDACITY_AT_STARTUP = True
CART_DIRECTORY = "/home/kae/radio/CartTracks"
COLOUR_BITRATE_LOW = "#ffcdd2" COLOUR_BITRATE_LOW = "#ffcdd2"
COLOUR_BITRATE_MEDIUM = "#ffeb6f" COLOUR_BITRATE_MEDIUM = "#ffeb6f"
COLOUR_BITRATE_OK = "#dcedc8" COLOUR_BITRATE_OK = "#dcedc8"
COLOUR_CART_ERROR = "#dc3545"
COLOUR_CART_PLAYING = "#248f24"
COLOUR_CART_READY = "#ffc107"
COLOUR_CURRENT_HEADER = "#d4edda" COLOUR_CURRENT_HEADER = "#d4edda"
COLOUR_CURRENT_PLAYLIST = "#7eca8f" COLOUR_CURRENT_PLAYLIST = "#7eca8f"
COLOUR_CURRENT_TAB = "#248f24" COLOUR_CURRENT_TAB = "#248f24"

View File

@ -51,6 +51,68 @@ Base = declarative_base()
# Database classes # Database classes
class Carts(Base):
__tablename__ = 'carts'
id: int = Column(Integer, primary_key=True, autoincrement=True)
cart_number = Column(Integer, nullable=False, unique=True)
name = Column(String(256), index=True)
duration = Column(Integer, index=True)
path = Column(String(2048), index=False)
enabled = Column(Boolean, default=False, nullable=False)
def __repr__(self) -> str:
return (
f"<Carts(id={self.id}, cart={self.cart}, "
f"name={self.name}, path={self.path}>"
)
def __init__(self, session: Session, cart_number: int, name: str = None,
duration: int = None, path: str = None,
enabled: bool = True) -> None:
"""Create new cart"""
self.cart_number = cart_number
self.name = name
self.duration = duration
self.path = path
self.enabled = enabled
session.add(self)
session.commit()
@classmethod
def enabled_carts(cls, session: Session) -> List["Carts"]:
"""Return a list of all active carts"""
return (
session.execute(
select(Carts)
.where(Carts.enabled.is_(True))
.order_by(Carts.cart_number)
)
.scalars()
.all()
)
@classmethod
def get_or_create(cls, session: Session, cart_number: int) -> "Carts":
"""
Return cart with passed cart number, or create a record if
none exists.
"""
try:
return (
session.execute(
select(Carts)
.where(Carts.cart_number == cart_number)
).scalar_one()
)
except NoResultFound:
return Carts(session, cart_number)
class NoteColours(Base): class NoteColours(Base):
__tablename__ = 'notecolours' __tablename__ = 'notecolours'

View File

@ -9,8 +9,8 @@ import threading
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import List, Optional from typing import List, Optional
from PyQt5.QtCore import QDate, QEvent, Qt, QTime, QTimer from PyQt5.QtCore import QDate, QEvent, Qt, QSize, QTime, QTimer
from PyQt5.QtGui import QColor, QPalette from PyQt5.QtGui import QColor, QPalette, QFont
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QApplication,
QDialog, QDialog,
@ -21,6 +21,7 @@ from PyQt5.QtWidgets import (
QListWidgetItem, QListWidgetItem,
QMainWindow, QMainWindow,
QMessageBox, QMessageBox,
QPushButton,
) )
from dbconfig import engine, Session from dbconfig import engine, Session
@ -29,18 +30,20 @@ import music
from models import ( from models import (
Base, Base,
Carts,
Playdates, Playdates,
PlaylistRows, PlaylistRows,
Playlists, Playlists,
Settings, Settings,
Tracks Tracks
) )
from config import Config
from playlists import PlaylistTab from playlists import PlaylistTab
from sqlalchemy.orm.exc import DetachedInstanceError from sqlalchemy.orm.exc import DetachedInstanceError
from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore
from ui.dlg_search_database_ui import Ui_Dialog # type: ignore from ui.dlg_search_database_ui import Ui_Dialog # type: ignore
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
from ui.downloadcsv_ui import Ui_DateSelect # type: ignore from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
from config import Config
from ui.main_window_ui import Ui_MainWindow # type: ignore from ui.main_window_ui import Ui_MainWindow # type: ignore
from utilities import check_db, update_bitrates from utilities import check_db, update_bitrates
@ -90,36 +93,110 @@ class Window(QMainWindow, Ui_MainWindow):
self.statusbar.addWidget(self.txtSearch) self.statusbar.addWidget(self.txtSearch)
self.txtSearch.setHidden(True) self.txtSearch.setHidden(True)
self.hide_played_tracks = False self.hide_played_tracks = False
self.carts = {}
self.visible_playlist_tab: Callable[[], PlaylistTab] = \ self.visible_playlist_tab: Callable[[], PlaylistTab] = \
self.tabPlaylist.currentWidget self.tabPlaylist.currentWidget
self.load_last_playlists() self.load_last_playlists()
self.carts_init()
self.enable_play_next_controls() self.enable_play_next_controls()
self.timer.start(Config.TIMER_MS) self.timer.start(Config.TIMER_MS)
self.connect_signals_slots() self.connect_signals_slots()
def about(self) -> None: def cart_add(self) -> None:
"""Get git tag and database name""" """Add a cart"""
try: pass
git_tag = str(
subprocess.check_output( def cart_button(self, cart: Carts) -> QPushButton:
['git', 'describe'], stderr=subprocess.STDOUT """Create a cart pushbutton"""
)
).strip('\'b\\n') btn = QPushButton(self)
except subprocess.CalledProcessError as exc_info: btn.setEnabled(True)
git_tag = str(exc_info.output) btn.setMinimumSize(QSize(147, 61))
font = QFont()
font.setPointSize(14)
btn.setFont(font)
btn.setObjectName("cart_" + str(cart.cart_number))
btn.setText(cart.name)
btn.clicked.connect(self.cart_click)
# Insert button on left of cart space after existing buttons
self.horizontalLayout_Carts.insertWidget(len(self.carts), btn)
return btn
def cart_click(self) -> None:
"""Handle cart click"""
# print(f"cart_click({self.sender()=})")
# print(f"{self.carts[self.sender()]['name']}")
btn = self.sender()
cart = self.carts[btn]
if helpers.file_is_readable(cart['path']):
cart['player'].play()
self.carts[btn]['playing'] = True
colour = Config.COLOUR_CART_PLAYING
else:
colour = Config.COLOUR_CART_ERROR
btn.setStyleSheet("background-color: " + colour + ";\n")
def cart_edit(self, cart_number: int) -> None:
"""Edit carts"""
with Session() as session: with Session() as session:
dbname = session.bind.engine.url.database cart = Carts.get_or_create(session, cart_number)
dlg = CartDialog(self, cart, cart_number)
if dlg.exec():
name = dlg.ui.lineEditName.text()
if not name:
QMessageBox.warning(self, "Error", "Name required")
return
path = dlg.path
if not path:
QMessageBox.warning(self, "Error", "Filename required")
return
cart.name = name
cart.path = path
if cart.path:
tags = helpers.get_tags(cart.path)
cart.duration = tags['duration']
cart.enabled = dlg.ui.chkEnabled.isChecked()
session.add(cart)
session.commit()
QMessageBox.information( def carts_init(self) -> None:
self, """Initialse carts data structures"""
"About",
f"MusicMuster {git_tag}\n\nDatabase: {dbname}", with Session() as session:
QMessageBox.Ok for cart in Carts.enabled_carts(session):
) btn = self.cart_button(cart)
self.carts[btn] = {}
self.carts[btn]['name'] = cart.name
self.carts[btn]['duration'] = cart.duration
self.carts[btn]['path'] = cart.path
self.carts[btn]['playing'] = False
player = self.music.VLC.media_player_new(cart.path)
player.audio_set_volume(Config.VOLUME_VLC_DEFAULT)
self.carts[btn]['player'] = player
if helpers.file_is_readable(cart.path):
colour = Config.COLOUR_CART_READY
else:
colour = Config.COLOUR_CART_ERROR
btn.setStyleSheet("background-color: " + colour + ";\n")
def cart_tick(self) -> None:
"""Cart clock actions"""
for btn, cart in self.carts.items():
if cart['playing']:
if not cart['player'].is_playing():
# Cart has finished playing
cart['playing'] = False
cart['player'].set_position(0)
colour = Config.COLOUR_CART_READY
btn.setStyleSheet("background-color: " + colour + ";\n")
def clear_selection(self) -> None: def clear_selection(self) -> None:
""" Clear selected row""" """ Clear selected row"""
@ -205,8 +282,10 @@ class Window(QMainWindow, Ui_MainWindow):
def connect_signals_slots(self) -> None: def connect_signals_slots(self) -> None:
self.action_About.triggered.connect(self.about) self.action_About.triggered.connect(self.about)
self.actionAdd_cart.triggered.connect(self.cart_add)
self.action_Clear_selection.triggered.connect(self.clear_selection) self.action_Clear_selection.triggered.connect(self.clear_selection)
self.actionDebug.triggered.connect(self.debug) self.actionDebug.triggered.connect(self.debug)
self.actionAdd_cart.triggered.connect(self.cart_add)
self.actionClosePlaylist.triggered.connect(self.close_playlist_tab) self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
self.actionDownload_CSV_of_played_tracks.triggered.connect( self.actionDownload_CSV_of_played_tracks.triggered.connect(
self.download_played_tracks) self.download_played_tracks)
@ -438,6 +517,28 @@ class Window(QMainWindow, Ui_MainWindow):
self.stop_playing(fade=True) self.stop_playing(fade=True)
def about(self) -> None:
"""Get git tag and database name"""
try:
git_tag = str(
subprocess.check_output(
['git', 'describe'], stderr=subprocess.STDOUT
)
).strip('\'b\\n')
except subprocess.CalledProcessError as exc_info:
git_tag = str(exc_info.output)
with Session() as session:
dbname = session.bind.engine.url.database
QMessageBox.information(
self,
"About",
f"MusicMuster {git_tag}\n\nDatabase: {dbname}",
QMessageBox.Ok
)
def get_one_track(self, session: Session) -> Optional[Tracks]: def get_one_track(self, session: Session) -> Optional[Tracks]:
"""Show dialog box to select one track and return it to caller""" """Show dialog box to select one track and return it to caller"""
@ -987,6 +1088,8 @@ class Window(QMainWindow, Ui_MainWindow):
# Update TOD clock # Update TOD clock
self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT)) self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
# Update carts
self.cart_tick()
self.even_tick = not self.even_tick self.even_tick = not self.even_tick
if not self.even_tick: if not self.even_tick:
@ -1079,6 +1182,44 @@ class Window(QMainWindow, Ui_MainWindow):
self.hdrNextTrack.setText("") self.hdrNextTrack.setText("")
class CartDialog(QDialog):
"""Edit cart details"""
def __init__(self, parent: QMainWindow,
cart: Optional[Carts],
cart_number: int) -> None:
"""
Manage carts
"""
super().__init__(parent)
self.ui = Ui_DialogCartEdit()
self.ui.setupUi(self)
self.ui.chkEnabled.setChecked(True)
self.path = ""
self.ui.windowTitle = "Edit Cart " + str(cart_number)
if cart:
self.ui.lineEditName.setText(cart.name)
self.path = cart.path
self.ui.lblPath.setText(self.path)
self.ui.btnFile.clicked.connect(self.choose_file)
def choose_file(self) -> None:
"""File select"""
dlg = QFileDialog()
dlg.setFileMode(QFileDialog.ExistingFile)
dlg.setViewMode(QFileDialog.Detail)
dlg.setDirectory(Config.CART_DIRECTORY)
dlg.setNameFilter("Music files (*.flac *.mp3)")
if dlg.exec_():
self.path = dlg.selectedFiles()[0]
self.ui.lblPath.setText(self.path)
class DbDialog(QDialog): class DbDialog(QDialog):
"""Select track from database""" """Select track from database"""

142
app/ui/dlg_Cart.ui Normal file
View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogCartEdit</class>
<widget class="QDialog" name="DialogCartEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>171</height>
</rect>
</property>
<property name="windowTitle">
<string>Carts</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>200</x>
<y>140</y>
<width>171</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>76</width>
<height>19</height>
</rect>
</property>
<property name="text">
<string>&amp;Name:</string>
</property>
<property name="buddy">
<cstring>lineEditName</cstring>
</property>
</widget>
<widget class="QLineEdit" name="lineEditName">
<property name="geometry">
<rect>
<x>90</x>
<y>60</y>
<width>291</width>
<height>27</height>
</rect>
</property>
<property name="inputMask">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblPath">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>xxx</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="btnFile">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>100</width>
<height>27</height>
</rect>
</property>
<property name="text">
<string>&amp;File</string>
</property>
</widget>
<widget class="QCheckBox" name="chkEnabled">
<property name="geometry">
<rect>
<x>280</x>
<y>100</y>
<width>104</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>&amp;Enabled</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DialogCartEdit</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DialogCartEdit</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -13,8 +13,8 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Dialog</string> <string>Dialog</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout_2">
<item> <item row="0" column="0">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
@ -121,14 +121,8 @@
</item> </item>
</layout> </layout>
</item> </item>
<item> <item row="3" column="0">
<widget class="QLabel" name="dbPath"> <widget class="QLabel" name="dbPath">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>

54
app/ui/dlg_cart_ui.py Normal file
View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'app/ui/dlg_Cart.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# 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.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_DialogCartEdit(object):
def setupUi(self, DialogCartEdit):
DialogCartEdit.setObjectName("DialogCartEdit")
DialogCartEdit.resize(400, 171)
self.buttonBox = QtWidgets.QDialogButtonBox(DialogCartEdit)
self.buttonBox.setGeometry(QtCore.QRect(200, 140, 171, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(DialogCartEdit)
self.label.setGeometry(QtCore.QRect(10, 60, 76, 19))
self.label.setObjectName("label")
self.lineEditName = QtWidgets.QLineEdit(DialogCartEdit)
self.lineEditName.setGeometry(QtCore.QRect(90, 60, 291, 27))
self.lineEditName.setInputMask("")
self.lineEditName.setObjectName("lineEditName")
self.lblPath = QtWidgets.QLabel(DialogCartEdit)
self.lblPath.setGeometry(QtCore.QRect(10, 10, 371, 41))
self.lblPath.setTextFormat(QtCore.Qt.PlainText)
self.lblPath.setWordWrap(True)
self.lblPath.setObjectName("lblPath")
self.btnFile = QtWidgets.QPushButton(DialogCartEdit)
self.btnFile.setGeometry(QtCore.QRect(10, 100, 100, 27))
self.btnFile.setObjectName("btnFile")
self.chkEnabled = QtWidgets.QCheckBox(DialogCartEdit)
self.chkEnabled.setGeometry(QtCore.QRect(280, 100, 104, 25))
self.chkEnabled.setObjectName("chkEnabled")
self.label.setBuddy(self.lineEditName)
self.retranslateUi(DialogCartEdit)
self.buttonBox.accepted.connect(DialogCartEdit.accept) # type: ignore
self.buttonBox.rejected.connect(DialogCartEdit.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(DialogCartEdit)
def retranslateUi(self, DialogCartEdit):
_translate = QtCore.QCoreApplication.translate
DialogCartEdit.setWindowTitle(_translate("DialogCartEdit", "Carts"))
self.label.setText(_translate("DialogCartEdit", "&Name:"))
self.lblPath.setText(_translate("DialogCartEdit", "xxx"))
self.btnFile.setText(_translate("DialogCartEdit", "&File"))
self.chkEnabled.setText(_translate("DialogCartEdit", "&Enabled"))

View File

@ -291,6 +291,23 @@ padding-left: 8px;</string>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_Carts">
<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>
</layout>
</item>
<item row="3" column="0">
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -325,7 +342,7 @@ padding-left: 8px;</string>
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QFrame" name="frame_5"> <widget class="QFrame" name="frame_5">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: rgb(192, 191, 188)</string> <string notr="true">background-color: rgb(192, 191, 188)</string>
@ -844,10 +861,17 @@ padding-left: 8px;</string>
<addaction name="action_About"/> <addaction name="action_About"/>
<addaction name="actionDebug"/> <addaction name="actionDebug"/>
</widget> </widget>
<widget class="QMenu" name="menu_Carts">
<property name="title">
<string>&amp;Carts</string>
</property>
<addaction name="actionAdd_cart"/>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuPlaylist"/> <addaction name="menuPlaylist"/>
<addaction name="menuSearc_h"/> <addaction name="menuSearc_h"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
<addaction name="menu_Carts"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"> <widget class="QStatusBar" name="statusbar">
<property name="enabled"> <property name="enabled">
@ -1132,6 +1156,11 @@ padding-left: 8px;</string>
<string>Debug</string> <string>Debug</string>
</property> </property>
</action> </action>
<action name="actionAdd_cart">
<property name="text">
<string>Add &amp;cart...</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -158,6 +158,11 @@ class Ui_MainWindow(object):
self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_4.setObjectName("frame_4") self.frame_4.setObjectName("frame_4")
self.gridLayout_4.addWidget(self.frame_4, 1, 0, 1, 1) self.gridLayout_4.addWidget(self.frame_4, 1, 0, 1, 1)
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout()
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_Carts.addItem(spacerItem)
self.gridLayout_4.addLayout(self.horizontalLayout_Carts, 2, 0, 1, 1)
self.splitter = QtWidgets.QSplitter(self.centralwidget) self.splitter = QtWidgets.QSplitter(self.centralwidget)
self.splitter.setOrientation(QtCore.Qt.Vertical) self.splitter.setOrientation(QtCore.Qt.Vertical)
self.splitter.setObjectName("splitter") self.splitter.setObjectName("splitter")
@ -171,7 +176,7 @@ class Ui_MainWindow(object):
self.tabInfolist.setTabsClosable(True) self.tabInfolist.setTabsClosable(True)
self.tabInfolist.setMovable(True) self.tabInfolist.setMovable(True)
self.tabInfolist.setObjectName("tabInfolist") self.tabInfolist.setObjectName("tabInfolist")
self.gridLayout_4.addWidget(self.splitter, 2, 0, 1, 1) self.gridLayout_4.addWidget(self.splitter, 3, 0, 1, 1)
self.frame_5 = QtWidgets.QFrame(self.centralwidget) self.frame_5 = QtWidgets.QFrame(self.centralwidget)
self.frame_5.setStyleSheet("background-color: rgb(192, 191, 188)") self.frame_5.setStyleSheet("background-color: rgb(192, 191, 188)")
self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)
@ -363,7 +368,7 @@ class Ui_MainWindow(object):
self.gridLayout_3.addWidget(self.btnHidePlayed, 1, 0, 1, 1) self.gridLayout_3.addWidget(self.btnHidePlayed, 1, 0, 1, 1)
self.horizontalLayout.addWidget(self.frame_3) self.horizontalLayout.addWidget(self.frame_3)
self.horizontalLayout_2.addLayout(self.horizontalLayout) self.horizontalLayout_2.addLayout(self.horizontalLayout)
self.gridLayout_4.addWidget(self.frame_5, 3, 0, 1, 1) self.gridLayout_4.addWidget(self.frame_5, 4, 0, 1, 1)
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, 1280, 26)) self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 26))
@ -376,6 +381,8 @@ class Ui_MainWindow(object):
self.menuSearc_h.setObjectName("menuSearc_h") self.menuSearc_h.setObjectName("menuSearc_h")
self.menuHelp = QtWidgets.QMenu(self.menubar) self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp") self.menuHelp.setObjectName("menuHelp")
self.menu_Carts = QtWidgets.QMenu(self.menubar)
self.menu_Carts.setObjectName("menu_Carts")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setEnabled(True) self.statusbar.setEnabled(True)
@ -482,6 +489,8 @@ class Ui_MainWindow(object):
self.actionNew_from_template.setObjectName("actionNew_from_template") self.actionNew_from_template.setObjectName("actionNew_from_template")
self.actionDebug = QtWidgets.QAction(MainWindow) self.actionDebug = QtWidgets.QAction(MainWindow)
self.actionDebug.setObjectName("actionDebug") self.actionDebug.setObjectName("actionDebug")
self.actionAdd_cart = QtWidgets.QAction(MainWindow)
self.actionAdd_cart.setObjectName("actionAdd_cart")
self.menuFile.addAction(self.actionNewPlaylist) self.menuFile.addAction(self.actionNewPlaylist)
self.menuFile.addAction(self.actionOpenPlaylist) self.menuFile.addAction(self.actionOpenPlaylist)
self.menuFile.addAction(self.actionClosePlaylist) self.menuFile.addAction(self.actionClosePlaylist)
@ -521,10 +530,12 @@ class Ui_MainWindow(object):
self.menuSearc_h.addAction(self.actionSelect_previous_track) self.menuSearc_h.addAction(self.actionSelect_previous_track)
self.menuHelp.addAction(self.action_About) self.menuHelp.addAction(self.action_About)
self.menuHelp.addAction(self.actionDebug) self.menuHelp.addAction(self.actionDebug)
self.menu_Carts.addAction(self.actionAdd_cart)
self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuPlaylist.menuAction()) self.menubar.addAction(self.menuPlaylist.menuAction())
self.menubar.addAction(self.menuSearc_h.menuAction()) self.menubar.addAction(self.menuSearc_h.menuAction())
self.menubar.addAction(self.menuHelp.menuAction()) self.menubar.addAction(self.menuHelp.menuAction())
self.menubar.addAction(self.menu_Carts.menuAction())
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1) self.tabPlaylist.setCurrentIndex(-1)
@ -563,6 +574,7 @@ class Ui_MainWindow(object):
self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime")) self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
self.menuSearc_h.setTitle(_translate("MainWindow", "&Search")) self.menuSearc_h.setTitle(_translate("MainWindow", "&Search"))
self.menuHelp.setTitle(_translate("MainWindow", "&Help")) self.menuHelp.setTitle(_translate("MainWindow", "&Help"))
self.menu_Carts.setTitle(_translate("MainWindow", "&Carts"))
self.actionPlay_next.setText(_translate("MainWindow", "&Play next")) self.actionPlay_next.setText(_translate("MainWindow", "&Play next"))
self.actionPlay_next.setShortcut(_translate("MainWindow", "Return")) self.actionPlay_next.setShortcut(_translate("MainWindow", "Return"))
self.actionSkipToNext.setText(_translate("MainWindow", "Skip to &next")) self.actionSkipToNext.setText(_translate("MainWindow", "Skip to &next"))
@ -617,5 +629,6 @@ class Ui_MainWindow(object):
self.actionSave_as_template.setText(_translate("MainWindow", "Save as template...")) self.actionSave_as_template.setText(_translate("MainWindow", "Save as template..."))
self.actionNew_from_template.setText(_translate("MainWindow", "New from template...")) self.actionNew_from_template.setText(_translate("MainWindow", "New from template..."))
self.actionDebug.setText(_translate("MainWindow", "Debug")) self.actionDebug.setText(_translate("MainWindow", "Debug"))
self.actionAdd_cart.setText(_translate("MainWindow", "Add &cart..."))
from infotabs import InfoTabs from infotabs import InfoTabs
import icons_rc import icons_rc

View File

@ -0,0 +1,41 @@
"""Add carts
Revision ID: 6730f03317df
Revises: b4f524e2140c
Create Date: 2022-09-13 19:41:33.181752
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6730f03317df'
down_revision = 'b4f524e2140c'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('carts',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('cart_number', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=256), nullable=True),
sa.Column('duration', sa.Integer(), nullable=True),
sa.Column('path', sa.String(length=2048), nullable=True),
sa.Column('enabled', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('cart_number')
)
op.create_index(op.f('ix_carts_duration'), 'carts', ['duration'], unique=False)
op.create_index(op.f('ix_carts_name'), 'carts', ['name'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_carts_name'), table_name='carts')
op.drop_index(op.f('ix_carts_duration'), table_name='carts')
op.drop_table('carts')
# ### end Alembic commands ###