Sampled Instrument Player with static and monolithic design. All instruments are built-in.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
15 KiB

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
This file is part of Laborejo ( https://www.laborejo.org )
Laborejo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
from PyQt5 import QtCore, QtGui, QtWidgets
from hashlib import md5
2 years ago
def makeValueWidget(value:any):
"""Create a widget just from a value. Needs an external label e.g. in a formLayout.
First usecase was laborejo lilypond metadata and properties where it edited an engine-dict
directly and inplace.
Use with getValueFromWidget"""
types = {
str : QtWidgets.QLineEdit,
int : QtWidgets.QSpinBox,
float : QtWidgets.QDoubleSpinBox,
bool : QtWidgets.QCheckBox,
}
typ = type(value)
widget = types[typ]()
if typ == str:
widget.setText(value)
elif typ == int or typ == float:
widget.setValue(value)
elif typ == bool:
widget.setChecked(value)
return widget
def getValueFromWidget(widget):
"""Use with makeValueWidget"""
typ = type(widget)
if typ == QtWidgets.QLineEdit:
return widget.text()
elif typ == QtWidgets.QSpinBox or typ == QtWidgets.QDoubleSpinBox:
return widget.value()
elif typ == QtWidgets.QCheckBox:
return widget.isChecked()
def iconFromString(st, size=128):
px = QtGui.QPixmap(size,size)
color = stringToColor(st)
px.fill(color)
return QtGui.QIcon(px)
def stringToColor(st):
"""Convert a string to QColor. Same string, same color
Is used for group coloring"""
if st:
c = md5(st.encode()).hexdigest()
return QtGui.QColor(int(c[0:9],16) % 255, int(c[10:19],16) % 255, int(c[20:29],16)% 255, 255)
else:
return QtGui.QColor(255,255,255,255) #Return White
def invertColor(qcolor):
r = 255 - qcolor.red()
g = 255 - qcolor.green()
b = 255 - qcolor.blue()
return QtGui.QColor(r, g, b, qcolor.alpha())
def removeInstancesFromScene(qtGraphicsClass):
""""Remove all instances of a qt class that implements .instances=[] from the QGraphicsScene.
Don't use for items or anything in the notation view. This is used by the likes of the conductor
only since they exist only once and gets redrawn completely each time."""
for instance in qtGraphicsClass.instances:
instance.setParentItem(None)
instance.scene().removeWhenIdle(instance)
qtGraphicsClass.instances = []
def callContextMenu(listOfLabelsAndFunctions):
menu = QtWidgets.QMenu()
for text, function in listOfLabelsAndFunctions:
if text == "separator":
menu.addSeparator()
else:
a = QtWidgets.QAction(text, menu)
menu.addAction(a)
a.triggered.connect(function)
pos = QtGui.QCursor.pos()
pos.setY(pos.y() + 5)
menu.exec_(pos)
def stretchLine(qGraphicsLineItem, factor):
line = qGraphicsLineItem.line()
line.setLength(line.length() * factor)
qGraphicsLineItem.setLine(line)
def stretchRect(qGraphicsRectItem, factor):
r = qGraphicsRectItem.rect()
r.setRight(r.right() * factor)
qGraphicsRectItem.setRect(r)
class QHLine(QtWidgets.QFrame):
def __init__(self):
super().__init__()
self.setFrameShape(QtWidgets.QFrame.HLine)
self.setFrameShadow(QtWidgets.QFrame.Sunken)
def setPaletteAndFont(qtApp):
"""Set our programs color
This is in its own function because it is a annoying to scroll by that in init.
http://doc.qt.io/qt-5/qpalette.html"""
fPalBlue = QtGui.QPalette()
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Window, QtGui.QColor(32, 35, 39))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Window, QtGui.QColor(37, 40, 45))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Window, QtGui.QColor(37, 40, 45))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, QtGui.QColor(89, 95, 104))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.WindowText, QtGui.QColor(223, 237, 255))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, QtGui.QColor(223, 237, 255))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Base, QtGui.QColor(48, 53, 60))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, QtGui.QColor(55, 61, 69))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Base, QtGui.QColor(55, 61, 69))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, QtGui.QColor(60, 64, 67))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, QtGui.QColor(69, 73, 77))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, QtGui.QColor(69, 73, 77))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, QtGui.QColor(182, 193, 208))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, QtGui.QColor(182, 193, 208))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, QtGui.QColor(182, 193, 208))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, QtGui.QColor(42, 44, 48))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, QtGui.QColor(42, 44, 48))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, QtGui.QColor(42, 44, 48))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Text, QtGui.QColor(96, 103, 113))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, QtGui.QColor(210, 222, 240))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Text, QtGui.QColor(210, 222, 240))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Button, QtGui.QColor(51, 55, 62))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Button, QtGui.QColor(59, 63, 71))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Button, QtGui.QColor(59, 63, 71))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, QtGui.QColor(98, 104, 114))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, QtGui.QColor(210, 222, 240))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, QtGui.QColor(210, 222, 240))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, QtGui.QColor(255, 255, 255))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.BrightText, QtGui.QColor(255, 255, 255))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, QtGui.QColor(255, 255, 255))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Light, QtGui.QColor(59, 64, 72))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Light, QtGui.QColor(63, 68, 76))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Light, QtGui.QColor(63, 68, 76))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, QtGui.QColor(48, 52, 59))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Midlight, QtGui.QColor(51, 56, 63))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, QtGui.QColor(51, 56, 63))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, QtGui.QColor(18, 19, 22))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Dark, QtGui.QColor(20, 22, 25))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, QtGui.QColor(20, 22, 25))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, QtGui.QColor(28, 30, 34))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Mid, QtGui.QColor(32, 35, 39))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, QtGui.QColor(32, 35, 39))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, QtGui.QColor(13, 14, 16))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Shadow, QtGui.QColor(15, 16, 18))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, QtGui.QColor(15, 16, 18))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Highlight, QtGui.QColor(32, 35, 39))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Highlight, QtGui.QColor(14, 14, 17))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Highlight, QtGui.QColor(27, 28, 33))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, QtGui.QColor(89, 95, 104))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.HighlightedText, QtGui.QColor(217, 234, 253))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.HighlightedText, QtGui.QColor(223, 237, 255))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Link, QtGui.QColor(79, 100, 118))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.Link, QtGui.QColor(156, 212, 255))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Link, QtGui.QColor(156, 212, 255))
fPalBlue.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.LinkVisited, QtGui.QColor(51, 74, 118))
fPalBlue.setColor(QtGui.QPalette.Active, QtGui.QPalette.LinkVisited, QtGui.QColor(64, 128, 255))
fPalBlue.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.LinkVisited, QtGui.QColor(64, 128, 255))
qtApp.setPalette(fPalBlue)
font = QtGui.QFont("DejaVu Sans", 10)
font.setPixelSize(12)
qtApp.setFont(font)
return fPalBlue
class ToggleSwitch(QtWidgets.QAbstractButton):
"""From https://stackoverflow.com/questions/14780517/toggle-switch-in-qt/38102598
Usage:
# Thumb size < track size (Gitlab style)
s1 = ToggleSwitch()
s1.toggled.connect(lambda c: print('toggled', c))
s1.clicked.connect(lambda c: print('clicked', c))
s1.pressed.connect(lambda: print('pressed'))
s1.released.connect(lambda: print('released'))
s2 = ToggleSwitch()
s2.setEnabled(False)
# Thumb size > track size (Android style)
s3 = ToggleSwitch(thumb_radius=11, track_radius=8)
s4 = ToggleSwitch(thumb_radius=11, track_radius=8)
s4.setEnabled(False)
"""
def __init__(self, parent=None, track_radius=10, thumb_radius=8):
super().__init__(parent=parent)
self.setCheckable(True)
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self._track_radius = track_radius
self._thumb_radius = thumb_radius
self._margin = max(0, self._thumb_radius - self._track_radius)
self._base_offset = max(self._thumb_radius, self._track_radius)
self._end_offset = {
True: lambda: self.width() - self._base_offset,
False: lambda: self._base_offset,
}
self._offset = self._base_offset
palette = self.palette()
if self._thumb_radius > self._track_radius:
self._track_color = {
True: palette.highlight(),
False: palette.mid(),
}
self._thumb_color = {
True: palette.highlight(),
False: palette.light(),
}
self._text_color = {
True: palette.highlightedText().color(),
False: palette.mid().color(),
}
self._thumb_text = {
True: '',
False: '',
}
self._track_opacity = 0.5
else:
self._thumb_color = {
True: palette.highlightedText(),
False: palette.light(),
}
self._track_color = {
True: palette.highlight(),
False: palette.mid(),
}
self._text_color = {
True: palette.highlight().color(),
False: palette.mid().color(),
}
self._thumb_text = {
True: '',
False: '',
}
self._track_opacity = 0.7
3 years ago
@property
def offset(self):
return self._offset
@offset.setter
def offset(self, value):
self._offset = value
self.update()
def sizeHint(self): # pylint: disable=invalid-name
return QtCore.QSize(
4 * self._track_radius + 2 * self._margin,
2 * self._track_radius + 2 * self._margin,
)
def setChecked(self, checked):
super().setChecked(checked)
self.offset = self._end_offset[checked]()
def resizeEvent(self, event):
super().resizeEvent(event)
self.offset = self._end_offset[self.isChecked()]()
def paintEvent(self, event): # pylint: disable=invalid-name, unused-argument
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing, True)
p.setPen(QtCore.Qt.NoPen)
track_opacity = self._track_opacity
thumb_opacity = 1.0
text_opacity = 1.0
if self.isEnabled():
track_brush = self._track_color[self.isChecked()]
thumb_brush = self._thumb_color[self.isChecked()]
text_color = self._text_color[self.isChecked()]
else:
track_opacity *= 0.8
track_brush = self.palette().shadow()
thumb_brush = self.palette().mid()
text_color = self.palette().shadow().color()
p.setBrush(track_brush)
p.setOpacity(track_opacity)
p.drawRoundedRect(
self._margin,
self._margin,
self.width() - 2 * self._margin,
self.height() - 2 * self._margin,
self._track_radius,
self._track_radius,
)
p.setBrush(thumb_brush)
p.setOpacity(thumb_opacity)
p.drawEllipse(
self.offset - self._thumb_radius,
self._base_offset - self._thumb_radius,
2 * self._thumb_radius,
2 * self._thumb_radius,
)
p.setPen(text_color)
p.setOpacity(text_opacity)
font = p.font()
3 years ago
font.setPixelSize(int(1.5 * self._thumb_radius))
p.setFont(font)
p.drawText(
QtCore.QRectF(
self.offset - self._thumb_radius,
self._base_offset - self._thumb_radius,
2 * self._thumb_radius,
2 * self._thumb_radius,
),
QtCore.Qt.AlignCenter,
self._thumb_text[self.isChecked()],
)
def mouseReleaseEvent(self, event): # pylint: disable=invalid-name
super().mouseReleaseEvent(event)
"""
broken since Qt5.15
if event.button() == QtCore.Qt.LeftButton:
anim = QtCore.QPropertyAnimation(self, b'offset', self)
anim.setDuration(120)
anim.setStartValue(self.offset)
anim.setEndValue(self._end_offset[self.isChecked()]())
anim.start()
"""
def enterEvent(self, event): # pylint: disable=invalid-name
self.setCursor(QtCore.Qt.PointingHandCursor)
super().enterEvent(event)
class FancySwitch(ToggleSwitch):
pass