1 /** -*- mode: c++ ; c-basic-offset: 2 -*-
2 * @file MainWindow.cpp
3 * @author Sebastien Fourey
4 * @date July 2010
5 * @brief Declaration of the class MainWindow
6 *
7 * This file is part of the ZArt software's source code.
8 *
9 * Copyright Sebastien Fourey / GREYC Ensicaen (2010-...)
10 *
11 * https://foureys.users.greyc.fr/
12 *
13 * This software is a computer program whose purpose is to demonstrate
14 * the possibilities of the GMIC image processing language by offering the
15 * choice of several manipulations on a video stream acquired from a webcam. In
16 * other words, ZArt is a GUI for G'MIC real-time manipulations on the output
17 * of a webcam.
18 *
19 * This software is governed by the CeCILL license under French law and
20 * abiding by the rules of distribution of free software. You can use,
21 * modify and/ or redistribute the software under the terms of the CeCILL
22 * license as circulated by CEA, CNRS and INRIA at the following URL
23 * "http://www.cecill.info". See also the directory "Licence" which comes
24 * with this source code for the full text of the CeCILL license.
25 *
26 * As a counterpart to the access to the source code and rights to copy,
27 * modify and redistribute granted by the license, users are provided only
28 * with a limited warranty and the software's author, the holder of the
29 * economic rights, and the successive licensors have only limited
30 * liability.
31 *
32 * In this respect, the user's attention is drawn to the risks associated
33 * with loading, using, modifying and/or developing or reproducing the
34 * software by the user in light of its specific status of free software,
35 * that may mean that it is complicated to manipulate, and that also
36 * therefore means that it is reserved for developers and experienced
37 * professionals having in-depth computer knowledge. Users are therefore
38 * encouraged to load and test the software's suitability as regards their
39 * requirements in conditions enabling the security of their systems and/or
40 * data to be ensured and, more generally, to use and operate it in the
41 * same conditions as regards security.
42 *
43 * The fact that you are presently reading this means that you have had
44 * knowledge of the CeCILL license and that you accept its terms.
45 */
46 #include <iostream>
47
48 #include <QAction>
49 #include <QActionGroup>
50 #include <QButtonGroup>
51 #include <QComboBox>
52 #include <QDesktopServices>
53 #include <QDir>
54 #include <QDoubleSpinBox>
55 #include <QFileDialog>
56 #include <QFileInfo>
57 #include <QGridLayout>
58 #include <QImageWriter>
59 #include <QInputDialog>
60 #include <QKeySequence>
61 #include <QLabel>
62 #include <QList>
63 #include <QMessageBox>
64 #include <QNetworkAccessManager>
65 #include <QNetworkReply>
66 #include <QNetworkRequest>
67 #include <QSettings>
68 #include <QShortcut>
69 #include <QSlider>
70 #include <QToolTip>
71 #include <QTreeWidgetItemIterator>
72 #include <QUrl>
73 #include <QtXml>
74
75 #include "Common.h"
76 #include "DialogAbout.h"
77 #include "DialogLicense.h"
78 #include "FilterThread.h"
79 #include "FullScreenWidget.h"
80 #include "ImageConverter.h"
81 #include "ImageView.h"
82 #include "MainWindow.h"
83 #include "OutputWindow.h"
84 #include "TreeWidgetPresetItem.h"
85 #include "WebcamSource.h"
86
87 #if QT_VERSION >= 0x050201
88 #define CURRENTDATA(CBOX) (CBOX->currentData())
89 #else
90 #define CURRENTDATA(CBOX) (CBOX->itemData(CBOX->currentIndex()))
91 #endif
92
MainWindow(QWidget * parent)93 MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent), _filterThread(0), _source(Webcam), _currentSource(&_webcam), _currentDir("."), _zeroFPS(false), _presetsCount(0)
94 {
95 setupUi(this);
96
97 _outputWindow = 0;
98
99 delete _frameImageView->layout();
100 _frameImageView->setLayout(new QGridLayout);
101 _frameImageView->layout()->addWidget(_imageView);
102
103 QMargins margins = _rightPanel->layout()->contentsMargins();
104 margins.setTop(0);
105 margins.setBottom(0);
106 _rightPanel->layout()->setContentsMargins(margins);
107 _rightPanelSplitter->setCollapsible(0, false);
108
109 _verticalSplitter->setCollapsible(0, false);
110 _verticalSplitter->setCollapsible(1, true);
111
112 _leftSplitter->setCollapsible(0, false);
113 _leftSplitter->setCollapsible(1, true);
114
115 _fullScreenWidget = new FullScreenWidget(this);
116 _fullScreenWidget->setFavesModel(_cbFaves->model());
117 connect(_fullScreenWidget, SIGNAL(escapePressed()), this, SLOT(toggleFullScreenMode()));
118 connect(_fullScreenWidget->imageView(), SIGNAL(keypointPositionsChanged()), this, SLOT(onFullScreenKeypointsEvent()));
119 connect(_fullScreenWidget->imageView(), SIGNAL(resized(QSize)), this, SLOT(fullScreenImageViewResized(QSize)));
120
121 _displayMode = InWindow;
122
123 QSettings settings;
124
125 // Menu and actions
126 QMenu * menu;
127 menu = menuBar()->addMenu("&File");
128
129 QAction * action = new QAction("&Save presets...", this);
130 action->setShortcut(QKeySequence::SaveAs);
131 #if QT_VERSION >= 0x040600
132 action->setIcon(QIcon::fromTheme("document-save-as"));
133 #endif
134 menu->addAction(action);
135 connect(action, SIGNAL(triggered()), this, SLOT(savePresetsFile()));
136
137 action = new QAction("&Quit", this);
138 action->setShortcut(QKeySequence::Quit);
139 #if QT_VERSION >= 0x040600
140 action->setIcon(QIcon::fromTheme("application-exit", QIcon(":/images/application-exit.png")));
141 #else
142 action->setIcon(QIcon(":/images/application-exit.png"));
143 #endif
144 connect(action, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
145 menu->addAction(action);
146
147 #if QT_VERSION >= 0x040600
148 _tbZoomOriginal->setIcon(QIcon::fromTheme("zoom-original", QIcon(":/images/zoom-original.png")));
149 _tbZoomFit->setIcon(QIcon::fromTheme("zoom-fit-best", QIcon(":/images/zoom-fit-best.png")));
150 _tbCamResolutionsRefresh->setIcon(QIcon::fromTheme("view-refresh"));
151 #else
152 _tbZoomOriginal->setIcon(QIcon(":/images/zoom-original.png"));
153 _tbZoomFit->setIcon(QIcon(":/images/zoom-fit-best.png"));
154 #endif
155
156 connect(_tbCamResolutionsRefresh, SIGNAL(clicked(bool)), this, SLOT(onRefreshCameraResolutions()));
157
158 // Find available cameras, and setup the default one
159 QList<int> cameras = WebcamSource::getWebcamList();
160 int firstUnused = WebcamSource::getFirstUnusedWebcam();
161 initGUIFromCameraList(cameras, firstUnused);
162
163 QSize cameraSize = CURRENTDATA(_comboCamResolution).toSize();
164 if (!cameraSize.isValid()) {
165 _imageView->resize(QSize(640, 480));
166 } else {
167 _imageView->resize(cameraSize);
168 }
169 _frameImageView->resize(_imageView->size());
170
171 // Options menu
172 menu = menuBar()->addMenu("&Options");
173
174 action = menu->addAction("Show right &panel", this, SLOT(onRightPanel(bool)), QKeySequence("Ctrl+F"));
175 action->setCheckable(true);
176 action->setChecked(settings.value("showRightPanel", true).toBool());
177
178 action = menu->addAction("&Full screen", this, SLOT(toggleFullScreenMode()), QKeySequence(Qt::Key_F5));
179 action->setShortcutContext(Qt::ApplicationShortcut);
180
181 _outputWindowAction = new QAction("&Secondary window", this);
182 _outputWindowAction->setShortcut(QKeySequence("Ctrl+O"));
183 _outputWindowAction->setShortcutContext(Qt::ApplicationShortcut);
184 _outputWindowAction->setCheckable(true);
185 connect(_outputWindowAction, SIGNAL(toggled(bool)), this, SLOT(onOutputWindow(bool)));
186 menu->addAction(_outputWindowAction);
187
188 menu->addSeparator();
189 action = menu->addAction("Detect &cameras", this, SLOT(onDetectCameras()));
190 menu->addSeparator();
191
192 // Presets
193 QString presetsConfig = settings.value("Presets", QString("Built-in")).toString();
194
195 QActionGroup * group = new QActionGroup(menu);
196 group->setExclusive(true);
197
198 // Built-in
199 _builtInPresetsAction = new QAction("&Built-in presets", menu);
200 _builtInPresetsAction->setCheckable(true);
201 connect(_builtInPresetsAction, SIGNAL(toggled(bool)), this, SLOT(onUseBuiltinPresets(bool)));
202 group->addAction(_builtInPresetsAction);
203 menu->addAction(_builtInPresetsAction);
204 _builtInPresetsAction->setChecked(true); // Default to Built-in presets
205
206 // File
207 action = menu->addAction("&Presets file...", this, SLOT(setPresetsFile()));
208 action->setCheckable(true);
209 group->addAction(action);
210 QString filename = settings.value("PresetsFile", QString()).toString();
211 if (presetsConfig == "File" && !filename.isEmpty()) {
212 setPresetsFile(filename);
213 action->setChecked(true);
214 }
215
216 // Refresh
217 menu->addSeparator();
218
219 action = menu->addAction("&Reload presets", this, SLOT(onReloadPresets()), QKeySequence("Ctrl+R"));
220 action->setShortcutContext(Qt::ApplicationShortcut);
221
222 // Help menu
223 menu = menuBar()->addMenu("&Help");
224
225 action = menu->addAction("&Visit G'MIC website", this, SLOT(visitGMIC()));
226 action->setIcon(QIcon(":/images/gmic_hat.png"));
227 action = menu->addAction("&License...", this, SLOT(license()));
228 action = menu->addAction("&About...", this, SLOT(about()));
229
230 _imageView->QWidget::resize(_webcam.width(), _webcam.height());
231
232 _bgZoom = new QButtonGroup(this);
233 _bgZoom->setExclusive(true);
234 _tbZoomFit->setCheckable(true);
235 _tbZoomFit->setChecked(true);
236 _tbZoomOriginal->setCheckable(true);
237 _bgZoom->addButton(_tbZoomOriginal);
238 _bgZoom->addButton(_tbZoomFit);
239
240 _cbPreviewMode->addItem("Full", FilterThread::Full);
241 _cbPreviewMode->addItem("Top", FilterThread::TopHalf);
242 _cbPreviewMode->addItem("Left", FilterThread::LeftHalf);
243 _cbPreviewMode->addItem("Bottom", FilterThread::BottomHalf);
244 _cbPreviewMode->addItem("Right", FilterThread::RightHalf);
245 _cbPreviewMode->addItem("Duplicate horizontal", FilterThread::DuplicateHorizontal);
246 _cbPreviewMode->addItem("Duplicate vertical", FilterThread::DuplicateVertical);
247 _cbPreviewMode->addItem("Original", FilterThread::Original);
248
249 #if (QT_VERSION >= 0x040600)
250 _cbPreviewMode->setItemIcon(0, QIcon::fromTheme("view-fullscreen"));
251 _cbPreviewMode->setItemIcon(1, QIcon::fromTheme("go-up"));
252 _cbPreviewMode->setItemIcon(2, QIcon::fromTheme("go-previous"));
253 _cbPreviewMode->setItemIcon(3, QIcon::fromTheme("go-down"));
254 _cbPreviewMode->setItemIcon(4, QIcon::fromTheme("go-next"));
255 _cbPreviewMode->setItemIcon(5, QIcon::fromTheme("edit-copy"));
256 _cbPreviewMode->setItemIcon(6, QIcon::fromTheme("edit-copy"));
257 _cbPreviewMode->setItemIcon(7, QIcon::fromTheme("go-home"));
258 _tbCamera->setIcon(QIcon::fromTheme("camera-photo", QIcon(":/images/camera.png")));
259 #else
260 _tbCamera->setIcon(QIcon(":images/camera.png");
261 #endif
262
263 connect(_cbPreviewMode, SIGNAL(activated(int)), this, SLOT(onPreviewModeChanged(int)));
264
265 connect(_tbZoomOriginal, SIGNAL(clicked()), _imageView, SLOT(zoomOriginal()));
266
267 connect(_tbZoomFit, SIGNAL(clicked()), _imageView, SLOT(zoomFitBest()));
268
269 connect(_pbApply, SIGNAL(clicked()), this, SLOT(commandModified()));
270
271 connect(_tbCamera, SIGNAL(clicked()), this, SLOT(snapshot()));
272
273 _imageView->setMouseTracking(true);
274
275 connect(_imageView, SIGNAL(mousePress(QMouseEvent *)), this, SLOT(imageViewMouseEvent(QMouseEvent *)));
276
277 connect(_imageView, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(imageViewMouseEvent(QMouseEvent *)));
278
279 connect(_imageView, SIGNAL(keypointPositionsChanged()), this, SLOT(onImageViewKeypointsEvent()));
280
281 connect(_imageView, SIGNAL(resized(QSize)), this, SLOT(imageViewResized(QSize)));
282
283 connect(_treeGPresets, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(presetClicked(QTreeWidgetItem *, int)));
284
285 connect(_fullScreenWidget->treeWidget(), SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(presetClicked(QTreeWidgetItem *, int)));
286
287 connect(_commandEditor, SIGNAL(commandModified()), this, SLOT(commandModified()));
288
289 _sliderWebcamSkipFrames->setRange(0, 10);
290 connect(_sliderWebcamSkipFrames, SIGNAL(valueChanged(int)), this, SLOT(setWebcamSkipFrames(int)));
291
292 _sliderVideoSkipFrames->setRange(0, 10);
293 connect(_sliderVideoSkipFrames, SIGNAL(valueChanged(int)), this, SLOT(setVideoSkipFrames(int)));
294
295 _sliderImageFPS->setValue(24);
296 _sliderImageFPS->setToolTip("24 fps");
297 connect(_sliderImageFPS, SIGNAL(valueChanged(int)), this, SLOT(setImageFPS(int)));
298 _zeroFPS = false;
299
300 _sliderVideoFPS->setValue(24);
301 _sliderVideoFPS->setToolTip("24 fps");
302 connect(_sliderVideoFPS, SIGNAL(valueChanged(int)), this, SLOT(setVideoFPS(int)));
303
304 _cbVideoFileLoop->setChecked(true);
305 _videoFile.setLoop(true);
306 connect(_cbVideoFileLoop, SIGNAL(toggled(bool)), this, SLOT(onVideoFileLoop(bool)));
307
308 _webcamParamsWidget->setVisible(_source == Webcam);
309 _imageParamsWidget->setVisible(_source == StillImage);
310 _videoParamsWidget->setVisible(_source == Video);
311
312 connect(_pbOpenImageFile, SIGNAL(clicked()), this, SLOT(onOpenImageFile()));
313 connect(_pbOpenVideoFile, SIGNAL(clicked()), this, SLOT(onOpenVideoFile()));
314
315 // Image filter for the "Save as..." dialog
316 QList<QByteArray> formats = QImageWriter::supportedImageFormats();
317 QList<QByteArray>::iterator it = formats.begin();
318 QList<QByteArray>::iterator end = formats.end();
319 _imageFilters = "Image file (";
320 while (it != end) {
321 _imageFilters += QString("*.") + QString(*it).toLower() + " ";
322 ++it;
323 }
324 _imageFilters.chop(1);
325 _imageFilters += ")";
326
327 _startStopAction = new QAction(this);
328 _startStopAction->setToolTip("Launch processing (Ctrl+P)");
329 _startStopAction->setShortcut(QKeySequence("Ctrl+P"));
330 _startStopAction->setShortcutContext(Qt::ApplicationShortcut);
331 _startStopAction->setCheckable(true);
332
333 _tbPlay->setDefaultAction(_startStopAction);
334 changePlayButtonAppearence(false);
335 connect(_fullScreenWidget, SIGNAL(spaceBarPressed()), _startStopAction, SLOT(toggle()));
336 connect(_startStopAction, SIGNAL(toggled(bool)), this, SLOT(changePlayButtonAppearence(bool)));
337 connect(_startStopAction, SIGNAL(toggled(bool)), this, SLOT(onPlayAction(bool)));
338
339 connect(_commandParamsWidget, SIGNAL(valueChanged()), this, SLOT(onCommandParametersChanged()));
340 connect(_fullScreenWidget->commandParamsWidget(), SIGNAL(valueChanged()), this, SLOT(onCommandParametersChangedFullScreen()));
341
342 if (!settings.value("showRightPanel", true).toBool())
343 _rightPanel->hide();
344
345 if (_comboWebcam->count()) {
346 _webcam.setCameraIndex(CURRENTDATA(_comboWebcam).toInt());
347 // Update actual source capture size
348 _webcam.start();
349 _webcam.stop();
350 }
351
352 // Favorites
353 #if QT_VERSION >= 0x040600
354 _tbAddFave->setIcon(QIcon::fromTheme("list-add", QIcon(":/images/list-add.png")));
355 _tbRemoveFave->setIcon(QIcon::fromTheme("list-remove", QIcon(":/images/list-remove.png")));
356 #else
357 _tbAddFave->setIcon(QIcon(":/images/list-add.png"));
358 _tbRemoveFave->setIcon(QIcon(":/images/list-remove.png"));
359 #endif
360 _tbRenameFave->setIcon(QIcon(":/images/rename.png"));
361 int favesCount = settings.value("Faves/Count", 0).toInt();
362 for (int i = 0; i < favesCount; ++i) {
363 QStringList list = settings.value(QString("Faves/%1").arg(i), QStringList()).toStringList();
364 if (list.size() >= 3) {
365 _cbFaves->addItem(list[0], list);
366 }
367 }
368 _cbFaves->setEnabled(favesCount);
369 _tbRenameFave->setEnabled(favesCount);
370 _tbRemoveFave->setEnabled(favesCount);
371 _fullScreenWidget->cbFaves()->setEnabled(favesCount);
372 _tbAddFave->setEnabled(false);
373
374 connect(_tbAddFave, SIGNAL(clicked(bool)), this, SLOT(onAddFave()));
375 connect(_tbRemoveFave, SIGNAL(clicked(bool)), this, SLOT(onRemoveFave()));
376 connect(_tbRenameFave, SIGNAL(clicked(bool)), this, SLOT(onRenameFave()));
377 connect(_cbFaves, SIGNAL(activated(int)), this, SLOT(onFaveSelected(int)));
378 connect(_fullScreenWidget->cbFaves(), SIGNAL(activated(int)), _cbFaves, SLOT(setCurrentIndex(int)));
379 connect(_fullScreenWidget->cbFaves(), SIGNAL(activated(int)), this, SLOT(onFaveSelected(int)));
380
381 updateWindowTitle();
382 }
383
384 MainWindow::~MainWindow()
385 {
386 QSettings settings;
387 for (int i = 0; i < _cameraDefaultResolutionsIndexes.size(); ++i) {
388 const int index = _cameraDefaultResolutionsIndexes[i];
389 settings.setValue(QString("WebcamSource/DefaultResolutionCam%1").arg(i), (index == -1) ? QSize() : WebcamSource::webcamResolutions(i).at(index));
390 }
391 settings.remove("Faves");
392 settings.setValue("Faves/Count", _cbFaves->count());
393 for (int i = 0; i < _cbFaves->count(); ++i) {
394 settings.setValue(QString("Faves/%1").arg(i), _cbFaves->itemData(i).toStringList());
395 }
396 if (_filterThread) {
397 _filterThread->stop();
398 _filterThreadSemaphore.release();
399 _filterThread->wait();
400 delete _filterThread;
401 }
402 delete _fullScreenWidget;
403 if (_outputWindow) {
404 delete _outputWindow;
405 }
406 }
407
408 void MainWindow::setCurrentPreset(QDomNode node)
409 {
410 QDomNode command = node.namedItem("command");
411 QString text = command.firstChild().toText().data().trimmed();
412 if (text == "_none_") {
413 _commandEditor->clear();
414 _commandEditor->setEnabled(false);
415 } else {
416 _commandEditor->setEnabled(true);
417 _commandEditor->setPlainText(text);
418 }
419 _currentPresetNode = node;
420 if (_displayMode == FullScreen) {
421 _fullScreenWidget->commandParamsWidget()->saveValuesInDOM();
422 _fullScreenWidget->commandParamsWidget()->build(node);
423 _commandParamsWidget->build(node);
424 } else {
425 _commandParamsWidget->saveValuesInDOM();
426 _commandParamsWidget->build(node);
427 }
428 }
429
430 void MainWindow::showOneSourceImage()
431 {
432 _currentSource->capture();
433 cv::Mat * image = _currentSource->image();
434 if (image) {
435 _imageView->imageMutex().lock();
436 QSize size(image->cols, image->rows);
437 if (_imageView->image().size() != size) {
438 _imageView->image() = QImage(size, QImage::Format_RGB888);
439 }
440 ImageConverter::convert(image, &(_imageView->image()));
441 _imageView->imageMutex().unlock();
442 _imageView->checkSize();
443 _imageView->repaint();
444 }
445 }
446
447 void MainWindow::about()
448 {
449 DialogAbout * d = new DialogAbout(this);
450 d->exec();
451 delete d;
452 }
453
454 void MainWindow::visitGMIC()
455 {
456 QDesktopServices::openUrl(QUrl("http://gmic.eu/"));
457 }
458
459 void MainWindow::license()
460 {
461 DialogLicense * d = new DialogLicense(this);
462 d->exec();
463 delete d;
464 }
465
466 QString MainWindow::getPreset(const QString & name)
467 {
468 QDomNodeList list = _presets.elementsByTagName("preset");
469 for (int i = 0; i < list.count(); ++i) {
470 QDomNode n = list.at(i);
471 if (n.attributes().namedItem("name").nodeValue() == name) {
472 QDomNode command = n.namedItem("command");
473 return command.firstChild().toText().data().trimmed();
474 }
475 }
476 return QString();
477 }
478
479 void MainWindow::updateKeypointsInViews()
480 {
481 KeypointList keypoints = (_displayMode == InWindow) ? _commandParamsWidget->keypoints() : _fullScreenWidget->commandParamsWidget()->keypoints();
482 if (_displayMode == InWindow) {
483 _imageView->setKeypoints(keypoints);
484 _imageView->repaint();
485 }
486 _fullScreenWidget->imageView()->setKeypoints(keypoints);
487 if (_displayMode == FullScreen) {
488 _fullScreenWidget->imageView()->repaint();
489 }
490 if (_outputWindow) {
491 _outputWindow->imageView()->setKeypoints(keypoints);
492 }
493 if (_outputWindow && _outputWindow->isVisible() && _outputWindowAction->isChecked()) {
494 _outputWindow->imageView()->repaint();
495 }
496 }
497
498 void MainWindow::onImageAvailable()
499 {
500 if (_displayMode == InWindow) {
501 _imageView->checkSize();
502 _imageView->repaint();
503 }
504 if (_displayMode == FullScreen) {
505 _fullScreenWidget->imageView()->checkSize();
506 _fullScreenWidget->imageView()->repaint();
507 }
508 if (_outputWindow && _outputWindow->isVisible() && _outputWindowAction->isChecked()) {
509 _outputWindow->imageView()->checkSize();
510 _outputWindow->imageView()->repaint();
511 }
512 }
513
514 void MainWindow::play()
515 {
516 int pm = _cbPreviewMode->itemData(_cbPreviewMode->currentIndex()).toInt();
517 FilterThread::PreviewMode previewMode = static_cast<FilterThread::PreviewMode>(pm);
518 ImageView * viewA = (_displayMode == InWindow) ? _imageView : _fullScreenWidget->imageView();
519 ImageView * viewB = 0;
520 if (_outputWindow && _outputWindow->isVisible() && _outputWindowAction->isChecked()) {
521 viewB = _outputWindow->imageView();
522 }
523
524 switch (_source) {
525 case Webcam:
526 _filterThread = new FilterThread(_webcam, _commandEditor->toPlainText(), &viewA->image(), &viewA->imageMutex(), (viewB) ? &viewB->image() : 0, (viewB) ? &viewB->imageMutex() : 0, previewMode,
527 _sliderWebcamSkipFrames->value(), -1, 0);
528 break;
529 case StillImage:
530 _filterThread = new FilterThread(_stillImage, _commandEditor->toPlainText(), &viewA->image(), &viewA->imageMutex(), (viewB) ? &viewB->image() : 0, (viewB) ? &viewB->imageMutex() : 0, previewMode,
531 0, _sliderImageFPS->value(), &_filterThreadSemaphore);
532 break;
533 case Video:
534 _filterThread = new FilterThread(_videoFile, _commandEditor->toPlainText(), &viewA->image(), &viewA->imageMutex(), (viewB) ? &viewB->image() : 0, (viewB) ? &viewB->imageMutex() : 0, previewMode,
535 _sliderVideoSkipFrames->value(), _sliderVideoFPS->value(), 0);
536 break;
537 }
538 connect(_filterThread, SIGNAL(imageAvailable()), this, SLOT(onImageAvailable()));
539 connect(_filterThread, SIGNAL(finished()), this, SLOT(onFilterThreadFinished()));
540 connect(_filterThread, SIGNAL(endOfCapture()), this, SLOT(onEndOfSource()));
541 if (_displayMode == FullScreen) {
542 _filterThread->setArguments(_fullScreenWidget->commandParamsWidget()->valueString());
543 } else {
544 _filterThread->setArguments(_commandParamsWidget->valueString());
545 }
546 imageViewResized(_imageView->size());
547 if (_outputWindow && _outputWindow->isVisible()) {
548 outputWindowImageViewResized(_outputWindow->imageView()->size());
549 }
550 if (_source == StillImage) {
551 _filterThreadSemaphore.release();
552 }
553 updateKeypointsInViews();
554 _filterThread->start();
555 }
556
557 void MainWindow::stop()
558 {
559 if (_filterThread) {
560 _filterThread->stop();
561 _filterThreadSemaphore.release();
562 _filterThread->wait();
563 _filterThread = 0;
564 }
565 }
566
567 void MainWindow::onEndOfSource()
568 {
569 _startStopAction->setChecked(false);
570 }
571
572 void MainWindow::onFilterThreadFinished()
573 {
574 delete sender();
575 }
576
577 void MainWindow::onCommandParametersChanged()
578 {
579 if (_filterThread) {
580 _filterThread->setArguments(_commandParamsWidget->valueString());
581 if (_source == StillImage && _zeroFPS)
582 _filterThreadSemaphore.release();
583 }
584 updateKeypointsInViews();
585 }
586
587 void MainWindow::onCommandParametersChangedFullScreen()
588 {
589 if (_filterThread) {
590 if (_displayMode == FullScreen) {
591 _filterThread->setArguments(_fullScreenWidget->commandParamsWidget()->valueString());
592 if (_source == StillImage && _zeroFPS)
593 _filterThreadSemaphore.release();
594 }
595 }
596 updateKeypointsInViews();
597 }
598
599 void MainWindow::toggleFullScreenMode()
600 {
601 bool running = _filterThread && _filterThread->isRunning();
602 if (_displayMode == FullScreen) {
603 TreeWidgetPresetItem * item = dynamic_cast<TreeWidgetPresetItem *>(_fullScreenWidget->treeWidget()->currentItem());
604 _fullScreenWidget->close();
605 _displayMode = InWindow;
606 stop();
607 _fullScreenWidget->commandParamsWidget()->saveValuesInDOM();
608 _commandParamsWidget->build(_currentPresetNode);
609 if (item) {
610 _treeGPresets->setCurrentItem(findPresetItem(_treeGPresets, item->path()));
611 }
612 } else { // InWindow to FullScreen mode
613 TreeWidgetPresetItem * item = dynamic_cast<TreeWidgetPresetItem *>(_treeGPresets->currentItem());
614 _displayMode = FullScreen;
615 stop();
616 _commandParamsWidget->saveValuesInDOM();
617 _fullScreenWidget->imageView()->image() = _imageView->image();
618 _fullScreenWidget->imageView()->zoomFitBest();
619 _fullScreenWidget->commandParamsWidget()->build(_currentPresetNode);
620 _fullScreenWidget->showFullScreen();
621 if (item) {
622 QTreeWidget * tw = _fullScreenWidget->treeWidget();
623 tw->setCurrentItem(findPresetItem(tw, item->path()));
624 }
625 }
626 if (running) {
627 play();
628 }
629 }
630
631 void MainWindow::onPlayAction(bool on)
632 {
633 if (!on && _filterThread) {
634 stop();
635 if (_source == Webcam) {
636 _webcam.stop();
637 }
638 changePlayButtonAppearence(false);
639 return;
640 }
641 if (on && !_filterThread) {
642 if ((_source == Video && _videoFile.filename().isEmpty()) || (_source == StillImage && _stillImage.filename().isEmpty())) {
643 QMessageBox::information(this, "Information", "No input file.\nPlease select one first.");
644 _tabParams->setCurrentIndex(0);
645 _startStopAction->setChecked(false);
646 } else {
647 if (_source == Webcam) {
648 _webcam.start();
649 }
650 play();
651 changePlayButtonAppearence(true);
652 }
653 return;
654 }
655 }
656
657 void MainWindow::onComboSourceChanged(int i)
658 {
659 static bool firstRun = true;
660 bool running = _filterThread && _filterThread->isRunning();
661 if (running) {
662 stop();
663 }
664 _source = static_cast<Source>(_comboSource->itemData(i).toInt());
665 switch (_source) {
666 case Webcam:
667 _currentSource = &_webcam;
668 break;
669 case StillImage:
670 _currentSource = &_stillImage;
671 _webcam.stop();
672 break;
673 case Video:
674 _currentSource = &_videoFile;
675 _webcam.stop();
676 break;
677 }
678 _webcamParamsWidget->setVisible(_source == Webcam);
679 _imageParamsWidget->setVisible(_source == StillImage);
680 _videoParamsWidget->setVisible(_source == Video);
681 updateWindowTitle();
682 if (_source == StillImage && _stillImage.filename().isEmpty() && (!firstRun || WebcamSource::getCachedWebcamList().size())) {
683 onOpenImageFile();
684 }
685 if (_source == Video && _videoFile.filename().isEmpty())
686 onOpenVideoFile();
687 if (running) {
688 if (_source == Webcam) {
689 _webcam.start();
690 }
691 play();
692 } else {
693 showOneSourceImage();
694 }
695 firstRun = false;
696 }
697
698 void MainWindow::setInputImage(QString filepath)
699 {
700 _stillImage.loadImage(filepath);
701 }
702
703 void MainWindow::setInputVideo(QString filepath)
704 {
705 _videoFile.loadVideoFile(filepath);
706 }
707
708 void MainWindow::onOpenImageFile()
709 {
710 QString filename;
711 filename = QFileDialog::getOpenFileName(this, "Select an image file", _stillImage.filePath().isEmpty() ? _videoFile.filePath() : _stillImage.filePath(),
712 "Image files (*.bmp *.gif *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm *.svg)");
713 if (filename.isEmpty()) {
714 return;
715 }
716 if (_source == StillImage && _filterThread) {
717 stop();
718 if (_stillImage.loadImage(filename)) {
719 updateWindowTitle();
720 }
721 play();
722 } else {
723 if (_stillImage.loadImage(filename)) {
724 updateWindowTitle();
725 showOneSourceImage();
726 }
727 }
728 }
729
730 void MainWindow::onOpenVideoFile()
731 {
732 QString filename;
733 filename = QFileDialog::getOpenFileName(this, "Select a video file", _videoFile.filePath().isEmpty() ? _stillImage.filePath() : _videoFile.filePath(), "Video files (*.avi *.mpg)");
734 if (filename.isEmpty())
735 return;
736 if (_source == Video && _filterThread) {
737 stop();
738 if (_videoFile.loadVideoFile(filename)) {
739 updateWindowTitle();
740 play();
741 }
742 } else {
743 if (_videoFile.loadVideoFile(filename)) {
744 updateWindowTitle();
745 showOneSourceImage();
746 }
747 }
748 }
749
750 void MainWindow::updateWindowTitle()
751 {
752 QString name;
753 switch (_source) {
754 case Webcam:
755 setWindowTitle(QString("ZArt %1 (Webcam %2x%3)").arg(ZART_VERSION_STRING).arg(_currentSource->width()).arg(_currentSource->height()));
756 break;
757 case StillImage:
758 name = QFileInfo(_stillImage.filename()).fileName();
759 if (name.isEmpty())
760 setWindowTitle(QString("ZArt %1 (No input file)").arg(ZART_VERSION_STRING));
761 else
762 setWindowTitle(QString("ZArt %1 (%2 %3x%4)").arg(ZART_VERSION_STRING).arg(name).arg(_currentSource->width()).arg(_currentSource->height()));
763 break;
764 case Video:
765 name = QFileInfo(_videoFile.filename()).fileName();
766 if (name.isEmpty())
767 setWindowTitle(QString("ZArt %1 (No input file)").arg(ZART_VERSION_STRING));
768 else
769 setWindowTitle(QString("ZArt %1 (%2 %3x%4)").arg(ZART_VERSION_STRING).arg(name).arg(_currentSource->width()).arg(_currentSource->height()));
770 break;
771 }
772 }
773
774 void MainWindow::onVideoFileLoop(bool on)
775 {
776 _videoFile.setLoop(on);
777 }
778
779 void MainWindow::changePlayButtonAppearence(bool on)
780 {
781 if (on) {
782 #if QT_VERSION >= 0x040600
783 _startStopAction->setIcon(QIcon::fromTheme("media-playback-stop", QIcon(":/images/media-playback-stop.png")));
784 #else
785 _startStopAction->setIcon(QIcon(":/images/media-playback-stop.png"));
786 #endif
787 _startStopAction->setToolTip("Stop processing (Ctrl+P)");
788 } else {
789 #if QT_VERSION >= 0x040600
790 _startStopAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(":/images/media-playback-start.png")));
791 #else
792 _startStopAction->setIcon(QIcon(":/images/media-playback-start.png"));
793 #endif
794 _startStopAction->setToolTip("Launch processing (Ctrl+P)");
795 }
796 }
797
798 void MainWindow::imageViewMouseEvent(QMouseEvent * event)
799 {
800 int buttons = 0;
801 if (event->buttons() & Qt::LeftButton) {
802 buttons |= 1;
803 }
804 if (event->buttons() & Qt::RightButton) {
805 buttons |= 2;
806 }
807 if (event->buttons() & Qt::MidButton) {
808 buttons |= 4;
809 }
810 if (_filterThread) {
811 _filterThread->setMousePosition(event->x(), event->y(), buttons);
812 }
813 if (_source == StillImage && _zeroFPS) {
814 _filterThreadSemaphore.release();
815 }
816 }
817
818 void MainWindow::imageViewResized(QSize size)
819 {
820 if (!_outputWindow || !_outputWindow->isVisible()) {
821 if (_filterThread) {
822 _filterThread->setViewSize(size);
823 }
824 }
825 }
826
827 void MainWindow::outputWindowImageViewResized(QSize size)
828 {
829 if (size.isValid() && _filterThread) {
830 _filterThread->setViewSize(size);
831 }
832 }
833
834 void MainWindow::fullScreenImageViewResized(QSize size)
835 {
836 if (size.isValid() && _filterThread) {
837 _filterThread->setViewSize(size);
838 }
839 }
840
841 void MainWindow::commandModified()
842 {
843 if (_filterThread && _filterThread->isRunning()) {
844 stop();
845 play();
846 }
847 }
848
849 void MainWindow::presetClicked(QTreeWidgetItem * item, int)
850 {
851 TreeWidgetPresetItem * presetItem = dynamic_cast<TreeWidgetPresetItem *>(item);
852 if (!presetItem) {
853 return;
854 }
855 if (presetItem->node().isNull()) { // A "folder" has been clicked, not a preset
856 _tbAddFave->setEnabled(false);
857 return;
858 }
859 _tbAddFave->setEnabled(true);
860 setCurrentPreset(presetItem->node());
861 _tabParams->setCurrentIndex(1);
862 int previewMode = _cbPreviewMode->itemData(_cbPreviewMode->currentIndex()).toInt();
863 if (previewMode == FilterThread::Original) {
864 _cbPreviewMode->setCurrentIndex(0);
865 onPreviewModeChanged(0);
866 }
867 commandModified();
868 }
869
870 void MainWindow::snapshot()
871 {
872 if (_filterThread)
873 _startStopAction->setChecked(false);
874 QString filename = QFileDialog::getSaveFileName(this, "Save image as...", _currentDir, _imageFilters, 0, 0);
875 if (!filename.isEmpty()) {
876 QFileInfo info(filename);
877 _currentDir = info.filePath();
878 QImageWriter writer(filename);
879 _imageView->imageMutex().lock();
880 writer.write(_imageView->image());
881 _imageView->imageMutex().unlock();
882 }
883 }
884
885 void MainWindow::setWebcamSkipFrames(int i)
886 {
887 _sliderWebcamSkipFrames->setToolTip(QString("%1").arg(i));
888 QToolTip::showText(_sliderWebcamSkipFrames->mapToGlobal(QPoint(0, 0)), QString("%1").arg(i), _sliderWebcamSkipFrames);
889 if (_filterThread) {
890 _filterThread->setFrameSkip(i);
891 }
892 }
893
894 void MainWindow::setVideoSkipFrames(int i)
895 {
896 _sliderVideoSkipFrames->setToolTip(QString("%1").arg(i));
897 QToolTip::showText(_sliderVideoSkipFrames->mapToGlobal(QPoint(0, 0)), QString("%1").arg(i), _sliderVideoSkipFrames);
898 if (_filterThread) {
899 _filterThread->setFrameSkip(i);
900 }
901 }
902
903 void MainWindow::setImageFPS(int fps)
904 {
905 _sliderImageFPS->setToolTip(QString("%1 fps").arg(fps));
906 QToolTip::showText(_sliderImageFPS->mapToGlobal(QPoint(0, 0)), QString("%1 fps").arg(fps), _sliderImageFPS);
907 if (_filterThread) {
908 _filterThread->setFPS(fps);
909 }
910 if (_source == StillImage && _zeroFPS) {
911 _filterThreadSemaphore.release();
912 }
913 _zeroFPS = !fps;
914 }
915
916 void MainWindow::setVideoFPS(int fps)
917 {
918 _sliderVideoFPS->setToolTip(QString("%1 fps").arg(fps));
919 QToolTip::showText(_sliderVideoFPS->mapToGlobal(QPoint(0, 0)), QString("%1 fps").arg(fps), _sliderVideoFPS);
920 if (_filterThread) {
921 _filterThread->setFPS(fps);
922 }
923 }
924
925 void MainWindow::onWebcamComboChanged(int index)
926 {
927 index = _comboWebcam->itemData(index).toInt();
928 if (_source == Webcam && _filterThread && _filterThread->isRunning()) {
929 stop();
930 _webcam.stop();
931 updateCameraResolutionCombo();
932 _webcam.setCaptureSize(CURRENTDATA(_comboCamResolution).toSize());
933 _webcam.setCameraIndex(index);
934 _webcam.start();
935 play();
936 } else {
937 updateCameraResolutionCombo();
938 _webcam.setCaptureSize(CURRENTDATA(_comboCamResolution).toSize());
939 _webcam.setCameraIndex(index);
940 }
941 }
942
943 void MainWindow::onWebcamResolutionComboChanged(int i)
944 {
945 int currentCam = _comboWebcam->currentIndex();
946 _cameraDefaultResolutionsIndexes[currentCam] = i;
947 QSize resolution = CURRENTDATA(_comboCamResolution).toSize();
948 if (_source == Webcam && _filterThread && _filterThread->isRunning()) {
949 stop();
950 _webcam.setCaptureSize(resolution);
951 play();
952 } else {
953 _webcam.setCaptureSize(resolution);
954 // Update actual source capture size
955 _webcam.start();
956 _webcam.stop();
957 }
958 updateWindowTitle();
959 }
960
961 void MainWindow::setPresets(const QDomElement & domE)
962 {
963 _treeGPresets->clear();
964 _presetsCount = 0;
965 addPresets(domE, 0);
966
967 QString label;
968 label.sprintf("Presets (%d)", _presetsCount);
969 _treeGPresets->setHeaderLabel(label);
970 _fullScreenWidget->treeWidget()->setHeaderLabel(label);
971
972 _fullScreenWidget->treeWidget()->clear();
973 int count = _treeGPresets->topLevelItemCount();
974 for (int i = 0; i < count; ++i) {
975 _fullScreenWidget->treeWidget()->addTopLevelItem(_treeGPresets->topLevelItem(i)->clone());
976 }
977 }
978
979 void MainWindow::addPresets(const QDomElement & domE, TreeWidgetPresetItem * parent)
980 {
981 for (QDomNode node = domE.firstChild(); !node.isNull(); node = node.nextSibling()) {
982 QString name = node.attributes().namedItem("name").nodeValue();
983 if (node.nodeName() == QString("preset")) {
984 QStringList strList;
985 strList << name;
986 if (!parent) {
987 _treeGPresets->addTopLevelItem(new TreeWidgetPresetItem(strList, node));
988 } else {
989 new TreeWidgetPresetItem(parent, strList, node);
990 }
991 ++_presetsCount;
992 } else if (node.nodeName() == QString("preset_group")) {
993 TreeWidgetPresetItem * folder = new TreeWidgetPresetItem(QStringList() << name);
994 _treeGPresets->addTopLevelItem(folder);
995 addPresets(node.toElement(), folder);
996 }
997 }
998 }
999
1000 void MainWindow::setPresetsFile(const QString & file)
1001 {
1002 QString filename = file;
1003 if (filename.isEmpty()) {
1004 QSettings settings;
1005 QString s = settings.value("PresetsFile").toString();
1006 QString dir = ".";
1007 if (QFileInfo(s).exists())
1008 dir = QFileInfo(s).absolutePath();
1009 filename = QFileDialog::getOpenFileName(this, "Open a presets file", dir, "Preset files (*.xml)");
1010 }
1011 if (!filename.isEmpty()) {
1012 QSettings settings;
1013 settings.setValue("PresetsFile", filename);
1014 settings.setValue("Presets", "File");
1015
1016 QFile presetsTreeFile(filename);
1017 QString error;
1018 presetsTreeFile.open(QIODevice::ReadOnly);
1019 _presets.setContent(&presetsTreeFile, false, &error);
1020 presetsTreeFile.close();
1021 setPresets(_presets.elementsByTagName("document").at(0).toElement());
1022 } else {
1023 _builtInPresetsAction->setChecked(true);
1024 }
1025 }
1026
1027 void MainWindow::onUseBuiltinPresets(bool on)
1028 {
1029 if (on) {
1030 QFile presetsTreeFile(":/presets.xml");
1031 QString error;
1032 presetsTreeFile.open(QIODevice::ReadOnly);
1033 _presets.setContent(&presetsTreeFile, false, &error);
1034 presetsTreeFile.close();
1035 setPresets(_presets.elementsByTagName("document").at(0).toElement());
1036 QSettings().setValue("Presets", "Built-in");
1037 }
1038 }
1039
1040 void MainWindow::onReloadPresets()
1041 {
1042 if (_builtInPresetsAction->isChecked()) {
1043 onUseBuiltinPresets(true);
1044 return;
1045 }
1046 QSettings settings;
1047 QString filename = settings.value("PresetsFile").toString();
1048 setPresetsFile(filename);
1049 }
1050
1051 void MainWindow::savePresetsFile()
1052 {
1053 QString filename = QFileDialog::getSaveFileName(this, "Save presets file", ".", "Preset files (*.xml)");
1054 if (!filename.isEmpty()) {
1055 QFile presetsFile(filename);
1056 presetsFile.open(QIODevice::WriteOnly);
1057 presetsFile.write(_presets.toByteArray());
1058 presetsFile.close();
1059 }
1060 }
1061
1062 void MainWindow::onPreviewModeChanged(int index)
1063 {
1064 int mode = _cbPreviewMode->itemData(index).toInt();
1065 if (_filterThread)
1066 _filterThread->setPreviewMode(static_cast<FilterThread::PreviewMode>(mode));
1067 }
1068
1069 void MainWindow::onRightPanel(bool on)
1070 {
1071 if (on && !_rightPanel->isVisible()) {
1072 _rightPanel->show();
1073 QSettings().setValue("showRightPanel", true);
1074 return;
1075 }
1076 if (!on && _rightPanel->isVisible()) {
1077 _rightPanel->hide();
1078 QSettings().setValue("showRightPanel", false);
1079 return;
1080 }
1081 }
1082
1083 void MainWindow::updateCameraResolutionCombo()
1084 {
1085 disconnect(_comboCamResolution, SIGNAL(currentIndexChanged(int)), this, 0);
1086 int index = _comboWebcam->currentIndex();
1087 _comboCamResolution->clear();
1088 const QList<QSize> & resolutions = WebcamSource::webcamResolutions(index);
1089 QList<QSize>::const_iterator it = resolutions.begin();
1090 while (it != resolutions.end()) {
1091 _comboCamResolution->addItem(QString("%1 x %2").arg(it->width()).arg(it->height()), *it);
1092 ++it;
1093 }
1094 const int defaultResIndex = index >= 0 ? _cameraDefaultResolutionsIndexes[index] : -1;
1095 if (defaultResIndex >= 0) {
1096 _comboCamResolution->setCurrentIndex(defaultResIndex);
1097 }
1098 connect(_comboCamResolution, SIGNAL(currentIndexChanged(int)), this, SLOT(onWebcamResolutionComboChanged(int)));
1099 }
1100
1101 TreeWidgetPresetItem * MainWindow::findPresetItem(QTreeWidget * tree, const QString & folder, const QString & name)
1102 {
1103 if (folder.isEmpty()) {
1104 QTreeWidgetItemIterator it(tree);
1105 while (*it) {
1106 if ((*it)->text(0) == name) {
1107 return dynamic_cast<TreeWidgetPresetItem *>(*it);
1108 }
1109 ++it;
1110 }
1111 } else {
1112 QTreeWidgetItemIterator it(tree);
1113 while (*it) {
1114 if ((*it)->text(0) == folder) {
1115 int count = (*it)->childCount();
1116 for (int i = 0; i < count; ++i) {
1117 QTreeWidgetItem * item = (*it)->child(i);
1118 if (item->text(0) == name) {
1119 return dynamic_cast<TreeWidgetPresetItem *>(item);
1120 }
1121 }
1122 }
1123 ++it;
1124 }
1125 }
1126 return 0;
1127 }
1128
1129 TreeWidgetPresetItem * MainWindow::findPresetItem(QTreeWidget * tree, const QStringList & path)
1130 {
1131 switch (path.size()) {
1132 case 1:
1133 return findPresetItem(tree, QString(), path[0]);
1134 break;
1135 case 2:
1136 return findPresetItem(tree, path[0], path[1]);
1137 break;
1138 default:
1139 return 0;
1140 }
1141 }
1142
1143 QString MainWindow::faveUniqueName(const QString & name)
1144 {
1145 int count = _cbFaves->count();
1146 int n = 1;
1147 for (int i = 0; i < count; ++i) {
1148 QString s = _cbFaves->itemText(i);
1149 s.replace(QRegExp(" \\(\\d*\\)"), "");
1150 if (s == name) {
1151 ++n;
1152 }
1153 }
1154 if (n > 1) {
1155 return QString("%1 (%2)").arg(name).arg(n);
1156 } else {
1157 return name;
1158 }
1159 }
1160
1161 void MainWindow::onRefreshCameraResolutions()
1162 {
1163 WebcamSource::clearSavedSettings();
1164 onDetectCameras();
1165 }
1166
1167 void MainWindow::onDetectCameras()
1168 {
1169 if (_source == Webcam && _filterThread) {
1170 stop();
1171 _webcam.stop();
1172 }
1173 centralWidget()->setEnabled(false);
1174 statusBar()->showMessage("Updating camera resolutions list...");
1175 menuBar()->setEnabled(false);
1176 QList<int> camList = WebcamSource::getWebcamList();
1177 int firstUnused = WebcamSource::getFirstUnusedWebcam();
1178 WebcamSource::retrieveWebcamResolutions(camList, 0, statusBar());
1179 initGUIFromCameraList(camList, firstUnused);
1180 statusBar()->showMessage(QString());
1181 centralWidget()->setEnabled(true);
1182 menuBar()->setEnabled(true);
1183 }
1184
1185 void MainWindow::initGUIFromCameraList(const QList<int> & camList, int firstUnused)
1186 {
1187 disconnect(_comboWebcam, SIGNAL(currentIndexChanged(int)), this, 0);
1188 disconnect(_comboSource, SIGNAL(currentIndexChanged(int)), this, 0);
1189
1190 QSettings settings;
1191 _comboSource->clear();
1192 _comboWebcam->clear();
1193
1194 if (camList.size() == 0) {
1195 _tabParams->setCurrentIndex(0);
1196 _comboSource->addItem("Image", QVariant(StillImage));
1197 _comboSource->addItem("Video file", QVariant(Video));
1198 #if QT_VERSION >= 0x040600
1199 _comboSource->setItemIcon(0, QIcon::fromTheme("image-x-generic"));
1200 _comboSource->setItemIcon(1, QIcon::fromTheme("video-x-generic"));
1201 #endif
1202 if (_source == Webcam) {
1203 _source = StillImage;
1204 _currentSource = &_stillImage;
1205 }
1206 } else {
1207 _tabParams->setCurrentIndex(0);
1208 _comboSource->addItem("Webcam", QVariant(Webcam));
1209 _comboSource->addItem("Image", QVariant(StillImage));
1210 _comboSource->addItem("Video file", QVariant(Video));
1211 #if QT_VERSION >= 0x040600
1212 _comboSource->setItemIcon(0, QIcon::fromTheme("camera-web"));
1213 _comboSource->setItemIcon(1, QIcon::fromTheme("image-x-generic"));
1214 _comboSource->setItemIcon(2, QIcon::fromTheme("video-x-generic"));
1215 #endif
1216 _comboWebcam->setEnabled(camList.size() > 1);
1217 _cameraDefaultResolutionsIndexes.clear();
1218 for (int iCam = 0; iCam < camList.size(); ++iCam) {
1219 if (WebcamSource::webcamResolutions(iCam).isEmpty()) {
1220 _cameraDefaultResolutionsIndexes.push_back(-1);
1221 continue;
1222 }
1223 _comboWebcam->addItem(QString("Webcam %1").arg(camList[iCam]), QVariant(camList[iCam]));
1224 QSize size = settings.value(QString("WebcamSource/DefaultResolutionCam%1").arg(iCam), QSize()).toSize();
1225 if (size.isValid() && WebcamSource::webcamResolutions(iCam).contains(size)) {
1226 _cameraDefaultResolutionsIndexes.push_back(WebcamSource::webcamResolutions(iCam).indexOf(size));
1227 } else {
1228 _cameraDefaultResolutionsIndexes.push_back(WebcamSource::webcamResolutions(iCam).size() - 1);
1229 }
1230 }
1231 _comboWebcam->setCurrentIndex((firstUnused == -1) ? 0 : firstUnused);
1232 connect(_comboWebcam, SIGNAL(currentIndexChanged(int)), this, SLOT(onWebcamComboChanged(int)));
1233 onWebcamComboChanged(0);
1234 }
1235 connect(_comboSource, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboSourceChanged(int)));
1236 onComboSourceChanged(0);
1237 }
1238
1239 void MainWindow::onOutputWindow(bool on)
1240 {
1241 if (on) {
1242 if (!_outputWindow) {
1243 _outputWindow = new OutputWindow(this);
1244 connect(_outputWindow, SIGNAL(aboutToClose()), this, SLOT(onOutputWindowClosing()));
1245 connect(_outputWindow->imageView(), SIGNAL(keypointPositionsChanged()), this, SLOT(onOutputWindowKeypointsEvent()));
1246 connect(_outputWindow->imageView(), SIGNAL(resized(QSize)), this, SLOT(outputWindowImageViewResized(QSize)));
1247 }
1248 if (!_outputWindow->isVisible()) {
1249 bool running = _filterThread && _filterThread->isRunning();
1250 stop();
1251 _outputWindow->show();
1252 if (running) {
1253 play();
1254 }
1255 }
1256 }
1257 if (!on && _outputWindow && _outputWindow->isVisible()) {
1258 _outputWindow->onShowFullscreen(false);
1259 _outputWindow->close();
1260 imageViewResized(_imageView->size());
1261 }
1262 }
1263
1264 void MainWindow::onOutputWindowClosing()
1265 {
1266 _outputWindowAction->setChecked(false);
1267 }
1268
1269 void MainWindow::onAddFave()
1270 {
1271 TreeWidgetPresetItem * item = dynamic_cast<TreeWidgetPresetItem *>(_treeGPresets->currentItem());
1272 QStringList faveData;
1273 if (item && (!item->node().isNull())) {
1274 // "Display Name", "Folder", "Filter Name", "Parameter0", "Parameter1", "Parameter3", etc.
1275 faveData.append(faveUniqueName(item->text(0)));
1276 QTreeWidgetItem * parent = item->parent();
1277 if (parent) {
1278 faveData.append(parent->text(0));
1279 } else {
1280 faveData.append("");
1281 }
1282 faveData.append(item->text(0));
1283 faveData.append(_commandParamsWidget->valueStringList());
1284 _cbFaves->addItem(faveData[0], faveData);
1285 _cbFaves->setCurrentIndex(_cbFaves->count() - 1);
1286 _cbFaves->setEnabled(true);
1287 _fullScreenWidget->cbFaves()->setEnabled(true);
1288 _cbFaves->model()->sort(0, Qt::AscendingOrder);
1289 _tbRemoveFave->setEnabled(true);
1290 _tbRenameFave->setEnabled(true);
1291 }
1292 }
1293
1294 void MainWindow::onRemoveFave()
1295 {
1296 _cbFaves->removeItem(_cbFaves->currentIndex());
1297 _cbFaves->setEnabled(_cbFaves->count());
1298 _fullScreenWidget->cbFaves()->setEnabled(_cbFaves->count());
1299 _tbRemoveFave->setEnabled(_cbFaves->count());
1300 _tbRenameFave->setEnabled(_cbFaves->count());
1301 }
1302
1303 void MainWindow::onFaveSelected(int index)
1304 {
1305 _tbRemoveFave->setEnabled(true);
1306 _tbRenameFave->setEnabled(true);
1307 QStringList list = _cbFaves->itemData(index).toStringList();
1308 QString folder = list[1];
1309 QString name = list[2];
1310 TreeWidgetPresetItem * item = findPresetItem(_treeGPresets, folder, name);
1311 if (item) {
1312 list.pop_front();
1313 list.pop_front();
1314 list.pop_front();
1315 _treeGPresets->setCurrentItem(item);
1316 QTreeWidget * tree = _fullScreenWidget->treeWidget();
1317 TreeWidgetPresetItem * itemFS = findPresetItem(tree, folder, name);
1318 tree->setCurrentItem(itemFS);
1319 presetClicked(item, 0);
1320 _commandParamsWidget->setValues(list);
1321 }
1322 }
1323
1324 void MainWindow::onRenameFave()
1325 {
1326 int index = _cbFaves->currentIndex();
1327 QStringList list = CURRENTDATA(_cbFaves).toStringList();
1328 QString newName = QInputDialog::getText(this, "Rename a fave", "Enter a new name", QLineEdit::Normal, list[0], 0);
1329 if (!newName.isNull()) {
1330 _cbFaves->setItemText(index, newName);
1331 list[0] = newName;
1332 _cbFaves->setItemData(index, list);
1333 }
1334 }
1335
1336 void MainWindow::closeEvent(QCloseEvent * event)
1337 {
1338 if (_outputWindow && _outputWindow->isVisible()) {
1339 _outputWindow->close();
1340 }
1341 if (_fullScreenWidget && _displayMode == FullScreen) {
1342 toggleFullScreenMode();
1343 }
1344 event->accept();
1345 }
1346
1347 void MainWindow::onImageViewKeypointsEvent()
1348 {
1349 _commandParamsWidget->setKeypoints(_imageView->keypoints(), true);
1350 }
1351
1352 void MainWindow::onOutputWindowKeypointsEvent()
1353 {
1354 _commandParamsWidget->setKeypoints(_outputWindow->imageView()->keypoints(), true);
1355 }
1356
1357 void MainWindow::onFullScreenKeypointsEvent()
1358 {
1359 _fullScreenWidget->commandParamsWidget()->setKeypoints(_fullScreenWidget->imageView()->keypoints(), true);
1360 }
1361