#!/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.m_pixmap = QPixmap(":/bitmaps/dial_01d.png") self.m_pixmapNum = "01" self.m_customPaint = self.CUSTOM_PAINT_NULL self.m_hovered = False self.m_hoverStep = self.HOVER_MIN if self.m_pixmap.width() > self.m_pixmap.height(): self.m_orientation = self.HORIZONTAL else: self.m_orientation = self.VERTICAL self.m_label = "" self.m_labelPos = QPointF(0.0, 0.0) self.m_labelFont = QFont() self.m_labelFont.setPointSize(6) self.m_labelWidth = 0 self.m_labelHeight = 0 self.m_labelGradient = QLinearGradient(0, 0, 0, 1) if self.palette().window().color().lightness() > 100: # Light background c = self.palette().dark().color() self.m_color1 = c self.m_color2 = QColor(c.red(), c.green(), c.blue(), 0) self.m_colorT = [self.palette().buttonText().color(), self.palette().mid().color()] else: # Dark background self.m_color1 = QColor(0, 0, 0, 255) self.m_color2 = QColor(0, 0, 0, 0) self.m_colorT = [Qt.white, Qt.darkGray] self.updateSizes() def getSize(self): return self.p_size def setCustomPaint(self, paint): self.m_customPaint = paint self.m_labelPos.setY(self.p_size + self.m_labelHeight/2) self.update() def setEnabled(self, enabled): if self.isEnabled() != enabled: self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmapNum, "" if enabled else "d")) self.updateSizes() self.update() QDial.setEnabled(self, enabled) def setLabel(self, label): self.m_label = label self.m_labelWidth = QFontMetrics(self.m_labelFont).width(label) self.m_labelHeight = QFontMetrics(self.m_labelFont).height() self.m_labelPos.setX(float(self.p_size)/2 - float(self.m_labelWidth)/2) self.m_labelPos.setY(self.p_size + self.m_labelHeight) self.m_labelGradient.setColorAt(0.0, self.m_color1) self.m_labelGradient.setColorAt(0.6, self.m_color1) self.m_labelGradient.setColorAt(1.0, self.m_color2) self.m_labelGradient.setStart(0, float(self.p_size)/2) self.m_labelGradient.setFinalStop(0, self.p_size + self.m_labelHeight + 5) self.m_labelGradient_rect = QRectF(float(self.p_size)/8, float(self.p_size)/2, float(self.p_size)*6/8, self.p_size+self.m_labelHeight+5) self.update() def setPixmap(self, pixmapId): self.m_pixmapNum = "%02i" % pixmapId self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmapNum, "" if self.isEnabled() else "d")) if self.m_pixmap.width() > self.m_pixmap.height(): self.m_orientation = self.HORIZONTAL else: self.m_orientation = self.VERTICAL self.updateSizes() self.update() def minimumSizeHint(self): return QSize(self.p_size, self.p_size) def sizeHint(self): return QSize(self.p_size, self.p_size) def updateSizes(self): self.p_width = self.m_pixmap.width() self.p_height = self.m_pixmap.height() if self.p_width < 1: self.p_width = 1 if self.p_height < 1: self.p_height = 1 if self.m_orientation == self.HORIZONTAL: self.p_size = self.p_height self.p_count = self.p_width / self.p_height else: self.p_size = self.p_width self.p_count = self.p_height / self.p_width self.setMinimumSize(self.p_size, self.p_size + self.m_labelHeight + 5) self.setMaximumSize(self.p_size, self.p_size + self.m_labelHeight + 5) def enterEvent(self, event): self.m_hovered = True if self.m_hoverStep == self.HOVER_MIN: self.m_hoverStep += 1 QDial.enterEvent(self, event) def leaveEvent(self, event): self.m_hovered = False if self.m_hoverStep == self.HOVER_MAX: self.m_hoverStep -= 1 QDial.leaveEvent(self, event) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) if self.m_label: if self.m_customPaint == self.CUSTOM_PAINT_NULL: painter.setPen(self.m_color2) painter.setBrush(self.m_labelGradient) painter.drawRect(self.m_labelGradient_rect) painter.setFont(self.m_labelFont) painter.setPen(self.m_colorT[0 if self.isEnabled() else 1]) painter.drawText(self.m_labelPos, self.m_label) 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.p_size, self.p_size) per = int((self.p_count - 1) * value) if self.m_orientation == self.HORIZONTAL: xpos = self.p_size * per ypos = 0.0 else: xpos = 0.0 ypos = self.p_size * per source = QRectF(xpos, ypos, self.p_size, self.p_size) painter.drawPixmap(target, self.m_pixmap, source) # Custom knobs (Dry/Wet and Volume) if self.m_customPaint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL): # knob color colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.m_hoverStep*7) colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.m_hoverStep*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.m_customPaint == 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.m_customPaint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R): # knob color color = QColor(0xAD + self.m_hoverStep*5, 0xD5 + self.m_hoverStep*4, 0x4B + self.m_hoverStep*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.m_customPaint == self.CUSTOM_PAINT_CARLA_L: startAngle = 216*16 spanAngle = -252.0*16*value elif self.m_customPaint == 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.m_hoverStep < self.HOVER_MAX: self.m_hoverStep += 1 if self.m_hovered else -1 QTimer.singleShot(20, self, SLOT("update()")) else: target = QRectF(0.0, 0.0, self.p_size, self.p_size) painter.drawPixmap(target, self.m_pixmap, target) event.accept() def resizeEvent(self, event): self.updateSizes() QDial.resizeEvent(self, event)