我正在使用QML画布绘制图像。该图像使用Python,OpenCV和NumPy加载到后端,并通过自定义图像提供程序提供给QML,该图像提供程序继承自QQuickImageProvider。要在QML中重新加载图像,请执行以下操作:
unloadImage('image://<providerName>/<ID>')
loadImage('image://<providerName>/<ID>')
只要图像足够大,此方法就可以很好地工作。但是,一旦图像大小低于某个阈值(在我的PC上大约为1.57 MB), unloadImage 函数似乎就会停止工作。调用它之后,我不应该再次绘制图像,但是仍然可以。而且,不再调用我的 QQuickImageProvider 的 requestImage 函数,如果在调用该函数之前不调用 unloadImage ,情况也是如此。 loadImage 函数。
有人知道我在做什么错吗?
编辑:
这是一个最小的代码示例,它重现了我的错误:
main.py
import sys
import time
from PySide2 import QtGui, QtQml
from PySide2.QtQuick import QQuickImageProvider
from PySide2.QtGui import QImage
from PySide2.QtCore import QObject, Signal, Slot
import numpy as np
import cv2
class ImageProvider(QQuickImageProvider, QObject):
def __init__(self):
# Load images:
# - The first image is scaled large enough
self.img1 = np.require(np.flip(cv2.imread("messi5.jpg"), 2), np.uint8, "C")
self.img1 = cv2.resize(self.img1, (0,0), fx=3, fy=3)
# - The second image is too small and won't allow a reload
self.img2 = np.require(np.flip(cv2.imread("lena.jpg"), 2), np.uint8, "C")
# Shown image
self.image = None
# Initialize parent objects
QObject.__init__(self)
QQuickImageProvider.__init__(self, QQuickImageProvider.Image)
# =======
# Signals
# =======
updateImage = Signal()
# =====
# Slots
# =====
def requestImage(self, ident, size, requestedSize):
print("ImageProvider: loading image")
format = QImage.Format_RGB888
(h, w) = self.image.shape[:2]
line_len = self.image.strides[0]
img = QImage(self.image, w, h, line_len, format)
return img
@Slot(result="QVariantList")
def getImageSize(self):
h, w = self.image.shape[:2]
return [w, h]
@Slot()
def changeImage(self):
if np.array_equal(self.image, self.img1):
self.show(self.img2)
else:
self.show(self.img1)
# ==============
# public methods
# ==============
def show(self, Image):
self.image = Image
self.updateImage.emit()
if __name__ == "__main__":
# Create app
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
# Create backend
image_provider = ImageProvider()
# Register backend
engine.addImageProvider("CustProv", image_provider)
engine.rootContext().setContextProperty("CustProv", image_provider)
# Load window
engine.load('main.qml')
win = engine.rootObjects()[0]
win.show()
# Show image
image_provider.show(image_provider.img1)
sys.exit(app.exec_())
main.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 800
height: 480
// =============
// Event Handler
// =============
onHeightChanged: {
imagePainter.resize()
}
onWidthChanged: {
imagePainter.resize()
}
// ========
// Children
// ========
Item {
anchors {
top: parent.top
bottom: button.top
right: parent.right
left: parent.left
bottomMargin: 10
}
Canvas {
id: imagePainter
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
// =================
// Custom Properties
// =================
property var size: {
'width': -1,
'height': -1
}
property real scale: 1
property var image: 'image://CustProv/image'
// =========
// Functions
// =========
function resize() {
console.log("Resizing canvas contents")
var wToH = size.width/size.height
if (parent.width/parent.height >= wToH) {
// image aspect ratio is narrower than parent
// aspect ratio: Use full height
height = parent.height
width = wToH * parent.height
scale = height/size.height
}
else {
// image aspect ratio is wider than parent
// aspect ratio: use full width
width = parent.width
height = parent.width / wToH
scale = width/size.width
}
// repaint the image
requestPaint();
}
function reload() {
console.log("Reload triggered")
// First, get the new image size
var imSize = CustProv.getImageSize()
size.width = imSize[0]
size.height = imSize[1]
resize()
// Reload image
unloadImage(image) <--- Seems to fail for small images
loadImage(image)
}
// =============
// Event Handler
// =============
Component.onCompleted: {
console.log("connecting external signals")
CustProv.updateImage.connect(reload)
}
onPaint: {
// Invoked by requestPaint()
if (!isImageLoaded(image)) {
return
}
var ctx = getContext('2d')
ctx.clearRect(0, 0, width, height)
ctx.scale(scale, scale)
ctx.drawImage(image, 0, 0)
ctx.scale(1/scale, 1/scale)
}
onImageLoaded: {
requestPaint()
}
}
}
Button {
id: button
anchors {
right: parent.right
bottom: parent.bottom
bottomMargin: 10
rightMargin: 10
}
width: 200
height: 25
text: "Change Image"
onClicked: {
CustProv.changeImage()
}
}
}
我尽了最大的努力将示例缩小到绝对的最小值,但没有比这更小的示例。抱歉。
答案 0 :(得分:0)
由QQuickImageProvider返回的图像会自动缓存,类似于QML引擎加载的任何图像。当从缓存中加载带有“ image://”前缀的图像时,相关图像提供者将不会调用requestImage()和requestPixmap()。如果应该始终从图像提供者那里获取图像,而根本不应该对其进行缓存,则将相关Image,BorderImage或AnimatedImage对象的cache属性设置为false。
因此,您需要将cache
组件的Image
属性设置为false
。
要重新加载图像,只需将源路径修改为null并将其还原为以前的值。