'''Provides the side panel shown alongside the visulaiser'''
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.font import Font
from config import GUIConfig, SimulatorConfig
conf = GUIConfig()
visConf = SimulatorConfig()
[docs]class SimulatorPanel(tk.Frame):
'''Side panel of the window, extends tk.Frame.
Shows information about the visulisation.
:param parent: Parent :class:`tkinter.Widget`
:param sim: :class:`~gui.sim.Simulator` instance to control
:param visualiser: :class:`~visualiser.visualiser.SimulatorVisualiser`
instance to take information from
:param kwargs: keyword arguments to pass to superclass constructor
'''
def __init__(self, parent, sim, visualiser, **kwargs):
super().__init__(parent, **kwargs, background='white')
self.grid(padx=10, pady=30)
self.columnconfigure(0, weight=1)
self._parent = parent
self._simulator = sim
self._visualiser = visualiser
self._rowCount = 0
self._innerRowCount = 0
# initialise fonts
self._normal = Font(**conf.Font)
self._bold = Font(**conf.FontBold)
self._head = Font(**conf.FontHeading)
# initilise used properties
self._modeL = None
self._modeC = None
self._mode = None
self._time = None
self._distance = None
self._collisions = None
self._collisionsC = None
self._lane = None
self._laneC = None
self._speed = None
# mode indicator
row = self._newRow()
row.grid(pady=30)
row.columnconfigure(0, weight=1, uniform='a')
self._makeLabel('mode', 'Mode', row)
self._modeL.config(font=self._head, width=4)
self._modeC.config(anchor='w')
self._mode.set('manual')
# pause/resume & restart button
row = self._newRow()
row.grid(pady=10)
row.columnconfigure(0, weight=1, uniform='a')
self._pause = tk.StringVar()
self._pauseB = ttk.Button(
row, textvariable=self._pause,
command=self._call(self._visualiser.togglePause))
self._pauseB.grid(row=0, column=0, sticky='ew', padx=(0, 5))
self._restartB = ttk.Button(row, text='Restart',
command=self._call(self._simulator.restart))
self._restartB.grid(row=0, column=1, sticky='ew', padx=(5, 0))
# other indicators
row = self._newRow()
self._makeLabel('time', 'Session', row)
self._makeLabel('distance', 'Distance', row)
self._makeLabel('collisions', 'Collisions', row)
self._makeLabel('lane', 'Lane', row)
self._makeLabel('speed', 'Speed', row)
# finish & save button
self._save = tk.StringVar()
self._saveB = ttk.Button(self, textvariable=self._save,
command=self._call(self._parent.finish))
self._saveB.grid(row=self._rowCount, rowspan=2, column=0, pady=30,
sticky='ew')
self._rowCount += 1
[docs] def _newRow(self, **kwargs):
'''Create a new row to be used
:return: The new :class:`~tkinter.ttk.Frame`
'''
frame = tk.Frame(self, **kwargs, background='white')
frame.grid(row=self._rowCount, column=0, sticky='ew')
frame.columnconfigure(1, weight=1, uniform='a')
self._rowCount += 1
self._innerRowCount = 0
return frame
[docs] def _makeLabel(self, name, text, row):
'''Create and attach multiple labels to the given row
:param name: :class:`str` used in property names
:param text: :class:`str` to use as label
:param row: :class:`tkinter.Widget` to attach to
'''
label = tk.Label(row, text=text, font=self._bold, background='white',
anchor='e', width=8)
label.grid(
row=self._innerRowCount, column=0, padx=10, pady=5, sticky='nesw')
setattr(self, '_%sL' % name, label)
var = tk.StringVar()
setattr(self, '_%s' % name, var)
container = tk.Label(row, textvariable=var, font=self._normal,
background='white')
container.grid(row=self._innerRowCount, column=1, sticky='nesw')
setattr(self, '_%sC' % name, container)
self._innerRowCount += 1
return var
[docs] def _call(self, func):
'''Return a function that focuses the panel then calls `func`
Used for button commands
'''
def inner(): # pylint: disable=missing-docstring
self.focus()
func()
return inner
[docs] def tick(self):
'''Update displayed information using the
:class:`~visualiser.visualiser.SimulatorVisualiser`
'''
vis = self._visualiser
mode = vis.mode
self._mode.set(conf.ModeText[mode])
# pause button text
self._pause.set('Resume' if vis.pause else 'Pause')
# session time in mins and secs
mins, secs = divmod(vis.sessionTime, 60)
self._time.set('%02d:%02d' % (mins, secs))
# distance in meters
dist = vis.distanceTravelled
self._distance.set(
'%.0fm' % dist if dist < 1000
else '%.2fkm' % (dist / 1000))
# collision counter
cols = []
if mode != 1:
# user car collisions
cols.append(vis.collisions)
if mode > 0:
# agent car collisions
cols.append(vis.agentCollisions)
self._collisions.set('/'.join(map(str, cols)))
self._collisionsC.config(
foreground=('red' if sum(cols) > 0 else 'black'))
# update lane label, color red if offroad
laneMap = {0: 'offroad', 1: 'left', 2: 'middle', 3: 'right', 4: 'offroad'}
laneList = []
offroad = False
if mode != 1:
# user car lane
laneList.append(laneMap[vis.car.lane])
offroad = offroad or vis.car.lane in [0, 4]
if mode > 0:
# agent car lane
laneList.append(laneMap[vis.agentCar.lane])
offroad = offroad or vis.agentCar.lane in [0, 4]
self._lane.set('/'.join(laneList))
self._laneC.config(foreground=('red' if offroad else 'black'))
# speed indicator
self._speed.set('%dkph' % (vis.car.speed * 18/5))
self._save.set('Finish' +('' if mode > 0 else ' & Save'))