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.
buffer
object, but for python3 it's amemoryview
. Usingbdata.data
will work in your example for both python2 and python3, but, frustratingly,bdata.T.data
does not work with python3. Which is totally baffling, given that the error now isunexpected type 'memoryview'
?! It seems that there is some subtle difference between the two memoryviews that PyQt is not able to handle. – Leroibdata.data
, and it doesn't give any errors, but it doesn't run the GUI either! An empty list gets printed fromtest_picker
, but no GUI appears. Is there something else wrong with the rest of the example? – SpinQtGui.QImage(bdata.data, ...
, but it does dump core on exit. That can easily be fixed by giving the scene a parent, though:QGraphicsScene(0, 0, wid, hgt, self)
. Other than that, I can't think why it doesn't work for you. What platform are you on, and what versions of Qt, PyQt and numpy are you using? – Leroitest_picker
directly under theif __name__ ==...
, then the GUI does show up (usingbdata.data
), but it also prints the empty list before I exit the GUI (no points are saved). Python 2 still works like I want it to. ARGH. – Spin.copy()
) it's fine (both 2 and 3). Turns out that.T
is an exception that somehow just happens to work with Python 2, for some unknown reason. – Spin.copy()
... – Spin