Compare commits

...

7 Commits

Author SHA1 Message Date
nou 33344ce413 Update icons 2026-05-05 18:52:57 +02:00
nou 0a11289f34 Add automatic check for new versions 2026-04-28 17:09:49 +02:00
nou 53167e7bb5 Implement fits_memfile workaround
CFITSIO doesn't support non-ASCI characters in file paths
2026-04-27 18:40:57 +02:00
nou 1e940de31b Use std::filesystem::path for XISF files 2026-04-27 18:39:41 +02:00
nou f1b5ec81c0 Workaround for CFITSIO failing to load path containing non-ASCI chars 2026-04-19 13:25:32 +02:00
nou 8b8759facb Delay restoring state of database header 2026-04-13 13:30:54 +02:00
nou 468862ad35 Do not print debug logs by default 2026-04-13 13:29:54 +02:00
28 changed files with 291 additions and 111 deletions
+6 -1
View File
@@ -39,6 +39,7 @@ set(TENMON_SRC
src/filemanager.h src/filemanager.cpp src/filemanager.ui src/filemanager.h src/filemanager.cpp src/filemanager.ui
src/filesystemwidget.cpp src/filesystemwidget.h src/filesystemwidget.cpp src/filesystemwidget.h
src/fitskeyword.ui src/fitskeyword.ui
src/fitswrapper.h src/fitswrapper.cpp
src/histogram.cpp src/histogram.h src/histogram.cpp src/histogram.h
src/httpdownloader.h src/httpdownloader.cpp src/httpdownloader.h src/httpdownloader.cpp
src/imageinfo.cpp src/imageinfo.h src/imageinfo.cpp src/imageinfo.h
@@ -133,8 +134,12 @@ if(UNIX AND NOT APPLE)
install(SCRIPT install.cmake) install(SCRIPT install.cmake)
else() else()
install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications") install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
install(FILES resources/space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps") install(FILES resources/space.nouspiro.tenmon_16.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/16x16/apps" RENAME space.nouspiro.tenmon.png)
install(FILES resources/space.nouspiro.tenmon_32.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/32x32/apps" RENAME space.nouspiro.tenmon.png)
install(FILES resources/space.nouspiro.tenmon_64.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps" RENAME space.nouspiro.tenmon.png)
install(FILES resources/space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png) install(FILES resources/space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
install(FILES resources/space.nouspiro.tenmon_256.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps" RENAME space.nouspiro.tenmon.png)
install(FILES resources/space.nouspiro.tenmon_512.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps" RENAME space.nouspiro.tenmon.png)
install(FILES space.nouspiro.tenmon.xisf.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages") install(FILES space.nouspiro.tenmon.xisf.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages")
endif() endif()
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo") install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
+1 -1
View File
@@ -1,5 +1,5 @@
<table><tr> <table><tr>
<td style="padding-right:10px"><img src=":/space.nouspiro.tenmon.png"></td> <td style="padding-right:10px"><img src=":/space.nouspiro.tenmon_64.png"></td>
<td><h3>Tenmon</h3> <td><h3>Tenmon</h3>
Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br> Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br>
v@GITVERSION@ Copyright © 2026 Dušan Poizl<br><br> v@GITVERSION@ Copyright © 2026 Dušan Poizl<br><br>
+5 -1
View File
@@ -2,6 +2,10 @@ find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource) find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
find_program(XDG-MIME xdg-mime) find_program(XDG-MIME xdg-mime)
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 resources/space.nouspiro.tenmon.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 16 resources/space.nouspiro.tenmon_16.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 32 resources/space.nouspiro.tenmon_32.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 resources/space.nouspiro.tenmon_64.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 resources/space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 resources/space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 256 resources/space.nouspiro.tenmon_256.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 512 resources/space.nouspiro.tenmon_512.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
execute_process(COMMAND ${XDG-MIME} install --novendor space.nouspiro.tenmon.xisf.xml WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) execute_process(COMMAND ${XDG-MIME} install --novendor space.nouspiro.tenmon.xisf.xml WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
+1 -1
Submodule libXISF updated: 7b70b6a081...de757840b3
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

+1 -1
View File
@@ -15,7 +15,7 @@
<file>bggr.png</file> <file>bggr.png</file>
<file>grbg.png</file> <file>grbg.png</file>
<file>gbrg.png</file> <file>gbrg.png</file>
<file>space.nouspiro.tenmon.png</file> <file>space.nouspiro.tenmon_64.png</file>
<file>../translations/tenmon_pt_BR.qm</file> <file>../translations/tenmon_pt_BR.qm</file>
<file alias="help">../about/help_en</file> <file alias="help">../about/help_en</file>
<file>colormap.png</file> <file>colormap.png</file>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.
+1 -1
View File
@@ -337,7 +337,7 @@ void BatchProcessing::newMessageCli(const QString &message, bool error)
if(error) if(error)
qWarning() << message; qWarning() << message;
else else
qDebug() << message; qInfo() << message;
} }
QJSValue BatchProcessing::getString(const QString &label, const QString &text) QJSValue BatchProcessing::getString(const QString &label, const QString &text)
+2 -2
View File
@@ -447,7 +447,7 @@ DatabaseTreeView::DatabaseTreeView(Database *database, QWidget *parent) : QWidge
,_database(database) ,_database(database)
{ {
QVBoxLayout *vlayout = new QVBoxLayout(this); QVBoxLayout *vlayout = new QVBoxLayout(this);
QHBoxLayout *hlayout = new QHBoxLayout(this); QHBoxLayout *hlayout = new QHBoxLayout;
_model = new DatabaseTree(database, this); _model = new DatabaseTree(database, this);
_treeView = new QTreeView(this); _treeView = new QTreeView(this);
@@ -473,7 +473,7 @@ DatabaseTreeView::DatabaseTreeView(Database *database, QWidget *parent) : QWidge
connect(_filters, &QComboBox::currentIndexChanged, this, &DatabaseTreeView::filterChanged); connect(_filters, &QComboBox::currentIndexChanged, this, &DatabaseTreeView::filterChanged);
filterChanged(_filters->currentIndex()); filterChanged(_filters->currentIndex());
QStackedWidget *stackedWidget = new QStackedWidget; QStackedWidget *stackedWidget = new QStackedWidget(this);
stackedWidget->addWidget(_treeView); stackedWidget->addWidget(_treeView);
stackedWidget->addWidget(_tableView); stackedWidget->addWidget(_tableView);
+21 -17
View File
@@ -1,19 +1,20 @@
#include "databaseview.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QSettings>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QSqlError>
#include <QDebug>
#include <QMenu>
#include <QContextMenuEvent>
#include <QRegularExpression>
#include <QGuiApplication>
#include <QClipboard>
#include <QMimeData>
#include <iostream>
#include "batchprocessing.h" #include "batchprocessing.h"
#include "databaseview.h"
#include <QClipboard>
#include <QContextMenuEvent>
#include <QDebug>
#include <QDialogButtonBox>
#include <QGuiApplication>
#include <QHeaderView>
#include <QMenu>
#include <QMimeData>
#include <QPushButton>
#include <QRegularExpression>
#include <QSettings>
#include <QSqlError>
#include <QTimer>
#include <QVBoxLayout>
#include <iostream>
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"}; const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
@@ -248,7 +249,7 @@ void FITSFileModel::prepareQuery()
{ {
setHeaderData(i++, Qt::Horizontal, column); setHeaderData(i++, Qt::Horizontal, column);
} }
std::cout << sql.toStdString() << std::endl; qDebug() << "DB SQL" << sql;
if(lastError().type() != QSqlError::NoError) if(lastError().type() != QSqlError::NoError)
qDebug() << "Database error" << lastError(); qDebug() << "Database error" << lastError();
@@ -318,9 +319,12 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
m_model = new FITSFileModel(m_database, this); m_model = new FITSFileModel(m_database, this);
QSettings settings; QSettings settings;
m_tableView->setModel(m_model);
m_model->setColumns(settings.value("databaseview/selectedColumns", DEFAULT_COLUMNS).toStringList()); m_model->setColumns(settings.value("databaseview/selectedColumns", DEFAULT_COLUMNS).toStringList());
m_tableView->setModel(m_model);
QTimer::singleShot(200, [this](){
QSettings settings;
m_tableView->horizontalHeader()->restoreState(settings.value("databaseview/header").toByteArray()); m_tableView->horizontalHeader()->restoreState(settings.value("databaseview/header").toByteArray());
});
QHBoxLayout *hlayout = new QHBoxLayout(); QHBoxLayout *hlayout = new QHBoxLayout();
layout->addLayout(hlayout); layout->addLayout(hlayout);
+89
View File
@@ -0,0 +1,89 @@
#include "fitswrapper.h"
#include <QDebug>
#include <QFile>
FITSWrapper::FITSWrapper(const QString &path, int mode, bool open)
{
_path = path;
_mode = mode;
if(open)
fits_open_diskfile(&_file, path.toLocal8Bit().data(), mode, &_status);
else
fits_create_diskfile(&_file, path.toLocal8Bit().data(), &_status);
if(_status == FILE_NOT_OPENED || _status == FILE_NOT_CREATED)
{
qWarning() << "Could not open file directly trying memfile workaround" << path;
if(open)
{
QFile fr(path);
if(fr.open(QIODevice::ReadOnly))
{
_bufferSize = fr.size();
_bufferPtr = malloc(_bufferSize);
fr.read((char*)_bufferPtr, _bufferSize);
_status = 0;
fits_open_memfile(&_file, "memfile", mode, &_bufferPtr, &_bufferSize, 0, realloc, &_status);
if(_status)
{
free(_bufferPtr);
_bufferPtr = nullptr;
_bufferSize = 0;
qWarning() << "fits_open_memfile failed";
}
}
else
{
qWarning() << "QFile failed to open file" << path;
_status = FILE_NOT_OPENED;
}
}
else
{
_bufferSize = 2880;
_bufferPtr = malloc(_bufferSize);
fits_create_memfile(&_file, &_bufferPtr, &_bufferSize, 0, realloc, &_status);
if(_status)
{
free(_bufferPtr);
_bufferPtr = nullptr;
_bufferSize = 0;
}
}
}
}
FITSWrapper::~FITSWrapper()
{
if(_file)
{
_status = 0;
fits_close_file(_file, &_status);
}
if(_mode == READWRITE && _bufferPtr)
{
qDebug() << "Writing FITS memfile" << _bufferSize;
QFile fw(_path);
if(fw.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
if(fw.write((char*)_bufferPtr, _bufferSize) != (qint64)_bufferSize)
qWarning() << "Failed to write to file";
}
else
qWarning() << "Failed to open fits file for writing";
}
free(_bufferPtr);
}
int FITSWrapper::status() const
{
return _status;
}
FITSWrapper::operator fitsfile *()
{
return _file;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef FITSWRAPPER_H
#define FITSWRAPPER_H
#include <QByteArray>
#include <QString>
#include <fitsio.h>
class FITSWrapper
{
public:
explicit FITSWrapper(const QString &path, int mode, bool open);
~FITSWrapper();
FITSWrapper(FITSWrapper &other) = delete;
int status() const;
operator fitsfile*();
private:
QString _path;
int _status = 0;
int _mode = 0;
fitsfile *_file;
size_t _bufferSize = 0;
void *_bufferPtr = nullptr;
};
#endif // FITSWRAPPER_H
+19 -18
View File
@@ -1,13 +1,13 @@
#include "loadimage.h" #include "loadimage.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
#include <libraw/libraw.h>
#include <fitsio2.h>
#include "libxisf.h" #include "libxisf.h"
#include <libexif/exif-data.h>
#include "rawimage.h" #include "rawimage.h"
#include <QDebug>
#include <QDir>
#include <QElapsedTimer>
#include <QFileInfo>
#include <libexif/exif-data.h>
#include <libraw/libraw.h>
#include "fitswrapper.h"
QString makeUNCPath(const QString &path) QString makeUNCPath(const QString &path)
{ {
@@ -85,7 +85,6 @@ int loadFITSHeader(fitsfile *file, ImageInfoData &info)
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index) bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index)
{ {
fitsfile *file;
int status = 0; int status = 0;
int num = 0; int num = 0;
long naxes[3] = {0}; long naxes[3] = {0};
@@ -99,7 +98,9 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
return false; return false;
}; };
fits_open_diskfile(&file, path.toLocal8Bit().data(), READONLY, &status); FITSWrapper file(path, READONLY, true);
status = file.status();
if(status)return checkError(); if(status)return checkError();
fits_get_num_hdus(file, &num, &status); fits_get_num_hdus(file, &num, &status);
if(status)return checkError(); if(status)return checkError();
@@ -222,7 +223,6 @@ noload:
} }
} }
fits_close_file(file, &status);
return true; return true;
} }
@@ -231,7 +231,8 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
try try
{ {
LibXISF::XISFReader xisf; LibXISF::XISFReader xisf;
xisf.open(path.toLocal8Bit().data()); QFileInfo fileInfo(path);
xisf.open(fileInfo.filesystemFilePath());
if(index >= (uint32_t)xisf.imagesCount())return false; if(index >= (uint32_t)xisf.imagesCount())return false;
const LibXISF::Image &xisfImage = xisf.getImage(index); const LibXISF::Image &xisfImage = xisf.getImage(index);
@@ -344,15 +345,14 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
bool readFITSHeader(const QString &path, ImageInfoData &info) bool readFITSHeader(const QString &path, ImageInfoData &info)
{ {
fitsfile *fr;
int status = 0;
QString path2 = makeUNCPath(path); QString path2 = makeUNCPath(path);
fits_open_diskfile(&fr, path2.toLocal8Bit().data(), READONLY, &status);
if(fr && status == 0) FITSWrapper file(path, READONLY, true);
int status = file.status();
if(file && status == 0)
{ {
status = loadFITSHeader(fr, info); status = loadFITSHeader(file, info);
fits_close_file(fr, &status);
} }
return status == 0; return status == 0;
} }
@@ -363,7 +363,8 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
try try
{ {
LibXISF::XISFReader xisf; LibXISF::XISFReader xisf;
xisf.open(path2.toLocal8Bit().data()); QFileInfo fileInfo(path2);
xisf.open(fileInfo.filesystemFilePath());
const LibXISF::Image &image = xisf.getImage(0, false); const LibXISF::Image &image = xisf.getImage(0, false);
auto fitskeywords = image.fitsKeywords(); auto fitskeywords = image.fitsKeywords();
+10 -8
View File
@@ -1,13 +1,14 @@
#include "loadrunable.h" #include "loadrunable.h"
#include "fitswrapper.h"
#include "imageringlist.h" #include "imageringlist.h"
#include "loadimage.h"
#include "rawimage.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QFileInfo> #include <QFileInfo>
#include <QPainter> #include <QPainter>
#include <QElapsedTimer>
#include <QDebug>
#include <algorithm> #include <algorithm>
#include <fitsio2.h> #include <fitsio2.h>
#include "rawimage.h"
#include "loadimage.h"
#include <lcms2.h> #include <lcms2.h>
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail) : LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail) :
@@ -269,7 +270,8 @@ void ConvertRunable::run()
image.setByteshuffling(true); image.setByteshuffling(true);
xisf.writeImage(image); xisf.writeImage(image);
xisf.save(m_outfile.toLocal8Bit().data()); QFileInfo fileInfo(makeUNCPath(m_outfile));
xisf.save(fileInfo.filesystemFilePath());
} }
catch(LibXISF::Error &err) catch(LibXISF::Error &err)
{ {
@@ -281,9 +283,10 @@ void ConvertRunable::run()
if(m_format == "fits") if(m_format == "fits")
{ {
int status = 0; int status = 0;
fitsfile *fw;
if(QFileInfo(m_outfile).exists())QFile::remove(m_outfile); if(QFileInfo(m_outfile).exists())QFile::remove(m_outfile);
fits_create_diskfile(&fw, m_outfile.toLocal8Bit().data(), &status); FITSWrapper fw(m_outfile, READWRITE, false);
if(fw.status())return;
if(!m_params.compressionType.isEmpty()) if(!m_params.compressionType.isEmpty())
{ {
if(m_params.compressionType == "gzip") if(m_params.compressionType == "gzip")
@@ -292,7 +295,6 @@ void ConvertRunable::run()
fits_set_compression_type(fw, RICE_1, &status); fits_set_compression_type(fw, RICE_1, &status);
} }
writeFITSImage(fw, rawimage, imageinfo); writeFITSImage(fw, rawimage, imageinfo);
fits_close_file(fw, &status);
return; return;
} }
+20 -2
View File
@@ -1,15 +1,28 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <QApplication> #include <QApplication>
#include <QSurfaceFormat>
#include <QTranslator>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QSettings> #include <QSettings>
#include <QSurfaceFormat>
#include <QTranslator>
#include <stdlib.h> #include <stdlib.h>
#include "../thumbnailer/genthumbnail.h" #include "../thumbnailer/genthumbnail.h"
#ifdef Q_OS_WIN64 #ifdef Q_OS_WIN64
#include <windows.h> #include <windows.h>
#endif #endif
bool DEBUG_LOG = false;
QtMessageHandler defaultHandler = nullptr;
void messageHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
{
if(defaultHandler)
{
if(DEBUG_LOG || type != QtMsgType::QtDebugMsg)
defaultHandler(type, ctx, message);
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifdef __linux__ #ifdef __linux__
@@ -29,6 +42,7 @@ int main(int argc, char *argv[])
freopen("CONOUT$", "w", stderr); freopen("CONOUT$", "w", stderr);
} }
#endif #endif
defaultHandler = qInstallMessageHandler(messageHandler);
QCommandLineParser cmd; QCommandLineParser cmd;
cmd.addOption({"gl", "Use desktop OpenGL. This is default on x86 and MacOS platform."}); cmd.addOption({"gl", "Use desktop OpenGL. This is default on x86 and MacOS platform."});
@@ -40,6 +54,7 @@ int main(int argc, char *argv[])
cmd.addOption({"scriptarg", "String that will be passed to script as variable \"scriparg\"", "arg"}); cmd.addOption({"scriptarg", "String that will be passed to script as variable \"scriparg\"", "arg"});
cmd.addOption({"outdir", "Output dir for script (default: CWD)", "dir", "."}); cmd.addOption({"outdir", "Output dir for script (default: CWD)", "dir", "."});
cmd.addOption({"noexit", "Do not exit application when script finish"}); cmd.addOption({"noexit", "Do not exit application when script finish"});
cmd.addOption({"debug", "Print debug info"});
cmd.addHelpOption(); cmd.addHelpOption();
QStringList cmdArgs; QStringList cmdArgs;
for(int i = 0; i < argc; i++) for(int i = 0; i < argc; i++)
@@ -51,6 +66,9 @@ int main(int argc, char *argv[])
if(cmd.isSet("gles")) if(cmd.isSet("gles"))
useGLES = true; useGLES = true;
if(cmd.isSet("debug"))
DEBUG_LOG = true;
if(cmd.isSet("thumb")) if(cmd.isSet("thumb"))
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
+50 -27
View File
@@ -1,37 +1,38 @@
#include "about.h"
#include "batchprocessing.h"
#include "filemanager.h"
#include "histogram.h"
#include "loadrunable.h"
#include "mainwindow.h" #include "mainwindow.h"
#include <QScrollArea> #include "markedfiles.h"
#include "settingsdialog.h"
#include "statusbar.h"
#include <QActionGroup>
#include <QDebug>
#include <QDesktopServices>
#include <QDir> #include <QDir>
#include <QDockWidget>
#include <QFileDialog>
#include <QGuiApplication>
#include <QImageReader>
#include <QImageWriter>
#include <QJsonDocument>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <QMenuBar> #include <QMenuBar>
#include <QFileDialog>
#include <QStandardPaths>
#include <QMessageBox> #include <QMessageBox>
#include <QMimeDatabase>
#include <QNetworkReply>
#include <QProgressDialog> #include <QProgressDialog>
#include <QDebug> #include <QScrollArea>
#include <QDockWidget> #include <QSettings>
#include <QActionGroup> #include <QStandardPaths>
#include <QStatusBar>
#include <QThreadPool>
#include <QSessionManager>
#include <QTimer>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <QSettings>
#include <QGuiApplication>
#include <QThreadPool>
#include <QStatusBar>
#include <QImageReader>
#include <QImageWriter>
#include <QMimeDatabase>
#include <QDesktopServices>
#include <QJsonDocument>
#include <QNetworkReply>
#include <QTimer>
#include "loadrunable.h"
#include "markedfiles.h"
#include "about.h"
#include "statusbar.h"
#include "settingsdialog.h"
#include "histogram.h"
#include "batchprocessing.h"
#include "filemanager.h"
#ifdef __linux__ #ifdef __linux__
#include <sys/ioctl.h> #include <sys/ioctl.h>
@@ -404,6 +405,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
if(_plateSolving)_plateSolving->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); if(_plateSolving)_plateSolving->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
m_stretchPanel->setFloatable(false); m_stretchPanel->setFloatable(false);
} }
if(settings.value("settings/checkupdate", false).toBool())
{
QDateTime lastcheck = settings.value("settings/lastcheck").toDateTime();
QDateTime weekago = QDateTime::currentDateTimeUtc().addDays(-7);
if(!lastcheck.isValid() || lastcheck < weekago)
checkNewVersion(true);
}
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@@ -576,6 +585,8 @@ void MainWindow::loadFile(const QString &path)
if(!path.isEmpty()) if(!path.isEmpty())
{ {
QFileInfo info(path); QFileInfo info(path);
if(info.exists() && info.isReadable())
{
m_ringList->setFile(info.canonicalFilePath()); m_ringList->setFile(info.canonicalFilePath());
updateWindowTitle(); updateWindowTitle();
if(info.isDir()) if(info.isDir())
@@ -587,6 +598,11 @@ void MainWindow::loadFile(const QString &path)
if(settings.value("settings/bestfit", false).toBool()) if(settings.value("settings/bestfit", false).toBool())
m_image->bestFit(); m_image->bestFit();
} }
else
{
qWarning() << "File doesn't exist or is not readable";
}
}
} }
void MainWindow::loadFiles(const QStringList &paths) void MainWindow::loadFiles(const QStringList &paths)
@@ -815,21 +831,27 @@ void MainWindow::exportCSV()
m_databaseView->exportCSV(file); m_databaseView->exportCSV(file);
} }
void MainWindow::checkNewVersion() void MainWindow::checkNewVersion(bool autocheck)
{ {
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request(QUrl("https://gitea.nouspiro.space/api/v1/repos/nou/tenmon/releases/latest")); QNetworkRequest request(QUrl("https://gitea.nouspiro.space/api/v1/repos/nou/tenmon/releases/latest"));
request.setRawHeader("accept", "application/json"); request.setRawHeader("accept", "application/json");
QNetworkReply *reply = manager->get(request); QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::finished, [this, manager, reply](){ connect(reply, &QNetworkReply::finished, [this, manager, reply, autocheck](){
QJsonParseError error; QJsonParseError error;
QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error); QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error);
if(json.isObject() && json.object().contains("tag_name")) if(json.isObject() && json.object().contains("tag_name"))
{ {
QSettings settings;
settings.setValue("settings/lastcheck", QDateTime::currentDateTimeUtc());
QString tag = json.object().value("tag_name").toString(); QString tag = json.object().value("tag_name").toString();
QString version = getVersion(); QString version = getVersion();
if(version >= tag) if(version >= tag)
{
if(!autocheck)
QMessageBox::information(this, tr("Update check"), tr("You have newest version")); QMessageBox::information(this, tr("Update check"), tr("You have newest version"));
}
else else
{ {
if(QMessageBox::question(this, tr("Update check"), tr("New version %1 is available. Do you want to download it now?").arg(tag)) == QMessageBox::Yes) if(QMessageBox::question(this, tr("Update check"), tr("New version %1 is available. Do you want to download it now?").arg(tag)) == QMessageBox::Yes)
@@ -843,6 +865,7 @@ void MainWindow::checkNewVersion()
} }
else else
{ {
if(!autocheck)
QMessageBox::warning(this, tr("Update check"), tr("Failed to check version")); QMessageBox::warning(this, tr("Update check"), tr("Failed to check version"));
} }
+1 -1
View File
@@ -69,7 +69,7 @@ public slots:
void showMarkFilesDialog(); void showMarkFilesDialog();
void showSettingsDialog(); void showSettingsDialog();
void exportCSV(); void exportCSV();
void checkNewVersion(); void checkNewVersion(bool autocheck = false);
void openFileManager(); void openFileManager();
void runScript(const QString &script, const QString &outdir, const QStringList &paths, const QString &arg, bool exit); void runScript(const QString &script, const QString &outdir, const QStringList &paths, const QString &arg, bool exit);
}; };
+19 -18
View File
@@ -1,16 +1,17 @@
#include "scriptengine.h" #include "scriptengine.h"
#include <QDir> #include "batchprocessing.h"
#include <QFileInfo> #include "fitswrapper.h"
#include <QDebug> #include "libxisf.h"
#include <QInputDialog> #include "loadimage.h"
#include <QJsonValue>
#include <QJSValueIterator>
#include "loadrunable.h" #include "loadrunable.h"
#include "rawimage.h" #include "rawimage.h"
#include "loadimage.h" #include <QDebug>
#include "batchprocessing.h" #include <QDir>
#include <QFileInfo>
#include <QInputDialog>
#include <QJSValueIterator>
#include <QJsonValue>
#include <fitsio2.h> #include <fitsio2.h>
#include "libxisf.h"
#ifdef PLATESOLVER #ifdef PLATESOLVER
#include "solver.h" #include "solver.h"
#endif // PLATESOLVER #endif // PLATESOLVER
@@ -672,10 +673,11 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
if(isFITS(suffix())) if(isFITS(suffix()))
{ {
fitsfile *file;
int status = 0; int status = 0;
QString path = makeUNCPath(_path); QString path = makeUNCPath(_path);
fits_open_diskfile(&file, path.toLocal8Bit().data(), READWRITE, &status); FITSWrapper file(path, READWRITE, true);
status = file.status();
int num = 0; int num = 0;
fits_get_num_hdus(file, &num, &status); fits_get_num_hdus(file, &num, &status);
if(status) if(status)
@@ -795,8 +797,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
return false; return false;
} }
} }
fits_close_file(file, &status); if(status)qWarning() << "Failed to modify FITS header" << status;
return status == 0; return status == 0;
} }
else if(isXISF(suffix())) else if(isXISF(suffix()))
@@ -804,9 +805,9 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
try try
{ {
LibXISF::XISFModify modifyXISF; LibXISF::XISFModify modifyXISF;
QString in = makeUNCPath(absoluteFilePath()); QFileInfo in(makeUNCPath(absoluteFilePath()));
QString out = in + "~"; QFileInfo out(in.absoluteFilePath() + "~");
modifyXISF.open(in.toLocal8Bit().data()); modifyXISF.open(in.filesystemFilePath());
qDebug() << "modify" << in << out; qDebug() << "modify" << in << out;
for(auto &remove : modify->_remove) for(auto &remove : modify->_remove)
@@ -821,9 +822,9 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
for(auto &property : modify->_property) for(auto &property : modify->_property)
modifyXISF.updateProperty(modify->_imageIdx, property); modifyXISF.updateProperty(modify->_imageIdx, property);
modifyXISF.save(out.toLocal8Bit().toStdString()); modifyXISF.save(out.filesystemFilePath());
modifyXISF.close(); modifyXISF.close();
std::filesystem::rename(out.toLocal8Bit().toStdString(), in.toLocal8Bit().toStdString()); std::filesystem::rename(out.filesystemFilePath(), in.filesystemFilePath());
return true; return true;
} }
catch(std::filesystem::filesystem_error &err) catch(std::filesystem::filesystem_error &err)
+6
View File
@@ -147,6 +147,10 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
else if(lang == "sk")m_lang->setCurrentIndex(2); else if(lang == "sk")m_lang->setCurrentIndex(2);
else if(lang == "pt_BR")m_lang->setCurrentIndex(3); else if(lang == "pt_BR")m_lang->setCurrentIndex(3);
m_checkUpdate = new QCheckBox(tr("Check for new version"), this);
m_checkUpdate->setToolTip(tr("Check for new version every week"));
m_checkUpdate->setChecked(settings.value("settings/checkupdate", false).toBool());
layout->addRow(tr("Image preload count"), m_preloadImages); layout->addRow(tr("Image preload count"), m_preloadImages);
layout->addRow(tr("Thumbnails size"), m_thumSize); layout->addRow(tr("Thumbnails size"), m_thumSize);
layout->addRow(tr("Saturation"), m_saturation); layout->addRow(tr("Saturation"), m_saturation);
@@ -156,6 +160,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
layout->addRow(m_qualityThumbnail); layout->addRow(m_qualityThumbnail);
layout->addRow(m_useNativeDialog); layout->addRow(m_useNativeDialog);
layout->addRow(m_bestFit); layout->addRow(m_bestFit);
layout->addRow(m_checkUpdate);
layout->addRow(new QLabel(tr("FITS header highlight"), this)); layout->addRow(new QLabel(tr("FITS header highlight"), this));
layout->addRow(m_headerHighlight); layout->addRow(m_headerHighlight);
layout->addRow(m_keyword, color); layout->addRow(m_keyword, color);
@@ -265,4 +270,5 @@ void SettingsDialog::saveSettings()
case 3: lang = "pt_BR"; break; case 3: lang = "pt_BR"; break;
} }
settings.setValue("settings/lang", lang); settings.setValue("settings/lang", lang);
settings.setValue("settings/checkupdate", m_checkUpdate->isChecked());
} }
+1
View File
@@ -33,6 +33,7 @@ private:
QColor m_color = Qt::yellow; QColor m_color = Qt::yellow;
QLineEdit *m_keyword; QLineEdit *m_keyword;
QComboBox *m_lang; QComboBox *m_lang;
QCheckBox *m_checkUpdate;
}; };
#endif // SETTINGSDIALOG_H #endif // SETTINGSDIALOG_H
+1
View File
@@ -1,6 +1,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <fstream>
#include "../src/rawimage.h" #include "../src/rawimage.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" #include "stb_image_write.h"