diff --git a/toonz/sources/toonz/penciltestpopup.cpp b/toonz/sources/toonz/penciltestpopup.cpp index 61e47eb..8d5fa45 100644 --- a/toonz/sources/toonz/penciltestpopup.cpp +++ b/toonz/sources/toonz/penciltestpopup.cpp @@ -90,6 +90,8 @@ #include <QThreadPool> #include <QHostInfo> #include <QDesktopServices> +#include <QDialogButtonBox> +#include <QMessageBox> #ifdef _WIN32 #include <dshow.h> @@ -412,6 +414,24 @@ bool getRasterLevelSize(TXshLevel* level, TDimension& dim) { return true; } +class IconView : public QWidget { + QIcon m_icon; + +public: + IconView(const QString& iconName, const QString& toolTipStr = "", + QWidget* parent = 0) + : QWidget(parent), m_icon(createQIcon(iconName.toUtf8())) { + setMinimumSize(18, 18); + setToolTip(toolTipStr); + } + +protected: + void paintEvent(QPaintEvent* e) { + QPainter p(this); + p.drawPixmap(QRect(0, 2, 18, 18), m_icon.pixmap(18, 18)); + } +}; + } // namespace //============================================================================= @@ -483,14 +503,17 @@ void MyVideoWidget::computeTransform(QSize imgSize) { .scale(scale, scale); } -void MyVideoWidget::setSubCameraSize(QSize size) { +void MyVideoWidget::setSubCameraRect(QRect rect) { QSize frameSize = m_image.size(); - assert(frameSize == size.expandedTo(frameSize)); + assert(frameSize == rect.size().expandedTo(frameSize)); - m_subCameraRect.setSize(size); + m_subCameraRect = rect; // make sure the sub camera is inside of the frame - if (!QRect(QPoint(0, 0), frameSize).contains(m_subCameraRect)) + if (rect.isValid() && + !QRect(QPoint(0, 0), frameSize).contains(m_subCameraRect)) { m_subCameraRect.moveCenter(QRect(QPoint(0, 0), frameSize).center()); + emit subCameraChanged(false); + } update(); } @@ -654,10 +677,10 @@ void MyVideoWidget::mouseMoveEvent(QMouseEvent* event) { camSize.height() - m_preSubCameraRect.bottom()); m_subCameraRect.setBottom(m_preSubCameraRect.bottom() + offset.y()); } - // if the sub camera size is changed, notify the parent for updating the - // fields - emit subCameraResized(true); } + // if the sub camera size is changed, notify the parent for updating the + // fields + emit subCameraChanged(true); update(); } } @@ -685,7 +708,8 @@ void MyVideoWidget::mouseReleaseEvent(QMouseEvent* event) { return; m_preSubCameraRect = QRect(); - if (m_activeSubHandle != HandleFrame) emit subCameraResized(false); + + emit subCameraChanged(false); // restart the camera emit startCamera(); @@ -1269,6 +1293,177 @@ void PencilTestSaveInFolderPopup::updateParentFolder() { //============================================================================= +SubCameraButton::SubCameraButton(const QString& text, QWidget* parent) + : QPushButton(text, parent) { + setObjectName("SubcameraButton"); + setIconSize(QSize(16, 16)); + setIcon(createQIcon("subcamera")); + setCheckable(true); + + // load preference file + TFilePath layoutDir = ToonzFolder::getMyModuleDir(); + TFilePath prefPath = layoutDir + TFilePath("camera_capture_subcamera.ini"); + // In case the personal settings is not exist (for new users) + if (!TFileStatus(prefPath).doesExist()) { + TFilePath templatePath = ToonzFolder::getTemplateModuleDir() + + TFilePath("camera_capture_subcamera.ini"); + // If there is the template, copy it to the personal one + if (TFileStatus(templatePath).doesExist()) + TSystem::copyFile(prefPath, templatePath); + } + m_settings.reset(new QSettings( + QString::fromStdWString(prefPath.getWideString()), QSettings::IniFormat)); +} + +void SubCameraButton::contextMenuEvent(QContextMenuEvent* event) { + // return if the current camera is not valid + if (!m_curResolution.isValid()) return; + // load presets for the current resolution + QString groupName = QString("%1x%2") + .arg(m_curResolution.width()) + .arg(m_curResolution.height()); + m_settings->beginGroup(groupName); + + bool hasPresets = !m_settings->childKeys().isEmpty(); + + if (!m_curSubCamera.isValid() && !hasPresets) { + m_settings->endGroup(); + return; + } + + QMenu menu(this); + menu.setToolTipsVisible(true); + + for (auto key : m_settings->childKeys()) { + QAction* scAct = menu.addAction(key); + scAct->setData(m_settings->value(key)); + QRect rect = m_settings->value(key).toRect(); + if (m_curSubCamera == rect) { + scAct->setCheckable(true); + scAct->setChecked(true); + scAct->setEnabled(false); + } else { + scAct->setToolTip(QString("%1 x %2, X%3 Y%4") + .arg(rect.width()) + .arg(rect.height()) + .arg(rect.x()) + .arg(rect.y())); + connect(scAct, SIGNAL(triggered()), this, SLOT(onSubCameraAct())); + } + } + + if (hasPresets) menu.addSeparator(); + + // save preset (visible if the subcamera is active) + if (m_curSubCamera.isValid()) { + QAction* saveAct = menu.addAction(tr("Save Current Subcamera")); + connect(saveAct, SIGNAL(triggered()), this, SLOT(onSaveSubCamera())); + } + + // delete preset (visible if there is any) + if (hasPresets) { + QMenu* delMenu = menu.addMenu(tr("Delete Preset")); + for (auto key : m_settings->childKeys()) { + QAction* delAct = delMenu->addAction(tr("Delete %1").arg(key)); + delAct->setData(key); + connect(delAct, SIGNAL(triggered()), this, SLOT(onDeletePreset())); + } + } + + m_settings->endGroup(); + + menu.exec(event->globalPos()); +} + +void SubCameraButton::onSubCameraAct() { + QRect subCameraRect = qobject_cast<QAction*>(sender())->data().toRect(); + emit subCameraPresetSelected(subCameraRect); +} + +void SubCameraButton::onSaveSubCamera() { + auto initDialog = [](QLineEdit** lineEdit) { + QDialog* ret = new QDialog(); + *lineEdit = new QLineEdit(ret); + QDialogButtonBox* buttonBox = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + QVBoxLayout* lay = new QVBoxLayout(); + lay->setMargin(5); + lay->setSpacing(10); + lay->addWidget(*lineEdit); + lay->addWidget(buttonBox); + ret->setLayout(lay); + connect(buttonBox, &QDialogButtonBox::accepted, ret, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, ret, &QDialog::reject); + return ret; + }; + + static QDialog* nameDialog = nullptr; + static QLineEdit* lineEdit = nullptr; + if (!nameDialog) nameDialog = initDialog(&lineEdit); + + // ask name + QString oldName; + QString groupName = QString("%1x%2") + .arg(m_curResolution.width()) + .arg(m_curResolution.height()); + m_settings->beginGroup(groupName); + for (auto key : m_settings->childKeys()) { + if (m_curSubCamera == m_settings->value(key).toRect()) { + oldName = key; + } + } + lineEdit->setText(oldName); + lineEdit->selectAll(); + + if (nameDialog->exec() == QDialog::Rejected) { + m_settings->endGroup(); + return; + } + + QString newName = lineEdit->text(); + + if (newName.isEmpty()) { + m_settings->endGroup(); + return; + } + + // ask if there are the same name / data in the existing entries + if (m_settings->contains(newName) || !oldName.isEmpty()) { + QString txt = + tr("Overwriting the existing subcamera preset. Are you sure?"); + if (QMessageBox::Yes != QMessageBox::question(this, tr("Question"), txt)) + return; + if (!oldName.isEmpty()) m_settings->remove(oldName); + } + + // register + m_settings->setValue(newName, m_curSubCamera); + m_settings->endGroup(); +} + +void SubCameraButton::onDeletePreset() { + QString key = qobject_cast<QAction*>(sender())->data().toString(); + QString groupName = QString("%1x%2") + .arg(m_curResolution.width()) + .arg(m_curResolution.height()); + m_settings->beginGroup(groupName); + if (!m_settings->contains(key)) { + m_settings->endGroup(); + return; + } + + QString txt = tr("Deleting the subcamera preset %1. Are you sure?").arg(key); + if (QMessageBox::Yes != QMessageBox::question(this, tr("Question"), txt)) { + m_settings->endGroup(); + return; + } + + m_settings->remove(key); + m_settings->endGroup(); +} + +//============================================================================= + PencilTestPopup::PencilTestPopup() // set the parent 0 in order to enable the popup behind the main window : Dialog(0, false, false, "PencilTest") @@ -1347,9 +1542,11 @@ PencilTestPopup::PencilTestPopup() QPushButton* subfolderButton = new QPushButton(tr("Subfolder"), this); // subcamera - m_subcameraButton = new QPushButton(tr("Subcamera"), this); + m_subcameraButton = new SubCameraButton(tr("Subcamera"), this); m_subWidthFld = new IntLineEdit(this); m_subHeightFld = new IntLineEdit(this); + m_subXPosFld = new IntLineEdit(this); + m_subYPosFld = new IntLineEdit(this); QWidget* subCamWidget = new QWidget(this); // Calibration @@ -1430,10 +1627,6 @@ PencilTestPopup::PencilTestPopup() m_saveInFolderPopup->hide(); - m_subcameraButton->setObjectName("SubcameraButton"); - m_subcameraButton->setIconSize(QSize(16, 16)); - m_subcameraButton->setIcon(createQIcon("subcamera")); - m_subcameraButton->setCheckable(true); m_subcameraButton->setChecked(false); subCamWidget->setHidden(true); @@ -1449,6 +1642,12 @@ PencilTestPopup::PencilTestPopup() m_calibration.label->hide(); m_calibration.exportBtn->setEnabled(false); + int subCameraFieldWidth = fontMetrics().width("00000") + 5; + m_subWidthFld->setFixedWidth(subCameraFieldWidth); + m_subHeightFld->setFixedWidth(subCameraFieldWidth); + m_subXPosFld->setFixedWidth(subCameraFieldWidth); + m_subYPosFld->setFixedWidth(subCameraFieldWidth); + m_dpiBtn->setObjectName("SubcameraButton"); //---- layout ---- @@ -1477,9 +1676,19 @@ PencilTestPopup::PencilTestPopup() subCamLay->setMargin(0); subCamLay->setSpacing(3); { + subCamLay->addWidget(new IconView("edit_scale", tr("Size"), this), 0); subCamLay->addWidget(m_subWidthFld, 0); subCamLay->addWidget(new QLabel("x", this), 0); subCamLay->addWidget(m_subHeightFld, 0); + + subCamLay->addSpacing(3); + subCamLay->addWidget( + new IconView("edit_position", tr("Position"), this), 0); + subCamLay->addWidget(new QLabel("X:", this), 0); + subCamLay->addWidget(m_subXPosFld, 0); + subCamLay->addWidget(new QLabel("Y:", this), 0); + subCamLay->addWidget(m_subYPosFld, 0); + subCamLay->addStretch(0); } subCamWidget->setLayout(subCamLay); @@ -1697,12 +1906,19 @@ PencilTestPopup::PencilTestPopup() SLOT(onSubCameraToggled(bool))); ret = ret && connect(m_subcameraButton, SIGNAL(toggled(bool)), subCamWidget, SLOT(setVisible(bool))); + ret = ret && connect(m_subcameraButton, + SIGNAL(subCameraPresetSelected(const QRect&)), this, + SLOT(onSubCameraPresetSelected(const QRect&))); ret = ret && connect(m_subWidthFld, SIGNAL(editingFinished()), this, - SLOT(onSubCameraSizeEdited())); + SLOT(onSubCameraRectEdited())); ret = ret && connect(m_subHeightFld, SIGNAL(editingFinished()), this, - SLOT(onSubCameraSizeEdited())); - ret = ret && connect(m_videoWidget, SIGNAL(subCameraResized(bool)), this, - SLOT(onSubCameraResized(bool))); + SLOT(onSubCameraRectEdited())); + ret = ret && connect(m_subXPosFld, SIGNAL(editingFinished()), this, + SLOT(onSubCameraRectEdited())); + ret = ret && connect(m_subYPosFld, SIGNAL(editingFinished()), this, + SLOT(onSubCameraRectEdited())); + ret = ret && connect(m_videoWidget, SIGNAL(subCameraChanged(bool)), this, + SLOT(onSubCameraChanged(bool))); ret = ret && connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); @@ -1913,6 +2129,7 @@ void PencilTestPopup::onResolutionComboActivated() { // reset subcamera info m_subcameraButton->setChecked(false); // this will hide the size fields + m_subcameraButton->setCurResolution(m_resolution); m_subWidthFld->setRange(10, newResolution.width()); m_subHeightFld->setRange(10, newResolution.height()); // if there is no existing level or its size is larger than the current camera @@ -3187,32 +3404,58 @@ void PencilTestPopup::onSceneSwitched() { //----------------------------------------------------------------------------- void PencilTestPopup::onSubCameraToggled(bool on) { - m_videoWidget->setSubCameraSize( - on ? QSize(m_subWidthFld->getValue(), m_subHeightFld->getValue()) - : QSize()); + QRect subCamera = + on ? QRect(m_subXPosFld->getValue(), m_subYPosFld->getValue(), + m_subWidthFld->getValue(), m_subHeightFld->getValue()) + : QRect(); + m_videoWidget->setSubCameraRect(subCamera); + m_subcameraButton->setCurSubCamera(subCamera); refreshFrameInfo(); } //----------------------------------------------------------------------------- -void PencilTestPopup::onSubCameraResized(bool isDragging) { - QSize subSize = m_videoWidget->subCameraRect().size(); - assert(subSize.isValid()); - m_subWidthFld->setValue(subSize.width()); - m_subHeightFld->setValue(subSize.height()); - if (!isDragging) refreshFrameInfo(); +void PencilTestPopup::onSubCameraChanged(bool isDragging) { + QRect subCameraRect = m_videoWidget->subCameraRect(); + assert(subCameraRect.isValid()); + m_subWidthFld->setValue(subCameraRect.width()); + m_subHeightFld->setValue(subCameraRect.height()); + m_subXPosFld->setValue(subCameraRect.x()); + m_subYPosFld->setValue(subCameraRect.y()); + + if (!isDragging) { + refreshFrameInfo(); + m_subcameraButton->setCurSubCamera(subCameraRect); + } } //----------------------------------------------------------------------------- -void PencilTestPopup::onSubCameraSizeEdited() { - m_videoWidget->setSubCameraSize( - QSize(m_subWidthFld->getValue(), m_subHeightFld->getValue())); +void PencilTestPopup::onSubCameraRectEdited() { + m_videoWidget->setSubCameraRect( + QRect(m_subXPosFld->getValue(), m_subYPosFld->getValue(), + m_subWidthFld->getValue(), m_subHeightFld->getValue())); refreshFrameInfo(); } //----------------------------------------------------------------------------- +void PencilTestPopup::onSubCameraPresetSelected(const QRect& subCameraRect) { + assert(subCameraRect.isValid()); + m_subWidthFld->setValue(subCameraRect.width()); + m_subHeightFld->setValue(subCameraRect.height()); + m_subXPosFld->setValue(subCameraRect.x()); + m_subYPosFld->setValue(subCameraRect.y()); + + if (!m_subcameraButton->isChecked()) + // calling onSubCameraToggled() accordingly + m_subcameraButton->setChecked(true); + else + onSubCameraToggled(true); +} + +//----------------------------------------------------------------------------- + void PencilTestPopup::onCalibCapBtnClicked() { m_calibration.captureCue = true; } diff --git a/toonz/sources/toonz/penciltestpopup.h b/toonz/sources/toonz/penciltestpopup.h index d05ba72..f9a419c 100644 --- a/toonz/sources/toonz/penciltestpopup.h +++ b/toonz/sources/toonz/penciltestpopup.h @@ -14,6 +14,7 @@ #include <QAbstractVideoSurface> #include <QRunnable> #include <QLineEdit> +#include <QPushButton> // forward decl. class QCamera; @@ -22,7 +23,6 @@ class QCameraImageCapture; class QComboBox; class QSlider; class QCheckBox; -class QPushButton; class QVideoFrame; class QTimer; class QIntValidator; @@ -93,7 +93,7 @@ public: repaint(); } - void setSubCameraSize(QSize size); + void setSubCameraRect(QRect rect); QRect subCameraRect() { return m_subCameraRect; } void computeTransform(QSize imgSize); @@ -112,7 +112,7 @@ protected slots: signals: void startCamera(); void stopCamera(); - void subCameraResized(bool isDragging); + void subCameraChanged(bool isDragging); }; //============================================================================= @@ -220,6 +220,33 @@ protected slots: }; //============================================================================= +// SubCameraButton +// button with context menu to open preset +//----------------------------------------------------------------------------- + +class SubCameraButton : public QPushButton { + Q_OBJECT + std::unique_ptr<QSettings> m_settings; + + QSize m_curResolution; + QRect m_curSubCamera; + +public: + SubCameraButton(const QString& text, QWidget* parent = 0); + void setCurResolution(const QSize& size) { m_curResolution = size; } + void setCurSubCamera(const QRect& rect) { m_curSubCamera = rect; } + +protected: + void contextMenuEvent(QContextMenuEvent* event) override; +protected slots: + void onSubCameraAct(); + void onSaveSubCamera(); + void onDeletePreset(); +signals: + void subCameraPresetSelected(const QRect&); +}; + +//============================================================================= // PencilTestPopup //----------------------------------------------------------------------------- @@ -262,8 +289,9 @@ class PencilTestPopup : public DVGui::Dialog { QToolButton* m_previousLevelButton; - QPushButton* m_subcameraButton; + SubCameraButton* m_subcameraButton; DVGui::IntLineEdit *m_subWidthFld, *m_subHeightFld; + DVGui::IntLineEdit *m_subXPosFld, *m_subYPosFld; QSize m_allowedCameraSize; bool m_captureWhiteBGCue; @@ -353,8 +381,9 @@ protected slots: void onSceneSwitched(); void onSubCameraToggled(bool); - void onSubCameraResized(bool isDragging); - void onSubCameraSizeEdited(); + void onSubCameraChanged(bool isDragging); + void onSubCameraRectEdited(); + void onSubCameraPresetSelected(const QRect&); void onTimeout();