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

4
#include "savingdialog.hpp"
5
#include "structurewriter.hpp"
6
#include "structurereader.hpp"
7
8
9
10
#include "factory.hpp"
#include "transitionrule.hpp"
#include "neighborhoodrule.hpp"
#include "propertyvisitors.hpp"
11
#include "modelloadingdialog.hpp"
12
#include "configurationloadingdialog.hpp"
13
#include "neighborhoodDialog.hpp"
Yann Boucher's avatar
Yann Boucher committed
14
#include "colorlabel.h"
15

16
#include <QJsonArray>
Merwane Bouri's avatar
Merwane Bouri committed
17
18
#include <QDate>
#include <QTextStream>
19
#include <QInputDialog>
Anthony Noir's avatar
Anthony Noir committed
20
#include <QTimer>
21

22
23
24
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
      , ui(new Ui::MainWindow)
Anthony Noir's avatar
Anthony Noir committed
25
26
27
      , simulation()
      , timer(new QTimer(this))
      , m_customizing(false)
28
29
{
    ui->setupUi(this);
30

31
32
33
    m_transition_rule = nullptr;
    m_neighborhood_rule = nullptr;

34
35
    init_transition_neighborhood_list();
    update_transition_settings();
36
    update_neighborhood_settings();
37

38
    connect(ui->action_save_struct, &QAction::triggered, this, &MainWindow::afficher_interface_sauvegarde_structure);
39
    connect(ui->struct_library, &StructureLibraryView::structure_copied, this, &MainWindow::copy_structure_clicked);
40
    connect(ui->grid_view, &GridView::zoom_changed, ui->struct_library, &StructureLibraryView::update_cell_pixel_size);
Yann Boucher's avatar
Yann Boucher committed
41
42
43
44
45
46
47
48
49
    connect(ui->statesSettingsButton, &QPushButton::clicked, this, [this]
            {
                ColorLabel* dialog = new ColorLabel(simulation.getAlphabet(), this);
                if (dialog->exec())
                {
                    simulation.setAlphabet(dialog->getAlphabet());
                    ui->grid_view->set_alphabet(dialog->getAlphabet());
                }
            });
50
51
    connect(ui->transition_list, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int)
            { update_transition_settings(); });
52
53
54
55
56
57
58
59
60
61
    connect(ui->action_propos_de_Cellulut, &QAction::triggered, this, [this](bool)
            {
                QMessageBox::about(this, "À propos de Cellulut",
                                   "Projet de C++ réalisé dans le cadre de l'UV LO21 de l'UTC.\n\n"
                                   "Contributeurs : Anthony Noir, Eugène Pin, Merwane Bouri, Arthur Detree, Yann Boucher");
            });
    connect(ui->action_propos_de_Qt, &QAction::triggered, this, [this](bool)
            {
                QMessageBox::aboutQt(this, "À propos de Qt");
            });
62
63
64
65
    connect(ui->actionCharger_depuis_une_image, &QAction::triggered, this, [this](bool)
            {
                load_from_image();
            });
66

67
    ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_screen_size());
68
69

    load_model(default_model());
Anthony Noir's avatar
Anthony Noir committed
70
71
72
73
74

    connect(timer, &QTimer::timeout, this, [this](){
        on_nextButton_clicked();
        timer->start();
    });
75
76
77
78
79
}

MainWindow::~MainWindow()
{
    delete ui;
80
    // FIXME : il y a un risque de fuite de mémoire avec la gestion de pointeurs de m_transition_rule et m_neighborhood_rule, si l'application est quitée alors que ils pointent vers de la mémoire non transférée vers Simulation
81
82
}

83
84
/// Connections

85
86
87
88
89
90
91
92
93
void MainWindow::on_simSpeedSpinbox_valueChanged(int arg1)
{
    ui->simSpeedSlider->setValue(arg1);
}

void MainWindow::on_simSpeedSlider_valueChanged(int value)
{
    ui->simSpeedSpinbox->setValue(value);
}
94
95
96

void MainWindow::on_openPatternButton_clicked()
{
97
    load_grid_configuration();
98
99
100
101
102
}

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
103
104
105
106
107
108

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

    load_model(dialog.model());
109
110
}

111
void MainWindow::on_neighborhood_list_currentTextChanged(const QString &)
112
{
113
    update_neighborhood_settings();
114
115
}

116
void MainWindow::on_widthSpinBox_valueChanged(int)
117
118
119
120
{
    ui->validateGridDim->setEnabled(true);
}

121
void MainWindow::on_heightSpinBox_valueChanged(int)
122
123
124
125
{
    ui->validateGridDim->setEnabled(true);
}

126
QString MainWindow::afficher_interface_sauvegarde_structure()
127
{
128
    SavingDialog dialog(this);
129
    if (!dialog.exec())
130
        return "";
131

132
    QString filename = QFileDialog::getSaveFileName(this, "Choisir un nom de fichier", "", "Format Cellulut (*.json);;Format Golly RLE (*.rle)");
133
    QFileInfo info(filename);
134
    //printf("%s\n", info.suffix().toStdString().c_str());
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    // 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());
160
        return "";
161
162
163
164
    }

    QTextStream stream(&file);
    stream << QString::fromStdString(save_data);
165
166

    return filename;
167
168
}

169

170
171
void MainWindow::on_validateGridDim_clicked()
{
172
173
    Grid oldGrid = ui->grid_view->get_grid();

174

175
176
177
    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);
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    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
    {
195
196
        //std::cout << "superieur\n";
        fflush(stdout);
197
198
199
        for (unsigned y = 0; y < nbrRow ; y++) {
            for (unsigned x = 0; x < nbrCol ; x++) {
                Coord pos = {static_cast<int>(x), static_cast<int>(y)};
200
201
202
203
204
205
                newGrid.set_cell(pos, oldGrid.get_state(pos));
                //std::cout << "oldState : " << oldGrid.get_state(pos) << endl;
            }
        }
    }

206
    ui->grid_view->copy_grid(newGrid);
207

208
209
    ui->validateGridDim->setEnabled(false);
}
210
211
212
213
214
215
216

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

    ui->grid_view->set_current_pen(val);
}
217
218
219

void MainWindow::on_randomPatternButton_clicked()
{
220
    int alphabetSize = simulation.getAlphabet().taille();
221
222
223
224
225
226
227
228
229
230

    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)
        {
231
            unsigned state = rand() % alphabetSize;
232
233
234
235
236
            Coord pos = {static_cast<int>(x), static_cast<int>(y)};
            newGrid.set_cell(pos, state);
        }
    }

237
    ui->grid_view->copy_grid(newGrid);
238
}
239
240
241

void MainWindow::on_nbrStatesComboBox_currentTextChanged(const QString &arg1)
{
242
243
    // A mettre dans la fonction valider
    /*
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    // 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);
            }
        }
    }
263
    */
264
}
265

266
267
268
269
270
void MainWindow::on_drawCellButton_clicked()
{
    ui->grid_view->fill_selection(ui->nbrStateComboBox->currentIndex());
}

271
272
273
274
275
void MainWindow::copy_structure_clicked(const Structure &s)
{
    ui->grid_view->set_clipboard(s);
    statusBar()->showMessage("Structure copied.", 5000);
}
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292

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();
293
294
    m_transition_rule = Factory<TransitionRule>::make(selected).release();
    if (!m_transition_rule)
295
296
        return;

297
    if (m_transition_rule->get_properties().size() == 0)
298
299
300
301
302
303
        ui->rule_settings_area->hide();
    else
    {
        ui->rule_settings_area->show();

        UIBuilderVisitor visit(ui->rule_settings_area);
304
        for (auto& prop : m_transition_rule->get_properties())
305
306
            prop->accept(visit);
    }
307
}
308
309
310
void MainWindow::update_neighborhood_settings()
{
    std::string selected = ui->neighborhood_list->currentText().toStdString();
311
312
    m_neighborhood_rule = Factory<NeighborhoodRule>::make(selected).release();
    if (!m_neighborhood_rule)
313
314
        return;

315
    if (m_neighborhood_rule->get_properties().size() == 0)
316
317
318
319
320
321
        ui->neighborhood_settings_area->hide();
    else
    {
        ui->neighborhood_settings_area->show();

        UIBuilderVisitor visit(ui->neighborhood_settings_area);
322
        for (auto& prop : m_neighborhood_rule->get_properties())
323
324
325
            prop->accept(visit);
    }
}
326

327
328
void MainWindow::enable_rule_customization()
{
Yann Boucher's avatar
Yann Boucher committed
329
    //ui->simulation_tab->setEnabled(false);
330
    ui->customize_button->setText("Cancel customization");
331
    ui->rule_settings->setEnabled(true);
332
    m_customizing = true;
333
334
}

335
336
void MainWindow::disable_rule_customization()
{
Yann Boucher's avatar
Yann Boucher committed
337
    //ui->simulation_tab->setEnabled(true);
338
339
    ui->customize_button->setEnabled(true);
    ui->rule_settings->setEnabled(false);
340
341
    ui->customize_button->setText("Customize...");
    m_customizing = false;
342
343
344
345
346
}

void MainWindow::load_model(const QJsonObject &obj)
{
    ui->rule_name->setText(obj.value("title").toString());
347

348
    ui->transition_list->setCurrentText(obj.value("transition_name").toString());
349
    ui->neighborhood_list->setCurrentText(obj.value("neighborhood_name").toString());
350

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    bool alpha_initialise = false;
    Alphabet alpha;
    if (obj.value("alphabet").isArray())
    {
        for (auto entry : obj.value("alphabet").toArray())
        {
            if (!entry.isObject())
                continue;

            auto entry_obj = entry.toObject();
            std::string name = entry_obj.value("name").toString().toStdString();

            if (!entry_obj.value("color").isArray())
                continue;

            auto color_array = entry_obj.value("color").toArray();
            stateColor color(color_array[0].toInt(), color_array[1].toInt(), color_array[2].toInt());

            // Initialisation avec le premier élement
            if (!alpha_initialise)
            {
                alpha = Alphabet(state(color, name));
                alpha_initialise = true;
            }
            else
                alpha.newEtat(state(color, name));
        }
    }


381
    update_transition_settings();
382
    update_neighborhood_settings();
383
384
    disable_rule_customization();

385
386
    {
        PropertyLoaderVisitor loader(obj.value("transition_data").toObject());
387
        for (auto& prop : m_transition_rule->get_properties())
388
389
            prop->accept(loader);
        UIBuilderVisitor visit(ui->rule_settings_area);
390
        for (auto& prop : m_transition_rule->get_properties())
391
392
393
394
395
            prop->accept(visit);
    }

    {
        PropertyLoaderVisitor loader(obj.value("neighborhood_data").toObject());
396
        for (auto& prop : m_neighborhood_rule->get_properties())
397
398
            prop->accept(loader);
        UIBuilderVisitor visit(ui->neighborhood_settings_area);
399
        for (auto& prop : m_neighborhood_rule->get_properties())
400
401
            prop->accept(visit);
    }
402

403
404
    ui_update_alphabet(alpha);

405
    // 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
406
407
    simulation.setNeighborhoodRule(m_neighborhood_rule);
    simulation.setTransitionRule(m_transition_rule);
408
    simulation.setAlphabet(alpha);
Yann Boucher's avatar
Yann Boucher committed
409
    ui->grid_view->set_alphabet(alpha);
410
411

    m_loaded_model = obj;
412
413
414
}

void MainWindow::save_model()
415
416
417
418
419
420
421
{
    SavingDialog dialog(this);
    if (!dialog.exec())
        return;

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

422
    PropertySaverVisitor trans_saver;
423
    for (auto& prop : m_transition_rule->get_properties())
424
425
426
        prop->accept(trans_saver);

    PropertySaverVisitor neighborhood_saver;
427
    for (auto& prop : m_neighborhood_rule->get_properties())
428
        prop->accept(neighborhood_saver);
429
430
431
432
433
434
435
436

    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();
437
438
439
440
    root["transition_data"] = trans_saver.save();

    root["neighborhood_name"] = ui->neighborhood_list->currentText();
    root["neighborhood_data"] = neighborhood_saver.save();
441
442
443

    QJsonArray alphabet;

444
445
446
447
448
449
450
451
452
453
454
455
456
    for (unsigned i = 0; i < simulation.getAlphabet().taille(); ++i)
    {
        auto state = simulation.getAlphabet().getState(i);
        QJsonArray color;
        QJsonObject entry;
        entry["name"] = QString::fromStdString(state.getStateLabel());
        color.push_back(state.getColor().getRed());
        color.push_back(state.getColor().getGreen());
        color.push_back(state.getColor().getBlue());
        entry["color"] = color;
        alphabet.push_back(entry);
    }

457
458
459
460
    root["alphabet"] = alphabet;

    QJsonDocument doc(root);
    QFile file(filename);
461
462
463
464
465
    if (!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate))
    {
        QMessageBox::information(this, "Impossible de créer le fichier",
                                 file.errorString());
    }
466
467
    file.write(doc.toJson());
    file.close();
468
469
470

    ui->rule_name->setText(dialog.titre());
    disable_rule_customization();
471
472

    load_model(root);
473
474
}

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
static unsigned closest_state(const Alphabet& alph, const QColor& color)
{
    unsigned best_distance = std::numeric_limits<unsigned>::max();
    unsigned best_match = 0;
    for (unsigned i = 0; i < alph.taille(); ++i)
    {
        state s = alph.getState(i);
        unsigned r_diff = qAbs(color.red() - s.getColor().getRed());
        unsigned g_diff = qAbs(color.green() - s.getColor().getGreen());
        unsigned b_diff = qAbs(color.blue() - s.getColor().getBlue());
        unsigned distance = r_diff*r_diff + g_diff*g_diff + b_diff*b_diff;
        if (distance < best_distance)
        {
            best_distance = distance;
            best_match = i;
        }
    }

    return best_match;
}

void MainWindow::load_from_image()
{
498
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"",tr("Image (*.png *.jpg *.jpeg *.bmp *.gif)"));
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
    QImage img(fileName);
    if (img.isNull())
        return;

    Grid grid = ui->grid_view->get_grid();

    img = img.scaled(grid.get_col(),
                     grid.get_rows());
    for (int x = 0; x < img.width(); ++x)
        for (int y = 0; y < img.height(); ++y)
        {
            unsigned closest = closest_state(ui->grid_view->alphabet(), img.pixelColor(x, y));
            grid.set_cell(Coord{x, y}, closest);
        }

    ui->grid_view->copy_grid(grid);
}

517
518
519
520
void MainWindow::ui_update_alphabet(const Alphabet &alpha)
{
    ui->grid_view->set_alphabet(alpha);
    ui->nbrStateComboBox->clear();
Eugene Pin's avatar
Eugene Pin committed
521
522
    std::cout << alpha.taille() << endl;
    fflush(stdout);
523
524
525
526
    for (unsigned i = 0; i < alpha.taille(); ++i)
        ui->nbrStateComboBox->addItem(QString::number(i));
}

527
528
void MainWindow::save_grid_configuration()
{
529
530
531
532
533
534
535
536
537
    const Grid& grid = ui->grid_view->get_grid();
    Structure grid_data = grid.to_structure();

    QString title = QInputDialog::getText(this, "Choisir un titre", "Titre de la configuration : ");
    if (title.isEmpty())
        return;

    RLEStructureWriter writer;
    std::string structure_data = writer.save_structure(grid_data);
538
539

    QJsonObject json_data;
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
    json_data["width"] = (int)grid.get_col();
    json_data["height"] = (int)grid.get_rows();
    json_data["title"] = title;
    json_data["model"] = m_loaded_model.value("title");
    json_data["left"] = grid_data.top_left.x;
    json_data["top"] = grid_data.top_left.y;
    json_data["data"] = QString::fromStdString(structure_data);

    QString filename = QFileDialog::getSaveFileName(this, "Choisir un nom de fichier", "", "Configuration Cellulut (*.json)");
    if (filename.isEmpty())
        return;

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

    QJsonDocument doc(json_data);
    file.write(doc.toJson());
    file.close();
562
563
564
565
}

void MainWindow::load_grid_configuration()
{
566
567
568
569
570
571
572
573
574
575
576
577
    ConfigurationLoadingDialog dialog(m_loaded_model.value("title").toString(), this);
    if (!dialog.exec())
        return;

    Grid g(dialog.configuration().value("width").toInt(10),
           dialog.configuration().value("height").toInt(10));
    Coord origin;
    origin.x = dialog.configuration().value("left").toInt(0);
    origin.y = dialog.configuration().value("top").toInt(0);

    RLEStructureReader reader(dialog.configuration().value("data").toString().toStdString());
    Structure s = reader.read_structure();
578

579
580
581
582
    ui->grid_view->copy_grid(g);
    ui->grid_view->paste_structure_at(origin, s);
    ui->widthSpinBox->setValue(g.get_col());
    ui->heightSpinBox->setValue(g.get_rows());
583
584
}

585
586
QJsonObject MainWindow::default_model() const
{
587
588
589
    const char* json = R"(
    {
        "title" : "Game of Life",
590
591
        "neighborhood_name" : "Moore",
        "neighborhood_data" : {},
592
593
594
595
596
597
598
599
600
        "transition_name" : "Game of Life",
        "transition_data" : {},
        "alphabet" : [
        {"name" : "Dead", "color" : [255, 255, 255]},
        {"name" : "Alive", "color" : [0, 0, 255]}]
    }
)";

    QJsonDocument doc = QJsonDocument::fromJson(QByteArray(json));
601

602
    return doc.object();
603
604
605
606
607
608
609
610
611
}

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

void MainWindow::on_customize_button_clicked()
{
612
613
614
615
616
617
618
619
620
    if (!m_customizing)
    {
        enable_rule_customization();
    }
    else
    {
        disable_rule_customization();
        load_model(m_loaded_model);
    }
621
}
622

623
624
625

void MainWindow::on_savePatternButton_clicked()
{
626
    save_grid_configuration();
627
}
Anthony Noir's avatar
Anthony Noir committed
628
629
630
631
632
633

void MainWindow::on_nextButton_clicked()
{
    simulation.setGrid(ui->grid_view->get_grid());
    simulation.step();
    ui->grid_view->copy_grid(simulation.getGrid());
Anthony Noir's avatar
Anthony Noir committed
634
    ui->stepsPeriodLabel->setText(QString::number(simulation.getPeriod())+" steps");
Anthony Noir's avatar
Anthony Noir committed
635
    ui->nbStepsLabel->setText(QString::number(simulation.getTime())+" steps");
Anthony Noir's avatar
Anthony Noir committed
636
637
638
639
640
641
642
643
}

void MainWindow::on_prevButton_clicked()
{
    if(!simulation.back()) {
        QMessageBox::critical(this, "Retour arrière impossible", "L'historique est vide : impossible de revenir en arrière.");
    }
    ui->grid_view->copy_grid(simulation.getGrid());
Anthony Noir's avatar
Anthony Noir committed
644
    ui->nbStepsLabel->setText(QString::number(simulation.getTime())+" steps");
Anthony Noir's avatar
Anthony Noir committed
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
}

void MainWindow::on_playPauseButton_clicked()
{
    if(timer->isActive()) {
        //Pause
        timer->stop();
    } else {
        //Play
        int frequence = ui->simSpeedSpinbox->value();
        if(frequence == 0) {
            QMessageBox::critical(this, "Impossible de démarrer", "Impossible de lancer la simulation à une vitesse nulle.");
        } else if(!simulation.runnable()) {
            QMessageBox::critical(this, "Impossible de démarrer", "Impossible de lancer la simulation : les règles ne sont pas définies ou incompatibles.");
        } else {
            timer->setInterval(1000/frequence);
            timer->start();
        }
    }
}
Anthony Noir's avatar
reset    
Anthony Noir committed
665
666
667
668
669
670
671

void MainWindow::on_resetButton_clicked() {
    if(timer->isActive()) {
        timer->stop();
    }
    simulation.reset();
    ui->grid_view->copy_grid(simulation.getGrid());
Anthony Noir's avatar
Anthony Noir committed
672
    ui->stepsPeriodLabel->setText(QString::number(simulation.getPeriod())+" steps");
Anthony Noir's avatar
Anthony Noir committed
673
    ui->nbStepsLabel->setText(QString::number(simulation.getTime())+" steps");
Anthony Noir's avatar
reset    
Anthony Noir committed
674
}
Anthony Noir's avatar
Anthony Noir committed
675
676
677
678

void MainWindow::on_recordSpinBox_valueChanged(int newSize) {
    simulation.setHistorySize(newSize);
}