#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2019 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
more specifically its template base application .
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 ; logging . info ( " import {} " . format ( __file__ ) )
#Third Party
from PyQt5 import QtCore , QtGui , QtWidgets , QtOpenGL
#Template Modules
from template . helper import onlyOne
#Our Modules
from . constantsAndConfigs import constantsAndConfigs
from . scorescene import GuiScore
import engine . api as api
class ScoreView ( QtWidgets . QGraphicsView ) :
def __init__ ( self , mainWindow ) :
super ( ) . __init__ ( )
self . mainWindow = mainWindow
self . scoreScene = GuiScore ( parentView = self )
self . setScene ( self . scoreScene )
viewport = QtOpenGL . QGLWidget ( QtOpenGL . QGLFormat ( QtOpenGL . QGL . SampleBuffers ) )
viewport . format ( ) . setSwapInterval ( 0 ) #disable VSync.
viewport . setAutoFillBackground ( False )
viewport = QtWidgets . QOpenGLWidget ( )
#These special parameters should not matter. Run with the default.
#viewportFormat = QtGui.QSurfaceFormat()
#viewportFormat.setSwapInterval(0) #disable VSync
#viewportFormat.setSamples(2**8)
#viewportFormat.setDefaultFormat(viewportFormat)
#viewport.setFormat(viewportFormat)
self . setViewport ( viewport )
self . setAlignment ( QtCore . Qt . AlignLeft | QtCore . Qt . AlignTop )
#self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
self . setDragMode ( QtWidgets . QGraphicsView . NoDrag )
api . callbacks . setCursor . append ( self . centerOnCursor ) #returns a dict
api . callbacks . updateBlockTrack . append ( self . updateMode ) # We need this after every update because the track is redrawn after each update and we don't know what 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 ) :
self . scoreScene . stretchXCoordinates ( factor )
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 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 resizeEvent ( self , event ) :
self . scoreScene . grid . reactToresizeEventOrZoom ( )
super ( ) . resizeEvent ( event )
def changeGridRhythm ( self ) :
GridRhytmEdit ( mainWindow = self . mainWindow ) #handles everything.
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 .
"""
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
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 " )