Compare commits

..

6 Commits

Author SHA1 Message Date
nou 2b56af27fe Add explicit link to Svg module to solve some issues with SVG icon 2026-03-15 17:47:51 +01:00
nou 8edf746827 Use bindvalue in DatabaseTableView 2026-03-15 17:47:24 +01:00
nou 729a330e6c Add backspace as move to trash shortcut for MacOS 2026-03-15 17:45:01 +01:00
nou 1ac5a4e42a Update metainfo 2026-02-16 22:52:28 +01:00
nou 83d212aa91 Enable sorting of FITS header 2026-02-16 22:29:25 +01:00
nou bd24fba407 Update README 2026-02-11 21:26:33 +01:00
8 changed files with 70 additions and 12 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ if(SANITIZE_ADDRESS_LEAK)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak")
endif(SANITIZE_ADDRESS_LEAK) endif(SANITIZE_ADDRESS_LEAK)
find_package(Qt6 COMPONENTS Widgets Sql OpenGLWidgets Qml Charts REQUIRED) find_package(Qt6 COMPONENTS Widgets Sql OpenGLWidgets Qml Charts Svg REQUIRED)
find_library(EXIF_LIB exif REQUIRED) find_library(EXIF_LIB exif REQUIRED)
find_library(FITS_LIB cfitsio REQUIRED) find_library(FITS_LIB cfitsio REQUIRED)
find_library(RAW_LIB NAMES raw_r REQUIRED) find_library(RAW_LIB NAMES raw_r REQUIRED)
@@ -105,7 +105,7 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}") message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml Qt6::Charts ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF) target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml Qt6::Charts Qt6::Svg ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF)
if(APPLE) if(APPLE)
target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation") target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation")
elseif(UNIX) elseif(UNIX)
+5 -1
View File
@@ -2,7 +2,7 @@ FITS/XISF image viewer with multithreaded image loading
To get all dependencies install these packages To get all dependencies install these packages
sudo apt install qt6-base-dev qt6-declarative-dev libqt6opengl6-dev libraw-dev libexif-dev libcfitsio-dev wcslib-dev cmake libzstd-dev libqt6sql6-sqlite sudo apt install qt6-base-dev qt6-declarative-dev qt6-charts-dev libqt6opengl6-dev libraw-dev libexif-dev libcfitsio-dev wcslib-dev cmake libzstd-dev libqt6sql6-sqlite
on OpenSUSE on OpenSUSE
@@ -26,6 +26,10 @@ Then to build run standard cmake sequence
cmake --build build cmake --build build
./build/tenmon ./build/tenmon
To install it to system run this command as root
cmake --install build
For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
crash. crash.
+9
View File
@@ -47,6 +47,7 @@
</keywords> </keywords>
<url type="homepage">https://nouspiro.space/?page_id=206</url> <url type="homepage">https://nouspiro.space/?page_id=206</url>
<url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url> <url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url>
<url type="vcs-browser">https://gitea.nouspiro.space/nou/tenmon</url>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>Main window with image</caption> <caption>Main window with image</caption>
@@ -59,6 +60,14 @@
</screenshots> </screenshots>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>
<releases> <releases>
<release version="20260217" date="2026-02-17">
<description>
<ul>
<li>Fix potentional crash</li>
<li>Enable sorting of FITS info</li>
</ul>
</description>
</release>
<release version="20251101" date="2025-11-01"> <release version="20251101" date="2025-11-01">
<description> <description>
<ul> <ul>
+5
View File
@@ -307,6 +307,11 @@ QVector<SkyObject> Database::getObjects(double minRa, double maxRa, double minDe
return objects; return objects;
} }
const QSqlDatabase &Database::db() const
{
return database;
}
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs) bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs)
{ {
if(scannedDirs.contains(dir.canonicalPath()))return true; if(scannedDirs.contains(dir.canonicalPath()))return true;
+1
View File
@@ -42,6 +42,7 @@ public:
void reindex(QProgressDialog *progress); void reindex(QProgressDialog *progress);
QStringList getFitsKeywords(); QStringList getFitsKeywords();
QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec); QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec);
const QSqlDatabase& db() const;
protected: protected:
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs); bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
bool indexFile(const QFileInfo &file); bool indexFile(const QFileInfo &file);
+40 -8
View File
@@ -163,26 +163,49 @@ void FITSFileModel::prepareQuery()
QString join; QString join;
QStringList where; QStringList where;
QString sql = m_columns.size() ? "SELECT f.file," : "SELECT f.file"; QString sql = m_columns.size() ? "SELECT f.file," : "SELECT f.file";
QVariantList bindValues;
for(int i=0; i<m_value.size(); i++) for(int i=0; i<m_value.size(); i++)
{ {
if(m_key[i] == "file") if(m_key[i] == "file")
where.append(QString(" f.file LIKE '%1' ").arg(m_value[i])); {
where.append(" f.file LIKE ? ");
bindValues.append(m_value[i]);
}
else if(m_key[i] == "RA pos") else if(m_key[i] == "RA pos")
where.append(QString(" %1 BETWEEN f.minRa AND f.maxRa ").arg(RA(m_value[i]))); {
where.append(" ? BETWEEN f.minRa AND f.maxRa ");
bindValues.append(RA(m_value[i]));
}
else if(m_key[i] == "DEC pos") else if(m_key[i] == "DEC pos")
where.append(QString(" %1 BETWEEN f.minDec AND f.maxDec ").arg(DEC(m_value[i]))); {
where.append(" ? BETWEEN f.minDec AND f.maxDec ");
bindValues.append(DEC(m_value[i]));
}
else if(m_key[i] == "RA range") else if(m_key[i] == "RA range")
where.append(QString(" crVal1 BETWEEN %1 AND %2 ").arg(RA(m_value[i])).arg(RA(m_limit[i]))); {
where.append(" crVal1 BETWEEN ? AND ? ");
bindValues.append(RA(m_value[i]));
bindValues.append(RA(m_limit[i]));
}
else if(m_key[i] == "DEC range") else if(m_key[i] == "DEC range")
where.append(QString(" crVal2 BETWEEN %1 AND %2 ").arg(DEC(m_value[i])).arg(DEC(m_limit[i]))); {
where.append(" crVal2 BETWEEN ? AND ? ");
bindValues.append(DEC(m_value[i]));
bindValues.append(DEC(m_limit[i]));
}
else else
join += QString(" JOIN fits_headers AS s%1 ON f.id=s%1.id_file AND s%1.key='%2' AND s%1.value LIKE '%3'").arg(i).arg(m_key[i]).arg(m_value[i]); {
join += QString(" JOIN fits_headers AS s%1 ON f.id=s%1.id_file AND s%1.key=? AND s%1.value LIKE ?").arg(i);
bindValues.append(m_key[i]);
bindValues.append(m_value[i]);
}
} }
int i=0; int i=0;
for(auto &column : m_columns) for(auto &column : m_columns)
{ {
cols += QString("GROUP_CONCAT(h%1.value) AS h%1_value,").arg(i); cols += QString("GROUP_CONCAT(h%1.value) AS h%1_value,").arg(i);
join += QString(" LEFT JOIN fits_headers AS h%1 ON f.id=h%1.id_file AND h%1.key='%2'").arg(i).arg(column); join += QString(" LEFT JOIN fits_headers AS h%1 ON f.id=h%1.id_file AND h%1.key=?").arg(i);
bindValues.append(column);
i++; i++;
} }
cols.chop(1); cols.chop(1);
@@ -191,7 +214,16 @@ void FITSFileModel::prepareQuery()
sql += join; sql += join;
if(!where.isEmpty())sql += " WHERE " + where.join("AND"); if(!where.isEmpty())sql += " WHERE " + where.join("AND");
sql += " GROUP BY f.id" + m_sort; sql += " GROUP BY f.id" + m_sort;
setQuery(sql);
QSqlQuery query(m_database->db());
query.prepare(sql);
for(int i = 0; i < bindValues.size(); i++)
query.bindValue(i, bindValues[i]);
if(!query.exec())
qWarning() << "Failed to exectute query" << query.lastQuery();
setQuery(std::move(query));
setHeaderData(0, Qt::Horizontal, tr("File name")); setHeaderData(0, Qt::Horizontal, tr("File name"));
i = 1; i = 1;
for(auto &column : m_columns) for(auto &column : m_columns)
+2
View File
@@ -11,6 +11,8 @@ ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent)
setIndentation(5); setIndentation(5);
QSettings settings; QSettings settings;
header()->restoreState(settings.value("imageinfo/headerstate").toByteArray()); header()->restoreState(settings.value("imageinfo/headerstate").toByteArray());
setSortingEnabled(true);
header()->setSortIndicatorClearable(true);
} }
ImageInfo::~ImageInfo() ImageInfo::~ImageInfo()
+6 -1
View File
@@ -196,7 +196,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
#endif #endif
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked); fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked); fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked); QAction *deleteAction = fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
#ifdef Q_OS_MACOS
deleteAction->setShortcuts(QList<QKeySequence>({Qt::Key_Backspace, QKeySequence::Delete}));
#else
deleteAction->setShortcuts(QKeySequence::Delete);
#endif
fileMenu->addSeparator(); fileMenu->addSeparator();
fileMenu->addAction(tr("Index directory"), this, static_cast<void (MainWindow::*)()>(&MainWindow::indexDir)); fileMenu->addAction(tr("Index directory"), this, static_cast<void (MainWindow::*)()>(&MainWindow::indexDir));
fileMenu->addAction(tr("Reindex files"), this, &MainWindow::reindex); fileMenu->addAction(tr("Reindex files"), this, &MainWindow::reindex);