I have a uitable and a function that returns the id of the item once the user clicks on its respective row. The id is also a global variable, as it is used over various functions.
The idea is that I create an array with all the user chosen items from the uitable, terminated when save playlist is clicked. Ex. if the user chooses items with id's 5, 7, 9 in succession and then clicks the 'save playlist' button, I want the array to hold
5 7 9
I thought that the best way to do this is using a while loop. The while loop should check if the save button has been clicked. I have a global variable that changes once the program goes into the 'save playlist' button callback function.
The problem is that once the user chooses an ID, the array keeps on iterating until the user chooses another ID, and will keep on iterating etc etc until the save button is clicked. Ex: the user clicks on ID 5, the array will record 55555555555555... recurring until user clicks on ex. ID 7: 555555555555557777777777, since the program keeps on looping and looping without pausing.
The code looks like this:
while (keeprunning)
idvec = [idvec id];
end
keeprunning is a global variable and is initialized with a value of 1 in the opening function. It is changed to 0 in the 'save playlist' callback function.
I thought to fix this problem by introducing a new global variable call it 'itemselected' that initializes in the opening function as 1 and changes to 1 again everytime the cell selection callback is called. After an ID is added to the array in the while loop, this variable resets to 0.
The idea is that the loop should iterate while the 'save playlist' button is not clicked (controlled by the keeprunning global variable) BUT the program should not let the while loop iterate until the user clicks on another row i.e. until the cell selection call back function is called again.
I need something like this but this doesn't work (infinite loop). Or else some other suggestion??
while (keeprunning || trackselected)
idvec = [idvec id];
trackselected = 0;
end
Thank you in advance
To record a history of the user selecting fields inside a uitable, do something like this:
function tbl_example
% Create the figure
fh = figure('Visible', 'off')
ud.selected_list = [];
d = gallery('integerdata',100,[10 3],0);
t = uitable(fh,'Data',d,'ColumnWidth',{50}, 'cellselectioncallback', #cell_history );
set( fh, 'visible', 'on' );
set( fh, 'userdata', ud );
return
function cell_history( h, event_data )
fh = get( h, 'Parent' )
ud = get( fh, 'userdata' );
indx = event_data.Indices;
ud.selected_list = [ ud.selected_list h.Data( indx(1), indx(2) ) ]
set( fh, 'userdata', ud );
return
Then, when you hit your "playlist" button, just read the userdata field in the parent figure handle.
I fixed this problem after stopping from thinking about it and doing something else; fascinatingly it came to me as a very simple method.
What I did was create a global array in the CellSelectionCallback. The program checks whether the program was initiated to create a playlist through the use of a boolean variable in the 'create a playlist' button callback, and in this case the global array is appended with the chosen row's item's id. Since the array is global it can be used over different functions. The array is flushed after 'save playlist' button is clicked.
It was as simple as that, however, sometimes simple is not that simple, especially after about 10 hours of seeing matlab code.
Related
I have created a very simple GUI in appdesigner (Matlab) with one dropdown menu. Additionally, I took the code that got generated (under 'Code View' tab) and pasted that in a normal .m file (because, I want to further add some more contents to this code). My question is how can I access certain variable from this self generated code, so that I can play with that value outside of the main class?
For example:
In App class, for this dropdown menu section, following line of code got generated:
app.ColorDropDown = uidropdown(app.UIFigure);
app.ColorDropDown.Items = {'Red', 'Blue', 'Yellow'};
app.ColorDropDown.Position = [277 178 140 22];
app.ColorDropDown.Value = 'Red';
Outside of this app class: Depending upon the value that was selected in this dropdown menu, I want to capture that in a normal variable, and show some other results based on the color selected
Thanks
It seems like the essence of your question is about sharing data between GUIs as you state you want to "show some other results based on the color selected".
Note: MATLAB documentation cautions: Do not use UserData property in apps you create with App Designer.
The documentation goes on to recommend approaches for sharing data among app designer apps and creating multiwindow apps in app designer.
For the sake of completeness, I'll provide a detailed example, different from the MATLAB documentation, of how this can be accomplished entirely within the App Designer.
Setup
I created 2 app designer apps, controlGUI.mlapp and plotGUI.mlapp. Running controlGUI() loads both mlapp files at once and then allows you to control aspects of the plotGUI from callbacks in the controlGUI. Of course, the plotGUI could do anything but I have it changing visual aspects of the plotted data.
Details
For simplicity, I am setting the controlGUI be the main application. I can do this be adding a property in controlGUI named PlotWindow and having it contain the output of calling plotGUI(); the other mlapp. I also have a callback function I recycle for the three dropdowns that updates a DATA property in the plotGUI. This DATA property has a listener attached that fires a callback to update the uiaxes object.
The plotGUI
The magic is really in this object. That is, I created a property named DATA and altered the access. By setting the property access SetObservable = true, we can then add a PostSet event listener, which I stored in a private property called dataChangedListener during the startupFcn.
properties (Access = public, SetObservable=true)
DATA % A struct with xdata, ydata, fields. Other line prop fields ok
end
properties (Access = private)
dataChangedListener % listener object for when DATA is changed.
end
Then the startup function, startupFcn, initializes the DATA property with some (arbitrary) data as struct then adds and enables the PostSet listener.
% Code that executes after component creation
function startupFcn(app, data)
if nargin < 2
% create a default dataset
data = struct();
data.xdata = linspace(0,1,1001);
data.ydata = sin(data.xdata.*2*pi*10);%10hz signal
data.color = app.Axes.ColorOrder(1,:);
end
app.DATA = data;
% plot the data
line(data, 'parent', app.Axes);
% add and enable PostSet event listener for the DATA property
app.dataChangedListener = addlistener(...
app,'DATA','PostSet', ...
#(s,e) app.updatePlot ...
);
app.dataChangedListener.Enabled = true;
end
The PostSet listener calls the method app.updatePlot(), so we have to add this method to our app. This method will get called whenever anything in app.DATA gets modified. So I created the "function" (as the Code Browser calls it) which simply deletes the axes children (the existing line) and uses the low-level version of line() to draw a primitive line based on the app.DATA struct object:
function updatePlot(app)
%clear plot
delete(app.Axes.Children);
% draw the new plot
line(app.DATA, 'parent', app.Axes);
end
Yes, line() will accept a struct which has field names that correspond to valid line properties but I can't seem to find the reference specifically. But if you read about the inputParser object... sorry, getting off topic.
The controlGUI
This object is simpler. It holds the plotGUI in a property, allowing direct access to the plotGUI properties and public methods.
properties (Access = public)
PlotWindow = plotGUI() % holds the reference to the plot window
end
I also created an updatePlot callback method here, different from the plotGUI method, bad naming on my part. Nonetheless, this control method takes the values from controlGUI dropdowns and then modifies the plotGUI DATA struct, stored in app.PlotWindow.DATA. So this one callback is called whenever any of the dropdowns are changed (ValueChangedFcn). Consequently, the PostSet listener for the DATA struct will fire the callback to update the plot accordingly.
% Value changed function: LineColor, LineStyle, MarkerStyle
function updatePlot(app, event)
mStyle= app.MarkerStyle.Value;
lStyle= app.LineStyle.Value;
lColor = app.LineColor.Value;
d = app.PlotWindow.DATA;
switch mStyle
case 'none'
d.marker = 'none';
case 'circle'
d.marker = 'o';
case 'diamond'
d.marker = 'diamond';
case 'point'
d.marker = '.';
end
switch lStyle
case 'none'
d.linestyle = 'none';
case 'solid'
d.linestyle = '-';
case 'dashed'
d.linestyle = '--';
case 'dotted'
d.linestyle = ':';
end
d.color = lColor;
% set the data back into the plotGUI
% The plotGUI's DATA PostSet listener will update the actual plot
app.PlotWindow.DATA = d;
end
You aren't supposed to copy/paste the code outside of the code editor with App Designer. If you want to add your own code, you should add a new function to your class using the "Function" button in App Designer. The app can also call any other matlab function so you could just pass the information from the app to another function by calling it inside the App Designer code
For example see this example from a demo I created. It uses a drop down ChartTypeDropDown to determine what type of chart it should draw. I added a new function called DrawChart which uses the data from the GUI to draw a graph depending on the values selected/entered in to the various boxes.
function results = DrawChart(app)
chartType = app.ChartTypeDropDown.Value;
chartTime = app.TimeEditField.Value;
chartA = app.AEditField.Value;
chartB = app.BEditField.Value;
chartC = app.CEditField.Value;
t = [0:0.1:chartTime];
if strcmp(chartType,'Sin')
y = chartA * sin(chartB*t)+chartC;
elseif strcmp(chartType,'Cos')
y = chartA * cos(chartB*t)+chartC;
elseif strcmp(chartType,'Exp')
y = exp(t);
else
y = x;
end
ax = app.UIAxes;
ax.Title.String = chartType;
plot(ax,t,y);
end
This function is called by the callback linked to a button
function DrawButtonPushed(app, event)
DrawChart(app);
end
Note how I call regular matlab functions such as sin/cos/exp. These could just as easily be a Matlab function that you have written.
I am creating a GUI using GUIDE in MATLAB2015. I have a drop down menu which the user selects a message to view, after that they click the Add push button to add the message name to a list box and display the data it contains in a table.
My problem is, if I want to add more than one message, instead of adding that message, it overwrites the previous one. Below is my current code.
addData = getappdata(handles.msgSel_menu, 'Data');
boxMsg = get(handles.msgSel_menu,'String');
boxMsgVal = get(handles.msgSel_menu,'Value');
set(handles.activeDataBox,'String',boxMsg{boxMsgVal});
set(handles.data_table, 'Data', addData);
Apologies if this has been done multiple times before, but as I'm relatively new to MATLAB I could do with a little explanation of any code that fixes my issue.
You have to get the initial string cell from your listbox then you add the desired element to the cell. From the resulting cell variable, you update the listbox using the set command.
Here is a simple example.
addData = getappdata(handles.msgSel_menu, 'Data');
current_data = get(handles.activeDataBox, 'String'); % get the current string
new_data = current_data; % set new_data to the initial string ...
new_data{ length(current_data) + 1 } = addData ; % ... then you add the desired element by incrementing the cell
set(handles.activeDataBox, 'String', new_data); % update your listbox
EDIT : The code has been updated.
I am using the KeyPressFcn method for the edit box to test whether enter is pressed. I can use call_back but there is not event_data call_back function.
If I press one time on the button of Enter, than text doesn't rewrite, but if I double time on the button of Enter (speedly), than text rewrite.
What reasons this behaviour?
function WriteData(val, name, ind)
global solver;
switch ind
case {14, 15}
value = strcat('#(t)', val);
case 16
value = strcat('#(x)', val);
case {17, 18}
value = strcat('#(x,t)', val);
end
eval(strcat('solver.', name, ' = ', num2str(val) ) );
function edit1_KeyPressFcn(hObject, eventdata, handles)
val = get(hObject, 'String');
[~, ~, var] = GetActiveData(handles.listbox1);
ind = get(handles.listbox1, 'Value');
if (strcmp(eventdata.Key, 'return') )
WriteData(val, var, ind );
end
According to the documentation found in UIControl Properties (http://www.mathworks.com/help/matlab/ref/uicontrol-properties.html;jsessionid=49b9dc47d9f964ec95a4fe2cc9f3),
This callback function executes when the uicontrol object has focus and the user presses a key. If you do not define a function for this property, MATLAB passes key presses to the parent figure. Repeated key presses retain the focus of the uicontrol, and the function executes with each key press. If the user presses multiple keys at approximately the same time, MATLAB detects the key press for the last key pressed.
More simply put, the Callback will be called when you press enter the first time and the KeyPressFcn will be applied on the second press.
i' m try to create a simplex and fast gui on matlab. I made a button with a callback function, but I don' t know how to bring up a string that depends upon the result of the function. For example if the result of the function is 1, must appear the string "All ok!", if the result of the function is 0, must appear the string "It' s wrong!!!".
You can create a Static Text object or an inactive Edit Text object and modify the String property from your script with:
set(handles.edit_text, 'String', 'your_string')
An alternative to the static/edit uicontrol is to use a msgbox or an errordlg to display the result to the user depending on the result of your function.
if myFunction
msgbox ( 'All Ok' );
else
errordlg ( 'Error in Function', 'Error' )
end
I created a matlab GUI using GUIDE.
I created several panels with static text boxes inside them. I would like to write values to all the boxes once I push an "update" pushbutton.
for instance, I would like to write to a box with tag AV1, and the text box is inside panel "uipanel2".
Both ways give errors:
set(handles.AV1,'String','hi');
The above code does not work as it says the field does not exist. This makes sense as I need to access the panel first.
So below I access the panel, but how do I get to its children?
set(handles.uipanel2.AV1,'String','hi');
this code gives the following error: Attempt to reference field of non-structure array.
Children is a field so if you want the children you can try get(handles.uipanel2,'Children') and it will give you an array with handles to the children. It will look like numbers to you in the same way that the handle to uipanel2 looks like a number.
Here's an example:
function testGUI
fig = figure(1);
panel = uipanel(fig);
tbox = uicontrol('Style','text','String','hello','parent',panel);
ch = get(panel,'Children')
get(ch,'Type')
get(ch,'String')
end
It shows how to get the Children of the panel object with ch = get(panel,'Children') which should print something to console that looks like:
ch =
182.0011
And to show you that this ch is in fact a handle to the static textbox that is a child of the panel, I've printed out the type and string of ch to console which should be the following:
ans =
uicontrol
ans =
hello
And here's an example of how to get the string in a textbox to update when you press a Push Button:
function testGUI
fig = figure(1);
panel = uipanel(fig);
tbox = uicontrol('Style','text','String','hello','parent',panel);
button = uicontrol('Style','PushButton','String','push me',...
'Position',[100 100 50 25]);
set(button,'Callback',#mycallback)
function mycallback(src,eventdata)
set(tbox,'String','updated')
end
end