]> git.mar77i.info Git - bigintmandel/blob - bigintmandelwidget.cpp
split out menubar
[bigintmandel] / bigintmandelwidget.cpp
1
2 // bigintmandelwidget.cpp
3
4 #include <QFileDialog>
5 #include <QLayout>
6 #include <QMouseEvent>
7 #include <QPainter>
8 #include <QPushButton>
9 #include <QtConcurrent/QtConcurrent>
10
11 #include "menubar.h"
12 #include "bigintmandelwidget.h"
13
14 static inline void start_calculation(
15 QFutureWatcher<MandelResultCell> *fw, QVector<MandelCell> cells
16 ) {
17 fw->setFuture(
18 QtConcurrent::mapped(
19 cells,
20 [](const MandelCell &cell){ return cell.iterate(); }
21 )
22 );
23 }
24
25 BigintMandelWidget::BigintMandelWidget(QWidget *parent)
26 : QWidget(parent),
27 fw(new QFutureWatcher<MandelResultCell>(this)),
28 settings(128, QSize(502, 334)),
29 menu_bar(new MenuBar(this)),
30 scroll_area(new QScrollArea(this)),
31 img_label(new QLabel(this)),
32 img_dirty(true),
33 status_bar(new QStatusBar(this)),
34 settings_widget(new SettingsWidget(this)),
35 draw_progress(-1)
36 {
37 scroll_area->setWidget(img_label);
38 connect(
39 settings_widget,
40 &QDialog::accepted,
41 this,
42 &BigintMandelWidget::settings_widget_accepted
43 );
44 connect(
45 fw,
46 &QFutureWatcher<MandelResultCell>::resultReadyAt,
47 this,
48 &BigintMandelWidget::finished_cell
49 );
50 connect(
51 fw,
52 &QFutureWatcher<MandelResultCell>::finished,
53 this,
54 &BigintMandelWidget::finished
55 );
56 start_calculation(fw, settings.get_cells());
57 setLayout(new QVBoxLayout());
58 layout()->addWidget(menu_bar);
59 layout()->addWidget(scroll_area);
60 status_bar->setSizeGripEnabled(false);
61 layout()->addWidget(status_bar);
62 img_label->resize(settings.get_params().get_size());
63 }
64
65 BigintMandelWidget::~BigintMandelWidget() {
66 fw->cancel();
67 fw->waitForFinished();
68 }
69
70 void BigintMandelWidget::finished_cell(int num) {
71 int y = num / settings.get_params().get_size().width();
72 if (y > draw_progress)
73 draw_progress = y;
74 settings.finished_cell(num, fw->resultAt(num));
75 update_img();
76 }
77
78 void BigintMandelWidget::finished() {
79 draw_progress = -1;
80 settings_widget->set_finished(true);
81 update_img();
82 }
83
84 static inline QPixmap enhance_pixmap(QPixmap pixmap, int draw_progress) {
85 QPainter qp;
86 if (draw_progress > -1) {
87 qp.begin(&pixmap);
88 qp.setPen(Qt::GlobalColor::gray);
89 qp.drawLine(0, draw_progress, pixmap.width() - 1, draw_progress);
90 qp.end();
91 }
92 return pixmap;
93 }
94
95 static inline void calculating_status(
96 QStatusBar *status_bar, int &prev_num_threads
97 ) {
98 QTextStream ss(new QString());
99 int num_threads = QThreadPool::globalInstance()->activeThreadCount();
100 if (prev_num_threads == num_threads)
101 return;
102 ss << "Calculating with " << num_threads << " threads ...";
103 status_bar->showMessage(ss.readAll());
104 prev_num_threads = num_threads;
105 }
106
107 static inline void finished_status(QStatusBar *status_bar) {
108 status_bar->showMessage("Click the rendering to zoom.");
109 }
110
111 void BigintMandelWidget::paintEvent(QPaintEvent *event) {
112 static int prev_num_threads = -1;
113 if (!img_dirty)
114 return;
115 img_label->setPixmap(enhance_pixmap(settings.get_pixmap(), draw_progress));
116 img_label->resize(img_label->pixmap().size());
117 img_dirty = false;
118 if (fw->isFinished())
119 finished_status(status_bar);
120 else {
121 calculating_status(status_bar, prev_num_threads);
122 prev_num_threads = -1;
123 }
124 }
125
126 void BigintMandelWidget::mousePressEvent(QMouseEvent *event) {
127 QSize size(settings.get_params().get_size());
128 QPoint pos(event->pos());
129 QWidget *w;
130 for (w = img_label; w != this; w = w->parentWidget())
131 pos = w->mapFromParent(pos);
132 if (event->button() != Qt::MouseButton::LeftButton
133 || !fw->isFinished()
134 || pos.x() < 0
135 || pos.x() >= size.width()
136 || pos.y() < 0
137 || pos.y() >= size.height())
138 return;
139 settings.zoom(get_ideal_size(), menu_bar->get_zoom_factor(), pos);
140 start_calculation(fw, settings.get_cells());
141 update_img();
142 }
143
144 void BigintMandelWidget::update_img() {
145 img_dirty = true;
146 update();
147 }
148
149 static inline QString get_save_file_name(
150 QWidget *parent = nullptr,
151 const QString &caption = QString(),
152 const QString &dir = QString(),
153 const QString &filter_title = QString(),
154 const QStringList &ext_list = QStringList{},
155 const QString default_ext = QString()
156 ) {
157 QTextStream ss(new QString());
158 QStringList::const_iterator ext_list_iter;
159 QString file_name;
160 qsizetype pos;
161 ss << filter_title << " (" << ext_list.join(" ") << ")";
162 file_name = QFileDialog::getSaveFileName(
163 parent, caption, dir, *ss.string()
164 );
165 if (file_name.isEmpty())
166 return file_name;
167 ss.seek(0);
168 ss.string()->clear();
169 ss << file_name;
170 pos = file_name.lastIndexOf(".");
171 if (pos < 0 || !ext_list.contains(file_name.mid(pos)))
172 ss << default_ext;
173 return *ss.string();
174 }
175
176 void BigintMandelWidget::export_img() {
177 QString file_name = get_save_file_name(
178 this,
179 "Save image",
180 "",
181 "Images",
182 QStringList{".png", ".xpm", ".jpg"},
183 ".png"
184 );
185 if (!file_name.isEmpty())
186 settings.save_img(file_name);
187 }
188
189 void BigintMandelWidget::exec_settings_widget() {
190 settings_widget->update_fields(settings.get_params(), fw->isFinished());
191 settings_widget->exec();
192 }
193
194 void BigintMandelWidget::reset() {
195 fw->cancel();
196 fw->waitForFinished();
197 settings.reset(128, get_ideal_size());
198 start_calculation(fw, settings.get_cells());
199 update_img();
200 }
201
202 void BigintMandelWidget::settings_widget_accepted() {
203 settings.set_max_iter(settings_widget->get_max_iter().toULongLong());
204 start_calculation(fw, settings.get_cells());
205 update_img();
206 }
207
208 void BigintMandelWidget::load_data() {
209 QFile file;
210 QJsonObject json;
211 QByteArray qba;
212 QString file_name = QFileDialog::getOpenFileName(
213 this, "Load Data", "", "BigintMandel Data (*.json *.json.qcompress)"
214 );
215 if (file_name.isEmpty())
216 return;
217 file.setFileName(file_name);
218 if (!file.open(QIODevice::ReadOnly)) {
219 qWarning("Couldn't open save file.");
220 return;
221 }
222 qba = file.readAll();
223 file.close();
224 if (file_name.endsWith(".qcompress"))
225 qba = qUncompress(qba);
226 settings.from_json(QJsonDocument::fromJson(qba).object());
227 update_img();
228 }
229
230 void BigintMandelWidget::save_data() {
231 QFile file;
232 QByteArray qba;
233 QString file_name = get_save_file_name(
234 this,
235 "Save data",
236 "",
237 "Bigintmandel Data",
238 QStringList{".json", ".json.qcompress"},
239 ".json.qcompress"
240 );
241 if (file_name.isEmpty())
242 return;
243 qba = QJsonDocument(settings.to_json()).toJson();
244 if (file_name.endsWith(".qcompress"))
245 qba = qCompress(qba, 9);
246 file.setFileName(file_name);
247 if (!file.open(QIODevice::WriteOnly)) {
248 qWarning("Couldn't open save file.");
249 return;
250 }
251 file.write(qba);
252 file.close();
253 }