internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: pyqtgraph/pyqtgraph

Issue:
FileForwarder thread doesn't terminate on child process end
On windows, when the child process is ended, the FileForwarder's that are created to forward output don't detect the process is close, and spin, using up a core's worth of CPU, making it impossible to restart remote plot windows on an error.

To see this error:
```py3
import pyqtgraph.multiprocess as mp
proc = mp.QtProcess()
proc.join()
```

then, a CPU core is pinned at 100%.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/multiprocess/processes.py b/pyqtgraph/multiprocess/processes.py
--- a/pyqtgraph/multiprocess/processes.py
+++ b/pyqtgraph/multiprocess/processes.py
@@ -166,6 +166,14 @@ def join(self, timeout=10):
                     raise Exception('Timed out waiting for remote process to end.')
                 time.sleep(0.05)
         self.conn.close()
+
+        # Close remote polling threads, otherwise they will spin continuously
+        if hasattr(self, "_stdoutForwarder"):
+            self._stdoutForwarder.finish.set()
+            self._stderrForwarder.finish.set()
+            self._stdoutForwarder.join()
+            self._stderrForwarder.join()
+
         self.debugMsg('Child process exited. (%d)' % self.proc.returncode)
 
     def debugMsg(self, msg, *args):
@@ -473,23 +481,24 @@ def __init__(self, input, output, color):
         self.lock = threading.Lock()
         self.daemon = True
         self.color = color
+        self.finish = threading.Event()
         self.start()
 
     def run(self):
         if self.output == 'stdout' and self.color is not False:
-            while True:
+            while not self.finish.is_set():
                 line = self.input.readline()
                 with self.lock:
                     cprint.cout(self.color, line, -1)
         elif self.output == 'stderr' and self.color is not False:
-            while True:
+            while not self.finish.is_set():
                 line = self.input.readline()
                 with self.lock:
                     cprint.cerr(self.color, line, -1)
         else:
             if isinstance(self.output, str):
                 self.output = getattr(sys, self.output)
-            while True:
+            while not self.finish.is_set():
                 line = self.input.readline()
                 with self.lock:
-                    self.output.write(line)
+                    self.output.write(line.decode('utf8'))


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
 'str' doesn't support the buffer interface
testing on Windows Qt5.5.1/ PyQtgraph github of 20160102, I have the following error on the "3D Graphics / Image" test:

```
Using PyQt5 (default graphics system)
Traceback (most recent call last):
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 55, in addItem
    item.initializeGL()
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\pyqtgraph\opengl\items\GLImageItem.py", line 34, in initializ
eGL
    glEnable(GL_TEXTURE_2D)
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\OpenGL\platform\baseplatform.py", line 402, in __call__
    return self( *args, **named )
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\OpenGL\error.py", line 232, in glCheckError
    baseOperation = baseOperation,
OpenGL.error.GLError: GLError(
        err = 1280,
        description = b'\xe9num\xe9rant non valide',
        baseOperation = glEnable,
        cArguments = (GL_TEXTURE_2D,)
)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\pyqtgraph\examples\GLImageItem.py", line 41, in <module>
    w.addItem(v1)
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 57, in addItem
    self.checkOpenGLVersion('Error while adding item %s to GLViewWidget.' % str(
item))
  File "D:\WinPython\basedir34\buildQt5\winpython-3.4.4.amd64\python-3.4.4.amd64
\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 382, in checkOpenGLVe
rsion
    if int(ver.split('.')[0]) < 2:
TypeError: 'str' does not support the buffer interface
```



Discussion:

Resolving patch:
diff --git a/pyqtgraph/opengl/GLViewWidget.py b/pyqtgraph/opengl/GLViewWidget.py
--- a/pyqtgraph/opengl/GLViewWidget.py
+++ b/pyqtgraph/opengl/GLViewWidget.py
@@ -425,9 +425,9 @@ def evalKeyState(self):
     def checkOpenGLVersion(self, msg):
         ## Only to be called from within exception handler.
         ver = glGetString(GL_VERSION).split()[0]
-        if int(ver.split('.')[0]) < 2:
+        if int(ver.split(b'.')[0]) < 2:
             from .. import debug
-            pyqtgraph.debug.printExc()
+            debug.printExc()
             raise Exception(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
         else:
             raise


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Selecting flowchart input or output nodes in Flowchart widget gives KeyError
The Flowchart widget has a flowchart visualisation and a tree view. The input and output nodes are in the visualisation within FlowchartWidget but not the tree of FlowchartCtrlWidget. When the user clicks on the input or output node to select it in the FlowchartWidget, an attempt it made to highlight it in the FlowchartCtrlWidget. This results in a KeyError, and therefore stops the data on those nodes being displayed in the FlowchartWidget.

To reproduce, run the Flowchart example: _examples/Flowchart.py_. Click Flowchart in the first window, and then click on the input or output node in the flow chart of the second window. A KeyError is raised.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/flowchart/Flowchart.py b/pyqtgraph/flowchart/Flowchart.py
--- a/pyqtgraph/flowchart/Flowchart.py
+++ b/pyqtgraph/flowchart/Flowchart.py
@@ -763,6 +763,9 @@ def select(self, node):
         item = self.items[node]
         self.ui.ctrlList.setCurrentItem(item)
 
+    def clearSelection(self):
+        self.ui.ctrlList.selectionModel().clearSelection()
+
 
 class FlowchartWidget(dockarea.DockArea):
     """Includes the actual graphical flowchart and debugging interface"""
@@ -890,7 +893,10 @@ def selectionChanged(self):
             item = items[0]
             if hasattr(item, 'node') and isinstance(item.node, Node):
                 n = item.node
-                self.ctrl.select(n)
+                if n in self.ctrl.items:
+                    self.ctrl.select(n)
+                else:
+                    self.ctrl.clearSelection()
                 data = {'outputs': n.outputValues(), 'inputs': n.inputValues()}
                 self.selNameLabel.setText(n.name())
                 if hasattr(n, 'nodeName'):


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Crash on closing Matplotlib export
E.g. when opening the Matplotlib exporter multiple times, and closing the windows again, Python crashes with a segmentation fault.

This is caused by the Matplotlib QMainWindow listening to the closeEvent and deleting the only reference of the window before it is closed properly.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/exporters/Matplotlib.py b/pyqtgraph/exporters/Matplotlib.py
--- a/pyqtgraph/exporters/Matplotlib.py
+++ b/pyqtgraph/exporters/Matplotlib.py
@@ -124,5 +124,4 @@ def __getattr__(self, attr):
         
     def closeEvent(self, ev):
         MatplotlibExporter.windows.remove(self)
-
-
+        self.deleteLater()


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
TreeWidget.topLevelItems is broken on Python3
As per the title. The method uses `xrange`, which obviously is not available in Python 3.
I haven't tried it, but I assume the regression was introduced with 6c7e0fa, where an `from ..python2_3 import xrange` was removed from the corresponding file.

I can see two possible fixes for the issue.

1. Change `xrange` to `range`, which is available in both languages.
2. Revert the removal of the `python2_3` compatibility import.

@campagnola Let me know which one you prefer, and I'll submit a PR.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/widgets/TreeWidget.py b/pyqtgraph/widgets/TreeWidget.py
--- a/pyqtgraph/widgets/TreeWidget.py
+++ b/pyqtgraph/widgets/TreeWidget.py
@@ -201,7 +201,7 @@ def takeTopLevelItem(self, index):
         return item
 
     def topLevelItems(self):
-        return map(self.topLevelItem, xrange(self.topLevelItemCount()))
+        return [self.topLevelItem(i) for i in range(self.topLevelItemCount())]
         
     def clear(self):
         items = self.topLevelItems()


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Typo in checkSceneChange method of ViewBox?
https://github.com/pyqtgraph/pyqtgraph/blob/9aaae8d5dc86d5373d297f5763ab65d8043afcdb/pyqtgraph/graphicsItems/ViewBox/ViewBox.py#L277

ViewBox has an attribute called `_lastScene`, but it doesn't seem to have one called `lastScene`.

So I suggest replacing line 277,
`if self._lastScene is not None and hasattr(self.lastScene, 'sigPrepareForPaint')`,
with
`if self._lastScene is not None and hasattr(self._lastScene, 'sigPrepareForPaint')`.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
--- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
+++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py
@@ -277,7 +277,7 @@ def checkSceneChange(self):
         scene = self.scene()
         if scene == self._lastScene:
             return
-        if self._lastScene is not None and hasattr(self.lastScene, 'sigPrepareForPaint'):
+        if self._lastScene is not None and hasattr(self._lastScene, 'sigPrepareForPaint'):
             self._lastScene.sigPrepareForPaint.disconnect(self.prepareForPaint)
         if scene is not None and hasattr(scene, 'sigPrepareForPaint'):
             scene.sigPrepareForPaint.connect(self.prepareForPaint)


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Numpy FutureWarning when setting bins in ImageItem.getHistogram()
If I set the `bins` parameter of the `ImageItem.getHistogram()` method to a Numpy array, I get the following Future warning...

```
/Users/kenter/prog/py/pyqtgraph/pyqtgraph/graphicsItems/ImageItem.py:493: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  if bins == 'auto':
```

The solution seems to me to replace `if bins == 'auto'` with `if bins is 'auto'`. 

I didn't make a pull request since it's only one line, but if you like I can make a PR. Just let me know.

I use Numpy 1.15.4 and PyQtGraph 0.10.


Discussion:
While `is` can work for string comparison it can't be relied upon (for example: https://stackoverflow.com/a/1504742/1639671)

I would just add an `isinstance(bins, str)` to avoid a numpy array getting compared to 'auto'. That or `not isinstance(bins, np.ndarray)`, but there are so many kinds of sequences (like pandas series) that might not enjoy being compared to a string, it makes more sense to just require it be a str or str subclass to use the auto option.
I thought that it could be relied on because Python maintains a collection of string constants. But I realize now that you can also get to the same result by constructing strings. Obvious in retrospect. Thanks.

Resolving patch:
diff --git a/pyqtgraph/graphicsItems/ImageItem.py b/pyqtgraph/graphicsItems/ImageItem.py
--- a/pyqtgraph/graphicsItems/ImageItem.py
+++ b/pyqtgraph/graphicsItems/ImageItem.py
@@ -488,7 +488,7 @@ def getHistogram(self, bins='auto', step='auto', perChannel=False, targetImageSi
             step = (step, step)
         stepData = self.image[::step[0], ::step[1]]
 
-        if bins == 'auto':
+        if 'auto' == bins:
             mn = np.nanmin(stepData)
             mx = np.nanmax(stepData)
             if mx == mn:


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Bug in PlotItem.py: addLine(pos=user_pos, angle=user_angle) cannot work
Compare 
https://github.com/pyqtgraph/pyqtgraph/blob/develop/pyqtgraph/graphicsItems/PlotItem/PlotItem.py#L570
specifying the "angle" kwd on the addLine() call will lead to `InfiniteLine.__init__(pos, angle, **kwds)` being called with two angle keywords (the first, explicit one, and the one in **kwds).

Suggestion: 
instead of 

```
        angle = kwds.get('angle', 0 if x is None else 90)
```

do 

```
        kwds['angle'] = kwds.get('angle', 0 if x is None else 90)
```



Discussion:

Resolving patch:
diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
--- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
+++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
@@ -545,9 +545,9 @@ def addLine(self, x=None, y=None, z=None, **kwds):
         :func:`InfiniteLine.__init__() <pyqtgraph.InfiniteLine.__init__>`.
         Returns the item created.
         """
-        pos = kwds.get('pos', x if x is not None else y)
-        angle = kwds.get('angle', 0 if x is None else 90)
-        line = InfiniteLine(pos, angle, **kwds)
+        kwds['pos'] = kwds.get('pos', x if x is not None else y)
+        kwds['angle'] = kwds.get('angle', 0 if x is None else 90)
+        line = InfiniteLine(**kwds)
         self.addItem(line)
         if z is not None:
             line.setZValue(z)


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
HistogramLUTWidget kargs bug?
# lut_widget = HistogramLUTWidget(background='w')

 File "/usr/local/lib/python3.4/dist-packages/pyqtgraph-0.9.8-py3.4.egg/pyqtgraph/widgets/HistogramLUTWidget.py", line 18, in **init**
    self.item = HistogramLUTItem(_args, *_kargs)
# TypeError: **init**() got an unexpected keyword argument 'background'

I can fix it by:

class HistogramLUTWidget(pg.GraphicsView):

```
def __init__(self, parent=None,  *args, **kargs):
    # background = kargs.get('background', 'default')
    background = kargs.pop('background', 'default')
```

...



Discussion:

Resolving patch:
diff --git a/pyqtgraph/widgets/HistogramLUTWidget.py b/pyqtgraph/widgets/HistogramLUTWidget.py
--- a/pyqtgraph/widgets/HistogramLUTWidget.py
+++ b/pyqtgraph/widgets/HistogramLUTWidget.py
@@ -13,7 +13,7 @@
 class HistogramLUTWidget(GraphicsView):
     
     def __init__(self, parent=None,  *args, **kargs):
-        background = kargs.get('background', 'default')
+        background = kargs.pop('background', 'default')
         GraphicsView.__init__(self, parent, useOpenGL=False, background=background)
         self.item = HistogramLUTItem(*args, **kargs)
         self.setCentralItem(self.item)


Test patch:
diff --git a/pyqtgraph/widgets/tests/test_histogramlutwidget.py b/pyqtgraph/widgets/tests/test_histogramlutwidget.py
new file mode 100644
--- /dev/null
+++ b/pyqtgraph/widgets/tests/test_histogramlutwidget.py
@@ -0,0 +1,44 @@
+"""
+HistogramLUTWidget test:
+
+Tests the creation of a HistogramLUTWidget.
+"""
+
+import pyqtgraph as pg
+from pyqtgraph.Qt import QtGui
+import numpy as np
+
+def testHistogramLUTWidget():
+    pg.mkQApp()
+    
+    win = QtGui.QMainWindow()
+    win.show()
+
+    cw = QtGui.QWidget()
+    win.setCentralWidget(cw)
+
+    l = QtGui.QGridLayout()
+    cw.setLayout(l)
+    l.setSpacing(0)
+
+    v = pg.GraphicsView()
+    vb = pg.ViewBox()
+    vb.setAspectLocked()
+    v.setCentralItem(vb)
+    l.addWidget(v, 0, 0, 3, 1)
+
+    w = pg.HistogramLUTWidget(background='w')
+    l.addWidget(w, 0, 1)
+
+    data = pg.gaussianFilter(np.random.normal(size=(256, 256, 3)), (20, 20, 0))
+    for i in range(32):
+        for j in range(32):
+            data[i*8, j*8] += .1
+    img = pg.ImageItem(data)
+    vb.addItem(img)
+    vb.autoRange()
+
+    w.setImageItem(img)
+    
+    QtGui.QApplication.processEvents()
+
Repository: pyqtgraph/pyqtgraph

Issue:
OSError on exit()
Minimal example:
(macOS 10.12, python 3.5.4 & 3.6.1, pyqtgraph version 0.10.0)

```
import pyqtgraph as pg
pg.exit()
```

> OSError: [Errno 9] Bad file descriptor


Discussion:
This issue is still persistent on macOS 14.

Traceback is as follows:

```
>>> pg.exit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pyqtgraph/__init__.py", line 373, in exit
    os.close(fd)
OSError: [Errno 9] Bad file descriptor
```

Resolving patch:
diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py
--- a/pyqtgraph/__init__.py
+++ b/pyqtgraph/__init__.py
@@ -369,8 +369,12 @@ def exit():
     ## close file handles
     if sys.platform == 'darwin':
         for fd in range(3, 4096):
-            if fd not in [7]:  # trying to close 7 produces an illegal instruction on the Mac.
+            if fd in [7]:  # trying to close 7 produces an illegal instruction on the Mac.
+                continue
+            try:
                 os.close(fd)
+            except OSError:
+                pass
     else:
         os.closerange(3, 4096) ## just guessing on the maximum descriptor count..
 


Test patch:
diff --git a/pyqtgraph/tests/test_exit_crash.py b/pyqtgraph/tests/test_exit_crash.py
--- a/pyqtgraph/tests/test_exit_crash.py
+++ b/pyqtgraph/tests/test_exit_crash.py
@@ -1,7 +1,12 @@
-import os, sys, subprocess, tempfile
+import os
+import sys
+import subprocess
+import tempfile
 import pyqtgraph as pg
 import six
 import pytest
+import textwrap
+import time
 
 code = """
 import sys
@@ -14,6 +19,25 @@
 skipmessage = ('unclear why this test is failing. skipping until someone has'
                ' time to fix it')
 
+
+def call_with_timeout(*args, **kwargs):
+    """Mimic subprocess.call with timeout for python < 3.3"""
+    wait_per_poll = 0.1
+    try:
+        timeout = kwargs.pop('timeout')
+    except KeyError:
+        timeout = 10
+
+    rc = None
+    p = subprocess.Popen(*args, **kwargs)
+    for i in range(int(timeout/wait_per_poll)):
+        rc = p.poll()
+        if rc is not None:
+            break
+        time.sleep(wait_per_poll)
+    return rc
+
+
 @pytest.mark.skipif(True, reason=skipmessage)
 def test_exit_crash():
     # For each Widget subclass, run a simple python script that creates an
@@ -40,3 +64,15 @@ def test_exit_crash():
         assert proc.wait() == 0
 
     os.remove(tmp)
+
+
+def test_pg_exit():
+    # test the pg.exit() function
+    code = textwrap.dedent("""
+        import pyqtgraph as pg
+        app = pg.mkQApp()
+        pg.plot()
+        pg.exit()
+    """)
+    rc = call_with_timeout([sys.executable, '-c', code], timeout=5)
+    assert rc == 0
Repository: pyqtgraph/pyqtgraph

Issue:
Can't select last image in image stack by drag and drop of slider
Minimum non-working example:

``` python
import numpy as np
import pyqtgraph as pg

a = np.random.rand(3,10,10)
a[2] = 0
pg.image(a)
```

Now, try to drag the slider to the last image (the "0" one). This won't work.
However, navigating there by using the arrow keys just works.

ImageView not showing the last frame
Quite self explanatory i think. If you load a 3 dimension array into imageView you cant seek to the last frame. However playing the sequence ends up in the last frame.


Discussion:

Resolving patch:
diff --git a/pyqtgraph/imageview/ImageView.py b/pyqtgraph/imageview/ImageView.py
--- a/pyqtgraph/imageview/ImageView.py
+++ b/pyqtgraph/imageview/ImageView.py
@@ -740,7 +740,7 @@ def timeIndex(self, slider):
             return (0,0)
         
         t = slider.value()
-        
+
         xv = self.tVals
         if xv is None:
             ind = int(t)
@@ -748,7 +748,7 @@ def timeIndex(self, slider):
             if len(xv) < 2:
                 return (0,0)
             totTime = xv[-1] + (xv[-1]-xv[-2])
-            inds = np.argwhere(xv < t)
+            inds = np.argwhere(xv <= t)
             if len(inds) < 1:
                 return (0,t)
             ind = inds[-1,0]


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
Unpredictable behavior in inshaders.py in 'heightColor' shader
Hello everyone, thak you for your project. It's very cool!
But I found unpredictable behavior in shaders.py in 'heightColor' shader

[documentation](https://github.com/pyqtgraph/pyqtgraph/blob/develop/pyqtgraph/opengl/shaders.py#L140-L146) formula: pow(z \* colorMap[0] + colorMap[1], colorMap[2])
[source code](https://github.com/pyqtgraph/pyqtgraph/blob/develop/pyqtgraph/opengl/shaders.py#L164) formula: pow(colorMap[0] \* (pos.z + colorMap[1]), colorMap[2])

Please fix it.
Thank you.



Discussion:

Resolving patch:
diff --git a/pyqtgraph/opengl/shaders.py b/pyqtgraph/opengl/shaders.py
--- a/pyqtgraph/opengl/shaders.py
+++ b/pyqtgraph/opengl/shaders.py
@@ -140,9 +140,9 @@ def initShaders():
         ## colors fragments by z-value.
         ## This is useful for coloring surface plots by height.
         ## This shader uses a uniform called "colorMap" to determine how to map the colors:
-        ##    red   = pow(z * colorMap[0] + colorMap[1], colorMap[2])
-        ##    green = pow(z * colorMap[3] + colorMap[4], colorMap[5])
-        ##    blue  = pow(z * colorMap[6] + colorMap[7], colorMap[8])
+        ##    red   = pow(colorMap[0]*(z + colorMap[1]), colorMap[2])
+        ##    green = pow(colorMap[3]*(z + colorMap[4]), colorMap[5])
+        ##    blue  = pow(colorMap[6]*(z + colorMap[7]), colorMap[8])
         ## (set the values like this: shader['uniformMap'] = array([...])
         ShaderProgram('heightColor', [
             VertexShader("""


Test patch:
Repository: pyqtgraph/pyqtgraph

Issue:
RecursionError with custom node
In a custom Node, I try to initialize with `CtrlNode.__init__(self, name, terminals=terminals)`, but I get a RecursionError:

``` python
Traceback (most recent call last):
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/Flowchart.py", line 871, in nodeMenuTriggered
    self.chart.createNode(nodeType, pos=pos)
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/Flowchart.py", line 174, in createNode
    node = self.library.getNodeType(nodeType)(name)
  File "/data/libs.git/JML/python/TBA/nodes.py", line 37, in __init__
    CtrlNode.__init__(self, name, terminals=terminals)
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/library/common.py", line 89, in __init__
    if hasattr(self, 'uiTemplate'):
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/Node.py", line 193, in __getattr__
    if attr not in self.terminals:
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/Node.py", line 193, in __getattr__
    if attr not in self.terminals:
(...)
  File "/usr/lib/python3.5/site-packages/pyqtgraph/flowchart/Node.py", line 193, in __getattr__
    if attr not in self.terminals:
RecursionError: maximum recursion depth exceeded while calling a Python object
```

The problem is that `__getattr__` checks for `self.terminals` which is not yet defined, so  `__getattr__` is called again and so on.

I think putting the `if ui is None:` block after `Node.__init__` in  `CtrlNode.__init__` would do the trick.



Discussion:

Resolving patch:
diff --git a/pyqtgraph/flowchart/library/common.py b/pyqtgraph/flowchart/library/common.py
--- a/pyqtgraph/flowchart/library/common.py
+++ b/pyqtgraph/flowchart/library/common.py
@@ -91,14 +91,15 @@ class CtrlNode(Node):
     sigStateChanged = QtCore.Signal(object)
     
     def __init__(self, name, ui=None, terminals=None):
+        if terminals is None:
+            terminals = {'In': {'io': 'in'}, 'Out': {'io': 'out', 'bypass': 'In'}}
+        Node.__init__(self, name=name, terminals=terminals)
+        
         if ui is None:
             if hasattr(self, 'uiTemplate'):
                 ui = self.uiTemplate
             else:
                 ui = []
-        if terminals is None:
-            terminals = {'In': {'io': 'in'}, 'Out': {'io': 'out', 'bypass': 'In'}}
-        Node.__init__(self, name=name, terminals=terminals)
         
         self.ui, self.stateGroup, self.ctrls = generateUi(ui)
         self.stateGroup.sigChanged.connect(self.changed)


Test patch: