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.' |             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
								
								
								
								
							
							
						
						
									
										102
									
								
								main.py
								
								
								
								
							|  | @ -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): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue