Source code for gui.sim
'''Provides the main simulator, which includes the Pygame visualiser and a
Tkinter panel'''
import random
import tkinter as tk
import tkinter.ttk as ttk
from time import time
from collections import deque
from visualiser import SimulatorVisualiser
from visualiser.util import Actions
from config import GUIConfig, SimulatorConfig
from audri import Agent
from data import saveData, loadData
from .panel import SimulatorPanel
visConf = SimulatorConfig()
conf = GUIConfig()
[docs]class Simulator(tk.Frame):
'''The GUI component of the visualiser
Contains the :class:`SimulatorVisualiser` (Pygame canvas) in a
:class:`tkinter.ttk.Frame`.
The :class:`SimulatorPanel` is displayed alongside it.'''
def __init__(self, root, main):
'''Initialise the window and its components'''
super().__init__(root, background='white')
self.columnconfigure(1, weight=1)
self._main = main
self.agent = Agent()
self._datasetName = None
self._lastRecord = time() # when last feature vector was recorded
self._snapshots = []
self.agent = Agent()
self._snapshots = deque()
self._canvas = tk.Frame(self, width=conf.VisualiserWidth,
height=conf.Height, background='white')
self._canvas.grid(row=0, column=0)
# update so visualiser can be attached
self.update()
self._visualiser = SimulatorVisualiser(str(self._canvas.winfo_id()))
self._panel = SimulatorPanel(self, self, self._visualiser)
self._panel.grid(row=0, column=1, sticky='nsew')
self.mode = 0
random.seed(visConf.RandomSeed)
@property
def mode(self):
'''
| The current mode in which the simulator should run in.
| 0 = Manual: Collect training data
| 1 = AUDRI: Train AUDRI and let it control the car
| 2 = Compare: Allow both expert and AUDRI to control two different \
cars, side by side
:getter: Get the current mode
:setter: Set the current mode, also setting the mode of the
:class:`~visualiser.visualiser.SimulatorVisualiser`.
Prevents setting an invalid mode
:type: :class:`int`
'''
return self._mode
@mode.setter
def mode(self, val):
if val < 0 or val > 2:
return
self._mode = val
self._visualiser.mode = val
[docs] def setDataset(self, name):
'''Set the :py:attr:`_datasetName` property from the
:class:`~gui.sim.NameDatasetPopup`, unpause the
:class:`~visualiser.visualiser.SimulatorVisualiser`,
and return focus to the main window
:param name: :class:`str` name of dataset to store training data in
'''
self._datasetName = name
self._visualiser.pause = False
self._main.focus_force()
[docs] def focus(self):
'''If loaded in manual mode, create a :class:`~gui.sim.NameDatasetPopup`
to choose the training data name.
If in AUDRI mode, train the model
'''
self._visualiser.pause = True
if self.mode == 0 and self._datasetName is None:
NameDatasetPopup(self)
elif self.mode > 0:
self.agent.train(loadData('data'))
self._visualiser.pause = False
width = conf.VisualiserWidth *(1 if self.mode < 2 else 2)
self._canvas.config(w=width)
[docs] def tick(self):
'''Call visualiser and panel tick frequently'''
if not self._visualiser.pause and self.mode == 0 \
and self._visualiser.lastActionTime >= self._lastRecord:
# record actions when they are performed in training mode
self._lastRecord = time()
self._snapshots.append(self._visualiser.stateVector())
elif not self._visualiser.pause \
and time() -self._lastRecord >= visConf.RecordInterval:
# record data or allow agent to perform an action at regular
# intervals
self._lastRecord = time()
if self.mode > 0:
state = self._visualiser.stateVector(agent=True)
action = self.agent.action(state)
print('Predict:', Actions(action), '\n')
self._visualiser.doAct(Actions(action), agent=True)
else:
self._snapshots.append(self._visualiser.stateVector())
self._visualiser.tick()
self._panel.tick()
[docs] def keyPress(self, event):
'''Pass key press events to visualiser'''
self._visualiser.keyPress(event.keysym)
[docs] def restart(self):
'''Reset random seed and restart visualiser using
:py:meth:`~visualiser.visualiser.SimulatorVisualiser.reset`
'''
self._snapshots.clear()
random.seed(visConf.RandomSeed)
self._visualiser.reset()
[docs] def finish(self):
'''Stop the visualiser and return to the main menu'''
self._visualiser.reset()
self._visualiser.pause = True
if self.mode == 0:
saveData(self._datasetName, self._snapshots)
self._datasetName = None
self._snapshots = []
self.mode = 0
self._main.back()