Gui: add an image view
This commit is contained in:
@@ -1066,6 +1066,7 @@ SET(View_CPP_SRCS
|
||||
MDIViewPy.cpp
|
||||
MDIViewPyWrap.cpp
|
||||
GraphvizView.cpp
|
||||
ImageView.cpp
|
||||
ActiveObjectList.cpp
|
||||
)
|
||||
SET(View_HPP_SRCS
|
||||
@@ -1073,6 +1074,7 @@ SET(View_HPP_SRCS
|
||||
MDIViewPy.h
|
||||
MDIViewPyWrap.h
|
||||
GraphvizView.h
|
||||
ImageView.h
|
||||
ActiveObjectList.h
|
||||
)
|
||||
SET(View_SRCS
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
# include <QFileInfo>
|
||||
# include <QFont>
|
||||
# include <QFontMetrics>
|
||||
# include <QImageReader>
|
||||
# include <QMessageBox>
|
||||
# include <QPainter>
|
||||
# include <QPointer>
|
||||
@@ -62,6 +63,7 @@
|
||||
#include "DlgSettingsImageImp.h"
|
||||
#include "Document.h"
|
||||
#include "FileDialog.h"
|
||||
#include "ImageView.h"
|
||||
#include "Macro.h"
|
||||
#include "MainWindow.h"
|
||||
#include "NavigationStyle.h"
|
||||
@@ -1807,12 +1809,12 @@ StdViewScreenShot::StdViewScreenShot()
|
||||
: Command("Std_ViewScreenShot")
|
||||
{
|
||||
sGroup = "Standard-View";
|
||||
sMenuText = QT_TR_NOOP("Save picture...");
|
||||
sMenuText = QT_TR_NOOP("Save image...");
|
||||
sToolTipText= QT_TR_NOOP("Creates a screenshot of the active view");
|
||||
sWhatsThis = "Std_ViewScreenShot";
|
||||
sStatusTip = QT_TR_NOOP("Creates a screenshot of the active view");
|
||||
sPixmap = "camera-photo";
|
||||
eType = Alter3DView;
|
||||
eType = Alter3DView;
|
||||
}
|
||||
|
||||
void StdViewScreenShot::activated(int iMsg)
|
||||
@@ -1969,6 +1971,51 @@ bool StdViewScreenShot::isActive()
|
||||
return isViewOfType(Gui::View3DInventor::getClassTypeId());
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_ViewLoadImage
|
||||
//===========================================================================
|
||||
DEF_STD_CMD(StdViewLoadImage)
|
||||
|
||||
StdViewLoadImage::StdViewLoadImage()
|
||||
: Command("Std_ViewLoadImage")
|
||||
{
|
||||
sGroup = "Standard-View";
|
||||
sMenuText = QT_TR_NOOP("Load image...");
|
||||
sToolTipText= QT_TR_NOOP("Loads a image");
|
||||
sWhatsThis = "Std_ViewLoadPicture";
|
||||
sStatusTip = QT_TR_NOOP("Loads a image");
|
||||
//sPixmap = "camera-photo";
|
||||
eType = 0;
|
||||
}
|
||||
|
||||
void StdViewLoadImage::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
// add all supported QImage formats
|
||||
QStringList mimeTypeFilters;
|
||||
QList<QByteArray> supportedMimeTypes = QImageReader::supportedMimeTypes();
|
||||
for (const auto& mimeTypeName : supportedMimeTypes) {
|
||||
mimeTypeFilters.append(QString::fromLatin1(mimeTypeName));
|
||||
}
|
||||
|
||||
// Reading an image
|
||||
QFileDialog dialog(Gui::getMainWindow());
|
||||
dialog.setWindowTitle(QObject::tr("Choose an image file to open"));
|
||||
dialog.setMimeTypeFilters(mimeTypeFilters);
|
||||
dialog.selectMimeTypeFilter(QString::fromLatin1("image/png"));
|
||||
dialog.setDefaultSuffix(QString::fromLatin1("png"));
|
||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
dialog.setOption(QFileDialog::DontUseNativeDialog);
|
||||
|
||||
if (dialog.exec()) {
|
||||
QString fileName = dialog.selectedFiles().constFirst();
|
||||
ImageView* view = new ImageView(Gui::getMainWindow());
|
||||
view->loadFile(fileName);
|
||||
view->resize(400, 300);
|
||||
Gui::getMainWindow()->addWindow(view);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_ViewCreate
|
||||
@@ -3775,6 +3822,7 @@ void CreateViewStdCommands()
|
||||
|
||||
rcCmdMgr.addCommand(new StdCmdViewCreate());
|
||||
rcCmdMgr.addCommand(new StdViewScreenShot());
|
||||
rcCmdMgr.addCommand(new StdViewLoadImage());
|
||||
rcCmdMgr.addCommand(new StdMainFullscreen());
|
||||
rcCmdMgr.addCommand(new StdViewDockUndockFullscreen());
|
||||
rcCmdMgr.addCommand(new StdCmdSetAppearance());
|
||||
|
||||
363
src/Gui/ImageView.cpp
Normal file
363
src/Gui/ImageView.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QAction>
|
||||
# include <QApplication>
|
||||
# include <QContextMenuEvent>
|
||||
# include <QClipboard>
|
||||
# include <QCursor>
|
||||
# include <QFileInfo>
|
||||
# include <QImageReader>
|
||||
# include <QLabel>
|
||||
# include <QMenu>
|
||||
# include <QMessageBox>
|
||||
# include <QMimeData>
|
||||
# include <QPainter>
|
||||
# include <QPixmap>
|
||||
# include <QPrintDialog>
|
||||
# include <QPrinter>
|
||||
# include <QScrollArea>
|
||||
# include <QScrollBar>
|
||||
#endif
|
||||
|
||||
#include "ImageView.h"
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
ImageView::ImageView(QWidget* parent)
|
||||
: MDIView(nullptr, parent)
|
||||
, imageLabel(new QLabel)
|
||||
, scrollArea(new QScrollArea)
|
||||
, scaleFactor{1.0}
|
||||
, dragging{false}
|
||||
{
|
||||
imageLabel->setBackgroundRole(QPalette::Base);
|
||||
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
imageLabel->setScaledContents(true);
|
||||
|
||||
scrollArea->setBackgroundRole(QPalette::Dark);
|
||||
scrollArea->setWidget(imageLabel);
|
||||
scrollArea->setVisible(false);
|
||||
setCentralWidget(scrollArea);
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
bool ImageView::loadFile(const QString& fileName)
|
||||
{
|
||||
QImageReader reader(fileName);
|
||||
reader.setAutoTransform(true);
|
||||
QImage image = reader.read();
|
||||
if (image.isNull()) {
|
||||
QMessageBox::information(this, tr("Failed to load image file"),
|
||||
tr("Cannot load file %1: %2")
|
||||
.arg(fileName, reader.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
setImage(image);
|
||||
setWindowFilePath(fileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImageView::setImage(const QImage& image)
|
||||
{
|
||||
rawImage = image;
|
||||
imageLabel->setPixmap(QPixmap::fromImage(image));
|
||||
imageLabel->adjustSize();
|
||||
scrollArea->setVisible(true);
|
||||
scaleFactor = 1.0;
|
||||
}
|
||||
|
||||
void ImageView::scaleImage(double factor)
|
||||
{
|
||||
scaleFactor *= factor;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
|
||||
imageLabel->resize(scaleFactor * imageLabel->pixmap(Qt::ReturnByValue).size());
|
||||
#else
|
||||
imageLabel->resize(scaleFactor * imageLabel->pixmap()->size());
|
||||
#endif
|
||||
|
||||
adjustScrollBar(scrollArea->horizontalScrollBar(), factor);
|
||||
adjustScrollBar(scrollArea->verticalScrollBar(), factor);
|
||||
}
|
||||
|
||||
void ImageView::adjustScrollBar(QScrollBar *scrollBar, double factor)
|
||||
{
|
||||
scrollBar->setValue(int(factor * scrollBar->value()
|
||||
+ ((factor - 1) * scrollBar->pageStep()/2)));
|
||||
}
|
||||
|
||||
bool ImageView::canZoomIn() const
|
||||
{
|
||||
int maxWidth{10000};
|
||||
return !isFitToWindow() && imageLabel->width() < maxWidth;
|
||||
}
|
||||
|
||||
bool ImageView::canZoomOut() const
|
||||
{
|
||||
int minWidth{200};
|
||||
return !isFitToWindow() && imageLabel->width() > minWidth;
|
||||
}
|
||||
|
||||
void ImageView::zoomIn()
|
||||
{
|
||||
double scale{1.25};
|
||||
scaleImage(scale);
|
||||
}
|
||||
|
||||
void ImageView::zoomOut()
|
||||
{
|
||||
double scale{0.8};
|
||||
scaleImage(scale);
|
||||
}
|
||||
|
||||
void ImageView::normalSize()
|
||||
{
|
||||
imageLabel->adjustSize();
|
||||
scaleFactor = 1.0;
|
||||
}
|
||||
|
||||
void ImageView::fitToWindow(bool fitView)
|
||||
{
|
||||
scrollArea->setWidgetResizable(fitView);
|
||||
if (!fitView) {
|
||||
normalSize();
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageView::isFitToWindow() const
|
||||
{
|
||||
return scrollArea->widgetResizable();
|
||||
}
|
||||
|
||||
bool ImageView::canDrag() const
|
||||
{
|
||||
return scrollArea->verticalScrollBar()->isVisible() ||
|
||||
scrollArea->horizontalScrollBar()->isVisible();
|
||||
}
|
||||
|
||||
void ImageView::startDrag()
|
||||
{
|
||||
dragging = true;
|
||||
}
|
||||
|
||||
void ImageView::stopDrag()
|
||||
{
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
bool ImageView::isDragging() const
|
||||
{
|
||||
return dragging;
|
||||
}
|
||||
|
||||
void ImageView::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction* fitToWindowAct = menu.addAction(tr("Fit to window"));
|
||||
fitToWindowAct->setCheckable(true);
|
||||
fitToWindowAct->setChecked(isFitToWindow());
|
||||
connect(fitToWindowAct, &QAction::toggled, this, &ImageView::fitToWindow);
|
||||
|
||||
QAction* zoomInAct = menu.addAction(tr("Zoom in"), this, &ImageView::zoomIn);
|
||||
zoomInAct->setEnabled(canZoomIn());
|
||||
|
||||
QAction* zoomOutAct = menu.addAction(tr("Zoom out"), this, &ImageView::zoomOut);
|
||||
zoomOutAct->setEnabled(canZoomOut());
|
||||
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
void ImageView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->buttons().testFlag(Qt::MiddleButton)) {
|
||||
if (canDrag()) {
|
||||
setCursor(QCursor(Qt::ClosedHandCursor));
|
||||
startDrag();
|
||||
dragPos = event->pos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!event->buttons().testFlag(Qt::MiddleButton)) {
|
||||
if (isDragging()) {
|
||||
stopDrag();
|
||||
unsetCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if (isDragging()) {
|
||||
QScrollBar* hBar = scrollArea->horizontalScrollBar();
|
||||
QScrollBar* vBar = scrollArea->verticalScrollBar();
|
||||
QPoint delta = event->pos() - dragPos;
|
||||
hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
|
||||
vBar->setValue(vBar->value() - delta.y());
|
||||
dragPos = event->pos();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::dropEvent(QDropEvent* event)
|
||||
{
|
||||
const QMimeData* data = event->mimeData();
|
||||
if (data->hasUrls()) {
|
||||
loadImageFromUrl(data->urls());
|
||||
}
|
||||
else {
|
||||
MDIView::dropEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::dragEnterEvent(QDragEnterEvent* event)
|
||||
{
|
||||
const QMimeData* data = event->mimeData();
|
||||
if (data->hasUrls()) {
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::loadImageFromUrl(const QList<QUrl>& urls)
|
||||
{
|
||||
if (urls.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl& url = urls.first();
|
||||
const QFileInfo info(url.toLocalFile());
|
||||
if (info.exists() && info.isFile()) {
|
||||
loadFile(info.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
void ImageView::pasteImage()
|
||||
{
|
||||
QImage image = imageFromClipboard();
|
||||
if (!image.isNull()) {
|
||||
setImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageView::canPasteImage() const
|
||||
{
|
||||
return !imageFromClipboard().isNull();
|
||||
}
|
||||
|
||||
QImage ImageView::imageFromClipboard()
|
||||
{
|
||||
QImage image;
|
||||
if (const QMimeData *mimeData = QApplication::clipboard()->mimeData()) {
|
||||
if (mimeData->hasImage()) {
|
||||
image = qvariant_cast<QImage>(mimeData->imageData());
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void ImageView::print(QPrinter* printer)
|
||||
{
|
||||
QPrintDialog dialog(printer, this);
|
||||
if (dialog.exec()) {
|
||||
QPainter painter(printer);
|
||||
QPixmap pixmap = QPixmap::fromImage(rawImage);
|
||||
QRect rect = painter.viewport();
|
||||
QSize size = pixmap.size();
|
||||
size.scale(rect.size(), Qt::KeepAspectRatio);
|
||||
painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
|
||||
painter.setWindow(pixmap.rect());
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageView::onMsg(const char* pMsg,const char**)
|
||||
{
|
||||
if (strcmp("ViewFit", pMsg) == 0) {
|
||||
fitToWindow(true);
|
||||
return true;
|
||||
}
|
||||
if (strcmp("ZoomIn", pMsg) == 0) {
|
||||
zoomIn();
|
||||
return true;
|
||||
}
|
||||
if (strcmp("ZoomOut", pMsg) == 0) {
|
||||
zoomOut();
|
||||
return true;
|
||||
}
|
||||
if (strcmp("Paste", pMsg) == 0) {
|
||||
pasteImage();
|
||||
return true;
|
||||
}
|
||||
if (strcmp("Print", pMsg) == 0) {
|
||||
print();
|
||||
return true;
|
||||
}
|
||||
if (strcmp("PrintPreview", pMsg) == 0) {
|
||||
printPreview();
|
||||
return true;
|
||||
}
|
||||
if (strcmp("PrintPdf", pMsg) == 0) {
|
||||
printPdf();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageView::onHasMsg(const char* pMsg) const
|
||||
{
|
||||
if (strcmp("ViewFit", pMsg) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp("ZoomIn", pMsg) == 0) {
|
||||
return canZoomIn();
|
||||
}
|
||||
if (strcmp("ZoomOut", pMsg) == 0) {
|
||||
return canZoomOut();
|
||||
}
|
||||
if (strcmp("Paste", pMsg) == 0) {
|
||||
return canPasteImage();
|
||||
}
|
||||
if (strcmp("Print", pMsg) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp("PrintPreview", pMsg) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp("PrintPdf", pMsg) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "moc_ImageView.cpp"
|
||||
98
src/Gui/ImageView.h
Normal file
98
src/Gui/ImageView.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GUI_IMAGE_VIEW_H
|
||||
#define GUI_IMAGE_VIEW_H
|
||||
|
||||
#include <Gui/MDIView.h>
|
||||
|
||||
class QLabel;
|
||||
class QScrollArea;
|
||||
class QScrollBar;
|
||||
class QUrl;
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class GuiExport ImageView : public MDIView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ImageView(QWidget* parent);
|
||||
bool loadFile(const QString &);
|
||||
|
||||
const char *getName() const override {
|
||||
return "ImageView";
|
||||
}
|
||||
|
||||
/// Message handler
|
||||
bool onMsg(const char* pMsg, const char** ppReturn) override;
|
||||
/// Message handler test
|
||||
bool onHasMsg(const char* pMsg) const override;
|
||||
|
||||
/** @name Printing */
|
||||
//@{
|
||||
using MDIView::print;
|
||||
void print(QPrinter* printer) override;
|
||||
//@}
|
||||
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
|
||||
private:
|
||||
void setImage(const QImage& image);
|
||||
void scaleImage(double factor);
|
||||
static void adjustScrollBar(QScrollBar *scrollBar, double factor);
|
||||
bool canZoomIn() const;
|
||||
bool canZoomOut() const;
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void normalSize();
|
||||
void fitToWindow(bool fitView);
|
||||
bool isFitToWindow() const;
|
||||
bool canDrag() const;
|
||||
void startDrag();
|
||||
void stopDrag();
|
||||
bool isDragging() const;
|
||||
void pasteImage();
|
||||
bool canPasteImage() const;
|
||||
static QImage imageFromClipboard();
|
||||
void loadImageFromUrl(const QList<QUrl>&);
|
||||
|
||||
private:
|
||||
QImage rawImage;
|
||||
QLabel *imageLabel;
|
||||
QScrollArea *scrollArea;
|
||||
double scaleFactor;
|
||||
bool dragging;
|
||||
QPoint dragPos;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //GUI_IMAGE_VIEW_H
|
||||
@@ -697,6 +697,7 @@ MenuItem* StdWorkbench::setupMenuBar() const
|
||||
*tool << "Std_DlgParameter"
|
||||
<< "Separator"
|
||||
<< "Std_ViewScreenShot"
|
||||
<< "Std_ViewLoadImage"
|
||||
<< "Std_SceneInspector"
|
||||
<< "Std_DependencyGraph"
|
||||
<< "Std_ProjectUtil"
|
||||
|
||||
Reference in New Issue
Block a user