Compare commits

...

90 Commits

Author SHA1 Message Date
Dušan Poizl 39775b5e98 Scale float images to 0,1 range on load 4 weeks ago
Dušan Poizl 93b56e2966 Workaround for QTBUG-87332 2 months ago
Dušan Poizl 2e41464ff4 Update build instructions 2 months ago
Dušan Poizl b6ae7d4cdb Update french help, fix few typo. Thanks to Patrick Chevalley 2 months ago
Dušan Poizl 1bd48e8fb4 Refres database table when indexing is done 2 months ago
Dušan Poizl 1d65eda490 Search with CRVAL# 2 months ago
Dušan Poizl 97346df596 Update help 2 months ago
Dušan Poizl a157b274a2 Save to database CRVALi 2 months ago
Dušan Poizl 6466702819 Set correct type to vertex attribute 2 months ago
Dušan Poizl b4ea65b42a Upload sizes to OpenGL only once per draw 2 months ago
Dušan Poizl 1682de4e1b Update translations 2 months ago
Dušan Poizl 00872b31df Add icon for MacOS 2 months ago
Dušan Poizl 2884787916 Changes to build on MacOS 2 months ago
Dušan Poizl 86ea9fc137 Add MacOS PCL libs 2 months ago
Dušan Poizl 67199a033d Better status bar 2 months ago
Dušan Poizl 0f182900c2 Fix build on older gcc 2 months ago
Dušan Poizl 19ed5ae1a4 Better handling of FITS records 2 months ago
Dušan Poizl 46215c7a7d Workaround for incorect handling of PV1_2 2 months ago
Dušan Poizl c346487504 Add parsing WCS info from XISF 2 months ago
Dušan Poizl 5b6fead6f1 Fix issue with numeric values in XISF FITS header 2 months ago
Dušan Poizl 3e94aa0eda Add search by RA and DEC point 2 months ago
Dušan Poizl 08e70cdb52 Calculate bounds on indexing 2 months ago
Dušan Poizl 04e587b51c Add WCSData::calculateBounds 2 months ago
Dušan Poizl 42dd55244a Add wcslib as lib name 2 months ago
Dušan Poizl 6b9ea5e4b9 Add support for WCS 2 months ago
Dušan Poizl 701a425cc7 Add support for 16 bit PNG images 2 months ago
Dušan Poizl 9cd2ae14b3 Add status bar with color value 2 months ago
Dušan Poizl f7e4e1874f Proper filter setting 2 months ago
Dušan Poizl e6749fc487 Move shaders to subdirectory 2 months ago
Dušan Poizl dc6aa6baa8 Fix bug with wayland backend 2 months ago
Dušan Poizl c8a70d22f8 Show marked files in file list bold 2 months ago
Dušan Poizl dbb533176c Remove unecessary call 3 months ago
Dušan Poizl 032f5b0577 Improve file selection in file system widget 3 months ago
Dušan Poizl eb417010c3 Set max value in autostretch 3 months ago
Dušan Poizl 5b44d2ac69 Add support for NEF, DNG 3 months ago
Dušan Poizl d1df789691 French help and updated translations 3 months ago
Dušan Poizl ab7d04b625 Workaround for unsupported QSqlQuery::size() 3 months ago
Dušan Poizl a4cfc65d4b Wrap reindex into transaction 3 months ago
Dušan Poizl b4746be190 Update help about marking images 3 months ago
Dušan Poizl 9ceb7556f9 Add marking and unmark from thumbnails view 3 months ago
Dušan Poizl 41b29f0701 Show marked files in database view 3 months ago
Dušan Poizl 67ae2d4b62 Mark unmark files from database view 3 months ago
Dušan Poizl b6b6863331 Add override keyword 3 months ago
Dušan Poizl 571fa57af2 Update translations 3 months ago
Dušan Poizl abb3d631bf Double click in file tree open file 3 months ago
Dušan Poizl b65911943e Fix in context menu 3 months ago
Dušan Poizl 9d9f8db499 Fix crash 3 months ago
Dušan Poizl 3060b17c0c Translations update 3 months ago
Dušan Poizl fcf336d63a Second call to QTranslator::load() seem to clear translation 3 months ago
Dušan Poizl 54ef8e990c Selecting thumbnails 3 months ago
Dušan Poizl 94466a6b9b Draw file name under thumbnail 3 months ago
Dušan Poizl b84d8127ad Add copy, move and index action to File tree 3 months ago
Dušan Poizl c971a919ec Add Filetree dock 3 months ago
Dušan Poizl 8c248b7cfc Add French tranlation, credit Patrick Chevalley 3 months ago
Dušan Poizl 43b510a78c Try load translation from application dir 3 months ago
Dušan Poizl 105fba814d Fix drag&drop files on windows 4 months ago
Dušan Poizl 555e6c11c8 Update english help thanks to gunarm 4 months ago
Dušan Poizl 748f5fac03 Update README 4 months ago
Dušan Poizl adc7d07b75 Update help 4 months ago
Dušan Poizl ba450ee554 Error message when OpenGL can't be intialized 4 months ago
Dušan Poizl cc69b7bc2d Fix ifdef 4 months ago
Dušan Poizl 42b619641a Add help dialog 4 months ago
Dušan Poizl 9af4fa1b99 Update translations 4 months ago
Dušan Poizl cbc6775756 Add version to About dialog 4 months ago
Dušan Poizl cee6979ece Reorganize file menu 4 months ago
Dušan Poizl 903ec65d52 Add reindex action 4 months ago
Dušan Poizl 95e4774507 Add translations 4 months ago
Dušan Poizl 2410c51d5d Reformat PCL license 4 months ago
Dušan Poizl 17bca74362 Open external link in About 4 months ago
Dušan Poizl c70123cf7b Prevent crash when changing dir while thumnails are loading 4 months ago
Dušan Poizl 12c6385f77 Add About Dialog and PCL LICENSE 4 months ago
Dušan Poizl 39f3ec7d30 Better tooltip for auto stretch toogle 4 months ago
Dušan Poizl 8b968ddcb1 Add move marked files 4 months ago
Dušan Poizl da1843e48c Add button to autostrech for each file 4 months ago
Dušan Poizl e0d473c8c8 Add marked files dialog 4 months ago
Dušan Poizl 92f9920f24 Add saving to FITS and XISF 4 months ago
Dušan Poizl f68a9c4d7c Right path for flatpak 4 months ago
Dušan Poizl 027a38cb42 Revert install.cmake 4 months ago
Dušan Poizl 47d5a9fc96 Add xisf to mime 4 months ago
Dušan Poizl 061bb3892e Install desktop icon even without xdg-icon-resource 4 months ago
Dušan Poizl b0b1a3a14b Bumb GLSL version to 330 4 months ago
Dušan Poizl ea834ebd16 Change stretchpanel to QToolBar 4 months ago
Dušan Poizl ce836a8ff3 Change fullscreen shortcut 4 months ago
Dušan Poizl a1848b27bf Don't call update for each thumbnail loaded 4 months ago
Dušan Poizl fabf3f0c1a Separate thumnail loading to different pool 4 months ago
Dušan Poizl cba8a0bb9c Clear image sizes buffer to prevent graphical glitches 4 months ago
Dušan Poizl 4e6230eef2 Add thumbnails 4 months ago
Dušan Poizl 2c95364fc4 Reorganize CMakeLists.txt 4 months ago
Dušan Poizl 26be690c70 Add drop file support 4 months ago
Dušan Poizl 56d6db8bc3 Explicitly link gslcblas 4 months ago
  1. 43
      3rdparty/PCL_LICENSE
  2. BIN
      3rdparty/lib/MacOS/libPCL-pxi.a
  3. BIN
      3rdparty/lib/MacOS/libRFC6234-pxi.a
  4. BIN
      3rdparty/lib/MacOS/liblcms-pxi.a
  5. BIN
      3rdparty/lib/MacOS/liblz4-pxi.a
  6. BIN
      3rdparty/lib/MacOS/libzlib-pxi.a
  7. 72
      CMakeLists.txt
  8. 25
      README
  9. 55
      about.cpp
  10. 20
      about.h
  11. BIN
      about/filter.png
  12. 118
      about/help_en
  13. 106
      about/help_fr
  14. 83
      about/help_sk
  15. 25
      about/pcl
  16. BIN
      about/stretch-panel.png
  17. 20
      about/tenmon
  18. 202
      database.cpp
  19. 9
      database.h
  20. 171
      databaseview.cpp
  21. 31
      databaseview.h
  22. 106
      filesystemwidget.cpp
  23. 20
      filesystemwidget.h
  24. 19
      gitversion.cmake
  25. 6
      gitversion.h.in
  26. 229
      imageinfo.cpp
  27. 43
      imageinfo.h
  28. 143
      imageringlist.cpp
  29. 28
      imageringlist.h
  30. 12
      imagescrollarea.h
  31. 436
      imagescrollareagl.cpp
  32. 76
      imagescrollareagl.h
  33. 2
      install.cmake
  34. 207
      loadrunable.cpp
  35. 14
      loadrunable.h
  36. 17
      main.cpp
  37. 259
      mainwindow.cpp
  38. 22
      mainwindow.h
  39. 65
      markedfiles.cpp
  40. 20
      markedfiles.h
  41. BIN
      nuke_a.png
  42. 2
      org.nou.tenmon.desktop
  43. 0
      org.nou.tenmon.png
  44. 137
      rawimage.cpp
  45. 13
      rawimage.h
  46. 27
      resources.qrc
  47. 10
      shaders/image.frag
  48. 2
      shaders/image.vert
  49. 22
      shaders/thumb.frag
  50. 20
      shaders/thumb.vert
  51. 23
      statusbar.cpp
  52. 19
      statusbar.h
  53. 2
      stfslider.cpp
  54. 79
      stretchpanel.cpp
  55. 77
      stretchtoolbar.cpp
  56. 14
      stretchtoolbar.h
  57. BIN
      tenmon.icns
  58. BIN
      translations/tenmon_en.qm
  59. 450
      translations/tenmon_en.ts
  60. BIN
      translations/tenmon_fr.qm
  61. 450
      translations/tenmon_fr.ts
  62. BIN
      translations/tenmon_sk.qm
  63. 463
      translations/tenmon_sk.ts

@ -0,0 +1,43 @@
*******************************************************************************
PixInsight Class Library License Version 1.0
*******************************************************************************
Copyright (c) 2003-2021, Pleiades Astrophoto S.L. All Rights Reserved.
Redistribution and use in both source and binary forms, with or without
modification, is permitted provided that the following conditions are met:
1. All redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. All redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names of
their contributors, may be used to endorse or promote products derived from
this software without specific prior written permission. For written
permission, please contact info@pixinsight.com.
4. All products derived from this software, in any form whatsoever, must
reproduce the following acknowledgment in the end-user documentation and/or
other materials provided with the product:
"This product is based on software from the PixInsight project, developed by
Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
Alternatively, if that is where third-party acknowledgments normally appear,
this acknowledgment must be reproduced in the product itself.
THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS INTERRUPTION; PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, DATA OR PROFITS) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -15,11 +15,14 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
find_package(Qt5 COMPONENTS Widgets Sql OpenGL REQUIRED)
find_package(OpenCV REQUIRED)
find_library(GSL_LIB gsl REQUIRED)
find_library(GSLCBLAS_LIB gslcblas REQUIRED)
find_library(EXIF_LIB exif REQUIRED)
find_library(FITS_LIB cfitsio REQUIRED)
find_library(RAW_LIB NAMES raw_r raw REQUIRED)
find_library(RAW_LIB NAMES raw_r REQUIRED)
find_library(WCS_LIB wcs wcslib PATHS REQUIRED)
set(TENMON_SRC
about.cpp
database.cpp
databaseview.cpp
filesystemwidget.cpp
@ -30,32 +33,75 @@ set(TENMON_SRC
loadrunable.cpp
main.cpp
mainwindow.cpp
markedfiles.cpp
rawimage.cpp
starfit.cpp
statusbar.cpp
stfslider.cpp
stretchpanel.cpp
stretchtoolbar.cpp
)
qt5_add_resources(TENMON_SRC resources.qrc)
if(WIN32)
list(APPEND TENMON_SRC icon.rc)
add_compile_definitions("__PCL_WINDOWS")
endif(WIN32)
if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
set(tenmon_ICON "")
elseif(APPLE)
add_compile_definitions("__PCL_MACOS")
set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/tenmon.icns)
set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
else()
add_compile_definitions("__PCL_LINUX")
set(tenmon_ICON "")
endif()
add_executable(tenmon ${TENMON_SRC})
add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include)
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include ${CMAKE_BINARY_DIR})
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql Qt5::OpenGL ${OpenCV_LIBS} ${GSL_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB})
if(WIN32)
target_link_directories(tenmon PRIVATE 3rdparty/lib/Windows)
elseif(APPLE)
target_link_directories(tenmon PRIVATE 3rdparty/lib/MacOS)
else()
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
endif()
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB})
if(APPLE)
target_link_libraries(tenmon PCL-pxi lcms-pxi lz4-pxi RFC6234-pxi zlib-pxi "-framework CoreFoundation")
else()
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib)
endif(APPLE)
target_link_directories(tenmon PRIVATE 3rdparty/lib/${CMAKE_HOST_SYSTEM_NAME})
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib)
if(LIBRAW_STATIC)
add_compile_definitions("LIBRAW_NODLL")
target_link_libraries(tenmon jasper)
endif()
install(TARGETS tenmon BUNDLE DESTINATION .)
if(UNIX AND NOT APPLE)
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
if(XDG-DESKTOP-MENU_EXECUTABLE)
install(SCRIPT install.cmake)
else()
if(DEFINED ENV{FLATPAK_DEST})
install(FILES org.nou.tenmon.desktop DESTINATION "$ENV{FLATPAK_DEST}/share/applications")
install(FILES org.nou.tenmon.png DESTINATION "$ENV{FLATPAK_DEST}/share/icons/hicolor/32x32/apps")
else()
install(FILES org.nou.tenmon.desktop DESTINATION "/usr/share/applications")
install(FILES org.nou.tenmon.png DESTINATION "/usr/share/icons/hicolor/32x32/apps")
endif()
endif()
endif(UNIX AND NOT APPLE)
install(TARGETS tenmon)
if(UNIX)
install(SCRIPT install.cmake)
endif(UNIX)
option(RELEASE_BUILD "Release build" OFF)
if(RELEASE_BUILD)
add_custom_target(tenmon_version COMMAND ${CMAKE_COMMAND} -Dlocal_dir="${CMAKE_CURRENT_SOURCE_DIR}" -Doutput_dir="${CMAKE_CURRENT_BINARY_DIR}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/gitversion.cmake")
add_dependencies(tenmon tenmon_version)
else()
execute_process(COMMAND ${CMAKE_COMMAND} -Dlocal_dir=${CMAKE_CURRENT_SOURCE_DIR} -Doutput_dir=${CMAKE_CURRENT_BINARY_DIR}
-P "${CMAKE_CURRENT_SOURCE_DIR}/gitversion.cmake")
endif()

@ -1,11 +1,24 @@
Simple image viewer with multithreaded image loading
FITS/XISF image viewer with multithreaded image loading
To get all dependencies install these packages
sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev
sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev libopencv-dev cmake
Then to build run
on OpenSUSE
qmake .
make
./tenmon
sudo zypper install opencv-devel gsl-devel exif-devel libraw-devel wcslib-devel libqt5-qtbase-devel
MacOS X
To compile on MacOS install XCode first. Then install homebrew in x86_64 mode
with "arch -i x86_64". Building on native ARM is not supported.
homebrew install qt5 libraw cfitsio libexif libgsl wcslib opencv
You may need to set CMAKE_PREFIX_PATH for Qt5 and OpenCV so CMake can find them.
Then to build run standard cmake
cmake -B build -S .
cmake --build build
./build/tenmon

@ -0,0 +1,55 @@
#include "about.h"
#include <QTextEdit>
#include <QLabel>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QFile>
#include <QLocale>
#include "gitversion.h"
About::About(QWidget *parent) : QDialog(parent)
{
setWindowTitle(tr("About Tenmon"));
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel(this);
QFile tenmonText(":/about/tenmon");
tenmonText.open(QIODevice::ReadOnly);
QByteArray text = tenmonText.readAll();
text.replace("@GITVERSION@", GITVERSION);
label->setText(text);
label->setOpenExternalLinks(true);
QTextEdit *pcl = new QTextEdit(this);
QFile pclText(":/about/pcl");
pclText.open(QIODevice::ReadOnly);
pcl->setText(pclText.readAll());
pcl->setReadOnly(true);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
layout->addWidget(label);
layout->addWidget(pcl);
layout->addWidget(buttonBox);
}
HelpDialog::HelpDialog(QWidget *parent) : QDialog(parent)
{
setWindowTitle(tr("Help"));
resize(800, 600);
QLocale locale;
QString l = QLocale::languageToString(locale.language());
QVBoxLayout *layout = new QVBoxLayout(this);
QTextEdit *helpText = new QTextEdit(this);
helpText->setReadOnly(true);
QFile tenmonText(":/help");
tenmonText.open(QIODevice::ReadOnly);
helpText->setHtml(tenmonText.readAll());
layout->addWidget(helpText);
}

@ -0,0 +1,20 @@
#ifndef ABOUT_H
#define ABOUT_H
#include <QDialog>
class About : public QDialog
{
Q_OBJECT
public:
About(QWidget *parent = nullptr);
};
class HelpDialog : public QDialog
{
Q_OBJECT
public:
HelpDialog(QWidget *parent = nullptr);
};
#endif // ABOUT_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@ -0,0 +1,118 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<head>
<style type="text/css">
h1, h2, h3, h4 { padding:0px; margin:10px; }
p { padding:0px; margin:5px; }
img { margin: 5px; }
</style>
</head>
<body>
<h2>Tenmon help</h2>
<p>Tenmon is intended primarily for viewing astro photos and images. It supports the following formats:
<ul>
<li>FITS 8, 16 bit integer and 32 bit float</li>
<li>XISF 8, 16 bit integer and 32 bit float</li>
<li>JPEG and PNG images</li>
<li>CR2, NEF, DNG raw images</li>
</ul>
</p>
<h3>Main windows</h3>
<p>The main window shows the currently loaded image. On the left is the <i>Image info</i> panel which displays details about the loaded image.
The <i>File system</i> panel shows other images in the same directory as the loaded image. At the top is the main menu and below that is the
<i>Stretch panel</i> containing various options for auto-stretching linear image data.</p>
<p>All panels in the interface can be moved around and/or closed. Any closed or non-visible panel can be
re-opened through the <i>Docks</i> menu at the top.</p>
<p>At bottom there is status bar that show current lightness or red, green, blue pixel value under mouse cursor then X and Y coordinates and
if image contain World Coordinate System metadata it show celestial coordinates.</p>
<h3>Opening and saving images</h3>
<p>To load an image select <i>File->Open</i> and choose the file. After a file is loaded, it becomes visible in the main image panel, and the
<i>File system</i> panel will show the other images in the same directory.</p>
<p>The loaded image can be exported to a different format with <i>File->Save as</i>. Any of the formats JPEG, PNG FITS and XISF can be selected.
In the case of saving JPEG or PNG, the stretch function is applied to the saved image. FITS and XISF are saved/converted without applying the stretch.
To open an image, you can also drag and drop it to main window.</p>
<h3>View</h3>
<p>The <i>View</i> menu has options to control the size and scale of displayed images:
<ul>
<li><i>Zoom In</i> and <i>Zoom Out</i> magnify and shrink the image. The mouse wheel can be also used to zoom freely.</li>
<li><i>Best fit</i> auto-zooms the image to fit the current size of the window.</li>
<li><i>100%</i> will zoom to 1:1 scale.</li>
<li><i>Fullscreen</i> enlarges the main window to the whole screen.</li>
<li><i>Thumbnails</i> will display small thumbnails for all images in the current directory.</li>
</ul>
</p>
<h3>Stretch toolbar</h3>
<p>This panel changes how images are displayed.
<br><img src=":/about/stretch-panel.png"></p>
<p>Starting on the left, there is slider scale with three adjustable points to manually control the stretch.
<ul>
<li>black point - all pixels with lower value (darker) than this setting will be clipped black</li>
<li>mid point - defines the value to be stretched to 50% intensity</li>
<li>white point - all pixels with higher value (brighter) than this will be clipped white</li>
</ul>
Following the slider are 5 buttons for automatic stretching:
<ul>
<li><i>Auto Stretch</i> automatically apply black and mid points to render the image with optimal brightness.</li>
<li><i>Reset</i> reset three values for black, mid and white point to default.</li>
<li><i>Invert</i> invert colors to display the image as negative.</li>
<li><i>Super pixel CFA </i> average 2x2 pixels into one (suitable for images from colour camera).</li>
<li><i>Apply Auto stretch on load</i> toggle automatically applying Autostretch for each image when loaded.</p>
</ul>
<h3>Marking images</h3>
<p>Images can be marked in the <i>Select</i> menu. To show a list of only the marked images, use <i>Select->Show marked</i>.
This dialog can be useful to clear marks from images. Marked images show a <b>*</b> character in the title bar of the main window.
Marked images can be copied or moved to a selected directory with <i>File->Copy/Move marked files</i>.
After copying or moving, the list of marked files is cleared. The list of marked files will be remembered after quitting the program.</p>
<p>Another way to mark images is in database view where you can select rows and then select mark or unmark action in context menu. Marked
files will be shown with bold text. Third way to mark files is from thumbnails view where you can press press <i>Shift</i> and click with left
mouse button and drag across thumbnails to mark them. Holding <i>Ctrl</i> will unmark files.</p>
<h3>File system and tree</h3>
<p>File system panel contain list of images in current opened directory. You can select file from this list and it will be displayed. It is also possible to
use arrow keys to go back (left and up) and forth (right and down) between images.</p>
<p>File tree show file system structure. You can right click to show context menu to perform various actions from <i>File</i> menu. There are also few others
<ul>
<li><i>Set as root directory</i> show only this directory and subdirectories</li>
<li><i>Reset root directory</i> show whole file system</li>
<li><i>Go up</i> show directory that is one level above current root directory</li>
</ul>
</p>
<h3>Database of FITS/XISF files</h3>
<p>Tenmon can scan a directory of FITS/XISF files and index metadata from FITS headers into it's internal database. This allows searching and sorting images based on that metadata.</p>
<p>To populate the database, select a directory of FITS/XISF files with <i>File->Index directory</i>.
After the selected directory is searched, metadata parsed from the images will be stored in the database.
To refresh the database, run <i>File->Reindex</i>. This will update any changed metadata and remove any record of
deleted files. To index new files, simply run <i>Index directory</i> again.</p>
<p>The database is viewed through a panel which is not visible in the default layout. To add the database panel to the view,
toggle <i>Docks->FITS/XISF database</i>. Once visible, database panel shows the database as a table with a column for each property.
Below the table is button to select which columns/properties are displayed.</p>
<p>Also at the bottom of the database panel are three combo boxes and text inputs used for filtering.
Select the property to filter on with the combo box and in the adjacent text box enter a string to search for in that property.
These three combo box contain list of all properties that are found during indexing except first five. First one set searching in file name.
Next two "RA pos" and "DEC pos" allow to filter out indexed images that contain point with entered RA/DEC coordinate. Expected format is three
number separated by space. In case of "DEC pos" it also accept +- sign. Omitting one or two last number is also valid. Some examples "02 12 32" "-12 43 12" "+45 32" "13".
So for RA it means hour, minutes and seconds while for DEC it is degrees, minutes and seconds.
Setting both "RA pos" and "DEC pos" can return images that doesn't contain entered point as it search against minimum and maximum RA/DEC coordinates that images contain.
"RA range" and "DEC range" filter out images which center coordinate is within entered range.
Pressing Enter or clicking on <i>Filter</i> button will filter out database record according to search parameter.
<p>Wildcards:
<ul>
<li><b>%</b> (percent) is a wildcard representing zero or more of any characters.</li>
<li><b>_</b> (underscore) is a wildcard for exactly one of any character.</i>
<li>Without wildcard characters, the exact string must match.</li>
</ul>
</p>
<br><img src=":/about/filter.png"><br>
This example filters for files where: "Bias" is in the file name, the OBJECT property is "M_42" (where the underscore can be any single character), and the DATE property begins with "2022".
</p>
<p><small>PS: Kanji in icon means astronomy in Japanese</small></p>
</body>
</html>

@ -0,0 +1,106 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<head>
<style type="text/css">
h1, h2, h3, h4 { padding:0px; margin:10px; }
p { padding:0px; margin:5px; }
img { margin: 5px; }
</style>
</head>
<body>
<h2>Aide de Tenmon</h2>
<p>Tenmon est destiné principalement à la visualisation de photos et d'images astronomique. Il prend en charge les formats suivants :
<ul>
<li>FITS 8, 16 bit entier et 32 bit point flottant</li>
<li>XISF 8, 16 bit entier et 32 bit point flottant</li>
<li>images JPEG et PNG</li>
<li>images RAW CR2, NEF, DNG</li>
</ul>
</p>
<h3>Fenêtre principale</h3>
<p>La fenêtre principale affiche l'image actuellement chargée. Sur la gauche se trouve le panneau <i>Informations de l'image</i> qui affiche des détails sur l'image chargée.
Le panneau <i>Système de fichier</i> affiche d'autres images dans le même répertoire que l'image chargée.
En haut se trouve le menu principal et en dessous se trouve le panneau <i>Réglage de la luminosité</i> contenant diverses options pour l'étirement automatique des images linéaires.</p>
<p>Tous les panneaux de l'interface peuvent être déplacés et/ou fermés. Tout panneau fermé ou non visible peut être rouvert via le menu <i>Fenêtres encrables</i> en haut.</p>
<p>En bas, il y a une barre d'état qui affiche l'intensité ou la valeur du pixel rouge, vert, bleu sous le curseur de la souris, puis les coordonnées X et Y et,
si l'image contient des métadonnées du World Coordinate System, elle affiche les coordonnées célestes.</p>
<h3>Ouvrir et enregistrer des images</h3>
<p>Pour charger une image, sélectionnez <i>Fichier->Ouvrir</i> et choisissez le fichier. Une fois qu'un fichier est chargé,
il devient visible dans le panneau d'image et le panneau du <i>système de fichiers</i> affiche les autres images dans le même répertoire.</p>
<p>L'image chargée peut être exportée dans un format différent avec <i>Fichier->Enregistrer sous</i>. Tous les formats JPEG, PNG FITS et XISF
peuvent être sélectionnés. Dans le cas d'un enregistrement JPEG ou PNG, la fonction d'étirement de la luminosité est appliquée à l'image enregistrée.
FITS et XISF sont enregistrés/convertis sans appliquer l'étirement.
Pour ouvrir une image, vous pouvez également la faire glisser et la déposer dans la fenêtre principale.</p>
<h3>Voir</h3>
<p>Le menu <i>Voir</i> propose des options pour contrôler la taille et l'échelle des images affichées :
<ul>
<li><i>Zoom avant</i> et <i>Zoom arrière</i> agrandissent et rétrécissent l'image. La molette de la souris peut également être utilisée pour zoomer librement.</li>
<li><i>Meilleur ajustement</i>, zoom automatiquement l'image pour l'adapter à la taille actuelle de la fenêtre.</li>
<li><i>100 %</i>, zoom à l'échelle 1:1.</li>
<li><i>Plein écran</i> agrandit la fenêtre principale sur tout l'écran.</li>
<li><i>Vignettes</i>, affiche de petites vignettes pour toutes les images du répertoire actuel.</li>
</ul>
<p>
<h3>Barre d'outils du réglage de la luminosité</h3>
<p>Ces outils modifient la luminosité des images affichées.
<br><img src=":/about/stretch-panel.png"></p>
<p>À partir de la gauche, il y a une échelle de luminosité avec trois points réglables pour contrôler manuellement l'étirement.
<ul>
<li>point noir - tous les pixels avec une valeur inférieure (plus sombre) que ce paramètre seront écrêtés en noir</li>
<li>point médian - définit la valeur à étirer à 50 % d'intensité</li>
<li>point blanc - tous les pixels avec une valeur supérieure (plus lumineuse) que celle-ci seront écrêtés en blanc</li>
</ul>
Après le curseur se trouvent 5 boutons pour la luminosité automatique :
<ul>
<li><i>Luminosité automatique</i>, applique automatiquement les points noirs et moyens pour rendre l'image avec une luminosité optimale.</li>
<li><i>Réinitialiser</i>, réinitialise les trois valeurs pour le point noir, moyen et blanc par défaut.</li>
<li><i>Inverser</i>, inverse les couleurs pour afficher l'image en négatif.</li>
<li><i>Super pixel CFA </i>, moyenne 2x2 pixels en un (adapté aux images d'une caméra couleur).</li>
<li><i>Appliquer la luminosité automatique au chargement</i>, applique la luminosité automatique pour chaque image lors du chargement.</p>
</ul>
<h3>Marquer les images</h3>
<p>Les images peuvent être marquées dans le menu <i>Sélectionner</i>. Pour afficher une liste des seules images marquées, utilisez <i>Sélectionner->Afficher marqué</i>. Cette boîte de dialogue peut être utile pour effacer les marques des images. Les images marquées affichent un caractère <b>*</b> dans la barre de titre de la fenêtre principale. Les images marquées peuvent être copiées ou déplacées vers un répertoire sélectionné avec <i>Fichier->Copier/Déplacer les fichiers marqués</i>. Après la copie ou le déplacement, la liste des fichiers marqués est effacée. La liste des fichiers marqués sera mémorisée après avoir quitté le programme.</p>
<p>Une autre façon de marquer des images est dans la vue de la base de données où vous pouvez sélectionner des lignes, puis sélectionner l'action marquer ou décocher dans le menu contextuel. Les fichiers marqués seront affichés en texte gras. La troisième façon de marquer les fichiers est de voir les vignettes où vous pouvez appuyer sur <i>Maj</i> et cliquer avec le bouton gauche de la souris et faire glisser sur les vignettes pour les marquer. Maintenir <i>Ctrl</i> décochera les fichiers.</p>
<h3>Système de fichier et arborescence</h3>
<p>Le panneau du système de fichiers contient la liste des images du répertoire ouvert. Vous pouvez sélectionner un fichier dans cette liste et il sera affiché. Il est également possible
d'utiliser les touches fléchées pour revenir en arrière (gauche et haut) et avancer (droite et bas) entre les images.</p>
<p>L'arborescence des fichiers montre la structure du système de fichiers. Vous pouvez cliquer avec le bouton droit de la souris pour afficher un menu contextuel permettant d'effectuer diverses actions du menu <i>Fichier</i>. Il y a aussi les actions suivantes
<ul>
<li><i>Définir comme répertoire racine</i> afficher uniquement ce répertoire et ses sous-répertoires</li>
<li><i>Réinitialiser la racine</i> afficher tout le système de fichiers</li>
<li><i>Monter</i> afficher le répertoire qui est un niveau au-dessus du répertoire racine actuel</li>
</ul>
</p>
<h3>Base de donnée de fichiers FITS/XISF</h3>
<p>Tenmon peut analyser un répertoire de fichiers FITS/XISF et indexer les métadonnées des en-têtes FITS dans sa base de données interne. Cela permet de rechercher et de trier des images en fonction de ces métadonnées.</p>
<p>Pour remplir la base de données, sélectionnez un répertoire de fichiers FITS/XISF avec <i>Fichier->Indexer le répertoire</i>. Une fois le répertoire parcouru, les métadonnées analysées à partir des images seront stockées dans la base de données. Pour actualiser la base de données, exécutez <i>Fichier-> Ré-indexer les fichiers</i>. Cela mettra à jour toutes les métadonnées modifiées et supprimera tout enregistrement de fichiers supprimés. Pour indexer de nouveaux fichiers, exécutez simplement à nouveau <i>Fichier->Indexer le répertoire</i>.</p>
<p>La base de données est visualisée via un panneau qui n'est pas visible dans la mise en page par défaut. Pour ajouter le panneau de base de données à la vue, basculez <i>Fenêtres encrables->Base de données FITS/XISF</i>. Une fois visible, le panneau de la base de données affiche la base de données sous forme de tableau avec une colonne pour chaque propriété. Sous le tableau se trouve un bouton pour sélectionner les colonnes/propriétés à afficher.</p>
<p>Au bas du panneau de la base de données se trouvent également trois boîtes de liste déroulante et des entrées de texte utilisées pour le filtrage. Sélectionnez la propriété à filtrer dans une liste déroulante et dans la zone de texte adjacente, entrez un texte à rechercher pour cette propriété.
Ces trois boîtes contiennent la liste de toutes les propriétés qui sont trouvées pendant l'indexation, sauf les trois premières. La première définit la recherche dans le nom du fichier.
Les deux suivantes "RA pos" et "DEC pos" permettent de filtrer les images indexées qui contiennent un point avec les coordonnées RA/DEC entrée. Le format attendu est trois nombres séparés par un espace.
Dans le cas de "DEC pos", il accepte également le signe +-. L'omission d'un ou deux derniers chiffres est également valable. Quelques exemples "02 12 32" "-12 43 12" "+45 32" "13".
Le fait de définir à la fois "RA pos" et "DEC pos" peut renvoyer des images qui ne contiennent pas le point saisi, car la recherche est faite sur les coordonnées RA/DEC minimum et maximum que les images contiennent.
"RA range" et "DEC range" filtrent les images dont les coordonnées centrale se trouvent dans la plage saisie.
En appuyant sur la touche Enter ou en cliquant sur le bouton <i>Filtre</i>, les enregistrements de la base de données seront filtrés en fonction des paramètres de recherche.
<p>Caractères génériques :
<ul>
<li><b>%</b> (pourcentage) est un caractère générique représentant zéro ou plusieurs caractères.</li>
<li><b>_</b> (trait de soulignement) est un caractère générique pour exactement un caractère quelconque.</li>
<li>En l'absence de caractères génériques, le texte exacte doit correspondre.</li>
</ul>
</p>
<br><img src=":/about/filter.png"><br>
Cet exemple filtre les fichiers où : "Bias" figure dans le nom de fichier, la propriété OBJECT est "M_42" (où le trait de soulignement peut être n'importe quel caractère) et la propriété DATE commence par "2022".
</p>
<p><small>PS: Le Kanji de icône (tenmon) signifie astronomie en japonais</small></p>
</body>
</html>

@ -0,0 +1,83 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<head>
<style type="text/css">
h1, h2, h3, h4 { padding:0px; margin:10px; }
p { padding:0px; margin:5px 5px 10px 5px; }
</style>
</head>
<body>
<h2>Tenmon pomocník</h2>
<p>Tenmon slúži primárne na zobrazenie astronomických fotiek a obrázkov. Dokáže otvoriť nasledovné formáty:
<ul>
<li>FITS 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
<li>XISF 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
<li>JPEG a PNG obrázky</li>
<li>CR2, NEF, DNG raw obrázky</li>
</ul>
</p>
<h3>Hlavné okno</h3>
<p>V hlavnom okne sa zobrazujú načítané obrázky. Naľavo sú potom <i>Informácie o obrázku</i> kde sa zobrazujú podrobné
informácie o aktuálnom obrázku a <i>Zoznam súborov</i> kde sú všetky obrázky z adresára kde je aktuálne zobrazený obrázok.
Hore je hlavné menu a pod ním je <i>Panel úrovní</i>. Všetky panely sa dajú zavrieť a presúvať. Zatvorený panel sa dá znova
zobraziť v menu <i>Dokovacie panely</i>.
</p>
<h3>Otváranie a ukladanie obrázkov</h3>
<p>Otvoriť obrázok je možné v menu <i>Súbor->Otvoriť</i>. Po vybraní súboru ktorý sa má otvoriť je
tento zobrazený v hlavnom okne. Taktiež sú v panely <i>Súborový systém</i> zobrazené ďalšie obrázky v
adresári kde sa nachádza zobrazený obrázok.</p>
<p>Aktuálne zobrazený obrázok je možné uložiť v inom formáte cez voľbu <i>Súbor->Ulož ako</i>. Dá sa vybrať
formát JPEG, PNG, FITS a XISF. V prípade JPEG alebo PNG sa aplikuje funkcia na úpravu úrovní. Pri FITS a XISF
sa dáta skonvertujú bez zmeny úrovní.</p>
<h3>Zobrazenie</h3>
<p>Menu <i>Zobrazenie</i> ovplyvňuje veľkosť a škálu zobrazovaných obrázkov.
<i>Priblížiť/Oddialiť</i> zväčší a zmenší obrázok. Na toto tiež slúži aj kolečko myši. <i>Najlepšia veľkosť</i> zobrazí
obrázok tak aby bol zobrazený na celú plochu. <i>100%</i> zobrazí obrázok v pomere 1:1. <i>Celá obrazovka</i> zobrazí
hlavné okno na celú obrazovku. <i>Náhľady</i> zobrazí malé náhľady pre všetky obrázky z aktuálneho adresára.
</p>
<h3>Panel úrovní</h3>
<p>
Tento panel umožňuje upraviť spôsob ako sa zobrazujú obrazové dáta. Ako prvá je na tomto panely posuvná škála
na ktorej sa dajú nastaviť tri body.
<br><br><img src=":/about/stretch-panel.png">
<ul>
<li>čierny bod - všetky pixeli s hodnotou menšou ako nastavená budú zobrazené ako čierne</li>
<li>stredný bod - pixeli s touto hodnotou budú zobrazené ako 50% šedá</li>
<li>biely bod - pixeli nad touto hodnotou budú zobrazené ako biele</li>
</ul>
Nasleduje tlačidlo ktoré nastaví hodnoty čierneho a stredného bodu tak aby bol obrázok zobrazený optimálnym jasom.
Druhé tlačidlo resetuje hodnoty pre čierny, stredný a biely bod na východzie hodnoty. Invertovanie farieb zobrazí obrázok ako negatív.
Super pixel CFA spriemeruje dva krát dva pixeli do jedného čo je vhodné pri prezeraní surových obrázkov z farebných kamier.
Posledné tlačidlo zapína a vypína nastavovanie optimálnych hodnôt úrovní pre každý obrázok zvlášť.</p>
<h3>Označovanie obrázkov</h3>
<p>Obrázky je možné si označiť cez menu <i>Výber</i>. Zoznam takto označených obrázkov sa dá zobraziť cez
<i>Výber->Ukázať označené</i>. V tomto dialógu sa dá tiež tento zoznam upraviť. Okrem toho sa pri označených
obrázkoch zobrazuje znak * v záhlaví hlavného okna. Takto označené obrázky je potom možné skopírovať alebo
presunúť do vybraného adresára pomocou <i>Súbor->Skopírovať/Presunúť označené súbory</i>. Po skopírovaní alebo
presunutú sa zoznam označených obrázkov vymaže. Program si tento zoznam pamätá aj po svojom ukončení.</p>
<p>Ďalší spôsob ako označiť obrázky je cez databázu FITS/XISF kde je možné vybrať jednotlivé riadky. Potom stačí
vybrať označit alebo odznačiť v kontextovom menu. Označené súbory budú zobrazené tučným textom. Tretí spôsob na označenie
obrázkov je možné cez náhľady. Držaním <i>Shift</i> a následne kliknutím ľavým tlačítkom myši sa daný obrázok označí.
Pre odznačenie je treba držať <i>Ctrl</i></p>
<h3>Databáza FITS/XISF súborov</h3>
<p>Program vie prehľadať adresár a indexovať meta údaje z FIST a XISF obrázkov do internej databázy v ktorej sa dá
následne vyhľadávať. Najprv je treba indexovať adresár s FIST/XISF obrázkami <i>Súbor->Indexovať adresár</i>.
Vybraný adresár je prehľadaný a meta údaje z FIST a XISF obrázkov sú uložené do databázy. Na kontrolu a obnovu datábazy
je možné spustiť re-indexáciu <i>Súbor->Reindex</i>. Toto obnoví zmenené údaje a odstráni záznamy o už neexistujúcich súboroch.
Pre indexovanie nových súborov je treba znova pustiť indexáciu.</p>
<p>Pre zobrazenie databázy je treba zobraziť jej panel cez <i>Dokovacie panely->Databáza FITS/XISF súborov</i>. Databáza je zobrazená vo forme tabuľky
kde sú jednotlivé stĺpcoch zobrazené vybrané vlastnosti. V spodnej časti panelu je tlačidlo ktoré zobrazí dialóg na výber zobrazovaných
sĺpcov. Nasledujú tri výberové a textové polia. Tieto slúžia na vyhľadávanie v databáze. Výberové pole určuje stĺpec v ktorom sa
má vyhľadávať a do textového poľa sa zadáva hodnota na vyhľadanie.
<br><img src=":/about/filter.png"><br>
V nasledovnom príklade sa vyhľadajú súbory ktoré majú v mene súboru "Bias", OBJECT je M_42 a DATE začína reťazcom 2022. Znak % sa berie ako
zástupný znak za hocijaký reťazec znakov aj žiadny. Znak _ je tiež zástupný znak zastupujúci práve jeden znak.
Bez použitia zástupných znakov sa vyhľadá iba presný výskyt.</p>
</body>
</html>

@ -0,0 +1,25 @@
*******************************************************************************
PixInsight Class Library License Version 1.0
*******************************************************************************
Copyright (c) 2003-2021, Pleiades Astrophoto S.L. All Rights Reserved.
Redistribution and use in both source and binary forms, with or without modification, is permitted provided that the following conditions are met:
1. All redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. All redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names of their contributors, may be used to endorse or promote products derived from this software without specific prior written permission. For written permission, please contact info@pixinsight.com.
4. All products derived from this software, in any form whatsoever, must reproduce the following acknowledgment in the end-user documentation and/or other materials provided with the product:
"This product is based on software from the PixInsight project, developed by
Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
Alternatively, if that is where third-party acknowledgments normally appear,
this acknowledgment must be reproduced in the product itself.
THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -0,0 +1,20 @@
<table><tr>
<td style="padding-right:10px"><img src=":/org.nou.tenmon.png"></td>
<td><h3>Tenmon</h3>
Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br>
v@GITVERSION@ Copyright © 2022 Dušan Poizl<br><br>
This program is free software: you can redistribute it and/or modify<br>
it under the terms of the GNU General Public License as published by<br>
the Free Software Foundation, either version 3 of the License, or<br>
(at your option) any later version.<br><br>
This program is distributed in the hope that it will be useful,<br>
but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
GNU General Public License for more details.<br><br>
You should have received a copy of the GNU General Public License<br>
along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
</td>
</tr></table>

@ -22,16 +22,32 @@ bool Database::init()
if(m_database.isValid())
{
m_database.setDatabaseName(dir.absoluteFilePath("database.db"));
m_database.setDatabaseName(dir.absoluteFilePath("database2.db"));
if(m_database.open())
{
m_database.exec("PRAGMA foreign_keys = ON");
m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER,"
"key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)");
m_database.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)");
m_database.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)");
int version = checkVersion();
if(version == 0)
{
m_database.exec("PRAGMA user_version = 1");
m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME,"
" minRa REAL, maxRa REAL, minDec REAL, maxDec REAL, crVal1 REAL, crVal2 REAL)");
m_database.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER,"
"key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)");
m_database.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)");
m_database.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)");
m_database.exec("CREATE INDEX IF NOT EXISTS minRa_idx ON fits_files(minRa)");
m_database.exec("CREATE INDEX IF NOT EXISTS maxRa_idx ON fits_files(maxRa)");
m_database.exec("CREATE INDEX IF NOT EXISTS minDec_idx ON fits_files(minDec)");
m_database.exec("CREATE INDEX IF NOT EXISTS maxDec_idx ON fits_files(maxDec)");
}
else if(version > 1)
{
qDebug() << "Database version is too new";
return false;
}
QSqlError error = m_database.lastError();
if(error.type() == QSqlError::NoError)
@ -45,6 +61,8 @@ bool Database::init()
m_insertFile = QSqlQuery(m_database);
m_insertFile.prepare("INSERT INTO fits_files (file, mtime) VALUES (?, ?)");
m_insertFileWcs = QSqlQuery(m_database);
m_insertFileWcs.prepare("INSERT INTO fits_files (file, mtime, minRa, maxRa, minDec, maxDec, crVal1, crVal2) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
m_insertFitsHeader = QSqlQuery(m_database);
m_insertFitsHeader.prepare("INSERT INTO fits_headers (id_file, key, value, comment) VALUES (?, ?, ?, ?)");
m_checkFile = QSqlQuery(m_database);
@ -55,7 +73,6 @@ bool Database::init()
m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?");
return true;
}
qDebug() << error.text();
}
}
@ -76,6 +93,20 @@ bool Database::unmark(const QString &filename)
return checkError(m_unmarkQuery);
}
bool Database::mark(const QStringList &filenames)
{
m_markQuery.bindValue(0, filenames);
m_markQuery.execBatch();
return checkError(m_markQuery);
}
bool Database::unmark(const QStringList &filenames)
{
m_unmarkQuery.bindValue(0, filenames);
m_unmarkQuery.execBatch();
return checkError(m_unmarkQuery);
}
bool Database::isMarked(const QString &filename)
{
m_isMarkedQuery.bindValue(":name", filename);
@ -98,6 +129,11 @@ QStringList Database::getMarkedFiles()
return files;
}
void Database::clearMarkedFiles()
{
QSqlDatabase::database().exec("DELETE FROM files");
}
bool Database::checkError(QSqlQuery &query)
{
QSqlError error = query.lastError();
@ -110,6 +146,15 @@ bool Database::checkError(QSqlQuery &query)
}
}
int Database::checkVersion()
{
QSqlDatabase db = QSqlDatabase::database();
QSqlQuery query = db.exec("PRAGMA user_version");
if(query.next())
return query.value(0).toInt();
return -1;
}
static QStringList nameFilters = {"*.fit", "*.fits", "*.xisf"};
static int countFiles(const QDir &dir, int count = 0)
@ -129,9 +174,45 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
QSqlDatabase database = QSqlDatabase::database();
database.transaction();
if(indexDir2(dir, progress))
{
database.commit();
emit databaseChanged();
}
else
{
database.rollback();
}
}
void Database::reindex(QProgressDialog *progress)
{
QVariantList deleteids;
QSqlDatabase database = QSqlDatabase::database();
database.transaction();
QSqlQuery size = database.exec("SELECT COUNT(*) FROM fits_files");
size.next();
progress->setMaximum(size.value(0).toInt());
QSqlQuery files = database.exec("SELECT id,file,mtime FROM fits_files");
int i = 0;
while(files.next())
{
QString path = files.value(1).toString();
QFileInfo file(path);
if(file.exists() && file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate) != files.value(2).toString())
indexFile(file);
if(!file.exists())
deleteids.append(files.value(0));
progress->setValue(i++);
if(progress->wasCanceled())
{
database.rollback();
return;
}
}
QSqlQuery deleteFiles("DELETE FROM fits_files WHERE id = ?", database);
deleteFiles.bindValue(0, deleteids);
deleteFiles.execBatch();
database.commit();
}
QStringList Database::getFitsKeywords()
@ -156,34 +237,63 @@ bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
if(!indexDir2(dir.filePath(d), progress))
return false;
}
ImageInfoData info;
for(const QFileInfo &file : files)
{
progress->setValue(m_progress++);
if(progress->wasCanceled())return false;
QString filePath = file.absoluteFilePath();
QString mtime = file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate);
m_checkFile.bindValue(0, filePath);
m_checkFile.exec();
if(m_checkFile.next())
if(!indexFile(file))return false;
}
return true;
}
bool Database::indexFile(const QFileInfo &file)
{
ImageInfoData info;
QString filePath = file.absoluteFilePath();
QString mtime = file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate);
m_checkFile.bindValue(0, filePath);
m_checkFile.exec();
if(m_checkFile.next())
{
if(m_checkFile.value(1).toString() == mtime)
return true;
else
{
m_deleteFile.bindValue(0, m_checkFile.value(0).toLongLong());
m_deleteFile.exec();
}
}
bool ok;
if(filePath.endsWith(".xisf", Qt::CaseInsensitive))
ok = readXISFHeader(filePath, info);
else
ok = readFITSHeader(filePath, info);
qlonglong last_id = -1;
if(ok)
{
if(info.wcs)
{
if(m_checkFile.value(1).toString() == mtime)
continue;
else
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
info.wcs->calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
qDebug() << "bounds" << minRa << maxRa << minDec << maxDec;
m_insertFileWcs.bindValue(0, filePath);
m_insertFileWcs.bindValue(1, mtime);
m_insertFileWcs.bindValue(2, minRa);
m_insertFileWcs.bindValue(3, maxRa);
m_insertFileWcs.bindValue(4, minDec);
m_insertFileWcs.bindValue(5, maxDec);
m_insertFileWcs.bindValue(6, crVal1);
m_insertFileWcs.bindValue(7, crVal2);
if(!m_insertFileWcs.exec())
{
m_deleteFile.bindValue(0, m_checkFile.value(0).toLongLong());
m_deleteFile.exec();
qDebug() << m_insertFileWcs.lastError();
return false;
}
last_id = m_insertFileWcs.lastInsertId().toLongLong();
}
bool ok;
if(filePath.endsWith(".xisf", Qt::CaseInsensitive))
ok = readXISFHeader(filePath, info);
else
ok = readFITSHeader(filePath, info);
qlonglong last_id = -1;
if(ok)
{
m_insertFile.bindValue(0, filePath);
m_insertFile.bindValue(1, mtime);
@ -193,26 +303,26 @@ bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
return false;
}
last_id = m_insertFile.lastInsertId().toLongLong();
QVariantList file_id, keys, values, comments;
for(auto &record : info.fitsHeader)
{
file_id << last_id;
keys << QString(record.key);
values << record.value.toString();
comments << QString(record.comment);
}
m_insertFitsHeader.bindValue(0, file_id);
m_insertFitsHeader.bindValue(1, keys);
m_insertFitsHeader.bindValue(2, values);
m_insertFitsHeader.bindValue(3, comments);
if(!m_insertFitsHeader.execBatch())
{
qDebug() << m_insertFitsHeader.lastError();
return false;
}
}
qDebug() << filePath << last_id;
info.fitsHeader.clear();
QVariantList file_id, keys, values, comments;
for(const auto &record : info.fitsHeader)
{
file_id << last_id;
keys << QString(record.key);
values << record.value.toString();
comments << QString(record.comment);
}
m_insertFitsHeader.bindValue(0, file_id);
m_insertFitsHeader.bindValue(1, keys);
m_insertFitsHeader.bindValue(2, values);
m_insertFitsHeader.bindValue(3, comments);
if(!m_insertFitsHeader.execBatch())
{
qDebug() << m_insertFitsHeader.lastError();
return false;
}
}
qDebug() << filePath << last_id;
return true;
}

@ -15,6 +15,7 @@ class Database : public QObject
QSqlQuery m_isMarkedQuery;
QSqlQuery m_insertFile;
QSqlQuery m_insertFileWcs;
QSqlQuery m_insertFitsHeader;
QSqlQuery m_checkFile;
QSqlQuery m_headerKeywords;
@ -26,14 +27,22 @@ public:
bool init();
bool mark(const QString &filename);
bool unmark(const QString &filename);
bool mark(const QStringList &filenames);
bool unmark(const QStringList &filenames);
bool isMarked(const QString &filename);
QStringList getMarkedFiles();
void clearMarkedFiles();
void indexDir(const QDir &dir, QProgressDialog *progress);
void reindex(QProgressDialog *progress);
QStringList getFitsKeywords();
protected:
bool indexDir2(const QDir &dir, QProgressDialog *progress);
bool indexFile(const QFileInfo &file);
bool checkError(QSqlQuery &query);
int checkVersion();
signals:
void databaseChanged();
};
#endif // DATABASE_H

@ -6,10 +6,36 @@
#include <QHeaderView>
#include <QSqlError>
#include <QDebug>
#include <QMenu>
#include <QContextMenuEvent>
#include <QRegExp>
#include <iostream>
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
double RA(const QString &ra)
{
QRegularExpression reg("(\\d+)\\s*(\\d+)?\\s*(\\d+)?");
QRegularExpressionMatch match = reg.match(ra);
double h = match.captured(1).toDouble();
double m = match.captured(2).toDouble();
double s = match.captured(3).toDouble();
qDebug() << match.capturedTexts() << h << m << s;
return h*15 + m*0.25 + s*15/3600;
}
double DEC(const QString &dec)
{
QRegularExpression reg("([\\+\\-])?(\\d+)\\s*(\\d+)?\\s*(\\d+)?");
QRegularExpressionMatch match = reg.match(dec);
double sign = match.captured(1) == "-" ? -1 : 1;
double d = match.captured(2).toDouble();
double m = match.captured(3).toDouble();
double s = match.captured(4).toDouble();
qDebug() << match.capturedTexts() << sign << d << m << s;
return sign * (d + m/60 + s/3600);
}
SelectColumnsDialog::SelectColumnsDialog(QWidget *parent) : QDialog(parent)
{
m_listWidget = new QListWidget(this);
@ -47,9 +73,9 @@ QStringList SelectColumnsDialog::selectedColumns()
return ret;
}
FITSFileModel::FITSFileModel(QObject *parent) : QSqlQueryModel(parent)
FITSFileModel::FITSFileModel(Database *database, QObject *parent) : QSqlQueryModel(parent)
, m_database(database)
{
}
void FITSFileModel::sort(int column, Qt::SortOrder order)
@ -71,31 +97,79 @@ void FITSFileModel::setColumns(const QStringList &columns)
prepareQuery();
}
void FITSFileModel::setFilter(const QStringList &key, const QStringList &value)
void FITSFileModel::setFilter(const QStringList &key, const QStringList &value, const QStringList &limit)
{
if(value.isEmpty())
{
m_key.clear();
m_value.clear();
m_limit.clear();
}
else
{
m_key = key;
m_value = value;
m_limit = limit;
}
prepareQuery();
}
QVariant FITSFileModel::data(const QModelIndex &index, int role) const
{
if(role == Qt::FontRole && index.column() == 0)
{
QFont font;
QString file = index.data().toString();
font.setBold(m_markedFiles.contains(file));
return font;
}
return QSqlQueryModel::data(index, role);
}
void FITSFileModel::filesMarked(const QModelIndexList &indexes)
{
for(auto &index : indexes)
{
QString file = index.data().toString();
if(!m_markedFiles.contains(file))
{
m_markedFiles.insert(file);
emit dataChanged(index, index, {Qt::FontRole});
}
}
}
void FITSFileModel::filesUnmarked(const QModelIndexList &indexes)
{
for(auto &index : indexes)
{
QString file = index.data().toString();
if(m_markedFiles.contains(file))
{
m_markedFiles.remove(file);
emit dataChanged(index, index, {Qt::FontRole});
}
}
}
void FITSFileModel::prepareQuery()
{
QString cols;
QString join;
QString where;
QStringList where;
QString sql = "SELECT f.file,";
for(int i=0; i<m_value.size(); i++)
{
if(m_key[i] == "file")
where = QString(" WHERE f.file LIKE '%1'").arg(m_value[i]);
where.append(QString(" f.file LIKE '%1' ").arg(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])));
else if(m_key[i] == "DEC pos")
where.append(QString(" %1 BETWEEN f.minDec AND f.maxDec ").arg(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])));
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])));
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]);
}
@ -110,7 +184,7 @@ void FITSFileModel::prepareQuery()
sql += cols;
sql += " FROM fits_files AS f";
sql += join;
sql += where;
if(!where.isEmpty())sql += " WHERE " + where.join("AND");
sql += " GROUP BY f.id" + m_sort;