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.
384 lines
15 KiB
384 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
|
|
|
|
|
|
|
|
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
|
|
|
|
@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()
|
|
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
|
|
|