Can now open UI for a blank project.

looper
Ben Niemann 7 years ago
parent e6712055db
commit 22e1b8eeb4

@ -10,3 +10,6 @@ from .commands import (
from .process_manager import (
ProcessManager, ProcessImpl
)
from .callbacks import (
CallbackRegistry,
)

@ -11,10 +11,133 @@ from . import mutations
logger = logging.getLogger(__name__)
# TODO: merge with other one
class ObjectNotAttachedError(Exception):
pass
class ObjectList(object):
def __init__(self, prop_name, instance):
self._prop_name = prop_name
self._instance = instance
self._objs = []
def __len__(self):
return len(self._objs)
def __getitem__(self, idx):
return self._objs[idx]
def __setitem__(self, idx, obj):
self.__delitem__(idx)
self.insert(idx, obj)
def __delitem__(self, idx):
self._objs[idx].detach()
self._objs[idx].clear_parent_container()
del self._objs[idx]
for i in range(idx, len(self._objs)):
self._objs[i].set_index(i)
self._instance.listeners.call(self._prop_name, 'delete', idx)
def append(self, obj):
self.insert(len(self._objs), obj)
def insert(self, idx, obj):
obj.attach(self._instance)
obj.set_parent_container(self)
self._objs.insert(idx, obj)
for i in range(idx, len(self._objs)):
self._objs[i].set_index(i)
self._instance.listeners.call(self._prop_name, 'insert', idx, obj)
def clear(self):
for obj in self._objs:
obj.detach()
obj.clear_parent_container()
self._objs.clear()
self._instance.listeners.call(self._prop_name, 'clear')
class ObjectProxy(object):
def __init__(self, obj_id, cls):
self.id = obj_id
self.cls = cls
self.attrs = {}
self.parent = None
self.is_root = False
self.listeners = core.CallbackRegistry()
self.__parent_container = None
self.__index = None
@property
def root(self):
if self.parent is None:
if self.is_root:
return self
raise ObjectNotAttachedError
return self.parent.root
def attach(self, parent):
assert self.parent is None
self.parent = parent
def detach(self):
assert self.parent is not None
self.parent = None
def __getattr__(self, name):
try:
return self.attrs[name]
except KeyError:
raise AttributeError("%s has no property %s" % (self.cls, name)) from None
def set_attribute(self, name, value):
# TODO: fire off listeners
self.attrs[name] = value
def set_parent_container(self, prop):
self.__parent_container = prop
def clear_parent_container(self):
self.__parent_container = None
self.__index = None
def set_index(self, index):
if self.__parent_container is None:
raise ObjectNotAttachedError
self.__index = index
@property
def index(self):
if self.__parent_container is None:
raise ObjectNotAttachedError
assert self.__index is not None
return self.__index
@property
def is_first(self):
if self.__index is None:
raise NotListMemberError
return self.__index == 0
@property
def is_last(self):
if self.__index is None:
raise NotListMemberError
return self.__index == len(self.__parent_container) - 1
@property
def prev_sibling(self):
if self.is_first:
raise IndexError("First list member has no previous sibling.")
return self.__parent_container[self.index - 1]
@property
def next_sibling(self):
if self.is_last:
raise IndexError("Last list member has no next sibling.")
return self.__parent_container[self.index + 1]
class ProjectClientBase(object):
@ -39,6 +162,11 @@ class ProjectClientMixin(object):
self._object_map = {}
self.project = None
def __set_project(self, root_id):
assert self.project is None
self.project = self._object_map[root_id]
self.project.is_root = True
async def setup(self):
await super().setup()
self.server.add_command_handler(
@ -54,7 +182,7 @@ class ProjectClientMixin(object):
'START_SESSION', self.server.address)
if root_id is not None:
# Connected to a loaded project.
self.project = self._object_map[root_id]
self.__set_project(root_id)
async def disconnect(self):
if self._session_id is not None:
@ -65,40 +193,41 @@ class ProjectClientMixin(object):
await self._stub.close()
self._stub = None
def apply_properties(self, obj, properties):
for prop_name, prop_type, value in properties:
if prop_type == 'scalar':
obj.set_attribute(prop_name, value)
elif prop_type == 'list':
obj.set_attribute(prop_name, value)
elif prop_type == 'obj':
# TODO: detach prev child.
child = None
if value is not None:
child = self._object_map[value]
child.attach(obj)
obj.set_attribute(prop_name, child)
elif prop_type == 'objlist':
# TODO: detach prev children.
# TODO: use object wrapper that knows about listeners
l = ObjectList(prop_name, obj)
for child_id in value:
child = self._object_map[child_id]
l.append(child)
obj.set_attribute(prop_name, l)
else:
raise ValueError(
"Property type %s not supported." % prop_type)
def handle_project_mutation(self, mutation):
logger.info("Mutation received: %s" % mutation)
try:
if isinstance(mutation, mutations.SetProperties):
obj = self._object_map[mutation.id]
for prop_name, prop_type, value in mutation.properties:
if prop_type == 'scalar':
setattr(obj, prop_name, value)
elif prop_type == 'list':
setattr(obj, prop_name, value)
elif prop_type == 'obj':
setattr(obj, prop_name, self._object_map[value] if value is not None else None)
elif prop_type == 'objlist':
l = [self._object_map[v] for v in value]
setattr(obj, prop_name, l)
else:
raise ValueError(
"Property type %s not supported." % prop_type)
self.apply_properties(obj, mutation.properties)
elif isinstance(mutation, mutations.AddObject):
obj = ObjectProxy(mutation.id, mutation.cls)
for prop_name, prop_type, value in mutation.properties:
if prop_type == 'scalar':
setattr(obj, prop_name, value)
elif prop_type == 'list':
setattr(obj, prop_name, value)
elif prop_type == 'obj':
setattr(obj, prop_name, self._object_map[value] if value is not None else None)
elif prop_type == 'objlist':
l = [self._object_map[v] for v in value]
setattr(obj, prop_name, l)
else:
raise ValueError(
"Property type %s not supported." % prop_type)
self.apply_properties(obj, mutation.properties)
self._object_map[mutation.id] = obj
elif isinstance(mutation, mutations.UpdateObjectList):
@ -107,6 +236,7 @@ class ProjectClientMixin(object):
if mutation.args[0] == 'insert':
idx, child_id = mutation.args[1:]
child = self._object_map[child_id]
child.parent = obj
lst.insert(idx, child)
else:
raise ValueError(mutation.args[0])
@ -127,17 +257,17 @@ class ProjectClientMixin(object):
async def create(self, path):
assert self.project is None
root_id = await self._stub.call('CREATE', path)
self.project = self._object_map[root_id]
self.__set_project(root_id)
async def create_inmemory(self):
assert self.project is None
root_id = await self._stub.call('CREATE_INMEMORY')
self.project = self._object_map[root_id]
self.__set_project(root_id)
async def open(self, path):
assert self.project is None
root_id = await self._stub.call('OPEN', path)
self.project = self._object_map[root_id]
self.__set_project(root_id)
async def close(self):
assert self.project is not None

@ -149,20 +149,23 @@ class BaseEditorApp(QApplication):
self.addProject(project)
return project
def addProject(self, project):
self.win.addProjectView(project)
def _updateOpenedProjects(self):
self.settings.setValue(
'opened_projects',
[project.path for project in self._projects if project.path])
sorted(
project.path
for project
in self.project_registry.projects.values()
if project.path))
def addProject(self, project):
self.win.addProjectView(project)
self._updateOpenedProjects()
def removeProject(self, project):
self.win.removeProjectView(project)
#self._projects.remove(project)
self.settings.setValue(
'opened_projects',
[project.path for project in self._projects if project.path])
self._updateOpenedProjects()
class EditorApp(BaseEditorApp):

@ -114,7 +114,7 @@ class ProjectView(QWidget):
sheet_view.updateView()
def closeEvent(self, event):
logger.info("CloseEvent received.")
logger.info("CloseEvent received: %s", event)
self._sheet_listener.remove()
@ -123,7 +123,7 @@ class ProjectView(QWidget):
self._sheets_widget.removeWidget(sheet_view)
sheet_view.close()
self._project.close()
#TODO: self._project.close()
event.accept()
def onSheetsChanged(self, action, *args):

@ -114,6 +114,10 @@ class MeasureItem(QGraphicsItem):
self._layers = {}
def duration(self):
# TODO: mimic music.Measure.duration
return Duration(4, 4)
def boundingRect(self):
return QRectF(0, 0, self._layout.width, self._layout.height)
@ -392,6 +396,10 @@ class ScoreMeasureItem(MeasureItem):
self.setAcceptHoverEvents(True)
@property
def time_signature(self):
return self._sheet_view.get_time_signature(self._measure.index)
_accidental_map = {
'': 'accidental-natural',
'#': 'accidental-sharp',
@ -424,7 +432,7 @@ class ScoreMeasureItem(MeasureItem):
notes_width = 0
for note in self._measure.notes:
notes_width += int(400 * note.duration)
width += max(int(400 * self._measure.duration), notes_width)
width += max(int(400 * self.duration()), notes_width)
width += 10
@ -458,7 +466,7 @@ class ScoreMeasureItem(MeasureItem):
black = QColor(200, 200, 200)
if is_first and self._measure:
track = self._measure.track
track = self._measure.parent
text = self._name_item = QGraphicsSimpleTextItem(layer)
text.setText("> %s" % track.name)
@ -536,14 +544,14 @@ class ScoreMeasureItem(MeasureItem):
time_sig_upper = QGraphicsTextItem(layer)
time_sig_upper.setFont(font)
time_sig_upper.setHtml(
'<center>%d</center>' % self._measure.time_signature.upper)
'<center>%d</center>' % self.time_signature.upper)
time_sig_upper.setTextWidth(50)
time_sig_upper.setPos(x - 10, self._layout.baseline - 52)
time_sig_lower = QGraphicsTextItem(layer)
time_sig_lower.setFont(font)
time_sig_lower.setHtml(
'<center>%d</center>' % self._measure.time_signature.lower)
'<center>%d</center>' % self.time_signature.lower)
time_sig_lower.setTextWidth(50)
time_sig_lower.setPos(x - 10, self._layout.baseline - 15)
x += 40
@ -1153,8 +1161,8 @@ class SheetPropertyTrackItem(TrackItem):
track_cls_map = {
ScoreTrack: ScoreTrackItem,
SheetPropertyTrack: SheetPropertyTrackItem,
'ScoreTrack': ScoreTrackItem,
'SheetPropertyTrack': SheetPropertyTrackItem,
}
@ -1241,11 +1249,14 @@ class SheetView(QGraphicsView):
self._tracks_listener = self._sheet.listeners.add(
'tracks', self.onTracksChanged)
def get_time_signature(self, measure_idx):
return self._sheet.property_track.measures[measure_idx].time_signature
def setInfoMessage(self, msg):
self._window.setInfoMessage(msg)
def createTrackItem(self, track):
track_item_cls = track_cls_map[track.__class__]
track_item_cls = track_cls_map[track.cls]
return track_item_cls(self._app, self._project, self, track)
def insertTrack(self, idx, track):
@ -1423,7 +1434,8 @@ class SheetView(QGraphicsView):
self.clearLayer(self._layers[layer_id])
text = QGraphicsSimpleTextItem(self._layers[Layer.MAIN])
text.setText("%s/%s" % (self._project.name, self._sheet.name))
#text.setText("%s/%s" % (self._project.name, self._sheet.name))
text.setText("%s/%s" % (self._project.id, self._sheet.name))
text.setPos(0, 0)
track_items = [self._property_track_item] + self._tracks

@ -31,7 +31,7 @@ class TracksModel(QAbstractListModel):
super().__init__(parent)
self._sheet = sheet
self._project = sheet.project
self._project = sheet.parent
self._tracks_listener = self._sheet.listeners.add(
'tracks', self.onTracksChanged)

Loading…
Cancel
Save