From 6428d677788ebe7664898e3935127b1f0d21b802 Mon Sep 17 00:00:00 2001 From: mar77i Date: Sun, 5 May 2024 16:11:26 +0200 Subject: [PATCH] improve settingswidget, allow changing all fields --- CMakeLists.txt | 2 + bigintmandelwidget.cpp | 33 ++++++-- bigintmandelwidget.h | 2 + mandel.cpp | 48 ++++++++---- mandel.h | 44 +++++++---- menubar.cpp | 6 ++ menubar.h | 2 +- settingswidget.cpp | 174 ++++++++++++++++++++++++++++++----------- settingswidget.h | 84 +++++++++++++++++--- uint64validator.cpp | 26 ++++++ uint64validator.h | 35 +++++++++ 11 files changed, 364 insertions(+), 92 deletions(-) create mode 100644 uint64validator.cpp create mode 100644 uint64validator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d4929c..1c44864 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,8 @@ set(PROJECT_SOURCES menubar.h mandellabel.cpp mandellabel.h + uint64validator.cpp + uint64validator.h ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) diff --git a/bigintmandelwidget.cpp b/bigintmandelwidget.cpp index 6280e54..06d8b4f 100644 --- a/bigintmandelwidget.cpp +++ b/bigintmandelwidget.cpp @@ -51,8 +51,7 @@ BigintMandelWidget::BigintMandelWidget(QWidget *parent) } BigintMandelWidget::~BigintMandelWidget() { - fw->cancel(); - fw->waitForFinished(); + stop(); } void BigintMandelWidget::finished_cell(int num) { @@ -65,7 +64,6 @@ void BigintMandelWidget::finished_cell(int num) { void BigintMandelWidget::finished() { mandel_label->set_draw_progress(-1); - settings_widget->set_finished(true); update(); } @@ -89,6 +87,7 @@ void BigintMandelWidget::update_status_bar() { } void BigintMandelWidget::start() { + stop(); fw->setFuture( QtConcurrent::mapped( settings.get_cells(), @@ -99,6 +98,11 @@ void BigintMandelWidget::start() { update(); } +void BigintMandelWidget::stop() { + fw->cancel(); + fw->waitForFinished(); +} + void BigintMandelWidget::mousePressEvent(QMouseEvent *event) { QSize size(settings.get_params().get_size()); QPoint pos(event->pos()); @@ -163,19 +167,25 @@ void BigintMandelWidget::export_img() { } void BigintMandelWidget::exec_settings_widget() { - settings_widget->update_fields(settings.get_params(), fw->isFinished()); + settings_widget->update_fields(settings.get_params()); settings_widget->exec(); } void BigintMandelWidget::reset() { - fw->cancel(); - fw->waitForFinished(); settings.reset(128, get_ideal_size()); start(); } void BigintMandelWidget::settings_widget_accepted() { - settings.set_max_iter(settings_widget->get_max_iter().toULongLong()); + if (settings_widget->clear_image()) { + settings.set_params(settings_widget->get_params()); + settings.clear(); + } else { + quint64 max_iter = settings_widget->get_max_iter(); + if (max_iter <= settings.get_max_iter()) + return; + settings.set_max_iter(max_iter); + } start(); } @@ -230,3 +240,12 @@ void BigintMandelWidget::update() { update_status_bar(); QWidget::update(); } + +void BigintMandelWidget::resize_to_window() { + QSize size = get_ideal_size(); + if (size == settings.get_params().get_size()) + return; + settings.set_size(size); + settings.clear(); + start(); +} diff --git a/bigintmandelwidget.h b/bigintmandelwidget.h index 8f20425..db2f2d6 100644 --- a/bigintmandelwidget.h +++ b/bigintmandelwidget.h @@ -29,6 +29,7 @@ class BigintMandelWidget : public QWidget { void update_status_bar(); void start(); + void stop(); inline const QSize get_ideal_size() const { return scroll_area->size().shrunkBy(scroll_area->contentsMargins()); } @@ -56,6 +57,7 @@ public Q_SLOTS: void load_data(); void save_data(); void update(); + void resize_to_window(); }; #endif // BIGINTMANDELWIDGET_H diff --git a/mandel.cpp b/mandel.cpp index a6946a2..74a1a11 100644 --- a/mandel.cpp +++ b/mandel.cpp @@ -8,25 +8,37 @@ #include "colors.h" #include "mandel.h" +static inline const MpzPoint calculate_offset( + const MpzPoint center_f, const QSize size +) { + return MpzPoint( + center_f.get_x() - size.width() / 2, + center_f.get_y() + size.height() / 2 + ); +} + MandelParams::MandelParams() {} MandelParams::MandelParams( - size_t max_iter, + quint64 max_iter, QSize size, MpzPoint center_f, mpz_class one ) : max_iter(max_iter), size(size), center_f(center_f), - one(one), - left(center_f.get_x() - size.width() / 2.), - top(center_f.get_y() + size.height() / 2.) {} + offset(calculate_offset(center_f, size)), + one(one) {} MandelParams::MandelParams(const MandelParams &other) : max_iter(other.max_iter), size(other.size), center_f(other.center_f), - one(other.one), - left(center_f.get_x() - size.width() / 2.), - top(center_f.get_y() + size.height() / 2.) {} + offset(calculate_offset(other.center_f, other.size)), + one(other.one) {} + +void MandelParams::set_size(const QSize &size) { + this->size = size; + this->offset = calculate_offset(center_f, size); +} MandelParams MandelParams::from_json(const QJsonObject &json) { return MandelParams( @@ -80,11 +92,17 @@ static inline QImage *setup_image(QImage *img, const QSize &size) { return img; } -MandelSettings::MandelSettings(size_t max_iter, const QSize size) +MandelSettings::MandelSettings(quint64 max_iter, const QSize size) : img(nullptr) { reset(max_iter, size); } +void MandelSettings::clear() { + img = setup_image(img, params.get_size()); + img->fill(Qt::GlobalColor::black); + setup_cells(cells, ¶ms); +} + void MandelSettings::zoom(const QSize size, int zoom_factor, const QPoint pos) { MpzPoint p = cells[img->width() * pos.y() + pos.x()].get_rpos0(); params = MandelParams( @@ -93,9 +111,7 @@ void MandelSettings::zoom(const QSize size, int zoom_factor, const QPoint pos) { MpzPoint(p.get_x() * zoom_factor, p.get_y() * zoom_factor), params.get_one() * zoom_factor ); - img = setup_image(img, size); - img->fill(Qt::GlobalColor::black); - setup_cells(cells, ¶ms); + clear(); } void MandelSettings::finished_cell(int num, const MandelResultCell &result) { @@ -112,7 +128,7 @@ void MandelSettings::save_img(QString file_name) { img->save(file_name); } -void MandelSettings::reset(size_t max_iter, const QSize size) { +void MandelSettings::reset(quint64 max_iter, const QSize size) { mpz_class one; MpzPoint center_f; int ione = std::min(size.width(), size.height()) / 3; @@ -125,8 +141,7 @@ void MandelSettings::reset(size_t max_iter, const QSize size) { one = mpz_class(ione + 1); center_f = MpzPoint(one * -3 / 4, 0); params = MandelParams(max_iter, size, center_f, one); - img = setup_image(img, size); - setup_cells(cells, ¶ms); + clear(); } void MandelSettings::from_json(const QJsonObject &json) { @@ -159,8 +174,9 @@ MandelCell::MandelCell(const MandelCell &cell) rpos0(cell.rpos0) {} void MandelCell::setup(const QPoint pos) { + const MpzPoint &offset = params->get_offset(); this->pos = pos; - rpos0 = MpzPoint(params->get_left() + pos.x(), params->get_top() - pos.y()); + rpos0 = MpzPoint(offset.get_x() + pos.x(), offset.get_y() - pos.y()); result = MandelResultCell(); } @@ -168,7 +184,7 @@ MandelResultCell MandelCell::iterate() const { MpzPoint rpos = result.get_rpos(), sq; mpz_class one = params->get_one(), four = one * one * 4; mpz_class sqx, sqy; - size_t iter = result.get_iter(); + quint64 iter = result.get_iter(); for (; iter < params->get_max_iter(); iter++) { sqx = rpos.get_x() * rpos.get_x(); sqy = rpos.get_y() * rpos.get_y(); diff --git a/mandel.h b/mandel.h index 3d31b7d..3522cc6 100644 --- a/mandel.h +++ b/mandel.h @@ -21,39 +21,43 @@ public: }; class MandelResultCell { - size_t iter; + quint64 iter; MpzPoint rpos; public: MandelResultCell() : iter(0) {} - explicit MandelResultCell(size_t iter, MpzPoint rpos) + explicit MandelResultCell(quint64 iter, MpzPoint rpos) : iter(iter), rpos(rpos) {} - inline const size_t get_iter() const { return iter; } + inline const quint64 get_iter() const { return iter; } inline const MpzPoint get_rpos() const { return rpos; } }; class MandelParams { private: - size_t max_iter; + quint64 max_iter; QSize size; - MpzPoint center_f; - mpz_class left, top, one; + MpzPoint center_f, offset; + mpz_class one; public: // required by the MandelSettings constructor MandelParams(); explicit MandelParams( - size_t max_iter, + quint64 max_iter, QSize size, MpzPoint center_f, mpz_class one ); MandelParams(const MandelParams &other); - inline const size_t get_max_iter() const { return max_iter; } - inline void set_max_iter(const size_t max_iter) { this->max_iter = max_iter; } + inline const quint64 get_max_iter() const { return max_iter; } + inline void set_max_iter(const quint64 max_iter) { + this->max_iter = max_iter; + } inline const QSize get_size() const { return size; } + void set_size(const QSize &size); inline const MpzPoint get_center_f() const { return center_f; } - inline const mpz_class get_left() const { return left; } - inline const mpz_class get_top() const { return top; } + inline const MpzPoint &get_offset() const { + return offset; + } inline const mpz_class get_one() const { return one; } static MandelParams from_json(const QJsonObject &json); @@ -68,16 +72,26 @@ class MandelSettings { QVector cells; public: - explicit MandelSettings(size_t max_iter, QSize size); + explicit MandelSettings(quint64 max_iter, QSize size); + void clear(); inline QPixmap get_pixmap() const { return QPixmap::fromImage(*img); } inline const QVector get_cells() const { return cells; } inline const MandelParams &get_params() const { return params; } - inline void set_max_iter(size_t max_iter) { params.set_max_iter(max_iter); } + inline void set_params(const MandelParams ¶ms) { + this->params = params; + } + inline quint64 get_max_iter() const { return params.get_max_iter(); } + inline void set_max_iter(quint64 max_iter) { + params.set_max_iter(max_iter); + } + inline void set_size(const QSize &size) { + params.set_size(size); + } void zoom(const QSize size, int zoom_factor, const QPoint pos); void finished_cell(int num, const MandelResultCell &cell); void save_img(QString file_name); - void reset(size_t max_iter, QSize size); + void reset(quint64 max_iter, QSize size); void from_json(const QJsonObject &json); QJsonObject to_json(); }; @@ -102,7 +116,7 @@ public: inline const QPoint get_pos() const { return pos; } inline const MpzPoint get_rpos0() const { return rpos0; } inline const QColor get_color() const { - size_t iter = result.get_iter(); + quint64 iter = result.get_iter(); return iter < params->get_max_iter() ? colors[iter % colors.size()] : Qt::GlobalColor::black; diff --git a/menubar.cpp b/menubar.cpp index 39683c3..f43fd13 100644 --- a/menubar.cpp +++ b/menubar.cpp @@ -46,6 +46,12 @@ MenuBar::MenuBar(BigintMandelWidget *parent) reset_action = calc_menu->addAction( "&Reset", no_key, parent, &BigintMandelWidget::reset ); + resize_action = calc_menu->addAction( + "Resi&ze to Window", + no_key, + parent, + &BigintMandelWidget::resize_to_window + ); settings_action = calc_menu->addAction( "&Settings", no_key, parent, &BigintMandelWidget::exec_settings_widget ); diff --git a/menubar.h b/menubar.h index e1b0702..e1377f6 100644 --- a/menubar.h +++ b/menubar.h @@ -15,7 +15,7 @@ class MenuBar : public QMenuBar { QAction *load_action, *save_action; QAction *export_action, *exit_action; QMenu *calc_menu; - QAction *reset_action, *settings_action; + QAction *reset_action, *resize_action, *settings_action; QMenu *zoom_menu; QAction *no_action, *two_action, *four_action; QAction *eight_action, *sixteen_action; diff --git a/settingswidget.cpp b/settingswidget.cpp index f87086f..dce5e99 100644 --- a/settingswidget.cpp +++ b/settingswidget.cpp @@ -1,69 +1,155 @@ // settingswidget.cpp +#include #include #include -#include #include #include "settingswidget.h" +#include "uint64validator.h" + +SettingsWidgetFieldBase::SettingsWidgetFieldBase( + const QString &label, QWidget *parent +) : edit(new QLineEdit("", parent)), + label(label) +{ + edit->setMinimumWidth(256); +} + +void SettingsWidgetFieldBase::add_to_layout(QGridLayout *grid_layout) { + int row_count = grid_layout->rowCount(); + grid_layout->addWidget( + new QLabel(label, edit->parentWidget()), row_count, 0 + ); + grid_layout->addWidget(edit, row_count, 1); +} + +void SettingsWidgetFieldBase::add_on_editing_finished( + SettingsWidget *settings_widget, const char *method_name +) { + QObject::connect(edit, SIGNAL(editingFinished()), settings_widget, method_name); +} + +void SettingsWidgetFieldBase::reset() { + edit->setText(orig_value); +} + +SettingsWidgetField::SettingsWidgetField( + const QString &label, QWidget *parent +) : SettingsWidgetFieldBase(label, parent) { + edit->setValidator(new UInt64Validator(edit)); +} + +void SettingsWidgetField::set_value(quint64 value) { + orig_value = QString::number(value); + reset(); +} + +SettingsWidgetField::SettingsWidgetField( + const QString &label, QWidget *parent +) : SettingsWidgetFieldBase(label, parent) { + edit->setValidator(new QIntValidator(edit)); +} + +void SettingsWidgetField::set_value(int value) { + orig_value = QString::number(value); + reset(); +} + +SettingsWidgetField::SettingsWidgetField( + const QString &label, QWidget *parent +) : SettingsWidgetFieldBase(label, parent) { + static QRegularExpression number_re("^\\d+$"); + edit->setValidator(new QRegularExpressionValidator(number_re)); +} + +void SettingsWidgetField::set_value(const mpz_class &value) { + orig_value = QString::QString::fromStdString(value.get_str()); + reset(); +} SettingsWidget::SettingsWidget(QWidget *parent) : QDialog(parent), - max_iter(new QLineEdit("", this)), - width(new QLineEdit("", this)), - height(new QLineEdit("", this)), - center_f_x(new QLineEdit("", this)), - center_f_y(new QLineEdit("", this)), - one(new QLineEdit("", this)) + max_iter("max_iter", this), + width("width", this), + height("height", this), + center_f_x("x", this), + center_f_y("y", this), + one("one", this), + clear_image_checkbox(new QCheckBox("Clear image", this)) { - static QStringList labels = { - "max_iter", "width", "height", "x", "y", "one" - }; - static QList line_edits = { - max_iter, width, height, center_f_x, center_f_y, one - }; - QPushButton *apply_button = new QPushButton("Apply"); + QPushButton *accept_button = new QPushButton("&Accept"); + QPushButton *reject_button = new QPushButton("&Reject"); QGridLayout *grid_layout = new QGridLayout(this); + QBoxLayout *button_row = new QBoxLayout(QBoxLayout::Direction::RightToLeft); int i; - setLayout(grid_layout); - for (i = 0; i < line_edits.size(); i++) { - grid_layout->addWidget(new QLabel(labels[i], this), i, 0); - if (i == 0) - line_edits[i]->setValidator( - new QIntValidator(0, INT_MAX, line_edits[i]) - ); - else - line_edits[i]->setReadOnly(true); - line_edits[i]->setMinimumWidth(256); - grid_layout->addWidget(line_edits[i], i, 1); - } + max_iter.add_to_layout(grid_layout); + width.add_to_layout(grid_layout); + width.add_on_editing_finished(this, SLOT(update_form())); + height.add_to_layout(grid_layout); + height.add_on_editing_finished(this, SLOT(update_form())); + center_f_x.add_to_layout(grid_layout); + center_f_x.add_on_editing_finished(this, SLOT(update_form())); + center_f_y.add_to_layout(grid_layout); + center_f_y.add_on_editing_finished(this, SLOT(update_form())); + one.add_to_layout(grid_layout); + one.add_on_editing_finished(this, SLOT(update_form())); + connect( + clear_image_checkbox, + &QCheckBox::checkStateChanged, + this, + &SettingsWidget::clear_image_checkbox_changed + ); + grid_layout->addWidget(clear_image_checkbox, grid_layout->rowCount(), 1); connect( - apply_button, + accept_button, &QPushButton::clicked, this, - &SettingsWidget::apply + &SettingsWidget::accept ); - grid_layout->addWidget(apply_button, i, 1); + button_row->addWidget(accept_button); + connect( + reject_button, + &QPushButton::clicked, + this, + &SettingsWidget::reject + ); + button_row->addWidget(reject_button); + grid_layout->addLayout(button_row, grid_layout->rowCount(), 0, 1, 2); } -void SettingsWidget::update_fields( - const MandelParams ¶ms, const bool is_finished -) { +void SettingsWidget::update_fields(const MandelParams ¶ms) { QSize size = params.get_size(); MpzPoint center_f = params.get_center_f(); - max_iter->setText(QString::number(params.get_max_iter())); - width->setText(QString::number(size.width())); - height->setText(QString::number(size.height())); - center_f_x->setText(QString::fromStdString(center_f.get_x().get_str())); - center_f_y->setText(QString::fromStdString(center_f.get_y().get_str())); - one->setText(QString::fromStdString(params.get_one().get_str())); - set_finished(is_finished); + max_iter.set_value(params.get_max_iter()); + width.set_value(size.width()); + height.set_value(size.height()); + center_f_x.set_value(center_f.get_x()); + center_f_y.set_value(center_f.get_y()); + one.set_value(params.get_one()); + clear_image_checkbox->setChecked(false); +} + +void SettingsWidget::update_form() { + if ( + width.is_edited() + || height.is_edited() + || center_f_x.is_edited() + || center_f_y.is_edited() + || one.is_edited() + ) + // todo: add a messagebox that we do this + clear_image_checkbox->setChecked(true); } -void SettingsWidget::apply() { - if (is_finished) - accept(); - else - reject(); +void SettingsWidget::clear_image_checkbox_changed(Qt::CheckState state) { + if (state == Qt::Unchecked) { + // todo: add a messagebox if we should do this + width.reset(); + height.reset(); + center_f_x.reset(); + center_f_y.reset(); + one.reset(); + } } diff --git a/settingswidget.h b/settingswidget.h index 0fab8c1..450c12c 100644 --- a/settingswidget.h +++ b/settingswidget.h @@ -4,30 +4,96 @@ #ifndef SETTINGSWIDGET_H #define SETTINGSWIDGET_H +#include #include #include +#include #include #include "mandel.h" +class SettingsWidget; + +class SettingsWidgetFieldBase { +protected: + QLineEdit *edit; + const QString label; + QString orig_value; + +public: + SettingsWidgetFieldBase(const QString &label, QWidget *parent = nullptr); + inline bool is_edited() const { return edit->text() != orig_value; } + void add_to_layout(QGridLayout *grid_layout); + void add_on_editing_finished( + SettingsWidget *settings_widget, const char *method_name + ); + void reset(); +}; + +template class SettingsWidgetField; +template <> +class SettingsWidgetField : public SettingsWidgetFieldBase { +public: + explicit SettingsWidgetField( + const QString &label, QWidget *parent = nullptr + ); + void set_value(quint64 value); + inline const quint64 get_value() const { + return edit->text().toULongLong(); + } +}; + +template <> +class SettingsWidgetField : public SettingsWidgetFieldBase { +public: + explicit SettingsWidgetField( + const QString &label, QWidget *parent = nullptr + ); + void set_value(int value); + inline const int get_value() const { + return edit->text().toInt(); + } +}; + +template <> +class SettingsWidgetField : public SettingsWidgetFieldBase { +public: + explicit SettingsWidgetField( + const QString &label, QWidget *parent = nullptr + ); + void set_value(const mpz_class &value); + inline const mpz_class get_value() const { + return mpz_class(edit->text().toStdString()); + } +}; + class SettingsWidget : public QDialog { Q_OBJECT - QLineEdit *max_iter, *width, *height, *center_f_x, *center_f_y, *one; - bool is_finished; + SettingsWidgetField max_iter; + SettingsWidgetField width, height; + SettingsWidgetField center_f_x, center_f_y, one; + QCheckBox *clear_image_checkbox; public: explicit SettingsWidget(QWidget *parent = nullptr); - void update_fields(const MandelParams ¶ms, const bool is_finished); - inline const QString get_max_iter() const { return max_iter->text(); }; - - inline void set_finished(const bool is_finished) { - this->is_finished = is_finished; - max_iter->setReadOnly(!is_finished); + void update_fields(const MandelParams ¶ms); + inline const quint64 get_max_iter() const { return max_iter.get_value(); } + inline const MandelParams get_params() { + return MandelParams( + max_iter.get_value(), + QSize(width.get_value(), height.get_value()), + MpzPoint(center_f_x.get_value(), center_f_y.get_value()), + one.get_value() + ); + } + inline bool clear_image() const { + return clear_image_checkbox->isChecked(); } public Q_SLOTS: - void apply(); + void update_form(); + void clear_image_checkbox_changed(Qt::CheckState state); }; #endif // SETTINGSWIDGET_H diff --git a/uint64validator.cpp b/uint64validator.cpp new file mode 100644 index 0000000..6b78d2c --- /dev/null +++ b/uint64validator.cpp @@ -0,0 +1,26 @@ + +// uint64validator.cpp + +#include "uint64validator.h" + +UInt64Validator::UInt64Validator( + quint64 bottom, quint64 top, QObject *parent +) : QValidator(parent), bottom(bottom), top(top) {} +UInt64Validator::UInt64Validator(QObject *parent) +: UInt64Validator( + std::numeric_limits::min(), + std::numeric_limits::max(), + parent + ) {} + +QValidator::State UInt64Validator::validate(QString &input, int&) const { + bool ok = false; + quint64 num = input.toULongLong(&ok); + if (!ok) + return QValidator::Invalid; + if (num < bottom) + return QValidator::Intermediate; + if (num > top) + return QValidator::Invalid; + return QValidator::Acceptable; +} diff --git a/uint64validator.h b/uint64validator.h new file mode 100644 index 0000000..1e20666 --- /dev/null +++ b/uint64validator.h @@ -0,0 +1,35 @@ + +// uint64validator.h + +#ifndef UINT64VALIDATOR_H +#define UINT64VALIDATOR_H + +#include + +class UInt64Validator : public QValidator { + Q_OBJECT + quint64 bottom; + quint64 top; + +public: + UInt64Validator(quint64 bottom, quint64 top, QObject *parent = nullptr); + UInt64Validator(QObject *parent = nullptr); + inline quint64 get_bottom() const { return bottom; } + inline quint64 get_top() const { return top; } + inline void set_bottom(quint64 bottom) { + set_range(bottom, get_top()); + } + inline void set_top(quint64 top) { + set_range(get_bottom(), top); + } + void set_range(quint64 bottom, quint64 top) { + if(this->bottom == bottom && this->top == top) + return; + this->bottom = bottom; + this->top = top; + emit changed(); + } + QValidator::State validate(QString &input, int&) const override; +}; + +#endif // UINT64VALIDATOR_H -- 2.47.0