interface.cpp 12.3 KB
Newer Older
1
2
3
#include "interface.hpp"
#include "ui_interface.h"

4
#include "savingdialog.hpp"
5
#include "structurewriter.hpp"
6
7
8
9
#include "factory.hpp"
#include "transitionrule.hpp"
#include "neighborhoodrule.hpp"
#include "propertyvisitors.hpp"
10
#include "modelloadingdialog.hpp"
11

12
13
#include <QJsonArray>

14
15
16
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
      , ui(new Ui::MainWindow)
17
      , simulation()
18
19
{
    ui->setupUi(this);
20

21
22
23
    m_transition_rule = nullptr;
    m_neighborhood_rule = nullptr;

24
25
    init_transition_neighborhood_list();
    update_transition_settings();
26
    update_neighborhood_settings();
27

28
    connect(ui->action_save_struct, &QAction::triggered, this, &MainWindow::afficher_interface_sauvegarde_structure);
29
    connect(ui->struct_library, &StructureLibraryView::structure_copied, this, &MainWindow::copy_structure_clicked);
30
    connect(ui->grid_view, &GridView::zoom_changed, ui->struct_library, &StructureLibraryView::update_cell_pixel_size);
31
32
    connect(ui->transition_list, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int)
            { update_transition_settings(); });
33
34

    ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_pixel_size());
35
36

    load_model(default_model());
37
38
39
40
41
}

MainWindow::~MainWindow()
{
    delete ui;
42
43
44
45
    if (m_transition_rule)
        delete m_transition_rule;
    if (m_neighborhood_rule)
        delete m_neighborhood_rule;
46
47
}

48
49
/// Connections

50
51
52
53
54
55
56
57
58
void MainWindow::on_simSpeedSpinbox_valueChanged(int arg1)
{
    ui->simSpeedSlider->setValue(arg1);
}

void MainWindow::on_simSpeedSlider_valueChanged(int value)
{
    ui->simSpeedSpinbox->setValue(value);
}
59
60
61

void MainWindow::on_openPatternButton_clicked()
{
62
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"",tr("Pattern file (*.json)"));
63
64
    if(fileName == ""){
        // L'utilisateur a annulé la recherche
65
        return;
66
67
68
69
70
71
72
    }
    // Indiquer qu'un nouveau pattern vient d'être ouvert
}

void MainWindow::on_openRuleButton_clicked()
{
    // Indiquer qu'une nouvelle règle vient d'être ouverte. La règle est sauvegardé, donc on peut lancer la simulation
73
74
75
76
77
78

    ModelLoadingDialog dialog(this);
    if (!dialog.exec())
        return;

    load_model(dialog.model());
79
80
}

81
void MainWindow::on_neighborhood_list_currentTextChanged(const QString &)
82
{
83
    update_neighborhood_settings();
84
85
}

86
void MainWindow::on_widthSpinBox_valueChanged(int)
87
88
89
90
{
    ui->validateGridDim->setEnabled(true);
}

91
void MainWindow::on_heightSpinBox_valueChanged(int)
92
93
94
95
{
    ui->validateGridDim->setEnabled(true);
}

96
97
void MainWindow::afficher_interface_sauvegarde_structure(bool)
{
98
    SavingDialog dialog(this);
99
100
101
102
103
    if (!dialog.exec())
        return;

    QString filename = QFileDialog::getSaveFileName(this, "Choisir un nom de fichier", QString(), "Format Cellulut (*.json);;Format Golly RLE (*.rle)");
    QFileInfo info(filename);
104
    //printf("%s\n", info.suffix().toStdString().c_str());
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    // on pourrait utiliser un Factory ici, mais c'est possiblement overkill, les possibilités d'évolution de format de fichier sont faibles et ne justifient pas l'effort

    Structure s = ui->grid_view->selected_cells();
    s.author = dialog.auteur().toStdString();
    s.title = dialog.titre().toStdString();
    s.desc = dialog.desc().toStdString();
    s.date = dialog.date().toString().toStdString();

    std::string save_data;
    if (info.suffix() == "json")
    {
        JSONStructureWriter writer;
        save_data = writer.save_structure(s);
    }
    else if (info.suffix() == "rle")
    {
        RLEStructureWriter writer;
        save_data = writer.save_structure(s);
    }

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::information(this, "Impossible de créer le fichier",
                                 file.errorString());
        return;
    }

    QTextStream stream(&file);
    stream << QString::fromStdString(save_data);
135
136
}

137

138
139
void MainWindow::on_validateGridDim_clicked()
{
140
141
    Grid oldGrid = ui->grid_view->get_grid();

142

143
144
145
    unsigned int nbrRow = ui->heightSpinBox->value(); // nbr de lignes => axe y
    unsigned int nbrCol = ui->widthSpinBox->value(); // nbr de colonne => axe x
    Grid newGrid(nbrRow, nbrCol);
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    unsigned oldNbrRow = oldGrid.get_rows(); // rows = nbr de lignes => axe y
    unsigned oldNbrCol = oldGrid.get_col(); // col = nbr de colonne => axe x

    if(oldNbrRow <= nbrRow && oldNbrCol <= nbrCol) {
        //std::cout << "superieur\n";
        fflush(stdout);
        for (unsigned y = 0; y < oldNbrRow ; y++) {
            for (unsigned x = 0; x < oldNbrCol ; x++) {
                Coord pos = {static_cast<int>(x), static_cast<int>(y)};
                newGrid.set_cell(pos, oldGrid.get_state(pos));
                //std::cout << "oldState : " << oldGrid.get_state(pos) << endl;
            }
        }
    }
    else
    {
163
164
        //std::cout << "superieur\n";
        fflush(stdout);
165
166
167
        for (unsigned y = 0; y < nbrRow ; y++) {
            for (unsigned x = 0; x < nbrCol ; x++) {
                Coord pos = {static_cast<int>(x), static_cast<int>(y)};
168
169
170
171
172
173
                newGrid.set_cell(pos, oldGrid.get_state(pos));
                //std::cout << "oldState : " << oldGrid.get_state(pos) << endl;
            }
        }
    }

174
    ui->grid_view->copy_grid(newGrid);
175

176
177
    ui->validateGridDim->setEnabled(false);
}
178
179
180
181
182
183
184

void MainWindow::on_nbrStateComboBox_currentTextChanged(const QString &arg1)
{
    unsigned val = arg1.toInt();

    ui->grid_view->set_current_pen(val);
}
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

void MainWindow::on_randomPatternButton_clicked()
{
    // TODO Prendre en compte l'alphabet !
    // int nbr = rand() % nbr de states différents;

    Grid oldGrid = ui->grid_view->get_grid();
    unsigned nbrRow = oldGrid.get_rows(); // rows = nbr de lignes => axe y
    unsigned nbrCol = oldGrid.get_col(); // col = nbr de colonne => axe x
    Grid newGrid(nbrRow, nbrCol);

    for (unsigned y = 0; y < nbrRow; ++y)
    {
        for (unsigned x = 0; x < nbrCol; ++x)
        {
            // TODO Remplacer 2 par le nbr d'états différents
            // TODO comprendre pourquoi il faut inverser x et y (???)
            unsigned state = rand() % 2;
            Coord pos = {static_cast<int>(x), static_cast<int>(y)};
204
            //std::cout << "position x= "<< pos.x << " y= "<< pos.y << endl;
205
206
207
208
            newGrid.set_cell(pos, state);
        }
    }

209
    ui->grid_view->copy_grid(newGrid);
210
}
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

void MainWindow::on_nbrStatesComboBox_currentTextChanged(const QString &arg1)
{
    // Nombre d'états possibles
    int val = arg1.toInt();
    // Nombre de valeurs dans le comboxBox (en partant de 0)
    int currentNbrValue = ui->nbrStateComboBox->count();
    // Si on change le nbr d'états
    if (val != currentNbrValue) {
        // Si on ajoute des états
        if (val > currentNbrValue) {
            for(int i = currentNbrValue; i < val; i++ ) {
                ui->nbrStateComboBox->addItem(QString::number(i));
            }
        }
        // Si on retire des états
        else {
            for(int i = currentNbrValue - 1; i >= val; i-- ) {
                ui->nbrStateComboBox->removeItem(i);
            }
        }
    }

}
235

236
237
238
239
240
void MainWindow::on_drawCellButton_clicked()
{
    ui->grid_view->fill_selection(ui->nbrStateComboBox->currentIndex());
}

241
242
243
244
245
void MainWindow::copy_structure_clicked(const Structure &s)
{
    ui->grid_view->set_clipboard(s);
    statusBar()->showMessage("Structure copied.", 5000);
}
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

void MainWindow::init_transition_neighborhood_list()
{
    for (const auto& transition : Factory<TransitionRule>::list_choices())
    {
        ui->transition_list->addItem(QString::fromStdString(transition));
    }

    for (const auto& rule : Factory<NeighborhoodRule>::list_choices())
    {
        ui->neighborhood_list->addItem(QString::fromStdString(rule));
    }
}

void MainWindow::update_transition_settings()
{
    std::string selected = ui->transition_list->currentText().toStdString();
263
264
    m_transition_rule = Factory<TransitionRule>::make(selected).release();
    if (!m_transition_rule)
265
266
        return;

267
    if (m_transition_rule->get_properties().size() == 0)
268
269
270
271
272
273
        ui->rule_settings_area->hide();
    else
    {
        ui->rule_settings_area->show();

        UIBuilderVisitor visit(ui->rule_settings_area);
274
        for (auto& prop : m_transition_rule->get_properties())
275
276
            prop->accept(visit);
    }
277
}
278
279
280
void MainWindow::update_neighborhood_settings()
{
    std::string selected = ui->neighborhood_list->currentText().toStdString();
281
282
    m_neighborhood_rule = Factory<NeighborhoodRule>::make(selected).release();
    if (!m_neighborhood_rule)
283
284
        return;

285
    if (m_neighborhood_rule->get_properties().size() == 0)
286
287
288
289
290
291
        ui->neighborhood_settings_area->hide();
    else
    {
        ui->neighborhood_settings_area->show();

        UIBuilderVisitor visit(ui->neighborhood_settings_area);
292
        for (auto& prop : m_neighborhood_rule->get_properties())
293
294
295
            prop->accept(visit);
    }
}
296

297
298
299
300
301
void MainWindow::enable_rule_customization()
{
    ui->simulation_tab->setEnabled(false);
    ui->customize_button->setEnabled(false);
    ui->rule_settings->setEnabled(true);
302
303
304

    update_neighborhood_settings();
    update_transition_settings();
305
306
}

307
308
309
310
311
312
313
314
315
316
317
318
void MainWindow::disable_rule_customization()
{
    ui->simulation_tab->setEnabled(true);
    ui->customize_button->setEnabled(true);
    ui->rule_settings->setEnabled(false);
}

void MainWindow::load_model(const QJsonObject &obj)
{
    ui->rule_name->setText(obj.value("title").toString());
    // TODO : load alphabet
    ui->transition_list->setCurrentText(obj.value("transition_name").toString());
319
    ui->neighborhood_list->setCurrentText(obj.value("neighborhood_name").toString());
320
321

    update_transition_settings();
322
    update_neighborhood_settings();
323
324
    disable_rule_customization();

325
326
    {
        PropertyLoaderVisitor loader(obj.value("transition_data").toObject());
327
        for (auto& prop : m_transition_rule->get_properties())
328
329
            prop->accept(loader);
        UIBuilderVisitor visit(ui->rule_settings_area);
330
        for (auto& prop : m_transition_rule->get_properties())
331
332
333
334
335
            prop->accept(visit);
    }

    {
        PropertyLoaderVisitor loader(obj.value("neighborhood_data").toObject());
336
        for (auto& prop : m_neighborhood_rule->get_properties())
337
338
            prop->accept(loader);
        UIBuilderVisitor visit(ui->neighborhood_settings_area);
339
        for (auto& prop : m_neighborhood_rule->get_properties())
340
341
            prop->accept(visit);
    }
342
343
344
345

    // On transfère la propriété de ces pointeurs vers Simulation, qui en est désormais propriétaire pour l'exécution de l'automate
    simulation.setNeighborhoodRule(m_neighborhood_rule); m_neighborhood_rule = nullptr;
    simulation.setTransitionRule(m_transition_rule); m_transition_rule = nullptr;
346
347
348
}

void MainWindow::save_model()
349
350
351
352
353
354
355
{
    SavingDialog dialog(this);
    if (!dialog.exec())
        return;

    QString filename = QFileDialog::getSaveFileName(this, "Choisir un nom de fichier", QString(), "Modèle d'automate (*.json)");

356
    PropertySaverVisitor trans_saver;
357
    for (auto& prop : m_transition_rule->get_properties())
358
359
360
        prop->accept(trans_saver);

    PropertySaverVisitor neighborhood_saver;
361
    for (auto& prop : m_neighborhood_rule->get_properties())
362
        prop->accept(neighborhood_saver);
363
364
365
366
367
368
369
370

    QJsonObject root;
    root["title"] = dialog.titre();
    root["author"] = dialog.auteur();
    root["desc"] = dialog.desc();
    root["date"] = dialog.date().toString();

    root["transition_name"] = ui->transition_list->currentText();
371
372
373
374
    root["transition_data"] = trans_saver.save();

    root["neighborhood_name"] = ui->neighborhood_list->currentText();
    root["neighborhood_data"] = neighborhood_saver.save();
375
376
377
378
379
380
381
382
383
384
385

    // TODO : sauvegarder l'alphabet quand on pourra y accéder avec Simulation
    QJsonArray alphabet;

    root["alphabet"] = alphabet;

    QJsonDocument doc(root);
    QFile file(filename);
    file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
    file.write(doc.toJson());
    file.close();
386
387
388

    ui->rule_name->setText(dialog.titre());
    disable_rule_customization();
389
390

    load_model(root);
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
}

QJsonObject MainWindow::default_model() const
{
    QJsonObject obj;
    obj["title"] = "Game of Life";
    obj["transition_name"] = "Game of Life";
    obj["transition_data"] = QJsonObject();

    return obj;
}

void MainWindow::on_saveRuleButton_clicked()
{
    save_model();
}

void MainWindow::on_customize_button_clicked()
{
    enable_rule_customization();
411
}