Commit 0f97ac77 authored by Luke Campagnola's avatar Luke Campagnola
Browse files

merge from dev

parents e6a017a8 9fa590d0
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.python2_3 import sortList
#try:
#from PyQt4 import QtOpenGL
#HAVE_OPENGL = True
......@@ -505,18 +506,19 @@ class GraphicsScene(QtGui.QGraphicsScene):
menusToAdd = []
while item is not self:
item = item.parentItem()
if item is None:
item = self
if not hasattr(item, "getContextMenus"):
continue
subMenus = item.getContextMenus(event)
if subMenus is None:
continue
if type(subMenus) is not list: ## so that some items (like FlowchartViewBox) can return multiple menus
subMenus = [subMenus]
for sm in subMenus:
menusToAdd.append(sm)
......
## Do all Qt imports from here to allow easier PyQt / PySide compatibility
#from PySide import QtGui, QtCore, QtOpenGL, QtSvg
from PyQt4 import QtGui, QtCore
try:
from PyQt4 import QtSvg
except ImportError:
pass
try:
from PyQt4 import QtOpenGL
except ImportError:
pass
USE_PYSIDE = False ## If False, import PyQt4. If True, import PySide
## Note that when switching between PyQt and PySide, all template
## files (*.ui) must be rebuilt for the target library.
if USE_PYSIDE:
from PySide import QtGui, QtCore, QtOpenGL, QtSvg
import PySide
VERSION_INFO = 'PySide ' + PySide.__version__
else:
from PyQt4 import QtGui, QtCore
try:
from PyQt4 import QtSvg
except ImportError:
pass
try:
from PyQt4 import QtOpenGL
except ImportError:
pass
if not hasattr(QtCore, 'Signal'):
QtCore.Signal = QtCore.pyqtSignal
VERSION_INFO = 'PyQt4 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR
......@@ -76,7 +76,7 @@ class SRTTransform(QtGui.QTransform):
m = pg.SRTTransform3D(m)
angle, axis = m.getRotation()
if angle != 0 and (axis[0] != 0 or axis[1] != 0 or axis[2] != 1):
print angle, axis
print("angle: %s axis: %s" % (str(angle), str(axis)))
raise Exception("Can only convert 4x4 matrix to 3x3 if rotation is around Z-axis.")
self._state = {
'pos': Point(m.getTranslation()),
......
# -*- coding: utf-8 -*-
from Qt import QtCore, QtGui
from Vector import Vector
from SRTTransform import SRTTransform
from .Qt import QtCore, QtGui
from .Vector import Vector
from .SRTTransform import SRTTransform
import pyqtgraph as pg
import numpy as np
import scipy.linalg
......@@ -136,15 +136,15 @@ class SRTTransform3D(QtGui.QMatrix4x4):
try:
evals, evecs = scipy.linalg.eig(r)
except:
print "Rotation matrix:", r
print "Scale:", scale
print "Original matrix:", m
print("Rotation matrix: %s" % str(r))
print("Scale: %s" % str(scale))
print("Original matrix: %s" % str(m))
raise
eigIndex = np.argwhere(np.abs(evals-1) < 1e-7)
if len(eigIndex) < 1:
print "eigenvalues:", evals
print "eigenvectors:", evecs
print "index:", eigIndex, evals-1
print("eigenvalues: %s" % str(evals))
print("eigenvectors: %s" % str(evecs))
print("index: %s, %s" % (str(eigIndex), str(evals-1)))
raise Exception("Could not determine rotation axis.")
axis = evecs[eigIndex[0,0]].real
axis /= ((axis**2).sum())**0.5
......@@ -259,23 +259,23 @@ if __name__ == '__main__':
tr3 = QtGui.QTransform()
tr3.translate(20, 0)
tr3.rotate(45)
print "QTransform -> Transform:", SRTTransform(tr3)
print("QTransform -> Transform: %s" % str(SRTTransform(tr3)))
print "tr1:", tr1
print("tr1: %s" % str(tr1))
tr2.translate(20, 0)
tr2.rotate(45)
print "tr2:", tr2
print("tr2: %s" % str(tr2))
dt = tr2/tr1
print "tr2 / tr1 = ", dt
print("tr2 / tr1 = %s" % str(dt))
print "tr2 * tr1 = ", tr2*tr1
print("tr2 * tr1 = %s" % str(tr2*tr1))
tr4 = SRTTransform()
tr4.scale(-1, 1)
tr4.rotate(30)
print "tr1 * tr4 = ", tr1*tr4
print("tr1 * tr4 = %s" % str(tr1*tr4))
w1 = widgets.TestROI((19,19), (22, 22), invertible=True)
#w2 = widgets.TestROI((0,0), (150, 150))
......
......@@ -5,7 +5,7 @@ Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from Qt import QtGui, QtCore
from .Qt import QtGui, QtCore
import numpy as np
class Vector(QtGui.QVector3D):
......
......@@ -10,6 +10,7 @@ of a large group of widgets.
from .Qt import QtCore, QtGui
import weakref, inspect
from .python2_3 import asUnicode
__all__ = ['WidgetGroup']
......
# -*- coding: utf-8 -*-
REVISION = None
### import all the goodies and add some helper functions for easy CLI use
## 'Qt' is a local module; it is intended mainly to cover up the differences
## between PyQt4 and PySide.
from .Qt import QtGui
from .Qt import QtGui
## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)
#if QtGui.QApplication.instance() is None:
#app = QtGui.QApplication([])
import sys
import os, sys
## check python version
if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] != 7):
## 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]))
## helpers for 2/3 compatibility
......@@ -30,13 +33,15 @@ else:
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
CONFIG_OPTIONS = {
'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
'leftButtonPan': True, ## if false, left button drags a rubber band for zooming in viewbox
'foregroundColor': (200,200,200),
'backgroundColor': (0,0,0),
'foreground': (150, 150, 150), ## default foreground color for axes, labels, etc.
'background': (0, 0, 0), ## default background for GraphicsWidget
'antialias': False,
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
}
def setConfigOption(opt, value):
CONFIG_OPTIONS[opt] = value
......@@ -44,6 +49,23 @@ def getConfigOption(opt):
return CONFIG_OPTIONS[opt]
def systemInfo():
print("sys.platform: %s" % sys.platform)
print("sys.version: %s" % sys.version)
from .Qt import VERSION_INFO
print("qt bindings: %s" % VERSION_INFO)
global REVISION
if REVISION is None: ## this code was probably checked out from bzr; look up the last-revision file
lastRevFile = os.path.join(os.path.dirname(__file__), '.bzr', 'branch', 'last-revision')
if os.path.exists(lastRevFile):
REVISION = open(lastRevFile, 'r').read().strip()
print("pyqtgraph: %s" % REVISION)
print("config:")
import pprint
pprint.pprint(CONFIG_OPTIONS)
## Rename orphaned .pyc files. This is *probably* safe :)
def renamePyc(startDir):
......@@ -105,7 +127,7 @@ def importAll(path, excludes=()):
globals()[k] = getattr(mod, k)
importAll('graphicsItems')
importAll('widgets', excludes=['MatplotlibWidget'])
importAll('widgets', excludes=['MatplotlibWidget', 'RemoteGraphicsView'])
from .imageview import *
from .WidgetGroup import *
......
......@@ -13,6 +13,7 @@ import re, os, sys
from collections import OrderedDict
GLOBAL_PATH = None # so not thread safe.
from . import units
from .python2_3 import asUnicode
class ParseError(Exception):
def __init__(self, message, lineNum, line, fileName=None):
......
from PyQt4 import QtCore, QtGui
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.python2_3 import asUnicode
class CmdInput(QtGui.QLineEdit):
......@@ -25,10 +26,10 @@ class CmdInput(QtGui.QLineEdit):
self.execCmd()
else:
QtGui.QLineEdit.keyPressEvent(self, ev)
self.history[0] = unicode(self.text())
self.history[0] = asUnicode(self.text())
def execCmd(self):
cmd = unicode(self.text())
cmd = asUnicode(self.text())
if len(self.history) == 1 or cmd != self.history[1]:
self.history.insert(1, cmd)
#self.lastCmd = cmd
......
from pyqtgraph.Qt import QtCore, QtGui
import sys, re, os, time, traceback
import sys, re, os, time, traceback, subprocess
import pyqtgraph as pg
import template
from . import template
import pyqtgraph.exceptionHandling as exceptionHandling
import pickle
EDITOR = "pykate {fileName}:{lineNum}"
class ConsoleWidget(QtGui.QWidget):
"""
Widget displaying console output and accepting command input.
......@@ -24,7 +22,7 @@ class ConsoleWidget(QtGui.QWidget):
be baffling and frustrating to users since it would appear the program has frozen.
- some terminals (eg windows cmd.exe) have notoriously unfriendly interfaces
- ability to add extra features like exception stack introspection
- ability to have multiple interactive prompts for remotely generated processes
- ability to have multiple interactive prompts, including for spawned sub-processes
"""
def __init__(self, parent=None, namespace=None, historyFile=None, text=None, editor=None):
......@@ -72,8 +70,8 @@ class ConsoleWidget(QtGui.QWidget):
self.ui.historyList.itemDoubleClicked.connect(self.cmdDblClicked)
self.ui.exceptionBtn.toggled.connect(self.ui.exceptionGroup.setVisible)
self.ui.catchAllExceptionsBtn.toggled.connect(self.catchAllToggled)
self.ui.catchNextExceptionBtn.toggled.connect(self.catchNextToggled)
self.ui.catchAllExceptionsBtn.toggled.connect(self.catchAllExceptions)
self.ui.catchNextExceptionBtn.toggled.connect(self.catchNextException)
self.ui.clearExceptionBtn.clicked.connect(self.clearExceptionClicked)
self.ui.exceptionStackList.itemClicked.connect(self.stackItemClicked)
self.ui.exceptionStackList.itemDoubleClicked.connect(self.stackItemDblClicked)
......@@ -229,15 +227,25 @@ class ConsoleWidget(QtGui.QWidget):
def flush(self):
pass
def catchAllToggled(self, b):
if b:
def catchAllExceptions(self, catch=True):
"""
If True, the console will catch all unhandled exceptions and display the stack
trace. Each exception caught clears the last.
"""
self.ui.catchAllExceptionsBtn.setChecked(catch)
if catch:
self.ui.catchNextExceptionBtn.setChecked(False)
exceptionHandling.register(self.allExceptionsHandler)
else:
exceptionHandling.unregister(self.allExceptionsHandler)
def catchNextToggled(self, b):
if b:
def catchNextException(self, catch=True):
"""
If True, the console will catch the next unhandled exception and display the stack
trace.
"""
self.ui.catchNextExceptionBtn.setChecked(catch)
if catch:
self.ui.catchAllExceptionsBtn.setChecked(False)
exceptionHandling.register(self.nextExceptionHandler)
else:
......@@ -254,11 +262,15 @@ class ConsoleWidget(QtGui.QWidget):
pass
def stackItemDblClicked(self, item):
global EDITOR
editor = self.editor
if editor is None:
editor = pg.getConfigOption('editorCommand')
if editor is None:
return
tb = self.currentFrame()
lineNum = tb.tb_lineno
fileName = tb.tb_frame.f_code.co_filename
os.system(EDITOR.format(fileName=fileName, lineNum=lineNum))
subprocess.Popen(self.editor.format(fileName=fileName, lineNum=lineNum), shell=True)
def allExceptionsHandler(self, *args):
......
from Console import ConsoleWidget
\ No newline at end of file
from .Console import ConsoleWidget
\ No newline at end of file
......@@ -91,7 +91,7 @@ class Ui_Form(object):
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
Form.setWindowTitle(QtGui.QApplication.translate("Console", "Console", None, QtGui.QApplication.UnicodeUTF8))
self.historyBtn.setText(QtGui.QApplication.translate("Form", "History..", None, QtGui.QApplication.UnicodeUTF8))
self.exceptionBtn.setText(QtGui.QApplication.translate("Form", "Exceptions..", None, QtGui.QApplication.UnicodeUTF8))
self.exceptionGroup.setTitle(QtGui.QApplication.translate("Form", "Exception Handling", None, QtGui.QApplication.UnicodeUTF8))
......@@ -101,4 +101,4 @@ class Ui_Form(object):
self.runSelectedFrameCheck.setText(QtGui.QApplication.translate("Form", "Run commands in selected stack frame", None, QtGui.QApplication.UnicodeUTF8))
self.exceptionInfoLabel.setText(QtGui.QApplication.translate("Form", "Exception Info", None, QtGui.QApplication.UnicodeUTF8))
from CmdInput import CmdInput
from .CmdInput import CmdInput
......@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
<string>Console</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
......@@ -153,7 +153,7 @@
<customwidget>
<class>CmdInput</class>
<extends>QLineEdit</extends>
<header>CmdInput</header>
<header>.CmdInput</header>
</customwidget>
</customwidgets>
<resources/>
......
......@@ -2,6 +2,7 @@
from pyqtgraph.Qt import QtCore, QtGui
from .Container import *
from .DockDrop import *
from .Dock import Dock
import pyqtgraph.debug as debug
import weakref
......
import sys
## Make sure pyqtgraph is importable
p = os.path.dirname(os.path.abspath(__file__))
p = os.path.join(p, '..', '..')
sys.path.insert(0, p)
from pyqtgraph.Qt import QtCore, QtGui
from .DockArea import *
from .Dock import *
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.resize(800,800)
from .Dock import Dock
d1 = Dock("Dock1", size=(200,200))
d2 = Dock("Dock2", size=(100,100))
d3 = Dock("Dock3", size=(1,1))
d4 = Dock("Dock4", size=(50,50))
d5 = Dock("Dock5", size=(100,100))
d6 = Dock("Dock6", size=(300,300))
area.addDock(d1, 'left')
area.addDock(d2, 'right')
area.addDock(d3, 'bottom')
area.addDock(d4, 'right')
area.addDock(d5, 'left', d1)
area.addDock(d6, 'top', d4)
area.moveDock(d6, 'above', d4)
d3.hideTitleBar()
print("===build complete====")
for d in [d1, d2, d3, d4, d5]:
w = QtGui.QWidget()
l = QtGui.QVBoxLayout()
w.setLayout(l)
btns = []
for i in range(4):
btns.append(QtGui.QPushButton("%s Button %d"%(d.name(), i)))
l.addWidget(btns[-1])
d.w = (w, l, btns)
d.addWidget(w)
import pyqtgraph as pg
p = pg.PlotWidget()
d6.addWidget(p)
print("===widgets added===")
#s = area.saveState()
#print "\n\n-------restore----------\n\n"
#area.restoreState(s)
s = None
def save():
global s
s = area.saveState()
def load():
global s
area.restoreState(s)
#d6.container().setCurrentIndex(0)
#d2.label.setTabPos(40)
#win2 = QtGui.QMainWindow()
#area2 = DockArea()
#win2.setCentralWidget(area2)
#win2.resize(800,800)
win.show()
#win2.show()
......@@ -18,6 +18,7 @@ import sys, os
# documentation root, use os.path.abspath to make it absolute, like shown here.
path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(path, '..', '..', '..'))
print sys.path
# -- General configuration -----------------------------------------------------
......
......@@ -43,5 +43,12 @@ While I consider this approach somewhat lazy, it is often the case that 'lazy' i
Embedding widgets inside PyQt applications
------------------------------------------
For the serious application developer, all of the functionality in pyqtgraph is available via widgets that can be embedded just like any other Qt widgets. Most importantly, see: :class:`PlotWidget <pyqtgraph.PlotWidget>`, :class:`ImageView <pyqtgraph.ImageView>`, :class:`GraphicsLayoutWidget <pyqtgraph.GraphicsLayoutWidget>`, and :class:`GraphicsView <pyqtgraph.GraphicsView>`. Pyqtgraph's widgets can be included in Designer's ui files via the "Promote To..." functionality.
For the serious application developer, all of the functionality in pyqtgraph is available via :ref:`widgets <api_widgets>` that can be embedded just like any other Qt widgets. Most importantly, see: :class:`PlotWidget <pyqtgraph.PlotWidget>`, :class:`ImageView <pyqtgraph.ImageView>`, :class:`GraphicsLayoutWidget <pyqtgraph.GraphicsLayoutWidget>`, and :class:`GraphicsView <pyqtgraph.GraphicsView>`. Pyqtgraph's widgets can be included in Designer's ui files via the "Promote To..." functionality:
#. In Designer, create a QGraphicsView widget ("Graphics View" under the "Display Widgets" category).
#. Right-click on the QGraphicsView and select "Promote To...".
#. Under "Promoted class name", enter the class name you wish to use ("PlotWidget", "GraphicsLayoutWidget", etc).
#. Under "Header file", enter "pyqtgraph".
#. Click "Add", then click "Promote".
See the designer documentation for more information on promoting widgets.
Line, Fill, and Color
=====================
Many functions and methods in pyqtgraph accept arguments specifying the line style (pen), fill style (brush), or color.
Qt relies on its QColor, QPen and QBrush classes for specifying line and fill styles for all of its drawing.
Internally, pyqtgraph uses the same system but also allows many shorthand methods of specifying
the same style options.
For these function arguments, the following values may be used:
Many functions and methods in pyqtgraph accept arguments specifying the line style (pen), fill style (brush), or color.
For most of these function arguments, the following values may be used:
* single-character string representing color (b, g, r, c, m, y, k, w)
* (r, g, b) or (r, g, b, a) tuple
* single greyscale value (0.0 - 1.0)
* (index, maximum) tuple for automatically iterating through colors (see functions.intColor)
* (index, maximum) tuple for automatically iterating through colors (see :func:`intColor <pyqtgraph.intColor>`)
* QColor
* QPen / QBrush where appropriate
Notably, more complex pens and brushes can be easily built using the mkPen() / mkBrush() functions or with Qt's QPen and QBrush classes::
Notably, more complex pens and brushes can be easily built using the
:func:`mkPen() <pyqtgraph.mkPen>` / :func:`mkBrush() <pyqtgraph.mkBrush>` functions or with Qt's QPen and QBrush classes::
mkPen('y', width=3, style=QtCore.Qt.DashLine) ## Make a dashed yellow line 2px wide
mkPen(0.5) ## solid grey line 1px wide
mkPen(color=(200, 200, 255), style=QtCore.Qt.DotLine) ## Dotted pale-blue line
See the Qt documentation for 'QPen' and 'PenStyle' for more options.
Colors can also be built using mkColor(), intColor(), hsvColor(), or Qt's QColor class
See the Qt documentation for 'QPen' and 'PenStyle' for more line-style options and 'QBrush' for more fill options.
Colors can also be built using :func:`mkColor() <pyqtgraph.mkColor>`,
:func:`intColor() <pyqtgraph.intColor>`, :func:`hsvColor() <pyqtgraph.hsvColor>`, or Qt's QColor class.
Default Background and Foreground Colors
----------------------------------------
By default, pyqtgraph uses a black background for its plots and grey for axes, text, and plot lines.
These defaults can be changed using pyqtgraph.setConfigOption()::
import pyqtgraph as pg
## Switch to using white background and black foreground
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
## The following plot has inverted colors
pg.plot([1,4,2,3,5])
(Note that this must be set *before* creating any widgets)
.. _api_widgets:
Pyqtgraph's Widgets
===================
......
......@@ -4,41 +4,73 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import user
import numpy as np
app = QtGui.QApplication([])
view = pg.GraphicsView()
l = pg.GraphicsLayout(border=pg.mkPen(0, 0, 255))
l = pg.GraphicsLayout(border=(100,100,100))
view.setCentralItem(l)
view.show()
view.resize(800,600)
## Title at top
text = """
This example demonstrates the use of GraphicsLayout to arrange items in a grid.<br>
The items added to the layout must be subclasses of QGraphicsWidget (this includes <br>
PlotItem, ViewBox, LabelItem, and GrphicsLayout itself).
"""
l.addLabel(text, col=1, colspan=4)
l.nextRow()
## Put vertical label on left side
l.addLabel('Long Vertical Label', angle=-90, rowspan=3)
## Add 3 plots into the first row (automatic position)
p1 = l.addPlot()
p2 = l.addPlot()
p3 = l.addPlot()
p1 = l.addPlot(title="Plot 1")
p2 = l.addPlot(title="Plot 2")
vb = l.addViewBox(lockAspect=True)
img = pg.ImageItem(np.random.normal(size=(100,100)))
vb.addItem(img)
vb.autoRange()
## Add a viewbox into the second row (automatic position)
## Add a sub-layout into the second row (automatic position)
## The added item should avoid the first column, which is already filled
l.nextRow()
vb = l.addViewBox(colspan=3)
l2 = l.addLayout(colspan=3, border=(50,0,0))
l2.setContentsMargins(10, 10, 10, 10)
l2.addLabel("Sub-layout: this layout demonstrates the use of shared axes and axis labels", colspan=3)
l2.nextRow()
l2.addLabel('Vertical Axis Label', angle=-90, rowspan=2)
p21 = l2.addPlot()
p22 = l2.addPlot()
l2.nextRow()
p23 = l2.addPlot()
p24 = l2.addPlot()
l2.nextRow()
l2.addLabel("HorizontalAxisLabel",