Bugfixes and updates to functions.py:
- generalized makeARGB API: can now process arrays of arbitrary shape. - affineSlice automatically converts vector arguments to array - new function applyLookupTable taken from makeARGB - isosurface function returns array Updated VideoSpeedTest example to follow new makeARGB API LayoutWidget: row argument now accepts 'next' as value ParameterTree bugfix: avoid infinite recursion when accessing non-existent attributes ViewBox: avoid exit error caused when cleanup callback is invoked while python is shutting down
This commit is contained in:
parent
a5a40be8bb
commit
e5f383fbb5
@ -61,37 +61,41 @@ ui.alphaCheck.toggled.connect(updateLUT)
|
|||||||
def updateScale():
|
def updateScale():
|
||||||
global ui
|
global ui
|
||||||
spins = [ui.minSpin1, ui.maxSpin1, ui.minSpin2, ui.maxSpin2, ui.minSpin3, ui.maxSpin3]
|
spins = [ui.minSpin1, ui.maxSpin1, ui.minSpin2, ui.maxSpin2, ui.minSpin3, ui.maxSpin3]
|
||||||
if ui.rgbCheck.isChecked():
|
if ui.rgbLevelsCheck.isChecked():
|
||||||
for s in spins[2:]:
|
for s in spins[2:]:
|
||||||
s.setEnabled(True)
|
s.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
for s in spins[2:]:
|
for s in spins[2:]:
|
||||||
s.setEnabled(False)
|
s.setEnabled(False)
|
||||||
ui.rgbCheck.toggled.connect(updateScale)
|
ui.rgbLevelsCheck.toggled.connect(updateScale)
|
||||||
|
|
||||||
cache = {}
|
cache = {}
|
||||||
def mkData():
|
def mkData():
|
||||||
global data, cache, ui
|
global data, cache, ui
|
||||||
dtype = ui.dtypeCombo.currentText()
|
dtype = (ui.dtypeCombo.currentText(), ui.rgbCheck.isChecked())
|
||||||
if dtype not in cache:
|
if dtype not in cache:
|
||||||
if dtype == 'uint8':
|
if dtype[0] == 'uint8':
|
||||||
dt = np.uint8
|
dt = np.uint8
|
||||||
loc = 128
|
loc = 128
|
||||||
scale = 64
|
scale = 64
|
||||||
mx = 255
|
mx = 255
|
||||||
elif dtype == 'uint16':
|
elif dtype[0] == 'uint16':
|
||||||
dt = np.uint16
|
dt = np.uint16
|
||||||
loc = 4096
|
loc = 4096
|
||||||
scale = 1024
|
scale = 1024
|
||||||
mx = 2**16
|
mx = 2**16
|
||||||
elif dtype == 'float':
|
elif dtype[0] == 'float':
|
||||||
dt = np.float
|
dt = np.float
|
||||||
loc = 1.0
|
loc = 1.0
|
||||||
scale = 0.1
|
scale = 0.1
|
||||||
|
|
||||||
data = np.random.normal(size=(20,512,512), loc=loc, scale=scale)
|
if ui.rgbCheck.isChecked():
|
||||||
data = ndi.gaussian_filter(data, (0, 3, 3))
|
data = np.random.normal(size=(20,512,512,3), loc=loc, scale=scale)
|
||||||
if dtype != 'float':
|
data = ndi.gaussian_filter(data, (0, 6, 6, 0))
|
||||||
|
else:
|
||||||
|
data = np.random.normal(size=(20,512,512), loc=loc, scale=scale)
|
||||||
|
data = ndi.gaussian_filter(data, (0, 6, 6))
|
||||||
|
if dtype[0] != 'float':
|
||||||
data = np.clip(data, 0, mx)
|
data = np.clip(data, 0, mx)
|
||||||
data = data.astype(dt)
|
data = data.astype(dt)
|
||||||
cache[dtype] = data
|
cache[dtype] = data
|
||||||
@ -100,7 +104,7 @@ def mkData():
|
|||||||
updateLUT()
|
updateLUT()
|
||||||
mkData()
|
mkData()
|
||||||
ui.dtypeCombo.currentIndexChanged.connect(mkData)
|
ui.dtypeCombo.currentIndexChanged.connect(mkData)
|
||||||
|
ui.rgbCheck.toggled.connect(mkData)
|
||||||
|
|
||||||
ptr = 0
|
ptr = 0
|
||||||
lastTime = ptime.time()
|
lastTime = ptime.time()
|
||||||
@ -113,7 +117,7 @@ def update():
|
|||||||
useLut = None
|
useLut = None
|
||||||
|
|
||||||
if ui.scaleCheck.isChecked():
|
if ui.scaleCheck.isChecked():
|
||||||
if ui.rgbCheck.isChecked():
|
if ui.rgbLevelsCheck.isChecked():
|
||||||
useScale = [
|
useScale = [
|
||||||
[ui.minSpin1.value(), ui.maxSpin1.value()],
|
[ui.minSpin1.value(), ui.maxSpin1.value()],
|
||||||
[ui.minSpin2.value(), ui.maxSpin2.value()],
|
[ui.minSpin2.value(), ui.maxSpin2.value()],
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<zorder>fpsLabel</zorder>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
@ -84,7 +83,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="rgbCheck">
|
<widget class="QCheckBox" name="rgbLevelsCheck">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>RGB</string>
|
<string>RGB</string>
|
||||||
</property>
|
</property>
|
||||||
@ -218,6 +217,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="rgbCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>RGB</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Sep 9 14:41:31 2012
|
# Created: Sun Nov 4 18:24:20 2012
|
||||||
# by: PyQt4 UI code generator 4.9.1
|
# by: PyQt4 UI code generator 4.9.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@ -55,9 +55,9 @@ class Ui_MainWindow(object):
|
|||||||
self.scaleCheck = QtGui.QCheckBox(self.centralwidget)
|
self.scaleCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
self.scaleCheck.setObjectName(_fromUtf8("scaleCheck"))
|
self.scaleCheck.setObjectName(_fromUtf8("scaleCheck"))
|
||||||
self.gridLayout_2.addWidget(self.scaleCheck, 3, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.scaleCheck, 3, 0, 1, 1)
|
||||||
self.rgbCheck = QtGui.QCheckBox(self.centralwidget)
|
self.rgbLevelsCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
self.rgbCheck.setObjectName(_fromUtf8("rgbCheck"))
|
self.rgbLevelsCheck.setObjectName(_fromUtf8("rgbLevelsCheck"))
|
||||||
self.gridLayout_2.addWidget(self.rgbCheck, 3, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.rgbLevelsCheck, 3, 1, 1, 1)
|
||||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||||
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
||||||
self.minSpin1 = SpinBox(self.centralwidget)
|
self.minSpin1 = SpinBox(self.centralwidget)
|
||||||
@ -124,6 +124,9 @@ class Ui_MainWindow(object):
|
|||||||
self.fpsLabel.setAlignment(QtCore.Qt.AlignCenter)
|
self.fpsLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.fpsLabel.setObjectName(_fromUtf8("fpsLabel"))
|
self.fpsLabel.setObjectName(_fromUtf8("fpsLabel"))
|
||||||
self.gridLayout_2.addWidget(self.fpsLabel, 0, 0, 1, 4)
|
self.gridLayout_2.addWidget(self.fpsLabel, 0, 0, 1, 4)
|
||||||
|
self.rgbCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
|
self.rgbCheck.setObjectName(_fromUtf8("rgbCheck"))
|
||||||
|
self.gridLayout_2.addWidget(self.rgbCheck, 2, 1, 1, 1)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
@ -138,12 +141,13 @@ class Ui_MainWindow(object):
|
|||||||
self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8))
|
self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.dtypeCombo.setItemText(2, QtGui.QApplication.translate("MainWindow", "float", None, QtGui.QApplication.UnicodeUTF8))
|
self.dtypeCombo.setItemText(2, QtGui.QApplication.translate("MainWindow", "float", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.scaleCheck.setText(QtGui.QApplication.translate("MainWindow", "Scale Data", None, QtGui.QApplication.UnicodeUTF8))
|
self.scaleCheck.setText(QtGui.QApplication.translate("MainWindow", "Scale Data", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
self.rgbLevelsCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_2.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_2.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_3.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_3.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_4.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_4.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.lutCheck.setText(QtGui.QApplication.translate("MainWindow", "Use Lookup Table", None, QtGui.QApplication.UnicodeUTF8))
|
self.lutCheck.setText(QtGui.QApplication.translate("MainWindow", "Use Lookup Table", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.alphaCheck.setText(QtGui.QApplication.translate("MainWindow", "alpha", None, QtGui.QApplication.UnicodeUTF8))
|
self.alphaCheck.setText(QtGui.QApplication.translate("MainWindow", "alpha", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8))
|
self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget
|
from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Sep 9 14:41:31 2012
|
# Created: Sun Nov 4 18:24:21 2012
|
||||||
# by: pyside-uic 0.2.13 running on PySide 1.1.0
|
# by: pyside-uic 0.2.13 running on PySide 1.1.0
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@ -50,9 +50,9 @@ class Ui_MainWindow(object):
|
|||||||
self.scaleCheck = QtGui.QCheckBox(self.centralwidget)
|
self.scaleCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
self.scaleCheck.setObjectName("scaleCheck")
|
self.scaleCheck.setObjectName("scaleCheck")
|
||||||
self.gridLayout_2.addWidget(self.scaleCheck, 3, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.scaleCheck, 3, 0, 1, 1)
|
||||||
self.rgbCheck = QtGui.QCheckBox(self.centralwidget)
|
self.rgbLevelsCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
self.rgbCheck.setObjectName("rgbCheck")
|
self.rgbLevelsCheck.setObjectName("rgbLevelsCheck")
|
||||||
self.gridLayout_2.addWidget(self.rgbCheck, 3, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.rgbLevelsCheck, 3, 1, 1, 1)
|
||||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
self.minSpin1 = SpinBox(self.centralwidget)
|
self.minSpin1 = SpinBox(self.centralwidget)
|
||||||
@ -119,6 +119,9 @@ class Ui_MainWindow(object):
|
|||||||
self.fpsLabel.setAlignment(QtCore.Qt.AlignCenter)
|
self.fpsLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.fpsLabel.setObjectName("fpsLabel")
|
self.fpsLabel.setObjectName("fpsLabel")
|
||||||
self.gridLayout_2.addWidget(self.fpsLabel, 0, 0, 1, 4)
|
self.gridLayout_2.addWidget(self.fpsLabel, 0, 0, 1, 4)
|
||||||
|
self.rgbCheck = QtGui.QCheckBox(self.centralwidget)
|
||||||
|
self.rgbCheck.setObjectName("rgbCheck")
|
||||||
|
self.gridLayout_2.addWidget(self.rgbCheck, 2, 1, 1, 1)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
@ -133,12 +136,13 @@ class Ui_MainWindow(object):
|
|||||||
self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8))
|
self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.dtypeCombo.setItemText(2, QtGui.QApplication.translate("MainWindow", "float", None, QtGui.QApplication.UnicodeUTF8))
|
self.dtypeCombo.setItemText(2, QtGui.QApplication.translate("MainWindow", "float", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.scaleCheck.setText(QtGui.QApplication.translate("MainWindow", "Scale Data", None, QtGui.QApplication.UnicodeUTF8))
|
self.scaleCheck.setText(QtGui.QApplication.translate("MainWindow", "Scale Data", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
self.rgbLevelsCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_2.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_2.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_3.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_3.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_4.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_4.setText(QtGui.QApplication.translate("MainWindow", "<--->", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.lutCheck.setText(QtGui.QApplication.translate("MainWindow", "Use Lookup Table", None, QtGui.QApplication.UnicodeUTF8))
|
self.lutCheck.setText(QtGui.QApplication.translate("MainWindow", "Use Lookup Table", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.alphaCheck.setText(QtGui.QApplication.translate("MainWindow", "alpha", None, QtGui.QApplication.UnicodeUTF8))
|
self.alphaCheck.setText(QtGui.QApplication.translate("MainWindow", "alpha", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8))
|
self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget
|
from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget
|
||||||
|
365
functions.py
365
functions.py
@ -434,8 +434,10 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
|
|||||||
|
|
||||||
|
|
||||||
## make sure vectors are arrays
|
## make sure vectors are arrays
|
||||||
vectors = np.array(vectors)
|
if not isinstance(vectors, np.ndarray):
|
||||||
origin = np.array(origin)
|
vectors = np.array(vectors)
|
||||||
|
if not isinstance(origin, np.ndarray):
|
||||||
|
origin = np.array(origin)
|
||||||
origin.shape = (len(axes),) + (1,)*len(shape)
|
origin.shape = (len(axes),) + (1,)*len(shape)
|
||||||
|
|
||||||
## Build array of sample locations.
|
## Build array of sample locations.
|
||||||
@ -580,6 +582,114 @@ def solveBilinearTransform(points1, points2):
|
|||||||
|
|
||||||
return matrix
|
return matrix
|
||||||
|
|
||||||
|
def rescaleData(data, scale, offset, dtype=None):
|
||||||
|
"""Return data rescaled and optionally cast to a new dtype::
|
||||||
|
|
||||||
|
data => (data-offset) * scale
|
||||||
|
|
||||||
|
Uses scipy.weave (if available) to improve performance.
|
||||||
|
"""
|
||||||
|
global USE_WEAVE
|
||||||
|
if dtype is None:
|
||||||
|
dtype = data.dtype
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not USE_WEAVE:
|
||||||
|
raise Exception('Weave is disabled; falling back to slower version.')
|
||||||
|
|
||||||
|
newData = np.empty((data.size,), dtype=dtype)
|
||||||
|
flat = np.ascontiguousarray(data).reshape(data.size)
|
||||||
|
size = data.size
|
||||||
|
|
||||||
|
code = """
|
||||||
|
double sc = (double)scale;
|
||||||
|
double off = (double)offset;
|
||||||
|
for( int i=0; i<size; i++ ) {
|
||||||
|
newData[i] = ((double)flat[i] - off) * sc;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
scipy.weave.inline(code, ['flat', 'newData', 'size', 'offset', 'scale'], compiler='gcc')
|
||||||
|
data = newData.reshape(data.shape)
|
||||||
|
except:
|
||||||
|
if USE_WEAVE:
|
||||||
|
debug.printExc("Error; disabling weave.")
|
||||||
|
USE_WEAVE = False
|
||||||
|
|
||||||
|
#p = np.poly1d([scale, -offset*scale])
|
||||||
|
#data = p(data).astype(dtype)
|
||||||
|
d2 = data-offset
|
||||||
|
d2 *= scale
|
||||||
|
data = d2.astype(dtype)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def applyLookupTable(data, lut):
|
||||||
|
"""
|
||||||
|
Uses values in *data* as indexes to select values from *lut*.
|
||||||
|
The returned data has shape data.shape + lut.shape[1:]
|
||||||
|
|
||||||
|
Uses scipy.weave to improve performance if it is available.
|
||||||
|
Note: color gradient lookup tables can be generated using GradientWidget.
|
||||||
|
"""
|
||||||
|
global USE_WEAVE
|
||||||
|
|
||||||
|
if data.dtype.kind not in ('i', 'u'):
|
||||||
|
data = data.astype(int)
|
||||||
|
|
||||||
|
## using np.take appears to be faster than even the scipy.weave method and takes care of clipping as well.
|
||||||
|
return np.take(lut, data, axis=0, mode='clip')
|
||||||
|
|
||||||
|
### old methods:
|
||||||
|
#data = np.clip(data, 0, lut.shape[0]-1)
|
||||||
|
|
||||||
|
#try:
|
||||||
|
#if not USE_WEAVE:
|
||||||
|
#raise Exception('Weave is disabled; falling back to slower version.')
|
||||||
|
|
||||||
|
### number of values to copy for each LUT lookup
|
||||||
|
#if lut.ndim == 1:
|
||||||
|
#ncol = 1
|
||||||
|
#else:
|
||||||
|
#ncol = sum(lut.shape[1:])
|
||||||
|
|
||||||
|
### output array
|
||||||
|
#newData = np.empty((data.size, ncol), dtype=lut.dtype)
|
||||||
|
|
||||||
|
### flattened input arrays
|
||||||
|
#flatData = data.flatten()
|
||||||
|
#flatLut = lut.reshape((lut.shape[0], ncol))
|
||||||
|
|
||||||
|
#dataSize = data.size
|
||||||
|
|
||||||
|
### strides for accessing each item
|
||||||
|
#newStride = newData.strides[0] / newData.dtype.itemsize
|
||||||
|
#lutStride = flatLut.strides[0] / flatLut.dtype.itemsize
|
||||||
|
#dataStride = flatData.strides[0] / flatData.dtype.itemsize
|
||||||
|
|
||||||
|
### strides for accessing individual values within a single LUT lookup
|
||||||
|
#newColStride = newData.strides[1] / newData.dtype.itemsize
|
||||||
|
#lutColStride = flatLut.strides[1] / flatLut.dtype.itemsize
|
||||||
|
|
||||||
|
#code = """
|
||||||
|
|
||||||
|
#for( int i=0; i<dataSize; i++ ) {
|
||||||
|
#for( int j=0; j<ncol; j++ ) {
|
||||||
|
#newData[i*newStride + j*newColStride] = flatLut[flatData[i*dataStride]*lutStride + j*lutColStride];
|
||||||
|
#}
|
||||||
|
#}
|
||||||
|
#"""
|
||||||
|
#scipy.weave.inline(code, ['flatData', 'flatLut', 'newData', 'dataSize', 'ncol', 'newStride', 'lutStride', 'dataStride', 'newColStride', 'lutColStride'])
|
||||||
|
#newData = newData.reshape(data.shape + lut.shape[1:])
|
||||||
|
##if np.any(newData != lut[data]):
|
||||||
|
##print "mismatch!"
|
||||||
|
|
||||||
|
#data = newData
|
||||||
|
#except:
|
||||||
|
#if USE_WEAVE:
|
||||||
|
#debug.printExc("Error; disabling weave.")
|
||||||
|
#USE_WEAVE = False
|
||||||
|
#data = lut[data]
|
||||||
|
|
||||||
|
#return data
|
||||||
|
|
||||||
|
|
||||||
def makeRGBA(*args, **kwds):
|
def makeRGBA(*args, **kwds):
|
||||||
@ -587,164 +697,132 @@ def makeRGBA(*args, **kwds):
|
|||||||
kwds['useRGBA'] = True
|
kwds['useRGBA'] = True
|
||||||
return makeARGB(*args, **kwds)
|
return makeARGB(*args, **kwds)
|
||||||
|
|
||||||
def makeARGB(data, lut=None, levels=None, useRGBA=False):
|
def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
|
||||||
"""
|
"""
|
||||||
Convert a 2D or 3D array into an ARGB array suitable for building QImages
|
Convert an array of values into an ARGB array suitable for building QImages, OpenGL textures, etc.
|
||||||
Will optionally do scaling and/or table lookups to determine final colors.
|
|
||||||
|
|
||||||
Returns the ARGB array (values 0-255) and a boolean indicating whether there is alpha channel data.
|
Returns the ARGB array (values 0-255) and a boolean indicating whether there is alpha channel data.
|
||||||
|
This is a two stage process:
|
||||||
|
|
||||||
|
1) Rescale the data based on the values in the *levels* argument (min, max).
|
||||||
|
2) Determine the final output by passing the rescaled values through a lookup table.
|
||||||
|
|
||||||
|
Both stages are optional.
|
||||||
|
|
||||||
|
============ ==================================================================================
|
||||||
Arguments:
|
Arguments:
|
||||||
data - 2D or 3D numpy array of int/float types
|
data numpy array of int/float types. If
|
||||||
|
levels List [min, max]; optionally rescale data before converting through the
|
||||||
|
lookup table. The data is rescaled such that min->0 and max->*scale*::
|
||||||
|
|
||||||
For 2D arrays (x, y):
|
rescaled = (clip(data, min, max) - min) * (*scale* / (max - min))
|
||||||
* The color will be determined using a lookup table (see argument 'lut').
|
|
||||||
* If levels are given, the data is rescaled and converted to int
|
|
||||||
before using the lookup table.
|
|
||||||
|
|
||||||
For 3D arrays (x, y, rgba):
|
It is also possible to use a 2D (N,2) array of values for levels. In this case,
|
||||||
* The third axis must have length 3 or 4 and will be interpreted as RGBA.
|
it is assumed that each pair of min,max values in the levels array should be
|
||||||
* The 'lut' argument is not allowed.
|
applied to a different subset of the input data (for example, the input data may
|
||||||
|
already have RGB values and the levels are used to independently scale each
|
||||||
|
channel). The use of this feature requires that levels.shape[0] == data.shape[-1].
|
||||||
|
scale The maximum value to which data will be rescaled before being passed through the
|
||||||
|
lookup table (or returned if there is no lookup table). By default this will
|
||||||
|
be set to the length of the lookup table, or 256 is no lookup table is provided.
|
||||||
|
For OpenGL color specifications (as in GLColor4f) use scale=1.0
|
||||||
|
lut Optional lookup table (array with dtype=ubyte).
|
||||||
|
Values in data will be converted to color by indexing directly from lut.
|
||||||
|
The output data shape will be input.shape + lut.shape[1:].
|
||||||
|
|
||||||
lut - Lookup table for 2D data. May be 1D or 2D (N,rgba) and must have dtype=ubyte.
|
Note: the output of makeARGB will have the same dtype as the lookup table, so
|
||||||
Values in data will be converted to color by indexing directly from lut.
|
for conversion to QImage, the dtype must be ubyte.
|
||||||
Lookup tables can be built using GradientWidget.
|
|
||||||
levels - List [min, max]; optionally rescale data before converting through the
|
|
||||||
lookup table. rescaled = (data-min) * len(lut) / (max-min)
|
|
||||||
useRGBA - If True, the data is returned in RGBA order (useful for building OpenGL textures). The default is
|
|
||||||
False, which returns in BGRA order for use with QImage.
|
|
||||||
|
|
||||||
|
Lookup tables can be built using GradientWidget.
|
||||||
|
useRGBA If True, the data is returned in RGBA order (useful for building OpenGL textures).
|
||||||
|
The default is False, which returns in ARGB order for use with QImage
|
||||||
|
(Note that 'ARGB' is a term used by the Qt documentation; the _actual_ order
|
||||||
|
is BGRA).
|
||||||
|
============ ==================================================================================
|
||||||
"""
|
"""
|
||||||
prof = debug.Profiler('functions.makeARGB', disabled=True)
|
prof = debug.Profiler('functions.makeARGB', disabled=True)
|
||||||
|
|
||||||
## sanity checks
|
if lut is not None and not isinstance(lut, np.ndarray):
|
||||||
if data.ndim == 3:
|
lut = np.array(lut)
|
||||||
if data.shape[2] not in (3,4):
|
if levels is not None and not isinstance(levels, np.ndarray):
|
||||||
raise Exception("data.shape[2] must be 3 or 4")
|
levels = np.array(levels)
|
||||||
#if lut is not None:
|
|
||||||
#raise Exception("can not use lookup table with 3D data")
|
## sanity checks
|
||||||
elif data.ndim != 2:
|
#if data.ndim == 3:
|
||||||
raise Exception("data must be 2D or 3D")
|
#if data.shape[2] not in (3,4):
|
||||||
|
#raise Exception("data.shape[2] must be 3 or 4")
|
||||||
|
##if lut is not None:
|
||||||
|
##raise Exception("can not use lookup table with 3D data")
|
||||||
|
#elif data.ndim != 2:
|
||||||
|
#raise Exception("data must be 2D or 3D")
|
||||||
|
|
||||||
|
#if lut is not None:
|
||||||
|
##if lut.ndim == 2:
|
||||||
|
##if lut.shape[1] :
|
||||||
|
##raise Exception("lut.shape[1] must be 3 or 4")
|
||||||
|
##elif lut.ndim != 1:
|
||||||
|
##raise Exception("lut must be 1D or 2D")
|
||||||
|
#if lut.dtype != np.ubyte:
|
||||||
|
#raise Exception('lookup table must have dtype=ubyte (got %s instead)' % str(lut.dtype))
|
||||||
|
|
||||||
if lut is not None:
|
|
||||||
if lut.ndim == 2:
|
|
||||||
if lut.shape[1] not in (3,4):
|
|
||||||
raise Exception("lut.shape[1] must be 3 or 4")
|
|
||||||
elif lut.ndim != 1:
|
|
||||||
raise Exception("lut must be 1D or 2D")
|
|
||||||
if lut.dtype != np.ubyte:
|
|
||||||
raise Exception('lookup table must have dtype=ubyte (got %s instead)' % str(lut.dtype))
|
|
||||||
|
|
||||||
if levels is not None:
|
if levels is not None:
|
||||||
levels = np.array(levels)
|
if levels.ndim == 1:
|
||||||
if levels.shape == (2,):
|
if len(levels) != 2:
|
||||||
pass
|
raise Exception('levels argument must have length 2')
|
||||||
elif levels.shape in [(3,2), (4,2)]:
|
elif levels.ndim == 2:
|
||||||
if data.ndim == 3:
|
if lut is not None and lut.ndim > 1:
|
||||||
raise Exception("Can not use 2D levels with 3D data.")
|
raise Exception('Cannot make ARGB data when bot levels and lut have ndim > 2')
|
||||||
if lut is not None:
|
if levels.shape != (data.shape[-1], 2):
|
||||||
raise Exception('Can not use 2D levels and lookup table together.')
|
raise Exception('levels must have shape (data.shape[-1], 2)')
|
||||||
else:
|
else:
|
||||||
raise Exception("Levels must have shape (2,) or (3,2) or (4,2)")
|
print levels
|
||||||
|
raise Exception("levels argument must be 1D or 2D.")
|
||||||
|
#levels = np.array(levels)
|
||||||
|
#if levels.shape == (2,):
|
||||||
|
#pass
|
||||||
|
#elif levels.shape in [(3,2), (4,2)]:
|
||||||
|
#if data.ndim == 3:
|
||||||
|
#raise Exception("Can not use 2D levels with 3D data.")
|
||||||
|
#if lut is not None:
|
||||||
|
#raise Exception('Can not use 2D levels and lookup table together.')
|
||||||
|
#else:
|
||||||
|
#raise Exception("Levels must have shape (2,) or (3,2) or (4,2)")
|
||||||
|
|
||||||
prof.mark('1')
|
prof.mark('1')
|
||||||
|
|
||||||
if lut is not None:
|
if scale is None:
|
||||||
lutLength = lut.shape[0]
|
if lut is not None:
|
||||||
else:
|
scale = lut.shape[0]
|
||||||
lutLength = 256
|
else:
|
||||||
|
scale = 255.
|
||||||
## weave requires contiguous arrays
|
|
||||||
global USE_WEAVE
|
|
||||||
if (levels is not None or lut is not None) and USE_WEAVE:
|
|
||||||
data = np.ascontiguousarray(data)
|
|
||||||
|
|
||||||
## Apply levels if given
|
## Apply levels if given
|
||||||
if levels is not None:
|
if levels is not None:
|
||||||
|
|
||||||
try: ## use weave to speed up scaling
|
if isinstance(levels, np.ndarray) and levels.ndim == 2:
|
||||||
if not USE_WEAVE:
|
## we are going to rescale each channel independently
|
||||||
raise Exception('Weave is disabled; falling back to slower version.')
|
if levels.shape[0] != data.shape[-1]:
|
||||||
if levels.ndim == 1:
|
raise Exception("When rescaling multi-channel data, there must be the same number of levels as channels (data.shape[-1] == levels.shape[0])")
|
||||||
scale = float(lutLength) / (levels[1]-levels[0])
|
newData = np.empty(data.shape, dtype=int)
|
||||||
offset = float(levels[0])
|
for i in range(data.shape[-1]):
|
||||||
data = rescaleData(data, scale, offset)
|
minVal, maxVal = levels[i]
|
||||||
else:
|
if minVal == maxVal:
|
||||||
if data.ndim == 2:
|
maxVal += 1e-16
|
||||||
newData = np.empty(data.shape+(levels.shape[0],), dtype=np.uint32)
|
newData[...,i] = rescaleData(data[...,i], scale/(maxVal-minVal), minVal, dtype=int)
|
||||||
for i in range(levels.shape[0]):
|
data = newData
|
||||||
scale = float(lutLength / (levels[i,1]-levels[i,0]))
|
else:
|
||||||
offset = float(levels[i,0])
|
minVal, maxVal = levels
|
||||||
newData[...,i] = rescaleData(data, scale, offset)
|
if minVal == maxVal:
|
||||||
elif data.ndim == 3:
|
maxVal += 1e-16
|
||||||
newData = np.empty(data.shape, dtype=np.uint32)
|
data = rescaleData(data, scale/(maxVal-minVal), minVal, dtype=int)
|
||||||
for i in range(data.shape[2]):
|
|
||||||
scale = float(lutLength / (levels[i,1]-levels[i,0]))
|
|
||||||
offset = float(levels[i,0])
|
|
||||||
#print scale, offset, data.shape, newData.shape, levels.shape
|
|
||||||
newData[...,i] = rescaleData(data[...,i], scale, offset)
|
|
||||||
data = newData
|
|
||||||
except:
|
|
||||||
if USE_WEAVE:
|
|
||||||
debug.printExc("Error; disabling weave.")
|
|
||||||
USE_WEAVE = False
|
|
||||||
|
|
||||||
if levels.ndim == 1:
|
|
||||||
if data.ndim == 2:
|
|
||||||
levels = levels[np.newaxis, np.newaxis, :]
|
|
||||||
else:
|
|
||||||
levels = levels[np.newaxis, np.newaxis, np.newaxis, :]
|
|
||||||
else:
|
|
||||||
levels = levels[np.newaxis, np.newaxis, ...]
|
|
||||||
if data.ndim == 2:
|
|
||||||
data = data[..., np.newaxis]
|
|
||||||
data = ((data-levels[...,0]) * lutLength) / (levels[...,1]-levels[...,0])
|
|
||||||
|
|
||||||
prof.mark('2')
|
prof.mark('2')
|
||||||
|
|
||||||
|
|
||||||
## apply LUT if given
|
## apply LUT if given
|
||||||
if lut is not None and data.ndim == 2:
|
if lut is not None:
|
||||||
|
data = applyLookupTable(data, lut)
|
||||||
if data.dtype.kind not in ('i', 'u'):
|
|
||||||
data = data.astype(int)
|
|
||||||
|
|
||||||
data = np.clip(data, 0, lutLength-1)
|
|
||||||
try:
|
|
||||||
if not USE_WEAVE:
|
|
||||||
raise Exception('Weave is disabled; falling back to slower version.')
|
|
||||||
|
|
||||||
newData = np.empty((data.size,) + lut.shape[1:], dtype=np.uint8)
|
|
||||||
flat = data.reshape(data.size)
|
|
||||||
size = data.size
|
|
||||||
ncol = lut.shape[1]
|
|
||||||
newStride = newData.strides[0]
|
|
||||||
newColStride = newData.strides[1]
|
|
||||||
lutStride = lut.strides[0]
|
|
||||||
lutColStride = lut.strides[1]
|
|
||||||
flatStride = flat.strides[0] / flat.dtype.itemsize
|
|
||||||
|
|
||||||
#print "newData:", newData.shape, newData.dtype
|
|
||||||
#print "flat:", flat.shape, flat.dtype, flat.min(), flat.max()
|
|
||||||
#print "lut:", lut.shape, lut.dtype
|
|
||||||
#print "size:", size, "ncols:", ncol
|
|
||||||
#print "strides:", newStride, newColStride, lutStride, lutColStride, flatStride
|
|
||||||
|
|
||||||
code = """
|
|
||||||
|
|
||||||
for( int i=0; i<size; i++ ) {
|
|
||||||
for( int j=0; j<ncol; j++ ) {
|
|
||||||
newData[i*newStride + j*newColStride] = lut[flat[i*flatStride]*lutStride + j*lutColStride];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
scipy.weave.inline(code, ['flat', 'lut', 'newData', 'size', 'ncol', 'newStride', 'lutStride', 'flatStride', 'newColStride', 'lutColStride'])
|
|
||||||
data = newData.reshape(data.shape + lut.shape[1:])
|
|
||||||
except:
|
|
||||||
if USE_WEAVE:
|
|
||||||
debug.printExc("Error; disabling weave.")
|
|
||||||
USE_WEAVE = False
|
|
||||||
data = lut[data]
|
|
||||||
else:
|
else:
|
||||||
if data.dtype is not np.ubyte:
|
if data.dtype is not np.ubyte:
|
||||||
data = np.clip(data, 0, 255).astype(np.ubyte)
|
data = np.clip(data, 0, 255).astype(np.ubyte)
|
||||||
@ -895,25 +973,6 @@ def imageToArray(img, copy=False, transpose=True):
|
|||||||
return arr
|
return arr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def rescaleData(data, scale, offset):
|
|
||||||
newData = np.empty((data.size,), dtype=np.int)
|
|
||||||
flat = data.reshape(data.size)
|
|
||||||
size = data.size
|
|
||||||
|
|
||||||
code = """
|
|
||||||
double sc = (double)scale;
|
|
||||||
double off = (double)offset;
|
|
||||||
for( int i=0; i<size; i++ ) {
|
|
||||||
newData[i] = (int)(((double)flat[i] - off) * sc);
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
scipy.weave.inline(code, ['flat', 'newData', 'size', 'offset', 'scale'], compiler='gcc')
|
|
||||||
data = newData.reshape(data.shape)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
#def isosurface(data, level):
|
#def isosurface(data, level):
|
||||||
#"""
|
#"""
|
||||||
#Generate isosurface from volumetric data using marching tetrahedra algorithm.
|
#Generate isosurface from volumetric data using marching tetrahedra algorithm.
|
||||||
@ -1090,7 +1149,7 @@ def isosurface(data, level):
|
|||||||
*data* 3D numpy array of scalar values
|
*data* 3D numpy array of scalar values
|
||||||
*level* The level at which to generate an isosurface
|
*level* The level at which to generate an isosurface
|
||||||
|
|
||||||
Returns a list of faces; each face is a list of three vertexes and each vertex is a tuple of three floats.
|
Returns an array of vertex coordinates (N, 3, 3);
|
||||||
|
|
||||||
This function is SLOW; plenty of room for optimization here.
|
This function is SLOW; plenty of room for optimization here.
|
||||||
"""
|
"""
|
||||||
@ -1457,7 +1516,7 @@ def isosurface(data, level):
|
|||||||
pts.append(p)
|
pts.append(p)
|
||||||
facets.append(pts)
|
facets.append(pts)
|
||||||
|
|
||||||
return facets
|
return np.array(facets)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,13 +166,15 @@ class GraphicsItem(object):
|
|||||||
## attempt to re-scale direction vector to fit within the precision of the coordinate system
|
## attempt to re-scale direction vector to fit within the precision of the coordinate system
|
||||||
if direction.x() == 0:
|
if direction.x() == 0:
|
||||||
r = abs(dt.m32())/(abs(dt.m12()) + abs(dt.m22()))
|
r = abs(dt.m32())/(abs(dt.m12()) + abs(dt.m22()))
|
||||||
|
#r = 1.0/(abs(dt.m12()) + abs(dt.m22()))
|
||||||
elif direction.y() == 0:
|
elif direction.y() == 0:
|
||||||
r = abs(dt.m31())/(abs(dt.m11()) + abs(dt.m21()))
|
r = abs(dt.m31())/(abs(dt.m11()) + abs(dt.m21()))
|
||||||
|
#r = 1.0/(abs(dt.m11()) + abs(dt.m21()))
|
||||||
else:
|
else:
|
||||||
r = ((abs(dt.m32())/(abs(dt.m12()) + abs(dt.m22()))) * (abs(dt.m31())/(abs(dt.m11()) + abs(dt.m21()))))**0.5
|
r = ((abs(dt.m32())/(abs(dt.m12()) + abs(dt.m22()))) * (abs(dt.m31())/(abs(dt.m11()) + abs(dt.m21()))))**0.5
|
||||||
direction = direction * r
|
directionr = direction * r
|
||||||
|
|
||||||
viewDir = Point(dt.map(direction) - dt.map(Point(0,0)))
|
viewDir = Point(dt.map(directionr) - dt.map(Point(0,0)))
|
||||||
if viewDir.manhattanLength() == 0:
|
if viewDir.manhattanLength() == 0:
|
||||||
return None, None ## pixel size cannot be represented on this scale
|
return None, None ## pixel size cannot be represented on this scale
|
||||||
|
|
||||||
@ -182,7 +184,7 @@ class GraphicsItem(object):
|
|||||||
normView = viewDir.norm() ## direction of one pixel orthogonal to line
|
normView = viewDir.norm() ## direction of one pixel orthogonal to line
|
||||||
normOrtho = orthoDir.norm()
|
normOrtho = orthoDir.norm()
|
||||||
except:
|
except:
|
||||||
raise Exception("Invalid direction %s" %direction)
|
raise Exception("Invalid direction %s" %directionr)
|
||||||
|
|
||||||
|
|
||||||
dti = fn.invertQTransform(dt)
|
dti = fn.invertQTransform(dt)
|
||||||
|
@ -166,7 +166,7 @@ class ViewBox(GraphicsWidget):
|
|||||||
ViewBox.NamedViews[name] = self
|
ViewBox.NamedViews[name] = self
|
||||||
ViewBox.updateAllViewLists()
|
ViewBox.updateAllViewLists()
|
||||||
sid = id(self)
|
sid = id(self)
|
||||||
self.destroyed.connect(lambda: ViewBox.forgetView(sid, name) if ViewBox is not None else None)
|
self.destroyed.connect(lambda: ViewBox.forgetView(sid, name) if (ViewBox is not None and 'sid' in locals() and 'name' in locals()) else None)
|
||||||
#self.destroyed.connect(self.unregister)
|
#self.destroyed.connect(self.unregister)
|
||||||
|
|
||||||
def unregister(self):
|
def unregister(self):
|
||||||
@ -1154,7 +1154,7 @@ class ViewBox(GraphicsWidget):
|
|||||||
if any(changed):
|
if any(changed):
|
||||||
self.sigRangeChanged.emit(self, self.state['viewRange'])
|
self.sigRangeChanged.emit(self, self.state['viewRange'])
|
||||||
|
|
||||||
self.sigTransformChanged.emit(self)
|
self.sigTransformChanged.emit(self) ## segfaults here: 1
|
||||||
|
|
||||||
def paint(self, p, opt, widget):
|
def paint(self, p, opt, widget):
|
||||||
if self.border is not None:
|
if self.border is not None:
|
||||||
|
@ -460,7 +460,10 @@ class Parameter(QtCore.QObject):
|
|||||||
self.childs.pop(self.childs.index(child))
|
self.childs.pop(self.childs.index(child))
|
||||||
child.parentChanged(None)
|
child.parentChanged(None)
|
||||||
self.sigChildRemoved.emit(self, child)
|
self.sigChildRemoved.emit(self, child)
|
||||||
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
|
try:
|
||||||
|
child.sigTreeStateChanged.disconnect(self.treeStateChanged)
|
||||||
|
except TypeError: ## already disconnected
|
||||||
|
pass
|
||||||
|
|
||||||
def clearChildren(self):
|
def clearChildren(self):
|
||||||
"""Remove all child parameters."""
|
"""Remove all child parameters."""
|
||||||
@ -550,6 +553,8 @@ class Parameter(QtCore.QObject):
|
|||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
## Leaving this undocumented because I might like to remove it in the future..
|
## Leaving this undocumented because I might like to remove it in the future..
|
||||||
#print type(self), attr
|
#print type(self), attr
|
||||||
|
if 'names' not in self.__dict__:
|
||||||
|
raise AttributeError(attr)
|
||||||
if attr in self.names:
|
if attr in self.names:
|
||||||
return self.param(attr)
|
return self.param(attr)
|
||||||
else:
|
else:
|
||||||
|
@ -56,8 +56,13 @@ class LayoutWidget(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
Add a widget to the layout and place it in the next available cell (or in the cell specified).
|
Add a widget to the layout and place it in the next available cell (or in the cell specified).
|
||||||
"""
|
"""
|
||||||
if row is None:
|
if row == 'next':
|
||||||
|
self.nextRow()
|
||||||
row = self.currentRow
|
row = self.currentRow
|
||||||
|
elif row is None:
|
||||||
|
row = self.currentRow
|
||||||
|
|
||||||
|
|
||||||
if col is None:
|
if col is None:
|
||||||
col = self.nextCol(colspan)
|
col = self.nextCol(colspan)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user