- Don't use QGraphicsView anymore. It was convenient, but got very slow for large sheets. - Use widgets with custom paint method, just painting what's on the screen. - Split sheet view into three separate widgets: - SheetEditor with the sheet itself - Static TimeLine at the top. - Static TrackList at the left (not yet used for much). - Rendering of Score measures is somewhat degraded... - Batching of updates has been removed, but doesn't seem to be needed right now (Qt's batching of widget updates seems to do the trick well enough). - Created separate package for SheetView related classes. - I'm a slacker re unittests :(looper
parent
73d97421a1
commit
ab0887f438
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
|
||||
class InstrumentDBProcessBase(object):
|
||||
async def setup(self):
|
||||
await super().setup()
|
||||
|
||||
self.server.add_command_handler(
|
||||
'START_SESSION', self.handle_start_session)
|
||||
self.server.add_command_handler(
|
||||
'END_SESSION', self.handle_end_session)
|
||||
self.server.add_command_handler('SHUTDOWN', self.handle_shutdown)
|
||||
self.server.add_command_handler(
|
||||
'START_SCAN', self.handle_start_scan)
|
||||
|
||||
def handle_start_session(self, client_address, flags):
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_end_session(self, session_id):
|
||||
raise NotImplementedError
|
||||
|
||||
async def handle_shutdown(self):
|
||||
raise NotImplementedError
|
||||
|
||||
async def handle_start_scan(self, session_id):
|
||||
raise NotImplementedError
|
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
|
||||
class NodeDBProcessBase(object):
|
||||
async def setup(self):
|
||||
await super().setup()
|
||||
|
||||
self.server.add_command_handler(
|
||||
'START_SESSION', self.handle_start_session)
|
||||
self.server.add_command_handler(
|
||||
'END_SESSION', self.handle_end_session)
|
||||
self.server.add_command_handler('SHUTDOWN', self.handle_shutdown)
|
||||
self.server.add_command_handler(
|
||||
'START_SCAN', self.handle_start_scan)
|
||||
|
||||
def handle_start_session(self, client_address, flags):
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_end_session(self, session_id):
|
||||
raise NotImplementedError
|
||||
|
||||
async def handle_shutdown(self):
|
||||
raise NotImplementedError
|
||||
|
||||
async def handle_start_scan(self, session_id):
|
||||
raise NotImplementedError
|
@ -1,480 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from fractions import Fraction
|
||||
import logging
|
||||
import enum
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from .misc import QGraphicsGroup
|
||||
from . import layout
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Layer(enum.IntEnum):
|
||||
BG = 0
|
||||
MAIN = 1
|
||||
DEBUG = 2
|
||||
EDIT = 3
|
||||
MOUSE = 4
|
||||
EVENTS = 5
|
||||
|
||||
NUM_LAYERS = 6
|
||||
|
||||
|
||||
|
||||
class TrackItemImpl(object):
|
||||
def __init__(self, sheet_view, track, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._sheet_view = sheet_view
|
||||
self._track = track
|
||||
|
||||
self._layout = None
|
||||
|
||||
self._listeners = [
|
||||
self._track.listeners.add('name', self.onNameChanged),
|
||||
self._track.listeners.add('muted', self.onMutedChanged),
|
||||
self._track.listeners.add('volume', self.onVolumeChanged),
|
||||
self._track.listeners.add('visible', self.onVisibleChanged),
|
||||
]
|
||||
|
||||
def close(self):
|
||||
for listener in self._listeners:
|
||||
listener.remove()
|
||||
|
||||
@property
|
||||
def track(self):
|
||||
return self._track
|
||||
|
||||
def onNameChanged(self, old_name, new_name):
|
||||
# TODO: only update the first measure.
|
||||
self._sheet_view.updateSheet()
|
||||
|
||||
def onMutedChanged(self, old_value, new_value):
|
||||
pass # TODO
|
||||
|
||||
def onVolumeChanged(self, old_value, new_value):
|
||||
pass # TODO
|
||||
|
||||
def onVisibleChanged(self, old_value, new_value):
|
||||
self._sheet_view.updateSheet()
|
||||
|
||||
def getLayout(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def renderTrack(self, y, track_layout):
|
||||
self._layout = track_layout
|
||||
|
||||
def buildContextMenu(self, menu):
|
||||
track_properties_action = QtWidgets.QAction(
|
||||
"Edit track properties...", menu,
|
||||
statusTip="Edit the properties of this track.",
|
||||
triggered=self.onTrackProperties)
|
||||
menu.addAction(track_properties_action)
|
||||
|
||||
remove_track_action = QtWidgets.QAction(
|
||||
"Remove track", menu,
|
||||
statusTip="Remove this track.",
|
||||
triggered=self.onRemoveTrack)
|
||||
menu.addAction(remove_track_action)
|
||||
|
||||
def onRemoveTrack(self):
|
||||
self.send_command_async(
|
||||
self._track.parent.id, 'RemoveTrack',
|
||||
track=self._track.index)
|
||||
|
||||
def onTrackProperties(self):
|
||||
dialog = QtWidgets.QDialog()
|
||||
dialog.setWindowTitle("Track Properties")
|
||||
|
||||
name = QtWidgets.QLineEdit(dialog)
|
||||
name.setText(self._track.name)
|
||||
|
||||
form_layout = QtWidgets.QFormLayout()
|
||||
form_layout.addRow("Name", name)
|
||||
|
||||
close = QtWidgets.QPushButton("Close")
|
||||
close.clicked.connect(dialog.close)
|
||||
|
||||
buttons = QtWidgets.QHBoxLayout()
|
||||
buttons.addStretch(1)
|
||||
buttons.addWidget(close)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addLayout(form_layout)
|
||||
layout.addLayout(buttons)
|
||||
dialog.setLayout(layout)
|
||||
|
||||
ret = dialog.exec_()
|
||||
|
||||
self.send_command_async(
|
||||
self._track.id, 'UpdateTrackProperties',
|
||||
name=name.text())
|
||||
|
||||
|
||||
class MeasureLayout(object):
|
||||
def __init__(self, duration):
|
||||
self.duration = duration
|
||||
self.size = QtCore.QSize()
|
||||
self.baseline = 0
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.width > 0 and self.height > 0
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self.size.width()
|
||||
|
||||
@width.setter
|
||||
def width(self, value):
|
||||
self.size.setWidth(value)
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
return self.size.height()
|
||||
|
||||
@height.setter
|
||||
def height(self, value):
|
||||
self.size.setHeight(value)
|
||||
|
||||
@property
|
||||
def extend_above(self):
|
||||
return self.baseline
|
||||
|
||||
@property
|
||||
def extend_below(self):
|
||||
return self.height - self.baseline
|
||||
|
||||
def __eq__(self, other):
|
||||
assert isinstance(other, MeasureLayout)
|
||||
return (self.size == other.size) and (self.baseline == other.baseline)
|
||||
|
||||
|
||||
class MeasureItemImpl(QtWidgets.QGraphicsObject):
|
||||
def __init__(self, sheet_view, track_item, measure_reference, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._sheet_view = sheet_view
|
||||
self._track_item = track_item
|
||||
self._measure_reference = measure_reference
|
||||
if self._measure_reference is not None:
|
||||
self._measure = measure_reference.measure
|
||||
self._measure_listener = self._measure_reference.listeners.add(
|
||||
'measure_id', self.measureChanged)
|
||||
else:
|
||||
self._measure = None
|
||||
self._measure_listener = None
|
||||
|
||||
self._layout = None
|
||||
|
||||
self._layers = {}
|
||||
self._layers[Layer.BG] = QGraphicsGroup()
|
||||
|
||||
self._background = QtWidgets.QGraphicsRectItem(self._layers[Layer.BG])
|
||||
self._background.setPen(QtGui.QPen(Qt.NoPen))
|
||||
self._background.setBrush(QtGui.QColor(240, 240, 255))
|
||||
self._background.setVisible(False)
|
||||
|
||||
self._selected = False
|
||||
|
||||
self.setAcceptHoverEvents(True)
|
||||
self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable)
|
||||
|
||||
@property
|
||||
def measure(self):
|
||||
return self._measure
|
||||
|
||||
@property
|
||||
def measure_reference(self):
|
||||
return self._measure_reference
|
||||
|
||||
@property
|
||||
def track_item(self):
|
||||
return self._track_item
|
||||
|
||||
def boundingRect(self):
|
||||
return QtCore.QRectF(0, 0, self._layout.width, self._layout.height)
|
||||
|
||||
def paint(self, painter, option, widget=None):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
if self._measure_listener is not None:
|
||||
self._measure_listener.remove()
|
||||
|
||||
def hoverEnterEvent(self, event):
|
||||
super().hoverEnterEvent(event)
|
||||
|
||||
self.setFocus()
|
||||
|
||||
def measureChanged(self, old_value, new_value):
|
||||
self._measure = self._measure_reference.measure
|
||||
self.recomputeLayout()
|
||||
|
||||
def recomputeLayout(self):
|
||||
self._sheet_view.scheduleCallback(
|
||||
'%s:recomputeLayout' % id(self),
|
||||
self._recomputeLayoutInternal)
|
||||
|
||||
def _recomputeLayoutInternal(self):
|
||||
layout = self.getLayout()
|
||||
if layout != self._layout:
|
||||
self._sheet_view.updateSheet()
|
||||
else:
|
||||
self.updateMeasure()
|
||||
|
||||
def getLayout(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def setLayout(self, layout):
|
||||
self._layout = layout
|
||||
|
||||
def updateMeasure(self):
|
||||
self._sheet_view.scheduleCallback(
|
||||
'%s:updateMeasure' % id(self),
|
||||
self._updateMeasureInternal)
|
||||
|
||||
def _updateMeasureInternal(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def layers(self):
|
||||
return sorted(self._layers.keys())
|
||||
|
||||
def getLayer(self, layer_id):
|
||||
return self._layers.get(layer_id, None)
|
||||
|
||||
def width(self):
|
||||
return self._layout.width
|
||||
|
||||
def buildContextMenu(self, menu):
|
||||
insert_measure_action = QtWidgets.QAction(
|
||||