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")
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(FITS_LIB cfitsio 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}")
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)
target_link_libraries(tenmon PRIVATE Qt6::DBus "-framework CoreFoundation")
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
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
@@ -26,6 +26,10 @@ Then to build run standard cmake sequence
cmake --build build
./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
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
crash.
+9
View File
@@ -47,6 +47,7 @@
</keywords>
<url type="homepage">https://nouspiro.space/?page_id=206</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>
<screenshot type="default">
<caption>Main window with image</caption>
@@ -59,6 +60,14 @@
</screenshots>
<content_rating type="oars-1.1"/>
<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">
<description>
<ul>
+5
View File
@@ -307,6 +307,11 @@ QVector<SkyObject> Database::getObjects(double minRa, double maxRa, double minDe
return objects;
}
const QSqlDatabase &Database::db() const
{
return database;
}
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs)
{
if(scannedDirs.contains(dir.canonicalPath()))return true;
+1
View File
@@ -42,6 +42,7 @@ public:
void reindex(QProgressDialog *progress);
QStringList getFitsKeywords();
QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec);
const QSqlDatabase& db() const;
protected:
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
bool indexFile(const QFileInfo &file);
+40 -8
View File
@@ -163,26 +163,49 @@ void FITSFileModel::prepareQuery()
QString join;
QStringList where;
QString sql = m_columns.size() ? "SELECT f.file," : "SELECT f.file";
QVariantList bindValues;
for(int i=0; i<m_value.size(); i++)
{
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")
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")
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")
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")
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
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;
for(auto &column : m_columns)
{
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++;
}
cols.chop(1);
@@ -191,7 +214,16 @@ void FITSFileModel::prepareQuery()
sql += join;
if(!where.isEmpty())sql += " WHERE " + where.join("AND");
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"));
i = 1;
for(auto &column : m_columns)
+2
View File
@@ -11,6 +11,8 @@ ImageInfo::ImageInfo(QWidget *parent) : QTreeWidget(parent)
setIndentation(5);
QSettings settings;
header()->restoreState(settings.value("imageinfo/headerstate").toByteArray());
setSortingEnabled(true);
header()->setSortIndicatorClearable(true);
}
ImageInfo::~ImageInfo()
+6 -1
View File
@@ -196,7 +196,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
#endif
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 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->addAction(tr("Index directory"), this, static_cast<void (MainWindow::*)()>(&MainWindow::indexDir));
fileMenu->addAction(tr("Reindex files"), this, &MainWindow::reindex);