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:
parent
c7264ded31
commit
f328a6dae6
4
main.kv
4
main.kv
|
@ -12,6 +12,7 @@
|
|||
text: 'Continuing will clear the graph and turn off logging to file.'
|
||||
BoxLayout:
|
||||
orientation: 'horizontal'
|
||||
size_hint_y: 0.1
|
||||
Button:
|
||||
id: cancel_button
|
||||
text: 'Cancel'
|
||||
|
@ -70,11 +71,10 @@
|
|||
text: 'Select a reference temperature profile (csv)'
|
||||
size_hint_y: 0.1
|
||||
FileChooserListView:
|
||||
size_hint_y: 0.8
|
||||
id: filechooser
|
||||
filters: ['*.csv']
|
||||
BoxLayout:
|
||||
size_hiny_y: 0.1
|
||||
size_hint_y: 0.1
|
||||
orientation: 'horizontal'
|
||||
Button:
|
||||
text: 'Cancel'
|
||||
|
|
102
main.py
102
main.py
|
@ -8,7 +8,7 @@ from kivy.core.image.img_pygame import ImageLoaderPygame
|
|||
import kivy.graphics.texture
|
||||
from kivy.graphics.texture import Texture
|
||||
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.button import Button
|
||||
from kivy.uix.dropdown import DropDown
|
||||
|
@ -23,6 +23,8 @@ import kivy.lang
|
|||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.image
|
||||
import matplotlib.lines
|
||||
import matplotlib.patches
|
||||
import matplotlib.pyplot
|
||||
import numpy
|
||||
import os
|
||||
|
@ -366,6 +368,21 @@ class PlotWidget(Image):
|
|||
image_output_file = StringProperty('')
|
||||
raw_data_output_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):
|
||||
super(PlotWidget, self).__init__(**kwargs)
|
||||
|
@ -381,6 +398,34 @@ class PlotWidget(Image):
|
|||
self.image_data = None
|
||||
self._image_raw_data = 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):
|
||||
|
@ -390,6 +435,7 @@ class PlotWidget(Image):
|
|||
self.texture = None
|
||||
self.image_output_file = ''
|
||||
self.raw_data_output_file = ''
|
||||
self.do_update()
|
||||
|
||||
|
||||
def do_update(self):
|
||||
|
@ -417,30 +463,44 @@ class PlotWidget(Image):
|
|||
|
||||
|
||||
def to_image_data(self):
|
||||
if not self.data.any():
|
||||
return
|
||||
#Logger.debug('self.data: ' + str(self.data))
|
||||
#Logger.debug('self.data shape: ' + str(self.data.shape))
|
||||
data = numpy.copy(self.data)
|
||||
data = data.transpose()
|
||||
#Logger.debug('transpoed data: ' + str(data))
|
||||
#Logger.debug('transposed data shape: ' + str(data.shape))
|
||||
t, temperature = numpy.split(data, 2, axis = 0)
|
||||
reference_data = None
|
||||
# Add in polygons for hilightning particular temperature ranges of interest.
|
||||
plot_args = []
|
||||
t = None
|
||||
time_max = 60
|
||||
temp_max = 80
|
||||
handles = []
|
||||
if self.data.any():
|
||||
data = numpy.copy(self.data)
|
||||
data = data.transpose()
|
||||
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():
|
||||
reference_data = numpy.copy(self.reference_profile)
|
||||
reference_data = reference_data.transpose()
|
||||
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
|
||||
t2 = numpy.add(t2, t[0][0])
|
||||
#Logger.debug('PlotWidget: reference profile t: ' + str(t2))
|
||||
#Logger.debug('PlotWidget: reference profile temp: ' + str(reference_temperature))
|
||||
#Logger.debug('PlotWidget: time: ' + str(t[0]))
|
||||
#Logger.debug('PlotWidget: temperature: ' + str(temperature[0]))
|
||||
if reference_data is not None and reference_data.any():
|
||||
self.plot_axes.plot(t[0], temperature[0], 'b', t2[0], reference_temperature[0], 'r')
|
||||
else:
|
||||
self.plot_axes.plot(t[0], temperature[0], 'b')
|
||||
if t:
|
||||
t2 = numpy.add(t2, t[0][0])
|
||||
if t2[0][-1] > time_max:
|
||||
time_max = t2[0][-1]
|
||||
if numpy.nanmax(reference_temperature[0]) > temp_max:
|
||||
temp_max = numpy.nanmax(reference_temperature[0])
|
||||
plot_args.extend((t2[0], reference_temperature[0], 'r'))
|
||||
handles.append('Reference Temperature Profile')
|
||||
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()
|
||||
self.figure.savefig(image_data, format = 'png')
|
||||
image_data.seek(0)
|
||||
|
@ -487,6 +547,7 @@ class PlotWidget(Image):
|
|||
def on_reference_profile_file(self, *args, **kwargs):
|
||||
Logger.debug('PlotWidget: on_reference_profile_file ' + str(self.reference_profile_file))
|
||||
self.load_reference_profile()
|
||||
self.do_update()
|
||||
|
||||
|
||||
def load_reference_profile(self):
|
||||
|
@ -496,6 +557,7 @@ class PlotWidget(Image):
|
|||
delimiter=',')
|
||||
Logger.debug('PlotWidget: reference_profile: ' + str(self.reference_profile))
|
||||
|
||||
|
||||
class YesNoModalView(Popup):
|
||||
|
||||
def process(self, result, *args, **kwargs):
|
||||
|
|
Loading…
Reference in New Issue