
10 changed files with 1017 additions and 393 deletions
@ -0,0 +1,330 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
|
|||
This 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 3 of the License, or |
|||
(at your option) 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. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
import logging; logger = logging.getLogger(__name__); logger.info("import") |
|||
|
|||
#Standard Library Modules |
|||
|
|||
#Third Party Modules |
|||
from PyQt5 import QtWidgets, QtCore, QtGui, QtOpenGL |
|||
|
|||
#Template Modules |
|||
from template.qtgui.helper import stretchRect |
|||
from template.engine.duration import baseDurationToTraditionalNumber |
|||
|
|||
#User modules |
|||
import engine.api as api |
|||
|
|||
|
|||
MAX_DURATION = 200 #to keep the code copy/paste compatible with piano grid we use the same constant but use our own value |
|||
STAFFLINEGAP = 20 #cannot be changed during runtime |
|||
SCOREHEIGHT = STAFFLINEGAP * 128 #notes |
|||
|
|||
|
|||
class VerticalPiano(QtWidgets.QGraphicsView): |
|||
def __init__(self, mainWindow): |
|||
super().__init__(mainWindow) |
|||
self.mainWindow = mainWindow |
|||
|
|||
viewport = QtWidgets.QOpenGLWidget() |
|||
viewportFormat = QtGui.QSurfaceFormat() |
|||
viewportFormat.setSwapInterval(0) #disable VSync |
|||
#viewportFormat.setSamples(2**8) #By default, the highest number of samples available is used. |
|||
viewportFormat.setDefaultFormat(viewportFormat) |
|||
viewport.setFormat(viewportFormat) |
|||
self.setViewport(viewport) |
|||
|
|||
self.setAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) |
|||
self.setDragMode(QtWidgets.QGraphicsView.NoDrag) |
|||
|
|||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) |
|||
#self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) |
|||
|
|||
self.pianoScene = _VerticalPianoScene(self) |
|||
self.setScene(self.pianoScene) |
|||
|
|||
self.setSceneRect(QtCore.QRectF(0, 0, MAX_DURATION/2, SCOREHEIGHT)) #x,y,w,h |
|||
#self.setFixedHeight(SCOREHEIGHT+3) # Don't set to scoreheight. Then we don't get a scrollbar. We need to set the sceneRect to 100%, not the view. |
|||
#self.mainWindow.ui.verticalPianoFrame.setFixedHeight(SCOREHEIGHT+3) #Don't. This makes the whole window a fixed size! |
|||
#self.setFixedWidth(MAX_DURATION) #Also done by parent widget in mainWindow |
|||
|
|||
self.setLineWidth(0) |
|||
|
|||
self.centerOn(0, 64*STAFFLINEGAP) |
|||
|
|||
|
|||
class _VerticalPianoScene(QtWidgets.QGraphicsScene): |
|||
"""Most of this is copy paste from piano grid""" |
|||
|
|||
def __init__(self, parentView): |
|||
super().__init__() |
|||
|
|||
self.parentView = parentView |
|||
#Set color, otherwise it will be transparent in window managers or wayland that want that. |
|||
self.backColor = QtGui.QColor() |
|||
self.backColor.setNamedColor("#fdfdff") |
|||
self.setBackgroundBrush(self.backColor) |
|||
|
|||
self.linesHorizontal = [] |
|||
self.highlights = {} |
|||
self.colorKeys = {} |
|||
self.blackKeys = [] |
|||
self.numberLabels = [] #index is pitch |
|||
|
|||
|
|||
self._selectedInstrument = None #tuple instrumentStatus, instrumentData |
|||
self._leftMouseDown = False #For note preview |
|||
|
|||
self.gridPen = QtGui.QPen(QtCore.Qt.SolidLine) |
|||
self.gridPen.setCosmetic(True) |
|||
|
|||
#Create two lines for the upper/lower boundaries first. They are just cosmetic |
|||
boldPen = QtGui.QPen(QtCore.Qt.SolidLine) |
|||
boldPen.setCosmetic(True) |
|||
boldPen.setWidth(1) |
|||
|
|||
hlineUp = QtWidgets.QGraphicsLineItem(0, 0, MAX_DURATION*2, 0) #x1, y1, x2, y2 |
|||
hlineUp.setPen(boldPen) |
|||
self.addItem(hlineUp) |
|||
hlineUp.setPos(0, 0) |
|||
hlineUp.setEnabled(False) |
|||
hlineUp.setAcceptedMouseButtons(QtCore.Qt.NoButton) |
|||
hlineUp.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity, True) |
|||
self.linesHorizontal.append(hlineUp) |
|||
|
|||
hlineDown = QtWidgets.QGraphicsLineItem(0, 0, MAX_DURATION*2, 0) #x1, y1, x2, y2 |
|||
hlineDown.setPen(boldPen) |
|||
self.addItem(hlineDown) |
|||
hlineDown.setPos(0, 128 * STAFFLINEGAP) |
|||
hlineDown.setEnabled(False) |
|||
hlineDown.setAcceptedMouseButtons(QtCore.Qt.NoButton) |
|||
hlineDown.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity, True) |
|||
self.linesHorizontal.append(hlineDown) |
|||
|
|||
for i in range(128): |
|||
hline = QtWidgets.QGraphicsLineItem(0, 0, MAX_DURATION*2, 0) #x1, y1, x2, y2 |
|||
hline.setPen(self.gridPen) |
|||
self.addItem(hline) |
|||
hline.setPos(0, i * STAFFLINEGAP) |
|||
hline.setEnabled(False) |
|||
hline.setAcceptedMouseButtons(QtCore.Qt.NoButton) |
|||
self.linesHorizontal.append(hline) |
|||
|
|||
blackKey = i % 12 in (1, 3, 6, 8, 10) |
|||
if blackKey: |
|||
bk = BlackKey(self) |
|||
self.blackKeys.append(bk) |
|||
self.addItem(bk) |
|||
bk.setPos(0, (127-i) * STAFFLINEGAP) |
|||
|
|||
#Various purpose color keys. They are opaque and are on top of white/black keys |
|||
ck = ColorKey(self, QtGui.QColor("cyan")) |
|||
self.addItem(ck) |
|||
self.colorKeys[i] = ck |
|||
ck.setPos(0, (127-i) * STAFFLINEGAP) |
|||
|
|||
#Highlights on top of colors. Indication if note is played. |
|||
hl = Highlight(self) |
|||
self.addItem(hl) |
|||
self.highlights[i] = hl |
|||
hl.setPos(0, (127-i) * STAFFLINEGAP) |
|||
|
|||
#Numbers last so they are on top. |
|||
numberLabel = NumberLabel(self, 127-i) |
|||
self.addItem(numberLabel) |
|||
self.numberLabels.append(numberLabel) #index is pitch |
|||
numberLabel.setPos(0, i * STAFFLINEGAP + 2) |
|||
numberLabel.setZValue(10) |
|||
|
|||
self.numberLabels.reverse() |
|||
|
|||
self.fakeDeactivationOverlay = QtWidgets.QGraphicsRectItem(0,0,MAX_DURATION,SCOREHEIGHT) |
|||
self.fakeDeactivationOverlay.setBrush(QtGui.QColor("black")) |
|||
self.fakeDeactivationOverlay.setOpacity(0.6) |
|||
self.fakeDeactivationOverlay.setEnabled(False) |
|||
self.addItem(self.fakeDeactivationOverlay) |
|||
self.fakeDeactivationOverlay.setPos(0,0) |
|||
self.fakeDeactivationOverlay.show() |
|||
#Keyboard Creation Done |
|||
|
|||
api.callbacks.instrumentMidiNoteOnActivity.append(self.highlightNoteOn) |
|||
api.callbacks.instrumentMidiNoteOffActivity.append(self.highlightNoteOff) |
|||
api.callbacks.instrumentStatusChanged.append(self.instrumentStatusChanged) |
|||