функция должна работать, пока не будет нажата кнопка
Здравствйте. Есть программа, C++ QT, задача такова, что у меня есть эмуляция процесса зарядки аккумулятора, который срабатывает после нажатия кнопки, также существуют ещё две кнопки: "прекращение зарядки" и "ускоренная зарядка". Вопрос, собственно, в том, что при нажатии кнопки "запуск зарядки" или "ускоренная зарядка", остальные кнопки блокируются, до завершения работы функции зарядки, что делает невозможным принудительное прекращение этой самой зарядки, используя кнопку "Прекращение зарядки". Как дать пользователю возможность использовать остальные кнопки во время процесса работы функции её принудительного завершения? Сама зарядка происходит через цикл do {} while(); с установленными задержками увеличения уровня заряда.
void MainWindow::charge()
{
int krya;
double i = 0;
double dt = 1;
if (currentVoltage <= (minVoltage + (maxVoltage - minVoltage) / 5) && (currentTime < needTime))
do
{
currentVoltage += (amperage * voltage * 3 * dt)/k; // тут должна быть функция зарядки конкретного аккумулятора .
Sleep(1000 * dt);
i += 10 * dt;
currentVolume = (currentVoltage - minVoltage) * k;
currentTime = currentVolume / consumptions;
QString output2 {"%время зарядки: "},
output3,
output4 {"мc\nтекущее напряжение акб: "},
output5;
krya = 100 * (currentVoltage-minVoltage) / (maxVoltage-minVoltage) ;
output3.setNum(i);
output5.setNum(currentVoltage);
ui->progressBar->setValue(krya);
ui->progressBar->value();
ui->main_label->setText(output2 + output3 + output4 + output5);
} while (currentVoltage < (minVoltage + (maxVoltage - minVoltage) / 5 )&&(currentTime<needTime));
Ответы (2 шт):
Используйте QFutureWatcher и QtConcurrent для запуска в отдельном потоке, это поможет не блокировать основной поток
Сделал небольшой примерчик. В качестве future выступает QtConcurrent::map. В основном окне расположены 2 кнопки start и stop, и Label. Методы onStartBtnClicked и onStopBtnClicked обрабатывают нажатие соответствующих кнопок.
По изменению progressValue устанавливается значение в Label
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QObject>
#include <QMainWindow>
#include <QFutureWatcher>
#include <QFuture>
#include <QtConcurrent>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onStartBtnClicked();
void onStopBtnClicked();
void setValue(const int &value);
private:
Ui::MainWindow *ui;
QFutureWatcher<void> watcher;
const int iterations = 1000;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&watcher, &QFutureWatcher<void>::progressValueChanged, this, &MainWindow::setValue);
connect(ui->startBtn, &QPushButton::clicked, this, &MainWindow::onStartBtnClicked);
connect(ui->stopBtn, &QPushButton::clicked, this, &MainWindow::onStopBtnClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onStartBtnClicked()
{
QList<int> list;
for (int i = 0; i < iterations; i+=5)
list.append(i);
std::function<void(int&)> spin = [](int &iteration) {
qDebug() << "iteration" << iteration << "in thread" << QThread::currentThreadId();
QThread::sleep(1);
};
watcher.setFuture(QtConcurrent::map(list, spin));
}
void MainWindow::onStopBtnClicked()
{
watcher.cancel();
watcher.waitForFinished();
}
void MainWindow::setValue(const int &value)
{
ui->label->setText(QString::number(value));
}
Первый вариант это в пределах одного потока эмулировать зарядку с помощью таймера:
QTimer* timer; // объявляем таймер как поле класса вашего окна
// где-то в конструкторе окна:
timer = new QTimer(this);
timer->setInterval(1000); // это вместо вашего sleep
// раз в секунду обновляем заряд
connect(timer, &QTimer::timeout, this, &MainWindow::charge);
timer->start();
// в методе charge не делаем цикл, а обрабатываем только один шаг
ui->progressBar->setValue(krya);
ui->progressBar->value();
ui->main_label->setText(output2 + output3 + output4 + output5);
// в конце проверяем, если зарядка закончена, останавливаем таймер
if (currentVoltage < (minVoltage + (maxVoltage - minVoltage) / 5 )&&(currentTime<needTime))
timer->stop();
Остановку зарядки делаем так же остановкой таймера.
Второй вариант это действительно запустить функцию charge в отдельном потоке, но вместо прямого управления элементами гуя бросать из цикла сигналы с обновленными параметрами, а сигнал присоединить к слоту обновления. И придется попрыгать с остановкой, видимо делать атомарный флажок остановки и проверять его в цикле зарядки.