2 // bigintmandelwidget.cpp
9 #include <QtConcurrent/QtConcurrent>
11 #include "bigintmandelwidget.h"
13 static inline QMenuBar
*setup_menu_bar(BigintMandelWidget
*parent
) {
14 QMenuBar
*menu_bar
= new QMenuBar(parent
);
15 QMenu
*menu
= new QMenu("&File", parent
);
16 QPushButton
*button
= new QPushButton("Settings");
17 QAction
*two
, *four
, *eight
, *sixteen
;
18 menu_bar
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
20 menu
->addAction("&Reset"),
23 &BigintMandelWidget::reset
26 menu
->addAction("&Load"),
29 &BigintMandelWidget::load_data
32 menu
->addAction("&Save"),
35 &BigintMandelWidget::save_data
39 menu
->addAction("&Export"),
42 &BigintMandelWidget::export_img
46 menu
->addAction("E&xit"),
49 &BigintMandelWidget::close
51 menu_bar
->addMenu(menu
);
52 menu
= new QMenu("&Zoom factor", parent
);
53 two
= menu
->addAction("&2");
54 four
= menu
->addAction("&4");
55 eight
= menu
->addAction("&8");
56 sixteen
= menu
->addAction("1&6");
57 two
->setCheckable(true);
58 two
->setChecked(true);
59 four
->setCheckable(true);
60 eight
->setCheckable(true);
61 sixteen
->setCheckable(true);
66 [two
, four
, eight
, sixteen
, parent
]() {
67 two
->setChecked(true);
68 four
->setChecked(false);
69 eight
->setChecked(false);
70 sixteen
->setChecked(false);
71 parent
->set_zoom_factor(2);
78 [two
, four
, eight
, sixteen
, parent
]() {
79 two
->setChecked(false);
80 four
->setChecked(true);
81 eight
->setChecked(false);
82 sixteen
->setChecked(false);
83 parent
->set_zoom_factor(4);
90 [two
, four
, eight
, sixteen
, parent
]() {
91 two
->setChecked(false);
92 four
->setChecked(false);
93 eight
->setChecked(true);
94 sixteen
->setChecked(false);
95 parent
->set_zoom_factor(8);
102 [two
, four
, eight
, sixteen
, parent
]() {
103 two
->setChecked(false);
104 four
->setChecked(false);
105 eight
->setChecked(false);
106 sixteen
->setChecked(true);
107 parent
->set_zoom_factor(16);
110 menu_bar
->addMenu(menu
);
113 &QPushButton::clicked
,
115 &BigintMandelWidget::exec_settings_widget
117 menu_bar
->setCornerWidget(button
);
121 static inline void start_calculation(
122 QFutureWatcher
<MandelResultCell
> *fw
, QVector
<MandelCell
> cells
125 QtConcurrent::mapped(
127 [](const MandelCell
&cell
){ return cell
.iterate(); }
132 BigintMandelWidget::BigintMandelWidget(QWidget
*parent
)
134 fw(new QFutureWatcher
<MandelResultCell
>(this)),
135 settings(128, QSize(502, 334)),
136 scroll_area(new QScrollArea(this)),
137 img_label(new QLabel(this)),
139 status_bar(new QStatusBar(this)),
140 settings_widget(new SettingsWidget(this)),
143 scroll_area
->setWidget(img_label
);
148 &BigintMandelWidget::settings_widget_accepted
152 &QFutureWatcher
<MandelResultCell
>::resultReadyAt
,
154 &BigintMandelWidget::finished_cell
158 &QFutureWatcher
<MandelResultCell
>::finished
,
160 &BigintMandelWidget::finished
162 start_calculation(fw
, settings
.get_cells());
163 setLayout(new QVBoxLayout());
164 layout()->addWidget(setup_menu_bar(this));
165 layout()->addWidget(scroll_area
);
166 status_bar
->setSizeGripEnabled(false);
167 layout()->addWidget(status_bar
);
168 img_label
->resize(settings
.get_params().get_size());
171 BigintMandelWidget::~BigintMandelWidget() {
173 fw
->waitForFinished();
176 void BigintMandelWidget::finished_cell(int num
) {
177 int y
= num
/ settings
.get_params().get_size().width();
178 if (y
> draw_progress
)
180 settings
.finished_cell(num
, fw
->resultAt(num
));
184 void BigintMandelWidget::finished() {
186 settings_widget
->set_finished(true);
190 static inline QPixmap
enhance_pixmap(QPixmap pixmap
, int draw_progress
) {
192 if (draw_progress
> -1) {
194 qp
.setPen(Qt::GlobalColor::gray
);
195 qp
.drawLine(0, draw_progress
, pixmap
.width() - 1, draw_progress
);
201 static inline void calculating_status(
202 QStatusBar
*status_bar
, int &prev_num_threads
204 QTextStream
ss(new QString());
205 int num_threads
= QThreadPool::globalInstance()->activeThreadCount();
206 if (prev_num_threads
== num_threads
)
208 ss
<< "Calculating with " << num_threads
<< " threads ...";
209 status_bar
->showMessage(ss
.readAll());
210 prev_num_threads
= num_threads
;
213 static inline void finished_status(QStatusBar
*status_bar
) {
214 status_bar
->showMessage("Click the rendering to zoom.");
217 void BigintMandelWidget::paintEvent(QPaintEvent
*event
) {
218 static int prev_num_threads
= -1;
221 img_label
->setPixmap(enhance_pixmap(settings
.get_pixmap(), draw_progress
));
222 img_label
->resize(img_label
->pixmap().size());
224 if (fw
->isFinished())
225 finished_status(status_bar
);
227 calculating_status(status_bar
, prev_num_threads
);
228 prev_num_threads
= -1;
232 void BigintMandelWidget::mousePressEvent(QMouseEvent
*event
) {
233 QSize
size(settings
.get_params().get_size());
234 QPoint
pos(event
->pos());
236 for (w
= img_label
; w
!= this; w
= w
->parentWidget())
237 pos
= w
->mapFromParent(pos
);
238 if (event
->button() != Qt::MouseButton::LeftButton
241 || pos
.x() >= size
.width()
243 || pos
.y() >= size
.height())
245 settings
.zoom(get_ideal_size(), zoom_factor
, pos
);
246 start_calculation(fw
, settings
.get_cells());
250 void BigintMandelWidget::update_img() {
255 static inline QString
get_save_file_name(
256 QWidget
*parent
= nullptr,
257 const QString
&caption
= QString(),
258 const QString
&dir
= QString(),
259 const QString
&filter_title
= QString(),
260 const QStringList
&ext_list
= QStringList
{},
261 const QString default_ext
= QString()
263 QTextStream
ss(new QString());
264 QStringList::const_iterator ext_list_iter
;
267 ss
<< filter_title
<< " (" << ext_list
.join(" ") << ")";
268 file_name
= QFileDialog::getSaveFileName(
269 parent
, caption
, dir
, *ss
.string()
271 if (file_name
.isEmpty())
274 ss
.string()->clear();
276 pos
= file_name
.lastIndexOf(".");
277 if (pos
< 0 || !ext_list
.contains(file_name
.mid(pos
)))
282 void BigintMandelWidget::export_img() {
283 QString file_name
= get_save_file_name(
288 QStringList
{".png", ".xpm", ".jpg"},
291 if (!file_name
.isEmpty())
292 settings
.save_img(file_name
);
295 void BigintMandelWidget::exec_settings_widget() {
296 settings_widget
->update_fields(settings
.get_params(), fw
->isFinished());
297 settings_widget
->exec();
300 void BigintMandelWidget::reset() {
302 fw
->waitForFinished();
303 settings
.reset(128, get_ideal_size());
304 start_calculation(fw
, settings
.get_cells());
308 void BigintMandelWidget::settings_widget_accepted() {
309 settings
.set_max_iter(settings_widget
->get_max_iter().toULongLong());
310 start_calculation(fw
, settings
.get_cells());
314 void BigintMandelWidget::load_data() {
318 QString file_name
= QFileDialog::getOpenFileName(
319 this, "Load Data", "", "BigintMandel Data (*.json *.json.qcompress)"
321 if (file_name
.isEmpty())
323 file
.setFileName(file_name
);
324 if (!file
.open(QIODevice::ReadOnly
)) {
325 qWarning("Couldn't open save file.");
328 qba
= file
.readAll();
330 if (file_name
.endsWith(".qcompress"))
331 qba
= qUncompress(qba
);
332 settings
.from_json(QJsonDocument::fromJson(qba
).object());
336 void BigintMandelWidget::save_data() {
339 QString file_name
= get_save_file_name(
344 QStringList
{".json", ".json.qcompress"},
347 if (file_name
.isEmpty())
349 qba
= QJsonDocument(settings
.to_json()).toJson();
350 if (file_name
.endsWith(".qcompress"))
351 qba
= qCompress(qba
, 9);
352 file
.setFileName(file_name
);
353 if (!file
.open(QIODevice::WriteOnly
)) {
354 qWarning("Couldn't open save file.");