Browse Source

Create sub pattern for rows with context menu

master
Nils 5 years ago
parent
commit
77f8dbb2dc
  1. 17
      engine/api.py
  2. 8
      engine/pattern.py
  3. 18
      qtgui/mainwindow.py
  4. 36
      qtgui/pattern_grid.py

17
engine/api.py

@ -506,7 +506,7 @@ def removeStep(trackId, index, pitch):
updatePlayback() updatePlayback()
callbacks._removeStep(track, index, pitch) callbacks._removeStep(track, index, pitch)
def setScale(trackId, scale): def setScale(trackId, scale, callback = True):
"""Expects a scale list or tuple from lowest index to highest. """Expects a scale list or tuple from lowest index to highest.
Actual pitches don't matter.""" Actual pitches don't matter."""
track = session.data.trackById(trackId) track = session.data.trackById(trackId)
@ -514,7 +514,8 @@ def setScale(trackId, scale):
track.pattern.buildExportCache() track.pattern.buildExportCache()
track.buildTrack() track.buildTrack()
updatePlayback() updatePlayback()
callbacks._trackMetaDataChanged(track) if callback:
callbacks._trackMetaDataChanged(track)
def setSimpleNoteNames(trackId, simpleNoteNames): def setSimpleNoteNames(trackId, simpleNoteNames):
"""note names is a list of strings with length 128. One name for each midi note. """note names is a list of strings with length 128. One name for each midi note.
@ -584,6 +585,18 @@ def patternRowRepeatFromStep(trackId, pitchindex, index):
updatePlayback() updatePlayback()
callbacks._patternChanged(track) callbacks._patternChanged(track)
def patternRowChangeVelocity(trackId, pitchindex, delta):
track = session.data.trackById(trackId)
for note in track.pattern.getRow(pitchindex):
new = note["velocity"] + delta
note["velocity"] = min(max(new,0), 127)
track.pattern.buildExportCache()
track.buildTrack()
updatePlayback()
callbacks._patternChanged(track)
major = [0, 2, 4, 5, 7, 9, 11, 12] #this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword major = [0, 2, 4, 5, 7, 9, 11, 12] #this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword
schemesDict = { schemesDict = {
#this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword #this if sorted by pitch, lowest to highest. Patroneo works in reverse order to accomodate the row/column approach of a grid. We reverse in setScaleToKeyword

8
engine/pattern.py

@ -118,7 +118,7 @@ class Pattern(object):
lst.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel}) lst.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel})
self.data = lst self.data = lst
def _getRow(self, pitchindex): def getRow(self, pitchindex):
"""Returns a row of steps, sorted by index/column. """Returns a row of steps, sorted by index/column.
Includes the original mutable dictionaries, which can be changed Includes the original mutable dictionaries, which can be changed
""" """
@ -132,13 +132,13 @@ class Pattern(object):
def clearRow(self, pitchindex): def clearRow(self, pitchindex):
"""pure convenience. This could be done with """pure convenience. This could be done with
repeatFromStep on the first empty step""" repeatFromStep on the first empty step"""
existingSteps = self._getRow(pitchindex) existingSteps = self.getRow(pitchindex)
for step in existingSteps: for step in existingSteps:
self.data.remove(step) self.data.remove(step)
def _rowAsBooleans(self, pitchindex): def _rowAsBooleans(self, pitchindex):
"""Existence or not""" """Existence or not"""
existingSteps = self._getRow(pitchindex) existingSteps = self.getRow(pitchindex)
existingIndices = set(s["index"] for s in existingSteps) existingIndices = set(s["index"] for s in existingSteps)
result = [False] * self.parentTrack.parentData.howManyUnits result = [False] * self.parentTrack.parentData.howManyUnits
@ -168,7 +168,7 @@ class Pattern(object):
def invertRow(self, pitchindex): def invertRow(self, pitchindex):
vel = self.averageVelocity vel = self.averageVelocity
existingSteps = self._getRow(pitchindex) existingSteps = self.getRow(pitchindex)
existingIndices = set(s["index"] for s in existingSteps) existingIndices = set(s["index"] for s in existingSteps)
for step in existingSteps: for step in existingSteps:

18
qtgui/mainwindow.py

@ -111,18 +111,21 @@ class MainWindow(TemplateMainWindow):
self.ui.centralwidget.addAction(self.ui.actionToStart) #no action without connection to a widget. self.ui.centralwidget.addAction(self.ui.actionToStart) #no action without connection to a widget.
self.ui.actionToStart.triggered.connect(self.ui.toStartButton.click) self.ui.actionToStart.triggered.connect(self.ui.toStartButton.click)
##Song Editor ##Song Editor
self.ui.songEditorView.parentMainWindow = self self.ui.songEditorView.parentMainWindow = self
self.songEditor = SongEditor(parentView=self.ui.songEditorView) self.songEditor = SongEditor(parentView=self.ui.songEditorView)
self.ui.songEditorView.setScene(self.songEditor) self.ui.songEditorView.setScene(self.songEditor)
#self.ui.songEditorView.setViewport(QtWidgets.QOpenGLWidget())
self.ui.trackEditorView.parentMainWindow = self
self.ui.trackEditorView.parentMainWindow = self
self.trackLabelEditor = TrackLabelEditor(parentView=self.ui.trackEditorView) self.trackLabelEditor = TrackLabelEditor(parentView=self.ui.trackEditorView)
self.ui.trackEditorView.setScene(self.trackLabelEditor) self.ui.trackEditorView.setScene(self.trackLabelEditor)
#self.ui.trackEditorView.setViewport(QtWidgets.QOpenGLWidget())
self.ui.timelineView.parentMainWindow = self self.ui.timelineView.parentMainWindow = self
self.timeline = Timeline(parentView=self.ui.timelineView) self.timeline = Timeline(parentView=self.ui.timelineView)
self.ui.timelineView.setScene(self.timeline) self.ui.timelineView.setScene(self.timeline)
#self.ui.timelineView.setViewport(QtWidgets.QOpenGLWidget())
#Sync the vertical trackEditorView scrollbar (which is never shown) with the songEditorView scrollbar. #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 :) self.ui.songEditorView.setVerticalScrollBar(self.ui.trackEditorView.verticalScrollBar()) #this seems backwards, but it is correct :)
@ -134,7 +137,8 @@ class MainWindow(TemplateMainWindow):
self.ui.gridView.parentMainWindow = self self.ui.gridView.parentMainWindow = self
self.patternGrid = PatternGrid(parentView=self.ui.gridView) self.patternGrid = PatternGrid(parentView=self.ui.gridView)
self.ui.gridView.setScene(self.patternGrid) self.ui.gridView.setScene(self.patternGrid)
self.ui.gridView.setRenderHints(QtGui.QPainter.TextAntialiasing)
#self.ui.gridView.setViewport(QtWidgets.QOpenGLWidget())
#Toolbar, which needs the widgets above already established #Toolbar, which needs the widgets above already established
self._populateToolbar() self._populateToolbar()

36
qtgui/pattern_grid.py

@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
from time import time from time import time
import engine.api as api #Session is already loaded and created, no duplication. import engine.api as api #Session is already loaded and created, no duplication.
from template.engine import pitch from template.engine import pitch
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
SIZE_UNIT = 40 SIZE_UNIT = 40
SIZE_TOP_OFFSET = 75 SIZE_TOP_OFFSET = 75
@ -349,10 +349,32 @@ class PatternGrid(QtWidgets.QGraphicsScene):
self._zoomFactor = max(0.1, round(self._zoomFactor - 0.25, 2)) self._zoomFactor = max(0.1, round(self._zoomFactor - 0.25, 2))
self._zoom(event) self._zoom(event)
event.accept() event.accept()
elif QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.AltModifier:
potentialStep = self.itemAt(event.scenePos().x(), event.scenePos().y(), self.parentView.transform())
if type(potentialStep) is Step:
trackId = self.parentView.parentMainWindow.currentTrackId
if event.delta() > 0:
delta = 2
else:
delta = -2
api.patternRowChangeVelocity(trackId, potentialStep.row, delta)
self.showVelocities() #removed in self.keyReleaseEvent
event.accept()
else:
event.ignore()
super().wheelEvent(event)
else: else:
event.ignore() event.ignore()
super().wheelEvent(event) super().wheelEvent(event)
def keyReleaseEvent(self, event):
"""Complementary for wheelEvent with Alt to change row velocity.
It is hard to detect the Alt key. We just brute force because there are not many
keyPresses in Patroneo at all."""
self.hideVelocities()
event.ignore()
super().keyReleaseEvent(event)
def _zoom(self, event): def _zoom(self, event):
if 0.1 < self._zoomFactor < 5: if 0.1 < self._zoomFactor < 5:
self.parentView.resetTransform() self.parentView.resetTransform()
@ -623,10 +645,12 @@ class Scale(QtWidgets.QGraphicsRectItem):
for pitchWidget in self.pitchWidgets: for pitchWidget in self.pitchWidgets:
pitchWidget.spinBoxValueChanged() #change all current pitchWidgets pitchWidget.spinBoxValueChanged() #change all current pitchWidgets
def sendToEngine(self): def sendToEngine(self, callback=True):
result = [widget.spinBox.value() for widget in self.pitchWidgets] result = [widget.spinBox.value() for widget in self.pitchWidgets]
#result.reverse() #result.reverse()
api.setScale(trackId=self.parentScene.parentView.parentMainWindow.currentTrackId, scale=result) trackId = self.parentScene.parentView.parentMainWindow.currentTrackId
if trackId: #startup check
api.setScale(trackId, scale=result, callback=callback)
class TransposeControls(QtWidgets.QWidget): class TransposeControls(QtWidgets.QWidget):
"""Communication with the scale spinBoxes is done via api callbacks. We just fire and forget""" """Communication with the scale spinBoxes is done via api callbacks. We just fire and forget"""
@ -738,12 +762,13 @@ class PitchWidget(QtWidgets.QGraphicsProxyWidget):
def __init__(self, parentItem): def __init__(self, parentItem):
super().__init__() super().__init__()
self.parentItem = parentItem self.parentItem = parentItem
self.spinBox = QtWidgets.QSpinBox() self.spinBox = QtWidgets.QSpinBox()
#self.spinBox.setFrame(True) #self.spinBox.setFrame(True)
self.spinBox.setMinimum(0) self.spinBox.setMinimum(0)
self.spinBox.setMaximum(127) self.spinBox.setMaximum(127)
self.spinBox.stepBy = self.stepBy self.spinBox.stepBy = self.stepBy
#self.spinBox.setValue(0) #No init value. This is changed on active track callback #self.spinBox.setValue(0) #No init value. This is changed on active track callback
widget = QtWidgets.QWidget() widget = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout() layout = QtWidgets.QHBoxLayout()
@ -792,7 +817,8 @@ class PitchWidget(QtWidgets.QGraphicsProxyWidget):
exit() exit()
def spinBoxValueChanged(self): def spinBoxValueChanged(self):
self.label.setText(self.midiToNotename(self.spinBox.value())) self.label.setText(self.midiToNotename(self.spinBox.value()))
self.parentItem.sendToEngine(callback=False)
def spinBoxEditingFinished(self): def spinBoxEditingFinished(self):
if not self.rememberLastValue == self.spinBox.value(): if not self.rememberLastValue == self.spinBox.value():

Loading…
Cancel
Save