1
votes

I have a GUI with a UITable (built in GUIDE). I read in two numerical values and through a series of steps convert the floating-point value to a string. I want the user to have the ability to click on a specific cell in the UITable containing the two values (now as strings), and read those values back as floating-point values. For whatever reason, I can only ever get my code to read in the first floating-point value. My code (in order) is below.

Step 1: Access data and convert to string and place string in corresponding column.

function fillAnnotRangeInfo(obj, selectedAxes)

    selectedAxesTag = selectedAxes.Tag; 
    rawAxesTag = obj.rawDataDisplayAxes.Tag; 
    psdAxesTag = obj.psdDataDisplayAxes.Tag; 

    % Depending on which axes the user clicks on, the units will either be Hz or s 
    if strcmp(selectedAxesTag, rawAxesTag)
        dataRange = strcat(num2str(obj.t1), {'s'}, {' - '}, num2str(obj.t2), {'s'});
    elseif strcmp(selectedAxesTag, psdAxesTag)
        dataRange = strcat(num2str(obj.t1), {'Hz'}, {' - '}, num2str(obj.t2), {'Hz'}); 
    end 

    obj.nextRow.AnnotRange = dataRange; 

end  

Step 2: Determine if user clicked in correct cell and attempt to read two floating-point values out.

% --- Executes when selected cell(s) is changed in existingAnnotationsTable.
function existingAnnotationsTable_CellSelectionCallback(hObject, eventdata, handles)
% hObject    handle to existingAnnotationsTable (see GCBO)
% eventdata  structure with the following fields (see MATLAB.UI.CONTROL.TABLE)
%   Indices: row and column indices of the cell(s) currently selecteds
% handles    structure with handles and user data (see GUIDATA)

% AE = handles.UserData; 

Indices = eventdata.Indices; 

% Determine if column is column of interest 
if Indices(2) == 4
    rangeData = handles.existingAnnotationsTable.Data;
    rangeData = rangeData{Indices(1), Indices(2)}; 
    annoRange = sscanf(rangeData, '%f')
else

end


return

Ultimately, the result I get is if I have a string exactly as follows: "7.4250Hz - 32.502Hz" (or replace Hz with "s"), my program will only produce "7.4250". Nothing more nothing less. I've tried textscan, sscanf, and strread. Each one I explicitly set my filter to floating-point (%f). With strread I tried setting it to cycle through more than once (strread('string', %f, 2)). I have no idea what else to do or try or change.

REGARDING THE ANSWERS BELOW FOR FUTURE READERS: Technically any one of the answers is "correct" (I tried them). They all are applicable to a slightly different situation. Ben's answer is good for getting a result in a single step if you have a fixed format. My own answer works to break down a string across multiple steps giving access to the data each step (useful for performing multiple operations) whilst still being able to deal with varying formats. Andras' answer is useful for getting it done in one step providing a result instantly whilst still being able to deal with varying formats.

3
Have you tried the trivial sscanf(rangedata,'%fHz - %fHz')? - Andras Deak -- Слава Україні
In retrospect my comment might have come through as condescending, which was not at all my intent. By trivial I just meant that if I would run into this problem I would just try this before looking at the help (since I don't even try to memorize the differences of C and matlab sscanf, and I'm too lazy to start with reading long helps). - Andras Deak -- Слава Україні
@AndrasDeak no worries! - DeeWBee

3 Answers

2
votes

Have a look at the sscanf documentation here, if you want a set of floats from a string you need to specify the format. Here is an example from this page similar to yours:

tempString = '78°F 72°F 64°F 66°F 49°F';

degrees = char(176);
tempNumeric = sscanf(tempString, ['%d' degrees 'F'])'
tempNumeric =
    78    72    64    66    49

In your specific case you could try:

val = sscanf(rangedata,'%fHz - %fHz')
1
votes

Ben's answer is the correct answer in the case of a fixed format. However, in my case, my format changes. Therefore my proposed solution (tested and verified) is to use strtokto "break-down" the string into two separate strings, making it much more manageable and easier to parse. Code below. Thoughts?

% --- Executes when selected cell(s) is changed in existingAnnotationsTable.
function existingAnnotationsTable_CellSelectionCallback(hObject, eventdata, handles)
% hObject    handle to existingAnnotationsTable (see GCBO)
% eventdata  structure with the following fields (see MATLAB.UI.CONTROL.TABLE)
%   Indices: row and column indices of the cell(s) currently selecteds
% handles    structure with handles and user data (see GUIDATA)

% AE = handles.UserData; 

Indices = eventdata.Indices; 

% Determine if column is column of interest 
if Indices(2) == 4
    rangeData = handles.existingAnnotationsTable.Data;
    rangeData = rangeData{Indices(1), Indices(2)}; 

    [dataStart, dataRemain] = strtok(rangeData); 
    % In this case, since there will be just a '-' as the token...
    % ...we don't care about the token and only save the remainder. 
    [~, dataEnd] = strtok(dataRemain); 
    dataStart = sscanf(dataStart, '%f') 
    dataEnd = sscanf(dataEnd, '%f')

else

end
1
votes

Motivated by my own comment about using regexp before sscanf, here's a solution which only uses the former:

str=regexp(data,'(?<dataStart>[\d\.]+)[^\d\.]+(?<dataEnd>[\d\.]+)','names');

after which str will have the fields dataStart and dataEnd as strings (so you'll need num2str afterwards). Note that this only works for simple floating point numbers (but of course the regexp could be further complicated ad nauseam if necessary). The upside is that it can be adapted to more tricky input strings, for instance the above will treat any number and kind of non-numeric (and non-dot) text between the first two numbers. The downside is regexp.