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.
140 lines
7.1 KiB
140 lines
7.1 KiB
#! /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 .songeditor import SongEditor, TrackLabelEditor
|
|
from .timeline import Timeline
|
|
from .pattern_grid import PatternGrid
|
|
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", "Prefer clone track over adding a new empty track when creating a new pattern for an existing 'real world' instrument."),
|
|
QtCore.QCoreApplication.translate("About", "You can run multiple Patroneo instances in parallel to create complex polyrhythms."),
|
|
QtCore.QCoreApplication.translate("About", "To revert all steps that are longer or shorter than default invert the pattern twice in a row."),
|
|
QtCore.QCoreApplication.translate("About", "Control a synth with MIDI Control Changes (CC) by routing a Patroneo track into a midi plugin that converts notes to CC."),
|
|
] + About.didYouKnow
|
|
|
|
super().__init__()
|
|
|
|
#New menu entries and template-menu overrides
|
|
|
|
#Playback Controls
|
|
|
|
width = 65
|
|
|
|
self.ui.playPauseButton.setFixedWidth(width)
|
|
self.ui.playPauseButton.setText("")
|
|
self.ui.playPauseButton.setIcon(QtGui.QIcon(':playpause.png'))
|
|
self.ui.playPauseButton.clicked.connect(api.playPause)
|
|
self.ui.playPauseButton.setToolTip(QtCore.QCoreApplication.translate("PlaybackControls", "[Space] Play / Pause"))
|
|
self.ui.centralwidget.addAction(self.ui.actionPlayPause) #no action without connection to a widget.
|
|
self.ui.actionPlayPause.triggered.connect(self.ui.playPauseButton.click)
|
|
|
|
self.ui.loopButton.setFixedWidth(width)
|
|
self.ui.loopButton.setText("")
|
|
self.ui.loopButton.setIcon(QtGui.QIcon(':loop.png'))
|
|
self.ui.loopButton.setToolTip(QtCore.QCoreApplication.translate("PlaybackControls", "[L] Loop current Measure"))
|
|
self.ui.loopButton.clicked.connect(api.toggleLoop)
|
|
self.ui.centralwidget.addAction(self.ui.actionLoop) #no action without connection to a widget.
|
|
self.ui.actionLoop.triggered.connect(self.ui.loopButton.click)
|
|
|
|
def callback_loopButtonText(measureNumber):
|
|
if not measureNumber is None:
|
|
nrstr = str(measureNumber+1)
|
|
self.ui.loopButton.setText(nrstr)
|
|
else:
|
|
self.ui.loopButton.setText("")
|
|
api.callbacks.loopChanged.append(callback_loopButtonText)
|
|
|
|
|
|
self.ui.toStartButton.setFixedWidth(width)
|
|
self.ui.toStartButton.setText("")
|
|
self.ui.toStartButton.setIcon(QtGui.QIcon(':tostart.png'))
|
|
self.ui.toStartButton.setToolTip(QtCore.QCoreApplication.translate("PlaybackControls", "[Backspace] Jump to Start"))
|
|
self.ui.toStartButton.clicked.connect(api.toStart)
|
|
self.ui.centralwidget.addAction(self.ui.actionToStart) #no action without connection to a widget.
|
|
self.ui.actionToStart.triggered.connect(self.ui.toStartButton.click)
|
|
|
|
##Song Editor
|
|
self.ui.songEditorView.parentMainWindow = self
|
|
self.songEditor = SongEditor(parentView=self.ui.songEditorView)
|
|
self.ui.songEditorView.setScene(self.songEditor)
|
|
|
|
self.ui.trackEditorView.parentMainWindow = self
|
|
self.trackLabelEditor = TrackLabelEditor(parentView=self.ui.trackEditorView)
|
|
self.ui.trackEditorView.setScene(self.trackLabelEditor)
|
|
|
|
self.ui.timelineView.parentMainWindow = self
|
|
self.timeline = Timeline(parentView=self.ui.timelineView)
|
|
self.ui.timelineView.setScene(self.timeline)
|
|
|
|
#Sync the vertical trackEditorView scrollbar (which is never shown) with the songEditorView scrollbar.
|
|
self.ui.songEditorView.setVerticalScrollBar(self.ui.trackEditorView.verticalScrollBar()) #this seems backwards, but it is correct :)
|
|
|
|
#Sync the horizontal timelineView scrollbar (which is never shown) with the songEditorView scrollbar.
|
|
self.ui.songEditorView.setHorizontalScrollBar(self.ui.timelineView.horizontalScrollBar()) #this seems backwards, but it is correct :)
|
|
|
|
##Pattern Editor
|
|
self.ui.gridView.parentMainWindow = self
|
|
self.patternGrid = PatternGrid(parentView=self.ui.gridView)
|
|
self.ui.gridView.setScene(self.patternGrid)
|
|
|
|
|
|
#There is always a track. Forcing that to be active is better than having to hide all the pattern widgets, or to disable them.
|
|
self.chooseCurrentTrack(api.session.data.tracks[0].export()) #By Grabthar's hammer, by the suns of Worvan, what a hack! #TODO: Access to the sessions data structure directly instead of api. Not good. Getter function or api @property is cleaner.
|
|
|
|
|
|
|
|
self.start() #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.
|
|
|
|
def addTrack(self):
|
|
"""Add a new track and initialize it with some data from the current one"""
|
|
scale = api.session.data.trackById(self.currentTrackId).pattern.scale #TODO: Access to the sessions data structure directly instead of api. Not good. Getter function or api @property is cleaner.
|
|
api.addTrack(scale)
|
|
|