Unverified Commit 6e33f50c authored by Jeff Niu's avatar Jeff Niu Committed by GitHub

Python engine/scripting environment (#37)

* Added an embedded move function and exposed to python interpreter

* added compatibility with OSX

* Added setup.sh for vagrant env and documentation
parent 7a81893f
......@@ -244,7 +244,4 @@ FakesAssemblies/
# CLion proj files
.idea/
# CMake file
CMakeLists.txt
*.swp
language: cpp
compiler: gcc
dist: trusty
language: cpp
compiler: gcc
dist: trusty
sudo: false
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
before_install:
- sudo apt-get update -qq
- g++ --version
- python3 --version
- cmake --version
install:
- sudo apt-get install -qq g++-6
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90
- sudo apt-get install -y qt4-dev-tools
install:
- sudo apt-get install qtdeclarative5-dev -y -qq
- sudo apt-get install libfontconfig1 mesa-common-dev libglu1-mesa-dev libudev-dev libxi6 libsm6 libxrender1 libegl1-mesa -y -qq
script:
- mkdir build
- cd build
- qmake -o makefile ../minotaur.pro
- make
script:
- mkdir build
- cd build
- cmake ../
- make
notifications:
notifications:
slack: uwnrg:zSxSzV4CSAa1o8tA6jjMCJSP
set(PYTHON_SCRIPT_DIR python-scripts)
macro(add_python_target target)
foreach (file ${target})
get_filename_component(file_name ${file} NAME)
configure_file(${file} ${PYTHON_SCRIPT_DIR}/${file_name} COPYONLY)
endforeach ()
endmacro()
if (APPLE)
# Apple hard sets these variables for system Python
unset(PYTHON_LIBRARY CACHE)
unset(PYTHON_INCLUDE_DIR CACHE)
unset(PYTHON_LIBRARIES CACHE)
endif()
cmake_minimum_required(VERSION 3.1.0)
project(minotaur-cpp)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS} -std=c++11")
find_package(PythonLibs 3.4 REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
if (Qt5_POSITION_INDEPENDENT_CODE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
## Third Party files
set(THIRD_PARTY_COMMON
third-party/qextserialport.h
third-party/qextserialport_p.h
third-party/qextserialport_global.h
third-party/qextserialenumerator.h
third-party/qextserialenumerator_p.h
third-party/qextserialport.cpp
third-party/qextserialenumerator.cpp)
if (WIN32)
set(THIRD_PARTY
third-party/qextserialenumerator_win.cpp
third-party/qextserialport_win.cpp)
endif()
if (APPLE)
set(THIRD_PARTY
third-party/qextserialenumerator_unix.cpp
third-party/qextserialport_unix.cpp)
endif()
if (UNIX)
set(THIRD_PARTY
third-party/qextserialenumerator_unix.cpp
third-party/qextserialport_unix.cpp)
endif()
file(GLOB_RECURSE SOURCE_FILES
${PROJECT_SOURCE_DIR}/code/*.cpp
${PROJECT_SOURCE_DIR}/code/controller/*.cpp
${PROJECT_SOURCE_DIR}/code/graphics/*.cpp
${PROJECT_SOURCE_DIR}/code/gui/*.cpp
${PROJECT_SOURCE_DIR}/code/interpreter/*.cpp
${PROJECT_SOURCE_DIR}/code/utility/*.cpp)
file(GLOB_RECURSE HEADER_FILES
${PROJECT_SOURCE_DIR}/code/controller/*.h
${PROJECT_SOURCE_DIR}/code/graphics/*.h
${PROJECT_SOURCE_DIR}/code/gui/*.h
${PROJECT_SOURCE_DIR}/code/interpreter/*.h
${PROJECT_SOURCE_DIR}/code/utility/*.h)
file(GLOB_RECURSE FORM_FILES
${PROJECT_SOURCE_DIR}/code/gui/*.ui)
add_python_target(code/scripts/robot.py)
include_directories(${Qt5Widgets_INCLUDE_DIRS})
include_directories(${PYTHON_INCLUDE_DIRS})
include_directories(third-party)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
get_target_property(QtCore_location Qt5::Core LOCATION)
add_executable(minotaur-cpp ${SOURCE_FILES} ${HEADER_FILES} ${FORM_FILES} ${THIRD_PARTY} ${THIRD_PARTY_COMMON})
target_link_libraries(minotaur-cpp Qt5::Widgets Qt5::Core Qt5::Gui)
target_link_libraries(minotaur-cpp ${PYTHON_LIBRARIES})
target_include_directories(minotaur-cpp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(minotaur-cpp PRIVATE PYTHON_SCRIPT_DIR="${PYTHON_SCRIPT_DIR}")
if (WIN32)
target_link_libraries(minotaur-cpp advapi32 user32 setupapi)
endif ()
......@@ -390,3 +390,135 @@ are approved, the reviewer labels the PR as reviewed and assigns the PR to the
maintainer so they can merge the PR. After the merge is done, the maintainer will
assign it back to the author to keep track of ownership in the Kanban board (which
is found under the Projects tab on Github).
# Configuring Vagrant Development Environment
This file will describe the necessary steps to setup a virtual development
environment on macOS Sierra 10.12.6 using Vagrant. The steps on other operating
systems should be the exact same except for installing vagrant.
### Installing Vagrant
1. Download and install Vagrant from `https://www.vagrantup.com/downloads.html`
2. Download and install Oracle VirtualBox from `https://www.virtualbox.org/wiki/Downloads`
### Initializing Virtual Environment
1. Create a directory for Vagrant boxes `mkdir vagrant-boxes`
2. Setup the official box `vagrant init hashicorp/precise64`
3. Download and install XQuartz `https://www.xquartz.org/`
4. Edit the `Vagrantfile` created by `vagrant init` to have the line at the end
```config.ssh.forward_x11 = true```
5. Run `vagrant up`
6. Run `vagrant ssh-config`
### Automatically Configure the virtual environment
1. Access the box using `vagrant ssh`
2. Install git with `sudo apt-get install git`
3. Clone the repo and configure with
```
git clone https://github.com/mogball/minotaur-cpp.git
cd minotaur-cpp
chmod 755 setup.sh
./vagrant_setup.sh
```
4. To configure and run in the future, make sure to add Qt to your path
```
export PATH=/home/vagrant/Qt5.7.0/5.7/gcc_64/bin:$PATH
```
5. Then use the commands
```
mkdir build
cd build
cmake ../CMakeLists.txt
make
./minotaur-cpp
```
### Manually Configure the virtual environment
1. Access the box using `vagrant ssh`
#### Installing Qt
1. Download the Qt 5.7 installer `wget http://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-linux-x64-5.7.0.run`
2. Adjust permissions `chmod +x qt-opensource-linux-x64-5.7.0.run`
3. We must now install the required dependecies to run the installer
```
sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install libfontconfig1
sudo apt-get install mesa-common-dev
sudo apt-get install libglu1-mesa-dev
```
4. Run the installer with `./qt-opensource-linux-x64-5.7.0.run`, and if all goes
well, the installer should open in a new window through `XQuartz`
5. You can login, or click `Skip`
6. Hit `Deselect All` and select only `Desktop gcc 64-bit`
7. Accept and install; do not launch Qt Creator
8. After install add Qt to the path with `export PATH=/home/vagrant/Qt5.7.0/5.7/gcc_64/bin:$PATH`
#### Installing Python
1. We require minimum Python 3.4, however Ubuntu 12.04 only goes up to Python 3.2
2. Run `sudo apt-get install python-software-properties` so that
we have `add-apt-repository`
3. Add the PPA with Python 3.4 by running `sudo add-apt-repository ppa:fkrull/deadsnakes`
4. Run `sudo apt-get update`
5. Run `sudo apt-get install python3.4`
6. Run `sudo apt-get install python3.4-dev`
#### Install cmake
1. We require minimum cmake 3.1.0, so we will build cmake 3.2.2 from source
2. Download the source with `wget http://www.cmake.org/files/v3.2/cmake-3.2.2.tar.gz --no-check-certificate`
3. The `--no-check-certificate` is necessary because of Vagrant, and now install with
```
tar xf cmake-3.2.2.tar.gz
cd cmake-3.2.2
./configure
make
sudo make install
```
4. You can verify the installation with `cmake --version`
#### Cloning the repo
1. Install git with `sudo apt-get install git`
2. Clone the repo with `git clone https://github.com/mogball/minotaur-cpp.git`
3. Inside the repo, run `cmake CMakeLists.txt`
4. If all goes well, cmake will generate build files successfully
#### Installing gcc
1. We require gcc 4.8 to support C++11
2. Add the PPA `sudo add-apt-repository ppa:ubuntu-toolchain-r/test`
3. Install gcc 4.8 by running
```
sudo apt-get update
sudo apt-get install gcc-4.8 g++-4.8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 20
```
4. You can verify that gcc is at version 4.8 by running `gcc --version`
#### Installing additional dependencies
1. We need to install a few more things before we can build and run minotaur-cpp
```
sudo apt-get install libudev-dev
sudo apt-get install libxi6
sudo apt-get install libsm6
sudo apt-get install libxrender1
sudo apt-get install libegl1-mesa
```
#### Running minotaur
1. Build minotaur with `make`
2. Run minotaur with `./minotaur-cpp`
......@@ -44,8 +44,6 @@ public:
void move(Dir dir, int timer = STEP_TIME);
virtual void move(Vector2i dir, int timer = STEP_TIME) = 0;
// This is just a wrapper to convert from Dir enum to Vector2i parameter
protected:
Controller(int t_invert_x, int t_invert_y);
......
......@@ -12,7 +12,7 @@ void Simulator::move(Vector2i dir, int timer) {
#endif
dir.x_comp *= m_invert_x;
dir.y_comp *= m_invert_y;
robot_pos += dir * 10;
robot_pos += dir;
// Update the graphics scene
m_simulator_scene->update();
......
......@@ -7,11 +7,14 @@
class SimulatorScene;
// Simulated controller, which directs inputs to an on-screen robot
class Simulator : public Controller {
public:
Simulator(int t_invert_x = 1, int t_invert_y = 1);
void move(Vector2i dir, int timer);
// Set pointer to the simulator scene where the robot is drawn
void setSimulatorScene(SimulatorScene *simulator_scene);
// Get (x,y) position of the robot
Vector2i *getRobotPos();
private:
......
#include "robotgraphicsitem.h"
RobotGraphicsItem::RobotGraphicsItem() : QGraphicsEllipseItem(0, 0, 15, 15) {
}
RobotGraphicsItem::~RobotGraphicsItem() {
}
void RobotGraphicsItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget) {
// Paint a blue circle to represent the robot
painter->setBrush(QBrush(Qt::blue));
painter->drawEllipse((int) x(), (int) y(), 10, 10);
}
\ No newline at end of file
......@@ -6,9 +6,11 @@
#include <QStyleOptionGraphicsItem>
#include <QWidget>
// Graphics item that is drawn to represent the robot
class RobotGraphicsItem : public QGraphicsEllipseItem {
public:
RobotGraphicsItem();
~RobotGraphicsItem();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
};
......
......@@ -18,6 +18,8 @@ SimulatorScene::~SimulatorScene() {
}
void SimulatorScene::update() {
// Get the robot position from the simulator controller
// and set the position of the robot graphics on the scene
Vector2i robot_pos = *m_simulator_controller->getRobotPos();
#ifndef NDEBUG
Logger::log("Beginning graphic scene update", Logger::DEBUG);
......
......@@ -7,13 +7,16 @@
class Simulator;
// Graphics scene where the simulation is drawn
class SimulatorScene : public QGraphicsScene {
public:
SimulatorScene(std::shared_ptr<Simulator> &simulator_controller, QObject *parent);
// Redraw the scene with new positions
void update();
~SimulatorScene();
private:
// Pointer to robot graphics
QGraphicsItem *robot_graphics;
std::shared_ptr<Simulator> m_simulator_controller;
};
......
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "../interpreter/embeddedcontroller.h"
#include "../interpreter/pythonengine.h"
MainWindow::MainWindow(QWidget *parent, const char *title) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
......@@ -13,21 +16,24 @@ MainWindow::MainWindow(QWidget *parent, const char *title) :
m_simulator = std::shared_ptr<Simulator>(new Simulator(1, -1));
m_controller = m_actuator;
m_controller_type = Controller::Type::ACTUATOR;
m_script_engine = std::shared_ptr<ScriptEngine>(new ScriptEngine(m_controller));
// Bind controller to Python Engine
EmbeddedController::getInstance().bind_controller(&m_controller);
PythonEngine::getInstance().append_module("emb", &Embedded::PyInit_emb);
// Setup subwindows
actuator_setup_window = new ActuatorSetup(m_actuator, this);
simulator_window = new SimulatorWindow(m_simulator, this);
script_window = new ScriptWindow(this);
m_simulator->setSimulatorScene(simulator_window->getSimulatorScene());
action_about_window = new ActionAbout();
script_window = new ScriptWindow(m_script_engine);
// Setup slot connections
connect(ui->setup_actuator, SIGNAL(triggered()), this, SLOT(openActuatorSetup()));
connect(ui->switch_to_actuator_mode, SIGNAL(triggered()), this, SLOT(switchToActuator()));
connect(ui->switch_to_simulator_mode, SIGNAL(triggered()), this, SLOT(switchToSimulator()));
connect(ui->start_python_interpreter, SIGNAL(triggered()), this, SLOT(openPythonInterpreter()));
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(openActionAbout()));
connect(ui->open_script_window, SIGNAL(triggered()), this, SLOT(openScriptWindow()));
// setup focus and an event filter to capture key events
this->installEventFilter(this);
......@@ -89,6 +95,7 @@ MainWindow::~MainWindow() {
// Destroy all subwindows
delete actuator_setup_window;
delete simulator_window;
delete script_window;
delete ui;
}
......@@ -131,10 +138,10 @@ void MainWindow::openActuatorSetup() {
actuator_setup_window->show();
}
void MainWindow::openActionAbout() {
action_about_window->show();
void MainWindow::openPythonInterpreter() {
script_window->show();
}
void MainWindow::openScriptWindow() {
script_window->show();
void MainWindow::openActionAbout() {
action_about_window->show();
}
......@@ -9,7 +9,6 @@
#include "../controller/controller.h"
#include "../controller/simulator.h"
#include "../script-engine/scriptengine.h"
#include "actuatorsetup.h"
#include "simulatorwindow.h"
......@@ -31,14 +30,14 @@ public:
void keyPressEvent(QKeyEvent*);
~MainWindow();
public slots:
public Q_SLOTS:
void openActuatorSetup();
void openScriptWindow();
void openPythonInterpreter();
inline void switchToActuator() { switchControllerTo(Controller::Type::ACTUATOR); }
inline void switchToSimulator() { switchControllerTo(Controller::Type::SIMULATOR); }
void openActionAbout();
private slots:
private Q_SLOTS:
// Button click events
void on_move_button_clicked();
// Mouse events
......@@ -54,7 +53,6 @@ private:
std::shared_ptr<Controller> m_controller;
std::shared_ptr<Actuator> m_actuator;
std::shared_ptr<Simulator> m_simulator;
std::shared_ptr<ScriptEngine> m_script_engine;
bool eventFilter(QObject*, QEvent*);
void switchControllerTo(Controller::Type const type);
};
......
......@@ -177,6 +177,7 @@ p, li { white-space: pre-wrap; }
<addaction name="switch_to_simulator_mode"/>
<addaction name="separator"/>
<addaction name="setup_actuator"/>
<addaction name="start_python_interpreter"/>
<addaction name="separator"/>
<addaction name="exit"/>
</widget>
......@@ -417,9 +418,9 @@ p, li { white-space: pre-wrap; }
<string>About</string>
</property>
</action>
<action name="open_script_window">
<action name="start_python_interpreter">
<property name="text">
<string>Script Engine</string>
<string>Python Interpreter</string>
</property>
</action>
</widget>
......
#include "scriptwindow.h"
#include "ui_scriptwindow.h"
ScriptWindow::ScriptWindow(std::shared_ptr<ScriptEngine>& script_engine, QWidget* parent) :
QDialog(parent),
ui(new Ui::ScriptWindow),
m_script_engine(script_engine)
{
ScriptWindow::ScriptWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::ScriptWindow) {
ui->setupUi(this);
this->setWindowTitle("Python Interpreter");
// Create text edit and text display
m_interpreter_text_edit = new InterpreterTextEdit(this);
m_results_text_display = new ResultsTextDisplay(this);
ui->interpreterLayout->addWidget(m_interpreter_text_edit);
ui->displayLayout->addWidget(m_results_text_display);
QFont font;
// Make the font look like code
font.setFamily("Courier");
font.setStyleHint(QFont::Monospace);
font.setFixedPitch(true);
font.setPointSize(8);
QFontMetrics metrics(font);
const int tabSize = 4;
const int tabStopWidth = tabSize * metrics.width(' ');
m_interpreter_text_edit->setFont(font);
m_results_text_display->setFont(font);
m_interpreter_text_edit->setTabStopWidth(tabStopWidth);
m_results_text_display->setTabStopWidth(tabStopWidth);
// Connect signals to slots
connect(ui->resetButton, SIGNAL(clicked()), this, SLOT(resetInterpreter()));
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(closeInterpreter()));
connect(ui->runButton, SIGNAL(clicked()), this, SLOT(runScript()));
connect(m_interpreter_text_edit, SIGNAL(scriptSubmitted()), this, SLOT(runScript()));
connect(this, SIGNAL(scriptSubmitted(const QString&, const QString&, const QString&)),
m_results_text_display, SLOT(appendResults(const QString&, const QString&, const QString&)));
}
ScriptWindow::~ScriptWindow()
{
ScriptWindow::~ScriptWindow() {
delete ui;
delete m_interpreter_text_edit;
delete m_results_text_display;
}
void ScriptWindow::setVisible(bool visible) {
if (visible) PythonEngine::getInstance().initialize();
else PythonEngine::getInstance().stopEngine();
QDialog::setVisible(visible);
}
void ScriptWindow::reject() {
PythonEngine::getInstance().stopEngine();
QDialog::reject();
}
// Clear the interpreter text fields and restart the engine
void ScriptWindow::resetInterpreter() {
m_interpreter_text_edit->clear();
m_results_text_display->clear();
PythonEngine::getInstance().stopEngine();
PythonEngine::getInstance().initialize();
}
void ScriptWindow::closeInterpreter() {
PythonEngine::getInstance().stopEngine();
this->reject();
}
// Parse a script from the interpreter text edit and send
// to python engine, then display the results
void ScriptWindow::runScript() {
if (!PythonEngine::getInstance().isReady()) return;
std::string script = m_interpreter_text_edit->toPlainText().toStdString();
std::string *out = new std::string;
std::string *err = new std::string;
PythonEngine::getInstance().run(script, out, err);
// Emit signal to output display to append results
Q_EMIT scriptSubmitted(
QString::fromStdString(script),
QString::fromStdString(*out),
QString::fromStdString(*err));
m_interpreter_text_edit->clear();
delete out;
delete err;
}
\ No newline at end of file
......@@ -4,24 +4,41 @@
#include <QDialog>
#include <memory>
#include "../script-engine/scriptengine.h"
#include "../interpreter/pythonengine.h"
#include "../interpreter/interpretertextedit.h"
#include "../interpreter/resultstextdisplay.h"
namespace Ui {
class ScriptWindow;
class ScriptWindow;
}
class ScriptWindow : public QDialog
{
Q_OBJECT
class ScriptWindow : public QDialog {
Q_OBJECT
public:
explicit ScriptWindow(std::shared_ptr<ScriptEngine>&, QWidget *parent = 0);
~ScriptWindow();
explicit ScriptWindow(QWidget *parent = 0);
~ScriptWindow() override;
protected:
void setVisible(bool visible);
void reject();
private Q_SLOTS:
void resetInterpreter();
void closeInterpreter();
// Run the current buffered script
void runScript();
Q_SIGNALS:
// Signal fired when the user submits a python script
void scriptSubmitted(const QString &script, const QString &out, const QString &err);
private:
Ui::ScriptWindow *ui;
std::shared_ptr<ScriptEngine> m_script_engine;
// Where the user enters python commands
InterpreterTextEdit *m_interpreter_text_edit;
// Where the python output is displayed
ResultsTextDisplay *m_results_text_display;
};
#endif // SCRIPTWINDOW_H
......@@ -6,23 +6,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>512</width>
<height>362</height>
<width>510</width>
<height>650</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QTextEdit" name="textEdit">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>371</width>
<height>341</height>
</rect>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
......@@ -60,9 +50,9 @@
<property name="geometry">
<rect>
<x>390</x>
<y>290</y>
<width>101</width>
<height>61</height>
<y>550</y>
<width>100</width>
<height>83</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
......@@ -73,6 +63,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
......@@ -82,6 +79,28 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_3">
<property name="geometry">
<rect>
<x>10</x>
<y>330</y>
<width>371</width>
<height>301</height>
</rect>
</property>
<layout class="QVBoxLayout" name="interpreterLayout"/>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_4">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>371</width>
<height>301</height>
</rect>
</property>
<layout class="QVBoxLayout" name="displayLayout"/>
</widget>
</widget>
<resources/>
<connections/>
......
......@@ -7,19 +7,31 @@ SimulatorWindow::SimulatorWindow(std::shared_ptr<Simulator> simulator, QWidget *
QDialog(parent),
ui(new Ui::SimulatorWindow) {
ui->setupUi(this);
setWindowTitle("Simulator");
m_simulator_scene = new SimulatorScene(simulator, ui->simulator_graphics_view);
ui->simulator_graphics_view->setScene(m_simulator_scene);
// Prevent resizing
this->setFixedSize(this->size());
}
void SimulatorWindow::keyPressEvent(QKeyEvent *event) {
// Forward key press events to the main window
QWidget *parent = parentWidget();
if (!parent) return;
MainWindow *mainWindow = (MainWindow*) parentWidget();
MainWindow *mainWindow = (MainWindow*) parent;
mainWindow->keyPressEvent(event);
}
void SimulatorWindow::reject() {
// When the user clicks exit, switch back to ACTUATOR
QWidget *parent = parentWidget();
if (!parent) return;
MainWindow *mainWindow = (MainWindow*) parent;
mainWindow->switchToActuator();
QDialog::reject();
}
SimulatorWindow::~SimulatorWindow() {
delete m_simulator_scene;
delete ui;
......