interface.cpp 12.5 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
#include "neighborhoodWidget.hpp"
12

13
14
#include <QJsonArray>

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

22
23
24
    m_transition_rule = nullptr;
    m_neighborhood_rule = nullptr;

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

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

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

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

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

49
50
/// Connections

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

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

void MainWindow::on_openPatternButton_clicked()
{
63
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"",tr("Pattern file (*.json)"));
64
65
    if(fileName == ""){
        // L'utilisateur a annulé la recherche
66
        return;
67
68
69
70
71
72
73
    }
    // 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
74
75
76
77
78
79

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

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

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

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

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

97
98
void MainWindow::afficher_interface_sauvegarde_structure(bool)
{
99
    SavingDialog dialog(this);
100
101
102
103
104
    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);
105
    //printf("%s\n", info.suffix().toStdString().c_str());
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
135
    // 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);
136
137
}

138

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

143

144
145
146
    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);
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    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
    {
164
165
        //std::cout << "superieur\n";
        fflush(stdout);
166
167
168
        for (unsigned y = 0; y < nbrRow ; y++) {
            for (unsigned x = 0; x < nbrCol ; x++) {
                Coord pos = {static_cast<int>(x), static_cast<int>(y)};
169
170
171
172
173
174
                newGrid.set_cell(pos, oldGrid.get_state(pos));
                //std::cout << "oldState : " << oldGrid.get_state(pos) << endl;
            }
        }
    }

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

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

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

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

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)};
205
            //std::cout << "position x= "<< pos.x << " y= "<< pos.y << endl;
206
207
208
209
            newGrid.set_cell(pos, state);
        }
    }

210
    ui->grid_view->copy_grid(newGrid);
211
}
212
213
214

void MainWindow::on_nbrStatesComboBox_currentTextChanged(const QString &arg1)
{
215
216
    // A mettre dans la fonction valider
    /*
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
    // 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);
            }
        }
    }
236
    */
237
}
238

239
240
241
242
243
void MainWindow::on_drawCellButton_clicked()
{
    ui->grid_view->fill_selection(ui->nbrStateComboBox->currentIndex());
}

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

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();
266
267
    m_transition_rule = Factory<TransitionRule>::make(selected).release();
    if (!m_transition_rule)
268
269
        return;

270
    if (m_transition_rule->get_properties().size() == 0)
271
272
273
274
275
276
        ui->rule_settings_area->hide();
    else
    {
        ui->rule_settings_area->show();

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

288
    if (m_neighborhood_rule->get_properties().size() == 0)
289
290
291
292
293
294
        ui->neighborhood_settings_area->hide();
    else
    {
        ui->neighborhood_settings_area->show();

        UIBuilderVisitor visit(ui->neighborhood_settings_area);
295
        for (auto& prop : m_neighborhood_rule->get_properties())
296
297
298
            prop->accept(visit);
    }
}
299

300
301
302
303
304
void MainWindow::enable_rule_customization()
{
    ui->simulation_tab->setEnabled(false);
    ui->customize_button->setEnabled(false);
    ui->rule_settings->setEnabled(true);
305
306
307

    update_neighborhood_settings();
    update_transition_settings();
308
309
}

310
311
312
313
314
315
316
317
318
319
320
321
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());
322
    ui->neighborhood_list->setCurrentText(obj.value("neighborhood_name").toString());
323
324

    update_transition_settings();
325
    update_neighborhood_settings();
326
327
    disable_rule_customization();

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

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

    // 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;
349
350
351
}

void MainWindow::save_model()
352
353
354
355
356
357
358
{
    SavingDialog dialog(this);
    if (!dialog.exec())
        return;

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

359
    PropertySaverVisitor trans_saver;
360
    for (auto& prop : m_transition_rule->get_properties())
361
362
363
        prop->accept(trans_saver);

    PropertySaverVisitor neighborhood_saver;
364
    for (auto& prop : m_neighborhood_rule->get_properties())
365
        prop->accept(neighborhood_saver);
366
367
368
369
370
371
372
373

    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();
374
375
376
377
    root["transition_data"] = trans_saver.save();

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

    // 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();
389
390
391

    ui->rule_name->setText(dialog.titre());
    disable_rule_customization();
392
393

    load_model(root);
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
}

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();
414
}
415
416
417
418
419
420
421
422
423

void MainWindow::show_neighborhood_widget(bool)
{
    NeighborhoodWidget widget(this);
    if (!widget.exec())
        return;


}