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.
145 lines
6.3 KiB
145 lines
6.3 KiB
#! /usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Copyright 2021, 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 = 25 #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.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._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."))
|
|
|
|
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)
|
|
|