1 /***************************************************************************
2 stopmotion.cpp - description
5 copyright : (C) 2010 by Jean-Baptiste Mardelle
6 email : jb@kdenlive.org
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
18 #include "stopmotion.h"
19 #include "../blackmagic/devices.h"
20 #if !defined(Q_OS_FREEBSD) && !defined(Q_OS_KFREEBSD)
21 #include "../v4l/v4lcapture.h"
23 #include "../slideshowclip.h"
24 #include "../profilesdialog.h"
25 #include "../mltdevicecapture.h"
26 #include "../recmonitor.h"
27 #include "../monitormanager.h"
28 #include "ui_smconfig_ui.h"
29 #include "kdenlivesettings.h"
33 #include <KGlobalSettings>
34 #include <KFileDialog>
35 #include <KStandardDirs>
36 #include <KMessageBox>
37 #include <kdeversion.h>
38 #include <KNotification>
40 #include <QtConcurrentRun>
41 #include <QInputDialog>
43 #include <QVBoxLayout>
47 #include <QWheelEvent>
50 MyLabel::MyLabel(QWidget* parent) :
55 void MyLabel::setImage(QImage img)
61 void MyLabel::wheelEvent(QWheelEvent* event)
63 if (event->delta() > 0) emit seek(true);
64 else emit seek(false);
68 void MyLabel::mousePressEvent(QMouseEvent*)
74 void MyLabel::paintEvent(QPaintEvent* event)
78 QRect r(0, 0, width(), height());
80 p.fillRect(r, QColor(KdenliveSettings::window_background()));
81 double aspect_ratio = (double) m_img.width() / m_img.height();
82 int pictureHeight = height();
83 int pictureWidth = width();
84 int calculatedWidth = aspect_ratio * height();
85 if (calculatedWidth > width()) pictureHeight = width() / aspect_ratio;
87 int calculatedHeight = width() / aspect_ratio;
88 if (calculatedHeight > height()) pictureWidth = height() * aspect_ratio;
90 p.drawImage(QRect((width() - pictureWidth) / 2, (height() - pictureHeight) / 2, pictureWidth, pictureHeight), m_img, QRect(0, 0, m_img.width(), m_img.height()));
95 StopmotionMonitor::StopmotionMonitor(QWidget *parent) :
96 AbstractMonitor(parent),
101 StopmotionMonitor::~StopmotionMonitor()
105 void StopmotionMonitor::setRender(MltDeviceCapture *render)
107 m_captureDevice = render;
110 AbstractRender *StopmotionMonitor::abstractRender()
112 return m_captureDevice;
115 const QString StopmotionMonitor::name() const
117 return QString("stopmotion");
121 void StopmotionMonitor::stop()
123 if (m_captureDevice) m_captureDevice->stop();
127 void StopmotionMonitor::start()
131 StopmotionWidget::StopmotionWidget(MonitorManager *manager, KUrl projectFolder, QList< QAction* > actions, QWidget* parent) :
133 , Ui::Stopmotion_UI()
134 , m_projectFolder(projectFolder)
135 , m_captureDevice(NULL)
137 , m_animatedIndex(-1)
140 , m_monitor(new StopmotionMonitor(this))
142 //setAttribute(Qt::WA_DeleteOnClose);
143 //HACK: the monitor widget is hidden, it is just used to control the capturedevice from monitormanager
144 m_monitor->setHidden(true);
145 connect(m_monitor, SIGNAL(stopCapture()), this, SLOT(slotStopCapture()));
146 m_manager->appendMonitor(m_monitor);
147 QAction* analyse = new QAction(i18n("Send frames to color scopes"), this);
148 analyse->setCheckable(true);
149 analyse->setChecked(KdenliveSettings::analyse_stopmotion());
150 connect(analyse, SIGNAL(triggered(bool)), this, SLOT(slotSwitchAnalyse(bool)));
152 QAction* mirror = new QAction(i18n("Mirror display"), this);
153 mirror->setCheckable(true);
154 //mirror->setChecked(KdenliveSettings::analyse_stopmotion());
155 connect(mirror, SIGNAL(triggered(bool)), this, SLOT(slotSwitchMirror(bool)));
159 setWindowTitle(i18n("Stop Motion Capture"));
160 setFont(KGlobalSettings::toolBarFont());
162 live_button->setIcon(KIcon("camera-photo"));
164 m_captureAction = actions.at(0);
165 connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
166 m_captureAction->setCheckable(true);
167 m_captureAction->setChecked(false);
168 capture_button->setDefaultAction(m_captureAction);
170 connect(actions.at(1), SIGNAL(triggered()), this, SLOT(slotSwitchLive()));
172 QAction *intervalCapture = new QAction(i18n("Interval capture"), this);
173 intervalCapture->setIcon(KIcon("chronometer"));
174 intervalCapture->setCheckable(true);
175 intervalCapture->setChecked(false);
176 capture_interval->setDefaultAction(intervalCapture);
178 preview_button->setIcon(KIcon("media-playback-start"));
179 capture_button->setEnabled(false);
183 QMenu* confMenu = new QMenu;
184 m_showOverlay = actions.at(2);
185 connect(m_showOverlay, SIGNAL(triggered(bool)), this, SLOT(slotShowOverlay(bool)));
186 overlay_button->setDefaultAction(m_showOverlay);
187 //confMenu->addAction(m_showOverlay);
189 m_effectIndex = KdenliveSettings::stopmotioneffect();
190 QMenu* effectsMenu = new QMenu(i18n("Overlay effect"));
191 QActionGroup* effectGroup = new QActionGroup(this);
192 QAction* noEffect = new QAction(i18n("No Effect"), effectGroup);
193 noEffect->setData(0);
194 QAction* contrastEffect = new QAction(i18n("Contrast"), effectGroup);
195 contrastEffect->setData(1);
196 QAction* edgeEffect = new QAction(i18n("Edge detect"), effectGroup);
197 edgeEffect->setData(2);
198 QAction* brightEffect = new QAction(i18n("Brighten"), effectGroup);
199 brightEffect->setData(3);
200 QAction* invertEffect = new QAction(i18n("Invert"), effectGroup);
201 invertEffect->setData(4);
202 QAction* thresEffect = new QAction(i18n("Threshold"), effectGroup);
203 thresEffect->setData(5);
205 effectsMenu->addAction(noEffect);
206 effectsMenu->addAction(contrastEffect);
207 effectsMenu->addAction(edgeEffect);
208 effectsMenu->addAction(brightEffect);
209 effectsMenu->addAction(invertEffect);
210 effectsMenu->addAction(thresEffect);
211 QList <QAction*> list = effectsMenu->actions();
212 for (int i = 0; i < list.count(); i++) {
213 list.at(i)->setCheckable(true);
214 if (list.at(i)->data().toInt() == m_effectIndex) {
215 list.at(i)->setChecked(true);
218 connect(effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotUpdateOverlayEffect(QAction*)));
219 confMenu->addMenu(effectsMenu);
221 QAction* showThumbs = new QAction(KIcon("image-x-generic"), i18n("Show sequence thumbnails"), this);
222 showThumbs->setCheckable(true);
223 showThumbs->setChecked(KdenliveSettings::showstopmotionthumbs());
224 connect(showThumbs, SIGNAL(triggered(bool)), this, SLOT(slotShowThumbs(bool)));
226 QAction* removeCurrent = new QAction(KIcon("edit-delete"), i18n("Delete current frame"), this);
227 removeCurrent->setShortcut(Qt::Key_Delete);
228 connect(removeCurrent, SIGNAL(triggered()), this, SLOT(slotRemoveFrame()));
230 QAction* conf = new QAction(KIcon("configure"), i18n("Configure"), this);
231 connect(conf, SIGNAL(triggered()), this, SLOT(slotConfigure()));
233 confMenu->addAction(showThumbs);
234 confMenu->addAction(removeCurrent);
235 confMenu->addAction(analyse);
236 confMenu->addAction(mirror);
237 confMenu->addAction(conf);
238 config_button->setIcon(KIcon("configure"));
239 config_button->setMenu(confMenu);
241 connect(sequence_name, SIGNAL(textChanged(const QString&)), this, SLOT(sequenceNameChanged(const QString&)));
242 connect(sequence_name, SIGNAL(currentIndexChanged(int)), live_button, SLOT(setFocus()));
244 // Video widget holder
245 QVBoxLayout *layout = new QVBoxLayout;
246 layout->setContentsMargins(0, 0, 0, 0);
247 layout->setSpacing(0);
248 m_videoBox = new VideoPreviewContainer();
249 m_videoBox->setContentsMargins(0, 0, 0, 0);
250 m_videoBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
251 //m_videoBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
252 m_videoBox->setLineWidth(4);
253 layout->addWidget(m_videoBox);
256 if (BMInterface::getBlackMagicDeviceList(capture_device)) {
257 // Found a BlackMagic device
259 if (QFile::exists(KdenliveSettings::video4vdevice())) {
260 #if !defined(Q_WS_MAC) && !defined(Q_OS_FREEBSD) && !defined(Q_OS_KFREEBSD)
261 // Video 4 Linux device detection
262 for (int i = 0; i < 10; i++) {
263 QString path = "/dev/video" + QString::number(i);
264 if (QFile::exists(path)) {
265 QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path);
266 if (!deviceInfo.isEmpty()) {
267 capture_device->addItem(deviceInfo.at(0), "v4l");
268 capture_device->setItemData(capture_device->count() - 1, path, Qt::UserRole + 1);
269 capture_device->setItemData(capture_device->count() - 1, deviceInfo.at(1), Qt::UserRole + 2);
270 if (path == KdenliveSettings::video4vdevice()) capture_device->setCurrentIndex(capture_device->count() - 1);
277 connect(capture_device, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDeviceHandler()));
279 connect(m_bmCapture, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
280 connect(m_bmCapture, SIGNAL(gotFrame(QImage)), this, SIGNAL(gotFrame(QImage)));
281 } else live_button->setEnabled(false);*/
283 m_frame_preview = new MyLabel(this);
284 connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool)));
285 connect(m_frame_preview, SIGNAL(switchToLive()), this, SLOT(slotSwitchLive()));
286 layout->addWidget(m_frame_preview);
287 m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
288 video_preview->setLayout(layout);
290 //kDebug()<<video_preview->winId();
293 // Create MLT producer data
294 if (capture_device->itemData(capture_device->currentIndex()) == "v4l") {
295 // Capture using a video4linux device
296 profilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux");
300 profilePath = KdenliveSettings::current_profile();
303 m_captureDevice = new MltDeviceCapture(profilePath, m_videoBox, this);
304 m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion();
305 m_monitor->setRender(m_captureDevice);
306 connect(m_captureDevice, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
308 live_button->setChecked(false);
309 button_addsequence->setEnabled(false);
310 connect(live_button, SIGNAL(toggled(bool)), this, SLOT(slotLive(bool)));
311 connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
312 connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview(bool)));
313 connect(frame_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotShowSelectedFrame()));
314 connect(frame_list, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotShowSelectedFrame()));
315 connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
317 frame_list->addAction(removeCurrent);
318 frame_list->setContextMenuPolicy(Qt::ActionsContextMenu);
319 frame_list->setHidden(!KdenliveSettings::showstopmotionthumbs());
320 parseExistingSequences();
321 QTimer::singleShot(500, this, SLOT(slotLive()));
322 connect(&m_intervalTimer, SIGNAL(timeout()), this, SLOT(slotCaptureFrame()));
323 m_intervalTimer.setSingleShot(true);
324 m_intervalTimer.setInterval(KdenliveSettings::captureinterval() * 1000);
327 StopmotionWidget::~StopmotionWidget()
329 m_manager->removeMonitor(m_monitor);
330 if (m_captureDevice) {
331 m_captureDevice->stop();
332 delete m_captureDevice;
333 m_captureDevice = NULL;
339 void StopmotionWidget::slotUpdateOverlayEffect(QAction* act)
341 if (act) m_effectIndex = act->data().toInt();
342 KdenliveSettings::setStopmotioneffect(m_effectIndex);
346 void StopmotionWidget::closeEvent(QCloseEvent* e)
349 QDialog::closeEvent(e);
352 void StopmotionWidget::slotConfigure()
357 d.setWindowTitle(i18n("Configure Stop Motion"));
358 ui.sm_interval->setValue(KdenliveSettings::captureinterval());
359 ui.sm_interval->setSuffix(ki18np(" second", " seconds"));
360 ui.sm_notifytime->setSuffix(ki18np(" second", " seconds"));
361 ui.sm_notifytime->setValue(KdenliveSettings::sm_notifytime());
362 connect(ui.sm_prenotify, SIGNAL(toggled(bool)), ui.sm_notifytime, SLOT(setEnabled(bool)));
363 ui.sm_prenotify->setChecked(KdenliveSettings::sm_prenotify());
364 ui.sm_loop->setChecked(KdenliveSettings::sm_loop());
365 ui.sm_framesplayback->setValue(KdenliveSettings::sm_framesplayback());
367 if (d.exec() == QDialog::Accepted) {
368 KdenliveSettings::setSm_loop(ui.sm_loop->isChecked());
369 KdenliveSettings::setCaptureinterval(ui.sm_interval->value());
370 KdenliveSettings::setSm_framesplayback(ui.sm_framesplayback->value());
371 KdenliveSettings::setSm_notifytime(ui.sm_notifytime->value());
372 KdenliveSettings::setSm_prenotify(ui.sm_prenotify->isChecked());
373 m_intervalTimer.setInterval(KdenliveSettings::captureinterval() * 1000);
377 void StopmotionWidget::slotShowThumbs(bool show)
379 KdenliveSettings::setShowstopmotionthumbs(show);
382 sequenceNameChanged(sequence_name->currentText());
387 frame_list->setHidden(!show);
391 void StopmotionWidget::slotUpdateDeviceHandler()
394 delete m_captureDevice;
395 m_captureDevice = NULL;
396 /*QString data = capture_device->itemData(capture_device->currentIndex()).toString();
401 m_layout->removeWidget(m_frame_preview);
403 #if !defined(Q_WS_MAC) && !defined(Q_OS_FREEBSD) && !defined(Q_OS_KFREEBSD)
404 m_bmCapture = new V4lCaptureHandler(m_layout);
405 m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString());
408 m_bmCapture = new BmdCaptureHandler(m_layout);
409 if (m_bmCapture) connect(m_bmCapture, SIGNAL(gotMessage(const QString&)), this, SLOT(slotGotHDMIMessage(const QString&)));
411 live_button->setEnabled(m_bmCapture != NULL);
412 m_layout->addWidget(m_frame_preview);*/
415 void StopmotionWidget::slotGotHDMIMessage(const QString& message)
417 log_box->insertItem(0, message);
420 void StopmotionWidget::parseExistingSequences()
422 sequence_name->clear();
423 sequence_name->addItem(QString());
424 QDir dir(m_projectFolder.path());
426 filters << "*_0000.png";
427 //dir.setNameFilters(filters);
428 QStringList sequences = dir.entryList(filters, QDir::Files, QDir::Name);
429 //kDebug()<<"PF: "<<<<", sm: "<<sequences;
430 foreach(QString sequencename, sequences) {
431 sequence_name->addItem(sequencename.section("_", 0, -2));
435 void StopmotionWidget::slotSwitchLive()
437 setUpdatesEnabled(false);
438 slotLive(!live_button->isChecked());
439 /*if (m_frame_preview->isHidden()) {
440 //if (m_bmCapture) m_bmCapture->hidePreview(true);
441 m_videoBox->setHidden(true);
442 m_frame_preview->setHidden(false);
444 m_frame_preview->setHidden(true);
445 //if (m_bmCapture) m_bmCapture->hidePreview(false);
446 m_videoBox->setHidden(false);
447 capture_button->setEnabled(true);
449 setUpdatesEnabled(true);
452 void StopmotionWidget::slotStopCapture()
457 void StopmotionWidget::slotLive(bool isOn)
459 live_button->blockSignals(true);
460 capture_button->setEnabled(false);
462 m_frame_preview->setHidden(true);
463 m_videoBox->setHidden(false);
466 MltVideoProfile profile;
470 // Create MLT producer data
471 if (capture_device->itemData(capture_device->currentIndex()) == "v4l") {
472 // Capture using a video4linux device
473 profilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux");
474 profile = ProfilesDialog::getVideoProfile(profilePath);
475 service = "avformat-novalidate";
476 QString devicePath = capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString();
477 resource = QString("video4linux2:%1?width:%2&height:%3&frame_rate:%4").arg(devicePath).arg(profile.width).arg(profile.height).arg((double) profile.frame_rate_num / profile.frame_rate_den);
481 profilePath = KdenliveSettings::current_profile();
482 profile = ProfilesDialog::getVideoProfile(profilePath);
483 service = "decklink";
484 resource = capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString();
487 if (m_captureDevice == NULL) {
488 m_captureDevice = new MltDeviceCapture(profilePath, m_videoBox, this);
489 m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion();
490 m_monitor->setRender(m_captureDevice);
491 connect(m_captureDevice, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
494 m_manager->activateMonitor("stopmotion");
495 QString producer = createProducer(profile, service, resource);
496 if (m_captureDevice->slotStartPreview(producer, true)) {
497 if (m_showOverlay->isChecked()) {
501 capture_button->setEnabled(true);
502 live_button->setChecked(true);
503 log_box->insertItem(-1, i18n("Playing %1x%2 (%3 fps)", profile.width, profile.height, locale.toString((double)profile.frame_rate_num/profile.frame_rate_den).rightJustified(2, '0')));
504 log_box->setCurrentIndex(0);
507 kDebug()<<"// problem starting stopmo";
508 log_box->insertItem(-1, i18n("Failed to start device"));
509 log_box->setCurrentIndex(0);
513 m_frame_preview->setHidden(false);
514 live_button->setChecked(false);
515 if (m_captureDevice) {
516 m_captureDevice->stop();
517 m_videoBox->setHidden(true);
518 log_box->insertItem(-1, i18n("Stopped"));
519 log_box->setCurrentIndex(0);
520 //delete m_captureDevice;
521 //m_captureDevice = NULL;
526 if (isOn && m_bmCapture) {
527 //m_frame_preview->setImage(QImage());
528 m_frame_preview->setHidden(true);
529 m_bmCapture->startPreview(KdenliveSettings::hdmi_capturedevice(), KdenliveSettings::hdmi_capturemode(), false);
530 capture_button->setEnabled(true);
531 live_button->setChecked(true);
533 if (m_bmCapture) m_bmCapture->stopPreview();
534 m_frame_preview->setHidden(false);
535 capture_button->setEnabled(false);
536 live_button->setChecked(false);
538 live_button->blockSignals(false);
541 void StopmotionWidget::slotShowOverlay(bool isOn)
544 // Overlay last frame of the sequence
550 m_captureDevice->setOverlay(QString());
554 void StopmotionWidget::reloadOverlay()
556 QString path = getPathForFrame(m_sequenceFrame - 1);
557 if (!QFile::exists(path)) {
558 log_box->insertItem(-1, i18n("No previous frame found"));
559 log_box->setCurrentIndex(0);
562 m_captureDevice->setOverlay(path);
565 void StopmotionWidget::slotUpdateOverlay()
567 if (m_captureDevice == NULL) return;
572 switch (m_effectIndex) {
574 tag = "frei0r.contrast0r";
575 params << "Contrast=1.2";
579 params << "x_scatter=4" << "y_scatter=4" << "scale=1" << "mix=0";
582 tag = "frei0r.brightness";
583 params << "Brightness=0.7";
590 params << "midpoint=125";
596 m_captureDevice->setOverlayEffect(tag, params);
599 void StopmotionWidget::sequenceNameChanged(const QString& name)
601 // Get rid of frames from previous sequence
602 disconnect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
604 m_future.waitForFinished();
606 if (name.isEmpty()) {
607 button_addsequence->setEnabled(false);
609 // Check if we are editing an existing sequence
610 QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_filesList);
611 m_sequenceFrame = m_filesList.isEmpty() ? 0 : SlideshowClip::getFrameNumberFromPath(m_filesList.last()) + 1;
612 if (!m_filesList.isEmpty()) {
613 m_sequenceName = sequence_name->currentText();
614 connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
615 m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
616 button_addsequence->setEnabled(true);
619 connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
620 button_addsequence->setEnabled(false);
622 capture_button->setEnabled(live_button->isChecked());
626 void StopmotionWidget::slotCaptureFrame()
628 if (m_captureDevice == NULL) return;
629 if (sequence_name->currentText().isEmpty()) {
630 QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name"));
631 if (seqName.isEmpty()) {
632 m_captureAction->setChecked(false);
635 sequence_name->blockSignals(true);
636 sequence_name->setItemText(sequence_name->currentIndex(), seqName);
637 sequence_name->blockSignals(false);
639 if (m_sequenceName != sequence_name->currentText()) {
640 m_sequenceName = sequence_name->currentText();
643 //capture_button->setEnabled(false);
644 if (m_intervalTimer.isActive()) {
645 // stop interval capture
646 m_intervalTimer.stop();
649 QString currentPath = getPathForFrame(m_sequenceFrame);
650 m_captureDevice->captureFrame(currentPath);
651 KNotification::event("FrameCaptured", i18n("Frame Captured"), QPixmap(), this);
653 button_addsequence->setEnabled(true);
654 if (capture_interval->isChecked()) {
655 if (KdenliveSettings::sm_prenotify()) QTimer::singleShot((KdenliveSettings::captureinterval() - KdenliveSettings::sm_notifytime()) * 1000, this, SLOT(slotPreNotify()));
656 m_intervalTimer.start();
658 else m_captureAction->setChecked(false);
661 void StopmotionWidget::slotPreNotify()
663 if (m_captureAction->isChecked()) KNotification::event("ReadyToCapture", i18n("Going to Capture Frame"), QPixmap(), this);
667 void StopmotionWidget::slotNewThumb(const QString &path)
669 if (!KdenliveSettings::showstopmotionthumbs()) return;
670 m_filesList.append(path);
671 if (m_showOverlay->isChecked()) reloadOverlay();
672 if (!m_future.isRunning()) m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
676 void StopmotionWidget::slotPrepareThumbs()
678 if (m_filesList.isEmpty()) return;
679 QString path = m_filesList.takeFirst();
680 emit doCreateThumbs(QImage(path), SlideshowClip::getFrameNumberFromPath(path));
684 void StopmotionWidget::slotCreateThumbs(QImage img, int ix)
687 m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
691 int width = height * img.width() / img.height();
692 frame_list->setIconSize(QSize(width, height));
693 QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
694 QString nb = QString::number(ix);
696 QFontInfo finfo(font());
697 p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(80, 80, 80, 150));
699 p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
702 QListWidgetItem* item = new QListWidgetItem(icon, QString(), frame_list);
703 item->setToolTip(getPathForFrame(ix, sequence_name->currentText()));
704 item->setData(Qt::UserRole, ix);
705 frame_list->blockSignals(true);
706 frame_list->setCurrentItem(item);
707 frame_list->blockSignals(false);
708 m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
711 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
713 if (seqName.isEmpty()) seqName = m_sequenceName;
714 return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
717 void StopmotionWidget::slotShowFrame(const QString& path)
721 capture_button->setEnabled(false);
724 //m_videoBox->setHidden(true);
726 m_frame_preview->setImage(img);
727 m_frame_preview->setHidden(false);
728 m_frame_preview->update();
732 void StopmotionWidget::slotShowSelectedFrame()
734 QListWidgetItem* item = frame_list->currentItem();
736 //int ix = item->data(Qt::UserRole).toInt();;
737 slotShowFrame(item->toolTip());
741 void StopmotionWidget::slotAddSequence()
743 emit addOrUpdateSequence(getPathForFrame(0));
746 void StopmotionWidget::slotPlayPreview(bool animate)
751 m_animationList.clear();
754 if (KdenliveSettings::showstopmotionthumbs()) {
755 if (KdenliveSettings::sm_framesplayback() == 0) frame_list->setCurrentRow(0);
756 else frame_list->setCurrentRow(frame_list->count() - KdenliveSettings::sm_framesplayback());
757 QTimer::singleShot(200, this, SLOT(slotAnimate()));
759 SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_animationList);
760 if (KdenliveSettings::sm_framesplayback() > 0) {
761 // only play the last x frames
762 while (m_animationList.count() > KdenliveSettings::sm_framesplayback() + 1) {
763 m_animationList.removeFirst();
771 void StopmotionWidget::slotAnimate()
774 if (KdenliveSettings::showstopmotionthumbs()) {
775 int newRow = frame_list->currentRow() + 1;
776 if (KdenliveSettings::sm_loop() || newRow < frame_list->count()) {
777 if (newRow >= frame_list->count()) {
778 if (KdenliveSettings::sm_framesplayback() == 0) newRow = 0;
780 // seek to correct frame
781 newRow = frame_list->count() - KdenliveSettings::sm_framesplayback();
784 frame_list->setCurrentRow(newRow);
785 QTimer::singleShot(100, this, SLOT(slotAnimate()));
789 if (m_animatedIndex >= m_animationList.count()) {
790 if (KdenliveSettings::sm_loop()) m_animatedIndex = 0;
791 else m_animatedIndex = -1;
793 if (m_animatedIndex > -1) {
794 slotShowFrame(m_animationList.at(m_animatedIndex));
796 QTimer::singleShot(100, this, SLOT(slotAnimate()));
802 preview_button->setChecked(false);
806 QListWidgetItem* StopmotionWidget::getFrameFromIndex(int ix)
808 QListWidgetItem* item = NULL;
810 if (ix >= frame_list->count()) {
811 pos = frame_list->count() - 1;
814 item = frame_list->item(pos);
816 int value = item->data(Qt::UserRole).toInt();
817 if (value == ix) return item;
818 else if (value < ix) {
820 while (pos < frame_list->count()) {
821 item = frame_list->item(pos);
822 value = item->data(Qt::UserRole).toInt();
823 if (value == ix) return item;
829 item = frame_list->item(pos);
830 value = item->data(Qt::UserRole).toInt();
831 if (value == ix) return item;
839 void StopmotionWidget::selectFrame(int ix)
841 frame_list->blockSignals(true);
842 QListWidgetItem* item = getFrameFromIndex(ix);
844 frame_list->setCurrentItem(item);
845 frame_list->blockSignals(false);
848 void StopmotionWidget::slotSeekFrame(bool forward)
850 int ix = frame_list->currentRow();
852 if (ix < frame_list->count() - 1) frame_list->setCurrentRow(ix + 1);
853 } else if (ix > 0) frame_list->setCurrentRow(ix - 1);
856 void StopmotionWidget::slotRemoveFrame()
858 if (frame_list->currentItem() == NULL) return;
859 QString path = frame_list->currentItem()->toolTip();
860 if (KMessageBox::questionYesNo(this, i18n("Delete frame %1 from disk?", path), i18n("Delete Frame")) != KMessageBox::Yes) return;
863 QListWidgetItem* item = frame_list->takeItem(frame_list->currentRow());
864 int ix = item->data(Qt::UserRole).toInt();
865 if (ix == m_sequenceFrame - 1) {
866 // We are removing the last frame, update counter
867 QListWidgetItem* item2 = frame_list->item(frame_list->count() - 1);
868 if (item2) m_sequenceFrame = item2->data(Qt::UserRole).toInt() + 1;
874 void StopmotionWidget::slotSwitchAnalyse(bool isOn)
876 KdenliveSettings::setAnalyse_stopmotion(isOn);
877 if (m_captureDevice) m_captureDevice->sendFrameForAnalysis = isOn;
880 void StopmotionWidget::slotSwitchMirror(bool isOn)
882 //KdenliveSettings::setAnalyse_stopmotion(isOn);
883 if (m_captureDevice) m_captureDevice->mirror(isOn);
886 const QString StopmotionWidget::createProducer(MltVideoProfile profile, const QString &service, const QString &resource)
890 QString playlist = "<mlt title=\"capture\"><producer id=\"producer0\" in=\"0\" out=\"99999\"><property name=\"mlt_type\">producer</property><property name=\"length\">100000</property><property name=\"eof\">pause</property><property name=\"resource\">" + resource + "</property><property name=\"mlt_service\">" + service + "</property></producer>";
893 playlist.append("<playlist id=\"playlist0\"></playlist>");
896 playlist.append("<playlist id=\"playlist1\"><entry producer=\"producer0\" in=\"0\" out=\"99999\"/></playlist>");
898 playlist.append("<tractor id=\"tractor0\" title=\"video0\" global_feed=\"1\" in=\"0\" out=\"99999\">");
899 playlist.append("<track producer=\"playlist0\"/>");
900 playlist.append("<track producer=\"playlist1\"/>");
901 playlist.append("</tractor></mlt>");