I have written a small GUI using PyQt4 that displays an image and gets point coordinates that the user clicks on. I need to display a 2D numpy array as a grayscale, so I am creating a QImage from the array, then from that creating a QPixmap. In Python 2 it works fine.
When I moved to Python 3, however, it can't decide on a constructor for QImage - it gives me the following error:
TypeError: arguments did not match any overloaded call:
  QImage(): too many arguments
  QImage(QSize, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(str, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(sip.voidptr, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(str, int, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(sip.voidptr, int, int, int, QImage.Format): argument 1 has unexpected type 'numpy.ndarray'
  QImage(list-of-str): argument 1 has unexpected type 'numpy.ndarray'
  QImage(str, str format=None): argument 1 has unexpected type 'numpy.ndarray'
  QImage(QImage): argument 1 has unexpected type 'numpy.ndarray'
  QImage(object): too many arguments
As far as I can tell, the QImage constructor I was calling previously was one of these:
- QImage(str, int, int, QImage.Format)
- QImage(sip.voidptr, int, int, QImage.Format)
I'm assuming that a numpy array fits one of the protocols necessary for one of these. I'm thinking it might have to do with an array versus a view, but all the variations I've tried either produce the above error or just make the GUI exit without doing anything. How can I reproduce the Python 2 behavior in Python 3?
The following is a small example, in which the same exact code works fine under Python 2 but not Python 3:
from __future__ import (print_function, division)
from PyQt4 import QtGui, QtCore
import numpy as np
class MouseView(QtGui.QGraphicsView):
    mouseclick = QtCore.pyqtSignal(tuple)
    def __init__(self, scene, parent=None):
        super(MouseView, self).__init__(scene, parent=parent)
    def mousePressEvent(self, event):
        self.mouseclick.emit((event.x(),
                              self.scene().sceneRect().height() - event.y()))
class SimplePicker(QtGui.QDialog):
    def __init__(self, data, parent=None):
        super(SimplePicker, self).__init__(parent)
        mind = data.min()
        maxd = data.max()
        bdata = ((data - mind) / (maxd - mind) * 255.).astype(np.uint8)
        wdims = data.shape
        wid = wdims[0]
        hgt = wdims[1]
        # This is the line of interest - it works fine under Python 2, but not Python 3
        img = QtGui.QImage(bdata.T, wid, hgt,
                           QtGui.QImage.Format_Indexed8)
        self.scene = QtGui.QGraphicsScene(0, 0, wid, hgt)
        self.px = self.scene.addPixmap(QtGui.QPixmap.fromImage(img))
        view = MouseView(self.scene)
        view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        view.setSizePolicy(QtGui.QSizePolicy.Fixed,
                           QtGui.QSizePolicy.Fixed)
        view.setMinimumSize(wid, hgt)
        view.mouseclick.connect(self.click_point)
        quitb = QtGui.QPushButton("Done")
        quitb.clicked.connect(self.quit)
        lay = QtGui.QVBoxLayout()
        lay.addWidget(view)
        lay.addWidget(quitb)
        self.setLayout(lay)
        self.points = []
    def click_point(self, xy):
        self.points.append(xy)
    def quit(self):
        self.accept()
def test_picker():
    x, y = np.mgrid[0:100, 0:100]
    img = x * y
    app = QtGui.QApplication.instance()
    if app is None:
        app = QtGui.QApplication(['python'])
    picker = SimplePicker(img)
    picker.show()
    app.exec_()
    print(picker.points)
if __name__ == "__main__":
    test_picker()
I am using an Anaconda installation on Windows 7 64-bit, Qt 4.8.7, PyQt 4.10.4, numpy 1.9.2.
