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()
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.
Actual pitches don't matter."""
track = session.data.trackById(trackId)
@ -514,7 +514,8 @@ def setScale(trackId, scale):
track.pattern.buildExportCache()
track.buildTrack()
updatePlayback()
callbacks._trackMetaDataChanged(track)
if callback:
callbacks._trackMetaDataChanged(track)
def setSimpleNoteNames(trackId, simpleNoteNames):
"""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()
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
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

8
engine/pattern.py

@ -118,7 +118,7 @@ class Pattern(object):
lst.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel})
self.data = lst
def _getRow(self, pitchindex):
def getRow(self, pitchindex):
"""Returns a row of steps, sorted by index/column.
Includes the original mutable dictionaries, which can be changed
"""
@ -132,13 +132,13 @@ class Pattern(object):
def clearRow(self, pitchindex):
"""pure convenience. This could be done with
repeatFromStep on the first empty step"""
existingSteps = self._getRow(pitchindex)
existingSteps = self.getRow(pitchindex)
for step in existingSteps:
self.data.remove(step)
def _rowAsBooleans(self, pitchindex):
"""Existence or not"""
existingSteps = self._getRow(pitchindex)
existingSteps = self.getRow(pitchindex)
existingIndices = set(s["index"] for s in existingSteps)
result = [False] * self.parentTrack.parentData.howManyUnits
@ -168,7 +168,7 @@ class Pattern(object):
def invertRow(self, pitchindex):
vel = self.averageVelocity
existingSteps = self._getRow(pitchindex)
existingSteps = self.getRow(pitchindex)
existingIndices = set(s["index"] for s 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.actionToStart.triggered.connect(self.ui.toStartButton.click)
##Song Editor
self.ui.songEditorView.parentMainWindow = self
self.songEditor = SongEditor(parentView=self.ui.songEditorView)
##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.ui.songEditorView.setViewport(QtWidgets.QOpenGLWidget())
self.ui.trackEditorView.parentMainWindow = self
self.trackLabelEditor = TrackLabelEditor(parentView=self.ui.trackEditorView)
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.ui.timelineView.setScene(self.timeline)
#self.ui.timelineView.setViewport(QtWidgets.QOpenGLWidget())
#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 :)
@ -134,7 +137,8 @@ class MainWindow(TemplateMainWindow):
self.ui.gridView.parentMainWindow = self
self.patternGrid = PatternGrid(parentView=self.ui.gridView)
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
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
import engine.api as api #Session is already loaded and created, no duplication.
from template.engine import pitch
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
SIZE_UNIT = 40
SIZE_TOP_OFFSET = 75
@ -349,10 +349,32 @@ class PatternGrid(QtWidgets.QGraphicsScene):
self._zoomFactor = max(0.1, round(self._zoomFactor - 0.25, 2))
self._zoom(event)
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:
event.ignore()
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):
if 0.1 < self._zoomFactor < 5:
self.parentView.resetTransform()
@ -623,10 +645,12 @@ class Scale(QtWidgets.QGraphicsRectItem):
for pitchWidget in self.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.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):
"""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):
super().__init__()
self.parentItem = parentItem
self.spinBox = QtWidgets.QSpinBox()
self.spinBox = QtWidgets.QSpinBox()
#self.spinBox.setFrame(True)
self.spinBox.setMinimum(0)
self.spinBox.setMaximum(127)
self.spinBox.stepBy = self.stepBy
#self.spinBox.setValue(0) #No init value. This is changed on active track callback
widget = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout()
@ -792,7 +817,8 @@ class PitchWidget(QtWidgets.QGraphicsProxyWidget):
exit()
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):
if not self.rememberLastValue == self.spinBox.value():

Loading…
Cancel
Save