Create a mask in Simulink with edit field as enumeration - matlab

I want to create a mask on a subsystem, like the mask of the Enumerated Constant (shown below). As you see in the blue circle, the Value can be edited using a drop down list of possibilities. The Enumerated Constant
If creating a mask it is indeed possible to make a Popup instead of an Edit but the problem with this is that the list of possible selections needs to be manually created inside the mask. What I want is that this Edit value only shows the possible selections of a certain enumeration that I want to specify only ones. The Enumerated Constant mask does this with an Edit type but even then it works.
Of course I tried to reverse engineer this from this block but I can not find how Matlab does this.

I am not sure if my initial question was quite clear if I read it now. The enumerals should be taken from an enumerated type from a Data Dictionary. So based on the answers of Julian and DrBuck I could answer my own question.
I first get the enumerated type from the data dictionary.
% set name of DD and type
DDName = 'types.sldd';
EventType = 'Dem_EventIdType';
% Get DD entry
myDictionaryObj = Simulink.data.dictionary.open(DDName);
dDataSectObj = getSection(myDictionaryObj,'Design Data');
entryObj = getEntry(dDataSectObj,EventType,'DataSource',DDName);
entryValue = getValue(entryObj);
Then I get the enumerals and add the event type to it. This will be used to fill the pop-up options.
% Get enumerals
NoOfEvents = length(entryValue.Enumerals);
for i = 1:NoOfEvents
EventIDs{i,1} = [EventType '.' entryValue.Enumerals(i).Name];
end
After this I used the above proposed code to manipulate the created pop-up menu with the enumerals from the Data Dictionary.
maskObj= Simulink.Mask.get(gcb);
par_name = 'Value_eventID'; % name
par_idx = find(strcmp({maskObj.Parameters.Name},par_name)); % index of parameter
maskObj.Parameters(par_idx).TypeOptions = EventIDs; % enum options
I do this not in the 'Initialization Commands' but in the callback function of the 'Refresh events' button. When the block is added from the library it only contains the INVALID_EVENT. After a refresh it does a fresh look-up and adds the current enumerals to the list.
My end result:

I couldn't quite figure out how to do this but hopefully this answer should get you on the right track.
Create two popups on the mask, eg 'popup1' and 'popup2'. Hardcode your type options to popup1, and leave popup2 blank and disabled (because it's dependent on what you select with popup1). You can then use a callback on popup1 to populate popup2 when the first one is clicked. Your callback would look something like this:
% Grab the value selected from popup1
value = get_param(gcb, 'popup_1');
% Do some sort of check/switch statement to set your options
if value == 1
% Enable popup2
set_param(gcb, 'MaskEnables', {'on', 'on'});
% Set the type options for popup2
set_param(gcb, 'MaskStyleString', 'popup(1|2|3|4|5),popup(my|new|options)');
end if
Have a look at this and this in the Matlab/Simulink documentation. get_param and set_param are useful if a bit unintuitive. It looks like in later versions of Simulink you can use maskObj = Simulink.Mask.create(gcb); methods to do this sort of stuff, which might be easier.

as DrBuck sugessted, you should use maskObj = Simulink.Mask.create(gcb) to create a mask for the currently selected block (gcb) or select an already existing mask by maskObj=Simulink.Mask.get(gcb). Then, add parameters to it:
par_name = 'foo'; % name
par_prompt = 'This is my enum constant'; %prompt
maskObj.addParameter('Prompt',par_prompt,'Name',par_name); % add parameter
The field you are looking for is called TypeOptions, but first you must find the correct parameter number by e.g.
par_idx = find(strcmp({maskObj.Parameters.Name},par_name)); % index of parameter
Set the mask parameter to 'popup' and create your enum values:
maskObj.Parameters(par_idx).Type = 'popup'; % type
maskObj.Parameters(par_idx).TypeOptions = {'Option1','Option2'}; % enum options
There you are ;)
HTH Julian

Related

Callback sharing data in Matlab

I have being to share data between several callback options in Matlab, however no succes so far. I have a gui with multiple tables which I use to get the input from the user. I have multiple callback functions for the different tables. I would like to use the data from table 1 and callback 1 together with the date in table 2 in callback two.
function MaterialProperties(Material, Data)
Material_data = get(Material, 'Data');
% Avoid bluehighlight in table
set(Material,'Data',{'dummy'});
set(Material,'Data', Material_data);
% Store variable in workspace
assignin('base','Material_data',Material_data)
%Mat_data = guidata(gcbo);
%for i=1:size(Material_data,2)
% Mat_data.MatData{i}=Material_data{i};
%end
% Save the change you made to the structure
guidata(gcbo,Mat_data)
assignin('base','Mat_data',Mat_data)
end
function Stacking_sequence(Layup, Data)
% I want to use the date (Material_data) of MaterialProperties here in this callback
layup_data = get(Layup, 'Data');
%overwrite data with a dummy and restore the old data afterwards, to force deselection
set(Layup,'Data',{'dummy'});
set(Layup,'Data', layup_data );
%store variable in workspace
assignin('base','layup_data',layup_data)
layup = strsplit(layup_data{1,1},'\');
assignin('base','layup',layup)
end
Can anyone help. I tried theMatlab help, but the suggestions stated there didn't work (maybe I implemented it wrong)
It looks like you simply need to retrieve the handles structure at the beginning of callback 2, like you did in the first callback:
Mat_data = guidata(gcbo);
after which it should be available in the 2nd callback. By the way this very line and the 3 lines following it are commented in your code is that a mistake?
Alternative solution:
As an alternative solution, you can use setappdata/getappdata to share data between function callbacks as well as in the command window, depending on where you store those data.
For example, if you save Material_data at the end of the 1st callback using something like this:
setappdata(0,'MatData',Material_Data); % Save in the Matlab root 0 (accessible everywhere), and give some dummy name)
Then at the beginning of the 2nd callback, you can retrieve the data using getappdata:
Material_Data = getappdata(0,'MatData');
and you're good to go. Instead of using the 0 root, you could also store the data in the GUI itself, using for example handles.FigureGUI or whatever the name of the figure is. Then the data would be available only if the figure is not closed/deleted. Play around with those and see what you prefer.
Hope that helps!

Matlab - using a function mulitple times in the same workspace, to add values and fields to a structure

I have a structure such as:
specimen.trial1 = 1
I now want to add another trial to the specimen, so that
specimen.trial1 = 1
specimen.trial2 = 2
I can do this without a problem within the workspace and command window. But, if I'm using a function to calculate the numbers for each trial (with dynamic fields), the new field and value erases the previous one. Eg:
function [specimen] = dummy(trial,value)
specimen.(trial) = value
end
run the function:
[specimen] = dummy('trial1',1)
then run the function again with different inputs, but keeping the structure intact in the workspace
[specimen] = dummy('trial2',2)
Instead of getting a structure with 2 fields, I get just one with Trial2 being the only field. Does that make any sense? What would like is to use the outputs of a function to progressively add to a structure.
Thank you,
Chris
Yes it makes sense, because you're creating a new struct specimen within your function.
Solution: pass the the previous specimen to the function as well.
function [specimen] = dummy(specimen,trial,value)
specimen.(trial) = value
end
and call:
[specimen] = dummy(specimen,'trial1',1)
or alternativly leave out the assignment at all and use the following
function [output] = dummy(value)
output = value
end
and call:
[specimen.trail1] = dummy(1)
which really depends on what you actually want to do. Put passing a name to a function which uses this name to define a struct is a little pointless unless you "use" that name otherwise. Also if you want to have input-dependent dynamic names you'd also go with the first alternative

Auto generate a name when saving file in Matlab?

I am working on a GUI and I have a file named 'work.mid'. The user can make some modifications to it and when they click the save button I want it to be saved as 'work1.mid' to 'c:\saved_datas\'. When they click that button second time, it should save it as 'work2.mid', on the third time 'work3.mid' and so on. Here's the code I have so far:
nmat = readmidi_java('work.mid');
Name = fullfile('c:\saved_datas\', '?????');
writemidi_java(nmat, Name);
Figuring out what should go at ????? is where I'm stuck.
The following code would work if you have no prior work*.mid or if you have any number of sequential work*.mid files inside c:\saved_datas\. Now, if the files are not in sequence, this code could be tweaked for that, just let me know if you would like to handle that case too.
Code listed here -
%// Parameters
org_filename = 'work.mid';
main_dir = 'c:\saved_datas\'; %//'
%// Your code
nmat = readmidi_java(org_filename);
%// Added code
[~,filename_noext,ext] = fileparts(org_filename)
filenames = ls(strcat(main_dir,filename_noext,'*',ext))
new_filename = strcat(filename_noext,num2str(size(filenames,1)+1),ext)
Name = fullfile(main_dir,new_filename)
%// Your code
writemidi_java(nmat, Name);
For achieving uniqueness of filenames, some also use timestamps. This could be implemented like this -
org_filename = 'work.mid'; %//'
main_dir = 'c:\saved_datas\'; %//'
[~,filename_noext,ext] = fileparts(org_filename)
new_filename = strcat('filename_noext','-',datestr(clock,'yyyy-mm-dd-hh-MM-SS'),ext)
Name = fullfile(main_dir,new_filename);
This could be done a couple of ways depending on how you have structured your GUI. You need to keep track of how many times the button has been pressed. In the callback for the button you could use a persistent variable ('count') and increment it by one at the start of the function. Then construct the filename with filename = ['work' num2str(count) '.mid']. Alternatively you could increment a class member variable if you have implemented your GUI using OOP.
To save the file use the 'save()' function with the previously constructed file name and a reference to the variable.
Check out the documentation for persistent variables, save, fullfile and uiputfile for extra info.

how can I get the current mouse position using a click. MATLAB

In this post,Matlab: How to get the current mouse position on a click by using callbacks , it shows something as follows:
function mytestfunction()
f=figure;
set(f,'WindowButtonDownFcn',#mytestcallback)
function mytestcallback(hObject,~)
pos=get(hObject,'CurrentPoint');
disp(['You clicked X:',num2str(pos(1)),', Y:',num2str(pos(2))]);
However, I cannot get the pos and use it in mytestfunction().
could anyone help ? Thanks !
If you are not using GUIDE and hence do not have the handles structure (see bottom), you can utilize the UserData property of the figure to pass any information:
set(gcf,'UserData',pos);
Then you can access pos from anywhere else via:
pos = get(gcf,'UserData');
See this MathWorks description of the UserData property, and this full example. From the first page:
All GUI components, including menus and the figure itself have a UserData property. You can assign any valid MATLAB workspace value as the UserData property's value, but only one value can exist at a time.
As a workaround to this limitation, you could assign a struct to UserData that has all the properties needed by your program stored in different fields.
A detail I left out in the commands above is the figure/object handle (you probably don't actually want to use gcf). In mytestfunction you have it stored in f. In the callback you can find the parent figure of the hObject by:
f = ancestor(hObject,'figure');
One way to use the above approach is to simply wait for changes in the UserData property:
function mytestfunction()
f=figure; set(f,'WindowButtonDownFcn',#mytestcallback)
maxPos=10; cnt=0;
while cnt<maxPos, waitfor(f,'UserData'); pos=get(gcf,'UserData'), cnt=cnt+1; end
function mytestcallback(hObject,~)
pos=get(hObject,'CurrentPoint');
set(ancestor(hObject,'figure'),'UserData',pos);
Note that another way to handle events is to implement a listener to respond to the clicking event, but the WindowButtonDownFcn callback should work fine.
Originally, I was thinking GUIDE, in which you would have the handles structure. This is one purpose of the handles structure. Store the position in a field of handles, and update it:
handles.pos = pos; % store it
guidata(hObject,handles); % update handles in GUI
Then back in mytestfunction or whatever other callback needs access to pos, you can use handles.pos, if the handles structure is up to date.

Uitable set callback to column name

I have a uitable in MATLAB and currently I have callback functions to every cell.
I have been trying for a while now to set a callback to the column and row name, but with no success. More specifically if the user clicks on a particular column name, is it possible to call a function?
Please let me know if you need any more info... I would appreciate any help.
Thanks in advance!
First of all, you have to register callback to table header object. It is JTableHeader object, and you can access with findjobj function.
I created demo for registering callback function for column name click event.
This callback function is used to modify the clicked column name. It is tested on Matlab R2015a.
To run this demo, download findjobj file, and put in the same folder. Then run below code.
function TableDemo()
% Demonstration of clickable columnname.
% In this example, we use the click event to modify column name.
figure('menubar','none','numbertitle','off', 'Name', 'DEMO');
myTable = uitable('Data', magic(4), 'ColumnName',{'A','B','C','D'}, 'ColumnWidth',{50});
% Accessing underlying java object.
jscrollpane = findjobj(myTable);
jtable = jscrollpane.getViewport.getView;
jheader= jtable.getTableHeader(); % Here, you got JTableHeader object.
h=handle(jheader, 'callbackproperties');
% Set a matlab function as MouseClickedCallback
set(h, 'MouseClickedCallback', {#onHeaderClick, jtable, myTable});
end
function onHeaderClick(src, evt, jtable, hTable)
if(get(evt, 'ClickCount') > 1)
disp('header double clicked');
% Get view index from current mouse point, and convert it to
% model index. Then add 1 because Matlab index starts from 1.
index = jtable.convertColumnIndexToModel(src.columnAtPoint(evt.getPoint())) + 1;
prompt={'Column Name'};
title='Enter column names';
numLines=1;
defaultAnswer=hTable.ColumnName(index);
options.Resize='on';
options.WindowStyle='modal';
newName=inputdlg(prompt,title,numLines,defaultAnswer,options);
if ~isempty(newName)
hTable.ColumnName(index) = newName;
end
end
end
This seems to be impossible using only the standard MATLAB interface to uitable.
However, you can access the underlying Java JTable as described on undocumentedmatlab.com. Using the JTable instance you should be able to install an appropriate event handler, see this question on SO and this other article on undocumentedmatlab.com.