def open(self): reg = CellTypeRegistrar.get() self.amOpen = True self.space.pushDims() self.space.setDim(self.space.X, ".ds.submit") self.space.setDim(self.space.Y, ".ds.entry") self.space.setDim(self.space.Z, ".ds.nil") self.entryCell = self.space.makeTransientCell(reg.fromName("text"), "") self.submitCell = self.space.makeTransientCell( reg.fromName("prog"), ("Submit", self.createDim, ())) # link up entry cell self.space.link(self.attachCell, self.space.POS, self.space.Y, self.entryCell) # link up submit button self.space.link(self.entryCell, self.space.POS, self.space.X, self.submitCell) # We want return to submit, just like a normal text entry self.entryCell.execute = self.createDim self.space.redraw()
def open(self): reg = CellTypeRegistrar.get() self.amOpen = True self.space.pushDims() self.space.setDim(self.space.X, ".ds.submit") self.space.setDim(self.space.Y, ".ds.entry") self.space.setDim(self.space.Z, ".ds.nil") self.entryCell = self.space.makeTransientCell(reg.fromName("text"), "") self.submitCell = self.space.makeTransientCell(reg.fromName("prog"), ("Submit", self.createDim, ())) # link up entry cell self.space.link(self.attachCell, self.space.POS, self.space.Y, self.entryCell) # link up submit button self.space.link(self.entryCell, self.space.POS, self.space.X, self.submitCell) # We want return to submit, just like a normal text entry self.entryCell.execute = self.createDim self.space.redraw()
def open(self): reg = CellTypeRegistrar.get() self.amOpen = True self.space.pushDims() self.space.setDim(self.space.X, ".ds.new-cell-type-info") self.space.setDim(self.space.Y, ".ds.new-cell-types") self.space.setDim(self.space.Z, ".ds.new-cell-subtypes") chugCell = self.space.acursedCell infoChugCell = None prog = reg.fromName("prog") text = reg.fromName("text") for (n, t) in reg.registrants(): # the tuple operator is ',' never forget cell = self.space.makeTransientCell(prog, (n, self._createTypedCell, (t,))) infoCell = self.space.makeTransientCell(text, reg.typeInfo(t)) self.transientCells.extend([cell, infoCell]) # hook up our prog cell downward self.space.link(chugCell, self.space.POS, self.space.Y, cell) # hook up info cell rightward self.space.link(cell, self.space.POS, self.space.X, infoCell) # hook up info cell downward if infoChugCell: self.space.link(infoChugCell, self.space.POS, self.space.Y, infoCell) chugCell = cell infoChugCell = infoCell # hook up our prog cell ring rank self.space.link(self.transientCells[-2], self.space.POS, self.space.Y, self.attachCell) # hook up info cell ring rank self.space.link(self.transientCells[-1], self.space.POS, self.space.Y, self.transientCells[1]) self.space.redraw()
def open(self): reg = CellTypeRegistrar.get() self.amOpen = True self.space.pushDims() self.space.setDim(self.space.X, ".ds.new-cell-type-info") self.space.setDim(self.space.Y, ".ds.new-cell-types") self.space.setDim(self.space.Z, ".ds.new-cell-subtypes") chugCell = self.space.acursedCell infoChugCell = None prog = reg.fromName("prog") text = reg.fromName("text") for (n, t) in reg.registrants(): # the tuple operator is ',' never forget cell = self.space.makeTransientCell(prog, (n, self._createTypedCell, (t, ))) infoCell = self.space.makeTransientCell(text, reg.typeInfo(t)) self.transientCells.extend([cell, infoCell]) # hook up our prog cell downward self.space.link(chugCell, self.space.POS, self.space.Y, cell) # hook up info cell rightward self.space.link(cell, self.space.POS, self.space.X, infoCell) # hook up info cell downward if infoChugCell: self.space.link(infoChugCell, self.space.POS, self.space.Y, infoCell) chugCell = cell infoChugCell = infoCell # hook up our prog cell ring rank self.space.link(self.transientCells[-2], self.space.POS, self.space.Y, self.attachCell) # hook up info cell ring rank self.space.link(self.transientCells[-1], self.space.POS, self.space.Y, self.transientCells[1]) self.space.redraw()
class DJSONBackend(object): NEG = Cell.NEG POS = Cell.POS reg = CellTypeRegistrar.get() def __setattr__(self, name, val): self.__dict__[name] = val def __init__(self, filey=None): # Only want this ever done once, since we will have a backend # managing each view object.__init__(self) self.filey = filey if self.filey: self.load(filey) @classmethod def makeCell(cls, cid, json_cells): # deleted cells stored as python None, json 'null' cinfo = json_cells[cid] if not cinfo: return None if not "type" in cinfo: raise ValueError("A JSON cell must contain a 'type' field") tName = cinfo["type"] cData = cinfo["data"] constructor = cls.reg.fromName(tName) if constructor: # save cons for cellizeCons later madeCell = constructor(cid, cData) else: madeCell = cls.reg.registerDynamicCell(tName, cid, cData) return madeCell @classmethod def fromCell(cls, cell): cType = cls.reg.fromType(cell) return { "type": cType, "data": cell.data, "cons": cls.freezeCellCons(cell) } @staticmethod def createNew(rootCell): me = DJSONBackend() DJSONBackend.createDummy(me) me.cells.append(rootCell) me.acursedId = 0 me.acursedIds.append(me.acursedId) return me @staticmethod def createDummy(back): # Eventually version will be useful for something back.version = 1 for i in ["allDims", "acursedIds", "dimConfig", "cells"]: back.__setattr__(i, []) startDims = [".ds.1", ".ds.2", ".ds.3"] back.allDims.extend(startDims) back.dimConfig.extend(list(startDims)) def load(self, filey): # I want to use 'with filey:' here, but for testing purposes # filey might be a StringIO, which doesn't humor me with an # __exit__/__enter__ method pair self.filey = filey space = json.load(self.filey) self.loadMetadata(space) self.loadCells(space) filey.close() @staticmethod def freezeCellCons(cell): cons = {} for (dim, d) in cell.cons.iteritems(): if None != d[0] and None != d[1]: cons.update({dim: [d[0].cellId, d[1].cellId]}) elif None != d[0]: cons.update({dim: [d[0].cellId, -1]}) else: cons.update({dim: [-1, d[1].cellId]}) return cons def thawCellCons(self, cell, json_cell): for (dim, direcs) in json_cell["cons"].iteritems(): if -1 != direcs[0]: targetCell = self.cells[direcs[0]] cell.addNegCon(dim, targetCell) if -1 != direcs[1]: targetCell = self.cells[direcs[1]] cell.addPosCon(dim, targetCell) def loadCells(self, space): json_cells = space["cells"] self.cells = [] # we are O(n^2) atm for i in xrange(len(json_cells)): self.cells.append(self.makeCell(i, json_cells)) for i in xrange(len(json_cells)): self.thawCellCons(self.cells[i], json_cells[i]) def loadMetadata(self, space): # Eventually version will be useful for something self.version = space["version"] for i in ["allDims", "acursedIds", "dimConfig"]: self.__setattr__(i, space[i]) self.acursedId = self.acursedIds[0] def saveMetadata(self, space): self.acursedIds[0] = self.acursedId space["version"] = self.version for i in ["allDims", "acursedIds", "dimConfig"]: space[i] = self.__getattribute__(i) def saveCells(self, space): space["cells"] = [] # Heard about this trick from python tricks somewhere cellsApp = space["cells"].append for c in self.cells: cellsApp(self.fromCell(c)) def saveAs(self, filey): """Save everything to filey's location and direct further operations there, thereafter.""" self.filey = filey self.save() def save(self): if self.filey: space = {} self.saveMetadata(space) self.saveCells(space) if hasattr(self.filey, 'name'): # We are probably a file-backed thing self.filey = file(self.filey.name, "w") json.dump(space, self.filey) self.filey.close() # close should flush else: # StringIO, something file-like, probably # for testing self.filey.truncate(0) json.dump(self.space, self.filey)
class DimSpace(QtCore.QObject): NEG = Cell.NEG POS = Cell.POS X = 0 Y = 1 Z = 2 dimChanged = pyqtSignal(int, str) reg = CellTypeRegistrar.get() def __init__(self, scene): QtCore.QObject.__init__(self) self.scene = scene self.back = None self.connections = [] self.reg.dynamicCellsRegistered.connect( self.updateDynamicallyTypedCells) @pyqtSlot(list) def updateDynamicallyTypedCells(self, cells): for c in cells: old_c = self.cells[c.cellId] if self.acursedCell == old_c: self.setAcursed(c) old_c.remove(self.space, cached=False) self.cells[c.cellId] = c self.redraw() def save(self): # self.dims always points to the latest dims self.back.dimConfig = self.dims # we maintain the acursedCell, the backends maintains the # acursedId, the two need only meet at save time self.back.acursedId = self.acursedCell.cellId self.back.save() def saveAs(self, path): filey = file(path, "w") self.back.saveAs(filey) def load(self, origin, path=None): if self.back: self.clear() if path: filey = file(path, "r") self.back = DJSONBackend(filey) else: rootCell = self.reg.fromName("text")(0, "Root") self.back = DJSONBackend.createNew(rootCell) self.acursedCell = self.back.cells[self.back.acursedId] # needs to update the backend's version at save time self.dims = self.back.dimConfig self.dimsStack = [self.dims] # just pointers to the backend's structures, no need to update self.allDims = self.back.allDims self.cells = self.back.cells self.origin = origin self.redraw() self.acursedCell.select() def swapDims(self): self.dims[0], self.dims[1] = self.dims[1], self.dims[0] self.dimChanged.emit(self.X, self.dims[0]) self.dimChanged.emit(self.Y, self.dims[1]) def nameDim(self, dim): if not dim in self.allDims: self.allDims.append(dim) def pushDims(self): self.dimsStack.append(list(self.dims)) self.dims = self.dimsStack[-1] def popDims(self): if len(self.dimsStack) > 1: othDims = self.dimsStack.pop() self.dims = self.dimsStack[-1] for i in xrange(len(self.dims)): if self.dims[i] != othDims[i]: self.dimChanged.emit(i, self.dims[i]) def setDim(self, appDim, boundDim): self.dims[appDim] = boundDim self.dimChanged.emit(appDim, boundDim) def getDim(self, appDim): return self.dims[appDim] def dirToString(self, direc): if direc == self.NEG: return "Negward" return "Posward" def dimToString(self, appDim): return chr(appDim + ord('X')) def removeCell(self, cell): cell.remove(self.scene) cell.unlink() if not cell.isTransient(): self.cells[cell.cellId] = None def removeLink(self, cell, appDim, direction=None): cell.removeCon(self.dims[appDim], repair=True, direction=direction) def link(self, linkFrom, moveDir, appDim, linkTo, exclusive=None): if exclusive: linkFrom.unlink(repair=False) if self.POS == moveDir: linkFrom.addPosCon(self.dims[appDim], linkTo) linkTo.addNegCon(self.dims[appDim], linkFrom) else: linkFrom.addNegCon(self.dims[appDim], linkTo) linkTo.addPosCon(self.dims[appDim], linkFrom) def makeTransientCell(self, theType, *args): # cellId is really only checked at save time now that # we use cells as connections for in memory cells # Since we ignore transient cells anyway, this id can # be any garbage value cell = theType(-1, *args) cell.setTransient() cell.sel_brush = QtGui.QBrush(QtGui.QColor("magenta")) return cell @staticmethod def makeCell(self, theType, *args): cell = theType(len(self.cells), *args) self.cells.append(cell) def makeCellConcrete(self, transCell): transCell.setTransient(False) transCell.cellId = len(self.cells) transCell.sel_brush = QtGui.QBrush(QtGui.QColor("cyan")) self.cells.append(transCell) def setAcursed(self, cell): self.acursedCell.deselect() cell.select() self.acursedCell = cell self.acursedId = cell.cellId def removeTransientCells(self, cell, prevCell=None, moveDir=None, dimen=None): if cell.isTransient(): # cannot unlink yet cell.remove(self.scene, cached=False) self.cells[cell.cellId] = None return True def chugDim(self, moveDir, appDim): dim = self.dims[appDim] if moveDir == self.NEG: direc = -1 else: direc = 1 print("dims:", self.allDims) ind = (self.allDims.index(dim) + direc) % len(self.allDims) self.dims[appDim] = self.allDims[ind] self.dimChanged.emit(appDim, self.dims[appDim]) def redraw(self): """Call after dimensions are changed""" self.clear() self.broadcast(self.redrawCell, self.acursedCell) self.broadcast(self.drawCons, self.acursedCell, allCons=True) def fixOverlap(self, cell, prevCell=None, moveDir=None, dimen=None): if not prevCell: return self.placeRelativeTo(cell, prevCell, moveDir, dimen) def redrawCell(self, cell, prevCell=None, moveDir=None, dimen=None): if prevCell == None: cell.add(self) cell.setPos(self.origin) return self.placeRelativeTo(cell, prevCell, moveDir, dimen) def chugDraw(self): """No new cells to create, regroup cells around new acursed""" self.broadcast(self.chugDrawCell, self.acursedCell) if not self.connections and len(self.cells) > 1: self.broadcast(self.drawCons, self.acursedCell, allCons=True) def chugDrawCell(self, cell, prevCell=None, moveDir=None, dimen=None): if prevCell == None: cell.add(self) cell.setPos(self.origin) return self.placeRelativeTo(cell, prevCell, moveDir, dimen) def removeOverlap(self, cell, prevCell=None, moveDir=None, dimen=None): if prevCell == None: return items = cell.collidingItems(QtCore.Qt.IntersectsItemBoundingRect) if items: for item in items: if isinstance(item, QtGui.QGraphicsSimpleTextItem): inter = cell.rect().intersected(item.rect()) def drawCons(self, cell, prevCell=None, moveDir=None, dimen=None): if prevCell == None: return conMap = map(lambda x: (x.linkTo, x.linkFrom), self.connections) if (prevCell, cell) in conMap: return con = Connection(self.scene, prevCell, moveDir, dimen, cell) con.position() self.connections.append(con) def placeRelativeTo(self, cell, adjCell, moveDir, dimen): # TODO: Z will crash and burn atm cell.add(self) if adjCell == self.acursedCell: cell.setZValue(6) elif adjCell.isConnectedTo(self.acursedCell): cell.setZValue(4) newRect = QtCore.QRectF(cell.skin.sceneBoundingRect()) adjRect = adjCell.skin.sceneBoundingRect() adjRect.moveTopLeft(adjCell.skin.targetPos()) if dimen == self.X: if moveDir == self.NEG: newRect.moveCenter( QPointF(adjRect.left() - 10 - newRect.width() / 2, adjRect.center().y())) else: newRect.moveCenter( QPointF(adjRect.right() + 10 + newRect.width() / 2, adjRect.center().y())) elif dimen == self.Y: if moveDir == self.NEG: newRect.moveCenter( QPointF(adjRect.center().x(), adjRect.top() - 10 - newRect.height() / 2)) else: newRect.moveCenter( QPointF(adjRect.center().x(), adjRect.bottom() + 10 + newRect.height() / 2)) center = (newRect.center().x(), newRect.center().y()) cell.setPos(center) return True def colwiseTraversal(self, func, curCell, allCons=False, marked=None, moveDir=None): if not marked: marked = [curCell] func(curCell, None, None, None) if cell.hasCon(self.dims[self.X], self.NEG) and not \ self.POS == moveDir: cony = cell.getCon(self.dims[self.X], self.NEG) if cony not in marked: marked.append(cony) self.colwiseTraversal(func, cony, allCons, marked, self.NEG) elif allCons: func(cony, cell, self.NEG, self.X) if cell.hasCon(self.dims[self.X], self.POS) and not \ self.NEG == moveDir: cony = cell.getCon(self.dims[self.X], self.POS) if cony not in marked: marked.append(cony) self.colwiseTraversal(func, cony, allCons, marked, self.POS) elif allCons: func(cony, cell, self.POS, self.X) if cell.hasCon(self.dims[self.Y], self.NEG) and not \ self.POS == moveDir: cony = cell.getCon(self.dims[self.Y], self.NEG) if cony not in marked: marked.append(cony) self.colwiseTraversal(func, cony, allCons, marked, self.NEG) elif allCons: func(cony, cell, self.NEG, self.Y) if cell.hasCon(self.dims[self.Y], self.POS) and not \ self.NEG == moveDir: cony = cell.getCon(self.dims[self.Y], self.POS) if cony not in marked: marked.append(cony) self.colwiseTraversal(func, cony, allCons, marked, self.POS) elif allCons: func(cony, cell, self.POS, self.Y) def broadcast(self, func, curCell, allCons=False): #if not isinstance(curCell, list): #curCell = [ curCell ] #marked = [ curCell[0] ] #goCons = [ (cell, None, None, None) for cell in curCell ] marked = [curCell] goCons = [(curCell, None, None, None)] for (cell, prevCell, moveDir, dimen) in goCons: func(cell, prevCell, moveDir, dimen) for i in xrange(len(self.dims)): if cell.hasCon(self.dims[i], self.NEG) and not \ (i == dimen and self.POS == moveDir): cony = cell.getCon(self.dims[i], self.NEG) if cony not in marked: marked.append(cony) goCons.append((cony, cell, self.NEG, i)) elif allCons: func(cony, cell, self.NEG, i) if cell.hasCon(self.dims[i], self.POS) and not \ (i == dimen and self.NEG == moveDir): cony = cell.getCon(self.dims[i], self.POS) if cony not in marked: marked.append(cony) goCons.append((cony, cell, self.POS, i)) elif allCons: func(cony, cell, self.POS, i) def executeCell(self): print("executing cell:", self.acursedCell, self.acursedCell.getChild()) self.acursedCell.execute() def editCell(self): self.acursedCell.edit() def clear(self): # Cache our cell skins in our cells for c in self.cells: if c: c.remove(self.scene) # Clear connections from scene for con in self.connections: con.remove() self.connections = [] def chug(self, direction, apparentDim): if apparentDim < len(self.dims) and apparentDim >= 0: curDim = self.dims[apparentDim] if self.acursedCell.hasCon(curDim, direction): self.setAcursed(self.acursedCell.getCon(curDim, direction)) return True return False def chugWhile(self, count, direc, dim): if callable(count): while count(self.acursedCell) and self.chug(direc, dim): pass else: while count >= 0 and chug(direc, dim): count -= 1