FotoBox
fotobox.cpp
Go to the documentation of this file.
1 /* fotobox.cpp
2  *
3  * Copyright (c) 2016 Thomas Kais
4  *
5  * This file is subject to the terms and conditions defined in
6  * file 'COPYING', which is part of this source code package.
7  */
8 #include "fotobox.h"
9 
10 #include "preferenceprovider.h"
11 #include "preferences.h"
12 #include "ui_fotobox.h"
13 
14 #include <QDate>
15 #include <QDir>
16 #include <QKeyEvent>
17 #include <QPainter>
18 
19 namespace FotoBox {
20 
21 FotoBox::FotoBox(QWidget *parent)
22  : QDialog(parent)
23  , m_ui(new Ui::FotoBoxDialog)
24  , m_countdown(this)
25  , m_workerThread(this)
26  , m_camera(this)
27  , m_photoDir(PreferenceProvider::instance().photoFolder() + QDir::separator())
28 {
29  //setup GUI
30  m_ui->setupUi(this);
31 
32  //e.g. 'FotoBox v1.2.3 (Copyright 2016 Thomas Kais)'
33  setWindowTitle(QStringLiteral("%1 v%2 (Copyright 2016 %3)").arg(QApplication::applicationName(), QApplication::applicationVersion(), QApplication::organizationName()));
34 
35  //set Background Color
36  setStyleSheet(QStringLiteral("#FotoBoxDialog { background-color:%1; }").arg(PreferenceProvider::instance().backgroundColor()));
37 
38  //delete everything on close
39  setAttribute(Qt::WA_DeleteOnClose);
40 
41  //show QStatusBar only when needed (safe space for the photos)
42  connect(m_ui->statusBar, &QStatusBar::messageChanged, this, [&](const QString &i_message) { i_message.isNull() ? m_ui->statusBar->hide() : m_ui->statusBar->show(); });
43  //hide status bar (ATTENTION: don't use at other places, see connect line above)
44  m_ui->statusBar->hide();
45 
46  //with or without buttons?
47  buttons();
48 
49  //Buzzer class (Raspberry Pi GPIO using pigpio)
50 #if defined(BUZZER_AVAILABLE)
51  if (PreferenceProvider::instance().queryInterval() > 0) {
52  buzzer();
53  }
54 #endif
55 
56  //countdown?
57  countdown();
58 }
59 
60 void FotoBox::buttons()
61 {
62  if (PreferenceProvider::instance().showButtons()) {
63  //connect buttons
64  connect(m_ui->btnStart, &QPushButton::clicked, this, &FotoBox::start);
65  connect(m_ui->btnPreferencesDialog, &QPushButton::clicked, this, &FotoBox::preferenceDialog);
66  connect(m_ui->btnQuitApp, &QPushButton::clicked, QCoreApplication::instance(), &QCoreApplication::quit);
67  } else {
68  //hide mouse cursor
69  QGuiApplication::setOverrideCursor(Qt::BlankCursor);
70  //hide buttons
71  m_ui->btnStart->hide();
72  m_ui->btnPreferencesDialog->hide();
73  m_ui->btnQuitApp->hide();
74  }
75 }
76 
77 void FotoBox::buzzer()
78 {
79 #if defined(BUZZER_AVAILABLE)
80  if (m_workerThread.isRunning()) {
81  return;
82  }
83 
84  if (!m_buzzer.initialise()) {
85  //pigpio init not successfull
86  m_ui->statusBar->showMessage(tr("Buzzer isn't working. Please check 'pigpio' deamon."), STATUSBAR_MSG_TIMEOUT);
87  return;
88  }
89 
90  //move to a thread
91  m_buzzer.moveToThread(&m_workerThread);
92 
93  //connect the start signal for buzzer
94  connect(this, &FotoBox::startBuzzer, &m_buzzer, &Buzzer::queryPin);
95  //start fotobox if buzzer was triggered
96  connect(&m_buzzer, &Buzzer::triggered, this, &FotoBox::start);
97 
98  m_workerThread.start();
99 
100  //start query
101  emit startBuzzer();
102 #endif
103 }
104 
105 
106 void FotoBox::countdown()
107 {
108  //initialize countdown
109  m_ui->lcdCountdown->hide();
110 
111  if (PreferenceProvider::instance().countdown() > 0) {
112  //add countdown
113  m_countdown.setStartTime(static_cast<unsigned int>(PreferenceProvider::instance().countdown()));
114  connect(this, &FotoBox::start, &m_countdown, &Countdown::start);
115  connect(&m_countdown, &Countdown::elapsed, this, &FotoBox::photo);
116 
117  //update UI
118  connect(&m_countdown, &Countdown::update, this, [&](const unsigned int i_timeLeft) {
119  //hide photo and show countdown
120  m_ui->lblPhoto->hide();
121  m_ui->lcdCountdown->show();
122  m_ui->lcdCountdown->display(QString::number(i_timeLeft));
123  });
124 
125  //apply font color
126  auto palette = m_ui->lcdCountdown->palette();
127  palette.setColor(QPalette::WindowText, PreferenceProvider::instance().countdownColor());
128  m_ui->lcdCountdown->setPalette(palette);
129  } else {
130  //disable countdown
131  connect(this, &FotoBox::start, this, &FotoBox::photo);
132  }
133 }
134 
135 FotoBox::~FotoBox()
136 {
137 #if defined(BUZZER_AVAILABLE)
138  if (PreferenceProvider::instance().queryInterval() > 0) {
139  //stop query pin
140  m_buzzer.stop();
141  }
142 #endif
143  //terminate and delete Buzzer thread
144  m_workerThread.quit();
145  m_workerThread.wait();
146 
147  delete m_ui;
148 }
149 
150 
151 void FotoBox::keyPressEvent(QKeyEvent *event)
152 {
153  //prevent triggering method too often
154  if (!event->isAutoRepeat()) {
155  //start FotoBox
156  if (std::any_of(m_triggerKey.cbegin(), m_triggerKey.cend(), [&](const Qt::Key i_key) -> bool { return event->key() == i_key; })) {
157  Q_EMIT start();
158  return;
159  }
160 
161  //preferences dialog
162  if (std::any_of(m_preferenceKey.cbegin(), m_preferenceKey.cend(), [&](const Qt::Key i_key) -> bool { return event->key() == i_key; })) {
163  preferenceDialog();
164  return;
165  }
166 
167  //quit application
168  if (std::any_of(m_quitKey.cbegin(), m_quitKey.cend(), [&](const Qt::Key i_key) -> bool { return event->key() == i_key; })) {
169  if (event->modifiers() == Qt::ShiftModifier) {
170  QCoreApplication::quit();
171  }
172  m_ui->statusBar->showMessage(tr("To quit the application, please hold down the Shift key while press Escape key."), STATUSBAR_MSG_TIMEOUT);
173  return;
174  }
175  }
176 }
177 
178 void FotoBox::mouseReleaseEvent(QMouseEvent *event)
179 {
180  //touch support only when no buttons are shown
181  if (!PreferenceProvider::instance().showButtons() && (event->button() == Qt::LeftButton || event->button() == Qt::RightButton)) {
182  Q_EMIT start();
183  return;
184  }
185 
186  QWidget::mouseReleaseEvent(event);
187 }
188 
189 void FotoBox::preferenceDialog()
190 {
191  //Preferences dialog
192  auto *dialog = new Preferences;
193 
194  //restore mouse cursor
195  QApplication::restoreOverrideCursor();
196 
197  //close fotobox and show preferences
198  reject();
199 
200 #if defined(Q_OS_MACOS)
201  //QTBUG-36714: Window can't be closed on Mac OS X after calling showFullScreen()
202  closeFullscreenWindowOnMac();
203 #endif
204 
205  dialog->show();
206 }
207 
208 void FotoBox::photo()
209 {
210  //show label and hide other widgets
211  m_ui->lcdCountdown->hide();
212  QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
213 
214  //remove current photo
215  m_ui->lblPhoto->show();
216  m_ui->lblPhoto->clear();
217  m_ui->lblPhoto->repaint();
218 
219  //take a photo
220  if (m_camera.shootPhoto()) {
221  //load photo
222  loadPhoto(m_photoDir + m_camera.currentPhoto());
223  } else {
224  m_ui->statusBar->showMessage(tr("Error: Taking a photo isn't working correctly!"), STATUSBAR_MSG_TIMEOUT);
225  }
226 
227  //restart countdown and Buzzer
228  if (PreferenceProvider::instance().countdown() > 0) {
229  m_countdown.reset();
230  }
231 #if defined(BUZZER_AVAILABLE)
232  if (PreferenceProvider::instance().queryInterval() > 0) {
233  emit startBuzzer();
234  }
235 #endif
236 }
237 
238 void FotoBox::loadPhoto(const QString &i_filePath)
239 {
240  //try to load the photo shot by camera
241  if (m_photo.load(i_filePath)) {
242  //resize picture to label size
243  QSize size(m_ui->lblPhoto->width(), m_ui->lblPhoto->height());
244 
245  //show photo in grayscale (monochrome photography)
246  if (PreferenceProvider::instance().grayscale()) {
247  const auto grey = m_photo.toImage().convertToFormat(QImage::Format_Grayscale8);
248  m_photo.convertFromImage(grey);
249  }
250 
251  //scale photo and show it immediately
252  m_photo = m_photo.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
253  m_ui->lblPhoto->setPixmap(m_photo);
254  m_ui->lblPhoto->repaint();
255  } else {
256  m_ui->statusBar->showMessage(tr("Couldn't load the photo."), STATUSBAR_MSG_TIMEOUT);
257  }
258 }
259 
260 void FotoBox::drawText(const QString &i_text)
261 {
262  //painter begins painting the paint device immediately!
263  QPainter painter(&m_photo);
264 
265  //set color and font
266  painter.setPen(QPen(PreferenceProvider::instance().backgroundColor()));
267 
268  //calculate best font size
269  QFont font = painter.font();
270  const auto size = calculateFontSize(m_photo.rect().width(), painter.fontMetrics().boundingRect(i_text).width());
271  font.setPointSizeF(font.pointSizeF() * size);
272  painter.setFont(font);
273 
274  //draw text on image
275  painter.drawText(m_photo.rect(), Qt::AlignCenter, i_text);
276 
277  m_ui->lblPhoto->setPixmap(m_photo);
278 }
279 
280 auto FotoBox::calculateFontSize(const double i_width, const double i_widthFont) -> double
281 {
282  auto factor = i_width / i_widthFont;
283  if ((factor < 1) || (factor > COMPARE)) {
284  return factor;
285  }
286  return 0.0;
287 }
288 
289 } // end namespace FotoBox
void queryPin()
Query the Raspberry Pi pin.
Definition: buzzer.cpp:79
void triggered()
Buzzer was pressed.
auto start() -> bool
Start countdown.
Definition: countdown.cpp:57
void elapsed()
Countdown elapsed.
void update(unsigned int)
Update countdown.
The FotoBox class Main class to control UI and controll the process.
Definition: fotobox.h:31
Preference who stores the preferences.
static PreferenceProvider & instance()
get instance (Meyers Singleton)
Preference class to change and store the application settings.
Definition: preferences.h:26
Definition: fotobox.h:20