Browse Source

qml: add QR code imageprovider using qrcode/PIL

adds buildozer 'pillow' recipe to requirements

add initial PoC on qml receive tab
patch-4
Sander van Grieken 3 years ago
parent
commit
492f246b9a
  1. 3
      contrib/android/buildozer_qml.spec
  2. 32
      electrum/gui/qml/components/Receive.qml
  3. 8
      electrum/gui/qml/components/WalletMainView.qml
  4. 7
      electrum/gui/qml/qeapp.py
  5. 51
      electrum/gui/qml/qeqr.py

3
contrib/android/buildozer_qml.spec

@ -51,7 +51,8 @@ requirements =
libsecp256k1, libsecp256k1,
cryptography, cryptography,
pyqt5sip, pyqt5sip,
pyqt5 pyqt5,
pillow
# (str) Presplash of the application # (str) Presplash of the application
#presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png #presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png

32
electrum/gui/qml/components/Receive.qml

@ -0,0 +1,32 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.0
import QtQuick.Controls.Material 2.0
import org.electrum 1.0
Pane {
id: rootItem
visible: Daemon.currentWallet !== undefined
ColumnLayout {
width: parent.width
spacing: 20
Image {
id: img
}
TextField {
id: text
}
Button {
text: 'generate'
onClicked: {
img.source = 'image://qrgen/' + text.text
}
}
}
}

8
electrum/gui/qml/components/WalletMainView.qml

@ -63,11 +63,9 @@ Item {
currentIndex: tabbar.currentIndex currentIndex: tabbar.currentIndex
Item { Item {
Receive {
ColumnLayout { id: receive
width: parent.width anchors.fill: parent
y: 20
spacing: 20
} }
} }

7
electrum/gui/qml/qeapp.py

@ -10,7 +10,7 @@ from .qeconfig import QEConfig
from .qedaemon import QEDaemon, QEWalletListModel from .qedaemon import QEDaemon, QEWalletListModel
from .qenetwork import QENetwork from .qenetwork import QENetwork
from .qewallet import QEWallet from .qewallet import QEWallet
from .qeqr import QEQR from .qeqr import QEQR, QEQRImageProvider
from .qewalletdb import QEWalletDB from .qewalletdb import QEWalletDB
from .qebitcoin import QEBitcoin from .qebitcoin import QEBitcoin
@ -36,6 +36,9 @@ class ElectrumQmlApplication(QGuiApplication):
self.engine = QQmlApplicationEngine(parent=self) self.engine = QQmlApplicationEngine(parent=self)
self.engine.addImportPath('./qml') self.engine.addImportPath('./qml')
self.qr_ip = QEQRImageProvider()
self.engine.addImageProvider('qrgen', self.qr_ip)
self.context = self.engine.rootContext() self.context = self.engine.rootContext()
self._singletons['config'] = QEConfig(config) self._singletons['config'] = QEConfig(config)
self._singletons['network'] = QENetwork(daemon.network) self._singletons['network'] = QENetwork(daemon.network)
@ -63,7 +66,7 @@ class ElectrumQmlApplication(QGuiApplication):
def message_handler(self, line, funct, file): def message_handler(self, line, funct, file):
# filter out common harmless messages # filter out common harmless messages
if re.search('file:///.*TypeError:\ Cannot\ read\ property.*null$', file): if re.search('file:///.*TypeError: Cannot read property.*null$', file):
return return
self.logger.warning(file) self.logger.warning(file)

51
electrum/gui/qml/qeqr.py

@ -1,8 +1,15 @@
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl
from PyQt5.QtGui import QImage
from PyQt5.QtQuick import QQuickImageProvider
from electrum.logging import get_logger from electrum.logging import get_logger
#from PIL import Image import qrcode
#from qrcode.image.styledpil import StyledPilImage
#from qrcode.image.styles.moduledrawers import *
from PIL import Image, ImageQt
from ctypes import * from ctypes import *
class QEQR(QObject): class QEQR(QObject):
@ -11,22 +18,25 @@ class QEQR(QObject):
self._text = text self._text = text
_logger = get_logger(__name__) _logger = get_logger(__name__)
scan_ready_changed = pyqtSignal()
_ready = True scanReadyChanged = pyqtSignal()
imageChanged = pyqtSignal()
_scanReady = True
_image = None
@pyqtSlot('QImage') @pyqtSlot('QImage')
def scanImage(self, image=None): def scanImage(self, image=None):
if not self._ready: if not self._scanReady:
self._logger.warning("Already processing an image. Check 'ready' property before calling scanImage") self._logger.warning("Already processing an image. Check 'ready' property before calling scanImage")
return return
self._ready = False self._scanReady = False
self.scan_ready_changed.emit() self.scanReadyChanged.emit()
pilimage = self.convertToPILImage(image) pilimage = self.convertToPILImage(image)
self.parseQR(pilimage) self.parseQR(pilimage)
self._ready = True self._scanReady = True
def logImageStats(self, image): def logImageStats(self, image):
self._logger.info('width: ' + str(image.width())) self._logger.info('width: ' + str(image.width()))
@ -47,13 +57,32 @@ class QEQR(QObject):
memmove(c_buf, c_void_p(rawimage.__int__()), numbytes) memmove(c_buf, c_void_p(rawimage.__int__()), numbytes)
buf2 = bytes(buf) buf2 = bytes(buf)
return None #Image.frombytes('RGBA', (image.width(), image.height()), buf2, 'raw') return Image.frombytes('RGBA', (image.width(), image.height()), buf2, 'raw')
def parseQR(self, image): def parseQR(self, image):
# TODO # TODO
pass pass
@pyqtProperty(bool, notify=scan_ready_changed) @pyqtProperty(bool, notify=scanReadyChanged)
def ready(self): def scanReady(self):
return self._ready return self._scanReady
@pyqtProperty('QImage', notify=imageChanged)
def image(self):
return self._image
class QEQRImageProvider(QQuickImageProvider):
def __init__(self, parent=None):
super().__init__(QQuickImageProvider.Image)
_logger = get_logger(__name__)
def requestImage(self, qstr, size):
self._logger.debug('QR requested for %s' % qstr)
qr = qrcode.QRCode(version=1, box_size=8, border=2)
qr.add_data(qstr)
qr.make(fit=True)
pimg = qr.make_image(fill_color='black', back_color='white') #image_factory=StyledPilImage, module_drawer=CircleModuleDrawer())
qimg = ImageQt.ImageQt(pimg)
return qimg, qimg.size()

Loading…
Cancel
Save