keeping track of multiple lines properties

Hi,
I have a gui that makes multiple lines on a single axis. The number of lines depends on the number of "channels" the user selects. I have given the user the ability to change the color of the line and marker style
I have made a structure to keep track of the properties/characteristics of the line. Some of the properties are the line handle, color, and marker style. An array of this structure is created when my plot function is called.
The code:
if true
% function line_edit(src,evnt,num_select,line_struct,handles)
% src - the object that is the source of the event
% evnt - empty for this property
%get selection type
sel_type = get(gcbf,'SelectionType');
%handle to parent axes
h_axes = get(src,'Parent');
%handle to all lines in axes
line_handles = get(h_axes,'Children');
if ~isempty(getappdata(handles.import_data,'line_struct'))
line_struct = getappdata(handles.import_data,'line_struct');
end
%call style_change function which creates structure with line info
line_struct = style_change(sel_type, num_select, line_struct, src);
for i = 1:num_select
if strcmpi(line_struct(i).ChanName, get(src,'DisplayName'))
%set line color
set(src,'Color',line_struct(i).color)
%set line style
set(src,'LineStyle',line_struct(i).linest)
%set marker style
set(src,'Marker',line_struct(i).markerst)
end
end
setappdata(handles.import_data,'line_struct',line_struct) end
I also created a button down function for the lines to dynamically change the color and marker style. When this function is called I use setappdata to save the new line structure and for access by other functions.
However,I am having a hard time keeping the right structure with the right line when I add or delete new lines to the axis. For instance the user has selected channels 1 and 3. The user then changes line 3 from red to green and the marker style from 'none' to '+'. Then the user selects channel 2. All the information from 3 is in 2 and 3 is set to default values. Same thing happens when the user deletes a channel. I am trying to make a unique identifier for each line so the integrity of the structure remains. Any thoughts on how I should go about this?
If my intentions are confusing you, please ask me questions. And if you need a better idea of what my code is doing, please ask me questions
Thanks, Chad

4 Comments

Why don't you just use the line handle array and retrieve/set properties for the requested handles?
That works when the user does not add or delete a channel after the color or marker style has been changed. When a channel is added (or deleted) the handle to each line changes and thew line structure needs to take that into account in order to set the new lines to its previous properties
Why do they all change instead of just deleting one or adding one?
Why do all the handles change? I guess it is just how matlab assigns handles. The value for the handle for each line only changes when a channel is added or deleted. So if I want to keep the properties of all the other lines I need to know the new handles in order to set the new lines to the previous properties. These properties (line style, marker style, color, etc.) are reset when I get the new handles so I need to update them(the properties)by using the previously stored line structure.
One of the lineseries properties is UserData so I am thinking of setting the UserData to hold this line_structure. But then that leads to the question how do I make sure that I have the right handle to individually set the UserData appropriately?
I am out of ideas and have been getting frustrated lol.

Sign in to comment.

Answers (1)

Your approach sounds way to complicated - there's no need to make a separate structure with all the object properties. Storing and using the same variable in two different locations (object properties and your extra struct) is very likely to become a disaster.
If I understand correctly, the user can select a trace by clicking it, and then change the properties elsewhere in the GUI. The only thing the ButtonDownFcn needs to do is mark this trace as the 'current' trace. Something like this.
function lineButtonDownFcn(hObject,evnt)
%save line as current in GUI appdata
hGUI = getappdata(0,'yourguihandle')
setappdata(hGUI,'currentTrace',hObject');
In the functions modifying line properties, you simply retrieve the currentTrace handle from appdata, and set the property of choice.
hGUI = getappdata(0,'yourguihandle');
currentTrace = getappdata(hGUI,'currentTrace')
set(currentTrace,'Color','r')

12 Comments

Im not sure I am following your approach. Currently, I pass the address of the line_edit function into the line buttondwnfcn. This takes care of changing the selected line's color and line style and marker style. But when the plot updates, for example by the user changing the number of FFT points, those changes just made will be lost because my plot update function gets the line handles. So I need a way to "remember" what changes have been made to which line.
These changes are already 'remembered' by the line itself, so you can just get the information from their. I assume you have one update plot function that goes over all the settings every time? You could just retrieve the current settings with get:
currentSettings = get(currentTrace);
However, in your situation it might be better to only update the property you want to change. There's usually no need to re-plot a line just to update its color or even its X or Y values. Just updating the object property is much faster, and easier to debug.
Yeah I avoided replotting everytime a style change was made as it speeds up the program.
After rereading your suggested code I think I understand it. But Im not sure how well it would work when another line is added. I may have already tried something similar and it tries to access a handle that doesnt exist so there isnt a way to retrieve the previously stored properties. That is why I went with a structure to temporarily hold the values
You mean that the user can toggle between whether a line is displayed or not? Instead of deleting the line, you can also set the line property 'Visible' to 'off'. This way the handle will still exist, and you can retrieve all its properties with get.
I just got an idea! When the initial plot(s) are made I store in one of the structures fields the channel name. I then use this channel name to set the DisplayName property for use by the legend. The display name stays with each line so I can use findobj(gca,'DisplayName',channame) to get the right handle to each line.
There are two ways my 'plot_update' function is called. The changing of a slider or a selection change in the channel name list box. My button down function only affects the selected line and does not call the 'plot_update'. It only sets the properties of that line using the source handle.
I am still trying to figure out how to keep properties when the 'plot_update' function is called. I am having problems specifically when a channel is added or deleted because the handles to the other lines change.
Arthur
Arthur on 6 Sep 2013
Edited: Arthur on 6 Sep 2013
Can you post a snippet of your code from the plot function here? There's no need for the other handles the change.
if ~isempty(getappdata(handles.import_data,'line_struct'))
%create line handle(s) for acces to properties
line_handle = plot_options(gca, y_scale, x_scale, pxx, F, t_unit, grid_m, colors);
line_struct = getappdata(handles.import_data,'line_struct');
for i = 1:num_select
name_temp(i) = line_struct(i).ChanName;
end
if ischar(name_temp)
name_temp = cellstr(name_temp);
end
itemp = find(~ismember(name_temp,channame(index)));
line_struct(itemp) = [];
end
for i = 1:num_select
set(line_handle(i),'color',line_struct(i).color)
set(line_handle(i),'lineStyle',line_struct(i).linest)
set(line_handle(i),'marker',line_struct(i).markerst)
end
line_struct = line_structure(num_select, line_handle,channame(index));
setappdata(handles.import_data,'line_struct',line_struct)
else
%create line handle(s) for access to properties
line_handle = plot_options(gca, y_scale, x_scale, pxx, F, t_unit, grid_m, colors);
end
%update line_Structure
line_struct = line_structure(num_select, line_handle,channame(index));
end
Can you explain a bit more how a user can change a property? So how is the line selected, how is the new property value selected, how do you store this information. To me it seems that you make it to complicated at this stage. I have made similar GUIs in the past, and there really is no need to keep a line_struct like you do here.
Of course. The button down function is linked to a 'line_edit' function. This line_edit takes input parameters: src(the handle to line that was selected), line_struct (previosuly created structure), and handles (for access to other functions). Depending on the selection type on the line (right click or left) uigetcolor is called or I change the line to '+' markers and vice versa. The line structure is updated to reflect these changes. Then I use src handle and line struct to set that line's properties. Finally I setappdata with the new line structure
I am trying to find a way to do this without having to use the struct but I simply cannot. So with that said I have rewritten some code using the struct. I havent compiled it yet but just wanted to show the jist of it:
if isempty(getappdata(handles.import_data,'line_struct'))
%initial line handle
line_handle = plot_options(gca, y_scale, x_scale, pxx, F, t_unit, grid_m, colors);
%create structure to hold information on each line
line_struct = line_structure(num_select, line_handle, channame(index));
%create appdata to be used by other functions
setappdata(handles.import_data,'line_struct',line_struct)
%set appropriate name for each channel to be displayed for legend
for i = 1:num_select
set(line_handle(i),'DisplayName',line_struct(i).ChanName);
end
else
line_struct_temp = getappdata(handles.import_data,'line_struct');
tempSize = max(size(line_struct_temp));
for i = 1:num_select
line_handle(i) = findobj(gca,'DisplayName',channame(index(i)));
for j = 1:tempSize
if strcmpi(get(line_handle(i),'DisplayName'),line_struct_temp(j).ChanName)
line_struct_temp(j).handle = line_handle(i);
end
end
end
line_struct = line_structure(num_select,line_handle, channame(index));
for i = 1:num_select
for j = 1:tempSize
if line_struct_temp(j).handle == line_struct(i).handle
line_struct(i) = line_struct_temp(j);
end
end
set(line_handle(i),'color',line_struct(i).color)
set(line_handle(i),'lineStyle',line_struct(i).linest)
set(line_handle(i),'marker',line_struct(i).markerst)
end
end end
Ok your code is only becoming more and more complicated. Also, you're still updating all the properties of all lines every time, which you don't have to. Let me show you how I would create such a GUI.
First the plot function. This function will be called once, just after loading the data
function update_plot()
%get the data and handles
hGUI = getappdata(0,'myGUI');
handles = getappdata(hGUI,'handles');
data = getappdata(hGUi,'data');
%plot the data with default appearance
hold(handles.axes,'on')
for i = 1:length(data)
handles.line(i) = plot(handles.axes,data(i).x,data(i).y,...
'ButtonDownFcn', @selectTrace);
end
%store handles
setappdata(hGUI,'handles',handles)
function selectTrace(hObject,~)
%store the handle of the selected trace as current
hGUI = getappdata(0,'myGUI');
setappdata(hGUI,'CurrentTrace',hObject);
I'll assume you have some popupboxes and/or tickboxes to set the properties of the lines. For instance, the Callback function for a popup setting the color of the line will look like this
function popup_selectColorCallback(hObject,eventdata,handles)
%get the color that was selected in the popup
switch get(hObject,'Value')
case 1
%red
color = 'r';
case 2
%green
color = 'g';
case 3
%blue
color = 'b';
end
%get the currently selected trace, and update its color
hGUI = getappdata(0,'myGUI');
currentTrace = getappdata(hGUI,'currentTrace');
set(currentTrace,'Color',color)
And that's it. As you see, I only update the property that I want to change, and just forget about the others. In this way, you do not have to collect all the properties of all the lines.
Deleting a line also won't be a problem. Because once it's deleted, the user can not click on it anymore, so it will never become your currentTrace.

Sign in to comment.

Asked:

on 5 Sep 2013

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!