Kstars

imageviewer.cpp
1/*
2 SPDX-FileCopyrightText: 2001 Thomas Kabelmann <tk78@gmx.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "imageviewer.h"
8#include "Options.h"
9
10#ifndef KSTARS_LITE
11#include "kstars.h"
12#include <KMessageBox>
13#include <KFormat>
14#endif
15
16#include "ksnotification.h"
17#include <QFileDialog>
18#include <QJsonObject>
19#include <QPainter>
20#include <QResizeEvent>
21#include <QScreen>
22#include <QStatusBar>
23#include <QTemporaryFile>
24#include <QVBoxLayout>
25#include <QPushButton>
26#include <QApplication>
27
28QUrl ImageViewer::lastURL = QUrl::fromLocalFile(QDir::homePath());
29
30ImageLabel::ImageLabel(QWidget *parent) : QFrame(parent)
31{
32#ifndef KSTARS_LITE
34 setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
35 setLineWidth(2);
36#endif
37}
38
39void ImageLabel::setImage(const QImage &img)
40{
41#ifndef KSTARS_LITE
42 m_Image = img;
43 pix = QPixmap::fromImage(m_Image);
44#endif
45}
46
47void ImageLabel::invertPixels()
48{
49#ifndef KSTARS_LITE
50 m_Image.invertPixels();
52#endif
53}
54
55void ImageLabel::paintEvent(QPaintEvent *)
56{
57#ifndef KSTARS_LITE
58 QPainter p;
59 p.begin(this);
60 int x = 0;
61 if (pix.width() < width())
62 x = (width() - pix.width()) / 2;
63 p.drawPixmap(x, 0, pix);
64 p.end();
65#endif
66}
67
68void ImageLabel::resizeEvent(QResizeEvent *event)
69{
70 int w = pix.width();
71 int h = pix.height();
72
73 if (event->size().width() == w && event->size().height() == h)
74 return;
75
77}
78
79ImageViewer::ImageViewer(const QString &caption, QWidget *parent) : QDialog(parent), fileIsImage(false), downloadJob(nullptr)
80{
81#ifndef KSTARS_LITE
82 init(caption, QString());
83#endif
84}
85
87 : QDialog(parent), m_ImageUrl(url)
88{
89#ifndef KSTARS_LITE
90 init(url.fileName(), capText);
91
92 // check URL
93 if (!m_ImageUrl.isValid())
94 qDebug() << Q_FUNC_INFO << "URL is malformed: " << m_ImageUrl;
95
96 if (m_ImageUrl.isLocalFile())
97 {
98 loadImage(m_ImageUrl.toLocalFile());
99 return;
100 }
101
102 {
104 tempfile.open();
105 file.setFileName(tempfile.fileName());
106 } // we just need the name and delete the tempfile from disc; if we don't do it, a dialog will be show
107
108 loadImageFromURL();
109#endif
110}
111
112void ImageViewer::init(QString caption, QString capText)
113{
114#ifndef KSTARS_LITE
116 setModal(false);
117 setWindowTitle(i18nc("@title:window", "KStars image viewer: %1", caption));
118
119 // Create widget
120 QFrame *page = new QFrame(this);
121
122 //setMainWidget( page );
123 QVBoxLayout *mainLayout = new QVBoxLayout;
124 mainLayout->addWidget(page);
125 setLayout(mainLayout);
126
128 mainLayout->addWidget(buttonBox);
129 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
130
131 QPushButton *invertB = new QPushButton(i18n("Invert colors"));
132 invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects "
133 "only the display and not the saving."));
134 QPushButton *saveB = new QPushButton(QIcon::fromTheme("document-save"), i18n("Save"));
135 saveB->setToolTip(i18n("Save the image to disk"));
136
139
140 connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors()));
141 connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisc()));
142
143 m_View = new ImageLabel(page);
144 m_View->setAutoFillBackground(true);
145 m_Caption = new QLabel(page);
146 m_Caption->setAutoFillBackground(true);
148 m_Caption->setText(capText);
149 // Add layout
150 QVBoxLayout *vlay = new QVBoxLayout(page);
151 vlay->setSpacing(0);
152 vlay->setContentsMargins(0, 0, 0, 0);
153 vlay->addWidget(m_View);
154 vlay->addWidget(m_Caption);
155
156 //Reverse colors
157 QPalette p = palette();
160 m_Caption->setPalette(p);
161 m_View->setPalette(p);
162
163 //If the caption is wider than the image, try to shrink the font a bit
164 QFont capFont = m_Caption->font();
165 capFont.setPointSize(capFont.pointSize() - 2);
166 m_Caption->setFont(capFont);
167#endif
168}
169
171{
172 QString filename = file.fileName();
173 if (filename.startsWith(QLatin1String("/tmp/")) || filename.contains("/Temp"))
174 {
175 if (m_ImageUrl.isEmpty() == false ||
176 KMessageBox::questionYesNo(nullptr, i18n("Remove temporary file %1 from disk?", filename),
177 i18n("Confirm Removal"), KStandardGuiItem::yes(), KStandardGuiItem::no(),
178 "imageviewer_temporary_file_removal") == KMessageBox::Yes)
179 QFile::remove(filename);
180 }
181
183}
184
185void ImageViewer::loadImageFromURL()
186{
187#ifndef KSTARS_LITE
189
190 if (!saveURL.isValid())
191 qWarning() << "tempfile-URL is malformed";
192
194
195 downloadJob.setProgressDialogEnabled(true, i18n("Download"),
196 i18n("Please wait while image is being downloaded..."));
197 connect(&downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady()));
198 connect(&downloadJob, SIGNAL(canceled()), this, SLOT(close()));
199 connect(&downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
200
201 downloadJob.get(m_ImageUrl);
202#endif
203}
204
205void ImageViewer::downloadReady()
206{
207#ifndef KSTARS_LITE
209
210 if (file.open(QFile::WriteOnly))
211 {
212 file.write(downloadJob.downloadedData());
213 file.close(); // to get the newest information from the file and not any information from opening of the file
214
215 if (file.exists())
216 {
217 showImage();
218 return;
219 }
220
221 close();
222 }
223 else
224 KSNotification::error(file.errorString(), i18n("Image Viewer"));
225#endif
226}
227
228void ImageViewer::downloadError(const QString &errorString)
229{
230#ifndef KSTARS_LITE
232 KSNotification::error(errorString);
233#endif
234}
235
236bool ImageViewer::loadImage(const QString &filename)
237{
238#ifndef KSTARS_LITE
239 // If current file is temporary, remove from disk.
240 if (file.fileName().startsWith(QLatin1String("/tmp/")) || file.fileName().contains("/Temp"))
241 QFile::remove(file.fileName());
242
243 file.setFileName(filename);
244 return showImage();
245#else
246 return false;
247#endif
248}
249
250bool ImageViewer::showImage()
251{
252#ifndef KSTARS_LITE
253 QImage image;
254
255 if (!image.load(file.fileName()))
256 {
257 KSNotification::error(i18n("Loading of the image %1 failed.", m_ImageUrl.url()));
258 close();
259 return false;
260 }
261
262 fileIsImage = true; // we loaded the file and know now, that it is an image
263
264 //If the image is larger than screen width and/or screen height,
265 //shrink it to fit the screen
267 int w = deskRect.width(); // screen width
268 int h = deskRect.height(); // screen height
269
270 if (image.width() <= w && image.height() > h) //Window is taller than desktop
271 image = image.scaled(int(image.width() * h / image.height()), h);
272 else if (image.height() <= h && image.width() > w) //window is wider than desktop
273 image = image.scaled(w, int(image.height() * w / image.width()));
274 else if (image.width() > w && image.height() > h) //window is too tall and too wide
275 {
276 //which needs to be shrunk least, width or height?
277 float fx = float(w) / float(image.width());
278 float fy = float(h) / float(image.height());
279 if (fx > fy) //width needs to be shrunk less, so shrink to fit in height
280 image = image.scaled(int(image.width() * fy), h);
281 else //vice versa
282 image = image.scaled(w, int(image.height() * fx));
283 }
284
285 show(); // hide is default
286
287 m_View->setImage(image);
288 w = image.width();
289
290 //If the caption is wider than the image, set the window size
291 //to fit the caption
292 if (m_Caption->width() > w)
293 w = m_Caption->width();
294 //setFixedSize( w, image.height() + m_Caption->height() );
295
296 resize(w, image.height());
297 update();
298 show();
299
300 return true;
301#else
302 return false;
303#endif
304}
305
306void ImageViewer::saveFileToDisc()
307{
308#ifndef KSTARS_LITE
309 QFileDialog dialog;
310
311 QUrl newURL =
312 dialog.getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save Image"), lastURL); // save-dialog with default filename
313 if (!newURL.isEmpty())
314 {
315 //QFile f (newURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile() + '/' + newURL.fileName());
316 QFile f(newURL.toLocalFile());
317 if (f.exists())
318 {
320 i18n("A file named \"%1\" already exists. "
321 "Overwrite it?",
322 newURL.fileName()),
323 i18n("Overwrite File?"), KStandardGuiItem::overwrite()) == KMessageBox::Cancel))
324 return;
325
326 f.remove();
327 }
328
329 lastURL = QUrl(newURL.toString(QUrl::RemoveFilename));
330
331 saveFile(newURL);
332 }
333#endif
334}
335
336void ImageViewer::saveFile(QUrl &url)
337{
338 // synchronous access to prevent segfaults
339
340 //if (!KIO::NetAccess::file_copy (QUrl (file.fileName()), url, (QWidget*) 0))
341 //QUrl tmpURL((file.fileName()));
342 //tmpURL.setScheme("file");
343
344 if (file.copy(url.toLocalFile()) == false)
345 //if (KIO::file_copy(tmpURL, url)->exec() == false)
346 {
347 QString text = i18n("Saving of the image %1 failed.", url.toString());
348#ifndef KSTARS_LITE
349 KSNotification::error(text);
350#else
351 qDebug() << Q_FUNC_INFO << text;
352#endif
353 }
354#ifndef KSTARS_LITE
355 else
356 KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString()));
357#endif
358}
359
360void ImageViewer::invertColors()
361{
362#ifndef KSTARS_LITE
363 // Invert colors
364 m_View->invertPixels();
365 m_View->update();
366#endif
367}
368
369QJsonObject ImageViewer::metadata()
370{
371#ifndef KSTARS_LITE
373 if (m_View && !m_View->m_Image.isNull())
374 {
375 md.insert("resolution", QString("%1x%2").arg(m_View->m_Image.width()).arg(m_View->m_Image.height()));
376 // sizeInBytes is only available in 5.10+
377 md.insert("size", KFormat().formatByteSize(m_View->m_Image.bytesPerLine() * m_View->m_Image.height()));
378 md.insert("bin", "1x1");
379 md.insert("bpp", QString::number(m_View->m_Image.bytesPerLine() / m_View->m_Image.width() * 8));
380 }
381 return md;
382#endif
383}
~ImageViewer() override
Destructor.
bool loadImage(const QString &filename)
loadImage Load image from local file and display it
ImageViewer(const QString &caption, QWidget *parent=nullptr)
Creates empty viewer.
static KStars * Instance()
Definition kstars.h:123
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem overwrite()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void setSpacing(int spacing) override
void setModal(bool modal)
virtual void reject()
void rejected()
QPushButton * addButton(StandardButton button)
QString homePath()
bool copy(const QString &fileName, const QString &newName)
bool exists(const QString &fileName)
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
bool remove()
void setFileName(const QString &name)
virtual void close() override
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
virtual bool event(QEvent *e) override
void setFrameShape(Shape)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QIcon fromTheme(const QString &name)
qsizetype bytesPerLine() const const
int height() const const
void invertPixels(InvertMode mode)
bool isNull() const const
bool load(QIODevice *device, const char *format)
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
int width() const const
QString errorString() const const
qint64 write(const QByteArray &data)
void setText(const QString &)
void setContentsMargins(const QMargins &margins)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool begin(QPaintDevice *device)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
bool end()
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
int width() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
KeepAspectRatio
WaitCursor
SmoothTransformation
WA_DeleteOnClose
RemoveFilename
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
void setAutoFillBackground(bool enabled)
bool close()
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setLayout(QLayout *layout)
void show()
void resize(const QSize &)
void update()
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:48:25 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.