Filechooser UI should now have smaller buttons; Added enzyme activity zones & legend; added legend for data lines; default view renders a graph; graph now updates properly after clearing data and after selecting a reference profile

This commit is contained in:
Kienan Stewart 2015-03-21 14:07:17 -04:00
parent c7264ded31
commit f328a6dae6
2 changed files with 84 additions and 22 deletions

View File

@ -12,6 +12,7 @@
text: 'Continuing will clear the graph and turn off logging to file.' text: 'Continuing will clear the graph and turn off logging to file.'
BoxLayout: BoxLayout:
orientation: 'horizontal' orientation: 'horizontal'
size_hint_y: 0.1
Button: Button:
id: cancel_button id: cancel_button
text: 'Cancel' text: 'Cancel'
@ -70,11 +71,10 @@
text: 'Select a reference temperature profile (csv)' text: 'Select a reference temperature profile (csv)'
size_hint_y: 0.1 size_hint_y: 0.1
FileChooserListView: FileChooserListView:
size_hint_y: 0.8
id: filechooser id: filechooser
filters: ['*.csv'] filters: ['*.csv']
BoxLayout: BoxLayout:
size_hiny_y: 0.1 size_hint_y: 0.1
orientation: 'horizontal' orientation: 'horizontal'
Button: Button:
text: 'Cancel' text: 'Cancel'

102
main.py
View File

@ -8,7 +8,7 @@ from kivy.core.image.img_pygame import ImageLoaderPygame
import kivy.graphics.texture import kivy.graphics.texture
from kivy.graphics.texture import Texture from kivy.graphics.texture import Texture
from kivy.logger import Logger from kivy.logger import Logger
from kivy.properties import DictProperty, ListProperty, NumericProperty, ObjectProperty, StringProperty from kivy.properties import BooleanProperty, DictProperty, ListProperty, NumericProperty, ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown from kivy.uix.dropdown import DropDown
@ -23,6 +23,8 @@ import kivy.lang
import matplotlib import matplotlib
matplotlib.use('Agg') matplotlib.use('Agg')
import matplotlib.image import matplotlib.image
import matplotlib.lines
import matplotlib.patches
import matplotlib.pyplot import matplotlib.pyplot
import numpy import numpy
import os import os
@ -366,6 +368,21 @@ class PlotWidget(Image):
image_output_file = StringProperty('') image_output_file = StringProperty('')
raw_data_output_file = StringProperty('') raw_data_output_file = StringProperty('')
reference_profile_file = StringProperty('') reference_profile_file = StringProperty('')
# @TODO Include a way to change these properties.
show_betaglucan = BooleanProperty(True)
show_protease = BooleanProperty(True)
show_betaamylase = BooleanProperty(True)
show_alphaamylase = BooleanProperty(True)
# Common data that we should only instantiate once
# Source : http://www.howtobrew.com/section3/chapter14-1.html
# accessed on march 21, 2015
# x_vertices = ((x, y), width, height, color, legend_string)
betaglucan_vertices = ((0, 35), 12000, 10, '#57ff14', 'Betaglucanase')
protease_vertices = ((0,45), 12000, 10, '#f3ff14', 'Protease / Peptidase')
betaamylase_vertices = ((0,55), 12000, 10.6, '#ffc014', 'Beta Amylase')
alphaamylase_vertices = ((0, 67.7), 12000, 4.5, '#ff6614', 'Alpha Amylase')
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(PlotWidget, self).__init__(**kwargs) super(PlotWidget, self).__init__(**kwargs)
@ -381,6 +398,34 @@ class PlotWidget(Image):
self.image_data = None self.image_data = None
self._image_raw_data = None self._image_raw_data = None
self.reference_profile = None self.reference_profile = None
self.do_update()
def update_patches(self, tmax = None):
items = [
(self.show_betaglucan, self.betaglucan_vertices),
(self.show_protease, self.protease_vertices),
(self.show_betaamylase, self.betaamylase_vertices),
(self.show_alphaamylase, self.alphaamylase_vertices)
]
legend_handles = []
legend_strings = []
for show, config in items:
color = '0.5' # default color.
if show:
origin, width, height, color, name = config
if tmax is not None:
width = tmax
p = self.plot_axes.add_patch(
matplotlib.patches.Rectangle(origin, width, height,
fc = color, visible = True,
fill = True)
#matplotlib.patches.Polygon(vertices, facecolor = color)
)
legend_handles.append(p)
legend_strings.append(name)
self.figure.legend(legend_handles, legend_strings, 'upper left',
title = 'Enzyme Activity Zones')
def clear_data(self): def clear_data(self):
@ -390,6 +435,7 @@ class PlotWidget(Image):
self.texture = None self.texture = None
self.image_output_file = '' self.image_output_file = ''
self.raw_data_output_file = '' self.raw_data_output_file = ''
self.do_update()
def do_update(self): def do_update(self):
@ -417,30 +463,44 @@ class PlotWidget(Image):
def to_image_data(self): def to_image_data(self):
if not self.data.any(): # Add in polygons for hilightning particular temperature ranges of interest.
return plot_args = []
#Logger.debug('self.data: ' + str(self.data)) t = None
#Logger.debug('self.data shape: ' + str(self.data.shape)) time_max = 60
data = numpy.copy(self.data) temp_max = 80
data = data.transpose() handles = []
#Logger.debug('transpoed data: ' + str(data)) if self.data.any():
#Logger.debug('transposed data shape: ' + str(data.shape)) data = numpy.copy(self.data)
t, temperature = numpy.split(data, 2, axis = 0) data = data.transpose()
reference_data = None t, temperature = numpy.split(data, 2, axis = 0)
if t[0][-1] > time_max:
time_max = t[0][-1]
if numpy.nanmax(temperature[0]) > temp_max:
temp_max = numpy.nanmax(temperature[0])
plot_args.extend((t[0], temperature[0], 'b'))
handles.append('Recorded Temperature Profile')
if self.reference_profile is not None and self.reference_profile.any(): if self.reference_profile is not None and self.reference_profile.any():
reference_data = numpy.copy(self.reference_profile) reference_data = numpy.copy(self.reference_profile)
reference_data = reference_data.transpose() reference_data = reference_data.transpose()
t2, reference_temperature = numpy.split(reference_data, 2, axis = 0) t2, reference_temperature = numpy.split(reference_data, 2, axis = 0)
# Add t[0] to all reference points to make it fit on the current graph # Add t[0] to all reference points to make it fit on the current graph
t2 = numpy.add(t2, t[0][0]) if t:
#Logger.debug('PlotWidget: reference profile t: ' + str(t2)) t2 = numpy.add(t2, t[0][0])
#Logger.debug('PlotWidget: reference profile temp: ' + str(reference_temperature)) if t2[0][-1] > time_max:
#Logger.debug('PlotWidget: time: ' + str(t[0])) time_max = t2[0][-1]
#Logger.debug('PlotWidget: temperature: ' + str(temperature[0])) if numpy.nanmax(reference_temperature[0]) > temp_max:
if reference_data is not None and reference_data.any(): temp_max = numpy.nanmax(reference_temperature[0])
self.plot_axes.plot(t[0], temperature[0], 'b', t2[0], reference_temperature[0], 'r') plot_args.extend((t2[0], reference_temperature[0], 'r'))
else: handles.append('Reference Temperature Profile')
self.plot_axes.plot(t[0], temperature[0], 'b') lines = self.plot_axes.plot(*plot_args)
# Patches must be changed after the axes are plotted.
self.update_patches(time_max)
self.plot_axes.legend(lines, handles, 'upper right', title = 'Temperature Profiles')
if not lines:
# Set a default in case no data is plotted. This may allow patches to show,
# and it will look a little nicer before recording starts.
self.plot_axes.set_xlim(0, time_max)
self.plot_axes.set_ylim(0, temp_max)
image_data = StringIO.StringIO() image_data = StringIO.StringIO()
self.figure.savefig(image_data, format = 'png') self.figure.savefig(image_data, format = 'png')
image_data.seek(0) image_data.seek(0)
@ -487,6 +547,7 @@ class PlotWidget(Image):
def on_reference_profile_file(self, *args, **kwargs): def on_reference_profile_file(self, *args, **kwargs):
Logger.debug('PlotWidget: on_reference_profile_file ' + str(self.reference_profile_file)) Logger.debug('PlotWidget: on_reference_profile_file ' + str(self.reference_profile_file))
self.load_reference_profile() self.load_reference_profile()
self.do_update()
def load_reference_profile(self): def load_reference_profile(self):
@ -496,6 +557,7 @@ class PlotWidget(Image):
delimiter=',') delimiter=',')
Logger.debug('PlotWidget: reference_profile: ' + str(self.reference_profile)) Logger.debug('PlotWidget: reference_profile: ' + str(self.reference_profile))
class YesNoModalView(Popup): class YesNoModalView(Popup):
def process(self, result, *args, **kwargs): def process(self, result, *args, **kwargs):