13 #include "ui_commandlineoptions.h"
14 #include "ui_preferences.h"
16 #include <QColorDialog>
17 #include <QDesktopServices>
18 #include <QFileDialog>
19 #include <QMessageBox>
20 #include <QPrinterInfo>
23 #include <QStandardPaths>
28 : QDialog(parent, Qt::Window)
29 , m_ui(new
Ui::PreferencesDialog)
30 , m_settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::applicationName(), QCoreApplication::applicationName(), this)
31 , m_countdown(this, COUNTDOWN_START_VALUE)
43 setAttribute(Qt::WA_DeleteOnClose);
63 setWindowTitle(tr(
"launching %1 v%2 in %3 seconds").arg(QApplication::applicationName(), QApplication::applicationVersion()).arg(i_timeLeft));
77 const auto availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
78 if (frameGeometry().height() > availableGeometry.height()) {
83 setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), availableGeometry));
90 m_ui->lblPigpioDeamon->setText(tr(
"'pigpio' deamon is reachable"));
92 m_ui->lblPigpioDeamon->setStyleSheet(QStringLiteral(
"QLabel { color : red; }"));
93 m_ui->lblPigpioDeamon->setText(tr(
"'pigpio' deamon is unreachable"));
100 connect(qobject_cast<QApplication*>(QCoreApplication::instance()), &QGuiApplication::applicationStateChanged,
this, [&](
const Qt::ApplicationState state) {
101 if (state != Qt::ApplicationActive) {
115 connect(
m_ui->txtShowColorCD, &QLineEdit::textChanged,
m_ui->txtShowColorCD, &QLineEdit::setToolTip);
120 connect(
m_ui->txtShowColorBG, &QLineEdit::textChanged,
m_ui->txtShowColorBG, &QLineEdit::setToolTip);
126 connect(
m_ui->cmbCameraMode,
static_cast<void (QComboBox::*)(
int)
>(&QComboBox::currentIndexChanged),
this, [&]() {
128 m_ui->txtArgumentLine->setText(m_ui->cmbCameraMode->currentData().toString());
131 connect(
m_ui->btnCameraModeReload, &QAbstractButton::clicked,
this, [&]() {
132 verifyApplication(m_ui->cmbCameraMode->currentText());
135 connect(
m_ui->txtArgumentLine, &QLineEdit::textChanged,
this, [&](
const QString &i_value) {
137 m_ui->cmbCameraMode->setItemData(m_ui->cmbCameraMode->currentIndex(), i_value);
147 connect(
m_ui->buttonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject);
148 connect(
m_ui->buttonBox, &QDialogButtonBox::clicked,
this, [&](QAbstractButton *button) {
150 if (button == m_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
151 restoreDefaultPreferences();
155 connect(m_ui->buttonBox, &QDialogButtonBox::helpRequested,
this, [&]() {
157 QDesktopServices::openUrl( { QStringLiteral(
"https://gitlab.com/tomikais/fotobox/blob/master/README.md") } );
161 void Preferences::setButtonIcons()
164 m_ui->btnChooseDirectory->setIcon(style()->standardIcon(QStyle::SP_DirOpenIcon));
165 m_ui->btnClearDirectoryContent->setIcon(style()->standardIcon(QStyle::SP_DialogDiscardButton));
166 m_ui->btnCameraModeReload->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
167 m_ui->btnArgumentLineHelp->setIcon(style()->standardIcon(QStyle::SP_TitleBarContextHelpButton));
170 void Preferences::startFotoBox()
181 dialog->showFullScreen();
184 void Preferences::mouseMoveEvent(QMouseEvent *event)
189 QWidget::mouseMoveEvent(event);
192 void Preferences::stopCountdownMode()
194 if (m_countdown.isActive()) {
199 setMouseTracking(
false);
200 m_ui->scrollArea->setMouseTracking(
false);
201 m_ui->scrollAreaWidgetContents->setMouseTracking(
false);
202 m_ui->tabWidget->setMouseTracking(
false);
203 m_ui->tabGeneral->setMouseTracking(
false);
204 m_ui->tabExpert->setMouseTracking(
false);
205 m_ui->buttonBox->setMouseTracking(
false);
208 setWindowTitle(QStringLiteral(
"%1 v%2 (Copyright 2016 %3) - ").arg(QApplication::applicationName(), QApplication::applicationVersion(), QApplication::organizationName()) + tr(
"preferences"));
212 void Preferences::loadPreferences()
214 m_settings.beginGroup(QStringLiteral(
"FotoBox"));
215 m_ui->txtPhotoFolder->setText(m_settings.value(m_ui->txtPhotoFolder->objectName(), m_ui->txtPhotoFolder->text()).toString());
216 m_ui->txtPhotoName->setText(m_settings.value(m_ui->txtPhotoName->objectName(), m_ui->txtPhotoName->text()).toString());
217 m_ui->spbCountdown->setValue(m_settings.value(m_ui->spbCountdown->objectName(), m_ui->spbCountdown->value()).toInt());
218 m_ui->txtShowColorCD->setText(m_settings.value(m_ui->txtShowColorCD->objectName(), m_ui->txtShowColorCD->text()).toString());
219 m_ui->chbButtons->setChecked(m_settings.value(m_ui->chbButtons->objectName(), m_ui->chbButtons->isChecked()).toBool());
220 m_ui->txtShowColorBG->setText(m_settings.value(m_ui->txtShowColorBG->objectName(), m_ui->txtShowColorBG->text()).toString());
221 m_settings.endGroup();
223 m_settings.beginGroup(QStringLiteral(
"Buzzer"));
224 m_ui->spbInputPin->setValue(m_settings.value(m_ui->spbInputPin->objectName(), m_ui->spbInputPin->value()).toInt());
225 m_ui->spbOutputPin->setValue(m_settings.value(m_ui->spbOutputPin->objectName(), m_ui->spbOutputPin->value()).toInt());
226 m_ui->spbQueryInterval->setValue(m_settings.value(m_ui->spbQueryInterval->objectName(), m_ui->spbQueryInterval->value()).toInt());
227 m_settings.endGroup();
229 m_settings.beginGroup(QStringLiteral(
"Camera"));
231 const auto data = m_settings.value(m_ui->cmbCameraMode->objectName() + QStringLiteral(
"Data")).toStringList();
232 const auto text = m_settings.value(m_ui->cmbCameraMode->objectName() + QStringLiteral(
"Text")).toStringList();
234 m_ui->cmbCameraMode->clear();
235 for (
int i = 0; i < data.count(); ++i) {
236 m_ui->cmbCameraMode->addItem(text.at(i), data.at(i));
239 m_ui->cmbCameraMode->setCurrentText(m_settings.value(m_ui->cmbCameraMode->objectName(), m_ui->cmbCameraMode->currentText()).toString());
241 m_ui->spbTimout->setValue(m_settings.value(m_ui->spbTimout->objectName(), m_ui->spbTimout->value()).toInt());
242 m_ui->chbGrayscale->setChecked(m_settings.value(m_ui->chbGrayscale->objectName(), m_ui->chbGrayscale->isChecked()).toBool());
243 m_settings.endGroup();
245 m_settings.beginGroup(QStringLiteral(
"PrintSetup"));
246 m_ui->chbPrint->setChecked(m_settings.value(m_ui->chbPrint->objectName(), m_ui->chbPrint->isChecked()).toBool());
247 const auto &printerName = m_settings.value(m_ui->cmbPrinterName->objectName(), m_ui->cmbPrinterName->currentText()).toString();
248 const auto printer = m_ui->cmbPrinterName->findText(printerName);
251 m_ui->cmbPrinterName->setCurrentIndex(printer);
253 m_settings.endGroup();
256 void Preferences::colorDialog()
259 QColorDialog dialog(
this);
261 if (dialog.exec() == QDialog::Accepted) {
263 const auto *button = qobject_cast<QToolButton *>(sender());
264 if (button == m_ui->btnChooseColorCD) {
266 m_ui->txtShowColorCD->setText(dialog.selectedColor().name());
268 if (button == m_ui->btnChooseColorBG) {
270 m_ui->txtShowColorBG->setText(dialog.selectedColor().name());
275 void Preferences::commandLineOptionsDialog()
278 auto *dialog =
new QDialog(
this, Qt::SplashScreen);
279 Ui::CommandLineOptionsDialog ui;
288 void Preferences::chooseDirectory()
291 QFileDialog dialog(
this, tr(
"choose directory"), m_ui->txtPhotoFolder->text());
292 dialog.setFileMode(QFileDialog::Directory);
293 dialog.setOptions(QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
296 if (dialog.exec() == QDialog::Accepted) {
297 const auto path = dialog.directory().absolutePath();
298 if (!verifyPath(path)) {
302 m_ui->txtPhotoFolder->setText(QDir::toNativeSeparators(path));
306 void Preferences::clearDirectoryContent()
309 const QString photoFolder(PreferenceProvider::instance().PreferenceProvider::photoFolder());
312 const auto &text = tr(
"Clear all JPEGs in the photo folder (leave subfolders untouched)?") + QStringLiteral(
"\n\"") + photoFolder +
'\"';
313 const auto &result = QMessageBox::warning(
this, tr(
"WARNING: clear directory content"), text, QMessageBox::Yes | QMessageBox::No);
315 if (result == QMessageBox::Yes) {
317 QDir dir(photoFolder);
320 dir.setNameFilters({ QStringLiteral(
"*.jpg"), QStringLiteral(
"*.jpeg"), QStringLiteral(
"*.jpe"), QStringLiteral(
"*.jif"), QStringLiteral(
"*.jfif"), QStringLiteral(
"*.jfi") });
322 const auto &entryList = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);
323 for(
const auto &dirItem : entryList)
331 auto Preferences::verifyPath(
const QString &i_path) ->
bool
333 QFileInfo path(i_path);
336 if (!path.exists()) {
339 if (!dir.mkpath(QStringLiteral(
"."))) {
340 QMessageBox::warning(
this, tr(
"photo folder") , tr(
"The directory doesn't exist and also couldn't be created."));
347 QMessageBox::warning(
this, tr(
"photo folder") , tr(
"Please select a directory and not a file."));
352 if (!path.isReadable() || !path.isWritable()) {
353 QMessageBox::warning(
this, tr(
"photo folder") , tr(
"Write and read rights are required. Please check the permission of the directory."));
360 void Preferences::showColor(
const QString &i_colorName)
363 QColor color(i_colorName);
366 auto *lineEdit = qobject_cast<QLineEdit *>(sender());
367 if (lineEdit !=
nullptr) {
368 auto palette = lineEdit->palette();
369 palette.setColor(QPalette::Text, color);
370 palette.setColor(QPalette::Base, color);
371 lineEdit->setPalette(palette);
377 void Preferences::savePreferences()
379 m_settings.beginGroup(QStringLiteral(
"FotoBox"));
380 m_settings.setValue(m_ui->txtPhotoFolder->objectName(), PreferenceProvider::instance().photoFolder());
381 m_settings.setValue(m_ui->txtPhotoName->objectName(), PreferenceProvider::instance().photoName());
382 m_settings.setValue(m_ui->spbCountdown->objectName(), PreferenceProvider::instance().countdown());
383 m_settings.setValue(m_ui->txtShowColorCD->objectName(), PreferenceProvider::instance().countdownColor());
384 m_settings.setValue(m_ui->chbButtons->objectName(), PreferenceProvider::instance().showButtons());
385 m_settings.setValue(m_ui->txtShowColorBG->objectName(), PreferenceProvider::instance().backgroundColor());
386 m_settings.endGroup();
388 m_settings.beginGroup(QStringLiteral(
"Buzzer"));
389 m_settings.setValue(m_ui->spbInputPin->objectName(), PreferenceProvider::instance().inputPin());
390 m_settings.setValue(m_ui->spbOutputPin->objectName(), PreferenceProvider::instance().outputPin());
391 m_settings.setValue(m_ui->spbQueryInterval->objectName(), PreferenceProvider::instance().queryInterval());
392 m_settings.endGroup();
394 m_settings.beginGroup(QStringLiteral(
"Camera"));
396 QStringList itemText;
397 QStringList itemData;
398 const auto size = m_ui->cmbCameraMode->count();
399 itemText.reserve(size);
400 itemData.reserve(size);
401 for (
int i = 0; i < size; ++i) {
402 itemText << m_ui->cmbCameraMode->itemText(i);
403 itemData << m_ui->cmbCameraMode->itemData(i).toString();
405 m_settings.setValue(m_ui->cmbCameraMode->objectName() + QStringLiteral(
"Text"), itemText);
406 m_settings.setValue(m_ui->cmbCameraMode->objectName() + QStringLiteral(
"Data"), itemData);
407 m_settings.setValue(m_ui->cmbCameraMode->objectName(), PreferenceProvider::instance().cameraMode());
409 m_settings.setValue(m_ui->spbTimout->objectName(), PreferenceProvider::instance().timeoutValue());
410 m_settings.setValue(m_ui->chbGrayscale->objectName(), PreferenceProvider::instance().grayscale());
411 m_settings.endGroup();
413 m_settings.beginGroup(QStringLiteral(
"PrintSetup"));
414 m_settings.setValue(m_ui->chbPrint->objectName(), PreferenceProvider::instance().print());
415 m_settings.setValue(m_ui->cmbPrinterName->objectName(), PreferenceProvider::instance().printerName());
416 m_settings.endGroup();
419 void Preferences::restoreDefaultPreferences()
422 m_ui->txtPhotoFolder->setText(QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) + QDir::separator() + QApplication::applicationName());
423 m_ui->txtPhotoName->setText(QStringLiteral(
"eventname.jpg"));
424 m_ui->spbCountdown->setValue(DEFAULT_COUNTDOWN_START_VALUE);
425 m_ui->txtShowColorCD->setText(QStringLiteral(
"#ff0000"));
426 m_ui->chbButtons->setChecked(
true);
427 m_ui->txtShowColorBG->setText(QStringLiteral(
"#000000"));
430 m_ui->spbInputPin->setValue(DEFAULT_INPUT_PIN);
431 m_ui->spbOutputPin->setValue(DEFAULT_OUTPUT_PIN);
432 m_ui->spbQueryInterval->setValue(DEFAULT_QUERY_INTERVAL);
435 m_ui->cmbCameraMode->clear();
436 m_ui->cmbCameraMode->addItem(QStringLiteral(
"gphoto2"),
"--capture-image-and-download --keep --force-overwrite --filename %1");
437 m_ui->cmbCameraMode->addItem(QStringLiteral(
"raspistill"),
"--output %1 --width 1920 --height 1080 --quality 75 --nopreview --timeout 1");
438 m_ui->spbTimout->setValue(DEFAULT_TIMEOUT);
439 m_ui->chbGrayscale->setChecked(
false);
443 m_ui->cmbPrinterName->clear();
444 m_ui->cmbPrinterName->addItems(QPrinterInfo::availablePrinterNames());
445 auto defaultPrinter = m_ui->cmbPrinterName->findText(QPrinterInfo::defaultPrinterName());
446 m_ui->cmbPrinterName->setCurrentIndex(defaultPrinter);
447 m_ui->chbPrint->setChecked(
true);
450 void Preferences::verifyApplication(
const QString &i_name)
453 if (i_name.isEmpty()) {
458 if (i_name == QStringLiteral(
"gphoto2")) {
459 const auto message = tr(
"'%1' is missing%2")
460 .arg(i_name, tr(
": <a href='https://github.com/gonzalo/gphoto2-updater/'>Linux (gphoto2 updater)</a>"
461 "/<a href='https://brew.sh/'>macOS (Homebrew)</a>"));
462 if (applicationAvailable(i_name, message)) {
464 m_ui->lblCameraModeInfo->setText(gphotoInfo(i_name));
470 if (i_name == QStringLiteral(
"raspistill")) {
471 const auto message = tr(
"'%1' is missing%2")
472 .arg(i_name, tr(
": <a href='https://www.raspberrypi.org/documentation/usage/camera/'>"
473 "Raspberry Pi Camera Module - enabling the camera</a>"));
474 applicationAvailable(i_name, message);
479 const auto message = tr(
"'%1' is missing%2").arg(i_name, QLatin1String(
""));
480 applicationAvailable(i_name, message);
483 auto Preferences::applicationAvailable(
const QString &i_name,
const QString &i_message) ->
bool
487 process.start(i_name, {}, QIODevice::NotOpen);
488 process.waitForFinished();
489 const auto EXIT_CODE_OUT_OF_RANGE = 255;
490 if (process.exitCode() == EXIT_CODE_OUT_OF_RANGE) {
492 m_ui->lblCameraModeInfo->setStyleSheet(QStringLiteral(
"QLabel { color : red; }"));
493 m_ui->lblCameraModeInfo->setText(i_message);
494 m_ui->cmbCameraMode->setItemData(m_ui->cmbCameraMode->currentIndex(), QBrush(Qt::red), Qt::ForegroundRole);
498 m_ui->lblCameraModeInfo->setStyleSheet(QLatin1String(
""));
499 m_ui->lblCameraModeInfo->clear();
500 m_ui->cmbCameraMode->setItemData(m_ui->cmbCameraMode->currentIndex(), QBrush(Qt::black), Qt::ForegroundRole);
504 auto Preferences::gphotoInfo(
const QString &i_name) -> QString
509 QProcess process(
this);
510 auto env = QProcessEnvironment::systemEnvironment();
511 env.insert(QStringLiteral(
"LC_ALL"), QStringLiteral(
"C"));
512 process.setProcessEnvironment(env);
513 process.start(i_name, {QStringLiteral(
"--version"), QStringLiteral(
"--summary")});
514 process.waitForFinished();
515 const auto output = QString::fromLatin1(process.readAllStandardOutput());
518 QString pattern(QStringLiteral(
"^gphoto2\\s{2,}(?<gphoto2>\\d+\\.\\d+\\.\\d+).*\\n^libgphoto2\\s{2,}(?<libgphoto2>\\d+\\.\\d+\\.\\d+)"));
520 QRegularExpression regex(pattern, QRegularExpression::MultilineOption);
521 auto match = regex.match(output);
522 QString gphoto2(i_name);
523 QString libgphoto2(QStringLiteral(
"libgphoto2"));
524 if (match.hasMatch()) {
525 gphoto2 += QStringLiteral(
" v") + match.captured(gphoto2);
526 libgphoto2 += QStringLiteral(
" v") + match.captured(libgphoto2);
527 result = gphoto2 + QStringLiteral(
" / ") + libgphoto2;
530 pattern = QStringLiteral(
"^[Mm]odel:\\s(.*$)");
532 regex.setPattern(pattern);
533 match = regex.match(output);
534 result += QStringLiteral(
"\n");
535 if (match.hasMatch()) {
536 const auto matches = match.lastCapturedIndex();
537 result += tr(
"camera model: %1").arg(match.captured(1));
540 for (
int i = 2; i <= matches; ++i) {
541 result += QStringLiteral(
" / ") + match.captured(i);
545 result += tr(
"camera model: %1").arg(tr(
"NOT DETECTED"));
static auto checkDeamon() -> bool
Check if pigpio daemon is reachable.
auto start() -> bool
Start countdown.
void elapsed()
Countdown elapsed.
void update(unsigned int)
Update countdown.
void setOutputPin(unsigned int i_value)
set pigpio GPIO output pin
void setInputPin(unsigned int i_value)
set pigpio GPIO input pin
void setQueryInterval(unsigned int i_value)
set how often the pin should be queried
void setTimeoutValue(int i_value)
set timeout value for the camera framework
void setCountdown(int i_value)
set countdown until photo is taken
void setCameraMode(const QString &i_value)
set the camera framework to be used
void setArgumentLine(const QString &i_value)
set camera framework arguments
void setPhotoFolder(const QString &i_value)
set photo output directory
void setPhotoName(const QString &i_value)
set photo name template
void setPrinterName(const QString &i_value)
set printer name
void setShowButtons(bool i_value)
set whether buttons are displayed on the UI
void setCountdownColor(const QString &i_value)
set font color of the countdown
static PreferenceProvider & instance()
get instance (Meyers Singleton)
void setGrayscale(bool i_value)
set grayscale bool (monochrome photography)
void setPrint(bool i_value)
set print on or off
void setBackgroundColor(const QString &i_value)
set background color of the FotoBox UI
Preferences(QWidget *parent=nullptr)
Preferences constructor.
void pigpioDeamon()
Check if pigpio deamon is reachable.
void clearDirectoryContent()
Clear directory content of the photo directory.
void commandLineOptionsDialog()
Show help dialog for gphoto2/raspistill command line options.
Ui::PreferencesDialog * m_ui
void chooseDirectory()
Open a QFileDialog to choose the photo directory.
void restoreDefaultPreferences()
Restore default preferences.
void loadPreferences()
Load application settings from INI file.
void stopCountdownMode()
Stop countdown, stop mouse tracking and set normal window title name.
void showColor(const QString &i_colorName)
Show the given color in QLineEdit.
void connectUi()
Signal & Slot connect.
void windowPosition()
Set the window position.
void verifyApplication(const QString &i_name)
Check camera application availability and provide help if needed.
void setButtonIcons()
Set icons for all QToolButtons.
void colorDialog()
Open a color picker to choose background color for FotoBox.
~Preferences() override
Preferences destructor.
bool verifyPath(const QString &i_path)
Check if the path is useable.
void startFotoBox()
Start the FotoBox.