7#include "imageoverlaycomponent.h"
11#include "skypainter.h"
14#include "fitsviewer/fitsdata.h"
16#include "auxiliary/kspaths.h"
19#include <QTableWidget>
20#include <QImageReader>
23#include <QtConcurrent>
24#include <QRegularExpression>
26#include "ekos/auxiliary/solverutils.h"
27#include "ekos/auxiliary/stellarsolverprofile.h"
49constexpr int UNPROCESSED_INDEX = 0;
50constexpr int OK_INDEX = 4;
73void setupTableItem(
QTableWidget *table,
int row,
int column,
const QString &text,
bool editable =
true)
77 if (column >= NUM_COLUMNS)
84 table->
setItem(row, column, item);
88bool overlaySorter(
const ImageOverlay &o1,
const ImageOverlay &o2)
103 QRegularExpression re3(
"^(\\d+)\\s*h\\s(\\d+)\\s*[m\'\\s]\\s*(\\d+\\.*\\d*)\\s*([s\"]?)$");
105 return re1.match(trimmedInput).hasMatch() ||
106 re2.match(trimmedInput).hasMatch() ||
107 re3.match(trimmedInput).hasMatch();
114 if (!item)
return false;
127 return toDecString(
dms(dec));
135 m_Directory =
dir.absolutePath();
146void ImageOverlayComponent::cellChanged(
int row,
int col)
148 if (!m_Initialized || col < 0 || col >= NUM_COLUMNS || row < 0 || row >= m_ImageOverlayTable->rowCount())
return;
151 if (col == STATUS_COL || col == EAST_TO_RIGHT_COL)
return;
153 QTableWidgetItem *item = m_ImageOverlayTable->item(row, col);
157 QString itemString = item->
text();
158 auto overlay = m_Overlays[row];
162 const bool useHMS = isHMS(itemString);
166 item->
setText(dms(overlay.m_RA).toHMSString());
167 QString msg =
i18n(
"Bad RA string entered for %1. Reset to original value.", overlay.m_Filename);
174 else if (col == DEC_COL)
180 item->
setText(toDecString(overlay.m_DEC));
181 QString msg =
i18n(
"Bad DEC string entered for %1. Reset to original value.", overlay.m_Filename);
185 item->
setText(toDecString(decDMS));
187 else if (col == ORIENTATION_COL)
189 bool angleOK =
false;
190 double angle = itemString.
toDouble(&angleOK);
191 if (!angleOK || angle > 360 || angle < -360)
193 item->
setText(QString(
"%1").arg(overlay.m_Orientation, 0,
'f', 2));
194 QString msg =
i18n(
"Bad orientation angle string entered for %1. Reset to original value.", overlay.m_Filename);
198 else if (col == ARCSEC_PER_PIXEL_COL)
200 bool scaleOK =
false;
201 double scale = itemString.
toDouble(&scaleOK);
202 if (!scaleOK || scale < 0 || scale > 1000)
204 item->
setText(QString(
"%1").arg(overlay.m_ArcsecPerPixel, 0,
'f', 2));
205 QString msg =
i18n(
"Bad scale angle string entered for %1. Reset to original value.", overlay.m_Filename);
213void ImageOverlayComponent::statusCellChanged(
int row)
215 if (row < 0 || row >= m_ImageOverlayTable->rowCount())
return;
217 auto overlay = m_Overlays[row];
224 QComboBox *statusItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(row, STATUS_COL));
229 QTableWidgetItem *raItem = m_ImageOverlayTable->item(row, RA_COL);
231 const bool useHMS = isHMS(raItem->
text());
233 if (!raOK || raDMS.
Degrees() == 0)
235 QString msg =
i18n(
"Cannot set status to OK. Legal non-0 RA value required.");
241 QTableWidgetItem *decItem = m_ImageOverlayTable->item(row, DEC_COL);
242 if (!decItem)
return;
246 QString msg =
i18n(
"Cannot set status to OK. Legal non-0 DEC value required.");
251 bool angleOK =
false;
252 QTableWidgetItem *angleItem = m_ImageOverlayTable->item(row, ORIENTATION_COL);
253 if (!angleItem)
return;
254 const double angle = angleItem->
text().
toDouble(&angleOK);
255 if (!angleOK || angle > 360 || angle < -360)
257 QString msg =
i18n(
"Cannot set status to OK. Legal orientation value required.");
262 bool scaleOK =
false;
263 QTableWidgetItem *scaleItem = m_ImageOverlayTable->item(row, ARCSEC_PER_PIXEL_COL);
264 if (!scaleItem)
return;
265 const double scale = scaleItem->
text().
toDouble(&scaleOK);
266 if (!scaleOK || scale < 0 || scale > 1000)
268 QString msg =
i18n(
"Cannot set status to OK. Legal non-0 a-s/px value required.");
275 QComboBox *statusItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(row, STATUS_COL));
280 m_Overlays[row].m_Status = ImageOverlay::AVAILABLE;
281 m_Overlays[row].m_RA = raDMS.
Degrees();
282 m_Overlays[row].m_DEC = decDMS.
Degrees();
283 m_Overlays[row].m_ArcsecPerPixel = scale;
284 m_Overlays[row].m_Orientation = angle;
285 const QComboBox *ewItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(row, EAST_TO_RIGHT_COL));
286 m_Overlays[row].m_EastToTheRight = ewItem->
currentIndex();
288 if (m_Overlays[row].m_Img.get() ==
nullptr)
291 const QString fullFilename = QString(
"%1/%2").arg(m_Directory).arg(m_Overlays[row].m_Filename);
292 QImage *img = loadImageFile(fullFilename, !m_Overlays[row].m_EastToTheRight);
293 m_Overlays[row].m_Width = img->
width();
294 m_Overlays[row].m_Height = img->
height();
295 m_Overlays[row].m_Img.reset(img);
298 QString msg =
i18n(
"Stored OK status for %1.", m_Overlays[row].m_Filename);
305ImageOverlayComponent::~ImageOverlayComponent()
307 if (m_LoadImagesFuture.isRunning())
309 m_LoadImagesFuture.cancel();
310 m_LoadImagesFuture.waitForFinished();
314void ImageOverlayComponent::selectionChanged()
316 if (m_Initialized && Options::showSelectedImageOverlay())
322 return Options::showImageOverlays();
327#if !defined(KSTARS_LITE)
342 m_ImageOverlayTable = table;
347 m_SolveButton = solveButton;
348 m_TableGroupBox = tableGroupBox;
349 m_SolverProfile = solverProfile;
352 m_StatusDisplay = statusDisplay;
359 initSolverProfiles();
363void ImageOverlayComponent::initSolverProfiles()
365 QString savedOptionsProfiles = QDir(KSPaths::writableLocation(
368 QList<SSolver::Parameters> optionsList;
369 if(QFile(savedOptionsProfiles).exists())
370 optionsList = StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles);
372 optionsList = Ekos::getDefaultAlignOptionsProfiles();
374 m_SolverProfile->clear();
375 for(
auto ¶m : optionsList)
376 m_SolverProfile->addItem(param.listName);
377 m_SolverProfile->setCurrentIndex(Options::solveOptionsProfile());
380void ImageOverlayComponent::updateStatusDisplay(
const QString &message)
382 if (!m_StatusDisplay)
384 m_LogText.insert(0, message);
385 m_StatusDisplay->setPlainText(m_LogText.join(
"\n"));
390void ImageOverlayComponent::updateTable()
394 QDir directory(m_Directory);
395 emit updateLog(
i18n(
"Updating from directory: %1", m_Directory));
396 QStringList images = directory.entryList(QStringList() <<
"*",
QDir::Files);
397 QSet<QString> imageFiles;
398 foreach(QString filename, images)
400 if (!FITSData::readableFilename(filename))
402 imageFiles.
insert(filename);
406 QList<QString> sortedImageFiles;
407 for (
const auto &fn : imageFiles)
409 std::sort(sortedImageFiles.
begin(), sortedImageFiles.
end(), overlaySorter);
412 QList<ImageOverlay> tempOverlays;
413 QMap<QString, int> tempMap;
415 for (
int i = 0; i < m_Overlays.size(); ++i)
417 auto &fname = m_Overlays[i].m_Filename;
418 if (sortedImageFiles.
indexOf(fname) >= 0)
420 tempOverlays.
append(m_Overlays[i]);
421 tempMap[fname] = tempOverlays.
size() - 1;
426 m_Overlays = tempOverlays;
427 m_Filenames = tempMap;
431 for (
const auto &filename : sortedImageFiles)
433 auto item = m_Filenames.find(filename);
434 if (item == m_Filenames.end())
437 ImageOverlay overlay(filename);
438 const int size = m_Filenames.size();
439 m_Overlays.push_back(overlay);
440 m_Filenames[filename] = size;
444 emit updateLog(
i18n(
"%1 overlays (%2 new, %3 deleted) %4 solved", m_Overlays.size(), numNew, numDeleted,
446 m_TableGroupBox->setTitle(
i18n(
"Image Overlays. %1 images, %2 available.", m_Overlays.size(), numAvailable()));
453void ImageOverlayComponent::loadAllImageFiles()
455#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
456 m_LoadImagesFuture =
QtConcurrent::run(&ImageOverlayComponent::loadImageFileLoop,
this);
458 m_LoadImagesFuture =
QtConcurrent::run(
this, &ImageOverlayComponent::loadImageFileLoop);
462void ImageOverlayComponent::loadImageFileLoop()
464 emit updateLog(
i18n(
"Loading image files..."));
465 while (loadImageFile());
467 for (
const auto &o : m_Overlays)
468 if (o.m_Img.get() !=
nullptr)
470 emit updateLog(
i18n(
"%1 image files loaded.", num));
472 m_ImageOverlayTable->setEditTriggers(m_EditTriggers);
473 m_Initialized =
true;
476void ImageOverlayComponent::addTemporaryImageOverlay(
const ImageOverlay &overlay)
478 m_TemporaryOverlays.push_back(overlay);
481QImage *ImageOverlayComponent::loadImageFile (
const QString &fullFilename,
bool mirror)
483 QSharedPointer<QImage> tempImage(
new QImage(fullFilename));
484 if (tempImage.get() ==
nullptr)
return nullptr;
485 int scaleWidth = std::min(tempImage->width(), Options::imageOverlayMaxDimension());
486 QImage *processedImg =
new QImage;
495bool ImageOverlayComponent::loadImageFile()
497 bool updatedSomething =
false;
499 for (
auto &o : m_Overlays)
501 if (o.m_Status == o.ImageOverlay::AVAILABLE && o.m_Img.get() ==
nullptr)
504 QImage *img = loadImageFile(fullFilename, !o.m_EastToTheRight);
506 updatedSomething =
true;
513 return updatedSomething;
518void ImageOverlayComponent::initializeGui()
520 if (!m_ImageOverlayTable)
return;
526 setupTable(m_ImageOverlayTable);
529 for (
int i = 0; i < m_Overlays.size(); ++i)
531 const ImageOverlay &overlay = m_Overlays[row];
533 setupTableItem(m_ImageOverlayTable, row, FILENAME_COL, overlay.m_Filename,
false);
535 QStringList StatusNames =
539 QComboBox *statusBox =
new QComboBox();
540 for (
int i = 0; i < ImageOverlay::NUM_STATUS; ++i)
541 statusBox->
addItem(StatusNames[i]);
545 statusCellChanged(row);
548 m_ImageOverlayTable->setCellWidget(row, STATUS_COL, statusBox);
550 setupTableItem(m_ImageOverlayTable, row, ORIENTATION_COL, QString(
"%1").arg(overlay.m_Orientation, 0,
'f', 2));
551 setupTableItem(m_ImageOverlayTable, row, RA_COL, dms(overlay.m_RA).toHMSString());
552 setupTableItem(m_ImageOverlayTable, row, DEC_COL, toDecString(overlay.m_DEC));
553 setupTableItem(m_ImageOverlayTable, row, ARCSEC_PER_PIXEL_COL, QString(
"%1").arg(overlay.m_ArcsecPerPixel, 0,
'f', 2));
556 setupTableItem(m_ImageOverlayTable, row, WIDTH_COL, QString(
"%1").arg(overlay.m_Width),
false);
557 setupTableItem(m_ImageOverlayTable, row, HEIGHT_COL, QString(
"%1").arg(overlay.m_Height),
false);
559 QComboBox *mirroredBox =
new QComboBox();
569 m_ImageOverlayTable->setCellWidget(row, EAST_TO_RIGHT_COL, mirroredBox);
573 m_ImageOverlayTable->resizeColumnsToContents();
574 m_TableGroupBox->setTitle(
i18n(
"Image Overlays. %1 images, %2 available.", m_Overlays.size(), numAvailable()));
579void ImageOverlayComponent::loadFromUserDB()
581 QList<ImageOverlay *>
list;
584 std::sort(m_Overlays.begin(), m_Overlays.end(), overlaySorter);
587 for (
const auto &o : m_Overlays)
589 m_Filenames[o.m_Filename] = index;
594void ImageOverlayComponent::saveToUserDB()
597 for (
const ImageOverlay &metadata : m_Overlays)
601void ImageOverlayComponent::solveImage(
const QString &filename)
603 if (!m_Initialized)
return;
604 m_SolveButton->setText(
i18n(
"Abort"));
605 const int solverTimeout = Options::imageOverlayTimeout();
607 QString savedOptionsProfiles = QDir(KSPaths::writableLocation(
609 auto profiles = (QFile(savedOptionsProfiles).exists()) ?
610 StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles) :
611 Ekos::getDefaultAlignOptionsProfiles();
613 const int index = m_SolverProfile->currentIndex();
614 auto parameters = index < profiles.size() ? profiles.at(index) : profiles.at(0);
616 parameters.search_radius = parameters.search_radius * 2;
621 if (m_RowsToSolve.size() > 1)
622 emit updateLog(
i18n(
"Solving: %1. %2 in queue.", filename, m_RowsToSolve.size()));
624 emit updateLog(
i18n(
"Solving: %1.", filename));
628 int row = m_RowsToSolve[0];
629 QString raString = m_ImageOverlayTable->item(row, RA_COL)->text().toLatin1().data();
630 QString decString = m_ImageOverlayTable->item(row, DEC_COL)->text().toLatin1().data();
631 QString scaleString = m_ImageOverlayTable->item(row, ARCSEC_PER_PIXEL_COL)->text().toLatin1().data();
634 const bool useHMS = isHMS(raString);
637 bool scaleOK =
false;
638 double scale = scaleString.
toDouble(&scaleOK);
639 scaleOK = scaleOK && scale != 0.00;
642 if (!scaleOK && Options::imageOverlayDefaultScale() > 0.0001)
644 scale = Options::imageOverlayDefaultScale();
650 auto lowScale = scale * 0.75;
651 auto highScale = scale * 1.25;
652 m_Solver->useScale(
true, lowScale, highScale);
657 m_Solver->runSolver(filename);
660void ImageOverlayComponent::tryAgain()
662 m_TryAgainTimer.stop();
663 if (!m_Initialized)
return;
664 if (m_RowsToSolve.size() > 0)
668int ImageOverlayComponent::numAvailable()
671 for (
const auto &o : m_Overlays)
672 if (o.m_Status == ImageOverlay::AVAILABLE)
677void ImageOverlayComponent::show()
679 if (!m_Initialized || !m_ImageOverlayTable)
return;
680 auto selections = m_ImageOverlayTable->selectionModel();
681 if (selections->hasSelection())
683 auto selectedIndexes = selections->selectedIndexes();
684 const int row = selectedIndexes.at(0).row();
685 if (m_Overlays.size() > row && row >= 0)
687 if (m_Overlays[row].m_Status != ImageOverlay::AVAILABLE)
689 emit updateLog(
i18n(
"Can't show %1. Not plate solved.", m_Overlays[row].m_Filename));
692 if (m_Overlays[row].m_Img.get() ==
nullptr)
694 emit updateLog(
i18n(
"Can't show %1. Image not loaded.", m_Overlays[row].m_Filename));
697 const double ra = m_Overlays[row].m_RA;
698 const double dec = m_Overlays[row].m_DEC;
701 auto localTime = KStarsData::Instance()->
geo()->UTtoLT(KStarsData::Instance()->clock()->utc());
702 const dms raDms(ra), decDms(dec);
703 SkyPoint coord(raDms, decDms);
704 coord.apparentCoord(
static_cast<long double>(J2000),
KStars::Instance()->data()->ut().djd());
707 Options::setIsTracking(
false);
710 SkyMap::Instance()->
setFocus(dms(coord.ra()), dms(coord.dec()));
713 double zoomFactor = (400 * 60.0 * 10800.0) / (m_Overlays[row].m_Width * m_Overlays[row].m_ArcsecPerPixel *
dms::PI);
721void ImageOverlayComponent::abortSolving()
723 if (!m_Initialized)
return;
724 m_RowsToSolve.clear();
727 emit updateLog(
i18n(
"Solving aborted."));
728 m_SolveButton->setText(
i18n(
"Solve"));
731void ImageOverlayComponent::startSolving()
733 if (!m_Initialized)
return;
734 if (m_SolveButton->text() ==
i18n(
"Abort"))
739 if (m_Solver && m_Solver->isRunning())
742 if (m_RowsToSolve.size() > 0)
743 m_TryAgainTimer.start(2000);
747 if (m_RowsToSolve.size() == 0)
749 QSet<int> selectedRows;
750 auto selections = m_ImageOverlayTable->selectionModel();
751 if (selections->hasSelection())
754 auto selectedIndexes = selections->selectedIndexes();
755 for (
int i = 0; i < selectedIndexes.count(); ++i)
758 const int row = selectedIndexes.at(i).row();
759 if ((m_Overlays[row].m_Status == ImageOverlay::AVAILABLE) &&
760 !shouldSolveAnyway(m_ImageOverlayTable, row))
762 emit updateLog(
i18n(
"Skipping already solved: %1.", m_Overlays[row].m_Filename));
768 m_RowsToSolve.clear();
769 for (
int row : selectedRows)
770 m_RowsToSolve.push_back(row);
773 if (m_RowsToSolve.size() > 0)
775 const int row = m_RowsToSolve[0];
776 const QString filename =
777 QString(
"%1/%2").
arg(m_Directory).
arg(m_Overlays[row].m_Filename);
778 if ((m_Overlays[row].m_Status == ImageOverlay::AVAILABLE) &&
779 !shouldSolveAnyway(m_ImageOverlayTable, row))
781 emit updateLog(
i18n(
"%1 already solved. Skipping.", filename));
782 m_RowsToSolve.removeFirst();
783 if (m_RowsToSolve.size() > 0)
788 auto img =
new QImage(filename);
789 m_Overlays[row].m_Width = img->width();
790 m_Overlays[row].m_Height = img->height();
791 solveImage(filename);
795void ImageOverlayComponent::reload()
797 if (!m_Initialized)
return;
798 m_Initialized =
false;
799 emit updateLog(
i18n(
"Reloading. Image overlays temporarily disabled."));
804void ImageOverlayComponent::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
805 double elapsedSeconds)
807 disconnect(m_Solver.get(), &SolverUtils::done,
this, &ImageOverlayComponent::solverDone);
808 m_SolveButton->setText(
i18n(
"Solve"));
809 if (m_RowsToSolve.size() == 0)
812 const int solverRow = m_RowsToSolve[0];
813 m_RowsToSolve.removeFirst();
815 QComboBox *statusItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(solverRow, STATUS_COL));
819 m_Overlays[solverRow].m_Status = ImageOverlay::PLATE_SOLVE_FAILURE;
820 statusItem->
setCurrentIndex(
static_cast<int>(m_Overlays[solverRow].m_Status));
825 m_Overlays[solverRow].m_Status = ImageOverlay::PLATE_SOLVE_FAILURE;
826 statusItem->
setCurrentIndex(
static_cast<int>(m_Overlays[solverRow].m_Status));
830 m_Overlays[solverRow].m_Orientation = solution.orientation;
831 m_Overlays[solverRow].m_RA = solution.ra;
832 m_Overlays[solverRow].m_DEC = solution.dec;
833 m_Overlays[solverRow].m_ArcsecPerPixel = solution.pixscale;
834 m_Overlays[solverRow].m_EastToTheRight = solution.parity;
835 m_Overlays[solverRow].m_Status = ImageOverlay::AVAILABLE;
837 QString msg =
i18n(
"Solver success in %1s: RA %2 DEC %3 Scale %4 Angle %5",
846 auto overlay = m_Overlays[solverRow];
847 m_ImageOverlayTable->item(solverRow, RA_COL)->setText(dms(overlay.m_RA).toHMSString());
848 m_ImageOverlayTable->item(solverRow, DEC_COL)->setText(toDecString(overlay.m_DEC));
849 m_ImageOverlayTable->item(solverRow, ARCSEC_PER_PIXEL_COL)->setText(
850 QString(
"%1").arg(overlay.m_ArcsecPerPixel, 0,
'f', 2));
851 m_ImageOverlayTable->item(solverRow, ORIENTATION_COL)->setText(QString(
"%1").arg(overlay.m_Orientation, 0,
'f', 2));
852 QComboBox *ewItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(solverRow, EAST_TO_RIGHT_COL));
855 QComboBox *statusItem =
dynamic_cast<QComboBox*
>(m_ImageOverlayTable->cellWidget(solverRow, STATUS_COL));
859 QString fullFilename = QString(
"%1/%2").
arg(m_Directory).
arg(m_Overlays[solverRow].m_Filename);
860 QImage *img = loadImageFile(fullFilename, !m_Overlays[solverRow].m_EastToTheRight);
861 m_Overlays[solverRow].m_Img.reset(img);
865 if (m_RowsToSolve.size() > 0)
869 emit updateLog(
i18n(
"Done solving. %1 available.", numAvailable()));
870 m_TableGroupBox->setTitle(
i18n(
"Image Overlays. %1 images, %2 available.", m_Overlays.size(), numAvailable()));
void draw(SkyPainter *skyp) override
Draw the object on the SkyMap skyp a pointer to the SkyPainter to use.
bool GetAllImageOverlays(QList< ImageOverlay > *imageOverlayList)
Gets all the image overlay rows from the database.
bool AddImageOverlay(const ImageOverlay &overlay)
Adds a new image overlay row into the database.
bool DeleteAllImageOverlays()
Deletes all image overlay rows from the database.
static KStars * Instance()
SkyComponent represents an object on the sky map.
SkyComposite is a kind of container class for SkyComponent objects.
void setZoomFactor(double factor)
@ Set zoom factor.
void forceUpdate(bool now=false)
Recalculates the positions of objects in the sky, and then repaints the sky map.
void setFocus(SkyPoint *f)
sets the central focus point of the sky map.
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
void setFocusPoint(SkyPoint *f)
set the FocusPoint; the position that is to be the next Destination.
Draws things on the sky, without regard to backend.
virtual bool drawImageOverlay(const QList< ImageOverlay > *imageOverlays, bool useCache=false)=0
drawImageOverlay Draws a user-supplied image onto the skymap
An angle, stored as degrees, but expressible in many ways.
virtual bool setFromString(const QString &s, bool isDeg=true)
Attempt to parse the string argument as a dms value, and set the dms object accordingly.
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
const double & Degrees() const
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
void setText(const QString &text)
void activated(int index)
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
QImage mirrored(bool horizontal, bool vertical) &&
QImage scaledToWidth(int width, Qt::TransformationMode mode) const const
void append(QList< T > &&value)
qsizetype indexOf(const AT &value, qsizetype from) const const
void push_back(parameter_type value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
iterator insert(const T &value)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
double toDouble(bool *ok) const const
QString toLower() const const
QString trimmed() const const
QTextStream & dec(QTextStream &stream)
void setShowGrid(bool show)
void setWordWrap(bool on)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
QString text() const const
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)