Skip to content

Commit ba45aa6

Browse files
committed
Initial commit.
0 parents  commit ba45aa6

File tree

7 files changed

+334
-0
lines changed

7 files changed

+334
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*~
2+
*.asv

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Unit Converter Component
2+
3+
Version: 1.0
4+
5+
Converts input into a standardized unit.
6+
7+
![The Unit Converter Component.](https://insidelabs-git.mathworks.com/charting/chartsforfileexchange/-/raw/filled_line_chart/unitConverterComponent/unitConverterComponent.png)
8+
9+
## Syntax
10+
Note: A conversion table containing units and conversion factors is used. The conversion factors are taken from [Wikipedia](https://en.wikipedia.org/wiki/Conversion_of_units#Length). The conversion works by converting from the `DisplayUnit` to the standard unit for the table, then to the `TargetUnit`.
11+
<details><summary>Click to view the default conversion table (relative to meters)</summary>
12+
13+
| RowNames | ConversionFactors |
14+
| ------ | ------ |
15+
| mile | 1609.344 |
16+
| foot | 0.3048 |
17+
| inch | 0.0254 |
18+
| meter | 1 |
19+
| yard | 0.9144 |
20+
21+
</details>
22+
23+
- `unitConverterComponent(parent)` creates a unit converter in the specified parent container. The parent can be a figure or one of its child containers.
24+
- `unitConverterComponent('TargetUnit', Unit)` - specifies the target unit that the input values are converted to, containing a dropdown and numeric edit field. Use this option with any of the input argument combinations in the previous syntaxes.
25+
- `unitConverterComponent(_, Name, Value)` - creates a unit converter with properties specified by one or more name-value arguments.
26+
27+
This information is also available if you call `help unitConverterComponent`.
28+
29+
## Name-Value Pair Arguments/Properties
30+
- `DisplayUnit` (1 x 1 text) - the unit the app user passes in through the dropdown. The list of units from which the app user can select as the `DisplayUnit` is determined by the row names of units in the conversion table.
31+
- `DisplayValue` (double) - the number of the specified unit the app user passes in through the numeric edit field.
32+
- `TargetUnit` (1 x 1 text) - the unit the app author chooses to do conversions into.
33+
- `Value` (double) - the final converted value in the target unit.
34+
- `ConversionTable` (n x 1 table)- the conversion table used to determine the units supported and their respective conversion factor. It must contain row names of `n` units with the first column being conversion factors into the `TargetUnit`. The conversion factors into a specified unit may not neccesarily be the `TargetUnit`.
35+
- `FontSize` (double) - the font size of the text box and dropdown.
36+
- `FontName` (must be a member of available system fonts in `listfonts`) - the font used for the text box and dropdown.
37+
- `FontColor` (1 x 3 RGB triplet) - the color of the text in the edit field.
38+
- `BackgroundColor` (1 x 3 RGB triplet) - the background color of the edit field.
39+
- `TextFieldLayout` (`'side-by-side' or 'stacked'`) - the position of the edit field and the dropdown.
40+
41+
## Method
42+
- `makeDefaultConversionTable(obj)` - Resets the conversion table to the default one.
43+
44+
## Example
45+
View the `example.mlapp` file for more examples. This app shows the converted value based on the user's input.
46+
47+
Let's say there is an app author trying to create an app that collects different distances in a physics experiment and compile them into a dashboard. If it has an international audience, it may collect data in the form of 'meters' or 'kilometers', or for the U.S, in 'feet', 'miles', and perhaps even 'yards'. In other words, there are many different units an app user may wish to input. To standardize the data and make it easier for comparison by the app author, the component can be used.
48+
49+
```
50+
unitConverterComponent('TargetUnit', 'mile')
51+
```
52+
53+
Now, all values entered into the component will be converted to miles for only the app author to see.
54+
55+
To export the component into App Designer, run the following in the Command Window ([instructions](https://www.mathworks.com/help/matlab/creating_guis/custom-ui-component-classes-in-app-designer.html) under 'Configure Custom UI Component'):
56+
```
57+
appdesigner.customcomponent.configureMetadata('C:\MyComponents\unitConverterComponent.m'); % insert your file path
58+
```
59+
60+

SECURITY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Reporting Security Vulnerabilities
2+
3+
If you believe you have discovered a security vulnerability, please report it to
4+
[security@mathworks.com](mailto:security@mathworks.com). Please see
5+
[MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6+
for additional information.

example.mlapp

18.8 KB
Binary file not shown.

license.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Copyright (c) 2022, The MathWorks, Inc.
2+
All rights reserved.
3+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
5+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
6+
3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings.
7+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8+
9+
10+
11+

unitConverterComponent.m

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
classdef unitConverterComponent < matlab.ui.componentcontainer.ComponentContainer
2+
% unitConverterComponent converts input into a standardized unit
3+
%
4+
% c = unitConverterComponent(parent) creates a unit converter in the
5+
% specified parent container. The parent can be a figure or one of its
6+
% child containers.
7+
%
8+
% c = unitConverterComponent(____, 'TargetUnit', Unit) specifies the
9+
% target unit that the input values are converted to, containing a
10+
% dropdown and numeric edit field. Use this option with any of the
11+
% input argument combinations in the previous syntaxes.
12+
%
13+
% c = unitConverterComponent(____, Name, Value) creates a unit
14+
% converter with properties specified by one or more name-value
15+
% arguments.
16+
17+
% Copyright 2022 The MathWorks, Inc.
18+
19+
properties
20+
DisplayValue (1,1) {mustBeNumeric} = 0
21+
FontSize (1,:) {mustBePositive} = 14
22+
FontName (1,:) {mustBeValidFont} = 'Arial'
23+
FontColor {validatecolor} = [0 0 0]
24+
TextFieldLayout (1,:) {mustBeMember(TextFieldLayout, {'side-by-side', 'stacked'})} = 'side-by-side'
25+
end
26+
27+
properties (Dependent)
28+
Value
29+
ConversionTable (:,1) {mustBeValidConversionTable}
30+
DisplayUnit (1,1) string {mustBeTextScalar}
31+
TargetUnit (1,1) string {mustBeTextScalar}
32+
end
33+
34+
properties (Access = protected)
35+
% Use an internal conversion table to store the conversion table,
36+
% display unit, and target unit. It allows the user to freely
37+
% change the dependent properties while storing the most recent
38+
% state of the component.
39+
InternalConversionTable (:,3) = makeDefaultInternalConversionTable()
40+
end
41+
42+
events (HasCallbackProperty, NotifyAccess = private)
43+
ValueChanged
44+
end
45+
46+
properties (Access = private, Transient, NonCopyable)
47+
Grid matlab.ui.container.GridLayout
48+
EditField matlab.ui.control.NumericEditField
49+
DropDown matlab.ui.control.DropDown
50+
end
51+
52+
methods(Access = private)
53+
function valChanged(obj)
54+
notify(obj, "ValueChanged");
55+
end
56+
end
57+
58+
methods(Static)
59+
function tbl = makeDefaultConversionTable()
60+
tbl = makeDefaultConversionTable();
61+
end
62+
end
63+
64+
methods
65+
function tbl = get.ConversionTable(obj)
66+
% Conversion table is contained within the row names and first
67+
% column of the internal table
68+
tbl = obj.InternalConversionTable(:,1);
69+
end
70+
71+
function set.ConversionTable(obj, tbl)
72+
% Input validation
73+
mustBeValidConversionTable(tbl);
74+
currentDisplayUnit = obj.DisplayUnit;
75+
currentTargetUnit = obj.TargetUnit;
76+
77+
% When DisplayUnit isn't present, set it to the first unit
78+
if sum(strcmp(tbl.Properties.RowNames, currentDisplayUnit)) ~= 1
79+
currentDisplayUnit = tbl.Properties.RowNames(1);
80+
end
81+
82+
% Validate target unit is in the table - if not, set it to the
83+
% unit with a conversion factor of 1
84+
if sum(strcmp(tbl.Properties.RowNames, currentTargetUnit)) ~= 1
85+
currentTargetUnit = tbl.Properties.RowNames(tbl{:,1} == 1);
86+
end
87+
88+
% Update conversion table while also storing the display and
89+
% target unit
90+
tbl.DisplayUnit = strcmp(tbl.Properties.RowNames, currentDisplayUnit);
91+
tbl.TargetUnit = strcmp(tbl.Properties.RowNames, currentTargetUnit);
92+
obj.InternalConversionTable = tbl;
93+
94+
% Update dropdown with the new units and the display unit
95+
obj.DropDown.Items = tbl.Properties.RowNames;
96+
obj.DropDown.Value = currentDisplayUnit;
97+
end
98+
99+
function set.FontColor(obj, color)
100+
obj.FontColor = validatecolor(color);
101+
end
102+
103+
function set.FontName(obj, font)
104+
obj.FontName = mustBeValidFont(font);
105+
end
106+
107+
function tbl = get.InternalConversionTable(obj)
108+
tbl = obj.InternalConversionTable;
109+
110+
% Update internal table with the display unit
111+
tbl.DisplayUnit = ...
112+
strcmp(tbl.Properties.RowNames, obj.DisplayUnit);
113+
end
114+
115+
function set.DisplayUnit(obj, val)
116+
mustBeValidUnit(val, obj.ConversionTable);
117+
obj.DropDown.Value = val;
118+
end
119+
120+
function val = get.DisplayUnit(obj)
121+
val = obj.DropDown.Value;
122+
end
123+
124+
function set.TargetUnit(obj, val)
125+
% Allow uppercase target units to be accepted
126+
val = lower(val);
127+
mustBeValidUnit(val, obj.ConversionTable);
128+
129+
% Update internal table with the new target unit
130+
obj.InternalConversionTable.TargetUnit = ...
131+
strcmp(obj.InternalConversionTable.Properties.RowNames, val);
132+
end
133+
134+
function val = get.TargetUnit(obj)
135+
val = obj.InternalConversionTable.Properties.RowNames(...
136+
obj.InternalConversionTable.TargetUnit);
137+
end
138+
139+
function set.DisplayValue(obj, val)
140+
obj.EditField.Value = val; %#ok<MCSUP>
141+
end
142+
143+
function val = get.DisplayValue(obj)
144+
val = obj.EditField.Value;
145+
end
146+
147+
function set.Value(obj, val)
148+
% Convert from the target unit to the display unit
149+
obj.DisplayValue = convertUnit(obj.ConversionTable, val, obj.TargetUnit, obj.DisplayUnit);
150+
end
151+
152+
function val = get.Value(obj)
153+
% Convert to the standard unit (default is meters), then to the target unit
154+
val = convertUnit(obj.ConversionTable, obj.DisplayValue, obj.DisplayUnit, obj.TargetUnit);
155+
end
156+
end
157+
158+
methods(Access = protected)
159+
function setup(obj)
160+
% Create UI components
161+
obj.Grid = uigridlayout(obj, 'Padding', 0);
162+
obj.EditField = uieditfield(obj.Grid,'numeric', ...
163+
'HorizontalAlignment', 'center');
164+
obj.EditField.ValueChangedFcn = @(~, ~) obj.valChanged;
165+
166+
tbl = makeDefaultConversionTable();
167+
obj.DropDown = uidropdown(obj.Grid, ...
168+
'Editable', 'on', ...
169+
'Items', tbl.Properties.RowNames);
170+
obj.DropDown.ValueChangedFcn = @(~, ~) obj.valChanged;
171+
end
172+
173+
function update(obj)
174+
% Update the stylistic properties for the UI components
175+
obj.Grid.BackgroundColor = obj.BackgroundColor;
176+
obj.DropDown.FontName = obj.FontName;
177+
obj.EditField.FontName = obj.FontName;
178+
obj.DropDown.FontColor = obj.FontColor;
179+
obj.EditField.FontColor = obj.FontColor;
180+
obj.DropDown.FontSize = obj.FontSize;
181+
obj.EditField.FontSize = obj.FontSize;
182+
183+
% Update layout of UI components
184+
if strcmp(obj.TextFieldLayout, 'side-by-side')
185+
obj.Position(3:4) = [150 25];
186+
obj.Grid.RowHeight = {'fit'};
187+
obj.Grid.ColumnWidth = {'1x','fit'};
188+
obj.DropDown.Layout.Row = 1;
189+
obj.DropDown.Layout.Column = 2;
190+
elseif strcmp(obj.TextFieldLayout, 'stacked')
191+
obj.Position(3:4) = [100 60];
192+
obj.Grid.RowHeight = {'fit','fit'};
193+
obj.Grid.ColumnWidth = {'1x'};
194+
obj.DropDown.Layout.Row = 2;
195+
obj.DropDown.Layout.Column = 1;
196+
end
197+
end
198+
end
199+
end
200+
201+
function unitTable = makeDefaultConversionTable()
202+
unit = [
203+
"mile";
204+
"foot";
205+
"inch";
206+
"meter";
207+
"yard"
208+
];
209+
210+
% Taken from wikipedia:
211+
% https://en.wikipedia.org/wiki/Conversion_of_units#Length
212+
ConversionFactors = [
213+
1609.344;
214+
0.3048;
215+
0.0254;
216+
1;
217+
0.9144;
218+
];
219+
220+
unitTable = table(ConversionFactors, 'RowNames', unit);
221+
end
222+
223+
function tbl = makeDefaultInternalConversionTable()
224+
t = makeDefaultConversionTable();
225+
t.DisplayUnit = strcmp(t.Properties.RowNames, 'mile');
226+
t.TargetUnit = t.ConversionFactors == 1;
227+
tbl = t;
228+
end
229+
230+
231+
function mustBeValidConversionTable(tbl)
232+
if isempty(tbl.Properties.RowNames)
233+
error("Conversion table must have row names.");
234+
elseif width(tbl) ~= 1
235+
error("Conversion table must have one column containing the conversion factors.")
236+
elseif ~isnumeric(tbl{:,1})
237+
error("Conversion factors must all be numeric.");
238+
elseif ~any(tbl{:,1} == 1)
239+
error("Standard unit must be in conversion table with a conversion factor of 1.")
240+
end
241+
end
242+
243+
function font = mustBeValidFont(font)
244+
font = validatestring(font, listfonts);
245+
end
246+
247+
function mustBeValidUnit(unit, tbl)
248+
mustBeMember(unit, tbl.Properties.RowNames);
249+
end
250+
251+
function val = convertUnit(tbl, fromValue, fromUnit, toUnit)
252+
factor = tbl{fromUnit, :};
253+
defaultVal = fromValue * factor;
254+
val = defaultVal / (tbl{toUnit, :});
255+
end

unitConverterComponent.png

5.63 KB
Loading

0 commit comments

Comments
 (0)