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 )
83 void saveData(QByteArray& buffer)
85 QDataStream ds(&buffer, QIODevice::WriteOnly);
94 ds << m_keyboardEnabled;
100 ds << m_velocityTint;
104 ds << m_showColorScale;
105 ds << m_hilightPalette;
106 ds << m_backgroundPalette;
107 ds << m_foregroundPalette;
111 ds << m_usingNativeFilter;
114 void loadData(QByteArray& buffer)
117 QDataStream ds(&buffer, QIODevice::ReadOnly);
126 ds >> m_keyboardEnabled;
127 ds >> m_mouseEnabled;
128 ds >> m_touchEnabled;
129 ds >> m_mousePressed;
132 ds >> m_velocityTint;
136 ds >> m_showColorScale;
137 ds >> m_hilightPalette;
138 ds >> m_backgroundPalette;
139 ds >> m_foregroundPalette;
143 ds >> m_usingNativeFilter;
157 bool m_keyboardEnabled;
164 PianoHandler *m_handler;
166 QHash<int, PianoKey *> m_keys;
167 QMap<int, KeyLabel *> m_labels;
168 QStringList m_noteNames;
169 QStringList m_names_s;
170 QStringList m_names_f;
171 bool m_showColorScale;
172 PianoPalette m_hilightPalette;
173 PianoPalette m_backgroundPalette;
174 PianoPalette m_foregroundPalette;
177 bool m_usingNativeFilter;
180 QMap<int, PianoKey *> m_touched;
183const int KEYWIDTH = 180;
184const int KEYHEIGHT = 720;
186static qreal sceneWidth(
int keys) {
187 return KEYWIDTH * qCeil( keys * 7.0 / 12.0 );
201 const QColor& keyPressedColor,
203 :
QGraphicsScene( QRectF(0, 0, sceneWidth(numKeys), KEYHEIGHT), parent ),
204 d(new PianoScenePrivate(baseOctave, numKeys, startKey))
206 if (keyPressedColor.isValid()) {
210 d->m_view =
dynamic_cast<PianoKeybd*
>(parent);
211 if (d->m_view !=
nullptr) {
212 setFont(d->m_view->font());
214 int upperLimit = d->m_numKeys + d->m_startKey;
215 int adj = d->m_startKey % 12;
217 for(
int i = d->m_startKey; i < upperLimit; ++i)
220 PianoKey* key =
nullptr;
221 KeyLabel* lbl =
nullptr;
222 int ocs = i / 12 * 7;
226 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH;
227 key =
new PianoKey( QRectF(x, 0, KEYWIDTH, KEYHEIGHT),
false, i );
228 lbl =
new KeyLabel(key);
229 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(0));
231 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH + KEYWIDTH * 0.6 + 1;
232 key =
new PianoKey( QRectF( x, 0, KEYWIDTH * 0.8 - 1, KEYHEIGHT * 0.6 ),
true, i );
234 lbl =
new KeyLabel(key);
235 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(1));
238 lbl->setFont(font());
239 key->setAcceptTouchEvents(
true);
240 key->setPressedBrush(hilightBrush);
241 d->m_keys.insert(i, key);
242 d->m_labels.insert(i, lbl);
260 return {
static_cast<int>(sceneWidth(d->m_numKeys)), KEYHEIGHT};
278 return d->m_keybdMap;
303 d->m_handler = handler;
312 return d->m_hilightPalette;
321 key->setPressed(
true);
322 int n = key->getNote() + d->m_baseOctave*12 + d->m_transpose;
323 QString s = QString(
"#%1 (%2)").arg(n).arg(
noteName(key));
325 KeyLabel* lbl =
dynamic_cast<KeyLabel*
>(key->childItems().constFirst());
326 if (lbl !=
nullptr) {
327 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 3 : 2));
329 lbl->setVisible(
true);
343 if (d->m_velocityTint && (vel >= 0) && (vel < 128) && color.isValid() ) {
344 QBrush hilightBrush(color.lighter(200 - vel));
345 key->setPressedBrush(hilightBrush);
346 }
else if (color.isValid()) {
347 key->setPressedBrush(color);
371 key->setPressed(
false);
373 KeyLabel* lbl =
dynamic_cast<KeyLabel*
>(key->childItems().constFirst());
374 if (lbl !=
nullptr) {
377 lbl->setVisible(
false);
391 int n = note - d->m_baseOctave*12 - d->m_transpose;
392 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n) && color.isValid())
393 showKeyOn(d->m_keys.value(n), color, vel);
404 int n = note - d->m_baseOctave*12 - d->m_transpose;
405 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
417 int n = note - d->m_baseOctave*12 - d->m_transpose;
418 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
439 int n = d->m_baseOctave*12 + note + d->m_transpose;
440 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
441 if (d->m_handler !=
nullptr) {
442 d->m_handler->noteOn(n, vel);
458 int n = d->m_baseOctave*12 + note + d->m_transpose;
459 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
460 if (d->m_handler !=
nullptr) {
461 d->m_handler->noteOff(n, vel);
478 switch (d->m_hilightPalette.paletteId()) {
480 c = d->m_hilightPalette.getColor(0);
483 c = d->m_hilightPalette.getColor(key->getType());
486 c = d->m_hilightPalette.getColor(d->m_channel);
489 c = d->m_hilightPalette.getColor(key->getDegree());
495 if (d->m_velocityTint && (vel >= 0) && (vel < 128)) {
496 QBrush h(c.lighter(200 - vel));
497 key->setPressedBrush(h);
499 key->setPressedBrush(c);
531 int vel = d->m_velocity * pressure;
543 int vel = d->m_velocity * pressure;
554 if (d->m_keys.contains(note))
555 keyOn(d->m_keys.value(note));
566 if (d->m_keys.contains(note))
567 keyOff(d->m_keys.value(note));
588 PianoKey* key =
nullptr;
589 QList<QGraphicsItem *> ptitems = this->items(p, Qt::IntersectsItemShape, Qt::DescendingOrder);
590 foreach(QGraphicsItem *itm, ptitems) {
591 key =
dynamic_cast<PianoKey*
>(itm);
604 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
605 if (d->m_mousePressed) {
607 PianoKey* lastkey =
getKeyForPos(mouseEvent->lastScenePos());
608 if ((lastkey !=
nullptr) && (lastkey != key) && lastkey->isPressed()) {
611 if ((key !=
nullptr) && !key->isPressed()) {
614 mouseEvent->accept();
626 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
628 if (key !=
nullptr && !key->isPressed()) {
630 d->m_mousePressed =
true;
631 mouseEvent->accept();
643 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
644 d->m_mousePressed =
false;
646 if (key !=
nullptr && key->isPressed()) {
648 mouseEvent->accept();
661 if (d->m_keybdMap !=
nullptr) {
662 KeyboardMap::ConstIterator it = d->m_keybdMap->constFind(key);
663 if ((it != d->m_keybdMap->constEnd()) && (it.key() == key)) {
664 int note = it.value();
679 if (d->m_keys.contains(note))
680 return d->m_keys.value(note);
690 if ( d->m_keyboardEnabled &&
691 !d->m_usingNativeFilter &&
692 !keyEvent->isAutoRepeat() )
694 int keyid = d->m_rawkbd ?
695#if defined(Q_OS_MACOS)
696 keyEvent->nativeVirtualKey()
698 keyEvent->nativeScanCode()
717 if ( d->m_keyboardEnabled &&
718 !d->m_usingNativeFilter &&
719 !keyEvent->isAutoRepeat() )
721 int keyid = d->m_rawkbd ?
722#if defined(Q_OS_MACOS)
723 keyEvent->nativeVirtualKey()
725 keyEvent->nativeScanCode()
746 return QGraphicsScene::event(
event);
754 foreach(PianoKey* key, d->m_keys) {
755 key->setPressed(
false);
767 if (color.isValid()) {
769 d->m_hilightPalette.setColor(0, color);
770 QBrush hilightBrush(color);
771 for (PianoKey* key : qAsConst(d->m_keys)) {
772 key->setPressedBrush(hilightBrush);
782 d->m_hilightPalette.resetColors();
784 for (PianoKey* key : qAsConst(d->m_keys)) {
785 key->setPressedBrush(hilightBrush);
803 for (PianoKey* key : qAsConst(d->m_keys)) {
804 int n = d->m_baseOctave*12 + key->getNote() + d->m_transpose;
805 bool b = !(n > d->m_maxNote) && !(n < d->m_minNote);
816 if (d->m_minNote != note) {
837 if (d->m_maxNote != note) {
849 return d->m_transpose;
858 if (d->m_baseOctave != base) {
859 d->m_baseOctave = base;
880 return d->m_startKey;
890 return (note + d->m_transpose + 12) % 12 == 0;
900 Q_ASSERT(key !=
nullptr);
901 int note = key->getNote();
902 int num = (note + d->m_transpose + 12) % 12;
903 int adj = ((note + d->m_transpose < 0) ? 2 : 1) - d->m_octave + 1;
904 int oct = d->m_baseOctave + ((note + d->m_transpose) / 12) - adj;
905 if (d->m_noteNames.isEmpty()) {
907 if (!d->m_names_f.isEmpty() && !d->m_names_s.isEmpty()) {
908 switch(d->m_alterations) {
910 name = d->m_names_f.value(num);
913 name = d->m_names_s.value(num);
916 if (key->isBlack()) {
919 name = d->m_names_s.value(num);
928 return QString(
"%1%2").arg(name).arg(oct);
931 if (d->m_noteNames.length() == 128) {
932 int n = d->m_baseOctave*12 + note + d->m_transpose;
934 if (n >= 0 && n < d->m_noteNames.length()) {
935 return d->m_noteNames.value(n);
937 }
else if (d->m_noteNames.length() >= 12) {
939 return d->m_noteNames.value(num);
941 return QString(
"%1%2").arg(d->m_noteNames.value(num)).arg(oct);
953 for (KeyLabel* lbl : qAsConst(d->m_labels)) {
954 PianoKey* key =
dynamic_cast<PianoKey*
>(lbl->parentItem());
955 if (key !=
nullptr) {
956 lbl->setVisible(
false);
957 lbl->setFont(font());
958 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 1 : 0));
959 lbl->setOrientation(d->m_orientation);
962 lbl->setVisible((d->m_showLabels ==
ShowAlways) ||
973 for (PianoKey* key : qAsConst(d->m_keys)) {
974 if (d->m_showColorScale && (d->m_backgroundPalette.paletteId() ==
PAL_SCALE)) {
975 int degree = key->getNote() % 12;
976 key->setBrush(d->m_backgroundPalette.getColor(degree));
978 key->setBrush(d->m_backgroundPalette.getColor(key->isBlack() ? 1 : 0));
980 key->setPressed(
false);
992 if (d->m_showLabels != show) {
993 d->m_showLabels = show;
1005 return d->m_alterations;
1015 if (d->m_alterations != use) {
1016 d->m_alterations = use;
1036 if (d->m_orientation != orientation) {
1037 d->m_orientation = orientation;
1042bool PianoScene::isKeyboardEnabled()
const
1044 return d->m_keyboardEnabled;
1049 if (d->m_octave != octave) {
1050 d->m_octave = octave;
1057 return d->m_orientation;
1066 if (d->m_transpose != transpose && transpose > -12 && transpose < 12) {
1067 d->m_transpose = transpose;
1080 return d->m_showLabels;
1089 if (d->m_rawkbd != b) {
1100 return d->m_noteNames;
1109 return d->m_names_s;
1118 return d->m_velocity;
1127 d->m_velocity = velocity;
1137 return d->m_channel;
1147 d->m_channel = channel;
1157 d->m_noteNames = names;
1167 d->m_noteNames.clear();
1177 if (enable != d->m_keyboardEnabled) {
1178 d->m_keyboardEnabled = enable;
1188 return d->m_mouseEnabled;
1197 if (enable != d->m_mouseEnabled) {
1198 d->m_mouseEnabled = enable;
1208 return d->m_touchEnabled;
1217 if (enable != d->m_touchEnabled) {
1218 d->m_touchEnabled = enable;
1228 return d->m_velocityTint;
1238 d->m_velocityTint = enable;
1246 d->m_names_s = QStringList{
1259 d->m_names_f = QStringList{
1281 if (d->m_showColorScale != show) {
1282 d->m_showColorScale = show;
1294 return d->m_hilightPalette.getColor(0);
1303 if (d->m_hilightPalette != p) {
1304 d->m_hilightPalette = p;
1316 return d->m_backgroundPalette;
1325 if (d->m_backgroundPalette != p) {
1326 d->m_backgroundPalette = p;
1338 return d->m_foregroundPalette;
1347 if (d->m_foregroundPalette != p) {
1348 d->m_foregroundPalette = p;
1360 return d->m_showColorScale;
1363void PianoScene::setKeyPicture(
const bool natural,
const QPixmap &pix)
1365 d->m_keyPix[int(natural)] = pix;
1366 for (PianoKey* key : qAsConst(d->m_keys)) {
1367 if (key->isBlack() == !natural) {
1368 key->setPixmap(pix);
1373QPixmap PianoScene::getKeyPicture(
const bool natural)
1375 return d->m_keyPix[int(natural)];
1378void PianoScene::setUseKeyPictures(
const bool enable)
1380 d->m_useKeyPix = enable;
1381 for (PianoKey* key : qAsConst(d->m_keys)) {
1382 key->setUsePixmap(enable);
1386bool PianoScene::getUseKeyPictures()
const
1388 return d->m_useKeyPix;
1391void PianoScene::saveData(QByteArray &ba)
1396void PianoScene::loadData(QByteArray &ba)
1408 switch(touchEvent->type()) {
1409 case QEvent::TouchEnd:
1410 case QEvent::TouchCancel:
1412 foreach(PianoKey *key, d->m_touched) {
1414 if (key->isPressed()) {
1418 d->m_touched.clear();
1419 touchEvent->accept();
1422 case QEvent::TouchBegin:
1423 case QEvent::TouchUpdate:
1425 QList<QTouchEvent::TouchPoint> touchPoints =
1426 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1427 touchEvent->touchPoints();
1429 touchEvent->points();
1432 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1433 touchEvent->device()->capabilities().testFlag(QTouchDevice::Pressure);
1435 touchEvent->device()->capabilities().testFlag(QInputDevice::Capability::Pressure);
1437 foreach(
const QTouchEvent::TouchPoint& touchPoint, touchPoints) {
1439 switch (touchPoint.state()) {
1440#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1441 case Qt::TouchPointReleased:
1443 case QEventPoint::Released:
1446 PianoKey* key = d->m_touched.value(touchPoint.id());
1447 if (key !=
nullptr) {
1449 if (key->isPressed()) {
1451 keyOff(key, touchPoint.pressure());
1456 d->m_touched.remove(touchPoint.id());
1460#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1461 case Qt::TouchPointPressed:
1463 case QEventPoint::Pressed:
1467 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1468 touchPoint.pos().toPoint()
1470 touchPoint.position().toPoint()
1473 if (key !=
nullptr) {
1475 if (!key->isPressed()) {
1477 keyOn(key, touchPoint.pressure());
1481 key->ensureVisible();
1483 d->m_touched[touchPoint.id()] = key;
1487#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1488 case Qt::TouchPointMoved:
1490 case QEventPoint::Updated:
1494 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1495 touchPoint.pos().toPoint()
1497 touchPoint.position().toPoint()
1500 PianoKey* lastkey = d->m_touched.value(touchPoint.id());
1501 if ((lastkey !=
nullptr) && (lastkey != key)) {
1503 if (lastkey->isPressed()) {
1505 keyOff(lastkey, touchPoint.pressure());
1510 d->m_touched.remove(touchPoint.id());
1512 if (key !=
nullptr) {
1514 if (!key->isPressed()) {
1516 keyOn(key, touchPoint.pressure());
1521 d->m_touched[touchPoint.id()] = key;
1529 touchEvent->accept();
1544 if (newState != d->m_usingNativeFilter) {
1545 d->m_usingNativeFilter = newState;
1555 return d->m_usingNativeFilter;
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.