#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
Laborejo2 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 " )
#Third Party
from PyQt5 import QtCore , QtGui , QtWidgets
#Template Modules
from template . helper import onlyOne
#Our Modules
from . constantsAndConfigs import constantsAndConfigs
from . scorescene import GuiScore
from . submenus import GridRhytmEdit
import engine . api as api
class ScoreView ( QtWidgets . QGraphicsView ) :
def __init__ ( self , mainWindow , parentSplitter ) :
super ( ) . __init__ ( parentSplitter )
self . mainWindow = mainWindow
self . scoreScene = GuiScore ( parentView = self )
self . setScene ( self . scoreScene )
opengl = True
if opengl :
#Scrolling without openGl is sluggish. This is a sure improvement. -2022.
from PyQt5 import QtOpenGL
viewport = QtWidgets . QOpenGLWidget ( )
#viewport = QtOpenGL.QGLWidget(QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers))
#These special parameters should not matter. Run with the default.
viewportFormat = QtGui . QSurfaceFormat ( )
viewportFormat . setSwapInterval ( 0 ) #disable VSync
viewportFormat . setDefaultFormat ( viewportFormat )
viewport . setFormat ( viewportFormat )
self . setViewport ( viewport )
self . viewport ( ) . setAutoFillBackground ( False )
self . setAlignment ( QtCore . Qt . AlignLeft | QtCore . Qt . AlignTop )
#self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
#self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self . setDragMode ( QtWidgets . QGraphicsView . NoDrag )
self . horizontalScrollBar ( ) . valueChanged . connect ( self . scoreScene . reactToHorizontalScroll )
api . callbacks . setCursor . append ( self . centerOnCursor ) #returns a dict
self . _lastSavedMode = None #CC, Blocks, Notes. for self.updateMode
api . callbacks . updateBlockTrack . append ( self . updateModeSingleTrackRedraw ) # We need this after every update because the track is redrawn after each update and we don't know what mode-components to show
style = """
QScrollBar : horizontal {
border : 1 px solid black ;
}
QScrollBar : : handle : horizontal {
background : #00b2b2;
}
QScrollBar : vertical {
border : 1 px solid black ;
}
QScrollBar : : handle : vertical {
background : #00b2b2;
}
"""
self . setStyleSheet ( style )
def wheelEvent ( self , event ) :
if QtWidgets . QApplication . keyboardModifiers ( ) in ( QtCore . Qt . ControlModifier , QtCore . Qt . ControlModifier | QtCore . Qt . ShiftModifier ) : #a workaround for a qt bug. see score.wheelEvent docstring.
event . ignore ( ) #do not send to scene, but tell the mainWindow to use it.
else :
super ( ) . wheelEvent ( event ) #send to scene
def centerOnCursor ( self , cursorExportObject ) :
if ( not constantsAndConfigs . followPlayhead ) or not api . playbackStatus ( ) :
self . centerOn ( self . scoreScene . cursor . scenePos ( ) )
#discard cursorExportObject.
def toggleFollowPlayhead ( self ) :
constantsAndConfigs . followPlayhead = not constantsAndConfigs . followPlayhead
self . mainWindow . ui . actionFollow_Playhead . setChecked ( constantsAndConfigs . followPlayhead )
#we register a callback in self init that checks constantsAndConfigs.followPlayhead
def stretchXCoordinates ( self , factor ) :
""" Cumulative factor, multiplication """
self . scoreScene . stretchXCoordinates ( factor )
if not self . mode ( ) == " block " :
self . centerOnCursor ( None )
def zoom ( self , scaleFactor : float ) :
""" Scale factor is absolute """
self . resetTransform ( )
self . scale ( scaleFactor , scaleFactor )
def toggleNoteheadsRectangles ( self ) :
""" Each notehead/rectangle toggles its own state.
That means each GuiChord gets toggled individually .
Both versions , notehead and rectangle , exist all the time , so
nothing gets recreated , just visibility toggled .
Rectangles / Noteheads are their own realm and do not conflict
with CC View toggle or other view modes . """
constantsAndConfigs . noteHeadMode = not constantsAndConfigs . noteHeadMode
self . mainWindow . ui . actionToggle_Notehead_Rectangles . setChecked ( not constantsAndConfigs . noteHeadMode )
self . scoreScene . toggleNoteheadsRectangles ( )
def _switchToRectanglesForFunction ( self , function ) :
""" some actions like actionVelocityMore, actionVelocityLess,
actionDurationModMore , actionDurationModLess ,
actionReset_Velocity_Duration_Mod can be called even if
not in rectangle mode . We switch into rectangle mode for them
so the user can see the changes . """
if not self . mainWindow . ui . actionToggle_Notehead_Rectangles . isChecked ( ) :
self . toggleNoteheadsRectangles ( )
function ( ) #this is most likely an api function
def resizeEvent ( self , event ) :
""" Triggers at least when the window is resized """
self . scoreScene . grid . reactToresizeEventOrZoom ( )
super ( ) . resizeEvent ( event )
def changeGridRhythm ( self ) :
GridRhytmEdit ( mainWindow = self . mainWindow ) #handles everything.
def mode ( self ) :
""" Return the current edit mode as string.
Mostly needed for structures blockAt and other
functions that need to find the target of a mouse click . """
if self . mainWindow . ui . actionCC_Mode . isChecked ( ) :
return " cc "
elif self . mainWindow . ui . actionNotation_Mode . isChecked ( ) :
return " notation "
elif self . mainWindow . ui . actionBlock_Mode . isChecked ( ) :
return " block "
else :
raise ValueError ( " Edit Mode unknown " )
def updateModeSingleTrackRedraw ( self , trackId : int , trackExport : tuple ) :
""" trackExport is a tuple of block export dicts """
self . scoreScene . updateModeSingleTrackRedraw ( nameAsString = self . mode ( ) , trackId = trackId , trackExport = trackExport )
def updateMode ( self , * args ) :
""" Switch through different views for editing:
notes and item edit
CC curves
note - blocks only ( without items )
Which mode is active depends entirely on the state of qt checkboxes in the menu .
The menu - actions call this function . We make sure and double sure that there is never
ambiguity in the menu or in the program mode itself .
If you want to check for the mode in any place of the program use self . mode ( ) .
It is just above this function .
Therefore : Every mode switch is only allowed through this function .
There is no direct setting of the mode . You should call the menu
action directly if you want to programatically change the mode .
Different functions and callbacks call this as an update , and they just send their arguments ,
which have different types and formats . We don ' t care since our only information is the menu
state and the variables we set ourselves .
This is rarely called without arguments , manually , on program start just to
filter out the unused part .
"""
assert onlyOne ( ( self . mainWindow . ui . actionCC_Mode . isChecked ( ) , self . mainWindow . ui . actionNotation_Mode . isChecked ( ) , self . mainWindow . ui . actionBlock_Mode . isChecked ( ) ) )
if self . mainWindow . ui . actionData_Editor . isChecked ( ) :
#no modechange in the track editor
return
calledByMenu = args == ( True , ) # and not by track update or manually on e.g. program start to only show the right portions of the scene
if self . mainWindow . ui . actionCC_Mode . isChecked ( ) :
self . mainWindow . menuActionDatabase . ccEditMode ( )
self . mainWindow . menuActionDatabase . writeProtection ( True )
self . scoreScene . updateMode ( " cc " )
self . mainWindow . menuActionDatabase . loadToolbarContext ( " cc " )
elif self . mainWindow . ui . actionNotation_Mode . isChecked ( ) :
self . mainWindow . menuActionDatabase . noteEditMode ( )
self . mainWindow . menuActionDatabase . writeProtection ( False )
self . scoreScene . updateMode ( " notation " )
self . mainWindow . menuActionDatabase . loadToolbarContext ( " notation " )
elif self . mainWindow . ui . actionBlock_Mode . isChecked ( ) :
self . mainWindow . menuActionDatabase . noteEditMode ( )
self . mainWindow . menuActionDatabase . writeProtection ( True )
self . scoreScene . updateMode ( " block " )
self . mainWindow . menuActionDatabase . loadToolbarContext ( " block " )
else :
raise ValueError ( " Edit Mode unknown " )
self . _lastSavedMode = self . mode ( )