| @@ -0,0 +1,270 @@ | |||
| /* | |||
| * Digital Peak Meter, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "digitalpeakmeter.hpp" | |||
| #include <QtGui/QPainter> | |||
| DigitalPeakMeter::DigitalPeakMeter(QWidget* parent) | |||
| : QWidget(parent), | |||
| m_paintTimer(this) | |||
| { | |||
| m_channels = 0; | |||
| m_orientation = VERTICAL; | |||
| m_smoothMultiplier = 1; | |||
| m_colorBackground = QColor("#111111"); | |||
| m_gradientMeter = QLinearGradient(0, 0, 1, 1); | |||
| setChannels(0); | |||
| setColor(GREEN); | |||
| m_paintTimer.setInterval(60); | |||
| connect(&m_paintTimer, SIGNAL(timeout()), this, SLOT(update())); | |||
| m_paintTimer.start(); | |||
| } | |||
| void DigitalPeakMeter::displayMeter(int meter, float level) | |||
| { | |||
| Q_ASSERT(meter > 0 && meter <= m_channels); | |||
| if (meter <= 0 || meter > m_channels) | |||
| return qCritical("DigitalPeakMeter::displayMeter(%i, %f) - invalid meter number", meter, level); | |||
| if (level < 0.0f) | |||
| level = -level; | |||
| else if (level > 1.0f) | |||
| level = 1.0f; | |||
| m_channelsData[meter-1] = level; | |||
| } | |||
| void DigitalPeakMeter::setChannels(int channels) | |||
| { | |||
| Q_ASSERT(channels >= 0); | |||
| if (channels < 0) | |||
| return qCritical("DigitalPeakMeter::setChannels(%i) - 'channels' must be a positive integer", channels); | |||
| m_channels = channels; | |||
| m_channelsData.clear(); | |||
| m_lastValueData.clear(); | |||
| for (int i=0; i < channels; i++) | |||
| { | |||
| m_channelsData.append(0.0f); | |||
| m_lastValueData.append(0.0f); | |||
| } | |||
| } | |||
| void DigitalPeakMeter::setColor(Color color) | |||
| { | |||
| if (color == GREEN) | |||
| { | |||
| m_colorBase = QColor(93, 231, 61); | |||
| m_colorBaseT = QColor(15, 110, 15, 100); | |||
| } | |||
| else if (color == BLUE) | |||
| { | |||
| m_colorBase = QColor(82, 238, 248); | |||
| m_colorBaseT = QColor(15, 15, 110, 100); | |||
| } | |||
| else | |||
| return qCritical("DigitalPeakMeter::setColor(%i) - invalid color", color); | |||
| setOrientation(m_orientation); | |||
| } | |||
| void DigitalPeakMeter::setOrientation(Orientation orientation) | |||
| { | |||
| m_orientation = orientation; | |||
| if (m_orientation == HORIZONTAL) | |||
| { | |||
| m_gradientMeter.setColorAt(0.0f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.2f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.4f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.6f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.8f, Qt::yellow); | |||
| m_gradientMeter.setColorAt(1.0f, Qt::red); | |||
| } | |||
| else if (m_orientation == VERTICAL) | |||
| { | |||
| m_gradientMeter.setColorAt(0.0f, Qt::red); | |||
| m_gradientMeter.setColorAt(0.2f, Qt::yellow); | |||
| m_gradientMeter.setColorAt(0.4f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.6f, m_colorBase); | |||
| m_gradientMeter.setColorAt(0.8f, m_colorBase); | |||
| m_gradientMeter.setColorAt(1.0f, m_colorBase); | |||
| } | |||
| else | |||
| return qCritical("DigitalPeakMeter::setOrientation(%i) - invalid orientation", orientation); | |||
| updateSizes(); | |||
| } | |||
| void DigitalPeakMeter::setRefreshRate(int rate) | |||
| { | |||
| Q_ASSERT(rate > 0); | |||
| m_paintTimer.stop(); | |||
| m_paintTimer.setInterval(rate); | |||
| m_paintTimer.start(); | |||
| } | |||
| void DigitalPeakMeter::setSmoothRelease(int value) | |||
| { | |||
| Q_ASSERT(value >= 0 && value <= 5); | |||
| if (value < 0) | |||
| value = 0; | |||
| else if (value > 5) | |||
| value = 5; | |||
| m_smoothMultiplier = value; | |||
| } | |||
| QSize DigitalPeakMeter::minimumSizeHint() const | |||
| { | |||
| return QSize(30, 30); | |||
| } | |||
| QSize DigitalPeakMeter::sizeHint() const | |||
| { | |||
| return QSize(m_width, m_height); | |||
| } | |||
| void DigitalPeakMeter::updateSizes() | |||
| { | |||
| m_width = width(); | |||
| m_height = height(); | |||
| m_sizeMeter = 0; | |||
| if (m_orientation == HORIZONTAL) | |||
| { | |||
| m_gradientMeter.setFinalStop(m_width, 0); | |||
| if (m_channels > 0) | |||
| m_sizeMeter = m_height/m_channels; | |||
| } | |||
| else if (m_orientation == VERTICAL) | |||
| { | |||
| m_gradientMeter.setFinalStop(0, m_height); | |||
| if (m_channels > 0) | |||
| m_sizeMeter = m_width/m_channels; | |||
| } | |||
| } | |||
| void DigitalPeakMeter::paintEvent(QPaintEvent*) | |||
| { | |||
| QPainter painter(this); | |||
| painter.setPen(Qt::black); | |||
| painter.setBrush(Qt::black); | |||
| painter.drawRect(0, 0, m_width, m_height); | |||
| int meterX = 0; | |||
| painter.setPen(m_colorBackground); | |||
| painter.setBrush(m_gradientMeter); | |||
| for (int i=0; i < m_channels; i++) | |||
| { | |||
| float value, level = m_channelsData[i]; | |||
| if (level == m_lastValueData[i]) | |||
| continue; | |||
| if (m_orientation == HORIZONTAL) | |||
| value = level * m_width; | |||
| else if (m_orientation == VERTICAL) | |||
| value = float(m_height) - (level * m_height); | |||
| else | |||
| value = 0.0f; | |||
| if (value < 0.0f) | |||
| value = 0.0f; | |||
| else if (m_smoothMultiplier > 0) | |||
| value = (m_lastValueData[i] * m_smoothMultiplier + value) / (m_smoothMultiplier + 1); | |||
| if (m_orientation == HORIZONTAL) | |||
| painter.drawRect(0, meterX, value, m_sizeMeter); | |||
| else if (m_orientation == VERTICAL) | |||
| painter.drawRect(meterX, value, m_sizeMeter, m_height); | |||
| meterX += m_sizeMeter; | |||
| m_lastValueData[i] = value; | |||
| } | |||
| painter.setBrush(QColor(0, 0, 0, 0)); | |||
| if (m_orientation == HORIZONTAL) | |||
| { | |||
| // Variables | |||
| int lsmall = m_width; | |||
| int lfull = m_height - 1; | |||
| // Base | |||
| painter.setPen(m_colorBaseT); | |||
| painter.drawLine(lsmall * 0.25f, 2, lsmall * 0.25f, lfull-2); | |||
| painter.drawLine(lsmall * 0.50f, 2, lsmall * 0.50f, lfull-2); | |||
| // Yellow | |||
| painter.setPen(QColor(110, 110, 15, 100)); | |||
| painter.drawLine(lsmall * 0.70f, 2, lsmall * 0.70f, lfull-2); | |||
| painter.drawLine(lsmall * 0.83f, 2, lsmall * 0.83f, lfull-2); | |||
| // Orange | |||
| painter.setPen(QColor(180, 110, 15, 100)); | |||
| painter.drawLine(lsmall * 0.90f, 2, lsmall * 0.90f, lfull-2); | |||
| // Red | |||
| painter.setPen(QColor(110, 15, 15, 100)); | |||
| painter.drawLine(lsmall * 0.96f, 2, lsmall * 0.96f, lfull-2); | |||
| } | |||
| else if (m_orientation == VERTICAL) | |||
| { | |||
| // Variables | |||
| int lsmall = m_height; | |||
| int lfull = m_width - 1; | |||
| // Base | |||
| painter.setPen(m_colorBaseT); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.25f), lfull-2, lsmall - (lsmall * 0.25f)); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.50f), lfull-2, lsmall - (lsmall * 0.50f)); | |||
| // Yellow | |||
| painter.setPen(QColor(110, 110, 15, 100)); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.70f), lfull-2, lsmall - (lsmall * 0.70f)); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.83f), lfull-2, lsmall - (lsmall * 0.83f)); | |||
| // Orange | |||
| painter.setPen(QColor(180, 110, 15, 100)); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.90f), lfull-2, lsmall - (lsmall * 0.90f)); | |||
| // Red | |||
| painter.setPen(QColor(110, 15, 15, 100)); | |||
| painter.drawLine(2, lsmall - (lsmall * 0.96f), lfull-2, lsmall - (lsmall * 0.96f)); | |||
| } | |||
| } | |||
| void DigitalPeakMeter::resizeEvent(QResizeEvent* event) | |||
| { | |||
| updateSizes(); | |||
| QWidget::resizeEvent(event); | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| /* | |||
| * Digital Peak Meter, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef DIGITALPEAKMETER_HPP | |||
| #define DIGITALPEAKMETER_HPP | |||
| #include <QtCore/QTimer> | |||
| #include <QtGui/QWidget> | |||
| class DigitalPeakMeter : public QWidget | |||
| { | |||
| public: | |||
| enum Orientation { | |||
| HORIZONTAL = 1, | |||
| VERTICAL = 2 | |||
| }; | |||
| enum Color { | |||
| GREEN = 1, | |||
| BLUE = 2 | |||
| }; | |||
| DigitalPeakMeter(QWidget* parent); | |||
| void displayMeter(int meter, float level); | |||
| void setChannels(int channels); | |||
| void setColor(Color color); | |||
| void setOrientation(Orientation orientation); | |||
| void setRefreshRate(int rate); | |||
| void setSmoothRelease(int value); | |||
| QSize minimumSizeHint() const; | |||
| QSize sizeHint() const; | |||
| protected: | |||
| void updateSizes(); | |||
| void paintEvent(QPaintEvent* event); | |||
| void resizeEvent(QResizeEvent* event); | |||
| private: | |||
| int m_channels; | |||
| int m_smoothMultiplier; | |||
| int m_width, m_height, m_sizeMeter; | |||
| Orientation m_orientation; | |||
| QColor m_colorBackground; | |||
| QLinearGradient m_gradientMeter; | |||
| QColor m_colorBase; | |||
| QColor m_colorBaseT; | |||
| QList<float> m_channelsData; | |||
| QList<float> m_lastValueData; | |||
| QTimer m_paintTimer; | |||
| }; | |||
| #endif // DIGITALPEAKMETER_HPP | |||
| @@ -0,0 +1,233 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # Digital Peak Meter, a custom Qt4 widget | |||
| # Copyright (C) 2011-2012 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 COPYING file | |||
| # Imports (Global) | |||
| from PyQt4.QtCore import qCritical, Qt, QTimer, QSize | |||
| from PyQt4.QtGui import QColor, QLinearGradient, QPainter, QWidget | |||
| # Widget Class | |||
| class DigitalPeakMeter(QWidget): | |||
| # enum Orientation | |||
| HORIZONTAL = 1 | |||
| VERTICAL = 2 | |||
| # enum Color | |||
| GREEN = 1 | |||
| BLUE = 2 | |||
| def __init__(self, parent): | |||
| QWidget.__init__(self, parent) | |||
| self.m_channels = 0 | |||
| self.m_orientation = self.VERTICAL | |||
| self.m_smoothMultiplier = 1 | |||
| self.m_colorBackground = QColor("#111111") | |||
| self.m_gradientMeter = QLinearGradient(0, 0, 1, 1) | |||
| self.setChannels(0) | |||
| self.setColor(self.GREEN) | |||
| self.m_paintTimer = QTimer(self) | |||
| self.m_paintTimer.setInterval(60) | |||
| self.m_paintTimer.timeout.connect(self.update) | |||
| self.m_paintTimer.start() | |||
| def displayMeter(self, meter, level): | |||
| if meter <= 0 or meter > self.m_channels: | |||
| return qCritical("DigitalPeakMeter::displayMeter(%i, %f) - invalid meter number" % (meter, level)) | |||
| if level < 0.0: | |||
| level = -level | |||
| elif level > 1.0: | |||
| level = 1.0 | |||
| self.m_channelsData[meter-1] = level | |||
| def setChannels(self, channels): | |||
| if (channels < 0): | |||
| return qCritical("DigitalPeakMeter::setChannels(%i) - 'channels' must be a positive integer" % channels) | |||
| self.m_channels = channels | |||
| self.m_channelsData = [] | |||
| self.m_lastValueData = [] | |||
| for x in range(channels): | |||
| self.m_channelsData.append(0.0) | |||
| self.m_lastValueData.append(0.0) | |||
| def setColor(self, color): | |||
| if color == self.GREEN: | |||
| self.m_colorBase = QColor(93, 231, 61) | |||
| self.m_colorBaseT = QColor(15, 110, 15, 100) | |||
| elif color == self.BLUE: | |||
| self.m_colorBase = QColor(82, 238, 248) | |||
| self.m_colorBaseT = QColor(15, 15, 110, 100) | |||
| else: | |||
| return qCritical("DigitalPeakMeter::setColor(%i) - invalid color" % color) | |||
| self.setOrientation(self.m_orientation) | |||
| def setOrientation(self, orientation): | |||
| self.m_orientation = orientation | |||
| if self.m_orientation == self.HORIZONTAL: | |||
| self.m_gradientMeter.setColorAt(0.0, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.2, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.4, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.6, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.8, Qt.yellow) | |||
| self.m_gradientMeter.setColorAt(1.0, Qt.red) | |||
| elif self.m_orientation == self.VERTICAL: | |||
| self.m_gradientMeter.setColorAt(0.0, Qt.red) | |||
| self.m_gradientMeter.setColorAt(0.2, Qt.yellow) | |||
| self.m_gradientMeter.setColorAt(0.4, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.6, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(0.8, self.m_colorBase) | |||
| self.m_gradientMeter.setColorAt(1.0, self.m_colorBase) | |||
| else: | |||
| return qCritical("DigitalPeakMeter::setOrientation(%i) - invalid orientation" % orientation) | |||
| self.updateSizes() | |||
| def setRefreshRate(self, rate): | |||
| self.m_paintTimer.stop() | |||
| self.m_paintTimer.setInterval(rate) | |||
| self.m_paintTimer.start() | |||
| def setSmoothRelease(self, value): | |||
| if value < 0: | |||
| value = 0 | |||
| elif value > 5: | |||
| value = 5 | |||
| self.m_smoothMultiplier = value | |||
| def minimumSizeHint(self): | |||
| return QSize(30, 30) | |||
| def sizeHint(self): | |||
| return QSize(self.m_width, self.m_height) | |||
| def updateSizes(self): | |||
| self.m_width = self.width() | |||
| self.m_height = self.height() | |||
| self.m_sizeMeter = 0 | |||
| if self.m_orientation == self.HORIZONTAL: | |||
| self.m_gradientMeter.setFinalStop(self.m_width, 0) | |||
| if self.m_channels > 0: | |||
| self.m_sizeMeter = self.m_height / self.m_channels | |||
| elif self.m_orientation == self.VERTICAL: | |||
| self.m_gradientMeter.setFinalStop(0, self.m_height) | |||
| if self.m_channels > 0: | |||
| self.m_sizeMeter = self.m_width / self.m_channels | |||
| def paintEvent(self, event): | |||
| painter = QPainter(self) | |||
| painter.setPen(Qt.black) | |||
| painter.setBrush(Qt.black) | |||
| painter.drawRect(0, 0, self.m_width, self.m_height) | |||
| meterX = 0 | |||
| painter.setPen(self.m_colorBackground) | |||
| painter.setBrush(self.m_gradientMeter) | |||
| for i in range(self.m_channels): | |||
| level = self.m_channelsData[i] | |||
| if level == self.m_lastValueData[i]: | |||
| continue | |||
| if self.m_orientation == self.HORIZONTAL: | |||
| value = level * self.m_width | |||
| elif self.m_orientation == self.VERTICAL: | |||
| value = float(self.m_height) - (level * self.m_height) | |||
| else: | |||
| value = 0.0 | |||
| if value < 0.0: | |||
| value = 0.0 | |||
| elif self.m_smoothMultiplier > 0: | |||
| value = (self.m_lastValueData[i] * self.m_smoothMultiplier + value) / (self.m_smoothMultiplier + 1) | |||
| if self.m_orientation == self.HORIZONTAL: | |||
| painter.drawRect(0, meterX, value, self.m_sizeMeter) | |||
| elif self.m_orientation == self.VERTICAL: | |||
| painter.drawRect(meterX, value, self.m_sizeMeter, self.m_height) | |||
| meterX += self.m_sizeMeter | |||
| self.m_lastValueData[i] = value | |||
| painter.setBrush(QColor(0, 0, 0, 0)) | |||
| if self.m_orientation == self.HORIZONTAL: | |||
| # Variables | |||
| lsmall = self.m_width | |||
| lfull = self.m_height - 1 | |||
| # Base | |||
| painter.setPen(self.m_colorBaseT) | |||
| painter.drawLine(lsmall * 0.25, 2, lsmall * 0.25, lfull-2) | |||
| painter.drawLine(lsmall * 0.50, 2, lsmall * 0.50, lfull-2) | |||
| # Yellow | |||
| painter.setPen(QColor(110, 110, 15, 100)) | |||
| painter.drawLine(lsmall * 0.70, 2, lsmall * 0.70, lfull-2) | |||
| painter.drawLine(lsmall * 0.83, 2, lsmall * 0.83, lfull-2) | |||
| # Orange | |||
| painter.setPen(QColor(180, 110, 15, 100)) | |||
| painter.drawLine(lsmall * 0.90, 2, lsmall * 0.90, lfull-2) | |||
| # Red | |||
| painter.setPen(QColor(110, 15, 15, 100)) | |||
| painter.drawLine(lsmall * 0.96, 2, lsmall * 0.96, lfull-2) | |||
| elif self.m_orientation == self.VERTICAL: | |||
| # Variables | |||
| lsmall = self.m_height | |||
| lfull = self.m_width - 1 | |||
| # Base | |||
| painter.setPen(self.m_colorBaseT) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.25), lfull-2, lsmall - (lsmall * 0.25)) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.50), lfull-2, lsmall - (lsmall * 0.50)) | |||
| # Yellow | |||
| painter.setPen(QColor(110, 110, 15, 100)) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.70), lfull-2, lsmall - (lsmall * 0.70)) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.82), lfull-2, lsmall - (lsmall * 0.82)) | |||
| # Orange | |||
| painter.setPen(QColor(180, 110, 15, 100)) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.90), lfull-2, lsmall - (lsmall * 0.90)) | |||
| # Red | |||
| painter.setPen(QColor(110, 15, 15, 100)) | |||
| painter.drawLine(2, lsmall - (lsmall * 0.96), lfull-2, lsmall - (lsmall * 0.96)) | |||
| def resizeEvent(self, event): | |||
| self.updateSizes() | |||
| QWidget.resizeEvent(self, event) | |||
| @@ -0,0 +1,92 @@ | |||
| /* | |||
| * Pixmap Button, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "ledbutton.hpp" | |||
| #include <QtGui/QPainter> | |||
| LEDButton::LEDButton(QWidget* parent): | |||
| QPushButton(parent) | |||
| { | |||
| m_pixmap_rect = QRectF(0, 0, 0, 0); | |||
| setCheckable(true); | |||
| setText(""); | |||
| setColor(BLUE); | |||
| } | |||
| LEDButton::~LEDButton() | |||
| { | |||
| } | |||
| void LEDButton::setColor(Color color) | |||
| { | |||
| m_color = color; | |||
| int size; | |||
| if (1) //color in (self.BLUE, self.GREEN, self.RED, self.YELLOW): | |||
| size = 14; | |||
| else if (color == BIG_RED) | |||
| size = 64; | |||
| else | |||
| return qCritical("LEDButton::setColor(%i) - Invalid color", color); | |||
| setPixmapSize(size); | |||
| } | |||
| void LEDButton::setPixmapSize(int size) | |||
| { | |||
| m_pixmap_rect = QRectF(0, 0, size, size); | |||
| setMinimumWidth(size); | |||
| setMaximumWidth(size); | |||
| setMinimumHeight(size); | |||
| setMaximumHeight(size); | |||
| } | |||
| void LEDButton::paintEvent(QPaintEvent*) | |||
| { | |||
| QPainter painter(this); | |||
| if (isChecked()) | |||
| { | |||
| if (m_color == BLUE) | |||
| m_pixmap.load(":/bitmaps/led_blue.png"); | |||
| else if (m_color == GREEN) | |||
| m_pixmap.load(":/bitmaps/led_green.png"); | |||
| else if (m_color == RED) | |||
| m_pixmap.load(":/bitmaps/led_red.png"); | |||
| else if (m_color == YELLOW) | |||
| m_pixmap.load(":/bitmaps/led_yellow.png"); | |||
| else if (m_color == BIG_RED) | |||
| m_pixmap.load(":/bitmaps/led-big_on.png"); | |||
| else | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| if (1) //self.m_color in (self.BLUE, self.GREEN, self.RED, self.YELLOW): | |||
| m_pixmap.load(":/bitmaps/led_off.png"); | |||
| else if (m_color == BIG_RED) | |||
| m_pixmap.load(":/bitmaps/led-big_off.png"); | |||
| else | |||
| return; | |||
| } | |||
| painter.drawPixmap(m_pixmap_rect, m_pixmap, m_pixmap_rect); | |||
| } | |||
| @@ -0,0 +1,54 @@ | |||
| /* | |||
| * Pixmap Button, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef LEDBUTTON_HPP | |||
| #define LEDBUTTON_HPP | |||
| #include <QtGui/QPixmap> | |||
| #include <QtGui/QPushButton> | |||
| class LEDButton : public QPushButton | |||
| { | |||
| Q_OBJECT | |||
| public: | |||
| enum Color { | |||
| BLUE = 1, | |||
| GREEN = 2, | |||
| RED = 3, | |||
| YELLOW = 4, | |||
| BIG_RED = 5 | |||
| }; | |||
| LEDButton(QWidget* parent); | |||
| ~LEDButton(); | |||
| void setColor(Color color); | |||
| protected: | |||
| void setPixmapSize(int size); | |||
| void paintEvent(QPaintEvent* event); | |||
| private: | |||
| QPixmap m_pixmap; | |||
| QRectF m_pixmap_rect; | |||
| Color m_color; | |||
| }; | |||
| #endif // LEDBUTTON_HPP | |||
| @@ -0,0 +1,86 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # Pixmap Button, a custom Qt4 widget | |||
| # Copyright (C) 2011-2012 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 COPYING file | |||
| # Imports (Global) | |||
| from PyQt4.QtCore import qCritical, QRectF | |||
| from PyQt4.QtGui import QPainter, QPixmap, QPushButton | |||
| # Widget Class | |||
| class LEDButton(QPushButton): | |||
| BLUE = 1 | |||
| GREEN = 2 | |||
| RED = 3 | |||
| YELLOW = 4 | |||
| BIG_RED = 5 | |||
| def __init__(self, parent): | |||
| QPushButton.__init__(self, parent) | |||
| self.m_pixmap = QPixmap() | |||
| self.m_pixmap_rect = QRectF(0, 0, 0, 0) | |||
| self.setCheckable(True) | |||
| self.setText("") | |||
| self.setColor(self.BLUE) | |||
| def setColor(self, color): | |||
| self.m_color = color | |||
| if color in (self.BLUE, self.GREEN, self.RED, self.YELLOW): | |||
| size = 14 | |||
| elif color == self.BIG_RED: | |||
| size = 32 | |||
| else: | |||
| return qCritical("LEDButton::setColor(%i) - Invalid color" % color) | |||
| self.setPixmapSize(size) | |||
| def setPixmapSize(self, size): | |||
| self.m_pixmap_rect = QRectF(0, 0, size, size) | |||
| self.setMinimumWidth(size) | |||
| self.setMaximumWidth(size) | |||
| self.setMinimumHeight(size) | |||
| self.setMaximumHeight(size) | |||
| def paintEvent(self, event): | |||
| painter = QPainter(self) | |||
| if self.isChecked(): | |||
| if self.m_color == self.BLUE: | |||
| self.m_pixmap.load(":/bitmaps/led_blue.png") | |||
| elif self.m_color == self.GREEN: | |||
| self.m_pixmap.load(":/bitmaps/led_green.png") | |||
| elif self.m_color == self.RED: | |||
| self.m_pixmap.load(":/bitmaps/led_red.png") | |||
| elif self.m_color == self.YELLOW: | |||
| self.m_pixmap.load(":/bitmaps/led_yellow.png") | |||
| elif self.m_color == self.BIG_RED: | |||
| self.m_pixmap.load(":/bitmaps/led-big_on.png") | |||
| else: | |||
| return | |||
| else: | |||
| if self.m_color in (self.BLUE, self.GREEN, self.RED, self.YELLOW): | |||
| self.m_pixmap.load(":/bitmaps/led_off.png") | |||
| elif self.m_color == self.BIG_RED: | |||
| self.m_pixmap.load(":/bitmaps/led-big_off.png") | |||
| else: | |||
| return | |||
| painter.drawPixmap(self.m_pixmap_rect, self.m_pixmap, self.m_pixmap_rect) | |||
| @@ -0,0 +1,118 @@ | |||
| /* | |||
| * Parameter Progress-Bar, a custom Qt4 widget | |||
| * Copyright (C) 2012 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 COPYING file | |||
| */ | |||
| #include "paramspinbox.h" | |||
| #include <QtGui/QMouseEvent> | |||
| ParamProgressBar::ParamProgressBar(QWidget* parent) | |||
| : QProgressBar(parent) | |||
| { | |||
| m_leftClickDown = false; | |||
| m_minimum = 0.0f; | |||
| m_maximum = 1.0f; | |||
| m_rvalue = 0.0f; | |||
| m_textCall = nullptr; | |||
| setMinimum(0); | |||
| setMaximum(1000); | |||
| setValue(0); | |||
| setFormat("(none)"); | |||
| } | |||
| void ParamProgressBar::set_minimum(float value) | |||
| { | |||
| m_minimum = value; | |||
| } | |||
| void ParamProgressBar::set_maximum(float value) | |||
| { | |||
| m_maximum = value; | |||
| } | |||
| void ParamProgressBar::set_value(float value) | |||
| { | |||
| m_rvalue = value; | |||
| float vper = (value - m_minimum) / (m_maximum - m_minimum); | |||
| setValue(vper * 1000); | |||
| } | |||
| void ParamProgressBar::set_label(QString label) | |||
| { | |||
| m_label = label; | |||
| if (m_label == "(coef)") | |||
| { | |||
| m_label = ""; | |||
| m_preLabel = "*"; | |||
| } | |||
| } | |||
| void ParamProgressBar::set_text_call(TextCallback* textCall) | |||
| { | |||
| m_textCall = textCall; | |||
| } | |||
| void ParamProgressBar::handleMouseEventPos(const QPoint& pos) | |||
| { | |||
| float xper = float(pos.x()) / width(); | |||
| float value = xper * (m_maximum - m_minimum) + m_minimum; | |||
| if (value < m_minimum) | |||
| value = m_minimum; | |||
| else if (value > m_maximum) | |||
| value = m_maximum; | |||
| emit valueChangedFromBar(value); | |||
| } | |||
| void ParamProgressBar::mousePressEvent(QMouseEvent* event) | |||
| { | |||
| if (event->button() == Qt::LeftButton) | |||
| { | |||
| handleMouseEventPos(event->pos()); | |||
| m_leftClickDown = true; | |||
| } | |||
| else | |||
| m_leftClickDown = false; | |||
| QProgressBar::mousePressEvent(event); | |||
| } | |||
| void ParamProgressBar::mouseMoveEvent(QMouseEvent* event) | |||
| { | |||
| if (m_leftClickDown) | |||
| handleMouseEventPos(event->pos()); | |||
| QProgressBar::mouseMoveEvent(event); | |||
| } | |||
| void ParamProgressBar::mouseReleaseEvent(QMouseEvent* event) | |||
| { | |||
| m_leftClickDown = false; | |||
| QProgressBar::mouseReleaseEvent(event); | |||
| } | |||
| void ParamProgressBar::paintEvent(QPaintEvent* event) | |||
| { | |||
| if (m_textCall) | |||
| setFormat(QString("%1 %2 %3").arg(m_preLabel).arg(m_textCall->textCallBack()).arg(m_label)); | |||
| else | |||
| setFormat(QString("%1 %2 %3").arg(m_preLabel).arg(m_rvalue).arg(m_label)); | |||
| QProgressBar::paintEvent(event); | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| * Parameter Progress-Bar, a custom Qt4 widget | |||
| * Copyright (C) 2012 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 COPYING file | |||
| */ | |||
| #ifndef PARAMPROGRESSBAR_H | |||
| #define PARAMPROGRESSBAR_H | |||
| #include <QtGui/QProgressBar> | |||
| class TextCallback | |||
| { | |||
| public: | |||
| virtual ~TextCallback() {} | |||
| virtual const char* textCallBack() = 0; | |||
| }; | |||
| class ParamProgressBar : public QProgressBar | |||
| { | |||
| Q_OBJECT | |||
| public: | |||
| ParamProgressBar(QWidget* parent); | |||
| void set_minimum(float value); | |||
| void set_maximum(float value); | |||
| void set_value(float value); | |||
| void set_label(QString label); | |||
| void set_text_call(TextCallback* textCall); | |||
| signals: | |||
| void valueChangedFromBar(float value); | |||
| protected: | |||
| void handleMouseEventPos(const QPoint& pos); | |||
| void mousePressEvent(QMouseEvent* event); | |||
| void mouseMoveEvent(QMouseEvent* event); | |||
| void mouseReleaseEvent(QMouseEvent* event); | |||
| void paintEvent(QPaintEvent* event); | |||
| private: | |||
| bool m_leftClickDown; | |||
| float m_minimum; | |||
| float m_maximum; | |||
| float m_rvalue; | |||
| QString m_label; | |||
| QString m_preLabel; | |||
| TextCallback* m_textCall; | |||
| }; | |||
| #endif // #define PARAMPROGRESSBAR_H | |||
| @@ -0,0 +1,383 @@ | |||
| #!/usr/bin/env python | |||
| # -*- coding: utf-8 -*- | |||
| # Imports (Global) | |||
| from PyQt4.QtCore import pyqtSlot, Qt, QTimer, SIGNAL, SLOT | |||
| from PyQt4.QtGui import QAbstractSpinBox, QComboBox, QCursor, QDialog, QInputDialog, QMenu, QPainter, QProgressBar, QValidator | |||
| from PyQt4.QtGui import QStyleFactory | |||
| from math import isnan | |||
| # Imports (Custom) | |||
| import ui_inputdialog_value | |||
| def fix_value(value, minimum, maximum): | |||
| if isnan(value): | |||
| print("Parameter is NaN! - %f" % value) | |||
| return minimum | |||
| elif value < minimum: | |||
| print("Parameter too low! - %f/%f" % (value, minimum)) | |||
| return minimum | |||
| elif value > maximum: | |||
| print("Parameter too high! - %f/%f" % (value, maximum)) | |||
| return maximum | |||
| else: | |||
| return value | |||
| #QPlastiqueStyle = QStyleFactory.create("Plastique") | |||
| # Custom InputDialog with Scale Points support | |||
| class CustomInputDialog(QDialog, ui_inputdialog_value.Ui_Dialog): | |||
| def __init__(self, parent, label, current, minimum, maximum, step, scalePoints): | |||
| QDialog.__init__(self, parent) | |||
| self.setupUi(self) | |||
| self.label.setText(label) | |||
| self.doubleSpinBox.setMinimum(minimum) | |||
| self.doubleSpinBox.setMaximum(maximum) | |||
| self.doubleSpinBox.setValue(current) | |||
| self.doubleSpinBox.setSingleStep(step) | |||
| self.ret_value = current | |||
| if not scalePoints: | |||
| self.groupBox.setVisible(False) | |||
| self.resize(200, 0) | |||
| else: | |||
| text = "<table>" | |||
| for scalePoint in scalePoints: | |||
| text += "<tr><td align='right'>%f</td><td align='left'> - %s</td></tr>" % (scalePoint['value'], scalePoint['label']) | |||
| text += "</table>" | |||
| self.textBrowser.setText(text) | |||
| self.resize(200, 300) | |||
| self.connect(self, SIGNAL("accepted()"), self.setReturnValue) | |||
| def setReturnValue(self): | |||
| self.ret_value = self.doubleSpinBox.value() | |||
| def done(self, r): | |||
| QDialog.done(self, r) | |||
| self.close() | |||
| # Progress-Bar used for ParamSpinBox | |||
| class ParamProgressBar(QProgressBar): | |||
| def __init__(self, parent): | |||
| QProgressBar.__init__(self, parent) | |||
| self.m_leftClickDown = False | |||
| self.m_minimum = 0.0 | |||
| self.m_maximum = 1.0 | |||
| self.m_rvalue = 0.0 | |||
| self.m_label = "" | |||
| self.m_preLabel = " " | |||
| self.m_textCall = None | |||
| self.setMinimum(0) | |||
| self.setMaximum(1000) | |||
| self.setValue(0) | |||
| self.setFormat("(none)") | |||
| def set_minimum(self, value): | |||
| self.m_minimum = value | |||
| def set_maximum(self, value): | |||
| self.m_maximum = value | |||
| def set_value(self, value): | |||
| self.m_rvalue = value | |||
| vper = (value - self.m_minimum) / (self.m_maximum - self.m_minimum) | |||
| self.setValue(vper * 1000) | |||
| def set_label(self, label): | |||
| self.m_label = label.strip() | |||
| if self.m_label == "(coef)": | |||
| self.m_label = "" | |||
| self.m_preLabel = "*" | |||
| self.update() | |||
| def set_text_call(self, textCall): | |||
| self.m_textCall = textCall | |||
| def handleMouseEventPos(self, pos): | |||
| xper = float(pos.x()) / self.width() | |||
| value = xper * (self.m_maximum - self.m_minimum) + self.m_minimum | |||
| if value < self.m_minimum: | |||
| value = self.m_minimum | |||
| elif value > self.m_maximum: | |||
| value = self.m_maximum | |||
| self.emit(SIGNAL("valueChangedFromBar(double)"), value) | |||
| def mousePressEvent(self, event): | |||
| if event.button() == Qt.LeftButton: | |||
| self.handleMouseEventPos(event.pos()) | |||
| self.m_leftClickDown = True | |||
| else: | |||
| self.m_leftClickDown = False | |||
| QProgressBar.mousePressEvent(self, event) | |||
| def mouseMoveEvent(self, event): | |||
| if self.m_leftClickDown: | |||
| self.handleMouseEventPos(event.pos()) | |||
| QProgressBar.mouseMoveEvent(self, event) | |||
| def mouseReleaseEvent(self, event): | |||
| self.m_leftClickDown = False | |||
| QProgressBar.mouseReleaseEvent(self, event) | |||
| def paintEvent(self, event): | |||
| if self.m_textCall: | |||
| self.setFormat("%s %s %s" % (self.m_preLabel, self.m_textCall(), self.m_label)) | |||
| else: | |||
| self.setFormat("%s %f %s" % (self.m_preLabel, self.m_rvalue, self.m_label)) | |||
| QProgressBar.paintEvent(self, event) | |||
| # Special SpinBox used for parameters | |||
| class ParamSpinBox(QAbstractSpinBox): | |||
| def __init__(self, parent): | |||
| QAbstractSpinBox.__init__(self, parent) | |||
| self._minimum = 0.0 | |||
| self._maximum = 1.0 | |||
| self._default = 0.0 | |||
| self._value = None | |||
| self._step = 0.0 | |||
| self._step_small = 0.0 | |||
| self._step_large = 0.0 | |||
| self._read_only = False | |||
| self._scalepoints = None | |||
| self._have_scalepoints = False | |||
| self.bar = ParamProgressBar(self) | |||
| self.bar.setContextMenuPolicy(Qt.NoContextMenu) | |||
| self.bar.show() | |||
| self.lineEdit().setVisible(False) | |||
| self.connect(self.bar, SIGNAL("valueChangedFromBar(double)"), self.handleValueChangedFromBar) | |||
| self.connect(self, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomMenu) | |||
| QTimer.singleShot(0, self, SLOT("slot_updateBarGeometry()")) | |||
| #def force_plastique_style(self): | |||
| #self.setStyle(QPlastiqueStyle) | |||
| def set_minimum(self, value): | |||
| self._minimum = value | |||
| self.bar.set_minimum(value) | |||
| def set_maximum(self, value): | |||
| self._maximum = value | |||
| self.bar.set_maximum(value) | |||
| def set_default(self, value): | |||
| value = fix_value(value, self._minimum, self._maximum) | |||
| self._default = value | |||
| def set_value(self, value, send=True): | |||
| value = fix_value(value, self._minimum, self._maximum) | |||
| if self._value != value: | |||
| self._value = value | |||
| self.bar.set_value(value) | |||
| if self._have_scalepoints: | |||
| self.set_scalepoint_value(value) | |||
| if send: | |||
| self.emit(SIGNAL("valueChanged(double)"), value) | |||
| self.update() | |||
| return True | |||
| else: | |||
| return False | |||
| def set_step(self, value): | |||
| if value == 0.0: | |||
| self._step = 0.001 | |||
| else: | |||
| self._step = value | |||
| if self._step_small > value: | |||
| self._step_small = value | |||
| if self._step_large < value: | |||
| self._step_large = value | |||
| def set_step_small(self, value): | |||
| if value == 0.0: | |||
| self._step_small = 0.0001 | |||
| elif value > self._step: | |||
| self._step_small = self._step | |||
| else: | |||
| self._step_small = value | |||
| def set_step_large(self, value): | |||
| if value == 0.0: | |||
| self._step_large = 0.1 | |||
| elif value < self._step: | |||
| self._step_large = self._step | |||
| else: | |||
| self._step_large = value | |||
| def set_label(self, label): | |||
| self.bar.set_label(label) | |||
| def set_text_call(self, textCall): | |||
| self.bar.set_text_call(textCall) | |||
| def set_read_only(self, yesno): | |||
| self.setButtonSymbols(QAbstractSpinBox.UpDownArrows if yesno else QAbstractSpinBox.NoButtons) | |||
| self._read_only = yesno | |||
| self.setReadOnly(yesno) | |||
| def set_scalepoints(self, scalepoints, use_scalepoints): | |||
| if len(scalepoints) > 0: | |||
| self._scalepoints = scalepoints | |||
| self._have_scalepoints = use_scalepoints | |||
| if use_scalepoints: | |||
| # Hide ProgressBar and create a ComboBox | |||
| self.bar.close() | |||
| self.box = QComboBox(self) | |||
| self.box.setContextMenuPolicy(Qt.NoContextMenu) | |||
| self.box.show() | |||
| self.slot_updateBarGeometry() | |||
| for scalepoint in scalepoints: | |||
| self.box.addItem("%f - %s" % (scalepoint['value'], scalepoint['label'])) | |||
| if self._value != None: | |||
| self.set_scalepoint_value(self._value) | |||
| self.connect(self.box, SIGNAL("currentIndexChanged(QString)"), self.handleValueChangedFromBox) | |||
| else: | |||
| self._scalepoints = None | |||
| def set_scalepoint_value(self, value): | |||
| value = self.get_nearest_scalepoint(value) | |||
| for i in range(self.box.count()): | |||
| if float(self.box.itemText(i).split(" - ", 1)[0] == value): | |||
| self.box.setCurrentIndex(i) | |||
| break | |||
| def get_nearest_scalepoint(self, real_value): | |||
| final_value = 0.0 | |||
| for i in range(len(self._scalepoints)): | |||
| scale_value = self._scalepoints[i]['value'] | |||
| if i == 0: | |||
| final_value = scale_value | |||
| else: | |||
| srange1 = abs(real_value - scale_value) | |||
| srange2 = abs(real_value - final_value) | |||
| if srange2 > srange1: | |||
| final_value = scale_value | |||
| return final_value | |||
| def handleValueChangedFromBar(self, value): | |||
| if self._read_only: | |||
| return | |||
| step = int(0.5 + ((value - self._minimum) / self._step)) | |||
| real_value = self._minimum + (step * self._step) | |||
| self.set_value(real_value) | |||
| def handleValueChangedFromBox(self, box_text): | |||
| if self._read_only: | |||
| return | |||
| value = float(box_text.split(" - ", 1)[0]) | |||
| last_scale_value = self._scalepoints[len(self._scalepoints) - 1]['value'] | |||
| if value == last_scale_value: | |||
| value = self._maximum | |||
| self.set_value(value) | |||
| def showCustomMenu(self, pos): | |||
| menu = QMenu(self) | |||
| act_x_reset = menu.addAction(self.tr("Reset (%f)" % self._default)) | |||
| menu.addSeparator() | |||
| act_x_copy = menu.addAction(self.tr("Copy (%f)" % self._value)) | |||
| if False and not self._read_only: | |||
| act_x_paste = menu.addAction(self.tr("Paste (%s)" % "TODO")) | |||
| else: | |||
| act_x_paste = menu.addAction(self.tr("Paste")) | |||
| act_x_paste.setEnabled(False) | |||
| menu.addSeparator() | |||
| act_x_set = menu.addAction(self.tr("Set value...")) | |||
| if self._read_only: | |||
| act_x_reset.setEnabled(False) | |||
| act_x_paste.setEnabled(False) | |||
| act_x_set.setEnabled(False) | |||
| # TODO - NOT IMPLEMENTED YET | |||
| act_x_copy.setEnabled(False) | |||
| act_x_sel = menu.exec_(QCursor.pos()) | |||
| if act_x_sel == act_x_set: | |||
| dialog = CustomInputDialog(self, self.parent().label.text(), self._value, self._minimum, self._maximum, self._step, self._scalepoints) | |||
| if dialog.exec_(): | |||
| value = dialog.ret_value | |||
| self.set_value(value) | |||
| elif act_x_sel == act_x_copy: | |||
| pass | |||
| elif act_x_sel == act_x_paste: | |||
| pass | |||
| elif act_x_sel == act_x_reset: | |||
| self.set_value(self._default) | |||
| def stepBy(self, steps): | |||
| if steps == 0 or self._value == None: | |||
| return | |||
| value = self._value + (steps * self._step) | |||
| if value < self._minimum: | |||
| value = self._minimum | |||
| elif value > self._maximum: | |||
| value = self._maximum | |||
| self.set_value(value) | |||
| def stepEnabled(self): | |||
| if self._read_only or self._value == None: | |||
| return QAbstractSpinBox.StepNone | |||
| elif self._value <= self._minimum: | |||
| return QAbstractSpinBox.StepUpEnabled | |||
| elif self._value >= self._maximum: | |||
| return QAbstractSpinBox.StepDownEnabled | |||
| else: | |||
| return QAbstractSpinBox.StepUpEnabled | QAbstractSpinBox.StepDownEnabled | |||
| def updateAll(self): | |||
| self.update() | |||
| self.bar.update() | |||
| if self._have_scalepoints: | |||
| self.box.update() | |||
| @pyqtSlot() | |||
| def slot_updateBarGeometry(self): | |||
| self.bar.setGeometry(self.lineEdit().geometry()) | |||
| if self._have_scalepoints: | |||
| self.box.setGeometry(self.lineEdit().geometry()) | |||
| def resizeEvent(self, event): | |||
| QTimer.singleShot(0, self, SLOT("slot_updateBarGeometry()")) | |||
| QAbstractSpinBox.resizeEvent(self, event) | |||
| @@ -0,0 +1,327 @@ | |||
| /* | |||
| * Pixmap Dial, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "pixmapdial.hpp" | |||
| #include <cmath> | |||
| #include <QtCore/QTimer> | |||
| #include <QtGui/QPainter> | |||
| PixmapDial::PixmapDial(QWidget* parent) | |||
| : QDial(parent) | |||
| { | |||
| m_pixmap.load(":/bitmaps/dial_01d.png"); | |||
| m_pixmap_n_str = "01"; | |||
| m_custom_paint = CUSTOM_PAINT_NULL; | |||
| m_hovered = false; | |||
| m_hover_step = HOVER_MIN; | |||
| if (m_pixmap.width() > m_pixmap.height()) | |||
| m_orientation = HORIZONTAL; | |||
| else | |||
| m_orientation = VERTICAL; | |||
| m_label = ""; | |||
| m_label_pos = QPointF(0.0f, 0.0f); | |||
| m_label_width = 0; | |||
| m_label_height = 0; | |||
| m_label_gradient = QLinearGradient(0, 0, 0, 1); | |||
| if (palette().window().color().lightness() > 100) | |||
| { | |||
| // Light background | |||
| QColor c = palette().dark().color(); | |||
| m_color1 = c; | |||
| m_color2 = QColor(c.red(), c.green(), c.blue(), 0); | |||
| m_colorT[0] = palette().buttonText().color(); | |||
| m_colorT[1] = palette().mid().color(); | |||
| } | |||
| else | |||
| { | |||
| // Dark background | |||
| m_color1 = QColor(0, 0, 0, 255); | |||
| m_color2 = QColor(0, 0, 0, 0); | |||
| m_colorT[0] = Qt::white; | |||
| m_colorT[1] = Qt::darkGray; | |||
| } | |||
| updateSizes(); | |||
| } | |||
| int PixmapDial::getSize() const | |||
| { | |||
| return p_size; | |||
| } | |||
| void PixmapDial::setCustomPaint(CustomPaint paint) | |||
| { | |||
| m_custom_paint = paint; | |||
| update(); | |||
| } | |||
| void PixmapDial::setEnabled(bool enabled) | |||
| { | |||
| if (isEnabled() != enabled) | |||
| { | |||
| m_pixmap.load(QString(":/dial_%1%2.png").arg(m_pixmap_n_str).arg(enabled ? "" : "d")); | |||
| updateSizes(); | |||
| update(); | |||
| } | |||
| QDial::setEnabled(enabled); | |||
| } | |||
| void PixmapDial::setLabel(QString label) | |||
| { | |||
| m_label = label; | |||
| m_label_width = QFontMetrics(font()).width(label); | |||
| m_label_height = QFontMetrics(font()).height(); | |||
| m_label_pos.setX(float(p_size)/2 - float(m_label_width)/2); | |||
| m_label_pos.setY(p_size + m_label_height); | |||
| m_label_gradient.setColorAt(0.0f, m_color1); | |||
| m_label_gradient.setColorAt(0.6f, m_color1); | |||
| m_label_gradient.setColorAt(1.0f, m_color2); | |||
| m_label_gradient.setStart(0, float(p_size)/2); | |||
| m_label_gradient.setFinalStop(0, p_size+m_label_height+5); | |||
| m_label_gradient_rect = QRectF(float(p_size)/8, float(p_size)/2, float(p_size*6)/8, p_size+m_label_height+5); | |||
| update(); | |||
| } | |||
| void PixmapDial::setPixmap(int pixmapId) | |||
| { | |||
| m_pixmap_n_str.sprintf("%02i", pixmapId); | |||
| m_pixmap.load(QString(":/bitmaps/dial_%1%2.png").arg(m_pixmap_n_str).arg(isEnabled() ? "" : "d")); | |||
| if (m_pixmap.width() > m_pixmap.height()) | |||
| m_orientation = HORIZONTAL; | |||
| else | |||
| m_orientation = VERTICAL; | |||
| updateSizes(); | |||
| update(); | |||
| } | |||
| QSize PixmapDial::minimumSizeHint() const | |||
| { | |||
| return QSize(p_size, p_size); | |||
| } | |||
| QSize PixmapDial::sizeHint() const | |||
| { | |||
| return QSize(p_size, p_size); | |||
| } | |||
| void PixmapDial::updateSizes() | |||
| { | |||
| p_width = m_pixmap.width(); | |||
| p_height = m_pixmap.height(); | |||
| if (p_width < 1) | |||
| p_width = 1; | |||
| if (p_height < 1) | |||
| p_height = 1; | |||
| if (m_orientation == HORIZONTAL) | |||
| { | |||
| p_size = p_height; | |||
| p_count = p_width/p_height; | |||
| } | |||
| else | |||
| { | |||
| p_size = p_width; | |||
| p_count = p_height/p_width; | |||
| } | |||
| setMinimumSize(p_size, p_size + m_label_height + 5); | |||
| setMaximumSize(p_size, p_size + m_label_height + 5); | |||
| } | |||
| void PixmapDial::enterEvent(QEvent* event) | |||
| { | |||
| m_hovered = true; | |||
| if (m_hover_step == HOVER_MIN) | |||
| m_hover_step += 1; | |||
| QDial::enterEvent(event); | |||
| } | |||
| void PixmapDial::leaveEvent(QEvent* event) | |||
| { | |||
| m_hovered = false; | |||
| if (m_hover_step == HOVER_MAX) | |||
| m_hover_step -= 1; | |||
| QDial::leaveEvent(event); | |||
| } | |||
| void PixmapDial::paintEvent(QPaintEvent*) | |||
| { | |||
| QPainter painter(this); | |||
| if (! m_label.isEmpty()) | |||
| { | |||
| painter.setPen(m_color2); | |||
| painter.setBrush(m_label_gradient); | |||
| painter.drawRect(m_label_gradient_rect); | |||
| painter.setPen(m_colorT[isEnabled() ? 0 : 1]); | |||
| painter.drawText(m_label_pos, m_label); | |||
| } | |||
| if (isEnabled()) | |||
| { | |||
| float current = value()-minimum(); | |||
| float divider = maximum()-minimum(); | |||
| if (divider == 0.0f) | |||
| return; | |||
| float value = current/divider; | |||
| QRectF source, target(0.0f, 0.0f, p_size, p_size); | |||
| int xpos, ypos, per = (p_count-1)*value; | |||
| if (m_orientation == HORIZONTAL) | |||
| { | |||
| xpos = p_size*per; | |||
| ypos = 0.0f; | |||
| } | |||
| else | |||
| { | |||
| xpos = 0.0f; | |||
| ypos = p_size*per; | |||
| } | |||
| source = QRectF(xpos, ypos, p_size, p_size); | |||
| painter.drawPixmap(target, m_pixmap, source); | |||
| // Custom knobs (Dry/Wet and Volume) | |||
| if (m_custom_paint == CUSTOM_PAINT_CARLA_WET || m_custom_paint == CUSTOM_PAINT_CARLA_VOL) | |||
| { | |||
| // knob color | |||
| QColor colorGreen(0x5D, 0xE7, 0x3D, 191 + m_hover_step*7); | |||
| QColor colorBlue(0x3E, 0xB8, 0xBE, 191 + m_hover_step*7); | |||
| // draw small circle | |||
| QRectF ballRect(8.0, 8.0, 15.0, 15.0); | |||
| QPainterPath ballPath; | |||
| ballPath.addEllipse(ballRect); | |||
| //painter.drawRect(ballRect); | |||
| float tmpValue = (0.375f + 0.75f*value); | |||
| float ballValue = tmpValue - floorf(tmpValue); | |||
| QPointF ballPoint(ballPath.pointAtPercent(ballValue)); | |||
| // draw arc | |||
| int startAngle = 216*16; | |||
| int spanAngle = -252*16*value; | |||
| if (m_custom_paint == CUSTOM_PAINT_CARLA_WET) | |||
| { | |||
| painter.setBrush(colorBlue); | |||
| painter.setPen(QPen(colorBlue, 0)); | |||
| painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)); | |||
| QConicalGradient gradient(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) | |||
| else if (m_custom_paint == CUSTOM_PAINT_CARLA_L || m_custom_paint == CUSTOM_PAINT_CARLA_R) | |||
| { | |||
| // knob color | |||
| QColor color(0xAD + m_hover_step*5, 0xD5 + m_hover_step*4, 0x4B + m_hover_step*5); | |||
| // draw small circle | |||
| QRectF ballRect(7.0, 8.0, 11.0, 12.0); | |||
| QPainterPath ballPath; | |||
| ballPath.addEllipse(ballRect); | |||
| //painter.drawRect(ballRect); | |||
| float tmpValue = (0.375f + 0.75f*value); | |||
| float ballValue = tmpValue - floorf(tmpValue); | |||
| QPointF ballPoint(ballPath.pointAtPercent(ballValue)); | |||
| painter.setBrush(color); | |||
| painter.setPen(QPen(color, 0)); | |||
| painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0f, 2.0f)); | |||
| int startAngle, spanAngle; | |||
| // draw arc | |||
| if (m_custom_paint == CUSTOM_PAINT_CARLA_L) | |||
| { | |||
| startAngle = 216*16; | |||
| spanAngle = -252.0*16*value; | |||
| } | |||
| else if (m_custom_paint == 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 (HOVER_MIN < m_hover_step && m_hover_step < HOVER_MAX) | |||
| { | |||
| m_hover_step += m_hovered ? 1 : -1; | |||
| QTimer::singleShot(20, this, SLOT(update())); | |||
| } | |||
| } | |||
| if (HOVER_MIN < m_hover_step && m_hover_step < HOVER_MAX) | |||
| { | |||
| m_hover_step += m_hovered ? 1 : -1; | |||
| QTimer::singleShot(20, this, SLOT(update())); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| QRectF target(0.0, 0.0, p_size, p_size); | |||
| painter.drawPixmap(target, m_pixmap, target); | |||
| } | |||
| } | |||
| void PixmapDial::resizeEvent(QResizeEvent* event) | |||
| { | |||
| updateSizes(); | |||
| QDial::resizeEvent(event); | |||
| } | |||
| @@ -0,0 +1,89 @@ | |||
| /* | |||
| * Pixmap Dial, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef PIXMAPDIAL_HPP | |||
| #define PIXMAPDIAL_HPP | |||
| #include <QtGui/QDial> | |||
| #include <QtGui/QPixmap> | |||
| class PixmapDial : public QDial | |||
| { | |||
| public: | |||
| 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 | |||
| }; | |||
| PixmapDial(QWidget* parent); | |||
| int getSize() const; | |||
| void setCustomPaint(CustomPaint paint); | |||
| void setEnabled(bool enabled); | |||
| void setLabel(QString label); | |||
| void setPixmap(int pixmapId); | |||
| QSize minimumSizeHint() const; | |||
| QSize sizeHint() const; | |||
| protected: | |||
| void updateSizes(); | |||
| void enterEvent(QEvent* event); | |||
| void leaveEvent(QEvent* event); | |||
| void paintEvent(QPaintEvent* event); | |||
| void resizeEvent(QResizeEvent* event); | |||
| private: | |||
| enum Orientation { | |||
| HORIZONTAL = 0, | |||
| VERTICAL = 1 | |||
| }; | |||
| static const unsigned short HOVER_MIN = 0; | |||
| static const unsigned short HOVER_MAX = 9; | |||
| // ------------------------------------- | |||
| QPixmap m_pixmap; | |||
| QString m_pixmap_n_str; | |||
| CustomPaint m_custom_paint; | |||
| Orientation m_orientation; | |||
| bool m_hovered; | |||
| unsigned short m_hover_step; | |||
| QString m_label; | |||
| QPointF m_label_pos; | |||
| int m_label_width; | |||
| int m_label_height; | |||
| QLinearGradient m_label_gradient; | |||
| QRectF m_label_gradient_rect; | |||
| QColor m_color1; | |||
| QColor m_color2; | |||
| QColor m_colorT[2]; | |||
| int p_width, p_height, p_size, p_count; | |||
| }; | |||
| #endif // PIXMAPDIAL_HPP | |||
| @@ -0,0 +1,277 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # Pixmap Dial, a custom Qt4 widget | |||
| # Copyright (C) 2011-2012 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 COPYING file | |||
| # Imports (Global) | |||
| from math import floor | |||
| from PyQt4.QtCore import Qt, QPointF, QRectF, QTimer, QSize, SLOT | |||
| from PyQt4.QtGui import QColor, QConicalGradient, QDial, QFontMetrics, 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_pixmap_n_str = "01" | |||
| self.m_custom_paint = self.CUSTOM_PAINT_NULL | |||
| self.m_hovered = False | |||
| self.m_hover_step = 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_label_pos = QPointF(0.0, 0.0) | |||
| self.m_label_width = 0 | |||
| self.m_label_height = 0 | |||
| self.m_label_gradient = 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_custom_paint = paint | |||
| self.update() | |||
| def setEnabled(self, enabled): | |||
| if self.isEnabled() != enabled: | |||
| self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmap_n_str, "" if enabled else "d")) | |||
| self.updateSizes() | |||
| self.update() | |||
| QDial.setEnabled(self, enabled) | |||
| def setLabel(self, label): | |||
| self.m_label = label | |||
| self.m_label_width = QFontMetrics(self.font()).width(label) | |||
| self.m_label_height = QFontMetrics(self.font()).height() | |||
| self.m_label_pos.setX(float(self.p_size)/2 - float(self.m_label_width)/2) | |||
| self.m_label_pos.setY(self.p_size + self.m_label_height) | |||
| self.m_label_gradient.setColorAt(0.0, self.m_color1) | |||
| self.m_label_gradient.setColorAt(0.6, self.m_color1) | |||
| self.m_label_gradient.setColorAt(1.0, self.m_color2) | |||
| self.m_label_gradient.setStart(0, float(self.p_size)/2) | |||
| self.m_label_gradient.setFinalStop(0, self.p_size + self.m_label_height + 5) | |||
| self.m_label_gradient_rect = QRectF(float(self.p_size)/8, float(self.p_size)/2, float(self.p_size)*6/8, self.p_size+self.m_label_height+5) | |||
| self.update() | |||
| def setPixmap(self, pixmapId): | |||
| self.m_pixmap_n_str = "%02i" % pixmapId | |||
| self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmap_n_str, "" 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_label_height + 5) | |||
| self.setMaximumSize(self.p_size, self.p_size + self.m_label_height + 5) | |||
| def enterEvent(self, event): | |||
| self.m_hovered = True | |||
| if self.m_hover_step == self.HOVER_MIN: | |||
| self.m_hover_step += 1 | |||
| QDial.enterEvent(self, event) | |||
| def leaveEvent(self, event): | |||
| self.m_hovered = False | |||
| if self.m_hover_step == self.HOVER_MAX: | |||
| self.m_hover_step -= 1 | |||
| QDial.leaveEvent(self, event) | |||
| def paintEvent(self, event): | |||
| painter = QPainter(self) | |||
| painter.setRenderHint(QPainter.Antialiasing, True) | |||
| if self.m_label: | |||
| painter.setPen(self.m_color2) | |||
| painter.setBrush(self.m_label_gradient) | |||
| painter.drawRect(self.m_label_gradient_rect) | |||
| painter.setPen(self.m_colorT[0 if self.isEnabled() else 1]) | |||
| painter.drawText(self.m_label_pos, 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_custom_paint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL): | |||
| # knob color | |||
| colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.m_hover_step*7) | |||
| colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.m_hover_step*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_custom_paint == 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_custom_paint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R): | |||
| # knob color | |||
| color = QColor(0xAD + self.m_hover_step*5, 0xD5 + self.m_hover_step*4, 0x4B + self.m_hover_step*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_custom_paint == self.CUSTOM_PAINT_CARLA_L: | |||
| startAngle = 216*16 | |||
| spanAngle = -252.0*16*value | |||
| elif self.m_custom_paint == 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_hover_step < self.HOVER_MAX: | |||
| self.m_hover_step += 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) | |||
| def resizeEvent(self, event): | |||
| self.updateSizes() | |||
| QDial.resizeEvent(self, event) | |||
| @@ -0,0 +1,524 @@ | |||
| /* | |||
| * Pixmap Keyboard, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "pixmapkeyboard.hpp" | |||
| #include <QtCore/QMap> | |||
| #include <QtCore/QTimer> | |||
| #include <QtGui/QKeyEvent> | |||
| #include <QtGui/QMouseEvent> | |||
| #include <QtGui/QPainter> | |||
| QMap<int, QRectF> midi_key2rect_map_horizontal; | |||
| QMap<int, QRectF> midi_key2rect_map_vertical; | |||
| QMap<int, int> midi_keyboard2key_map; | |||
| QVector<int> blackNotes; | |||
| static bool pixmapkeyboard_initiated = false; | |||
| void pixmapkeyboard_init() | |||
| { | |||
| if (pixmapkeyboard_initiated) | |||
| return; | |||
| pixmapkeyboard_initiated = true; | |||
| // midi_key2rect_map_horizontal ------ | |||
| midi_key2rect_map_horizontal[0] = QRectF(0, 0, 18, 64); // C | |||
| midi_key2rect_map_horizontal[1] = QRectF(13, 0, 11, 42); // C# | |||
| midi_key2rect_map_horizontal[2] = QRectF(18, 0, 25, 64); // D | |||
| midi_key2rect_map_horizontal[3] = QRectF(37, 0, 11, 42); // D# | |||
| midi_key2rect_map_horizontal[4] = QRectF(42, 0, 18, 64); // E | |||
| midi_key2rect_map_horizontal[5] = QRectF(60, 0, 18, 64); // F | |||
| midi_key2rect_map_horizontal[6] = QRectF(73, 0, 11, 42); // F# | |||
| midi_key2rect_map_horizontal[7] = QRectF(78, 0, 25, 64); // G | |||
| midi_key2rect_map_horizontal[8] = QRectF(97, 0, 11, 42); // G# | |||
| midi_key2rect_map_horizontal[9] = QRectF(102, 0, 25, 64); // A | |||
| midi_key2rect_map_horizontal[10] = QRectF(121, 0, 11, 42); // A# | |||
| midi_key2rect_map_horizontal[11] = QRectF(126, 0, 18, 64); // B | |||
| // midi_key2rect_map_vertical -------- | |||
| midi_key2rect_map_vertical[11] = QRectF(0, 0, 64, 18); // B | |||
| midi_key2rect_map_vertical[10] = QRectF(0, 14, 42, 7); // A# | |||
| midi_key2rect_map_vertical[9] = QRectF(0, 18, 64, 24); // A | |||
| midi_key2rect_map_vertical[8] = QRectF(0, 38, 42, 7); // G# | |||
| midi_key2rect_map_vertical[7] = QRectF(0, 42, 64, 24); // G | |||
| midi_key2rect_map_vertical[6] = QRectF(0, 62, 42, 7); // F# | |||
| midi_key2rect_map_vertical[5] = QRectF(0, 66, 64, 18); // F | |||
| midi_key2rect_map_vertical[4] = QRectF(0, 84, 64, 18); // E | |||
| midi_key2rect_map_vertical[3] = QRectF(0, 98, 42, 7); // D# | |||
| midi_key2rect_map_vertical[2] = QRectF(0, 102, 64, 24); // D | |||
| midi_key2rect_map_vertical[1] = QRectF(0, 122, 42, 7); // C# | |||
| midi_key2rect_map_vertical[0] = QRectF(0, 126, 64, 18); // C | |||
| // midi_keyboard2key_map ------------- | |||
| // 3th octave | |||
| midi_keyboard2key_map[Qt::Key_Z] = 48; | |||
| midi_keyboard2key_map[Qt::Key_S] = 49; | |||
| midi_keyboard2key_map[Qt::Key_X] = 50; | |||
| midi_keyboard2key_map[Qt::Key_D] = 51; | |||
| midi_keyboard2key_map[Qt::Key_C] = 52; | |||
| midi_keyboard2key_map[Qt::Key_V] = 53; | |||
| midi_keyboard2key_map[Qt::Key_G] = 54; | |||
| midi_keyboard2key_map[Qt::Key_B] = 55; | |||
| midi_keyboard2key_map[Qt::Key_H] = 56; | |||
| midi_keyboard2key_map[Qt::Key_N] = 57; | |||
| midi_keyboard2key_map[Qt::Key_J] = 58; | |||
| midi_keyboard2key_map[Qt::Key_M] = 59; | |||
| // 4th octave | |||
| midi_keyboard2key_map[Qt::Key_Q] = 60; | |||
| midi_keyboard2key_map[Qt::Key_2] = 61; | |||
| midi_keyboard2key_map[Qt::Key_W] = 62; | |||
| midi_keyboard2key_map[Qt::Key_3] = 63; | |||
| midi_keyboard2key_map[Qt::Key_E] = 64; | |||
| midi_keyboard2key_map[Qt::Key_R] = 65; | |||
| midi_keyboard2key_map[Qt::Key_5] = 66; | |||
| midi_keyboard2key_map[Qt::Key_T] = 67; | |||
| midi_keyboard2key_map[Qt::Key_6] = 68; | |||
| midi_keyboard2key_map[Qt::Key_Y] = 69; | |||
| midi_keyboard2key_map[Qt::Key_7] = 70; | |||
| midi_keyboard2key_map[Qt::Key_U] = 71; | |||
| blackNotes << 1; | |||
| blackNotes << 3; | |||
| blackNotes << 6; | |||
| blackNotes << 8; | |||
| blackNotes << 10; | |||
| } | |||
| PixmapKeyboard::PixmapKeyboard(QWidget* parent) | |||
| : QWidget(parent), | |||
| m_font("Monospace", 8, QFont::Normal) | |||
| { | |||
| pixmapkeyboard_init(); | |||
| m_octaves = 6; | |||
| m_lastMouseNote = -1; | |||
| m_needsUpdate = false; | |||
| setCursor(Qt::PointingHandCursor); | |||
| setMode(HORIZONTAL); | |||
| } | |||
| void PixmapKeyboard::allNotesOff() | |||
| { | |||
| m_enabledKeys.clear(); | |||
| m_needsUpdate = true; | |||
| QTimer::singleShot(0, this, SLOT(updateOnce())); | |||
| emit notesOff(); | |||
| } | |||
| void PixmapKeyboard::sendNoteOn(int note, bool sendSignal) | |||
| { | |||
| if (0 <= note && note <= 127 && ! m_enabledKeys.contains(note)) | |||
| { | |||
| m_enabledKeys.append(note); | |||
| if (sendSignal) | |||
| emit noteOn(note); | |||
| m_needsUpdate = true; | |||
| QTimer::singleShot(0, this, SLOT(updateOnce())); | |||
| } | |||
| if (m_enabledKeys.count() == 1) | |||
| emit notesOn(); | |||
| } | |||
| void PixmapKeyboard::sendNoteOff(int note, bool sendSignal) | |||
| { | |||
| if (note >= 0 && note <= 127 && m_enabledKeys.contains(note)) | |||
| { | |||
| m_enabledKeys.removeOne(note); | |||
| if (sendSignal) | |||
| emit noteOff(note); | |||
| m_needsUpdate = true; | |||
| QTimer::singleShot(0, this, SLOT(updateOnce())); | |||
| } | |||
| if (m_enabledKeys.count() == 0) | |||
| emit notesOff(); | |||
| } | |||
| void PixmapKeyboard::setMode(Orientation mode, Color color) | |||
| { | |||
| if (color == COLOR_CLASSIC) | |||
| { | |||
| m_colorStr = "classic"; | |||
| } | |||
| else if (color == COLOR_ORANGE) | |||
| { | |||
| m_colorStr = "orange"; | |||
| } | |||
| else | |||
| { | |||
| qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color); | |||
| return setMode(mode); | |||
| } | |||
| if (mode == HORIZONTAL) | |||
| { | |||
| m_midi_map = &midi_key2rect_map_horizontal; | |||
| m_pixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(m_colorStr)); | |||
| m_pixmap_mode = HORIZONTAL; | |||
| p_width = m_pixmap.width(); | |||
| p_height = m_pixmap.height() / 2; | |||
| } | |||
| else if (mode == VERTICAL) | |||
| { | |||
| m_midi_map = &midi_key2rect_map_vertical; | |||
| m_pixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(m_colorStr)); | |||
| m_pixmap_mode = VERTICAL; | |||
| p_width = m_pixmap.width() / 2; | |||
| p_height = m_pixmap.height(); | |||
| } | |||
| else | |||
| { | |||
| qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color); | |||
| return setMode(HORIZONTAL); | |||
| } | |||
| setOctaves(m_octaves); | |||
| } | |||
| void PixmapKeyboard::setOctaves(int octaves) | |||
| { | |||
| Q_ASSERT(octaves >= 1 && octaves <= 6); | |||
| if (octaves < 1) | |||
| octaves = 1; | |||
| else if (octaves > 6) | |||
| octaves = 6; | |||
| m_octaves = octaves; | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| { | |||
| setMinimumSize(p_width * m_octaves, p_height); | |||
| setMaximumSize(p_width * m_octaves, p_height); | |||
| } | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| { | |||
| setMinimumSize(p_width, p_height * m_octaves); | |||
| setMaximumSize(p_width, p_height * m_octaves); | |||
| } | |||
| update(); | |||
| } | |||
| void PixmapKeyboard::handleMousePos(const QPoint& pos) | |||
| { | |||
| int note, octave; | |||
| QPointF n_pos; | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| { | |||
| if (pos.x() < 0 or pos.x() > m_octaves * 144) | |||
| return; | |||
| int posX = pos.x() - 1; | |||
| octave = posX / p_width; | |||
| n_pos = QPointF(posX % p_width, pos.y()); | |||
| } | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| { | |||
| if (pos.y() < 0 or pos.y() > m_octaves * 144) | |||
| return; | |||
| int posY = pos.y() - 1; | |||
| octave = m_octaves - posY / p_height; | |||
| n_pos = QPointF(pos.x(), posY % p_height); | |||
| } | |||
| else | |||
| return; | |||
| octave += 3; | |||
| if ((*m_midi_map)[1].contains(n_pos)) // C# | |||
| note = 1; | |||
| else if ((*m_midi_map)[3].contains(n_pos)) // D# | |||
| note = 3; | |||
| else if ((*m_midi_map)[6].contains(n_pos)) // F# | |||
| note = 6; | |||
| else if ((*m_midi_map)[8].contains(n_pos)) // G# | |||
| note = 8; | |||
| else if ((*m_midi_map)[10].contains(n_pos))// A# | |||
| note = 10; | |||
| else if ((*m_midi_map)[0].contains(n_pos)) // C | |||
| note = 0; | |||
| else if ((*m_midi_map)[2].contains(n_pos)) // D | |||
| note = 2; | |||
| else if ((*m_midi_map)[4].contains(n_pos)) // E | |||
| note = 4; | |||
| else if ((*m_midi_map)[5].contains(n_pos)) // F | |||
| note = 5; | |||
| else if ((*m_midi_map)[7].contains(n_pos)) // G | |||
| note = 7; | |||
| else if ((*m_midi_map)[9].contains(n_pos)) // A | |||
| note = 9; | |||
| else if ((*m_midi_map)[11].contains(n_pos))// B | |||
| note = 11; | |||
| else | |||
| note = -1; | |||
| if (note != -1) | |||
| { | |||
| note += octave * 12; | |||
| if (m_lastMouseNote != note) | |||
| { | |||
| sendNoteOff(m_lastMouseNote); | |||
| sendNoteOn(note); | |||
| } | |||
| } | |||
| else | |||
| sendNoteOff(m_lastMouseNote); | |||
| m_lastMouseNote = note; | |||
| } | |||
| void PixmapKeyboard::keyPressEvent(QKeyEvent* event) | |||
| { | |||
| if (! event->isAutoRepeat()) | |||
| { | |||
| int qKey = event->key(); | |||
| if (midi_keyboard2key_map.keys().contains(qKey)) | |||
| sendNoteOn(midi_keyboard2key_map[qKey]); | |||
| } | |||
| QWidget::keyPressEvent(event); | |||
| } | |||
| void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event) | |||
| { | |||
| if (! event->isAutoRepeat()) | |||
| { | |||
| int qKey = event->key(); | |||
| if (midi_keyboard2key_map.keys().contains(qKey)) | |||
| sendNoteOff(midi_keyboard2key_map[qKey]); | |||
| } | |||
| QWidget::keyReleaseEvent(event); | |||
| } | |||
| void PixmapKeyboard::mousePressEvent(QMouseEvent* event) | |||
| { | |||
| m_lastMouseNote = -1; | |||
| handleMousePos(event->pos()); | |||
| setFocus(); | |||
| QWidget::mousePressEvent(event); | |||
| } | |||
| void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event) | |||
| { | |||
| handleMousePos(event->pos()); | |||
| QWidget::mousePressEvent(event); | |||
| } | |||
| void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event) | |||
| { | |||
| if (m_lastMouseNote != -1) | |||
| { | |||
| sendNoteOff(m_lastMouseNote); | |||
| m_lastMouseNote = -1; | |||
| } | |||
| QWidget::mouseReleaseEvent(event); | |||
| } | |||
| void PixmapKeyboard::paintEvent(QPaintEvent*) | |||
| { | |||
| QPainter painter(this); | |||
| // ------------------------------------------------------------- | |||
| // Paint clean keys (as background) | |||
| for (int octave=0; octave < m_octaves; octave++) | |||
| { | |||
| QRectF target; | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| target = QRectF(p_width * octave, 0, p_width, p_height); | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| target = QRectF(0, p_height * octave, p_width, p_height); | |||
| else | |||
| return; | |||
| QRectF source = QRectF(0, 0, p_width, p_height); | |||
| painter.drawPixmap(target, m_pixmap, source); | |||
| } | |||
| // ------------------------------------------------------------- | |||
| // Paint (white) pressed keys | |||
| bool paintedWhite = false; | |||
| for (int i=0; i < m_enabledKeys.count(); i++) | |||
| { | |||
| int octave, note = m_enabledKeys[i]; | |||
| QRectF pos = _getRectFromMidiNote(note); | |||
| if (_isNoteBlack(note)) | |||
| continue; | |||
| if (note < 36) | |||
| // cannot paint this note | |||
| continue; | |||
| else if (note < 48) | |||
| octave = 0; | |||
| else if (note < 60) | |||
| octave = 1; | |||
| else if (note < 72) | |||
| octave = 2; | |||
| else if (note < 84) | |||
| octave = 3; | |||
| else if (note < 96) | |||
| octave = 4; | |||
| else if (note < 108) | |||
| octave = 5; | |||
| else | |||
| // cannot paint this note either | |||
| continue; | |||
| if (m_pixmap_mode == VERTICAL) | |||
| octave = m_octaves - octave - 1; | |||
| QRectF target, source; | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| { | |||
| target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height()); | |||
| source = QRectF(pos.x(), p_height, pos.width(), pos.height()); | |||
| } | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| { | |||
| target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height()); | |||
| source = QRectF(p_width, pos.y(), pos.width(), pos.height()); | |||
| } | |||
| else | |||
| return; | |||
| paintedWhite = true; | |||
| painter.drawPixmap(target, m_pixmap, source); | |||
| } | |||
| // ------------------------------------------------------------- | |||
| // Clear white keys border | |||
| if (paintedWhite) | |||
| { | |||
| for (int octave=0; octave < m_octaves; octave++) | |||
| { | |||
| foreach (int note, blackNotes) | |||
| { | |||
| QRectF target, source; | |||
| QRectF pos = _getRectFromMidiNote(note); | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| { | |||
| target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height()); | |||
| source = QRectF(pos.x(), 0, pos.width(), pos.height()); | |||
| } | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| { | |||
| target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height()); | |||
| source = QRectF(0, pos.y(), pos.width(), pos.height()); | |||
| } | |||
| else | |||
| return; | |||
| painter.drawPixmap(target, m_pixmap, source); | |||
| } | |||
| } | |||
| } | |||
| // ------------------------------------------------------------- | |||
| // Paint (black) pressed keys | |||
| for (int i=0; i < m_enabledKeys.count(); i++) | |||
| { | |||
| int octave, note = m_enabledKeys[i]; | |||
| QRectF pos = _getRectFromMidiNote(note); | |||
| if (! _isNoteBlack(note)) | |||
| continue; | |||
| if (note < 36) | |||
| // cannot paint this note | |||
| continue; | |||
| else if (note < 48) | |||
| octave = 0; | |||
| else if (note < 60) | |||
| octave = 1; | |||
| else if (note < 72) | |||
| octave = 2; | |||
| else if (note < 84) | |||
| octave = 3; | |||
| else if (note < 96) | |||
| octave = 4; | |||
| else if (note < 108) | |||
| octave = 5; | |||
| else | |||
| // cannot paint this note either | |||
| continue; | |||
| if (m_pixmap_mode == VERTICAL) | |||
| octave = m_octaves - octave - 1; | |||
| QRectF target, source; | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| { | |||
| target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height()); | |||
| source = QRectF(pos.x(), p_height, pos.width(), pos.height()); | |||
| } | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| { | |||
| target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height()); | |||
| source = QRectF(p_width, pos.y(), pos.width(), pos.height()); | |||
| } | |||
| else | |||
| return; | |||
| painter.drawPixmap(target, m_pixmap, source); | |||
| } | |||
| // Paint C-number note info | |||
| painter.setFont(m_font); | |||
| painter.setPen(Qt::black); | |||
| for (int i=0; i < m_octaves; i++) | |||
| { | |||
| if (m_pixmap_mode == HORIZONTAL) | |||
| painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2)); | |||
| else if (m_pixmap_mode == VERTICAL) | |||
| painter.drawText(45, (m_octaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2)); | |||
| } | |||
| } | |||
| void PixmapKeyboard::updateOnce() | |||
| { | |||
| if (m_needsUpdate) | |||
| { | |||
| update(); | |||
| m_needsUpdate = false; | |||
| } | |||
| } | |||
| bool PixmapKeyboard::_isNoteBlack(int note) | |||
| { | |||
| int baseNote = note % 12; | |||
| return blackNotes.contains(baseNote); | |||
| } | |||
| QRectF PixmapKeyboard::_getRectFromMidiNote(int note) | |||
| { | |||
| return (*m_midi_map)[note % 12]; | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| * Pixmap Keyboard, a custom Qt4 widget | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef PIXMAPKEYBOARD_HPP | |||
| #define PIXMAPKEYBOARD_HPP | |||
| #include <QtGui/QPixmap> | |||
| #include <QtGui/QWidget> | |||
| class PixmapKeyboard : public QWidget | |||
| { | |||
| Q_OBJECT | |||
| public: | |||
| enum Color { | |||
| COLOR_CLASSIC = 0, | |||
| COLOR_ORANGE = 1 | |||
| }; | |||
| enum Orientation { | |||
| HORIZONTAL = 0, | |||
| VERTICAL = 1 | |||
| }; | |||
| PixmapKeyboard(QWidget* parent); | |||
| void allNotesOff(); | |||
| void sendNoteOn(int note, bool sendSignal=true); | |||
| void sendNoteOff(int note, bool sendSignal=true); | |||
| void setMode(Orientation mode, Color color=COLOR_ORANGE); | |||
| void setOctaves(int octaves); | |||
| signals: | |||
| void noteOn(int); | |||
| void noteOff(int); | |||
| void notesOn(); | |||
| void notesOff(); | |||
| protected: | |||
| void handleMousePos(const QPoint&); | |||
| void keyPressEvent(QKeyEvent*); | |||
| void keyReleaseEvent(QKeyEvent*); | |||
| void mousePressEvent(QMouseEvent*); | |||
| void mouseMoveEvent(QMouseEvent*); | |||
| void mouseReleaseEvent(QMouseEvent*); | |||
| void paintEvent(QPaintEvent*); | |||
| private Q_SLOTS: | |||
| void updateOnce(); | |||
| private: | |||
| QPixmap m_pixmap; | |||
| Orientation m_pixmap_mode; | |||
| QString m_colorStr; | |||
| QFont m_font; | |||
| int m_octaves; | |||
| int m_lastMouseNote; | |||
| int p_width, p_height; | |||
| bool m_needsUpdate; | |||
| QList<int> m_enabledKeys; | |||
| QMap<int, QRectF> *m_midi_map; | |||
| bool _isNoteBlack(int note); | |||
| QRectF _getRectFromMidiNote(int note); | |||
| }; | |||
| #endif // PIXMAPKEYBOARD_HPP | |||
| @@ -0,0 +1,420 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # Pixmap Keyboard, a custom Qt4 widget | |||
| # Copyright (C) 2011-2012 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 COPYING file | |||
| # Imports (Global) | |||
| from PyQt4.QtCore import pyqtSlot, qCritical, Qt, QPointF, QRectF, QTimer, SIGNAL, SLOT | |||
| from PyQt4.QtGui import QFont, QPainter, QPixmap, QWidget | |||
| midi_key2rect_map_horizontal = { | |||
| '0': QRectF(0, 0, 18, 64), # C | |||
| '1': QRectF(13, 0, 11, 42), # C# | |||
| '2': QRectF(18, 0, 25, 64), # D | |||
| '3': QRectF(37, 0, 11, 42), # D# | |||
| '4': QRectF(42, 0, 18, 64), # E | |||
| '5': QRectF(60, 0, 18, 64), # F | |||
| '6': QRectF(73, 0, 11, 42), # F# | |||
| '7': QRectF(78, 0, 25, 64), # G | |||
| '8': QRectF(97, 0, 11, 42), # G# | |||
| '9': QRectF(102, 0, 25, 64), # A | |||
| '10': QRectF(121, 0, 11, 42), # A# | |||
| '11': QRectF(126, 0, 18, 64) # B | |||
| } | |||
| midi_key2rect_map_vertical = { | |||
| '11': QRectF(0, 0, 64, 18), # B | |||
| '10': QRectF(0, 14, 42, 7), # A# | |||
| '9': QRectF(0, 18, 64, 24), # A | |||
| '8': QRectF(0, 38, 42, 7), # G# | |||
| '7': QRectF(0, 42, 64, 24), # G | |||
| '6': QRectF(0, 62, 42, 7), # F# | |||
| '5': QRectF(0, 66, 64, 18), # F | |||
| '4': QRectF(0, 84, 64, 18), # E | |||
| '3': QRectF(0, 98, 42, 7), # D# | |||
| '2': QRectF(0, 102, 64, 24), # D | |||
| '1': QRectF(0, 122, 42, 7), # C# | |||
| '0': QRectF(0, 126, 64, 18) # C | |||
| } | |||
| midi_keyboard2key_map = { | |||
| # 3th octave | |||
| '%i' % Qt.Key_Z: 48, | |||
| '%i' % Qt.Key_S: 49, | |||
| '%i' % Qt.Key_X: 50, | |||
| '%i' % Qt.Key_D: 51, | |||
| '%i' % Qt.Key_C: 52, | |||
| '%i' % Qt.Key_V: 53, | |||
| '%i' % Qt.Key_G: 54, | |||
| '%i' % Qt.Key_B: 55, | |||
| '%i' % Qt.Key_H: 56, | |||
| '%i' % Qt.Key_N: 57, | |||
| '%i' % Qt.Key_J: 58, | |||
| '%i' % Qt.Key_M: 59, | |||
| # 4th octave | |||
| '%i' % Qt.Key_Q: 60, | |||
| '%i' % Qt.Key_2: 61, | |||
| '%i' % Qt.Key_W: 62, | |||
| '%i' % Qt.Key_3: 63, | |||
| '%i' % Qt.Key_E: 64, | |||
| '%i' % Qt.Key_R: 65, | |||
| '%i' % Qt.Key_5: 66, | |||
| '%i' % Qt.Key_T: 67, | |||
| '%i' % Qt.Key_6: 68, | |||
| '%i' % Qt.Key_Y: 69, | |||
| '%i' % Qt.Key_7: 70, | |||
| '%i' % Qt.Key_U: 71, | |||
| } | |||
| # MIDI Keyboard, using a pixmap for painting | |||
| class PixmapKeyboard(QWidget): | |||
| # enum Color | |||
| COLOR_CLASSIC = 0 | |||
| COLOR_ORANGE = 1 | |||
| # enum Orientation | |||
| HORIZONTAL = 0 | |||
| VERTICAL = 1 | |||
| def __init__(self, parent): | |||
| QWidget.__init__(self, parent) | |||
| self.m_octaves = 6 | |||
| self.m_lastMouseNote = -1 | |||
| self.m_needsUpdate = False | |||
| self.m_enabledKeys = [] | |||
| self.m_font = QFont("Monospace", 8, QFont.Normal) | |||
| self.m_pixmap = QPixmap("") | |||
| self.setCursor(Qt.PointingHandCursor) | |||
| self.setMode(self.HORIZONTAL) | |||
| def allNotesOff(self): | |||
| self.m_enabledKeys = [] | |||
| self.m_needsUpdate = True | |||
| QTimer.singleShot(0, self, SLOT("slot_updateOnce()")) | |||
| self.emit(SIGNAL("notesOff()")) | |||
| def sendNoteOn(self, note, sendSignal=True): | |||
| if 0 <= note <= 127 and note not in self.m_enabledKeys: | |||
| self.m_enabledKeys.append(note) | |||
| if sendSignal: | |||
| self.emit(SIGNAL("noteOn(int)"), note) | |||
| self.m_needsUpdate = True | |||
| QTimer.singleShot(0, self, SLOT("slot_updateOnce()")) | |||
| if len(self.m_enabledKeys) == 1: | |||
| self.emit(SIGNAL("notesOn()")) | |||
| def sendNoteOff(self, note, sendSignal=True): | |||
| if 0 <= note <= 127 and note in self.m_enabledKeys: | |||
| self.m_enabledKeys.remove(note) | |||
| if sendSignal: | |||
| self.emit(SIGNAL("noteOff(int)"), note) | |||
| self.m_needsUpdate = True | |||
| QTimer.singleShot(0, self, SLOT("slot_updateOnce()")) | |||
| if len(self.m_enabledKeys) == 0: | |||
| self.emit(SIGNAL("notesOff()")) | |||
| def setMode(self, mode, color=COLOR_ORANGE): | |||
| if color == self.COLOR_CLASSIC: | |||
| self.m_colorStr = "classic" | |||
| elif color == self.COLOR_ORANGE: | |||
| self.m_colorStr = "orange" | |||
| else: | |||
| qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color" % (mode, color)) | |||
| return self.setMode(mode) | |||
| if mode == self.HORIZONTAL: | |||
| self.m_midi_map = midi_key2rect_map_horizontal | |||
| self.m_pixmap.load(":/bitmaps/kbd_h_%s.png" % self.m_colorStr) | |||
| self.m_pixmap_mode = self.HORIZONTAL | |||
| self.p_width = self.m_pixmap.width() | |||
| self.p_height = self.m_pixmap.height() / 2 | |||
| elif mode == self.VERTICAL: | |||
| self.m_midi_map = midi_key2rect_map_vertical | |||
| self.m_pixmap.load(":/bitmaps/kbd_v_%s.png" % self.m_colorStr) | |||
| self.m_pixmap_mode = self.VERTICAL | |||
| self.p_width = self.m_pixmap.width() / 2 | |||
| self.p_height = self.m_pixmap.height() | |||
| else: | |||
| qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode" % (mode, color)) | |||
| return self.setMode(self.HORIZONTAL) | |||
| self.setOctaves(self.m_octaves) | |||
| def setOctaves(self, octaves): | |||
| if octaves < 1: | |||
| octaves = 1 | |||
| elif octaves > 8: | |||
| octaves = 8 | |||
| self.m_octaves = octaves | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| self.setMinimumSize(self.p_width * self.m_octaves, self.p_height) | |||
| self.setMaximumSize(self.p_width * self.m_octaves, self.p_height) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| self.setMinimumSize(self.p_width, self.p_height * self.m_octaves) | |||
| self.setMaximumSize(self.p_width, self.p_height * self.m_octaves) | |||
| self.update() | |||
| def handleMousePos(self, pos): | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| if pos.x() < 0 or pos.x() > self.m_octaves * 144: | |||
| return | |||
| posX = pos.x() - 1 | |||
| octave = int(posX / self.p_width) | |||
| n_pos = QPointF(posX % self.p_width, pos.y()) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| if pos.y() < 0 or pos.y() > self.m_octaves * 144: | |||
| return | |||
| posY = pos.y() - 1 | |||
| octave = int(self.m_octaves - posY / self.p_height) | |||
| n_pos = QPointF(pos.x(), posY % self.p_height) | |||
| else: | |||
| return | |||
| octave += 3 | |||
| if self.m_midi_map['1'].contains(n_pos): # C# | |||
| note = 1 | |||
| elif self.m_midi_map['3'].contains(n_pos): # D# | |||
| note = 3 | |||
| elif self.m_midi_map['6'].contains(n_pos): # F# | |||
| note = 6 | |||
| elif self.m_midi_map['8'].contains(n_pos): # G# | |||
| note = 8 | |||
| elif self.m_midi_map['10'].contains(n_pos):# A# | |||
| note = 10 | |||
| elif self.m_midi_map['0'].contains(n_pos): # C | |||
| note = 0 | |||
| elif self.m_midi_map['2'].contains(n_pos): # D | |||
| note = 2 | |||
| elif self.m_midi_map['4'].contains(n_pos): # E | |||
| note = 4 | |||
| elif self.m_midi_map['5'].contains(n_pos): # F | |||
| note = 5 | |||
| elif self.m_midi_map['7'].contains(n_pos): # G | |||
| note = 7 | |||
| elif self.m_midi_map['9'].contains(n_pos): # A | |||
| note = 9 | |||
| elif self.m_midi_map['11'].contains(n_pos):# B | |||
| note = 11 | |||
| else: | |||
| note = -1 | |||
| if note != -1: | |||
| note += octave * 12 | |||
| if self.m_lastMouseNote != note: | |||
| self.sendNoteOff(self.m_lastMouseNote) | |||
| self.sendNoteOn(note) | |||
| else: | |||
| self.sendNoteOff(self.m_lastMouseNote) | |||
| self.m_lastMouseNote = note | |||
| def keyPressEvent(self, event): | |||
| if not event.isAutoRepeat(): | |||
| qKey = str(event.key()) | |||
| if qKey in midi_keyboard2key_map.keys(): | |||
| self.sendNoteOn(midi_keyboard2key_map.get(qKey)) | |||
| QWidget.keyPressEvent(self, event) | |||
| def keyReleaseEvent(self, event): | |||
| if not event.isAutoRepeat(): | |||
| qKey = str(event.key()) | |||
| if qKey in midi_keyboard2key_map.keys(): | |||
| self.sendNoteOff(midi_keyboard2key_map.get(qKey)) | |||
| QWidget.keyReleaseEvent(self, event) | |||
| def mousePressEvent(self, event): | |||
| self.m_lastMouseNote = -1 | |||
| self.handleMousePos(event.pos()) | |||
| self.setFocus() | |||
| QWidget.mousePressEvent(self, event) | |||
| def mouseMoveEvent(self, event): | |||
| self.handleMousePos(event.pos()) | |||
| QWidget.mousePressEvent(self, event) | |||
| def mouseReleaseEvent(self, event): | |||
| if self.m_lastMouseNote != -1: | |||
| self.sendNoteOff(self.m_lastMouseNote) | |||
| self.m_lastMouseNote = -1 | |||
| QWidget.mouseReleaseEvent(self, event) | |||
| def paintEvent(self, event): | |||
| painter = QPainter(self) | |||
| # ------------------------------------------------------------- | |||
| # Paint clean keys (as background) | |||
| for octave in range(self.m_octaves): | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| target = QRectF(self.p_width * octave, 0, self.p_width, self.p_height) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| target = QRectF(0, self.p_height * octave, self.p_width, self.p_height) | |||
| else: | |||
| return | |||
| source = QRectF(0, 0, self.p_width, self.p_height) | |||
| painter.drawPixmap(target, self.m_pixmap, source) | |||
| # ------------------------------------------------------------- | |||
| # Paint (white) pressed keys | |||
| paintedWhite = False | |||
| for i in range(len(self.m_enabledKeys)): | |||
| note = self.m_enabledKeys[i] | |||
| pos = self._getRectFromMidiNote(note) | |||
| if self._isNoteBlack(note): | |||
| continue | |||
| if note < 36: | |||
| # cannot paint this note | |||
| continue | |||
| elif note < 48: | |||
| octave = 0 | |||
| elif note < 60: | |||
| octave = 1 | |||
| elif note < 72: | |||
| octave = 2 | |||
| elif note < 84: | |||
| octave = 3 | |||
| elif note < 96: | |||
| octave = 4 | |||
| elif note < 108: | |||
| octave = 5 | |||
| elif note < 120: | |||
| octave = 6 | |||
| elif note < 132: | |||
| octave = 7 | |||
| else: | |||
| # cannot paint this note either | |||
| continue | |||
| if self.m_pixmap_mode == self.VERTICAL: | |||
| octave = self.m_octaves - octave - 1 | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height()) | |||
| source = QRectF(pos.x(), self.p_height, pos.width(), pos.height()) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height()) | |||
| source = QRectF(self.p_width, pos.y(), pos.width(), pos.height()) | |||
| else: | |||
| return | |||
| paintedWhite = True | |||
| painter.drawPixmap(target, self.m_pixmap, source) | |||
| # ------------------------------------------------------------- | |||
| # Clear white keys border | |||
| if paintedWhite: | |||
| for octave in range(self.m_octaves): | |||
| for note in (1, 3, 6, 8, 10): | |||
| pos = self._getRectFromMidiNote(note) | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height()) | |||
| source = QRectF(pos.x(), 0, pos.width(), pos.height()) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height()) | |||
| source = QRectF(0, pos.y(), pos.width(), pos.height()) | |||
| else: | |||
| return | |||
| painter.drawPixmap(target, self.m_pixmap, source) | |||
| # ------------------------------------------------------------- | |||
| # Paint (black) pressed keys | |||
| for i in range(len(self.m_enabledKeys)): | |||
| note = self.m_enabledKeys[i] | |||
| pos = self._getRectFromMidiNote(note) | |||
| if not self._isNoteBlack(note): | |||
| continue | |||
| if note < 36: | |||
| # cannot paint this note | |||
| continue | |||
| elif note < 48: | |||
| octave = 0 | |||
| elif note < 60: | |||
| octave = 1 | |||
| elif note < 72: | |||
| octave = 2 | |||
| elif note < 84: | |||
| octave = 3 | |||
| elif note < 96: | |||
| octave = 4 | |||
| elif note < 108: | |||
| octave = 5 | |||
| elif note < 120: | |||
| octave = 6 | |||
| elif note < 132: | |||
| octave = 7 | |||
| else: | |||
| # cannot paint this note either | |||
| continue | |||
| if self.m_pixmap_mode == self.VERTICAL: | |||
| octave = self.m_octaves - octave - 1 | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height()) | |||
| source = QRectF(pos.x(), self.p_height, pos.width(), pos.height()) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height()) | |||
| source = QRectF(self.p_width, pos.y(), pos.width(), pos.height()) | |||
| else: | |||
| return | |||
| painter.drawPixmap(target, self.m_pixmap, source) | |||
| # Paint C-number note info | |||
| painter.setFont(self.m_font) | |||
| painter.setPen(Qt.black) | |||
| for i in range(self.m_octaves): | |||
| if self.m_pixmap_mode == self.HORIZONTAL: | |||
| painter.drawText(i * 144, 48, 18, 18, Qt.AlignCenter, "C%i" % int(i + 2)) | |||
| elif self.m_pixmap_mode == self.VERTICAL: | |||
| painter.drawText(45, (self.m_octaves * 144) - (i * 144) - 16, 18, 18, Qt.AlignCenter, "C%i" % int(i + 2)) | |||
| @pyqtSlot() | |||
| def slot_updateOnce(self): | |||
| if self.m_needsUpdate: | |||
| self.update() | |||
| self.m_needsUpdate = False | |||
| def _isNoteBlack(self, note): | |||
| baseNote = note % 12 | |||
| return bool(baseNote in (1, 3, 6, 8, 10)) | |||
| def _getRectFromMidiNote(self, note): | |||
| return self.m_midi_map.get(str(note % 12)) | |||