Browse Source

Start python level automated tests, WIP

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.5.0
falkTX 2 years ago
parent
commit
121d7f617b
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
14 changed files with 437 additions and 758 deletions
  1. +11
    -0
      source/frontend/Makefile
  2. +3
    -19
      source/frontend/widgets/canvaspreviewframe.py
  3. +3
    -2
      source/frontend/widgets/collapsablewidget.py
  4. +278
    -0
      source/frontend/widgets/commondial.py
  5. +45
    -51
      source/frontend/widgets/digitalpeakmeter.py
  6. +14
    -13
      source/frontend/widgets/draggablegraphicsview.py
  7. +9
    -7
      source/frontend/widgets/ledbutton.py
  8. +5
    -5
      source/frontend/widgets/paramspinbox.py
  9. +23
    -14
      source/frontend/widgets/pianoroll.py
  10. +0
    -99
      source/frontend/widgets/pixmapbutton.py
  11. +13
    -257
      source/frontend/widgets/pixmapdial.py
  12. +16
    -28
      source/frontend/widgets/pixmapkeyboard.py
  13. +4
    -2
      source/frontend/widgets/scalablebutton.py
  14. +13
    -261
      source/frontend/widgets/scalabledial.py

+ 11
- 0
source/frontend/Makefile View File

@@ -132,6 +132,17 @@ debug:


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


lint:
pylint \
--extension-pkg-whitelist=PyQt5 \
--max-line-length=120 \
--max-locals=25 \
--max-statements=100 \
--disable=bare-except,duplicate-code,invalid-name,missing-docstring,too-many-branches,too-many-instance-attributes,too-many-statements \
$(wildcard widgets/*.py)

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

i18n_update: $(TSs) i18n_update: $(TSs)
i18n_release: $(QMs) i18n_release: $(QMs)




+ 3
- 19
source/frontend/widgets/canvaspreviewframe.py View File

@@ -162,9 +162,9 @@ class CanvasPreviewFrame(QFrame):
if self.fZoomCursors[self._kCursorName] != cursorName: if self.fZoomCursors[self._kCursorName] != cursorName:
prefix = ":/cursors/zoom" prefix = ":/cursors/zoom"
self.fZoomCursors[self._kCursorName] = cursorName self.fZoomCursors[self._kCursorName] = cursorName
self.fZoomCursors[self._kCursorZoom] = QCursor(QPixmap("{}_{}.png".format(prefix, cursorName)), 8, 7)
self.fZoomCursors[self._kCursorZoomIn] = QCursor(QPixmap("{}-in_{}.png".format(prefix, cursorName)), 8, 7)
self.fZoomCursors[self._kCursorZoomOut] = QCursor(QPixmap("{}-out_{}.png".format(prefix, cursorName)), 8, 7)
self.fZoomCursors[self._kCursorZoom] = QCursor(QPixmap(f"{prefix}_{cursorName}.png"), 8, 7)
self.fZoomCursors[self._kCursorZoomIn] = QCursor(QPixmap(f"{prefix}-in_{cursorName}.png"), 8, 7)
self.fZoomCursors[self._kCursorZoomOut] = QCursor(QPixmap(f"{prefix}-out_{cursorName}.png"), 8, 7)


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


@@ -406,19 +406,3 @@ class CanvasPreviewFrame(QFrame):
self.fFrameWidth = 1 if self.fUseCustomPaint else self.frameWidth() self.fFrameWidth = 1 if self.fUseCustomPaint else self.frameWidth()


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

if __name__ == '__main__':
# pylint: disable=unused-import
# pylint: disable=ungrouped-imports
import sys
import resources_rc
from PyQt5.QtWidgets import QApplication
# pylint: enable=unused-import
# pylint: enable=ungrouped-imports

app = QApplication(sys.argv)

gui = CanvasPreviewFrame(None)
gui.show()

sys.exit(app.exec_())

+ 3
- 2
source/frontend/widgets/collapsablewidget.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Collapsible Box, a custom Qt widget # Collapsible Box, a custom Qt widget
# Copyright (C) 2019 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2019-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -20,7 +20,6 @@
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import pyqtSlot, Qt from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QCursor, QFont
from PyQt5.QtWidgets import QFrame, QSizePolicy, QToolButton, QVBoxLayout, QWidget from PyQt5.QtWidgets import QFrame, QSizePolicy, QToolButton, QVBoxLayout, QWidget


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
@@ -75,3 +74,5 @@ class CollapsibleBox(QFrame):


def getContentLayout(self): def getContentLayout(self):
return self.content_layout return self.content_layout

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

+ 278
- 0
source/frontend/widgets/commondial.py View File

@@ -0,0 +1,278 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Common Dial, a custom Qt widget
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
#
# This program 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 2 of
# the License, or 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.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global)

from math import isnan

from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF
from PyQt5.QtGui import QColor, QFont, QLinearGradient, QPainter
from PyQt5.QtWidgets import QDial

# ---------------------------------------------------------------------------------------------------------------------
# Widget Class

# to be implemented by subclasses
#def updateSizes(self):
#def paintDial(self, painter):

class CommonDial(QDial):
# enum CustomPaintMode
CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient)
CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3)
CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3)
CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3)
CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3)
CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6)
CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient

# enum Orientation
HORIZONTAL = 0
VERTICAL = 1

HOVER_MIN = 0
HOVER_MAX = 9

MODE_DEFAULT = 0
MODE_LINEAR = 1

# signals
dragStateChanged = pyqtSignal(bool)
realValueChanged = pyqtSignal(float)

def __init__(self, parent, index):
QDial.__init__(self, parent)

self.fDialMode = self.MODE_LINEAR

self.fMinimum = 0.0
self.fMaximum = 1.0
self.fRealValue = 0.0
self.fPrecision = 10000
self.fIsInteger = False

self.fIsHovered = False
self.fIsPressed = False
self.fHoverStep = self.HOVER_MIN

self.fLastDragPos = None
self.fLastDragValue = 0.0

self.fIndex = index

self.fLabel = ""
self.fLabelPos = QPointF(0.0, 0.0)
self.fLabelFont = QFont(self.font())
self.fLabelFont.setPixelSize(8)
self.fLabelWidth = 0
self.fLabelHeight = 0

if self.palette().window().color().lightness() > 100:
# Light background
c = self.palette().dark().color()
self.fLabelGradientColor1 = c
self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0)
self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
else:
# Dark background
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2)

self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0)

self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL
self.fCustomPaintColor = QColor(0xff, 0xff, 0xff)

# Fake internal value, custom precision
QDial.setMinimum(self, 0)
QDial.setMaximum(self, self.fPrecision)
QDial.setValue(self, 0)

self.valueChanged.connect(self.slot_valueChanged)

def forceWhiteLabelGradientText(self):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

def setLabelColor(self, enabled, disabled):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [enabled, disabled]

def getIndex(self):
return self.fIndex

def setIndex(self, index):
self.fIndex = index

def setPrecision(self, value, isInteger):
self.fPrecision = value
self.fIsInteger = isInteger
QDial.setMaximum(self, int(value))

def setMinimum(self, value):
self.fMinimum = value

def setMaximum(self, value):
self.fMaximum = value

def rvalue(self):
return self.fRealValue

def setValue(self, value, emitSignal=False):
if self.fRealValue == value or isnan(value):
return

if value <= self.fMinimum:
qtValue = 0
self.fRealValue = self.fMinimum

elif value >= self.fMaximum:
qtValue = int(self.fPrecision)
self.fRealValue = self.fMaximum

else:
qtValue = round(float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision)
self.fRealValue = value

# Block change signal, we'll handle it ourselves
self.blockSignals(True)
QDial.setValue(self, qtValue)
self.blockSignals(False)

if emitSignal:
self.realValueChanged.emit(self.fRealValue)

def setCustomPaintMode(self, paintMode):
if self.fCustomPaintMode == paintMode:
return

self.fCustomPaintMode = paintMode
self.update()

def setCustomPaintColor(self, color):
if self.fCustomPaintColor == color:
return

self.fCustomPaintColor = color
self.update()

def setLabel(self, label):
if self.fLabel == label:
return

self.fLabel = label
self.updateSizes()
self.update()

@pyqtSlot(int)
def slot_valueChanged(self, value):
self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum
self.realValueChanged.emit(self.fRealValue)

def enterEvent(self, event):
self.fIsHovered = True
if self.fHoverStep == self.HOVER_MIN:
self.fHoverStep = self.HOVER_MIN + 1
QDial.enterEvent(self, event)

def leaveEvent(self, event):
self.fIsHovered = False
if self.fHoverStep == self.HOVER_MAX:
self.fHoverStep = self.HOVER_MAX - 1
QDial.leaveEvent(self, event)

def mousePressEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
QDial.mousePressEvent(self, event)
return

if event.button() == Qt.LeftButton:
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:
QDial.mouseMoveEvent(self, event)
return

if not self.fIsPressed:
return

diff = (self.fMaximum - self.fMinimum) / 4.0
pos = event.pos()
dx = diff * float(pos.x() - self.fLastDragPos.x()) / self.width()
dy = diff * float(pos.y() - self.fLastDragPos.y()) / self.height()
value = self.fLastDragValue + dx - dy

if value < self.fMinimum:
value = self.fMinimum
elif value > self.fMaximum:
value = self.fMaximum
elif self.fIsInteger:
value = float(round(value))

self.setValue(value, True)

def mouseReleaseEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
QDial.mouseReleaseEvent(self, event)
return

if self.fIsPressed:
self.fIsPressed = False
self.dragStateChanged.emit(False)

def paintEvent(self, event):
painter = QPainter(self)
event.accept()

painter.save()
painter.setRenderHint(QPainter.Antialiasing, True)

if self.fLabel:
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
painter.setPen(self.fLabelGradientColor2)
painter.setBrush(self.fLabelGradient)
painter.drawRect(self.fLabelGradientRect)

painter.setFont(self.fLabelFont)
painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
painter.drawText(self.fLabelPos, self.fLabel)

self.paintDial(painter)

painter.restore()

def resizeEvent(self, event):
QDial.resizeEvent(self, event)
self.updateSizes()

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

+ 45
- 51
source/frontend/widgets/digitalpeakmeter.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Digital Peak Meter, a custom Qt widget # Digital Peak Meter, a custom Qt widget
# Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -16,16 +16,16 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from math import sqrt from math import sqrt


from PyQt5.QtCore import qCritical, Qt, QTimer, QSize, QLineF, QRectF
from PyQt5.QtCore import qCritical, Qt, QSize, QLineF, QRectF
from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class # Widget Class


class DigitalPeakMeter(QWidget): class DigitalPeakMeter(QWidget):
@@ -43,7 +43,7 @@ class DigitalPeakMeter(QWidget):
STYLE_RNCBC = 3 STYLE_RNCBC = 3
STYLE_CALF = 4 STYLE_CALF = 4


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


def __init__(self, parent): def __init__(self, parent):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
@@ -71,7 +71,7 @@ class DigitalPeakMeter(QWidget):


self.updateGrandient() self.updateGrandient()


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


def channelCount(self): def channelCount(self):
return self.fChannelCount return self.fChannelCount
@@ -81,13 +81,14 @@ class DigitalPeakMeter(QWidget):
return return


if count < 0: if count < 0:
return qCritical("DigitalPeakMeter::setChannelCount(%i) - channel count must be a positive integer or zero" % count)
qCritical(f"DigitalPeakMeter::setChannelCount({count}) - channel count must be a positive integer or zero")
return


self.fChannelCount = count self.fChannelCount = count
self.fChannelData = [] self.fChannelData = []
self.fLastChannelData = [] self.fLastChannelData = []


for x in range(count):
for _ in range(count):
self.fChannelData.append(0.0) self.fChannelData.append(0.0)
self.fLastChannelData.append(0.0) self.fLastChannelData.append(0.0)


@@ -98,7 +99,7 @@ class DigitalPeakMeter(QWidget):
self.setMinimumSize(0, 0) self.setMinimumSize(0, 0)
self.setMaximumSize(9999, 9999) self.setMaximumSize(9999, 9999)


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


def meterColor(self): def meterColor(self):
return self.fMeterColor return self.fMeterColor
@@ -108,7 +109,8 @@ class DigitalPeakMeter(QWidget):
return return


if color not in (self.COLOR_GREEN, self.COLOR_BLUE): if color not in (self.COLOR_GREEN, self.COLOR_BLUE):
return qCritical("DigitalPeakMeter::setMeterColor(%i) - invalid color" % color)
qCritical(f"DigitalPeakMeter::setMeterColor({color}) - invalid color")
return


if color == self.COLOR_GREEN: if color == self.COLOR_GREEN:
self.fMeterColorBase = QColor(93, 231, 61) self.fMeterColorBase = QColor(93, 231, 61)
@@ -121,7 +123,7 @@ class DigitalPeakMeter(QWidget):


self.updateGrandient() self.updateGrandient()


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


def meterLinesEnabled(self): def meterLinesEnabled(self):
return self.fMeterLinesEnabled return self.fMeterLinesEnabled
@@ -132,7 +134,7 @@ class DigitalPeakMeter(QWidget):


self.fMeterLinesEnabled = yesNo self.fMeterLinesEnabled = yesNo


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


def meterOrientation(self): def meterOrientation(self):
return self.fMeterOrientation return self.fMeterOrientation
@@ -142,13 +144,14 @@ class DigitalPeakMeter(QWidget):
return return


if orientation not in (self.HORIZONTAL, self.VERTICAL): if orientation not in (self.HORIZONTAL, self.VERTICAL):
return qCritical("DigitalPeakMeter::setMeterOrientation(%i) - invalid orientation" % orientation)
qCritical(f"DigitalPeakMeter::setMeterOrientation({orientation}) - invalid orientation")
return


self.fMeterOrientation = orientation self.fMeterOrientation = orientation


self.updateGrandient() self.updateGrandient()


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


def meterStyle(self): def meterStyle(self):
return self.fMeterStyle return self.fMeterStyle
@@ -158,7 +161,8 @@ class DigitalPeakMeter(QWidget):
return return


if style not in (self.STYLE_DEFAULT, self.STYLE_OPENAV, self.STYLE_RNCBC, self.STYLE_CALF): if style not in (self.STYLE_DEFAULT, self.STYLE_OPENAV, self.STYLE_RNCBC, self.STYLE_CALF):
return qCritical("DigitalPeakMeter::setMeterStyle(%i) - invalid style" % style)
qCritical(f"DigitalPeakMeter::setMeterStyle({style}) - invalid style")
return


if style == self.STYLE_DEFAULT: if style == self.STYLE_DEFAULT:
self.fMeterBackground = QColor("#070707") self.fMeterBackground = QColor("#070707")
@@ -182,7 +186,7 @@ class DigitalPeakMeter(QWidget):


self.updateGrandient() self.updateGrandient()


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


def smoothMultiplier(self): def smoothMultiplier(self):
return self.fSmoothMultiplier return self.fSmoothMultiplier
@@ -192,28 +196,37 @@ class DigitalPeakMeter(QWidget):
return return


if not isinstance(value, int): if not isinstance(value, int):
return qCritical("DigitalPeakMeter::setSmoothMultiplier() - value must be an integer")
qCritical("DigitalPeakMeter::setSmoothMultiplier() - value must be an integer")
return
if value < 0: if value < 0:
return qCritical("DigitalPeakMeter::setSmoothMultiplier(%i) - value must be >= 0" % value)
qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be >= 0")
return
if value > 5: if value > 5:
return qCritical("DigitalPeakMeter::setSmoothMultiplier(%i) - value must be < 5" % value)
qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be < 5")
return


self.fSmoothMultiplier = value self.fSmoothMultiplier = value


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


def displayMeter(self, meter, level, forced = False): def displayMeter(self, meter, level, forced = False):
if not isinstance(meter, int): if not isinstance(meter, int):
return qCritical("DigitalPeakMeter::displayMeter(,) - meter value must be an integer")
qCritical("DigitalPeakMeter::displayMeter(,) - meter value must be an integer")
return
if not isinstance(level, float): if not isinstance(level, float):
return qCritical("DigitalPeakMeter::displayMeter(%i,) - level value must be a float" % (meter,))
qCritical(f"DigitalPeakMeter::displayMeter({meter},) - level value must be a float")
return
if meter <= 0 or meter > self.fChannelCount: if meter <= 0 or meter > self.fChannelCount:
return qCritical("DigitalPeakMeter::displayMeter(%i, %f) - invalid meter number" % (meter, level))
qCritical(f"DigitalPeakMeter::displayMeter({meter}, {level}) - invalid meter number")
return


i = meter - 1 i = meter - 1


if self.fSmoothMultiplier > 0 and not forced: if self.fSmoothMultiplier > 0 and not forced:
level = (self.fLastChannelData[i] * float(self.fSmoothMultiplier) + level) / float(self.fSmoothMultiplier + 1)
level = (
(self.fLastChannelData[i] * float(self.fSmoothMultiplier) + level)
/ float(self.fSmoothMultiplier + 1)
)


if level < 0.001: if level < 0.001:
level = 0.0 level = 0.0
@@ -226,7 +239,7 @@ class DigitalPeakMeter(QWidget):


self.fLastChannelData[i] = level self.fLastChannelData[i] = level


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


def updateGrandient(self): def updateGrandient(self):
self.fMeterGradient = QLinearGradient(0, 0, 1, 1) self.fMeterGradient = QLinearGradient(0, 0, 1, 1)
@@ -287,15 +300,15 @@ class DigitalPeakMeter(QWidget):
elif self.fMeterOrientation == self.VERTICAL: elif self.fMeterOrientation == self.VERTICAL:
self.fMeterGradient.setFinalStop(0, self.height()) self.fMeterGradient.setFinalStop(0, self.height())


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


def minimumSizeHint(self): def minimumSizeHint(self):
return QSize(10, 10)
return QSize(20, 10) if self.fMeterOrientation == self.HORIZONTAL else QSize(10, 20)


def sizeHint(self): def sizeHint(self):
return QSize(self.width(), self.height()) return QSize(self.width(), self.height())


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


def drawCalf(self, event): def drawCalf(self, event):
painter = QPainter(self) painter = QPainter(self)
@@ -323,7 +336,8 @@ class DigitalPeakMeter(QWidget):


def paintEvent(self, event): def paintEvent(self, event):
if self.fMeterStyle == self.STYLE_CALF: if self.fMeterStyle == self.STYLE_CALF:
return self.drawCalf(event)
self.drawCalf(event)
return


painter = QPainter(self) painter = QPainter(self)
event.accept() event.accept()
@@ -440,30 +454,10 @@ class DigitalPeakMeter(QWidget):
painter.setPen(QColor(110, 15, 15, 100)) painter.setPen(QColor(110, 15, 15, 100))
painter.drawLine(QLineF(2, lsmall - (lsmall * 0.96), lfull-2.0, lsmall - (lsmall * 0.96))) painter.drawLine(QLineF(2, lsmall - (lsmall * 0.96), lfull-2.0, lsmall - (lsmall * 0.96)))


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


def resizeEvent(self, event): def resizeEvent(self, event):
QWidget.resizeEvent(self, event) QWidget.resizeEvent(self, event)
self.updateGrandientFinalStop() self.updateGrandientFinalStop()


# ------------------------------------------------------------------------------------------------------------
# Main Testing

if __name__ == '__main__':
import sys
import resources_rc
from PyQt5.QtWidgets import QApplication

app = QApplication(sys.argv)

gui = DigitalPeakMeter(None)
gui.setChannelCount(2)
#gui.setMeterOrientation(DigitalPeakMeter.HORIZONTAL)
gui.setMeterStyle(DigitalPeakMeter.STYLE_RNCBC)
gui.displayMeter(1, 0.5)
gui.displayMeter(2, 0.8)
gui.show()

sys.exit(app.exec_())

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

+ 14
- 13
source/frontend/widgets/draggablegraphicsview.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Middle-click draggable QGraphicsView # Middle-click draggable QGraphicsView
# Copyright (C) 2016-2020 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2016-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -16,19 +16,20 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import Qt, QEvent, QTimer
import os
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QCursor, QMouseEvent from PyQt5.QtGui import QCursor, QMouseEvent
from PyQt5.QtWidgets import QGraphicsView
from PyQt5.QtWidgets import QGraphicsView, QMessageBox


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Custom Stuff) # Imports (Custom Stuff)


from carla_shared import *
from carla_shared import MACOS, CustomMessageBox, gCarla


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class # Widget Class


class DraggableGraphicsView(QGraphicsView): class DraggableGraphicsView(QGraphicsView):
@@ -49,7 +50,7 @@ class DraggableGraphicsView(QGraphicsView):


self.setAcceptDrops(True) self.setAcceptDrops(True)


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


def isDragUrlValid(self, filename): def isDragUrlValid(self, filename):
lfilename = filename.lower() lfilename = filename.lower()
@@ -59,7 +60,7 @@ class DraggableGraphicsView(QGraphicsView):
#return True #return True
if MACOS and lfilename.endswith(".vst"): if MACOS and lfilename.endswith(".vst"):
return True return True
elif lfilename.endswith(".vst3") and ".vst3" in self.fSupportedExtensions:
if lfilename.endswith(".vst3") and ".vst3" in self.fSupportedExtensions:
return True return True


elif os.path.isfile(filename): elif os.path.isfile(filename):
@@ -68,7 +69,7 @@ class DraggableGraphicsView(QGraphicsView):


return False return False


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


def dragEnterEvent(self, event): def dragEnterEvent(self, event):
urls = event.mimeData().urls() urls = event.mimeData().urls()
@@ -93,7 +94,7 @@ class DraggableGraphicsView(QGraphicsView):
self.fWasLastDragValid = False self.fWasLastDragValid = False
QGraphicsView.dragLeaveEvent(self, event) QGraphicsView.dragLeaveEvent(self, event)


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


def dropEvent(self, event): def dropEvent(self, event):
event.acceptProposedAction() event.acceptProposedAction()
@@ -111,7 +112,7 @@ class DraggableGraphicsView(QGraphicsView):
self.tr("Failed to load file"), self.tr("Failed to load file"),
gCarla.gui.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) gCarla.gui.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)


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


def mousePressEvent(self, event): def mousePressEvent(self, event):
if event.button() == self.fMiddleButton and not (event.modifiers() & Qt.ControlModifier): if event.button() == self.fMiddleButton and not (event.modifiers() & Qt.ControlModifier):
@@ -151,4 +152,4 @@ class DraggableGraphicsView(QGraphicsView):
return return
QGraphicsView.wheelEvent(self, event) QGraphicsView.wheelEvent(self, event)


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

+ 9
- 7
source/frontend/widgets/ledbutton.py View File

@@ -16,7 +16,7 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import QRectF from PyQt5.QtCore import QRectF
@@ -24,7 +24,7 @@ from PyQt5.QtGui import QPainter, QPixmap
from PyQt5.QtSvg import QSvgWidget from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import QPushButton from PyQt5.QtWidgets import QPushButton


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class # Widget Class


class LEDButton(QPushButton): class LEDButton(QPushButton):
@@ -61,10 +61,10 @@ class LEDButton(QPushButton):
self.fLastColor = self.UNSET self.fLastColor = self.UNSET


if self._loadImageNowIfNeeded(): if self._loadImageNowIfNeeded():
if isinstance(self.fImage, QPixmap):
size = self.fImage.width()
else:
size = self.fImage.sizeHint().width()
#if isinstance(self.fImage, QPixmap):
#size = self.fImage.width()
#else:
#size = self.fImage.sizeHint().width()


self.fRect = QRectF(self.fImage.rect()) self.fRect = QRectF(self.fImage.rect())
self.setFixedSize(self.fImage.size()) self.setFixedSize(self.fImage.size())
@@ -87,7 +87,7 @@ class LEDButton(QPushButton):
def _loadImageNowIfNeeded(self): def _loadImageNowIfNeeded(self):
if self.isChecked(): if self.isChecked():
if self.fLastColor == self.fColor: if self.fLastColor == self.fColor:
return
return False
if self.fColor == self.OFF: if self.fColor == self.OFF:
img = ":/scalable/led_off.svg" img = ":/scalable/led_off.svg"
elif self.fColor == self.BLUE: elif self.fColor == self.BLUE:
@@ -122,3 +122,5 @@ class LEDButton(QPushButton):
self.update() self.update()


return True return True

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

+ 5
- 5
source/frontend/widgets/paramspinbox.py View File

@@ -76,7 +76,9 @@ class CustomInputDialog(QDialog):
text = "<table>" text = "<table>"
for scalePoint in scalePoints: for scalePoint in scalePoints:
valuestr = ("%i" if decimals == 0 else "%f") % scalePoint['value'] valuestr = ("%i" if decimals == 0 else "%f") % scalePoint['value']
text += "<tr><td align='right'>%s</td><td align='left'> - %s</td></tr>" % (valuestr, scalePoint['label'])
text += "<tr>"
text += f"<td align='right'>{valuestr}</td><td align='left'> - {scalePoint['label']}</td>"
text += "</tr>"
text += "</table>" text += "</table>"
self.ui.textBrowser.setText(text) self.ui.textBrowser.setText(text)
self.resize(200, 300) self.resize(200, 300)
@@ -341,10 +343,8 @@ class ParamSpinBox(QAbstractSpinBox):
else: else:
self.fStep = value self.fStep = value


if self.fStepSmall > value:
self.fStepSmall = value
if self.fStepLarge < value:
self.fStepLarge = value
self.fStepSmall = min(self.fStepSmall, value)
self.fStepLarge = max(self.fStepLarge, value)


self.fBar.fIsInteger = bool(self.fStepSmall == 1.0) self.fBar.fIsInteger = bool(self.fStepSmall == 1.0)




+ 23
- 14
source/frontend/widgets/pianoroll.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# A piano roll viewer/editor # A piano roll viewer/editor
# Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2014-2015 Perry Nguyen # Copyright (C) 2014-2015 Perry Nguyen
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
@@ -21,15 +21,15 @@
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal
from PyQt5.QtGui import QColor, QCursor, QFont, QPen, QPainter, QTransform
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
from PyQt5.QtGui import QColor, QCursor, QFont, QPen, QPainter
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem, QGraphicsRectItem, QGraphicsSimpleTextItem
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
from PyQt5.QtWidgets import QApplication, QComboBox, QHBoxLayout, QLabel, QStyle, QVBoxLayout, QWidget
from PyQt5.QtWidgets import QApplication, QStyle, QWidget


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# Imports (Custom) # Imports (Custom)


from carla_shared import *
#from carla_shared import *


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# MIDI definitions, copied from CarlaMIDI.h # MIDI definitions, copied from CarlaMIDI.h
@@ -57,17 +57,26 @@ def MIDI_IS_SYSTEM_MESSAGE(status): return status >= MIDI_STATUS_BIT and s
def MIDI_IS_OSC_MESSAGE(status): return status == '/' or status == '#' def MIDI_IS_OSC_MESSAGE(status): return status == '/' or status == '#'


# MIDI Channel message type # MIDI Channel message type
def MIDI_IS_STATUS_NOTE_OFF(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_OFF
def MIDI_IS_STATUS_NOTE_ON(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_ON
def MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_POLYPHONIC_AFTERTOUCH
def MIDI_IS_STATUS_CONTROL_CHANGE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CONTROL_CHANGE
def MIDI_IS_STATUS_PROGRAM_CHANGE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PROGRAM_CHANGE
def MIDI_IS_STATUS_CHANNEL_PRESSURE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CHANNEL_PRESSURE
def MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PITCH_WHEEL_CONTROL
def MIDI_IS_STATUS_NOTE_OFF(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_OFF
def MIDI_IS_STATUS_NOTE_ON(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_ON
def MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_POLYPHONIC_AFTERTOUCH
def MIDI_IS_STATUS_CONTROL_CHANGE(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CONTROL_CHANGE
def MIDI_IS_STATUS_PROGRAM_CHANGE(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PROGRAM_CHANGE
def MIDI_IS_STATUS_CHANNEL_PRESSURE(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CHANNEL_PRESSURE
def MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status):
return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PITCH_WHEEL_CONTROL


# MIDI Utils # MIDI Utils
def MIDI_GET_STATUS_FROM_DATA(data): return data[0] & MIDI_STATUS_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else data[0]
def MIDI_GET_CHANNEL_FROM_DATA(data): return data[0] & MIDI_CHANNEL_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else 0
def MIDI_GET_STATUS_FROM_DATA(data):
return data[0] & MIDI_STATUS_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else data[0]
def MIDI_GET_CHANNEL_FROM_DATA(data):
return data[0] & MIDI_CHANNEL_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else 0


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Graphics Items # Graphics Items


+ 0
- 99
source/frontend/widgets/pixmapbutton.py View File

@@ -1,99 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Pixmap Button, a custom Qt widget
# Copyright (C) 2013-2019 Filipe Coelho <falktx@falktx.com>
#
# This program 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 2 of
# the License, or 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.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from PyQt5.QtCore import QPointF, QRectF
from PyQt5.QtGui import QColor, QFont, QPainter, QPixmap
from PyQt5.QtWidgets import QPushButton

# ------------------------------------------------------------------------------------------------------------
# Widget Class

class PixmapButton(QPushButton):
def __init__(self, parent):
QPushButton.__init__(self, parent)

self.fPixmapNormal = QPixmap()
self.fPixmapDown = QPixmap()
self.fPixmapHover = QPixmap()
self.fPixmapRect = QRectF(0, 0, 0, 0)

self.fIsHovered = False

self.fTopText = ""
self.fTopTextColor = QColor()
self.fTopTextFont = QFont()

self.setText("")

def setPixmaps(self, normal, down, hover):
self.fPixmapNormal.load(normal)
self.fPixmapDown.load(down)
self.fPixmapHover.load(hover)

width = self.fPixmapNormal.width()
height = self.fPixmapNormal.height()

self.fPixmapRect = QRectF(0, 0, width, height)

self.setMinimumSize(width, height)
self.setMaximumSize(width, height)

def setTopText(self, text, color, font):
self.fTopText = text
self.fTopTextColor = color
self.fTopTextFont = font

def enterEvent(self, event):
self.fIsHovered = True
QPushButton.enterEvent(self, event)

def leaveEvent(self, event):
self.fIsHovered = False
QPushButton.leaveEvent(self, event)

def paintEvent(self, event):
painter = QPainter(self)
event.accept()

if not self.isEnabled():
painter.save()
painter.setOpacity(0.2)
painter.drawPixmap(self.fPixmapRect, self.fPixmapNormal, self.fPixmapRect)
painter.restore()

elif self.isChecked() or self.isDown():
painter.drawPixmap(self.fPixmapRect, self.fPixmapDown, self.fPixmapRect)

elif self.fIsHovered:
painter.drawPixmap(self.fPixmapRect, self.fPixmapHover, self.fPixmapRect)

else:
painter.drawPixmap(self.fPixmapRect, self.fPixmapNormal, self.fPixmapRect)

if not self.fTopText:
return

painter.save()
painter.setPen(self.fTopTextColor)
painter.setBrush(self.fTopTextColor)
painter.setFont(self.fTopTextFont)
painter.drawText(QPointF(10, 16), self.fTopText)
painter.restore()

+ 13
- 257
source/frontend/widgets/pixmapdial.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Pixmap Dial, a custom Qt widget # Pixmap Dial, a custom Qt widget
# Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -16,64 +16,24 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from math import cos, floor, pi, sin, isnan
from math import cos, floor, pi, sin


from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QConicalGradient, QFont, QFontMetrics
from PyQt5.QtGui import QLinearGradient, QPainter, QPainterPath, QPen, QPixmap
from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
from PyQt5.QtWidgets import QDial from PyQt5.QtWidgets import QDial


# ------------------------------------------------------------------------------------------------------------
# Widget Class

class PixmapDial(QDial):
# enum CustomPaintMode
CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient)
CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3)
CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3)
CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3)
CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3)
CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6)
CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient

# enum Orientation
HORIZONTAL = 0
VERTICAL = 1

HOVER_MIN = 0
HOVER_MAX = 9
from .commondial import CommonDial


MODE_DEFAULT = 0
MODE_LINEAR = 1

# signals
dragStateChanged = pyqtSignal(bool)
realValueChanged = pyqtSignal(float)
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class


class PixmapDial(CommonDial):
def __init__(self, parent, index=0): def __init__(self, parent, index=0):
QDial.__init__(self, parent)

self.fDialMode = self.MODE_LINEAR

self.fMinimum = 0.0
self.fMaximum = 1.0
self.fRealValue = 0.0
self.fPrecision = 10000
self.fIsInteger = False
CommonDial.__init__(self, parent, index)


self.fIsHovered = False
self.fIsPressed = False
self.fHoverStep = self.HOVER_MIN

self.fLastDragPos = None
self.fLastDragValue = 0.0

self.fIndex = index
self.fPixmap = QPixmap(":/bitmaps/dial_01d.png") self.fPixmap = QPixmap(":/bitmaps/dial_01d.png")
self.fPixmapNum = "01" self.fPixmapNum = "01"


@@ -82,60 +42,11 @@ class PixmapDial(QDial):
else: else:
self.fPixmapOrientation = self.VERTICAL self.fPixmapOrientation = self.VERTICAL


self.fLabel = ""
self.fLabelPos = QPointF(0.0, 0.0)
self.fLabelFont = QFont(self.font())
self.fLabelFont.setPixelSize(8)
self.fLabelWidth = 0
self.fLabelHeight = 0

if self.palette().window().color().lightness() > 100:
# Light background
c = self.palette().dark().color()
self.fLabelGradientColor1 = c
self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0)
self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
else:
# Dark background
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2)

self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0)

self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL
self.fCustomPaintColor = QColor(0xff, 0xff, 0xff)

self.updateSizes() self.updateSizes()


# Fake internal value, custom precision
QDial.setMinimum(self, 0)
QDial.setMaximum(self, self.fPrecision)
QDial.setValue(self, 0)

self.valueChanged.connect(self.slot_valueChanged)

def getIndex(self):
return self.fIndex

def getBaseSize(self): def getBaseSize(self):
return self.fPixmapBaseSize return self.fPixmapBaseSize


def forceWhiteLabelGradientText(self):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

def setLabelColor(self, enabled, disabled):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [enabled, disabled]

def updateSizes(self): def updateSizes(self):
self.fPixmapWidth = self.fPixmap.width() self.fPixmapWidth = self.fPixmap.width()
self.fPixmapHeight = self.fPixmap.height() self.fPixmapHeight = self.fPixmap.height()
@@ -178,31 +89,6 @@ class PixmapDial(QDial):


self.fLabelGradientRect = QRectF(float(self.fPixmapBaseSize)/8.0, float(self.fPixmapBaseSize)/2.0, float(self.fPixmapBaseSize*3)/4.0, self.fPixmapBaseSize+self.fLabelHeight+5) self.fLabelGradientRect = QRectF(float(self.fPixmapBaseSize)/8.0, float(self.fPixmapBaseSize)/2.0, float(self.fPixmapBaseSize*3)/4.0, self.fPixmapBaseSize+self.fLabelHeight+5)


def setCustomPaintMode(self, paintMode):
if self.fCustomPaintMode == paintMode:
return

self.fCustomPaintMode = paintMode
self.update()

def setCustomPaintColor(self, color):
if self.fCustomPaintColor == color:
return

self.fCustomPaintColor = color
self.update()

def setLabel(self, label):
if self.fLabel == label:
return

self.fLabel = label
self.updateSizes()
self.update()

def setIndex(self, index):
self.fIndex = index

def setPixmap(self, pixmapId): def setPixmap(self, pixmapId):
self.fPixmapNum = "%02i" % pixmapId self.fPixmapNum = "%02i" % pixmapId
self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d")) self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d"))
@@ -229,46 +115,6 @@ class PixmapDial(QDial):
self.updateSizes() self.updateSizes()
self.update() self.update()


def setPrecision(self, value, isInteger):
self.fPrecision = value
self.fIsInteger = isInteger
QDial.setMaximum(self, value)

def setMinimum(self, value):
self.fMinimum = value

def setMaximum(self, value):
self.fMaximum = value

def setValue(self, value, emitSignal=False):
if self.fRealValue == value or isnan(value):
return

if value <= self.fMinimum:
qtValue = 0
self.fRealValue = self.fMinimum

elif value >= self.fMaximum:
qtValue = self.fPrecision
self.fRealValue = self.fMaximum

else:
qtValue = round(float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision)
self.fRealValue = value

# Block change signal, we'll handle it ourselves
self.blockSignals(True)
QDial.setValue(self, qtValue)
self.blockSignals(False)

if emitSignal:
self.realValueChanged.emit(self.fRealValue)

@pyqtSlot(int)
def slot_valueChanged(self, value):
self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum
self.realValueChanged.emit(self.fRealValue)

@pyqtSlot() @pyqtSlot()
def slot_updatePixmap(self): def slot_updatePixmap(self):
self.setPixmap(int(self.fPixmapNum)) self.setPixmap(int(self.fPixmapNum))
@@ -280,81 +126,13 @@ class PixmapDial(QDial):
return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize) return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize)


def changeEvent(self, event): def changeEvent(self, event):
QDial.changeEvent(self, event)
CommonDial.changeEvent(self, event)


# Force pixmap update if enabled state changes # Force pixmap update if enabled state changes
if event.type() == QEvent.EnabledChange: if event.type() == QEvent.EnabledChange:
self.setPixmap(int(self.fPixmapNum)) self.setPixmap(int(self.fPixmapNum))


def enterEvent(self, event):
self.fIsHovered = True
if self.fHoverStep == self.HOVER_MIN:
self.fHoverStep = self.HOVER_MIN + 1
QDial.enterEvent(self, event)

def leaveEvent(self, event):
self.fIsHovered = False
if self.fHoverStep == self.HOVER_MAX:
self.fHoverStep = self.HOVER_MAX - 1
QDial.leaveEvent(self, event)

def mousePressEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
return QDial.mousePressEvent(self, event)

if event.button() == Qt.LeftButton:
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:
return QDial.mouseMoveEvent(self, event)

if not self.fIsPressed:
return

range = (self.fMaximum - self.fMinimum) / 4.0
pos = event.pos()
dx = range * float(pos.x() - self.fLastDragPos.x()) / self.width()
dy = range * float(pos.y() - self.fLastDragPos.y()) / self.height()
value = self.fLastDragValue + dx - dy

if value < self.fMinimum:
value = self.fMinimum
elif value > self.fMaximum:
value = self.fMaximum
elif self.fIsInteger:
value = float(round(value))

self.setValue(value, True)

def mouseReleaseEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
return QDial.mouseReleaseEvent(self, event)

if self.fIsPressed:
self.fIsPressed = False
self.dragStateChanged.emit(False)

def paintEvent(self, event):
painter = QPainter(self)
event.accept()

painter.save()
painter.setRenderHint(QPainter.Antialiasing, True)

if self.fLabel:
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
painter.setPen(self.fLabelGradientColor2)
painter.setBrush(self.fLabelGradient)
painter.drawRect(self.fLabelGradientRect)

painter.setFont(self.fLabelFont)
painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
painter.drawText(self.fLabelPos, self.fLabel)

def paintDial(self, painter):
if self.isEnabled(): if self.isEnabled():
normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum) normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize) target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
@@ -494,26 +272,4 @@ class PixmapDial(QDial):
target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize) target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
painter.drawPixmap(target, self.fPixmap, target) painter.drawPixmap(target, self.fPixmap, target)


painter.restore()

def resizeEvent(self, event):
QDial.resizeEvent(self, event)
self.updateSizes()

# ------------------------------------------------------------------------------------------------------------
# Main Testing

if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
import resources_rc

app = QApplication(sys.argv)
gui = PixmapDial(None)
#gui.setEnabled(True)
#gui.setEnabled(False)
gui.setPixmap(3)
gui.setLabel("hahaha")
gui.show()

sys.exit(app.exec_())
# ---------------------------------------------------------------------------------------------------------------------

+ 16
- 28
source/frontend/widgets/pixmapkeyboard.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Pixmap Keyboard, a custom Qt widget # Pixmap Keyboard, a custom Qt widget
# Copyright (C) 2011-2020 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -16,19 +16,19 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import pyqtSignal, pyqtSlot, qCritical, Qt, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QFont, QPainter, QPixmap
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QPainter, QPixmap
from PyQt5.QtWidgets import QActionGroup, QMenu, QScrollArea, QWidget from PyQt5.QtWidgets import QActionGroup, QMenu, QScrollArea, QWidget


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Custom) # Imports (Custom)


from carla_shared import QSafeSettings from carla_shared import QSafeSettings


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


kMidiKey2RectMapHorizontal = [ kMidiKey2RectMapHorizontal = [
QRectF(0, 0, 24, 57), # C QRectF(0, 0, 24, 57), # C
@@ -175,6 +175,12 @@ kValidColors = ("Blue", "Green", "Orange", "Red")


kBlackNotes = (1, 3, 6, 8, 10) kBlackNotes = (1, 3, 6, 8, 10)


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

def _isNoteBlack(note):
baseNote = note % 12
return bool(baseNote in kBlackNotes)

# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# MIDI Keyboard, using a pixmap for painting # MIDI Keyboard, using a pixmap for painting


@@ -502,7 +508,7 @@ class PixmapKeyboard(QWidget):
for note in self.fEnabledKeys: for note in self.fEnabledKeys:
pos = self._getRectFromMidiNote(note) pos = self._getRectFromMidiNote(note)


if self._isNoteBlack(note):
if _isNoteBlack(note):
continue continue


if note < 12: if note < 12:
@@ -558,7 +564,7 @@ class PixmapKeyboard(QWidget):
for note in self.fEnabledKeys: for note in self.fEnabledKeys:
pos = self._getRectFromMidiNote(note) pos = self._getRectFromMidiNote(note)


if not self._isNoteBlack(note):
if not _isNoteBlack(note):
continue continue


if note < 12: if note < 12:
@@ -605,15 +611,11 @@ class PixmapKeyboard(QWidget):
Qt.AlignCenter, Qt.AlignCenter,
"C{}".format(octave)) "C{}".format(octave))


def _isNoteBlack(self, note):
baseNote = note % 12
return bool(baseNote in kBlackNotes)

def _getRectFromMidiNote(self, note): def _getRectFromMidiNote(self, note):
baseNote = note % 12 baseNote = note % 12
return self.fKey2RectMap[baseNote] return self.fKey2RectMap[baseNote]


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Horizontal scroll area for keyboard # Horizontal scroll area for keyboard


class PixmapKeyboardHArea(QScrollArea): class PixmapKeyboardHArea(QScrollArea):
@@ -641,18 +643,4 @@ class PixmapKeyboardHArea(QScrollArea):
def slot_initScrollbarValue(self): def slot_initScrollbarValue(self):
self.horizontalScrollBar().setValue(int(self.horizontalScrollBar().maximum()/2)) self.horizontalScrollBar().setValue(int(self.horizontalScrollBar().maximum()/2))


# ------------------------------------------------------------------------------------------------------------
# Main Testing

if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
import resources_rc

app = QApplication(sys.argv)

gui = PixmapKeyboard(None)
gui.setEnabled(True)
gui.show()

sys.exit(app.exec_())
# ---------------------------------------------------------------------------------------------------------------------

+ 4
- 2
source/frontend/widgets/scalablebutton.py View File

@@ -16,7 +16,7 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from PyQt5.QtCore import QPointF, QRectF from PyQt5.QtCore import QPointF, QRectF
@@ -24,7 +24,7 @@ from PyQt5.QtGui import QColor, QFont, QPainter, QPixmap
from PyQt5.QtSvg import QSvgWidget from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import QPushButton from PyQt5.QtWidgets import QPushButton


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class # Widget Class


class ScalableButton(QPushButton): class ScalableButton(QPushButton):
@@ -136,3 +136,5 @@ class ScalableButton(QPushButton):


else: else:
self.fImageNormal.renderer().render(painter, self.fImageRect) self.fImageNormal.renderer().render(painter, self.fImageRect)

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

+ 13
- 261
source/frontend/widgets/scalabledial.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-


# Scalable Dial, a custom Qt widget # Scalable Dial, a custom Qt widget
# Copyright (C) 2011-2020 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as # modify it under the terms of the GNU General Public License as
@@ -16,65 +16,24 @@
# #
# For a full copy of the GNU General Public License see the doc/GPL.txt file. # For a full copy of the GNU General Public License see the doc/GPL.txt file.


# ------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global) # Imports (Global)


from math import cos, floor, pi, sin, isnan
from math import cos, floor, pi, sin


from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
from PyQt5.QtGui import QColor, QConicalGradient, QFont, QFontMetrics
from PyQt5.QtGui import QLinearGradient, QPainter, QPainterPath, QPen, QPixmap
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.QtSvg import QSvgWidget
from PyQt5.QtWidgets import QDial


# ------------------------------------------------------------------------------------------------------------
# Widget Class

class ScalableDial(QDial):
# enum CustomPaintMode
CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient)
CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3)
CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3)
CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4)
CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3)
CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3)
CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6)
CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient

# enum Orientation
HORIZONTAL = 0
VERTICAL = 1

HOVER_MIN = 0
HOVER_MAX = 9
from .commondial import CommonDial


MODE_DEFAULT = 0
MODE_LINEAR = 1

# signals
dragStateChanged = pyqtSignal(bool)
realValueChanged = pyqtSignal(float)
# ---------------------------------------------------------------------------------------------------------------------
# Widget Class


class ScalableDial(CommonDial):
def __init__(self, parent, index=0): def __init__(self, parent, index=0):
QDial.__init__(self, parent)

self.fDialMode = self.MODE_LINEAR

self.fMinimum = 0.0
self.fMaximum = 1.0
self.fRealValue = 0.0
self.fPrecision = 10000
self.fIsInteger = False
CommonDial.__init__(self, parent, index)


self.fIsHovered = False
self.fIsPressed = False
self.fHoverStep = self.HOVER_MIN

self.fLastDragPos = None
self.fLastDragValue = 0.0

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


@@ -83,60 +42,11 @@ class ScalableDial(QDial):
else: else:
self.fImageOrientation = self.VERTICAL self.fImageOrientation = self.VERTICAL


self.fLabel = ""
self.fLabelPos = QPointF(0.0, 0.0)
self.fLabelFont = QFont(self.font())
self.fLabelFont.setPixelSize(8)
self.fLabelWidth = 0
self.fLabelHeight = 0

if self.palette().window().color().lightness() > 100:
# Light background
c = self.palette().dark().color()
self.fLabelGradientColor1 = c
self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0)
self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
else:
# Dark background
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1)
self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2)

self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0)

self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL
self.fCustomPaintColor = QColor(0xff, 0xff, 0xff)

self.updateSizes() self.updateSizes()


# Fake internal value, custom precision
QDial.setMinimum(self, 0)
QDial.setMaximum(self, self.fPrecision)
QDial.setValue(self, 0)

self.valueChanged.connect(self.slot_valueChanged)

def getIndex(self):
return self.fIndex

def getBaseSize(self): def getBaseSize(self):
return self.fImageBaseSize return self.fImageBaseSize


def forceWhiteLabelGradientText(self):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [Qt.white, Qt.darkGray]

def setLabelColor(self, enabled, disabled):
self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
self.fLabelGradientColorT = [enabled, disabled]

def updateSizes(self): def updateSizes(self):
if isinstance(self.fImage, QPixmap): if isinstance(self.fImage, QPixmap):
self.fImageWidth = self.fImage.width() self.fImageWidth = self.fImage.width()
@@ -184,31 +94,6 @@ class ScalableDial(QDial):
self.fLabelGradientRect = QRectF(float(self.fImageBaseSize)/8.0, float(self.fImageBaseSize)/2.0, self.fLabelGradientRect = QRectF(float(self.fImageBaseSize)/8.0, float(self.fImageBaseSize)/2.0,
float(self.fImageBaseSize*3)/4.0, self.fImageBaseSize+self.fLabelHeight+5) float(self.fImageBaseSize*3)/4.0, self.fImageBaseSize+self.fLabelHeight+5)


def setCustomPaintMode(self, paintMode):
if self.fCustomPaintMode == paintMode:
return

self.fCustomPaintMode = paintMode
self.update()

def setCustomPaintColor(self, color):
if self.fCustomPaintColor == color:
return

self.fCustomPaintColor = color
self.update()

def setLabel(self, label):
if self.fLabel == label:
return

self.fLabel = label
self.updateSizes()
self.update()

def setIndex(self, index):
self.fIndex = index

def setImage(self, imageId): def setImage(self, imageId):
self.fImageNum = "%02i" % imageId self.fImageNum = "%02i" % imageId
if imageId in (2,6,7,8,9,10,11,12,13): if imageId in (2,6,7,8,9,10,11,12,13):
@@ -247,49 +132,6 @@ class ScalableDial(QDial):
self.updateSizes() self.updateSizes()
self.update() self.update()


def setPrecision(self, value, isInteger):
self.fPrecision = value
self.fIsInteger = isInteger
QDial.setMaximum(self, int(value))

def setMinimum(self, value):
self.fMinimum = value

def setMaximum(self, value):
self.fMaximum = value

def rvalue(self):
return self.fRealValue

def setValue(self, value, emitSignal=False):
if self.fRealValue == value or isnan(value):
return

if value <= self.fMinimum:
qtValue = 0
self.fRealValue = self.fMinimum

elif value >= self.fMaximum:
qtValue = int(self.fPrecision)
self.fRealValue = self.fMaximum

else:
qtValue = round(float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision)
self.fRealValue = value

# Block change signal, we'll handle it ourselves
self.blockSignals(True)
QDial.setValue(self, qtValue)
self.blockSignals(False)

if emitSignal:
self.realValueChanged.emit(self.fRealValue)

@pyqtSlot(int)
def slot_valueChanged(self, value):
self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum
self.realValueChanged.emit(self.fRealValue)

@pyqtSlot() @pyqtSlot()
def slot_updateImage(self): def slot_updateImage(self):
self.setImage(int(self.fImageNum)) self.setImage(int(self.fImageNum))
@@ -301,81 +143,13 @@ class ScalableDial(QDial):
return QSize(self.fImageBaseSize, self.fImageBaseSize) return QSize(self.fImageBaseSize, self.fImageBaseSize)


def changeEvent(self, event): def changeEvent(self, event):
QDial.changeEvent(self, event)
CommonDial.changeEvent(self, event)


# Force svg update if enabled state changes # Force svg update if enabled state changes
if event.type() == QEvent.EnabledChange: if event.type() == QEvent.EnabledChange:
self.slot_updateImage() self.slot_updateImage()


def enterEvent(self, event):
self.fIsHovered = True
if self.fHoverStep == self.HOVER_MIN:
self.fHoverStep = self.HOVER_MIN + 1
QDial.enterEvent(self, event)

def leaveEvent(self, event):
self.fIsHovered = False
if self.fHoverStep == self.HOVER_MAX:
self.fHoverStep = self.HOVER_MAX - 1
QDial.leaveEvent(self, event)

def mousePressEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
return QDial.mousePressEvent(self, event)

if event.button() == Qt.LeftButton:
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:
return QDial.mouseMoveEvent(self, event)

if not self.fIsPressed:
return

range = (self.fMaximum - self.fMinimum) / 4.0
pos = event.pos()
dx = range * float(pos.x() - self.fLastDragPos.x()) / self.width()
dy = range * float(pos.y() - self.fLastDragPos.y()) / self.height()
value = self.fLastDragValue + dx - dy

if value < self.fMinimum:
value = self.fMinimum
elif value > self.fMaximum:
value = self.fMaximum
elif self.fIsInteger:
value = float(round(value))

self.setValue(value, True)

def mouseReleaseEvent(self, event):
if self.fDialMode == self.MODE_DEFAULT:
return QDial.mouseReleaseEvent(self, event)

if self.fIsPressed:
self.fIsPressed = False
self.dragStateChanged.emit(False)

def paintEvent(self, event):
painter = QPainter(self)
event.accept()

painter.save()
painter.setRenderHint(QPainter.Antialiasing, True)

if self.fLabel:
if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
painter.setPen(self.fLabelGradientColor2)
painter.setBrush(self.fLabelGradient)
painter.drawRect(self.fLabelGradientRect)

painter.setFont(self.fLabelFont)
painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
painter.drawText(self.fLabelPos, self.fLabel)

def paintDial(self, painter):
if self.isEnabled(): if self.isEnabled():
normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum) normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
curLayer = int((self.fImageLayersCount - 1) * normValue) curLayer = int((self.fImageLayersCount - 1) * normValue)
@@ -521,26 +295,4 @@ class ScalableDial(QDial):
else: else:
self.fImage.renderer().render(painter, target) self.fImage.renderer().render(painter, target)


painter.restore()

def resizeEvent(self, event):
QDial.resizeEvent(self, event)
self.updateSizes()

# ------------------------------------------------------------------------------------------------------------
# Main Testing

if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
import resources_rc

app = QApplication(sys.argv)
gui = ScalableDial(None)
#gui.setEnabled(True)
#gui.setEnabled(False)
gui.setSvg(3)
gui.setLabel("hahaha")
gui.show()

sys.exit(app.exec_())
# ---------------------------------------------------------------------------------------------------------------------

Loading…
Cancel
Save