Replace text buttons with SVG icons, rebrand to Getting To It

This commit is contained in:
2026-05-08 18:33:01 +10:00
parent 95f74d813f
commit 680a9b5d51
7 changed files with 338 additions and 50 deletions
+16 -12
View File
@@ -4,10 +4,12 @@ from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QFrame, QSizePolicy, QGridLayout
)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtCore import Qt, QTimer, QSize
from PySide6.QtGui import QFont
import os
from icons import icon_play, icon_pause, icon_eject
class CartSlot(QFrame):
def __init__(self, cart_key: str, slot_number: int, engine, parent=None):
@@ -15,6 +17,8 @@ class CartSlot(QFrame):
self.cart_key = cart_key
self.slot_number = slot_number
self.engine = engine
self._playing_icon = False
self._playing_icon = False
self.setFrameStyle(QFrame.Box | QFrame.Raised)
self.setLineWidth(1)
@@ -108,13 +112,13 @@ class CartSlot(QFrame):
btn_col = QVBoxLayout()
btn_col.setSpacing(4)
self.btn_play = QPushButton("")
self.btn_play = QPushButton()
self.btn_play.setFixedSize(60, 44)
self.btn_play.setFont(QFont("Arial", 13, QFont.Bold))
self.btn_play.setIconSize(QSize(22, 22))
self.btn_play.setIcon(icon_play("#00ff41"))
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 3px;
}
@@ -122,13 +126,13 @@ class CartSlot(QFrame):
QPushButton:pressed { background-color: #0a5a2a; }
""")
self.btn_eject = QPushButton("")
self.btn_eject = QPushButton()
self.btn_eject.setFixedSize(60, 30)
self.btn_eject.setFont(QFont("Arial", 11, QFont.Bold))
self.btn_eject.setIconSize(QSize(18, 18))
self.btn_eject.setIcon(icon_eject("#ff4444"))
self.btn_eject.setStyleSheet("""
QPushButton {
background-color: #8a1a1a;
color: #ff4444;
border: 1px solid #555;
border-radius: 3px;
}
@@ -179,11 +183,11 @@ class CartSlot(QFrame):
self._set_ui_playing()
def _set_ui_playing(self):
self.btn_play.setText("")
self.btn_play.setIcon(icon_pause("#ffffff"))
self._playing_icon = True
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #00aa33;
color: white;
border: 1px solid #00ff41;
border-radius: 3px;
}
@@ -195,11 +199,11 @@ class CartSlot(QFrame):
)
def _set_ui_stopped(self):
self.btn_play.setText("")
self.btn_play.setIcon(icon_play("#00ff41"))
self._playing_icon = False
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 3px;
}
@@ -233,7 +237,7 @@ class CartSlot(QFrame):
def _refresh_display(self):
deck = self.engine.decks[self.cart_key]
if not deck.playing and self.btn_play.text() == "":
if not deck.playing and self._playing_icon:
self._set_ui_stopped()
self.status_label.setText("STOPPED")
+30 -23
View File
@@ -4,11 +4,16 @@ from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QFrame, QSizePolicy
)
from PySide6.QtCore import Qt, QTimer, Signal
from PySide6.QtCore import Qt, QTimer, Signal, QSize
from PySide6.QtGui import QFont, QColor, QPainter, QPen
import numpy as np
import os
from icons import (
icon_play, icon_pause, icon_stop, icon_skip_start,
icon_skip_end, icon_skip_back, icon_skip_forward, icon_eject
)
class WaveformWidget(QFrame):
"""Displays downsampled waveform with playhead position."""
@@ -207,18 +212,18 @@ class LCDDisplay(QFrame):
)
def make_transport_button(text: str,
def make_icon_button(icon: QIcon,
color: str = "#3a3a3a",
text_color: str = "white",
icon_color: str = "white",
width: int = 52,
height: int = 48) -> QPushButton:
btn = QPushButton(text)
btn = QPushButton()
btn.setFixedSize(width, height)
btn.setFont(QFont("Arial", 11, QFont.Bold))
btn.setIconSize(QSize(22, 22))
btn.setIcon(icon)
btn.setStyleSheet(f"""
QPushButton {{
background-color: {color};
color: {text_color};
border: 1px solid #555;
border-radius: 4px;
}}
@@ -244,6 +249,8 @@ class DeckWidget(QWidget):
self.accent_color = accent_color
self.current_theme = None
self.flash_state = False
self._playing_icon = False
self._playing_icon = False
# Convert "DECK 1" to "DECK_001"
deck_num = label.split()[-1]
@@ -295,12 +302,12 @@ class DeckWidget(QWidget):
transport = QHBoxLayout()
transport.setSpacing(6)
self.btn_to_start = make_transport_button("", "#3a3a3a", "white", 52, 48)
self.btn_back_15 = make_transport_button("-15", "#3a3a3a", "#aaaaaa", 52, 48)
self.btn_play = make_transport_button("", "#1a6b3a", "#00ff41", 68, 48)
self.btn_fwd_15 = make_transport_button("+15", "#3a3a3a", "#aaaaaa", 52, 48)
self.btn_to_end = make_transport_button("", "#3a3a3a", "white", 52, 48)
self.btn_stop = make_transport_button("", "#8a1a1a", "#ff4444", 52, 48)
self.btn_to_start = make_icon_button(icon_skip_start())
self.btn_back_15 = make_icon_button(icon_skip_back(), icon_color="#aaaaaa")
self.btn_play = make_icon_button(icon_play(), "#1a6b3a", "#00ff41", 68, 48)
self.btn_fwd_15 = make_icon_button(icon_skip_forward(), icon_color="#aaaaaa")
self.btn_to_end = make_icon_button(icon_skip_end())
self.btn_stop = make_icon_button(icon_stop(), "#8a1a1a", "#ff4444")
transport.addWidget(self.btn_to_start)
transport.addWidget(self.btn_back_15)
@@ -377,11 +384,11 @@ class DeckWidget(QWidget):
if deck.playing:
deck.pause()
deck.go_to_start()
self.btn_play.setText("")
self.btn_play.setIcon(icon_play("#00ff41"))
self._playing_icon = False
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 4px;
}
@@ -399,11 +406,11 @@ class DeckWidget(QWidget):
return
if deck.playing:
deck.pause()
self.btn_play.setText("")
self.btn_play.setIcon(icon_play("#00ff41"))
self._playing_icon = False
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 4px;
}
@@ -413,11 +420,11 @@ class DeckWidget(QWidget):
self.lcd.set_playing(False)
else:
deck.play()
self.btn_play.setText("")
self.btn_play.setIcon(icon_pause("#ffffff"))
self._playing_icon = True
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #00aa33;
color: white;
border: 1px solid #00ff41;
border-radius: 4px;
}
@@ -431,11 +438,11 @@ class DeckWidget(QWidget):
def _stop_eject(self):
deck = self.engine.decks[self.deck_key]
deck.stop()
self.btn_play.setText("")
self.btn_play.setIcon(icon_play("#00ff41"))
self._playing_icon = False
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 4px;
}
@@ -471,12 +478,12 @@ class DeckWidget(QWidget):
self.lcd.update_status("LOADED")
# Sync button if stopped manually
if not deck.playing and self.btn_play.text() == "":
self.btn_play.setText("")
if not deck.playing and self._playing_icon:
self.btn_play.setIcon(icon_play("#00ff41"))
self._playing_icon = False
self.btn_play.setStyleSheet("""
QPushButton {
background-color: #1a6b3a;
color: #00ff41;
border: 1px solid #555;
border-radius: 4px;
}
+13 -5
View File
@@ -13,9 +13,11 @@ from PySide6.QtWidgets import (
QLineEdit, QFormLayout, QDialogButtonBox,
QFileDialog
)
from PySide6.QtCore import Qt, Signal
from PySide6.QtCore import Qt, Signal, QSize
from PySide6.QtGui import QFont
from icons import icon_plus, icon_delete, icon_export
musicbrainzngs.set_useragent("RadioPanel", "1.0", "radio@panel.local")
@@ -111,13 +113,13 @@ class HistoryEntry(QFrame):
root.addLayout(text_col, stretch=1)
self._del_btn = QPushButton("")
self._del_btn = QPushButton()
self._del_btn.setFixedSize(24, 24)
self._del_btn.setFont(QFont("Arial", 10, QFont.Bold))
self._del_btn.setIconSize(QSize(14, 14))
self._del_btn.setIcon(icon_delete("#ff4444"))
self._del_btn.setStyleSheet("""
QPushButton {
background-color: #5a1a1a;
color: #ff4444;
border: 1px solid #444;
border-radius: 12px;
}
@@ -243,8 +245,10 @@ class HistoryWidget(QFrame):
bar = QHBoxLayout()
bar.setSpacing(4)
self._add_btn = QPushButton("+ ADD")
self._add_btn = QPushButton("ADD")
self._add_btn.setFixedHeight(28)
self._add_btn.setIconSize(QSize(16, 16))
self._add_btn.setIcon(icon_plus("#00ff41"))
self._add_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._add_btn.setStyleSheet("""
QPushButton {
@@ -298,6 +302,8 @@ class HistoryWidget(QFrame):
self._export_txt_btn = QPushButton("EXPORT TXT")
self._export_txt_btn.setFixedHeight(28)
self._export_txt_btn.setIconSize(QSize(16, 16))
self._export_txt_btn.setIcon(icon_export("#aaaaaa"))
self._export_txt_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._export_txt_btn.setStyleSheet("""
QPushButton {
@@ -313,6 +319,8 @@ class HistoryWidget(QFrame):
self._export_csv_btn = QPushButton("EXPORT CSV")
self._export_csv_btn.setFixedHeight(28)
self._export_csv_btn.setIconSize(QSize(16, 16))
self._export_csv_btn.setIcon(icon_export("#aaaaaa"))
self._export_csv_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._export_csv_btn.setStyleSheet("""
QPushButton {
+256
View File
@@ -0,0 +1,256 @@
# icons.py -- QPainter-drawn icons for Radio Panel
#
# Each function takes (color: str) and returns a QIcon painted at 24x24.
# All icons are simple geometric shapes -- clean, broadcast-console look.
from PySide6.QtGui import QPainter, QColor, QPen, QBrush, QPixmap, QIcon, QPainterPath
from PySide6.QtCore import Qt, QRectF, QPointF
_S = 24
def _make_icon(paint_fn, color: str) -> QIcon:
pix = QPixmap(_S, _S)
pix.fill(Qt.transparent)
p = QPainter(pix)
p.setRenderHint(QPainter.Antialiasing)
paint_fn(p, QRectF(2, 2, _S - 4, _S - 4), QColor(color))
p.end()
return QIcon(pix)
# ── Transport Icons ────────────────────────────────────────────────
def icon_play(color: str = "#00ff41") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
path = QPainterPath()
path.moveTo(r.left(), r.top())
path.lineTo(r.right(), r.center().y())
path.lineTo(r.left(), r.bottom())
path.closeSubpath()
p.drawPath(path)
return _make_icon(paint, color)
def icon_pause(color: str = "#ffffff") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
w = r.width() * 0.32
p.drawRect(QRectF(r.left(), r.top(), w, r.height()))
p.drawRect(QRectF(r.right() - w, r.top(), w, r.height()))
return _make_icon(paint, color)
def icon_stop(color: str = "#ff4444") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
margin = r.width() * 0.18
p.drawRect(r.adjusted(margin, margin, -margin, -margin))
return _make_icon(paint, color)
def icon_skip_start(color: str = "#ffffff") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
w = r.width() * 0.35
# Two triangles pointing left
path = QPainterPath()
path.moveTo(r.right(), r.top())
path.lineTo(r.left(), r.center().y())
path.lineTo(r.right(), r.bottom())
path.closeSubpath()
p.drawPath(path)
path2 = QPainterPath()
path2.moveTo(r.center().x(), r.top())
path2.lineTo(r.left(), r.center().y())
path2.lineTo(r.center().x(), r.bottom())
path2.closeSubpath()
p.drawPath(path2)
return _make_icon(paint, color)
def icon_skip_end(color: str = "#ffffff") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
path = QPainterPath()
path.moveTo(r.left(), r.top())
path.lineTo(r.right(), r.center().y())
path.lineTo(r.left(), r.bottom())
path.closeSubpath()
p.drawPath(path)
path2 = QPainterPath()
path2.moveTo(r.center().x(), r.top())
path2.lineTo(r.right(), r.center().y())
path2.lineTo(r.center().x(), r.bottom())
path2.closeSubpath()
p.drawPath(path2)
return _make_icon(paint, color)
def icon_skip_back(color: str = "#aaaaaa") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
path = QPainterPath()
path.moveTo(r.right() * 0.7, r.top())
path.lineTo(r.left(), r.center().y())
path.lineTo(r.right() * 0.7, r.bottom())
path.closeSubpath()
p.drawPath(path)
# Vertical bar
bar_w = r.width() * 0.12
p.drawRect(QRectF(r.right() - bar_w, r.top(), bar_w, r.height()))
return _make_icon(paint, color)
def icon_skip_forward(color: str = "#aaaaaa") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
p.setBrush(c)
path = QPainterPath()
path.moveTo(r.left() + r.width() * 0.3, r.top())
path.lineTo(r.right(), r.center().y())
path.lineTo(r.left() + r.width() * 0.3, r.bottom())
path.closeSubpath()
p.drawPath(path)
bar_w = r.width() * 0.12
p.drawRect(QRectF(r.left(), r.top(), bar_w, r.height()))
return _make_icon(paint, color)
def icon_eject(color: str = "#ff4444") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
# Upward triangle
mid_x = r.center().x()
path = QPainterPath()
path.moveTo(r.left() + r.width() * 0.2, r.center().y() + r.height() * 0.1)
path.lineTo(mid_x, r.top() + r.height() * 0.2)
path.lineTo(r.right() - r.width() * 0.2, r.center().y() + r.height() * 0.1)
p.drawPath(path)
# Horizontal line
p.drawLine(QPointF(r.left() + r.width() * 0.2, r.bottom() - r.height() * 0.1),
QPointF(r.right() - r.width() * 0.2, r.bottom() - r.height() * 0.1))
return _make_icon(paint, color)
# ── Action Icons ───────────────────────────────────────────────────
def icon_plus(color: str = "#00ff41") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
mid = r.center()
p.drawLine(QPointF(mid.x(), r.top()), QPointF(mid.x(), r.bottom()))
p.drawLine(QPointF(r.left(), mid.y()), QPointF(r.right(), mid.y()))
return _make_icon(paint, color)
def icon_delete(color: str = "#ff4444") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
p.drawLine(QPointF(r.left(), r.top()), QPointF(r.right(), r.bottom()))
p.drawLine(QPointF(r.right(), r.top()), QPointF(r.left(), r.bottom()))
return _make_icon(paint, color)
def icon_save(color: str = "#aaaaaa") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
# Arrow down into tray
mid_x = r.center().x()
p.drawLine(QPointF(mid_x, r.top()), QPointF(mid_x, r.bottom() - r.height() * 0.15))
# Arrow head
p.drawLine(QPointF(mid_x - r.width() * 0.25, r.bottom() - r.height() * 0.4),
QPointF(mid_x, r.bottom() - r.height() * 0.15))
p.drawLine(QPointF(mid_x + r.width() * 0.25, r.bottom() - r.height() * 0.4),
QPointF(mid_x, r.bottom() - r.height() * 0.15))
# Tray bottom line
p.drawLine(QPointF(r.left(), r.bottom()), QPointF(r.right(), r.bottom()))
return _make_icon(paint, color)
def icon_load(color: str = "#aaaaaa") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
# Arrow up from tray
mid_x = r.center().x()
p.drawLine(QPointF(mid_x, r.bottom()), QPointF(mid_x, r.top() + r.height() * 0.15))
p.drawLine(QPointF(mid_x - r.width() * 0.25, r.top() + r.height() * 0.35),
QPointF(mid_x, r.top() + r.height() * 0.1))
p.drawLine(QPointF(mid_x + r.width() * 0.25, r.top() + r.height() * 0.35),
QPointF(mid_x, r.top() + r.height() * 0.1))
p.drawLine(QPointF(r.left(), r.bottom()), QPointF(r.right(), r.bottom()))
return _make_icon(paint, color)
def icon_export(color: str = "#aaaaaa") -> QIcon:
def paint(p, r, c):
pen = QPen(c, r.width() * 0.12)
pen.setCapStyle(Qt.RoundCap)
p.setPen(pen)
p.setBrush(Qt.NoBrush)
# Box
margin = r.width() * 0.15
p.drawRect(r.adjusted(margin, margin, -margin, -margin))
# Arrow out of box (top-right)
p.drawLine(QPointF(r.right() - margin, r.top() + margin),
QPointF(r.right(), r.top()))
p.drawLine(QPointF(r.right() - r.width() * 0.3, r.top()),
QPointF(r.right(), r.top()))
p.drawLine(QPointF(r.right(), r.top() + r.height() * 0.3),
QPointF(r.right(), r.top()))
return _make_icon(paint, color)
def icon_theme(color: str = "#00ff41") -> QIcon:
def paint(p, r, c):
p.setPen(Qt.NoPen)
# Draw 4 coloured quadrants
mid = r.center()
colors = [QColor("#00ff41"), QColor("#ffaa00"),
QColor("#00aaff"), QColor("#ff4444")]
for i, col in enumerate(colors):
path = QPainterPath()
if i == 0:
path.moveTo(mid)
path.arcTo(r, 0, 90)
path.closeSubpath()
elif i == 1:
path.moveTo(mid)
path.arcTo(r, 270, 90)
path.closeSubpath()
elif i == 2:
path.moveTo(mid)
path.arcTo(r, 180, 90)
path.closeSubpath()
else:
path.moveTo(mid)
path.arcTo(r, 90, 90)
path.closeSubpath()
p.setBrush(col)
p.drawPath(path)
# White circle in centre
p.setBrush(QColor("#ffffff"))
p.drawEllipse(mid, r.width() * 0.15, r.height() * 0.15)
return _make_icon(paint, color)
+2 -2
View File
@@ -24,7 +24,7 @@ class RadioPanelWindow(QMainWindow):
self.engine = AudioEngine()
self.setWindowTitle("Radio Panel")
self.setWindowTitle("Getting To It")
self.setMinimumSize(1280, 720)
self.resize(1920, 1080)
self._apply_global_style(THEMES[0])
@@ -204,7 +204,7 @@ class RadioPanelWindow(QMainWindow):
def main():
app = QApplication(sys.argv)
app.setApplicationName("Radio Panel")
app.setOrganizationName("GTI")
app.setOrganizationName("Getting To It")
palette = QPalette()
palette.setColor(QPalette.Window, QColor("#0d0d0d"))
+9 -1
View File
@@ -6,11 +6,15 @@ from PySide6.QtWidgets import (
QListWidget, QListWidgetItem, QAbstractItemView,
QMenu, QSplitter
)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtCore import Qt, QTimer, QSize, QSize
from PySide6.QtGui import QFont, QColor, QAction
import json
import os
from icons import icon_save, icon_load
from icons import icon_save, icon_load
# ─────────────────────────────────────────
# PLAYLIST ITEM
@@ -606,6 +610,8 @@ class PlaylistWidget(QWidget):
self._save_btn = QPushButton("SAVE")
self._save_btn.setFixedHeight(28)
self._save_btn.setIconSize(QSize(16, 16))
self._save_btn.setIcon(icon_save("#aaaaaa"))
self._save_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._save_btn.setStyleSheet("""
QPushButton {
@@ -621,6 +627,8 @@ class PlaylistWidget(QWidget):
self._load_btn = QPushButton("LOAD")
self._load_btn.setFixedHeight(28)
self._load_btn.setIconSize(QSize(16, 16))
self._load_btn.setIcon(icon_load("#aaaaaa"))
self._load_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._load_btn.setStyleSheet("""
QPushButton {
+9 -4
View File
@@ -1,10 +1,14 @@
# top_bar_widget.py
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton
from PySide6.QtCore import Qt, QTimer, Signal
from PySide6.QtCore import Qt, QTimer, Signal, QSize
from PySide6.QtGui import QFont
from datetime import datetime
from icons import icon_theme
from icons import icon_theme
THEMES = [
# Dark themes
@@ -229,16 +233,17 @@ class TopBarWidget(QFrame):
layout.setSpacing(12)
# Theme cycle button
self.theme_btn = QPushButton("")
self.theme_btn = QPushButton()
self.theme_btn.setFixedSize(40, 40)
self.theme_btn.setFont(QFont("Arial", 16, QFont.Bold))
self.theme_btn.setIconSize(QSize(22, 22))
self.theme_btn.setIcon(icon_theme())
self.theme_btn.setToolTip("Cycle colour theme")
self.theme_btn.clicked.connect(self._cycle_theme)
self._style_theme_btn()
layout.addWidget(self.theme_btn)
# Station name
self.station_label = QLabel("RADIO PANEL")
self.station_label = QLabel("GETTING TO IT")
self.station_label.setFont(QFont("Arial", 16, QFont.Bold))
self.station_label.setStyleSheet(
"color: #444444; background: transparent; border: none;"