Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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(OpenCV REQUIRED)
|
||||
find_library(GSL_LIB gsl REQUIRED)
|
||||
find_library(GSLCBLAS_LIB gslcblas REQUIRED)
|
||||
find_library(EXIF_LIB exif REQUIRED)
|
||||
find_library(FITS_LIB cfitsio REQUIRED)
|
||||
find_library(RAW_LIB NAMES raw_r raw REQUIRED)
|
||||
find_library(RAW_LIB NAMES raw_r REQUIRED)
|
||||
|
||||
set(TENMON_SRC
|
||||
about.cpp
|
||||
database.cpp
|
||||
databaseview.cpp
|
||||
filesystemwidget.cpp
|
||||
@@ -30,32 +32,62 @@ set(TENMON_SRC
|
||||
loadrunable.cpp
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
markedfiles.cpp
|
||||
rawimage.cpp
|
||||
starfit.cpp
|
||||
stfslider.cpp
|
||||
stretchpanel.cpp
|
||||
stretchtoolbar.cpp
|
||||
)
|
||||
|
||||
qt5_add_resources(TENMON_SRC resources.qrc)
|
||||
if(WIN32)
|
||||
list(APPEND TENMON_SRC icon.rc)
|
||||
add_compile_definitions("__PCL_WINDOWS")
|
||||
endif(WIN32)
|
||||
if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
|
||||
else()
|
||||
add_compile_definitions("__PCL_LINUX")
|
||||
endif()
|
||||
|
||||
add_executable(tenmon ${TENMON_SRC})
|
||||
add_executable(tenmon WIN32 ${TENMON_SRC})
|
||||
|
||||
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
||||
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include)
|
||||
target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} 3rdparty/include ${CMAKE_BINARY_DIR})
|
||||
|
||||
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql Qt5::OpenGL ${OpenCV_LIBS} ${GSL_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB})
|
||||
if(WIN32)
|
||||
target_link_directories(tenmon PRIVATE 3rdparty/lib/Windows)
|
||||
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 PCL lcms lz4 RFC6234 zlib)
|
||||
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)
|
||||
|
||||
if(LIBRAW_STATIC)
|
||||
add_compile_definitions("LIBRAW_NODLL")
|
||||
target_link_libraries(tenmon jasper)
|
||||
endif()
|
||||
|
||||
install(TARGETS tenmon)
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
./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,93 @@
|
||||
<!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>
|
||||
</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>
|
||||
|
||||
<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,78 @@
|
||||
<!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>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
+85
-53
@@ -98,6 +98,11 @@ QStringList Database::getMarkedFiles()
|
||||
return files;
|
||||
}
|
||||
|
||||
void Database::clearMarkedFiles()
|
||||
{
|
||||
QSqlDatabase::database().exec("DELETE FROM files");
|
||||
}
|
||||
|
||||
bool Database::checkError(QSqlQuery &query)
|
||||
{
|
||||
QSqlError error = query.lastError();
|
||||
@@ -134,6 +139,28 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
||||
database.rollback();
|
||||
}
|
||||
|
||||
void Database::reindex(QProgressDialog *progress)
|
||||
{
|
||||
QVariantList deleteids;
|
||||
QSqlDatabase database = QSqlDatabase::database();
|
||||
QSqlQuery files = database.exec("SELECT id,file,mtime FROM fits_files");
|
||||
progress->setMaximum(files.size());
|
||||
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++);
|
||||
}
|
||||
QSqlQuery deleteFiles("DELETE FROM fits_files WHERE id = ?", database);
|
||||
deleteFiles.bindValue(0, deleteids);
|
||||
deleteFiles.execBatch();
|
||||
}
|
||||
|
||||
QStringList Database::getFitsKeywords()
|
||||
{
|
||||
m_headerKeywords.exec();
|
||||
@@ -156,63 +183,68 @@ bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
|
||||
if(!indexDir2(dir.filePath(d), progress))
|
||||
return false;
|
||||
}
|
||||
ImageInfoData info;
|
||||
for(const QFileInfo &file : files)
|
||||
{
|
||||
progress->setValue(m_progress++);
|
||||
if(progress->wasCanceled())return false;
|
||||
QString filePath = file.absoluteFilePath();
|
||||
QString mtime = file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate);
|
||||
m_checkFile.bindValue(0, filePath);
|
||||
m_checkFile.exec();
|
||||
if(m_checkFile.next())
|
||||
{
|
||||
if(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();
|
||||
if(!indexFile(file))return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::indexFile(const QFileInfo &file)
|
||||
{
|
||||
ImageInfoData info;
|
||||
QString filePath = file.absoluteFilePath();
|
||||
QString mtime = file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate);
|
||||
m_checkFile.bindValue(0, filePath);
|
||||
m_checkFile.exec();
|
||||
if(m_checkFile.next())
|
||||
{
|
||||
if(m_checkFile.value(1).toString() == mtime)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
m_deleteFile.bindValue(0, m_checkFile.value(0).toLongLong());
|
||||
m_deleteFile.exec();
|
||||
}
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if(filePath.endsWith(".xisf", Qt::CaseInsensitive))
|
||||
ok = readXISFHeader(filePath, info);
|
||||
else
|
||||
ok = readFITSHeader(filePath, info);
|
||||
|
||||
qlonglong last_id = -1;
|
||||
if(ok)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,14 @@ public:
|
||||
bool unmark(const QString &filename);
|
||||
bool isMarked(const QString &filename);
|
||||
QStringList getMarkedFiles();
|
||||
void clearMarkedFiles();
|
||||
|
||||
void indexDir(const QDir &dir, QProgressDialog *progress);
|
||||
void reindex(QProgressDialog *progress);
|
||||
QStringList getFitsKeywords();
|
||||
protected:
|
||||
bool indexDir2(const QDir &dir, QProgressDialog *progress);
|
||||
bool indexFile(const QFileInfo &file);
|
||||
bool checkError(QSqlQuery &query);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
in vec2 qt_TexCoord0;
|
||||
uniform vec3 mtf_param;
|
||||
uniform bool bw;
|
||||
uniform bool invert;
|
||||
in vec2 qt_TexCoord0;
|
||||
out vec4 color;
|
||||
|
||||
vec4 MTF(vec4 x, vec3 m)
|
||||
@@ -16,7 +16,7 @@ vec4 MTF(vec4 x, vec3 m)
|
||||
|
||||
void main(void)
|
||||
{
|
||||
color = texture2D(qt_Texture0, qt_TexCoord0);
|
||||
color = texture(qt_Texture0, qt_TexCoord0);
|
||||
if(bw)color = color.rrra;
|
||||
color = MTF(color, mtf_param);
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#version 130
|
||||
#version 330
|
||||
|
||||
uniform sampler2D qt_Texture0;
|
||||
in vec2 qt_Vertex;
|
||||
|
||||
+56
-4
@@ -8,10 +8,11 @@ using namespace std;
|
||||
|
||||
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_released(true),
|
||||
m_current(false),
|
||||
m_number(number),
|
||||
m_name(name),
|
||||
m_ringList(ringList)
|
||||
{
|
||||
@@ -29,6 +30,14 @@ void Image::load()
|
||||
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()
|
||||
{
|
||||
m_rawImage.reset();
|
||||
@@ -46,6 +55,11 @@ RawImage *Image::rawImage()
|
||||
return m_rawImage.get();
|
||||
}
|
||||
|
||||
const RawImage *Image::thumbnail() const
|
||||
{
|
||||
return m_thumbnail.get();
|
||||
}
|
||||
|
||||
ImageInfoData Image::info() const
|
||||
{
|
||||
return m_info;
|
||||
@@ -56,6 +70,11 @@ bool Image::isCurrent() const
|
||||
return !m_released;
|
||||
}
|
||||
|
||||
int Image::number() const
|
||||
{
|
||||
return m_number;
|
||||
}
|
||||
|
||||
void Image::imageLoaded(void *rawImage, ImageInfoData info)
|
||||
{
|
||||
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)
|
||||
, m_liveMode(false)
|
||||
, m_analyzeLevel(None)
|
||||
{
|
||||
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
||||
m_thumbPool = new QThreadPool(this);
|
||||
}
|
||||
|
||||
ImageRingList::~ImageRingList()
|
||||
{
|
||||
QThreadPool::globalInstance()->clear();
|
||||
m_thumbPool->clear();
|
||||
|
||||
QThreadPool::globalInstance()->waitForDone();
|
||||
m_thumbPool->waitForDone();
|
||||
}
|
||||
|
||||
bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
||||
@@ -93,7 +123,7 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile)
|
||||
QStringList nameFilter;
|
||||
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.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;
|
||||
foreach(const QString &file, list)
|
||||
{
|
||||
@@ -209,6 +239,24 @@ 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();
|
||||
}
|
||||
|
||||
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
return createIndex(row, column, m_images.at(row).get());
|
||||
@@ -258,13 +306,17 @@ QVariant ImageRingList::headerData(int section, Qt::Orientation orientation, int
|
||||
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
||||
{
|
||||
QThreadPool::globalInstance()->clear();
|
||||
m_thumbPool->clear();
|
||||
QThreadPool::globalInstance()->waitForDone();
|
||||
m_thumbPool->waitForDone();
|
||||
beginResetModel();
|
||||
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(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*)));
|
||||
m_images.append(ptr);
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -10,6 +10,7 @@
|
||||
#include "rawimage.h"
|
||||
|
||||
class ImageRingList;
|
||||
class QThreadPool;
|
||||
|
||||
class Image : public QObject
|
||||
{
|
||||
@@ -17,22 +18,29 @@ class Image : public QObject
|
||||
bool m_loading;
|
||||
bool m_released;
|
||||
bool m_current;
|
||||
int m_number;
|
||||
std::unique_ptr<RawImage> m_rawImage;
|
||||
std::unique_ptr<RawImage> m_thumbnail;
|
||||
QString m_name;
|
||||
ImageInfoData m_info;
|
||||
ImageRingList *m_ringList;
|
||||
public:
|
||||
explicit Image(const QString name, ImageRingList *ringList);
|
||||
explicit Image(const QString name, int number, ImageRingList *ringList);
|
||||
void load();
|
||||
void loadThumbnail(QThreadPool *pool);
|
||||
void release();
|
||||
QString name() const;
|
||||
RawImage* rawImage();
|
||||
const RawImage* thumbnail() const;
|
||||
ImageInfoData info() const;
|
||||
bool isCurrent() const;
|
||||
int number() const;
|
||||
signals:
|
||||
void pixmapLoaded(Image *ptr);
|
||||
void thumbnailLoaded(Image *ptr);
|
||||
protected slots:
|
||||
void imageLoaded(void *rawImage, ImageInfoData info);
|
||||
void thumbnailLoadFinish(void *rawImage);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Image> ImagePtr;
|
||||
@@ -48,6 +56,7 @@ class ImageRingList : public QAbstractItemModel
|
||||
QFileSystemWatcher m_fileSystemWatcher;
|
||||
bool m_liveMode;
|
||||
AnalyzeLevel m_analyzeLevel;
|
||||
QThreadPool *m_thumbPool;
|
||||
public:
|
||||
explicit ImageRingList(QObject *parent = 0);
|
||||
~ImageRingList();
|
||||
@@ -62,6 +71,9 @@ public:
|
||||
void setFindStars(bool findStars);
|
||||
AnalyzeLevel analyzeLevel() const;
|
||||
void loadFile(int row);
|
||||
void loadThumbnails();
|
||||
void stopLoading();
|
||||
int imageCount() const;
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
@@ -75,6 +87,7 @@ protected:
|
||||
QList<ImagePtr>::iterator decrement(QList<ImagePtr>::iterator iter);
|
||||
signals:
|
||||
void pixmapLoaded(Image *image);
|
||||
void thumbnailLoaded(Image *image);
|
||||
void infoLoaded(ImageInfoData info);
|
||||
void currentImageChanged(int index);
|
||||
protected slots:
|
||||
|
||||
+198
-23
@@ -6,6 +6,9 @@
|
||||
#include <QOpenGLPixelTransferOptions>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QGridLayout>
|
||||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
#include <QCoreApplication>
|
||||
|
||||
struct RawImageType
|
||||
{
|
||||
@@ -54,6 +57,20 @@ ImageWidget::ImageWidget(QWidget *parent) : QOpenGLWidget(parent)
|
||||
m_range = UINT16_MAX;
|
||||
m_imgWidth = m_imgHeight = -1;
|
||||
m_superpixel = m_invert = false;
|
||||
m_showThumbnails = 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()
|
||||
@@ -113,6 +130,22 @@ void ImageWidget::blockRepaint(bool block)
|
||||
if(!block)update();
|
||||
}
|
||||
|
||||
void ImageWidget::allocateThumbnails(int count)
|
||||
{
|
||||
m_thumbnailTexture->destroy();
|
||||
m_thumbnailTexture->create();
|
||||
m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm);
|
||||
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
||||
m_thumbnailTexture->setLayers(count);
|
||||
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;
|
||||
m_thumbnailCount = count;
|
||||
}
|
||||
|
||||
void ImageWidget::setMTFParams(float low, float mid, float high)
|
||||
{
|
||||
m_low = low;
|
||||
@@ -162,6 +195,23 @@ QImage ImageWidget::renderToImage()
|
||||
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));
|
||||
if(!m_updateTimer->isActive())m_updateTimer->start();
|
||||
}
|
||||
|
||||
void ImageWidget::showThumbnail(bool enable)
|
||||
{
|
||||
m_showThumbnails = enable;
|
||||
update();
|
||||
}
|
||||
|
||||
void ImageWidget::paintGL()
|
||||
{
|
||||
if(m_blockRepaint)return;
|
||||
@@ -173,16 +223,35 @@ void ImageWidget::paintGL()
|
||||
if(height() > m_image->height()*m_scale)
|
||||
dy = -height()*0.5f + m_image->height()*m_scale*0.5f;
|
||||
|
||||
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);
|
||||
if(m_showThumbnails)
|
||||
{
|
||||
m_vaoThumb->bind();
|
||||
m_thumbnailTexture->bind(1);
|
||||
m_thumbnailProgram->bind();
|
||||
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
|
||||
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);
|
||||
}
|
||||
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)
|
||||
@@ -196,10 +265,15 @@ void ImageWidget::initializeGL()
|
||||
{
|
||||
f = context()->functions();
|
||||
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_vaoThumb = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
||||
m_vao->create();
|
||||
m_vaoThumb->create();
|
||||
m_vao->bind();
|
||||
|
||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
||||
@@ -226,7 +300,7 @@ void ImageWidget::initializeGL()
|
||||
m_buffer->create();
|
||||
m_buffer->bind();
|
||||
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->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert");
|
||||
@@ -245,6 +319,33 @@ void ImageWidget::initializeGL()
|
||||
m_program->setUniformValue("qt_Texture0", (GLuint)0);
|
||||
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->setFormat(QOpenGLTexture::RGB8U);
|
||||
m_image->allocateStorage();
|
||||
@@ -252,10 +353,42 @@ void ImageWidget::initializeGL()
|
||||
m_image->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
||||
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->setAlignment(1);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
ImageScrollAreaGL::ImageScrollAreaGL(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
@@ -267,6 +400,7 @@ ImageScrollAreaGL::ImageScrollAreaGL(QWidget *parent) : QWidget(parent)
|
||||
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
|
||||
m_scale = 1.0f;
|
||||
m_bestFit = false;
|
||||
m_thumbCount = 0;
|
||||
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(m_imageWidget, 0, 0);
|
||||
@@ -305,32 +439,66 @@ ImageWidget *ImageScrollAreaGL::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);
|
||||
setScrollRange(m_horizontalScrollBar, m_imgWidth*m_scale);
|
||||
m_verticalScrollBar->setRange(0, m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) * THUMB_SIZE_BORDER);
|
||||
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_verticalScrollBar->setRange(0, m_imgHeight*m_scale - m_verticalScrollBar->pageStep());
|
||||
m_horizontalScrollBar->setRange(0, m_imgWidth*m_scale - m_horizontalScrollBar->pageStep());
|
||||
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
||||
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);
|
||||
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER);
|
||||
}
|
||||
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)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
||||
m_horizontalScrollBar->setPageStep(m_imageWidget->width());
|
||||
if(m_thumbCount)
|
||||
{
|
||||
m_verticalScrollBar->setRange(0, m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) * THUMB_SIZE_BORDER);
|
||||
m_verticalScrollBar->setPageStep(THUMB_SIZE_BORDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_verticalScrollBar->setPageStep(m_imageWidget->height());
|
||||
m_horizontalScrollBar->setPageStep(m_imageWidget->width());
|
||||
}
|
||||
updateScrollbars();
|
||||
}
|
||||
|
||||
void ImageScrollAreaGL::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
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_lastPos = event->pos();
|
||||
}
|
||||
@@ -342,9 +510,16 @@ void ImageScrollAreaGL::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void ImageScrollAreaGL::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
m_bestFit = false;
|
||||
if(event->angleDelta().y() != 0)
|
||||
zoom(event->angleDelta().y() / 1200.0f);
|
||||
if(m_thumbCount)
|
||||
{
|
||||
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)
|
||||
|
||||
+20
-1
@@ -4,13 +4,15 @@
|
||||
#include <memory>
|
||||
#include <QObject>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions_3_2_Core>
|
||||
#include <QOpenGLFunctions_3_3_Core>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QScrollBar>
|
||||
#include <QTimer>
|
||||
#include "rawimage.h"
|
||||
#include "imageringlist.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -25,11 +27,17 @@ class ImageWidget : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
QOpenGLFunctions *f;
|
||||
QOpenGLFunctions_3_3_Core *f3;
|
||||
QTimer *m_updateTimer;
|
||||
std::unique_ptr<QOpenGLShaderProgram> m_program;
|
||||
std::unique_ptr<QOpenGLShaderProgram> m_thumbnailProgram;
|
||||
std::unique_ptr<QOpenGLBuffer> m_buffer;
|
||||
std::unique_ptr<QOpenGLBuffer> m_bufferSizes;
|
||||
std::unique_ptr<QOpenGLTexture> m_image;
|
||||
std::unique_ptr<QOpenGLVertexArrayObject> m_vao;
|
||||
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
|
||||
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
|
||||
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
|
||||
int m_width, m_height;
|
||||
int m_imgWidth, m_imgHeight;
|
||||
float m_low;
|
||||
@@ -42,6 +50,8 @@ class ImageWidget : public QOpenGLWidget
|
||||
bool m_bwImg;
|
||||
bool m_invert;
|
||||
bool m_superpixel;
|
||||
bool m_showThumbnails;
|
||||
int m_thumbnailCount;
|
||||
public:
|
||||
explicit ImageWidget(QWidget *parent = nullptr);
|
||||
~ImageWidget();
|
||||
@@ -49,16 +59,23 @@ public:
|
||||
void setImage(const QPixmap &pixmap);
|
||||
void setScale(float scale);
|
||||
void blockRepaint(bool block);
|
||||
void allocateThumbnails(int count);
|
||||
public slots:
|
||||
void setMTFParams(float low, float mid, float high);
|
||||
void setOffset(int dx, int dy);
|
||||
void superPixel(bool enable);
|
||||
void invert(bool enable);
|
||||
QImage renderToImage();
|
||||
void thumbnailLoaded(const Image *image);
|
||||
void showThumbnail(bool enable);
|
||||
protected:
|
||||
void paintGL();
|
||||
void resizeGL(int w, int h);
|
||||
void initializeGL();
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
signals:
|
||||
void fileDropped(const QString &path);
|
||||
};
|
||||
|
||||
class ImageScrollAreaGL : public QWidget
|
||||
@@ -71,12 +88,14 @@ class ImageScrollAreaGL : public QWidget
|
||||
QPoint m_lastPos;
|
||||
float m_scale;
|
||||
bool m_bestFit;
|
||||
int m_thumbCount;
|
||||
public:
|
||||
explicit ImageScrollAreaGL(QWidget *parent = nullptr);
|
||||
~ImageScrollAreaGL();
|
||||
void setImage(RawImage *image);
|
||||
void setImage(const QPixmap &pixmap);
|
||||
ImageWidget* imageWidget();
|
||||
void setThumbnails(int count);
|
||||
protected:
|
||||
void updateScrollbars(bool zoom = false);
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
||||
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-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})
|
||||
|
||||
+166
-5
@@ -13,10 +13,11 @@
|
||||
#include "rawimage.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_receiver(receiver),
|
||||
m_analyzeLevel(level)
|
||||
m_analyzeLevel(level),
|
||||
m_thumbnail(thumbnail)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -334,7 +335,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
|
||||
|
||||
void LoadRunable::run()
|
||||
{
|
||||
if(!m_receiver->isCurrent())
|
||||
if(!m_thumbnail && !m_receiver->isCurrent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -375,7 +376,7 @@ void LoadRunable::run()
|
||||
rawImage = new RawImage(img);
|
||||
}
|
||||
|
||||
if(rawImage && m_analyzeLevel >= Statistics)
|
||||
if(rawImage && m_analyzeLevel >= Statistics && !m_thumbnail)
|
||||
{
|
||||
double mean, median, min, max, mad;
|
||||
double stdDev;
|
||||
@@ -440,7 +441,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)
|
||||
@@ -478,3 +492,150 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -15,8 +15,18 @@ class LoadRunable : public QRunnable
|
||||
QString m_file;
|
||||
Image *m_receiver;
|
||||
AnalyzeLevel m_analyzeLevel;
|
||||
bool m_thumbnail;
|
||||
public:
|
||||
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level);
|
||||
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail = false);
|
||||
void run();
|
||||
};
|
||||
|
||||
class ConvertRunable : public QRunnable
|
||||
{
|
||||
QString m_infile;
|
||||
QString m_outfile;
|
||||
public:
|
||||
ConvertRunable(const QString &in, const QString &out);
|
||||
void run();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QTranslator>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QSurfaceFormat format;
|
||||
format.setMajorVersion(3);
|
||||
format.setMinorVersion(2);
|
||||
format.setMinorVersion(3);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
@@ -14,7 +15,11 @@ int main(int argc, char *argv[])
|
||||
QApplication a(argc, argv);
|
||||
a.setOrganizationName("nou");
|
||||
a.setApplicationName("Tenmon");
|
||||
a.setWindowIcon(QIcon(":/icon.png"));
|
||||
a.setWindowIcon(QIcon(":/org.nou.tenmon.png"));
|
||||
|
||||
QTranslator translator;
|
||||
if(translator.load(QLocale(), "tenmon", "_", ":/translations"))
|
||||
a.installTranslator(&translator);
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
+135
-54
@@ -14,6 +14,10 @@
|
||||
#include <unistd.h>
|
||||
#include <QSettings>
|
||||
#include <QCoreApplication>
|
||||
#include <QThreadPool>
|
||||
#include "loadrunable.h"
|
||||
#include "markedfiles.h"
|
||||
#include "about.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
@@ -41,11 +45,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
m_imageGL = new ImageScrollAreaGL(this);
|
||||
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, &StretchPanel::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
||||
connect(m_stretchPanel, &StretchPanel::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
||||
connect(m_stretchPanel, &StretchPanel::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
||||
connect(m_stretchPanel, &StretchToolbar::autoStretch, [&](){ m_stretchPanel->stretchImage(m_ringList->currentImage().get()); });
|
||||
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
|
||||
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
|
||||
|
||||
m_ringList = new ImageRingList(this);
|
||||
m_filesystem = new FilesystemWidget(m_ringList, this);
|
||||
@@ -58,17 +62,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
m_databaseView = new DataBaseView(m_database, this);
|
||||
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString)));
|
||||
|
||||
QDockWidget *stretchDock = new QDockWidget(tr("Stretch"), this);
|
||||
stretchDock->setWidget(m_stretchPanel);
|
||||
stretchDock->setObjectName("strechDock");
|
||||
addDockWidget(Qt::TopDockWidgetArea, stretchDock);
|
||||
addToolBar(Qt::TopToolBarArea, m_stretchPanel);
|
||||
|
||||
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
||||
filesystemDock->setWidget(m_filesystem);
|
||||
filesystemDock->setObjectName("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->setObjectName("databaseViewDock");
|
||||
databaseViewDock->hide();
|
||||
@@ -80,15 +81,24 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
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(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);
|
||||
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("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("Reindex files"), this, SLOT(reindex()));
|
||||
fileMenu->addSeparator();
|
||||
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
||||
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);
|
||||
|
||||
QMenu *viewMenu = new QMenu(tr("View"), this);
|
||||
@@ -96,7 +106,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
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("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->imageCount());
|
||||
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);
|
||||
|
||||
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
||||
@@ -105,6 +123,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
selectMenu->addSeparator();
|
||||
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("Show marked"), this, &MainWindow::showMarkFilesDialog);
|
||||
menuBar()->addMenu(selectMenu);
|
||||
|
||||
QMenu *analyzeMenu = new QMenu(tr("Analyze"), this);
|
||||
@@ -134,11 +153,16 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
|
||||
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
||||
dockMenu->addAction(infoDock->toggleViewAction());
|
||||
dockMenu->addAction(stretchDock->toggleViewAction());
|
||||
dockMenu->addAction(m_stretchPanel->toggleViewAction());
|
||||
dockMenu->addAction(filesystemDock->toggleViewAction());
|
||||
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
||||
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();
|
||||
QSettings settings;
|
||||
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
||||
@@ -232,6 +256,57 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::copyOrMove(bool copy)
|
||||
{
|
||||
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"), _lastDir);
|
||||
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()
|
||||
{
|
||||
socketNotifier->setEnabled(false);
|
||||
@@ -256,7 +331,7 @@ void MainWindow::loadFile()
|
||||
loadFile(file);
|
||||
}
|
||||
|
||||
void MainWindow::loadFile(const QString path)
|
||||
void MainWindow::loadFile(const QString &path)
|
||||
{
|
||||
if(!path.isEmpty())
|
||||
{
|
||||
@@ -289,17 +364,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()
|
||||
{
|
||||
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())
|
||||
{
|
||||
QImage img = m_imageGL->imageWidget()->renderToImage();
|
||||
if(!img.isNull())
|
||||
img.save(file);
|
||||
QFileInfo info(file);
|
||||
if(info.suffix().isEmpty())
|
||||
{
|
||||
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()
|
||||
{
|
||||
ImagePtr ptr = m_ringList->currentImage();
|
||||
@@ -342,42 +447,12 @@ void MainWindow::unmarkAndNext()
|
||||
|
||||
void MainWindow::copyMarked()
|
||||
{
|
||||
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"));
|
||||
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()));
|
||||
copyOrMove(true);
|
||||
}
|
||||
|
||||
if(dstFile.exists())
|
||||
continue;
|
||||
|
||||
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::moveMarked()
|
||||
{
|
||||
copyOrMove(false);
|
||||
}
|
||||
|
||||
void MainWindow::toggleFullScreen()
|
||||
@@ -414,6 +489,12 @@ void MainWindow::starFinder(bool findStars)
|
||||
m_ringList->setFindStars(findStars);
|
||||
}
|
||||
|
||||
void MainWindow::showMarkFilesDialog()
|
||||
{
|
||||
MarkedFiles markedFiles;
|
||||
markedFiles.exec();
|
||||
}
|
||||
|
||||
void MainWindow::updateWindowTitle()
|
||||
{
|
||||
ImagePtr ptr = m_ringList->currentImage();
|
||||
|
||||
+8
-3
@@ -9,7 +9,7 @@
|
||||
#include "imageinfo.h"
|
||||
#include "imagescrollareagl.h"
|
||||
#include "filesystemwidget.h"
|
||||
#include "stretchpanel.h"
|
||||
#include "stretchtoolbar.h"
|
||||
#include "databaseview.h"
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
@@ -18,7 +18,7 @@ class MainWindow : public QMainWindow
|
||||
ImageScrollArea *m_image;
|
||||
ImageScrollAreaGL *m_imageGL;
|
||||
ImageRingList *m_ringList;
|
||||
StretchPanel *m_stretchPanel;
|
||||
StretchToolbar *m_stretchPanel;
|
||||
Database *m_database;
|
||||
ImageInfo *m_info;
|
||||
FilesystemWidget *m_filesystem;
|
||||
@@ -36,25 +36,30 @@ protected:
|
||||
void setupSigterm();
|
||||
static void signalHandler(int);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void copyOrMove(bool copy);
|
||||
protected slots:
|
||||
void socketNotify();
|
||||
void updateWindowTitle();
|
||||
void pixmapLoaded(Image *image);
|
||||
void loadFile();
|
||||
void loadFile(const QString path);
|
||||
void loadFile(const QString &path);
|
||||
void loadFile(int row);
|
||||
void indexDir();
|
||||
void reindex();
|
||||
void saveAs();
|
||||
void convert(const QString &outfile);
|
||||
void markImage();
|
||||
void unmarkImage();
|
||||
void markAndNext();
|
||||
void unmarkAndNext();
|
||||
void copyMarked();
|
||||
void moveMarked();
|
||||
void toggleFullScreen();
|
||||
void liveMode(bool active);
|
||||
void imageStats(bool imageStats);
|
||||
void peakFinder(bool findPeaks);
|
||||
void starFinder(bool findStars);
|
||||
void showMarkFilesDialog();
|
||||
};
|
||||
|
||||
#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
|
||||
Name=Tenmon
|
||||
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
|
||||
MimeType=image/fits;
|
||||
MimeType=image/fits;image/x-xisf;
|
||||
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 <QDebug>
|
||||
|
||||
RawImage::ImgType CV2Type(int cvtype)
|
||||
{
|
||||
@@ -234,6 +233,11 @@ RawImage::ImgType RawImage::type() const
|
||||
return CV2Type(m_img.type());
|
||||
}
|
||||
|
||||
int RawImage::dataType() const
|
||||
{
|
||||
return m_img.type();
|
||||
}
|
||||
|
||||
uint32_t RawImage::norm() const
|
||||
{
|
||||
switch(m_img.type())
|
||||
@@ -259,3 +263,38 @@ const void *RawImage::data() const
|
||||
{
|
||||
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,9 @@
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <QImage>
|
||||
|
||||
const int THUMB_SIZE = 128;
|
||||
const int THUMB_SIZE_BORDER = 138;
|
||||
|
||||
class Peak
|
||||
{
|
||||
uint32_t m_v;
|
||||
@@ -42,6 +45,7 @@ protected:
|
||||
double m_min;
|
||||
double m_max;
|
||||
double m_mad;
|
||||
float m_thumbAspect;
|
||||
public:
|
||||
enum ImgType
|
||||
{
|
||||
@@ -69,9 +73,13 @@ public:
|
||||
uint32_t height() const;
|
||||
uint32_t size() const;
|
||||
ImgType type() const;
|
||||
int dataType() const;
|
||||
uint32_t norm() const;
|
||||
void* data();
|
||||
const void* data() const;
|
||||
void convertToThumbnail();
|
||||
float thumbAspect() const;
|
||||
const cv::Mat& mat() const;
|
||||
};
|
||||
|
||||
#endif // RAWIMAGE_H
|
||||
|
||||
+18
-1
@@ -2,11 +2,28 @@
|
||||
<qresource prefix="/shaders">
|
||||
<file>image.frag</file>
|
||||
<file>image.vert</file>
|
||||
<file>thumb.frag</file>
|
||||
<file>thumb.vert</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>icon.png</file>
|
||||
<file>invert.png</file>
|
||||
<file>nuke.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>
|
||||
</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 prefix="/help"/>
|
||||
<qresource prefix="/helpimg"/>
|
||||
</RCC>
|
||||
|
||||
@@ -12,6 +12,8 @@ static float clamp(float x)
|
||||
|
||||
STFSlider::STFSlider(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
setMinimumWidth(100);
|
||||
setMinimumHeight(15);
|
||||
setMaximumHeight(15);
|
||||
setMouseTracking(true);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stretchpanel.h"
|
||||
#include "stretchtoolbar.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QDebug>
|
||||
#include <QToolButton>
|
||||
@@ -15,45 +15,34 @@ float MTF(float x, float m)
|
||||
return ((m - 1) * x) / ((2 * m - 1) * x - m);
|
||||
}
|
||||
|
||||
StretchPanel::StretchPanel(QWidget *parent) : QWidget(parent)
|
||||
StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar"), parent)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
setObjectName("stretchtoolbar");
|
||||
m_stfSlider = new STFSlider(this);
|
||||
layout->addWidget(m_stfSlider);
|
||||
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"));
|
||||
QAction *autoStretchButton = addAction(QIcon(":/nuke.png"), tr("Auto Stretch F12"));
|
||||
autoStretchButton->setShortcut(Qt::Key_F12);
|
||||
connect(autoStretchButton, SIGNAL(pressed()), this, SIGNAL(autoStretch()));
|
||||
connect(autoStretchButton, SIGNAL(triggered()), this, SIGNAL(autoStretch()));
|
||||
|
||||
QToolButton *resetButton = new QToolButton(this);
|
||||
resetButton->setIcon(style()->standardIcon(QStyle::SP_DialogResetButton));
|
||||
resetButton->setToolTip(tr("Reset Screen Transfer Function F11"));
|
||||
QAction *resetButton = addAction(style()->standardIcon(QStyle::SP_DialogResetButton), tr("Reset Screen Transfer Function F11"));
|
||||
resetButton->setShortcut(Qt::Key_F11);
|
||||
connect(resetButton, &QToolButton::pressed, this, &StretchPanel::resetMTF);
|
||||
connect(resetButton, &QAction::triggered, this, &StretchToolbar::resetMTF);
|
||||
|
||||
QToolButton *invertButton = new QToolButton(this);
|
||||
invertButton->setIcon(QIcon(":/invert.png"));
|
||||
QAction *invertButton = addAction(QIcon(":/invert.png"), tr("Invert colors"));
|
||||
invertButton->setCheckable(true);
|
||||
connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool)));
|
||||
|
||||
QToolButton *superPixelButton = new QToolButton(this);
|
||||
superPixelButton->setIcon(QIcon(":/bayer.png"));
|
||||
QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Superpixel CFA draw 2x2 pixel as one"));
|
||||
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);
|
||||
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
|
||||
m_autoStretchOnLoad->setCheckable(true);
|
||||
}
|
||||
|
||||
void StretchPanel::stretchImage(Image *img)
|
||||
void StretchToolbar::stretchImage(Image *img)
|
||||
{
|
||||
if(img)
|
||||
{
|
||||
@@ -71,9 +60,16 @@ void StretchPanel::stretchImage(Image *img)
|
||||
}
|
||||
}
|
||||
|
||||
void StretchPanel::resetMTF()
|
||||
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
|
||||
#define STRETCHPANEL_H
|
||||
#ifndef STRETCHTOOLBAR_H
|
||||
#define STRETCHTOOLBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QToolBar>
|
||||
#include "stfslider.h"
|
||||
|
||||
class Image;
|
||||
|
||||
class StretchPanel : public QWidget
|
||||
class StretchToolbar : public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
STFSlider *m_stfSlider;
|
||||
QAction *m_autoStretchOnLoad;
|
||||
public:
|
||||
explicit StretchPanel(QWidget *parent = nullptr);
|
||||
explicit StretchToolbar(QWidget *parent = nullptr);
|
||||
public slots:
|
||||
void stretchImage(Image *img);
|
||||
void resetMTF();
|
||||
void imageLoaded(Image *img);
|
||||
signals:
|
||||
void paramChanged(float low, float mid, float high);
|
||||
void autoStretch();
|
||||
@@ -22,4 +24,4 @@ signals:
|
||||
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) * 138;
|
||||
|
||||
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,384 @@
|
||||
<?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>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>
|
||||
</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,397 @@
|
||||
<?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>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>
|
||||
</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