Source code for src.gui.graphing.embeddable

"""A panel for containing multiple graphs which can be embedded in a frame.
"""

import logging
from PIL import Image
import Queue
import tempfile
import threading
import time
import wx.lib.newevent

from src.core.configuration import c
from src.core.graph import AbstractGraphManager
from src.tools.general import gridArrangement

__all__ = ['EmbeddableGraphManager', 'GraphThread']
log = logging.getLogger('transport')

ASCHECK = wx.ID_ANY
FIG_WIDTH = 380
FIG_HEIGHT = 280

UPDATE_DELAY = c.getGraphDelay()

[docs]class EmbeddableGraphManager(AbstractGraphManager): """A class to manage the graphs for an experiment. Parameters ---------- parentFrame : wx.Window The frame or panel which contains the panels for the individual graphs. graphPanels : list of GraphPanel A list containing all of the `GraphPanel` objects which this `GraphManager` will manage. """ def __init__(self, parentFrame, graphPanels): """Instantiate a graph manager.""" super(EmbeddableGraphManager, self).__init__(parentFrame) self.parentFrame = parentFrame self.graphPanels = graphPanels self.graphs = None self.graphThread = None self.figures = None
[docs] def setGraphs(self, graphs): """Set the graphs to manage. Parameters ---------- graphs : list of Graph A list of `Graph` objects which will send their data to this manager to be put into the appropriate panels. """ self.graphs = graphs for graph, panel in zip(graphs, self.graphPanels): currQueue = Queue.Queue() graph.setDataQueue(currQueue) panel.setDataQueue(currQueue) self.graphThread = GraphThread(self.parentFrame, self.graphPanels)
[docs] def abort(self, timeout=10): """Stop the thread for updating the graphs.""" log.info('Ending graph manager.') self.graphThread.abort() self.graphThread.join(timeout) self.figures = self.graphThread.getFigures() self.graphThread = None
[docs] def start(self): """Start the thread for updating the graphs.""" self.graphThread.start()
[docs] def getGraphs(self): """Return the figures for the graphs so that they can be saved.""" return self.graphThread.getFigures()
[docs] def saveGraphs(self, filename): """Save the graphs to a file. Resize all graphs to the appropriate size. Then arrange them into a grid and save that grid to the file specified by `filename`. Parameters ---------- filename : str A string pointing to which the graph images should be saved. Note that all graphs go to the same file. `filename` should be a complete path name, including an extension. """ if __debug__: log.debug('Attempting to save graph to file %s', filename) output = [] for figure in self.figures: fn = tempfile.mktemp('.png') output.append((figure, fn)) #_saveSub(output, filename) wx.CallAfter(_saveSub, output, filename)
[docs]class GraphThread(threading.Thread): """A thread to update the graphs at regular intervals. Parameters ---------- parentFrame : wx.Window The frame or panel which contains the panels for the individual graphs. graphPanels : list of GraphPanel A list containing all of the `GraphPanel` objects which this thread will manage. """ def __init__(self, parentFrame, graphPanels): super(GraphThread, self).__init__() self.setDaemon(True) (self.UpdateEvent, self.EVT_UPDATE_GRAPH) = wx.lib.newevent.NewEvent() self.graphPanels = graphPanels for graph in self.graphPanels: graph.Bind(self.EVT_UPDATE_GRAPH, graph.onUpdate) self.keepGoing = True self.running = False self.figure = None if __debug__: log.debug('Graph frame shown.')
[docs] def abort(self): """Signal that the thread should stop.""" self.keepGoing = False
def run(self): log.info('Beginning execution of graph thread.') self.running = True while self.keepGoing: evt = self.UpdateEvent() for graph in self.graphPanels: wx.PostEvent(graph, evt) time.sleep(UPDATE_DELAY) for graph in self.graphPanels: wx.PostEvent(graph, evt) log.info('Ending execution of graph thread.') self.running = False def getFigures(self): figures = [] for graph in self.graphPanels: figures.append(graph.fig) return figures def isRunning(self): return self.running
def _saveSub(data, outputFilename): filenames = [] for fig, filename in data: filenames.append(filename) fig.savefig(filename) rows, cols = gridArrangement(len(filenames)) outputImage = Image.new("RGBA", ((FIG_WIDTH+20)*cols, (FIG_HEIGHT+20)*rows)) index = 0 for row in range(rows): for col in range(cols): if index >= len(filenames): break curr = Image.open(filenames[index]) curr = curr.resize((FIG_WIDTH, FIG_HEIGHT), Image.ANTIALIAS) x = (10 + FIG_WIDTH)*col + 10 y = (10 + FIG_HEIGHT)*row + 10 outputImage.paste(curr, (x, y)) index += 1 outputImage.save(outputFilename)