Changeset View
Changeset View
Standalone View
Standalone View
kstars/fitsviewer/fitstab.cpp
Show All 22 Lines | |||||
23 | #include "ksnotification.h" | 23 | #include "ksnotification.h" | ||
24 | #include "kstars.h" | 24 | #include "kstars.h" | ||
25 | #include "Options.h" | 25 | #include "Options.h" | ||
26 | #include "ui_fitsheaderdialog.h" | 26 | #include "ui_fitsheaderdialog.h" | ||
27 | #include "ui_statform.h" | 27 | #include "ui_statform.h" | ||
28 | 28 | | |||
29 | #include <KMessageBox> | 29 | #include <KMessageBox> | ||
30 | #include <QtConcurrent> | 30 | #include <QtConcurrent> | ||
31 | #include <QIcon> | ||||
32 | | ||||
33 | | ||||
34 | namespace { | ||||
35 | const char kAutoToolTip[] = "Automatically find stretch parameters"; | ||||
36 | const char kStretchOffToolTip[] = "Stretch the image"; | ||||
37 | const char kStretchOnToolTip[] = "Disable stretching of the image."; | ||||
38 | } // namespace | ||||
31 | 39 | | |||
32 | FITSTab::FITSTab(FITSViewer *parent) : QWidget(parent) | 40 | FITSTab::FITSTab(FITSViewer *parent) : QWidget(parent) | ||
33 | { | 41 | { | ||
34 | viewer = parent; | 42 | viewer = parent; | ||
35 | undoStack = new QUndoStack(this); | 43 | undoStack = new QUndoStack(this); | ||
36 | undoStack->setUndoLimit(10); | 44 | undoStack->setUndoLimit(10); | ||
37 | undoStack->clear(); | 45 | undoStack->clear(); | ||
38 | connect(undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(modifyFITSState(bool))); | 46 | connect(undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(modifyFITSState(bool))); | ||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Line(s) | |||||
93 | 101 | | |||
94 | void FITSTab::clearRecentFITS() | 102 | void FITSTab::clearRecentFITS() | ||
95 | { | 103 | { | ||
96 | disconnect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS); | 104 | disconnect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS); | ||
97 | recentImages->clear(); | 105 | recentImages->clear(); | ||
98 | connect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS); | 106 | connect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS); | ||
99 | } | 107 | } | ||
100 | 108 | | |||
109 | namespace { | ||||
110 | | ||||
111 | // Sets the text value in the slider's value display, and if adjustSlider is true, | ||||
112 | // moves the slider to the correct position. | ||||
113 | void setSlider(QSlider *slider, QLabel *label, float value, float maxValue, bool adjustSlider) | ||||
114 | { | ||||
115 | if (adjustSlider) | ||||
116 | slider->setValue(static_cast<int>(value * 10000 / maxValue)); | ||||
117 | QString valStr = QString("%1").arg(static_cast<double>(value), 5, 'f', 4); | ||||
118 | label->setText(valStr); | ||||
119 | } | ||||
120 | | ||||
121 | // Adds the following to a horizontal layout (left to right): a vertical line, | ||||
122 | // a label with the slider's name, a slider, and a text field to display the slider's value. | ||||
123 | void setupStretchSlider(QSlider *slider, QLabel *label, QLabel *val, int fontSize, | ||||
124 | const QString& name, QHBoxLayout *layout) | ||||
125 | { | ||||
126 | QFrame* line = new QFrame(); | ||||
127 | line->setFrameShape(QFrame::VLine); | ||||
128 | line->setFrameShadow(QFrame::Sunken); | ||||
129 | layout->addWidget(line); | ||||
130 | QFont font = label->font(); | ||||
131 | font.setPointSize(fontSize); | ||||
132 | | ||||
133 | label->setText(name); | ||||
134 | label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||||
135 | label->setFont(font); | ||||
136 | layout->addWidget(label); | ||||
137 | slider->setMinimum(0); | ||||
138 | slider->setMaximum(10000); | ||||
139 | slider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); | ||||
140 | layout->addWidget(slider); | ||||
141 | val->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||||
142 | val->setFont(font); | ||||
143 | layout->addWidget(val); | ||||
144 | } | ||||
145 | | ||||
146 | // Adds a button with the icon and tooltip to the layout. | ||||
147 | void setupStretchButton(QPushButton *button, const QString &iconName, const QString &tip, QHBoxLayout *layout) | ||||
148 | { | ||||
149 | button->setIcon(QIcon::fromTheme(iconName)); | ||||
150 | button->setIconSize(QSize(22, 22)); | ||||
151 | button->setToolTip(tip); | ||||
152 | button->setCheckable(true); | ||||
153 | button->setChecked(true); | ||||
154 | button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||||
155 | layout->addWidget(button); | ||||
156 | } | ||||
157 | | ||||
158 | } // namespace | ||||
159 | | ||||
160 | // Updates all the widgets in the stretch area to display the view's stretch parameters. | ||||
161 | void FITSTab::setStretchUIValues(bool adjustSliders) | ||||
162 | { | ||||
163 | StretchParams1Channel params = view->getStretchParams().grey_red; | ||||
164 | setSlider(shadowsSlider.get(), shadowsVal.get(), params.shadows, maxShadows, adjustSliders); | ||||
165 | setSlider(midtonesSlider.get(), midtonesVal.get(), params.midtones, maxMidtones, adjustSliders); | ||||
166 | setSlider(highlightsSlider.get(), highlightsVal.get(), params.highlights, maxHighlights, adjustSliders); | ||||
167 | | ||||
168 | | ||||
169 | bool stretchActive = view->isImageStretched(); | ||||
170 | if (stretchActive) | ||||
171 | { | ||||
172 | stretchButton->setChecked(true); | ||||
173 | stretchButton->setToolTip(kStretchOnToolTip); | ||||
174 | } | ||||
175 | else | ||||
176 | { | ||||
177 | stretchButton->setChecked(false); | ||||
178 | stretchButton->setToolTip(kStretchOffToolTip); | ||||
179 | } | ||||
180 | | ||||
181 | // Only activeate the auto button if stretching is on and auto-stretching is not set. | ||||
182 | if (stretchActive && !view->getAutoStretch()) | ||||
183 | { | ||||
184 | autoButton->setEnabled(true); | ||||
185 | autoButton->setIcon(QIcon::fromTheme("tools-wizard")); | ||||
186 | autoButton->setIconSize(QSize(22, 22)); | ||||
187 | autoButton->setToolTip(kAutoToolTip); | ||||
188 | } | ||||
189 | else | ||||
190 | { | ||||
191 | autoButton->setEnabled(false); | ||||
192 | autoButton->setIcon(QIcon()); | ||||
193 | autoButton->setIconSize(QSize(22, 22)); | ||||
194 | autoButton->setToolTip(""); | ||||
195 | } | ||||
196 | autoButton->setChecked(view->getAutoStretch()); | ||||
197 | | ||||
198 | // Disable most of the UI if stretching is not active. | ||||
199 | shadowsSlider->setEnabled(stretchActive); | ||||
200 | shadowsVal->setEnabled(stretchActive); | ||||
201 | shadowsLabel->setEnabled(stretchActive); | ||||
202 | midtonesSlider->setEnabled(stretchActive); | ||||
203 | midtonesVal->setEnabled(stretchActive); | ||||
204 | midtonesLabel->setEnabled(stretchActive); | ||||
205 | highlightsSlider->setEnabled(stretchActive); | ||||
206 | highlightsVal->setEnabled(stretchActive); | ||||
207 | highlightsLabel->setEnabled(stretchActive); | ||||
208 | } | ||||
209 | | ||||
210 | // Adjusts the maxShadows value so that we have room to adjust the slider. | ||||
211 | void FITSTab::rescaleShadows() | ||||
212 | { | ||||
213 | if (!view) return; | ||||
214 | StretchParams1Channel params = view->getStretchParams().grey_red; | ||||
215 | maxShadows = std::max(0.002f, std::min(1.0f, params.shadows * 2.0f)); | ||||
216 | setStretchUIValues(true); | ||||
217 | } | ||||
218 | | ||||
219 | // Adjusts the maxMidtones value so that we have room to adjust the slider. | ||||
220 | void FITSTab::rescaleMidtones() | ||||
221 | { | ||||
222 | if (!view) return; | ||||
223 | StretchParams1Channel params = view->getStretchParams().grey_red; | ||||
224 | maxMidtones = std::max(.002f, std::min(1.0f, params.midtones * 2.0f)); | ||||
225 | setStretchUIValues(true); | ||||
226 | } | ||||
227 | | ||||
228 | QHBoxLayout* FITSTab::setupStretchBar() | ||||
229 | { | ||||
230 | constexpr int fontSize = 12; | ||||
231 | | ||||
232 | QHBoxLayout *stretchBarLayout = new QHBoxLayout(); | ||||
233 | | ||||
234 | stretchButton.reset(new QPushButton()); | ||||
235 | setupStretchButton(stretchButton.get(), "transform-move", kStretchOffToolTip, stretchBarLayout); | ||||
236 | | ||||
237 | // Shadows | ||||
238 | shadowsLabel.reset(new QLabel()); | ||||
239 | shadowsVal.reset(new QLabel()); | ||||
240 | shadowsSlider.reset(new QSlider(Qt::Horizontal, this)); | ||||
241 | setupStretchSlider(shadowsSlider.get(), shadowsLabel.get(), shadowsVal.get(), fontSize, "Shadows", stretchBarLayout); | ||||
242 | | ||||
243 | // Midtones | ||||
244 | midtonesLabel.reset(new QLabel()); | ||||
245 | midtonesVal.reset(new QLabel()); | ||||
246 | midtonesSlider.reset(new QSlider(Qt::Horizontal, this)); | ||||
247 | setupStretchSlider(midtonesSlider.get(), midtonesLabel.get(), midtonesVal.get(), fontSize, "Midtones", stretchBarLayout); | ||||
248 | | ||||
249 | // Highlights | ||||
250 | highlightsLabel.reset(new QLabel()); | ||||
251 | highlightsVal.reset(new QLabel()); | ||||
252 | highlightsSlider.reset(new QSlider(Qt::Horizontal, this)); | ||||
253 | setupStretchSlider(highlightsSlider.get(), highlightsLabel.get(), highlightsVal.get(), fontSize, "Hightlights", stretchBarLayout); | ||||
254 | | ||||
255 | // Separator | ||||
256 | QFrame* line4 = new QFrame(); | ||||
257 | line4->setFrameShape(QFrame::VLine); | ||||
258 | line4->setFrameShadow(QFrame::Sunken); | ||||
259 | stretchBarLayout->addWidget(line4); | ||||
260 | | ||||
261 | autoButton.reset(new QPushButton()); | ||||
262 | setupStretchButton(autoButton.get(), "tools-wizard", kAutoToolTip, stretchBarLayout); | ||||
263 | | ||||
264 | connect(stretchButton.get(), &QPushButton::clicked, [=]() { | ||||
265 | // This will toggle whether we're currently stretching. | ||||
266 | view->setStretch(!view->isImageStretched()); | ||||
267 | }); | ||||
268 | | ||||
269 | // Make rough displays for the slider movement. | ||||
270 | connect(shadowsSlider.get(), &QSlider::sliderMoved, [=](int value) { | ||||
271 | StretchParams params = view->getStretchParams(); | ||||
272 | params.grey_red.shadows = this->maxShadows * value / 10000.0f; | ||||
273 | view->setSampling(4); | ||||
274 | view->setStretchParams(params); | ||||
275 | view->setSampling(1); | ||||
276 | }); | ||||
277 | connect(midtonesSlider.get(), &QSlider::sliderMoved, [=](int value) { | ||||
278 | StretchParams params = view->getStretchParams(); | ||||
279 | params.grey_red.midtones = this->maxMidtones * value / 10000.0f; | ||||
280 | view->setSampling(4); | ||||
281 | view->setStretchParams(params); | ||||
282 | view->setSampling(1); | ||||
283 | }); | ||||
284 | connect(highlightsSlider.get(), &QSlider::sliderMoved, [=](int value) { | ||||
285 | StretchParams params = view->getStretchParams(); | ||||
286 | params.grey_red.highlights = this->maxHighlights * value / 10000.0f; | ||||
287 | view->setSampling(4); | ||||
288 | view->setStretchParams(params); | ||||
289 | view->setSampling(1); | ||||
290 | }); | ||||
291 | | ||||
292 | // Make a final full-res display when the slider is released. | ||||
293 | connect(shadowsSlider.get(), &QSlider::sliderReleased, [=]() { | ||||
294 | if (!view) return; | ||||
295 | rescaleShadows(); | ||||
296 | StretchParams params = view->getStretchParams(); | ||||
297 | view->setStretchParams(params); | ||||
298 | }); | ||||
299 | connect(midtonesSlider.get(), &QSlider::sliderReleased, [=]() { | ||||
300 | if (!view) return; | ||||
301 | rescaleMidtones(); | ||||
302 | StretchParams params = view->getStretchParams(); | ||||
303 | view->setStretchParams(params); | ||||
304 | }); | ||||
305 | connect(highlightsSlider.get(), &QSlider::sliderReleased, [=]() { | ||||
306 | if (!view) return; | ||||
307 | StretchParams params = view->getStretchParams(); | ||||
308 | view->setStretchParams(params); | ||||
309 | }); | ||||
310 | | ||||
311 | connect(autoButton.get(), &QPushButton::clicked, [=]() { | ||||
312 | // If we're not currently using automatic stretch parameters, turn that on. | ||||
313 | // If we're already using automatic parameters, don't do anything. | ||||
314 | // User can just move the sliders to take manual control. | ||||
315 | if (!view->getAutoStretch()) | ||||
316 | view->setAutoStretchParams(); | ||||
317 | else | ||||
318 | KMessageBox::information(this, "You are already using automatic stretching. To manually stretch, drag a slider."); | ||||
319 | setStretchUIValues(false); | ||||
320 | }); | ||||
321 | | ||||
322 | // This is mostly useful right at the start, when the image is displayed without any user interaction. | ||||
323 | // Check for slider-in-use, as we don't wont to rescale while the user is active. | ||||
324 | connect(view.get(), &FITSView::newStatus, [=](const QString &ignored) { | ||||
325 | Q_UNUSED(ignored) | ||||
326 | bool slidersInUse = shadowsSlider->isSliderDown() || midtonesSlider->isSliderDown() || | ||||
327 | highlightsSlider->isSliderDown(); | ||||
328 | if (!slidersInUse) | ||||
329 | { | ||||
330 | rescaleShadows(); | ||||
331 | rescaleMidtones(); | ||||
332 | } | ||||
333 | setStretchUIValues(!slidersInUse); | ||||
334 | }); | ||||
335 | | ||||
336 | return stretchBarLayout; | ||||
337 | } | ||||
338 | | ||||
101 | bool FITSTab::setupView(FITSMode mode, FITSScale filter) | 339 | bool FITSTab::setupView(FITSMode mode, FITSScale filter) | ||
102 | { | 340 | { | ||
103 | if (view.get() == nullptr) | 341 | if (view.get() == nullptr) | ||
104 | { | 342 | { | ||
105 | view.reset(new FITSView(this, mode, filter)); | 343 | view.reset(new FITSView(this, mode, filter)); | ||
106 | view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | 344 | view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||
107 | QVBoxLayout *vlayout = new QVBoxLayout(); | 345 | QVBoxLayout *vlayout = new QVBoxLayout(); | ||
108 | 346 | | |||
Show All 40 Lines | |||||
149 | fitsSplitter->addWidget(scrollFitsPanel); | 387 | fitsSplitter->addWidget(scrollFitsPanel); | ||
150 | fitsSplitter->addWidget(view.get()); | 388 | fitsSplitter->addWidget(view.get()); | ||
151 | 389 | | |||
152 | 390 | | |||
153 | //This code allows the fitsTools to start in a closed state | 391 | //This code allows the fitsTools to start in a closed state | ||
154 | fitsSplitter->setSizes(QList<int>() << 0 << view->width() ); | 392 | fitsSplitter->setSizes(QList<int>() << 0 << view->width() ); | ||
155 | 393 | | |||
156 | vlayout->addWidget(fitsSplitter); | 394 | vlayout->addWidget(fitsSplitter); | ||
395 | vlayout->addLayout(setupStretchBar()); | ||||
157 | 396 | | |||
158 | connect(fitsSplitter, &QSplitter::splitterMoved, histogram, &FITSHistogram::resizePlot); | 397 | connect(fitsSplitter, &QSplitter::splitterMoved, histogram, &FITSHistogram::resizePlot); | ||
159 | 398 | | |||
160 | | ||||
161 | setLayout(vlayout); | 399 | setLayout(vlayout); | ||
162 | connect(view.get(), &FITSView::newStatus, this, &FITSTab::newStatus); | 400 | connect(view.get(), &FITSView::newStatus, this, &FITSTab::newStatus); | ||
163 | connect(view.get(), &FITSView::debayerToggled, this, &FITSTab::debayerToggled); | 401 | connect(view.get(), &FITSView::debayerToggled, this, &FITSTab::debayerToggled); | ||
164 | 402 | | |||
165 | // On Failure to load | 403 | // On Failure to load | ||
166 | connect(view.get(), &FITSView::failed, this, &FITSTab::failed); | 404 | connect(view.get(), &FITSView::failed, this, &FITSTab::failed); | ||
167 | 405 | | |||
168 | return true; | 406 | return true; | ||
▲ Show 20 Lines • Show All 319 Lines • Show Last 20 Lines |