I'm attempting to create an animated plot in app designer. So i've got 2 variables u and x displayed on a graph in respect to time with a time slider and a button under it. I wanted to know if it was possible to have a loop always checking in the background if the button is showing '||' so that it would increment uiaxis to display the new set of u and x variable associated with a time increment. And it would keep looping until the button has been pressed or time is tmax where it will restart at 0 and stop incrementing. I've attempted it with a changing value callback however it doesn't seem to loop correctly.
Related
I created a uitable (new version using appdesigner) in MATLAB and wanted to support right clicking on cells and showing a cell specific context menu. Much to my surprise there seemed to be no way to support this.
The context menu only seems to trigger with right click on the uitable, but there is no way of knowing which cell was selected (I think, maybe not?). I created a workaround where I left clicked to select a cell, and during that selection I right clicked using a Java Mouse robot to trigger the context menu. This is super ugly but sort of works. Except, if you need to bring up the menu twice on the same cell. Apparently the cell selected callback only fires once for the cell, until a new cell is selected. I tried literally putting two tables in the same spot and upon selecting one toggling to the other, but the memory of cell selection is table specific, so this only worked for two clicks before both tables had been clicked on the same cell, and toggling visibility back to the first resulted in the cell selection callback not firing (since the cell had not changed) . I tried various approaches to try and deselect the cell (disable/enable, visibility change, data change, etc.), but the cell selection callback never changed.
I even tried having duplicate columns, where the goal was to hide a column, where normally columns 1 and 2 would be visible (column 3 out of view due to size), and then on clicking on column 2, column 2 would hide itself (0 width) and column 3 (an exact duplicate) would move into its place, thus seeming to the user like multi-clicking was supported. Unfortunately I can't set the column width to 0 -- or rather, setting it to 0 doesn't completely hide the column. Instead there seems to be some minimal width to the column and the whole thing looked awful.
I wanted to do something similar with a listbox (right click support), but again I couldn't figure out how to identify where I was right clicking. I eventually settled on left clicking on a listbox and using the mouse robot approach to right click to bring up the context menu. Unlike the uitable, it was fairly easy to clear the selection on the listbox (set listbox.Value = {}). However, I strongly dislike the left click instead of right click approach and I'd rather have multiple columns.
Any suggestions would be much appreciated!!!
So I found an approach that is better than using a robot. I had tried this but was missing a critical portion which I will describe below.
Upon selecting a row in the table, the open command can be used to launch a context menu. My problem was that I didn't know where to launch the menu. I tried CurrentPoint for the figure, but it was 0,0 (or in general not valid)
Here's the current documentation for CurrentPoint:
Current point, returned as a two-element vector. The vector contains
the (x, y) coordinates of the mouse pointer, measured from the
lower-left corner of the figure. The values are in units specified by
the Units property.
The coordinates update when you do any of the following:
Press the mouse button within the figure.
Release the mouse button after pressing it within the figure.
Press the mouse button within the figure, and then release it outside
the figure.
Rotate the scroll wheel within the figure.
Move the mouse within the figure (without pressing any buttons),
provided that the WindowButtonMotionFcn property is not empty.
If the figure has a callback that responds to mouse interactions, and
you trigger that callback faster than the system can execute the code,
the coordinates might not reflect the actual location of the pointer.
Instead, they are the location when the callback began execution.
If you use the CurrentPoint property to plot points, the coordinate
values might contain rounding error.
Here's the critical line again:
"Move the mouse within the figure (without pressing any buttons), provided that the WindowButtonMotionFcn property is not empty."
So when a selection of a cell happens, the CurrentPoint is not valid. However, if we simply define a WindowButtonMotionFcn, then it is!
So the general idea is to have a callback for the table when a cell is selected (SelectionChangedFcn) and to set a dummy callback for WindowButtonMotionFcn
The final point is that a context menu can be launched with the open function if you specify a given location to launch it at. This is different from attaching it to an object and having it automatically launch on right click.
Here's some example code. If you comment out the callback for windows motion then the whole thing doesn't work! Unfortunately it is a left click for targeting the cell but at least it avoids the non-sense I was using with a java robot right click.
classdef wtf < handle
properties
h %struct, this was an appdesigner handle
cm %context menu
end
methods
function obj = wtf()
h = struct;
h.UIFigure = uifigure();
h.UITable = uitable(h.UIFigure);
obj.h = h;
obj.h.UITable.CellSelectionCallback = #obj.tableCall;
%obj.h.UITable.SelectionChangedFcn = #obj.tableCall;
%Some data ...
s = struct;
s.a = (1:4)';
s.b = (5:8)';
obj.h.UITable.Data = struct2table(s);
%Our context menu
cm = uicontextmenu(obj.h.UIFigure);
m = uimenu(cm,'Text','Menu1');
obj.cm = cm;
%WTF ... without this we don't get a valid CurrentPoint
obj.h.UIFigure.WindowButtonMotionFcn = #obj.mouseMove;
end
function tableCall(obj,x,y)
%y - event info
%x - impacted object
cp = get (obj.h.UIFigure, 'CurrentPoint');
open(obj.cm,cp(1),cp(2));
selected_cell = y.Indices;
%selected_cell = y.Selection;
x.Selection = []; %allows reselecting same cell without
%needing to select another cell first
%Now we can run something on the context menu
%that targets the selected cell
end
function mouseMove(obj,x,y)
%we could store a point here
end
end
end
I realize that App designer does not support interactive figure manipulation, but I am wondering if I can open a separate figure window (not a UI window) with my graphic displayed on it so that I can still get the location of my mouse clicks. Currently the code below displays the figure on my GUI, and then opens another blank figure that records my mouse clicks. This is fine, but I need to also display the figure in the new window as well, and am having trouble doing so.
first frame = vid(:,:,:,1);
imshow(firstframe,'Parent',app.UIAxes);
[centers_X centers_Y]=getpts;
What worked for me was setting a callback on the image rather than the axes:
ih = imshow(firstframe,'Parent',app.UIAxes);
ih.ButtonDownFcn = {#im_ButtonDownFcn, app}; %app will be passed to the callback
Then in a separate file in the same folder (or as a private function within the appdesigner... it should work but I haven't tried it):
function im_ButtonDownFcn(im, hit, app)
mouse_pos = flip(hit.IntersectionPoint(1:2)); %gives floats. Round if you want integers e.g. for indexing pixels
I'm using a programmatic GUI in MATLAB which uses multiple figure windows. When I press the button 'Redraw' in Figure A, a new figure appears (Figure B) with some data plotted. I want the focus to immediately switch back to Figure A because there are many hotkeys (WindowKeyPressFcn) that I use in that window to update the plots in Figure B.
There are two problems here:
1) The last line of the callback for the button 'Redraw' does switch focus back to Figure A, BUT only if Figure B exists already. That is, the first time Figure B is created, it remains in focus. If I then use Figure A to update the plots in Figure B, the focus correctly switches back to Figure A. I can't think of why it behaves differently during the first redraw and all subsequent calls.
2) The even bigger issue is that if I set a breakpoint anywhere in the code and then resume execution, the focus switches back to Figure A as I want. So, why does entering the debugger and doing nothing else fix the problem? How can I find the issue if everything works in the debugger?
Thanks in advance!
EDIT: To my great surprise, I was able to reproduce this "Heisenbug" by writing my first ever programmatic GUI. This should be the simplest example of my problem. To see it in action, simply run the code below and click on the push button. For some reason, when Window 2 is created for the first time, the focus does NOT switch back to Window 1 as intended. It works properly for all subsequent button presses. Try closing Window 2 and pushing the button again, the error will keep occurring.
As mentioned in the original post, setting a breakpoint in the code resolves the issue. Set a breakpoint at line 27, then resume execution and Window 1 will be in focus.
What is happening here?
function heisenbug
%% Main problem:
% After clicking the push button, I want the focus to
% always switch back to Window 1 (the one containing the button).
% However, this does not work when Window 2 is first created.
%%
%% Create and then hide the GUI as it is being constructed
f = figure('Visible','off','name','Window 1','units','normalized','Position',[0.1 0.1 0.5 0.5]);
%% Initialize handles structure
handles = guihandles(f);
handles.window2 = [];
guidata(f,handles)
%% Make a button
hbutton = uicontrol('Style','pushbutton','String','Push me','units','normalized',...
'Position',[0.1 0.1 0.8 0.8],...
'Callback',#button_Callback);
%% Make the GUI visible
f.Visible = 'on';
%% Button callback
function button_Callback(source,eventData)
handles = guidata(gcbo);
% If Window 2 already exists, plot a circle, then switch focus back to Window 1.
if ~isempty(handles.window2) && ishandle(handles.window2)
figure(handles.window2);
plot(1,1,'bo')
figure(f);
% Otherwise, create Window 2 and do the same thing.
else
handles.window2 = figure('Name','Window 2','units','normalized','position',[0.4 0.1 0.5 0.5]);
plot(1,1,'bo')
figure(f)
end
guidata(source,handles)
end
end
My post was quickly answered by Adam (thanks!) on the MathWorks site: http://se.mathworks.com/matlabcentral/answers/299607-simply-entering-the-debugger-during-execution-of-matlab-gui-fixes-error-that-persists-during-normal.
I needed to insert a pause(0.05) after Window 2 is created, before trying to switch the focus back with figure(f). Otherwise the focus is stolen back to Window 2 when it finishes plotting.
I want to detect how many times the user click spacebar in 5 seconds
Is there any good way to fix this problem?
Thanks
One way to easily read user inputs from the keyboard is to create a new figure and specify a KeyPressFcn callback function, which is executed automatically if any key is pressed.
Lets start off by creating a new figure. As we don't need the figure to display anything, let's make it as small as possible (i.e. 1 by 1 pixel) and place it at the lower corner of the display:
f = figure('Position',[0,0,1,1]);
Now we'll set the UserData property of the figure - which we will use as counter - to zero:
set(f,'UserData',0);
Now let's see what to do when a key is pressed: We can create a small callback function which checks if the pressed button was a space and increases the UserData counter if that was the case. We'll call that function isspace:
function isspace(hObject,callbackData)
if get(hObject,'CurrentCharacter') == ' '
set(hObject,'UserData',get(hObject,'UserData')+1);
end
end
Now simply set up the figure to use this function as KeyPressFcn by
set(f,'KeyPressFcn',#isspace);
This already counts the number of times space is pressed. The current value of the counter is read by
get(f,'UserData');
Now we need the time measurement. This can be done using a timer. We'll configure it to go off after 5 seconds and then assing a new value in the base workspace. For that we need a callback function timerCallback.m
function timerCallback(hObj,eventData)
assignin('base','nSpace',get(gcf,'UserData'));
delete(gcf);
stop(hObj);
delete(hObj);
end
t = timer('StartDelay',5,'TimerFcn',#timerCallback);
start(t);
And that's it: First create the figure, create the timer and after 5 seconds you get the number of key presses in the variable nSpace in your workspace and the window is closed.
I am trying to implement a very simple GUI that consists of just one pushButton. I want it to begin by just having START as a label. Then on press it changes to STOP. When the user clicks the button the first time the callback sets a boolean to true and changes the label. When the Button is clicked a second time the boolean is changed to false and the GUI closes.
I can't find anything on how to make a simple GUI like this in MATLAB. the GUIDE tool makes no sense to me and seems to generate so much useless code. Matlab buttons are wrappers for jButtons as seen here
GUIDE is quite straightforward - the automated tool generates stubs for all the callbacks, so that all is left is to fill in the code to be executed whenever the callback runs. If you prefer to create the GUI programmatically, you can create the button you want as follows:
%# create GUI figure - could set plenty of options here, of course
guiFig = figure;
%# create callback that stores the state in UserData, and picks from
%# one of two choices
choices = {'start','stop'};
cbFunc = #(hObject,eventdata)set(hObject,'UserData',~get(hObject,'UserData'),...
'string',choices{1+get(hObject,'UserData')});
%# create the button
uicontrol('parent',guiFig,'style','pushbutton',...
'string','start','callback',cbFunc,'UserData',true,...
'units','normalized','position',[0.4 0.4 0.2 0.2])