20#include <QApplication>
23#include <QGraphicsSceneMouseEvent>
28#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
29#include <QTouchDevice>
31#include <QInputDevice>
49class PianoScene::PianoScenePrivate
66 m_keyboardEnabled( true ),
67 m_mouseEnabled( true ),
68 m_touchEnabled( true ),
69 m_mousePressed( false ),
72 m_velocityTint( true ),
74 m_keybdMap( nullptr ),
75 m_showColorScale( false ),
77 m_backgroundPalette(PianoPalette(
PAL_KEYS)),
78 m_foregroundPalette(PianoPalette(
PAL_FONT)),
80 m_usingNativeFilter( false ),
81 m_octaveSubscript( true )
84 void saveData(QByteArray& buffer)
86 QDataStream ds(&buffer, QIODevice::WriteOnly);
95 ds << m_keyboardEnabled;
101 ds << m_velocityTint;
105 ds << m_showColorScale;
106 ds << m_hilightPalette;
107 ds << m_backgroundPalette;
108 ds << m_foregroundPalette;
112 ds << m_usingNativeFilter;
113 ds << m_octaveSubscript;
116 void loadData(QByteArray& buffer)
119 QDataStream ds(&buffer, QIODevice::ReadOnly);
128 ds >> m_keyboardEnabled;
129 ds >> m_mouseEnabled;
130 ds >> m_touchEnabled;
131 ds >> m_mousePressed;
134 ds >> m_velocityTint;
138 ds >> m_showColorScale;
139 ds >> m_hilightPalette;
140 ds >> m_backgroundPalette;
141 ds >> m_foregroundPalette;
145 ds >> m_usingNativeFilter;
146 ds >> m_octaveSubscript;
149 QString noteName( PianoKey* key,
bool richText )
151 Q_ASSERT(key !=
nullptr);
152 int note = key->getNote();
153 int num = (note + m_transpose + 12) % 12;
154 int adj = ((note + m_transpose < 0) ? 2 : 1) - m_octave + 1;
155 int oct = m_baseOctave + ((note + m_transpose) / 12) - adj;
156 QString nameMask = QLatin1String(richText && m_octaveSubscript ?
"%1<sub>%2</sub>" :
"%1%2");
157 if (m_noteNames.isEmpty()) {
159 if (!m_names_f.isEmpty() && !m_names_s.isEmpty()) {
160 switch(m_alterations) {
162 name = m_names_f.value(num);
165 name = m_names_s.value(num);
168 if (key->isBlack()) {
171 name = m_names_s.value(num);
180 return nameMask.arg(name).arg(oct);
183 if (m_noteNames.length() == 128) {
184 int n = m_baseOctave*12 + note + m_transpose;
186 if (n >= 0 && n < m_noteNames.length()) {
187 return m_noteNames.value(n);
189 }
else if (m_noteNames.length() >= 12) {
191 return m_noteNames.value(num);
193 return nameMask.arg(m_noteNames.value(num)).arg(oct);
211 bool m_keyboardEnabled;
218 PianoHandler *m_handler;
220 QHash<int, PianoKey *> m_keys;
221 QMap<int, KeyLabel *> m_labels;
222 QStringList m_noteNames;
223 QStringList m_names_s;
224 QStringList m_names_f;
225 bool m_showColorScale;
226 PianoPalette m_hilightPalette;
227 PianoPalette m_backgroundPalette;
228 PianoPalette m_foregroundPalette;
231 bool m_usingNativeFilter;
232 bool m_octaveSubscript;
235 QMap<int, PianoKey *> m_touched;
238const int KEYWIDTH = 180;
239const int KEYHEIGHT = 720;
241static qreal sceneWidth(
int keys) {
242 return KEYWIDTH * qCeil( keys * 7.0 / 12.0 );
256 const QColor& keyPressedColor,
258 :
QGraphicsScene( QRectF(0, 0, sceneWidth(numKeys), KEYHEIGHT), parent ),
259 d(new PianoScenePrivate(baseOctave, numKeys, startKey))
261 if (keyPressedColor.isValid()) {
265 d->m_view =
dynamic_cast<PianoKeybd*
>(parent);
266 if (d->m_view !=
nullptr) {
267 setFont(d->m_view->font());
269 int upperLimit = d->m_numKeys + d->m_startKey;
270 int adj = d->m_startKey % 12;
272 for(
int i = d->m_startKey; i < upperLimit; ++i)
275 PianoKey* key =
nullptr;
276 KeyLabel* lbl =
nullptr;
277 int ocs = i / 12 * 7;
281 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH;
282 key =
new PianoKey( QRectF(x, 0, KEYWIDTH, KEYHEIGHT),
false, i );
283 lbl =
new KeyLabel(key);
284 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(0));
286 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH + KEYWIDTH * 0.6 + 1;
287 key =
new PianoKey( QRectF( x, 0, KEYWIDTH * 0.8 - 1, KEYHEIGHT * 0.6 ),
true, i );
289 lbl =
new KeyLabel(key);
290 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(1));
293 lbl->setFont(font());
294 key->setAcceptTouchEvents(
true);
295 key->setPressedBrush(hilightBrush);
296 d->m_keys.insert(i, key);
297 d->m_labels.insert(i, lbl);
315 return {
static_cast<int>(sceneWidth(d->m_numKeys)), KEYHEIGHT};
333 return d->m_keybdMap;
358 d->m_handler = handler;
367 return d->m_hilightPalette;
376 key->setPressed(
true);
377 int n = key->getNote() + d->m_baseOctave*12 + d->m_transpose;
378 QString s = QString(
"#%1 (%2)").arg(n).arg(d->noteName(key,
false));
380 KeyLabel* lbl =
dynamic_cast<KeyLabel*
>(key->childItems().constFirst());
381 if (lbl !=
nullptr) {
382 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 3 : 2));
384 lbl->setVisible(
true);
398 if (d->m_velocityTint && (vel >= 0) && (vel < 128) && color.isValid() ) {
399 QBrush hilightBrush(color.lighter(200 - vel));
400 key->setPressedBrush(hilightBrush);
401 }
else if (color.isValid()) {
402 key->setPressedBrush(color);
426 key->setPressed(
false);
428 KeyLabel* lbl =
dynamic_cast<KeyLabel*
>(key->childItems().constFirst());
429 if (lbl !=
nullptr) {
432 lbl->setVisible(
false);
446 int n = note - d->m_baseOctave*12 - d->m_transpose;
447 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n) && color.isValid())
448 showKeyOn(d->m_keys.value(n), color, vel);
459 int n = note - d->m_baseOctave*12 - d->m_transpose;
460 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
472 int n = note - d->m_baseOctave*12 - d->m_transpose;
473 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
494 int n = d->m_baseOctave*12 + note + d->m_transpose;
495 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
496 if (d->m_handler !=
nullptr) {
497 d->m_handler->noteOn(n, vel);
513 int n = d->m_baseOctave*12 + note + d->m_transpose;
514 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
515 if (d->m_handler !=
nullptr) {
516 d->m_handler->noteOff(n, vel);
533 switch (d->m_hilightPalette.paletteId()) {
535 c = d->m_hilightPalette.getColor(0);
538 c = d->m_hilightPalette.getColor(key->getType());
541 c = d->m_hilightPalette.getColor(d->m_channel);
544 c = d->m_hilightPalette.getColor(key->getDegree());
550 if (d->m_velocityTint && (vel >= 0) && (vel < 128)) {
551 QBrush h(c.lighter(200 - vel));
552 key->setPressedBrush(h);
554 key->setPressedBrush(c);
586 int vel = d->m_velocity * pressure;
598 int vel = d->m_velocity * pressure;
609 if (d->m_keys.contains(note))
610 keyOn(d->m_keys.value(note));
621 if (d->m_keys.contains(note))
622 keyOff(d->m_keys.value(note));
643 PianoKey* key =
nullptr;
644 QList<QGraphicsItem *> ptitems = this->items(p, Qt::IntersectsItemShape, Qt::DescendingOrder);
645 foreach(QGraphicsItem *itm, ptitems) {
646 key =
dynamic_cast<PianoKey*
>(itm);
659 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
660 if (d->m_mousePressed) {
662 PianoKey* lastkey =
getKeyForPos(mouseEvent->lastScenePos());
663 if ((lastkey !=
nullptr) && (lastkey != key) && lastkey->isPressed()) {
666 if ((key !=
nullptr) && !key->isPressed()) {
669 mouseEvent->accept();
681 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
683 if (key !=
nullptr && !key->isPressed()) {
685 d->m_mousePressed =
true;
686 mouseEvent->accept();
698 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
699 d->m_mousePressed =
false;
701 if (key !=
nullptr && key->isPressed()) {
703 mouseEvent->accept();
716 if (d->m_keybdMap !=
nullptr) {
717 KeyboardMap::ConstIterator it = d->m_keybdMap->constFind(key);
718 if ((it != d->m_keybdMap->constEnd()) && (it.key() == key)) {
719 int note = it.value();
734 if (d->m_keys.contains(note))
735 return d->m_keys.value(note);
745 if ( d->m_keyboardEnabled &&
746 !d->m_usingNativeFilter &&
747 !keyEvent->isAutoRepeat() )
749 int keyid = d->m_rawkbd ?
750#if defined(Q_OS_MACOS)
751 keyEvent->nativeVirtualKey()
753 keyEvent->nativeScanCode()
772 if ( d->m_keyboardEnabled &&
773 !d->m_usingNativeFilter &&
774 !keyEvent->isAutoRepeat() )
776 int keyid = d->m_rawkbd ?
777#if defined(Q_OS_MACOS)
778 keyEvent->nativeVirtualKey()
780 keyEvent->nativeScanCode()
801 return QGraphicsScene::event(
event);
809 foreach(PianoKey* key, d->m_keys) {
810 key->setPressed(
false);
822 if (color.isValid()) {
824 d->m_hilightPalette.setColor(0, color);
825 QBrush hilightBrush(color);
826 for (PianoKey *key : std::as_const(d->m_keys)) {
827 key->setPressedBrush(hilightBrush);
837 d->m_hilightPalette.resetColors();
839 for (PianoKey *key : std::as_const(d->m_keys)) {
840 key->setPressedBrush(hilightBrush);
858 for (PianoKey *key : std::as_const(d->m_keys)) {
859 int n = d->m_baseOctave*12 + key->getNote() + d->m_transpose;
860 bool b = !(n > d->m_maxNote) && !(n < d->m_minNote);
871 if (d->m_minNote != note) {
892 if (d->m_maxNote != note) {
904 return d->m_transpose;
913 if (d->m_baseOctave != base) {
914 d->m_baseOctave = base;
935 return d->m_startKey;
945 return (note + d->m_transpose + 12) % 12 == 0;
955 Q_ASSERT(key !=
nullptr);
956 return d->noteName(key,
true);
964 for (KeyLabel *lbl : std::as_const(d->m_labels)) {
965 PianoKey* key =
dynamic_cast<PianoKey*
>(lbl->parentItem());
966 if (key !=
nullptr) {
967 lbl->setVisible(
false);
968 lbl->setFont(font());
969 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 1 : 0));
970 lbl->setOrientation(d->m_orientation);
971 lbl->setHtml(d->noteName(key,
true));
973 lbl->setVisible((d->m_showLabels ==
ShowAlways) ||
984 for (PianoKey *key : std::as_const(d->m_keys)) {
985 if (d->m_showColorScale && (d->m_backgroundPalette.paletteId() ==
PAL_SCALE)) {
986 int degree = key->getNote() % 12;
987 key->setBrush(d->m_backgroundPalette.getColor(degree));
989 key->setBrush(d->m_backgroundPalette.getColor(key->isBlack() ? 1 : 0));
991 key->setPressed(
false);
1003 if (d->m_showLabels != show) {
1004 d->m_showLabels = show;
1016 return d->m_alterations;
1026 if (d->m_alterations != use) {
1027 d->m_alterations = use;
1047 if (d->m_orientation != orientation) {
1048 d->m_orientation = orientation;
1053bool PianoScene::isKeyboardEnabled()
const
1055 return d->m_keyboardEnabled;
1060 if (d->m_octave != octave) {
1061 d->m_octave = octave;
1068 return d->m_orientation;
1077 if (d->m_transpose != transpose && transpose > -12 && transpose < 12) {
1078 d->m_transpose = transpose;
1091 return d->m_showLabels;
1100 if (d->m_rawkbd != b) {
1111 return d->m_noteNames;
1120 return d->m_names_s;
1129 return d->m_velocity;
1138 d->m_velocity = velocity;
1148 return d->m_channel;
1158 d->m_channel = channel;
1168 d->m_noteNames = names;
1178 d->m_noteNames.clear();
1188 if (enable != d->m_keyboardEnabled) {
1189 d->m_keyboardEnabled = enable;
1199 return d->m_mouseEnabled;
1208 if (enable != d->m_mouseEnabled) {
1209 d->m_mouseEnabled = enable;
1219 return d->m_touchEnabled;
1228 if (enable != d->m_touchEnabled) {
1229 d->m_touchEnabled = enable;
1239 return d->m_velocityTint;
1249 d->m_velocityTint = enable;
1257 d->m_names_s = QStringList{
1270 d->m_names_f = QStringList{
1292 if (d->m_showColorScale != show) {
1293 d->m_showColorScale = show;
1305 return d->m_hilightPalette.getColor(0);
1314 if (d->m_hilightPalette != p) {
1315 d->m_hilightPalette = p;
1327 return d->m_backgroundPalette;
1336 if (d->m_backgroundPalette != p) {
1337 d->m_backgroundPalette = p;
1349 return d->m_foregroundPalette;
1358 if (d->m_foregroundPalette != p) {
1359 d->m_foregroundPalette = p;
1371 return d->m_showColorScale;
1374void PianoScene::setKeyPicture(
const bool natural,
const QPixmap &pix)
1376 d->m_keyPix[int(natural)] = pix;
1377 for (PianoKey *key : std::as_const(d->m_keys)) {
1378 if (key->isBlack() == !natural) {
1379 key->setPixmap(pix);
1384QPixmap PianoScene::getKeyPicture(
const bool natural)
1386 return d->m_keyPix[int(natural)];
1389void PianoScene::setUseKeyPictures(
const bool enable)
1391 d->m_useKeyPix = enable;
1392 for (PianoKey *key : std::as_const(d->m_keys)) {
1393 key->setUsePixmap(enable);
1397bool PianoScene::getUseKeyPictures()
const
1399 return d->m_useKeyPix;
1402void PianoScene::saveData(QByteArray &ba)
1407void PianoScene::loadData(QByteArray &ba)
1419 switch(touchEvent->type()) {
1420 case QEvent::TouchEnd:
1421 case QEvent::TouchCancel:
1423 foreach(PianoKey *key, d->m_touched) {
1425 if (key->isPressed()) {
1429 d->m_touched.clear();
1430 touchEvent->accept();
1433 case QEvent::TouchBegin:
1434 case QEvent::TouchUpdate:
1436 QList<QTouchEvent::TouchPoint> touchPoints =
1437 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1438 touchEvent->touchPoints();
1440 touchEvent->points();
1443 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1444 touchEvent->device()->capabilities().testFlag(QTouchDevice::Pressure);
1446 touchEvent->device()->capabilities().testFlag(QInputDevice::Capability::Pressure);
1448 foreach(
const QTouchEvent::TouchPoint& touchPoint, touchPoints) {
1450 switch (touchPoint.state()) {
1451#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1452 case Qt::TouchPointReleased:
1454 case QEventPoint::Released:
1457 PianoKey* key = d->m_touched.value(touchPoint.id());
1458 if (key !=
nullptr) {
1460 if (key->isPressed()) {
1462 keyOff(key, touchPoint.pressure());
1467 d->m_touched.remove(touchPoint.id());
1471#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1472 case Qt::TouchPointPressed:
1474 case QEventPoint::Pressed:
1478 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1479 touchPoint.pos().toPoint()
1481 touchPoint.position().toPoint()
1484 if (key !=
nullptr) {
1486 if (!key->isPressed()) {
1488 keyOn(key, touchPoint.pressure());
1492 key->ensureVisible();
1494 d->m_touched[touchPoint.id()] = key;
1498#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1499 case Qt::TouchPointMoved:
1501 case QEventPoint::Updated:
1505 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1506 touchPoint.pos().toPoint()
1508 touchPoint.position().toPoint()
1511 PianoKey* lastkey = d->m_touched.value(touchPoint.id());
1512 if ((lastkey !=
nullptr) && (lastkey != key)) {
1514 if (lastkey->isPressed()) {
1516 keyOff(lastkey, touchPoint.pressure());
1521 d->m_touched.remove(touchPoint.id());
1523 if (key !=
nullptr) {
1525 if (!key->isPressed()) {
1527 keyOn(key, touchPoint.pressure());
1532 d->m_touched[touchPoint.id()] = key;
1540 touchEvent->accept();
1555 if (newState != d->m_usingNativeFilter) {
1556 d->m_usingNativeFilter = newState;
1566 return d->m_usingNativeFilter;
1575 if (d->m_octaveSubscript != enable) {
1576 d->m_octaveSubscript = enable;
1587 return d->m_octaveSubscript;
The QEvent class is the base class of all event classes.
The QGraphicsScene class provides a surface for managing a large number of 2D graphical items.
The QObject class is the base class of all Qt objects.
PianoScene class declaration.