Source code for gui.panel

'''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'))