#! /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 )