Qml实现类似ImageWatch的功能:
实现图像放大缩小,显示颜色值,拖动图像。
存在的问题:缩放坐标存在误差。
QML文件
import QtQuick
import QtQuick.Controls
Rectangle {
property int itemflag: -1; color: "lightGray"; clip: true;
onItemflagChanged: {
table.model = opencv.im(itemflag);
}
onWidthChanged: {
if (image.z === 1) {
scaleImage();
}
}
onHeightChanged: {
if (image.z === 1) {
scaleImage();
}
}
Image {
id: image; cache: false; clip: true; anchors.centerIn: parent; fillMode: Image.PreserveAspectFit; z: 1;
}
TableView {
id: table; columnSpacing: 0; rowSpacing: 0; z: -1; anchors.fill: parent;
property real cellsize: 7;
property bool textshow: false;
columnWidthProvider: function(column) {
return cellsize;
}
rowHeightProvider: function(row) {
return cellsize;
}
delegate: Rectangle {
color: bgcolor; //implicitWidth: table.cellsize; implicitHeight: table.cellsize; border.width: 1; border.color: "black";
Text {
text: display; visible: table.textshow; color: fgcolor;
horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; anchors.centerIn: parent;
}
}
ScrollBar.vertical: ScrollBar {
id: tableVB; policy: ScrollBar.AlwaysOff; orientation: Qt.Vertical; anchors.top: parent.top; anchors.right: parent.right; anchors.bottom: parent.bottom;
}
ScrollBar.horizontal: ScrollBar {
id: tableHB; policy: ScrollBar.AlwaysOff; orientation: Qt.Horizontal; anchors.left: parent.left; anchors.right: parent.right; anchors.bottom: parent.bottom;
}
}
Text {
id: xyval; anchors.right: parent.right; anchors.bottom: parent.bottom; width: 240; height: 30; font.pixelSize: 20; z: 3; visible: itemflag === 0;
}
MouseArea {
id: marea; anchors.fill: parent; acceptedButtons: Qt.LeftButton | Qt.RightButton; hoverEnabled: true; z: 2;
property bool pressflag: false;
property point pressPoint: Qt.point(0.0);
onClicked: function(mouse) {
if (mouse.button === Qt.LeftButton) {
}
if (mouse.button === Qt.RightButton) {
}
}
onDoubleClicked: function(mouse) {
if (mouse.button === Qt.LeftButton) {
}
if (mouse.button === Qt.RightButton) {
}
}
onWheel: function(wheel) {
if (wheel.angleDelta.y > 0) {
if (table.z === -1) {
image.z = -1; table.z = 1; table.contentX = 0; table.contentY = 0;
let zoomPrev = Qt.size(table.width, table.height);
let zoomLast = Qt.size(table.contentWidth, table.contentHeight);
// 定位单元格
tablepositions(Qt.point(wheel.x, wheel.y), zoomPrev, zoomLast);
}
else {
if (table.cellsize > 57) {
table.textshow = true; table.columnSpacing = 1; table.rowSpacing = 1;
}
if (table.cellsize+1 < 60) {
let zoomPrev = Qt.size(table.contentWidth, table.contentHeight);
table.cellsize += 1; table.forceLayout();
let zoomLast = Qt.size(table.contentWidth, table.contentHeight);
// 定位单元格
tablepositions(Qt.point(wheel.x, wheel.y), zoomPrev, zoomLast);
}
}
}
else {
if (table.z === 1) {
if (table.cellsize < 57) {
table.textshow = false; table.columnSpacing = 0; table.rowSpacing = 0;
}
if (table.cellsize-1 > 7) {
let zoomPrev = Qt.size(table.contentWidth, table.contentHeight);
table.cellsize -= 1; table.forceLayout();
let zoomLast = Qt.size(table.contentWidth, table.contentHeight);
// 定位单元格
tablepositions(Qt.point(wheel.x, wheel.y), zoomPrev, zoomLast);
}
else {
image.z = 1; table.z = -1;
}
}
}
}
onPressed: function(mouse) {
marea.pressflag = true;
let xTable = mouse.x / table.width * table.contentWidth ;
let yTable = mouse.y / table.height * table.contentHeight;
marea.pressPoint = Qt.point(xTable, yTable);
}
onReleased: function(mouse) {
marea.pressflag = false;
}
onPositionChanged: function(mouse) {
xyval.text = "X:" + mouse.x.toFixed(2) + " Y:" + mouse.y.toFixed(2);
if (!marea.pressflag) {
return;
}
let xTable = mouse.x / table.width * table.contentWidth ;
let yTable = mouse.y / table.height * table.contentHeight;
table.contentX += (marea.pressPoint.x - xTable) / (xTable / mouse.x);
table.contentY += (marea.pressPoint.y - yTable) / (yTable / mouse.y);
marea.pressPoint.x = xTable;
marea.pressPoint.y = yTable;
}
}
Connections {
target: opencv;
function onImageUpdate(target) {
if (target === itemflag) {
image.source = "";
image.source = "image://cv/" + target;
scaleImage();
}
}
}
function scaleImage() {
let dstSize = Qt.size(image.parent.width, image.parent.height);
let srcSize = image.sourceSize;
let wRatio = dstSize.width / srcSize.width;
let hRatio = dstSize.height / srcSize.height;
image.scale = Math.min(wRatio, hRatio);
}
function tablepositions(mousepos, zoomPrev, zoomLast) {
// 缩放前坐标
let xTablePrev = mousepos.x / table.width * zoomPrev.width ;
let yTablePrev = mousepos.y / table.height * zoomPrev.height;
// 缩放后坐标
let xTableLast = mousepos.x / table.width * zoomLast.width ;
let yTableLast = mousepos.y / table.height * zoomLast.height;
table.contentX += (xTableLast - xTablePrev) / (xTablePrev / mousepos.x);
table.contentY += (yTableLast - yTablePrev) / (yTablePrev / mousepos.y);
}
}
表格模型头文件
#pragma once
#include <QAbstractTableModel>
#include <qdebug.h>
#include <opencv2/opencv.hpp>
using namespace cv;
class ImageModel : public QAbstractTableModel
{
Q_OBJECT
public:
ImageModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {};
~ImageModel() {};
public:
Q_INVOKABLE void setImage(Mat &mat) {
beginResetModel();
m_mat = mat.clone();
endResetModel();
};
public:
int rowCount(const QModelIndex & = QModelIndex()) const override
{
return m_mat.rows;
}
int columnCount(const QModelIndex & = QModelIndex()) const override
{
return m_mat.cols;
}
QVariant data(const QModelIndex& index, int role) const override;
QHash<int, QByteArray> roleNames() const override
{
return { {Qt::DisplayRole, "display"}, {Qt::UserRole + 1, "bgcolor"}, {Qt::UserRole + 2, "fgcolor"} };
}
private:
Mat m_mat;
};
表格模型源文件
#include "ImageModel.h"
#include <qcolor.h>
QVariant ImageModel::data(const QModelIndex & index, int role) const
{
QVariant result;
if (m_mat.empty()) {
return result;
}
switch (m_mat.channels())
{
case 1:
{
uchar uval = *m_mat.ptr<uchar>(index.row(), index.column());
switch (role)
{
case Qt::DisplayRole:
{
result = QString("%1").arg(uval);
}break;
case Qt::UserRole + 1:
{
result = QColor::fromRgb(uval,uval,uval);
}break;
case Qt::UserRole + 2:
{
result = QColor::fromRgb(255 - uval, 255 - uval, 255 - uval);
}break;
}
}break;
case 3:
{
Mat_<Vec3b> vval = m_mat;
uchar v1 = vval(index.row(), index.column())[0];
uchar v2 = vval(index.row(), index.column())[1];
uchar v3 = vval(index.row(), index.column())[2];
switch (role)
{
case Qt::DisplayRole:
{
result = QString("%1\n%2\n%3").arg(v1).arg(v2).arg(v3);
//result = QString("%1, %2").arg(index.column()).arg(index.row());
}break;
case Qt::UserRole + 1:
{
result = QColor::fromRgb(v3, v2, v1);
}break;
case Qt::UserRole + 2:
{
result = QColor::fromRgb(255 - v3, 255 - v2, 255 - v1);
}break;
}
}break;
}
return result;
}
图像提供头文件
#pragma once
#include <QQuickImageProvider>
#include <opencv2/opencv.hpp>
using namespace cv;
class ImageProvider : public QQuickImageProvider
{
Q_OBJECT
public:
ImageProvider() : QQuickImageProvider(QQuickImageProvider::Image) {};
~ImageProvider() {};
public:
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
public:
void setImage(Mat &mat, int target);
Q_SIGNALS:
Q_INVOKABLE void imageUpdate(int target);
private:
QImage m_sr1_img, m_sr2_img, m_dst_img;
};
图像提供源文件
#include "ImageProvider.h"
#include <qimage.h>
#include <qdebug.h>
QImage ImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
{
int reqid = id.toInt();
QImage image;
switch (reqid)
{
case 0:
{
image = m_dst_img;
}break;
case 1:
{
image = m_sr1_img;
}break;
case 2:
{
image = m_sr2_img;
}break;
}
return image;// .scaled(requestedSize, Qt::KeepAspectRatio);
}
void ImageProvider::setImage(Mat &mat, int target)
{
QImage::Format format = QImage::Format_Invalid;
switch (mat.type())
{
case CV_8UC1:
format = QImage::Format_Grayscale8;
break;
case CV_8UC3:
format = QImage::Format_BGR888;
}
size_t size = mat.rows * mat.step;
uchar* pdata = (uchar*)malloc(size);
if (pdata != NULL) {
memcpy(pdata, mat.data, size);
}
switch (target)
{
case 0:
{
m_dst_img = QImage((uchar*)pdata, mat.cols, mat.rows, mat.step, format);
}break;
case 1:
{
m_sr1_img = QImage((uchar*)pdata, mat.cols, mat.rows, mat.step, format);
}break;
case 2:
{
m_sr2_img = QImage((uchar*)pdata, mat.cols, mat.rows, mat.step, format);
}break;
}
emit imageUpdate(target);
}