Commit 35c084be authored by Alexander Stippich's avatar Alexander Stippich
Browse files

implement batch mode

batch mode automatically starts a scan after the previous one
with a configurable time delay.

BUG: 411229
parent db2090c8
......@@ -43,6 +43,8 @@ target_sources(KF5Sane PRIVATE
options/ksanelistoption.cpp
options/ksaneinvertoption.cpp
options/ksanepagesizeoption.cpp
options/ksanebatchmodeoption.cpp
options/ksanebatchdelayoption.cpp
)
ecm_qt_declare_logging_category(KF5Sane
......
......@@ -220,6 +220,11 @@ void KSaneCore::stopScan()
if (d->m_scanThread->isRunning()) {
d->m_scanThread->cancelScan();
}
if (d->m_batchModeTimer.isActive()) {
d->m_batchModeTimer.stop();
Q_EMIT batchModeCountDown(0);
Q_EMIT scanFinished(KSaneScanStatus::NoError, i18n("Scanning stopped by user."));
}
}
QImage *KSaneCore::scanImage() const
......
......@@ -88,6 +88,8 @@ public:
GammaBlueOption,
BlackLevelOption,
WhiteLevelOption,
BatchModeOption,
BatchDelayOption,
};
/*
......@@ -293,6 +295,12 @@ Q_SIGNALS:
* @param deviceModel is the model name of the currently opened scanner.
*/
void openedDeviceInfoUpdated(const QString &deviceName, const QString &deviceVendor, const QString &deviceModel);
/**
* This signal is emitted for the count down when in batch mode.
* @param remainingSeconds are the remaining seconds until the next scan starts.
*/
void batchModeCountDown(int remainingSeconds);
private:
std::unique_ptr<KSaneCorePrivate> d;
......
......@@ -28,6 +28,8 @@
#include "ksaneintegeroption.h"
#include "ksaneinvertoption.h"
#include "ksanepagesizeoption.h"
#include "ksanebatchmodeoption.h"
#include "ksanebatchdelayoption.h"
namespace KSaneIface
{
......@@ -57,7 +59,9 @@ static const QHash<QString, KSaneCore::KSaneOptionName> stringEnumTranslation =
{ QStringLiteral(SANE_NAME_GAMMA_VECTOR_G), KSaneCore::GammaGreenOption },
{ QStringLiteral(SANE_NAME_GAMMA_VECTOR_B), KSaneCore::GammaBlueOption },
{ QStringLiteral(SANE_NAME_BLACK_LEVEL), KSaneCore::BlackLevelOption },
{ QStringLiteral(SANE_NAME_WHITE_LEVEL), KSaneCore::WhiteLevelOption }, };
{ QStringLiteral(SANE_NAME_WHITE_LEVEL), KSaneCore::WhiteLevelOption },
{ BatchModeOptionName, KSaneCore::BatchModeOption },
{ BatchDelayOptionName, KSaneCore::BatchDelayOption }, };
KSaneCorePrivate::KSaneCorePrivate(KSaneCore *parent):
q(parent)
......@@ -71,6 +75,9 @@ KSaneCorePrivate::KSaneCorePrivate(KSaneCore *parent):
m_auth = KSaneAuth::getInstance();
m_optionPollTimer.setInterval(100);
connect(&m_optionPollTimer, &QTimer::timeout, this, &KSaneCorePrivate::pollPollOptions);
m_batchModeTimer.setInterval(1000);
connect(&m_batchModeTimer, &QTimer::timeout, this, &KSaneCorePrivate::batchModeTimerUpdate);
}
KSaneCore::KSaneOpenStatus KSaneCorePrivate::loadDeviceOptions()
......@@ -188,17 +195,28 @@ KSaneCore::KSaneOpenStatus KSaneCorePrivate::loadDeviceOptions()
}
}
// add extra option for inverting image colors
KSaneBaseOption *invertOption = new KSaneInvertOption();
m_optionsList.append(invertOption);
m_externalOptionsList.append(new KSaneInternalOption(invertOption));
m_optionsLocation.insert(KSaneCore::InvertColorOption, m_optionsList.size() - 1);
// add extra option for selecting specific page sizes
KSaneBaseOption *pageSizeOption = new KSanePageSizeOption(optionTopLeftX, optionTopLeftY,
optionBottomRightX, optionBottomRightY, optionResolution);
m_optionsList.append(pageSizeOption);
m_externalOptionsList.append(new KSaneInternalOption(pageSizeOption));
m_optionsLocation.insert(KSaneCore::PageSizeOption, m_optionsList.size() - 1);
m_optionsLocation.insert(KSaneCore::PageSizeOption, m_optionsList.size() - 1);
// add extra option for batch mode scanning with a delay
m_batchMode = new KSaneBatchModeOption();
m_optionsList.append(m_batchMode);
m_externalOptionsList.append(new KSaneInternalOption(m_batchMode));
m_optionsLocation.insert(KSaneCore::BatchModeOption, m_optionsList.size() - 1);
m_batchModeDelay = new KSaneBatchDelayOption();
m_optionsList.append(m_batchModeDelay);
m_externalOptionsList.append(new KSaneInternalOption(m_batchModeDelay));
m_optionsLocation.insert(KSaneCore::BatchDelayOption, m_optionsList.size() - 1);
// add extra option for inverting image colors
KSaneBaseOption *invertOption = new KSaneInvertOption();
m_optionsList.append(invertOption);
m_externalOptionsList.append(new KSaneInternalOption(invertOption));
m_optionsLocation.insert(KSaneCore::InvertColorOption, m_optionsList.size() - 1);
// NOTICE The Pixma network backend behaves badly. polling a value will result in 1 second
// sleeps for every poll. The problem has been reported, but no easy/quick fix was available and
......@@ -332,7 +350,14 @@ void KSaneCorePrivate::imageScanFinished()
m_scanThread->start();
return;
}
// check if we should have timed batch scanning
if (m_batchMode->value().toBool() && !m_cancelMultiPageScan) {
// in batch mode only one area can be scanned per page
m_batchModeCounter = 0;
batchModeTimerUpdate();
m_batchModeTimer.start();
return;
}
// Check if we have a "wait for button" batch scanning
if (m_waitForExternalButton) {
qCDebug(KSANE_LOG) << "waiting for external button press to start next scan";
......@@ -392,4 +417,18 @@ void KSaneCorePrivate::setWaitForExternalButton(const QVariant &value)
m_waitForExternalButton = value.toBool();
}
void KSaneCorePrivate::batchModeTimerUpdate()
{
const int delay = m_batchModeDelay->value().toInt();
Q_EMIT q->batchModeCountDown(delay - m_batchModeCounter);
if (m_batchModeCounter >= delay) {
m_batchModeCounter = 0;
if (m_scanThread!= nullptr) {
m_scanThread->start();
}
m_batchModeTimer.stop();
}
m_batchModeCounter++;
}
} // NameSpace KSaneIface
......@@ -53,6 +53,7 @@ private Q_SLOTS:
void determineMultiPageScanning(const QVariant &value);
void setWaitForExternalButton(const QVariant &value);
void pollPollOptions();
void batchModeTimerUpdate();
public:
......@@ -86,6 +87,11 @@ public:
bool m_cancelMultiPageScan = false;
// next scanning will start with a hardware button press
bool m_waitForExternalButton = false;
// batch mode options
KSaneBaseOption *m_batchMode;
KSaneBaseOption *m_batchModeDelay;
QTimer m_batchModeTimer;
int m_batchModeCounter = 0;
};
} // NameSpace KSaneIface
......
......@@ -56,7 +56,8 @@ public:
UnitMilliMeter,
UnitDPI,
UnitPercent,
UnitMicroSecond
UnitMicroSecond,
UnitSecond
} KSaneOptionUnit;
Q_ENUM(KSaneOptionUnit);
......
......@@ -46,6 +46,7 @@ KSaneWidget::KSaneWidget(QWidget *parent)
connect(d->m_ksaneCoreInterface, &KSaneCore::scanFinished, d, &KSaneWidgetPrivate::scanDone);
connect(d->m_ksaneCoreInterface, &KSaneCore::userMessage, d, &KSaneWidgetPrivate::alertUser);
connect(d->m_ksaneCoreInterface, &KSaneCore::scanProgress, d, &KSaneWidgetPrivate::updateProgress);
connect(d->m_ksaneCoreInterface, &KSaneCore::batchModeCountDown, d, &KSaneWidgetPrivate::updateCountDown);
connect(d->m_ksaneCoreInterface, &KSaneCore::availableDevices, d, &KSaneWidgetPrivate::signalDevListUpdate);
connect(d->m_ksaneCoreInterface, &KSaneCore::buttonPressed, this, &KSaneWidget::buttonPressed);
connect(d->m_ksaneCoreInterface, &KSaneCore::openedDeviceInfoUpdated, this, &KSaneWidget::openedDeviceInfoUpdated);
......@@ -61,9 +62,14 @@ KSaneWidget::KSaneWidget(QWidget *parent)
d->m_warmingUp->setAlignment(Qt::AlignCenter);
d->m_warmingUp->setAutoFillBackground(true);
d->m_warmingUp->setBackgroundRole(QPalette::Highlight);
//d->m_warmingUp->setForegroundRole(QPalette::HighlightedText);
d->m_warmingUp->hide();
d->m_countDown = new QLabel;
d->m_countDown->setAlignment(Qt::AlignCenter);
d->m_countDown->setAutoFillBackground(true);
d->m_countDown->setBackgroundRole(QPalette::Highlight);
d->m_countDown->hide();
d->m_progressBar = new QProgressBar;
d->m_progressBar->setMaximum(100);
......@@ -152,12 +158,14 @@ KSaneWidget::KSaneWidget(QWidget *parent)
d->m_btnFrame->setMinimumHeight(minHeight);
d->m_activityFrame->setMinimumHeight(minHeight);
d->m_warmingUp->setMinimumHeight(minHeight);
d->m_countDown->setMinimumHeight(minHeight);
d->m_previewFrame = new QWidget;
QVBoxLayout *preview_layout = new QVBoxLayout(d->m_previewFrame);
preview_layout->setContentsMargins(0, 0, 0, 0);
preview_layout->addWidget(d->m_previewViewer, 100);
preview_layout->addWidget(d->m_warmingUp, 0);
preview_layout->addWidget(d->m_countDown, 0);
preview_layout->addWidget(d->m_activityFrame, 0);
preview_layout->addWidget(d->m_btnFrame, 0);
......
......@@ -1208,9 +1208,11 @@ void KSaneWidgetPrivate::updateProgress(int progress)
if (progress < 0 && !m_warmingUp->isVisible()) {
m_warmingUp->show();
m_activityFrame->hide();
m_countDown->hide();
} else {
m_warmingUp->hide();
m_activityFrame->show();
m_countDown->hide();
}
if (m_isPreview) {
// the image size might have changed
......@@ -1234,6 +1236,16 @@ void KSaneWidgetPrivate::updateProgress(int progress)
Q_EMIT q->scanProgress(progress);
}
void KSaneWidgetPrivate::updateCountDown(int remainingSeconds)
{
m_countDown->setText(i18n("Next scan starts in %1 s.", remainingSeconds));
if (remainingSeconds > 0 && !m_countDown->isVisible()) {
m_countDown->show();
m_warmingUp->hide();
m_activityFrame->hide();
}
}
void KSaneWidgetPrivate::alertUser(KSaneCore::KSaneScanStatus status, const QString &strStatus)
{
if (!q->isSignalConnected(QMetaMethod::fromSignal(&KSaneWidget::userMessage))) {
......
......@@ -71,6 +71,7 @@ public Q_SLOTS:
void previewScanDone(KSaneCore::KSaneScanStatus status, const QString &strStatus);
void oneFinalScanDone(KSaneCore::KSaneScanStatus status, const QString &strStatus);
void updateProgress(int progress);
void updateCountDown(int remainingSeconds);
void handleSelection(float tl_x, float tl_y, float br_x, float br_y);
void signalDevListUpdate(const QList<KSaneCore::DeviceInfo> &deviceList);
void imageReady(const QImage &image);
......@@ -130,6 +131,7 @@ public:
QWidget *m_activityFrame;
QLabel *m_warmingUp;
QLabel *m_countDown;
QProgressBar *m_progressBar;
QPushButton *m_cancelBtn;
......
/* ============================================================
*
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#include "ksanebatchdelayoption.h"
#include <ksane_debug.h>
namespace KSaneIface
{
KSaneBatchDelayOption::KSaneBatchDelayOption()
{
m_optionType = KSaneOption::TypeInteger;
}
KSaneOption::KSaneOptionState KSaneBatchDelayOption::state() const
{
return KSaneOption::StateActive;;
}
QString KSaneBatchDelayOption::name() const
{
return BatchDelayOptionName;
}
QString KSaneBatchDelayOption::title() const
{
return i18n("Batch mode time delay");
}
QString KSaneBatchDelayOption::description() const
{
return i18n("Specify the time delay between each scan when batch mode is enabled.");
}
QVariant KSaneBatchDelayOption::minimumValue() const
{
return 0;
}
QVariant KSaneBatchDelayOption::maximumValue() const
{
return 300;
}
QVariant KSaneBatchDelayOption::stepValue() const
{
return 1;
}
QVariant KSaneBatchDelayOption::value() const
{
return m_delayValue;
}
QString KSaneBatchDelayOption::valueAsString() const
{
return QString::number(m_delayValue);
}
KSaneOption::KSaneOptionUnit KSaneBatchDelayOption::valueUnit() const
{
return KSaneOption::UnitSecond;
}
bool KSaneBatchDelayOption::setValue(const QVariant &val)
{
bool ok;
int newValue = val.toInt(&ok);
if (ok && newValue != m_delayValue) {
m_delayValue = newValue;
Q_EMIT valueChanged(m_delayValue);
}
return ok;
}
} // NameSpace KSaneIface
/* ============================================================
*
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#ifndef KSANE_DELAY_OPTION_H
#define KSANE_DELAY_OPTION_H
#include "ksanebaseoption.h"
#include "ksaneoption.h"
namespace KSaneIface
{
static const QString BatchDelayOptionName = QStringLiteral("KSane::BatchTimeDelay");
class KSaneBatchDelayOption : public KSaneBaseOption
{
Q_OBJECT
public:
KSaneBatchDelayOption();
KSaneOption::KSaneOptionState state() const override;
QString name() const override;
QString title() const override;
QString description() const override;
QVariant minimumValue() const override;
QVariant maximumValue() const override;
QVariant stepValue() const override;
QVariant value() const override;
QString valueAsString() const override;
KSaneOption::KSaneOptionUnit valueUnit() const override;
public Q_SLOTS:
bool setValue(const QVariant &value) override;
private:
KSaneOption::KSaneOptionState m_state = KSaneOption::StateHidden;
int m_delayValue = 10;
};
} // NameSpace KSaneIface
#endif // KSANE_DELAY_OPTION_H
/* ============================================================
*
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#include "ksanebatchmodeoption.h"
#include <ksane_debug.h>
namespace KSaneIface
{
KSaneBatchModeOption::KSaneBatchModeOption()
{
m_optionType = KSaneOption::TypeBool;
}
KSaneOption::KSaneOptionState KSaneBatchModeOption::state() const
{
return KSaneOption::StateActive;
}
QString KSaneBatchModeOption::name() const
{
return BatchModeOptionName;
}
QString KSaneBatchModeOption::title() const
{
return i18n("Batch mode with time delay");
}
QString KSaneBatchModeOption::description() const
{
return i18n("Enables batch mode scanning. Continues scanning after a delay until canceled.");
}
bool KSaneBatchModeOption::setValue(const QVariant &value)
{
const bool toggled = value.toBool();
if (m_checked != toggled) {
m_checked = toggled;
Q_EMIT valueChanged(m_checked);
}
return true;
}
QVariant KSaneBatchModeOption::value() const
{
return m_checked;
}
QString KSaneBatchModeOption::valueAsString() const
{
if (state() == KSaneOption::StateHidden) {
return QString();
}
if (m_checked) {
return QStringLiteral("true");
} else {
return QStringLiteral("false");
}
}
} // NameSpace KSaneIface
/* ============================================================
*
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#ifndef KSANE_BATCH_OPTION_H
#define KSANE_BATCH_OPTION_H
#include "ksanebaseoption.h"
namespace KSaneIface
{
static const QString BatchModeOptionName = QStringLiteral("KSane::BatchMode");
class KSaneBatchModeOption : public KSaneBaseOption
{
Q_OBJECT
public:
KSaneBatchModeOption();
KSaneOption::KSaneOptionState state() const override;
QString name() const override;
QString title() const override;
QString description() const override;
QVariant value() const override;
QString valueAsString() const override;
public Q_SLOTS:
bool setValue(const QVariant &value) override;
private:
bool m_checked = false;
};
} // NameSpace KSaneIface
#endif // KSANE_BATCH_OPTION_H
......@@ -170,6 +170,9 @@ QString LabeledCombo::getStringWithUnitForInteger(int iValue) const
case KSaneOption::UnitMicroSecond:
return i18nc("Parameter and Unit (Microseconds)", "%1 µs", iValue);
break;
case KSaneOption::UnitSecond:
return i18nc("Parameter and Unit (seconds)", "%1 s", iValue);
break;
default:
return i18n("%1", iValue);
break;
......@@ -198,6 +201,9 @@ QString LabeledCombo::getStringWithUnitForFloat(float fValue) const
case KSaneOption::UnitMicroSecond:
return i18nc("Parameter and Unit (Microseconds)", "%1 µs", fValue);
break;
case KSaneOption::UnitSecond:
return i18nc("Parameter and Unit (seconds)", "%1 s", fValue);
break;
default:
return i18n("%1", fValue);
break;
......
......@@ -56,6 +56,9 @@ LabeledFSlider::LabeledFSlider(QWidget *parent, KSaneOption *option)
case KSaneOption::UnitMicroSecond:
unitSuffix = i18nc("Double numbers. SpinBox parameter unit (Microseconds)", " µs");
break;
case KSaneOption::UnitSecond:
unitSuffix = i18nc("SpinBox parameter unit (seconds)", " s");
break;
default:
unitSuffix = QString();
break;
......
......@@ -58,6 +58,9 @@ LabeledSlider::LabeledSlider(QWidget *parent, KSaneOption *option)
case KSaneOption::UnitMicroSecond:
unitSuffix = ki18ncp("SpinBox parameter unit (Microseconds)", " µs", " µs");
break;
case KSaneOption::UnitSecond:
unitSuffix = ki18ncp("SpinBox parameter unit (seconds)", " s", " s");
break;
default:
unitSuffix = KLocalizedString();
break;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment