diff --git a/Stuff/Modules/explorer.py b/Stuff/Modules/explorer.py index bee6f05..e9ca5ff 100644 --- a/Stuff/Modules/explorer.py +++ b/Stuff/Modules/explorer.py @@ -30,7 +30,7 @@ from showtracks import ShowTracks from filestorage import FileStorageFrame from commonframes import TimeFrame, returnName -from image import svgSave +from image import svgSave, ImagesOptions from processor import ProgressWindow from optionget import optionGet from graphs import getGraphTypes, Graphs, SvgGraph, SpeedGraph, DistanceFromCenterGraph @@ -125,6 +125,8 @@ def __init__(self, root): 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", @@ -226,7 +228,8 @@ def __init__(self, root): 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 = 1, row = 2, sticky = E) + 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)) @@ -259,8 +262,8 @@ def __init__(self, root): 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) - self.saveWhichFilesCombo.grid(column = 1, row = 1) + self.saveWhatCombo.grid(column = 1, row = 0, columnspan = 2) + self.saveWhichFilesCombo.grid(column = 1, row = 1, columnspan = 2) # statusBar binding self.statusBar.bind("", self._statusBarDoubleclick) @@ -290,6 +293,10 @@ def __init__(self, root): child.bind("", lambda e: self.stopBut.invoke()) + def imagesOptions(self): + ImagesOptions(self) + + def saveImages(self): "saves images for selected files" files = self._returnSelectedFiles() @@ -781,8 +788,8 @@ def _drawTrack(self): fill = "black", width = 2) if self.showShocksVar.get(): - indices = slice(2,4) if m.mode == "CA" else slice(7,9) - fun = self.roomCanv.create_oval if m.mode == "CA" else self.arenaCanv.create_oval + 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 @@ -892,7 +899,7 @@ def changedGraph(self): 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, sticky = (E, W)) + self.graph.grid(column = 0, row = 4, columnspan = 7, padx = 2, pady = 5) self.graph.bind("", self.graphClick) self.graph.bind("", self.graphPopUp) if self.initialized: diff --git a/Stuff/Modules/graphs.py b/Stuff/Modules/graphs.py index 7cd51a7..90550d1 100644 --- a/Stuff/Modules/graphs.py +++ b/Stuff/Modules/graphs.py @@ -92,25 +92,26 @@ def drawParameter(self, cm, parameter, purpose = "graph"): self.drawnParameter = parameter if parameter == "periodicity": - self.drawPeriods(cm.getPeriodicity(forGraph = True, time = self.maxTime / 60000, - startTime = self.minTime / 60000, - minSpeed = optionGet('MinSpeedPeriodicity', - 10, ['int', 'float']), - skip = optionGet('SkipPeriodicity', 12, ['int']), - smooth = optionGet('SmoothPeriodicity', 2, ['int']), - minTime = optionGet('MinTimePeriodicity', 9, - ['int', 'float', 'list']))) + periodicity = cm.getPeriodicity(forGraph = True, time = self.maxTime / 60000, + startTime = self.minTime / 60000, + minSpeed = optionGet('MinSpeedPeriodicity', + 10, ['int', 'float']), + skip = optionGet('SkipPeriodicity', 12, ['int']), + smooth = optionGet('SmoothPeriodicity', 2, ['int']), + minTime = optionGet('MinTimePeriodicity', 9, + ['int', 'float', 'list'])) + return self.drawPeriods(periodicity) elif parameter == "immobility": - self.drawPeriods(cm.getMaxTimeOfImmobility(forGraph = True, - time = self.maxTime / 60000, - startTime = self.minTime / 60000, - minSpeed = optionGet( - 'MinSpeedMaxTimeImmobility', 10, - ['int', 'float']), - skip = optionGet('SkipMaxTimeImmobility', - 12, 'int'), - smooth = optionGet( - 'SmoothMaxTimeImmobility', 2, 'int'))) + immobility = cm.getMaxTimeOfImmobility(forGraph = True, + time = self.maxTime / 60000, + startTime = self.minTime / 60000, + minSpeed = optionGet('MinSpeedMaxTimeImmobility', + 10, ['int', 'float']), + skip = optionGet('SkipMaxTimeImmobility', + 12, 'int'), + smooth = optionGet('SmoothMaxTimeImmobility', + 2, 'int')) + return self.drawPeriods(immobility) elif parameter == "mobility": immobility = cm.getMaxTimeOfImmobility(forGraph = True, time = self.maxTime / 60000, startTime = self.minTime / 60000, @@ -126,7 +127,7 @@ def drawParameter(self, cm, parameter, purpose = "graph"): t1 = times[0] mobility.append((t0, t1)) t0 = times[1] - self.drawPeriods(mobility) + return self.drawPeriods(mobility) elif parameter == "thigmotaxis": percentSize = optionGet("ThigmotaxisPercentSize", 20, ["int", "float"]) start = cm.findStart(self.minTime / 60000) @@ -156,7 +157,7 @@ def distance(line): break if outside: periods.append((t0, self.maxTime)) - self.drawPeriods(periods) + return self.drawPeriods(periods) elif parameter == "shocks": shocks = [] prev = 0 @@ -214,8 +215,8 @@ def distance(line): start = wrong prev = wrong wrongs.append((start, prev)) - self.drawPeriods([(cm.data[wrong[0] - 1][1], cm.data[wrong[1] - 1][1]) for\ - wrong in wrongs]) + bps = [(cm.data[wrong[0] - 1][1], cm.data[wrong[1] - 1][1]) for wrong in wrongs] + return self.drawPeriods(bps) elif parameter == "strategies": rows = optionGet('rowsStrategies', 25, 'int') minSpeed = optionGet('minSpeedStrategies', 10, ['int', 'float']) @@ -232,10 +233,18 @@ def distance(line): "reaction_counterclockwise": "red", "reaction_clockwise": "goldenrod1", "center": "wheat4"} - for strategy, periods in strategies.items(): - self.drawPeriods(periods, color = colors[strategy], width = 240) - self.lower("parameter") - + if purpose == "graph": + for strategy, periods in strategies.items(): + self.drawPeriods(periods, color = colors[strategy], width = 240) + self.lower("parameter") + else: + for strategy, periods in strategies.items(): + if strategy == "immobile": + continue + self.drawPeriods(periods, color = colors[strategy].replace("_", "").rstrip("14"), + width = 240, toReturn = False) + return self.returnPeriods() + def drawPeriods(self, periods, color = "red", width = 3): "draws selected parameter on top of the graph" @@ -296,9 +305,9 @@ def __init__(self, parent, cm, width = 600, height = 120): self.width = width self.drawnParameter = None self.parent = parent + self.periodText = "" self.__class__.__bases__ = (self.__class__.__bases__[1], self.__class__.__bases__[0]) - def __del__(self): self.__class__.__bases__ = (self.__class__.__bases__[1], self.__class__.__bases__[0]) @@ -312,11 +321,13 @@ def saveGraph(self, cm): return self.points, self.maxY, self.furtherText - def drawPeriods(self, periods, color = "red", width = 3): + def drawPeriods(self, periods, color = "red", width = 8, toReturn = True): "draws selected parameter on top of the graph" if not periods: - return + return "" timeSpread = (self.maxTime - self.minTime) + text = "" + line = '\n' for period in periods: if period[0] > self.minTime and period[1] < self.maxTime: begin = period[0] @@ -329,11 +340,17 @@ def drawPeriods(self, periods, color = "red", width = 3): end = self.maxTime else: continue - self.create_line(((begin - self.minTime) * self.width / timeSpread, - 0.03 * self.height, - (end - self.minTime) * self.width / timeSpread, - 0.03 * self.height), - fill = color, width = width, tags = "parameter") + text += line.format((begin - self.minTime) * self.width / timeSpread, + (width/480) * self.height, + (end - self.minTime) * self.width / timeSpread, + color, width/2) + self.periodText += text + if toReturn: + return self.periodText + + + def returnPeriods(self): + return self.periodText def drawTimes(self, times): @@ -345,7 +362,7 @@ def drawTimes(self, times): for time in times: if self.minTime < time < self.maxTime: x = (time - self.minTime) * self.width / timeSpread - text += ''.format(x) + text += '\n'.format(x) return text @@ -421,14 +438,24 @@ def CM_loaded(self, cm, initTime = 0, minTime = 0, maxTime = "max"): self.drawGraph(maxY = self.maxY, valueList = self.points) + def addYticks(self): + at = [] + labels = [] + for i in range((self.maxY // 20) + 1): + at.append(i*20 / self.maxY) + labels.append(str(i*20)) + return at, labels + + + def getYlabel(self): + return "Speed [m/s]" + class DistanceFromCenterGraph(Graphs, SvgGraph): "graph depicting distance from center of arena during the session" - def __init__(self, parent, cm = None, purpose = "graph"): - if purpose == "graph": - Graphs.__init__(self, parent) - else: - SvgGraph.__init__(self, parent, cm) + def __init__(self, parent, cm = None, purpose = "graph", width = 600): + self.primaryParent = Graphs if purpose == "graph" else SvgGraph + self.primaryParent.__init__(self, parent, cm = cm, width = width) def writeFurtherText(self): @@ -445,6 +472,7 @@ def compute(self, cm, smooth = 10): start = cm.findStart(self.minTime / 60000) self.radius = cm.radius + self.resolution = cm.trackerResolution Cx, Cy = cm.centerX, cm.centerY if m.mode == "RA": @@ -473,16 +501,28 @@ def CM_loaded(self, cm, initTime = 0, minTime = 0, maxTime = "max"): self.create_line((0, 10, self.width, 10), fill = "grey") self.drawGraph(maxY = self.maxY, valueList = self.points) - + + + def addYticks(self): + at = [] + labels = [] + maxYcm = self.maxY / self.resolution + for i in range((int(maxYcm) // 20) + 1): + at.append(i*20 / maxYcm) + labels.append(str(i*20)) + return at, labels + + + def getYlabel(self): + return "Distance [cm]" + class DistanceFromPlatformGraph(Graphs, SvgGraph): "graph depicting distance from platform during the MWM session" - def __init__(self, parent, cm = None, purpose = "graph"): - if purpose == "graph": - Graphs.__init__(self, parent) - else: - SvgGraph.__init__(self, parent, cm) + def __init__(self, parent, cm = None, purpose = "graph", width = 600): + self.primaryParent = Graphs if purpose == "graph" else SvgGraph + self.primaryParent.__init__(self, parent, cm = cm, width = width) def writeFurtherText(self): @@ -520,12 +560,13 @@ def CM_loaded(self, cm, initTime = 0, minTime = 0, maxTime = "max"): self.drawGraph(maxY = self.maxY, valueList = self.points) - + class DistanceFromRobotGraph(DistanceFromPlatformGraph): "graph depicting distance from the robot during the RA session" - def __init__(self, parent, cm = None, purpose = "graph"): - super().__init__(parent = parent, cm = cm, purpose = purpose) + def __init__(self, parent, cm = None, purpose = "graph", width = 600): + self.primaryParent = Graphs if purpose == "graph" else SvgGraph + self.primaryParent.__init__(self, parent, cm = cm, width = width) def compute(self, cm, smooth = 10): @@ -537,18 +578,17 @@ def compute(self, cm, smooth = 10): for line in cm.data[start:] if line[1] <= self.maxTime] self.maxY = cm.radius * 2 - + class AngleGraph(Graphs, SvgGraph): "graph depicting angle relative to the center of shock zone during the session" - def __init__(self, parent, cm = None, purpose = "graph"): - if purpose == "graph": - Graphs.__init__(self, parent) - else: - SvgGraph.__init__(self, parent, cm) - self.maxTime = eval(self.parent.timeFrame.timeVar.get()) * 60000 - self.minTime = eval(self.parent.timeFrame.startTimeVar.get()) * 60000 + def __init__(self, parent, cm = None, purpose = "graph", width = 600): + self.primaryParent = Graphs if purpose == "graph" else SvgGraph + self.primaryParent.__init__(self, parent, cm = cm, width = width) + + self.maxTime = eval(self.parent.timeFrame.timeVar.get()) * 60000 + self.minTime = eval(self.parent.timeFrame.startTimeVar.get()) * 60000 def compute(self, cm): @@ -625,7 +665,7 @@ def saveGraph(self, cm): self.compute(cm) wid = cm.width - size = (600, 120) + size = (self.width, self.height) maxX = len(self.angles) - self.crosses text = "" @@ -668,3 +708,12 @@ def _addLine(self, points): text += '" style = "fill:none;stroke:black"/>\n' return text + + def addYticks(self): + at = [0, 0.5, 1] + labels = ["0", "180", "360"] + return at, labels + + + def getYlabel(self): + return "Angle [deg]" diff --git a/Stuff/Modules/image.py b/Stuff/Modules/image.py index 3783bd5..a2ad76b 100644 --- a/Stuff/Modules/image.py +++ b/Stuff/Modules/image.py @@ -18,6 +18,7 @@ """ from math import radians, cos, sin +from tkinter import * import os @@ -27,6 +28,7 @@ from optionget import optionGet from graphs import getGraphTypes, Graphs, SvgGraph, SpeedGraph, DistanceFromCenterGraph from graphs import AngleGraph, DistanceFromPlatformGraph, DistanceFromRobotGraph +from window import placeWindow import mode as m @@ -49,24 +51,58 @@ def svgSave(cm, filename, what, root): file = os.path.splitext(os.path.basename(filename))[0] + "_" + what.replace(" ", "_") + ".svg" svg.save(os.path.join(directory, file)) webbrowser.open_new(os.path.join(directory, file)) # for testing + +class ImagesOptions(Toplevel): + def __init__(self, root): + super().__init__(root) + self.root = root + #placeWindow(self, 598, 208) + self.title = "Images options" + self.grab_set() + self.focus_set() + self.resizable(False, False) + + self.scale = optionGet("LastImageScale", 1, ["int", "float"]) + self.main = optionGet("LastImageScale", "default", "str") + self.xgap = optionGet("LastImageScale", 10, ["int", "float"]) + self.ygap = optionGet("LastImageScale", 10, ["int", "float"]) + self.xlab = optionGet("LastImageScale", "Time", "str") + self.ylab = optionGet("LastImageScale", "default", "str") + self.xticks = optionGet("LastImageScale", True, "bool") + self.yticks = optionGet("LastImageScale", True, "bool") + + self.okBut = ttk.Button(self, text = "Ok", command = self.okFun) + self.okBut.grid(column = 1, row = 1, padx = 3, pady = 4) + self.cancelBut = ttk.Button(self, text = "Cancel", command = self.cancelFun) + self.cancelBut.grid(column = 2, row = 1, padx = 3, pady = 4) + + + def okFun(self): + self.destroy() + + + def cancelFun(self): + self.destroy() + + class SVG(): "represents .svg file - text of the file is stored in self.content" def __init__(self, cm, components, root): self.start = int(root.timeFrame.startTimeVar.get()) self.end = int(root.timeFrame.timeVar.get()) - self.parameter = "shocks" # for testing + self.parameter = root.graphParameter.get() self.cm = cm self.components = components - self.scale = 1 - self.main = "Main" + self.scale = 1.5 + self.main = "default" self.xgap = 10 self.ygap = 10 - self.xlab = "xlab" - self.ylab = "ylab" + self.xlab = "Time" + self.ylab = "default" self.xticks = True self.yticks = True @@ -102,6 +138,7 @@ def __init__(self, cm, components, root): def addComponents(self): if self.main: self.components.append("main") + self.main = self.cm.nameA if self.xgap and "room" in self.components and "arena" in self.components: self.components.append("xgap") if self.ygap and "graph" in self.components and "arena" in self.components: @@ -120,16 +157,16 @@ def computeSize(self): x = [0] * 6 y = [0] * 7 # sizes [(x.size, column, columnspan), (y.size, row)] - sizes = {"main": [(0, 0), (20, 1)], + sizes = {"main": [(0, 0), (25, 1)], "arena": [(300, 3), (300, 2)], "room": [(300, 5), (300, 2)], "graph": [(600, 5, 3), (120, 4)], "xgap": [(self.xgap, 4), (0, 0)], "ygap": [(0, 0), (self.ygap, 3)], "xticks": [(0, 0), (10, 5)], - "yticks": [(10, 2), (0, 0)], + "yticks": [(20, 2), (0, 0)], "xlab": [(0, 0), (15, 6)], - "ylab": [(15, 1), (0, 0)] + "ylab": [(30, 1), (0, 0)] } for component in self.components: xs, ys = sizes[component] @@ -159,10 +196,10 @@ def add(self, new, x, y): def addMain(self): - x = (self.x[4] - self.x[3]) / 2 - main = ('{1}'.format(x, self.main)) - self.add(main, 3, 0) + self.add(main, 2, 0) def addArena(self): @@ -248,11 +285,15 @@ def addBoundary(self): def addGraph(self): - size = (600 + self.xgap, 120) - graph = ('\n'.format(size[0], size[1])) - + size = (self.x[5] - self.x[2], 120) yCoordinates, maxY, furtherText = self.graph.saveGraph(self.cm) + + graph = "" + graph += self.addParameter() + graph += ('\n'.format(size[0], size[1])) + graph += furtherText + points = [] if yCoordinates: length = len(yCoordinates) - 1 @@ -265,15 +306,15 @@ def addGraph(self): graph += ",".join(map(str, pair)) + " " graph += '" style = "fill:none;stroke:black"/>\n' - graph += self.addParameter() - graph += furtherText - self.add(graph, 2, 3) def addParameter(self): - text = self.graph.drawParameter(self.cm, self.parameter, purpose = "svg") - return text + if not self.parameter: + return "" + else: + text = self.graph.drawParameter(self.cm, self.parameter, purpose = "svg") + return text def addXticks(self): @@ -293,29 +334,40 @@ def addXticks(self): text = "" for tick, label in zip(ticks, labels): - x = (tick - self.start)/time * (600 + self.xgap) - text += ('{1}\n'.format(x, label)) - text += ''.format(x, -2, -10) + text += '\n'.format(x, -4, -10) self.add(text, 2, 5) def addYticks(self): - pass + at, labels = self.graph.addYticks() + tick = '\n' + label = '{}\n' + xdif = self.x[2] - self.x[1] + ydif = self.y[3] - self.y[4] + yticks = "" + for y, lab in zip(at, labels): + yticks += tick.format(xdif, y*ydif - ydif, xdif - 6) + yticks += label.format(xdif - 10, y*ydif - ydif + 4, lab) + self.add(yticks, 1, 3) def addXlab(self): - x = (self.x[4] - self.x[3]) / 2 + x = (self.x[5] - self.x[2]) / 2 xlab = ('{1}'.format(x, self.xlab)) - self.add(xlab, 3, 6) + self.add(xlab, 2, 6) def addYlab(self): + if self.ylab == "default": + self.ylab = self.graph.getYlabel() y = (self.y[4] - self.y[3]) / 2 ylab = ('{2}'.format(y, -y+10, self.ylab)) + 'transform="translate({1},{0}) rotate(270)">{2}'.format(y, -y+15, self.ylab)) self.add(ylab, 0, 3) @@ -329,19 +381,8 @@ def save(self, file): - def main(): - from cm import CM - import os - import os.path - svg = SVG(300, 300) - cm = CM(os.path.join(os.getcwd(), "TestingFiles", "09aNO465_Arena.dat")) - svg.drawAAPA(cm, "room", boundary = True, sector = True, shocks = True) - output = os.path.join(os.getcwd(), "TestingFiles", "test.svg") - svg.save(output) - os.startfile(output) - - + pass if __name__ == "__main__": main() diff --git a/Stuff/Modules/options.py b/Stuff/Modules/options.py index 24fda88..732287b 100644 --- a/Stuff/Modules/options.py +++ b/Stuff/Modules/options.py @@ -304,14 +304,16 @@ def __init__(self, root): self.cancelBut = ttk.Button(self, text = "Cancel", command = self.cancelFun) self.cancelBut.grid(column = 2, row = 1, padx = 3, pady = 4) - self.settingsFrame = ttk.Frame(self) - self.settingsFrame.grid(row = 0, column = 0, columnspan = 4, padx = 3, pady = 4, - sticky = (N, W, E)) - self.settingsFrame.columnconfigure(0, weight = 1) + self.settingsFrame2 = ttk.Frame(self) + self.optionFrames = [] mapping = {"degrees": "°", "seconds": "s", "percents": "%"} + numFrames = sum([1 for par in m.parameters.values() if par.options]) + split = numFrames > 7 + + count = 0 for name, par in sorted(m.parameters.items()): if not par.options: continue @@ -327,7 +329,24 @@ def __init__(self, root): text = text.strip() opt = option[0] options.append((text + ": ", (opt.name, opt.default, opt.types), unit)) - self.optionFrames.append(ParameterOptionFrame(self.settingsFrame, name, options)) + if split and count >= (numFrames // 2): + frame = self.settingsFrame2 + else: + frame = self.settingsFrame + self.optionFrames.append(ParameterOptionFrame(frame, name, options)) + count += 1 + + if numFrames > 7: + self.settingsFrame.grid(row = 0, column = 0, columnspan = 2, padx = 3, pady = 4, + sticky = (N, W, E)) + self.settingsFrame2.grid(row = 0, column = 2, columnspan = 2, padx = 3, pady = 4, + sticky = (N, W, E)) + else: + self.settingsFrame.grid(row = 0, column = 0, columnspan = 4, padx = 3, pady = 4, + sticky = (N, W, E)) + + self.settingsFrame.columnconfigure(0, weight = 1) + self.settingsFrame2.columnconfigure(0, weight = 1) for row, frame in enumerate(self.optionFrames): frame.grid(column = 0, row = row, pady = 3, padx = 3, sticky = (W, E))