Can I create a reference to a handle object in a way that I can replace the object itself at one point and the references will be updated?
Example:
classdef IShifter < handle
methods (Abstract)
x = Shift(this, x);
end
end
classdef Shifter1 < IShifter
methods
function x = Shift(this, x)
x = circshift(x, 1);
end
end
end
classdef Shifter2 < IShifter
methods
function x = Shift(this, x)
x = [ 0 ; x ];
end
end
end
classdef Item
properties (Access = 'private')
shifter; % should be a pointer/reference to the object which is in the respective parent container object
end
methods
function this = Item(shifter)
this.shifter = shifter;
end
function x = test(this, x)
x = this.shifter.Shift(x);
end
end
end
% note this is a value class, NOT a handle class!
classdef ItemContainer
properties
shifter;
items;
end
methods
function this = ItemContainer()
this.shifter = Shifter1;
this.items{1} = Item(this.shifter);
this.items{2} = Item(this.shifter);
end
function Test(this)
this.items{1}.Test( [ 1 2 3] )
this.items{2}.Test( [ 1 2 3] )
end
end
end
Then, the output should be:
items = ItemContainer();
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
items.shifter = Shifter2;
items.Test();
[ 0 1 2 ]
[ 0 1 2 ]
But instead it is:
items = ItemContainer();
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
items.shifter = Shifter2;
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
because assigning a new Shifter object to parent object items does not update the references in the containers.
I am looking for something like in C where all the "shifter" properties are pointers and I can put whatever Shifter object I want into this "address".
ItemContainer and Item are not handle classes.
I want to avoid using events to update the references or implementing a set method to update the references
The pass-by-reference concept in Matlab only goes so far (and is mostly limited to handle-classes anyway). It does not go as far as you'd like it to go.
If you don't want to use a set method, you can make items a dependent property with a get method that returns {this.shifter,this.shifter}
According to your comments, items is a bit more complicated than just a cell array. Thus, you may want to set up your object with a property itemsStore (or itemsCache, or whatever name you prefer for something that temporarily stores items), and a dependent property items. The get method to items checks whether itemsStore is empty; if yes, it reconstructs the item array and stores it in itemsStore, if not, it just returns the contents of itemStore. Additionally, you need to add a set method to shifter that empties itemsStore, so that items needs to be recreated. Note that MLint will give you a warning that a set method for a property shouldn't write into another property. This warning is meant to tell you that when you have saved an object and then load it again from disk, all the set methods will be executed. Depending on the order of execution, set methods that write into other properties may create unexpected results. In your case, it's no problem because an emptied itemStore is something that your code is designed to handle. If you want, you can right-click on the MLint warning, disabling it for that line.
Unfortunately I don't think this is possible in Matlab.
However, you can use a set method or subsasgn to redefine the content of items{1} and items{2} when item is defined. Then you will be able to get the behavior you're asking for.
Best,
Related
I'm trying to figure out how to make a GUI with radio buttons that will enable/disable options based on radio button selections. Here's an example of one way I have tried:
fig = uifigure('NumberTitle','off','Name','Option Selection','Position',[750,300,460,520]);
panel1 = uipanel(fig,'Title','Options','Position',[140,80,200,280]);
bg = uibuttongroup(fig,'Position',[140 290 200 51],'SelectionChangedFcn',#bselection);
rb1 = uiradiobutton(bg,'Position',[5 30 100 20],'FontWeight','bold');rb1.Text = 'Option 1';
rb2 = uiradiobutton(bg,'Position',[5 10 100 20],'FontWeight','bold');rb2.Text = 'Option 2';
Opt1_label = uilabel(panel1,'text','Option 1','Position',[5 180 100 20],'FontWeight','bold');
Opt1_field = uieditfield(panel1,'text','Position',[5 160 100 20]);
Opt2_label = uilabel(panel1,'text','Option 2','Position',[5 60 250 20],'FontWeight','bold');
Opt2_field = uitextarea(panel1,'Position',[5 15 190 45]);
function bselection(bg,Opt1_label,Opt1_field,Opt2_label,Opt2_field)
if bg.Buttons(1).Value == 1
Opt1_label.Enable=1;
Opt1_field.Enable=1;
Opt2_label.Enable=0;
Opt2_field.Enable=0;
elseif bg.Buttons(2).Value == 1
Opt1_label.Enable=0;
Opt1_field.Enable=0;
Opt2_label.Enable=1;
Opt2_field.Enable=1;
end
end
The enable/disable if statement works standalone, but I cannot figure out how to get it to work as a callback. Option 2 is not disabled even though option 1 is selected first by default, and if I select another radio button, either nothing changes or I will get an error saying the Enable handle isn't found.
Any ideas are much appreciated
Thanks in advance!
You have configured your function bgselection() to be a callback function for a UI element. Callback functions have two specific rules for the input arguments:
The first argument must be the handle of the object that called the function, and the second must be "eventData" for the callback event. So in your function, the argument Opt1_label is being treated as a eventData variable. Since it does not have the information expected in that variable, there is an error. So to follow this rule, change your function definition to:
function bselection(bg,eventData,Opt1_label,Opt1_field,Opt2_label,Opt2_field)
The second rule is that if you want to have more than the required two input arguments, you need to list them in a cell array when the function is assigned to the object. So in your case, define the uibuttongroup like this:
bg = uibuttongroup(fig,'Position',[140 290 200 51],'SelectionChangedFcn',{#bselection,Opt1_label,Opt1_field,Opt2_label,Opt2_field});
You will have to move the definitions of Opt1_, Opt2_ etc before the bg statement, so that they exist and can be included in the cell array.
You can see the documentation for this here. There are a couple other options for passing arguments, but these are the most common methods.
Also, the eventData has some useful information, such as the label of the button that is selected. Try adding disp(eventData) to the function to see what is included there.
I created a listbox and enabled multiple selections. My listbox contain numbers from 1 to 10. When I select 3, 1, and 8, the function always put my selections in alphabetical order (1,3,8). Is there any way I could make it not put my selection in alphabetical order? So if I select 3, 1, and 8 the output of my selection is 3, 1, 8.
Thank you.
For the purpose of this answer I'm assuming you're using matlab-hg2.
From the docs for uicontrol:
'listbox' ... The Value property stores the row indexes of currently selected list box items, and is a vector value when you select multiple items. After any mouse button up event that changes the Value property, MATLAB evaluates the list box's callback routine. To delay action when multiple items can be selected, you can associate a "Done" push button with the list box. Use the callback for that button to evaluate the list box Value property.
From the above we learn that the information on the selected lines is returned in Value. From there, it's a matter of keeping track of what's selected. This can be quite easily done using a persistent variable inside the listbox' Callback as shown in the following example:
function LISTBOX_EXAMPLE
hFig = figure('Units','Pixels','Position', [200 200 100 200],'Menubar','none');
uicontrol(hFig, ...
'Style', 'listbox',...
'Units','Pixels',...
'Position', [20 20 80 150],...
'Max',3,...
'Min',0,...
'String',num2cell(1:10),...
'Callback',#selectionCallback);
function selectionCallback(hObject,~)
persistent previouslyChosenItems
%// Elements were added:
if numel(previouslyChosenItems) < numel(hObject.Value)
previouslyChosenItems = union(previouslyChosenItems,hObject.Value,'stable');
%// Elements were removed:
elseif numel(previouslyChosenItems) > numel(hObject.Value)
previouslyChosenItems = intersect(previouslyChosenItems,hObject.Value,'stable');
%// A different element was selected (single element):
elseif numel(previouslyChosenItems) == numel(hObject.Value) && numel(hObject.Value)==1
previouslyChosenItems = hObject.Value;
end
disp(['Currently selected items (in order): ' num2str(previouslyChosenItems(:)')]);
end
end
Example output:
Currently selected items (in order): 7
Currently selected items (in order): 3
Currently selected items (in order): 3 9
Currently selected items (in order): 3 9 1
Then it's up to you to assign the value of previouslyChosenItems somplace useful.
I'm trying to write an object oriented program (as a learning exercise, I know there may be simpler ways to do it) in which beads bounce around a 2D plane bounded by a ring. Each bead is an object defined by a class ball. In setting the initial positions of the balls I need to check that no other ball has already been placed at the same x and y coordinates.
#Class for the beads
class ball:
NumbBalls = 0
#Constructor
def __init__(self,Beads):
self.ball = sphere(pos = vector(0,0,0), radius = radiusBall,color=color.red)
ball.NumbBalls += 1
self.ball.pos = self.createInitialPosition(Beads)
#Assign ball its initial position
def createInitialPosition(self,other):
#CODE to compare self.ball.pos and other.ball.pos which returns a unique position coordinate
#Main program
NumbBeads = 100
Beads = []#Create empty list for all the beads
#create balls in random initial positions
Beads = [ball(Beads) for item in range(NumbBeads)]
I can get this to work if I create two objects bead1 and bead2 and then pass bead1 and bead2 as arguments ie bead2 = ball(bead1) but how do I do this if I am generating a list of beads and want all of them to be compared with self. Hopefully that makes sense.
Perhaps rather than the approach you are currently taking, you should consider something along these lines (of course, with the necessary changes to your class definition/methods):
N = 100
initialPositions = []
while len(initialPositions) <= N:
newPosition = generateRandomPosition()
if newPosition in initialPositions:
pass
else:
initialPositions.append(newPosition)
ballList = [ ball(p) for p in initialPositions ]
In other words, generate a list of initial positions outside of the creation of your objects, and do whatever validation/restriction you need during that creation. Then just create your objects from that list. If N is really large, you might want to consider a dictionary (or a mutable set of some sort) instead of a list for initialPositions, to help speed up the membership testing, but it's still the same concept...
I have created a uitable consists of, say, four columns.
colu={{'Sweet' 'Beautiful' 'Caring'},'numeric', 'numeric','numeric'}
dat={1 2 3 []; 4 5 6 []; 7 8 9 []};
A=uitable('outerposition',[0 0 1 1],'ColumnFormat',colu,'Data',dat);
What I wanted to do now is that when the code is run, and I choose 'Sweet' in the pop-up in the first cell, the cell (1,4) displays dat(1,1), or when I choose 'Beautiful' in the second cell in the first column, the cell (2,4) displays dat(2,1). Unlike in a popupmenu outside a uitable, I am not able to use get(popup,"value').
How could I possibly do what I wanted to? Thanks in advance!
You'll have to use the CellEditCallback property, which is a global callback that gets triggered when any cell is edited.
There are no callbacks you can set on individual cells.
A pseudo-code template that should get you started:
function cellEditCallback(hTable, editEvent)
% get changed index
changedIndex = editEvent.Indices;
if changedIndex is a popup-cell:
% check new value
newValue = editEvent.NewData;
% set data in appropriate cell to corresponding value
...
As an aside, the columnFormat in the example does not match the data. It specifies column 1 as a popup-column, while according to your data, it should be column 4.
I also had to change [] to '' to make the popup work and set('ColumnEditable', logical([0,0,0,1])).
See e.g.
http://www.mathworks.de/products/matlab/examples.html?file=/products/demos/shipping/matlab/uitabledemo.html
for a more comprehensive example uitable application.
The table cell is edit with a simple click, I want it to be edit only on double click. Simple click will select the cell.
I'm use this property of uitable:
set(hTable, 'Data',data,...
'ColumnEditable', edit,...
First you need to set the cell editabiliy to false by default:
set(hTable,'ColumnEditable', [false false ...]); %accordingly your number of columns
and introduce a CellSelectionCallback:
set(hTable,'CellSelectionCallback',#cellSelect);
which calls the following function within the same script
function cellSelect(src,evt)
getstate = get(src,'ColumnEditable'); %gets status of editability
index = evt.Indices; %index of clicked cell
state = [false false ...]; %set all cells to default: not editable
state(index) = ~getstate(index); %except the clicked one, was it
%already false before set it true
set(src,'ColumnEditable', state) %pass property to table
end
and also a CellEditCallback:
set(hTable,'CellEditCallback',#cellEdit);
calling
function cellEdit(src,~)
state = [false false ...];
set(src,'ColumnEditable', state)
end
minimal example
function minimalTable
h = figure('Position',[600 400 402 100],'numbertitle','off','MenuBar','none');
defaultData = {'insert number...' , 'insert number...'};
uitable(h,'Units','normalized','Position',[0 0 1 1],...
'Data', defaultData,...
'ColumnName', [],'RowName',[],...
'ColumnWidth', {200 200},...
'ColumnEditable', [false false],...
'ColumnFormat', {'numeric' , 'numeric'},...
'CellSelectionCallback',#cellSelect);
end
function cellSelect(src,evt)
getstate = get(src,'ColumnEditable');
index = evt.Indices;
state = [false false];
state(index) = ~getstate(index);
set(src,'ColumnEditable', state)
end
function cellEdit(src,~)
state = [false false];
set(src,'ColumnEditable', state)
end
As you figured out this is not always working. Because you have the same issues like I had before with popup menus. It's exactly the same problem: ColumnEditable is just a row vector and not a matrix. I had to deal with the ColumnFormat property, which is also just a row vector. If the double click feature is really important to you, you can consult the following two answers:
Is it possible to prevent an uitable popup menu from popping up? Or: How to get a callback by clicking a cell, returning the row & column index?
How to deselect cells in uitable / how to disable cell selection highlighting?
The threads basically suggest to create a unique uitable for every single row, so that every single row has a unique ColumnEditable property. That's the only solution so far.
I'm afraid there is no simple solution. I can't offer you further help, except the complicated workarounds of the other answers. Or just use the simple one above and live with the little drawbacks.
Although this thread is old but in my opinion still valuable to some users.
I have tested the following with R2010b 32bit.
I have achieved editing only on double click simply by setting
set(hTable,'CellSelectionCallback',#tableCellSelectCB,'ColumnEditable',true)
and defining its function the following
function tableCellSelectCB(~,~)
try
h.jtable.getCellEditor().stopCellEditing();
catch
end
end
where h.jtable refers to the underlying java object of your uitable.
This way, I can select even single and multiple cells, without going into edit mode. On a double click on a single cells lets me edit its contents.
Extension to have individual editable rows
I wanted to have checkboxes in the top row and non-editable (not directly at least) data in the rest of the table. You can easily modify the above:
function tableCellSelectCB(~,evd)
if evd.Indices(1) > 1
try
h.jtable.getCellEditor().stopCellEditing();
catch
end
end
end