Создать QR-код в Python (PyQt)

В настоящее время я использую PyQt4 и qrcode4.0.4.

from PyQt4 import QtGui, QtCore
from PIL.ImageQt import ImageQt
import qrcode

class QRLabel(QtGui.QLabel):
    def __init__(self, text=""):
        super(QRLabel, self).__init__()
        self.setCode(text)

    def setCode(self, text=""):        
        self.text = text      
        qrImg = qrcode.make(text)
        imgQt = ImageQt(qrImg.convert("RGB"))   # keep a reference!
        pixm = QtGui.QPixmap.fromImage(imgQt)
        self.setPixmap(pixm.scaled(self.size(),QtCore.Qt.KeepAspectRatio))

Как видите, перед тем, как вы получите изображение на экран, вам нужно будет преодолеть несколько препятствий. QR-код начинается с изображения PIL RGBA, он преобразуется в RGB, затем в объект PIL ImageQt, затем в QPixmap, который затем помещается в QLabel с исправлением масштабирования.

Если вы явно не сохраните ссылку на imgQt, вы получите мусор при загрузке виджета.

Мой вопрос: могу ли я что-то сделать, чтобы улучшить это, так как кажется, что так много конверсий вовлечено.

2 ответа

Решение

Из документации qrcode вы можете создать свой собственный image_factory, что может позволить вам упростить процесс.

Вам просто нужно подкласс qrcode.image.base.BaseImage и переопределить new_image, drawrect а также save методы. Этот подкласс может обернуть QImage и, следовательно, устранить необходимость в шаге преобразования PIL.

ОБНОВЛЕНИЕ:

Вот демонстрация, которая устраняет зависимость PIL (что также хорошо, потому что я обнаружил, что PIL падает с определенными входами):

from PyQt4 import QtGui, QtCore
import qrcode

class Image(qrcode.image.base.BaseImage):
    def __init__(self, border, width, box_size):
        self.border = border
        self.width = width
        self.box_size = box_size
        size = (width + border * 2) * box_size
        self._image = QtGui.QImage(
            size, size, QtGui.QImage.Format_RGB16)
        self._image.fill(QtCore.Qt.white)

    def pixmap(self):
        return QtGui.QPixmap.fromImage(self._image)

    def drawrect(self, row, col):
        painter = QtGui.QPainter(self._image)
        painter.fillRect(
            (col + self.border) * self.box_size,
            (row + self.border) * self.box_size,
            self.box_size, self.box_size,
            QtCore.Qt.black)

    def save(self, stream, kind=None):
        pass

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.label = QtGui.QLabel(self)
        self.edit = QtGui.QLineEdit(self)
        self.edit.returnPressed.connect(self.handleTextEntered)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.edit)

    def handleTextEntered(self):
        text = unicode(self.edit.text())
        self.label.setPixmap(
            qrcode.make(text, image_factory=Image).pixmap())

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 200, 200)
    window.show()
    sys.exit(app.exec_())

Вы также можете использовать PNG в качестве промежуточного формата и сохранить его в памяти, используя StringIO.

import qrcode
import StringIO

def set_qr_label(label, text):
    """
    set qrcode image on QLabel

    @param label: QLabel
    @param text: text for the QR code
    """
    buf = StringIO.StringIO()
    img = qrcode.make(text)
    img.save(buf, "PNG")
    label.setText("")
    qt_pixmap = QtGui.QPixmap()
    qt_pixmap.loadFromData(buf.getvalue(), "PNG")
    label.setPixmap(qt_pixmap)

Отличный ответ от mgmax, но только на Python2. Для Python3 используйте:

import qrcode
from io import BytesIO

def set_qr_label(label, text):
    """
    set qrcode image on QLabel

    @param label: QLabel
    @param text: text for the QR code
    """
    buf = BytesIO()
    img = qrcode.make(text)
    img.save(buf, "PNG")
    label.setText("")
    qt_pixmap = QtGui.QPixmap()
    qt_pixmap.loadFromData(buf.getvalue(), "PNG")
    label.setPixmap(qt_pixmap)

Это ответ ekhumoro, работающий в pyqt5 в python3, надеюсь, что он поможет кому-то в будущем.

from PyQt5 import QtWidgets, QtCore, QtGui
import qrcode

class Image(qrcode.image.base.BaseImage):
    def __init__(self, border, width, box_size):
        self.border = border
        self.width = width
        self.box_size = box_size
        size = (width + border * 2) * box_size
        self._image = QtGui.QImage(
            size, size, QtGui.QImage.Format_RGB16)
        self._image.fill(QtCore.Qt.white)

    def pixmap(self):
        return QtGui.QPixmap.fromImage(self._image)

    def drawrect(self, row, col):
        painter = QtGui.QPainter(self._image)
        painter.fillRect(
            (col + self.border) * self.box_size,
            (row + self.border) * self.box_size,
            self.box_size, self.box_size,
            QtCore.Qt.black)

    def save(self, stream, kind=None):
        pass

class Window(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.label = QtWidgets.QLabel(self)
        self.edit = QtWidgets.QLineEdit(self)
        self.edit.returnPressed.connect(self.handleTextEntered)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.edit)

    def handleTextEntered(self):
        text = self.edit.text()#text = unicode(self.edit.text())
        self.label.setPixmap(
            qrcode.make(text, image_factory=Image).pixmap())

if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 200, 200)
    window.show()
    sys.exit(app.exec_())

Другие вопросы по тегам