mirror of
https://github.com/Cian-H/Melter.git
synced 2025-12-22 22:22:00 +00:00
First functioning version of app!
This commit is contained in:
BIN
Fonts/DroidSansMono.ttf
Normal file
BIN
Fonts/DroidSansMono.ttf
Normal file
Binary file not shown.
2
MTPy
2
MTPy
Submodule MTPy updated: bae1dfa134...477ca5c403
378
Main/Main_Desktop.py
Normal file
378
Main/Main_Desktop.py
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
# Kivy module imports
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
from kivy.properties import ObjectProperty
|
||||||
|
from kivy.uix.screenmanager import Screen
|
||||||
|
# Other python module imports
|
||||||
|
from common.MTPy_Modified import MT_Modded as MeltpoolTomography
|
||||||
|
from common.threading_decorators import run_in_thread
|
||||||
|
from types import SimpleNamespace
|
||||||
|
import operator as op
|
||||||
|
from ast import literal_eval
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
|
||||||
|
# Load kv files
|
||||||
|
Builder.load_file("Templates/melter_desktop.kv")
|
||||||
|
|
||||||
|
|
||||||
|
# This class contains the main window code
|
||||||
|
class Main(Screen):
|
||||||
|
# Declare variables to be usable in kivy script
|
||||||
|
mtpy = ObjectProperty(MeltpoolTomography())
|
||||||
|
cache = ObjectProperty(SimpleNamespace())
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Main, self).__init__(*args, **kwargs)
|
||||||
|
# Then, initialize an MTPy object for data processing
|
||||||
|
self.mtpy = MeltpoolTomography(quiet=True)
|
||||||
|
# list of shared choosers to keep the same between tabs
|
||||||
|
shared_io_choosers = ["io_chooser_dataloading",
|
||||||
|
"io_chooser_buildplate",
|
||||||
|
"io_chooser_sampledetection",
|
||||||
|
"io_chooser_persample"]
|
||||||
|
shared_io_choosers = [self.ids[x] for x in shared_io_choosers]
|
||||||
|
# Link progress bars in document to their associated functions
|
||||||
|
self.mtpy.progress_bars["read_layers"] = self.ids.read_layers_progbar
|
||||||
|
self.mtpy.progress_bars["apply_calibration_curve"] = self.ids.cal_curve_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["_layers_to_figures"] = self.ids.layers_to_figures_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["_layers_to_3dplot"] = self.ids.layers_to_3dplot_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["_layers_to_3dplot_interactive"] = self.ids.layers_to_3dplot_interactive_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["samples_to_figures"] = self.ids.samples_to_figures_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["samples_to_3dplot"] = self.ids.samples_to_3dplot_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["samples_to_3dplot_interactive"] = self.ids.samples_to_3dplot_interactive_progbar # noqa
|
||||||
|
self.mtpy.progress_bars["separate_samples"] = self.ids.kmeans_separate_samples_progbar # noqa
|
||||||
|
# self.mtpy.progress_bars["threshold_all_layers"] = self.ids.avgspeed_threshold_progbar # noqa
|
||||||
|
# self.mtpy.progress_bars["threshold_all_layers"] = self.ids.avgtemp_threshold_progbar # noqa
|
||||||
|
# Starting items in cache
|
||||||
|
starting_cache = {"shared_io_choosers": shared_io_choosers,
|
||||||
|
"in_path": "~", # path to input data
|
||||||
|
"out_path": "~", # path to output data
|
||||||
|
"last_loaded_path": False, # path to last loaded
|
||||||
|
"calibration_curve": False, # last cal curve used
|
||||||
|
"static_fileformats": # Allowed static formats
|
||||||
|
("png", "pdf", "ps", "eps", "svg"),
|
||||||
|
"thresh_functions": # Threshold functions available
|
||||||
|
{
|
||||||
|
">": op.gt,
|
||||||
|
"≥": op.ge,
|
||||||
|
"=": op.eq,
|
||||||
|
"≠": op.ne,
|
||||||
|
"≤": op.le,
|
||||||
|
"<": op.lt,
|
||||||
|
},
|
||||||
|
"progress_bars": self.mtpy.progress_bars}
|
||||||
|
|
||||||
|
self.cache = SimpleNamespace(**starting_cache)
|
||||||
|
# Make sure each shared io chooser is aware of others and parent app
|
||||||
|
for chooser in self.cache.shared_io_choosers:
|
||||||
|
chooser.cache.shared_io_choosers = \
|
||||||
|
[x for x in self.cache.shared_io_choosers if x != chooser]
|
||||||
|
chooser.cache.parent_app = self
|
||||||
|
# Next, populate dropdowns
|
||||||
|
# First, the dropdowns for matplotlib filetype options
|
||||||
|
self.ids.layers_to_figures_filetype_dropdown.populate_dropdown(
|
||||||
|
self.cache.static_fileformats)
|
||||||
|
self.ids.avgtemp_thresh_function_dropdown.populate_dropdown(
|
||||||
|
self.cache.thresh_functions.keys())
|
||||||
|
|
||||||
|
# Property returns a string summarising the status of data processing
|
||||||
|
@property
|
||||||
|
def data_status(self):
|
||||||
|
# if data_dict is present, generate string for data_dict info
|
||||||
|
if hasattr(self.mtpy, "data_dict"):
|
||||||
|
data_dict = self.mtpy.data_dict
|
||||||
|
if len(data_dict) > 0:
|
||||||
|
if "layers" not in locals():
|
||||||
|
layers = len(data_dict)
|
||||||
|
points_per_layer = round(sum((points.shape[1] for layer, points
|
||||||
|
in data_dict.items()))
|
||||||
|
/ layers)
|
||||||
|
layers_string = f"Layers: {layers}\nAverage Points Per Layer: {points_per_layer}" # noqa
|
||||||
|
else:
|
||||||
|
return "No data loaded!"
|
||||||
|
# if sample_dict is present, generate string for it
|
||||||
|
else:
|
||||||
|
layers_string = "Layer data not loaded..."
|
||||||
|
|
||||||
|
if hasattr(self.mtpy, "sample_dict"):
|
||||||
|
sample_dict = self.mtpy.sample_dict
|
||||||
|
if "layers" not in locals():
|
||||||
|
layers = len(sample_dict[sample_dict.keys()[0]])
|
||||||
|
if layers_string == "Layer data not loaded...":
|
||||||
|
layers_string += f"Layers: {layers}"
|
||||||
|
num_samples = len(sample_dict)
|
||||||
|
points_per_sample = round(sum((sum(len(points)
|
||||||
|
for layer, points in
|
||||||
|
layer_data.items()) / layers
|
||||||
|
for sample, layer_data in
|
||||||
|
sample_dict.items()))
|
||||||
|
/ num_samples)
|
||||||
|
samples_string = f"Number of Samples: {num_samples}\nAverage Points Per Sample: {points_per_sample}" # noqa
|
||||||
|
else:
|
||||||
|
samples_string = "Samples not separated..."
|
||||||
|
|
||||||
|
# Combine to form overall status string
|
||||||
|
outstring = f"{layers_string}\n{samples_string}"
|
||||||
|
# and add additional info at the end if present
|
||||||
|
if self.cache.calibration_curve:
|
||||||
|
outstring += f"\nCalibration Curve: {self.cache.calibration_curve}" # noqa
|
||||||
|
return outstring
|
||||||
|
|
||||||
|
# Updates data status displayed in data loading tab
|
||||||
|
def update_data_status(self):
|
||||||
|
self.ids.dataloading_display.text = self.data_status
|
||||||
|
|
||||||
|
# Parses text field inputs into **kwargs
|
||||||
|
def parse_kwargs(self, paramstring: str) -> dict:
|
||||||
|
if paramstring == "":
|
||||||
|
return dict()
|
||||||
|
parsed = []
|
||||||
|
neststring = "" # this string keeps track of level and type of nesting
|
||||||
|
prev_split = 0 # keeps track of previous split point
|
||||||
|
# This loop splits string at un-nested commas
|
||||||
|
for i, c in enumerate(paramstring):
|
||||||
|
if c == "," and neststring == "":
|
||||||
|
parsed.append(paramstring[prev_split:i])
|
||||||
|
prev_split = i + 1
|
||||||
|
elif c in ("'", '"'):
|
||||||
|
if len(neststring) > 0:
|
||||||
|
if c == neststring[-1]:
|
||||||
|
neststring = neststring[:-1]
|
||||||
|
else:
|
||||||
|
neststring += c
|
||||||
|
else:
|
||||||
|
neststring += c
|
||||||
|
elif c in ("(", "{", "["):
|
||||||
|
neststring += c
|
||||||
|
elif c in (")", "}", "]"):
|
||||||
|
if (c == ")" and neststring[-1] == "(" or
|
||||||
|
c == "}" and neststring[-1] == "{" or
|
||||||
|
c == "]" and neststring[-1] == "["):
|
||||||
|
neststring = neststring[:-1]
|
||||||
|
parsed.append(paramstring[prev_split:])
|
||||||
|
|
||||||
|
# parse into pairs of keywords and objects
|
||||||
|
parsed = (str.strip(x) for x in parsed)
|
||||||
|
parsed = (x.split("=") for x in parsed)
|
||||||
|
parsed = ((str.strip(y) for y in x) for x in parsed)
|
||||||
|
# Finally, interpret objects in the loop below
|
||||||
|
parsed = {kw: literal_eval(val) for kw, val in parsed}
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
# This function loads input data only if not already loaded
|
||||||
|
@run_in_thread
|
||||||
|
def load_data(self):
|
||||||
|
if self.cache.in_path != self.cache.last_loaded_path:
|
||||||
|
self.mtpy.data_path = self.cache.in_path
|
||||||
|
self.cache.last_loaded_path = self.cache.in_path
|
||||||
|
self.mtpy.read_layers()
|
||||||
|
self.update_data_status()
|
||||||
|
|
||||||
|
# applies calibration curve if has changed
|
||||||
|
# NOTE: relies on eval! Function may be dangerous
|
||||||
|
@run_in_thread
|
||||||
|
def apply_calibration_curve(self):
|
||||||
|
equation = self.ids.calibration_curve.text
|
||||||
|
equation = equation.replace(" ", "")
|
||||||
|
if ((equation != self.cache.calibration_curve) and
|
||||||
|
(equation != "y=x") and
|
||||||
|
(equation[:2] == "y=")):
|
||||||
|
def func(x):
|
||||||
|
return eval(equation[2:])
|
||||||
|
self.mtpy.apply_calibration_curve(func)
|
||||||
|
self.cache.calibration_curve = equation
|
||||||
|
self.update_data_status()
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function layers_to_figures
|
||||||
|
@run_in_thread
|
||||||
|
def layers_to_figures(self):
|
||||||
|
# get filetype and if not allowed replace with default (png)
|
||||||
|
filetype = self.ids.layers_to_figures_filetype_dropdown.text
|
||||||
|
if filetype not in self.cache.static_fileformats:
|
||||||
|
filetype = "png"
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.layers_to_figures_plot_w.active
|
||||||
|
colorbar = self.ids.layers_to_figures_colorbar.active
|
||||||
|
# then parse kwarg params
|
||||||
|
figureparams = self.parse_kwargs(
|
||||||
|
self.ids.layers_to_figures_figureparams.text)
|
||||||
|
scatterparams = self.parse_kwargs(
|
||||||
|
self.ids.layers_to_figures_plotparams.text)
|
||||||
|
self.mtpy.layers_to_figures(self.cache.out_path,
|
||||||
|
filetype=filetype,
|
||||||
|
plot_w=plot_w,
|
||||||
|
colorbar=colorbar,
|
||||||
|
figureparams=figureparams,
|
||||||
|
scatterparams=scatterparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function layers_to_3dplot
|
||||||
|
@run_in_thread
|
||||||
|
def layers_to_3dplot(self):
|
||||||
|
# get filetype and if not allowed replace with default (png)
|
||||||
|
filetype = self.ids.layers_to_3dplot_filetype_dropdown.text
|
||||||
|
if filetype not in self.cache.static_fileformats:
|
||||||
|
filetype = "png"
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.layers_to_3dplot_plot_w.active
|
||||||
|
colorbar = self.ids.layers_to_3dplot_colorbar.active
|
||||||
|
# then parse kwarg params
|
||||||
|
figureparams = self.parse_kwargs(
|
||||||
|
self.ids.layers_to_3dplot_figureparams.text)
|
||||||
|
plotparams = self.parse_kwargs(
|
||||||
|
self.ids.layers_to_3dplot_plotparams.text)
|
||||||
|
self.mtpy.layers_to_3dplot(self.cache.out_path,
|
||||||
|
filetype=filetype,
|
||||||
|
plot_w=plot_w,
|
||||||
|
colorbar=colorbar,
|
||||||
|
figureparams=figureparams,
|
||||||
|
plotparams=plotparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function layers_to_3dplot_interactive
|
||||||
|
@run_in_thread
|
||||||
|
def layers_to_3dplot_interactive(self):
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.layers_to_3dplot_interactive_plot_w.active
|
||||||
|
sliceable = self.ids.layers_to_3dplot_interactive_sliceable.active
|
||||||
|
downsampling = self.ids.layers_to_3dplot_interactive_downsampling.text
|
||||||
|
if downsampling == "":
|
||||||
|
downsampling = 1
|
||||||
|
else:
|
||||||
|
downsampling = int(downsampling)
|
||||||
|
# then parse kwarg params
|
||||||
|
plotparams = self.parse_kwargs(self.ids.layers_to_3dplot_interactive_plotparams.text) # noqa
|
||||||
|
self.mtpy.layers_to_3dplot_interactive(self.cache.out_path,
|
||||||
|
plot_w=plot_w,
|
||||||
|
sliceable=sliceable,
|
||||||
|
downsampling=downsampling,
|
||||||
|
plotparams=plotparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function samples_to_figures
|
||||||
|
@run_in_thread
|
||||||
|
def samples_to_figures(self):
|
||||||
|
# get filetype and if not allowed replace with default (png)
|
||||||
|
filetype = self.ids.samples_to_figures_filetype_dropdown.text
|
||||||
|
if filetype not in self.cache.static_fileformats:
|
||||||
|
filetype = "png"
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.samples_to_figures_plot_w.active
|
||||||
|
colorbar = self.ids.samples_to_figures_colorbar.active
|
||||||
|
# then parse kwarg params
|
||||||
|
figureparams = self.parse_kwargs(
|
||||||
|
self.ids.samples_to_figures_figureparams.text)
|
||||||
|
scatterparams = self.parse_kwargs(
|
||||||
|
self.ids.samples_to_figures_plotparams.text)
|
||||||
|
self.mtpy.samples_to_figures(self.cache.out_path,
|
||||||
|
filetype=filetype,
|
||||||
|
plot_w=plot_w,
|
||||||
|
colorbar=colorbar,
|
||||||
|
figureparams=figureparams,
|
||||||
|
scatterparams=scatterparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function samples_to_3dplot
|
||||||
|
@run_in_thread
|
||||||
|
def samples_to_3dplot(self):
|
||||||
|
# get filetype and if not allowed replace with default (png)
|
||||||
|
filetype = self.ids.samples_to_3dplot_filetype_dropdown.text
|
||||||
|
if filetype not in self.cache.static_fileformats:
|
||||||
|
filetype = "png"
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.samples_to_3dplot_plot_w.active
|
||||||
|
colorbar = self.ids.samples_to_3dplot_colorbar.active
|
||||||
|
# then parse kwarg params
|
||||||
|
figureparams = self.parse_kwargs(
|
||||||
|
self.ids.samples_to_3dplot_figureparams.text)
|
||||||
|
plotparams = self.parse_kwargs(
|
||||||
|
self.ids.samples_to_3dplot_plotparams.text)
|
||||||
|
self.mtpy.samples_to_3dplot(self.cache.out_path,
|
||||||
|
filetype=filetype,
|
||||||
|
plot_w=plot_w,
|
||||||
|
colorbar=colorbar,
|
||||||
|
figureparams=figureparams,
|
||||||
|
plotparams=plotparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy function layers_to_3dplot_interactive
|
||||||
|
@run_in_thread
|
||||||
|
def samples_to_3dplot_interactive(self):
|
||||||
|
# get checkbox parameters
|
||||||
|
plot_w = self.ids.samples_to_3dplot_interactive_plot_w.active
|
||||||
|
sliceable = self.ids.samples_to_3dplot_interactive_sliceable.active
|
||||||
|
downsampling = self.ids.samples_to_3dplot_interactive_downsampling.text
|
||||||
|
if downsampling == "":
|
||||||
|
downsampling = 1
|
||||||
|
else:
|
||||||
|
downsampling = int(downsampling)
|
||||||
|
# then parse kwarg params
|
||||||
|
plotparams = self.parse_kwargs(self.ids.samples_to_3dplot_interactive_plotparams.text) # noqa
|
||||||
|
self.mtpy.samples_to_3dplot_interactive(self.cache.out_path,
|
||||||
|
plot_w=plot_w,
|
||||||
|
sliceable=sliceable,
|
||||||
|
downsampling=downsampling,
|
||||||
|
plotparams=plotparams)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy module to threshold all layers based on speed
|
||||||
|
@run_in_thread
|
||||||
|
def avgspeed_threshold(self):
|
||||||
|
# get input parameters
|
||||||
|
thresh_percent = float(self.ids.avgspeed_thresh_thresh_percent.text)
|
||||||
|
avgof = int(self.ids.avgspeed_thresh_avgof.text)
|
||||||
|
# Link to progress bar (at time of call since this bar is shared)
|
||||||
|
self.mtpy.progress_bars["threshold_all_layers"] = self.ids.avgspeed_threshold_progbar # noqa
|
||||||
|
# then call the function
|
||||||
|
self.mtpy.threshold_all_layers(
|
||||||
|
self.mtpy.avgspeed_threshold,
|
||||||
|
{
|
||||||
|
"threshold_percent": thresh_percent,
|
||||||
|
"avgof": avgof
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# A wrapper function translating application state into a call to the
|
||||||
|
# mtpy module to threshold all layers based on temperature
|
||||||
|
@run_in_thread
|
||||||
|
def avgtemp_threshold(self):
|
||||||
|
# get filetype and if not allowed replace with default (png)
|
||||||
|
thresh_function = self.ids.avgtemp_thresh_function_dropdown.text
|
||||||
|
if thresh_function not in self.cache.thresh_functions.keys():
|
||||||
|
thresh_function = ">"
|
||||||
|
# get threshold percentage
|
||||||
|
thresh_percent = float(self.ids.avgtemp_thresh_thresh_percent.text)
|
||||||
|
# Link to progress bar (at time of call since this bar is shared)
|
||||||
|
self.mtpy.progress_bars["threshold_all_layers"] = self.ids.avgtemp_threshold_progbar # noqa
|
||||||
|
# then call the function
|
||||||
|
self.mtpy.threshold_all_layers(
|
||||||
|
self.mtpy.avgw_threshold,
|
||||||
|
{
|
||||||
|
"threshold_percent": thresh_percent,
|
||||||
|
"comparison_func": self.cache.thresh_functions[thresh_function]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@run_in_thread
|
||||||
|
def separate_samples(self):
|
||||||
|
# get input parameters
|
||||||
|
nsamples = int(self.ids.kmeans_nsamples.text)
|
||||||
|
# if only 0 or 1 samples, no need to separate
|
||||||
|
if nsamples == 0 or nsamples == 1:
|
||||||
|
return
|
||||||
|
console_io_buffer = self.ids.kmeans_separate_console_output.io_buffer
|
||||||
|
# Temporarily unmute mtpy for console output
|
||||||
|
self.mtpy.quiet = False
|
||||||
|
with redirect_stdout(console_io_buffer):
|
||||||
|
self.mtpy.detect_samples(nsamples)
|
||||||
|
print("\nSample detection complete!\n(Separation progress on bar above)") # noqa
|
||||||
|
# Then, remute once finished
|
||||||
|
self.mtpy.quiet = True
|
||||||
|
# Separate samples. Should use progbar so no need for teminal
|
||||||
|
self.mtpy.separate_samples()
|
||||||
|
# Finally, update the status string
|
||||||
|
self.update_data_status()
|
||||||
7
Main/__init__.py
Normal file
7
Main/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
"""
|
||||||
|
This internal "Main" module of the "Melter" project contains a collection of
|
||||||
|
main files for booting Melter GUIs in different environments
|
||||||
|
"""
|
||||||
31
Melter.py
Executable file
31
Melter.py
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
from kivy.app import App
|
||||||
|
# For polymorphism (e.g. phone app) use conditional import below
|
||||||
|
# The code below currently is kind of pointless and "Main_Phone" does not exist
|
||||||
|
# but it demonstrates the plan for polymorphism
|
||||||
|
mode = "desktop"
|
||||||
|
if mode == "desktop":
|
||||||
|
from Main.Main_Desktop import Main
|
||||||
|
elif mode == "phone":
|
||||||
|
from Main.Main_Phone import Main
|
||||||
|
|
||||||
|
|
||||||
|
# Create application class
|
||||||
|
class Melter(App):
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return Main()
|
||||||
|
|
||||||
|
|
||||||
|
dev_mode = False
|
||||||
|
|
||||||
|
|
||||||
|
if (__name__ == "__main__") and dev_mode:
|
||||||
|
# DEBUG
|
||||||
|
test = Melter()
|
||||||
|
test.run()
|
||||||
|
breakpoint()
|
||||||
|
elif __name__ == "__main__":
|
||||||
|
Melter().run()
|
||||||
50
Templates/console_output.py
Normal file
50
Templates/console_output.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from kivy.event import EventDispatcher
|
||||||
|
from kivy.properties import BooleanProperty
|
||||||
|
from kivy.uix.textinput import TextInput
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
# This variation of StringIO communicates back to parent observer
|
||||||
|
class ObservableStringIO(StringIO):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if "observer" in kwargs:
|
||||||
|
self.observer = kwargs.pop("observer")
|
||||||
|
super(ObservableStringIO, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def write(self, *args, **kwargs):
|
||||||
|
# Just need to flip trigger on write. Specific value doesnt matter
|
||||||
|
self.observer.trigger = not self.observer.trigger
|
||||||
|
super(ObservableStringIO, self).write(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# This StringIO wrapper object outputs string from io to target on every write
|
||||||
|
class StringIO_toString_Observer(EventDispatcher):
|
||||||
|
trigger = BooleanProperty()
|
||||||
|
|
||||||
|
def __init__(self, target, **kwargs):
|
||||||
|
self.io_buffer = ObservableStringIO(observer=self)
|
||||||
|
self.trigger = False # <- val doesnt matter as long a bool
|
||||||
|
self.target = target
|
||||||
|
self.target.text = str(self.io_buffer.getvalue())
|
||||||
|
super(StringIO_toString_Observer, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def on_trigger(self, instance, value):
|
||||||
|
self.target.text = str(self.io_buffer.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
# This console output widget can output io streams if redirected to its
|
||||||
|
# io_buffer property
|
||||||
|
class ConsoleOutput(TextInput):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# Define and apply default kwargs
|
||||||
|
defaultkwargs = {"readonly": True,
|
||||||
|
"background_color": (0, 0, 0, 1),
|
||||||
|
"foreground_color": (1, 1, 1, 1)}
|
||||||
|
kwargs = {k: (v if k not in kwargs else kwargs[k])
|
||||||
|
for k, v in defaultkwargs.items()}
|
||||||
|
# Then call super
|
||||||
|
super(ConsoleOutput, self).__init__(*args, **kwargs)
|
||||||
|
# Then add observer and ref to io_buffer for ease-of-use
|
||||||
|
self.observer = StringIO_toString_Observer(self)
|
||||||
|
self.io_buffer = self.observer.io_buffer
|
||||||
52
Templates/dropdown_button.py
Normal file
52
Templates/dropdown_button.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
# Kivy module imports
|
||||||
|
from kivy.uix.button import Button
|
||||||
|
from kivy.uix.dropdown import DropDown
|
||||||
|
|
||||||
|
|
||||||
|
class DropdownButton(Button):
|
||||||
|
def __init__(self, option_list=None, **kwargs):
|
||||||
|
# ensure "text" kwarg isnt present
|
||||||
|
if "test" in kwargs:
|
||||||
|
kwargs.pop("test")
|
||||||
|
|
||||||
|
# Add default args if they're not specifically assigned
|
||||||
|
self.defaultkwargs = \
|
||||||
|
{"background_color": [x*0.75 for x in self.background_color],
|
||||||
|
}
|
||||||
|
|
||||||
|
for keyword, arg in self.defaultkwargs.items():
|
||||||
|
if keyword not in kwargs:
|
||||||
|
kwargs[keyword] = arg
|
||||||
|
|
||||||
|
self.kwargs = kwargs
|
||||||
|
super(DropdownButton, self).__init__(**self.kwargs)
|
||||||
|
|
||||||
|
# Create lambdas for callbacks
|
||||||
|
self.__bind_button = lambda btn: self.dropdown_list.select(btn.text)
|
||||||
|
self.__update_label = lambda instance, x: setattr(self, "text", x)
|
||||||
|
|
||||||
|
if option_list is not None:
|
||||||
|
self.populate_dropdown(option_list)
|
||||||
|
|
||||||
|
def populate_dropdown(self, option_list):
|
||||||
|
kwargs = self.kwargs.copy()
|
||||||
|
kwargs["size_hint_y"] = None
|
||||||
|
if "height" not in kwargs:
|
||||||
|
kwargs["height"] = 50
|
||||||
|
if "__no_builder" in kwargs:
|
||||||
|
kwargs.pop("__no_builder")
|
||||||
|
|
||||||
|
self.dropdown_list = None
|
||||||
|
self.dropdown_list = DropDown()
|
||||||
|
|
||||||
|
for x in option_list:
|
||||||
|
button = Button(text=x, **kwargs)
|
||||||
|
# button = Button(text=x, size_hint_y=None, height=50)
|
||||||
|
button.bind(on_release=self.__bind_button)
|
||||||
|
self.dropdown_list.add_widget(button)
|
||||||
|
|
||||||
|
self.bind(on_release=self.dropdown_list.open)
|
||||||
|
self.dropdown_list.bind(on_select=self.__update_label)
|
||||||
29
Templates/file_chooser_popup.kv
Normal file
29
Templates/file_chooser_popup.kv
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# file_chooser_popup.kv
|
||||||
|
#:kivy 2.0
|
||||||
|
#:import path os.path.expanduser
|
||||||
|
|
||||||
|
<FileChooserPopup>:
|
||||||
|
title: "Choose a data folder"
|
||||||
|
size_hint: .9, .9
|
||||||
|
auto_dismiss: False
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
FileChooser:
|
||||||
|
id: filechooser
|
||||||
|
path: path("~")
|
||||||
|
dirselect: True
|
||||||
|
FileChooserIconLayout
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint: (1, 0.1)
|
||||||
|
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||||
|
spacing: 20
|
||||||
|
Button:
|
||||||
|
text: "Cancel"
|
||||||
|
on_release: root.dismiss()
|
||||||
|
Button:
|
||||||
|
text: "Load"
|
||||||
|
on_release: root.load(filechooser.selection)
|
||||||
|
id: ldbtn
|
||||||
|
disabled: True if filechooser.selection==[] else False
|
||||||
63
Templates/input_output_chooser.kv
Normal file
63
Templates/input_output_chooser.kv
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# input_output_chooser.kv
|
||||||
|
#:kivy 2.0
|
||||||
|
|
||||||
|
# Widget:
|
||||||
|
<InputOutputChooser>:
|
||||||
|
BoxLayout:
|
||||||
|
orientation: "vertical"
|
||||||
|
# First item is the input file directory chooser in stacked layout
|
||||||
|
StackLayout:
|
||||||
|
orientation: "lr-tb"
|
||||||
|
size_hint_y: None
|
||||||
|
size_hint_x: 1.
|
||||||
|
height: 30
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_x: 1.
|
||||||
|
# Containing a description of what the field points to
|
||||||
|
Label:
|
||||||
|
halign: "right"
|
||||||
|
text: "Layer data directory:"
|
||||||
|
size_hint_x: None
|
||||||
|
width: 160
|
||||||
|
# A textbox for filepath entry
|
||||||
|
TextInput:
|
||||||
|
id: in_path
|
||||||
|
readonly: True
|
||||||
|
hint_text: "Read pyrometry data from..."
|
||||||
|
# A button that opens the file chooser popup
|
||||||
|
Button:
|
||||||
|
text: "Choose Folder"
|
||||||
|
size_hint_x: None
|
||||||
|
width: 120
|
||||||
|
on_press: root.open_chooser("in_path")
|
||||||
|
|
||||||
|
# Second item is the output file directory chooser in stacked layout
|
||||||
|
StackLayout:
|
||||||
|
orientation: "lr-tb"
|
||||||
|
size_hint_y: None
|
||||||
|
size_hint_x: 1.
|
||||||
|
height: 30
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
size_hint_x: 1.
|
||||||
|
# Containing a description of what the field points to
|
||||||
|
Label:
|
||||||
|
halign: "right"
|
||||||
|
text: "Output directory:"
|
||||||
|
size_hint_x: None
|
||||||
|
width: 160
|
||||||
|
# A textbox for filepath entry
|
||||||
|
TextInput:
|
||||||
|
id: out_path
|
||||||
|
readonly: True
|
||||||
|
hint_text: "Output figures to..."
|
||||||
|
# A button that opens the file chooser popup
|
||||||
|
Button:
|
||||||
|
text: "Choose Folder"
|
||||||
|
size_hint_x: None
|
||||||
|
width: 120
|
||||||
|
on_press:
|
||||||
|
root.open_chooser("out_path")
|
||||||
57
Templates/input_output_chooser.py
Normal file
57
Templates/input_output_chooser.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
# Kivy module imports
|
||||||
|
from kivy.lang.builder import Builder
|
||||||
|
from kivy.uix.popup import Popup
|
||||||
|
from kivy.properties import ObjectProperty
|
||||||
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
# Other python module imports
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
Builder.load_file("Templates/file_chooser_popup.kv")
|
||||||
|
|
||||||
|
|
||||||
|
# Create classes for loaded kv files
|
||||||
|
# This class contains the popup for choosing files
|
||||||
|
class FileChooserPopup(Popup):
|
||||||
|
load = ObjectProperty()
|
||||||
|
|
||||||
|
|
||||||
|
class InputOutputChooser(BoxLayout):
|
||||||
|
load = ObjectProperty()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(InputOutputChooser, self).__init__(*args, **kwargs)
|
||||||
|
starting_cache = {"popups": {}, # A dict to contain all popup objects
|
||||||
|
"shared_io_choosers": False,
|
||||||
|
"parent_app": False}
|
||||||
|
self.cache = SimpleNamespace(**starting_cache)
|
||||||
|
|
||||||
|
# The functions "open" and "load" are used to load the file chooser popup
|
||||||
|
def open_chooser(self, pathattr: str):
|
||||||
|
# Wrapper function to allow for multiple different choosers
|
||||||
|
def load_chooser_wrapper(selection):
|
||||||
|
return self.load_chooser(pathattr, selection)
|
||||||
|
|
||||||
|
self.cache.popups[pathattr] = \
|
||||||
|
FileChooserPopup(load=load_chooser_wrapper)
|
||||||
|
self.cache.popups[pathattr].open()
|
||||||
|
|
||||||
|
def load_chooser(self, pathattr: str, selection):
|
||||||
|
path_string = str(selection[0])
|
||||||
|
setattr(self, pathattr, path_string)
|
||||||
|
self.cache.popups[pathattr].dismiss()
|
||||||
|
|
||||||
|
# check for non-empty list i.e. file selected
|
||||||
|
if pathattr in self.__dict__:
|
||||||
|
# set own details based on selection
|
||||||
|
id = getattr(self.ids, pathattr)
|
||||||
|
id.text = getattr(self, pathattr)
|
||||||
|
# set parameters for shared and parent if present
|
||||||
|
if self.cache.shared_io_choosers:
|
||||||
|
for chooser in self.cache.shared_io_choosers:
|
||||||
|
id = getattr(chooser.ids, pathattr)
|
||||||
|
id.text = getattr(self, pathattr)
|
||||||
|
if self.cache.parent_app:
|
||||||
|
setattr(self.cache.parent_app.cache, pathattr, path_string)
|
||||||
735
Templates/melter_desktop.kv
Normal file
735
Templates/melter_desktop.kv
Normal file
@@ -0,0 +1,735 @@
|
|||||||
|
# melter_desktop.kv
|
||||||
|
#:kivy 2.0
|
||||||
|
#:include Templates/input_output_chooser.kv
|
||||||
|
#:import InputOutputChooser Templates.input_output_chooser.InputOutputChooser
|
||||||
|
#:import DropdownButton Templates.dropdown_button.DropdownButton
|
||||||
|
#:import ConsoleOutput Templates.console_output.ConsoleOutput
|
||||||
|
|
||||||
|
|
||||||
|
<Main>:
|
||||||
|
name: "main_screen"
|
||||||
|
id: main_screen
|
||||||
|
TabbedPanel:
|
||||||
|
id: test
|
||||||
|
title: "Melter"
|
||||||
|
do_default_tab: False
|
||||||
|
# First tab is for loading data
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: loading_tab
|
||||||
|
text: "Data Loading"
|
||||||
|
# UI made up of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_dataloading
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for loading data
|
||||||
|
GridLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.8
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
cols: 1
|
||||||
|
# This is the button and progress bar for loading data
|
||||||
|
StackLayout:
|
||||||
|
size_hint_y: 0.15
|
||||||
|
orientation: "lr-tb"
|
||||||
|
Button:
|
||||||
|
size_hint_x: 0.25
|
||||||
|
text: "Load Pyrometry Data"
|
||||||
|
on_press: root.load_data()
|
||||||
|
ProgressBar:
|
||||||
|
id: read_layers_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
value: 0
|
||||||
|
# A button that applies the calibration curve
|
||||||
|
Button:
|
||||||
|
text: "Apply Calibration Curve"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
width: 120
|
||||||
|
on_press: root.apply_calibration_curve()
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.5
|
||||||
|
cols: 1
|
||||||
|
rows: 2
|
||||||
|
# Containing a description of what the field points to
|
||||||
|
Label:
|
||||||
|
halign: "right"
|
||||||
|
text: "Calibration Curve"
|
||||||
|
width: 160
|
||||||
|
# A textbox for cal curve equation entry
|
||||||
|
TextInput:
|
||||||
|
halign: "center"
|
||||||
|
valign: "center"
|
||||||
|
id: calibration_curve
|
||||||
|
readonly: False
|
||||||
|
hint_text: "y = x"
|
||||||
|
ProgressBar:
|
||||||
|
id: cal_curve_progbar
|
||||||
|
size_hint_x: 0.5
|
||||||
|
value: 0
|
||||||
|
# This label displays current status of data processing
|
||||||
|
Label:
|
||||||
|
id: dataloading_display
|
||||||
|
text: "No data loaded!"
|
||||||
|
halign: "center"
|
||||||
|
valign: "center"
|
||||||
|
|
||||||
|
# Second tab is for detecting & separating samples
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: detection_tab
|
||||||
|
text: "Sample\nDetection"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_sampledetection
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for per sample data
|
||||||
|
GridLayout:
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.75
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
cols: 1
|
||||||
|
# Below is a tabbed panel for different thresholding methods
|
||||||
|
TabbedPanel:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 0.25
|
||||||
|
do_default_tab: False
|
||||||
|
tab_pos: "left_top"
|
||||||
|
tab_width: self.height
|
||||||
|
# This is a messy solution, but uses a nested TabbedPanel
|
||||||
|
# to label the threshold functions. Would prefer better
|
||||||
|
# method. Maybe some kind of tooltip?
|
||||||
|
TabbedPanelItem:
|
||||||
|
text: "Thresholding\nTechniques"
|
||||||
|
TabbedPanel:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 1.0
|
||||||
|
do_default_tab: False
|
||||||
|
tab_pos: "left_top"
|
||||||
|
tab_width: self.height / 2
|
||||||
|
# First tab is options for speed based thresholding
|
||||||
|
TabbedPanelItem:
|
||||||
|
text: "Speed"
|
||||||
|
id: avgspeed_thresh_panel
|
||||||
|
# Create a panel to hold items
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 1.0
|
||||||
|
orientation: "lr-tb"
|
||||||
|
# Top panel contains buttons
|
||||||
|
#:set avgspeed_toprowheight 30.
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - avgspeed_toprowheight) / 2
|
||||||
|
# Here is the main options, held in center by spacers
|
||||||
|
Label:
|
||||||
|
text: "Thresholding percent:"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgspeed_toprowheight
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: avgspeed_thresh_thresh_percent
|
||||||
|
hint_text: "x < (% of max speed)"
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgspeed_toprowheight
|
||||||
|
size_hint_x: 0.25
|
||||||
|
Label:
|
||||||
|
text: "Rolling average:"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgspeed_toprowheight
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: avgspeed_thresh_avgof
|
||||||
|
hint_text: "n"
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgspeed_toprowheight
|
||||||
|
size_hint_x: 0.25
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - avgspeed_toprowheight) / 2
|
||||||
|
# This button and progress bar trigger and track the
|
||||||
|
# thresholding of data
|
||||||
|
Button:
|
||||||
|
text: "Threshold by rolling\naverage of speed"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.5
|
||||||
|
on_press: root.avgspeed_threshold()
|
||||||
|
ProgressBar:
|
||||||
|
id: avgspeed_threshold_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.5
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# Second tab is for tepmerature based thresholding
|
||||||
|
TabbedPanelItem:
|
||||||
|
text: "Temp"
|
||||||
|
id: avgtemp_thresh_panel
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 1.0
|
||||||
|
orientation: "lr-tb"
|
||||||
|
# Top panel contains buttons
|
||||||
|
#:set avgtemp_toprowheight 30.
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - avgtemp_toprowheight) / 2
|
||||||
|
# Here is the main options, held in center by spacers
|
||||||
|
Label:
|
||||||
|
text: "Keep points where: Temperature "
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgtemp_toprowheight
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
DropdownButton:
|
||||||
|
id: avgtemp_thresh_function_dropdown
|
||||||
|
text: ">"
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgtemp_toprowheight
|
||||||
|
TextInput:
|
||||||
|
id: avgtemp_thresh_thresh_percent
|
||||||
|
hint_text: "x"
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgtemp_toprowheight
|
||||||
|
size_hint_x: 0.1
|
||||||
|
Label:
|
||||||
|
text: "% of maximum"
|
||||||
|
size_hint_x: 0.3
|
||||||
|
size_hint_y: None
|
||||||
|
height: avgtemp_toprowheight
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - avgtemp_toprowheight) / 2
|
||||||
|
# This button and progress bar trigger and track the
|
||||||
|
# thresholding of data
|
||||||
|
Button:
|
||||||
|
text: "Threshold by\naverage temperature"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.5
|
||||||
|
on_press: root.avgtemp_threshold()
|
||||||
|
ProgressBar:
|
||||||
|
id: avgtemp_threshold_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.5
|
||||||
|
value: 0
|
||||||
|
# The next piece contains controls for KMeans sample separation
|
||||||
|
StackLayout:
|
||||||
|
#:set kmeans_toprowheight 30.
|
||||||
|
id: kmeans_panel
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 0.25
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - kmeans_toprowheight) / 2
|
||||||
|
Label:
|
||||||
|
text: "Number of Samples: "
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: None
|
||||||
|
height: kmeans_toprowheight
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: kmeans_nsamples
|
||||||
|
hint_text: "n"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: None
|
||||||
|
height: kmeans_toprowheight
|
||||||
|
# This label is a spacer
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: None
|
||||||
|
height: ((self.parent.height / 2) - kmeans_toprowheight) / 2
|
||||||
|
# This button and progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Separate Samples"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.5
|
||||||
|
on_press: root.separate_samples()
|
||||||
|
ProgressBar:
|
||||||
|
id: kmeans_separate_samples_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.5
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# The box below will display console output from kmeans
|
||||||
|
ScrollView:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 0.5
|
||||||
|
ConsoleOutput:
|
||||||
|
id: kmeans_separate_console_output
|
||||||
|
size_hint_x: 1.0
|
||||||
|
|
||||||
|
# Third tab is for generating whole buildplate figures
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: plate_vis_tab
|
||||||
|
text: "Buildplate\nVisualization"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_buildplate
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for buildplate plotting
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.8
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
orientation: "lr-tb"
|
||||||
|
#:set row_y_hint 0.15
|
||||||
|
#:set num_functions 3
|
||||||
|
#:set spacer_y_hint ((1-(row_y_hint*num_functions*2))/(num_functions-1))
|
||||||
|
|
||||||
|
# First function on this panel (layers_to_figures) begins here
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: layers_to_figures_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checkboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_figures_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_figures_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_figures_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_figures_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 2d\nbuidplate figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.layers_to_figures()
|
||||||
|
ProgressBar:
|
||||||
|
id: layers_to_figures_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# This spacer clearly separates panels for functions
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: spacer_y_hint
|
||||||
|
|
||||||
|
# Second function on this panel (layers_to_3dplot) begins here
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: layers_to_3dplot_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 3d\nbuidplate figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.layers_to_3dplot()
|
||||||
|
ProgressBar:
|
||||||
|
id: layers_to_3dplot_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# This spacer clearly separates panels for functions
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: spacer_y_hint
|
||||||
|
|
||||||
|
# Second function on this panel (layers_to_3dplot) begins here
|
||||||
|
# First part of this panel will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "lr-tb"
|
||||||
|
size_hint_x: 1.00
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checkboxes for toggling plot colouring and colourbars
|
||||||
|
# and downsampling factor input
|
||||||
|
#:set one_third 1/3
|
||||||
|
Label:
|
||||||
|
text: "Downsampling: "
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_interactive_downsampling
|
||||||
|
hint_text: "1"
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_interactive_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_interactive_sliceable
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Sliceable model"
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_interactive_plotparams
|
||||||
|
size_hint_x: 1.
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate interactive 3d\nbuidplate figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.layers_to_3dplot_interactive()
|
||||||
|
ProgressBar:
|
||||||
|
id: layers_to_3dplot_interactive_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# Fourth tab is for generating figures
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: sample_vis_tab
|
||||||
|
text: "Per Sample\nVisualization"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_persample
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for buildplate plotting
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.8
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
orientation: "lr-tb"
|
||||||
|
#:set row_y_hint 0.15
|
||||||
|
#:set num_functions 3
|
||||||
|
#:set spacer_y_hint ((1-(row_y_hint*num_functions*2))/(num_functions-1))
|
||||||
|
|
||||||
|
# First function on this panel (layers_to_figures) begins here
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: samples_to_figures_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checkboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_figures_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_figures_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_figures_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_figures_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 2d\nsample figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.samples_to_figures()
|
||||||
|
ProgressBar:
|
||||||
|
id: samples_to_figures_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# This spacer clearly separates panels for functions
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: spacer_y_hint
|
||||||
|
|
||||||
|
# Second function on this panel (layers_to_3dplot) begins here
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: samples_to_3dplot_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_3dplot_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_3dplot_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_3dplot_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_3dplot_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 3d\nsample figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.samples_to_3dplot()
|
||||||
|
ProgressBar:
|
||||||
|
id: samples_to_3dplot_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
|
|
||||||
|
# This spacer clearly separates panels for functions
|
||||||
|
Label:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: spacer_y_hint
|
||||||
|
|
||||||
|
# Second function on this panel (layers_to_3dplot) begins here
|
||||||
|
# First part of this panel will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "lr-tb"
|
||||||
|
size_hint_x: 1.00
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
# Checkboxes for toggling plot colouring and colourbars
|
||||||
|
# and downsampling factor input
|
||||||
|
#:set one_third 1/3
|
||||||
|
Label:
|
||||||
|
text: "Downsampling: "
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "right"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_3dplot_interactive_downsampling
|
||||||
|
hint_text: "1"
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_3dplot_interactive_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
CheckBox:
|
||||||
|
id: samples_to_3dplot_interactive_sliceable
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.25 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Sliceable model"
|
||||||
|
size_hint_x: 0.75 * one_third
|
||||||
|
size_hint_y: 0.5
|
||||||
|
text_size: self.size
|
||||||
|
halign: "left"
|
||||||
|
valign: "middle"
|
||||||
|
TextInput:
|
||||||
|
id: samples_to_3dplot_interactive_plotparams
|
||||||
|
size_hint_x: 1.
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate interactive 3d\nsample figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
on_press: root.samples_to_3dplot_interactive()
|
||||||
|
ProgressBar:
|
||||||
|
id: samples_to_3dplot_interactive_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: row_y_hint
|
||||||
|
value: 0
|
||||||
284
Templates/melter_desktop_backup.kv
Normal file
284
Templates/melter_desktop_backup.kv
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
# melter_desktop.kv
|
||||||
|
#:kivy 2.0
|
||||||
|
#:include Templates/input_output_chooser.kv
|
||||||
|
#:import InputOutputChooser Templates.input_output_chooser.InputOutputChooser
|
||||||
|
#:import DropdownButton Templates.dropdown_button.DropdownButton
|
||||||
|
|
||||||
|
|
||||||
|
<Main>:
|
||||||
|
name: "main_screen"
|
||||||
|
id: main_screen
|
||||||
|
TabbedPanel:
|
||||||
|
id: test
|
||||||
|
title: "Melter"
|
||||||
|
do_default_tab: False
|
||||||
|
|
||||||
|
# First tab is for loading data
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: loading_tab
|
||||||
|
text: "Data Loading"
|
||||||
|
|
||||||
|
# UI made up of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_dataloading
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for loading data
|
||||||
|
GridLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.8
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
# spacing: 2.
|
||||||
|
cols: 1
|
||||||
|
# This is the button and progress bar for loading data
|
||||||
|
StackLayout:
|
||||||
|
size_hint_y: 0.15
|
||||||
|
orientation: "lr-tb"
|
||||||
|
Button:
|
||||||
|
size_hint_x: 0.25
|
||||||
|
text: "Load Pyrometry Data"
|
||||||
|
on_press: root.load_data()
|
||||||
|
ProgressBar:
|
||||||
|
id: read_layers_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
value: 0.
|
||||||
|
# A button that applies the calibration curve
|
||||||
|
Button:
|
||||||
|
text: "Apply Calibration Curve"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
width: 120
|
||||||
|
on_press: root.apply_calibration_curve()
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.5
|
||||||
|
cols: 1
|
||||||
|
rows: 2
|
||||||
|
# Containing a description of what the field points to
|
||||||
|
Label:
|
||||||
|
halign: "right"
|
||||||
|
text: "Calibration Curve"
|
||||||
|
width: 160
|
||||||
|
# A textbox for cal curve equation entry
|
||||||
|
TextInput:
|
||||||
|
halign: "center"
|
||||||
|
valign: "center"
|
||||||
|
id: calibration_curve
|
||||||
|
readonly: False
|
||||||
|
hint_text: "y = x"
|
||||||
|
ProgressBar:
|
||||||
|
id: cal_curve_progbar
|
||||||
|
size_hint_x: 0.5
|
||||||
|
value: 0.
|
||||||
|
# This label displays current status of data processing
|
||||||
|
Label:
|
||||||
|
id: dataloading_display
|
||||||
|
text: "No data loaded!"
|
||||||
|
halign: "center"
|
||||||
|
valign: "center"
|
||||||
|
|
||||||
|
# Second tab is for detecting & separating samples
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: detection_tab
|
||||||
|
text: "Sample\nDetection"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_sampledetection
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for per sample data
|
||||||
|
GridLayout:
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.75
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
# spacing: 2.
|
||||||
|
cols: 2
|
||||||
|
Button:
|
||||||
|
text: "Test 5"
|
||||||
|
Button:
|
||||||
|
text: "Test 6"
|
||||||
|
Button:
|
||||||
|
text: "Test 7"
|
||||||
|
Button:
|
||||||
|
text: "Test 8"
|
||||||
|
|
||||||
|
# Third tab is for generating whole buildplate figures
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: plate_vis_tab
|
||||||
|
text: "Buildplate\nVisualization"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_buildplate
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for buildplate plotting
|
||||||
|
GridLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.8 # These the buttons and progress bar for the layers_to_figures function
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
cols: 1
|
||||||
|
# These the buttons and progress bar for the layers_to_figures function
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 1.0
|
||||||
|
orientation: "lr-tb"
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: layers_to_figures_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.15
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.15
|
||||||
|
# Checboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_figures_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_figures_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_figures_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_figures_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 2d\nbuidplate figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.15
|
||||||
|
on_press: root.layers_to_figures()
|
||||||
|
ProgressBar:
|
||||||
|
id: layers_to_figures_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.15
|
||||||
|
value: 0.
|
||||||
|
# These the buttons and progress bar for the layers_to_figures function
|
||||||
|
StackLayout:
|
||||||
|
size_hint_x: 1.0
|
||||||
|
size_hint_y: 0.15
|
||||||
|
orientation: "lr-tb"
|
||||||
|
# This button will be a dropdown
|
||||||
|
DropdownButton:
|
||||||
|
id: layers_to_3dplot_filetype_dropdown
|
||||||
|
text: "select file type\n(png)"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.15
|
||||||
|
# Right of the dropdown will be a suite of options
|
||||||
|
StackLayout:
|
||||||
|
orientation: "tb-lr"
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.15
|
||||||
|
# Checboxes for toggling plot colouring and colourbars
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_plot_w
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
CheckBox:
|
||||||
|
id: layers_to_3dplot_colorbar
|
||||||
|
active: True
|
||||||
|
size_hint_x: 0.1
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Colour by temperature"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
Label:
|
||||||
|
text: "Display colourbar"
|
||||||
|
size_hint_x: 0.4
|
||||||
|
size_hint_y: 0.5
|
||||||
|
# And text input for other figure parameters
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_figureparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Figure parameters..."
|
||||||
|
TextInput:
|
||||||
|
id: layers_to_3dplot_plotparams
|
||||||
|
size_hint_x: 0.5
|
||||||
|
size_hint_y: 0.5
|
||||||
|
hint_text: "Plot parameters..."
|
||||||
|
# This button an progress bar trigger and track the
|
||||||
|
# generation of figures
|
||||||
|
Button:
|
||||||
|
text: "Generate static 3d\nbuidplate figures"
|
||||||
|
size_hint_x: 0.25
|
||||||
|
size_hint_y: 0.15
|
||||||
|
on_press: root.layers_to_plot()
|
||||||
|
ProgressBar:
|
||||||
|
id: layers_to_3dplot_progbar
|
||||||
|
size_hint_x: 0.75
|
||||||
|
size_hint_y: 0.15
|
||||||
|
value: 0.
|
||||||
|
|
||||||
|
# Fourth tab is for generating figures
|
||||||
|
TabbedPanelItem:
|
||||||
|
id: sample_vis_tab
|
||||||
|
text: "Per Sample\nVisualization"
|
||||||
|
|
||||||
|
# Items are stacked from bottom to top. UI made of floating sub-layouts
|
||||||
|
FloatLayout:
|
||||||
|
|
||||||
|
# First item is an InputOutputChooser
|
||||||
|
InputOutputChooser:
|
||||||
|
id: io_chooser_persample
|
||||||
|
size_hint_x: 1.0
|
||||||
|
pos_hint: {"x": 0., "y": 0.875}
|
||||||
|
|
||||||
|
# Second item is a grid layout filled with buttons
|
||||||
|
# These buttons denote available functions for per sample data
|
||||||
|
GridLayout:
|
||||||
|
size_hint_x: 0.9
|
||||||
|
size_hint_y: 0.75
|
||||||
|
pos_hint: {"x": 0.05, "y": 0.05}
|
||||||
|
# spacing: 2.
|
||||||
|
cols: 2
|
||||||
|
Button:
|
||||||
|
text: "Test 9"
|
||||||
|
Button:
|
||||||
|
text: "Test 10"
|
||||||
|
Button:
|
||||||
|
text: "Test 11"
|
||||||
|
Button:
|
||||||
|
text: "Test 12"
|
||||||
11
__init__.py
Normal file
11
__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
"""
|
||||||
|
Melter is a GUI wrapper for the MTPy meltpool tomography and selective laser
|
||||||
|
melting pyrometric analysis library
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
__license__ = "MIT"
|
||||||
|
__status__ = "Prototype"
|
||||||
56
common/MTPy_Modified.py
Normal file
56
common/MTPy_Modified.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
from MTPy.mtpy import MeltpoolTomography
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
|
# A class for wrapping generators and adding the ability to increment
|
||||||
|
# a progress bar
|
||||||
|
class ProgBar_Wrapper():
|
||||||
|
|
||||||
|
def __init__(self, generator, progress_bar,
|
||||||
|
start=0, end=100, step=1):
|
||||||
|
# If the item isnt a generator make it into one
|
||||||
|
if not hasattr(generator, "__next__"):
|
||||||
|
generator = (x for x in generator)
|
||||||
|
self.generator = generator
|
||||||
|
self.progress_bar = progress_bar
|
||||||
|
self.progress_bar.value = start
|
||||||
|
self.progress_bar.max = end
|
||||||
|
self.step = step
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
output = next(self.generator)
|
||||||
|
self.progress_bar.value += self.step
|
||||||
|
return output
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
# This class is basically MTPy modified to replace tqdm progress bars with
|
||||||
|
# Kivy GUI ones
|
||||||
|
class MT_Modded(MeltpoolTomography):
|
||||||
|
|
||||||
|
# __init__ includes a dict containing progress bars and
|
||||||
|
# sets progressbars to be controlled by a method of the object
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["progressbar"] = self._progressbar
|
||||||
|
self.progress_bars = dict()
|
||||||
|
super(MT_Modded, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# This function is intended to replace tqdm in the mtpy module
|
||||||
|
def _progressbar(self, *args, **kwargs):
|
||||||
|
# get name of calling function for keeping track
|
||||||
|
current_func = inspect.getframeinfo(
|
||||||
|
inspect.currentframe().f_back).function
|
||||||
|
# if progbar has an entry
|
||||||
|
if current_func in self.progress_bars:
|
||||||
|
current_bar = self.progress_bars[current_func]
|
||||||
|
# Then, wrap the generator to increment value while iterating
|
||||||
|
wrapped_generator = ProgBar_Wrapper(args[0], current_bar,
|
||||||
|
start=0, end=kwargs["total"])
|
||||||
|
return wrapped_generator
|
||||||
|
else:
|
||||||
|
return args[0] # if not top-level, return unmodified generator
|
||||||
6
common/__init__.py
Normal file
6
common/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
"""
|
||||||
|
Common, internal objects for shared use in the "Melter" program
|
||||||
|
"""
|
||||||
14
common/threading_decorators.py
Normal file
14
common/threading_decorators.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# *_* coding: utf-8 *_*
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
# Wrapper for running a function in a parallel thread
|
||||||
|
def run_in_thread(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
thread = Thread(target=func, args=args, kwargs=kwargs)
|
||||||
|
thread.start()
|
||||||
|
return wrapper
|
||||||
Reference in New Issue
Block a user