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.
201 lines
9.5 KiB
201 lines
9.5 KiB
6 years ago
|
#! /usr/bin/env python3
|
||
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
Copyright 2018, 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.
|
||
|
|
||
|
The Template Base Application 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__))
|
||
|
|
||
|
#Standard Library Modules
|
||
|
import os.path
|
||
|
|
||
|
#Third Party Modules
|
||
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||
|
|
||
|
#Template Modules
|
||
|
from template.qtgui.mainwindow import MainWindow as TemplateMainWindow
|
||
|
from template.qtgui.menu import Menu
|
||
|
from template.qtgui.about import About
|
||
|
|
||
|
#Our modules
|
||
|
import engine.api as api
|
||
|
from midiinput.stepmidiinput import stepMidiInput #singleton instance
|
||
|
from .constantsAndConfigs import constantsAndConfigs
|
||
|
from .menu import MenuActionDatabase
|
||
|
from .scoreview import ScoreView
|
||
|
from .structures import GuiScore
|
||
|
from .trackEditor import TrackEditor
|
||
|
from .resources import *
|
||
|
|
||
|
class MainWindow(TemplateMainWindow):
|
||
|
|
||
|
def __init__(self):
|
||
|
"""The order of calls is very important.
|
||
|
The split ploint is calling the super.__init. Some functions need to be called before,
|
||
|
some after.
|
||
|
For example:
|
||
|
|
||
|
The about dialog is created in the template main window init. So we need to set additional
|
||
|
help texts before that init.
|
||
|
"""
|
||
|
|
||
|
#Inject more help texts in the templates About "Did You Know" field.
|
||
|
#About.didYouKnow is a class variable.
|
||
|
#Make the first three words matter!
|
||
|
#Do not start them all with "You can..." or "...that you can", in response to the Did you know? title.
|
||
|
#We use injection into the class and not a parameter because this dialog gets shown by creating an object. We can't give the parameters when this is shown via the mainWindow menu.
|
||
|
About.didYouKnow = [
|
||
|
QtCore.QCoreApplication.translate("About", "This is an example application. Extend it to your liking. Start by editing config.py")
|
||
|
] + About.didYouKnow
|
||
|
|
||
|
super().__init__()
|
||
|
|
||
|
#New menu entries and template-menu overrides
|
||
|
self.menu.addMenuEntry("menuDebug", "actionRedrawAllTracks", "Redraw all Tracks")
|
||
|
self.menu.connectMenuEntry("actionSave", api.save)
|
||
|
self.menu.hideSubmenu("menuFile")
|
||
|
self.menu.hideSubmenu("menuGeneric")
|
||
|
|
||
|
|
||
|
api.callbacks.setCursor.append(self.updateStatusBar) #returns a dict. This get's called after loading the file so the status bar is filled on self.show
|
||
|
|
||
|
self.initiGuiSharedDataToSave()
|
||
|
|
||
|
#Create the Main Widgets in the Stacked Widget
|
||
|
self.scoreView = ScoreView(self)
|
||
|
self.ui.mainStackWidget.addWidget(self.scoreView)
|
||
|
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.scoreView))
|
||
|
|
||
|
self.trackEditor = QtWidgets.QScrollArea()
|
||
|
self.trackEditor.setWidgetResizable(True)
|
||
|
self.actualTrackEditor = TrackEditor(self)
|
||
|
self.trackEditor.setWidget(self.actualTrackEditor)
|
||
|
self.ui.actionData_Editor.setChecked(False)
|
||
|
self.ui.mainStackWidget.addWidget(self.trackEditor)
|
||
|
|
||
|
#Bind shortcuts to actions (as init effect)
|
||
|
#TODO: Integrate better into template menu system.
|
||
|
self.menuActionDatabase = MenuActionDatabase(self) #The menu needs to be started before api.startEngine
|
||
|
|
||
|
#Make toolbars unclosable
|
||
|
##self.ui.toolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) #only for right mouse clicks. Keyboard context menu key still works.
|
||
|
##self.ui.leftToolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
|
||
|
self.setContextMenuPolicy(QtCore.Qt.NoContextMenu) #instead prevent the main window from having context menus.
|
||
|
|
||
|
#The statusbar is used for tooltips. To make it permanent we add our own widget
|
||
|
self.statusLabel = QtWidgets.QLabel()
|
||
|
self.statusBar().insertPermanentWidget(0, self.statusLabel)
|
||
|
|
||
|
self.scoreView.setFocus() #So the user can start typing from moment 0.
|
||
|
self.start() #Inherited from template main window #This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange.
|
||
|
stepMidiInput.start() #imported directly. Handles everything else internally, we just need to start it after the engine somehow. Which is here.
|
||
|
|
||
|
#Populate the left toolbar. The upper toolbar is created in menu.py
|
||
|
self.ui.leftToolBar.addWidget(LeftToolBarPrevailingDuration(self)) #needs stepmidiinput started
|
||
|
|
||
|
#Now all tracks and items from a loaded backend-file are created. We can setup the initial editMode and viewPort.
|
||
|
self.scoreView.updateMode() #hide CCs at program start and other stuff
|
||
|
self.scoreView.scoreScene.grid.redrawTickGrid() #Init the grid only after everything got loaded and drawn to prevent a gap in the display. #TODO: which might be a bug. but this here works fine.
|
||
|
|
||
|
|
||
|
def initiGuiSharedDataToSave(self):
|
||
|
"""Called by init"""
|
||
|
|
||
|
if not "last_export_dir" in api.session.guiSharedDataToSave:
|
||
|
api.session.guiSharedDataToSave["last_export_dir"] = os.path.expanduser("~")
|
||
|
|
||
|
if "grid_opacity" in api.session.guiSharedDataToSave:
|
||
|
constantsAndConfigs.gridOpacity = float(api.session.guiSharedDataToSave["grid_opacity"])
|
||
|
|
||
|
if "grid_rhythm" in api.session.guiSharedDataToSave:
|
||
|
#setting this is enough. When the grid gets created it fetches the constantsAndConfigs value.
|
||
|
#Set only in submenus.GridRhytmEdit
|
||
|
constantsAndConfigs.gridRhythm = int(api.session.guiSharedDataToSave["grid_rhythm"])
|
||
|
|
||
|
#Stretch
|
||
|
if "ticks_to_pixel_ratio" in api.session.guiSharedDataToSave:
|
||
|
#setting this is enough. Drawing on startup uses the constantsAndConfigs value.
|
||
|
#Set only in ScoreView._stretchXCoordinates
|
||
|
constantsAndConfigs.ticksToPixelRatio = float(api.session.guiSharedDataToSave["ticks_to_pixel_ratio"])
|
||
|
|
||
|
if "zoom_factor" in api.session.guiSharedDataToSave:
|
||
|
#setting this is enough. Drawing on startup uses the constantsAndConfigs value.
|
||
|
#Set only in ScoreView._zoom
|
||
|
constantsAndConfigs.zoomFactor = float(api.session.guiSharedDataToSave["zoom_factor"])
|
||
|
|
||
|
def updateStatusBar(self, exportCursorDict):
|
||
|
"""Every cursor movement updates the statusBar message"""
|
||
|
c = exportCursorDict
|
||
|
i = c["item"]
|
||
|
if i:
|
||
|
ly = i.lilypond()
|
||
|
if (not ly) or len(ly) > 10:
|
||
|
ly = ""
|
||
|
else:
|
||
|
ly = "Lilypond: <b>{}</b>".format(ly.replace("<", "<").replace(">", ">"))
|
||
|
itemMessage = "Item: <b>{}</b> {}".format(i.__class__.__name__, ly)
|
||
|
|
||
|
else:
|
||
|
itemMessage = "" #Appending
|
||
|
|
||
|
positionMessage = "Pos: <b>{}</b> Ticks: <b>{}</b> Pitch: <b>{}</b>".format(c["position"], c["tickindex"], c["lilypondPitch"])
|
||
|
|
||
|
message = "{} | {}".format(itemMessage, positionMessage)
|
||
|
#self.statusBar().showMessage(message) #overriden by tool tips, even empty ones
|
||
|
self.statusLabel.setText(message)
|
||
|
|
||
|
|
||
|
def toggleMainView(self):
|
||
|
if self.ui.actionData_Editor.isChecked():
|
||
|
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.trackEditor))
|
||
|
self.scoreView.setEnabled(False) #disables shortcut like cursor movement, but not all of them.
|
||
|
self.menuActionDatabase.writeProtection(True)
|
||
|
self.trackEditor.setEnabled(True)
|
||
|
else:
|
||
|
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.scoreView))
|
||
|
self.scoreView.setEnabled(True)
|
||
|
self.menuActionDatabase.writeProtection(False)
|
||
|
self.scoreView.updateMode()
|
||
|
self.trackEditor.setEnabled(False)
|
||
|
|
||
|
class LeftToolBarPrevailingDuration(QtWidgets.QLabel):
|
||
|
def __init__(self, mainWindow):
|
||
|
super().__init__(self.makeText(api.D4))
|
||
|
self.mainWindow = mainWindow
|
||
|
|
||
|
#self.setFont(constantsAndConfigs.musicFont) #TODO replace with svg
|
||
|
api.callbacks.prevailingBaseDurationChanged.append(self.changed)
|
||
|
|
||
|
def makeText(self, baseDuration):
|
||
|
if not stepMidiInput.midiInIsActive:
|
||
|
return ""
|
||
|
|
||
|
labelText = "<font size=6>"
|
||
|
for i in (api.D1, api.D2, api.D4, api.D8, api.D16): #,api.DB, api.DL):
|
||
|
if i == baseDuration:
|
||
|
labelText += "<font color='cyan'>"
|
||
|
labelText += constantsAndConfigs.realNoteDisplay[i]
|
||
|
if i == baseDuration:
|
||
|
labelText += "</font>"
|
||
|
labelText += "<br>"
|
||
|
labelText += "</font>"
|
||
|
return labelText
|
||
|
|
||
|
def changed(self, newBaseDuration):
|
||
|
self.setText(self.makeText(newBaseDuration))
|