Commit 03f5f426 authored by Yann Boucher's avatar Yann Boucher
Browse files

Implemented dialog for loading a model, and behavior for customizing it

parent 0cb352ba
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1101</width>
<height>706</height>
<width>1092</width>
<height>754</height>
</rect>
</property>
<property name="windowTitle">
......@@ -51,7 +51,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="simulation_tab">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
......@@ -395,15 +395,28 @@ pattern recorded :</string>
<property name="title">
<string>Rules</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QGridLayout" name="gridLayout_3">
<item row="4" column="0" colspan="3">
<widget class="QGroupBox" name="rule_settings">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="openRuleButton">
<widget class="QLabel" name="transitionFunctionLabel">
<property name="text">
<string>(file) Open rule</string>
<string>Transition function :</string>
</property>
</widget>
</item>
<item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<layout class="QGridLayout" name="statesNeighborhoodLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="neighborhoodLabel">
......@@ -507,35 +520,24 @@ pattern recorded :</string>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="transitionFunctionLabel">
<property name="text">
<string>Transition function :</string>
</property>
</widget>
</item>
<item>
<item row="2" column="1">
<widget class="QComboBox" name="transition_list"/>
</item>
</layout>
</item>
<item>
<item row="3" column="0" colspan="2">
<widget class="QGroupBox" name="rule_settings_area">
<property name="title">
<string>Transition rule settings</string>
</property>
</widget>
</item>
<item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="informationLabel">
<property name="text">
<string>New rule must be saved before running simulation</string>
</property>
</widget>
</item>
<item>
<item row="5" column="0" colspan="2">
<widget class="QPushButton" name="saveRuleButton">
<property name="text">
<string>Save rule</string>
......@@ -545,6 +547,47 @@ pattern recorded :</string>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QPushButton" name="openRuleButton">
<property name="text">
<string>Load rule</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="customize_button">
<property name="text">
<string>Customize...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout_2">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name : </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="rule_name">
<property name="text">
<string>RuleName</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
......@@ -577,7 +620,7 @@ pattern recorded :</string>
<rect>
<x>0</x>
<y>0</y>
<width>1101</width>
<width>1092</width>
<height>25</height>
</rect>
</property>
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModelLoadingDialog</class>
<widget class="QDialog" name="ModelLoadingDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>385</width>
<height>639</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="tree">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Title : </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="title">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_struct_author">
<property name="text">
<string>Author : </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="author">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Date : </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="date">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Description : </string>
</property>
<property name="buddy">
<cstring>desc</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPlainTextEdit" name="desc">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ModelLoadingDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>229</x>
<y>617</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ModelLoadingDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>297</x>
<y>623</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
......@@ -66,6 +66,8 @@ private slots:
void on_saveRuleButton_clicked();
void on_customize_button_clicked();
private:
//! \brief Initialiser la liste des transitions et voisinages disponibles
void init_transition_neighborhood_list();
......@@ -73,6 +75,20 @@ private:
//! \brief Mets à jour le widget contenant les paramètres pour la règle de transition choisie
void update_transition_settings();
//! \brief Configure pour activer la customisation d'une règle actuelle
void enable_rule_customization();
//! \brief Configure pour désactiver la customisation d'une règle actuelle
void disable_rule_customization();
//! \brief Charge un modèle d'automate à partir d'un objet JSON
void load_model(const QJsonObject& obj);
//! \brief Déclenche la confirmation et la sauvegarde du modèle édité actuel
void save_model();
//! \brief Retourne le modèle par défaut à charger
QJsonObject default_model() const;
private:
Ui::MainWindow *ui;
};
......
#ifndef MODELLOADINGDIALOG_HPP
#define MODELLOADINGDIALOG_HPP
#include <QDialog>
#include <QDir>
#include <QTreeWidgetItem>
#include <QFileSystemWatcher>
#include <QJsonObject>
namespace Ui {
class ModelLoadingDialog;
}
class ModelLoadingException : public std::exception
{
std::string _msg;
public:
ModelLoadingException(const std::string& msg) : _msg(msg){}
virtual const char* what() const noexcept override
{
return _msg.c_str();
}
};
class ModelLoadingDialog : public QDialog
{
Q_OBJECT
public:
explicit ModelLoadingDialog(QWidget *parent = nullptr);
~ModelLoadingDialog();
//! \brief Retoure le modèle choisi en tant qu'objet JSON.
QJsonObject model() const;
protected:
void done(int r);
private:
void load_models();
QTreeWidgetItem *add_directory_contents(const QDir& dir);
private slots:
void update_info(QTreeWidgetItem* item, int column);
private:
Ui::ModelLoadingDialog *ui;
QFileSystemWatcher m_watcher;
QJsonObject m_current_model;
};
#endif // MODELLOADINGDIALOG_HPP
......@@ -159,7 +159,7 @@ struct IntegerProperty : public PropertyImpl<IntegerProperty>
public:
//! \brief Construit un objet IntegerProperty avec une valeur min et une valeur max, non borné par défaut.
IntegerProperty(int min_value = std::numeric_limits<int>::min(), int max_value = std::numeric_limits<int>::max())
: prop_min(min_value), prop_max(max_value)
: val(min_value), prop_min(min_value), prop_max(max_value)
{}
public:
......
......@@ -7,6 +7,7 @@
#include "transitionrule.hpp"
#include "neighborhoodrule.hpp"
#include "propertyvisitors.hpp"
#include "modelloadingdialog.hpp"
#include <QJsonArray>
......@@ -26,6 +27,8 @@ MainWindow::MainWindow(QWidget *parent)
{ update_transition_settings(); });
ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_pixel_size());
load_model(default_model());
}
MainWindow::~MainWindow()
......@@ -47,20 +50,23 @@ void MainWindow::on_simSpeedSlider_valueChanged(int value)
void MainWindow::on_openPatternButton_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"/home",tr("PatternFile (*.pat)"));
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"",tr("Pattern file (*.json)"));
if(fileName == ""){
// L'utilisateur a annulé la recherche
return;
}
// Indiquer qu'un nouveau pattern vient d'être ouvert
}
void MainWindow::on_openRuleButton_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"/home",tr("RuleFile (*.rule)"));
if(fileName == ""){
// L'utilisateur a annulé la recherche
}
// Indiquer qu'une nouvelle règle vient d'être ouverte. La règle est sauvegardé, donc on peut lancer la simulation
ModelLoadingDialog dialog(this);
if (!dialog.exec())
return;
load_model(dialog.model());
}
void MainWindow::on_neighborhood_list_currentTextChanged(const QString &arg1)
......@@ -253,25 +259,54 @@ static std::unique_ptr<TransitionRule> transition_rule;
void MainWindow::update_transition_settings()
{
std::string selected = ui->transition_list->currentText().toStdString();
auto rule = Factory<TransitionRule>::make(selected);
if (!rule)
transition_rule = Factory<TransitionRule>::make(selected);
if (!transition_rule)
return;
if (rule->get_properties().size() == 0)
if (transition_rule->get_properties().size() == 0)
ui->rule_settings_area->hide();
else
{
ui->rule_settings_area->show();
UIBuilderVisitor visit(ui->rule_settings_area);
for (auto& prop : rule->get_properties())
for (auto& prop : transition_rule->get_properties())
prop->accept(visit);
}
}
transition_rule = std::move(rule);
void MainWindow::enable_rule_customization()
{
ui->simulation_tab->setEnabled(false);
ui->customize_button->setEnabled(false);
ui->rule_settings->setEnabled(true);
}
void MainWindow::on_saveRuleButton_clicked()
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());
update_transition_settings();
disable_rule_customization();
PropertyLoaderVisitor loader(obj.value("transition_data").toObject());
for (auto& prop : transition_rule->get_properties())
prop->accept(loader);
UIBuilderVisitor visit(ui->rule_settings_area);
for (auto& prop : transition_rule->get_properties())
prop->accept(visit);
}
void MainWindow::save_model()
{
SavingDialog dialog(this);
if (!dialog.exec())
......@@ -304,4 +339,27 @@ void MainWindow::on_saveRuleButton_clicked()
file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
file.write(doc.toJson());
file.close();
ui->rule_name->setText(dialog.titre());
disable_rule_customization();
}
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();
}
#include "modelloadingdialog.hpp"
#include "ui_modelloadingdialog.h"
#include <QJsonDocument>
#include <QMessageBox>
ModelLoadingDialog::ModelLoadingDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ModelLoadingDialog)
{
ui->setupUi(this);
load_models();
connect(ui->tree, &QTreeWidget::itemClicked, this, &ModelLoadingDialog::update_info);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString&)
{
load_models();
});
}
ModelLoadingDialog::~ModelLoadingDialog()
{
delete ui;
}
QJsonObject ModelLoadingDialog::model() const
{
return m_current_model;
}
void ModelLoadingDialog::load_models()
{
// clear the previously watched directories
m_watcher.removePaths(m_watcher.directories());
QTreeWidgetItem* model_list = add_directory_contents(QDir("models/"));
model_list->setText(0, "Models");
ui->tree->clear();
ui->tree->addTopLevelItem(model_list);
model_list->setExpanded(true);
}
QTreeWidgetItem *ModelLoadingDialog::add_directory_contents(const QDir &dir)
{
QTreeWidgetItem* root = new QTreeWidgetItem;
QFileInfoList dirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
Q_FOREACH (QFileInfo dir, dirs)
{
QTreeWidgetItem *child = add_directory_contents(QDir(dir.absoluteFilePath()));
// empty directory
if (child->childCount() == 0)
continue;
child->setText(0, dir.fileName());
root->addChild(child);
}
QFileInfoList files = dir.entryInfoList(QStringList() << "*.json", QDir::Files);
Q_FOREACH (QFileInfo file, files)
{
QTreeWidgetItem *child = new QTreeWidgetItem();
child->setText(0, file.fileName());
// data is the complete filepath of the structure, store it so it can be easily accessed when an item is selected
child->setData(0, Qt::UserRole, file.absoluteFilePath());
root->addChild(child);
}
// register this directory for filesystem watching
m_watcher.addPath(dir.absolutePath());
return root;
}
void ModelLoadingDialog::update_info(QTreeWidgetItem *item, int column)
{
(void)column;