First functioning version of app!

This commit is contained in:
Cian-H
2021-05-20 18:19:32 +01:00
parent 1788501adc
commit 3ef247fc70
16 changed files with 1774 additions and 1 deletions

BIN
Fonts/DroidSansMono.ttf Normal file

Binary file not shown.

2
MTPy

Submodule MTPy updated: bae1dfa134...477ca5c403

378
Main/Main_Desktop.py Normal file
View 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
View 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
View 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()

View 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

View 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)

View 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

View 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")

View 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
View 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

View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python3
# *_* coding: utf-8 *_*
"""
Common, internal objects for shared use in the "Melter" program
"""

View 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