diff --git a/__init__.py b/__init__.py index 4da9faee..ff214d92 100644 --- a/__init__.py +++ b/__init__.py @@ -15,8 +15,8 @@ import os, sys ## check python version ## Allow anything >= 2.7 -if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 7): - raise Exception("Pyqtgraph requires Python version 2.7 (this is %d.%d)" % (sys.version_info[0], sys.version_info[1])) +if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 6): + raise Exception("Pyqtgraph requires Python version 2.6 or greater (this is %d.%d)" % (sys.version_info[0], sys.version_info[1])) ## helpers for 2/3 compatibility from . import python2_3 diff --git a/configfile.py b/configfile.py index d413ea56..db7dc732 100644 --- a/configfile.py +++ b/configfile.py @@ -10,7 +10,7 @@ as it can be converted to/from a string using repr and eval. """ import re, os, sys -from collections import OrderedDict +from pgcollections import OrderedDict GLOBAL_PATH = None # so not thread safe. from . import units from .python2_3 import asUnicode diff --git a/examples/__main__.py b/examples/__main__.py index ae5163ee..19514ae1 100644 --- a/examples/__main__.py +++ b/examples/__main__.py @@ -9,7 +9,7 @@ else: from exampleLoaderTemplate_pyqt import Ui_Form import os, sys -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict examples = OrderedDict([ ('Command-line usage', 'CLIexample.py'), diff --git a/examples/parametertree.py b/examples/parametertree.py index cedbb889..d5724017 100644 --- a/examples/parametertree.py +++ b/examples/parametertree.py @@ -14,7 +14,6 @@ import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui -import collections app = QtGui.QApplication([]) import pyqtgraph.parametertree.parameterTypes as pTypes from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType diff --git a/flowchart/Flowchart.py b/flowchart/Flowchart.py index a5ea5f9a..6b1352d5 100644 --- a/flowchart/Flowchart.py +++ b/flowchart/Flowchart.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from pyqtgraph.Qt import QtCore, QtGui, USE_PYSIDE from .Node import * -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict from pyqtgraph.widgets.TreeWidget import * ## pyside and pyqt use incompatible ui files. diff --git a/flowchart/Node.py b/flowchart/Node.py index 661f3467..ed5c9714 100644 --- a/flowchart/Node.py +++ b/flowchart/Node.py @@ -3,7 +3,7 @@ from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.graphicsItems.GraphicsObject import GraphicsObject import pyqtgraph.functions as fn from .Terminal import * -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict from pyqtgraph.debug import * import numpy as np from .eq import * diff --git a/flowchart/library/__init__.py b/flowchart/library/__init__.py index be51925f..5b8df612 100644 --- a/flowchart/library/__init__.py +++ b/flowchart/library/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict import os, types from pyqtgraph.debug import printExc from ..Node import Node diff --git a/graphicsItems/ArrowItem.py b/graphicsItems/ArrowItem.py index 186ea55d..153ea712 100644 --- a/graphicsItems/ArrowItem.py +++ b/graphicsItems/ArrowItem.py @@ -42,7 +42,7 @@ class ArrowItem(QtGui.QGraphicsPathItem): def setStyle(self, **opts): self.opts = opts - opt = {k:self.opts[k] for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']} + opt = dict([(k,self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']]) self.path = fn.makeArrowPath(**opt) self.setPath(self.path) diff --git a/graphicsItems/GradientEditorItem.py b/graphicsItems/GradientEditorItem.py index 8e488a28..33970d2c 100644 --- a/graphicsItems/GradientEditorItem.py +++ b/graphicsItems/GradientEditorItem.py @@ -3,13 +3,14 @@ from pyqtgraph.python2_3 import sortList import pyqtgraph.functions as fn from .GraphicsObject import GraphicsObject from .GraphicsWidget import GraphicsWidget -import weakref, collections +import weakref +from pyqtgraph.pgcollections import OrderedDict import numpy as np __all__ = ['TickSliderItem', 'GradientEditorItem'] -Gradients = collections.OrderedDict([ +Gradients = OrderedDict([ ('thermal', {'ticks': [(0.3333, (185, 0, 0, 255)), (0.6666, (255, 220, 0, 255)), (1, (255, 255, 255, 255)), (0, (0, 0, 0, 255))], 'mode': 'rgb'}), ('flame', {'ticks': [(0.2, (7, 0, 220, 255)), (0.5, (236, 0, 134, 255)), (0.8, (246, 246, 0, 255)), (1.0, (255, 255, 255, 255)), (0.0, (0, 0, 0, 255))], 'mode': 'rgb'}), ('yellowy', {'ticks': [(0.0, (0, 0, 0, 255)), (0.2328863796753704, (32, 0, 129, 255)), (0.8362738179251941, (255, 255, 0, 255)), (0.5257586450247, (115, 15, 255, 255)), (1.0, (255, 255, 255, 255))], 'mode': 'rgb'} ), diff --git a/graphicsItems/PlotItem/PlotItem.py b/graphicsItems/PlotItem/PlotItem.py index c9ebe3f4..c50e25df 100644 --- a/graphicsItems/PlotItem/PlotItem.py +++ b/graphicsItems/PlotItem/PlotItem.py @@ -35,7 +35,6 @@ from .. LabelItem import LabelItem from .. GraphicsWidget import GraphicsWidget from .. ButtonItem import ButtonItem from pyqtgraph.WidgetGroup import WidgetGroup -import collections __all__ = ['PlotItem'] diff --git a/graphicsItems/ScatterPlotItem.py b/graphicsItems/ScatterPlotItem.py index 5517cc86..07365aa4 100644 --- a/graphicsItems/ScatterPlotItem.py +++ b/graphicsItems/ScatterPlotItem.py @@ -7,7 +7,7 @@ import numpy as np import scipy.stats import weakref import pyqtgraph.debug as debug -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict #import pyqtgraph as pg __all__ = ['ScatterPlotItem', 'SpotItem'] diff --git a/graphicsItems/ViewBox/ViewBox.py b/graphicsItems/ViewBox/ViewBox.py index 590edb7e..bd967d05 100644 --- a/graphicsItems/ViewBox/ViewBox.py +++ b/graphicsItems/ViewBox/ViewBox.py @@ -9,7 +9,6 @@ from pyqtgraph.GraphicsScene import GraphicsScene import pyqtgraph import weakref from copy import deepcopy -import collections __all__ = ['ViewBox'] @@ -111,12 +110,6 @@ class ViewBox(GraphicsWidget): } - #self.exportMethods = collections.OrderedDict([ - #('SVG', self.saveSvg), - #('Image', self.saveImage), - #('Print', self.savePrint), - #]) - self.setFlag(self.ItemClipsChildrenToShape) self.setFlag(self.ItemIsFocusable, True) ## so we can receive key presses diff --git a/parametertree/Parameter.py b/parametertree/Parameter.py index de1c9b63..2437c2d4 100644 --- a/parametertree/Parameter.py +++ b/parametertree/Parameter.py @@ -1,5 +1,6 @@ from pyqtgraph.Qt import QtGui, QtCore -import collections, os, weakref, re +import os, weakref, re +from pyqtgraph.pgcollections import OrderedDict from .ParameterItem import ParameterItem PARAM_TYPES = {} @@ -216,7 +217,7 @@ class Parameter(QtCore.QObject): def getValues(self): """Return a tree of all values that are children of this parameter""" - vals = collections.OrderedDict() + vals = OrderedDict() for ch in self: vals[ch.name()] = (ch.value(), ch.getValues()) return vals @@ -227,7 +228,7 @@ class Parameter(QtCore.QObject): The tree state may be restored from this structure using restoreState() """ state = self.opts.copy() - state['children'] = collections.OrderedDict([(ch.name(), ch.saveState()) for ch in self]) + state['children'] = OrderedDict([(ch.name(), ch.saveState()) for ch in self]) if state['type'] is None: global PARAM_NAMES state['type'] = PARAM_NAMES.get(type(self), None) @@ -363,7 +364,7 @@ class Parameter(QtCore.QObject): most parameters will accept a common set of options: value, name, limits, default, readonly, removable, renamable, visible, and enabled. """ - changed = collections.OrderedDict() + changed = OrderedDict() for k in opts: if k == 'value': self.setValue(opts[k]) diff --git a/parametertree/ParameterItem.py b/parametertree/ParameterItem.py index 30105d9b..376e900d 100644 --- a/parametertree/ParameterItem.py +++ b/parametertree/ParameterItem.py @@ -1,5 +1,5 @@ from pyqtgraph.Qt import QtGui, QtCore -import collections, os, weakref, re +import os, weakref, re class ParameterItem(QtGui.QTreeWidgetItem): """ diff --git a/parametertree/ParameterTree.py b/parametertree/ParameterTree.py index 433209e8..e57430ea 100644 --- a/parametertree/ParameterTree.py +++ b/parametertree/ParameterTree.py @@ -1,6 +1,6 @@ from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.widgets.TreeWidget import TreeWidget -import collections, os, weakref, re +import os, weakref, re #import functions as fn diff --git a/parametertree/parameterTypes.py b/parametertree/parameterTypes.py index 46cce317..e3c2b5d5 100644 --- a/parametertree/parameterTypes.py +++ b/parametertree/parameterTypes.py @@ -5,7 +5,8 @@ from .ParameterItem import ParameterItem from pyqtgraph.widgets.SpinBox import SpinBox from pyqtgraph.widgets.ColorButton import ColorButton import pyqtgraph as pg -import os, collections +import os +from pyqtgraph.pgcollections import OrderedDict class WidgetParameterItem(ParameterItem): """ @@ -481,8 +482,6 @@ class ListParameterItem(WidgetParameterItem): def limitsChanged(self, param, limits): # set up forward / reverse mappings for name:value - #self.forward = collections.OrderedDict([('', None)]) ## name: value - #self.reverse = collections.OrderedDict([(None, '')]) ## value: name if len(limits) == 0: limits = [''] ## Can never have an empty list--there is always at least a singhe blank item. @@ -507,8 +506,8 @@ class ListParameter(Parameter): itemClass = ListParameterItem def __init__(self, **opts): - self.forward = collections.OrderedDict() ## name: value - self.reverse = collections.OrderedDict() ## value: name + self.forward = OrderedDict() ## name: value + self.reverse = OrderedDict() ## value: name ## Parameter uses 'limits' option to define the set of allowed values if 'values' in opts: @@ -528,8 +527,8 @@ class ListParameter(Parameter): @staticmethod def mapping(limits): ## Return forward and reverse mapping dictionaries given a limit specification - forward = collections.OrderedDict() ## name: value - reverse = collections.OrderedDict() ## value: name + forward = OrderedDict() ## name: value + reverse = OrderedDict() ## value: name if isinstance(limits, dict): for k, v in limits.items(): forward[k] = v diff --git a/pgcollections.py b/pgcollections.py new file mode 100644 index 00000000..be5281f2 --- /dev/null +++ b/pgcollections.py @@ -0,0 +1,543 @@ +# -*- coding: utf-8 -*- +""" +advancedTypes.py - Basic data structures not included with python +Copyright 2010 Luke Campagnola +Distributed under MIT/X11 license. See license.txt for more infomation. + +Includes: + - OrderedDict - Dictionary which preserves the order of its elements + - BiDict, ReverseDict - Bi-directional dictionaries + - ThreadsafeDict, ThreadsafeList - Self-mutexed data structures +""" + +import threading, sys, copy, collections +#from debug import * + +try: + from collections import OrderedDict +except: + # Deprecated; this class is now present in Python 2.7 as collections.OrderedDict + # Only keeping this around for python2.6 support. + class OrderedDict(dict): + """extends dict so that elements are iterated in the order that they were added. + Since this class can not be instantiated with regular dict notation, it instead uses + a list of tuples: + od = OrderedDict([(key1, value1), (key2, value2), ...]) + items set using __setattr__ are added to the end of the key list. + """ + + def __init__(self, data=None): + self.order = [] + if data is not None: + for i in data: + self[i[0]] = i[1] + + def __setitem__(self, k, v): + if not self.has_key(k): + self.order.append(k) + dict.__setitem__(self, k, v) + + def __delitem__(self, k): + self.order.remove(k) + dict.__delitem__(self, k) + + def keys(self): + return self.order[:] + + def items(self): + it = [] + for k in self.keys(): + it.append((k, self[k])) + return it + + def values(self): + return [self[k] for k in self.order] + + def remove(self, key): + del self[key] + #self.order.remove(key) + + def __iter__(self): + for k in self.order: + yield k + + def update(self, data): + """Works like dict.update, but accepts list-of-tuples as well as dict.""" + if isinstance(data, dict): + for k, v in data.iteritems(): + self[k] = v + else: + for k,v in data: + self[k] = v + + def copy(self): + return OrderedDict(self.items()) + + def itervalues(self): + for k in self.order: + yield self[k] + + def iteritems(self): + for k in self.order: + yield (k, self[k]) + + def __deepcopy__(self, memo): + return OrderedDict([(k, copy.deepcopy(v, memo)) for k, v in self.iteritems()]) + + + +class ReverseDict(dict): + """extends dict so that reverse lookups are possible by requesting the key as a list of length 1: + d = BiDict({'x': 1, 'y': 2}) + d['x'] + 1 + d[[2]] + 'y' + """ + def __init__(self, data=None): + if data is None: + data = {} + self.reverse = {} + for k in data: + self.reverse[data[k]] = k + dict.__init__(self, data) + + def __getitem__(self, item): + if type(item) is list: + return self.reverse[item[0]] + else: + return dict.__getitem__(self, item) + + def __setitem__(self, item, value): + self.reverse[value] = item + dict.__setitem__(self, item, value) + + def __deepcopy__(self, memo): + raise Exception("deepcopy not implemented") + + +class BiDict(dict): + """extends dict so that reverse lookups are possible by adding each reverse combination to the dict. + This only works if all values and keys are unique.""" + def __init__(self, data=None): + if data is None: + data = {} + dict.__init__(self) + for k in data: + self[data[k]] = k + + def __setitem__(self, item, value): + dict.__setitem__(self, item, value) + dict.__setitem__(self, value, item) + + def __deepcopy__(self, memo): + raise Exception("deepcopy not implemented") + +class ThreadsafeDict(dict): + """Extends dict so that getitem, setitem, and contains are all thread-safe. + Also adds lock/unlock functions for extended exclusive operations + Converts all sub-dicts and lists to threadsafe as well. + """ + + def __init__(self, *args, **kwargs): + self.mutex = threading.RLock() + dict.__init__(self, *args, **kwargs) + for k in self: + if type(self[k]) is dict: + self[k] = ThreadsafeDict(self[k]) + + def __getitem__(self, attr): + self.lock() + try: + val = dict.__getitem__(self, attr) + finally: + self.unlock() + return val + + def __setitem__(self, attr, val): + if type(val) is dict: + val = ThreadsafeDict(val) + self.lock() + try: + dict.__setitem__(self, attr, val) + finally: + self.unlock() + + def __contains__(self, attr): + self.lock() + try: + val = dict.__contains__(self, attr) + finally: + self.unlock() + return val + + def __len__(self): + self.lock() + try: + val = dict.__len__(self) + finally: + self.unlock() + return val + + def clear(self): + self.lock() + try: + dict.clear(self) + finally: + self.unlock() + + def lock(self): + self.mutex.acquire() + + def unlock(self): + self.mutex.release() + + def __deepcopy__(self, memo): + raise Exception("deepcopy not implemented") + +class ThreadsafeList(list): + """Extends list so that getitem, setitem, and contains are all thread-safe. + Also adds lock/unlock functions for extended exclusive operations + Converts all sub-lists and dicts to threadsafe as well. + """ + + def __init__(self, *args, **kwargs): + self.mutex = threading.RLock() + list.__init__(self, *args, **kwargs) + for k in self: + self[k] = mkThreadsafe(self[k]) + + def __getitem__(self, attr): + self.lock() + try: + val = list.__getitem__(self, attr) + finally: + self.unlock() + return val + + def __setitem__(self, attr, val): + val = makeThreadsafe(val) + self.lock() + try: + list.__setitem__(self, attr, val) + finally: + self.unlock() + + def __contains__(self, attr): + self.lock() + try: + val = list.__contains__(self, attr) + finally: + self.unlock() + return val + + def __len__(self): + self.lock() + try: + val = list.__len__(self) + finally: + self.unlock() + return val + + def lock(self): + self.mutex.acquire() + + def unlock(self): + self.mutex.release() + + def __deepcopy__(self, memo): + raise Exception("deepcopy not implemented") + + +def makeThreadsafe(obj): + if type(obj) is dict: + return ThreadsafeDict(obj) + elif type(obj) is list: + return ThreadsafeList(obj) + elif type(obj) in [str, int, float, bool, tuple]: + return obj + else: + raise Exception("Not sure how to make object of type %s thread-safe" % str(type(obj))) + + +class Locker: + def __init__(self, lock): + self.lock = lock + self.lock.acquire() + def __del__(self): + try: + self.lock.release() + except: + pass + +class CaselessDict(OrderedDict): + """Case-insensitive dict. Values can be set and retrieved using keys of any case. + Note that when iterating, the original case is returned for each key.""" + def __init__(self, *args): + OrderedDict.__init__(self, {}) ## requirement for the empty {} here seems to be a python bug? + self.keyMap = OrderedDict([(k.lower(), k) for k in OrderedDict.keys(self)]) + if len(args) == 0: + return + elif len(args) == 1 and isinstance(args[0], dict): + for k in args[0]: + self[k] = args[0][k] + else: + raise Exception("CaselessDict may only be instantiated with a single dict.") + + #def keys(self): + #return self.keyMap.values() + + def __setitem__(self, key, val): + kl = key.lower() + if kl in self.keyMap: + OrderedDict.__setitem__(self, self.keyMap[kl], val) + else: + OrderedDict.__setitem__(self, key, val) + self.keyMap[kl] = key + + def __getitem__(self, key): + kl = key.lower() + if kl not in self.keyMap: + raise KeyError(key) + return OrderedDict.__getitem__(self, self.keyMap[kl]) + + def __contains__(self, key): + return key.lower() in self.keyMap + + def update(self, d): + for k, v in d.iteritems(): + self[k] = v + + def copy(self): + return CaselessDict(OrderedDict.copy(self)) + + def __delitem__(self, key): + kl = key.lower() + if kl not in self.keyMap: + raise KeyError(key) + OrderedDict.__delitem__(self, self.keyMap[kl]) + del self.keyMap[kl] + + def __deepcopy__(self, memo): + raise Exception("deepcopy not implemented") + + def clear(self): + OrderedDict.clear(self) + self.keyMap.clear() + + + +class ProtectedDict(dict): + """ + A class allowing read-only 'view' of a dict. + The object can be treated like a normal dict, but will never modify the original dict it points to. + Any values accessed from the dict will also be read-only. + """ + def __init__(self, data): + self._data_ = data + + ## List of methods to directly wrap from _data_ + wrapMethods = ['_cmp_', '__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'has_key', 'iterkeys', 'keys', ] + + ## List of methods which wrap from _data_ but return protected results + protectMethods = ['__getitem__', '__iter__', 'get', 'items', 'values'] + + ## List of methods to disable + disableMethods = ['__delitem__', '__setitem__', 'clear', 'pop', 'popitem', 'setdefault', 'update'] + + + ## Template methods + def wrapMethod(methodName): + return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) + + def protectMethod(methodName): + return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) + + def error(self, *args, **kargs): + raise Exception("Can not modify read-only list.") + + + ## Directly (and explicitly) wrap some methods from _data_ + ## Many of these methods can not be intercepted using __getattribute__, so they + ## must be implemented explicitly + for methodName in wrapMethods: + locals()[methodName] = wrapMethod(methodName) + + ## Wrap some methods from _data_ with the results converted to protected objects + for methodName in protectMethods: + locals()[methodName] = protectMethod(methodName) + + ## Disable any methods that could change data in the list + for methodName in disableMethods: + locals()[methodName] = error + + + ## Add a few extra methods. + def copy(self): + raise Exception("It is not safe to copy protected dicts! (instead try deepcopy, but be careful.)") + + def itervalues(self): + for v in self._data_.itervalues(): + yield protect(v) + + def iteritems(self): + for k, v in self._data_.iteritems(): + yield (k, protect(v)) + + def deepcopy(self): + return copy.deepcopy(self._data_) + + def __deepcopy__(self, memo): + return copy.deepcopy(self._data_, memo) + + + +class ProtectedList(collections.Sequence): + """ + A class allowing read-only 'view' of a list or dict. + The object can be treated like a normal list, but will never modify the original list it points to. + Any values accessed from the list will also be read-only. + + Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work. + However, doing this causes tuple(obj) to return unprotected results (importantly, this means + unpacking into function arguments will also fail) + """ + def __init__(self, data): + self._data_ = data + #self.__mro__ = (ProtectedList, object) + + ## List of methods to directly wrap from _data_ + wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index'] + + ## List of methods which wrap from _data_ but return protected results + protectMethods = ['__getitem__', '__getslice__', '__mul__', '__reversed__', '__rmul__'] + + ## List of methods to disable + disableMethods = ['__delitem__', '__delslice__', '__iadd__', '__imul__', '__setitem__', '__setslice__', 'append', 'extend', 'insert', 'pop', 'remove', 'reverse', 'sort'] + + + ## Template methods + def wrapMethod(methodName): + return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) + + def protectMethod(methodName): + return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) + + def error(self, *args, **kargs): + raise Exception("Can not modify read-only list.") + + + ## Directly (and explicitly) wrap some methods from _data_ + ## Many of these methods can not be intercepted using __getattribute__, so they + ## must be implemented explicitly + for methodName in wrapMethods: + locals()[methodName] = wrapMethod(methodName) + + ## Wrap some methods from _data_ with the results converted to protected objects + for methodName in protectMethods: + locals()[methodName] = protectMethod(methodName) + + ## Disable any methods that could change data in the list + for methodName in disableMethods: + locals()[methodName] = error + + + ## Add a few extra methods. + def __iter__(self): + for item in self._data_: + yield protect(item) + + + def __add__(self, op): + if isinstance(op, ProtectedList): + return protect(self._data_.__add__(op._data_)) + elif isinstance(op, list): + return protect(self._data_.__add__(op)) + else: + raise TypeError("Argument must be a list.") + + def __radd__(self, op): + if isinstance(op, ProtectedList): + return protect(op._data_.__add__(self._data_)) + elif isinstance(op, list): + return protect(op.__add__(self._data_)) + else: + raise TypeError("Argument must be a list.") + + def deepcopy(self): + return copy.deepcopy(self._data_) + + def __deepcopy__(self, memo): + return copy.deepcopy(self._data_, memo) + + def poop(self): + raise Exception("This is a list. It does not poop.") + + +class ProtectedTuple(collections.Sequence): + """ + A class allowing read-only 'view' of a tuple. + The object can be treated like a normal tuple, but its contents will be returned as protected objects. + + Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work. + However, doing this causes tuple(obj) to return unprotected results (importantly, this means + unpacking into function arguments will also fail) + """ + def __init__(self, data): + self._data_ = data + + ## List of methods to directly wrap from _data_ + wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__getnewargs__', '__gt__', '__hash__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index'] + + ## List of methods which wrap from _data_ but return protected results + protectMethods = ['__getitem__', '__getslice__', '__iter__', '__add__', '__mul__', '__reversed__', '__rmul__'] + + + ## Template methods + def wrapMethod(methodName): + return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) + + def protectMethod(methodName): + return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) + + + ## Directly (and explicitly) wrap some methods from _data_ + ## Many of these methods can not be intercepted using __getattribute__, so they + ## must be implemented explicitly + for methodName in wrapMethods: + locals()[methodName] = wrapMethod(methodName) + + ## Wrap some methods from _data_ with the results converted to protected objects + for methodName in protectMethods: + locals()[methodName] = protectMethod(methodName) + + + ## Add a few extra methods. + def deepcopy(self): + return copy.deepcopy(self._data_) + + def __deepcopy__(self, memo): + return copy.deepcopy(self._data_, memo) + + + +def protect(obj): + if isinstance(obj, dict): + return ProtectedDict(obj) + elif isinstance(obj, list): + return ProtectedList(obj) + elif isinstance(obj, tuple): + return ProtectedTuple(obj) + else: + return obj + + +if __name__ == '__main__': + d = {'x': 1, 'y': [1,2], 'z': ({'a': 2, 'b': [3,4], 'c': (5,6)}, 1, 2)} + dp = protect(d) + + l = [1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2}] + lp = protect(l) + + t = (1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2}) + tp = protect(t) \ No newline at end of file diff --git a/setup.py b/setup.py index fdf426cb..c0406ab9 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,10 @@ from distutils.core import setup +import os + +## generate list of all sub-packages +subdirs = [['pyqtgraph'] + i[0].split(os.path.sep)[1:] for i in os.walk('.') if '__init__.py' in i[2]] +subdirs = filter(lambda p: len(p) == 1 or p[1] != 'build', subdirs) +all_packages = ['.'.join(p) for p in subdirs] setup(name='pyqtgraph', version='', @@ -8,24 +14,8 @@ setup(name='pyqtgraph', author='Luke Campagnola', author_email='luke.campagnola@gmail.com', url='', - packages=['pyqtgraph', 'pyqtgraph.console', 'pyqtgraph.graphicsItems', 'pyqtgraph.widgets', 'pyqtgraph.metaarray', 'pyqtgraph.parametertree', 'pyqtgraph.flowchart', 'pyqtgraph.imageview', 'pyqtgraph.dockarea', 'pyqtgraph.examples', 'pyqtgraph.canvas', 'pyqtgraph.exporters', 'pyqtgraph.GraphicsScene', 'pyqtgraph.multiprocess', 'pyqtgraph.opengl'], + packages=all_packages, package_dir = {'pyqtgraph': '.'}, package_data={'pyqtgraph': ['graphicsItems/PlotItem/*.png']}, ) - - - -#Package: python-pyqtgraph -#Version: 196 -#Section: custom -#Priority: optional -#Architecture: all -#Essential: no -#Installed-Size: 4652 -#Maintainer: Luke Campagnola -#Homepage: http://luke.campagnola.me/code/pyqtgraph -#Depends: python (>= 2.7), python-qt4 | python-pyside, python-scipy -#Suggests: python-opengl, python-qt4-gl -#Description: Scientific Graphics and GUI Library for Python - #PyQtGraph is a pure-python graphics and GUI library built on PyQt4 and numpy. It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display. diff --git a/widgets/DataTreeWidget.py b/widgets/DataTreeWidget.py index 9a04d1d2..a6b5cac8 100644 --- a/widgets/DataTreeWidget.py +++ b/widgets/DataTreeWidget.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from pyqtgraph.Qt import QtGui, QtCore -from collections import OrderedDict +from pyqtgraph.pgcollections import OrderedDict import types, traceback import numpy as np diff --git a/widgets/GradientWidget.py b/widgets/GradientWidget.py index 35c6852d..ecf3f585 100644 --- a/widgets/GradientWidget.py +++ b/widgets/GradientWidget.py @@ -4,7 +4,6 @@ from .GraphicsView import GraphicsView from pyqtgraph.graphicsItems.GradientEditorItem import GradientEditorItem import weakref import numpy as np -import collections __all__ = ['TickSlider', 'GradientWidget', 'BlackWhiteSlider'] @@ -53,525 +52,3 @@ class GradientWidget(GraphicsView): return getattr(self.item, attr) - -#Gradients = collections.OrderedDict([ - #('thermal', {'ticks': [(0.3333, (185, 0, 0, 255)), (0.6666, (255, 220, 0, 255)), (1, (255, 255, 255, 255)), (0, (0, 0, 0, 255))], 'mode': 'rgb'}), - #('flame', {'ticks': [(0.2, (7, 0, 220, 255)), (0.5, (236, 0, 134, 255)), (0.8, (246, 246, 0, 255)), (1.0, (255, 255, 255, 255)), (0.0, (0, 0, 0, 255))], 'mode': 'rgb'}), - #('yellowy', {'ticks': [(0.0, (0, 0, 0, 255)), (0.2328863796753704, (32, 0, 129, 255)), (0.8362738179251941, (255, 255, 0, 255)), (0.5257586450247, (115, 15, 255, 255)), (1.0, (255, 255, 255, 255))], 'mode': 'rgb'} ), - #('bipolar', {'ticks': [(0.0, (0, 255, 255, 255)), (1.0, (255, 255, 0, 255)), (0.5, (0, 0, 0, 255)), (0.25, (0, 0, 255, 255)), (0.75, (255, 0, 0, 255))], 'mode': 'rgb'}), - #('spectrum', {'ticks': [(1.0, (255, 0, 255, 255)), (0.0, (255, 0, 0, 255))], 'mode': 'hsv'}), - #('cyclic', {'ticks': [(0.0, (255, 0, 4, 255)), (1.0, (255, 0, 0, 255))], 'mode': 'hsv'}), - #('greyclip', {'ticks': [(0.0, (0, 0, 0, 255)), (0.99, (255, 255, 255, 255)), (1.0, (255, 0, 0, 255))], 'mode': 'rgb'}), -#]) - - -#class TickSlider(GraphicsView): - #def __init__(self, parent=None, orientation='bottom', allowAdd=True, **kargs): - #self.orientation = orientation - #self.length = 100 - #self.tickSize = 15 - #self.ticks = {} - #self.maxDim = 20 - #GraphicsView.__init__(self, parent, useOpenGL=False) - #self.allowAdd = allowAdd - ##self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - ##self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - ##self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor) - ##self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter) - #self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing) - #self.orientations = { - #'left': (270, 1, -1), - #'right': (270, 1, 1), - #'top': (0, 1, -1), - #'bottom': (0, 1, 1) - #} - - ##self.scene = QtGui.QGraphicsScene() - ##self.setScene(self.scene) - - #self.setOrientation(orientation) - #self.setFrameStyle(QtGui.QFrame.NoFrame | QtGui.QFrame.Plain) - #self.setBackgroundRole(QtGui.QPalette.NoRole) - #self.setMouseTracking(True) - - - #def keyPressEvent(self, ev): - #ev.ignore() - - #def setMaxDim(self, mx=None): - #if mx is None: - #mx = self.maxDim - #else: - #self.maxDim = mx - - #if self.orientation in ['bottom', 'top']: - #self.setFixedHeight(mx) - #self.setMaximumWidth(16777215) - #else: - #self.setFixedWidth(mx) - #self.setMaximumHeight(16777215) - - #def setOrientation(self, ort): - #self.orientation = ort - #self.resetTransform() - #self.rotate(self.orientations[ort][0]) - #self.scale(*self.orientations[ort][1:]) - #self.setMaxDim() - - #def addTick(self, x, color=None, movable=True): - #if color is None: - #color = QtGui.QColor(255,255,255) - #tick = Tick(self, [x*self.length, 0], color, movable, self.tickSize) - #self.ticks[tick] = x - #self.scene.addItem(tick) - #return tick - - #def removeTick(self, tick): - #del self.ticks[tick] - #self.scene.removeItem(tick) - - #def tickMoved(self, tick, pos): - ##print "tick changed" - ### Correct position of tick if it has left bounds. - #newX = min(max(0, pos.x()), self.length) - #pos.setX(newX) - #tick.setPos(pos) - #self.ticks[tick] = float(newX) / self.length - - #def tickClicked(self, tick, ev): - #if ev.button() == QtCore.Qt.RightButton: - #self.removeTick(tick) - - #def widgetLength(self): - #if self.orientation in ['bottom', 'top']: - #return self.width() - #else: - #return self.height() - - #def resizeEvent(self, ev): - #wlen = max(40, self.widgetLength()) - #self.setLength(wlen-self.tickSize) - #bounds = self.scene().itemsBoundingRect() - #bounds.setLeft(min(-self.tickSize*0.5, bounds.left())) - #bounds.setRight(max(self.length + self.tickSize, bounds.right())) - ##bounds.setTop(min(bounds.top(), self.tickSize)) - ##bounds.setBottom(max(0, bounds.bottom())) - #self.setSceneRect(bounds) - #self.fitInView(bounds, QtCore.Qt.KeepAspectRatio) - - #def setLength(self, newLen): - #for t, x in self.ticks.items(): - #t.setPos(x * newLen, t.pos().y()) - #self.length = float(newLen) - - #def mousePressEvent(self, ev): - #QtGui.QGraphicsView.mousePressEvent(self, ev) - #self.ignoreRelease = False - #for i in self.items(ev.pos()): - #if isinstance(i, Tick): - #self.ignoreRelease = True - #break - ##if len(self.items(ev.pos())) > 0: ## Let items handle their own clicks - ##self.ignoreRelease = True - - #def mouseReleaseEvent(self, ev): - #QtGui.QGraphicsView.mouseReleaseEvent(self, ev) - #if self.ignoreRelease: - #return - - #pos = self.mapToScene(ev.pos()) - - #if ev.button() == QtCore.Qt.LeftButton and self.allowAdd: - #if pos.x() < 0 or pos.x() > self.length: - #return - #if pos.y() < 0 or pos.y() > self.tickSize: - #return - #pos.setX(min(max(pos.x(), 0), self.length)) - #self.addTick(pos.x()/self.length) - #elif ev.button() == QtCore.Qt.RightButton: - #self.showMenu(ev) - - - #def showMenu(self, ev): - #pass - - #def setTickColor(self, tick, color): - #tick = self.getTick(tick) - #tick.color = color - #tick.setBrush(QtGui.QBrush(QtGui.QColor(tick.color))) - - #def setTickValue(self, tick, val): - #tick = self.getTick(tick) - #val = min(max(0.0, val), 1.0) - #x = val * self.length - #pos = tick.pos() - #pos.setX(x) - #tick.setPos(pos) - #self.ticks[tick] = val - - #def tickValue(self, tick): - #tick = self.getTick(tick) - #return self.ticks[tick] - - #def getTick(self, tick): - #if type(tick) is int: - #tick = self.listTicks()[tick][0] - #return tick - - #def mouseMoveEvent(self, ev): - #QtGui.QGraphicsView.mouseMoveEvent(self, ev) - ##print ev.pos(), ev.buttons() - - #def listTicks(self): - #ticks = self.ticks.items() - #ticks.sort(lambda a,b: cmp(a[1], b[1])) - #return ticks - - -#class GradientWidget(TickSlider): - - #sigGradientChanged = QtCore.Signal(object) - - #def __init__(self, *args, **kargs): - #self.currentTick = None - #self.currentTickColor = None - #self.rectSize = 15 - #self.gradRect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, -self.rectSize, 100, self.rectSize)) - #self.backgroundRect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, -self.rectSize, 100, self.rectSize)) - #self.backgroundRect.setBrush(QtGui.QBrush(QtCore.Qt.DiagCrossPattern)) - #self.colorMode = 'rgb' - #TickSlider.__init__(self, *args, **kargs) - #self.colorDialog = QtGui.QColorDialog() - #self.colorDialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True) - #self.colorDialog.setOption(QtGui.QColorDialog.DontUseNativeDialog, True) - - #self.colorDialog.currentColorChanged.connect(self.currentColorChanged) - #self.colorDialog.rejected.connect(self.currentColorRejected) - - ##self.gradient = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(100,0)) - #self.scene.addItem(self.backgroundRect) - #self.scene.addItem(self.gradRect) - - #self.setMaxDim(self.rectSize + self.tickSize) - - ##self.btn = QtGui.QPushButton('RGB') - ##self.btnProxy = self.scene.addWidget(self.btn) - ##self.btnProxy.setFlag(self.btnProxy.ItemIgnoresTransformations) - ##self.btnProxy.scale(0.7, 0.7) - ##self.btnProxy.translate(-self.btnProxy.sceneBoundingRect().width()+self.tickSize/2., 0) - ##if self.orientation == 'bottom': - ##self.btnProxy.translate(0, -self.rectSize) - #self.rgbAction = QtGui.QAction('RGB', self) - #self.rgbAction.setCheckable(True) - #self.rgbAction.triggered.connect(lambda: self.setColorMode('rgb')) - #self.hsvAction = QtGui.QAction('HSV', self) - #self.hsvAction.setCheckable(True) - #self.hsvAction.triggered.connect(lambda: self.setColorMode('hsv')) - - #self.menu = QtGui.QMenu() - - ### build context menu of gradients - #global Gradients - #for g in Gradients: - #px = QtGui.QPixmap(100, 15) - #p = QtGui.QPainter(px) - #self.restoreState(Gradients[g]) - #grad = self.getGradient() - #brush = QtGui.QBrush(grad) - #p.fillRect(QtCore.QRect(0, 0, 100, 15), brush) - #p.end() - #label = QtGui.QLabel() - #label.setPixmap(px) - #label.setContentsMargins(1, 1, 1, 1) - #act = QtGui.QWidgetAction(self) - #act.setDefaultWidget(label) - #act.triggered.connect(self.contextMenuClicked) - #act.name = g - #self.menu.addAction(act) - - #self.menu.addSeparator() - #self.menu.addAction(self.rgbAction) - #self.menu.addAction(self.hsvAction) - - - #for t in self.ticks.keys(): - #self.removeTick(t) - #self.addTick(0, QtGui.QColor(0,0,0), True) - #self.addTick(1, QtGui.QColor(255,0,0), True) - #self.setColorMode('rgb') - #self.updateGradient() - - #def showMenu(self, ev): - #self.menu.popup(ev.globalPos()) - - #def contextMenuClicked(self, b): - #global Gradients - #act = self.sender() - #self.restoreState(Gradients[act.name]) - - #def setColorMode(self, cm): - #if cm not in ['rgb', 'hsv']: - #raise Exception("Unknown color mode %s. Options are 'rgb' and 'hsv'." % str(cm)) - - #try: - #self.rgbAction.blockSignals(True) - #self.hsvAction.blockSignals(True) - #self.rgbAction.setChecked(cm == 'rgb') - #self.hsvAction.setChecked(cm == 'hsv') - #finally: - #self.rgbAction.blockSignals(False) - #self.hsvAction.blockSignals(False) - #self.colorMode = cm - #self.updateGradient() - - #def updateGradient(self): - #self.gradient = self.getGradient() - #self.gradRect.setBrush(QtGui.QBrush(self.gradient)) - #self.sigGradientChanged.emit(self) - - #def setLength(self, newLen): - #TickSlider.setLength(self, newLen) - #self.backgroundRect.setRect(0, -self.rectSize, newLen, self.rectSize) - #self.gradRect.setRect(0, -self.rectSize, newLen, self.rectSize) - #self.updateGradient() - - #def currentColorChanged(self, color): - #if color.isValid() and self.currentTick is not None: - #self.setTickColor(self.currentTick, color) - #self.updateGradient() - - #def currentColorRejected(self): - #self.setTickColor(self.currentTick, self.currentTickColor) - #self.updateGradient() - - #def tickClicked(self, tick, ev): - #if ev.button() == QtCore.Qt.LeftButton: - #if not tick.colorChangeAllowed: - #return - #self.currentTick = tick - #self.currentTickColor = tick.color - #self.colorDialog.setCurrentColor(tick.color) - #self.colorDialog.open() - ##color = QtGui.QColorDialog.getColor(tick.color, self, "Select Color", QtGui.QColorDialog.ShowAlphaChannel) - ##if color.isValid(): - ##self.setTickColor(tick, color) - ##self.updateGradient() - #elif ev.button() == QtCore.Qt.RightButton: - #if not tick.removeAllowed: - #return - #if len(self.ticks) > 2: - #self.removeTick(tick) - #self.updateGradient() - - #def tickMoved(self, tick, pos): - #TickSlider.tickMoved(self, tick, pos) - #self.updateGradient() - - - #def getGradient(self): - #g = QtGui.QLinearGradient(QtCore.QPointF(0,0), QtCore.QPointF(self.length,0)) - #if self.colorMode == 'rgb': - #ticks = self.listTicks() - #g.setStops([(x, QtGui.QColor(t.color)) for t,x in ticks]) - #elif self.colorMode == 'hsv': ## HSV mode is approximated for display by interpolating 10 points between each stop - #ticks = self.listTicks() - #stops = [] - #stops.append((ticks[0][1], ticks[0][0].color)) - #for i in range(1,len(ticks)): - #x1 = ticks[i-1][1] - #x2 = ticks[i][1] - #dx = (x2-x1) / 10. - #for j in range(1,10): - #x = x1 + dx*j - #stops.append((x, self.getColor(x))) - #stops.append((x2, self.getColor(x2))) - #g.setStops(stops) - #return g - - #def getColor(self, x, toQColor=True): - #ticks = self.listTicks() - #if x <= ticks[0][1]: - #c = ticks[0][0].color - #if toQColor: - #return QtGui.QColor(c) # always copy colors before handing them out - #else: - #return (c.red(), c.green(), c.blue(), c.alpha()) - #if x >= ticks[-1][1]: - #c = ticks[-1][0].color - #if toQColor: - #return QtGui.QColor(c) # always copy colors before handing them out - #else: - #return (c.red(), c.green(), c.blue(), c.alpha()) - - #x2 = ticks[0][1] - #for i in range(1,len(ticks)): - #x1 = x2 - #x2 = ticks[i][1] - #if x1 <= x and x2 >= x: - #break - - #dx = (x2-x1) - #if dx == 0: - #f = 0. - #else: - #f = (x-x1) / dx - #c1 = ticks[i-1][0].color - #c2 = ticks[i][0].color - #if self.colorMode == 'rgb': - #r = c1.red() * (1.-f) + c2.red() * f - #g = c1.green() * (1.-f) + c2.green() * f - #b = c1.blue() * (1.-f) + c2.blue() * f - #a = c1.alpha() * (1.-f) + c2.alpha() * f - #if toQColor: - #return QtGui.QColor(r, g, b,a) - #else: - #return (r,g,b,a) - #elif self.colorMode == 'hsv': - #h1,s1,v1,_ = c1.getHsv() - #h2,s2,v2,_ = c2.getHsv() - #h = h1 * (1.-f) + h2 * f - #s = s1 * (1.-f) + s2 * f - #v = v1 * (1.-f) + v2 * f - #c = QtGui.QColor() - #c.setHsv(h,s,v) - #if toQColor: - #return c - #else: - #return (c.red(), c.green(), c.blue(), c.alpha()) - - #def getLookupTable(self, nPts, alpha=True): - #"""Return an RGB/A lookup table.""" - #if alpha: - #table = np.empty((nPts,4), dtype=np.ubyte) - #else: - #table = np.empty((nPts,3), dtype=np.ubyte) - - #for i in range(nPts): - #x = float(i)/(nPts-1) - #color = self.getColor(x, toQColor=False) - #table[i] = color[:table.shape[1]] - - #return table - - - - #def mouseReleaseEvent(self, ev): - #TickSlider.mouseReleaseEvent(self, ev) - #self.updateGradient() - - #def addTick(self, x, color=None, movable=True): - #if color is None: - #color = self.getColor(x) - #t = TickSlider.addTick(self, x, color=color, movable=movable) - #t.colorChangeAllowed = True - #t.removeAllowed = True - #return t - - #def saveState(self): - #ticks = [] - #for t in self.ticks: - #c = t.color - #ticks.append((self.ticks[t], (c.red(), c.green(), c.blue(), c.alpha()))) - #state = {'mode': self.colorMode, 'ticks': ticks} - #return state - - #def restoreState(self, state): - #self.setColorMode(state['mode']) - #for t in self.ticks.keys(): - #self.removeTick(t) - #for t in state['ticks']: - #c = QtGui.QColor(*t[1]) - #self.addTick(t[0], c) - #self.updateGradient() - - - -#class BlackWhiteSlider(GradientWidget): - #def __init__(self, parent): - #GradientWidget.__init__(self, parent) - #self.getTick(0).colorChangeAllowed = False - #self.getTick(1).colorChangeAllowed = False - #self.allowAdd = False - #self.setTickColor(self.getTick(1), QtGui.QColor(255,255,255)) - #self.setOrientation('right') - - #def getLevels(self): - #return (self.tickValue(0), self.tickValue(1)) - - #def setLevels(self, black, white): - #self.setTickValue(0, black) - #self.setTickValue(1, white) - - - - -#class GammaWidget(TickSlider): - #pass - - -#class Tick(QtGui.QGraphicsPolygonItem): - #def __init__(self, view, pos, color, movable=True, scale=10): - ##QObjectWorkaround.__init__(self) - #self.movable = movable - #self.view = weakref.ref(view) - #self.scale = scale - #self.color = color - ##self.endTick = endTick - #self.pg = QtGui.QPolygonF([QtCore.QPointF(0,0), QtCore.QPointF(-scale/3**0.5,scale), QtCore.QPointF(scale/3**0.5,scale)]) - #QtGui.QGraphicsPolygonItem.__init__(self, self.pg) - #self.setPos(pos[0], pos[1]) - #self.setFlags(QtGui.QGraphicsItem.ItemIsMovable | QtGui.QGraphicsItem.ItemIsSelectable) - #self.setBrush(QtGui.QBrush(QtGui.QColor(self.color))) - #if self.movable: - #self.setZValue(1) - #else: - #self.setZValue(0) - - ##def x(self): - ##return self.pos().x()/100. - - #def mouseMoveEvent(self, ev): - ##print self, "move", ev.scenePos() - #if not self.movable: - #return - #if not ev.buttons() & QtCore.Qt.LeftButton: - #return - - - #newPos = ev.scenePos() + self.mouseOffset - #newPos.setY(self.pos().y()) - ##newPos.setX(min(max(newPos.x(), 0), 100)) - #self.setPos(newPos) - #self.view().tickMoved(self, newPos) - #self.movedSincePress = True - ##self.emit(QtCore.SIGNAL('tickChanged'), self) - #ev.accept() - - #def mousePressEvent(self, ev): - #self.movedSincePress = False - #if ev.button() == QtCore.Qt.LeftButton: - #ev.accept() - #self.mouseOffset = self.pos() - ev.scenePos() - #self.pressPos = ev.scenePos() - #elif ev.button() == QtCore.Qt.RightButton: - #ev.accept() - ##if self.endTick: - ##return - ##self.view.tickChanged(self, delete=True) - - #def mouseReleaseEvent(self, ev): - ##print self, "release", ev.scenePos() - #if not self.movedSincePress: - #self.view().tickClicked(self, ev) - - ##if ev.button() == QtCore.Qt.LeftButton and ev.scenePos() == self.pressPos: - ##color = QtGui.QColorDialog.getColor(self.color, None, "Select Color", QtGui.QColorDialog.ShowAlphaChannel) - ##if color.isValid(): - ##self.color = color - ##self.setBrush(QtGui.QBrush(QtGui.QColor(self.color))) - ###self.emit(QtCore.SIGNAL('tickChanged'), self) - ##self.view.tickChanged(self) - - - - diff --git a/widgets/GraphicsView.py b/widgets/GraphicsView.py index 6f7da132..f773d929 100644 --- a/widgets/GraphicsView.py +++ b/widgets/GraphicsView.py @@ -120,6 +120,14 @@ class GraphicsView(QtGui.QGraphicsView): self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False) self.clickAccepted = False + def setAntialiasing(self, aa): + """Enable or disable default antialiasing. + Note that this will only affect items that do not specify their own antialiasing options.""" + if aa: + self.setRenderHints(self.renderHints() | QtGui.QPainter.Antialiasing) + else: + self.setRenderHints(self.renderHints() & ~QtGui.QPainter.Antialiasing) + def setBackground(self, background): """ Set the background color of the GraphicsView.