Can you detect when a title is added to a plot?

I have a GUI where a single figure has multiple pannels. Graphs can be added to these panels at any unknown time by a user. I want to populate a popup menu with the titles of graphs (axes) as they are added to these panels by the user. User population is to work the same as adding a plot to a standard figure in that after creating the figure, the code really has no idea at what point the user will add the plot and the title so there is no post add title point at which to pull the title. It would instead need to be able to react to the title being added. I have tried manualy creating a blank axes on the panel for which the user will plot to next so that the next plot is directed there and attached a propertly listerer to the string property of the title object of the axes. This should work except that it doesnt due to new plots to an existing axes result in the old axes title being deleted in the background with no clear way to find the handle to the new title. Any ideas on a good way to do this?

7 Kommentare

Jan
Jan am 27 Feb. 2019
What exactly is a "Graph" and how does the user add it? Would it be easy to create a button called "Add Graph" and insert some code inthe callback, which delivers the title to where it is needed? I'd recommend to avoid relying on magic side-effects and an automatic recognition. A straight-forward well defined way is smarter.
On the other hand, it should be easy to check all panels for included titles automatically. If you provide some code to create a minimal working example, it is much easier to suggest the needed additions.
Dan
Dan am 27 Feb. 2019
Thanks for the reply Jan! Unfortunatly I am not allowed to release any code without going through a review process so it would not be easy to provide a code example. Picture this as an extension of a figure. You build a figure with the command "figure" and then to plot to the figure you simply call the command "plot" or "surf" or whatever plotting command you like. The plots are added from a users code but the only way that the figure object would be able to know that plots have been added to it is by detection. The figure could have things added to it right after creation or hours after creation, it would have no way of knowing when the user would add to it therefor it can only be reactive.
Think of my GUI as working the exact same as what was desrived above. The object is created resulting in a figure that the user can add plots to. They call an add method which creates a new panel on the main figure and this new panel acts the exact same as the figure I described in the paragraph above. A user can plot to this panel simply by calling the plot command and insead of going to a figure like above, the plot goes to the panel. So the panel in this case acts identically to the newly created figure from the last example. It has no idea when the user will call the "plot" command (it is set up so that the new plot is directed to the panel, so the user would not need to provide the plot command with a handle to direct it). When the user does finally call the plot command and creates the plot they are likely to add a title to the plot and I want to capture this for the list. I have this working currently but it requires a call to a "build" method after all plots have been added to collect the titles. I would like to set up the code so that a build method is not needed and the gui simply updates as new content is added.
Hope this helps clear up the goals of the GUI, let me know if this makes sense and if you have any ideas.
Dan
Dan am 27 Feb. 2019
I should further add that the point of my GUI is to completely replicated the functions of a standard matlab figure but instead do it with multiple panels inside a single figure such that each panel is to behave as a standalone matlab figure would (which I have working).
Because of this we can ignore the set up of my GUI and instead think of adding a plot with the plot command to a standard matlab figure and then adding a title to the plot and having some code recognize that the title was added to the axes contained in the figure. One thing I know would work would be to make a wrapper function to the builtin "title" function that handles this bit and then calls the builtin title function afterwords to handle the creation of the title. While this would work, I am looking to keep all the code self contained in a single classdef file so am interested in other options.
Dan
Dan am 27 Feb. 2019
For a simple example consider the often used matlab example:
figure;
surf(peaks);
title('Peaks Example')
I would like to set up code such that the first of the three lines has been called so only the figure currently exists. Then some new coded is added before the user types "surf" and "title" such that when the user types these commands a reactive code element grabs the title the user entered in the "title" command. The goal is that the user does not have to use any modified code for plotting ie in this example the surf and title commands are called as typed above.
So our example becomes:
figure;
% Code that is set up to dected a title that is added
surf(peaks);
title('Peaks Example')
This simple example does include the issue that an axes does not exist yet, but new axes detection can be easily performed by listeners or the 'DefaultAxesCreateFcn' route. I have succesfully implimented both. The current set up I have is more like this:
figure;
axes;
% Code that is set up to dected a title that is added
surf(peaks);
title('Peaks Example')
where axes is used to ensure that the next plot goes to that location. Uncessary in this example but when done on a panel it ensures that the next plots go to the panel instead of the figure that the panel is on. Then any plots the user makes goes to the panel. Any subplots will also go the the panel replacing the original axes with the new subplot axes as would happen in this example if the surf commands were replaced with some subplot plots instead
I should further add that the point of my GUI is to completely replicated the functions of a standard matlab figure but instead do it with multiple panels inside a single figure such that each panel is to behave as a standalone matlab figure would (which I have working).
Ummm, it seems unlikely that you have it fully working. Standard MATLAB figures are not just graphics containers: figures also have behavior associated. For example, bode() forces the behaviour for the figure to change, and it does so in a way that is not compatible with having multiple panels each with different behaviour.
There is a way to do it in MATLAB, using routines that have little documentation, but you would have mentioned those if you were using them, as they have a bunch of complications.
Dan
Dan am 27 Feb. 2019
Im sure it is quite likely there are some unique cases that I have not completely handled, but for the case of bode() I would say that it functions just fine from what I am aware of and have tested with the one exception being that the title finding methods described in this thread wont work for it (which is fine, I have a default name in the popup with just the number of which the graph was added to the figure ie Graph 12). Features I can think of for this which are typically figure dependent include the Data Cursor function. Typically defining your own data tip function is figure dependent and thus multiple graphs would cause issues but from just now testing it works just fine with bode() even with multiple axes on multiple panels. This does leave me a little curious as to how the data cursor function on this graph is set up. I even tested moving the panel containing a bode plot to a new figure and this still seemed to work which I did not expect. Something I will have to look into as I would like to set up Data Cursors like this in some other code of mine.
I would be curious to hear what other aspects of bode() or other functions you dont think would work as I would like to test but as far as I can tell the panels in the figures work just fine for operation of the plots.
Dan
Dan am 27 Feb. 2019
I should also note that no undocumented matlab was used for this part of the code, although I do use a few background java aspects for unrelated parts of the code.

Melden Sie sich an, um zu kommentieren.

 Akzeptierte Antwort

Dan
Dan am 27 Feb. 2019
Bearbeitet: Dan am 28 Feb. 2019

2 Stimmen

I was able to figure out a good method for detecting the axes being reset which could then be used to create a new listener for the title. An example is as follows:
ax = axes;
TitleAddFcn = @(~,~)disp('Title changed');
addlistener(ax.Title,'String','PostSet',TitleAddFcn);
addlistener(ax,'Reset',@(src,~)... addlistener(src.Title,'String','PostSet',TitleAddFcn));
surf(peaks)
title('Hello World')
It turns out that an axes has a reset event that will notify when cla reset is called. cla reset is called by the newplot function whenever the axes NextPlot property is set to the default value of replace. newplot is called on all new plot calls in matlab. So everytime the function plot or other plotting functions are run newplot is called and if the NextPlot property is the default then cla reset will be called triggering the reset event for the axes. This event was found by doing the following:
mc = metaclass(axes); {mc.EventList.Name}
So now with this setup the listener is set to the axes title for the title change and a listener is set to determine if a reset of the axes is performed. If so a new axes title listner is created. It should be noted that addlistener is supposed to create listernes which are tied to their event object and thus should be deleted when the object is deleted. In our case if the axes is reset the original title object is deleted and the original title change listener should also be deleted. If the axes is deleted then both the reset listener and the current title change listener should be deleted so these should not need to be tracked to ensure there are no persistant listeners to deleted objects (this is atleast how I interpret it to work, if I am wrong please let me know).

Weitere Antworten (1)

Sean de Wolski
Sean de Wolski am 27 Feb. 2019

0 Stimmen

If the axes already exists, this seems to work
ax = axes
addlistener(ax.Title,'String','PostSet',@(~, ~)disp('hi'))
title('Hello World')
Now just change the listener callback to fire an update of your listbox.

7 Kommentare

Dan
Dan am 27 Feb. 2019
Hi Sean, thanks for your input. Indeed that does work, and is the route I went for trying to solve this problem, but there is an issue. If you add in a plot command...
ax = axes
addlistener(ax.Title,'String','PostSet',@(~, ~)disp('hi'))
surf(peaks)
title('Hello World')
'hi' will not be displayed. The reason for this is that the default axes NextPlot property is replace which will reset axes properties to their defaults before added new objects. Digging in deep I discovered that for the title object in the axes this results in a full deletion of the title object and then a replacement of that with a new blank title object. So in the above example the listenter is added to the ax.Title object but as soon as surf(peaks) is called the ax.Title becomes a deleted object. I know there is the option to change to replacechildren instead of replace for the NextPlot property, but it seems like it would be useful to reset axes properties in most cases except for the title object in our case which we would still want to be reset, just not deleted so that our listener still exists. Another option would be to migrate the listener to the new title object but I have not been succesful in doing this yet as the only approach I have thought of so far is to set up a delete function on the title object to detect the delete and then search for the new title oject which seems to not work due to the new object not existing at the time of the delete function call.
What about:
ax = axes('NextPlot', 'add');
addlistener(ax.Title,'String','PostSet',@(~, ~)disp('hi'))
surf(peaks)
title('Hello World')
Dan
Dan am 27 Feb. 2019
Jan this is a working solution to the issue. My only reasoning for not going this route is that I am wanting to perserve the default plotting behavior. I should have mentioned that one of the goals of my code is to give the illusion to the user that they are plotting into a standard figure instead of different panels in a figure (except for having to create an instance of the class and tell the object to add a new panel which would be similar to having to type figure() to get a new figure). This way the user does not have to change there code in any way from the way they would have done it just plotting into multiple figures. So to summarize I would like the user to have the option to use any 'NextPlot' mode available just like they could plotting into any figure.
Jan
Jan am 27 Feb. 2019
Bearbeitet: Jan am 27 Feb. 2019
@Dan: I understand. Is it really required to let the creation of a title trigger the event? What about letting the popup menu collect all existing titles dynamically before the menu is displayed? Then you do not need an additional method to handle deleted titles or axes.
Dan
Dan am 27 Feb. 2019
@Jan: As best I can think about it, that is the best way to do it as the popup exists and is displayed potentially before all graphs have been added. Adding graphs is at the will of the user the same as they could make a new figure at any point and start plotting to it. A better solution could be potentially finding a background java listener that only fires when the popupmenu is clicked/opened.
Jan
Jan am 28 Feb. 2019
@Dan: I've done equivalent things in the Matlab level by creating an 'Enable'='inactive' element and let its ButtonDownFcn (not the Callback) trigger the automatic collection of data. Then instead of a popup menu I show a UIContextMenu at the same position, because I can set its Visible status dynamically to let it appear as if it was an activated popup menu. Not nice to write, but looks fine and I have all functionality, where it is needed. The user can do, whatever he wants, because the pseudo-popup keeps the power of finding, what it is needed.
Of course I do like the idea of letting new objects send a message which allows to identify them. But as long as Matlab is not completely object oriented and the plot command is inherited from the current figure automatically, such that you can define figure depended modifications directly, I prefer the active collection instead of a message passing.
Dan
Dan am 28 Feb. 2019
@Jan: Not a bad idea. I could see that working pretty well. I have been shying away from this aproach (the menu click callback) though due to the popupmenu being hidden at points and being replaced with an undocumented style jcombobox and that I am a bit worried about checking the titles everytime the user interacts with the GUI as it would slow down the GUI as far as the user sees. As I see it I have the option of slowing down a bit at build via the listener methods or slowing down on each user interaction via button callbacks. One things I will need to test however is the slowdown of 2 listeners per axes in the figure. In the case where I have hundreds (or even thousands) of axes in the figure perhaps this might start causing issues. Would be nice if it were possible to just have a single listerner for a call to the title() fuction.

Melden Sie sich an, um zu kommentieren.

Gefragt:

Dan
am 27 Feb. 2019

Kommentiert:

Dan
am 28 Feb. 2019

Community Treasure Hunt

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

Start Hunting!

Translated by