Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb417010c3 | |||
| 5b44d2ac69 | |||
| d1df789691 | |||
| ab7d04b625 | |||
| a4cfc65d4b | |||
| b4746be190 | |||
| 9ceb7556f9 | |||
| 41b29f0701 | |||
| 67ae2d4b62 | |||
| b6b6863331 | |||
| 571fa57af2 | |||
| abb3d631bf | |||
| b65911943e | |||
| 9d9f8db499 | |||
| 3060b17c0c | |||
| fcf336d63a | |||
| 54ef8e990c | |||
| 94466a6b9b | |||
| b84d8127ad | |||
| c971a919ec | |||
| 8c248b7cfc | |||
| 43b510a78c | |||
| 105fba814d | |||
| 555e6c11c8 | |||
| 748f5fac03 | |||
| adc7d07b75 | |||
| ba450ee554 | |||
| cc69b7bc2d | |||
| 42b619641a | |||
| 9af4fa1b99 | |||
| cbc6775756 | |||
| cee6979ece | |||
| 903ec65d52 | |||
| 95e4774507 | |||
| 2410c51d5d | |||
| 17bca74362 | |||
| c70123cf7b | |||
| 12c6385f77 | |||
| 39f3ec7d30 | |||
| 8b968ddcb1 | |||
| da1843e48c | |||
| e0d473c8c8 | |||
| 92f9920f24 | |||
| f68a9c4d7c | |||
| 027a38cb42 | |||
| 47d5a9fc96 | |||
| 061bb3892e | |||
| b0b1a3a14b | |||
| ea834ebd16 | |||
| ce836a8ff3 | |||
| a1848b27bf | |||
| fabf3f0c1a | |||
| cba8a0bb9c | |||
| 4e6230eef2 | |||
| 2c95364fc4 | |||
| 26be690c70 | |||
| 56d6db8bc3 |
Vendored
+43
@@ -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.
|
||||||
|
|
||||||
|
*******************************************************************************
|
||||||
+42
-10
@@ -15,11 +15,13 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
|
|||||||
find_package(Qt5 COMPONENTS Widgets Sql OpenGL REQUIRED)
|
find_package(Qt5 COMPONENTS Widgets Sql OpenGL REQUIRED)
|
||||||
find_package(OpenCV REQUIRED)
|
find_package(OpenCV REQUIRED)
|
||||||
find_library(GSL_LIB gsl REQUIRED)
|
find_library(GSL_LIB gsl REQUIRED)
|
||||||
|
find_library(GSLCBLAS_LIB gslcblas 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 raw REQUIRED)
|
find_library(RAW_LIB NAMES raw_r REQUIRED)
|
||||||
|
|
||||||
set(TENMON_SRC
|
set(TENMON_SRC
|
||||||
|
about.cpp
|
||||||
database.cpp
|
database.cpp
|
||||||
databaseview.cpp
|
databaseview.cpp
|
||||||
filesystemwidget.cpp
|
filesystemwidget.cpp
|
||||||
@@ -30,32 +32,62 @@ set(TENMON_SRC
|
|||||||
loadrunable.cpp
|
loadrunable.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
|
markedfiles.cpp
|
||||||
rawimage.cpp
|
rawimage.cpp
|
||||||
starfit.cpp
|
starfit.cpp
|
||||||
stfslider.cpp
|
stfslider.cpp
|
||||||
stretchpanel.cpp
|
stretchtoolbar.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt5_add_resources(TENMON_SRC resources.qrc)
|
qt5_add_resources(TENMON_SRC resources.qrc)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND TENMON_SRC icon.rc)
|
list(APPEND TENMON_SRC icon.rc)
|
||||||
add_compile_definitions("__PCL_WINDOWS")
|
add_compile_definitions("__PCL_WINDOWS")
|
||||||
endif(WIN32)
|
else()
|
||||||
if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
|
|
||||||
add_compile_definitions("__PCL_LINUX")
|
add_compile_definitions("__PCL_LINUX")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(tenmon ${TENMON_SRC})
|
add_executable(tenmon WIN32 ${TENMON_SRC})
|
||||||
|
|
||||||
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
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)
|
||||||
|
else()
|
||||||
|
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_directories(tenmon PRIVATE 3rdparty/lib/${CMAKE_HOST_SYSTEM_NAME})
|
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB})
|
||||||
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib)
|
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)
|
install(TARGETS tenmon)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
install(SCRIPT install.cmake)
|
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)
|
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,11 @@
|
|||||||
Simple image viewer with multithreaded image loading
|
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 qtbase5-dev libraw-dev libexif-dev libcfitsio-dev
|
sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev cmake
|
||||||
|
|
||||||
Then to build run
|
Then to build run standard cmake
|
||||||
|
|
||||||
qmake .
|
cmake -B build -S .
|
||||||
make
|
make
|
||||||
./tenmon
|
./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,97 @@
|
|||||||
|
<!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>
|
||||||
|
|
||||||
|
<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 thumnails 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>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.
|
||||||
|
<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,84 @@
|
|||||||
|
<!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>
|
||||||
|
|
||||||
|
<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>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>Également au bas du panneau de la base de données se trouvent trois zones 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é.</p>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
+108
-53
@@ -76,6 +76,20 @@ bool Database::unmark(const QString &filename)
|
|||||||
return checkError(m_unmarkQuery);
|
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)
|
bool Database::isMarked(const QString &filename)
|
||||||
{
|
{
|
||||||
m_isMarkedQuery.bindValue(":name", filename);
|
m_isMarkedQuery.bindValue(":name", filename);
|
||||||
@@ -98,6 +112,11 @@ QStringList Database::getMarkedFiles()
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::clearMarkedFiles()
|
||||||
|
{
|
||||||
|
QSqlDatabase::database().exec("DELETE FROM files");
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::checkError(QSqlQuery &query)
|
bool Database::checkError(QSqlQuery &query)
|
||||||
{
|
{
|
||||||
QSqlError error = query.lastError();
|
QSqlError error = query.lastError();
|
||||||
@@ -134,6 +153,37 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
|||||||
database.rollback();
|
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()
|
QStringList Database::getFitsKeywords()
|
||||||
{
|
{
|
||||||
m_headerKeywords.exec();
|
m_headerKeywords.exec();
|
||||||
@@ -156,63 +206,68 @@ bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
|
|||||||
if(!indexDir2(dir.filePath(d), progress))
|
if(!indexDir2(dir.filePath(d), progress))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ImageInfoData info;
|
|
||||||
for(const QFileInfo &file : files)
|
for(const QFileInfo &file : files)
|
||||||
{
|
{
|
||||||
progress->setValue(m_progress++);
|
progress->setValue(m_progress++);
|
||||||
if(progress->wasCanceled())return false;
|
if(progress->wasCanceled())return false;
|
||||||
QString filePath = file.absoluteFilePath();
|
if(!indexFile(file))return false;
|
||||||
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)
|
|
||||||
continue;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
m_insertFile.bindValue(0, filePath);
|
|
||||||
m_insertFile.bindValue(1, mtime);
|
|
||||||
if(!m_insertFile.exec())
|
|
||||||
{
|
|
||||||
qDebug() << m_insertFile.lastError();
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
return true;
|
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)
|
||||||
|
{
|
||||||
|
m_insertFile.bindValue(0, filePath);
|
||||||
|
m_insertFile.bindValue(1, mtime);
|
||||||
|
if(!m_insertFile.exec())
|
||||||
|
{
|
||||||
|
qDebug() << m_insertFile.lastError();
|
||||||
|
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;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,13 +26,18 @@ public:
|
|||||||
bool init();
|
bool init();
|
||||||
bool mark(const QString &filename);
|
bool mark(const QString &filename);
|
||||||
bool unmark(const QString &filename);
|
bool unmark(const QString &filename);
|
||||||
|
bool mark(const QStringList &filenames);
|
||||||
|
bool unmark(const QStringList &filenames);
|
||||||
bool isMarked(const QString &filename);
|
bool isMarked(const QString &filename);
|
||||||
QStringList getMarkedFiles();
|
QStringList getMarkedFiles();
|
||||||
|
void clearMarkedFiles();
|
||||||
|
|
||||||
void indexDir(const QDir &dir, QProgressDialog *progress);
|
void indexDir(const QDir &dir, QProgressDialog *progress);
|
||||||
|
void reindex(QProgressDialog *progress);
|
||||||
QStringList getFitsKeywords();
|
QStringList getFitsKeywords();
|
||||||
protected:
|
protected:
|
||||||
bool indexDir2(const QDir &dir, QProgressDialog *progress);
|
bool indexDir2(const QDir &dir, QProgressDialog *progress);
|
||||||
|
bool indexFile(const QFileInfo &file);
|
||||||
bool checkError(QSqlQuery &query);
|
bool checkError(QSqlQuery &query);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+85
-5
@@ -6,6 +6,8 @@
|
|||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QContextMenuEvent>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
|
const QStringList DEFAULT_COLUMNS = {"EXPTIME", "OBJECT", "RA", "DEC"};
|
||||||
@@ -47,9 +49,9 @@ QStringList SelectColumnsDialog::selectedColumns()
|
|||||||
return ret;
|
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)
|
void FITSFileModel::sort(int column, Qt::SortOrder order)
|
||||||
@@ -86,6 +88,44 @@ void FITSFileModel::setFilter(const QStringList &key, const QStringList &value)
|
|||||||
prepareQuery();
|
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()
|
void FITSFileModel::prepareQuery()
|
||||||
{
|
{
|
||||||
QString cols;
|
QString cols;
|
||||||
@@ -122,6 +162,31 @@ void FITSFileModel::prepareQuery()
|
|||||||
std::cout << sql.toStdString() << std::endl;
|
std::cout << sql.toStdString() << std::endl;
|
||||||
if(lastError().type() != QSqlError::NoError)
|
if(lastError().type() != QSqlError::NoError)
|
||||||
qDebug() << lastError();
|
qDebug() << lastError();
|
||||||
|
|
||||||
|
m_markedFiles = m_database->getMarkedFiles().toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTableView::DatabaseTableView(QWidget *parent) : QTableView(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseTableView::contextMenuEvent(QContextMenuEvent *event)
|
||||||
|
{
|
||||||
|
QMenu menu;
|
||||||
|
QAction *mark = menu.addAction(tr("Mark"));
|
||||||
|
QAction *unmark = menu.addAction(tr("Unmark"));
|
||||||
|
|
||||||
|
QAction *a = menu.exec(event->globalPos());
|
||||||
|
if(a == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QModelIndexList indexes = selectionModel()->selectedRows();
|
||||||
|
|
||||||
|
if(a == mark)
|
||||||
|
emit filesMarked(indexes);
|
||||||
|
else if(a == unmark)
|
||||||
|
emit filesUnmarked(indexes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent)
|
DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent)
|
||||||
@@ -130,14 +195,15 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
|
|||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
m_tableView = new QTableView(this);
|
m_tableView = new DatabaseTableView(this);
|
||||||
m_tableView->verticalHeader()->setDefaultSectionSize(1);
|
m_tableView->verticalHeader()->setDefaultSectionSize(1);
|
||||||
m_tableView->setSortingEnabled(true);
|
m_tableView->setSortingEnabled(true);
|
||||||
m_tableView->horizontalHeader()->setSortIndicatorShown(true);
|
m_tableView->horizontalHeader()->setSortIndicatorShown(true);
|
||||||
|
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
layout->addWidget(m_tableView);
|
layout->addWidget(m_tableView);
|
||||||
connect(m_tableView, &QTableView::activated, this, &DataBaseView::itemActivated);
|
connect(m_tableView, &QTableView::activated, this, &DataBaseView::itemActivated);
|
||||||
|
|
||||||
m_model = new FITSFileModel(this);
|
m_model = new FITSFileModel(m_database, this);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
m_tableView->setModel(m_model);
|
m_tableView->setModel(m_model);
|
||||||
@@ -151,6 +217,21 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
|
|||||||
hlayout->addWidget(selectColumnsButton);
|
hlayout->addWidget(selectColumnsButton);
|
||||||
connect(selectColumnsButton, &QPushButton::pressed, this, &DataBaseView::selectColumns);
|
connect(selectColumnsButton, &QPushButton::pressed, this, &DataBaseView::selectColumns);
|
||||||
|
|
||||||
|
connect(m_tableView, &DatabaseTableView::filesMarked, [this](QModelIndexList indexes){
|
||||||
|
QStringList files;
|
||||||
|
for(auto &index : indexes)
|
||||||
|
files.append(index.data().toString());
|
||||||
|
m_database->mark(files);
|
||||||
|
m_model->filesMarked(indexes);
|
||||||
|
});
|
||||||
|
connect(m_tableView, &DatabaseTableView::filesUnmarked, [this](QModelIndexList indexes){
|
||||||
|
QStringList files;
|
||||||
|
for(auto &index : indexes)
|
||||||
|
files.append(index.data().toString());
|
||||||
|
m_database->unmark(files);
|
||||||
|
m_model->filesUnmarked(indexes);
|
||||||
|
});
|
||||||
|
|
||||||
for(int i=0; i<3; i++)
|
for(int i=0; i<3; i++)
|
||||||
{
|
{
|
||||||
m_filterKeyword[i] = new QComboBox(this);
|
m_filterKeyword[i] = new QComboBox(this);
|
||||||
@@ -214,4 +295,3 @@ void DataBaseView::applyFilter()
|
|||||||
}
|
}
|
||||||
m_model->setFilter(keys, values);
|
m_model->setFilter(keys, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-5
@@ -15,7 +15,7 @@ class SelectColumnsDialog : public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QListWidget *m_listWidget;
|
QListWidget *m_listWidget;
|
||||||
public:
|
public:
|
||||||
SelectColumnsDialog(QWidget *parent = nullptr);
|
explicit SelectColumnsDialog(QWidget *parent = nullptr);
|
||||||
void setColumns(QStringList columns);
|
void setColumns(QStringList columns);
|
||||||
QStringList selectedColumns();
|
QStringList selectedColumns();
|
||||||
};
|
};
|
||||||
@@ -27,26 +27,43 @@ class FITSFileModel : public QSqlQueryModel
|
|||||||
QString m_sort;
|
QString m_sort;
|
||||||
QStringList m_key;
|
QStringList m_key;
|
||||||
QStringList m_value;
|
QStringList m_value;
|
||||||
|
QSet<QString> m_markedFiles;
|
||||||
|
Database *m_database;
|
||||||
public:
|
public:
|
||||||
FITSFileModel(QObject *parent = nullptr);
|
explicit FITSFileModel(Database *database, QObject *parent = nullptr);
|
||||||
void sort(int column, Qt::SortOrder order);
|
void sort(int column, Qt::SortOrder order) override;
|
||||||
void setColumns(const QStringList &columns);
|
void setColumns(const QStringList &columns);
|
||||||
void setFilter(const QStringList &key, const QStringList &value);
|
void setFilter(const QStringList &key, const QStringList &value);
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
void filesMarked(const QModelIndexList &indexes);
|
||||||
|
void filesUnmarked(const QModelIndexList &indexes);
|
||||||
protected:
|
protected:
|
||||||
void prepareQuery();
|
void prepareQuery();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DatabaseTableView : public QTableView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DatabaseTableView(QWidget *parent = nullptr);
|
||||||
|
protected:
|
||||||
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
signals:
|
||||||
|
void filesMarked(QModelIndexList indexes);
|
||||||
|
void filesUnmarked(QModelIndexList indexes);
|
||||||
|
};
|
||||||
|
|
||||||
class DataBaseView : public QWidget
|
class DataBaseView : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
QTableView *m_tableView;
|
DatabaseTableView *m_tableView;
|
||||||
FITSFileModel *m_model;
|
FITSFileModel *m_model;
|
||||||
QComboBox *m_filterKeyword[3];
|
QComboBox *m_filterKeyword[3];
|
||||||
QLineEdit *m_search[3];
|
QLineEdit *m_search[3];
|
||||||
public:
|
public:
|
||||||
explicit DataBaseView(Database *database, QWidget *parent = nullptr);
|
explicit DataBaseView(Database *database, QWidget *parent = nullptr);
|
||||||
~DataBaseView();
|
~DataBaseView() override;
|
||||||
public slots:
|
public slots:
|
||||||
void selectColumns();
|
void selectColumns();
|
||||||
void loadDatabase();
|
void loadDatabase();
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#include "filesystemwidget.h"
|
#include "filesystemwidget.h"
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QContextMenuEvent>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent)
|
FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent)
|
||||||
, m_model(model)
|
, m_model(model)
|
||||||
@@ -34,3 +38,97 @@ void FilesystemWidget::fileClicked(const QModelIndex &index)
|
|||||||
{
|
{
|
||||||
emit fileSelected(index.row());
|
emit fileSelected(index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Filetree::Filetree(QWidget *parent) : QTreeView(parent)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
m_rootDir = settings.value("filetree/rootDir", QDir::homePath()).toString();
|
||||||
|
m_fileSystemModel = new QFileSystemModel(this);
|
||||||
|
m_fileSystemModel->setRootPath(m_rootDir);
|
||||||
|
m_fileSystemModel->setNameFilters({"*.fits", "*.fit", "*.xisf", "*.jpg", "*.jpeg", "*.png", "*.cr2", "*.nef", "*.dng"});
|
||||||
|
m_fileSystemModel->setNameFilterDisables(false);
|
||||||
|
setModel(m_fileSystemModel);
|
||||||
|
setRootIndex(m_fileSystemModel->index(m_rootDir));
|
||||||
|
header()->restoreState(settings.value("filetree/header").toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
Filetree::~Filetree()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("filetree/rootDir", m_rootDir);
|
||||||
|
settings.setValue("filetree/header", header()->saveState());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
||||||
|
{
|
||||||
|
QModelIndex index = indexAt(event->pos());
|
||||||
|
QFileInfo info = m_fileSystemModel->fileInfo(index);
|
||||||
|
QMenu menu;
|
||||||
|
QAction *open = nullptr;
|
||||||
|
QAction *setRoot = nullptr;
|
||||||
|
QAction *copy = nullptr;
|
||||||
|
QAction *move = nullptr;
|
||||||
|
QAction *indexDir = nullptr;
|
||||||
|
|
||||||
|
if(info.isFile())
|
||||||
|
open = menu.addAction(tr("Open"));
|
||||||
|
|
||||||
|
if(info.isDir())
|
||||||
|
{
|
||||||
|
open = menu.addAction(tr("Open"));
|
||||||
|
setRoot = menu.addAction(tr("Set as root"));
|
||||||
|
copy = menu.addAction(tr("Copy marked files"));
|
||||||
|
move = menu.addAction(tr("Move marked files"));
|
||||||
|
indexDir = menu.addAction(tr("Index directory"));
|
||||||
|
}
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
QAction *resetRoot = menu.addAction(tr("Reset root"));
|
||||||
|
QAction *goUp = menu.addAction(tr("Go up"));
|
||||||
|
|
||||||
|
QAction *a = menu.exec(event->globalPos());
|
||||||
|
if(a == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(a == open)
|
||||||
|
{
|
||||||
|
emit fileSelected(m_fileSystemModel->filePath(index));
|
||||||
|
}
|
||||||
|
else if(a == setRoot && index.isValid())
|
||||||
|
{
|
||||||
|
setRootIndex(index);
|
||||||
|
m_rootDir = m_fileSystemModel->filePath(index);
|
||||||
|
}
|
||||||
|
else if(a == resetRoot)
|
||||||
|
{
|
||||||
|
setRootIndex(QModelIndex());
|
||||||
|
m_rootDir = QDir::rootPath();
|
||||||
|
}
|
||||||
|
else if(a == goUp)
|
||||||
|
{
|
||||||
|
setRootIndex(rootIndex().parent());
|
||||||
|
m_rootDir = m_fileSystemModel->filePath(rootIndex().parent());
|
||||||
|
}
|
||||||
|
else if(a == copy)
|
||||||
|
{
|
||||||
|
emit copyFiles(m_fileSystemModel->filePath(index));
|
||||||
|
}
|
||||||
|
else if(a == move)
|
||||||
|
{
|
||||||
|
emit moveFiles(m_fileSystemModel->filePath(index));
|
||||||
|
}
|
||||||
|
else if(a == indexDir)
|
||||||
|
{
|
||||||
|
emit indexDirectory(m_fileSystemModel->filePath(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filetree::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QModelIndex index = indexAt(event->pos());
|
||||||
|
QFileInfo info = m_fileSystemModel->fileInfo(index);
|
||||||
|
if(info.isFile())
|
||||||
|
emit fileSelected(info.filePath());
|
||||||
|
else
|
||||||
|
QTreeView::mouseDoubleClickEvent(event);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
class FilesystemWidget : public QWidget
|
class FilesystemWidget : public QWidget
|
||||||
{
|
{
|
||||||
@@ -20,4 +21,21 @@ signals:
|
|||||||
void fileSelected(int row);
|
void fileSelected(int row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Filetree : public QTreeView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QFileSystemModel *m_fileSystemModel;
|
||||||
|
QString m_rootDir;
|
||||||
|
public:
|
||||||
|
explicit Filetree(QWidget *parent = nullptr);
|
||||||
|
~Filetree() override;
|
||||||
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
signals:
|
||||||
|
void fileSelected(const QString &path);
|
||||||
|
void copyFiles(const QString &path);
|
||||||
|
void moveFiles(const QString &path);
|
||||||
|
void indexDirectory(const QString &path);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // FILESYSTEMWIDGET_H
|
#endif // FILESYSTEMWIDGET_H
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
set(_build_version "unknown")
|
||||||
|
|
||||||
|
find_package(Git)
|
||||||
|
if(GIT_FOUND)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} describe --tags HEAD
|
||||||
|
WORKING_DIRECTORY "${local_dir}"
|
||||||
|
OUTPUT_VARIABLE _build_version
|
||||||
|
ERROR_QUIET
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
message(STATUS "GIT hash: ${_build_version}")
|
||||||
|
else()
|
||||||
|
message(STATUS "GIT not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "local:${local_dir} output:${output_dir}")
|
||||||
|
|
||||||
|
configure_file("${local_dir}/gitversion.h.in" "${output_dir}/gitversion.h" @ONLY)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef GITVERSION_H
|
||||||
|
#define GITVERSION_H
|
||||||
|
|
||||||
|
#define GITVERSION "@_build_version@"
|
||||||
|
|
||||||
|
#endif
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
#version 130
|
#version 330
|
||||||
|
|
||||||
uniform sampler2D qt_Texture0;
|
uniform sampler2D qt_Texture0;
|
||||||
in vec2 qt_TexCoord0;
|
|
||||||
uniform vec3 mtf_param;
|
uniform vec3 mtf_param;
|
||||||
uniform bool bw;
|
uniform bool bw;
|
||||||
uniform bool invert;
|
uniform bool invert;
|
||||||
|
in vec2 qt_TexCoord0;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
vec4 MTF(vec4 x, vec3 m)
|
vec4 MTF(vec4 x, vec3 m)
|
||||||
@@ -16,7 +16,7 @@ vec4 MTF(vec4 x, vec3 m)
|
|||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
color = texture2D(qt_Texture0, qt_TexCoord0);
|
color = texture(qt_Texture0, qt_TexCoord0);
|
||||||
if(bw)color = color.rrra;
|
if(bw)color = color.rrra;
|
||||||
color = MTF(color, mtf_param);
|
color = MTF(color, mtf_param);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#version 130
|
#version 330
|
||||||
|
|
||||||
uniform sampler2D qt_Texture0;
|
uniform sampler2D qt_Texture0;
|
||||||
in vec2 qt_Vertex;
|
in vec2 qt_Vertex;
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class ImageInfo : public QTreeWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ImageInfo(QWidget *parent);
|
explicit ImageInfo(QWidget *parent);
|
||||||
~ImageInfo();
|
~ImageInfo() override;
|
||||||
public slots:
|
public slots:
|
||||||
void setInfo(const ImageInfoData &info);
|
void setInfo(const ImageInfoData &info);
|
||||||
};
|
};
|
||||||
|
|||||||
+65
-5
@@ -8,10 +8,11 @@ using namespace std;
|
|||||||
|
|
||||||
const int DEFAULT_WIDTH = 2;
|
const int DEFAULT_WIDTH = 2;
|
||||||
|
|
||||||
Image::Image(const QString name, ImageRingList *ringList) :
|
Image::Image(const QString name, int number, ImageRingList *ringList) :
|
||||||
m_loading(false),
|
m_loading(false),
|
||||||
m_released(true),
|
m_released(true),
|
||||||
m_current(false),
|
m_current(false),
|
||||||
|
m_number(number),
|
||||||
m_name(name),
|
m_name(name),
|
||||||
m_ringList(ringList)
|
m_ringList(ringList)
|
||||||
{
|
{
|
||||||
@@ -29,6 +30,14 @@ void Image::load()
|
|||||||
emit pixmapLoaded(this);
|
emit pixmapLoaded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::loadThumbnail(QThreadPool *pool)
|
||||||
|
{
|
||||||
|
if(!m_thumbnail)
|
||||||
|
pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, true));
|
||||||
|
else
|
||||||
|
emit thumbnailLoaded(this);
|
||||||
|
}
|
||||||
|
|
||||||
void Image::release()
|
void Image::release()
|
||||||
{
|
{
|
||||||
m_rawImage.reset();
|
m_rawImage.reset();
|
||||||
@@ -46,6 +55,11 @@ RawImage *Image::rawImage()
|
|||||||
return m_rawImage.get();
|
return m_rawImage.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RawImage *Image::thumbnail() const
|
||||||
|
{
|
||||||
|
return m_thumbnail.get();
|
||||||
|
}
|
||||||
|
|
||||||
ImageInfoData Image::info() const
|
ImageInfoData Image::info() const
|
||||||
{
|
{
|
||||||
return m_info;
|
return m_info;
|
||||||
@@ -56,6 +70,11 @@ bool Image::isCurrent() const
|
|||||||
return !m_released;
|
return !m_released;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Image::number() const
|
||||||
|
{
|
||||||
|
return m_number;
|
||||||
|
}
|
||||||
|
|
||||||
void Image::imageLoaded(void *rawImage, ImageInfoData info)
|
void Image::imageLoaded(void *rawImage, ImageInfoData info)
|
||||||
{
|
{
|
||||||
m_loading = false;
|
m_loading = false;
|
||||||
@@ -71,17 +90,28 @@ void Image::imageLoaded(void *rawImage, ImageInfoData info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::thumbnailLoadFinish(void *rawImage)
|
||||||
|
{
|
||||||
|
m_thumbnail.reset(static_cast<RawImage*>(rawImage));
|
||||||
|
if(m_thumbnail)
|
||||||
|
emit thumbnailLoaded(this);
|
||||||
|
}
|
||||||
|
|
||||||
ImageRingList::ImageRingList(QObject *parent) : QAbstractItemModel(parent)
|
ImageRingList::ImageRingList(QObject *parent) : QAbstractItemModel(parent)
|
||||||
, m_liveMode(false)
|
, m_liveMode(false)
|
||||||
, m_analyzeLevel(None)
|
, m_analyzeLevel(None)
|
||||||
{
|
{
|
||||||
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
||||||
|
m_thumbPool = new QThreadPool(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageRingList::~ImageRingList()
|
ImageRingList::~ImageRingList()
|
||||||
{
|
{
|
||||||
QThreadPool::globalInstance()->clear();
|
QThreadPool::globalInstance()->clear();
|
||||||
|
m_thumbPool->clear();
|
||||||
|
|
||||||
QThreadPool::globalInstance()->waitForDone();
|
QThreadPool::globalInstance()->waitForDone();
|
||||||
|
m_thumbPool->waitForDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
||||||
@@ -91,9 +121,9 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
|||||||
if(dir.exists())
|
if(dir.exists())
|
||||||
{
|
{
|
||||||
QStringList nameFilter;
|
QStringList nameFilter;
|
||||||
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.fit" << "*.fits" << "*.xisf";
|
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.nef" << "*.dng" << "*.fit" << "*.fits" << "*.xisf";
|
||||||
|
|
||||||
QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name);
|
QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
|
||||||
QStringList absolutePaths;
|
QStringList absolutePaths;
|
||||||
foreach(const QString &file, list)
|
foreach(const QString &file, list)
|
||||||
{
|
{
|
||||||
@@ -209,6 +239,32 @@ void ImageRingList::loadFile(int row)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageRingList::loadThumbnails()
|
||||||
|
{
|
||||||
|
for(auto &img : m_images)
|
||||||
|
{
|
||||||
|
img->loadThumbnail(m_thumbPool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageRingList::stopLoading()
|
||||||
|
{
|
||||||
|
m_thumbPool->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageRingList::imageCount() const
|
||||||
|
{
|
||||||
|
return m_images.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ImageRingList::imageNames() const
|
||||||
|
{
|
||||||
|
QStringList ret;
|
||||||
|
for(auto &img : m_images)
|
||||||
|
ret.push_back(img->name());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return createIndex(row, column, m_images.at(row).get());
|
return createIndex(row, column, m_images.at(row).get());
|
||||||
@@ -258,13 +314,17 @@ QVariant ImageRingList::headerData(int section, Qt::Orientation orientation, int
|
|||||||
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
||||||
{
|
{
|
||||||
QThreadPool::globalInstance()->clear();
|
QThreadPool::globalInstance()->clear();
|
||||||
|
m_thumbPool->clear();
|
||||||
QThreadPool::globalInstance()->waitForDone();
|
QThreadPool::globalInstance()->waitForDone();
|
||||||
|
m_thumbPool->waitForDone();
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_images.clear();
|
m_images.clear();
|
||||||
foreach(const QString &file, files)
|
int i = 0;
|
||||||
|
for(const QString &file : files)
|
||||||
{
|
{
|
||||||
ImagePtr ptr = make_shared<Image>(file, this);
|
ImagePtr ptr = make_shared<Image>(file, i++, this);
|
||||||
connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*)));
|
connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*)));
|
||||||
|
connect(ptr.get(), SIGNAL(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*)));
|
||||||
m_images.append(ptr);
|
m_images.append(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-2
@@ -10,6 +10,7 @@
|
|||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
|
||||||
class ImageRingList;
|
class ImageRingList;
|
||||||
|
class QThreadPool;
|
||||||
|
|
||||||
class Image : public QObject
|
class Image : public QObject
|
||||||
{
|
{
|
||||||
@@ -17,22 +18,29 @@ class Image : public QObject
|
|||||||
bool m_loading;
|
bool m_loading;
|
||||||
bool m_released;
|
bool m_released;
|
||||||
bool m_current;
|
bool m_current;
|
||||||
|
int m_number;
|
||||||
std::unique_ptr<RawImage> m_rawImage;
|
std::unique_ptr<RawImage> m_rawImage;
|
||||||
|
std::unique_ptr<RawImage> m_thumbnail;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
ImageInfoData m_info;
|
ImageInfoData m_info;
|
||||||
ImageRingList *m_ringList;
|
ImageRingList *m_ringList;
|
||||||
public:
|
public:
|
||||||
explicit Image(const QString name, ImageRingList *ringList);
|
explicit Image(const QString name, int number, ImageRingList *ringList);
|
||||||
void load();
|
void load();
|
||||||
|
void loadThumbnail(QThreadPool *pool);
|
||||||
void release();
|
void release();
|
||||||
QString name() const;
|
QString name() const;
|
||||||
RawImage* rawImage();
|
RawImage* rawImage();
|
||||||
|
const RawImage* thumbnail() const;
|
||||||
ImageInfoData info() const;
|
ImageInfoData info() const;
|
||||||
bool isCurrent() const;
|
bool isCurrent() const;
|
||||||
|
int number() const;
|
||||||
signals:
|
signals:
|
||||||
void pixmapLoaded(Image *ptr);
|
void pixmapLoaded(Image *ptr);
|
||||||
|
void thumbnailLoaded(Image *ptr);
|
||||||
protected slots:
|
protected slots:
|
||||||
void imageLoaded(void *rawImage, ImageInfoData info);
|
void imageLoaded(void *rawImage, ImageInfoData info);
|
||||||
|
void thumbnailLoadFinish(void *rawImage);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Image> ImagePtr;
|
typedef std::shared_ptr<Image> ImagePtr;
|
||||||
@@ -48,9 +56,10 @@ class ImageRingList : public QAbstractItemModel
|
|||||||
QFileSystemWatcher m_fileSystemWatcher;
|
QFileSystemWatcher m_fileSystemWatcher;
|
||||||
bool m_liveMode;
|
bool m_liveMode;
|
||||||
AnalyzeLevel m_analyzeLevel;
|
AnalyzeLevel m_analyzeLevel;
|
||||||
|
QThreadPool *m_thumbPool;
|
||||||
public:
|
public:
|
||||||
explicit ImageRingList(QObject *parent = 0);
|
explicit ImageRingList(QObject *parent = 0);
|
||||||
~ImageRingList();
|
~ImageRingList() override;
|
||||||
bool setDir(const QString path, const QString ¤tFile = QString());
|
bool setDir(const QString path, const QString ¤tFile = QString());
|
||||||
void setFile(const QString &file);
|
void setFile(const QString &file);
|
||||||
ImagePtr currentImage();
|
ImagePtr currentImage();
|
||||||
@@ -62,6 +71,10 @@ public:
|
|||||||
void setFindStars(bool findStars);
|
void setFindStars(bool findStars);
|
||||||
AnalyzeLevel analyzeLevel() const;
|
AnalyzeLevel analyzeLevel() const;
|
||||||
void loadFile(int row);
|
void loadFile(int row);
|
||||||
|
void loadThumbnails();
|
||||||
|
void stopLoading();
|
||||||
|
int imageCount() const;
|
||||||
|
QStringList imageNames() const;
|
||||||
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QModelIndex parent(const QModelIndex &child) const override;
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
@@ -75,6 +88,7 @@ protected:
|
|||||||
QList<ImagePtr>::iterator decrement(QList<ImagePtr>::iterator iter);
|
QList<ImagePtr>::iterator decrement(QList<ImagePtr>::iterator iter);
|
||||||
signals:
|
signals:
|
||||||
void pixmapLoaded(Image *image);
|
void pixmapLoaded(Image *image);
|
||||||
|
void thumbnailLoaded(Image *image);
|
||||||
void infoLoaded(ImageInfoData info);
|
void infoLoaded(ImageInfoData info);
|
||||||
void currentImageChanged(int index);
|
void currentImageChanged(int index);
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|||||||
+6
-6
@@ -21,12 +21,12 @@ public slots:
|
|||||||
void bestFit();
|
void bestFit();
|
||||||
void oneToOne();
|
void oneToOne();
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event);
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void keyReleaseEvent(QKeyEvent *event);
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
void mouseMoveEvent(QMouseEvent *event);
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event);
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event);
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IMAGESCROLLAREA_H
|
#endif // IMAGESCROLLAREA_H
|
||||||
|
|||||||
+329
-54
@@ -6,6 +6,11 @@
|
|||||||
#include <QOpenGLPixelTransferOptions>
|
#include <QOpenGLPixelTransferOptions>
|
||||||
#include <QOpenGLFramebufferObject>
|
#include <QOpenGLFramebufferObject>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
struct RawImageType
|
struct RawImageType
|
||||||
{
|
{
|
||||||
@@ -41,7 +46,8 @@ void setScrollRange(QScrollBar *scrollBar, int newRange)
|
|||||||
scrollBar->setValue(relPos*newRange - page/2);
|
scrollBar->setValue(relPos*newRange - page/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWidget::ImageWidget(QWidget *parent) : QOpenGLWidget(parent)
|
ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(parent)
|
||||||
|
, m_database(database)
|
||||||
{
|
{
|
||||||
setFocusPolicy(Qt::ClickFocus);
|
setFocusPolicy(Qt::ClickFocus);
|
||||||
m_range = UINT16_MAX;
|
m_range = UINT16_MAX;
|
||||||
@@ -54,6 +60,21 @@ ImageWidget::ImageWidget(QWidget *parent) : QOpenGLWidget(parent)
|
|||||||
m_range = UINT16_MAX;
|
m_range = UINT16_MAX;
|
||||||
m_imgWidth = m_imgHeight = -1;
|
m_imgWidth = m_imgHeight = -1;
|
||||||
m_superpixel = m_invert = false;
|
m_superpixel = m_invert = false;
|
||||||
|
m_showThumbnails = false;
|
||||||
|
m_selecting = false;
|
||||||
|
m_thumbnailCount = 0;
|
||||||
|
m_updateTimer = new QTimer(this);
|
||||||
|
m_updateTimer->setInterval(500);
|
||||||
|
m_updateTimer->setSingleShot(true);
|
||||||
|
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
|
||||||
|
setAcceptDrops(true);
|
||||||
|
QTimer::singleShot(1000, [this](){
|
||||||
|
if(!isValid())
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWidget::~ImageWidget()
|
ImageWidget::~ImageWidget()
|
||||||
@@ -61,12 +82,13 @@ ImageWidget::~ImageWidget()
|
|||||||
makeCurrent();
|
makeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::setImage(const RawImage *image)
|
void ImageWidget::setImage(const RawImage *image, int index)
|
||||||
{
|
{
|
||||||
if(image == nullptr)return;
|
if(image == nullptr)return;
|
||||||
|
|
||||||
m_imgWidth = image->width();
|
m_imgWidth = image->width();
|
||||||
m_imgHeight = image->height();
|
m_imgHeight = image->height();
|
||||||
|
m_currentImg = index;
|
||||||
|
|
||||||
const RawImageType &rawImageType = rawImageTypes[image->type()];
|
const RawImageType &rawImageType = rawImageTypes[image->type()];
|
||||||
|
|
||||||
@@ -85,22 +107,6 @@ void ImageWidget::setImage(const RawImage *image)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::setImage(const QPixmap &pixmap)
|
|
||||||
{
|
|
||||||
QImage img = pixmap.toImage();
|
|
||||||
|
|
||||||
m_imgWidth = pixmap.width();
|
|
||||||
m_imgHeight = pixmap.height();
|
|
||||||
|
|
||||||
m_image->destroy();
|
|
||||||
m_image->setData(img);
|
|
||||||
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
|
||||||
m_image->setWrapMode(QOpenGLTexture::ClampToBorder);
|
|
||||||
m_image->setBorderColor(0, 0, 0, 0);
|
|
||||||
m_bwImg = false;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageWidget::setScale(float scale)
|
void ImageWidget::setScale(float scale)
|
||||||
{
|
{
|
||||||
m_scale = scale;
|
m_scale = scale;
|
||||||
@@ -113,6 +119,31 @@ void ImageWidget::blockRepaint(bool block)
|
|||||||
if(!block)update();
|
if(!block)update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageWidget::allocateThumbnails(const QStringList &paths)
|
||||||
|
{
|
||||||
|
int count = paths.size();
|
||||||
|
m_thumbnailCount = count;
|
||||||
|
m_thumnails.clear();
|
||||||
|
QStringList marked = m_database->getMarkedFiles();
|
||||||
|
for(auto &path : paths)
|
||||||
|
{
|
||||||
|
QString name = QFileInfo(path).fileName();
|
||||||
|
m_thumnails.push_back({name, path, QSize(0, 0), marked.contains(path), false});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_thumbnailTexture->destroy();
|
||||||
|
m_thumbnailTexture->create();
|
||||||
|
m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm);
|
||||||
|
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
||||||
|
m_thumbnailTexture->setLayers(paths.size());
|
||||||
|
m_thumbnailTexture->allocateStorage();
|
||||||
|
m_bufferSizes->bind();
|
||||||
|
float *tmp = new float[count*3];
|
||||||
|
memset(tmp, 0, count * sizeof(float)*3);
|
||||||
|
m_bufferSizes->allocate(tmp, count * sizeof(float)*3);
|
||||||
|
delete [] tmp;
|
||||||
|
}
|
||||||
|
|
||||||
void ImageWidget::setMTFParams(float low, float mid, float high)
|
void ImageWidget::setMTFParams(float low, float mid, float high)
|
||||||
{
|
{
|
||||||
m_low = low;
|
m_low = low;
|
||||||
@@ -162,6 +193,24 @@ QImage ImageWidget::renderToImage()
|
|||||||
return fbo.toImage(true);
|
return fbo.toImage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageWidget::thumbnailLoaded(const Image *image)
|
||||||
|
{
|
||||||
|
const RawImage *raw = image->thumbnail();
|
||||||
|
m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGB, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get());
|
||||||
|
float a = raw->thumbAspect();
|
||||||
|
int sizes[3] = { std::max(1, a > 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE * a)), std::max(1, a < 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE / a)), image->number() };
|
||||||
|
m_bufferSizes->bind();
|
||||||
|
m_bufferSizes->write(image->number() * sizeof(sizes), sizes, sizeof(sizes));
|
||||||
|
m_thumnails[image->number()].size = QSize(sizes[0], sizes[1]);
|
||||||
|
if(!m_updateTimer->isActive())m_updateTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::showThumbnail(bool enable)
|
||||||
|
{
|
||||||
|
m_showThumbnails = enable;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void ImageWidget::paintGL()
|
void ImageWidget::paintGL()
|
||||||
{
|
{
|
||||||
if(m_blockRepaint)return;
|
if(m_blockRepaint)return;
|
||||||
@@ -172,17 +221,65 @@ void ImageWidget::paintGL()
|
|||||||
dx = -width()*0.5f + m_image->width()*m_scale*0.5f;
|
dx = -width()*0.5f + m_image->width()*m_scale*0.5f;
|
||||||
if(height() > m_image->height()*m_scale)
|
if(height() > m_image->height()*m_scale)
|
||||||
dy = -height()*0.5f + m_image->height()*m_scale*0.5f;
|
dy = -height()*0.5f + m_image->height()*m_scale*0.5f;
|
||||||
|
QBrush highlight = style()->standardPalette().highlight();
|
||||||
|
|
||||||
m_program->bind();
|
if(m_showThumbnails)
|
||||||
m_program->setUniformValue("viewport", (float)width(), (float)height());
|
{
|
||||||
m_program->setUniformValue("offset", dx, dy);
|
m_vaoThumb->bind();
|
||||||
m_program->setUniformValue("mtf_param", m_low, m_mid, m_high);
|
m_thumbnailTexture->bind(1);
|
||||||
m_program->setUniformValue("zoom", 1.0f/m_scale);
|
m_thumbnailProgram->bind();
|
||||||
m_program->setUniformValue("bw", m_bwImg);
|
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
|
||||||
m_program->setUniformValue("invert", m_invert);
|
m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high);
|
||||||
|
m_thumbnailProgram->setUniformValue("invert", m_invert);
|
||||||
|
m_thumbnailProgram->setUniformValue("offset", 0, m_dy);
|
||||||
|
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
||||||
|
QMatrix4x4 mvp;
|
||||||
|
mvp.ortho(rect());
|
||||||
|
m_thumbnailProgram->setUniformValue("mvp", mvp);
|
||||||
|
if(f3)f3->glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_thumbnailCount);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
const int w = width()/THUMB_SIZE_BORDER;
|
||||||
|
const int off = (THUMB_SIZE_BORDER - THUMB_SIZE) / 2;
|
||||||
|
for(int i=0; i < m_thumbnailCount; i++)
|
||||||
|
{
|
||||||
|
float x = (i % w) * THUMB_SIZE_BORDER;
|
||||||
|
float y = i / w * THUMB_SIZE_BORDER_Y + THUMB_SIZE - m_dy + off;
|
||||||
|
QRectF rect(x, y, THUMB_SIZE_BORDER, 32);
|
||||||
|
painter.drawText(rect, Qt::AlignCenter | Qt::TextWrapAnywhere, QString(m_thumnails[i].name));
|
||||||
|
if(m_thumnails[i].selected)
|
||||||
|
{
|
||||||
|
painter.save();
|
||||||
|
QRectF thumbRect;
|
||||||
|
painter.setPen(Qt::red);
|
||||||
|
thumbRect.setSize(m_thumnails[i].size);
|
||||||
|
thumbRect.moveCenter(QPointF(x + THUMB_SIZE_BORDER / 2, y - THUMB_SIZE / 2));
|
||||||
|
painter.drawRect(thumbRect);
|
||||||
|
painter.restore();
|
||||||
|
}
|
||||||
|
if(m_currentImg == i)
|
||||||
|
{
|
||||||
|
painter.save();
|
||||||
|
painter.setPen(QPen(highlight, 2.0));
|
||||||
|
painter.drawRect((i % w) * THUMB_SIZE_BORDER + off, i / w * THUMB_SIZE_BORDER_Y - m_dy + off, THUMB_SIZE, THUMB_SIZE);
|
||||||
|
painter.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_vao->bind();
|
||||||
|
m_image->bind(0);
|
||||||
|
m_program->bind();
|
||||||
|
m_program->setUniformValue("viewport", (float)width(), (float)height());
|
||||||
|
m_program->setUniformValue("offset", dx, dy);
|
||||||
|
m_program->setUniformValue("mtf_param", m_low, m_mid, m_high);
|
||||||
|
m_program->setUniformValue("zoom", 1.0f/m_scale);
|
||||||
|
m_program->setUniformValue("bw", m_bwImg);
|
||||||
|
m_program->setUniformValue("invert", m_invert);
|
||||||
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
m_image->bind(0);
|
|
||||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidget::resizeGL(int w, int h)
|
void ImageWidget::resizeGL(int w, int h)
|
||||||
@@ -196,10 +293,15 @@ void ImageWidget::initializeGL()
|
|||||||
{
|
{
|
||||||
f = context()->functions();
|
f = context()->functions();
|
||||||
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
QOpenGLFunctions_3_2_Core *f3 = context()->versionFunctions<QOpenGLFunctions_3_2_Core>();
|
f3 = context()->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
||||||
|
|
||||||
|
if(f3 == nullptr)
|
||||||
|
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
||||||
|
|
||||||
m_vao = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
m_vao = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
||||||
|
m_vaoThumb = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
||||||
m_vao->create();
|
m_vao->create();
|
||||||
|
m_vaoThumb->create();
|
||||||
m_vao->bind();
|
m_vao->bind();
|
||||||
|
|
||||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
||||||
@@ -226,7 +328,7 @@ void ImageWidget::initializeGL()
|
|||||||
m_buffer->create();
|
m_buffer->create();
|
||||||
m_buffer->bind();
|
m_buffer->bind();
|
||||||
m_buffer->allocate(vertexs, sizeof(vertexs));
|
m_buffer->allocate(vertexs, sizeof(vertexs));
|
||||||
f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0);
|
// f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0);
|
||||||
|
|
||||||
m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
||||||
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert");
|
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert");
|
||||||
@@ -245,6 +347,33 @@ void ImageWidget::initializeGL()
|
|||||||
m_program->setUniformValue("qt_Texture0", (GLuint)0);
|
m_program->setUniformValue("qt_Texture0", (GLuint)0);
|
||||||
m_program->setUniformValue("scale", 1.0f, 0.0f);
|
m_program->setUniformValue("scale", 1.0f, 0.0f);
|
||||||
|
|
||||||
|
m_vaoThumb->bind();
|
||||||
|
|
||||||
|
m_thumbnailProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
||||||
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/thumb.vert");
|
||||||
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag");
|
||||||
|
if(f3)f3->glBindFragDataLocation(m_program->programId(), 0, "color");
|
||||||
|
m_thumbnailProgram->bind();
|
||||||
|
m_thumbnailProgram->enableAttributeArray("qt_Vertex");
|
||||||
|
m_thumbnailProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
|
||||||
|
m_thumbnailProgram->enableAttributeArray("qt_MultiTexCoord0");
|
||||||
|
m_thumbnailProgram->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);
|
||||||
|
if(!m_thumbnailProgram->link())
|
||||||
|
{
|
||||||
|
qDebug() << "Link failed" << m_thumbnailProgram->log();
|
||||||
|
}
|
||||||
|
m_thumbnailProgram->setUniformValue("qt_Texture0", (GLuint)1);
|
||||||
|
|
||||||
|
m_bufferSizes = std::unique_ptr<QOpenGLBuffer>(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
|
||||||
|
m_bufferSizes->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
m_bufferSizes->create();
|
||||||
|
m_bufferSizes->bind();
|
||||||
|
|
||||||
|
m_bufferSizes->allocate(12);
|
||||||
|
m_thumbnailProgram->enableAttributeArray("imageSize_num");
|
||||||
|
m_thumbnailProgram->setAttributeBuffer("imageSize_num", GL_FLOAT, 0, 3);
|
||||||
|
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
||||||
|
|
||||||
m_image = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2D));
|
m_image = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2D));
|
||||||
m_image->setFormat(QOpenGLTexture::RGB8U);
|
m_image->setFormat(QOpenGLTexture::RGB8U);
|
||||||
m_image->allocateStorage();
|
m_image->allocateStorage();
|
||||||
@@ -252,21 +381,135 @@ void ImageWidget::initializeGL()
|
|||||||
m_image->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
m_image->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
||||||
m_image->setMagnificationFilter(QOpenGLTexture::Linear);
|
m_image->setMagnificationFilter(QOpenGLTexture::Linear);
|
||||||
|
|
||||||
|
m_thumbnailTexture = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2DArray));
|
||||||
|
m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm);
|
||||||
|
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
||||||
|
m_thumbnailTexture->setLayers(1);
|
||||||
|
m_thumbnailTexture->allocateStorage();
|
||||||
|
m_thumbnailTexture->bind(1);
|
||||||
|
m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
||||||
|
m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
||||||
|
|
||||||
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
||||||
m_transferOptions->setAlignment(1);
|
m_transferOptions->setAlignment(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageScrollAreaGL::ImageScrollAreaGL(QWidget *parent) : QWidget(parent)
|
void ImageWidget::dragEnterEvent(QDragEnterEvent *event)
|
||||||
|
{
|
||||||
|
if(event->mimeData()->hasUrls() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction))
|
||||||
|
event->acceptProposedAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::dropEvent(QDropEvent *event)
|
||||||
|
{
|
||||||
|
if(event->mimeData()->hasUrls() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction))
|
||||||
|
{
|
||||||
|
for(const QUrl &url : event->mimeData()->urls())
|
||||||
|
{
|
||||||
|
if(url.isLocalFile())
|
||||||
|
{
|
||||||
|
emit fileDropped(url.toLocalFile());
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(m_thumbnailCount && event->button() == Qt::LeftButton && event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))
|
||||||
|
m_selecting = true;
|
||||||
|
|
||||||
|
if(m_selecting)
|
||||||
|
{
|
||||||
|
thumbSelect(event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(m_selecting)
|
||||||
|
{
|
||||||
|
thumbSelect(event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(m_selecting)
|
||||||
|
{
|
||||||
|
m_selecting = false;
|
||||||
|
event->accept();
|
||||||
|
QStringList mark;
|
||||||
|
QStringList unmark;
|
||||||
|
for(auto &thumb : m_thumnails)
|
||||||
|
{
|
||||||
|
if(thumb.dirty)
|
||||||
|
{
|
||||||
|
if(thumb.selected)
|
||||||
|
mark.append(thumb.path);
|
||||||
|
else
|
||||||
|
unmark.append(thumb.path);
|
||||||
|
|
||||||
|
thumb.dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!mark.isEmpty())
|
||||||
|
m_database->mark(mark);
|
||||||
|
if(!unmark.isEmpty())
|
||||||
|
m_database->unmark(unmark);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageWidget::thumbSelect(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QPoint p = event->pos();
|
||||||
|
const int off = (THUMB_SIZE_BORDER - THUMB_SIZE) / 2;
|
||||||
|
p.ry() += m_dy;
|
||||||
|
const int w = width()/THUMB_SIZE_BORDER;
|
||||||
|
int x = p.x() / THUMB_SIZE_BORDER;
|
||||||
|
int y = p.y() / THUMB_SIZE_BORDER_Y;
|
||||||
|
int i = y * w + x;
|
||||||
|
event->accept();
|
||||||
|
QRect rect(x * THUMB_SIZE_BORDER + off, y * THUMB_SIZE_BORDER_Y + off, THUMB_SIZE, THUMB_SIZE);
|
||||||
|
if(x < w && i < m_thumbnailCount && rect.contains(p, true))
|
||||||
|
{
|
||||||
|
bool oldVal = m_thumnails[i].selected;
|
||||||
|
bool newVal = oldVal;
|
||||||
|
if(event->modifiers() == Qt::ShiftModifier)
|
||||||
|
newVal = true;
|
||||||
|
if(event->modifiers() == Qt::ControlModifier)
|
||||||
|
newVal = false;
|
||||||
|
|
||||||
|
if(newVal != oldVal)
|
||||||
|
{
|
||||||
|
m_thumnails[i].selected = newVal;
|
||||||
|
m_thumnails[i].dirty = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageScrollAreaGL::ImageScrollAreaGL(Database *database, QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
m_imageWidget = new ImageWidget(this);
|
m_imageWidget = new ImageWidget(database, this);
|
||||||
|
|
||||||
m_verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
m_verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
||||||
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
|
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
|
||||||
m_scale = 1.0f;
|
m_scale = 1.0f;
|
||||||
m_bestFit = false;
|
m_bestFit = false;
|
||||||
|
m_thumbCount = 0;
|
||||||
|
|
||||||
layout->setSpacing(0);
|
layout->setSpacing(0);
|
||||||
layout->addWidget(m_imageWidget, 0, 0);
|
layout->addWidget(m_imageWidget, 0, 0);
|
||||||
@@ -282,55 +525,80 @@ ImageScrollAreaGL::~ImageScrollAreaGL()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::setImage(RawImage *image)
|
void ImageScrollAreaGL::setImage(RawImage *image, int index)
|
||||||
{
|
{
|
||||||
m_imageWidget->setImage(image);
|
m_imageWidget->setImage(image, index);
|
||||||
m_imgWidth = image->width();
|
m_imgWidth = image->width();
|
||||||
m_imgHeight = image->height();
|
m_imgHeight = image->height();
|
||||||
if(m_bestFit)bestFit();
|
if(m_bestFit)bestFit();
|
||||||
updateScrollbars();
|
updateScrollbars();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::setImage(const QPixmap &pixmap)
|
|
||||||
{
|
|
||||||
m_imageWidget->setImage(pixmap);
|
|
||||||
m_imgWidth = pixmap.width();
|
|
||||||
m_imgHeight = pixmap.height();
|
|
||||||
if(m_bestFit)bestFit();
|
|
||||||
updateScrollbars();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageWidget *ImageScrollAreaGL::imageWidget()
|
ImageWidget *ImageScrollAreaGL::imageWidget()
|
||||||
{
|
{
|
||||||
return m_imageWidget;
|
return m_imageWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::updateScrollbars(bool zoom)
|
void ImageScrollAreaGL::setThumbnails(int count)
|
||||||
{
|
{
|
||||||
if(zoom)
|
m_thumbCount = count;
|
||||||
|
if(m_thumbCount)
|
||||||
{
|
{
|
||||||
setScrollRange(m_verticalScrollBar, m_imgHeight*m_scale);
|
m_verticalScrollBar->setRange(0, m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) * THUMB_SIZE_BORDER_Y);
|
||||||
setScrollRange(m_horizontalScrollBar, m_imgWidth*m_scale);
|
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER_Y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_verticalScrollBar->setRange(0, m_imgHeight*m_scale - m_verticalScrollBar->pageStep());
|
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
||||||
m_horizontalScrollBar->setRange(0, m_imgWidth*m_scale - m_horizontalScrollBar->pageStep());
|
m_horizontalScrollBar->setPageStep(m_imageWidget->width());
|
||||||
|
}
|
||||||
|
updateScrollbars();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageScrollAreaGL::updateScrollbars(bool zoom)
|
||||||
|
{
|
||||||
|
if(m_thumbCount)
|
||||||
|
{
|
||||||
|
m_horizontalScrollBar->hide();
|
||||||
|
m_verticalScrollBar->show();
|
||||||
|
m_verticalScrollBar->setRange(0, m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) * THUMB_SIZE_BORDER_Y);
|
||||||
|
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER_Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(zoom)
|
||||||
|
{
|
||||||
|
setScrollRange(m_verticalScrollBar, m_imgHeight*m_scale);
|
||||||
|
setScrollRange(m_horizontalScrollBar, m_imgWidth*m_scale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_verticalScrollBar->setRange(0, m_imgHeight*m_scale - m_verticalScrollBar->pageStep());
|
||||||
|
m_horizontalScrollBar->setRange(0, m_imgWidth*m_scale - m_horizontalScrollBar->pageStep());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::resizeEvent(QResizeEvent *event)
|
void ImageScrollAreaGL::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
QWidget::resizeEvent(event);
|
QWidget::resizeEvent(event);
|
||||||
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
if(m_thumbCount)
|
||||||
m_horizontalScrollBar->setPageStep(m_imageWidget->width());
|
{
|
||||||
|
m_verticalScrollBar->setRange(0, m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) * THUMB_SIZE_BORDER_Y);
|
||||||
|
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER_Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
||||||
|
m_horizontalScrollBar->setPageStep(m_imageWidget->width());
|
||||||
|
}
|
||||||
updateScrollbars();
|
updateScrollbars();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::mouseMoveEvent(QMouseEvent *event)
|
void ImageScrollAreaGL::mouseMoveEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
QPoint delta = m_lastPos - event->pos();
|
QPoint delta = m_lastPos - event->pos();
|
||||||
m_horizontalScrollBar->setValue(m_horizontalScrollBar->value() + delta.x());
|
if(m_thumbCount == 0)m_horizontalScrollBar->setValue(m_horizontalScrollBar->value() + delta.x());
|
||||||
m_verticalScrollBar->setValue(m_verticalScrollBar->value() + delta.y());
|
m_verticalScrollBar->setValue(m_verticalScrollBar->value() + delta.y());
|
||||||
m_lastPos = event->pos();
|
m_lastPos = event->pos();
|
||||||
}
|
}
|
||||||
@@ -342,9 +610,16 @@ void ImageScrollAreaGL::mousePressEvent(QMouseEvent *event)
|
|||||||
|
|
||||||
void ImageScrollAreaGL::wheelEvent(QWheelEvent *event)
|
void ImageScrollAreaGL::wheelEvent(QWheelEvent *event)
|
||||||
{
|
{
|
||||||
m_bestFit = false;
|
if(m_thumbCount)
|
||||||
if(event->angleDelta().y() != 0)
|
{
|
||||||
zoom(event->angleDelta().y() / 1200.0f);
|
m_verticalScrollBar->setValue(m_verticalScrollBar->value() - event->angleDelta().y());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_bestFit = false;
|
||||||
|
if(event->angleDelta().y() != 0)
|
||||||
|
zoom(event->angleDelta().y() / 1200.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageScrollAreaGL::zoom(float delta)
|
void ImageScrollAreaGL::zoom(float delta)
|
||||||
|
|||||||
+49
-22
@@ -4,34 +4,44 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QOpenGLFunctions_3_2_Core>
|
#include <QOpenGLFunctions_3_3_Core>
|
||||||
#include <QOpenGLShaderProgram>
|
#include <QOpenGLShaderProgram>
|
||||||
#include <QOpenGLBuffer>
|
#include <QOpenGLBuffer>
|
||||||
#include <QOpenGLTexture>
|
#include <QOpenGLTexture>
|
||||||
#include <QOpenGLVertexArrayObject>
|
#include <QOpenGLVertexArrayObject>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QTimer>
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
#include "imageringlist.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
typedef enum
|
struct ImageThumb
|
||||||
{
|
{
|
||||||
Linear,
|
QString name;
|
||||||
Log,
|
QString path;
|
||||||
Sqrt,
|
QSize size;
|
||||||
Power,
|
bool selected;
|
||||||
Asinh,
|
bool dirty;
|
||||||
}StretchFunc;
|
};
|
||||||
|
|
||||||
class ImageWidget : public QOpenGLWidget
|
class ImageWidget : public QOpenGLWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QOpenGLFunctions *f;
|
QOpenGLFunctions *f;
|
||||||
|
QOpenGLFunctions_3_3_Core *f3;
|
||||||
|
QTimer *m_updateTimer;
|
||||||
std::unique_ptr<QOpenGLShaderProgram> m_program;
|
std::unique_ptr<QOpenGLShaderProgram> m_program;
|
||||||
|
std::unique_ptr<QOpenGLShaderProgram> m_thumbnailProgram;
|
||||||
std::unique_ptr<QOpenGLBuffer> m_buffer;
|
std::unique_ptr<QOpenGLBuffer> m_buffer;
|
||||||
|
std::unique_ptr<QOpenGLBuffer> m_bufferSizes;
|
||||||
std::unique_ptr<QOpenGLTexture> m_image;
|
std::unique_ptr<QOpenGLTexture> m_image;
|
||||||
std::unique_ptr<QOpenGLVertexArrayObject> m_vao;
|
std::unique_ptr<QOpenGLVertexArrayObject> m_vao;
|
||||||
|
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
|
||||||
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
|
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
|
||||||
|
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
int m_imgWidth, m_imgHeight;
|
int m_imgWidth, m_imgHeight;
|
||||||
|
int m_currentImg;
|
||||||
float m_low;
|
float m_low;
|
||||||
float m_mid;
|
float m_mid;
|
||||||
float m_high;
|
float m_high;
|
||||||
@@ -42,23 +52,39 @@ class ImageWidget : public QOpenGLWidget
|
|||||||
bool m_bwImg;
|
bool m_bwImg;
|
||||||
bool m_invert;
|
bool m_invert;
|
||||||
bool m_superpixel;
|
bool m_superpixel;
|
||||||
|
bool m_showThumbnails;
|
||||||
|
bool m_selecting;
|
||||||
|
int m_thumbnailCount;
|
||||||
|
QVector<ImageThumb> m_thumnails;
|
||||||
|
Database *m_database;
|
||||||
public:
|
public:
|
||||||
explicit ImageWidget(QWidget *parent = nullptr);
|
explicit ImageWidget(Database *database, QWidget *parent = nullptr);
|
||||||
~ImageWidget();
|
~ImageWidget() override;
|
||||||
void setImage(const RawImage *image);
|
void setImage(const RawImage *image, int index);
|
||||||
void setImage(const QPixmap &pixmap);
|
void setImage(const QPixmap &pixmap);
|
||||||
void setScale(float scale);
|
void setScale(float scale);
|
||||||
void blockRepaint(bool block);
|
void blockRepaint(bool block);
|
||||||
|
void allocateThumbnails(const QStringList &paths);
|
||||||
public slots:
|
public slots:
|
||||||
void setMTFParams(float low, float mid, float high);
|
void setMTFParams(float low, float mid, float high);
|
||||||
void setOffset(int dx, int dy);
|
void setOffset(int dx, int dy);
|
||||||
void superPixel(bool enable);
|
void superPixel(bool enable);
|
||||||
void invert(bool enable);
|
void invert(bool enable);
|
||||||
QImage renderToImage();
|
QImage renderToImage();
|
||||||
|
void thumbnailLoaded(const Image *image);
|
||||||
|
void showThumbnail(bool enable);
|
||||||
protected:
|
protected:
|
||||||
void paintGL();
|
void paintGL() override;
|
||||||
void resizeGL(int w, int h);
|
void resizeGL(int w, int h) override;
|
||||||
void initializeGL();
|
void initializeGL() override;
|
||||||
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
|
void dropEvent(QDropEvent *event) override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
void thumbSelect(QMouseEvent *event);
|
||||||
|
signals:
|
||||||
|
void fileDropped(const QString &path);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageScrollAreaGL : public QWidget
|
class ImageScrollAreaGL : public QWidget
|
||||||
@@ -71,18 +97,19 @@ class ImageScrollAreaGL : public QWidget
|
|||||||
QPoint m_lastPos;
|
QPoint m_lastPos;
|
||||||
float m_scale;
|
float m_scale;
|
||||||
bool m_bestFit;
|
bool m_bestFit;
|
||||||
|
int m_thumbCount;
|
||||||
public:
|
public:
|
||||||
explicit ImageScrollAreaGL(QWidget *parent = nullptr);
|
explicit ImageScrollAreaGL(Database *database, QWidget *parent = nullptr);
|
||||||
~ImageScrollAreaGL();
|
~ImageScrollAreaGL() override;
|
||||||
void setImage(RawImage *image);
|
void setImage(RawImage *image, int index);
|
||||||
void setImage(const QPixmap &pixmap);
|
|
||||||
ImageWidget* imageWidget();
|
ImageWidget* imageWidget();
|
||||||
|
void setThumbnails(int count);
|
||||||
protected:
|
protected:
|
||||||
void updateScrollbars(bool zoom = false);
|
void updateScrollbars(bool zoom = false);
|
||||||
void resizeEvent(QResizeEvent *event);
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
void mouseMoveEvent(QMouseEvent *event);
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event);
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
void zoom(float delta);
|
void zoom(float delta);
|
||||||
public slots:
|
public slots:
|
||||||
void zoomIn();
|
void zoomIn();
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
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)
|
||||||
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor org.nou.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor org.nou.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 32 icon.png org.nou.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 32 org.nou.tenmon.png org.nou.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|||||||
+169
-7
@@ -13,10 +13,11 @@
|
|||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "starfit.h"
|
#include "starfit.h"
|
||||||
|
|
||||||
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level) :
|
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
|
||||||
m_file(file),
|
m_file(file),
|
||||||
m_receiver(receiver),
|
m_receiver(receiver),
|
||||||
m_analyzeLevel(level)
|
m_analyzeLevel(level),
|
||||||
|
m_thumbnail(thumbnail)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +248,8 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
noload:
|
noload:
|
||||||
loadFITSHeader(file, info);
|
if(file)
|
||||||
|
loadFITSHeader(file, info);
|
||||||
|
|
||||||
fits_close_file(file, &status);
|
fits_close_file(file, &status);
|
||||||
if(status)
|
if(status)
|
||||||
@@ -334,7 +336,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
|
|||||||
|
|
||||||
void LoadRunable::run()
|
void LoadRunable::run()
|
||||||
{
|
{
|
||||||
if(!m_receiver->isCurrent())
|
if(!m_thumbnail && !m_receiver->isCurrent())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -345,7 +347,7 @@ void LoadRunable::run()
|
|||||||
|
|
||||||
RawImage *rawImage = nullptr;
|
RawImage *rawImage = nullptr;
|
||||||
bool raw = false;
|
bool raw = false;
|
||||||
if(m_file.endsWith(".CR2", Qt::CaseInsensitive))
|
if(m_file.endsWith(".CR2", Qt::CaseInsensitive) || m_file.endsWith(".NEF", Qt::CaseInsensitive) || m_file.endsWith(".DNG", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
timer.start();
|
timer.start();
|
||||||
loadRAW(m_file, info, &rawImage);
|
loadRAW(m_file, info, &rawImage);
|
||||||
@@ -375,7 +377,7 @@ void LoadRunable::run()
|
|||||||
rawImage = new RawImage(img);
|
rawImage = new RawImage(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rawImage && m_analyzeLevel >= Statistics)
|
if(rawImage && m_analyzeLevel >= Statistics && !m_thumbnail)
|
||||||
{
|
{
|
||||||
double mean, median, min, max, mad;
|
double mean, median, min, max, mad;
|
||||||
double stdDev;
|
double stdDev;
|
||||||
@@ -440,7 +442,20 @@ void LoadRunable::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info));
|
if(m_thumbnail)
|
||||||
|
{
|
||||||
|
if(rawImage)
|
||||||
|
{
|
||||||
|
rawImage->convertToThumbnail();
|
||||||
|
QMetaObject::invokeMethod(m_receiver, "thumbnailLoadFinish", Qt::QueuedConnection, Q_ARG(void*, rawImage));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readFITSHeader(const QString &path, ImageInfoData &info)
|
bool readFITSHeader(const QString &path, ImageInfoData &info)
|
||||||
@@ -478,3 +493,150 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConvertRunable::ConvertRunable(const QString &in, const QString &out) :
|
||||||
|
m_infile(in),
|
||||||
|
m_outfile(out)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeXISFImage(pcl::XISFWriter &writer, RawImage *rawimg)
|
||||||
|
{
|
||||||
|
const cv::Mat &cvmat = rawimg->mat();
|
||||||
|
T pclimg(rawimg->width(), rawimg->height(), cvmat.channels() == 1 ? pcl::ColorSpace::Gray : pcl::ColorSpace::RGB);
|
||||||
|
if(cvmat.channels() == 1)
|
||||||
|
{
|
||||||
|
memcpy(pclimg.PixelData(0), rawimg->data(), rawimg->size()*sizeof(typename T::sample));
|
||||||
|
}
|
||||||
|
if(cvmat.channels() == 3)
|
||||||
|
{
|
||||||
|
std::vector<cv::Mat> channels;
|
||||||
|
cv::split(cvmat, channels);
|
||||||
|
memcpy(pclimg.PixelData(0), channels[0].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
memcpy(pclimg.PixelData(1), channels[1].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
memcpy(pclimg.PixelData(2), channels[2].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
}
|
||||||
|
writer.WriteImage(pclimg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFITSImage(fitsfile *fw, RawImage *rawimage, ImageInfoData &imageinfo)
|
||||||
|
{
|
||||||
|
static QStringList skipKeys = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "BZERO", "BSCALE", "EXTEND"};
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
long firstpix[3] = {1,1,1};
|
||||||
|
|
||||||
|
int channels = rawimage->mat().channels();
|
||||||
|
int naxis = channels == 1 ? 2 : 3;
|
||||||
|
long naxes[3] = {(int)rawimage->width(), (int)rawimage->height(), rawimage->mat().channels()};
|
||||||
|
|
||||||
|
std::vector<cv::Mat> mat;
|
||||||
|
if(channels == 1)
|
||||||
|
mat.push_back(rawimage->mat());
|
||||||
|
else
|
||||||
|
cv::split(rawimage->mat(), mat);
|
||||||
|
|
||||||
|
switch(CV_MAT_DEPTH(rawimage->dataType()))
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
fits_create_img(fw, BYTE_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TBYTE, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CV_16U:
|
||||||
|
fits_create_img(fw, USHORT_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TUSHORT, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CV_32F:
|
||||||
|
fits_create_img(fw, FLOAT_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TFLOAT, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(const FITSRecord &record : imageinfo.fitsHeader)
|
||||||
|
{
|
||||||
|
if(skipKeys.contains(record.key))continue;
|
||||||
|
|
||||||
|
bool isdouble;
|
||||||
|
bool isint;
|
||||||
|
bool isbool = record.value.toString() == "T" || record.value.toString() == "F";
|
||||||
|
double vald = record.value.toDouble(&isdouble);
|
||||||
|
int valb = record.value.toString() == "T";
|
||||||
|
long long vall = record.value.toLongLong(&isint);
|
||||||
|
QByteArray str = record.value.toString().toLatin1();
|
||||||
|
if(isdouble)
|
||||||
|
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(isint)
|
||||||
|
fits_write_key(fw, TLONGLONG, record.key.data(), &vall, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(isbool)
|
||||||
|
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(record.key == "COMMENT")
|
||||||
|
fits_write_comment(fw, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(record.key == "HISTORY")
|
||||||
|
fits_write_history(fw, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else
|
||||||
|
fits_write_key(fw, TSTRING, record.key.data(), str.isEmpty() ? nullptr : str.data(), record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertRunable::run()
|
||||||
|
{
|
||||||
|
ImageInfoData imageinfo;
|
||||||
|
RawImage *rawimage = nullptr;
|
||||||
|
if(m_infile.endsWith(".FITS", Qt::CaseInsensitive) || m_infile.endsWith(".FIT", Qt::CaseInsensitive))
|
||||||
|
loadFITS(m_infile, imageinfo, &rawimage);
|
||||||
|
if(m_infile.endsWith(".XISF", Qt::CaseInsensitive))
|
||||||
|
loadXISF(m_infile, imageinfo, &rawimage);
|
||||||
|
|
||||||
|
if(rawimage)
|
||||||
|
{
|
||||||
|
if(m_outfile.endsWith(".XISF", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
pcl::XISFOptions options;
|
||||||
|
pcl::FITSKeywordArray fitskeywords;
|
||||||
|
for(auto &record : imageinfo.fitsHeader)
|
||||||
|
{
|
||||||
|
pcl::FITSHeaderKeyword key(pcl::IsoString(record.key.data()), pcl::IsoString(record.value.toString().toLatin1().data()), pcl::IsoString(record.comment.data()));
|
||||||
|
fitskeywords.Append(key);
|
||||||
|
}
|
||||||
|
pcl::XISFWriter xisf;
|
||||||
|
xisf.Create(m_outfile.utf16(), 1);
|
||||||
|
xisf.WriteFITSKeywords(fitskeywords);
|
||||||
|
switch(CV_MAT_DEPTH(rawimage->dataType()))
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
writeXISFImage<pcl::UInt8Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
case CV_16U:
|
||||||
|
writeXISFImage<pcl::UInt16Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
case CV_32F:
|
||||||
|
writeXISFImage<pcl::Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_outfile.endsWith(".FITS", Qt::CaseInsensitive) || m_outfile.endsWith(".FIT", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
fitsfile *fw;
|
||||||
|
if(QFileInfo(m_outfile).exists())QFile::remove(m_outfile);
|
||||||
|
fits_create_diskfile(&fw, m_outfile.toLocal8Bit().data(), &status);
|
||||||
|
writeFITSImage(fw, rawimage, imageinfo);
|
||||||
|
fits_close_file(fw, &status);
|
||||||
|
}
|
||||||
|
delete rawimage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-2
@@ -15,9 +15,19 @@ class LoadRunable : public QRunnable
|
|||||||
QString m_file;
|
QString m_file;
|
||||||
Image *m_receiver;
|
Image *m_receiver;
|
||||||
AnalyzeLevel m_analyzeLevel;
|
AnalyzeLevel m_analyzeLevel;
|
||||||
|
bool m_thumbnail;
|
||||||
public:
|
public:
|
||||||
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level);
|
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail = false);
|
||||||
void run();
|
void run() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertRunable : public QRunnable
|
||||||
|
{
|
||||||
|
QString m_infile;
|
||||||
|
QString m_outfile;
|
||||||
|
public:
|
||||||
|
ConvertRunable(const QString &in, const QString &out);
|
||||||
|
void run() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOADRUNABLE_H
|
#endif // LOADRUNABLE_H
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setMajorVersion(3);
|
format.setMajorVersion(3);
|
||||||
format.setMinorVersion(2);
|
format.setMinorVersion(3);
|
||||||
format.setOption(QSurfaceFormat::DebugContext);
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||||
QSurfaceFormat::setDefaultFormat(format);
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
@@ -14,7 +15,14 @@ int main(int argc, char *argv[])
|
|||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
a.setOrganizationName("nou");
|
a.setOrganizationName("nou");
|
||||||
a.setApplicationName("Tenmon");
|
a.setApplicationName("Tenmon");
|
||||||
a.setWindowIcon(QIcon(":/icon.png"));
|
a.setWindowIcon(QIcon(":/org.nou.tenmon.png"));
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
QTranslator translator2;
|
||||||
|
if(translator.load(QLocale(), "tenmon", "_", ":/translations"))
|
||||||
|
a.installTranslator(&translator);
|
||||||
|
if(translator2.load(QLocale(), "tenmon", "_", a.applicationDirPath()))
|
||||||
|
a.installTranslator(&translator2);
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.show();
|
w.show();
|
||||||
|
|||||||
+165
-61
@@ -14,6 +14,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QThreadPool>
|
||||||
|
#include "loadrunable.h"
|
||||||
|
#include "markedfiles.h"
|
||||||
|
#include "about.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -38,57 +42,75 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
//setCentralWidget(m_image);
|
//setCentralWidget(m_image);
|
||||||
resize(800, 600);
|
resize(800, 600);
|
||||||
|
|
||||||
m_imageGL = new ImageScrollAreaGL(this);
|
m_database = new Database(this);
|
||||||
|
if(!m_database->init())
|
||||||
|
QMessageBox::critical(this, tr("Can't open DB"), tr("Can't open SQLITE database"));
|
||||||
|
|
||||||
|
m_imageGL = new ImageScrollAreaGL(m_database, this);
|
||||||
setCentralWidget(m_imageGL);
|
setCentralWidget(m_imageGL);
|
||||||
|
|
||||||
m_stretchPanel = new StretchPanel(this);
|
m_stretchPanel = new StretchToolbar(this);
|
||||||
connect(m_stretchPanel, SIGNAL(paramChanged(float,float,float)), m_imageGL->imageWidget(), SLOT(setMTFParams(float,float,float)));
|
connect(m_stretchPanel, SIGNAL(paramChanged(float,float,float)), m_imageGL->imageWidget(), SLOT(setMTFParams(float,float,float)));
|
||||||
connect(m_stretchPanel, &StretchPanel::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
connect(m_stretchPanel, &StretchToolbar::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
||||||
connect(m_stretchPanel, &StretchPanel::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
||||||
connect(m_stretchPanel, &StretchPanel::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
||||||
|
|
||||||
m_ringList = new ImageRingList(this);
|
m_ringList = new ImageRingList(this);
|
||||||
m_filesystem = new FilesystemWidget(m_ringList, this);
|
m_filesystem = new FilesystemWidget(m_ringList, this);
|
||||||
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
|
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
|
||||||
|
|
||||||
m_database = new Database(this);
|
m_filetree = new Filetree(this);
|
||||||
if(!m_database->init())
|
connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
QMessageBox::critical(this, tr("Can't open DB"), tr("Can't open SQLITE database"));
|
connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); });
|
||||||
|
connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); });
|
||||||
|
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
||||||
|
|
||||||
m_databaseView = new DataBaseView(m_database, this);
|
m_databaseView = new DataBaseView(m_database, this);
|
||||||
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString)));
|
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString)));
|
||||||
|
|
||||||
QDockWidget *stretchDock = new QDockWidget(tr("Stretch"), this);
|
addToolBar(Qt::TopToolBarArea, m_stretchPanel);
|
||||||
stretchDock->setWidget(m_stretchPanel);
|
|
||||||
stretchDock->setObjectName("strechDock");
|
|
||||||
addDockWidget(Qt::TopDockWidgetArea, stretchDock);
|
|
||||||
|
|
||||||
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
||||||
filesystemDock->setWidget(m_filesystem);
|
filesystemDock->setWidget(m_filesystem);
|
||||||
filesystemDock->setObjectName("filesystemDock");
|
filesystemDock->setObjectName("filesystemDock");
|
||||||
addDockWidget(Qt::LeftDockWidgetArea, filesystemDock);
|
addDockWidget(Qt::LeftDockWidgetArea, filesystemDock);
|
||||||
|
|
||||||
QDockWidget *databaseViewDock = new QDockWidget(tr("FITS files database"), this);
|
QDockWidget *databaseViewDock = new QDockWidget(tr("FITS/XISF files database"), this);
|
||||||
databaseViewDock->setWidget(m_databaseView);
|
databaseViewDock->setWidget(m_databaseView);
|
||||||
databaseViewDock->setObjectName("databaseViewDock");
|
databaseViewDock->setObjectName("databaseViewDock");
|
||||||
databaseViewDock->hide();
|
databaseViewDock->hide();
|
||||||
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
|
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
|
||||||
|
|
||||||
|
QDockWidget *filetreeDock = new QDockWidget(tr("File tree"), this);
|
||||||
|
filetreeDock->setWidget(m_filetree);
|
||||||
|
filetreeDock->setObjectName("filetreeDock");
|
||||||
|
databaseViewDock->hide();
|
||||||
|
addDockWidget(Qt::LeftDockWidgetArea, filetreeDock);
|
||||||
|
|
||||||
setWindowTitle(tr("Tenmon"));
|
setWindowTitle(tr("Tenmon"));
|
||||||
|
|
||||||
connect(m_ringList, SIGNAL(pixmapLoaded(Image*)), this, SLOT(pixmapLoaded(Image*)));
|
connect(m_ringList, SIGNAL(pixmapLoaded(Image*)), this, SLOT(pixmapLoaded(Image*)));
|
||||||
connect(m_ringList, SIGNAL(currentImageChanged(int)), this, SLOT(updateWindowTitle()));
|
connect(m_ringList, SIGNAL(currentImageChanged(int)), this, SLOT(updateWindowTitle()));
|
||||||
connect(m_ringList, SIGNAL(infoLoaded(ImageInfoData)), m_info, SLOT(setInfo(const ImageInfoData&)));
|
connect(m_ringList, SIGNAL(infoLoaded(ImageInfoData)), m_info, SLOT(setInfo(const ImageInfoData&)));
|
||||||
connect(m_ringList, SIGNAL(currentImageChanged(int)), m_filesystem, SLOT(selectFile(int)));
|
connect(m_ringList, SIGNAL(currentImageChanged(int)), m_filesystem, SLOT(selectFile(int)));
|
||||||
|
connect(m_ringList, &ImageRingList::thumbnailLoaded, m_imageGL->imageWidget(), &ImageWidget::thumbnailLoaded);
|
||||||
|
connect(m_ringList, &ImageRingList::pixmapLoaded, m_stretchPanel, &StretchToolbar::imageLoaded);
|
||||||
|
connect(m_imageGL->imageWidget(), &ImageWidget::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
|
|
||||||
QMenu *fileMenu = new QMenu(tr("File"), this);
|
QMenu *fileMenu = new QMenu(tr("File"), this);
|
||||||
fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence("Ctrl+O"));
|
fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence::Open);
|
||||||
|
fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence::Save);
|
||||||
|
fileMenu->addSeparator();
|
||||||
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()));
|
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()));
|
||||||
fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence("Ctrl+S"));
|
fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked()));
|
||||||
|
fileMenu->addSeparator();
|
||||||
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
||||||
|
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
|
||||||
|
fileMenu->addSeparator();
|
||||||
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
||||||
liveModeAction->setCheckable(true);
|
liveModeAction->setCheckable(true);
|
||||||
fileMenu->addAction(tr("Exit"), this, SLOT(close()));
|
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, SLOT(close()));
|
||||||
|
exitAction->setShortcut(QKeySequence::Quit);
|
||||||
menuBar()->addMenu(fileMenu);
|
menuBar()->addMenu(fileMenu);
|
||||||
|
|
||||||
QMenu *viewMenu = new QMenu(tr("View"), this);
|
QMenu *viewMenu = new QMenu(tr("View"), this);
|
||||||
@@ -96,7 +118,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
||||||
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
|
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
|
||||||
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
|
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
|
||||||
viewMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence::FullScreen);
|
viewMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), Qt::CTRL + Qt::Key_F11);
|
||||||
|
QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){
|
||||||
|
m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageNames());
|
||||||
|
m_imageGL->imageWidget()->showThumbnail(checked);
|
||||||
|
m_imageGL->setThumbnails(checked ? m_ringList->imageCount() : 0);
|
||||||
|
if(checked)m_ringList->loadThumbnails();
|
||||||
|
else m_ringList->stopLoading();
|
||||||
|
}, Qt::Key_F2);
|
||||||
|
thumbnailsAction->setCheckable(true);
|
||||||
menuBar()->addMenu(viewMenu);
|
menuBar()->addMenu(viewMenu);
|
||||||
|
|
||||||
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
||||||
@@ -105,6 +135,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
selectMenu->addSeparator();
|
selectMenu->addSeparator();
|
||||||
selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M);
|
selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M);
|
||||||
selectMenu->addAction(tr("Unmark and next"), this, SLOT(unmarkAndNext()), Qt::Key_X);
|
selectMenu->addAction(tr("Unmark and next"), this, SLOT(unmarkAndNext()), Qt::Key_X);
|
||||||
|
selectMenu->addAction(tr("Show marked"), this, &MainWindow::showMarkFilesDialog);
|
||||||
menuBar()->addMenu(selectMenu);
|
menuBar()->addMenu(selectMenu);
|
||||||
|
|
||||||
QMenu *analyzeMenu = new QMenu(tr("Analyze"), this);
|
QMenu *analyzeMenu = new QMenu(tr("Analyze"), this);
|
||||||
@@ -122,7 +153,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
QAction *statsAction = analyzeGroup->addAction(tr("Image statistics"));
|
QAction *statsAction = analyzeGroup->addAction(tr("Image statistics"));
|
||||||
QAction *peakAction = analyzeGroup->addAction(tr("Peak finder"));
|
QAction *peakAction = analyzeGroup->addAction(tr("Peak finder"));
|
||||||
QAction *starAction = analyzeGroup->addAction("Star finder");
|
QAction *starAction = analyzeGroup->addAction(tr("Star finder"));
|
||||||
statsAction->setCheckable(true);
|
statsAction->setCheckable(true);
|
||||||
peakAction->setCheckable(true);
|
peakAction->setCheckable(true);
|
||||||
starAction->setCheckable(true);
|
starAction->setCheckable(true);
|
||||||
@@ -134,11 +165,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
||||||
dockMenu->addAction(infoDock->toggleViewAction());
|
dockMenu->addAction(infoDock->toggleViewAction());
|
||||||
dockMenu->addAction(stretchDock->toggleViewAction());
|
dockMenu->addAction(m_stretchPanel->toggleViewAction());
|
||||||
dockMenu->addAction(filesystemDock->toggleViewAction());
|
dockMenu->addAction(filesystemDock->toggleViewAction());
|
||||||
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
||||||
|
dockMenu->addAction(filetreeDock->toggleViewAction());
|
||||||
menuBar()->addMenu(dockMenu);
|
menuBar()->addMenu(dockMenu);
|
||||||
|
|
||||||
|
QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
|
||||||
|
helpMenu->addAction(tr("Help"), [this]{ HelpDialog help(this); help.exec(); }, QKeySequence::HelpContents);
|
||||||
|
helpMenu->addAction(tr("About Tenmon"), [this]{ About about(this); about.exec(); });
|
||||||
|
helpMenu->addAction(tr("About Qt"), [this](){ QMessageBox::aboutQt(this); });
|
||||||
|
|
||||||
setupSigterm();
|
setupSigterm();
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
||||||
@@ -232,6 +269,62 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
|||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::copyOrMove(bool copy)
|
||||||
|
{
|
||||||
|
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"), _lastDir);
|
||||||
|
copyOrMove(copy, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::copyOrMove(bool copy, const QString &dest)
|
||||||
|
{
|
||||||
|
QDir dir(dest);
|
||||||
|
if(!dest.isEmpty() && dir.exists())
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
QStringList files = m_database->getMarkedFiles();
|
||||||
|
QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this);
|
||||||
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
|
progress.show();
|
||||||
|
foreach(const QString &file, files)
|
||||||
|
{
|
||||||
|
QFileInfo info(file);
|
||||||
|
QFile srcFile(file);
|
||||||
|
QFile dstFile(dir.absoluteFilePath(info.fileName()));
|
||||||
|
|
||||||
|
if(dstFile.exists())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(progress.wasCanceled())
|
||||||
|
break;
|
||||||
|
#ifdef __linux__
|
||||||
|
if(copy)
|
||||||
|
{
|
||||||
|
srcFile.open(QIODevice::ReadOnly);
|
||||||
|
dstFile.open(QIODevice::WriteOnly);
|
||||||
|
if(ioctl(dstFile.handle(), BTRFS_IOC_CLONE, srcFile.handle()) < 0)
|
||||||
|
{
|
||||||
|
dstFile.remove();
|
||||||
|
dstFile.close();
|
||||||
|
qDebug() << dstFile.fileName();
|
||||||
|
srcFile.copy(dstFile.fileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
srcFile.rename(dstFile.fileName());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(copy)
|
||||||
|
srcFile.copy(dstFile.fileName());
|
||||||
|
else
|
||||||
|
srcFile.rename(dstFile.fileName());
|
||||||
|
#endif
|
||||||
|
progress.setValue(i++);
|
||||||
|
}
|
||||||
|
m_database->clearMarkedFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::socketNotify()
|
void MainWindow::socketNotify()
|
||||||
{
|
{
|
||||||
socketNotifier->setEnabled(false);
|
socketNotifier->setEnabled(false);
|
||||||
@@ -246,17 +339,17 @@ void MainWindow::pixmapLoaded(Image *image)
|
|||||||
//m_image->setImage(image->pixmap());
|
//m_image->setImage(image->pixmap());
|
||||||
if(image->rawImage())
|
if(image->rawImage())
|
||||||
{
|
{
|
||||||
m_imageGL->setImage(image->rawImage());
|
m_imageGL->setImage(image->rawImage(), image->number());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::loadFile()
|
void MainWindow::loadFile()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getOpenFileName(this, tr("Open file"), _lastDir, tr("Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)"));
|
QString file = QFileDialog::getOpenFileName(this, tr("Open file"), _lastDir, tr("Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)"));
|
||||||
loadFile(file);
|
loadFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::loadFile(const QString path)
|
void MainWindow::loadFile(const QString &path)
|
||||||
{
|
{
|
||||||
if(!path.isEmpty())
|
if(!path.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -281,6 +374,11 @@ void MainWindow::loadFile(int row)
|
|||||||
void MainWindow::indexDir()
|
void MainWindow::indexDir()
|
||||||
{
|
{
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir);
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir);
|
||||||
|
indexDir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::indexDir(const QString &dir)
|
||||||
|
{
|
||||||
if(!dir.isEmpty())
|
if(!dir.isEmpty())
|
||||||
{
|
{
|
||||||
QProgressDialog progressDialog(tr("Indexing FITS files"), tr("Cancel"), 0, 1, this);
|
QProgressDialog progressDialog(tr("Indexing FITS files"), tr("Cancel"), 0, 1, this);
|
||||||
@@ -289,17 +387,47 @@ void MainWindow::indexDir()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::reindex()
|
||||||
|
{
|
||||||
|
QProgressDialog progressDialog(tr("Indexing FITS files"), tr("Cancel"), 0, 1, this);
|
||||||
|
progressDialog.setModal(true);
|
||||||
|
m_database->reindex(&progressDialog);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::saveAs()
|
void MainWindow::saveAs()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("Images (*.jpg *.png *.JPG *.PNG)"));
|
QString selectedFilter;
|
||||||
|
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)"), &selectedFilter);
|
||||||
if(!file.isEmpty())
|
if(!file.isEmpty())
|
||||||
{
|
{
|
||||||
QImage img = m_imageGL->imageWidget()->renderToImage();
|
QFileInfo info(file);
|
||||||
if(!img.isNull())
|
if(info.suffix().isEmpty())
|
||||||
img.save(file);
|
{
|
||||||
|
if(selectedFilter.contains("jpg"))file += ".jpg";
|
||||||
|
if(selectedFilter.contains("png"))file += ".png";
|
||||||
|
if(selectedFilter.contains("fits"))file += ".fits";
|
||||||
|
if(selectedFilter.contains("xisf"))file += ".xisf";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.endsWith(".fits") || file.endsWith(".xisf"))
|
||||||
|
{
|
||||||
|
convert(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QImage img = m_imageGL->imageWidget()->renderToImage();
|
||||||
|
if(!img.isNull())
|
||||||
|
img.save(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::convert(const QString &outfile)
|
||||||
|
{
|
||||||
|
QString file = m_ringList->currentImage()->name();
|
||||||
|
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::markImage()
|
void MainWindow::markImage()
|
||||||
{
|
{
|
||||||
ImagePtr ptr = m_ringList->currentImage();
|
ImagePtr ptr = m_ringList->currentImage();
|
||||||
@@ -342,42 +470,12 @@ void MainWindow::unmarkAndNext()
|
|||||||
|
|
||||||
void MainWindow::copyMarked()
|
void MainWindow::copyMarked()
|
||||||
{
|
{
|
||||||
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"));
|
copyOrMove(true);
|
||||||
QDir dir(dest);
|
}
|
||||||
if(!dest.isEmpty() && dir.exists())
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
QStringList files = m_database->getMarkedFiles();
|
|
||||||
QProgressDialog progress(tr("Copying"), tr("Cancel"), 0, files.size(), this);
|
|
||||||
progress.setWindowModality(Qt::WindowModal);
|
|
||||||
progress.show();
|
|
||||||
foreach(const QString &file, files)
|
|
||||||
{
|
|
||||||
QFileInfo info(file);
|
|
||||||
QFile srcFile(file);
|
|
||||||
QFile dstFile(dir.absoluteFilePath(info.fileName()));
|
|
||||||
|
|
||||||
if(dstFile.exists())
|
void MainWindow::moveMarked()
|
||||||
continue;
|
{
|
||||||
|
copyOrMove(false);
|
||||||
if(progress.wasCanceled())
|
|
||||||
break;
|
|
||||||
#ifdef __linux__
|
|
||||||
srcFile.open(QIODevice::ReadOnly);
|
|
||||||
dstFile.open(QIODevice::WriteOnly);
|
|
||||||
if(ioctl(dstFile.handle(), BTRFS_IOC_CLONE, srcFile.handle()) < 0)
|
|
||||||
{
|
|
||||||
dstFile.remove();
|
|
||||||
dstFile.close();
|
|
||||||
qDebug() << dstFile.fileName();
|
|
||||||
srcFile.copy(dstFile.fileName());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
srcFile.copy(dstFile.fileName());
|
|
||||||
#endif
|
|
||||||
progress.setValue(i++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::toggleFullScreen()
|
void MainWindow::toggleFullScreen()
|
||||||
@@ -414,6 +512,12 @@ void MainWindow::starFinder(bool findStars)
|
|||||||
m_ringList->setFindStars(findStars);
|
m_ringList->setFindStars(findStars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::showMarkFilesDialog()
|
||||||
|
{
|
||||||
|
MarkedFiles markedFiles;
|
||||||
|
markedFiles.exec();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::updateWindowTitle()
|
void MainWindow::updateWindowTitle()
|
||||||
{
|
{
|
||||||
ImagePtr ptr = m_ringList->currentImage();
|
ImagePtr ptr = m_ringList->currentImage();
|
||||||
|
|||||||
+15
-7
@@ -9,7 +9,7 @@
|
|||||||
#include "imageinfo.h"
|
#include "imageinfo.h"
|
||||||
#include "imagescrollareagl.h"
|
#include "imagescrollareagl.h"
|
||||||
#include "filesystemwidget.h"
|
#include "filesystemwidget.h"
|
||||||
#include "stretchpanel.h"
|
#include "stretchtoolbar.h"
|
||||||
#include "databaseview.h"
|
#include "databaseview.h"
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
@@ -18,10 +18,11 @@ class MainWindow : public QMainWindow
|
|||||||
ImageScrollArea *m_image;
|
ImageScrollArea *m_image;
|
||||||
ImageScrollAreaGL *m_imageGL;
|
ImageScrollAreaGL *m_imageGL;
|
||||||
ImageRingList *m_ringList;
|
ImageRingList *m_ringList;
|
||||||
StretchPanel *m_stretchPanel;
|
StretchToolbar *m_stretchPanel;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
ImageInfo *m_info;
|
ImageInfo *m_info;
|
||||||
FilesystemWidget *m_filesystem;
|
FilesystemWidget *m_filesystem;
|
||||||
|
Filetree *m_filetree;
|
||||||
DataBaseView *m_databaseView;
|
DataBaseView *m_databaseView;
|
||||||
static int socketPair[2];
|
static int socketPair[2];
|
||||||
QSocketNotifier *socketNotifier;
|
QSocketNotifier *socketNotifier;
|
||||||
@@ -29,32 +30,39 @@ class MainWindow : public QMainWindow
|
|||||||
bool _maximized;
|
bool _maximized;
|
||||||
public:
|
public:
|
||||||
MainWindow(QWidget *parent = 0);
|
MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow();
|
~MainWindow() override;
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event);
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void keyReleaseEvent(QKeyEvent *event);
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
void setupSigterm();
|
void setupSigterm();
|
||||||
static void signalHandler(int);
|
static void signalHandler(int);
|
||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
void copyOrMove(bool copy);
|
||||||
|
void copyOrMove(bool copy, const QString &dest);
|
||||||
protected slots:
|
protected slots:
|
||||||
void socketNotify();
|
void socketNotify();
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
void pixmapLoaded(Image *image);
|
void pixmapLoaded(Image *image);
|
||||||
void loadFile();
|
void loadFile();
|
||||||
void loadFile(const QString path);
|
void loadFile(const QString &path);
|
||||||
void loadFile(int row);
|
void loadFile(int row);
|
||||||
void indexDir();
|
void indexDir();
|
||||||
|
void indexDir(const QString &dir);
|
||||||
|
void reindex();
|
||||||
void saveAs();
|
void saveAs();
|
||||||
|
void convert(const QString &outfile);
|
||||||
void markImage();
|
void markImage();
|
||||||
void unmarkImage();
|
void unmarkImage();
|
||||||
void markAndNext();
|
void markAndNext();
|
||||||
void unmarkAndNext();
|
void unmarkAndNext();
|
||||||
void copyMarked();
|
void copyMarked();
|
||||||
|
void moveMarked();
|
||||||
void toggleFullScreen();
|
void toggleFullScreen();
|
||||||
void liveMode(bool active);
|
void liveMode(bool active);
|
||||||
void imageStats(bool imageStats);
|
void imageStats(bool imageStats);
|
||||||
void peakFinder(bool findPeaks);
|
void peakFinder(bool findPeaks);
|
||||||
void starFinder(bool findStars);
|
void starFinder(bool findStars);
|
||||||
|
void showMarkFilesDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#include "markedfiles.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QTableView>
|
||||||
|
#include <QSqlTableModel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
|
||||||
|
MarkedFiles::MarkedFiles(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Marked files"));
|
||||||
|
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
m_tableView = new QTableView(this);
|
||||||
|
m_tableView->verticalHeader()->setDefaultSectionSize(1);
|
||||||
|
|
||||||
|
QSqlDatabase db = QSqlDatabase::database();
|
||||||
|
m_model = new QSqlTableModel(this, db);
|
||||||
|
|
||||||
|
m_model->setTable("files");
|
||||||
|
m_model->removeColumn(0);
|
||||||
|
m_model->setHeaderData(0, Qt::Horizontal, tr("Filename"));
|
||||||
|
m_model->select();
|
||||||
|
|
||||||
|
m_tableView->setModel(m_model);
|
||||||
|
m_tableView->resizeColumnsToContents();
|
||||||
|
m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
|
||||||
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
||||||
|
QPushButton *clearSelectedButton = new QPushButton(tr("Clear selected"), this);
|
||||||
|
QPushButton *clearAllButton = new QPushButton(tr("Clear all"), this);
|
||||||
|
|
||||||
|
connect(clearSelectedButton, &QPushButton::pressed, this, &MarkedFiles::clearSelected);
|
||||||
|
connect(clearAllButton, &QPushButton::pressed, this, &MarkedFiles::clearAll);
|
||||||
|
|
||||||
|
layout->addWidget(m_tableView);
|
||||||
|
layout->addLayout(hlayout);
|
||||||
|
hlayout->addWidget(clearSelectedButton);
|
||||||
|
hlayout->addWidget(clearAllButton);
|
||||||
|
|
||||||
|
resize(800, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkedFiles::clearSelected()
|
||||||
|
{
|
||||||
|
|
||||||
|
QSqlDatabase db = QSqlDatabase::database();
|
||||||
|
QSqlQuery query("DELETE FROM files where file = ?", db);
|
||||||
|
QModelIndexList rows = m_tableView->selectionModel()->selectedRows();
|
||||||
|
QStringList files;
|
||||||
|
for(const QModelIndex &row : rows)
|
||||||
|
{
|
||||||
|
files.append(row.data().toString());
|
||||||
|
}
|
||||||
|
query.bindValue(0, files);
|
||||||
|
query.execBatch();
|
||||||
|
m_model->select();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkedFiles::clearAll()
|
||||||
|
{
|
||||||
|
QSqlDatabase db = QSqlDatabase::database();
|
||||||
|
db.exec("DELETE FROM files");
|
||||||
|
m_model->select();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef MARKEDFILES_H
|
||||||
|
#define MARKEDFILES_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QTableView>
|
||||||
|
#include <QSqlTableModel>
|
||||||
|
|
||||||
|
class MarkedFiles : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QTableView *m_tableView;
|
||||||
|
QSqlTableModel *m_model;
|
||||||
|
public:
|
||||||
|
MarkedFiles(QWidget *parent = nullptr);
|
||||||
|
protected slots:
|
||||||
|
void clearSelected();
|
||||||
|
void clearAll();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MARKEDFILES_H
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 454 B |
@@ -5,5 +5,5 @@ Icon=org.nou.tenmon
|
|||||||
Comment=FITS Image viewer
|
Comment=FITS Image viewer
|
||||||
Name=Tenmon
|
Name=Tenmon
|
||||||
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
|
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
|
||||||
MimeType=image/fits;
|
MimeType=image/fits;image/x-xisf;
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
+40
-1
@@ -1,5 +1,4 @@
|
|||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
RawImage::ImgType CV2Type(int cvtype)
|
RawImage::ImgType CV2Type(int cvtype)
|
||||||
{
|
{
|
||||||
@@ -234,6 +233,11 @@ RawImage::ImgType RawImage::type() const
|
|||||||
return CV2Type(m_img.type());
|
return CV2Type(m_img.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RawImage::dataType() const
|
||||||
|
{
|
||||||
|
return m_img.type();
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t RawImage::norm() const
|
uint32_t RawImage::norm() const
|
||||||
{
|
{
|
||||||
switch(m_img.type())
|
switch(m_img.type())
|
||||||
@@ -259,3 +263,38 @@ const void *RawImage::data() const
|
|||||||
{
|
{
|
||||||
return m_img.ptr();
|
return m_img.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RawImage::convertToThumbnail()
|
||||||
|
{
|
||||||
|
m_thumbAspect = (float)width() / height();
|
||||||
|
switch(CV_MAT_DEPTH(m_img.type()))
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
m_img.convertTo(m_img, CV_16U, 255);
|
||||||
|
break;
|
||||||
|
case CV_32F:
|
||||||
|
m_img.convertTo(m_img, CV_16U, 65535);
|
||||||
|
break;
|
||||||
|
case CV_16U:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_img.channels() == 1)
|
||||||
|
cv::cvtColor(m_img, m_img, cv::COLOR_GRAY2RGB);
|
||||||
|
if(m_img.channels() == 4)
|
||||||
|
cv::cvtColor(m_img, m_img, cv::COLOR_BGRA2RGB);
|
||||||
|
cv::Size dsize(THUMB_SIZE, THUMB_SIZE);
|
||||||
|
cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
float RawImage::thumbAspect() const
|
||||||
|
{
|
||||||
|
return m_thumbAspect;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cv::Mat& RawImage::mat() const
|
||||||
|
{
|
||||||
|
return m_img;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
#include <opencv2/imgproc.hpp>
|
#include <opencv2/imgproc.hpp>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
|
||||||
|
const int THUMB_SIZE = 128;
|
||||||
|
const int THUMB_SIZE_BORDER = 138;
|
||||||
|
const int THUMB_SIZE_BORDER_Y = 158;
|
||||||
|
|
||||||
class Peak
|
class Peak
|
||||||
{
|
{
|
||||||
uint32_t m_v;
|
uint32_t m_v;
|
||||||
@@ -42,6 +46,7 @@ protected:
|
|||||||
double m_min;
|
double m_min;
|
||||||
double m_max;
|
double m_max;
|
||||||
double m_mad;
|
double m_mad;
|
||||||
|
float m_thumbAspect;
|
||||||
public:
|
public:
|
||||||
enum ImgType
|
enum ImgType
|
||||||
{
|
{
|
||||||
@@ -69,9 +74,13 @@ public:
|
|||||||
uint32_t height() const;
|
uint32_t height() const;
|
||||||
uint32_t size() const;
|
uint32_t size() const;
|
||||||
ImgType type() const;
|
ImgType type() const;
|
||||||
|
int dataType() const;
|
||||||
uint32_t norm() const;
|
uint32_t norm() const;
|
||||||
void* data();
|
void* data();
|
||||||
const void* data() const;
|
const void* data() const;
|
||||||
|
void convertToThumbnail();
|
||||||
|
float thumbAspect() const;
|
||||||
|
const cv::Mat& mat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RAWIMAGE_H
|
#endif // RAWIMAGE_H
|
||||||
|
|||||||
+20
-1
@@ -2,11 +2,30 @@
|
|||||||
<qresource prefix="/shaders">
|
<qresource prefix="/shaders">
|
||||||
<file>image.frag</file>
|
<file>image.frag</file>
|
||||||
<file>image.vert</file>
|
<file>image.vert</file>
|
||||||
|
<file>thumb.frag</file>
|
||||||
|
<file>thumb.vert</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>icon.png</file>
|
|
||||||
<file>invert.png</file>
|
<file>invert.png</file>
|
||||||
<file>nuke.png</file>
|
<file>nuke.png</file>
|
||||||
<file>bayer.png</file>
|
<file>bayer.png</file>
|
||||||
|
<file>org.nou.tenmon.png</file>
|
||||||
|
<file>nuke_a.png</file>
|
||||||
|
<file>about/tenmon</file>
|
||||||
|
<file>about/pcl</file>
|
||||||
|
<file>translations/tenmon_en.qm</file>
|
||||||
|
<file>translations/tenmon_sk.qm</file>
|
||||||
|
<file>about/filter.png</file>
|
||||||
|
<file>about/stretch-panel.png</file>
|
||||||
|
<file>translations/tenmon_fr.qm</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="en" prefix="/">
|
||||||
|
<file alias="help">about/help_en</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="sk" prefix="/">
|
||||||
|
<file alias="help">about/help_sk</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="fr" prefix="/">
|
||||||
|
<file alias="help">about/help_fr</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ static float clamp(float x)
|
|||||||
|
|
||||||
STFSlider::STFSlider(QWidget *parent) : QWidget(parent)
|
STFSlider::STFSlider(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setMinimumWidth(100);
|
||||||
setMinimumHeight(15);
|
setMinimumHeight(15);
|
||||||
setMaximumHeight(15);
|
setMaximumHeight(15);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
#include "stretchpanel.h"
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include "imageringlist.h"
|
|
||||||
|
|
||||||
const float BLACK_POINT_SIGMA = -2.8f;
|
|
||||||
const float MAD_TO_SIGMA = 1.4826f;
|
|
||||||
const float TARGET_BACKGROUND = 0.25f;
|
|
||||||
|
|
||||||
float MTF(float x, float m)
|
|
||||||
{
|
|
||||||
if(x < 0)return 0;
|
|
||||||
if(x > 1)return 1;
|
|
||||||
return ((m - 1) * x) / ((2 * m - 1) * x - m);
|
|
||||||
}
|
|
||||||
|
|
||||||
StretchPanel::StretchPanel(QWidget *parent) : QWidget(parent)
|
|
||||||
{
|
|
||||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
m_stfSlider = new STFSlider(this);
|
|
||||||
layout->addWidget(m_stfSlider);
|
|
||||||
connect(m_stfSlider, SIGNAL(paramChanged(float, float, float)), this, SIGNAL(paramChanged(float,float,float)));
|
|
||||||
|
|
||||||
QToolButton *autoStretchButton = new QToolButton(this);
|
|
||||||
autoStretchButton->setIcon(QIcon(":/nuke.png"));
|
|
||||||
autoStretchButton->setToolTip(tr("Auto Stretch F12"));
|
|
||||||
autoStretchButton->setShortcut(Qt::Key_F12);
|
|
||||||
connect(autoStretchButton, SIGNAL(pressed()), this, SIGNAL(autoStretch()));
|
|
||||||
|
|
||||||
QToolButton *resetButton = new QToolButton(this);
|
|
||||||
resetButton->setIcon(style()->standardIcon(QStyle::SP_DialogResetButton));
|
|
||||||
resetButton->setToolTip(tr("Reset Screen Transfer Function F11"));
|
|
||||||
resetButton->setShortcut(Qt::Key_F11);
|
|
||||||
connect(resetButton, &QToolButton::pressed, this, &StretchPanel::resetMTF);
|
|
||||||
|
|
||||||
QToolButton *invertButton = new QToolButton(this);
|
|
||||||
invertButton->setIcon(QIcon(":/invert.png"));
|
|
||||||
invertButton->setCheckable(true);
|
|
||||||
connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool)));
|
|
||||||
|
|
||||||
QToolButton *superPixelButton = new QToolButton(this);
|
|
||||||
superPixelButton->setIcon(QIcon(":/bayer.png"));
|
|
||||||
superPixelButton->setCheckable(true);
|
|
||||||
superPixelButton->setToolTip(tr("Superpixel CFA draw 2x2 pixel as one"));
|
|
||||||
connect(superPixelButton, SIGNAL(toggled(bool)), this, SIGNAL(superPixel(bool)));
|
|
||||||
|
|
||||||
layout->addWidget(autoStretchButton);
|
|
||||||
layout->addWidget(resetButton);
|
|
||||||
layout->addWidget(invertButton);
|
|
||||||
layout->addWidget(superPixelButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StretchPanel::stretchImage(Image *img)
|
|
||||||
{
|
|
||||||
if(img)
|
|
||||||
{
|
|
||||||
if(img->rawImage())
|
|
||||||
{
|
|
||||||
double median, mad;
|
|
||||||
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, nullptr, &mad);
|
|
||||||
median /= img->rawImage()->norm();
|
|
||||||
mad /= img->rawImage()->norm();
|
|
||||||
float bp = median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA;
|
|
||||||
float mid = MTF(median - bp, TARGET_BACKGROUND);
|
|
||||||
m_stfSlider->setMTFParams(bp, mid, 1.0f);
|
|
||||||
emit paramChanged(m_stfSlider->blackPoint(), m_stfSlider->midPoint(), 1.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StretchPanel::resetMTF()
|
|
||||||
{
|
|
||||||
m_stfSlider->setMTFParams(0, 0.5, 1);
|
|
||||||
emit paramChanged(0, 0.5, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include "stretchtoolbar.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include "imageringlist.h"
|
||||||
|
|
||||||
|
const float BLACK_POINT_SIGMA = -2.8f;
|
||||||
|
const float MAD_TO_SIGMA = 1.4826f;
|
||||||
|
const float TARGET_BACKGROUND = 0.25f;
|
||||||
|
|
||||||
|
float MTF(float x, float m)
|
||||||
|
{
|
||||||
|
if(x < 0)return 0;
|
||||||
|
if(x > 1)return 1;
|
||||||
|
return ((m - 1) * x) / ((2 * m - 1) * x - m);
|
||||||
|
}
|
||||||
|
|
||||||
|
StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar"), parent)
|
||||||
|
{
|
||||||
|
setObjectName("stretchtoolbar");
|
||||||
|
m_stfSlider = new STFSlider(this);
|
||||||
|
addWidget(m_stfSlider);
|
||||||
|
connect(m_stfSlider, SIGNAL(paramChanged(float, float, float)), this, SIGNAL(paramChanged(float,float,float)));
|
||||||
|
|
||||||
|
QAction *autoStretchButton = addAction(QIcon(":/nuke.png"), tr("Auto Stretch F12"));
|
||||||
|
autoStretchButton->setShortcut(Qt::Key_F12);
|
||||||
|
connect(autoStretchButton, SIGNAL(triggered()), this, SIGNAL(autoStretch()));
|
||||||
|
|
||||||
|
QAction *resetButton = addAction(style()->standardIcon(QStyle::SP_DialogResetButton), tr("Reset Screen Transfer Function F11"));
|
||||||
|
resetButton->setShortcut(Qt::Key_F11);
|
||||||
|
connect(resetButton, &QAction::triggered, this, &StretchToolbar::resetMTF);
|
||||||
|
|
||||||
|
QAction *invertButton = addAction(QIcon(":/invert.png"), tr("Invert colors"));
|
||||||
|
invertButton->setCheckable(true);
|
||||||
|
connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool)));
|
||||||
|
|
||||||
|
QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Superpixel CFA draw 2x2 pixel as one"));
|
||||||
|
superPixelButton->setCheckable(true);
|
||||||
|
connect(superPixelButton, SIGNAL(toggled(bool)), this, SIGNAL(superPixel(bool)));
|
||||||
|
|
||||||
|
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
|
||||||
|
m_autoStretchOnLoad->setCheckable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StretchToolbar::stretchImage(Image *img)
|
||||||
|
{
|
||||||
|
if(img)
|
||||||
|
{
|
||||||
|
if(img->rawImage())
|
||||||
|
{
|
||||||
|
double median, mad, max;
|
||||||
|
img->rawImage()->imageStats(nullptr, nullptr, &median, nullptr, &max, &mad);
|
||||||
|
median /= img->rawImage()->norm();
|
||||||
|
mad /= img->rawImage()->norm();
|
||||||
|
max /= img->rawImage()->norm();
|
||||||
|
if(max>1.0f)max = 1.0f;
|
||||||
|
float bp = median + mad * BLACK_POINT_SIGMA * MAD_TO_SIGMA;
|
||||||
|
float mid = MTF(median - bp, TARGET_BACKGROUND);
|
||||||
|
m_stfSlider->setMTFParams(bp, mid, max);
|
||||||
|
emit paramChanged(m_stfSlider->blackPoint(), m_stfSlider->midPoint(), max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StretchToolbar::resetMTF()
|
||||||
|
{
|
||||||
|
m_stfSlider->setMTFParams(0, 0.5, 1);
|
||||||
|
emit paramChanged(0, 0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StretchToolbar::imageLoaded(Image *img)
|
||||||
|
{
|
||||||
|
if(m_autoStretchOnLoad->isChecked())
|
||||||
|
stretchImage(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
#ifndef STRETCHPANEL_H
|
#ifndef STRETCHTOOLBAR_H
|
||||||
#define STRETCHPANEL_H
|
#define STRETCHTOOLBAR_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QToolBar>
|
||||||
#include "stfslider.h"
|
#include "stfslider.h"
|
||||||
|
|
||||||
class Image;
|
class Image;
|
||||||
|
|
||||||
class StretchPanel : public QWidget
|
class StretchToolbar : public QToolBar
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
STFSlider *m_stfSlider;
|
STFSlider *m_stfSlider;
|
||||||
|
QAction *m_autoStretchOnLoad;
|
||||||
public:
|
public:
|
||||||
explicit StretchPanel(QWidget *parent = nullptr);
|
explicit StretchToolbar(QWidget *parent = nullptr);
|
||||||
public slots:
|
public slots:
|
||||||
void stretchImage(Image *img);
|
void stretchImage(Image *img);
|
||||||
void resetMTF();
|
void resetMTF();
|
||||||
|
void imageLoaded(Image *img);
|
||||||
signals:
|
signals:
|
||||||
void paramChanged(float low, float mid, float high);
|
void paramChanged(float low, float mid, float high);
|
||||||
void autoStretch();
|
void autoStretch();
|
||||||
@@ -22,4 +24,4 @@ signals:
|
|||||||
void superPixel(bool enable);
|
void superPixel(bool enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // STRETCHPANEL_H
|
#endif // STRETCHTOOLBAR_H
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
uniform sampler2DArray qt_Texture0;
|
||||||
|
uniform vec3 mtf_param;
|
||||||
|
uniform bool invert;
|
||||||
|
in vec3 qt_TexCoord0;
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
vec4 MTF(vec4 x, vec3 m)
|
||||||
|
{
|
||||||
|
x = (x - m.x) / (m.z - m.x);
|
||||||
|
x = clamp(x, vec4(0.0), vec4(1.0));
|
||||||
|
return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
color = texture(qt_Texture0, qt_TexCoord0);
|
||||||
|
color = MTF(color, mtf_param);
|
||||||
|
if(invert)color = vec4(1.0) - color;
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 qt_Vertex;
|
||||||
|
in vec2 qt_MultiTexCoord0;
|
||||||
|
in ivec3 imageSize_num;
|
||||||
|
out vec3 qt_TexCoord0;
|
||||||
|
uniform ivec3 viewport_row;
|
||||||
|
uniform mat4 mvp;
|
||||||
|
uniform vec2 offset;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec2 pos = qt_Vertex * 0.5;
|
||||||
|
pos.y *= -1.0;
|
||||||
|
pos = pos * imageSize_num.xy + 69;
|
||||||
|
ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * ivec2(138, 158);
|
||||||
|
|
||||||
|
gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0);
|
||||||
|
qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,423 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>About</name>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>About Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DataBaseView</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Select columns</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Text to search, you can % as wildcard</source>
|
||||||
|
<translation>Text to search, you can % as wildcard</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filter</source>
|
||||||
|
<translation>Filter</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>FITSFileModel</name>
|
||||||
|
<message>
|
||||||
|
<source>File name</source>
|
||||||
|
<translation>File name</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Filetree</name>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Open</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Copy marked files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Move marked files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Index directory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set as root</source>
|
||||||
|
<translation>Set as root directory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset root</source>
|
||||||
|
<translation>Reset root directory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Go up</source>
|
||||||
|
<translation>Go up</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HelpDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Help</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageInfo</name>
|
||||||
|
<message>
|
||||||
|
<source>Property</source>
|
||||||
|
<translation>Property</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Value</source>
|
||||||
|
<translation>Value</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Comment</source>
|
||||||
|
<translation>Comment</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS Header</source>
|
||||||
|
<translation>FITS Header</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Image info</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageRingList</name>
|
||||||
|
<message>
|
||||||
|
<source>Name</source>
|
||||||
|
<translation>Name</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageWidget</name>
|
||||||
|
<message>
|
||||||
|
<source>OpenGL error</source>
|
||||||
|
<translation>OpenGL error</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
|
<translation>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Image info</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open DB</source>
|
||||||
|
<translation>Can't open DB</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open SQLITE database</source>
|
||||||
|
<translation>Can't open SQLITE database</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filesystem</source>
|
||||||
|
<translation>File system</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS files database</source>
|
||||||
|
<translation type="vanished">FITS files database</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Tenmon</source>
|
||||||
|
<translation>Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File</source>
|
||||||
|
<translation>File</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Open</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Copy marked files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save as</source>
|
||||||
|
<translation>Save as</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Live mode</source>
|
||||||
|
<translation>Live mode</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation>Exit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>View</source>
|
||||||
|
<translation>View</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom In</source>
|
||||||
|
<translation>Zoom In</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom Out</source>
|
||||||
|
<translation>Zoom Out</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Best Fit</source>
|
||||||
|
<translation>Best Fit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>100%</source>
|
||||||
|
<translation>100%</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fullscreen</source>
|
||||||
|
<translation>Fullscreen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select</source>
|
||||||
|
<translation>Select</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Mark</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Unmark</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark and next</source>
|
||||||
|
<translation>Mark and next</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark and next</source>
|
||||||
|
<translation>Unmark and next</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Analyze</source>
|
||||||
|
<translation>Analyze</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image statistics</source>
|
||||||
|
<translation>Image statistics</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peak finder</source>
|
||||||
|
<translation>Peak finder</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Docks</source>
|
||||||
|
<translation>Docks</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open file</source>
|
||||||
|
<translation>Open file</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select destination</source>
|
||||||
|
<translation>Select destination</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copying</source>
|
||||||
|
<translation>Copying</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Cancel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Move marked files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Index directory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails</source>
|
||||||
|
<translation>Thumbnails</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show marked</source>
|
||||||
|
<translation>Show marked</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Help</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>About Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Qt</source>
|
||||||
|
<translation>About Qt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moving</source>
|
||||||
|
<translation>Moving</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Indexing FITS files</source>
|
||||||
|
<translation>Indexing FITS files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
|
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reindex files</source>
|
||||||
|
<translation>Reindex files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS/XISF files database</source>
|
||||||
|
<translation>FITS/XISF files database</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File tree</source>
|
||||||
|
<translation>File tree</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Star finder</source>
|
||||||
|
<translation>Star finder</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MarkedFiles</name>
|
||||||
|
<message>
|
||||||
|
<source>Marked files</source>
|
||||||
|
<translation>Marked files</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>File name</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear selected</source>
|
||||||
|
<translation>Clear selected</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear all</source>
|
||||||
|
<translation>Clear all</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>ISO</source>
|
||||||
|
<translation>ISO</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shutter speed</source>
|
||||||
|
<translation>Shutter speed</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Width</source>
|
||||||
|
<translation>Width</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Height</source>
|
||||||
|
<translation>Height</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Error</source>
|
||||||
|
<translation>Error</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>File name</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mean</source>
|
||||||
|
<translation>Mean</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Standart deviation</source>
|
||||||
|
<translation>Standart deviation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Median</source>
|
||||||
|
<translation>Median</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minimum</source>
|
||||||
|
<translation>Minimum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Maximum</source>
|
||||||
|
<translation>Maximum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>MAD</source>
|
||||||
|
<translation>MAD</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks</source>
|
||||||
|
<translation>Peaks</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks draw</source>
|
||||||
|
<translation>Peaks draw</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM X</source>
|
||||||
|
<translation>FWHM X</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM Y</source>
|
||||||
|
<translation>FWHM Y</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsupported sample format</source>
|
||||||
|
<translation>Unsupported sample format</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SelectColumnsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Select columns</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>StretchToolbar</name>
|
||||||
|
<message>
|
||||||
|
<source>Stretch toolbar</source>
|
||||||
|
<translation>Stretch toolbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Auto Stretch F12</source>
|
||||||
|
<translation>Auto Stretch F12</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset Screen Transfer Function F11</source>
|
||||||
|
<translation>Reset Screen Transfer Function F11</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Invert colors</source>
|
||||||
|
<translation>Invert colors</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Superpixel CFA draw 2x2 pixel as one</source>
|
||||||
|
<translation>Superpixel CFA draw 2x2 pixel as one</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Apply auto stretch on load</source>
|
||||||
|
<translation>Apply auto stretch on load</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Binary file not shown.
@@ -0,0 +1,423 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="fr_FR" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>About</name>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>A propos de Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DataBaseView</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Choix des colonnes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Text to search, you can % as wildcard</source>
|
||||||
|
<translation>Texte à chercher, utilisez % comme caractère générique</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filter</source>
|
||||||
|
<translation>Filtre</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>FITSFileModel</name>
|
||||||
|
<message>
|
||||||
|
<source>File name</source>
|
||||||
|
<translation>Nom de fichier</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Filetree</name>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Ouvrir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Copier les fichiers marqués</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Déplacer les fichiers marqués</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Indexer le répertoire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set as root</source>
|
||||||
|
<translation>Définir comme répertoire racine</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset root</source>
|
||||||
|
<translation>Réinitialiser la racine</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Go up</source>
|
||||||
|
<translation>Monter</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HelpDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Aide</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageInfo</name>
|
||||||
|
<message>
|
||||||
|
<source>Property</source>
|
||||||
|
<translation>Propriété</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Value</source>
|
||||||
|
<translation>Valeur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Comment</source>
|
||||||
|
<translation>Commentaire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS Header</source>
|
||||||
|
<translation>En-tête FITS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Informations sur l'image</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageRingList</name>
|
||||||
|
<message>
|
||||||
|
<source>Name</source>
|
||||||
|
<translation>Nom</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageWidget</name>
|
||||||
|
<message>
|
||||||
|
<source>OpenGL error</source>
|
||||||
|
<translation>Erreur OpenGL</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
|
<translation>Impossible d'initialiser le contexte OpenGL 3.3. Assurez-vous que le pilote GPU approprié est installé.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Information sur l'image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open DB</source>
|
||||||
|
<translation>Ne peut ouvrir la base de donnée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open SQLITE database</source>
|
||||||
|
<translation>Ne peut ouvrir la base de donnée SQLITE</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filesystem</source>
|
||||||
|
<translation>Système de fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS files database</source>
|
||||||
|
<translation type="vanished">FITS files database</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Tenmon</source>
|
||||||
|
<translation>Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File</source>
|
||||||
|
<translation>Fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Ouvrir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Copier les fichiers marqués</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save as</source>
|
||||||
|
<translation>Enregistrer sous</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Live mode</source>
|
||||||
|
<translation>Mode temps réel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation>Sortir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>View</source>
|
||||||
|
<translation>Voir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom In</source>
|
||||||
|
<translation>Zoom avant</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom Out</source>
|
||||||
|
<translation>Zoom arrière</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Best Fit</source>
|
||||||
|
<translation>Meilleur ajustement</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>100%</source>
|
||||||
|
<translation>100%</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fullscreen</source>
|
||||||
|
<translation>Plein écran</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select</source>
|
||||||
|
<translation>Sélectionner</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Marquer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Décocher</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark and next</source>
|
||||||
|
<translation>Marquer et suivant</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark and next</source>
|
||||||
|
<translation>Décocher et suivant</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Analyze</source>
|
||||||
|
<translation>Analyse</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image statistics</source>
|
||||||
|
<translation>Statistiques de l'image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peak finder</source>
|
||||||
|
<translation>Détecteur de pic</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Docks</source>
|
||||||
|
<translation>Fenêtres encrables</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open file</source>
|
||||||
|
<translation>Ouvrir le ficher</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select destination</source>
|
||||||
|
<translation>Choisir la destination</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copying</source>
|
||||||
|
<translation>Copier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Abandon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Déplacer les fichiers marqués</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Indexer le répertoire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails</source>
|
||||||
|
<translation>Vignettes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show marked</source>
|
||||||
|
<translation>Afficher marqué</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Aide</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>A propos de Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Qt</source>
|
||||||
|
<translation>A propos de Qt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moving</source>
|
||||||
|
<translation>Déplacement</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Indexing FITS files</source>
|
||||||
|
<translation>Indexation des fichiers FITS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
|
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reindex files</source>
|
||||||
|
<translation>Ré-indexer les fichiers</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS/XISF files database</source>
|
||||||
|
<translation>Base de donnée FITS/XISF</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File tree</source>
|
||||||
|
<translation>Arborescence de fichiers</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Star finder</source>
|
||||||
|
<translation>Détecteur d'étoiles</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MarkedFiles</name>
|
||||||
|
<message>
|
||||||
|
<source>Marked files</source>
|
||||||
|
<translation>Fichiers marqués</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>Nom de fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear selected</source>
|
||||||
|
<translation>Effacer la sélection</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear all</source>
|
||||||
|
<translation>Effacer tout</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>ISO</source>
|
||||||
|
<translation>ISO</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shutter speed</source>
|
||||||
|
<translation>Vitesse d'obturation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Width</source>
|
||||||
|
<translation>Largeur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Height</source>
|
||||||
|
<translation>Hauteur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Error</source>
|
||||||
|
<translation>Erreur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>Nom de fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mean</source>
|
||||||
|
<translation>Moyenne</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Standart deviation</source>
|
||||||
|
<translation>Écart-type</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Median</source>
|
||||||
|
<translation>Médiane</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minimum</source>
|
||||||
|
<translation>Minimum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Maximum</source>
|
||||||
|
<translation>Maximum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>MAD</source>
|
||||||
|
<translation>MAD</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks</source>
|
||||||
|
<translation>Pics</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks draw</source>
|
||||||
|
<translation>Dessin des pic</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM X</source>
|
||||||
|
<translation>FWHM X</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM Y</source>
|
||||||
|
<translation>FWHM Y</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsupported sample format</source>
|
||||||
|
<translation>Format non pris en charge</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SelectColumnsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Choix des colonnes</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>StretchToolbar</name>
|
||||||
|
<message>
|
||||||
|
<source>Stretch toolbar</source>
|
||||||
|
<translation>Réglage de la luminosité</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Auto Stretch F12</source>
|
||||||
|
<translation>Luminosité automatique F12</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset Screen Transfer Function F11</source>
|
||||||
|
<translation>Réinitialiser la fonction de transfert d'écran F11</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Invert colors</source>
|
||||||
|
<translation>Inverser les couleurs</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Superpixel CFA draw 2x2 pixel as one</source>
|
||||||
|
<translation>Superpixel CFA dessine 2x2 pixels comme un seul</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Apply auto stretch on load</source>
|
||||||
|
<translation>Appliquer la luminosité automatiquement au chargement</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Binary file not shown.
@@ -0,0 +1,436 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="sk_SK" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>About</name>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>O Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DataBaseView</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Vyber stĺpce</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Text to search, you can % as wildcard</source>
|
||||||
|
<translation>Text na vyhľadanie, môžete použit % ako zástupný znak</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filter</source>
|
||||||
|
<translatorcomment>Meno súboru</translatorcomment>
|
||||||
|
<translation>Filter</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>FITSFileModel</name>
|
||||||
|
<message>
|
||||||
|
<source>File name</source>
|
||||||
|
<translation>Meno súboru</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Filetree</name>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Otvoriť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Skopírovať označené súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Presunúť označené súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Indexovať adresár</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Set as root</source>
|
||||||
|
<translation>Nastav koreňový adresár</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset root</source>
|
||||||
|
<translation>Resetuj koreňový adresár</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Go up</source>
|
||||||
|
<translation>O úroveň vyššie</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>HelpDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Pomoc</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageInfo</name>
|
||||||
|
<message>
|
||||||
|
<source>Property</source>
|
||||||
|
<translation>Vlastnosť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Value</source>
|
||||||
|
<translation>Hodnota</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Comment</source>
|
||||||
|
<translation>Komentár</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS Header</source>
|
||||||
|
<translation>FITS hlavička</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Informácie o obrázku</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageRingList</name>
|
||||||
|
<message>
|
||||||
|
<source>Name</source>
|
||||||
|
<translation>Meno</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ImageWidget</name>
|
||||||
|
<message>
|
||||||
|
<source>OpenGL error</source>
|
||||||
|
<translation>OpenGL chyba</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed.</source>
|
||||||
|
<translation>Nepodarilo sa incializovať OpenGL 3.3 context. Ubezpečte sa že sú nainštalované ovládače pre GPU.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<source>Image info</source>
|
||||||
|
<translation>Informácie o obrázku</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open DB</source>
|
||||||
|
<translation>Nie je možné otvoriť DB</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Can't open SQLITE database</source>
|
||||||
|
<translation>Nie je možné otvoriť SQLITE databázu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filesystem</source>
|
||||||
|
<translation>Zoznam súborov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS Editor</source>
|
||||||
|
<translation type="vanished">FITS editor</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS files database</source>
|
||||||
|
<translation type="vanished">Databáza FITS súborov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Tenmon</source>
|
||||||
|
<translation>Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File</source>
|
||||||
|
<translation>Súbor</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Otvoriť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy marked files</source>
|
||||||
|
<translation>Skopírovať označené súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save as</source>
|
||||||
|
<translation>Uložiť ako</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation>Ukončiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>View</source>
|
||||||
|
<translation>Zobrazenie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom In</source>
|
||||||
|
<translation>Priblížiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zoom Out</source>
|
||||||
|
<translation>Oddialiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Best Fit</source>
|
||||||
|
<translation>Najlepšia veľkosť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>100%</source>
|
||||||
|
<translation>100%</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fullscreen</source>
|
||||||
|
<translation>Celá obrazovka</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select</source>
|
||||||
|
<translation>Výber</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark</source>
|
||||||
|
<translation>Označiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark</source>
|
||||||
|
<translation>Odznačiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark and next</source>
|
||||||
|
<translation>Označiť a ďaľší</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unmark and next</source>
|
||||||
|
<translation>Odznačiť a ďaľší</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Analyze</source>
|
||||||
|
<translation>Analýza</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image statistics</source>
|
||||||
|
<translation>Štatistiky obrázka</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Docks</source>
|
||||||
|
<translation>Dokovacie panely</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open file</source>
|
||||||
|
<translation>Otvoriť súbor</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</source>
|
||||||
|
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.png *.JPG *.PNG)</source>
|
||||||
|
<translation type="vanished">Obrázky (*.jpg *.png *.JPG *.PNG)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select destination</source>
|
||||||
|
<translation>Vybrať cieľ</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copying</source>
|
||||||
|
<translation>Kopírovanie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Zrušiť</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Move marked files</source>
|
||||||
|
<translation>Presunúť označené súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Index directory</source>
|
||||||
|
<translation>Indexovať adresár</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Live mode</source>
|
||||||
|
<translation>Živý mód</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thumbnails</source>
|
||||||
|
<translation>Náhľady</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show marked</source>
|
||||||
|
<translation>Ukázať označené</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peak finder</source>
|
||||||
|
<translation>Vyhľadávač vrcholov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Pomoc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Tenmon</source>
|
||||||
|
<translation>O Tenmon</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Qt</source>
|
||||||
|
<translation>O Qt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moving</source>
|
||||||
|
<translation>Presúvanie</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
|
||||||
|
<translation>Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Indexing FITS files</source>
|
||||||
|
<translation>Indexovanie FITS/XISF súborov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
|
||||||
|
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reindex files</source>
|
||||||
|
<translation>Reindexuj súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FITS/XISF files database</source>
|
||||||
|
<translation>Databáza FITS/XISF súborov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File tree</source>
|
||||||
|
<translation>Strom súborov</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Star finder</source>
|
||||||
|
<translation>Vyhľadávač hviezd</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MarkedFiles</name>
|
||||||
|
<message>
|
||||||
|
<source>Marked files</source>
|
||||||
|
<translation>Označené súbory</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>Meno súboru</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear selected</source>
|
||||||
|
<translation>Zrušiť vybrané</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Clear all</source>
|
||||||
|
<translation>Zrušiť všetky</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<source>ISO</source>
|
||||||
|
<translation>ISO</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shutter speed</source>
|
||||||
|
<translation>Rýchlosť uzávierky</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Error</source>
|
||||||
|
<translation>Chyba</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsupported sample format</source>
|
||||||
|
<translation>Nepodporovaný formát</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Width</source>
|
||||||
|
<translation>Šírka</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Height</source>
|
||||||
|
<translation>Výška</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Filename</source>
|
||||||
|
<translation>Meno súboru</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mean</source>
|
||||||
|
<translation>Priemer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Standart deviation</source>
|
||||||
|
<translation>Štandardná odchýlka</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Median</source>
|
||||||
|
<translation>Medián</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Minimum</source>
|
||||||
|
<translation>Minimum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Maximum</source>
|
||||||
|
<translation>Maximum</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>MAD</source>
|
||||||
|
<translation>MAD</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks</source>
|
||||||
|
<translation>Vrcholky</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Peaks draw</source>
|
||||||
|
<translation>Vykreslené vrcholky</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM X</source>
|
||||||
|
<translation>FWHM X</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>FWHM Y</source>
|
||||||
|
<translation>FWHM Y</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SelectColumnsDialog</name>
|
||||||
|
<message>
|
||||||
|
<source>Select columns</source>
|
||||||
|
<translation>Výber stĺpcov</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>StretchToolbar</name>
|
||||||
|
<message>
|
||||||
|
<source>Stretch toolbar</source>
|
||||||
|
<translation>Panel úrovní</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Auto Stretch F12</source>
|
||||||
|
<translation>Automatické natiahnutie F12</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Reset Screen Transfer Function F11</source>
|
||||||
|
<translation>Resetuj funkciu prevodu na obrazovku F11</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Invert colors</source>
|
||||||
|
<translation>Invertuj farby</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Superpixel CFA draw 2x2 pixel as one</source>
|
||||||
|
<translation>Super pixel CFA kreslenie 2x2 pixelov ako jeden</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Apply auto stretch on load</source>
|
||||||
|
<translation>Aplikuj automatické natiahnutie pri načítaní</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Reference in New Issue
Block a user