diff --git a/flowchart/Flowchart.py b/flowchart/Flowchart.py index 18f1f211..35f848fd 100644 --- a/flowchart/Flowchart.py +++ b/flowchart/Flowchart.py @@ -71,7 +71,8 @@ class Flowchart(Node): if terminals is None: terminals = {} self.filePath = filePath - Node.__init__(self, name) ## create node without terminals; we'll add these later + Node.__init__(self, name, allowAddInput=True, allowAddOutput=True) ## create node without terminals; we'll add these later + self.inputWasSet = False ## flag allows detection of changes in the absence of input change. self._nodes = {} @@ -457,7 +458,7 @@ class Flowchart(Node): state = Node.saveState(self) state['nodes'] = [] state['connects'] = [] - state['terminals'] = self.saveTerminals() + #state['terminals'] = self.saveTerminals() for name, node in self._nodes.items(): cls = type(node) @@ -470,7 +471,7 @@ class Flowchart(Node): conn = self.listConnections() for a, b in conn: state['connects'].append((a.node().name(), a.name(), b.node().name(), b.name())) - + state['inputNode'] = self.inputNode.saveState() state['outputNode'] = self.outputNode.saveState() @@ -486,7 +487,8 @@ class Flowchart(Node): nodes.sort(lambda a, b: cmp(a['pos'][0], b['pos'][0])) for n in nodes: if n['name'] in self._nodes: - self._nodes[n['name']].moveBy(*n['pos']) + #self._nodes[n['name']].graphicsItem().moveBy(*n['pos']) + self._nodes[n['name']].restoreState(n['state']) continue try: node = self.createNode(n['class'], name=n['name']) @@ -498,7 +500,7 @@ class Flowchart(Node): self.inputNode.restoreState(state.get('inputNode', {})) self.outputNode.restoreState(state.get('outputNode', {})) - self.restoreTerminals(state['terminals']) + #self.restoreTerminals(state['terminals']) for n1, t1, n2, t2 in state['connects']: try: self.connectTerminals(self._nodes[n1][t1], self._nodes[n2][t2]) diff --git a/flowchart/Node.py b/flowchart/Node.py index e3dce61d..b79941db 100644 --- a/flowchart/Node.py +++ b/flowchart/Node.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- from pyqtgraph.Qt import QtCore, QtGui -#from PySide import QtCore, QtGui from pyqtgraph.graphicsItems.GraphicsObject import GraphicsObject import pyqtgraph.functions as fn from .Terminal import * from collections import OrderedDict from pyqtgraph.debug import * import numpy as np -#from pyqtgraph.ObjectWorkaround import QObjectWorkaround from .eq import * -#TETRACYCLINE = True def strDict(d): return dict([(str(k), v) for k, v in d.items()]) @@ -32,8 +29,8 @@ class Node(QtCore.QObject): self.bypassButton = None ## this will be set by the flowchart ctrl widget.. self._graphicsItem = None self.terminals = OrderedDict() - self._inputs = {} - self._outputs = {} + self._inputs = OrderedDict() + self._outputs = OrderedDict() self._allowAddInput = allowAddInput ## flags to allow the user to add/remove terminals self._allowAddOutput = allowAddOutput self._allowRemove = allowRemove @@ -85,24 +82,16 @@ class Node(QtCore.QObject): def terminalRenamed(self, term, oldName): """Called after a terminal has been renamed""" newName = term.name() - #print "node", self, "handling rename..", newName, oldName for d in [self.terminals, self._inputs, self._outputs]: if oldName not in d: continue - #print " got one" d[newName] = d[oldName] del d[oldName] self.graphicsItem().updateTerminals() - #self.emit(QtCore.SIGNAL('terminalRenamed'), term, oldName) self.sigTerminalRenamed.emit(term, oldName) def addTerminal(self, name, **opts): - #print "Node.addTerminal called. name:", name, "opts:", opts - #global TETRACYCLINE - #print "TETRACYCLINE: ", TETRACYCLINE - #if TETRACYCLINE: - #print "Creating Terminal..." name = self.nextTerminalName(name) term = Terminal(self, name, **opts) self.terminals[name] = term @@ -278,12 +267,20 @@ class Node(QtCore.QObject): def saveState(self): pos = self.graphicsItem().pos() - return {'pos': (pos.x(), pos.y()), 'bypass': self.isBypassed()} + state = {'pos': (pos.x(), pos.y()), 'bypass': self.isBypassed()} + termsEditable = self._allowAddInput | self._allowAddOutput + for term in self._inputs.values() + self._outputs.values(): + termsEditable |= term._renamable | term._removable | term._multiable + if termsEditable: + state['terminals'] = self.saveTerminals() + return state def restoreState(self, state): pos = state.get('pos', (0,0)) self.graphicsItem().setPos(*pos) self.bypass(state.get('bypass', False)) + if 'terminals' in state: + self.restoreTerminals(state['terminals']) def saveTerminals(self): terms = OrderedDict() @@ -309,8 +306,8 @@ class Node(QtCore.QObject): for t in self.terminals.values(): t.close() self.terminals = OrderedDict() - self._inputs = {} - self._outputs = {} + self._inputs = OrderedDict() + self._outputs = OrderedDict() def close(self): """Cleans up after the node--removes terminals, graphicsItem, widget""" @@ -493,10 +490,6 @@ class NodeGraphicsItem(GraphicsObject): self.hovered = False self.update() - #def mouseReleaseEvent(self, ev): - #ret = QtGui.QGraphicsItem.mouseReleaseEvent(self, ev) - #return ret - def keyPressEvent(self, ev): if ev.key() == QtCore.Qt.Key_Delete or ev.key() == QtCore.Qt.Key_Backspace: ev.accept() @@ -513,13 +506,8 @@ class NodeGraphicsItem(GraphicsObject): return GraphicsObject.itemChange(self, change, val) - #def contextMenuEvent(self, ev): - #ev.accept() - #self.menu.popup(ev.screenPos()) - def getMenu(self): return self.menu - def getContextMenus(self, event): return [self.menu] @@ -548,25 +536,3 @@ class NodeGraphicsItem(GraphicsObject): def addOutputFromMenu(self): ## called when add output is clicked in context menu self.node.addOutput(renamable=True, removable=True, multiable=False) - #def menuTriggered(self, action): - ##print "node.menuTriggered called. action:", action - #act = str(action.text()) - #if act == "Add input": - #self.node.addInput() - #self.updateActionMenu() - #elif act == "Add output": - #self.node.addOutput() - #self.updateActionMenu() - #elif act == "Remove node": - #self.node.close() - #else: ## only other option is to remove a terminal - #self.node.removeTerminal(act) - #self.terminalMenu.removeAction(action) - - #def updateActionMenu(self): - #for t in self.node.terminals: - #if t not in [str(a.text()) for a in self.terminalMenu.actions()]: - #self.terminalMenu.addAction(t) - #for a in self.terminalMenu.actions(): - #if str(a.text()) not in self.node.terminals: - #self.terminalMenu.removeAction(a) diff --git a/flowchart/Terminal.py b/flowchart/Terminal.py index 77f5b72c..f5181b08 100644 --- a/flowchart/Terminal.py +++ b/flowchart/Terminal.py @@ -45,7 +45,7 @@ class Terminal: self._value = {} ## dictionary of terminal:value pairs. else: self._value = None - + self.valueOk = None self.recolor() @@ -70,6 +70,8 @@ class Terminal: return self._value = val else: + if not isinstance(self._value, dict): + self._value = {} if val is not None: self._value.update(val) @@ -132,9 +134,14 @@ class Terminal: def isMultiValue(self): return self._multi - def setMultiValue(self, b): + def setMultiValue(self, multi): """Set whether this is a multi-value terminal.""" - self._multi = b + self._multi = multi + if not multi and len(self.inputTerminals()) > 1: + self.disconnectAll() + + for term in self.inputTerminals(): + self.inputChanged(term) def isOutput(self): return self._io == 'out' @@ -407,6 +414,8 @@ class TerminalGraphicsItem(GraphicsObject): multiAct = QtGui.QAction("Multi-value", self.menu) multiAct.setCheckable(True) multiAct.setChecked(self.term.isMultiValue()) + multiAct.setEnabled(self.term.isMultiable()) + multiAct.triggered.connect(self.toggleMulti) self.menu.addAction(multiAct) self.menu.multiAct = multiAct diff --git a/flowchart/library/Data.py b/flowchart/library/Data.py index 289cbba6..a7e08f8d 100644 --- a/flowchart/library/Data.py +++ b/flowchart/library/Data.py @@ -240,7 +240,7 @@ class EvalNode(Node): def saveState(self): state = Node.saveState(self) state['text'] = str(self.text.toPlainText()) - state['terminals'] = self.saveTerminals() + #state['terminals'] = self.saveTerminals() return state def restoreState(self, state): @@ -282,7 +282,7 @@ class ColumnJoinNode(Node): def addInput(self): #print "ColumnJoinNode.addInput called." - term = Node.addInput(self, 'input', renamable=True) + term = Node.addInput(self, 'input', renamable=True, removable=True) #print "Node.addInput returned. term:", term item = QtGui.QTreeWidgetItem([term.name()]) item.term = term @@ -322,16 +322,14 @@ class ColumnJoinNode(Node): def restoreState(self, state): Node.restoreState(self, state) - inputs = [inp.name() for inp in self.inputs()] + inputs = self.inputs() + order = [name for name in state['order'] if name in inputs] for name in inputs: - if name not in state['order']: - self.removeTerminal(name) - for name in state['order']: - if name not in inputs: - Node.addInput(self, name, renamable=True) + if name not in order: + order.append(name) self.tree.clear() - for name in state['order']: + for name in order: term = self[name] item = QtGui.QTreeWidgetItem([name]) item.term = term diff --git a/flowchart/library/functions.py b/flowchart/library/functions.py index 2b4e3881..646210e8 100644 --- a/flowchart/library/functions.py +++ b/flowchart/library/functions.py @@ -153,7 +153,7 @@ def denoise(data, radius=2, threshold=4): r2 = radius * 2 d1 = data.view(np.ndarray) - d2 = data[radius:] - data[:-radius] #a derivative + d2 = d1[radius:] - d1[:-radius] #a derivative #d3 = data[r2:] - data[:-r2] #d4 = d2 - d3 stdev = d2.std()