From f3928a548230b72ea2f100d438fd6fa96d2a8ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=A9rence=20Clastres?= Date: Mon, 1 Jun 2020 18:37:34 +0200 Subject: [PATCH] Fully Scalable UI Part 1 Code preparation for scalable LEDs, Dials and Buttons --- resources/resources.qrc | 53 +-- resources/ui/carla_edit.ui | 14 +- resources/ui/carla_plugin_calf.ui | 12 +- resources/ui/carla_plugin_classic.ui | 10 +- resources/ui/carla_plugin_compact.ui | 10 +- resources/ui/carla_plugin_default.ui | 65 ++- resources/ui/carla_plugin_presets.ui | 10 +- source/frontend/carla_skin.py | 163 +++---- source/frontend/carla_widgets.py | 14 +- source/frontend/widgets/ledbutton.cpp | 14 +- source/frontend/widgets/ledbutton.py | 40 +- source/frontend/widgets/scalablebutton.py | 98 ++++ source/frontend/widgets/scalabledial.py | 519 ++++++++++++++++++++++ 13 files changed, 838 insertions(+), 184 deletions(-) create mode 100644 source/frontend/widgets/scalablebutton.py create mode 100644 source/frontend/widgets/scalabledial.py diff --git a/resources/resources.qrc b/resources/resources.qrc index 51dc4a126..796bb706f 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -44,7 +44,7 @@ scalable/carla.svg scalable/carla-control.svg - + scalable/folder.svgz scalable/osc.svgz scalable/warning.svgz @@ -75,31 +75,32 @@ bitmaps/button_distrho_down-white.png bitmaps/button_distrho_hover-black.png bitmaps/button_distrho_hover-white.png - bitmaps/button_edit-black.png - bitmaps/button_edit-white.png - bitmaps/button_edit_down-black.png - bitmaps/button_edit_down-white.png - bitmaps/button_edit_hover-black.png - bitmaps/button_edit_hover-white.png + scalable/button_edit-black.svg + scalable/button_edit-white.svg + scalable/button_edit_down-black.svg + scalable/button_edit_down-white.svg + scalable/button_edit_hover-black.svg + scalable/button_edit_hover-white.svg bitmaps/button_file-black.png bitmaps/button_file-white.png bitmaps/button_file_down-black.png bitmaps/button_file_down-white.png bitmaps/button_file_hover-black.png bitmaps/button_file_hover-white.png - bitmaps/button_gui-black.png - bitmaps/button_gui-white.png - bitmaps/button_gui_down-black.png - bitmaps/button_gui_down-white.png - bitmaps/button_gui_hover-black.png - bitmaps/button_gui_hover-white.png - bitmaps/button_off.png - bitmaps/button_on.png - - bitmaps/dial_03.png - bitmaps/dial_03d.png - bitmaps/dial_04.png - bitmaps/dial_04d.png + scalable/button_gui-black.svg + scalable/button_gui-white.svg + scalable/button_gui_down-black.svg + scalable/button_gui_down-white.svg + scalable/button_gui_hover-black.svg + scalable/button_gui_hover-white.svg + + scalable/button_off.svg + scalable/button_on.svg + + scalable/dial_03.svg + scalable/dial_03d.svg + scalable/dial_04.svg + scalable/dial_04d.svg bitmaps/dial_06.png bitmaps/dial_06d.png bitmaps/dial_07.png @@ -125,11 +126,11 @@ bitmaps/kbd_down-orange.png bitmaps/kbd_down-red.png - bitmaps/led_off.png - bitmaps/led_blue.png - bitmaps/led_green.png - bitmaps/led_red.png - bitmaps/led_yellow.png + scalable/led_off.svg + scalable/led_red.svg + scalable/led_green.svg + scalable/led_blue.svg + scalable/led_yellow.svg bitmaps/led_calf_off.png bitmaps/led_calf_on.png @@ -160,6 +161,8 @@ scalable/pb_mplayer.svg scalable/pb_vlc.svg + + cursors/zoom-generic-white.png cursors/zoom-generic-black.png cursors/zoom-in-white.png diff --git a/resources/ui/carla_edit.ui b/resources/ui/carla_edit.ui index 95a6068c3..d04137055 100644 --- a/resources/ui/carla_edit.ui +++ b/resources/ui/carla_edit.ui @@ -108,7 +108,7 @@ - + 34 @@ -130,7 +130,7 @@ - + 34 @@ -183,7 +183,7 @@ 0 - + 26 @@ -205,7 +205,7 @@ - + 26 @@ -246,7 +246,7 @@ 0 - + 26 @@ -871,9 +871,9 @@ Plugin Name - PixmapDial + ScalableDial QDial -
widgets/pixmapdial.h
+
widgets/scalabledial.h
diff --git a/resources/ui/carla_plugin_calf.ui b/resources/ui/carla_plugin_calf.ui index 625e08c47..1eae2e720 100644 --- a/resources/ui/carla_plugin_calf.ui +++ b/resources/ui/carla_plugin_calf.ui @@ -59,7 +59,7 @@ 0 - + Enable @@ -301,7 +301,7 @@ 3 - + GUI @@ -311,7 +311,7 @@ - + Edit @@ -321,7 +321,7 @@ - + Remove @@ -411,9 +411,9 @@
widgets/ledbutton.h
- PixmapButton + ScalableButton QPushButton -
widgets/pixmapbutton.h
+
widgets/scalablebutton.h
diff --git a/resources/ui/carla_plugin_classic.ui b/resources/ui/carla_plugin_classic.ui index eb1a38d0c..9ca8af532 100644 --- a/resources/ui/carla_plugin_classic.ui +++ b/resources/ui/carla_plugin_classic.ui @@ -54,7 +54,7 @@ 4 - + 24 @@ -89,7 +89,7 @@ - + 24 @@ -124,7 +124,7 @@ - + 24 @@ -344,9 +344,9 @@
widgets/ledbutton.h
- PixmapButton + ScalableButton QPushButton -
widgets/pixmapbutton.h
+
widgets/scalablebutton.h
diff --git a/resources/ui/carla_plugin_compact.ui b/resources/ui/carla_plugin_compact.ui index 6d3d48913..30e646437 100644 --- a/resources/ui/carla_plugin_compact.ui +++ b/resources/ui/carla_plugin_compact.ui @@ -57,7 +57,7 @@ 0
- + 24 @@ -92,7 +92,7 @@ - + 24 @@ -127,7 +127,7 @@ - + 24 @@ -405,9 +405,9 @@
widgets/ledbutton.h
- PixmapButton + ScalableButton QPushButton -
widgets/pixmapbutton.h
+
widgets/scalablebutton.h
diff --git a/resources/ui/carla_plugin_default.ui b/resources/ui/carla_plugin_default.ui index 641fa7b18..b1d8d9668 100644 --- a/resources/ui/carla_plugin_default.ui +++ b/resources/ui/carla_plugin_default.ui @@ -58,11 +58,20 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 - + 24 @@ -79,8 +88,8 @@ - - :/bitmaps/button_off.png:/bitmaps/button_off.png + + ../scalable/button_off.svg../scalable/button_off.svg @@ -97,7 +106,7 @@ - + 24 @@ -114,8 +123,8 @@ - - :/bitmaps/button_gui.png:/bitmaps/button_gui.png + + :/bitmaps/button_gui-black.png:/bitmaps/button_gui-black.png @@ -132,7 +141,7 @@ - + 24 @@ -307,6 +316,12 @@ + + + 0 + 0 + + 14 @@ -322,6 +337,10 @@ + + + :/scalable/led_off.svg:/scalable/led_off.svg + true @@ -344,6 +363,10 @@ + + + :/scalable/led_off.svg:/scalable/led_off.svg + true @@ -366,6 +389,10 @@ + + + :/scalable/led_off.svg:/scalable/led_off.svg + true @@ -373,6 +400,9 @@ + + true + 14 @@ -388,6 +418,10 @@ + + + :/scalable/led_off.svg:/scalable/led_off.svg + true @@ -433,7 +467,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -490,9 +533,9 @@
widgets/ledbutton.h
- PixmapButton + ScalableButton QPushButton -
widgets/pixmapbutton.h
+
widgets/scalablebutton.h
diff --git a/resources/ui/carla_plugin_presets.ui b/resources/ui/carla_plugin_presets.ui index db3017f85..c6699a6c6 100644 --- a/resources/ui/carla_plugin_presets.ui +++ b/resources/ui/carla_plugin_presets.ui @@ -59,7 +59,7 @@ 0
- + 24 @@ -94,7 +94,7 @@ - + 24 @@ -129,7 +129,7 @@ - + 24 @@ -517,9 +517,9 @@
widgets/ledbutton.h
- PixmapButton + ScalableButton QPushButton -
widgets/pixmapbutton.h
+
widgets/scalablebutton.h
diff --git a/source/frontend/carla_skin.py b/source/frontend/carla_skin.py index 3df24b775..2e5c6b923 100644 --- a/source/frontend/carla_skin.py +++ b/source/frontend/carla_skin.py @@ -20,7 +20,7 @@ # Imports (Global) from PyQt5.QtCore import Qt, QRectF, QLineF -from PyQt5.QtGui import QFont, QFontDatabase, QPen, QPixmap +from PyQt5.QtGui import QFont, QFontDatabase, QPen from PyQt5.QtWidgets import QColorDialog, QFrame, QPushButton # ------------------------------------------------------------------------------------------------------------ @@ -34,7 +34,7 @@ import ui_carla_plugin_presets from carla_widgets import * from widgets.digitalpeakmeter import DigitalPeakMeter -from widgets.pixmapdial import PixmapDial +from widgets.scalabledial import ScalableDial # ------------------------------------------------------------------------------------------------------------ # Plugin Skin Rules (WORK IN PROGRESS) @@ -43,7 +43,7 @@ from widgets.pixmapdial import PixmapDial # Spacing of the top-most layout must be 1px. # Top and bottom margins must be 3px (can be split between different Qt layouts). # Left and right margins must be 6px (can be split between different Qt layouts). -# If the left or right side has built-in margins, say a transparent png border, +# If the left or right side has built-in margins, say a transparent svg border, # those margins must be taken into consideration. # # There's a top and bottom layout, separated by a horizontal line. @@ -158,33 +158,33 @@ def getColorFromCategory(category): # ------------------------------------------------------------------------------------------------------------ # -def setPixmapDialStyle(widget, parameterId, parameterCount, whiteLabels, skinStyle): +def setScalableDialStyle(widget, parameterId, parameterCount, whiteLabels, skinStyle): if skinStyle.startswith("calf"): - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_NO_GRADIENT) - widget.setPixmap(7) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_NO_GRADIENT) + widget.setSvg(7) elif skinStyle.startswith("openav"): - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_NO_GRADIENT) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_NO_GRADIENT) if parameterId == PARAMETER_DRYWET: - widget.setPixmap(13) + widget.setSvg(13) elif parameterId == PARAMETER_VOLUME: - widget.setPixmap(12) + widget.setSvg(12) else: - widget.setPixmap(11) + widget.setSvg(11) else: if parameterId == PARAMETER_DRYWET: - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_CARLA_WET) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_WET) elif parameterId == PARAMETER_VOLUME: - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_CARLA_VOL) + 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)) - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_COLOR) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_COLOR) if whiteLabels: colorEnabled = QColor("#BBB") @@ -194,7 +194,7 @@ def setPixmapDialStyle(widget, parameterId, parameterCount, whiteLabels, skinSty colorDisabled = QColor("#AAA") widget.setLabelColor(colorEnabled, colorDisabled) - widget.setPixmap(3) + widget.setSvg(3) # ------------------------------------------------------------------------------------------------------------ # Abstract plugin slot @@ -357,7 +357,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): if self.fPluginId == pluginId: self.customUiStateChanged(state) - #------------------------------------------------------------------ + # ------------------------------------------------------------------ def ready(self): self.fIsActive = bool(self.host.get_internal_parameter_value(self.fPluginId, PARAMETER_ACTIVE) >= 0.5) @@ -368,6 +368,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): if self.fSkinStyle.startswith("calf") or self.fSkinStyle.startswith("openav") or self.fSkinStyle in ( "3bandeq", "3bandsplitter", "pingpongpan", "nekobi", "calf_black", "zynfx"): + imageSuffix = "white" whiteLabels = True @@ -376,46 +377,46 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.b_enable.clicked.connect(self.slot_enableClicked) if isCalfSkin: - self.b_enable.setPixmaps(":/bitmaps/button_calf3.png", - ":/bitmaps/button_calf3_down.png", - ":/bitmaps/button_calf3.png") + self.b_enable.setSvgs(":/scalable/button_calf3.svg", + ":/scalable/button_calf3_down.svg", + ":/scalable/button_calf3.svg") else: - self.b_enable.setPixmaps(":/bitmaps/button_off.png", - ":/bitmaps/button_on.png", - ":/bitmaps/button_off.png") + self.b_enable.setSvgs(":/scalable/button_off.svg", + ":/scalable/button_on.svg", + ":/scalable/button_off.svg") if self.b_gui is not None: self.b_gui.clicked.connect(self.slot_showCustomUi) self.b_gui.setEnabled(bool(self.fPluginInfo['hints'] & PLUGIN_HAS_CUSTOM_UI)) if isCalfSkin: - self.b_gui.setPixmaps(":/bitmaps/button_calf2.png", - ":/bitmaps/button_calf2_down.png", - ":/bitmaps/button_calf2_hover.png") - elif self.fPluginInfo['iconName'] == "distrho" or self.fSkinStyle in ("3bandeq","3bandsplitter","pingpongpan", "nekobi"): - self.b_gui.setPixmaps(":/bitmaps/button_distrho-{}.png".format(imageSuffix), - ":/bitmaps/button_distrho_down-{}.png".format(imageSuffix), - ":/bitmaps/button_distrho_hover-{}.png".format(imageSuffix)) + self.b_gui.setSvgs(":/scalable/button_calf2.svg", + ":/scalable/button_calf2_down.svg", + ":/scalable/button_calf2_hover.svg") + elif self.fPluginInfo['iconName'] == "distrho" or self.fSkinStyle in ("3bandeq", "3bandsplitter", "pingpongpan", "nekobi"): + self.b_gui.setSvgs(":/scalable/button_distrho-{}.svg".format(imageSuffix), + ":/scalable/button_distrho_down-{}.svg".format(imageSuffix), + ":/scalable/button_distrho_hover-{}.svg".format(imageSuffix)) elif self.fPluginInfo['iconName'] == "file": - self.b_gui.setPixmaps(":/bitmaps/button_file-{}.png".format(imageSuffix), - ":/bitmaps/button_file_down-{}.png".format(imageSuffix), - ":/bitmaps/button_file_hover-{}.png".format(imageSuffix)) + self.b_gui.setSvgs(":/scalable/button_file-{}.svg".format(imageSuffix), + ":/scalable/button_file_down-{}.svg".format(imageSuffix), + ":/scalable/button_file_hover-{}.svg".format(imageSuffix)) else: - self.b_gui.setPixmaps(":/bitmaps/button_gui-{}.png".format(imageSuffix), - ":/bitmaps/button_gui_down-{}.png".format(imageSuffix), - ":/bitmaps/button_gui_hover-{}.png".format(imageSuffix)) + self.b_gui.setSvgs(":/scalable/button_gui-{}.svg".format(imageSuffix), + ":/scalable/button_gui_down-{}.svg".format(imageSuffix), + ":/scalable/button_gui_hover-{}.svg".format(imageSuffix)) if self.b_edit is not None: self.b_edit.clicked.connect(self.slot_showEditDialog) if isCalfSkin: - self.b_edit.setPixmaps(":/bitmaps/button_calf2.png".format(imageSuffix), - ":/bitmaps/button_calf2_down.png".format(imageSuffix), - ":/bitmaps/button_calf2_hover.png".format(imageSuffix)) + self.b_edit.setSvgs(":/scalable/button_calf2.svg", + ":/scalable/button_calf2_down.svg", + ":/scalable/button_calf2_hover.svg") else: - self.b_edit.setPixmaps(":/bitmaps/button_edit-{}.png".format(imageSuffix), - ":/bitmaps/button_edit_down-{}.png".format(imageSuffix), - ":/bitmaps/button_edit_hover-{}.png".format(imageSuffix)) + self.b_edit.setSvgs(":/scalable/button_edit-{}.svg".format(imageSuffix), + ":/scalable/button_edit_down-{}.svg".format(imageSuffix), + ":/scalable/button_edit_hover-{}.svg".format(imageSuffix)) else: # Edit button *must* be available @@ -594,7 +595,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): paramName = getParameterShortName(paramInfo['name']) - widget = PixmapDial(self, i) + widget = ScalableDial(self, i) widget.setLabel(paramName) widget.setMinimum(paramRanges['min']) widget.setMaximum(paramRanges['max']) @@ -603,28 +604,28 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): if isInteger: widget.setPrecision(paramRanges['max']-paramRanges['min'], True) - setPixmapDialStyle(widget, i, parameterCount, whiteLabels, self.fSkinStyle) + setScalableDialStyle(widget, i, parameterCount, whiteLabels, self.fSkinStyle) index += 1 self.fParameterList.append([i, widget]) layout.addWidget(widget) if self.w_knobs_right is not None and (self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET) != 0: - widget = PixmapDial(self, PARAMETER_DRYWET) + widget = ScalableDial(self, PARAMETER_DRYWET) widget.setLabel("Dry/Wet") widget.setMinimum(0.0) widget.setMaximum(1.0) - setPixmapDialStyle(widget, PARAMETER_DRYWET, 0, whiteLabels, self.fSkinStyle) + setScalableDialStyle(widget, PARAMETER_DRYWET, 0, whiteLabels, self.fSkinStyle) self.fParameterList.append([PARAMETER_DRYWET, widget]) self.w_knobs_right.layout().addWidget(widget) if self.w_knobs_right is not None and (self.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME) != 0: - widget = PixmapDial(self, PARAMETER_VOLUME) + widget = ScalableDial(self, PARAMETER_VOLUME) widget.setLabel("Volume") widget.setMinimum(0.0) widget.setMaximum(1.27) - setPixmapDialStyle(widget, PARAMETER_VOLUME, 0, whiteLabels, self.fSkinStyle) + setScalableDialStyle(widget, PARAMETER_VOLUME, 0, whiteLabels, self.fSkinStyle) self.fParameterList.append([PARAMETER_VOLUME, widget]) self.w_knobs_right.layout().addWidget(widget) @@ -646,7 +647,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.fAdjustViewableKnobCountScheduled = True QTimer.singleShot(5, self.adjustViewableKnobCount) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): return 32 @@ -657,7 +658,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): def getPluginId(self): return self.fPluginId - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def setPluginId(self, idx): self.fPluginId = idx @@ -677,7 +678,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.fIsSelected = yesNo self.update() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def setActive(self, active, sendCallback=False, sendHost=True): self.fIsActive = active @@ -729,7 +730,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.fEditDialog.setParameterValue(parameterId, value) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def setParameterValue(self, parameterId, value, sendCallback): if parameterId == PARAMETER_ACTIVE: @@ -753,7 +754,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): def setParameterMidiChannel(self, parameterId, channel): self.fEditDialog.setParameterMidiChannel(parameterId, channel) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def setProgram(self, index, sendCallback): self.fEditDialog.setProgram(index) @@ -773,12 +774,12 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.updateParameterValues() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def setOption(self, option, yesNo): self.fEditDialog.setOption(option, yesNo) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def showCustomUI(self): self.host.show_custom_ui(self.fPluginId, True) @@ -826,7 +827,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): if not ok: CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def activeChanged(self, onOff): self.fIsActive = onOff @@ -933,7 +934,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): def editDialogMidiActivityChanged(self, pluginId, onOff): self.midiActivityChanged(onOff) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def idleFast(self): # Input peaks @@ -994,7 +995,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.fEditDialog.idleSlow() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def drawOutline(self, painter): painter.save() @@ -1027,7 +1028,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): paramWidget.setValue(self.host.get_current_parameter_value(self.fPluginId, paramIndex)) paramWidget.blockSignals(False) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- @pyqtSlot(bool) def slot_enableClicked(self, yesNo): @@ -1376,7 +1377,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): sender.setValue(value, True) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- @pyqtSlot(bool) def slot_showCustomUi(self, show): @@ -1392,7 +1393,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- @pyqtSlot(bool) def slot_parameterDragStateChanged(self, touch): @@ -1421,7 +1422,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): self.host.set_midi_program(self.fPluginId, index) self.setMidiProgram(index, False) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def adjustViewableKnobCount(self): if self.w_knobs_left is None or self.spacer_knobs is None: @@ -1457,7 +1458,7 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): def testTimer(self): self.fIdleTimerId = self.startTimer(25) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def mouseDoubleClickEvent(self, event): QFrame.mouseDoubleClickEvent(self, event) @@ -1529,7 +1530,7 @@ class PluginSlot_Calf(AbstractPluginSlot): self.ui.label_active.setFont(self.fButtonFont) - self.ui.b_remove.setPixmaps(":/bitmaps/button_calf1.png", ":/bitmaps/button_calf1_down.png", ":/bitmaps/button_calf1_hover.png") + self.ui.b_remove.setSvgs(":/scalable/button_calf1.svg", ":/scalable/button_calf1_down.svg", ":/scalable/button_calf1_hover.svg") self.ui.b_edit.setTopText(self.tr("Edit"), self.fButtonColorOn, self.fButtonFont) self.ui.b_remove.setTopText(self.tr("Remove"), self.fButtonColorOn, self.fButtonFont) @@ -1573,12 +1574,12 @@ class PluginSlot_Calf(AbstractPluginSlot): self.ui.led_midi.setColor(self.ui.led_midi.CALF) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): return 94 if max(self.peak_in.channelCount(), self.peak_out.channelCount()) < 2 else 106 - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def editDialogPluginHintsChanged(self, pluginId, hints): if hints & PLUGIN_HAS_CUSTOM_UI: @@ -1588,7 +1589,7 @@ class PluginSlot_Calf(AbstractPluginSlot): AbstractPluginSlot.editDialogPluginHintsChanged(self, pluginId, hints) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def paintEvent(self, event): isBlack = bool(self.fSkinStyle == "calf_black") @@ -1636,12 +1637,12 @@ class PluginSlot_Classic(AbstractPluginSlot): self.ready() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): return 36 - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def paintEvent(self, event): painter = QPainter(self) @@ -1716,7 +1717,7 @@ class PluginSlot_Compact(AbstractPluginSlot): self.ready() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): if self.fSkinStyle == "calf_blue": @@ -1753,12 +1754,12 @@ class PluginSlot_Default(AbstractPluginSlot): self.ready() - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): return 80 - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def paintEvent(self, event): painter = QPainter(self) @@ -1934,14 +1935,14 @@ class PluginSlot_Presets(AbstractPluginSlot): else: paramName = getParameterShortName(paramName) - widget = PixmapDial(self, i) + widget = ScalableDial(self, i) widget.setLabel(paramName) widget.setMinimum(paramRanges['min']) widget.setMaximum(paramRanges['max']) - widget.setPixmap(3) + widget.setSvg(3) widget.setCustomPaintColor(QColor(83, 173, 10)) - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_COLOR) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_COLOR) widget.forceWhiteLabelGradientText() widget.hide() @@ -1952,35 +1953,35 @@ class PluginSlot_Presets(AbstractPluginSlot): self.ui.w_knobs_left.layout().addWidget(widget) if self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET: - widget = PixmapDial(self, PARAMETER_DRYWET) + widget = ScalableDial(self, PARAMETER_DRYWET) widget.setLabel("Wet") widget.setMinimum(0.0) widget.setMaximum(1.0) - widget.setPixmap(3) - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_CARLA_WET) + widget.setSvg(3) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_WET) widget.forceWhiteLabelGradientText() self.fParameterList.append([PARAMETER_DRYWET, widget]) self.ui.w_knobs_right.layout().addWidget(widget) if self.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME: - widget = PixmapDial(self, PARAMETER_VOLUME) + widget = ScalableDial(self, PARAMETER_VOLUME) widget.setLabel("Volume") widget.setMinimum(0.0) widget.setMaximum(1.27) - widget.setPixmap(3) - widget.setCustomPaintMode(PixmapDial.CUSTOM_PAINT_MODE_CARLA_VOL) + widget.setSvg(3) + widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_VOL) widget.forceWhiteLabelGradientText() self.fParameterList.append([PARAMETER_VOLUME, widget]) self.ui.w_knobs_right.layout().addWidget(widget) - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def getFixedHeight(self): return 80 - #------------------------------------------------------------------ + # ----------------------------------------------------------------- def paintEvent(self, event): painter = QPainter(self) diff --git a/source/frontend/carla_widgets.py b/source/frontend/carla_widgets.py index e8ba95b2a..78e0c3c47 100755 --- a/source/frontend/carla_widgets.py +++ b/source/frontend/carla_widgets.py @@ -585,8 +585,8 @@ class PluginEdit(QDialog): self.fPlayingNotes = [] # (channel, note) - self.fTabIconOff = QIcon(":/bitmaps/led_off.png") - self.fTabIconOn = QIcon(":/bitmaps/led_yellow.png") + self.fTabIconOff = QIcon(":/scalable/led_off.svg") + self.fTabIconOn = QIcon(":/scalable/led_yellow.svg") self.fTabIconTimers = [] # used during testing @@ -601,35 +601,35 @@ class PluginEdit(QDialog): self.ui.label_plugin.setFont(labelPluginFont) self.ui.dial_drywet.setCustomPaintMode(self.ui.dial_drywet.CUSTOM_PAINT_MODE_CARLA_WET) - self.ui.dial_drywet.setPixmap(3) + self.ui.dial_drywet.setSvg(3) self.ui.dial_drywet.setLabel("Dry/Wet") self.ui.dial_drywet.setMinimum(0.0) self.ui.dial_drywet.setMaximum(1.0) self.ui.dial_drywet.setValue(host.get_internal_parameter_value(pluginId, PARAMETER_DRYWET)) self.ui.dial_vol.setCustomPaintMode(self.ui.dial_vol.CUSTOM_PAINT_MODE_CARLA_VOL) - self.ui.dial_vol.setPixmap(3) + self.ui.dial_vol.setSvg(3) self.ui.dial_vol.setLabel("Volume") self.ui.dial_vol.setMinimum(0.0) self.ui.dial_vol.setMaximum(1.27) self.ui.dial_vol.setValue(host.get_internal_parameter_value(pluginId, PARAMETER_VOLUME)) self.ui.dial_b_left.setCustomPaintMode(self.ui.dial_b_left.CUSTOM_PAINT_MODE_CARLA_L) - self.ui.dial_b_left.setPixmap(4) + self.ui.dial_b_left.setSvg(4) self.ui.dial_b_left.setLabel("L") self.ui.dial_b_left.setMinimum(-1.0) self.ui.dial_b_left.setMaximum(1.0) self.ui.dial_b_left.setValue(host.get_internal_parameter_value(pluginId, PARAMETER_BALANCE_LEFT)) self.ui.dial_b_right.setCustomPaintMode(self.ui.dial_b_right.CUSTOM_PAINT_MODE_CARLA_R) - self.ui.dial_b_right.setPixmap(4) + self.ui.dial_b_right.setSvg(4) self.ui.dial_b_right.setLabel("R") self.ui.dial_b_right.setMinimum(-1.0) self.ui.dial_b_right.setMaximum(1.0) self.ui.dial_b_right.setValue(host.get_internal_parameter_value(pluginId, PARAMETER_BALANCE_RIGHT)) self.ui.dial_pan.setCustomPaintMode(self.ui.dial_b_right.CUSTOM_PAINT_MODE_CARLA_PAN) - self.ui.dial_pan.setPixmap(4) + self.ui.dial_pan.setSvg(4) self.ui.dial_pan.setLabel("Pan") self.ui.dial_pan.setMinimum(-1.0) self.ui.dial_pan.setMaximum(1.0) diff --git a/source/frontend/widgets/ledbutton.cpp b/source/frontend/widgets/ledbutton.cpp index b9fdeb3ef..c575594a9 100644 --- a/source/frontend/widgets/ledbutton.cpp +++ b/source/frontend/widgets/ledbutton.cpp @@ -32,7 +32,7 @@ LEDButton::LEDButton(QWidget* parent): setColor(BLUE); // matching fLastColor - fPixmap.load(":/bitmaps/led_off.png"); + fPixmap.load(":/scalable/led_off.svg"); } void LEDButton::setColor(Color color) @@ -58,19 +58,19 @@ void LEDButton::paintEvent(QPaintEvent* event) switch (fColor) { case OFF: - fPixmap.load(":/bitmaps/led_off.png"); + fPixmap.load(":/scalable/led_off.svg"); break; case BLUE: - fPixmap.load(":/bitmaps/led_blue.png"); + fPixmap.load(":/scalable/led_blue.svg"); break; case GREEN: - fPixmap.load(":/bitmaps/led_green.png"); + fPixmap.load(":/scalable/led_green.svg"); break; case RED: - fPixmap.load(":/bitmaps/led_red.png"); + fPixmap.load(":/scalable/led_red.svg"); break; case YELLOW: - fPixmap.load(":/bitmaps/led_yellow.png"); + fPixmap.load(":/scalable/led_yellow.svg"); break; default: return; @@ -81,7 +81,7 @@ void LEDButton::paintEvent(QPaintEvent* event) } else if (fLastColor != OFF) { - fPixmap.load(":/bitmaps/led_off.png"); + fPixmap.load(":/scalable/led_off.svg"); fLastColor = OFF; } diff --git a/source/frontend/widgets/ledbutton.py b/source/frontend/widgets/ledbutton.py index 77a3415d1..66d3f8bab 100644 --- a/source/frontend/widgets/ledbutton.py +++ b/source/frontend/widgets/ledbutton.py @@ -19,13 +19,13 @@ # ------------------------------------------------------------------------------------------------------------ # Imports (Global) -from PyQt5.QtCore import QRectF -from PyQt5.QtGui import QPainter, QPixmap +from PyQt5.QtSvg import QSvgWidget from PyQt5.QtWidgets import QPushButton # ------------------------------------------------------------------------------------------------------------ # Widget Class + class LEDButton(QPushButton): # normal OFF = 0 @@ -39,60 +39,50 @@ class LEDButton(QPushButton): def __init__(self, parent): QPushButton.__init__(self, parent) - self.fPixmap = QPixmap() - self.fPixmapRect = QRectF(0, 0, 0, 0) + self.fSvgWidget = QSvgWidget(self) self.setCheckable(True) self.setText("") self.fLastColor = self.OFF - self.fPixmap.load(":/bitmaps/led_off.png") + self.fSvgWidget.load(":/scalable/led_off.svg") self.setColor(self.BLUE) def setColor(self, color): self.fColor = color - self._loadPixmapNow() + self._loadSVGNow() # Fix calf off if color == self.CALF: - self.fPixmap.load(":/bitmaps/led_calf_off.png") - - size = self.fPixmap.width() - self.fPixmapRect = QRectF(0, 0, size, size) - - self.setMinimumSize(size, size) - self.setMaximumSize(size, size) + self.fSvgWidget.load(":/bitmaps/led_calf_off.png") def paintEvent(self, event): - painter = QPainter(self) event.accept() - self._loadPixmapNow() - - painter.drawPixmap(self.fPixmapRect, self.fPixmap, self.fPixmapRect) + self._loadSVGNow() - def _loadPixmapNow(self): + def _loadSVGNow(self): if self.isChecked(): if self.fLastColor != self.fColor: if self.fColor == self.OFF: - self.fPixmap.load(":/bitmaps/led_off.png") + self.fSvgWidget.load(":/scalable/led_off.svg") elif self.fColor == self.BLUE: - self.fPixmap.load(":/bitmaps/led_blue.png") + self.fSvgWidget.load(":/scalable/led_blue.svg") elif self.fColor == self.GREEN: - self.fPixmap.load(":/bitmaps/led_green.png") + self.fSvgWidget.load(":/scalable/led_green.svg") elif self.fColor == self.RED: - self.fPixmap.load(":/bitmaps/led_red.png") + self.fSvgWidget.load(":/scalable/led_red.svg") elif self.fColor == self.YELLOW: - self.fPixmap.load(":/bitmaps/led_yellow.png") + self.fSvgWidget.load(":/scalable/led_yellow.svg") elif self.fColor == self.CALF: - self.fPixmap.load(":/bitmaps/led_calf_on.png") + self.fSvgWidget.load(":/bitmaps/led_calf_on.png") else: return self.fLastColor = self.fColor elif self.fLastColor != self.OFF: - self.fPixmap.load(":/bitmaps/led_calf_off.png" if self.fColor == self.CALF else ":/bitmaps/led_off.png") + self.fSvgWidget.load(":/scalable/led_calf_off.png" if self.fColor == self.CALF else ":/scalable/led_off.svg") self.fLastColor = self.OFF diff --git a/source/frontend/widgets/scalablebutton.py b/source/frontend/widgets/scalablebutton.py new file mode 100644 index 000000000..b3a371973 --- /dev/null +++ b/source/frontend/widgets/scalablebutton.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Svg Button, a custom Qt widget +# Copyright (C) 2020 Filipe Coelho +# +# 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 +from PyQt5.QtSvg import QSvgWidget +from PyQt5.QtWidgets import QPushButton + +# ------------------------------------------------------------------------------------------------------------ +# Widget Class + + +class ScalableButton(QPushButton): + def __init__(self, parent): + QPushButton.__init__(self, parent) + + self.fSvgNormal = QSvgWidget() + self.fSvgDown = QSvgWidget() + self.fSvgHover = QSvgWidget() + + self.fIsHovered = False + + self.fTopText = "" + self.fTopTextColor = QColor() + self.fTopTextFont = QFont() + + self.setText("") + + def setSvgs(self, normal, down, hover): + self.fSvgNormal.load(normal) + self.fSvgDown.load(down) + self.fSvgHover.load(hover) + + width = self.fSvgNormal.sizeHint().width() + height = self.fSvgNormal.sizeHint().height() + + self.fSvgRect = 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) + self.fSvgNormal.renderer().render(painter, self.fSvgRect) + painter.restore() + + elif self.isChecked() or self.isDown(): + self.fSvgDown.renderer().render(painter, self.fSvgRect) + + elif self.fIsHovered: + self.fSvgHover.renderer().render(painter, self.fSvgRect) + else: + self.fSvgNormal.renderer().render(painter, self.fSvgRect) + 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() diff --git a/source/frontend/widgets/scalabledial.py b/source/frontend/widgets/scalabledial.py new file mode 100644 index 000000000..41991cb5f --- /dev/null +++ b/source/frontend/widgets/scalabledial.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Svg Dial, a custom Qt widget +# Copyright (C) 2011-2019 Filipe Coelho +# +# 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 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 +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 + + MODE_DEFAULT = 0 + MODE_LINEAR = 1 + + # signals + dragStateChanged = pyqtSignal(bool) + realValueChanged = pyqtSignal(float) + + 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 + + self.fIsHovered = False + self.fIsPressed = False + self.fHoverStep = self.HOVER_MIN + + self.fLastDragPos = None + self.fLastDragValue = 0.0 + + self.fIndex = index + self.fSvg = QSvgWidget(":/scalable/dial_03.svg") + self.fSvgNum = "01" + + if self.fSvg.sizeHint().width() > self.fSvg.sizeHint().height(): + self.fSvgOrientation = self.HORIZONTAL + else: + self.fSvgOrientation = 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() + + # 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): + return self.fSvgBaseSize + + 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): + self.fSvgWidth = self.fSvg.sizeHint().width() + self.fSvgHeight = self.fSvg.sizeHint().height() + + if self.fSvgWidth < 1: + self.fSvgWidth = 1 + + if self.fSvgHeight < 1: + self.fSvgHeight = 1 + + if self.fSvgOrientation == self.HORIZONTAL: + self.fSvgBaseSize = self.fSvgHeight + self.fSvgLayersCount = self.fSvgWidth / self.fSvgHeight + else: + self.fSvgBaseSize = self.fSvgWidth + self.fSvgLayersCount = self.fSvgHeight / self.fSvgWidth + + self.setMinimumSize(self.fSvgBaseSize, self.fSvgBaseSize + self.fLabelHeight + 5) + self.setMaximumSize(self.fSvgBaseSize, self.fSvgBaseSize + self.fLabelHeight + 5) + + if not self.fLabel: + self.fLabelHeight = 0 + self.fLabelWidth = 0 + return + + self.fLabelWidth = QFontMetrics(self.fLabelFont).width(self.fLabel) + self.fLabelHeight = QFontMetrics(self.fLabelFont).height() + + self.fLabelPos.setX(float(self.fSvgBaseSize)/2.0 - float(self.fLabelWidth)/2.0) + + if self.fSvgNum in ("01", "02", "07", "08", "09", "10"): + self.fLabelPos.setY(self.fSvgBaseSize + self.fLabelHeight) + elif self.fSvgNum in ("11",): + self.fLabelPos.setY(self.fSvgBaseSize + self.fLabelHeight*2/3) + else: + self.fLabelPos.setY(self.fSvgBaseSize + self.fLabelHeight/2) + + self.fLabelGradient.setStart(0, float(self.fSvgBaseSize)/2.0) + self.fLabelGradient.setFinalStop(0, self.fSvgBaseSize + self.fLabelHeight + 5) + + self.fLabelGradientRect = QRectF(float(self.fSvgBaseSize)/8.0, float(self.fSvgBaseSize)/2.0, + float(self.fSvgBaseSize*3)/4.0, self.fSvgBaseSize+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 setSvg(self, SvgId): + self.fSvgNum = "%02i" % SvgId + self.fSvg.load(":/scalable/dial_%s%s.svg" % (self.fSvgNum, "" if self.isEnabled() else "d")) + + if self.fSvg.width() > self.fSvg.height(): + self.fSvgOrientation = self.HORIZONTAL + else: + self.fSvgOrientation = self.VERTICAL + + # special svg + if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL: + # reserved for carla-wet, carla-vol, carla-pan and color + if self.fSvgNum == "03": + self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR + + # reserved for carla-L and carla-R + elif self.fSvgNum == "04": + self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L + + # reserved for zita + elif self.fSvgNum == "06": + self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA + + self.updateSizes() + 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: + 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() + def slot_updateSvg(self): + self.seSvg(int(self.fSvgNum)) + + def minimumSizeHint(self): + return QSize(self.fSvgBaseSize, self.fSvgBaseSize) + + def sizeHint(self): + return QSize(self.fSvgBaseSize, self.fSvgBaseSize) + + def changeEvent(self, event): + QDial.changeEvent(self, event) + + # Force svg update if enabled state changes + if event.type() == QEvent.EnabledChange: + self.setSvg(int(self.fSvgNum)) + + 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) + + if self.isEnabled(): + normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum) + curLayer = int((self.fSvgLayersCount - 1) * normValue) + + if self.fSvgOrientation == self.HORIZONTAL: + xpos = self.fSvgBaseSize * curLayer + ypos = 0.0 + else: + xpos = 0.0 + ypos = self.fSvgBaseSize * curLayer + + source = QRectF(xpos, ypos, self.fSvgBaseSize, self.fSvgBaseSize) + self.fSvg.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 = 220*16 + spanAngle = -258*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)) + + 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(2.0, 2.0, 30.5, 30.5, startAngle, 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 = 220*16 + spanAngle = -258.0*16*normValue + else: + startAngle = 320.0*16 + spanAngle = 262.0*16*(1.0-normValue) + + painter.setPen(QPen(color, 2)) + painter.drawArc(2, 2.0, 24.0, 24.0, startAngle, 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 = 220*16 + spanAngle = -258*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(2.0, 2.0, 30.5, 30.5, startAngle, 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 + else: + painter.restore() + return + + if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX: + self.fHoverStep += 1 if self.fIsHovered else -1 + QTimer.singleShot(20, self.update) + + else: # isEnabled() + target = QRectF(0.0, 0.0, self.fSvgBaseSize, self.fSvgBaseSize) + self.fSvg.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_())