Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32f91d7b2f | |||
| 69fbad34b6 | |||
| e026042604 | |||
| bb7e5182af | |||
| f0152e2496 | |||
| eccf928032 | |||
| 897306d1c3 | |||
| cbc779090f | |||
| d826744f26 | |||
| 3bdfb12d4f | |||
| c416ae9941 | |||
| abbba2890f | |||
| 9c6847d334 | |||
| af1c26a9fe | |||
| e0441a6494 | |||
| a88f05a9fe | |||
| b58559a18a | |||
| 2ac14a6c04 | |||
| b84256625c | |||
| 202a2b11b7 | |||
| 32f192ed7e | |||
| a0422683bd | |||
| ce67b35bfa | |||
| f016500f12 | |||
| 6069ebbbac | |||
| e587d84e05 | |||
| c01f2e328a | |||
| 8b498bbe73 |
+35
-33
@@ -23,39 +23,41 @@ find_library(FITS_LIB cfitsio REQUIRED)
|
|||||||
find_library(RAW_LIB NAMES raw_r REQUIRED)
|
find_library(RAW_LIB NAMES raw_r REQUIRED)
|
||||||
find_library(WCS_LIB wcs wcslib REQUIRED)
|
find_library(WCS_LIB wcs wcslib REQUIRED)
|
||||||
find_library(LCMS2_LIB lcms2 REQUIRED)
|
find_library(LCMS2_LIB lcms2 REQUIRED)
|
||||||
find_library(STELLARSOLVER_LIB stellarsolver)
|
find_library(STELLARSOLVER_LIB NAMES stellarsolver stellarsolver6)
|
||||||
|
|
||||||
add_subdirectory(libXISF)
|
add_subdirectory(libXISF)
|
||||||
|
|
||||||
set(TENMON_SRC
|
set(TENMON_SRC
|
||||||
about.cpp about.h
|
src/about.cpp src/about.h
|
||||||
batchprocessing.cpp batchprocessing.h batchprocessing.ui
|
src/batchprocessing.cpp src/batchprocessing.h src/batchprocessing.ui
|
||||||
chartgraph.h chartgraph.cpp
|
src/chartgraph.h src/chartgraph.cpp
|
||||||
database.cpp database.h
|
src/database.cpp src/database.h
|
||||||
databaseview.cpp databaseview.h
|
src/databaseview.cpp src/databaseview.h
|
||||||
delete.cpp
|
src/delete.cpp
|
||||||
filesystemwidget.cpp filesystemwidget.h
|
src/filemanager.h src/filemanager.cpp src/filemanager.ui
|
||||||
histogram.cpp histogram.h
|
src/filesystemwidget.cpp src/filesystemwidget.h
|
||||||
httpdownloader.h httpdownloader.cpp
|
src/fitskeyword.ui
|
||||||
imageinfo.cpp imageinfo.h
|
src/histogram.cpp src/histogram.h
|
||||||
imageinfodata.cpp imageinfodata.h
|
src/httpdownloader.h src/httpdownloader.cpp
|
||||||
imageringlist.cpp imageringlist.h
|
src/imageinfo.cpp src/imageinfo.h
|
||||||
imagescrollarea.cpp imagescrollarea.h
|
src/imageinfodata.cpp src/imageinfodata.h
|
||||||
imagewidget.h imagewidget.cpp
|
src/imageringlist.cpp src/imageringlist.h
|
||||||
loadimage.h loadimage.cpp
|
src/imagescrollarea.cpp src/imagescrollarea.h
|
||||||
loadrunable.cpp loadrunable.h
|
src/imagewidget.h src/imagewidget.cpp
|
||||||
main.cpp
|
src/loadimage.h src/loadimage.cpp
|
||||||
mainwindow.cpp mainwindow.h
|
src/loadrunable.cpp src/loadrunable.h
|
||||||
markedfiles.cpp markedfiles.h
|
src/main.cpp
|
||||||
mtfparam.h
|
src/mainwindow.cpp src/mainwindow.h
|
||||||
rawimage.cpp rawimage.h
|
src/markedfiles.cpp src/markedfiles.h
|
||||||
rawimage_sse.cpp
|
src/mtfparam.h
|
||||||
scriptengine.cpp scriptengine.h
|
src/rawimage.cpp src/rawimage.h
|
||||||
settingsdialog.cpp settingsdialog.h
|
src/rawimage_sse.cpp
|
||||||
statusbar.cpp statusbar.h
|
src/scriptengine.cpp src/scriptengine.h
|
||||||
stfslider.cpp stfslider.h
|
src/settingsdialog.cpp src/settingsdialog.h
|
||||||
stretchtoolbar.cpp stretchtoolbar.h
|
src/statusbar.cpp src/statusbar.h
|
||||||
tfloat16.h
|
src/stfslider.cpp src/stfslider.h
|
||||||
|
src/stretchtoolbar.cpp src/stretchtoolbar.h
|
||||||
|
src/tfloat16.h
|
||||||
thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h
|
thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ endif()
|
|||||||
qt_add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
|
qt_add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
|
||||||
|
|
||||||
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
||||||
target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR})
|
target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR} "src")
|
||||||
|
|
||||||
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
|
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
|
||||||
if(COLOR_MANAGMENT)
|
if(COLOR_MANAGMENT)
|
||||||
@@ -96,9 +98,9 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
|
|||||||
endif(MXE)
|
endif(MXE)
|
||||||
target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
|
target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
|
||||||
target_sources(tenmon PRIVATE
|
target_sources(tenmon PRIVATE
|
||||||
solver.cpp solver.h
|
src/solver.cpp src/solver.h
|
||||||
platesolving.cpp platesolving.h platesolving.ui
|
src/platesolving.cpp src/platesolving.h src/platesolving.ui
|
||||||
platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui
|
src/platesolvingsettings.cpp src/platesolvingsettings.h src/platesolvingsettings.ui
|
||||||
)
|
)
|
||||||
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
|
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
|
||||||
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
|
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
|
||||||
|
|||||||
@@ -29,3 +29,5 @@ Then to build run standard cmake sequence
|
|||||||
For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver
|
For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver
|
||||||
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
|
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
|
||||||
crash.
|
crash.
|
||||||
|
|
||||||
|
Using OpenNGC database https://github.com/mattiaverga/OpenNGC under CC-BY-SA-4.0 https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|||||||
+1
-1
Submodule libXISF updated: 9a32138f6a...c6581e1122
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 12.699999 12.699999"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="grid.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="9.5144352"
|
||||||
|
inkscape:cx="39.361243"
|
||||||
|
inkscape:cy="25.067174"
|
||||||
|
inkscape:window-width="2510"
|
||||||
|
inkscape:window-height="1371"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="skeletal"
|
||||||
|
id="path-effect636"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
|
||||||
|
copytype="single_stretched"
|
||||||
|
prop_scale="1"
|
||||||
|
scale_y_rel="false"
|
||||||
|
spacing="0"
|
||||||
|
normal_offset="0"
|
||||||
|
tang_offset="0"
|
||||||
|
prop_units="false"
|
||||||
|
vertical_pattern="false"
|
||||||
|
hide_knot="false"
|
||||||
|
fuse_tolerance="0" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="skeletal"
|
||||||
|
id="path-effect632"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
|
||||||
|
copytype="single_stretched"
|
||||||
|
prop_scale="1"
|
||||||
|
scale_y_rel="false"
|
||||||
|
spacing="0"
|
||||||
|
normal_offset="0"
|
||||||
|
tang_offset="0"
|
||||||
|
prop_units="false"
|
||||||
|
vertical_pattern="false"
|
||||||
|
hide_knot="false"
|
||||||
|
fuse_tolerance="0" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="skeletal"
|
||||||
|
id="path-effect628"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
|
||||||
|
copytype="single_stretched"
|
||||||
|
prop_scale="1"
|
||||||
|
scale_y_rel="false"
|
||||||
|
spacing="0"
|
||||||
|
normal_offset="0"
|
||||||
|
tang_offset="0"
|
||||||
|
prop_units="false"
|
||||||
|
vertical_pattern="false"
|
||||||
|
hide_knot="false"
|
||||||
|
fuse_tolerance="0" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="bspline"
|
||||||
|
id="path-effect624"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
weight="33.333333"
|
||||||
|
steps="2"
|
||||||
|
helper_size="0"
|
||||||
|
apply_no_weight="true"
|
||||||
|
apply_with_weight="true"
|
||||||
|
only_selected="false" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect620"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Vrstva 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="M -5,-5 13,13"
|
||||||
|
id="path616"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="path643"
|
||||||
|
cx="-4.9824347"
|
||||||
|
cy="-4.9865055"
|
||||||
|
r="12.973718" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path1665"
|
||||||
|
cx="-4.9600825"
|
||||||
|
cy="-4.9741392"
|
||||||
|
r="17.086035" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path1667"
|
||||||
|
cx="-5.0079365"
|
||||||
|
cy="-5.0034046"
|
||||||
|
r="21.147657" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.467;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="M 14.371451,3.5622727 -4.9904999,-5.0054782 4.2432806,13.903978"
|
||||||
|
id="path1734" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path1736"
|
||||||
|
cx="-5.155458"
|
||||||
|
cy="-5.1256938"
|
||||||
|
r="9.6808758" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
@@ -19,14 +19,16 @@
|
|||||||
<file>../translations/tenmon_pt_BR.qm</file>
|
<file>../translations/tenmon_pt_BR.qm</file>
|
||||||
<file alias="help">../about/help_en</file>
|
<file alias="help">../about/help_en</file>
|
||||||
<file>colormap.png</file>
|
<file>colormap.png</file>
|
||||||
|
<file>ngc.db</file>
|
||||||
|
<file>grid.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" lang="en">
|
<qresource lang="en" prefix="/">
|
||||||
<file alias="help">../about/help_en</file>
|
<file alias="help">../about/help_en</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" lang="sk">
|
<qresource lang="sk" prefix="/">
|
||||||
<file alias="help">../about/help_sk</file>
|
<file alias="help">../about/help_sk</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" lang="fr">
|
<qresource lang="fr" prefix="/">
|
||||||
<file alias="help">../about/help_fr</file>
|
<file alias="help">../about/help_fr</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
+45
-12
@@ -12,10 +12,12 @@ if(files.length == 0)
|
|||||||
throw "";
|
throw "";
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = core.getItem(["UPDATE", "ADD", "REMOVE"], "Do you want update, add or remove record?");
|
let action = core.getItem(["UPDATE", "UPDATE_ADD", "ADD", "REMOVE"], "Do you want update, add or remove record?");
|
||||||
|
|
||||||
let modify = new FITSRecordModify();
|
let modify = new FITSRecordModify();
|
||||||
|
|
||||||
|
let proceed = false;
|
||||||
|
|
||||||
if(action == "UPDATE")
|
if(action == "UPDATE")
|
||||||
{
|
{
|
||||||
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
||||||
@@ -25,28 +27,59 @@ if(action == "UPDATE")
|
|||||||
value = core.getString("Enter new value", value);
|
value = core.getString("Enter new value", value);
|
||||||
else
|
else
|
||||||
value = core.getFloat("Enter new value", value);
|
value = core.getFloat("Enter new value", value);
|
||||||
modify.updateKeyword(keyword, value);
|
|
||||||
|
if(keyword && value)
|
||||||
|
{
|
||||||
|
proceed = true;
|
||||||
|
modify.updateKeyword(keyword, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(action == "UPDATE_ADD")
|
||||||
|
{
|
||||||
|
let keyword = core.getString("Enter keyword to update");
|
||||||
|
let value = core.getString("Enter new value");
|
||||||
|
if(keyword && value)
|
||||||
|
{
|
||||||
|
proceed = true;
|
||||||
|
keyword = keyword.toUpperCase();
|
||||||
|
modify.updateKeyword(keyword, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(action == "ADD")
|
else if(action == "ADD")
|
||||||
{
|
{
|
||||||
let keyword = core.getString("Enter keyword to add");
|
let keyword = core.getString("Enter keyword to add");
|
||||||
let value = core.getString("Enter new value");
|
let value = core.getString("Enter new value");
|
||||||
keyword = keyword.toUpperCase();
|
if(keyword && value)
|
||||||
modify.addKeyword(keyword, value);
|
{
|
||||||
|
proceed = true;
|
||||||
|
keyword = keyword.toUpperCase();
|
||||||
|
modify.addKeyword(keyword, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(action == "REMOVE")
|
else if(action == "REMOVE")
|
||||||
{
|
{
|
||||||
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
||||||
let keyword = core.getItem(keywords, "Select keyword to remove");
|
let keyword = core.getItem(keywords, "Select keyword to remove");
|
||||||
modify.removeKeyword(keyword);
|
if(keyword)
|
||||||
}
|
|
||||||
|
|
||||||
for(file of files)
|
|
||||||
{
|
|
||||||
if(file.suffix() == "fits" || file.suffix() == "fit" || file.suffix() == "xisf")
|
|
||||||
{
|
{
|
||||||
core.log("Modifing " + file.fileName());
|
proceed = true;
|
||||||
file.modifyFITSRecords(modify);
|
modify.removeKeyword(keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(proceed)
|
||||||
|
{
|
||||||
|
for(file of files)
|
||||||
|
{
|
||||||
|
if(file.suffix() == "fits" || file.suffix() == "fit" || file.suffix() == "xisf")
|
||||||
|
{
|
||||||
|
core.log("Modifing " + file.fileName());
|
||||||
|
file.modifyFITSRecords(modify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.log("Canceled");
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
</release>
|
</release>
|
||||||
<release version="20240816" date="2024-08-16">
|
<release version="20240816" date="2024-08-16">
|
||||||
<description>
|
<description>
|
||||||
Fix saving image
|
<p>Fix saving image</p>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="20240616" date="2024-06-16">
|
<release version="20240616" date="2024-06-16">
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ BatchProcessing::BatchProcessing(Database *database, QWidget *parent) : QDialog(
|
|||||||
|
|
||||||
connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles);
|
connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles);
|
||||||
connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir);
|
connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir);
|
||||||
|
connect(_ui->addMarkedButton, &QPushButton::released, this, &BatchProcessing::addMarked);
|
||||||
connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath);
|
connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath);
|
||||||
connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths);
|
connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths);
|
||||||
connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript);
|
connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript);
|
||||||
@@ -164,6 +165,17 @@ void BatchProcessing::addDir()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::addMarked()
|
||||||
|
{
|
||||||
|
QStringList files = _database->getMarkedFiles();
|
||||||
|
for(const QString &file : files)
|
||||||
|
{
|
||||||
|
QFileInfo info(file);
|
||||||
|
if(info.exists() && info.isReadable())
|
||||||
|
_ui->pathsList->addItem(file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void BatchProcessing::removePath()
|
void BatchProcessing::removePath()
|
||||||
{
|
{
|
||||||
for(auto &item : _ui->pathsList->selectedItems())
|
for(auto &item : _ui->pathsList->selectedItems())
|
||||||
@@ -28,6 +28,7 @@ protected:
|
|||||||
public slots:
|
public slots:
|
||||||
void addFiles();
|
void addFiles();
|
||||||
void addDir();
|
void addDir();
|
||||||
|
void addMarked();
|
||||||
void removePath();
|
void removePath();
|
||||||
void removeAllPaths();
|
void removeAllPaths();
|
||||||
void browse();
|
void browse();
|
||||||
@@ -52,6 +52,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addMarkedButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add marked</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="removeButton">
|
<widget class="QPushButton" name="removeButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -16,10 +16,29 @@ bool Database::init(const QLatin1String &connectionName)
|
|||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
|
|
||||||
database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
||||||
|
ngc = QSqlDatabase::addDatabase("QSQLITE", connectionName + "ngc");
|
||||||
|
|
||||||
if(!dir.mkpath("."))
|
if(!dir.mkpath("."))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if(ngc.isValid())
|
||||||
|
{
|
||||||
|
QString ngcDb = dir.absoluteFilePath("ngc.db");
|
||||||
|
if(!QFile::exists(ngcDb))
|
||||||
|
QFile::copy(":/ngc.db", ngcDb);
|
||||||
|
|
||||||
|
ngc.setDatabaseName(ngcDb);
|
||||||
|
if(ngc.open())
|
||||||
|
{
|
||||||
|
m_getNgc = QSqlQuery(ngc);
|
||||||
|
m_getNgc.prepare("SELECT *,IIF(V_Mag IS NULL, B_Mag, V_Mag) AS mag FROM ngc WHERE RA_deg BETWEEN ? AND ? AND DEC_deg BETWEEN ? AND ?");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "Could not open NGC database";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(database.isValid())
|
if(database.isValid())
|
||||||
{
|
{
|
||||||
database.setDatabaseName(dir.absoluteFilePath("database2.db"));
|
database.setDatabaseName(dir.absoluteFilePath("database2.db"));
|
||||||
@@ -162,7 +181,7 @@ int Database::checkVersion(QSqlDatabase &db)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QStringList nameFilters = {"*.fit", "*.fits", "*.fz", "*.xisf"};
|
static QStringList nameFilters = {"*.fit", "*.fits", "*.fz", "*.fts", "*.xisf"};
|
||||||
|
|
||||||
static int countFiles(const QDir &dir, QStringList &scannedDirs)
|
static int countFiles(const QDir &dir, QStringList &scannedDirs)
|
||||||
{
|
{
|
||||||
@@ -182,7 +201,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
|||||||
QStringList scannedDirs;
|
QStringList scannedDirs;
|
||||||
int count = countFiles(dir, scannedDirs);
|
int count = countFiles(dir, scannedDirs);
|
||||||
progress->setMaximum(count);
|
progress->setMaximum(count);
|
||||||
QSqlDatabase database = QSqlDatabase::database();
|
|
||||||
database.transaction();
|
database.transaction();
|
||||||
|
|
||||||
scannedDirs.clear();
|
scannedDirs.clear();
|
||||||
@@ -200,7 +218,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
|||||||
void Database::reindex(QProgressDialog *progress)
|
void Database::reindex(QProgressDialog *progress)
|
||||||
{
|
{
|
||||||
QVariantList deleteids;
|
QVariantList deleteids;
|
||||||
QSqlDatabase database = QSqlDatabase::database();
|
|
||||||
database.transaction();
|
database.transaction();
|
||||||
QSqlQuery size("SELECT COUNT(*) FROM fits_files", database);
|
QSqlQuery size("SELECT COUNT(*) FROM fits_files", database);
|
||||||
size.next();
|
size.next();
|
||||||
@@ -239,6 +256,57 @@ QStringList Database::getFitsKeywords()
|
|||||||
return keywords;
|
return keywords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<SkyObject> Database::getObjects(double minRa, double maxRa, double minDec, double maxDec)
|
||||||
|
{
|
||||||
|
QVector<SkyObject> objects;
|
||||||
|
if(!ngc.isOpen())return objects;
|
||||||
|
|
||||||
|
m_getNgc.bindValue(0, minRa);
|
||||||
|
m_getNgc.bindValue(1, maxRa);
|
||||||
|
m_getNgc.bindValue(2, minDec);
|
||||||
|
m_getNgc.bindValue(3, maxDec);
|
||||||
|
|
||||||
|
if(m_getNgc.exec())
|
||||||
|
{
|
||||||
|
while(m_getNgc.next())
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString name2;
|
||||||
|
QString m = m_getNgc.value("M").toString();
|
||||||
|
QString ic = m_getNgc.value("IC").toString();
|
||||||
|
if(!m.isEmpty())
|
||||||
|
{
|
||||||
|
name = "M" + m;
|
||||||
|
m.clear();
|
||||||
|
}
|
||||||
|
else if(!ic.isEmpty())
|
||||||
|
{
|
||||||
|
name = "IC" + ic;
|
||||||
|
ic.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = m_getNgc.value("Name").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ic.isEmpty())name2 += "IC" + ic + " ";
|
||||||
|
name2 += m_getNgc.value("Common names").toString();
|
||||||
|
|
||||||
|
objects.append({
|
||||||
|
name,
|
||||||
|
name2,
|
||||||
|
{m_getNgc.value("RA_deg").toDouble(), m_getNgc.value("DEC_deg").toDouble()},
|
||||||
|
m_getNgc.value("MajAx").toDouble(),
|
||||||
|
m_getNgc.value("MinAx").toDouble(),
|
||||||
|
m_getNgc.value("PosAng").toDouble(),
|
||||||
|
m_getNgc.value("mag").isNull() ? NAN : m_getNgc.value("mag").toDouble(),
|
||||||
|
{0, 0},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs)
|
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs)
|
||||||
{
|
{
|
||||||
if(scannedDirs.contains(dir.canonicalPath()))return true;
|
if(scannedDirs.contains(dir.canonicalPath()))return true;
|
||||||
@@ -279,10 +347,10 @@ bool Database::indexFile(const QFileInfo &file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok;
|
bool ok = false;
|
||||||
if(filePath.endsWith(".xisf", Qt::CaseInsensitive))
|
if(isXISF(file.suffix()))
|
||||||
ok = readXISFHeader(filePath, info);
|
ok = readXISFHeader(filePath, info);
|
||||||
else
|
else if(isFITS(file.suffix()))
|
||||||
ok = readFITSHeader(filePath, info);
|
ok = readFITSHeader(filePath, info);
|
||||||
|
|
||||||
qlonglong last_id = -1;
|
qlonglong last_id = -1;
|
||||||
@@ -323,6 +391,7 @@ bool Database::indexFile(const QFileInfo &file)
|
|||||||
QVariantList file_id, keys, values, comments;
|
QVariantList file_id, keys, values, comments;
|
||||||
for(const auto &record : info.fitsHeader)
|
for(const auto &record : info.fitsHeader)
|
||||||
{
|
{
|
||||||
|
if(record.xisf && record.key.startsWith("PCL:"))continue;
|
||||||
file_id << last_id;
|
file_id << last_id;
|
||||||
keys << QString(record.key);
|
keys << QString(record.key);
|
||||||
values << record.value.toString();
|
values << record.value.toString();
|
||||||
@@ -6,11 +6,13 @@
|
|||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include "imageinfodata.h"
|
||||||
|
|
||||||
class Database : public QObject
|
class Database : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QSqlDatabase database;
|
QSqlDatabase database;
|
||||||
|
QSqlDatabase ngc;
|
||||||
QSqlQuery m_markQuery;
|
QSqlQuery m_markQuery;
|
||||||
QSqlQuery m_unmarkQuery;
|
QSqlQuery m_unmarkQuery;
|
||||||
QSqlQuery m_isMarkedQuery;
|
QSqlQuery m_isMarkedQuery;
|
||||||
@@ -22,6 +24,8 @@ class Database : public QObject
|
|||||||
QSqlQuery m_headerKeywords;
|
QSqlQuery m_headerKeywords;
|
||||||
QSqlQuery m_deleteFile;
|
QSqlQuery m_deleteFile;
|
||||||
|
|
||||||
|
QSqlQuery m_getNgc;
|
||||||
|
|
||||||
int m_progress;
|
int m_progress;
|
||||||
public:
|
public:
|
||||||
explicit Database(QObject *parent = 0);
|
explicit Database(QObject *parent = 0);
|
||||||
@@ -37,6 +41,7 @@ public:
|
|||||||
void indexDir(const QDir &dir, QProgressDialog *progress);
|
void indexDir(const QDir &dir, QProgressDialog *progress);
|
||||||
void reindex(QProgressDialog *progress);
|
void reindex(QProgressDialog *progress);
|
||||||
QStringList getFitsKeywords();
|
QStringList getFitsKeywords();
|
||||||
|
QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec);
|
||||||
protected:
|
protected:
|
||||||
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
|
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
|
||||||
bool indexFile(const QFileInfo &file);
|
bool indexFile(const QFileInfo &file);
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
#include "filemanager.h"
|
||||||
|
#include "ui_filemanager.h"
|
||||||
|
#include "ui_fitskeyword.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include "loadimage.h"
|
||||||
|
|
||||||
|
PathTabBar::PathTabBar(const QStringList &tabs) :
|
||||||
|
_tabs(tabs)
|
||||||
|
{
|
||||||
|
setTabsClosable(true);
|
||||||
|
setExpanding(false);
|
||||||
|
|
||||||
|
for(auto &t : _tabs)
|
||||||
|
{
|
||||||
|
QDir dir(t);
|
||||||
|
int i = addTab(tabName(t));
|
||||||
|
setTabToolTip(i, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(this, &QTabBar::currentChanged, [this](int index){
|
||||||
|
QString path = _tabs.at(index);
|
||||||
|
emit pathChanged(path);
|
||||||
|
});
|
||||||
|
connect(this, &QTabBar::tabCloseRequested, [this](int index){
|
||||||
|
if(_tabs.size() >= 2)
|
||||||
|
{
|
||||||
|
_tabs.remove(index);
|
||||||
|
removeTab(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(this, &QTabBar::currentChanged, [this](int index){
|
||||||
|
emit tabChanged(_tabs[index]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *PathTabBar::createLayout()
|
||||||
|
{
|
||||||
|
QHBoxLayout *hlayout = new QHBoxLayout();
|
||||||
|
|
||||||
|
hlayout->addWidget(this);
|
||||||
|
hlayout->addStretch(2);
|
||||||
|
QPushButton *addButton = new QPushButton("+");
|
||||||
|
connect(addButton, &QPushButton::clicked, [this](){
|
||||||
|
QString path = _tabs[currentIndex()];
|
||||||
|
_tabs.append(path);
|
||||||
|
int i = addTab(tabName(path));
|
||||||
|
setTabToolTip(i, path);
|
||||||
|
});
|
||||||
|
hlayout->addWidget(addButton);
|
||||||
|
return hlayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &PathTabBar::tabPaths() const
|
||||||
|
{
|
||||||
|
return _tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PathTabBar::currentTabPath() const
|
||||||
|
{
|
||||||
|
int index = std::clamp(currentIndex(), 0, (int)_tabs.size());
|
||||||
|
return _tabs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathTabBar::pathChanged(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
int index = currentIndex();
|
||||||
|
setTabText(index, tabName(path));
|
||||||
|
setTabToolTip(index, path);
|
||||||
|
_tabs[index] = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PathTabBar::tabName(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
if(dir.dirName().isEmpty())
|
||||||
|
return path;
|
||||||
|
else
|
||||||
|
return dir.dirName();
|
||||||
|
}
|
||||||
|
|
||||||
|
FITSSelection::FITSSelection(const QStringList &keywords, QWidget *parent) : QDialog(parent)
|
||||||
|
,ui(new Ui::FITSKeyword)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
connect(ui->addButton, &QPushButton::clicked, [this](){
|
||||||
|
auto item = ui->keywordList->findItems(ui->keyword->text(), Qt::MatchFixedString | Qt::MatchCaseSensitive);
|
||||||
|
if(item.size())return;
|
||||||
|
ui->keywordList->addItem(ui->keyword->text());
|
||||||
|
});
|
||||||
|
connect(ui->removeButton, &QPushButton::clicked, [this](){
|
||||||
|
auto items = ui->keywordList->selectedItems();
|
||||||
|
for(auto item : items)
|
||||||
|
delete item;
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->keywordList->addItems(keywords);
|
||||||
|
}
|
||||||
|
|
||||||
|
FITSSelection::~FITSSelection()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FITSSelection::FITSKeywords() const
|
||||||
|
{
|
||||||
|
QStringList keywords;
|
||||||
|
for(int i = 0; i < ui->keywordList->count(); i++)
|
||||||
|
keywords.append(ui->keywordList->item(i)->text());
|
||||||
|
|
||||||
|
return keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMainWindow(parent)
|
||||||
|
,ui(new Ui::FileManager)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
|
||||||
|
QString picturesPath;
|
||||||
|
if(standardLocations.size())
|
||||||
|
picturesPath = standardLocations.first();
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QStringList leftTabs = settings.value("filemanager/leftTabPaths", picturesPath).toStringList();
|
||||||
|
QStringList rightTabs = settings.value("filemanager/rightTabPaths", picturesPath).toStringList();
|
||||||
|
if(leftTabs.empty())leftTabs.append(picturesPath);
|
||||||
|
if(rightTabs.empty())rightTabs.append(picturesPath);
|
||||||
|
|
||||||
|
ui->leftTab->setOpenFilter(openFilter);
|
||||||
|
ui->rightTab->setOpenFilter(openFilter);
|
||||||
|
|
||||||
|
_rightTabBar = new PathTabBar(rightTabs);
|
||||||
|
ui->rightLayout->insertLayout(0, _rightTabBar->createLayout());
|
||||||
|
connect(_rightTabBar, &PathTabBar::tabChanged, ui->rightTab, &DirView::setDir);
|
||||||
|
|
||||||
|
_leftTabBar = new PathTabBar(leftTabs);
|
||||||
|
ui->leftLayout->insertLayout(0, _leftTabBar->createLayout());
|
||||||
|
connect(_leftTabBar, &PathTabBar::tabChanged, ui->leftTab, &DirView::setDir);
|
||||||
|
|
||||||
|
connect(ui->leftTab, &DirView::dirChanged, ui->leftPath, &QLineEdit::setText);
|
||||||
|
connect(ui->leftTab, &DirView::dirChanged, _leftTabBar, &PathTabBar::pathChanged);
|
||||||
|
connect(ui->rightTab, &DirView::dirChanged, ui->rightPath, &QLineEdit::setText);
|
||||||
|
connect(ui->rightTab, &DirView::dirChanged, _rightTabBar, &PathTabBar::pathChanged);
|
||||||
|
connect(ui->leftTab, &DirView::openFile, this, &FileManager::openFile);
|
||||||
|
connect(ui->rightTab, &DirView::openFile, this, &FileManager::openFile);
|
||||||
|
connect(ui->actionLoad_FITS_keywordsLeft, &QAction::toggled, ui->leftTab, &DirView::loadFitsKeywords);
|
||||||
|
connect(ui->actionLoad_FITS_keywordsRight, &QAction::toggled, ui->rightTab, &DirView::loadFitsKeywords);
|
||||||
|
|
||||||
|
ui->leftTab->setDir(_leftTabBar->currentTabPath());
|
||||||
|
ui->leftTab->setFITSKeywords(settings.value("filemanager/leftFitsKeywords", QStringList("OBJECT")).toStringList());
|
||||||
|
ui->leftTab->header()->restoreState(settings.value("filemanager/leftTabHeader").toByteArray());
|
||||||
|
ui->rightTab->setDir(_rightTabBar->currentTabPath());
|
||||||
|
ui->rightTab->setFITSKeywords(settings.value("filemanager/rightFitsKeywords", QStringList("OBJECT")).toStringList());
|
||||||
|
ui->rightTab->header()->restoreState(settings.value("filemanager/rightTabHeader").toByteArray());
|
||||||
|
|
||||||
|
ui->actionLoad_FITS_keywordsLeft->setChecked(settings.value("filemanager/leftLoadFitsKeywords", true).toBool());
|
||||||
|
ui->actionLoad_FITS_keywordsRight->setChecked(settings.value("filemanager/rightLoadFitsKeywords", true).toBool());
|
||||||
|
restoreGeometry(settings.value("filemanager/geometry").toByteArray());
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
connect(ui->actionSelect_columnsLeft, &QAction::triggered, this, &FileManager::selectFITSKeywords);
|
||||||
|
connect(ui->actionSelect_columnsRight, &QAction::triggered, this, &FileManager::selectFITSKeywords);
|
||||||
|
connect(ui->leftPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
|
||||||
|
connect(ui->rightPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
|
||||||
|
|
||||||
|
QFileInfoList drives = QDir::drives();
|
||||||
|
for(auto &drive : drives)
|
||||||
|
{
|
||||||
|
QString path = drive.absoluteFilePath();
|
||||||
|
ui->menuLeft_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->leftTab->setDir(path); });
|
||||||
|
ui->menuRight_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->rightTab->setDir(path); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileManager::~FileManager()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("filemanager/leftFitsKeywords", ui->leftTab->FITSKeywords());
|
||||||
|
settings.setValue("filemanager/leftTabPaths", _leftTabBar->tabPaths());
|
||||||
|
settings.setValue("filemanager/leftTabHeader", ui->leftTab->header()->saveState());
|
||||||
|
settings.setValue("filemanager/rightFitsKeywords", ui->rightTab->FITSKeywords());
|
||||||
|
settings.setValue("filemanager/rightTabPaths", _rightTabBar->tabPaths());
|
||||||
|
settings.setValue("filemanager/rightTabHeader", ui->rightTab->header()->saveState());
|
||||||
|
settings.setValue("filemanager/leftLoadFitsKeywords", ui->actionLoad_FITS_keywordsLeft->isChecked());
|
||||||
|
settings.setValue("filemanager/rightLoadFitsKeywords", ui->actionLoad_FITS_keywordsRight->isChecked());
|
||||||
|
settings.setValue("filemanager/geometry", saveGeometry());
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileManager::selectFITSKeywords()
|
||||||
|
{
|
||||||
|
QStringList columns;
|
||||||
|
if(sender() == ui->actionSelect_columnsLeft)
|
||||||
|
columns = ui->leftTab->FITSKeywords();
|
||||||
|
if(sender() == ui->actionSelect_columnsRight)
|
||||||
|
columns = ui->rightTab->FITSKeywords();
|
||||||
|
|
||||||
|
FITSSelection selection(columns, this);
|
||||||
|
int ret = selection.exec();
|
||||||
|
|
||||||
|
if(ret == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
if(sender() == ui->actionSelect_columnsLeft)
|
||||||
|
ui->leftTab->setFITSKeywords(selection.FITSKeywords());
|
||||||
|
|
||||||
|
if(sender() == ui->actionSelect_columnsRight)
|
||||||
|
ui->rightTab->setFITSKeywords(selection.FITSKeywords());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileManager::pathEdited()
|
||||||
|
{
|
||||||
|
if(sender() == ui->leftPath)
|
||||||
|
{
|
||||||
|
QDir dir(ui->leftPath->text());
|
||||||
|
if(dir.exists())
|
||||||
|
ui->leftTab->setDir(dir.absolutePath());
|
||||||
|
}
|
||||||
|
if(sender() == ui->rightPath)
|
||||||
|
{
|
||||||
|
QDir dir(ui->rightPath->text());
|
||||||
|
if(dir.exists())
|
||||||
|
ui->rightTab->setDir(dir.absolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCache<QString, ImageInfoData>* DirFileSystemModel::getCacheInstance()
|
||||||
|
{
|
||||||
|
static bool init = true;
|
||||||
|
static QCache<QString, ImageInfoData> cache;
|
||||||
|
if(!init)
|
||||||
|
{
|
||||||
|
cache.setMaxCost(10000);
|
||||||
|
init = false;
|
||||||
|
}
|
||||||
|
return &cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirFileSystemModel::DirFileSystemModel(QObject *parent) : QFileSystemModel(parent)
|
||||||
|
{
|
||||||
|
_cache = getCacheInstance();
|
||||||
|
setFilter(QDir::AllEntries | QDir::NoDot);
|
||||||
|
_fitsKeywords = {"OBJECT"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirFileSystemModel::setDir(const QString &path)
|
||||||
|
{
|
||||||
|
_dir = index(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DirFileSystemModel::dir() const
|
||||||
|
{
|
||||||
|
return fileInfo(_dir).canonicalFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirFileSystemModel::setFITSKeywords(const QStringList &keywords)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
_fitsKeywords = keywords;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &DirFileSystemModel::FITSKeywords() const
|
||||||
|
{
|
||||||
|
return _fitsKeywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags DirFileSystemModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
return QFileSystemModel::flags(index) & ~Qt::ItemIsEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirFileSystemModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return QFileSystemModel::columnCount(parent) + _fitsKeywords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DirFileSystemModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if(index.column() >= QFileSystemModel::columnCount() && role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
|
QFileInfo info = fileInfo(index);
|
||||||
|
QString path = info.canonicalFilePath();
|
||||||
|
QString suffix = info.suffix();
|
||||||
|
ImageInfoData *infoData = nullptr;
|
||||||
|
if(_cache->contains(path))
|
||||||
|
{
|
||||||
|
infoData = _cache->object(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(_loadFitsKeywords)
|
||||||
|
{
|
||||||
|
infoData = new ImageInfoData;
|
||||||
|
if(isFITS(suffix))
|
||||||
|
readFITSHeader(path, *infoData);
|
||||||
|
else if(isXISF(suffix))
|
||||||
|
readXISFHeader(path, *infoData);
|
||||||
|
_cache->insert(path, infoData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(infoData)
|
||||||
|
{
|
||||||
|
int column = index.column() - QFileSystemModel::columnCount();
|
||||||
|
if(column < _fitsKeywords.size())
|
||||||
|
{
|
||||||
|
const QString &key = _fitsKeywords.at(column);
|
||||||
|
for(auto &record : infoData->fitsHeader)
|
||||||
|
if(record.key == key)
|
||||||
|
return record.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return QFileSystemModel::data(index, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DirFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if(orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= QFileSystemModel::columnCount())
|
||||||
|
return _fitsKeywords.at(section - QFileSystemModel::columnCount());
|
||||||
|
|
||||||
|
return QFileSystemModel::headerData(section, orientation, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirFileSystemModel::hasChildren(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if(parent.parent() == _dir)return false;
|
||||||
|
|
||||||
|
return QFileSystemModel::hasChildren(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirFileSystemModel::loadFitsKeywords(bool enable)
|
||||||
|
{
|
||||||
|
_loadFitsKeywords = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirView::DirView(QWidget *parent) : QTreeView(parent)
|
||||||
|
{
|
||||||
|
_dirFileSystemModel = new DirFileSystemModel(this);
|
||||||
|
_dirFileSystemModel->setRootPath(QDir::drives().first().path());
|
||||||
|
_dirFileSystemModel->setReadOnly(false);
|
||||||
|
setDragEnabled(true);
|
||||||
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
setModel(_dirFileSystemModel);
|
||||||
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
|
connect(this, &QTreeView::doubleClicked, [this](const QModelIndex &index){
|
||||||
|
QFileInfo info = _dirFileSystemModel->fileInfo(index);
|
||||||
|
if(_dirFileSystemModel->isDir(index))
|
||||||
|
{
|
||||||
|
setDir(info.canonicalFilePath());
|
||||||
|
}
|
||||||
|
else if(info.isFile())
|
||||||
|
{
|
||||||
|
if(_openFilter.contains(info.suffix().toLower()))
|
||||||
|
emit openFile(info.absoluteFilePath());
|
||||||
|
else
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(header(), &QHeaderView::customContextMenuRequested, this, &DirView::headerContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirView::setDir(const QString &path)
|
||||||
|
{
|
||||||
|
QString oldPath = _dirFileSystemModel->dir();
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
const int ROOT_LEN = 3;
|
||||||
|
#else
|
||||||
|
const int ROOT_LEN = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(oldPath.left(ROOT_LEN) != path.left(ROOT_LEN))
|
||||||
|
_dirFileSystemModel->setRootPath(path.left(ROOT_LEN));
|
||||||
|
|
||||||
|
_dirFileSystemModel->setDir(path);
|
||||||
|
setRootIndex(_dirFileSystemModel->index(path, 0));
|
||||||
|
clearSelection();
|
||||||
|
if(oldPath != path)emit dirChanged(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DirView::dir() const
|
||||||
|
{
|
||||||
|
return _dirFileSystemModel->dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirView::setOpenFilter(const QSet<QString> &openFilter)
|
||||||
|
{
|
||||||
|
_openFilter = openFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirView::setFITSKeywords(const QStringList &keywords)
|
||||||
|
{
|
||||||
|
QString d = dir();
|
||||||
|
_dirFileSystemModel->setFITSKeywords(keywords);
|
||||||
|
setDir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &DirView::FITSKeywords() const
|
||||||
|
{
|
||||||
|
return _dirFileSystemModel->FITSKeywords();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirView::headerContextMenu(const QPoint &pos)
|
||||||
|
{
|
||||||
|
QHeaderView *head = header();
|
||||||
|
QMenu menu;
|
||||||
|
int count = head->count();
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
QAction *a = menu.addAction(head->model()->headerData(i, Qt::Horizontal).toString());
|
||||||
|
a->setCheckable(true);
|
||||||
|
a->setChecked(!head->isSectionHidden(i));
|
||||||
|
a->setData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction *a = menu.exec(mapToGlobal(pos));
|
||||||
|
if(a)
|
||||||
|
{
|
||||||
|
if(a->isChecked())head->showSection(a->data().toInt());
|
||||||
|
else head->hideSection(a->data().toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirView::loadFitsKeywords(bool enable)
|
||||||
|
{
|
||||||
|
_dirFileSystemModel->loadFitsKeywords(enable);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#ifndef FILEMANAGER_H
|
||||||
|
#define FILEMANAGER_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QCache>
|
||||||
|
#include <QFileSystemModel>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QTabBar>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include "imageinfodata.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class FileManager;
|
||||||
|
class FITSKeyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PathTabBar : public QTabBar
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PathTabBar(const QStringList &tabs);
|
||||||
|
QHBoxLayout* createLayout();
|
||||||
|
const QStringList& tabPaths() const;
|
||||||
|
QString currentTabPath() const;
|
||||||
|
public slots:
|
||||||
|
void pathChanged(const QString &path);
|
||||||
|
signals:
|
||||||
|
void tabChanged(const QString &path);
|
||||||
|
private:
|
||||||
|
QStringList _tabs;
|
||||||
|
QString tabName(const QString &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FITSSelection : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FITSSelection(const QStringList &keywords, QWidget *parent = nullptr);
|
||||||
|
~FITSSelection();
|
||||||
|
QStringList FITSKeywords() const;
|
||||||
|
private:
|
||||||
|
Ui::FITSKeyword *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileManager : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FileManager(const QSet<QString> &openFilter, QWidget *parent = nullptr);
|
||||||
|
~FileManager();
|
||||||
|
public slots:
|
||||||
|
void selectFITSKeywords();
|
||||||
|
void pathEdited();
|
||||||
|
signals:
|
||||||
|
void openFile(const QString &path);
|
||||||
|
private:
|
||||||
|
Ui::FileManager *ui;
|
||||||
|
PathTabBar *_leftTabBar;
|
||||||
|
PathTabBar *_rightTabBar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirFileSystemModel : public QFileSystemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
mutable QCache<QString, ImageInfoData> *_cache = nullptr;
|
||||||
|
static QCache<QString, ImageInfoData>* getCacheInstance();
|
||||||
|
QModelIndex _dir;
|
||||||
|
QStringList _fitsKeywords;
|
||||||
|
bool _loadFitsKeywords = true;
|
||||||
|
public:
|
||||||
|
explicit DirFileSystemModel(QObject *parent = nullptr);
|
||||||
|
void setDir(const QString &path);
|
||||||
|
QString dir() const;
|
||||||
|
void setFITSKeywords(const QStringList &keywords);
|
||||||
|
const QStringList& FITSKeywords() const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
bool hasChildren(const QModelIndex &parent) const override;
|
||||||
|
public slots:
|
||||||
|
void loadFitsKeywords(bool enable);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirView : public QTreeView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
DirFileSystemModel *_dirFileSystemModel = nullptr;
|
||||||
|
QSet<QString> _openFilter;
|
||||||
|
public:
|
||||||
|
explicit DirView(QWidget *parent = nullptr);
|
||||||
|
void setDir(const QString &path);
|
||||||
|
QString dir() const;
|
||||||
|
void setOpenFilter(const QSet<QString> &openFilter);
|
||||||
|
void setFITSKeywords(const QStringList &keywords);
|
||||||
|
const QStringList& FITSKeywords() const;
|
||||||
|
public slots:
|
||||||
|
void headerContextMenu(const QPoint &pos);
|
||||||
|
void loadFitsKeywords(bool enable);
|
||||||
|
signals:
|
||||||
|
void dirChanged(const QString &path);
|
||||||
|
void openFile(const QString &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEMANAGER_H
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FileManager</class>
|
||||||
|
<widget class="QMainWindow" name="FileManager">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1282</width>
|
||||||
|
<height>858</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>File Manager</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="leftLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="leftPath"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="DirView" name="leftTab"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="rightLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="rightPath"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="DirView" name="rightTab"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1282</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuLeft_Tab">
|
||||||
|
<property name="title">
|
||||||
|
<string>Left Tab</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionLoad_FITS_keywordsLeft"/>
|
||||||
|
<addaction name="actionSelect_columnsLeft"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuRight_Tab">
|
||||||
|
<property name="title">
|
||||||
|
<string>Right Tab</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionLoad_FITS_keywordsRight"/>
|
||||||
|
<addaction name="actionSelect_columnsRight"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuLeft_Tab"/>
|
||||||
|
<addaction name="menuRight_Tab"/>
|
||||||
|
</widget>
|
||||||
|
<action name="actionLoad_FITS_keywordsLeft">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Load FITS keywords</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionLoad_FITS_keywordsRight">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Load FITS keywords</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSelect_columnsLeft">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select columns</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSelect_columnsRight">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select columns</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>DirView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>filemanager.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
|
#include <QMimeDatabase>
|
||||||
|
|
||||||
FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent)
|
FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent)
|
||||||
, m_model(model)
|
, m_model(model)
|
||||||
@@ -117,6 +118,7 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
{
|
{
|
||||||
setRootIndex(index);
|
setRootIndex(index);
|
||||||
m_rootDir = m_fileSystemModel->filePath(index);
|
m_rootDir = m_fileSystemModel->filePath(index);
|
||||||
|
m_fileSystemModel->setRootPath(m_rootDir);
|
||||||
}
|
}
|
||||||
else if(a == resetRoot)
|
else if(a == resetRoot)
|
||||||
{
|
{
|
||||||
@@ -127,6 +129,7 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
{
|
{
|
||||||
setRootIndex(rootIndex().parent());
|
setRootIndex(rootIndex().parent());
|
||||||
m_rootDir = m_fileSystemModel->filePath(rootIndex().parent());
|
m_rootDir = m_fileSystemModel->filePath(rootIndex().parent());
|
||||||
|
m_fileSystemModel->setRootPath(m_rootDir);
|
||||||
}
|
}
|
||||||
else if(a == copy)
|
else if(a == copy)
|
||||||
{
|
{
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
|
#include <QIdentityProxyModel>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FITSKeyword</class>
|
||||||
|
<widget class="QDialog" name="FITSKeyword">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>432</width>
|
||||||
|
<height>443</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>FITS Columns</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="keywordList">
|
||||||
|
<property name="dragEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode">
|
||||||
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::MoveAction</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="keyword"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>FITSKeyword</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>215</x>
|
||||||
|
<y>420</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>215</x>
|
||||||
|
<y>221</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>FITSKeyword</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>215</x>
|
||||||
|
<y>420</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>215</x>
|
||||||
|
<y>221</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <wcslib/wcshdr.h>
|
#include <wcslib/wcshdr.h>
|
||||||
#include <wcslib/wcsfix.h>
|
#include <wcslib/wcsfix.h>
|
||||||
|
#include "database.h"
|
||||||
#include "libxisf.h"
|
#include "libxisf.h"
|
||||||
|
|
||||||
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
|
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
|
||||||
@@ -85,6 +86,7 @@ WCSDataT::WCSDataT(int width, int height, const QVector<FITSRecord> &header) :
|
|||||||
for(const FITSRecord &record : header)
|
for(const FITSRecord &record : header)
|
||||||
{
|
{
|
||||||
if(record.key.startsWith("PV"))continue;
|
if(record.key.startsWith("PV"))continue;
|
||||||
|
if(record.xisf)continue;
|
||||||
|
|
||||||
QByteArray rec;
|
QByteArray rec;
|
||||||
rec.append(record.key.leftJustified(8, ' '));
|
rec.append(record.key.leftJustified(8, ' '));
|
||||||
@@ -153,9 +155,9 @@ bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const
|
bool WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const
|
||||||
{
|
{
|
||||||
if(wcs == nullptr)return;
|
if(wcs == nullptr)return false;
|
||||||
|
|
||||||
minRa = 1000;
|
minRa = 1000;
|
||||||
maxRa = -1000;
|
maxRa = -1000;
|
||||||
@@ -208,6 +210,7 @@ void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, dou
|
|||||||
if(s.contains(scp))
|
if(s.contains(scp))
|
||||||
minDec = -90;
|
minDec = -90;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double hav(double x)
|
double hav(double x)
|
||||||
@@ -266,6 +269,16 @@ QString SkyPoint::toString() const
|
|||||||
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
|
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SkyPoint::RAString() const
|
||||||
|
{
|
||||||
|
return toHMS(ra / 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SkyPoint::DECString() const
|
||||||
|
{
|
||||||
|
return toDMS(dec);
|
||||||
|
}
|
||||||
|
|
||||||
double SkyPoint::fromHMS(const QString &hms)
|
double SkyPoint::fromHMS(const QString &hms)
|
||||||
{
|
{
|
||||||
double deg = fromDMS(hms);
|
double deg = fromDMS(hms);
|
||||||
@@ -308,11 +321,21 @@ QString SkyPoint::toHMS(double decHour)
|
|||||||
|
|
||||||
QString SkyPoint::toDMS(double deg)
|
QString SkyPoint::toDMS(double deg)
|
||||||
{
|
{
|
||||||
|
int sign = deg < 0.0 ? -1 : 1;
|
||||||
|
deg *= sign;
|
||||||
double d,m,s,md;
|
double d,m,s,md;
|
||||||
md = std::modf(deg, &d) * 60.0;
|
md = std::modf(deg, &d) * 60.0;
|
||||||
s = std::modf(md, &m) * 60.0;
|
s = std::modf(md, &m) * 60.0;
|
||||||
|
|
||||||
return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
|
return QString("%1˚ %2' %3\"").arg((int)d * sign, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyPoint SkyPoint::operator+(const SkyPoint &p)
|
||||||
|
{
|
||||||
|
SkyPoint ret;
|
||||||
|
ret.ra = ra + p.ra;
|
||||||
|
ret.dec = dec + p.dec;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkyPointScale ImageInfoData::getCenterRaDec() const
|
SkyPointScale ImageInfoData::getCenterRaDec() const
|
||||||
@@ -403,3 +426,148 @@ SkyPointScale ImageInfoData::getCenterRaDec() const
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkyPoint greatCircle(SkyPoint &p, double dist, double azm)
|
||||||
|
{
|
||||||
|
dist = dist * M_PI / 180;
|
||||||
|
azm = azm * M_PI / 180;
|
||||||
|
double dec0 = p.DEC() * M_PI / 180;
|
||||||
|
double ra0 = p.RA() * M_PI / 180;
|
||||||
|
double dec1 = std::asin(std::sin(dec0) * std::cos(dist) + std::cos(dec0) * std::sin(dist) * std::cos(azm));
|
||||||
|
double ra1 = ra0 + std::atan2(std::sin(azm) * std::sin(dist) * std::cos(dec0), std::cos(dist) - std::sin(dec0) * std::sin(dec1));
|
||||||
|
return SkyPoint(ra1 * 180 / M_PI, dec1 * 180 / M_PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyGrid WCSDataT::prepareGrid(uint32_t w, uint32_t h, Database *database)
|
||||||
|
{
|
||||||
|
SkyGrid skyGrid;
|
||||||
|
if(!wcs)return skyGrid;
|
||||||
|
|
||||||
|
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
|
||||||
|
calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
|
||||||
|
|
||||||
|
QPointF a,b;
|
||||||
|
worldToPixel(SkyPoint(crVal1, crVal2), a);
|
||||||
|
worldToPixel(SkyPoint(crVal1 + 0.01, crVal2), b);
|
||||||
|
skyGrid.rot_ang = std::atan2(b.y() - a.y(), b.x() - a.x()) / M_PI * -180.0;
|
||||||
|
|
||||||
|
if(database)
|
||||||
|
{
|
||||||
|
skyGrid.objects = database->getObjects(minRa, maxRa, minDec, maxDec);
|
||||||
|
for(auto &object : skyGrid.objects)
|
||||||
|
{
|
||||||
|
QPointF p;
|
||||||
|
if(worldToPixel(object.skyPoint, p))
|
||||||
|
object.pixel = p;
|
||||||
|
|
||||||
|
QPointF majax;
|
||||||
|
worldToPixel(greatCircle(object.skyPoint, (object.min_ax + object.maj_ax) / 120.0, object.pos_ang), majax);
|
||||||
|
majax -= p;
|
||||||
|
object.maj_ax = std::sqrt(QPointF::dotProduct(majax, majax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double raStep = 15;
|
||||||
|
double decStep = 15;
|
||||||
|
double raRange = maxRa - minRa;
|
||||||
|
double decRange = maxDec - minDec;
|
||||||
|
const QVector<double> raSteps = {15, 5, 2.5, 1.25, 0.25, 20/240.0, 10/240.0, 5/240.0, 1/240.0};
|
||||||
|
const QVector<double> decSteps = {20, 10, 5, 2, 1, 20/60.0, 10/60.0, 5/60.0, 2/60.0, 1/60.0, 20/3600.0, 10/3600.0, 5/3600.0, 2/3600.0, 1/3600.0};
|
||||||
|
|
||||||
|
for(double ra : raSteps)
|
||||||
|
{
|
||||||
|
if(ra * 5 <= raRange)
|
||||||
|
{
|
||||||
|
raStep = ra;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(double dec : decSteps)
|
||||||
|
{
|
||||||
|
if(dec * 5 <= decRange)
|
||||||
|
{
|
||||||
|
decStep = dec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minRa -= std::fmod(minRa, raStep);
|
||||||
|
minDec -= std::fmod(minDec, decStep);
|
||||||
|
if(minRa < 0)minRa -= raStep;
|
||||||
|
if(minDec < 0)minDec -= decStep;
|
||||||
|
|
||||||
|
QRectF clip(0, 0, w, h);
|
||||||
|
const double step = 0.2;
|
||||||
|
|
||||||
|
maxRa += raStep;
|
||||||
|
maxDec += decStep;
|
||||||
|
for(double ra = minRa; ra <= maxRa; ra += raStep)
|
||||||
|
{
|
||||||
|
QPointF p;
|
||||||
|
worldToPixel(SkyPoint(ra, minDec), p);
|
||||||
|
skyGrid.grid.moveTo(p);
|
||||||
|
for(double dec = minDec + decStep * step; dec <= maxDec; dec += decStep * step)
|
||||||
|
{
|
||||||
|
worldToPixel(SkyPoint(ra, dec), p);
|
||||||
|
skyGrid.grid.lineTo(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(double dec = minDec; dec <= maxDec; dec += decStep)
|
||||||
|
{
|
||||||
|
QPointF p;
|
||||||
|
worldToPixel(SkyPoint(minRa, dec), p);
|
||||||
|
skyGrid.grid.moveTo(p);
|
||||||
|
for(double ra = minRa + raStep * step; ra <= maxRa; ra += raStep * step)
|
||||||
|
{
|
||||||
|
worldToPixel(SkyPoint(ra, dec), p);
|
||||||
|
skyGrid.grid.lineTo(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkyPoint sp1, sp2,orig;
|
||||||
|
pixelToWorld(QPointF(-1, -1), orig);
|
||||||
|
sp1 = orig;
|
||||||
|
for(uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
QPointF p(x, 0);
|
||||||
|
if(!pixelToWorld(p, sp2))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(static_cast<int>(sp1.RA() / raStep) != static_cast<int>(sp2.RA() / raStep))
|
||||||
|
skyGrid.text.append({p, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()});
|
||||||
|
|
||||||
|
if(static_cast<int>(sp1.DEC() / decStep) != static_cast<int>(sp2.DEC() / decStep))
|
||||||
|
skyGrid.text.append({p, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()});
|
||||||
|
|
||||||
|
sp1 = sp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp1 = orig;
|
||||||
|
for(uint32_t y = 0; y < h; y++)
|
||||||
|
{
|
||||||
|
QPointF p(0, y);
|
||||||
|
if(!pixelToWorld(p, sp2))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(static_cast<int>(sp1.RA() / raStep) != static_cast<int>(sp2.RA() / raStep))
|
||||||
|
skyGrid.text.append({p, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()});
|
||||||
|
|
||||||
|
if(static_cast<int>(sp1.DEC() / decStep) != static_cast<int>(sp2.DEC() / decStep))
|
||||||
|
skyGrid.text.append({p, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()});
|
||||||
|
|
||||||
|
sp1 = sp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
skyGrid.empty = false;
|
||||||
|
return skyGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkyGrid::clear()
|
||||||
|
{
|
||||||
|
empty = true;
|
||||||
|
grid.clear();
|
||||||
|
text.clear();
|
||||||
|
objects.clear();
|
||||||
|
}
|
||||||
@@ -5,12 +5,15 @@
|
|||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QPainterPath>
|
||||||
#include <wcslib/wcs.h>
|
#include <wcslib/wcs.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace LibXISF { struct FITSKeyword; struct Property; }
|
namespace LibXISF { struct FITSKeyword; struct Property; }
|
||||||
|
|
||||||
|
class Database;
|
||||||
|
|
||||||
struct FITSRecord
|
struct FITSRecord
|
||||||
{
|
{
|
||||||
QByteArray key;
|
QByteArray key;
|
||||||
@@ -36,10 +39,13 @@ public:
|
|||||||
double RAHour() const { return ra / 15.0; }
|
double RAHour() const { return ra / 15.0; }
|
||||||
double DEC() const { return dec; }
|
double DEC() const { return dec; }
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
|
QString RAString() const;
|
||||||
|
QString DECString() const;
|
||||||
static double fromHMS(const QString &hms);
|
static double fromHMS(const QString &hms);
|
||||||
static double fromDMS(const QString &dms);
|
static double fromDMS(const QString &dms);
|
||||||
static QString toHMS(double decHour);
|
static QString toHMS(double decHour);
|
||||||
static QString toDMS(double deg);
|
static QString toDMS(double deg);
|
||||||
|
SkyPoint operator+(const SkyPoint &p);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkyPointScale
|
struct SkyPointScale
|
||||||
@@ -51,6 +57,28 @@ struct SkyPointScale
|
|||||||
double scaleHigh = 10000.0;
|
double scaleHigh = 10000.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SkyObject
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString name2;
|
||||||
|
SkyPoint skyPoint;
|
||||||
|
double maj_ax;
|
||||||
|
double min_ax;
|
||||||
|
double pos_ang;
|
||||||
|
double mag;
|
||||||
|
QPointF pixel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SkyGrid
|
||||||
|
{
|
||||||
|
bool empty = true;
|
||||||
|
QPainterPath grid;
|
||||||
|
QVector<QPair<QPointF, QString>> text;
|
||||||
|
QVector<SkyObject> objects;
|
||||||
|
double rot_ang = 0;
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
class WCSDataT
|
class WCSDataT
|
||||||
{
|
{
|
||||||
int nwcs = 0;
|
int nwcs = 0;
|
||||||
@@ -65,9 +93,10 @@ public:
|
|||||||
~WCSDataT();
|
~WCSDataT();
|
||||||
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const;
|
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const;
|
||||||
bool worldToPixel(const SkyPoint &point, QPointF &pixel) const;
|
bool worldToPixel(const SkyPoint &point, QPointF &pixel) const;
|
||||||
void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const;
|
bool calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const;
|
||||||
bool valid() const { return wcs; };
|
bool valid() const { return wcs; };
|
||||||
SkyPointScale getRaDecScale() const;
|
SkyPointScale getRaDecScale() const;
|
||||||
|
SkyGrid prepareGrid(uint32_t w, uint32_t h, Database *database);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageInfoData
|
struct ImageInfoData
|
||||||
@@ -142,6 +142,11 @@ void ImageScrollArea::falseColor(bool enable)
|
|||||||
m_imageWidget->falseColor(enable);
|
m_imageWidget->falseColor(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageScrollArea::drawGrid(bool enable)
|
||||||
|
{
|
||||||
|
m_imageWidget->drawGrid(enable);
|
||||||
|
}
|
||||||
|
|
||||||
QImage ImageScrollArea::renderToImage()
|
QImage ImageScrollArea::renderToImage()
|
||||||
{
|
{
|
||||||
return m_imageWidget->renderToImage();
|
return m_imageWidget->renderToImage();
|
||||||
@@ -31,6 +31,7 @@ public slots:
|
|||||||
void invert(bool enable);
|
void invert(bool enable);
|
||||||
void superPixel(bool enable);
|
void superPixel(bool enable);
|
||||||
void falseColor(bool enable);
|
void falseColor(bool enable);
|
||||||
|
void drawGrid(bool enable);
|
||||||
QImage renderToImage();
|
QImage renderToImage();
|
||||||
protected slots:
|
protected slots:
|
||||||
void scrollEvent();
|
void scrollEvent();
|
||||||
@@ -180,6 +180,10 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
|
|||||||
void ImageWidgetGL::setWCS(std::shared_ptr<WCSDataT> wcs)
|
void ImageWidgetGL::setWCS(std::shared_ptr<WCSDataT> wcs)
|
||||||
{
|
{
|
||||||
m_wcs = wcs;
|
m_wcs = wcs;
|
||||||
|
m_grid.clear();
|
||||||
|
|
||||||
|
if(m_drawGrid && m_wcs)
|
||||||
|
m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos)
|
void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos)
|
||||||
@@ -496,6 +500,18 @@ void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scal
|
|||||||
painter.drawImage(0, 0, img);
|
painter.drawImage(0, 0, img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageWidgetGL::drawGrid(bool enable)
|
||||||
|
{
|
||||||
|
if(m_grid.empty && m_wcs)
|
||||||
|
m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database);
|
||||||
|
|
||||||
|
if(enable != m_drawGrid)
|
||||||
|
{
|
||||||
|
m_drawGrid = enable;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageWidgetGL::paintGL()
|
void ImageWidgetGL::paintGL()
|
||||||
{
|
{
|
||||||
float dx = m_dx;
|
float dx = m_dx;
|
||||||
@@ -613,9 +629,43 @@ void ImageWidgetGL::paintGL()
|
|||||||
m_program->setUniformValue("colormapIdx", m_colormapIdx);
|
m_program->setUniformValue("colormapIdx", m_colormapIdx);
|
||||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
m_vao->release();
|
m_vao->release();
|
||||||
|
|
||||||
|
if(m_drawGrid && !m_grid.empty)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||||
|
|
||||||
|
painter.setPen(QPen(Qt::yellow, 1.0 / m_scale));
|
||||||
|
QTransform tran;
|
||||||
|
tran.translate(-std::floor(dx), -std::floor(dy));
|
||||||
|
tran.scale(m_scale, m_scale);
|
||||||
|
painter.setTransform(tran);
|
||||||
|
painter.setClipRect(0, 0, m_imgWidth, m_imgHeight);
|
||||||
|
painter.drawPath(m_grid.grid);
|
||||||
|
painter.setPen(Qt::yellow);
|
||||||
|
QFont font({"Arial", "serif-sans"});
|
||||||
|
font.setPointSizeF(12 / m_scale);
|
||||||
|
painter.setFont(font);
|
||||||
|
for(auto &text : m_grid.text)
|
||||||
|
painter.drawText(QRectF(text.first, QSizeF(4000, 4000)), text.second);
|
||||||
|
|
||||||
|
painter.setPen(QPen(Qt::green, 1.0 / m_scale));
|
||||||
|
QFontMetricsF fontMetric = QFontMetricsF(font);
|
||||||
|
for(auto &object : m_grid.objects)
|
||||||
|
{
|
||||||
|
QRectF rect = fontMetric.boundingRect(object.name);
|
||||||
|
rect.moveCenter(object.pixel);
|
||||||
|
painter.setTransform(tran);
|
||||||
|
painter.drawText(rect, Qt::TextDontClip, object.name);
|
||||||
|
|
||||||
|
painter.translate(object.pixel);
|
||||||
|
painter.rotate(object.pos_ang);
|
||||||
|
painter.drawEllipse(QPointF(0, 0), object.maj_ax, object.maj_ax);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageWidgetGL::resizeGL(int w, int h)
|
void ImageWidgetGL::resizeGL(int w, int h)
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <QOpenGLTexture>
|
#include <QOpenGLTexture>
|
||||||
#include <QOpenGLVertexArrayObject>
|
#include <QOpenGLVertexArrayObject>
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QPainterPath>
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "imageinfodata.h"
|
#include "imageinfodata.h"
|
||||||
@@ -37,6 +38,7 @@ public:
|
|||||||
virtual QImage renderToImage() = 0;
|
virtual QImage renderToImage() = 0;
|
||||||
virtual void thumbnailLoaded(const Image *image) = 0;
|
virtual void thumbnailLoaded(const Image *image) = 0;
|
||||||
virtual void showThumbnail(bool enable) = 0;
|
virtual void showThumbnail(bool enable) = 0;
|
||||||
|
virtual void drawGrid(bool enable) = 0;
|
||||||
|
|
||||||
static QImage loadColormap();
|
static QImage loadColormap();
|
||||||
};
|
};
|
||||||
@@ -70,6 +72,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
|
|||||||
GLuint m_debayerTex = 0;
|
GLuint m_debayerTex = 0;
|
||||||
std::shared_ptr<RawImage> m_rawImage;
|
std::shared_ptr<RawImage> m_rawImage;
|
||||||
std::shared_ptr<WCSDataT> m_wcs;
|
std::shared_ptr<WCSDataT> m_wcs;
|
||||||
|
SkyGrid m_grid;
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
int m_imgWidth = -1, m_imgHeight = -1;
|
int m_imgWidth = -1, m_imgHeight = -1;
|
||||||
int m_currentImg = 0;
|
int m_currentImg = 0;
|
||||||
@@ -87,6 +90,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
|
|||||||
bool m_selecting = false;
|
bool m_selecting = false;
|
||||||
bool m_sizesDirty = false;
|
bool m_sizesDirty = false;
|
||||||
bool m_srgb = false;
|
bool m_srgb = false;
|
||||||
|
bool m_drawGrid = false;
|
||||||
int m_thumbnailCount = 0;
|
int m_thumbnailCount = 0;
|
||||||
int m_maxTextureSize = 0;
|
int m_maxTextureSize = 0;
|
||||||
int m_maxArrayLayers = 0;
|
int m_maxArrayLayers = 0;
|
||||||
@@ -116,6 +120,7 @@ public:
|
|||||||
QImage renderToImage() override;
|
QImage renderToImage() override;
|
||||||
void thumbnailLoaded(const Image *image) override;
|
void thumbnailLoaded(const Image *image) override;
|
||||||
void showThumbnail(bool enable) override;
|
void showThumbnail(bool enable) override;
|
||||||
|
void drawGrid(bool enable) override;
|
||||||
protected:
|
protected:
|
||||||
void paintGL() override;
|
void paintGL() override;
|
||||||
void resizeGL(int w, int h) override;
|
void resizeGL(int w, int h) override;
|
||||||
@@ -241,18 +241,59 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
|
|||||||
{
|
{
|
||||||
info.fitsHeader.append(fits);
|
info.fitsHeader.append(fits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<FITSRecord> xisfWCS;
|
||||||
auto imageproperties = xisfImage.imageProperties();
|
auto imageproperties = xisfImage.imageProperties();
|
||||||
for(auto prop : imageproperties)
|
for(auto prop : imageproperties)
|
||||||
{
|
{
|
||||||
info.fitsHeader.append(prop);
|
info.fitsHeader.append(prop);
|
||||||
|
if(prop.id == "PCL:AstrometricSolution:ReferenceCelestialCoordinates" && prop.value.type() == LibXISF::Variant::Type::F64Vector)
|
||||||
|
{
|
||||||
|
auto val = prop.value.value<LibXISF::F64Vector>();
|
||||||
|
if(val.size() >= 2)
|
||||||
|
{
|
||||||
|
xisfWCS.append({"CRVAL1", val[0], "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CRVAL2", val[1], "value from PCL:AstrometricSolution"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(prop.id == "PCL:AstrometricSolution:ReferenceImageCoordinates" && prop.value.type() == LibXISF::Variant::Type::F64Vector)
|
||||||
|
{
|
||||||
|
auto val = prop.value.value<LibXISF::F64Vector>();
|
||||||
|
if(val.size() >= 2)
|
||||||
|
{
|
||||||
|
xisfWCS.append({"CRPIX1", val[0], "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CRPIX2", val[1], "value from PCL:AstrometricSolution"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(prop.id == "PCL:AstrometricSolution:LinearTransformationMatrix" && prop.value.type() == LibXISF::Variant::Type::F64Matrix)
|
||||||
|
{
|
||||||
|
auto val = prop.value.value<LibXISF::F64Matrix>();
|
||||||
|
if(val.cols() >= 2 && val.rows() >= 2)
|
||||||
|
{
|
||||||
|
xisfWCS.append({"CD1_1", val(0, 0), "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CD1_2", val(0, 1), "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CD2_1", val(1, 0), "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CD2_2", val(1, 1), "value from PCL:AstrometricSolution"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(prop.id == "PCL:AstrometricSolution:ProjectionSystem")
|
||||||
|
{
|
||||||
|
if(prop.value.toString() == "Gnomonic")
|
||||||
|
{
|
||||||
|
xisfWCS.append({"CTYPE1", "RA---TAN", "value from PCL:AstrometricSolution"});
|
||||||
|
xisfWCS.append({"CTYPE", "DEC--TAN", "value from PCL:AstrometricSolution"});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info.num = xisf.imagesCount();
|
info.num = xisf.imagesCount();
|
||||||
info.index = index;
|
info.index = index;
|
||||||
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
|
|
||||||
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
|
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
|
||||||
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
|
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
|
||||||
if(!info.wcs->valid())info.wcs.reset();
|
|
||||||
|
auto wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
|
||||||
|
if(!wcs->valid() && xisfWCS.size())wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), xisfWCS);
|
||||||
|
if(wcs->valid())info.wcs = wcs;
|
||||||
|
|
||||||
RawImage::DataType type;
|
RawImage::DataType type;
|
||||||
switch(xisfImage.sampleFormat())
|
switch(xisfImage.sampleFormat())
|
||||||
@@ -409,18 +450,19 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
|
|||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
|
QFileInfo fileInfo(path);
|
||||||
timer.start();
|
timer.start();
|
||||||
if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive))
|
if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
ret = loadRAW(path, info, rawImage);
|
ret = loadRAW(path, info, rawImage);
|
||||||
qDebug() << "LoadRAW" << timer.elapsed();
|
qDebug() << "LoadRAW" << timer.elapsed();
|
||||||
}
|
}
|
||||||
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive))
|
else if(isFITS(fileInfo.suffix()))
|
||||||
{
|
{
|
||||||
ret = loadFITS(path, info, rawImage, planar, index);
|
ret = loadFITS(path, info, rawImage, planar, index);
|
||||||
qDebug() << "LoadFITS" << timer.elapsed();
|
qDebug() << "LoadFITS" << timer.elapsed();
|
||||||
}
|
}
|
||||||
else if(path.endsWith(".XISF", Qt::CaseInsensitive))
|
else if(isXISF(fileInfo.suffix()))
|
||||||
{
|
{
|
||||||
ret = loadXISF(path, info, rawImage, planar, index);
|
ret = loadXISF(path, info, rawImage, planar, index);
|
||||||
qDebug() << "LoadXISF" << timer.elapsed();
|
qDebug() << "LoadXISF" << timer.elapsed();
|
||||||
@@ -444,3 +486,13 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFITS(const QString &suffix)
|
||||||
|
{
|
||||||
|
return suffix.compare("fits", Qt::CaseInsensitive) == 0 || suffix.compare("fit", Qt::CaseInsensitive) == 0 || suffix.compare("fts", Qt::CaseInsensitive) == 0 || suffix.compare("fz", Qt::CaseInsensitive) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isXISF(const QString &suffix)
|
||||||
|
{
|
||||||
|
return suffix.compare("xisf", Qt::CaseInsensitive) == 0;
|
||||||
|
}
|
||||||
@@ -10,5 +10,7 @@ QString makeUNCPath(const QString &path);
|
|||||||
bool readFITSHeader(const QString &path, ImageInfoData &info);
|
bool readFITSHeader(const QString &path, ImageInfoData &info);
|
||||||
bool readXISFHeader(const QString &path, ImageInfoData &info);
|
bool readXISFHeader(const QString &path, ImageInfoData &info);
|
||||||
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false);
|
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false);
|
||||||
|
bool isFITS(const QString &suffix);
|
||||||
|
bool isXISF(const QString &suffix);
|
||||||
|
|
||||||
#endif // LOADIMAGE_H
|
#endif // LOADIMAGE_H
|
||||||
@@ -165,11 +165,12 @@ void writeFITSImage(fitsfile *fw, std::shared_ptr<RawImage> rawimage, ImageInfoD
|
|||||||
double vald = record.value.toDouble(&isdouble);
|
double vald = record.value.toDouble(&isdouble);
|
||||||
int valb = record.value.toString() == "T";
|
int valb = record.value.toString() == "T";
|
||||||
long long vall = record.value.toLongLong(&isint);
|
long long vall = record.value.toLongLong(&isint);
|
||||||
|
if(isint)isint = vall == vald;
|
||||||
QByteArray str = record.value.toString().toLatin1();
|
QByteArray str = record.value.toString().toLatin1();
|
||||||
if(isdouble)
|
if(isint)
|
||||||
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);
|
fits_write_key(fw, TLONGLONG, record.key.data(), &vall, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(isdouble)
|
||||||
|
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
else if(isbool)
|
else if(isbool)
|
||||||
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
else if(record.key == "COMMENT")
|
else if(record.key == "COMMENT")
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "thumbnailer/genthumbnail.h"
|
#include "../thumbnailer/genthumbnail.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "histogram.h"
|
#include "histogram.h"
|
||||||
#include "batchprocessing.h"
|
#include "batchprocessing.h"
|
||||||
|
#include "filemanager.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -66,6 +67,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
_openFilter.append(tr(";;All files (*)"));
|
_openFilter.append(tr(";;All files (*)"));
|
||||||
nameFilter.append({"fit", "fits", "fts", "fz", "xisf", "cr2", "cr3", "nef", "dng"});
|
nameFilter.append({"fit", "fits", "fts", "fz", "xisf", "cr2", "cr3", "nef", "dng"});
|
||||||
QImageReader::setAllocationLimit(0);
|
QImageReader::setAllocationLimit(0);
|
||||||
|
_openSuffix = {nameFilter.constBegin(), nameFilter.constEnd()};
|
||||||
|
|
||||||
m_info = new ImageInfo(this);
|
m_info = new ImageInfo(this);
|
||||||
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
||||||
@@ -92,6 +94,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
connect(m_stretchPanel, &StretchToolbar::invert, m_image, &ImageScrollArea::invert);
|
connect(m_stretchPanel, &StretchToolbar::invert, m_image, &ImageScrollArea::invert);
|
||||||
connect(m_stretchPanel, &StretchToolbar::superPixel, m_image, &ImageScrollArea::superPixel);
|
connect(m_stretchPanel, &StretchToolbar::superPixel, m_image, &ImageScrollArea::superPixel);
|
||||||
connect(m_stretchPanel, &StretchToolbar::falseColor, m_image, &ImageScrollArea::falseColor);
|
connect(m_stretchPanel, &StretchToolbar::falseColor, m_image, &ImageScrollArea::falseColor);
|
||||||
|
connect(m_stretchPanel, &StretchToolbar::drawGrid, m_image, &ImageScrollArea::drawGrid);
|
||||||
|
|
||||||
m_ringList = new ImageRingList(m_database, nameFilter, this);
|
m_ringList = new ImageRingList(m_database, nameFilter, this);
|
||||||
m_filesystem = new FilesystemWidget(m_ringList, this);
|
m_filesystem = new FilesystemWidget(m_ringList, this);
|
||||||
@@ -99,11 +102,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
|
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
|
||||||
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
|
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
|
||||||
|
|
||||||
|
m_filetree = nullptr;
|
||||||
|
#if !defined(FLATPAK) || !defined(__aarch64__)//bug with QTreeView and QFileSystemModel on ARM64 under flatpak
|
||||||
m_filetree = new Filetree(this);
|
m_filetree = new Filetree(this);
|
||||||
connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); });
|
connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); });
|
||||||
connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); });
|
connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); });
|
||||||
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
||||||
|
#endif
|
||||||
|
|
||||||
m_databaseView = new DataBaseView(m_database, this);
|
m_databaseView = new DataBaseView(m_database, this);
|
||||||
connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
@@ -142,11 +148,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
databaseViewDock->hide();
|
databaseViewDock->hide();
|
||||||
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
|
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
|
||||||
|
|
||||||
QDockWidget *filetreeDock = new QDockWidget(tr("File tree"), this);
|
QDockWidget *filetreeDock = nullptr;
|
||||||
|
#if !defined(FLATPAK) || !defined(__aarch64__)
|
||||||
|
filetreeDock = new QDockWidget(tr("File tree"), this);
|
||||||
filetreeDock->setWidget(m_filetree);
|
filetreeDock->setWidget(m_filetree);
|
||||||
filetreeDock->setObjectName("filetreeDock");
|
filetreeDock->setObjectName("filetreeDock");
|
||||||
databaseViewDock->hide();
|
databaseViewDock->hide();
|
||||||
addDockWidget(Qt::LeftDockWidgetArea, filetreeDock);
|
addDockWidget(Qt::LeftDockWidgetArea, filetreeDock);
|
||||||
|
#endif
|
||||||
|
|
||||||
Histogram *histogram = new Histogram(this);
|
Histogram *histogram = new Histogram(this);
|
||||||
QDockWidget *histogramDock = new QDockWidget(tr("Histogram"), this);
|
QDockWidget *histogramDock = new QDockWidget(tr("Histogram"), this);
|
||||||
@@ -175,6 +184,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
|
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
|
||||||
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs);
|
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs);
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
|
#if !defined(FLATPAK) || !defined(__aarch64__)
|
||||||
|
fileMenu->addAction(tr("File manager"), this, &MainWindow::openFileManager);
|
||||||
|
#endif
|
||||||
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
|
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
|
||||||
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
|
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
|
||||||
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
|
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
|
||||||
@@ -190,7 +202,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode);
|
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode);
|
||||||
liveModeAction->setCheckable(true);
|
liveModeAction->setCheckable(true);
|
||||||
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, &MainWindow::close);
|
QAction *exitAction = fileMenu->addAction(tr("Exit"), QCoreApplication::instance(), &QCoreApplication::quit, Qt::QueuedConnection);
|
||||||
exitAction->setShortcut(QKeySequence::Quit);
|
exitAction->setShortcut(QKeySequence::Quit);
|
||||||
menuBar()->addMenu(fileMenu);
|
menuBar()->addMenu(fileMenu);
|
||||||
|
|
||||||
@@ -263,7 +275,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), Qt::Key_F3, m_ringList, &ImageRingList::toggleSlideshow);
|
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), Qt::Key_F3, m_ringList, &ImageRingList::toggleSlideshow);
|
||||||
slideshowAction->setCheckable(true);
|
slideshowAction->setCheckable(true);
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
viewMenu->addActions(m_stretchPanel->actions());
|
auto actionList = m_stretchPanel->actions();
|
||||||
|
actionList.removeFirst();
|
||||||
|
viewMenu->addActions(actionList);
|
||||||
menuBar()->addMenu(viewMenu);
|
menuBar()->addMenu(viewMenu);
|
||||||
|
|
||||||
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
||||||
@@ -309,7 +323,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
dockMenu->addAction(navigationToolbar->toggleViewAction());
|
dockMenu->addAction(navigationToolbar->toggleViewAction());
|
||||||
dockMenu->addAction(filesystemDock->toggleViewAction());
|
dockMenu->addAction(filesystemDock->toggleViewAction());
|
||||||
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
||||||
dockMenu->addAction(filetreeDock->toggleViewAction());
|
if(filetreeDock)dockMenu->addAction(filetreeDock->toggleViewAction());
|
||||||
dockMenu->addAction(histogramDock->toggleViewAction());
|
dockMenu->addAction(histogramDock->toggleViewAction());
|
||||||
#ifdef PLATESOLVER
|
#ifdef PLATESOLVER
|
||||||
dockMenu->addAction(_plateSolving->toggleViewAction());
|
dockMenu->addAction(_plateSolving->toggleViewAction());
|
||||||
@@ -360,7 +374,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
if(filetreeDock)filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
if(_plateSolving)_plateSolving->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
m_stretchPanel->setFloatable(false);
|
m_stretchPanel->setFloatable(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,6 +820,15 @@ void MainWindow::checkNewVersion()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::openFileManager()
|
||||||
|
{
|
||||||
|
#if !defined(FLATPAK) || !defined(__aarch64__)
|
||||||
|
FileManager *filemanager = new FileManager(_openSuffix);
|
||||||
|
connect(filemanager, &FileManager::openFile, this, static_cast<void (MainWindow::*)(const QString&)>(&MainWindow::loadFile));
|
||||||
|
filemanager->show();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::updateWindowTitle()
|
void MainWindow::updateWindowTitle()
|
||||||
{
|
{
|
||||||
ImagePtr ptr = m_ringList->currentImage();
|
ImagePtr ptr = m_ringList->currentImage();
|
||||||
@@ -23,13 +23,14 @@ class MainWindow : public QMainWindow
|
|||||||
FilesystemWidget *m_filesystem;
|
FilesystemWidget *m_filesystem;
|
||||||
Filetree *m_filetree;
|
Filetree *m_filetree;
|
||||||
DataBaseView *m_databaseView;
|
DataBaseView *m_databaseView;
|
||||||
PlateSolving *_plateSolving;
|
PlateSolving *_plateSolving = nullptr;
|
||||||
static int socketPair[2];
|
static int socketPair[2];
|
||||||
QSocketNotifier *socketNotifier;
|
QSocketNotifier *socketNotifier;
|
||||||
QString _lastDir;
|
QString _lastDir;
|
||||||
bool _maximized;
|
bool _maximized;
|
||||||
QString _openFilter;
|
QString _openFilter;
|
||||||
QString _saveFilter;
|
QString _saveFilter;
|
||||||
|
QSet<QString> _openSuffix;
|
||||||
public:
|
public:
|
||||||
MainWindow(QWidget *parent = 0);
|
MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow() override;
|
~MainWindow() override;
|
||||||
@@ -67,6 +68,7 @@ public slots:
|
|||||||
void showSettingsDialog();
|
void showSettingsDialog();
|
||||||
void exportCSV();
|
void exportCSV();
|
||||||
void checkNewVersion();
|
void checkNewVersion();
|
||||||
|
void openFileManager();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef PLATESOLVING_H
|
#ifndef PLATESOLVING_H
|
||||||
#define PLATESOLVING_H
|
#define PLATESOLVING_H
|
||||||
|
|
||||||
#include "qelapsedtimer.h"
|
#include <QElapsedTimer>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
|
|
||||||
class Solver;
|
class Solver;
|
||||||
@@ -403,9 +403,9 @@ uint32_t RawImage::norm() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RawImage::widthBytes() const
|
uint64_t RawImage::widthBytes() const
|
||||||
{
|
{
|
||||||
return m_ch * m_width * typeSize(m_type);
|
return (uint64_t)m_ch * m_width * typeSize(m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RawImage::widthSamples() const
|
uint32_t RawImage::widthSamples() const
|
||||||
@@ -97,7 +97,7 @@ public:
|
|||||||
uint64_t size() const;
|
uint64_t size() const;
|
||||||
DataType type() const;
|
DataType type() const;
|
||||||
uint32_t norm() const;
|
uint32_t norm() const;
|
||||||
uint32_t widthBytes() const;
|
uint64_t widthBytes() const;
|
||||||
uint32_t widthSamples() const;
|
uint32_t widthSamples() const;
|
||||||
void* data();
|
void* data();
|
||||||
const void* data() const;
|
const void* data() const;
|
||||||
@@ -92,6 +92,55 @@ bool ScriptEngine::isMarked(const File *file)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJSValue ScriptEngine::getObjects(double ra, double dec, double distance)
|
||||||
|
{
|
||||||
|
QVector<SkyObject> objects;
|
||||||
|
QMetaObject::invokeMethod(_database, [this, ra, dec, distance](){
|
||||||
|
return _database->getObjects(ra - distance, ra + distance, dec - distance, dec + distance); }, Qt::BlockingQueuedConnection, &objects);
|
||||||
|
|
||||||
|
QJSValue ret = newArray(objects.size());
|
||||||
|
qint32 i = 0;
|
||||||
|
for(auto &object : objects)
|
||||||
|
{
|
||||||
|
QJSValue jsObj = newObject();
|
||||||
|
jsObj.setProperty("name", object.name);
|
||||||
|
jsObj.setProperty("name2", object.name2);
|
||||||
|
jsObj.setProperty("ra", object.skyPoint.RA());
|
||||||
|
jsObj.setProperty("dec", object.skyPoint.DEC());
|
||||||
|
jsObj.setProperty("mag", object.mag);
|
||||||
|
ret.setProperty(i++, jsObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSValue ScriptEngine::getObjects(const QJSValue &bounds)
|
||||||
|
{
|
||||||
|
QVector<SkyObject> objects;
|
||||||
|
double minRa = bounds.property("minRA").toNumber();
|
||||||
|
double maxRa = bounds.property("maxRA").toNumber();
|
||||||
|
double minDec = bounds.property("minDEC").toNumber();
|
||||||
|
double maxDec = bounds.property("maxDEC").toNumber();
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(_database, [this, minRa, maxRa, minDec, maxDec](){
|
||||||
|
return _database->getObjects(minRa, maxRa, minDec, maxDec); }, Qt::BlockingQueuedConnection, &objects);
|
||||||
|
|
||||||
|
QJSValue ret = newArray(objects.size());
|
||||||
|
qint32 i = 0;
|
||||||
|
for(auto &object : objects)
|
||||||
|
{
|
||||||
|
QJSValue jsObj = newObject();
|
||||||
|
jsObj.setProperty("name", object.name);
|
||||||
|
jsObj.setProperty("name2", object.name2);
|
||||||
|
jsObj.setProperty("ra", object.skyPoint.RA());
|
||||||
|
jsObj.setProperty("dec", object.skyPoint.DEC());
|
||||||
|
jsObj.setProperty("mag", object.mag);
|
||||||
|
ret.setProperty(i++, jsObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptEngine::setMaxThread(int maxthread)
|
void ScriptEngine::setMaxThread(int maxthread)
|
||||||
{
|
{
|
||||||
int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1);
|
int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1);
|
||||||
@@ -406,16 +455,18 @@ void File::loadFitsKeywords()
|
|||||||
{
|
{
|
||||||
_fitsKeywordsLoaded = true;
|
_fitsKeywordsLoaded = true;
|
||||||
ImageInfoData info;
|
ImageInfoData info;
|
||||||
if(suffix().toLower() == "xisf")
|
if(isXISF(suffix()))
|
||||||
{
|
{
|
||||||
readXISFHeader(_path, info);
|
readXISFHeader(_path, info);
|
||||||
}
|
}
|
||||||
else if(suffix().toLower() == "fits" || suffix().toLower() == "fit")
|
else if(isFITS(suffix()))
|
||||||
{
|
{
|
||||||
readFITSHeader(_path, info);
|
readFITSHeader(_path, info);
|
||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
|
|
||||||
|
_wcs = info.wcs;
|
||||||
|
|
||||||
for(auto &record : info.fitsHeader)
|
for(auto &record : info.fitsHeader)
|
||||||
{
|
{
|
||||||
_fitsKeywords.append(record.key);
|
_fitsKeywords.append(record.key);
|
||||||
@@ -557,7 +608,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
|
|||||||
_fitsKeywordsLoaded = false;
|
_fitsKeywordsLoaded = false;
|
||||||
_fitsKeywords.clear();
|
_fitsKeywords.clear();
|
||||||
|
|
||||||
if(QRegularExpression("(fits?|fz|fts)", QRegularExpression::CaseInsensitiveOption).match(suffix()).hasMatch())
|
if(isFITS(suffix()))
|
||||||
{
|
{
|
||||||
fitsfile *file;
|
fitsfile *file;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
@@ -574,16 +625,22 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
|
|||||||
int naxis;
|
int naxis;
|
||||||
long naxes[3] = {0};
|
long naxes[3] = {0};
|
||||||
int type = -1;
|
int type = -1;
|
||||||
|
std::vector<int> imageIdxs;
|
||||||
for(int i=1; i <= num; i++)
|
for(int i=1; i <= num; i++)
|
||||||
{
|
{
|
||||||
fits_movabs_hdu(file, i, IMAGE_HDU, &status);
|
fits_movabs_hdu(file, i, IMAGE_HDU, &status);
|
||||||
fits_get_hdu_type(file, &type, &status);
|
fits_get_hdu_type(file, &type, &status);
|
||||||
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
|
if(type == IMAGE_HDU)
|
||||||
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
|
{
|
||||||
break;
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
|
||||||
if(i == num)return false;
|
if(naxis >= 2 && naxis <= 3)imageIdxs.push_back(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(modify->_imageIdx >= imageIdxs.size())return false;
|
||||||
|
fits_movabs_hdu(file, imageIdxs[modify->_imageIdx], &type, &status);
|
||||||
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
|
||||||
|
|
||||||
for(auto &remove : modify->_remove)
|
for(auto &remove : modify->_remove)
|
||||||
{
|
{
|
||||||
int status = 0;//we ignore errors from here
|
int status = 0;//we ignore errors from here
|
||||||
@@ -680,7 +737,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
|
|||||||
|
|
||||||
return status == 0;
|
return status == 0;
|
||||||
}
|
}
|
||||||
else if(suffix().toLower() == "xisf")
|
else if(isXISF(suffix()))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -691,13 +748,16 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
|
|||||||
qDebug() << "modify" << in << out;
|
qDebug() << "modify" << in << out;
|
||||||
|
|
||||||
for(auto &remove : modify->_remove)
|
for(auto &remove : modify->_remove)
|
||||||
modifyXISF.removeFITSKeyword(0, remove.toStdString());
|
modifyXISF.removeFITSKeyword(modify->_imageIdx, remove.toStdString());
|
||||||
|
|
||||||
for(auto &record : modify->_update)
|
for(auto &record : modify->_update)
|
||||||
modifyXISF.updateFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}, true);
|
modifyXISF.updateFITSKeyword(modify->_imageIdx, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}, true);
|
||||||
|
|
||||||
for(auto &record : modify->_add)
|
for(auto &record : modify->_add)
|
||||||
modifyXISF.addFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()});
|
modifyXISF.addFITSKeyword(modify->_imageIdx, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()});
|
||||||
|
|
||||||
|
for(auto &property : modify->_property)
|
||||||
|
modifyXISF.updateProperty(modify->_imageIdx, property);
|
||||||
|
|
||||||
modifyXISF.save(out.toLocal8Bit().toStdString());
|
modifyXISF.save(out.toLocal8Bit().toStdString());
|
||||||
modifyXISF.close();
|
modifyXISF.close();
|
||||||
@@ -706,6 +766,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
|
|||||||
}
|
}
|
||||||
catch(std::filesystem::filesystem_error &err)
|
catch(std::filesystem::filesystem_error &err)
|
||||||
{
|
{
|
||||||
|
if(_engine)_engine->newMessage("Failed to modify file " + _path + " " + err.what(), true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch(LibXISF::Error &err)
|
catch(LibXISF::Error &err)
|
||||||
@@ -788,6 +849,24 @@ QJSValue File::stats()
|
|||||||
return _stats;
|
return _stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJSValue File::calculatedBounds()
|
||||||
|
{
|
||||||
|
QJSValue ret = _engine->newObject();
|
||||||
|
loadFitsKeywords();
|
||||||
|
if(_wcs)
|
||||||
|
{
|
||||||
|
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
|
||||||
|
_wcs->calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
|
||||||
|
ret.setProperty("minRA", minRa);
|
||||||
|
ret.setProperty("maxRA", maxRa);
|
||||||
|
ret.setProperty("minDEC", minDec);
|
||||||
|
ret.setProperty("maxDEC", maxDec);
|
||||||
|
ret.setProperty("crVal1", crVal1);
|
||||||
|
ret.setProperty("crVal2", crVal2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef PLATESOLVER
|
#ifdef PLATESOLVER
|
||||||
QJSValue File::solve(bool updateHeader)
|
QJSValue File::solve(bool updateHeader)
|
||||||
{
|
{
|
||||||
@@ -857,6 +936,21 @@ void FITSRecordModify::addKeyword(const QString &key, const QVariant &value, con
|
|||||||
_update.append({key.toLatin1(), value, comment.toLatin1()});
|
_update.append({key.toLatin1(), value, comment.toLatin1()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FITSRecordModify::updateProperty(const QString &id, const LibXISF::Variant &value)
|
||||||
|
{
|
||||||
|
_property.append(LibXISF::Property(id.toStdString(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FITSRecordModify::imageIndex() const
|
||||||
|
{
|
||||||
|
return _imageIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FITSRecordModify::setImageIndex(uint32_t idx)
|
||||||
|
{
|
||||||
|
_imageIdx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
bool TextFile::open(const QString &path, const QString &mode)
|
bool TextFile::open(const QString &path, const QString &mode)
|
||||||
{
|
{
|
||||||
_fr.setFileName(path);
|
_fr.setFileName(path);
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <QSemaphore>
|
#include <QSemaphore>
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "imageinfodata.h"
|
#include "imageinfodata.h"
|
||||||
|
#include "libxisf.h"
|
||||||
|
|
||||||
class BatchProcessing;
|
class BatchProcessing;
|
||||||
class Solver;
|
class Solver;
|
||||||
@@ -41,6 +42,8 @@ public:
|
|||||||
Q_INVOKABLE void mark(File *file);
|
Q_INVOKABLE void mark(File *file);
|
||||||
Q_INVOKABLE void unmark(File *file);
|
Q_INVOKABLE void unmark(File *file);
|
||||||
Q_INVOKABLE bool isMarked(const File *file);
|
Q_INVOKABLE bool isMarked(const File *file);
|
||||||
|
Q_INVOKABLE QJSValue getObjects(double ra, double dec, double distance);
|
||||||
|
Q_INVOKABLE QJSValue getObjects(const QJSValue &bounds);
|
||||||
Q_INVOKABLE void setMaxThread(int maxthread);
|
Q_INVOKABLE void setMaxThread(int maxthread);
|
||||||
Q_INVOKABLE void sync();
|
Q_INVOKABLE void sync();
|
||||||
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
|
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
|
||||||
@@ -96,6 +99,7 @@ class File : public QObject
|
|||||||
bool _fitsKeywordsLoaded = false;
|
bool _fitsKeywordsLoaded = false;
|
||||||
QStringList _fitsKeywords;
|
QStringList _fitsKeywords;
|
||||||
QMultiHash<QString, FITSRecord> _fitsRecords;
|
QMultiHash<QString, FITSRecord> _fitsRecords;
|
||||||
|
std::shared_ptr<WCSDataT> _wcs;
|
||||||
void loadFitsKeywords();
|
void loadFitsKeywords();
|
||||||
bool mkpath(const QString &path) const;
|
bool mkpath(const QString &path) const;
|
||||||
QJSValue _stats;
|
QJSValue _stats;
|
||||||
@@ -124,6 +128,7 @@ public:
|
|||||||
Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
||||||
Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
||||||
Q_INVOKABLE QJSValue stats();
|
Q_INVOKABLE QJSValue stats();
|
||||||
|
Q_INVOKABLE QJSValue calculatedBounds();
|
||||||
#ifdef PLATESOLVER
|
#ifdef PLATESOLVER
|
||||||
Q_INVOKABLE QJSValue solve(bool updateHeader = false);
|
Q_INVOKABLE QJSValue solve(bool updateHeader = false);
|
||||||
Q_INVOKABLE QJSValue extractStars(bool hfr);
|
Q_INVOKABLE QJSValue extractStars(bool hfr);
|
||||||
@@ -136,6 +141,8 @@ class FITSRecordModify : public QObject
|
|||||||
QStringList _remove;
|
QStringList _remove;
|
||||||
QVector<FITSRecord> _update;
|
QVector<FITSRecord> _update;
|
||||||
QVector<FITSRecord> _add;
|
QVector<FITSRecord> _add;
|
||||||
|
QVector<LibXISF::Property> _property;
|
||||||
|
uint32_t _imageIdx = 0;
|
||||||
|
|
||||||
friend class File;
|
friend class File;
|
||||||
public:
|
public:
|
||||||
@@ -143,6 +150,10 @@ public:
|
|||||||
Q_INVOKABLE void removeKeyword(const QString &key);
|
Q_INVOKABLE void removeKeyword(const QString &key);
|
||||||
Q_INVOKABLE void updateKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
|
Q_INVOKABLE void updateKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
|
||||||
Q_INVOKABLE void addKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
|
Q_INVOKABLE void addKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
|
||||||
|
Q_PROPERTY(uint32_t imageIndex READ imageIndex WRITE setImageIndex);
|
||||||
|
void updateProperty(const QString &id, const LibXISF::Variant &value);
|
||||||
|
uint32_t imageIndex() const;
|
||||||
|
void setImageIndex(uint32_t idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextFile : public QObject
|
class TextFile : public QObject
|
||||||
@@ -186,6 +186,19 @@ bool Solver::updateHeader(QString &error)
|
|||||||
modify.updateKeyword("CTYPE2", "DEC--TAN", QByteArray("first parameter DEC, projection TANgential"));
|
modify.updateKeyword("CTYPE2", "DEC--TAN", QByteArray("first parameter DEC, projection TANgential"));
|
||||||
modify.updateKeyword("RADESYS", "ICRS", QByteArray("International Celestial Reference System"));
|
modify.updateKeyword("RADESYS", "ICRS", QByteArray("International Celestial Reference System"));
|
||||||
modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates"));
|
modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates"));
|
||||||
|
|
||||||
|
LibXISF::F64Matrix matrix(2, 2);
|
||||||
|
matrix(0, 0) = std::cos(rotationRad) * cdeltx;
|
||||||
|
matrix(0, 1) =-std::sin(rotationRad) * cdelty;
|
||||||
|
matrix(1, 0) = std::sin(rotationRad) * cdeltx;
|
||||||
|
matrix(1, 1) = std::cos(rotationRad) * cdelty;
|
||||||
|
|
||||||
|
modify.updateProperty("PCL:AstrometricSolution:ReferenceCelestialCoordinates", LibXISF::F64Vector({solution.ra, solution.dec}));
|
||||||
|
modify.updateProperty("PCL:AstrometricSolution:ReferenceImageCoordinates", LibXISF::F64Vector({_stats.width / 2.0, _stats.height / 2.0}));
|
||||||
|
modify.updateProperty("PCL:AstrometricSolution:LinearTransformationMatrix", LibXISF::F64Matrix(matrix));
|
||||||
|
modify.updateProperty("PCL:AstrometricSolution:ProjectionSystem", LibXISF::String("Gnomonic"));
|
||||||
|
modify.updateProperty("PCL:AstrometricSolution:ReferenceNativeCoordinates", LibXISF::F64Vector({0, 90}));
|
||||||
|
|
||||||
bool ret = file.modifyFITSRecords(&modify);
|
bool ret = file.modifyFITSRecords(&modify);
|
||||||
if(!ret)error = tr("Failed to update file header");
|
if(!ret)error = tr("Failed to update file header");
|
||||||
else emit headerUpdated(_path);
|
else emit headerUpdated(_path);
|
||||||
@@ -89,6 +89,11 @@ StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar")
|
|||||||
|
|
||||||
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
|
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
|
||||||
m_autoStretchOnLoad->setCheckable(true);
|
m_autoStretchOnLoad->setCheckable(true);
|
||||||
|
|
||||||
|
QAction *showGridButton = addAction(QIcon(":/grid.svg"), tr("Draw equatorial grid"));
|
||||||
|
showGridButton->setCheckable(true);
|
||||||
|
connect(showGridButton, &QAction::toggled, this, &StretchToolbar::drawGrid);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
m_autoStretchOnLoad->setChecked(settings.value("stretchtoolbar/autostretch", false).toBool());
|
m_autoStretchOnLoad->setChecked(settings.value("stretchtoolbar/autostretch", false).toBool());
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,7 @@ signals:
|
|||||||
void invert(bool enable);
|
void invert(bool enable);
|
||||||
void superPixel(bool enable);
|
void superPixel(bool enable);
|
||||||
void falseColor(bool enable);
|
void falseColor(bool enable);
|
||||||
|
void drawGrid(bool enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // STRETCHTOOLBAR_H
|
#endif // STRETCHTOOLBAR_H
|
||||||
@@ -6,9 +6,9 @@ if(BUILD_THUMBNAILER)
|
|||||||
Dll.cpp
|
Dll.cpp
|
||||||
loadimage.cpp
|
loadimage.cpp
|
||||||
TenmonThumbnailProvider.cpp
|
TenmonThumbnailProvider.cpp
|
||||||
../rawimage.h
|
../src/rawimage.h
|
||||||
../rawimage.cpp
|
../src/rawimage.cpp
|
||||||
../rawimage_sse.cpp)
|
../src/rawimage_sse.cpp)
|
||||||
set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "")
|
set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "")
|
||||||
|
|
||||||
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
|
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
|
||||||
@@ -19,8 +19,8 @@ if(BUILD_THUMBNAILER)
|
|||||||
qt_add_executable(tenmonthumbnailer
|
qt_add_executable(tenmonthumbnailer
|
||||||
main.cpp
|
main.cpp
|
||||||
loadimage.cpp
|
loadimage.cpp
|
||||||
../rawimage.cpp
|
../src/rawimage.cpp
|
||||||
../rawimage_sse.cpp)
|
../src/rawimage_sse.cpp)
|
||||||
|
|
||||||
target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF)
|
target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include <thumbcache.h> // For IThumbnailProvider.
|
#include <thumbcache.h> // For IThumbnailProvider.
|
||||||
#include <new>
|
#include <new>
|
||||||
#include "libxisf.h"
|
#include "libxisf.h"
|
||||||
#include "../rawimage.h"
|
#include "../src/rawimage.h"
|
||||||
|
|
||||||
bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
||||||
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "genthumbnail.h"
|
#include "genthumbnail.h"
|
||||||
#include "../rawimage.h"
|
#include "../src/rawimage.h"
|
||||||
#include "../loadimage.h"
|
#include "../src/loadimage.h"
|
||||||
|
|
||||||
|
|
||||||
int generateThumbnail(const QString &input, const QString &output, uint32_t size)
|
int generateThumbnail(const QString &input, const QString &output, uint32_t size)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "libxisf.h"
|
#include "libxisf.h"
|
||||||
#include "../rawimage.h"
|
#include "../src/rawimage.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "../rawimage.h"
|
#include "../src/rawimage.h"
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb_image_write.h"
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user