Gui: add an image view

This commit is contained in:
wmayer
2023-03-19 12:22:49 +01:00
parent 771315cfdc
commit 3cd81b9bb3
5 changed files with 514 additions and 2 deletions

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -697,6 +697,7 @@ MenuItem* StdWorkbench::setupMenuBar() const
*tool << "Std_DlgParameter"
<< "Separator"
<< "Std_ViewScreenShot"
<< "Std_ViewLoadImage"
<< "Std_SceneInspector"
<< "Std_DependencyGraph"
<< "Std_ProjectUtil"