Add function type hints. Section headers and note colours working

This commit is contained in:
Keith Edmunds 2022-08-05 21:52:17 +01:00
parent 4f03306aff
commit 7a14651bd7
6 changed files with 120 additions and 127 deletions

View File

@ -11,14 +11,12 @@ class Config(object):
COLOUR_CURRENT_PLAYLIST = "#7eca8f"
COLOUR_CURRENT_TAB = "#248f24"
COLOUR_ENDING_TIMER = "#dc3545"
COLOUR_EVEN_PLAYLIST = "#d9d9d9"
COLOUR_LONG_START = "#dc3545"
COLOUR_NEXT_HEADER = "#fff3cd"
COLOUR_NEXT_PLAYLIST = "#ffc107"
COLOUR_NEXT_TAB = "#b38600"
COLOUR_NORMAL_TAB = "#000000"
COLOUR_NOTES_PLAYLIST = "#b8daff"
COLOUR_ODD_PLAYLIST = "#f2f2f2"
COLOUR_PREVIOUS_HEADER = "#f8d7da"
COLOUR_UNREADABLE = "#dc3545"
COLOUR_WARNING_TIMER = "#ffc107"

View File

@ -2,9 +2,10 @@ import inspect
import logging
import os
from config import Config
from sqlalchemy import create_engine
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.orm import (sessionmaker, scoped_session)
from typing import Generator
from log import log
@ -38,7 +39,7 @@ engine = create_engine(
@contextmanager
def Session():
def Session() -> Generator[scoped_session, None, None]:
frame = inspect.stack()[2]
file = frame.filename
function = frame.function

View File

@ -11,7 +11,7 @@ from config import Config
class LevelTagFilter(logging.Filter):
"""Add leveltag"""
def filter(self, record):
def filter(self, record: logging.LogRecord):
# Extract the first character of the level name
record.leveltag = record.levelname[0]
@ -23,7 +23,7 @@ class LevelTagFilter(logging.Filter):
class DebugStdoutFilter(logging.Filter):
"""Filter debug messages sent to stdout"""
def filter(self, record):
def filter(self, record: logging.LogRecord):
if record.levelno != logging.DEBUG:
return True
if record.module in Config.DEBUG_MODULES:

View File

@ -1,7 +1,7 @@
#!/usr/bin/python3
#
# import os.path
# import re
import re
#
from dbconfig import Session
#
@ -46,8 +46,8 @@ from sqlalchemy.orm.exc import (
# from log import log.debug, log.error
#
Base = declarative_base()
#
#
# Database classes
class NoteColours(Base):
__tablename__ = 'notecolours'
@ -93,38 +93,40 @@ class NoteColours(Base):
#
# return session.query(NoteColours).filter(
# NoteColours.id == note_id).first()
#
# @staticmethod
# def get_colour(session: Session, text: str) -> Optional[str]:
# """
# Parse text and return colour string if matched, else None
# """
#
# for rec in (
# session.query(NoteColours)
# .filter(NoteColours.enabled.is_(True))
# .order_by(NoteColours.order)
# .all()
# ):
# if rec.is_regex:
# flags = re.UNICODE
# if not rec.is_casesensitive:
# flags |= re.IGNORECASE
# p = re.compile(rec.substring, flags)
# if p.match(text):
# return rec.colour
# else:
# if rec.is_casesensitive:
# if rec.substring in text:
# return rec.colour
# else:
# if rec.substring.lower() in text.lower():
# return rec.colour
#
# return None
#
#
#class Notes(Base):
@staticmethod
def get_colour(session: Session, text: str) -> Optional[str]:
"""
Parse text and return colour string if matched, else None
"""
if not text:
return None
for rec in session.execute(
select(NoteColours)
.filter(NoteColours.enabled.is_(True))
.order_by(NoteColours.order)
).scalars().all():
if rec.is_regex:
flags = re.UNICODE
if not rec.is_casesensitive:
flags |= re.IGNORECASE
p = re.compile(rec.substring, flags)
if p.match(text):
return rec.colour
else:
if rec.is_casesensitive:
if rec.substring in text:
return rec.colour
else:
if rec.substring.lower() in text.lower():
return rec.colour
return None
# class Notes(Base):
# __tablename__ = 'notes'
#
# id: int = Column(Integer, primary_key=True, autoincrement=True)
@ -514,7 +516,7 @@ class Settings(Base):
return int_setting
def update(self, session: Session, data):
def update(self, session: Session, data: "Settings"):
for key, value in data.items():
assert hasattr(self, key)
setattr(self, key, value)

View File

@ -17,14 +17,14 @@ import sys
# from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
from PyQt5.QtWidgets import (
QApplication,
# QDialog,
# QFileDialog,
# QInputDialog,
# QLabel,
# QLineEdit,
# QListWidgetItem,
# QDialog,
# QFileDialog,
# QInputDialog,
# QLabel,
# QLineEdit,
# QListWidgetItem,
QMainWindow,
# QMessageBox,
# QMessageBox,
)
#
from dbconfig import engine, Session
@ -494,7 +494,7 @@ class Window(QMainWindow, Ui_MainWindow):
# # also be saved to database
# self.visible_playlist_tab().insert_track(session, track)
def _load_last_playlists(self):
def _load_last_playlists(self) -> None:
"""Load the playlists that were open when the last session closed"""
with Session() as session:

View File

@ -6,9 +6,9 @@ from typing import List, Optional
#
# from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from PyQt5.Qt import QFont
from PyQt5.QtGui import (
QColor,
QFont,
# QDropEvent
)
# from PyQt5 import QtWidgets
@ -27,12 +27,12 @@ from PyQt5.QtWidgets import (
#
import helpers
# import os
# import re
import re
# import subprocess
# import threading
#
from config import Config
from datetime import datetime #, timedelta
from datetime import datetime # , timedelta
from helpers import (
get_relative_date,
# open_in_audacity
@ -45,10 +45,10 @@ from models import (
PlaylistRows,
Settings,
Tracks,
# NoteColours
NoteColours
)
from dbconfig import Session
# start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
class RowMeta:
@ -101,7 +101,7 @@ class PlaylistTab(QTableWidget):
PLAYLISTROW_ID = Qt.UserRole + 2
def __init__(self, musicmuster: QMainWindow, session: Session,
playlist_id: int, *args, **kwargs):
playlist_id: int, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.musicmuster: QMainWindow = musicmuster
self.playlist_id: int = playlist_id
@ -166,7 +166,7 @@ class PlaylistTab(QTableWidget):
# Now load our tracks and notes
self.populate(session, self.playlist_id)
def _column_resize(self, idx, old, new):
def _column_resize(self, idx: int, old: int, new: int) -> None:
"""
Called when column widths are changed.
@ -380,14 +380,10 @@ class PlaylistTab(QTableWidget):
self.insertRow(row)
# Add row metadata to userdata column
item: QTableWidgetItem = QTableWidgetItem()
item.setData(self.ROW_METADATA, 0)
self.setItem(row, columns['userdata'].idx, item)
# Prepare notes, start and end items for later
notes_item = QTableWidgetItem(row_data.note)
start_item = QTableWidgetItem()
end_item = QTableWidgetItem()
userdata_item = QTableWidgetItem()
userdata_item.setData(self.ROW_METADATA, 0)
userdata_item.setData(self.PLAYLISTROW_ID, row_data.id)
self.setItem(row, columns['userdata'].idx, userdata_item)
if row_data.track_id:
# Add track details to items
@ -395,13 +391,28 @@ class PlaylistTab(QTableWidget):
start_gap_item = QTableWidgetItem(str(start_gap))
if start_gap and start_gap >= 500:
start_gap_item.setBackground(QColor(Config.COLOUR_LONG_START))
self.setItem(row, columns['start_gap'].idx, start_gap_item)
title_item = QTableWidgetItem(row_data.track.title)
self.setItem(row, columns['title'].idx, title_item)
artist_item = QTableWidgetItem(row_data.track.artist)
self.setItem(row, columns['artist'].idx, artist_item)
duration_item = QTableWidgetItem(
helpers.ms_to_mmss(row_data.track.duration))
self.setItem(row, columns['duration'].idx, duration_item)
start_item = QTableWidgetItem()
self.setItem(row, columns['start_time'].idx, start_item)
end_item = QTableWidgetItem()
self.setItem(row, columns['end_time'].idx, end_item)
# As we have track info, any notes should be contained in
# the notes column
notes_item = QTableWidgetItem(row_data.note)
self.setItem(row, columns['row_notes'].idx, notes_item)
last_playtime = Playdates.last_played(session, row_data.track.id)
last_played_str = get_relative_date(last_playtime)
@ -412,39 +423,21 @@ class PlaylistTab(QTableWidget):
if not helpers.file_is_readable(row_data.track.path):
self._set_unreadable_row(row)
else:
# This is a note row so make empty items (row background
# won't be coloured without items present)
start_gap_item = QTableWidgetItem()
title_item = QTableWidgetItem()
artist_item = QTableWidgetItem()
duration_item = QTableWidgetItem()
last_played_item = QTableWidgetItem()
# Add items to table
self.setItem(row, columns['start_gap'].idx, start_gap_item)
self.setItem(row, columns['title'].idx, title_item)
self.setItem(row, columns['artist'].idx, artist_item)
self.setItem(row, columns['duration'].idx, duration_item)
self.setItem(row, columns['start_time'].idx, start_item)
self.setItem(row, columns['end_time'].idx, end_item)
self.setItem(row, columns['row_notes'].idx, notes_item)
# Save track_id and playlistrow_id
userdata_item = QTableWidgetItem()
if row_data.track_id:
# Save track_id
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
else:
# This is a section header so make empty items (row
# background won't be coloured without items present). Any
# notes should displayed starting in column 0
for i in range(2, len(columns) - 1):
self.setItem(row, i, QTableWidgetItem())
notes_item = QTableWidgetItem(row_data.note)
self.setItem(row, 1, notes_item)
self.setSpan(row, 1, 1, len(columns))
# Save (no) track_id
userdata_item.setData(self.ROW_TRACK_ID, 0)
userdata_item.setData(self.PLAYLISTROW_ID, row_data.id)
self.setItem(row, columns['userdata'].idx, userdata_item)
# Span note across table
if not row_data.track_id:
self.setSpan(row, columns['row_notes'].idx, len(columns), 1)
# Scroll to new row
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
if repaint:
self.save_playlist(session)
@ -870,6 +863,15 @@ class PlaylistTab(QTableWidget):
# Cycle through all rows
for row in range(self.rowCount()):
# Extract note text from database to ignore section timings
playlist_row = session.get(PlaylistRows,
self._get_playlistrow_id(row))
note_text = playlist_row.note
# Get note colour
note_colour = NoteColours.get_colour(session, note_text)
if not note_colour:
note_colour = Config.COLOUR_NOTES_PLAYLIST
# Get track if there is one
track_id = self._get_row_track_id(row)
track = None
@ -903,6 +905,11 @@ class PlaylistTab(QTableWidget):
else:
self.showRow(row)
# Colour any note
if note_text:
(self.item(row, columns['row_notes'].idx)
.setBackground(QColor(note_colour)))
# Render playing track
if row == current_row:
# Set start time
@ -973,21 +980,10 @@ class PlaylistTab(QTableWidget):
self._set_row_end_time(row, None)
# Don't dim unplayed tracks
self._set_row_bold(row)
# Stripe rows
if row % 2:
self._set_row_colour(
row, QColor(Config.COLOUR_ODD_PLAYLIST))
else:
self._set_row_colour(
row, QColor(Config.COLOUR_EVEN_PLAYLIST))
continue
# No track associated, so this row is a section header
# Extract note text from database to ignore section timings
playlist_row = session.get(PlaylistTracks,
self._get_playlistrow_id(row))
note_text = playlist_row.note
if filter_text:
if filter_text not in note_text.lower():
self.hideRow(row)
@ -1010,10 +1006,6 @@ class PlaylistTab(QTableWidget):
elif note_text.endswith("+"):
section_start_row = row
section_time = 0
# Set colour
note_colour = NoteColours.get_colour(session, note_text)
if not note_colour:
note_colour = Config.COLOUR_NOTES_PLAYLIST
self._set_row_colour(
row, QColor(note_colour)
)
@ -1249,7 +1241,7 @@ class PlaylistTab(QTableWidget):
# return self._meta_search(RowMeta.NOTE, one=False)
#
def _get_playlistrow_id(self, row):
def _get_playlistrow_id(self, row: int) -> int:
"""Return the playlistrow_id associated with this row"""
playlistrow_id = (self.item(row, columns['userdata'].idx)
@ -1257,7 +1249,7 @@ class PlaylistTab(QTableWidget):
return playlistrow_id
def _get_row_track_id(self, row):
def _get_row_track_id(self, row: int) -> int:
"""Return the track_id associated with this row or None"""
track_id = (self.item(row, columns['userdata'].idx)
@ -1309,20 +1301,20 @@ class PlaylistTab(QTableWidget):
return row[0]
else:
return None
#
# @staticmethod
# def _get_note_text_time(text: str) -> Optional[datetime]:
# """Return time specified as @hh:mm:ss in text"""
#
# match = start_time_re.search(text)
# if not match:
# return None
#
# try:
# return datetime.strptime(match.group(0)[1:],
# Config.NOTE_TIME_FORMAT)
# except ValueError:
# return None
@staticmethod
def _get_note_text_time(text: str) -> Optional[datetime]:
"""Return time specified as @hh:mm:ss in text"""
match = start_time_re.search(text)
if not match:
return None
try:
return datetime.strptime(match.group(0)[1:],
Config.NOTE_TIME_FORMAT)
except ValueError:
return None
def _get_played_track_rows(self) -> List[int]:
"""Return rows marked as played, or None"""
@ -1751,7 +1743,7 @@ class PlaylistTab(QTableWidget):
j: int
for j in range(2, self.columnCount()):
for j in range(1, self.columnCount()):
if self.item(row, j):
self.item(row, j).setBackground(colour)
#