#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Pixmap Dial, a custom Qt4 widget # Copyright (C) 2011-2013 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 GPL.txt file # ------------------------------------------------------------------------------------------------------------ # Imports (Global) from math import floor from PyQt4.QtCore import Qt, QPointF, QRectF, QTimer, QSize, SLOT from PyQt4.QtGui import QColor, QConicalGradient, QDial, QFont, QFontMetrics from PyQt4.QtGui import QLinearGradient, QPainter, QPainterPath, QPen, QPixmap # ------------------------------------------------------------------------------------------------------------ # Widget Class class PixmapDial(QDial): # enum Orientation HORIZONTAL = 0 VERTICAL = 1 # enum CustomPaint CUSTOM_PAINT_NULL = 0 CUSTOM_PAINT_CARLA_WET = 1 CUSTOM_PAINT_CARLA_VOL = 2 CUSTOM_PAINT_CARLA_L = 3 CUSTOM_PAINT_CARLA_R = 4 HOVER_MIN = 0 HOVER_MAX = 9 def __init__(self, parent): QDial.__init__(self, parent) self.fPixmap = QPixmap(":/bitmaps/dial_01d.png") self.fPixmapNum = "01" self.fCustomPaint = self.CUSTOM_PAINT_NULL self.fHovered = False self.fHoverStep = self.HOVER_MIN if self.fPixmap.width() > self.fPixmap.height(): self.fOrientation = self.HORIZONTAL else: self.fOrientation = self.VERTICAL self.fLabel = "" self.fLabelPos = QPointF(0.0, 0.0) self.fLabelFont = QFont() self.fLabelFont.setPointSize(6) self.fLabelWidth = 0 self.fLabelHeight = 0 self.fLabelGradient = QLinearGradient(0, 0, 0, 1) if self.palette().window().color().lightness() > 100: # Light background c = self.palette().dark().color() self.fColor1 = c self.fColor2 = QColor(c.red(), c.green(), c.blue(), 0) self.fColorT = [self.palette().buttonText().color(), self.palette().mid().color()] else: # Dark background self.fColor1 = QColor(0, 0, 0, 255) self.fColor2 = QColor(0, 0, 0, 0) self.fColorT = [Qt.white, Qt.darkGray] self.updateSizes() def getSize(self): return self.fSize def setCustomPaint(self, paint): self.fCustomPaint = paint self.fLabelPos.setY(self.fSize + self.fLabelHeight/2) self.update() def setEnabled(self, enabled): if self.isEnabled() != enabled: self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if enabled else "d")) self.updateSizes() self.update() QDial.setEnabled(self, enabled) def setLabel(self, label): self.fLabel = label self.fLabelWidth = QFontMetrics(self.fLabelFont).width(label) self.fLabelHeight = QFontMetrics(self.fLabelFont).height() self.fLabelPos.setX(float(self.fSize)/2 - float(self.fLabelWidth)/2) self.fLabelPos.setY(self.fSize + self.fLabelHeight) self.fLabelGradient.setColorAt(0.0, self.fColor1) self.fLabelGradient.setColorAt(0.6, self.fColor1) self.fLabelGradient.setColorAt(1.0, self.fColor2) self.fLabelGradient.setStart(0, float(self.fSize)/2) self.fLabelGradient.setFinalStop(0, self.fSize + self.fLabelHeight + 5) self.fLabelGradientRect = QRectF(float(self.fSize)/8, float(self.fSize)/2, float(self.fSize)*6/8, self.fSize+self.fLabelHeight+5) self.update() def setPixmap(self, pixmapId): self.fPixmapNum = "%02i" % pixmapId self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d")) if self.fPixmap.width() > self.fPixmap.height(): self.fOrientation = self.HORIZONTAL else: self.fOrientation = self.VERTICAL self.updateSizes() self.update() def minimumSizeHint(self): return QSize(self.fSize, self.fSize) def sizeHint(self): return QSize(self.fSize, self.fSize) def updateSizes(self): self.fWidth = self.fPixmap.width() self.fHeight = self.fPixmap.height() if self.fWidth < 1: self.fWidth = 1 if self.fHeight < 1: self.fHeight = 1 if self.fOrientation == self.HORIZONTAL: self.fSize = self.fHeight self.fCount = self.fWidth / self.fHeight else: self.fSize = self.fWidth self.fCount = self.fHeight / self.fWidth self.setMinimumSize(self.fSize, self.fSize + self.fLabelHeight + 5) self.setMaximumSize(self.fSize, self.fSize + self.fLabelHeight + 5) def enterEvent(self, event): self.fHovered = True if self.fHoverStep == self.HOVER_MIN: self.fHoverStep += 1 QDial.enterEvent(self, event) def leaveEvent(self, event): self.fHovered = False if self.fHoverStep == self.HOVER_MAX: self.fHoverStep -= 1 QDial.leaveEvent(self, event) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) event.accept() if self.fLabel: if self.fCustomPaint == self.CUSTOM_PAINT_NULL: painter.setPen(self.fColor2) painter.setBrush(self.fLabelGradient) painter.drawRect(self.fLabelGradientRect) painter.setFont(self.fLabelFont) painter.setPen(self.fColorT[0 if self.isEnabled() else 1]) painter.drawText(self.fLabelPos, self.fLabel) if self.isEnabled(): current = float(self.value() - self.minimum()) divider = float(self.maximum() - self.minimum()) if divider == 0.0: return value = current / divider target = QRectF(0.0, 0.0, self.fSize, self.fSize) per = int((self.fCount - 1) * value) if self.fOrientation == self.HORIZONTAL: xpos = self.fSize * per ypos = 0.0 else: xpos = 0.0 ypos = self.fSize * per source = QRectF(xpos, ypos, self.fSize, self.fSize) painter.drawPixmap(target, self.fPixmap, source) # Custom knobs (Dry/Wet and Volume) if self.fCustomPaint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL): # knob color colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.fHoverStep*7) colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.fHoverStep*7) # 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*value) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) # draw arc startAngle = 216*16 spanAngle = -252*16*value if self.fCustomPaint == self.CUSTOM_PAINT_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(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle) # Custom knobs (L and R) elif self.fCustomPaint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R): # knob color color = QColor(0xAD + self.fHoverStep*5, 0xD5 + self.fHoverStep*4, 0x4B + self.fHoverStep*5) # 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*value) 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.fCustomPaint == self.CUSTOM_PAINT_CARLA_L: startAngle = 216*16 spanAngle = -252.0*16*value elif self.fCustomPaint == self.CUSTOM_PAINT_CARLA_R: startAngle = 324.0*16 spanAngle = 252.0*16*(1.0-value) else: return painter.setPen(QPen(color, 2)) painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle) if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX: self.fHoverStep += 1 if self.fHovered else -1 QTimer.singleShot(20, self, SLOT("update()")) else: # isEnabled() target = QRectF(0.0, 0.0, self.fSize, self.fSize) painter.drawPixmap(target, self.fPixmap, target) def resizeEvent(self, event): self.updateSizes() QDial.resizeEvent(self, event)