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
export MM_ENV="DEVELOPMENT"
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
export MM_ENV="DEVELOPMENT"
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 = 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_carts
[post_write_hooks]
# 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_OK_THRESHOLD = 300
CHECK_AUDACITY_AT_STARTUP = True
CART_DIRECTORY = "/home/kae/radio/CartTracks"
COLOUR_BITRATE_LOW = "#ffcdd2"
COLOUR_BITRATE_MEDIUM = "#ffeb6f"
COLOUR_BITRATE_OK = "#dcedc8"
COLOUR_CART_ERROR = "#dc3545"
COLOUR_CART_PLAYING = "#248f24"
COLOUR_CART_READY = "#ffc107"
COLOUR_CURRENT_HEADER = "#d4edda"
COLOUR_CURRENT_PLAYLIST = "#7eca8f"
COLOUR_CURRENT_TAB = "#248f24"

View File

@ -51,6 +51,68 @@ Base = declarative_base()
# 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):
__tablename__ = 'notecolours'

View File

@ -9,8 +9,8 @@ import threading
from datetime import datetime, timedelta
from typing import List, Optional
from PyQt5.QtCore import QDate, QEvent, Qt, QTime, QTimer
from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtCore import QDate, QEvent, Qt, QSize, QTime, QTimer
from PyQt5.QtGui import QColor, QPalette, QFont
from PyQt5.QtWidgets import (
QApplication,
QDialog,
@ -21,6 +21,7 @@ from PyQt5.QtWidgets import (
QListWidgetItem,
QMainWindow,
QMessageBox,
QPushButton,
)
from dbconfig import engine, Session
@ -29,18 +30,20 @@ import music
from models import (
Base,
Carts,
Playdates,
PlaylistRows,
Playlists,
Settings,
Tracks
)
from config import Config
from playlists import PlaylistTab
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_SelectPlaylist_ui import Ui_dlgSelectPlaylist # 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 utilities import check_db, update_bitrates
@ -90,36 +93,110 @@ class Window(QMainWindow, Ui_MainWindow):
self.statusbar.addWidget(self.txtSearch)
self.txtSearch.setHidden(True)
self.hide_played_tracks = False
self.carts = {}
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
self.tabPlaylist.currentWidget
self.load_last_playlists()
self.carts_init()
self.enable_play_next_controls()
self.timer.start(Config.TIMER_MS)
self.connect_signals_slots()
def about(self) -> None:
"""Get git tag and database name"""
def cart_add(self) -> None:
"""Add a cart"""
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)
pass
def cart_button(self, cart: Carts) -> QPushButton:
"""Create a cart pushbutton"""
btn = QPushButton(self)
btn.setEnabled(True)
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:
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(
self,
"About",
f"MusicMuster {git_tag}\n\nDatabase: {dbname}",
QMessageBox.Ok
)
def carts_init(self) -> None:
"""Initialse carts data structures"""
with Session() as session:
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:
""" Clear selected row"""
@ -205,8 +282,10 @@ class Window(QMainWindow, Ui_MainWindow):
def connect_signals_slots(self) -> None:
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.actionDebug.triggered.connect(self.debug)
self.actionAdd_cart.triggered.connect(self.cart_add)
self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
self.actionDownload_CSV_of_played_tracks.triggered.connect(
self.download_played_tracks)
@ -438,6 +517,28 @@ class Window(QMainWindow, Ui_MainWindow):
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]:
"""Show dialog box to select one track and return it to caller"""
@ -987,6 +1088,8 @@ class Window(QMainWindow, Ui_MainWindow):
# Update TOD clock
self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
# Update carts
self.cart_tick()
self.even_tick = not self.even_tick
if not self.even_tick:
@ -1079,6 +1182,44 @@ class Window(QMainWindow, Ui_MainWindow):
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):
"""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">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
@ -121,14 +121,8 @@
</item>
</layout>
</item>
<item>
<item row="3" column="0">
<widget class="QLabel" name="dbPath">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</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>
</item>
<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">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -325,7 +342,7 @@ padding-left: 8px;</string>
</widget>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QFrame" name="frame_5">
<property name="styleSheet">
<string notr="true">background-color: rgb(192, 191, 188)</string>
@ -844,10 +861,17 @@ padding-left: 8px;</string>
<addaction name="action_About"/>
<addaction name="actionDebug"/>
</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="menuPlaylist"/>
<addaction name="menuSearc_h"/>
<addaction name="menuHelp"/>
<addaction name="menu_Carts"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="enabled">
@ -1132,6 +1156,11 @@ padding-left: 8px;</string>
<string>Debug</string>
</property>
</action>
<action name="actionAdd_cart">
<property name="text">
<string>Add &amp;cart...</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -158,6 +158,11 @@ 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.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.setOrientation(QtCore.Qt.Vertical)
self.splitter.setObjectName("splitter")
@ -171,7 +176,7 @@ class Ui_MainWindow(object):
self.tabInfolist.setTabsClosable(True)
self.tabInfolist.setMovable(True)
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.setStyleSheet("background-color: rgb(192, 191, 188)")
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.horizontalLayout.addWidget(self.frame_3)
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)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 26))
@ -376,6 +381,8 @@ class Ui_MainWindow(object):
self.menuSearc_h.setObjectName("menuSearc_h")
self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
self.menu_Carts = QtWidgets.QMenu(self.menubar)
self.menu_Carts.setObjectName("menu_Carts")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setEnabled(True)
@ -482,6 +489,8 @@ class Ui_MainWindow(object):
self.actionNew_from_template.setObjectName("actionNew_from_template")
self.actionDebug = QtWidgets.QAction(MainWindow)
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.actionOpenPlaylist)
self.menuFile.addAction(self.actionClosePlaylist)
@ -521,10 +530,12 @@ class Ui_MainWindow(object):
self.menuSearc_h.addAction(self.actionSelect_previous_track)
self.menuHelp.addAction(self.action_About)
self.menuHelp.addAction(self.actionDebug)
self.menu_Carts.addAction(self.actionAdd_cart)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuPlaylist.menuAction())
self.menubar.addAction(self.menuSearc_h.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.menubar.addAction(self.menu_Carts.menuAction())
self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1)
@ -563,6 +574,7 @@ class Ui_MainWindow(object):
self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
self.menuSearc_h.setTitle(_translate("MainWindow", "&Search"))
self.menuHelp.setTitle(_translate("MainWindow", "&Help"))
self.menu_Carts.setTitle(_translate("MainWindow", "&Carts"))
self.actionPlay_next.setText(_translate("MainWindow", "&Play next"))
self.actionPlay_next.setShortcut(_translate("MainWindow", "Return"))
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.actionNew_from_template.setText(_translate("MainWindow", "New from template..."))
self.actionDebug.setText(_translate("MainWindow", "Debug"))
self.actionAdd_cart.setText(_translate("MainWindow", "Add &cart..."))
from infotabs import InfoTabs
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 ###