You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

153 lines
7.0 KiB

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
This file is part of Patroneo ( https://www.laborejo.org )
Laborejo 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")
SIZE_UNIT = 30 #this is in manual sync with songeditor.py SIZE_UNIT
import engine.api as api #Session is already loaded and created, no duplication.
from PyQt5 import QtCore, QtGui, QtWidgets
class Timeline(QtWidgets.QGraphicsScene):
def __init__(self, parentView):
super().__init__()
self.parentView = parentView
self.statusMessage = self.parentView.parentMainWindow.statusBar().showMessage #a version with the correct path of this is in every class of Patroneo
self.addItem(TimelineRect(parentScene=self))
#Set color, otherwise it will be transparent in window managers or wayland that want that.
self.backColor = QtGui.QColor(55, 61, 69)
self.setBackgroundBrush(self.backColor)
class TimelineRect(QtWidgets.QGraphicsRectItem):
"""Shows information about song progression.
JACK transport only shares the current time.
We cannot draw anything ahead of time other than what we know ourselves.
We rely on external tempo information and cannot react to tempo changes.
Our linear value is measures, so we display these."""
def __init__(self, parentScene):
self.height = 25
super().__init__(0, 0, 1, self.height)
self.parentScene = parentScene
self.statusMessage = self.parentScene.parentView.parentMainWindow.statusBar().showMessage #a version with the correct path of this is in every class of Patroneo
self._cachedExportDictScore = {}
role = QtGui.QPalette.Light
c = self.parentScene.parentView.parentMainWindow.fPalBlue.color(role)
self.setBrush(c)
role = QtGui.QPalette.BrightText
self.brightText = self.parentScene.parentView.parentMainWindow.fPalBlue.color(role)
self._cachedSubdivisions = 1
self.measureNumbers = []
#self._buffer_measuresPerGroup set in callback_setnumberOfMeasures, changed in wheelEvent. Utilized in hoverLeaveEvent
self._pressed = False
api.callbacks.numberOfMeasuresChanged.append(self.callback_setnumberOfMeasures)
api.callbacks.scoreChanged.append(self.callback_setnumberOfMeasures) #sends information about measuresPerGroup
api.callbacks.subdivisionsChanged.append(self.cache_subdivisions) #sends information about measuresPerGroup
api.callbacks.timeSignatureChanged.append(self.cache_timesignature)
self.setToolTip(QtCore.QCoreApplication.translate("Timeline", "Click to set playback position. Scroll with mousewheel to adjust measure grouping."))
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event):
self.statusMessage(QtCore.QCoreApplication.translate("Statusbar", "Timeline: Click to set playback position. Scroll with mousewheel to adjust measure grouping. Right click on measures below for options to use these groups."))
def hoverLeaveEvent(self, event):
self.statusMessage("")
def cache_timesignature(self, howManyUnits, whatTypeOfUnit):
self._cachedExportDictScore["howManyUnits"] = howManyUnits
self._cachedExportDictScore["whatTypeOfUnit"] = whatTypeOfUnit
def cache_subdivisions(self, subdivisions):
self._cachedSubdivisions = subdivisions
def callback_setnumberOfMeasures(self, exportDictScore):
"""We only draw one number and line for each group and not the barlines in between"""
self._cachedExportDictScore = exportDictScore
requestAmountOfMeasures = exportDictScore["numberOfMeasures"]
self._buffer_measuresPerGroup = exportDictScore["measuresPerGroup"]
self.setRect(0,0,requestAmountOfMeasures * SIZE_UNIT, SIZE_UNIT)
#Delete old
for l in self.measureNumbers:
l.setParentItem(None)
self.parentScene.removeItem(l)
self.measureNumbers = []
#Create new
for i in range(requestAmountOfMeasures+1):
if i > 0 and (i+1) % exportDictScore["measuresPerGroup"] == 1:
measure = QtWidgets.QGraphicsSimpleTextItem(str(i)) #str(i).zfill(3)
measure.setBrush(self.brightText)
measure.setParentItem(self)
measure.setPos((i-1)*SIZE_UNIT, 5) #some magic pixel values for finetuning.
#measure.setEnabled(False) #Contrary to intuition this will not make this item ignore mouse clicks but just eat them. Enabling fowards mouse item to the timeline below.
measure.setFlag(self.ItemIgnoresTransformations)
self.measureNumbers.append(measure)
barline = QtWidgets.QGraphicsLineItem(0,0,0,self.height)
barline.setParentItem(self)
barline.setPen(self.brightText)
barline.setPos(i*SIZE_UNIT, 0)
#barline.setEnabled(False) #Contrary to intuition this will not make this item ignore mouse clicks but just eat them. Enabling fowards mouse item to the timeline below.
barline.setFlag(self.ItemIgnoresTransformations)
self.measureNumbers.append(barline)
def _sendPlaybackPositionToEngine(self, posX):
oneMeasureInTicks = ( self._cachedExportDictScore["howManyUnits"] * self._cachedExportDictScore["whatTypeOfUnit"] ) / self._cachedSubdivisions
ratio = oneMeasureInTicks / SIZE_UNIT
value = posX * ratio
api.seek(int(value))
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
event.accept()
self._sendPlaybackPositionToEngine(event.scenePos().x())
self._pressed = True
def mouseMoveEvent(self, event):
if self._pressed:
self._sendPlaybackPositionToEngine(event.scenePos().x())
event.accept()
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
event.accept()
self._pressed = False
def wheelEvent(self, event):
"""This buffers until hoverLeaveEvent and then the new value is sent in self.hoverLeaveEvent"""
event.accept()
if event.delta() > 0:
self._buffer_measuresPerGroup += 1
else:
self._buffer_measuresPerGroup = max(1, self._buffer_measuresPerGroup-1)
api.set_measuresPerGroup(self._buffer_measuresPerGroup)