add
mapcavas添加功能:镜像和旋转,并保存配置
This commit is contained in:
@ -12,6 +12,9 @@ const double AppSettings::kDefaultScanSpeed = 1.0;
|
|||||||
const double AppSettings::kDefaultReturnSpeed = 5.0;
|
const double AppSettings::kDefaultReturnSpeed = 5.0;
|
||||||
const int AppSettings::kDefaultGonggaShanRecordPort = 666;
|
const int AppSettings::kDefaultGonggaShanRecordPort = 666;
|
||||||
const AppSettings::HyperimgDisplayMode AppSettings::kDefaultHyperimgDisplayMode = HyperimgDisplayMode::Waterfall;
|
const AppSettings::HyperimgDisplayMode AppSettings::kDefaultHyperimgDisplayMode = HyperimgDisplayMode::Waterfall;
|
||||||
|
const bool AppSettings::kDefaultImageHorizontalMirror = true;
|
||||||
|
const bool AppSettings::kDefaultImageVerticalMirror = true;
|
||||||
|
const int AppSettings::kDefaultImageRotation = 0;
|
||||||
|
|
||||||
AppSettings::AppSettings()
|
AppSettings::AppSettings()
|
||||||
: m_settings(QSettings::IniFormat, QSettings::UserScope,
|
: m_settings(QSettings::IniFormat, QSettings::UserScope,
|
||||||
@ -216,3 +219,33 @@ void AppSettings::setHyperimgDisplayMode(HyperimgDisplayMode mode)
|
|||||||
{
|
{
|
||||||
m_settings.setValue("Display/HyperimgDisplayMode", static_cast<int>(mode));
|
m_settings.setValue("Display/HyperimgDisplayMode", static_cast<int>(mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AppSettings::imageHorizontalMirror() const
|
||||||
|
{
|
||||||
|
return m_settings.value("Display/ImageHorizontalMirror", kDefaultImageHorizontalMirror).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSettings::setImageHorizontalMirror(bool value)
|
||||||
|
{
|
||||||
|
m_settings.setValue("Display/ImageHorizontalMirror", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppSettings::imageVerticalMirror() const
|
||||||
|
{
|
||||||
|
return m_settings.value("Display/ImageVerticalMirror", kDefaultImageVerticalMirror).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSettings::setImageVerticalMirror(bool value)
|
||||||
|
{
|
||||||
|
m_settings.setValue("Display/ImageVerticalMirror", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppSettings::imageRotation() const
|
||||||
|
{
|
||||||
|
return m_settings.value("Display/ImageRotation", kDefaultImageRotation).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSettings::setImageRotation(int value)
|
||||||
|
{
|
||||||
|
m_settings.setValue("Display/ImageRotation", ((value % 360) + 360) % 360);
|
||||||
|
}
|
||||||
|
|||||||
@ -73,6 +73,18 @@ public:
|
|||||||
// 图像显示模式
|
// 图像显示模式
|
||||||
HyperimgDisplayMode hyperimgDisplayMode() const;
|
HyperimgDisplayMode hyperimgDisplayMode() const;
|
||||||
void setHyperimgDisplayMode(HyperimgDisplayMode mode);
|
void setHyperimgDisplayMode(HyperimgDisplayMode mode);
|
||||||
|
|
||||||
|
// 图像显示变换(左右镜像/上下镜像/旋转),全局生效
|
||||||
|
bool imageHorizontalMirror() const;
|
||||||
|
void setImageHorizontalMirror(bool value);
|
||||||
|
|
||||||
|
bool imageVerticalMirror() const;
|
||||||
|
void setImageVerticalMirror(bool value);
|
||||||
|
|
||||||
|
// 旋转角度,取值 0、90、180、270
|
||||||
|
int imageRotation() const;
|
||||||
|
void setImageRotation(int value);
|
||||||
|
|
||||||
// 在此处添加更多参数的 getter/setter ...
|
// 在此处添加更多参数的 getter/setter ...
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -94,4 +106,7 @@ private:
|
|||||||
static const double kDefaultReturnSpeed;
|
static const double kDefaultReturnSpeed;
|
||||||
static const int kDefaultGonggaShanRecordPort;
|
static const int kDefaultGonggaShanRecordPort;
|
||||||
static const HyperimgDisplayMode kDefaultHyperimgDisplayMode;
|
static const HyperimgDisplayMode kDefaultHyperimgDisplayMode;
|
||||||
|
static const bool kDefaultImageHorizontalMirror;
|
||||||
|
static const bool kDefaultImageVerticalMirror;
|
||||||
|
static const int kDefaultImageRotation;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -959,6 +959,9 @@ void HPPA::initMenubarToolbar()
|
|||||||
|
|
||||||
ui.mActionPan->setIcon(QIcon(":/svg/resources/icons/svg/pan.svg"));
|
ui.mActionPan->setIcon(QIcon(":/svg/resources/icons/svg/pan.svg"));
|
||||||
ui.mActionSpectral->setIcon(QIcon(":/svg/resources/icons/svg/spectral.svg"));
|
ui.mActionSpectral->setIcon(QIcon(":/svg/resources/icons/svg/spectral.svg"));
|
||||||
|
ui.mActionRotation90->setIcon(QIcon(":/svg/resources/icons/svg/rotate90.svg"));
|
||||||
|
ui.mActionVerticalMirror->setIcon(QIcon(":/svg/resources/icons/svg/VerticalMirror.svg"));
|
||||||
|
ui.mActionHorizontalMirror->setIcon(QIcon(":/svg/resources/icons/svg/HorizontalMirror.svg"));
|
||||||
|
|
||||||
connect(ui.mActionPan, &QAction::toggled, this, [=](bool checked) {
|
connect(ui.mActionPan, &QAction::toggled, this, [=](bool checked) {
|
||||||
if (checked)
|
if (checked)
|
||||||
@ -974,6 +977,34 @@ void HPPA::initMenubarToolbar()
|
|||||||
ui.mActionSpectral->setIcon(QIcon(":/svg/resources/icons/svg/spectral.svg"));
|
ui.mActionSpectral->setIcon(QIcon(":/svg/resources/icons/svg/spectral.svg"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ui.mActionRotation90, &QAction::triggered, this, [=](bool) {
|
||||||
|
QWidget* currentWidget = m_imageViewerTabWidget->currentWidget();
|
||||||
|
if (!currentWidget) return;
|
||||||
|
|
||||||
|
QList<Mapcavas*> canvases = currentWidget->findChildren<Mapcavas*>();
|
||||||
|
if (canvases.isEmpty()) return;
|
||||||
|
|
||||||
|
canvases[0]->rotateImage90();
|
||||||
|
});
|
||||||
|
connect(ui.mActionVerticalMirror, &QAction::triggered, this, [=](bool) {
|
||||||
|
QWidget* currentWidget = m_imageViewerTabWidget->currentWidget();
|
||||||
|
if (!currentWidget) return;
|
||||||
|
|
||||||
|
QList<Mapcavas*> canvases = currentWidget->findChildren<Mapcavas*>();
|
||||||
|
if (canvases.isEmpty()) return;
|
||||||
|
|
||||||
|
canvases[0]->toggleVerticalMirror();
|
||||||
|
});
|
||||||
|
connect(ui.mActionHorizontalMirror, &QAction::triggered, this, [=](bool) {
|
||||||
|
QWidget* currentWidget = m_imageViewerTabWidget->currentWidget();
|
||||||
|
if (!currentWidget) return;
|
||||||
|
|
||||||
|
QList<Mapcavas*> canvases = currentWidget->findChildren<Mapcavas*>();
|
||||||
|
if (canvases.isEmpty()) return;
|
||||||
|
|
||||||
|
canvases[0]->toggleHorizontalMirror();
|
||||||
|
});
|
||||||
|
|
||||||
// 使用样式表设置透明背景
|
// 使用样式表设置透明背景
|
||||||
toolBar->setStyleSheet(R"(
|
toolBar->setStyleSheet(R"(
|
||||||
QToolBar {
|
QToolBar {
|
||||||
|
|||||||
@ -32,6 +32,9 @@
|
|||||||
<file>resources/icons/svg/tree_tri_down.svg</file>
|
<file>resources/icons/svg/tree_tri_down.svg</file>
|
||||||
<file>resources/icons/svg/tree_tri_right.svg</file>
|
<file>resources/icons/svg/tree_tri_right.svg</file>
|
||||||
<file>resources/icons/svg/mIconRaster.svg</file>
|
<file>resources/icons/svg/mIconRaster.svg</file>
|
||||||
|
<file>resources/icons/svg/HorizontalMirror.svg</file>
|
||||||
|
<file>resources/icons/svg/rotate90.svg</file>
|
||||||
|
<file>resources/icons/svg/VerticalMirror.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/png">
|
<qresource prefix="/png">
|
||||||
<file>resources/icons/png/Spectral_Insight_27.png</file>
|
<file>resources/icons/png/Spectral_Insight_27.png</file>
|
||||||
|
|||||||
24
HPPA/HPPA.ui
24
HPPA/HPPA.ui
@ -203,6 +203,9 @@ QToolBar QToolButton:hover {
|
|||||||
<addaction name="actionOpenDirectory"/>
|
<addaction name="actionOpenDirectory"/>
|
||||||
<addaction name="mActionPan"/>
|
<addaction name="mActionPan"/>
|
||||||
<addaction name="mActionSpectral"/>
|
<addaction name="mActionSpectral"/>
|
||||||
|
<addaction name="mActionVerticalMirror"/>
|
||||||
|
<addaction name="mActionHorizontalMirror"/>
|
||||||
|
<addaction name="mActionRotation90"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusBar">
|
<widget class="QStatusBar" name="statusBar">
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
@ -758,6 +761,27 @@ QPushButton:pressed
|
|||||||
<string>贡嘎山旋转平台</string>
|
<string>贡嘎山旋转平台</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="mActionVerticalMirror">
|
||||||
|
<property name="text">
|
||||||
|
<string>上下镜像</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="mActionHorizontalMirror">
|
||||||
|
<property name="text">
|
||||||
|
<string>左右镜像</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>左右镜像</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="mActionRotation90">
|
||||||
|
<property name="text">
|
||||||
|
<string>旋转90度</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>旋转90度</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
|||||||
@ -49,7 +49,8 @@ Mapcavas::Mapcavas(QWidget* pParent) :QGraphicsView(pParent)
|
|||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setFrameShape(QFrame::NoFrame);
|
setFrameShape(QFrame::NoFrame);
|
||||||
|
|
||||||
m_displayMode = AppSettings::instance().hyperimgDisplayMode();
|
loadSettings();
|
||||||
|
//addSceneCoordinateSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
Mapcavas::~Mapcavas()
|
Mapcavas::~Mapcavas()
|
||||||
@ -57,6 +58,94 @@ Mapcavas::~Mapcavas()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mapcavas::loadSettings()
|
||||||
|
{
|
||||||
|
updateDisplayMode();
|
||||||
|
|
||||||
|
//更新控制图像变换的变量m_imageTransformState
|
||||||
|
AppSettings& settings = AppSettings::instance();
|
||||||
|
m_imageTransformState.horizontalMirror = settings.imageHorizontalMirror();
|
||||||
|
m_imageTransformState.verticalMirror = settings.imageVerticalMirror();
|
||||||
|
m_imageTransformState.rotation = settings.imageRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::applyImageTransform()
|
||||||
|
{
|
||||||
|
if (m_GraphicsPixmapItemHandle == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const double sx = m_imageTransformState.horizontalMirror ? -1.0 : 1.0;
|
||||||
|
const double sy = m_imageTransformState.verticalMirror ? -1.0 : 1.0;
|
||||||
|
const QPointF center = m_GraphicsPixmapItemHandle->boundingRect().center();
|
||||||
|
|
||||||
|
QTransform transform_all;
|
||||||
|
transform_all.translate(center.x(), center.y());// 围绕图片中心变换
|
||||||
|
transform_all.scale(sx, sy);
|
||||||
|
transform_all.rotate(m_imageTransformState.rotation);
|
||||||
|
transform_all.translate(-center.x(), -center.y());
|
||||||
|
|
||||||
|
// setTransform 是替换而非叠加,因此可对已显示的图像重复调用
|
||||||
|
m_GraphicsPixmapItemHandle->setTransform(transform_all);
|
||||||
|
|
||||||
|
//QPointF itemOrigin(0, 0);
|
||||||
|
//QPointF sceneOrigin = m_GraphicsPixmapItemHandle->mapToScene(itemOrigin);
|
||||||
|
//QPointF sceneOrigin2 = m_GraphicsPixmapItemHandle->scenePos();
|
||||||
|
|
||||||
|
// 变换后重新定位十字叉,使其始终跟随同一个图像像素
|
||||||
|
repositionCrosshair();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::repositionCrosshair()
|
||||||
|
{
|
||||||
|
if (!m_hLine || !m_vLine || !m_GraphicsPixmapItemHandle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QPointF scenePt = m_GraphicsPixmapItemHandle->mapToScene(m_crosshairItemPos);
|
||||||
|
m_hLine->setPos(scenePt);
|
||||||
|
m_vLine->setPos(scenePt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::refreshImageTransform()
|
||||||
|
{
|
||||||
|
if (!HasImage())
|
||||||
|
return;
|
||||||
|
|
||||||
|
applyImageTransform();
|
||||||
|
|
||||||
|
if (m_displayMode == AppSettings::HyperimgDisplayMode::Full)
|
||||||
|
{
|
||||||
|
ensureSceneVisible();
|
||||||
|
}
|
||||||
|
else if (m_displayMode == AppSettings::HyperimgDisplayMode::Waterfall)
|
||||||
|
{
|
||||||
|
ensureWaterfallVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::rotateImage90()
|
||||||
|
{
|
||||||
|
AppSettings& settings = AppSettings::instance();
|
||||||
|
settings.setImageRotation(settings.imageRotation() + 90);
|
||||||
|
loadSettings();
|
||||||
|
refreshImageTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::toggleHorizontalMirror()
|
||||||
|
{
|
||||||
|
AppSettings& settings = AppSettings::instance();
|
||||||
|
settings.setImageHorizontalMirror(!settings.imageHorizontalMirror());
|
||||||
|
loadSettings();
|
||||||
|
refreshImageTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mapcavas::toggleVerticalMirror()
|
||||||
|
{
|
||||||
|
AppSettings& settings = AppSettings::instance();
|
||||||
|
settings.setImageVerticalMirror(!settings.imageVerticalMirror());
|
||||||
|
loadSettings();
|
||||||
|
refreshImageTransform();
|
||||||
|
}
|
||||||
|
|
||||||
void Mapcavas::updateDisplayMode()
|
void Mapcavas::updateDisplayMode()
|
||||||
{
|
{
|
||||||
m_displayMode = AppSettings::instance().hyperimgDisplayMode();
|
m_displayMode = AppSettings::instance().hyperimgDisplayMode();
|
||||||
@ -79,14 +168,12 @@ void Mapcavas::SetImage(QPixmap *image)
|
|||||||
m_GraphicsPixmapItemHandle->setPixmap(*image);
|
m_GraphicsPixmapItemHandle->setPixmap(*image);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_displayMode == AppSettings::HyperimgDisplayMode::Full)
|
refreshImageTransform();
|
||||||
{
|
}
|
||||||
ensureSceneVisible();
|
|
||||||
}
|
QGraphicsPixmapItem* Mapcavas::pixmapItemHandle()
|
||||||
else if (m_displayMode == AppSettings::HyperimgDisplayMode::Waterfall)
|
{
|
||||||
{
|
return m_GraphicsPixmapItemHandle;
|
||||||
ensureWaterfallVisible();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mapcavas::ensureSceneVisible()
|
void Mapcavas::ensureSceneVisible()
|
||||||
@ -108,28 +195,46 @@ void Mapcavas::ensureSceneVisible()
|
|||||||
|
|
||||||
void Mapcavas::ensureWaterfallVisible()
|
void Mapcavas::ensureWaterfallVisible()
|
||||||
{
|
{
|
||||||
resetTransform();
|
if (!HasImage())
|
||||||
|
|
||||||
auto items = this->scene()->items();
|
|
||||||
if (items.isEmpty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto scene_rect = this->scene()->itemsBoundingRect();
|
resetTransform();
|
||||||
|
|
||||||
|
QGraphicsPixmapItem* item = m_GraphicsPixmapItemHandle;
|
||||||
|
const QRectF item_rect = item->boundingRect();
|
||||||
|
if (item_rect.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 图像已经应用了 m_imageTransformState 的镜像/旋转,这里以场景中的实际位置为准
|
||||||
|
const QRectF scene_rect = item->sceneBoundingRect();
|
||||||
|
|
||||||
qreal view_width = viewport()->rect().width();
|
qreal view_width = viewport()->rect().width();
|
||||||
qreal view_height = viewport()->rect().height();
|
qreal view_height = viewport()->rect().height();
|
||||||
|
if (view_width <= 0.0 || view_height <= 0.0)
|
||||||
|
return;
|
||||||
|
if (scene_rect.width() <= 0.0 || scene_rect.height() <= 0.0)
|
||||||
|
return;
|
||||||
|
|
||||||
double x_scale = view_width / scene_rect.width();
|
// 图像坐标中:x 为扫描线空间轴(固定),y 为采集帧数(不断增长),最新一帧位于 y 最大处。
|
||||||
double y_scale = view_height / scene_rect.height();
|
// 经过镜像/旋转后,增长轴可能竖直(0°/180°)或水平(90°/270°)。
|
||||||
|
const QPointF growth_dir = item->mapToScene(QPointF(0.0, 1.0))
|
||||||
|
- item->mapToScene(QPointF(0.0, 0.0));
|
||||||
|
const bool growth_vertical = qAbs(growth_dir.y()) >= qAbs(growth_dir.x());
|
||||||
|
|
||||||
double scale_factor = qMin(x_scale, y_scale) * 0.9;
|
// 最新一帧(图像底边中心)在场景中的位置,用于判断应该贴哪条边
|
||||||
|
const QPointF newest_scene = item->mapToScene(
|
||||||
|
QPointF(item_rect.width() / 2.0, item_rect.height()));
|
||||||
|
|
||||||
|
if (growth_vertical)
|
||||||
|
{
|
||||||
|
// 竖直瀑布:空间轴横向铺满视图宽度,最新一帧固定在上/下边缘
|
||||||
|
const double x_scale = view_width / scene_rect.width();
|
||||||
scale(x_scale, x_scale);
|
scale(x_scale, x_scale);
|
||||||
m_scale *= x_scale;
|
m_scale *= x_scale;
|
||||||
|
|
||||||
// 计算缩放后的图片可见高度
|
// 计算缩放后的图片可见高度
|
||||||
qreal scaled_height = scene_rect.height() * x_scale;
|
qreal scaled_height = scene_rect.height() * x_scale;
|
||||||
|
|
||||||
// 根据图片高度决定 Y 坐标
|
|
||||||
qreal center_y;
|
qreal center_y;
|
||||||
if (scaled_height <= view_height)
|
if (scaled_height <= view_height)
|
||||||
{
|
{
|
||||||
@ -138,10 +243,40 @@ void Mapcavas::ensureWaterfallVisible()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
qreal half_view_height = view_height / 2.0 / x_scale;
|
qreal half_view_height = view_height / 2.0 / x_scale;
|
||||||
center_y = scene_rect.bottom() - half_view_height;
|
if (newest_scene.y() >= scene_rect.center().y())
|
||||||
|
center_y = scene_rect.bottom() - half_view_height; // 最新帧在下方,贴底
|
||||||
|
else
|
||||||
|
center_y = scene_rect.top() + half_view_height; // 最新帧在上方,贴顶
|
||||||
}
|
}
|
||||||
|
|
||||||
centerOn(scene_rect.center().x(), center_y);
|
centerOn(scene_rect.center().x(), center_y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 水平瀑布:空间轴竖向铺满视图高度,最新一帧固定在左/右边缘
|
||||||
|
const double y_scale = view_height / scene_rect.height();
|
||||||
|
scale(y_scale, y_scale);
|
||||||
|
m_scale *= y_scale;
|
||||||
|
|
||||||
|
// 计算缩放后的图片可见宽度
|
||||||
|
qreal scaled_width = scene_rect.width() * y_scale;
|
||||||
|
|
||||||
|
qreal center_x;
|
||||||
|
if (scaled_width <= view_width)
|
||||||
|
{
|
||||||
|
center_x = scene_rect.center().x();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qreal half_view_width = view_width / 2.0 / y_scale;
|
||||||
|
if (newest_scene.x() >= scene_rect.center().x())
|
||||||
|
center_x = scene_rect.right() - half_view_width; // 最新帧在右侧,贴右
|
||||||
|
else
|
||||||
|
center_x = scene_rect.left() + half_view_width; // 最新帧在左侧,贴左
|
||||||
|
}
|
||||||
|
|
||||||
|
centerOn(center_x, scene_rect.center().y());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mapcavas::HasImage()
|
bool Mapcavas::HasImage()
|
||||||
@ -164,21 +299,32 @@ void Mapcavas::updateCrosshair(double sceneX, double sceneY)
|
|||||||
if (!m_hLine)
|
if (!m_hLine)
|
||||||
{
|
{
|
||||||
m_hLine = m_qtGraphicsScene->addLine(0, 0, 0, 0, pen);
|
m_hLine = m_qtGraphicsScene->addLine(0, 0, 0, 0, pen);
|
||||||
|
m_hLine->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
|
||||||
m_hLine->setZValue(1e9);
|
m_hLine->setZValue(1e9);
|
||||||
}
|
}
|
||||||
if (!m_vLine)
|
if (!m_vLine)
|
||||||
{
|
{
|
||||||
m_vLine = m_qtGraphicsScene->addLine(0, 0, 0, 0, pen);
|
m_vLine = m_qtGraphicsScene->addLine(0, 0, 0, 0, pen);
|
||||||
|
m_vLine->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
|
||||||
m_vLine->setZValue(1e9);
|
m_vLine->setZValue(1e9);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hLine->setPen(pen);
|
m_hLine->setPen(pen);
|
||||||
m_vLine->setPen(pen);
|
m_vLine->setPen(pen);
|
||||||
|
|
||||||
m_hLine->setLine(sceneX - m_CrosshairHalfLen, sceneY,
|
// 设置 ItemIgnoresTransformations 后,图元的原点依旧按场景坐标定位,
|
||||||
sceneX + m_CrosshairHalfLen, sceneY);
|
// 但局部坐标不再受视图缩放影响(1 单位 = 1 屏幕像素)。
|
||||||
m_vLine->setLine(sceneX, sceneY - m_CrosshairHalfLen,
|
// 这里把锚点记录在图像(图元)坐标系中,再据此定位十字叉:
|
||||||
sceneX, sceneY + m_CrosshairHalfLen);
|
// 这样点击某个像素后,无论滚轮缩放还是镜像/旋转图像,十字叉都锚定在该像素上。
|
||||||
|
const QPointF scenePt(sceneX + 0.5, sceneY + 0.5);
|
||||||
|
m_crosshairItemPos = m_GraphicsPixmapItemHandle->mapFromScene(scenePt);
|
||||||
|
|
||||||
|
repositionCrosshair();
|
||||||
|
|
||||||
|
m_hLine->setLine(-m_CrosshairHalfLen, 0.0,
|
||||||
|
m_CrosshairHalfLen, 0.0);
|
||||||
|
m_vLine->setLine(0.0, -m_CrosshairHalfLen,
|
||||||
|
0.0, m_CrosshairHalfLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mapcavas::removeCrosshair()
|
void Mapcavas::removeCrosshair()
|
||||||
@ -197,6 +343,7 @@ void Mapcavas::removeCrosshair()
|
|||||||
delete m_vLine;
|
delete m_vLine;
|
||||||
m_vLine = nullptr;
|
m_vLine = nullptr;
|
||||||
}
|
}
|
||||||
|
m_crosshairItemPos = QPointF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -376,3 +523,91 @@ MapTool* Mapcavas::mapTool() const
|
|||||||
{
|
{
|
||||||
return m_mapTool;
|
return m_mapTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mapcavas::addSceneCoordinateSystem()
|
||||||
|
{
|
||||||
|
const double length = 200.0;
|
||||||
|
const double arrowSize = 10.0;
|
||||||
|
|
||||||
|
// 坐标系作为参考叠加层,需要始终绘制在图像和其他图元之上;
|
||||||
|
// 十字叉使用更大的 Z 值(1e9),因此这里取一个略小的值。
|
||||||
|
const double zOrder = 1e8;
|
||||||
|
|
||||||
|
// X 轴
|
||||||
|
QPen xPen(Qt::red);
|
||||||
|
xPen.setWidth(2);
|
||||||
|
|
||||||
|
QGraphicsLineItem* xAxis = m_qtGraphicsScene->addLine(
|
||||||
|
0, 0,
|
||||||
|
length, 0,
|
||||||
|
xPen
|
||||||
|
);
|
||||||
|
xAxis->setZValue(zOrder);
|
||||||
|
|
||||||
|
// X 箭头
|
||||||
|
QPolygonF xArrow;
|
||||||
|
xArrow << QPointF(length, 0)
|
||||||
|
<< QPointF(length - arrowSize, -arrowSize / 2)
|
||||||
|
<< QPointF(length - arrowSize, arrowSize / 2);
|
||||||
|
|
||||||
|
QGraphicsPolygonItem* xArrowItem = m_qtGraphicsScene->addPolygon(
|
||||||
|
xArrow,
|
||||||
|
xPen,
|
||||||
|
QBrush(Qt::red)
|
||||||
|
);
|
||||||
|
xArrowItem->setZValue(zOrder);
|
||||||
|
|
||||||
|
|
||||||
|
// Y 轴
|
||||||
|
QPen yPen(Qt::green);
|
||||||
|
yPen.setWidth(2);
|
||||||
|
|
||||||
|
QGraphicsLineItem* yAxis = m_qtGraphicsScene->addLine(
|
||||||
|
0, 0,
|
||||||
|
0, length,
|
||||||
|
yPen
|
||||||
|
);
|
||||||
|
yAxis->setZValue(zOrder);
|
||||||
|
|
||||||
|
// Y 箭头
|
||||||
|
QPolygonF yArrow;
|
||||||
|
yArrow << QPointF(0, length)
|
||||||
|
<< QPointF(-arrowSize / 2, length - arrowSize)
|
||||||
|
<< QPointF(arrowSize / 2, length - arrowSize);
|
||||||
|
|
||||||
|
QGraphicsPolygonItem* yArrowItem = m_qtGraphicsScene->addPolygon(
|
||||||
|
yArrow,
|
||||||
|
yPen,
|
||||||
|
QBrush(Qt::green)
|
||||||
|
);
|
||||||
|
yArrowItem->setZValue(zOrder);
|
||||||
|
|
||||||
|
|
||||||
|
// 原点
|
||||||
|
QGraphicsEllipseItem* originItem = m_qtGraphicsScene->addEllipse(
|
||||||
|
-4, -4,
|
||||||
|
8, 8,
|
||||||
|
QPen(Qt::blue),
|
||||||
|
QBrush(Qt::blue)
|
||||||
|
);
|
||||||
|
originItem->setZValue(zOrder);
|
||||||
|
|
||||||
|
|
||||||
|
// X 标签
|
||||||
|
QGraphicsTextItem* xText = m_qtGraphicsScene->addText("X");
|
||||||
|
xText->setDefaultTextColor(Qt::red);
|
||||||
|
xText->setPos(length + 5, -15);
|
||||||
|
xText->setZValue(zOrder);
|
||||||
|
|
||||||
|
// Y 标签
|
||||||
|
QGraphicsTextItem* yText = m_qtGraphicsScene->addText("Y");
|
||||||
|
yText->setDefaultTextColor(Qt::green);
|
||||||
|
yText->setPos(5, length + 5);
|
||||||
|
yText->setZValue(zOrder);
|
||||||
|
|
||||||
|
// 原点标签
|
||||||
|
QGraphicsTextItem* originText = m_qtGraphicsScene->addText("(0, 0)");
|
||||||
|
originText->setDefaultTextColor(Qt::blue);
|
||||||
|
originText->setPos(5, 5);
|
||||||
|
originText->setZValue(zOrder);
|
||||||
|
}
|
||||||
|
|||||||
@ -61,6 +61,16 @@ public:
|
|||||||
void unsetMapTool(MapTool* tool);
|
void unsetMapTool(MapTool* tool);
|
||||||
MapTool* mapTool() const;
|
MapTool* mapTool() const;
|
||||||
|
|
||||||
|
QGraphicsPixmapItem* pixmapItemHandle();
|
||||||
|
|
||||||
|
// 图像显示变换(左右镜像/上下镜像/旋转),状态保存在 AppSettings 中全局生效
|
||||||
|
void rotateImage90();
|
||||||
|
void toggleHorizontalMirror();
|
||||||
|
void toggleVerticalMirror();
|
||||||
|
|
||||||
|
// 把当前的镜像/旋转重新应用到已经显示的图像,并按显示模式重新适配视图
|
||||||
|
void refreshImageTransform();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QGraphicsScene *m_qtGraphicsScene;
|
QGraphicsScene *m_qtGraphicsScene;
|
||||||
private:
|
private:
|
||||||
@ -79,9 +89,24 @@ private:
|
|||||||
double m_CrosshairHalfLen = 10.0;
|
double m_CrosshairHalfLen = 10.0;
|
||||||
QGraphicsLineItem* m_hLine = nullptr; // horizontal line
|
QGraphicsLineItem* m_hLine = nullptr; // horizontal line
|
||||||
QGraphicsLineItem* m_vLine = nullptr; // vertical line
|
QGraphicsLineItem* m_vLine = nullptr; // vertical line
|
||||||
|
QPointF m_crosshairItemPos; // 十字叉锚点,保存在图像(图元)坐标系
|
||||||
|
|
||||||
AppSettings::HyperimgDisplayMode m_displayMode;
|
AppSettings::HyperimgDisplayMode m_displayMode;
|
||||||
|
|
||||||
|
struct ImageTransformState
|
||||||
|
{
|
||||||
|
bool horizontalMirror = true;
|
||||||
|
bool verticalMirror = true;
|
||||||
|
int rotation = 90*3; // 0、90、180、270
|
||||||
|
};
|
||||||
|
ImageTransformState m_imageTransformState;
|
||||||
|
|
||||||
|
void applyImageTransform();
|
||||||
|
void repositionCrosshair();
|
||||||
|
void loadSettings();
|
||||||
|
|
||||||
|
void addSceneCoordinateSystem();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void leftMouseButtonPressed(int, int, QVector<double>, QVector<double>);
|
void leftMouseButtonPressed(int, int, QVector<double>, QVector<double>);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,21 +37,35 @@ void MapToolSpectral::canvasMousePressEvent(QMouseEvent* e)
|
|||||||
if (!canvas())
|
if (!canvas())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QPointF scenePt = canvas()->mapToScene(e->pos());
|
//获取图像元素
|
||||||
const int x = static_cast<int>(std::floor(scenePt.x()));
|
QGraphicsPixmapItem* item = canvas()->pixmapItemHandle();
|
||||||
const int y = static_cast<int>(std::floor(scenePt.y()));
|
QPointF scenePos = item->pos();
|
||||||
|
QPointF scenePos2 = item->scenePos();
|
||||||
|
|
||||||
|
if (item==nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF scenePt = canvas()->mapToScene(e->pos());
|
||||||
|
int x_scenePt = static_cast<int>(std::floor(scenePt.x()));
|
||||||
|
int y_scenePt = static_cast<int>(std::floor(scenePt.y()));
|
||||||
|
|
||||||
|
QPointF itemPt = item->mapFromScene(scenePt);
|
||||||
|
int x_itemPt = static_cast<int>(std::floor(itemPt.x()));
|
||||||
|
int y_itemPt = static_cast<int>(std::floor(itemPt.y()));
|
||||||
|
|
||||||
auto* imageLayer = canvas()->imageLayer();
|
auto* imageLayer = canvas()->imageLayer();
|
||||||
RasterLayer* rl = imageLayer ? imageLayer->layer() : nullptr;
|
RasterLayer* rl = imageLayer ? imageLayer->layer() : nullptr;
|
||||||
if (rl && rl->isValidPixel(x, y))
|
if (rl && rl->isValidPixel(x_itemPt, y_itemPt))
|
||||||
{
|
{
|
||||||
canvas()->updateCrosshair(x + 0.5, y + 0.5);
|
canvas()->updateCrosshair(x_scenePt + 0.5, y_scenePt + 0.5);
|
||||||
|
|
||||||
QVector<double> wavelengths;
|
QVector<double> wavelengths;
|
||||||
QVector<double> spectrum;
|
QVector<double> spectrum;
|
||||||
if (rl->readPixelSpectrum(x, y, wavelengths, spectrum))
|
if (rl->readPixelSpectrum(x_itemPt, y_itemPt, wavelengths, spectrum))
|
||||||
{
|
{
|
||||||
emit spectralClicked(x, y, wavelengths, spectrum);
|
emit spectralClicked(x_itemPt, y_itemPt, wavelengths, spectrum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user