| @@ -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)) | |||||