Browse Source

create-Qt-pushbuttons

pull/1971/head
jpka- 11 months ago
parent
commit
11757e143a
3 changed files with 517 additions and 225 deletions
  1. +65
    -18
      source/frontend/carla_skin.py
  2. +139
    -10
      source/frontend/widgets/commondial.py
  3. +313
    -197
      source/frontend/widgets/scalabledial.py

+ 65
- 18
source/frontend/carla_skin.py View File

@@ -6,6 +6,7 @@
# Imports (Global)

from qt_compat import qt_config
import math

if qt_config == 5:
from PyQt5.QtCore import Qt, QRectF, QLineF, QTimer
@@ -154,7 +155,7 @@ def getColorFromCategory(category):
# ------------------------------------------------------------------------------------------------------------
#

def setScalableDialStyle(widget, parameterId, parameterCount, whiteLabels, skinStyle):
def setScalableDialStyle(widget, parameterId, whiteLabels, skinStyle):
if skinStyle.startswith("calf"):
widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_NO_GRADIENT)
widget.setImage(7)
@@ -176,10 +177,10 @@ def setScalableDialStyle(widget, parameterId, parameterCount, whiteLabels, skinS
widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_VOL)

else:
_r = 255 - int((float(parameterId)/float(parameterCount))*200.0)
_g = 55 + int((float(parameterId)/float(parameterCount))*200.0)
_b = 0 #(r-40)*4
widget.setCustomPaintColor(QColor(_r, _g, _b))
# _r = 255 - int((float(index)/float(parameterCount))*200.0)
# _g = 55 + int((float(index)/float(parameterCount))*200.0)
# _b = 0 #(r-40)*4
# widget.setCustomPaintColor(QColor(_r, _g, _b))
widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_COLOR)

if whiteLabels:
@@ -565,6 +566,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):

index = 0
layout = self.w_knobs_left.layout()

for i in range(parameterCount):
# 50 should be enough for everybody, right?
if index >= 50:
@@ -573,20 +575,37 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
paramInfo = self.host.get_parameter_info(self.fPluginId, i)
paramData = self.host.get_parameter_data(self.fPluginId, i)
paramRanges = self.host.get_parameter_ranges(self.fPluginId, i)
isBoolean = (paramData['hints'] & PARAMETER_IS_BOOLEAN) != 0
isInteger = (paramData['hints'] & PARAMETER_IS_INTEGER) != 0

scalePoints = []
prefix = ""
suffix = ""
if ((paramData['hints'] & PARAMETER_USES_SCALEPOINTS) != 0):
for j in range(paramInfo['scalePointCount']):
scalePoints.append(self.host.get_parameter_scalepoint_info(self.fPluginId, index, j))
suffix = paramInfo['unit'].strip()
# There is very few plugins with this, and none with scalepoints i found, but still... (but how do i test it?)
if suffix == "(coef)":
prefix = "* "
suffix = ""
else:
suffix = " " + suffix

if paramData['type'] != PARAMETER_INPUT:
continue
if paramData['hints'] & PARAMETER_IS_BOOLEAN:
continue
# if paramData['hints'] & PARAMETER_IS_BOOLEAN:
# continue
if (paramData['hints'] & PARAMETER_IS_ENABLED) == 0:
continue
if (paramData['hints'] & PARAMETER_USES_SCALEPOINTS) != 0 and not isInteger:
# NOTE: we assume integer scalepoints are continuous
continue
if isInteger and paramRanges['max']-paramRanges['min'] <= 3:
continue
print("INFO: Parameter "+str(i)+" is Disabled.")
# continue
# if (paramData['hints'] & PARAMETER_USES_SCALEPOINTS) != 0 and not isInteger:
# # NOTE: we assume integer scalepoints are continuous
# continue
# if isInteger and paramRanges['max']-paramRanges['min'] <= 3:
# continue
if paramInfo['name'].startswith("unused"):
print("INFO: Parameter "+str(i)+" is Unused, so skipped.")
continue

paramName = getParameterShortName(paramInfo['name'])
@@ -597,21 +616,49 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
widget.setMaximum(paramRanges['max'])
widget.hide()

if isInteger:
widget.setPrecision(paramRanges['max']-paramRanges['min'], True)
delta = paramRanges['max']-paramRanges['min']
if delta <= 0:
print("ERROR: Parameter "+str(i)+": Max and Min are same or wrong.")
return

if isBoolean: # Mimic as [0 or 1] integer
widget.setPrecision(1, True, scalePoints, prefix, suffix)

elif isInteger:
while delta > 50:
delta = int(math.ceil(delta / 2))
widget.setPrecision(delta, True, scalePoints, prefix, suffix)

setScalableDialStyle(widget, i, parameterCount, whiteLabels, self.fSkinStyle)
else: # Floats are finer-step smoothed
delta = paramRanges['max']-paramRanges['min']
while delta > 500:
delta = delta / 2.0
while delta < 250:
delta = delta * 2.0
widget.setPrecision(math.ceil(delta), False, scalePoints, prefix, suffix)

setScalableDialStyle(widget, 0, whiteLabels, self.fSkinStyle)

index += 1
self.fParameterList.append([i, widget])
layout.addWidget(widget)

CUSTOM_PAINT_HUE_FROM = -0.025 # Deep Red
CUSTOM_PAINT_HUE_SPAN = 0.4 # Deep Red -> Green. Can be negative.
for i in range(index):
widget = layout.itemAt(i).widget()
if widget is not None:
coef = i/(index-1) if index > 1 else 0.5 # 0.5 = Midrange
hue = (CUSTOM_PAINT_HUE_FROM + coef * CUSTOM_PAINT_HUE_SPAN) % 1.0
widget.setCustomPaintColor(QColor.fromHslF(hue, 1, 0.5, 1))


if self.w_knobs_right is not None and (self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET) != 0:
widget = ScalableDial(self, PARAMETER_DRYWET)
widget.setLabel("Dry/Wet")
widget.setMinimum(0.0)
widget.setMaximum(1.0)
setScalableDialStyle(widget, PARAMETER_DRYWET, 0, whiteLabels, self.fSkinStyle)
setScalableDialStyle(widget, PARAMETER_DRYWET, whiteLabels, self.fSkinStyle)

self.fParameterList.append([PARAMETER_DRYWET, widget])
self.w_knobs_right.layout().addWidget(widget)
@@ -621,7 +668,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
widget.setLabel("Volume")
widget.setMinimum(0.0)
widget.setMaximum(1.27)
setScalableDialStyle(widget, PARAMETER_VOLUME, 0, whiteLabels, self.fSkinStyle)
setScalableDialStyle(widget, PARAMETER_VOLUME, whiteLabels, self.fSkinStyle)

self.fParameterList.append([PARAMETER_VOLUME, widget])
self.w_knobs_right.layout().addWidget(widget)


+ 139
- 10
source/frontend/widgets/commondial.py View File

@@ -10,13 +10,13 @@ from math import isnan
from qt_compat import qt_config

if qt_config == 5:
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QRectF, QEvent
from PyQt5.QtGui import QColor, QFont, QLinearGradient, QPainter
from PyQt5.QtWidgets import QDial
from PyQt5.QtWidgets import QDial, QToolTip
elif qt_config == 6:
from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF
from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QRectF, QEvent
from PyQt6.QtGui import QColor, QFont, QLinearGradient, QPainter
from PyQt6.QtWidgets import QDial
from PyQt6.QtWidgets import QDial, QToolTip

# ---------------------------------------------------------------------------------------------------------------------
# Widget Class
@@ -61,6 +61,10 @@ class CommonDial(QDial):
self.fRealValue = 0.0
self.fPrecision = 10000
self.fIsInteger = False
self.fIsButton = False
self.fScalePoints = []
self.fScalePointsPrefix = ""
self.fScalePointsSuffix = ""

self.fIsHovered = False
self.fIsPressed = False
@@ -123,10 +127,17 @@ class CommonDial(QDial):
def setIndex(self, index):
self.fIndex = index

def setPrecision(self, value, isInteger):
def setPrecision(self, value, isInteger, scalePoints, prefix, suffix):
self.fPrecision = value
self.fIsInteger = isInteger
self.fScalePoints = scalePoints
self.fScalePointsPrefix = prefix
self.fScalePointsSuffix = suffix
# NOTE: Booleans are mimic as isInteger with range [0 or 1].
if self.fIsInteger and (self.fMinimum == 0) and (self.fMaximum in (1, 2)):
self.fIsButton = True
QDial.setMaximum(self, int(value))
# print("setPrecision "+str(value)+" "+str(isInteger)+" "+str(self.fIsButton))

def setMinimum(self, value):
self.fMinimum = value
@@ -188,7 +199,11 @@ class CommonDial(QDial):
self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum
self.realValueChanged.emit(self.fRealValue)


def enterEvent(self, event):

self.setFocus(Qt.MouseFocusReason)

self.fIsHovered = True
if self.fHoverStep == self.HOVER_MIN:
self.fHoverStep = self.HOVER_MIN + 1
@@ -206,10 +221,16 @@ class CommonDial(QDial):
return

if event.button() == Qt.LeftButton:
self.fIsPressed = True
self.fLastDragPos = event.pos()
self.fLastDragValue = self.fRealValue
self.dragStateChanged.emit(True)
if self.fIsButton:
value = self.value() + 1;
if (value > self.fMaximum):
value = 0;
self.setValue(value, True)
else:
self.fIsPressed = True
self.fLastDragPos = event.pos()
self.fLastDragValue = self.fRealValue
self.dragStateChanged.emit(True)

def mouseMoveEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
@@ -243,7 +264,71 @@ class CommonDial(QDial):
self.fIsPressed = False
self.dragStateChanged.emit(False)

def applyDelta(self, mod, delta):
if self.fIsButton:
self.setValue(self.rvalue() + delta, True)
return

if self.fIsInteger: # 4 to 50 ticks per revolution
if (mod & Qt.ShiftModifier):
delta = delta * 5
else: # Floats are 250 to 500 ticks per revolution
if (mod == (Qt.ControlModifier + Qt.ShiftModifier)):
delta = delta * 2/5
elif (mod & Qt.ControlModifier):
delta = delta * 2
elif (mod & Qt.ShiftModifier):
delta = delta * 50
else:
delta = delta * 10

self.setValue(self.rvalue() + float(self.fMaximum-self.fMinimum)*(float(delta)/float(self.fPrecision)), True)
return

def wheelEvent(self, event):
direction = event.angleDelta().y()
if direction < 0:
delta = -1.0
elif direction > 0:
delta = 1.0
else:
return

mod = int(event.modifiers())
self.applyDelta(mod, delta)
return

def keyPressEvent(self, event):
key = event.key()
mod = int(event.modifiers())
# print(str(self.value())+" "+str(self.rvalue())+" "+str(self.fMinimum)+" "+str(self.fMaximum))

match key:
case key if Qt.Key_0 <= key <= Qt.Key_9:
if self.fIsButton or (self.fIsInteger and (self.fMinimum == 0) and (self.fMaximum > 2) and (self.fMaximum <= 10)):
self.setValue(key-Qt.Key_0, True)
else:
self.setValue(self.fMinimum + float(self.fMaximum-self.fMinimum)/10.0*(key-Qt.Key_0), True)
case Qt.Key_Home: # NOTE: interferes with Canvas control hotkey
self.setValue(self.fMinimum, True)
case Qt.Key_End:
self.setValue(self.fMaximum, True)
case Qt.Key_PageDown:
self.applyDelta(mod, -1)

case Qt.Key_PageUp:
self.applyDelta(mod, 1)

return

def paintEvent(self, event):

def strFiveDigits(x):
if self.fIsInteger:
x = int(x)
ret = lambda x: float(x) if '.' in str(x) else int(x)
return str(ret(str(x)[:max(str(x).find('.'), 6 + ('-' in str(x)))].strip('.')))

painter = QPainter(self)
event.accept()

@@ -260,7 +345,51 @@ class CommonDial(QDial):
painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
painter.drawText(self.fLabelPos, self.fLabel)

self.paintDial(painter)
if self.fIsButton:
self.paintButton(painter, self.fMaximum)
else:
self.paintDial(painter)

# Display tooltip, above the knob (OS-independent, unlike of mouse tooltip).
# BUG Do not work if value not changed.
if self.fHoverStep == self.HOVER_MAX:
# It looks like {'value': 0.0, 'label': 'Channels 1 + 2'}

# First, we need to find exact or nearest match (index from value).
# It is also tests if we have scale points at all.
num = -1
realValue = self.rvalue()
for i in range(len(self.fScalePoints)):
scaleValue = self.fScalePoints[i]['value']
if i == 0:
finalValue = scaleValue
num = 0
else:
srange2 = abs(realValue - finalValue)
srange1 = abs(realValue - scaleValue)
if srange2 > srange1:
finalValue = scaleValue
num = i
if (srange1 == 0): # Exact match, save some CPU.
break

tip = ""
if (num >= 0): # Scalepoints are used
tip = str(self.fScalePoints[num]['label'])
if not self.fIsButton:
tip = self.fScalePointsPrefix + \
strFiveDigits(self.fScalePoints[num]['value']) + \
self.fScalePointsSuffix + ": " + tip
# We most probably not need tooltip for button, if it is not scalepoint.
elif not self.fIsButton:
tip = strFiveDigits(realValue)

# Wrong vert. position for Calf:
# QToolTip.showText(self.mapToGlobal(QPoint(0, 0-self.geometry().height())), tip)
# FIXME Still wrong vert. position for QT_SCALE_FACTOR=2.
QToolTip.showText(self.mapToGlobal(QPoint(0, 0-45)), tip)
else:
QToolTip.hideText()

painter.restore()



+ 313
- 197
source/frontend/widgets/scalabledial.py View File

@@ -5,18 +5,19 @@
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global)

from math import cos, floor, pi, sin
from math import cos, floor, pi, sin, copysign
import numpy as np

from qt_compat import qt_config

if qt_config == 5:
from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QLinearGradient, QRadialGradient, QConicalGradient, QFontMetrics, QPen
from PyQt5.QtWidgets import QToolTip
elif qt_config == 6:
from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
from PyQt6.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
from PyQt6.QtSvgWidgets import QSvgWidget
from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QPoint, QPointF, QRectF, QTimer, QSize
from PyQt6.QtGui import QColor, QLinearGradient, QRadialGradient, QConicalGradient, QFontMetrics, QPen
from PyQt5.QtWidgets import QToolTip

from .commondial import CommonDial
from carla_shared import fontMetricsHorizontalAdvance
@@ -27,44 +28,14 @@ from carla_shared import fontMetricsHorizontalAdvance
class ScalableDial(CommonDial):
def __init__(self, parent, index=0):
CommonDial.__init__(self, parent, index)

self.fImage = QSvgWidget(":/scalable/dial_03.svg")
self.fImageNum = "01"

if self.fImage.sizeHint().width() > self.fImage.sizeHint().height():
self.fImageOrientation = self.HORIZONTAL
else:
self.fImageOrientation = self.VERTICAL

self.updateSizes()
self.setImage(0)

def getBaseSize(self):
return self.fImageBaseSize

def updateSizes(self):
if isinstance(self.fImage, QPixmap):
self.fImageWidth = self.fImage.width()
self.fImageHeight = self.fImage.height()
else:
self.fImageWidth = self.fImage.sizeHint().width()
self.fImageHeight = self.fImage.sizeHint().height()

if self.fImageWidth < 1:
self.fImageWidth = 1

if self.fImageHeight < 1:
self.fImageHeight = 1

if self.fImageOrientation == self.HORIZONTAL:
self.fImageBaseSize = self.fImageHeight
self.fImageLayersCount = self.fImageWidth / self.fImageHeight
else:
self.fImageBaseSize = self.fImageWidth
self.fImageLayersCount = self.fImageHeight / self.fImageWidth

self.setMinimumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)
self.setMaximumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)

if not self.fLabel:
self.fLabelHeight = 0
self.fLabelWidth = 0
@@ -76,9 +47,9 @@ class ScalableDial(CommonDial):

self.fLabelPos.setX(float(self.fImageBaseSize)/2.0 - float(self.fLabelWidth)/2.0)

if self.fImageNum in ("01", "02", "07", "08", "09", "10"):
if self.fImageId in (1, 2, 7, 8, 9, 10):
self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight)
elif self.fImageNum in ("11",):
elif self.fImageId in (11,):
self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight*2/3)
else:
self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight/2)
@@ -90,46 +61,33 @@ class ScalableDial(CommonDial):
float(self.fImageBaseSize*3)/4.0, self.fImageBaseSize+self.fLabelHeight+5)

def setImage(self, imageId):
self.fImageNum = "%02i" % imageId
if imageId in (2,6,7,8,9,10,11,12,13):
img = ":/bitmaps/dial_%s%s.png" % (self.fImageNum, "" if self.isEnabled() else "d")
else:
img = ":/scalable/dial_%s%s.svg" % (self.fImageNum, "" if self.isEnabled() else "d")

if img.endswith(".png"):
if not isinstance(self.fImage, QPixmap):
self.fImage = QPixmap()
else:
if not isinstance(self.fImage, QSvgWidget):
self.fImage = QSvgWidget()

self.fImage.load(img)
self.fImageId = imageId
self.fImageBaseSize = 30 # internal
if (imageId == 0):
return

if self.fImage.width() > self.fImage.height():
self.fImageOrientation = self.HORIZONTAL
else:
self.fImageOrientation = self.VERTICAL
if imageId in (7, 8, 9, 10): # calf
self.fImageBaseSize = 40
elif imageId in (11, 12, 13): # openav
self.fImageBaseSize = 32

# special svgs
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
# reserved for carla-wet, carla-vol, carla-pan and color
if self.fImageNum == "03":
if self.fImageId == 3:
self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR

# reserved for carla-L and carla-R
elif self.fImageNum == "04":
elif self.fImageId == 4:
self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L

# reserved for zita
elif self.fImageNum == "06":
self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA
self.fImageBaseSize = 26

self.updateSizes()
self.update()

@pyqtSlot()
def slot_updateImage(self):
self.setImage(int(self.fImageNum))
self.setImage(int(self.fImageId))

def minimumSizeHint(self):
return QSize(self.fImageBaseSize, self.fImageBaseSize)
@@ -144,149 +102,307 @@ class ScalableDial(CommonDial):
if event.type() == QEvent.EnabledChange:
self.slot_updateImage()

def paintDial(self, painter):
def checkEnabled(self):
if self.isEnabled():
normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
curLayer = int((self.fImageLayersCount - 1) * normValue)
if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
self.fHoverStep += 1 if self.fIsHovered else -1
QTimer.singleShot(20, self.update)
return 1
else:
return 0

def normValue(self):
return float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)

def drawMark(self, painter, X, Y, r1, r2, angle, width, color):
x = X + r1 * cos(angle)
y = Y - r1 * sin(angle)
painter.setPen(QPen(color, width, cap=Qt.RoundCap))
if not (r1 == r2): # line
x1 = X + r2 * cos(angle)
y1 = Y - r2 * sin(angle)
painter.drawLine(QPointF(x, y), QPointF(x1, y1))
else: # ball
painter.drawEllipse(QRectF(x-width/2, y-width/2, width, width))

def gradMachined(self):
return {5.9, 10.7, 15.7, 20.8, 25.8, 30.6, 40.6, 45.9,
55.9, 60.7, 65.7, 70.8, 75.8, 80.6, 90.6, 95.9}

def grayGrad(self, painter, X, Y, a, b, gradPairs):
if b == -1:
grad = QConicalGradient(X, Y, a)
elif b == -2:
grad = QRadialGradient (X, Y, a)
else:
grad = QLinearGradient (X, Y, a, b)

for i in gradPairs:
grad.setColorAt(int(i)/100.0, QColor.fromHslF(0, 0, (i % 1.0), 1))

return grad


def paintDial(self, painter):

if self.fImageOrientation == self.HORIZONTAL:
xpos = self.fImageBaseSize * curLayer
ypos = 0.0
# Replace Qt draw over substrate bitmap or svg to
# all-in-one widget generated from stratch using Qt only,
# make it highly tuneable, and uniformly look like
# using HSL color model to make same brightness of colored things.
# We can also easily have color tinted (themed) knobs.
# Some things were simplified a little, to gain more speed.
# R: knob nib (cap) radius
def createDial(R, hue1, hue2, barWidth, angleSpan, ticks, dialType, value):

def rectBorder(w):
return QRectF(X-R-w, Y-R-w, (R+w)*2, (R+w)*2)

def gray(luma):
return QColor.fromHslF(0, 0, luma, 1)

X = Y = self.fImageBaseSize / 2 # center

ang0 = int((angleSpan/2+90)*16)
ang1 = -angleSpan*16

enabled = self.checkEnabled()
E = enabled * self.fHoverStep / 40 # enlight
S = enabled * 0.9 # saturation
color1 = QColor.fromHslF(hue1, S, 0.5+E, 1)
color2 = QColor.fromHslF(hue2, S, 0.5+E, 1)

if dialType == 1: # mimic svg dial
# light arc substrate: near black, 0.5 px exposed
painter.setPen(QPen(gray(0.10), barWidth+1, cap=Qt.FlatCap))
painter.drawArc(rectBorder(barWidth), ang0, ang1)

# light arc: gray bar
# should be combined with light (value) arc to be a bit faster ?
painter.setPen(QPen(self.grayGrad(painter, X, Y, 270, -1, {0.20, 100.15}), barWidth, cap=Qt.FlatCap))
painter.drawArc(rectBorder(barWidth), ang0, ang1)

# cap
painter.setBrush(self.grayGrad(painter, X-R, Y-R, R*2, -2, {0.45+E, 100.15+E}))
painter.setPen(QPen(gray(0.10), 0.5))
painter.drawEllipse(rectBorder(1))

elif dialType == 2: # calf
# outer chamfer & leds substrate
painter.setPen(QPen(self.grayGrad(painter, X, Y, 135, -1, {0.15, 50.50, 100.15}), 1.5, cap=Qt.FlatCap))
painter.setBrush(QColor.fromHslF(hue1, S, 0.05+E/2, 1))
painter.drawEllipse(rectBorder(barWidth*2-1))

# machined shiny cap with chamfer
painter.setPen(QPen(self.grayGrad(painter, X, Y, -45, -1, {0.15, 50.50, 100.15}), 1, cap=Qt.FlatCap))
painter.setBrush(self.grayGrad(painter, X, Y, 0, -1, self.gradMachined()))
painter.drawEllipse(rectBorder(1))

elif dialType == 3: # openav
# light arc substrate
painter.setPen(QPen(gray(0.20+E), barWidth))
painter.drawArc(rectBorder(barWidth), ang0, ang1)


# draw arc: forward, or reverse (for 'R' ch knob)
startAngle = int((copysign(angleSpan/2, value) + 90)*16)
gradient = QConicalGradient(X, Y, 270)

if (ticks == 0):
spanAngle = int(-angleSpan*16 * value)
gradient.setColorAt(0.25, color1)
gradient.setColorAt(0.75, color2)
else:
xpos = 0.0
ypos = self.fImageBaseSize * curLayer
# discretize scale: for 10 points, first will lit at 5%,
# then 15%, and last at 95% of normalized value,
# i.e. treshold is: center of point exactly matches knob mark angle
spanAngle = int(-int(angleSpan*value/(360/ticks)+0.5)*(360/ticks)*16)
for i in range(2, ticks-2, 1):
gradient.setColorAt((i+0.5-0.35)/ticks, color1.lighter(100))
gradient.setColorAt((i+0.5) /ticks, Qt.black)
gradient.setColorAt((i+0.5+0.35)/ticks, color1.lighter(100))

painter.setPen(QPen(gradient, barWidth, cap=Qt.RoundCap if dialType == 3 else Qt.FlatCap))
painter.drawArc(QRectF(rectBorder(barWidth)), startAngle, spanAngle)

# do not draw marks on disabled items
if enabled == 0:
return

# Forward or reverse (for 'R' ch knob)
angle = ((0.5-abs(value)) * copysign(angleSpan, value) + 90) * pi/180

match dialType:
case 1: # ball
self.drawMark(painter,X,Y, R*0.8, R*0.8, angle, barWidth/2, color1)
case 2: # line for calf
self.drawMark(painter,X,Y, R*0.6, R*0.9, angle, barWidth/2, Qt.black)
case 3: # line for openav
self.drawMark(painter,X,Y, 0, R+barWidth, angle, barWidth, color1)


# Custom knobs (Dry/Wet and Volume)
if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
hue2 = 0.5 # Blue
hue1 = hue2 if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_VOL else 0.3 # Green

createDial(10, hue1, hue2, 3, 260, 0, 1, self.normValue())

# Custom knobs (L and R)
elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
hue = 0.21 # Lime

# Shift to full negative range (incl. 0) for reversed knob
createDial(8, hue, hue, 2.5, 260, 0, 1, self.normValue()-1.0-np.nextafter(0, 1) if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_R else self.normValue())

# Custom knobs (Color) # internal
elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
# NOTE: here all incoming color data, except hue, is lost.
hue = (self.fCustomPaintColor.hueF() - 0.05) % 1.0

source = QRectF(xpos, ypos, self.fImageBaseSize, self.fImageBaseSize)
createDial(10, hue, hue, 3, 260, 0, 1, self.normValue())

if isinstance(self.fImage, QPixmap):
target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
painter.drawPixmap(target, self.fImage, source)
# Custom knobs
elif self.fImageId in (11, 12, 13): # openav
if self.fImageId == 11:
hue1 = hue2 = 0.05 # Orange
else:
self.fImage.renderer().render(painter, source)

# Custom knobs (Dry/Wet and Volume)
if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
# knob color
colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6)
colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6)

# draw small circle
ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
ballPath = QPainterPath()
ballPath.addEllipse(ballRect)
#painter.drawRect(ballRect)
tmpValue = (0.375 + 0.75*normValue)
ballValue = tmpValue - floor(tmpValue)
ballPoint = ballPath.pointAtPercent(ballValue)

# draw arc
startAngle = 218*16
spanAngle = -255*16*normValue

if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET:
painter.setBrush(colorBlue)
painter.setPen(QPen(colorBlue, 0))
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))

gradient = QConicalGradient(15.5, 15.5, -45)
gradient.setColorAt(0.0, colorBlue)
gradient.setColorAt(0.125, colorBlue)
gradient.setColorAt(0.625, colorGreen)
gradient.setColorAt(0.75, colorGreen)
gradient.setColorAt(0.76, colorGreen)
gradient.setColorAt(1.0, colorGreen)
painter.setBrush(gradient)
painter.setPen(QPen(gradient, 3))
hue2 = 0.5 # Blue
hue1 = hue2 if self.fImageId == 12 else 0.3 # Green

createDial(12, hue1, hue2, 2.5, 270, 0, 3, self.normValue())

elif self.fImageId in (1, 2, 7, 8, 9, 10): # calf
hue = 0.52 # Aqua

createDial(12, hue, hue, 4, (360/36)*29, 36, 2, self.normValue())

else:
print("Unknown type "+ str(self.fImageId) + " knob.")
return


def paintButton(self, painter, range_):

# W: button cap half-size ; w: bar width
# positions: can be 2 or 3
def createButton(W, hue, w, positions, btnType, value):

def rectBorder(w):
return QRectF(X-W-w, Y-W-w, (W+w)*2, (W+w)*2)

def gray(luma):
return QColor.fromHslF(0, 0, luma, 1)

X = Y = self.fImageBaseSize / 2 # center

enabled = self.checkEnabled()
E = enabled * self.fHoverStep / 40 # enlight
S = enabled * 0.9 # saturation
color = QColor.fromHslF(hue, S, 0.5+E, 1)

if btnType == 1: # internal
# light bar substrate: near black, 0.5 px exposed
painter.setPen(QPen(gray(0.10), w+1))
painter.drawLine(QPointF(X-W/2, Y-W-w), QPointF(X+W/2, Y-W-w))

# light bar: gray bar
painter.setPen(QPen(gray(0.20), w))
painter.drawLine(QPointF(X-W/2, Y-W-w), QPointF(X+W/2, Y-W-w))

# cap
painter.setBrush(self.grayGrad(painter, X-W/2, Y-W/2, W*2, -2, {0.15+E, 100.40+E}))
painter.setPen(QPen(gray(0.05), 0.5))
painter.drawRoundedRect(rectBorder(-1), 3, 3)

elif btnType == 2: # calf
# outer chamfer & leds substrate
painter.setPen(QPen(self.grayGrad(painter, X, Y, 135, -1, {24.25, 26.50, 76.50, 78.25}), 1.5, cap=Qt.FlatCap))
painter.setBrush(QColor.fromHslF(hue, S, 0.05+E/2, 1))
painter.drawRoundedRect(QRectF(X-W-1, Y-W-w-0-1, W*2+2, W*2+w+0+2), 4, 4)

# machined shiny cap with chamfer
painter.setPen(QPen(self.grayGrad(painter, X, Y, -45, -1, {24.25, 26.50, 74.50, 76.25}), 1, cap=Qt.FlatCap))
painter.setBrush(self.grayGrad(painter, X, Y, -30, -1, self.gradMachined( )))
painter.drawRoundedRect(rectBorder(-1), 3, 3)

elif btnType == 3: # openav
# light substrate
pen = QPen(gray(0.20+E), w)
painter.setPen(pen)
painter.drawRoundedRect(rectBorder(0), 3, 3)


# draw active lights
if (value > (1/2-(positions-2)*(2/3-1/2))):
if (1/3 < value < 2/3) and (positions == 3):
pos = 1 # Middle
else:
painter.setBrush(colorBlue)
painter.setPen(QPen(colorBlue, 0))
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))

painter.setBrush(colorBlue)
painter.setPen(QPen(colorBlue, 3))

painter.drawArc(QRectF(4.0, 4.0, 26.0, 26.0), int(startAngle), int(spanAngle))

# Custom knobs (L and R)
elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
# knob color
color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6)

# draw small circle
ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
ballPath = QPainterPath()
ballPath.addEllipse(ballRect)
#painter.drawRect(ballRect)
tmpValue = (0.375 + 0.75*normValue)
ballValue = tmpValue - floor(tmpValue)
ballPoint = ballPath.pointAtPercent(ballValue)

painter.setBrush(color)
painter.setPen(QPen(color, 0))
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))

# draw arc
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L:
startAngle = 218*16
spanAngle = -255*16*normValue
else:
startAngle = 322.0*16
spanAngle = 255.0*16*(1.0-normValue)

painter.setPen(QPen(color, 2.5))
painter.drawArc(QRectF(3.5, 3.5, 22.0, 22.0), int(startAngle), int(spanAngle))

# Custom knobs (Color)
elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
# knob color
color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6)

# draw small circle
ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
ballPath = QPainterPath()
ballPath.addEllipse(ballRect)
tmpValue = (0.375 + 0.75*normValue)
ballValue = tmpValue - floor(tmpValue)
ballPoint = ballPath.pointAtPercent(ballValue)

# draw arc
startAngle = 218*16
spanAngle = -255*16*normValue

painter.setBrush(color)
painter.setPen(QPen(color, 0))
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))

painter.setBrush(color)
painter.setPen(QPen(color, 3))
painter.drawArc(QRectF(4.0, 4.8, 26.0, 26.0), int(startAngle), int(spanAngle))

# Custom knobs (Zita)
elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA:
a = normValue * pi * 1.5 - 2.35
r = 10.0
x = 10.5
y = 10.5
x += r * sin(a)
y -= r * cos(a)
painter.setBrush(Qt.black)
painter.setPen(QPen(Qt.black, 2))
painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y))

# Custom knobs
pos = 2 # Max
else:
pos = 0

if btnType == 1: # internal
if (pos > 0):
painter.setPen(QPen(color, w))
if (pos == 1):
painter.drawLine(QPointF(X-W/2, Y-W-w), QPointF(X-w/2, Y-W-w))
else:
painter.drawLine(QPointF(X-W/2, Y-W-w), QPointF(X+W/2, Y-W-w))

elif btnType == 2: # calf
if (pos > 0):
grad = QLinearGradient(X-W, Y, X+W, Y)
for i in ({0.0, 30.6, 40.5, 45.7, 55.7, 60.5, 70.6, 100.0} if (pos>1)
else {20.0, 45.6, 55.6, 80.0}):
grad.setColorAt(int(i)/100.0, QColor.fromHslF(hue, S, (i % 1)+E, 1))
painter.setPen(QPen(grad, w-0.5, cap=Qt.FlatCap))
painter.drawLine(QPointF(X-W, Y-W-w/2), QPointF(X+W, Y-W-w/2))

elif btnType == 3: # openav
painter.setPen(QPen(color, w, cap=Qt.RoundCap))
if (pos > 0):
if (pos == 1):
painter.drawRoundedRect(rectBorder(-W/2), 3, 3)
else:
painter.drawRoundedRect(rectBorder(0), 3, 3)
else:
painter.drawLine(QPointF(X-0.1, Y), QPointF(X+0.1, Y))


# do not draw marks on disabled items
if enabled == 0:
return

if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
self.fHoverStep += 1 if self.fIsHovered else -1
QTimer.singleShot(20, self.update)
match btnType:
case 1: # ball at center
self.drawMark(painter,X,Y, 0, 0, 0, w/2, color)
# case 3: # openav
# painter.setPen(QPen(color, w, cap=Qt.RoundCap))
# painter.drawLine(QPointF(X-0.1, Y), QPointF(X+0.1, Y))

else: # isEnabled()
target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
if isinstance(self.fImage, QPixmap):
painter.drawPixmap(target, self.fImage, target)
else:
self.fImage.renderer().render(painter, target)

positions = range_ + 1;

# Custom knobs (Color) # internal
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
# NOTE: here all incoming color data, except hue, is lost.
hue = (self.fCustomPaintColor.hueF() - 0.05) % 1.0

createButton(10, hue, 3, positions, 1, self.normValue())

# Custom knobs
elif self.fImageId in (1, 2, 7, 8, 9, 10): # calf
hue = 0.55 # Sky

createButton(12, hue, 4, positions, 2, self.normValue())

elif self.fImageId in (11, 12, 13): # openav
hue = 0.05 # Orange

createButton(12, hue, 2.5, positions, 3, self.normValue())

else:
print("Unknown type "+ str(self.fImageId) + " button.")
return

# ---------------------------------------------------------------------------------------------------------------------

Loading…
Cancel
Save