I'm more on the no0b side of coding, so I apologize in advance for stupid question and / or poor coding practice. Basically, I have a dream of being able to click, place an impoint, record the location of that impoint, and then click again for another point (and record the location of that point) until I hit the "Done" button. However, my code is going sideways.
My button is created with the line below. I've set the variable j to be 1 earlier on in the code, just because I wanted to be able to do two things with CallBack and so I wanted some way to indicate the the button has been clicked (I am, however, certainly not attached to this as a method). When the user clicks the button, I would really like for 1) the loop for new impoints to stop and 2) the image to close.
uicontrol('Style','pushbutton','Position',[80 0 70 20],'String','Done','CallBack','j=0;');
My loop for impoints (with extra stuff that I don't think is relevant removed) is as follows:
for k = 1:2*mboxes*nboxes
if j == 0
close(gcf);
break;
elseif j == 1
fprintf('At line 56, j is %d and k is %d\n', j, k)
h = impoint;
setColor(h,'k');
location = h.getPosition;
end
clear h;
end
At the moment, my problems are two-fold. Really, three-fold, but I can grudgingly live with the third problem. Problem 1, which is most concerning to me, is that I have to click the button just before I want to finish, which is to say that I click the button and then set down another point before the window will close. (In other words, it's going through the loop one time more than I want and I'm not sure how to tell it not to do that.) Problem 2, which I'm sure should tell me something about how it's looping, is that the impoints aren't being deleted as I go along (though h does seem to be deleted at the end). Problem 3, which is aesthetic but really annoys me, is that I keep getting a blue impoint in the upper left corner (0,0) of my image before I click where I actually want.
Any help would be much, much appreciated. :)
I'm not sure about that, but I can only answer here not comment.... so what I guess:
Adressing your problem 2:
your h isn't deleted completly, try to use delete(h) instead of clear. This should also make your problem 3
Adressing your problem 1:
impoint get's your click position whereever you click I assume, even if you click on your button, so the two functions (get the impoint AND the click event) might overlap and give you that error. Maybe a look at the setPositionConstraintFcn can help you with this (enabling the click only for the area you want it to work).
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'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.
edit: I figured it out and don't see a place to mark this as answered. Thanks for the suggestions though everyone!
A couple of weeks ago I was trying to force MATLAB to display a y = 0 line for a plot I was making. It was easy enough to search for, but apparently I made it automatic. Now I can't find anything even remotely similar to this new problem. When I run this code:
plot(x,y_known,x,y_simulated);
legend('Simulated','This stupid line right here','Known')
I get the following:
Notice the line at y = 0, it is not in the code. I wouldn't really care, but it is the second line in the figure and it messes up my ability to create a legend. i.e., if the legend entry was just:
legend('Simulated','Known')
then the legend would say that the known value was green, which is certainly not the case.
I don't really want to create a handle for every single line I plot in the future, and would much rather just get rid of this line. Can anyone provide some help, or at least point me in the right direction?
edit: The y = 0 line also changes its line properties based on whatever is supplied to the first plot entry. So plot(x,y1,'--',x,y2); makes both y1 and y = 0 dashed, but plot(x,y1,x,y2,'--'); would just render the second line dashed
As an absolute last resort (after failing to find how the line actually gets there), what you can do is access the Children property of your axes and delete the child which is the line you don't want.
Something along the lines of:
ch = get(gca,'Children');
delete(ch(2)); %// Where 2 should be replaced by the child index you're trying to delete.
There was an errant two much earlier in the code, giving a variable 2 columns instead of one. I didn't realize Matlab would be helpful be helpful and plot both columns (I've always explicitly told it to plot both). Just another case of someone not thinking in vector mode when using Matlab!
So I have 3 lines (technically patches) that have some transparency enabled. These lines were generated by the patchline FEX submission. The FEX entry works great by making patches seem like lines by adding a NaN to the end of the data so it doesn't create a big black patch between the end points of my line.
My lines looks like this
__ __
\ /
_ \ / _
\ \__/ /
\______/
The patch extends from the left end point to the right end point in a straight line across the top and then follows the line on the bottom. The actual patch is hidden (not drawn) though so all the user sees is the lines
The problem is if I wanted to click the bottom hump of the top line, it acts like I clicked the bottom line because it is part of that patch.
I have thought of a couple of workarounds and was hoping you guys could point out flaws or add ideas I might have missed with my workarounds.
Workaround #1
Ideally, I would create an actual line (line series obj.) over the transparent lines. I would turn hittest off on the patches and I would make the actual line invisible, but turn hittest on. The problem I am 99% sure exists is that if visibilty is off, you can't click the object. If anybody has a work around to that issue (documented or otherwise), that would be great.
Workaround #2
Turn the patchline hittest property to off. Make mock line objs. that follow the patchline coords. and have a line width very very small so as to make the line ideally pretty much invisible and have hit test clicks point to these lines instead. Has anybody tried something like this before? (I'm at work and can't try it now)
Workaround #3
NOTE THAT I REALLY DONT WANT TO DO THIS It is of course possible to turn off hittest for the patch/line objs and use the axes buttondown to figure out what patch obj (just the line part) it was closest to.
Once again, any insight would be much appreciated either about my possible work arounds, the situation in general, etc.
Thanks, Shaun
The problem can be illustrated with two line-thick patches, one of which is NOT parallel to the axes:
patch([2 8 8],[5 10 10],'w','EdgeColor','b','EdgeAlpha',0.4,'LineWidth',3)
patch([2 8 8],[4 4 4],'w','EdgeColor','r','EdgeAlpha',0.4,'LineWidth',3)
xlim([0 10])
ylim([3 11])
You cannot select but those lines which are parallel to one of the axes.
Workaround #1: impossible.
Workaround #2: quite visible, why would you need transparent lines then?
Workaround #3: the only way...
Workaround alternative: submit to the TMW a technical request to improve clickability of patches.
Oleg, your initial post somehow inspired me to come up with my elegant solution. (Don't ask me how, just accept my "thank you" haha)
The elegant solution to my problem required me to go beyond patch properties and turn to the axes child order. Under the assumption that none of the lines would intersect each other, I was able to plot them in a order where the bottom one was first, then the second lowest, etc. until I got to the top (effectively rearranging child order).
If anybody else stumbles upon this, what you can do (assuming your lines don't intersect) is to sort the first Y (or whatever values) to go from lowest to highest (in this case) in your plot, and make sure your sort function returns the indicies.
I don't have my computer in front of me, but what you end up doing is effectively making an acontinuous for loop, looping through the indicies.
something like:
for i=[6 2 5 4 1 3]
plot(X(:,i),Y(:,i))
end
Where the 6th column was my lowest data, 2nd column was second lowest on plot, etc.
I am writing a program in which at some point a graph is plotted and displayed on screen. The user then needs to press 'y' or 'n' to accept or reject the graph. My current solution uses the PsychToolbox (the actual solution doesn't need to), which includes a command called 'KbCheck' which checks at the time of calling the state of all the keyboard buttons. My code looks like this:
function [keyPressed] = waitForYesNoKeypress
keyPressed = 0; % set this to zero until we receive a sensible keypress
while keyPressed == 0 % hang the system until a response is given
[ keyIsDown, seconds, keyCode ] = KbCheck; % check for keypress
if find(keyCode) == 89 | find(keyCode) == 78 % 89 = 'y', 78 = 'n'
keyPressed = find(keyCode);
end
end
The problem is, that the system really does 'hang' until a key is pressed. Ideally, I would be able to scroll, zoom, and generally interact with the graphs that are plotted onscreen so that I can really decide whether or not I want to press 'y' or 'n'!
I have tried adding 'drawnow;' into the while loop above but that doesn't work: I still am unable to interact with the plotted graphs until after I've accepted or rejected them.
The solution doesn't have to use PsychToolbox; I assume there are plenty of other options out there?
Thanks
I'd use the input function:
a = input('Accept this graph (y/n)? ','s')
if strcmpi(a,'y')
...
else
...
end
Although admittedly it requires two keypresses (y then Enter) rather the one.
Wait for buttonpress opens up a figure, which may be unwanted. Use instead
pause('on');
pause;
which lets the user pause until a key is pressed.
Why not using waitforbuttonpress instead?
Documentation: http://www.mathworks.fr/help/techdoc/ref/waitforbuttonpress.html
You don't want to use waitforbuttonpress since it locks the figure gui (no zooming, panning etc).
pause can cause the command window to steal the focus from the figure.
The solution I find to work best is to open the figure with a null keyPressFcn in order to avoid focus problems:
figure('KeyPressFcn',#(obj,evt) 0);
and then wait for CurrentCharacter property change:
waitfor(gcf,'CurrentCharacter');
curChar=uint8(get(gcf,'CurrentCharacter'));
Wait for key press or mouse-button click:
Example:
w = waitforbuttonpress;
if w == 0
disp('Button click')
else
disp('Key press')
end
for more information visit:
http://www.mathworks.com/help/matlab/ref/waitforbuttonpress.html
The waitforbuttonpress command is good but is triggered by either a mouse click or a key press. If you want it to trigger only from a key press, you can use the following hack:
while ~waitforbuttonpress
end