Browse Source

Add widgets

tags/1.9.4
falkTX 11 years ago
parent
commit
b1a241496b
15 changed files with 3095 additions and 0 deletions
  1. +270
    -0
      source/widgets/digitalpeakmeter.cpp
  2. +73
    -0
      source/widgets/digitalpeakmeter.hpp
  3. +233
    -0
      source/widgets/digitalpeakmeter.py
  4. +92
    -0
      source/widgets/ledbutton.cpp
  5. +54
    -0
      source/widgets/ledbutton.hpp
  6. +86
    -0
      source/widgets/ledbutton.py
  7. +118
    -0
      source/widgets/paramspinbox.cpp
  8. +64
    -0
      source/widgets/paramspinbox.hpp
  9. +383
    -0
      source/widgets/paramspinbox.py
  10. +327
    -0
      source/widgets/pixmapdial.cpp
  11. +89
    -0
      source/widgets/pixmapdial.hpp
  12. +277
    -0
      source/widgets/pixmapdial.py
  13. +524
    -0
      source/widgets/pixmapkeyboard.cpp
  14. +85
    -0
      source/widgets/pixmapkeyboard.hpp
  15. +420
    -0
      source/widgets/pixmapkeyboard.py

+ 270
- 0
source/widgets/digitalpeakmeter.cpp View File

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

+ 73
- 0
source/widgets/digitalpeakmeter.hpp View File

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

+ 233
- 0
source/widgets/digitalpeakmeter.py View File

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

+ 92
- 0
source/widgets/ledbutton.cpp View File

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

+ 54
- 0
source/widgets/ledbutton.hpp View File

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

+ 86
- 0
source/widgets/ledbutton.py View File

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

+ 118
- 0
source/widgets/paramspinbox.cpp View File

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

+ 64
- 0
source/widgets/paramspinbox.hpp View File

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

+ 383
- 0
source/widgets/paramspinbox.py View File

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

+ 327
- 0
source/widgets/pixmapdial.cpp View File

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

+ 89
- 0
source/widgets/pixmapdial.hpp View File

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

+ 277
- 0
source/widgets/pixmapdial.py View File

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

+ 524
- 0
source/widgets/pixmapkeyboard.cpp View File

@@ -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];
}

+ 85
- 0
source/widgets/pixmapkeyboard.hpp View File

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

+ 420
- 0
source/widgets/pixmapkeyboard.py View File

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

Loading…
Cancel
Save