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.