Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1310 lines (1096 sloc) 56.5 KB
"""
Copyright 2013 Štěpán Bahník
This file is part of Carousel Maze Manager.
Carousel Maze Manager is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Carousel Maze Manager is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Carousel Maze Manager. If not, see <http://www.gnu.org/licenses/>.
"""
from tkinter import *
from tkinter import ttk
from time import time
from math import cos, sin, radians, degrees, atan2, floor
import os
import os.path
from showtracks import ShowTracks
from filestorage import FileStorageFrame
from commonframes import TimeFrame, returnName
from image import svgSave, ImagesOptions
from processor import ProgressWindow
from optionget import optionGet
from graphs import getGraphTypes, Graphs, SvgGraph, SpeedGraph, DistanceFromCenterGraph
from graphs import AngleGraph, DistanceFromPlatformGraph, DistanceFromRobotGraph
from comment import Comment, commentColor
import mode as m
class Explorer(ttk.Frame):
"represents ''Explore' page in the main window notebook"
def __init__(self, root):
super().__init__(root)
self["padding"] = (10, 10, 12, 12)
self.animate = "stop"
self.initialized = False
self.root = root
self.fileStorage = self.root.fileStorage
# variables
self.status = StringVar()
self.curTime = DoubleVar()
self.speed = DoubleVar()
self.graphTypeVar = StringVar()
self.distanceVar = StringVar()
self.entrancesVar = StringVar()
self.selectedPVar = StringVar()
self.selectedParameter = StringVar()
self.graphParameter = StringVar()
self.timeVar = StringVar()
self.totDistanceVar = StringVar()
self.totEntrancesVar = StringVar()
self.totSelectedPVar = StringVar()
self.showTrackVar = BooleanVar()
self.removeReflectionsVar = BooleanVar()
self.showShocksVar = BooleanVar()
self.showTailVar = BooleanVar()
self.saveWhatVar = StringVar()
self.saveWhichFilesVar = StringVar()
self.curTime.set(0)
self.speed.set(1.0)
self.graphTypeVar.set("SpeedGraph(self)")
self.showTrackVar.set(False)
self.removeReflectionsVar.set(False)
self.showShocksVar.set(True)
self.showTailVar.set(False)
self.saveWhatVar.set("all")
self.saveWhichFilesVar.set("current")
self.selectedParameter.set("")
self.graphParameter.set("nothing")
# frames
self.controlsLF = ttk.Labelframe(self, text = "Controls")
self.graphLF = ttk.Labelframe(self, text = "Graph")
self.fileStorageFrame = FileStorageFrame(self, parent = "explorer")
self.fileFrame = FileFrame(self)
self.parametersLF = ttk.LabelFrame(self, text = "Parameters")
self.timeLabFrame = ttk.Labelframe(self, text = "Time")
self.timeLabFrame.root = self
self.timeFrame = TimeFrame(self.timeLabFrame, onChange = True)
if m.mode == "CM":
arenaText = "Arena frame"
elif m.mode == "RA":
arenaText = "Room frame"
else:
arenaText = "Animation"
self.arenaFrame = ttk.LabelFrame(self, text = arenaText)
if m.mode == "CM":
roomText = "Room frame"
elif m.mode == "RA":
roomText = "Robot frame"
else:
roomText = "Track"
self.roomFrame = ttk.LabelFrame(self, text = roomText)
self.speedScaleFrame = ttk.Frame(self)
self.speedScaleFrame.rowconfigure(0, weight = 1)
self.optionsLF = ttk.LabelFrame(self, text = "Options")
self.saveImagesLF = ttk.LabelFrame(self, text = "Save image(s)")
# canvases
self.arenaCanv = Canvas(self.arenaFrame, background = "white", height = 300, width = 300)
self.roomCanv = Canvas(self.roomFrame, background = "white", height = 300, width = 300)
# buttons
self.playBut = ttk.Button(self.controlsLF, text = "Play", command = self.playFun,
state = "disabled")
self.pauseBut = ttk.Button(self.controlsLF, text = "Pause", command = self.pauseFun,
state = "disabled")
self.stopBut = ttk.Button(self.controlsLF, text = "Stop", command = self.stopFun,
state = "disabled")
self.saveBut = ttk.Button(self.saveImagesLF, text = "Save", command = self.saveImages,
state = "disabled")
self.optionBut = ttk.Button(self.saveImagesLF, text = "Options",
command = self.imagesOptions)
# checkbuttons
self.removeReflections = ttk.Checkbutton(self.optionsLF, text = "Remove reflections",
variable = self.removeReflectionsVar,
command = self.toggleReflections)
self.showShocks = ttk.Checkbutton(self.optionsLF, text = "Show shocks",
variable = self.showShocksVar,
command = self.toggleShocks)
self.showTail = ttk.Checkbutton(self.optionsLF, text = "Show tail",
variable = self.showTailVar, command = self.toggleTail)
# radiobutton
self.showTrack = ttk.Radiobutton(self.optionsLF, text = "Show track",
variable = self.showTrackVar, value = True,
command = self.toggleAnimation)
self.showAnimation = ttk.Radiobutton(self.optionsLF, text = "Show animation",
variable = self.showTrackVar, value = False,
command = self.toggleAnimation)
# label
self.statusBar = ttk.Label(self, textvariable = self.status)
self.distance = ttk.Label(self.parametersLF, textvariable = self.distanceVar, width = 10,
anchor = "e")
self.entrances = ttk.Label(self.parametersLF, textvariable = self.entrancesVar, width = 13,
anchor = "e")
self.selectedP = ttk.Label(self.parametersLF, textvariable = self.selectedPVar, width = 13,
anchor = "e")
self.timePar = ttk.Label(self.parametersLF, textvariable = self.timeVar, width = 13,
anchor = "e")
self.totDistance = ttk.Label(self.parametersLF, textvariable = self.totDistanceVar,
width = 10, anchor = "e")
self.totEntrances = ttk.Label(self.parametersLF, textvariable = self.totEntrancesVar,
width = 10, anchor = "e")
self.totSelectedP = ttk.Label(self.parametersLF, textvariable = self.totSelectedPVar,
width = 10, anchor = "e")
self.distanceLab = ttk.Label(self.parametersLF, text = "Distance [m]", width = 11,
anchor = "w")
self.entrancesLab = ttk.Label(self.parametersLF, text = "Entrances", width = 11,
anchor = "w")
self.selectedPLab = ttk.Label(self.parametersLF, text = "Selected", width = 11,
anchor = "w")
self.timeLab = ttk.Label(self.parametersLF, text = "Time", width = 11, anchor = "w")
self.currentLab = ttk.Label(self.parametersLF, text = "Current", width = 10,
anchor = "e")
self.totalLab = ttk.Label(self.parametersLF, text = "Total", width = 10,
anchor = "e")
self.saveWhatLab = ttk.Label(self.saveImagesLF, text = "Save")
self.saveWhichFilesLab = ttk.Label(self.saveImagesLF, text = "File(s)")
# scales
self.timeSc = ttk.Scale(self, orient = HORIZONTAL, from_ = 0, to = 100,
variable = self.curTime, command = self.changedTime)
self.speedSc = ttk.Scale(self.speedScaleFrame, orient = VERTICAL, from_ = 50, to = 0.5,
variable = self.speed, command = self.changedSpeed)
# entry
self.speedEntry = ttk.Entry(self.speedScaleFrame, textvariable = self.speed, width = 4,
justify = "right", state = "readonly")
# graph
self.graph = eval(self.graphTypeVar.get())
self.graph.bind("<Button-1>", self.graphClick)
self.graph.bind("<Button-3>", self.graphPopUp)
# combobox
self.saveWhatCombo = ttk.Combobox(self.saveImagesLF, textvariable = self.saveWhatVar,
justify = "center", width = 15, state = "readonly")
self.saveWhatCombo["values"] = ("arena frame", "room frame", "both frames",
"graph", "all")
self.saveWhichFilesCombo = ttk.Combobox(self.saveImagesLF, textvariable =
self.saveWhichFilesVar, justify = "center",
width = 15, state = "readonly")
self.saveWhichFilesCombo["values"] = ("current", "tagged", "untagged", "all")
# adding to grid
self.controlsLF.grid(column = 0, row = 3, columnspan = 2, sticky = (N, W), pady = 2,
padx = 2)
self.graphLF.grid(column = 0, row = 5, columnspan = 1, pady = 2, padx = 2, sticky = (N, W))
self.fileStorageFrame.grid(column = 8, row = 0, pady = 2, padx = 3, sticky = (N, E))
self.fileFrame.grid(column = 8, row = 1, rowspan = 4, padx = 3, sticky = (N, S))
self.parametersLF.grid(column = 3, row = 3, columnspan = 4, sticky = (E, W))
self.timeLabFrame.grid(column = 1, row = 5, columnspan = 4, sticky = (N, W),
pady = 2, padx = 2)
self.timeFrame.grid(column = 0, row = 0)
self.arenaFrame.grid(column = 0, row = 0, rowspan = 2, columnspan = 3,
sticky = (N, S, W), padx = 2, pady = 2)
self.roomFrame.grid(column = 3, row = 0, rowspan = 2, columnspan = 4,
sticky = (N, S, E), padx = 2, pady = 2)
self.speedScaleFrame.grid(column = 7, row = 0, rowspan = 2, sticky = (N, S), padx = 4,
pady = 2)
self.optionsLF.grid(column = 5, row = 5, columnspan = 2, sticky = (N), padx = 2)
self.saveImagesLF.grid(column = 8, row = 5, sticky = (S), pady = 5)
self.arenaCanv.grid(column = 0, row = 0, padx = 1, pady = 1)
self.roomCanv.grid(column = 0, row = 0, padx = 1, pady = 1)
self.graph.grid(column = 0, row = 4, columnspan = 7, padx = 2, pady = 5)
self.playBut.grid(column = 0, row = 0, sticky = (N, S), padx = 2, pady = 2)
self.pauseBut.grid(column = 1, row = 0, sticky = (N, S), padx = 2, pady = 2)
self.stopBut.grid(column = 2, row = 0, sticky = (N, S), padx = 2, pady = 2)
self.saveBut.grid(column = 2, row = 2, sticky = E)
self.optionBut.grid(column = 0, row = 2, sticky = W, columnspan = 2)
self.removeReflections.grid(column = 1, row = 0, padx = 3, pady = 2, sticky = (N, W))
self.showTail.grid(column = 1, row = 2, padx = 3, pady = 2, sticky = (N, W))
if m.files == "pair":
self.showShocks.grid(column = 1, row = 1, padx = 3, pady = 2, sticky = (N, W))
self.showAnimation.grid(column = 0, row = 0, padx = 2, pady = 1, sticky = (N, W))
self.showTrack.grid(column = 0, row = 1, padx = 2, pady = 1, sticky = (N, W))
self.speedEntry.grid(column = 0, row = 1)
self.statusBar.grid(column = 0, row = 6, columnspan = 9, sticky = (S, E, W), padx = 2,
pady = 4)
self.distance.grid(column = 1, row = 1, sticky = E)
self.entrances.grid(column = 1, row = 2, sticky = E)
self.timePar.grid(column = 1, row = 4, sticky = E, padx = 2)
self.selectedP.grid(column = 1, row = 3, sticky = E)
self.totDistance.grid(column = 2, row = 1, sticky = E, padx = 4)
self.totEntrances.grid(column = 2, row = 2, sticky = E, padx = 4)
self.totSelectedP.grid(column = 2, row = 3, sticky = E, padx = 4)
self.distanceLab.grid(column = 0, row = 1, sticky = E)
self.entrancesLab.grid(column = 0, row = 2, sticky = E)
self.selectedPLab.grid(column = 0, row = 3, sticky = E)
self.timeLab.grid(column = 0, row = 4, sticky = E)
self.currentLab.grid(column = 1, row = 0, sticky = E, padx = 2, pady = 2)
self.totalLab.grid(column = 2, row = 0, sticky = E, padx = 4, pady = 2)
self.saveWhatLab.grid(column = 0, row = 0, padx = 2, sticky = E)
self.saveWhichFilesLab.grid(column = 0, row = 1, padx = 2, sticky = E)
self.timeSc.grid(column = 0, row = 2, columnspan = 7, sticky = (E, W), pady = 4, padx = 2)
self.speedSc.grid(column = 0, row = 0, sticky = (N, S, E, W), pady = 3)
self.saveWhatCombo.grid(column = 1, row = 0, columnspan = 2)
self.saveWhichFilesCombo.grid(column = 1, row = 1, columnspan = 2)
# statusBar binding
self.statusBar.bind("<Double-1>", self._statusBarDoubleclick)
# parametersLF popUp menu bindings
self.parametersLF.bind("<Button-3>", self.parametersPopUp)
for child in self.parametersLF.winfo_children():
child.bind("<Button-3>", self.parametersPopUp)
# checkbuttons for graph
for number, gType in enumerate(getGraphTypes()):
ttk.Radiobutton(self.graphLF, text = gType[0], variable = self.graphTypeVar,
command = self.changedGraph, value = gType[1])\
.grid(row = number, pady = 1, padx = 1, sticky = (W))
# bindings
self.bind("<Down>", lambda e: self.fileFrame.nextFun())
self.bind("<Right>", lambda e: self.fileFrame.nextFun())
self.bind("<Left>", lambda e: self.fileFrame.previousFun())
self.bind("<Up>", lambda e: self.fileFrame.previousFun())
self.bind("<x>", lambda e: self.playBut.invoke())
self.bind("<c>", lambda e: self.pauseBut.invoke())
self.bind("<v>", lambda e: self.stopBut.invoke())
for child in self.controlsLF.winfo_children():
child.bind("<x>", lambda e: self.playBut.invoke())
child.bind("<c>", lambda e: self.pauseBut.invoke())
child.bind("<v>", lambda e: self.stopBut.invoke())
def imagesOptions(self):
ImagesOptions(self)
def saveImages(self):
"saves images for selected files"
files = self._returnSelectedFiles()
if len(files) > 1:
progress = ProgressWindow(self, len(files), text = "saved")
elif len(files) == 1 and files[0]:
self.root.config(cursor = m.wait)
self.root.update()
else:
self.status.set("No file selected.")
self.bell()
return
problems = 0
for filename in files:
#try:
if filename in self.fileStorage.pairedfiles:
cm = m.CL(filename, self.fileStorage.pairedfiles[filename])
else:
cm = m.CL(filename, "auto")
if self.removeReflectionsVar.get():
cm.removeReflections(points = self.fileStorage.reflections.get(filename, None))
svgSave(cm, filename, self.saveWhatVar.get(), self)
#except Exception:
# problems += 1
if len(files) > 1:
progress.addOne()
if len(files) > 1:
progress.destroy()
if problems > 1:
self.status.set("{} problems occured!".format(problems))
self.bell()
elif problems == 1:
self.status.set("One problem occured!")
self.bell()
else:
self.status.set("Images were saved.")
else:
if problems:
self.status.set("A problem occured!")
self.bell()
else:
self.status.set("Image was saved.")
self.root.config(cursor = "")
def _returnSelectedFiles(self):
"returns files that are chosen for creating of images"
which = self.saveWhichFilesVar.get()
files = []
if which == "all":
files.extend(self.fileStorage.arenafiles)
elif which == "tagged":
files.extend(self.fileStorage.tagged)
elif which == "untagged":
files.extend([file for file in self.fileStorage if file not in
self.fileStorage.tagged])
elif which == "current":
files = [self.fileFrame.selected]
return files
def toggleAnimation(self):
"called when checkbutton for showing tracks is toggled"
if self.initialized:
self.initializeFile(self.fileFrame.selected, new = False, timeReset = False)
if not self.showTrackVar.get():
self.changedTime(value = self.curTime.get(), unit = "0-100")
def toggleReflections(self):
"called when reflections removal is changed - just refreshes canvases"
if self.initialized:
if self.removeReflectionsVar.get():
self.cm.removeReflections(points = self.fileStorage.reflections.get(self.cm.nameA,
None))
self.initializeFile(self.fileFrame.selected, new = False, timeReset = False)
else:
self.initializeFile(self.fileFrame.selected, new = True, timeReset = False)
if not self.showTrackVar.get():
self.changedTime(value = self.curTime.get(), unit = "0-100")
def toggleShocks(self):
"called when checkbutton for showing shocks is toggled"
if self.initialized and self.showTrackVar.get():
self.initializeFile(self.fileFrame.selected, new = False, timeReset = False)
def toggleTail(self):
"called when checkbutton for showing tail is toggled"
if self.initialized and not self.showTrackVar.get():
if self.showTailVar.get():
self.initializeFile(self.fileFrame.selected, new = False, timeReset = False)
self.changedTime(value = self.curTime.get(), unit = "0-100")
else:
self.arenaCanv.delete("trailA")
self.roomCanv.delete("trailR")
def playFun(self):
"starts animation"
self.timeFrame.changeState("disabled")
self.showTrack["state"] = "disabled"
self.saveBut.state(["disabled"])
self.animate = "" # status of the animation - '', 'pause', 'stop'
prevTime = time()
while not self.animate:
# finds current time
nowTime = time()
difTime = (nowTime - prevTime) * 1000
playTime = difTime * self.speed.get() + (
self.curTime.get() * (self.maxTime - self.minTime) / 100) + self.minTime
if playTime > self.maxTime:
self.curTime.set(100)
self.timeVar.set(self._timeFormat(self.maxTime))
self.animate = "stop"
self.timeFrame.changeState("!disabled")
self.showTrack["state"] = "!disabled"
self.saveBut.state(["!disabled"])
break
self.changedTime(playTime, unit = "ms")
prevTime = nowTime
def changedTime(self, value, unit = "0-100"):
"called when time is changed"
if not self.initialized or self.minTime > self.maxTime or self.maxTime < self.minTime:
return
if unit == "ms":
value = value
else:
value = ((float(value) * (self.maxTime - self.minTime)) / 100) + self.minTime
curLine = self._updateDefaultParameters(value)
self._updateSelectedParameter(value)
if self.showTailVar.get():
self._createTail(curLine)
Ax, Ay = curLine[self.cm.indices]
Ax *= self.scale
Ay *= self.scale
if m.mode == "CM":
Rx, Ry = curLine[2:4]
Rx *= self.scale
Ry *= self.scale
self.roomCanv.coords("ratR", (Rx + 16, Ry + 16, Rx + 24, Ry + 24))
self.roomCanv.lift("ratR")
elif m.mode == "RA":
Rx, Ry = curLine[2:4]
Rx *= self.scale
Ry *= self.scale
self.arenaCanv.coords("robotA", (Rx + 12, Ry + 12, Rx + 28, Ry + 28))
self.roomCanv.coords("ratR", ((Ax - Rx)/2 + 146, (Ay - Ry)/2 + 146,
(Ax - Rx)/2 + 154, (Ay - Ry)/2 + 154))
r = self.cm.sectorRadius * self.scale
self.arenaCanv.coords("shockZoneA", (Rx + 20 - r, Ry + 20 - r, Rx + 20 + r, Ry + 20 + r))
self.arenaCanv.lift("robotA")
self.arenaCanv.lift("shockZoneA")
self.roomCanv.lift("ratR")
self.arenaCanv.coords("ratA", (Ax + 16, Ay + 16, Ax + 24, Ay + 24))
self.arenaCanv.lift("ratA")
self._setShockColor(curLine)
self.graph.changedTime(value)
# changes position of a time scale
if value < self.maxTime:
self.curTime.set(((value - self.minTime) * 100) / (self.maxTime - self.minTime))
self.timeVar.set(self._timeFormat(value))
else:
self.curTime.set(100)
self.timeVar.set(self._timeFormat(self.maxTime))
self.update()
def _updateDefaultParameters(self, time):
indices = self.cm.indices
x0, y0 = self.cm.data[0][indices]
dist = 0
entrances = 0
prev = 0
skip = 25 # z options
start = self.cm.findStart(self.minTime / 60000)
for row, content in enumerate(self.cm.data[start:]):
if content[1] < time:
# distance
if row % skip == 0:
x1, y1 = content[indices]
diff = ((x1 - x0)**2 + (y1 - y0)**2)**0.5
dist += diff
x0, y0 = x1, y1
# entrances
if content[5] != 2 and prev != 2:
continue
elif content[5] != 2 and prev == 2:
if content[5] == 0:
prev = 0
continue
elif content[5] == 2 and prev == 2:
continue
elif content[5] == 2 and prev != 2:
entrances += 1
prev = 2
continue
else:
curLine = content
break
dist = dist / (self.cm.trackerResolution * 100) # conversion from pixels to metres
self.distanceVar.set("{:.1f}".format(dist))
self.entrancesVar.set(entrances)
return curLine
def _updateSelectedParameter(self, time):
if self.selectedParameter.get():
time /= 60000
startTime = self.minTime / 60000
try:
self.selectedPVar.set(self.computeSelected(self.cm, startTime, time))
except Exception:
self.selectedPVar.set("-")
def _createTail(self, curLine):
self.arenaCanv.delete("trailA")
self.roomCanv.delete("trailR")
if curLine[0] >= 10:
end = curLine[0]
adjust = 150 - self.cm.radius * self.scale
start = end - 500 if end > 500 else 0
start = round(start*2, -1) // 2
if m.mode == "CM":
trail = [tuple(content[2:4] + content[7:9]) for content in
self.cm.data[start:end:5]]
arena = []
room = []
for rx, ry, ax, ay in trail:
arena.append((ax*self.scale + adjust, ay*self.scale + adjust))
room.append((rx*self.scale + adjust, ry*self.scale + adjust))
arena.append((curLine[7] + adjust, curLine[8] + adjust))
room.append((curLine[2] + adjust, curLine[3] + adjust))
self.arenaCanv.create_line((arena), fill = "blue", width = 2, tag = "trailA")
self.roomCanv.create_line((room), fill = "blue", width = 2, tag = "trailR")
elif m.mode == "RA":
self.arenaCanv.delete("trailRobot")
trail = [tuple(content[2:4] + content[7:9]) for content in
self.cm.data[start:end:5]]
ratA = []
ratR = []
robotA = []
for rx, ry, ax, ay in trail:
ratA.append((ax*self.scale + adjust, ay*self.scale + adjust))
robotA.append((rx*self.scale + adjust, ry*self.scale + adjust))
ratR.append(((ax - rx)*self.scale / 2 + 150,
(ay - ry)*self.scale / 2 + 150))
ratA.append((curLine[7]*self.scale + adjust, curLine[8]*self.scale + adjust))
robotA.append((curLine[2]*self.scale + adjust, curLine[3]*self.scale + adjust))
ratR.append(((curLine[7] - curLine[2])*self.scale / 2 + 150,
(curLine[8] - curLine[3])*self.scale / 2 + 150))
self.arenaCanv.create_line((ratA), fill = "blue", width = 2, tag = "trailA")
self.roomCanv.create_line((ratR), fill = "blue", width = 2, tag = "trailR")
self.arenaCanv.create_line((robotA), fill = "green", width = 2, tag = "trailRobot")
else:
trail = [tuple(content[2:4]) for content in self.cm.data[start:end:5]]
arena = []
for ax, ay in trail:
arena.append((ax*self.scale + adjust, ay*self.scale + adjust))
arena.append((curLine[2] + adjust, curLine[3] + adjust))
self.arenaCanv.create_line((arena), fill = "blue", width = 2, tag = "trailA")
def _setShockColor(self, curLine):
if (m.mode == "CM" and curLine[6] > 0) or (m.mode == "MWM" and curLine[-1] > 0):
self.roomCanv.itemconfigure("ratR", fill = "red", outline = "red")
self.shock = True
elif m.mode == "RA":
if curLine[6] > 0:
self.arenaCanv.itemconfigure("ratA", fill = "red", outline = "red")
self.roomCanv.itemconfigure("ratR", fill = "red", outline = "red")
self.shock = True
else:
self.arenaCanv.itemconfigure("ratA", fill = "black", outline = "black")
self.roomCanv.itemconfigure("ratR", fill = "black", outline = "black")
self.shock = False
else:
self.roomCanv.itemconfigure("ratR", fill = "black", outline = "black")
self.shock = False
def setTime(self):
"called when maximum or minimum time is set"
if self.initialized:
nowTime = (self.curTime.get() / 100) * (self.maxTime - self.minTime) + self.minTime
self.initializeFile(self.fileFrame.selected, new = False)
if self.minTime <= nowTime <= self.maxTime:
self.graph.changedTime(nowTime)
self.changedTime(nowTime, unit = "ms")
elif self.minTime > nowTime:
self.graph.changedTime(self.minTime)
self.changedTime(self.minTime, unit = "ms")
else:
self.graph.changedTime(self.maxTime)
self.changedTime(self.maxTime, unit = "ms")
def pauseFun(self):
"pauses and unpauses animation"
if self.animate == "pause":
self.timeFrame.changeState("disabled")
self.showTrack["state"] = "disabled"
self.saveBut.state(["disabled"])
self.playFun()
else:
self.animate = "pause"
self.timeFrame.changeState("!disabled")
self.showTrack["state"] = "!disabled"
self.saveBut.state(["!disabled"])
def stopFun(self):
"stops animation"
self.animate = "stop"
self.changedTime(0)
self.timeFrame.changeState("!disabled")
self.showTrack["state"] = "!disabled"
self.saveBut.state(["!disabled"])
def initializeFile(self, filename, new = True, timeReset = True):
"initializes canvases, graph, etc."
if new:
successful = self._loadData(filename)
if not successful:
return
self.scale = 130 / self.cm.radius
self.shock = False
self.arenaCanv.delete("all")
self.roomCanv.delete("all")
if m.mode != "OF":
self.arenaCanv.create_oval(20, 20, 280, 280, outline = "black",
width = 2, tags = "arenaAF")
self.roomCanv.create_oval(20, 20, 280, 280, outline = "black",
width = 2, tags = "arenaRF")
else:
self.arenaCanv.create_rectangle(20, 20, 280, 280, outline = "black",
width = 2, tags = "arenaAF")
self.roomCanv.create_rectangle(20, 20, 280, 280, outline = "black",
width = 2, tags = "arenaRF")
if self.cm.centerAngle or m.mode == "RA":
self._createShockSector()
self.maxTime = min([self.cm.data[-1][1], eval(self.timeFrame.timeVar.get()) * 60000])
self.minTime = max([self.cm.data[0][1], eval(self.timeFrame.startTimeVar.get()) * 60000])
if self.showTrackVar.get() or m.files != "pair":
self._drawTrack()
if not self.showTrackVar.get() or m.files != "pair":
self._initializeAnimation()
self._setParameterDisplays(timeReset)
self.graph.delete("all")
if timeReset:
self.graph.CM_loaded(self.cm, minTime = self.minTime, maxTime = self.maxTime)
else:
self.graph.CM_loaded(self.cm, minTime = self.minTime, maxTime = self.maxTime,
initTime = (self.curTime.get() * (self.maxTime - self.minTime) /
100 + self.minTime))
self._changeButtonStatuses()
self._showComment(filename)
self.initialized = True
if new and timeReset:
self.curTime.set(0)
self.timeVar.set(self._timeFormat(self.minTime))
def _showComment(self, filename):
if self.fileStorage.comments[filename]:
comment = self.fileStorage.comments[filename].replace("\n", "\t")
if len(comment) > 150:
comment = comment[:149] + "(...)"
self.status.set(comment)
else:
self.status.set("")
def _statusBarDoubleclick(self, e):
"opens comment window after doubleclick to status bar"
if self.fileFrame.selected:
Comment(self.fileFrame, self.fileFrame.selected, True)
def _loadData(self, filename):
try:
if filename in self.fileStorage.pairedfiles:
self.cm = m.CL(filename, self.fileStorage.pairedfiles[filename])
else:
self.cm = m.CL(filename, "auto")
if self.removeReflectionsVar.get():
self.cm.removeReflections(points = self.fileStorage.reflections.get(filename,
None))
except Exception as e:
if optionGet("Developer", False, 'bool', True):
print(e)
self.status.set("File failed to load!")
self.bell()
return False
else:
return True
def _createShockSector(self):
if m.mode == "CM":
self.angle = self.cm.centerAngle
self.width = self.cm.width
a1 = radians(self.angle - (self.width / 2))
a2 = radians(self.angle + (self.width / 2))
Sx1, Sy1 = 150 + (cos(a1) * 130), 150 - (sin(a1) * 130)
Sx2, Sy2 = 150 + (cos(a2) * 130), 150 - (sin(a2) * 130)
self.roomCanv.create_line((Sx1, Sy1, 150, 150, Sx2, Sy2), fill = "red", width = 2,\
tags = "shockZone")
elif m.mode == "MWM":
x = self.cm.platformX * self.scale + 20
y = self.cm.platformY * self.scale + 20
r = self.cm.platformRadius * self.scale
self.arenaCanv.create_oval(x - r, y - r, x + r, y + r, outline = "red",
width = 2, tags = "platformAF")
self.roomCanv.create_oval(x - r, y - r, x + r, y + r, outline = "red",
width = 2, tags = "platformRF")
elif m.mode == "RA":
r = self.cm.sectorRadius * self.scale / 2
self.roomCanv.create_oval(150 - r, 150 - r, 150 + r, 150 + r, outline = "red",
width = 2, tags = "shockZoneR")
self.roomCanv.create_oval(142, 142, 158, 158, outline = "green3", fill = "green3",
width = 2, tags = "robotR")
def _drawTrack(self):
if m.mode == "CM" or m.mode == "RA":
data = [line[2:4] + line[6:9] for line in self.cm.data if
self.minTime <= line[1] <= self.maxTime]
arena = []
room = []
prev = [-100, -100, 0, -100, -100]
last = 0
for count, line in enumerate(data):
if abs(line[0] - prev[0]) + abs(line[1] - prev[1]) > 2 or count - last == 25 or\
line[2] > 0:
room.append(line[0:2])
arena.append(line[3:5])
last = count
prev = line
if m.mode == "CM":
self.roomCanv.create_line(([i * self.scale + 20 for line in room for i in line]),
fill = "black", width = 2)
else:
self.arenaCanv.create_line(([i * self.scale + 20 for line in room for i in line]),
fill = "green", width = 2)
self.roomCanv.create_line(([(i[0] - i[1]) * self.scale / 2 + 150 for line in
zip(arena, room) for i in zip(line[0], line[1])]),
fill = "black", width = 2)
self.arenaCanv.create_line(([i * self.scale + 20 for line in arena for i in line]),
fill = "black", width = 2)
if self.showShocksVar.get():
indices = slice(2,4) if m.mode == "CM" else slice(7,9)
fun = self.roomCanv.create_oval if m.mode == "CM" else self.arenaCanv.create_oval
shocks = [line[indices] for count, line in enumerate(self.cm.data) if
self.minTime <= line[1] <= self.maxTime and line[6] > 0 and
self.cm.data[count - 1][6] <= 0]
for shock in shocks:
fun(shock[0]*self.scale + 16, shock[1]*self.scale + 16,
shock[0]*self.scale + 24, shock[1]*self.scale + 24,
outline = "red", width = 3)
else:
data = [line[2:4] for line in self.cm.data if self.minTime <= line[1] <= self.maxTime]
points = []
prev = [-100, -100, 0, -100, -100]
last = 0
for count, line in enumerate(data):
if abs(line[0] - prev[0]) + abs(line[1] - prev[1]) > 2 or count - last == 10:
points.append(line)
last = count
prev = line
self.roomCanv.create_line(([item*self.scale + 20 for line in points for item in line]),
fill = "black", width = 2)
def _initializeAnimation(self):
# initial position
for line in self.cm.data:
if line[1] < self.minTime:
continue
else:
curLine = line
break
else:
self.status.set("Invalid start time set.")
self.bell()
return
# makes 'the rat' (i.e. two black points)
if m.files == "pair":
Rx, Ry = curLine[2:4]
Rx *= self.scale
Ry *= self.scale
Ax, Ay = curLine[self.cm.indices]
Ax *= self.scale
Ay *= self.scale
if self.showTailVar.get():
self.arenaCanv.create_line((Ax + 19, Ay + 19, Ax + 21, Ay + 21), fill = "blue",
width = 2, tag = "trailA")
if m.mode == "CM":
if self.showTailVar.get():
self.roomCanv.create_line((Rx + 19, Ry + 19, Rx + 21, Ry + 21),
fill = "blue", width = 2, tag = "trailR")
self.roomCanv.create_oval(Rx + 16, Ry + 16, Rx + 24, Ry + 24,
fill = "black", tags = "ratR")
elif m.mode == "RA":
self.roomCanv.create_oval((Ax - Rx)/2 + 146, (Ay - Ry)/2 + 146,
(Ax - Rx)/2 + 154, (Ay - Ry)/2 + 154,
fill = "black", tags = "ratR")
self.arenaCanv.create_oval(Rx + 12, Ry + 12, Rx + 28, Ry + 28, outline = "green3",
fill = "green3", tags = "robotA")
r = self.cm.sectorRadius * self.scale
self.arenaCanv.create_oval(Rx + 20 - r, Ry + 20 - r, Rx + 20 + r, Ry + 20 + r,
outline = "red", tags = "shockZoneA", width = 2)
self.arenaCanv.create_oval(Ax + 16, Ay + 16, Ax + 24, Ay + 24,
fill = "black", tags = "ratA")
self._setShockColor(curLine)
def _setParameterDisplays(self, timeReset):
self.totDistanceVar.set(self.cm.getDistance(time = self.maxTime / 60000,
startTime = self.minTime / 60000))
self.totEntrancesVar.set(self.cm.getEntrances(time = self.maxTime / 60000,
startTime = self.minTime / 60000))
if self.selectedParameter.get():
time = self.maxTime / 60000
startTime = self.minTime / 60000
self.totSelectedPVar.set(self.computeSelected(self.cm, startTime, time))
else:
self.totSelectedPVar.set("-")
if timeReset:
self.distanceVar.set("0.0")
self.entrancesVar.set("0")
self.selectedPVar.set("-")
def _changeButtonStatuses(self):
if not self.showTrackVar.get():
self.playBut.state(["!disabled"])
self.pauseBut.state(["!disabled"])
self.stopBut.state(["!disabled"])
self.timeSc.state(["!disabled"])
else:
self.playBut.state(["disabled"])
self.pauseBut.state(["disabled"])
self.stopBut.state(["disabled"])
self.timeSc.state(["disabled"])
def changedGraph(self):
"called when the graph is changed"
self.graph.delete("all")
drawnParameter = self.graph.drawnParameter
self.graph = eval(self.graphTypeVar.get())
self.graph.drawnParameter = drawnParameter
self.graph.grid(column = 0, row = 4, columnspan = 7, padx = 2, pady = 5)
self.graph.bind("<Button-1>", self.graphClick)
self.graph.bind("<Button-3>", self.graphPopUp)
if self.initialized:
self.graph.CM_loaded(self.cm, minTime = self.minTime, maxTime = self.maxTime,
initTime = ((self.curTime.get() * (self.maxTime - self.minTime) /
100) + self.minTime))
def graphClick(self, event):
"changes time when the graph is clicked upon"
if self.initialized and not self.showTrackVar.get():
self.graph.coords("timeMeasure", (event.x, 0, event.x, self.graph.height))
time = (event.x * 100) / self.graph.width
if time > 100:
time = 100
self.changedTime(time)
def graphPopUp(self, event):
"shows menu for selection of parameter to be shown in graph"
if not self.initialized:
return
menu = Menu(self, tearoff = 0)
parameters = {"CM": ("periodicity", "mobility", "immobility", "entrances", "shocks",
"bad points", "thigmotaxis", "strategies"),
"MWM": ("mobility", "immobility", "bad points", "thigmotaxis", "passes"),
"OF": ("mobility", "immobility", "bad points", "thigmotaxis"),
"RA": ("mobility", "immobility", "entrances", "shocks", "bad points",
"thigmotaxis")}
parameters["CMSF"] = parameters["CM"]
for parameter in parameters[m.mode]:
menu.add_radiobutton(label = "Show {}".format(parameter),
variable = self.graphParameter, value = parameter,
command = lambda: self.graph.drawParameter(
self.cm, self.graphParameter.get()))
menu.add_separator()
menu.add_radiobutton(label = "Don't show anything",
variable = self.graphParameter, value = "nothing",
command = lambda: self.graph.drawParameter(self.cm, ""))
menu.post(event.x_root, event.y_root)
def parametersPopUp(self, event):
"shows menu for selection of parameters to be shown in parameters frame"
menu = Menu(self, tearoff = 0)
notAvailable = ["Total distance", "Entrances", "Time in sectors", "Time in quadrants",
"Strategies"]
available = ["Bad points", "Outside points", "Reflections"]
menu.add_radiobutton(label = "Don't show anything",
variable = self.selectedParameter, value = "nothing",
command = self._changedSelectedParameter)
menu.add_separator()
for name, par in m.parameters.items():
if par.group not in ["info", "custom"] and name not in notAvailable or name in available:
menu.add_radiobutton(label = 'Show {}'.format(name.lower()),
variable = self.selectedParameter, value = name,
command = self._changedSelectedParameter)
menu.post(event.x_root, event.y_root)
def _changedSelectedParameter(self):
"called when selected parameter is changed"
if self.selectedParameter.get() == "nothing":
self.selectedParameter.set("")
self.totSelectedPVar.set("-")
self.selectedPVar.set("-")
self.note.unbind_widget(self.selectedPLab)
else:
selected = self.selectedParameter.get()
name = selected
if len(name) > 13:
mapping = {"Maximum": "Max.",
"Mean": "M",
"Time": "t.",
"time": "t.",
"Circular": "Circ.",
"variance": "var.",
"points": "pts."
}
for key, value in mapping.items():
name = name.replace(key, value)
if len(name) > 13:
name = name[:10] + "..."
self.selectedPLab["text"] = name
def createSelectedFun(method, **options):
def selectedFun(obj, start, stop):
return getattr(obj, method)(startTime = start, time = stop, **options)
return selectedFun
par = m.parameters[selected]
options = {name: optionGet(*option[0]) for name, option in par.options.items()}
self.computeSelected = createSelectedFun(par.method, **options)
if self.initialized:
time = self.maxTime / 60000
startTime = self.minTime / 60000
self.totSelectedPVar.set(self.computeSelected(self.cm, startTime, time))
nowTime = (self.curTime.get() / 100) * (self.maxTime - self.minTime) + self.minTime
time = nowTime / 60000
try:
self.selectedPVar.set(self.computeSelected(self.cm, startTime, time))
except Exception:
self.selectedPVar.set("-")
def unDraw(self):
"clears canvases, graph etc."
if self.initialized:
self.arenaCanv.delete("all")
self.roomCanv.delete("all")
self.graph.delete("all")
self.initialized = False
self.playBut.state(["disabled"])
self.pauseBut.state(["disabled"])
self.stopBut.state(["disabled"])
self.fileFrame.previousBut.state(["disabled"])
self.fileFrame.nextBut.state(["disabled"])
self.fileFrame.tagBut.state(["disabled"])
self.saveBut.state(["disabled"])
def checkProcessing(self):
"called when content of fileStorage is changed"
if self.fileStorage.arenafiles or self.fileStorage.wrongfiles:
self.fileStorageFrame.removeFiles.state(["!disabled"])
else:
self.fileStorageFrame.removeFiles.state(["disabled"])
self.fileStorageFrame.chosenVar.set(len(self.fileStorage))
self.fileStorageFrame.nonMatchingVar.set(len(self.fileStorage.wrongfiles))
self.fileFrame.files = [] + self.fileStorage.arenafiles
if not self.fileFrame.files or not self.fileFrame.selected in self.fileFrame.files:
self.unDraw()
if self.fileFrame.files:
self.fileFrame.previousBut.state(["!disabled"])
self.fileFrame.nextBut.state(["!disabled"])
self.fileFrame.tagBut.state(["!disabled"])
self.saveBut.state(["!disabled"])
if not self.fileFrame.selected in self.fileFrame.files:
self.fileFrame.selected = None
else:
self.fileFrame.selected = None
self.setTime()
self.fileFrame.drawTree(self.fileFrame.selected)
def initialize(self):
"""initializes the canvases etc. if files were added to fileStorage and file from
fileStorage is not initialized"""
if self.fileFrame.files:
if not self.fileFrame.selected in self.fileFrame.files:
self.initializeFile(self.fileFrame.files[0])
self.fileFrame.selected = self.fileFrame.files[0]
def _timeFormat(self, time):
"formats time from miliseconds to minutes:seconds"
minutes = time // 60000
seconds = (time % 60000) // 1000
return "{:d}:{:0>2d}".format(int(minutes), int(seconds))
def changedSpeed(self, value):
"rounds speed set by scale"
if eval(value) <= 1.0:
self.speed.set(round(self.speed.get(), 1))
elif eval(value) < 2.0:
self.speed.set(round(self.speed.get() * 5, 0) / 5)
elif eval(value) < 5.0:
self.speed.set(round(self.speed.get() * 2, 0) / 2)
elif eval(value) < 10.0:
self.speed.set(round(self.speed.get(), 0))
else:
self.speed.set(round(self.speed.get() * 5, -1) / 5)
class FileFrame(ttk.Frame):
"displays files in ShowTracks class"
def __init__(self, root):
super().__init__(root)
self.rowconfigure(0, weight = 1)
self.root = root
self.fileStorage = self.root.fileStorage
self.index = 0 # which file is selected when the FileTree is initialized
self.files = []
self.selected = None
# tree
self.treeFrame = ttk.Frame(self)
self.treeFrame.grid(column = 0, row = 0, columnspan = 3, sticky = (N, S, E, W))
self.treeFrame.rowconfigure(0, weight = 1)
self.treeFrame.columnconfigure(0, weight = 1)
self.tree = ttk.Treeview(self.treeFrame, selectmode = "browse", height = 20)
self.tree.grid(column = 0, row = 0, sticky = (N, S, E, W))
columns = ("tag")
self.tree["columns"] = columns
self.tree.column("#0", width = 250, anchor = "w")
self.tree.heading("#0", text = "File", command = self.orderByNames)
self.tree.column("tag", width = 40, anchor = "center")
self.tree.heading("tag", text = "Tag", command = self.orderByTag)
self.scrollbar = ttk.Scrollbar(self.treeFrame, orient = VERTICAL,
command = self.tree.yview)
self.scrollbar.grid(column = 1, row = 0, sticky = (N, S, E))
self.tree.configure(yscrollcommand = self.scrollbar.set)
self.tree.bind("<1>", lambda e: self.click(e))
self.tree.bind("<3>", lambda e: self.popUp(e))
self.tree.bind("<Double-1>", lambda e: self.doubleclick(e))
self.tree.tag_configure("comment", background = commentColor())
# previous and next buttons
self.fileLabFrame = ttk.Labelframe(self, text = "File")
self.fileLabFrame.grid(column = 0, row = 1, columnspan = 3, sticky = (N, W, E), pady = 2)
self.fileLabFrame.columnconfigure(2, weight = 1)
self.previousBut = ttk.Button(self.fileLabFrame, text = "Previous",
command = self.previousFun, state = "disabled")
self.previousBut.grid(column = 0, row = 0, padx = 2)
self.nextBut = ttk.Button(self.fileLabFrame, text = "Next", command = self.nextFun,
state = "disabled")
self.nextBut.grid(column = 1, row = 0, padx = 2)
# tag buttons
self.tagBut = ttk.Button(self.fileLabFrame, text = "Tag file", command = self.tagFun,
state = "disabled")
self.tagBut.grid(column = 3, row = 0, padx = 2)
# bindings
for child in self.winfo_children():
for grandchild in child.winfo_children():
grandchild.bind("<Down>", lambda e: self.nextFun())
grandchild.bind("<Right>", lambda e: self.nextFun())
grandchild.bind("<Left>", lambda e: self.previousFun())
grandchild.bind("<Up>", lambda e: self.previousFun())
def tagFun(self, index = "none"):
"tags the selected file"
selected = self.files[eval(self.tree.selection()[0])]
if index != "none":
self.index = index
else:
self.tagBut["text"] = "Remove tag"
self.tagBut["command"] = self.untagFun
self.fileStorage.tag(self.files[self.index])
self.drawTree(selected = selected)
def untagFun(self, index = "none"):
"untags the selected file"
selected = self.files[eval(self.tree.selection()[0])]
if index != "none":
self.index = index
else:
self.tagBut["text"] = "Tag file"
self.tagBut["command"] = self.tagFun
self.fileStorage.tagged.remove(self.files[self.index])
self.drawTree(selected = selected)
def checkTag(self):
"checks the state of tag button"
if self.files[self.index] in self.fileStorage.tagged:
command = self.untagFun
text = "Remove tag"
else:
command = self.tagFun
text = "Tag file"
self.tagBut["text"] = text
self.tagBut["command"] = command
def click(self, event):
"called when item in tree is clicked"
item = self.tree.identify("item", event.x, event.y)
if item:
self.tree.selection_set(item)
self.index = int(item)
self.checkTag()
self.selected = self.files[self.index]
self.root.initializeFile(self.files[self.index])
if not self.root.animate:
self.root.stopFun()
self.root.playFun()
def doubleclick(self, event):
"called when item in tree is doubleclicked"
item = self.tree.identify("item", event.x, event.y)
file = self.files[int(item)]
if file == self.selected:
Comment(self, file, True)
def previousFun(self):
"shows previous file"
if self.index != 0 and self.files:
self.prevIndex = self.index
self.index -= 1
self.tree.selection_set(str(self.index))
self.tree.see(str(self.index))
self.selected = self.files[self.index]
self.checkTag()
self.root.initializeFile(self.files[self.index])
if not self.root.animate:
self.root.stopFun()
self.root.playFun()
def nextFun(self):
"shows next file"
if self.index != len(self.files) - 1 and self.files:
self.prevIndex = self.index
self.index += 1
self.tree.selection_set(str(self.index))
self.tree.see(str(self.index))
self.selected = self.files[self.index]
self.checkTag()
self.root.initializeFile(self.files[self.index])
if not self.root.animate:
self.root.stopFun()
self.root.playFun()
def orderByNames(self):
"orders files by names"
if self.files:
self.files.sort()
self.drawTree(selected = self.selected)
def orderByTag(self):
"order files by tags"
if self.files:
self.files.sort(key = lambda i: (i in self.fileStorage.tagged), reverse = True)
self.drawTree(selected = self.selected)
def drawTree(self, selected = None):
"initializes (or refreshes) tree"
for child in self.tree.get_children():
self.tree.delete(child)
for count, file in enumerate(self.files):
tag = "x" if file in self.fileStorage.tagged else ""
values = (tag)
comment = "comment" if self.fileStorage.comments[file] else "withoutComment"
self.tree.insert("", "end", str(count), text = returnName(file, self.files),
values = values, tag = comment)
if selected:
self.index = self.files.index(selected)
elif self.files:
self.index = 0
if self.files:
self.tree.selection_set(str(self.index))
self.tree.see(str(self.index))
def refresh(self):
"refreshes the tree after adding a comment"
self.root._showComment(self.selected)
self.drawTree(selected = self.selected)
def _showTrack(self, file):
"opens ShowTracks; accessed by right click on file"
ShowTracks(self, self.fileStorage.arenafiles, file)
def popUp(self, event):
"called when tree item is clicked on"
item = self.tree.identify("item", event.x, event.y)
menu = Menu(self, tearoff = 0)
file = self.files[int(item)]
if item:
correct = True if file == self.selected else False
menu.add_command(label = "Add comment", command = lambda: Comment(self, file, correct))
if file in self.fileStorage.tagged:
menu.add_command(label = "Remove tag", command = lambda: self.untagFun(index =
int(item)))
else:
menu.add_command(label = "Add tag", command = lambda: self.tagFun(index =
int(item)))
menu.add_command(label = "Show track", command = lambda: self._showTrack(file))
menu.post(event.x_root, event.y_root)