Merge branch 'static_imports' into develop
No more dynamic imports; pg uses static imports throughout. Flowcharts and exporters use plugin systems
This commit is contained in:
commit
3488910810
32
CHANGELOG
32
CHANGELOG
@ -1,3 +1,35 @@
|
||||
pyqtgraph-0.9.9 [unreleased]
|
||||
|
||||
API / behavior changes:
|
||||
- Dynamic import system abandoned; pg now uses static imports throughout.
|
||||
- Flowcharts and exporters have new pluggin systems
|
||||
- Version strings:
|
||||
- __init__.py in git repo now contains latest release version string
|
||||
(previously, only packaged releases had version strings).
|
||||
- installing from git checkout that does not correspond to a release
|
||||
commit will result in a more descriptive version string.
|
||||
- Speed improvements in functions.makeARGB
|
||||
- ImageItem is faster by avoiding makeQImage(transpose=True)
|
||||
|
||||
New Features:
|
||||
- New HDF5 example for working with very large datasets
|
||||
- Added Qt.loadUiType function for PySide
|
||||
- Simplified Profilers; can be activated with environmental variables
|
||||
- Added Dock.raiseDock() method
|
||||
|
||||
Bugfixes:
|
||||
- PlotCurveItem now has correct clicking behavior--clicks within a few px
|
||||
of the line will trigger a signal.
|
||||
- Fixes related to CSV exporter:
|
||||
- CSV headers include data names, if available
|
||||
- Exporter correctly handles items with no data
|
||||
- pg.plot() avoids creating empty data item
|
||||
- removed call to reduce() from exporter; not available in python 3
|
||||
- Gave .name() methods to PlotDataItem, PlotCurveItem, and ScatterPlotItem
|
||||
- fixed ImageItem handling of rgb images
|
||||
- fixed makeARGB re-ordering of color channels
|
||||
|
||||
|
||||
pyqtgraph-0.9.8 2013-11-24
|
||||
|
||||
API / behavior changes:
|
||||
|
@ -83,9 +83,8 @@ class ImageViewNode(Node):
|
||||
else:
|
||||
self.view.setImage(data)
|
||||
|
||||
## register the class so it will appear in the menu of node types.
|
||||
## It will appear in the 'display' sub-menu.
|
||||
fclib.registerNodeType(ImageViewNode, [('Display',)])
|
||||
|
||||
|
||||
|
||||
## We will define an unsharp masking filter node as a subclass of CtrlNode.
|
||||
## CtrlNode is just a convenience class that automatically creates its
|
||||
@ -113,12 +112,25 @@ class UnsharpMaskNode(CtrlNode):
|
||||
strength = self.ctrls['strength'].value()
|
||||
output = dataIn - (strength * scipy.ndimage.gaussian_filter(dataIn, (sigma,sigma)))
|
||||
return {'dataOut': output}
|
||||
|
||||
|
||||
## To make our custom node classes available in the flowchart context menu,
|
||||
## we can either register them with the default node library or make a
|
||||
## new library.
|
||||
|
||||
|
||||
## register the class so it will appear in the menu of node types.
|
||||
## It will appear in a new 'image' sub-menu.
|
||||
fclib.registerNodeType(UnsharpMaskNode, [('Image',)])
|
||||
|
||||
|
||||
## Method 1: Register to global default library:
|
||||
#fclib.registerNodeType(ImageViewNode, [('Display',)])
|
||||
#fclib.registerNodeType(UnsharpMaskNode, [('Image',)])
|
||||
|
||||
## Method 2: If we want to make our custom node available only to this flowchart,
|
||||
## then instead of registering the node type globally, we can create a new
|
||||
## NodeLibrary:
|
||||
library = fclib.LIBRARY.copy() # start with the default node set
|
||||
library.addNodeType(ImageViewNode, [('Display',)])
|
||||
library.addNodeType(UnsharpMaskNode, [('Image',)])
|
||||
fc.setLibrary(library)
|
||||
|
||||
|
||||
## Now we will programmatically add nodes to define the function of the flowchart.
|
||||
## Normally, the user will do this manually or by loading a pre-generated
|
||||
|
@ -130,56 +130,119 @@ if __version__ is None and not hasattr(sys, 'frozen') and sys.version_info[0] ==
|
||||
## Import almost everything to make it available from a single namespace
|
||||
## don't import the more complex systems--canvas, parametertree, flowchart, dockarea
|
||||
## these must be imported separately.
|
||||
from . import frozenSupport
|
||||
def importModules(path, globals, locals, excludes=()):
|
||||
"""Import all modules residing within *path*, return a dict of name: module pairs.
|
||||
#from . import frozenSupport
|
||||
#def importModules(path, globals, locals, excludes=()):
|
||||
#"""Import all modules residing within *path*, return a dict of name: module pairs.
|
||||
|
||||
Note that *path* MUST be relative to the module doing the import.
|
||||
"""
|
||||
d = os.path.join(os.path.split(globals['__file__'])[0], path)
|
||||
files = set()
|
||||
for f in frozenSupport.listdir(d):
|
||||
if frozenSupport.isdir(os.path.join(d, f)) and f not in ['__pycache__', 'tests']:
|
||||
files.add(f)
|
||||
elif f[-3:] == '.py' and f != '__init__.py':
|
||||
files.add(f[:-3])
|
||||
elif f[-4:] == '.pyc' and f != '__init__.pyc':
|
||||
files.add(f[:-4])
|
||||
#Note that *path* MUST be relative to the module doing the import.
|
||||
#"""
|
||||
#d = os.path.join(os.path.split(globals['__file__'])[0], path)
|
||||
#files = set()
|
||||
#for f in frozenSupport.listdir(d):
|
||||
#if frozenSupport.isdir(os.path.join(d, f)) and f not in ['__pycache__', 'tests']:
|
||||
#files.add(f)
|
||||
#elif f[-3:] == '.py' and f != '__init__.py':
|
||||
#files.add(f[:-3])
|
||||
#elif f[-4:] == '.pyc' and f != '__init__.pyc':
|
||||
#files.add(f[:-4])
|
||||
|
||||
mods = {}
|
||||
path = path.replace(os.sep, '.')
|
||||
for modName in files:
|
||||
if modName in excludes:
|
||||
continue
|
||||
try:
|
||||
if len(path) > 0:
|
||||
modName = path + '.' + modName
|
||||
#mod = __import__(modName, globals, locals, fromlist=['*'])
|
||||
mod = __import__(modName, globals, locals, ['*'], 1)
|
||||
mods[modName] = mod
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_stack()
|
||||
sys.excepthook(*sys.exc_info())
|
||||
print("[Error importing module: %s]" % modName)
|
||||
#mods = {}
|
||||
#path = path.replace(os.sep, '.')
|
||||
#for modName in files:
|
||||
#if modName in excludes:
|
||||
#continue
|
||||
#try:
|
||||
#if len(path) > 0:
|
||||
#modName = path + '.' + modName
|
||||
#print( "from .%s import * " % modName)
|
||||
#mod = __import__(modName, globals, locals, ['*'], 1)
|
||||
#mods[modName] = mod
|
||||
#except:
|
||||
#import traceback
|
||||
#traceback.print_stack()
|
||||
#sys.excepthook(*sys.exc_info())
|
||||
#print("[Error importing module: %s]" % modName)
|
||||
|
||||
return mods
|
||||
#return mods
|
||||
|
||||
def importAll(path, globals, locals, excludes=()):
|
||||
"""Given a list of modules, import all names from each module into the global namespace."""
|
||||
mods = importModules(path, globals, locals, excludes)
|
||||
for mod in mods.values():
|
||||
if hasattr(mod, '__all__'):
|
||||
names = mod.__all__
|
||||
else:
|
||||
names = [n for n in dir(mod) if n[0] != '_']
|
||||
for k in names:
|
||||
if hasattr(mod, k):
|
||||
globals[k] = getattr(mod, k)
|
||||
#def importAll(path, globals, locals, excludes=()):
|
||||
#"""Given a list of modules, import all names from each module into the global namespace."""
|
||||
#mods = importModules(path, globals, locals, excludes)
|
||||
#for mod in mods.values():
|
||||
#if hasattr(mod, '__all__'):
|
||||
#names = mod.__all__
|
||||
#else:
|
||||
#names = [n for n in dir(mod) if n[0] != '_']
|
||||
#for k in names:
|
||||
#if hasattr(mod, k):
|
||||
#globals[k] = getattr(mod, k)
|
||||
|
||||
importAll('graphicsItems', globals(), locals())
|
||||
importAll('widgets', globals(), locals(),
|
||||
excludes=['MatplotlibWidget', 'RawImageWidget', 'RemoteGraphicsView'])
|
||||
# Dynamic imports are disabled. This causes too many problems.
|
||||
#importAll('graphicsItems', globals(), locals())
|
||||
#importAll('widgets', globals(), locals(),
|
||||
#excludes=['MatplotlibWidget', 'RawImageWidget', 'RemoteGraphicsView'])
|
||||
|
||||
from .graphicsItems.VTickGroup import *
|
||||
from .graphicsItems.GraphicsWidget import *
|
||||
from .graphicsItems.ScaleBar import *
|
||||
from .graphicsItems.PlotDataItem import *
|
||||
from .graphicsItems.GraphItem import *
|
||||
from .graphicsItems.TextItem import *
|
||||
from .graphicsItems.GraphicsLayout import *
|
||||
from .graphicsItems.UIGraphicsItem import *
|
||||
from .graphicsItems.GraphicsObject import *
|
||||
from .graphicsItems.PlotItem import *
|
||||
from .graphicsItems.ROI import *
|
||||
from .graphicsItems.InfiniteLine import *
|
||||
from .graphicsItems.HistogramLUTItem import *
|
||||
from .graphicsItems.GridItem import *
|
||||
from .graphicsItems.GradientLegend import *
|
||||
from .graphicsItems.GraphicsItem import *
|
||||
from .graphicsItems.BarGraphItem import *
|
||||
from .graphicsItems.ViewBox import *
|
||||
from .graphicsItems.ArrowItem import *
|
||||
from .graphicsItems.ImageItem import *
|
||||
from .graphicsItems.AxisItem import *
|
||||
from .graphicsItems.LabelItem import *
|
||||
from .graphicsItems.CurvePoint import *
|
||||
from .graphicsItems.GraphicsWidgetAnchor import *
|
||||
from .graphicsItems.PlotCurveItem import *
|
||||
from .graphicsItems.ButtonItem import *
|
||||
from .graphicsItems.GradientEditorItem import *
|
||||
from .graphicsItems.MultiPlotItem import *
|
||||
from .graphicsItems.ErrorBarItem import *
|
||||
from .graphicsItems.IsocurveItem import *
|
||||
from .graphicsItems.LinearRegionItem import *
|
||||
from .graphicsItems.FillBetweenItem import *
|
||||
from .graphicsItems.LegendItem import *
|
||||
from .graphicsItems.ScatterPlotItem import *
|
||||
from .graphicsItems.ItemGroup import *
|
||||
|
||||
from .widgets.MultiPlotWidget import *
|
||||
from .widgets.ScatterPlotWidget import *
|
||||
from .widgets.ColorMapWidget import *
|
||||
from .widgets.FileDialog import *
|
||||
from .widgets.ValueLabel import *
|
||||
from .widgets.HistogramLUTWidget import *
|
||||
from .widgets.CheckTable import *
|
||||
from .widgets.BusyCursor import *
|
||||
from .widgets.PlotWidget import *
|
||||
from .widgets.ComboBox import *
|
||||
from .widgets.GradientWidget import *
|
||||
from .widgets.DataFilterWidget import *
|
||||
from .widgets.SpinBox import *
|
||||
from .widgets.JoystickButton import *
|
||||
from .widgets.GraphicsLayoutWidget import *
|
||||
from .widgets.TreeWidget import *
|
||||
from .widgets.PathButton import *
|
||||
from .widgets.VerticalLabel import *
|
||||
from .widgets.FeedbackButton import *
|
||||
from .widgets.ColorButton import *
|
||||
from .widgets.DataTreeWidget import *
|
||||
from .widgets.GraphicsView import *
|
||||
from .widgets.LayoutWidget import *
|
||||
from .widgets.TableWidget import *
|
||||
from .widgets.ProgressDialog import *
|
||||
|
||||
from .imageview import *
|
||||
from .WidgetGroup import *
|
||||
@ -194,6 +257,7 @@ from .SignalProxy import *
|
||||
from .colormap import *
|
||||
from .ptime import time
|
||||
|
||||
|
||||
##############################################################
|
||||
## PyQt and PySide both are prone to crashing on exit.
|
||||
## There are two general approaches to dealing with this:
|
||||
|
@ -60,6 +60,6 @@ class CSVExporter(Exporter):
|
||||
fd.write('\n')
|
||||
fd.close()
|
||||
|
||||
|
||||
CSVExporter.register()
|
||||
|
||||
|
||||
|
@ -11,6 +11,14 @@ class Exporter(object):
|
||||
Abstract class used for exporting graphics to file / printer / whatever.
|
||||
"""
|
||||
allowCopy = False # subclasses set this to True if they can use the copy buffer
|
||||
Exporters = []
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
"""
|
||||
Used to register Exporter classes to appear in the export dialog.
|
||||
"""
|
||||
Exporter.Exporters.append(cls)
|
||||
|
||||
def __init__(self, item):
|
||||
"""
|
||||
@ -20,9 +28,6 @@ class Exporter(object):
|
||||
object.__init__(self)
|
||||
self.item = item
|
||||
|
||||
#def item(self):
|
||||
#return self.item
|
||||
|
||||
def parameters(self):
|
||||
"""Return the parameters used to configure this exporter."""
|
||||
raise Exception("Abstract method must be overridden in subclass.")
|
||||
@ -131,45 +136,4 @@ class Exporter(object):
|
||||
return preItems + rootItem + postItems
|
||||
|
||||
def render(self, painter, targetRect, sourceRect, item=None):
|
||||
|
||||
#if item is None:
|
||||
#item = self.item
|
||||
#preItems = []
|
||||
#postItems = []
|
||||
#if isinstance(item, QtGui.QGraphicsScene):
|
||||
#childs = [i for i in item.items() if i.parentItem() is None]
|
||||
#rootItem = []
|
||||
#else:
|
||||
#childs = item.childItems()
|
||||
#rootItem = [item]
|
||||
#childs.sort(lambda a,b: cmp(a.zValue(), b.zValue()))
|
||||
#while len(childs) > 0:
|
||||
#ch = childs.pop(0)
|
||||
#if int(ch.flags() & ch.ItemStacksBehindParent) > 0 or (ch.zValue() < 0 and int(ch.flags() & ch.ItemNegativeZStacksBehindParent) > 0):
|
||||
#preItems.extend(tree)
|
||||
#else:
|
||||
#postItems.extend(tree)
|
||||
|
||||
#for ch in preItems:
|
||||
#self.render(painter, sourceRect, targetRect, item=ch)
|
||||
### paint root here
|
||||
#for ch in postItems:
|
||||
#self.render(painter, sourceRect, targetRect, item=ch)
|
||||
|
||||
|
||||
self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
|
||||
|
||||
#def writePs(self, fileName=None, item=None):
|
||||
#if fileName is None:
|
||||
#self.fileSaveDialog(self.writeSvg, filter="PostScript (*.ps)")
|
||||
#return
|
||||
#if item is None:
|
||||
#item = self
|
||||
#printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
|
||||
#printer.setOutputFileName(fileName)
|
||||
#painter = QtGui.QPainter(printer)
|
||||
#self.render(painter)
|
||||
#painter.end()
|
||||
|
||||
#def writeToPrinter(self):
|
||||
#pass
|
||||
|
@ -98,4 +98,5 @@ class ImageExporter(Exporter):
|
||||
else:
|
||||
self.png.save(fileName)
|
||||
|
||||
ImageExporter.register()
|
||||
|
@ -57,6 +57,7 @@ class MatplotlibExporter(Exporter):
|
||||
else:
|
||||
raise Exception("Matplotlib export currently only works with plot items")
|
||||
|
||||
MatplotlibExporter.register()
|
||||
|
||||
|
||||
class MatplotlibWindow(QtGui.QMainWindow):
|
||||
@ -72,3 +73,5 @@ class MatplotlibWindow(QtGui.QMainWindow):
|
||||
|
||||
def closeEvent(self, ev):
|
||||
MatplotlibExporter.windows.remove(self)
|
||||
|
||||
|
||||
|
@ -63,3 +63,6 @@ class PrintExporter(Exporter):
|
||||
finally:
|
||||
self.setExportMode(False)
|
||||
painter.end()
|
||||
|
||||
|
||||
#PrintExporter.register()
|
||||
|
@ -404,6 +404,10 @@ def correctCoordinates(node, item):
|
||||
if removeTransform:
|
||||
grp.removeAttribute('transform')
|
||||
|
||||
|
||||
SVGExporter.register()
|
||||
|
||||
|
||||
def itemTransform(item, root):
|
||||
## Return the transformation mapping item to root
|
||||
## (actually to parent coordinate system of root)
|
||||
|
@ -1,27 +1,24 @@
|
||||
Exporters = []
|
||||
from pyqtgraph import importModules
|
||||
#from .. import frozenSupport
|
||||
import os
|
||||
d = os.path.split(__file__)[0]
|
||||
#files = []
|
||||
#for f in frozenSupport.listdir(d):
|
||||
#if frozenSupport.isdir(os.path.join(d, f)) and f != '__pycache__':
|
||||
#files.append(f)
|
||||
#elif f[-3:] == '.py' and f not in ['__init__.py', 'Exporter.py']:
|
||||
#files.append(f[:-3])
|
||||
|
||||
#for modName in files:
|
||||
#mod = __import__(modName, globals(), locals(), fromlist=['*'])
|
||||
for mod in importModules('', globals(), locals(), excludes=['Exporter']).values():
|
||||
if hasattr(mod, '__all__'):
|
||||
names = mod.__all__
|
||||
else:
|
||||
names = [n for n in dir(mod) if n[0] != '_']
|
||||
for k in names:
|
||||
if hasattr(mod, k):
|
||||
Exporters.append(getattr(mod, k))
|
||||
#Exporters = []
|
||||
#from pyqtgraph import importModules
|
||||
#import os
|
||||
#d = os.path.split(__file__)[0]
|
||||
#for mod in importModules('', globals(), locals(), excludes=['Exporter']).values():
|
||||
#if hasattr(mod, '__all__'):
|
||||
#names = mod.__all__
|
||||
#else:
|
||||
#names = [n for n in dir(mod) if n[0] != '_']
|
||||
#for k in names:
|
||||
#if hasattr(mod, k):
|
||||
#Exporters.append(getattr(mod, k))
|
||||
|
||||
from .Exporter import Exporter
|
||||
from .ImageExporter import *
|
||||
from .SVGExporter import *
|
||||
from .Matplotlib import *
|
||||
from .CSVExporter import *
|
||||
from .PrintExporter import *
|
||||
|
||||
|
||||
def listExporters():
|
||||
return Exporters[:]
|
||||
return Exporter.Exporters[:]
|
||||
|
||||
|
@ -14,7 +14,7 @@ else:
|
||||
|
||||
from .Terminal import Terminal
|
||||
from numpy import ndarray
|
||||
from . import library
|
||||
from .library import LIBRARY
|
||||
from pyqtgraph.debug import printExc
|
||||
import pyqtgraph.configfile as configfile
|
||||
import pyqtgraph.dockarea as dockarea
|
||||
@ -67,7 +67,8 @@ class Flowchart(Node):
|
||||
sigChartLoaded = QtCore.Signal()
|
||||
sigStateChanged = QtCore.Signal()
|
||||
|
||||
def __init__(self, terminals=None, name=None, filePath=None):
|
||||
def __init__(self, terminals=None, name=None, filePath=None, library=None):
|
||||
self.library = library or LIBRARY
|
||||
if name is None:
|
||||
name = "Flowchart"
|
||||
if terminals is None:
|
||||
@ -105,6 +106,10 @@ class Flowchart(Node):
|
||||
for name, opts in terminals.items():
|
||||
self.addTerminal(name, **opts)
|
||||
|
||||
def setLibrary(self, lib):
|
||||
self.library = lib
|
||||
self.widget().chartWidget.buildMenu()
|
||||
|
||||
def setInput(self, **args):
|
||||
"""Set the input values of the flowchart. This will automatically propagate
|
||||
the new values throughout the flowchart, (possibly) causing the output to change.
|
||||
@ -194,7 +199,7 @@ class Flowchart(Node):
|
||||
break
|
||||
n += 1
|
||||
|
||||
node = library.getNodeType(nodeType)(name)
|
||||
node = self.library.getNodeType(nodeType)(name)
|
||||
self.addNode(node, name, pos)
|
||||
return node
|
||||
|
||||
@ -846,13 +851,13 @@ class FlowchartWidget(dockarea.DockArea):
|
||||
self.nodeMenu.triggered.disconnect(self.nodeMenuTriggered)
|
||||
self.nodeMenu = None
|
||||
self.subMenus = []
|
||||
library.loadLibrary(reloadLibs=True)
|
||||
self.chart.library.reload()
|
||||
self.buildMenu()
|
||||
|
||||
def buildMenu(self, pos=None):
|
||||
self.nodeMenu = QtGui.QMenu()
|
||||
self.subMenus = []
|
||||
for section, nodes in library.getNodeTree().items():
|
||||
for section, nodes in self.chart.library.getNodeTree().items():
|
||||
menu = QtGui.QMenu(section)
|
||||
self.nodeMenu.addMenu(menu)
|
||||
for name in nodes:
|
||||
|
84
pyqtgraph/flowchart/NodeLibrary.py
Normal file
84
pyqtgraph/flowchart/NodeLibrary.py
Normal file
@ -0,0 +1,84 @@
|
||||
from pyqtgraph.pgcollections import OrderedDict
|
||||
from .Node import Node
|
||||
|
||||
def isNodeClass(cls):
|
||||
try:
|
||||
if not issubclass(cls, Node):
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
return hasattr(cls, 'nodeName')
|
||||
|
||||
|
||||
|
||||
class NodeLibrary:
|
||||
"""
|
||||
A library of flowchart Node types. Custom libraries may be built to provide
|
||||
each flowchart with a specific set of allowed Node types.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.nodeList = OrderedDict()
|
||||
self.nodeTree = OrderedDict()
|
||||
|
||||
def addNodeType(self, nodeClass, paths, override=False):
|
||||
"""
|
||||
Register a new node type. If the type's name is already in use,
|
||||
an exception will be raised (unless override=True).
|
||||
|
||||
Arguments:
|
||||
|
||||
nodeClass - a subclass of Node (must have typ.nodeName)
|
||||
paths - list of tuples specifying the location(s) this
|
||||
type will appear in the library tree.
|
||||
override - if True, overwrite any class having the same name
|
||||
"""
|
||||
if not isNodeClass(nodeClass):
|
||||
raise Exception("Object %s is not a Node subclass" % str(nodeClass))
|
||||
|
||||
name = nodeClass.nodeName
|
||||
if not override and name in self.nodeList:
|
||||
raise Exception("Node type name '%s' is already registered." % name)
|
||||
|
||||
self.nodeList[name] = nodeClass
|
||||
for path in paths:
|
||||
root = self.nodeTree
|
||||
for n in path:
|
||||
if n not in root:
|
||||
root[n] = OrderedDict()
|
||||
root = root[n]
|
||||
root[name] = nodeClass
|
||||
|
||||
def getNodeType(self, name):
|
||||
try:
|
||||
return self.nodeList[name]
|
||||
except KeyError:
|
||||
raise Exception("No node type called '%s'" % name)
|
||||
|
||||
def getNodeTree(self):
|
||||
return self.nodeTree
|
||||
|
||||
def copy(self):
|
||||
"""
|
||||
Return a copy of this library.
|
||||
"""
|
||||
lib = NodeLibrary()
|
||||
lib.nodeList = self.nodeList.copy()
|
||||
lib.nodeTree = self.treeCopy(self.nodeTree)
|
||||
return lib
|
||||
|
||||
@staticmethod
|
||||
def treeCopy(tree):
|
||||
copy = OrderedDict()
|
||||
for k,v in tree.items():
|
||||
if isNodeClass(v):
|
||||
copy[k] = v
|
||||
else:
|
||||
copy[k] = NodeLibrary.treeCopy(v)
|
||||
return copy
|
||||
|
||||
def reload(self):
|
||||
"""
|
||||
Reload Node classes in this library.
|
||||
"""
|
||||
raise NotImplementedError()
|
@ -1,103 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pyqtgraph.pgcollections import OrderedDict
|
||||
from pyqtgraph import importModules
|
||||
#from pyqtgraph import importModules
|
||||
import os, types
|
||||
from pyqtgraph.debug import printExc
|
||||
from ..Node import Node
|
||||
#from ..Node import Node
|
||||
from ..NodeLibrary import NodeLibrary, isNodeClass
|
||||
import pyqtgraph.reload as reload
|
||||
|
||||
|
||||
NODE_LIST = OrderedDict() ## maps name:class for all registered Node subclasses
|
||||
NODE_TREE = OrderedDict() ## categorized tree of Node subclasses
|
||||
# Build default library
|
||||
LIBRARY = NodeLibrary()
|
||||
|
||||
def getNodeType(name):
|
||||
try:
|
||||
return NODE_LIST[name]
|
||||
except KeyError:
|
||||
raise Exception("No node type called '%s'" % name)
|
||||
# For backward compatibility, expose the default library's properties here:
|
||||
NODE_LIST = LIBRARY.nodeList
|
||||
NODE_TREE = LIBRARY.nodeTree
|
||||
registerNodeType = LIBRARY.addNodeType
|
||||
getNodeTree = LIBRARY.getNodeTree
|
||||
getNodeType = LIBRARY.getNodeType
|
||||
|
||||
def getNodeTree():
|
||||
return NODE_TREE
|
||||
|
||||
def registerNodeType(cls, paths, override=False):
|
||||
"""
|
||||
Register a new node type. If the type's name is already in use,
|
||||
an exception will be raised (unless override=True).
|
||||
# Add all nodes to the default library
|
||||
from . import Data, Display, Filters, Operators
|
||||
for mod in [Data, Display, Filters, Operators]:
|
||||
#mod = getattr(__import__('', fromlist=[modName], level=1), modName)
|
||||
#mod = __import__(modName, level=1)
|
||||
nodes = [getattr(mod, name) for name in dir(mod) if isNodeClass(getattr(mod, name))]
|
||||
for node in nodes:
|
||||
LIBRARY.addNodeType(node, [(mod.__name__.split('.')[-1],)])
|
||||
|
||||
Arguments:
|
||||
cls - a subclass of Node (must have typ.nodeName)
|
||||
paths - list of tuples specifying the location(s) this
|
||||
type will appear in the library tree.
|
||||
override - if True, overwrite any class having the same name
|
||||
"""
|
||||
if not isNodeClass(cls):
|
||||
raise Exception("Object %s is not a Node subclass" % str(cls))
|
||||
#NODE_LIST = OrderedDict() ## maps name:class for all registered Node subclasses
|
||||
#NODE_TREE = OrderedDict() ## categorized tree of Node subclasses
|
||||
|
||||
#def getNodeType(name):
|
||||
#try:
|
||||
#return NODE_LIST[name]
|
||||
#except KeyError:
|
||||
#raise Exception("No node type called '%s'" % name)
|
||||
|
||||
#def getNodeTree():
|
||||
#return NODE_TREE
|
||||
|
||||
#def registerNodeType(cls, paths, override=False):
|
||||
#"""
|
||||
#Register a new node type. If the type's name is already in use,
|
||||
#an exception will be raised (unless override=True).
|
||||
|
||||
name = cls.nodeName
|
||||
if not override and name in NODE_LIST:
|
||||
raise Exception("Node type name '%s' is already registered." % name)
|
||||
#Arguments:
|
||||
#cls - a subclass of Node (must have typ.nodeName)
|
||||
#paths - list of tuples specifying the location(s) this
|
||||
#type will appear in the library tree.
|
||||
#override - if True, overwrite any class having the same name
|
||||
#"""
|
||||
#if not isNodeClass(cls):
|
||||
#raise Exception("Object %s is not a Node subclass" % str(cls))
|
||||
|
||||
NODE_LIST[name] = cls
|
||||
for path in paths:
|
||||
root = NODE_TREE
|
||||
for n in path:
|
||||
if n not in root:
|
||||
root[n] = OrderedDict()
|
||||
root = root[n]
|
||||
root[name] = cls
|
||||
|
||||
|
||||
|
||||
def isNodeClass(cls):
|
||||
try:
|
||||
if not issubclass(cls, Node):
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
return hasattr(cls, 'nodeName')
|
||||
|
||||
def loadLibrary(reloadLibs=False, libPath=None):
|
||||
"""Import all Node subclasses found within files in the library module."""
|
||||
|
||||
global NODE_LIST, NODE_TREE
|
||||
#if libPath is None:
|
||||
#libPath = os.path.dirname(os.path.abspath(__file__))
|
||||
#name = cls.nodeName
|
||||
#if not override and name in NODE_LIST:
|
||||
#raise Exception("Node type name '%s' is already registered." % name)
|
||||
|
||||
if reloadLibs:
|
||||
reload.reloadAll(libPath)
|
||||
#NODE_LIST[name] = cls
|
||||
#for path in paths:
|
||||
#root = NODE_TREE
|
||||
#for n in path:
|
||||
#if n not in root:
|
||||
#root[n] = OrderedDict()
|
||||
#root = root[n]
|
||||
#root[name] = cls
|
||||
|
||||
|
||||
|
||||
#def isNodeClass(cls):
|
||||
#try:
|
||||
#if not issubclass(cls, Node):
|
||||
#return False
|
||||
#except:
|
||||
#return False
|
||||
#return hasattr(cls, 'nodeName')
|
||||
|
||||
#def loadLibrary(reloadLibs=False, libPath=None):
|
||||
#"""Import all Node subclasses found within files in the library module."""
|
||||
|
||||
#global NODE_LIST, NODE_TREE
|
||||
|
||||
#if reloadLibs:
|
||||
#reload.reloadAll(libPath)
|
||||
|
||||
mods = importModules('', globals(), locals())
|
||||
#for f in frozenSupport.listdir(libPath):
|
||||
#pathName, ext = os.path.splitext(f)
|
||||
#if ext not in ('.py', '.pyc') or '__init__' in pathName or '__pycache__' in pathName:
|
||||
#continue
|
||||
#try:
|
||||
##print "importing from", f
|
||||
#mod = __import__(pathName, globals(), locals())
|
||||
#except:
|
||||
#printExc("Error loading flowchart library %s:" % pathName)
|
||||
#continue
|
||||
#mods = importModules('', globals(), locals())
|
||||
|
||||
for name, mod in mods.items():
|
||||
nodes = []
|
||||
for n in dir(mod):
|
||||
o = getattr(mod, n)
|
||||
if isNodeClass(o):
|
||||
#print " ", str(o)
|
||||
registerNodeType(o, [(name,)], override=reloadLibs)
|
||||
#nodes.append((o.nodeName, o))
|
||||
#if len(nodes) > 0:
|
||||
#NODE_TREE[name] = OrderedDict(nodes)
|
||||
#NODE_LIST.extend(nodes)
|
||||
#NODE_LIST = OrderedDict(NODE_LIST)
|
||||
#for name, mod in mods.items():
|
||||
#nodes = []
|
||||
#for n in dir(mod):
|
||||
#o = getattr(mod, n)
|
||||
#if isNodeClass(o):
|
||||
#registerNodeType(o, [(name,)], override=reloadLibs)
|
||||
|
||||
def reloadLibrary():
|
||||
loadLibrary(reloadLibs=True)
|
||||
#def reloadLibrary():
|
||||
#loadLibrary(reloadLibs=True)
|
||||
|
||||
loadLibrary()
|
||||
#NODE_LIST = []
|
||||
#for o in locals().values():
|
||||
#if type(o) is type(AddNode) and issubclass(o, Node) and o is not Node and hasattr(o, 'nodeName'):
|
||||
#NODE_LIST.append((o.nodeName, o))
|
||||
#NODE_LIST.sort(lambda a,b: cmp(a[0], b[0]))
|
||||
#NODE_LIST = OrderedDict(NODE_LIST)
|
||||
#loadLibrary()
|
||||
|
||||
|
||||
|
||||
|
@ -1,28 +1,20 @@
|
||||
from .GLViewWidget import GLViewWidget
|
||||
|
||||
from pyqtgraph import importAll
|
||||
#import os
|
||||
#def importAll(path):
|
||||
#d = os.path.join(os.path.split(__file__)[0], path)
|
||||
#files = []
|
||||
#for f in os.listdir(d):
|
||||
#if os.path.isdir(os.path.join(d, f)) and f != '__pycache__':
|
||||
#files.append(f)
|
||||
#elif f[-3:] == '.py' and f != '__init__.py':
|
||||
#files.append(f[:-3])
|
||||
|
||||
#for modName in files:
|
||||
#mod = __import__(path+"."+modName, globals(), locals(), fromlist=['*'])
|
||||
#if hasattr(mod, '__all__'):
|
||||
#names = mod.__all__
|
||||
#else:
|
||||
#names = [n for n in dir(mod) if n[0] != '_']
|
||||
#for k in names:
|
||||
#if hasattr(mod, k):
|
||||
#globals()[k] = getattr(mod, k)
|
||||
## dynamic imports cause too many problems.
|
||||
#from pyqtgraph import importAll
|
||||
#importAll('items', globals(), locals())
|
||||
|
||||
from .items.GLGridItem import *
|
||||
from .items.GLBarGraphItem import *
|
||||
from .items.GLScatterPlotItem import *
|
||||
from .items.GLMeshItem import *
|
||||
from .items.GLLinePlotItem import *
|
||||
from .items.GLAxisItem import *
|
||||
from .items.GLImageItem import *
|
||||
from .items.GLSurfacePlotItem import *
|
||||
from .items.GLBoxItem import *
|
||||
from .items.GLVolumeItem import *
|
||||
|
||||
importAll('items', globals(), locals())
|
||||
\
|
||||
from .MeshData import MeshData
|
||||
## for backward compatibility:
|
||||
#MeshData.MeshData = MeshData ## breaks autodoc.
|
||||
|
Loading…
Reference in New Issue
Block a user