Creating 1024 ToggleButtons with almost the same callback

Hey together,
For a 1024 LED-Matrix I wanted to create a GUI where u can swith every single pixel on and off with a certain brightness. I wanted to create toggle Buttons which switch the Pixel on/off. So I started creating a 1024 huge buttonmatrix in GUIDE with copy/pasting as much buttons as possible. Afterwards to make sure Pixel 1 really represent Pixel 1 I renamed all Buttons accrouding to their Pixelnumber. EDIT : The Tag goes like T#### , for example for Pixel 1020 T1020 and for Pixel 66 T66.
For the callback I wrote the following Method to just copy/paste in every callback :
global TData
brtn = get(handles.slider1, 'Value');
Tag = get(hObject, 'Tag');
PPos = 0;
Si = size(Tag);
for i = 2:Si(2)
PPos = PPos*10 + str2num(Tag(i));
end
if(brtn < 0.3)
brt = 0.3;
else
brt = brtn;
end
if(get(hObject,'Value') == 1)
set(hObject,'BackgroundColor',[1*brt 1*brt 0]);
TData(PPos) = 256 * brtn;
elseif (get(hObject,'Value') == 0)
set(hObject,'BackgroundColor',[0.149 0.149 0.149]);
TData(PPos) = 0;
end
So my question is it somehow possible to make this "smarter" with a Buttongroup etc. or do I really just have to copy/paste this 1024 times again?
Thanks for your answers!

 Accepted Answer

Jan
Jan on 19 Aug 2019
Edited: Jan on 19 Aug 2019
A simplified version of your code:
function buttonPress(hObject, EventData, handles)
global TData
brtn = get(handles.slider1, 'Value');
Tag = get(hObject, 'Tag');
PPos = sscanf(Tag, 'T%d');
brt = max(0.3, brtn);
if get(hObject,'Value')
set(hObject,'BackgroundColor', [brt, brt, 0]);
TData(PPos) = 256 * brtn;
else
set(hObject,'BackgroundColor', [0.149, 0.149, 0.149]);
TData(PPos) = 0;
end
You can use the same callback for all buttons. There is no need to create a specific function for each button, because the code uses the tag to perform the different actions.
By the way, global variables are a mess. Prefer to use guidata to store the values in the figure's ApplicationData. This avoid collisions with other programs and allows for a clean debugging of your code.
I prefer to create figures by code instead of using GUIDE. 1024 almost equal objects are easier to create in a loop:
FigH = figure;
index = 0;
shift = [0.0005, 0.0005, -0.001, -0.001];
for iy = 1:32
for ix = 1:32
index = index + 1;
uicontrol(FigH, 'Style', 'ToggleButton', ...
'String', sprintf('%d', index), ...
'Units', 'normalized', ...
'Position', [ix-1, iy-1, 1, 1] / 32 + shift, ...
'Callback', {@buttonPress, handles, index});
end
end
function buttonPress(hObject, EventData, handles, index)
PPos = index;
... see above
end

9 Comments

'Callback', {@buttonPress, handles, index});
One word of caution about this: the uicontrol created by the command of which that line is part will receive the handles structure as it exists when the button is created as input. Changes to the handles structure afterwards will not be reflected in that input.
If you plan to change the handles structure after button creation and need the button to have access to that updated structure, don't pass the handles in. Get them by calling guihandles with the handle to something in the GUI (the hObject input to the callback function will work nicely) as input.
Rather than creating 1024 individual buttons, I'd consider a slightly different approach. Create a uitable with false(32) as the Data property, and make sure the ColumnEditable property is set to true to make the contents of the uitable editable. Set the ColumnWidth appropriately so the cells in the uitable don't show much if any border around the checkboxes. This will look a little different, but once the user has selected the checkboxes to enter the data retrieving the data will require getting the uitable object's Data property rather than querying each individual button's Value.
Rather than creating 1024 individual buttons, I'd consider a slightly different approach
Personally, I'd create an image (e.g. with imagesc) and just hit test the image on its ButtonDownFcn. Although undocumented, the ButtonDownFcn function does give you the location of the click.
Here is a simple demo:
colormap(gray)
imagesc(randi([0 1], 32), 'ButtonDownFcn', @(src, args) set(src, 'CData', subsasgn(src.CData, struct('type', '()', 'subs', {num2cell(round(args.IntersectionPoint([2 1])))}), ~src.CData(sum(round(args.IntersectionPoint(1:2)) .* [32 1])-32))))
Excuse the unwieldy callback in order to make it a one liner.
Benedikt's comment mistakenly posted as an answer moved here:
So I would delete all automatically generated Button callbacks and replace it with a function like this:
function handles = buttonPress(hObject,handles)
global TData
brtn = get(handles.slider1, 'Value');
Tag = get(hObject, 'Tag');
PPos = sscanf(Tag, 'T%d');
brt = max(0.3, brtn);
if get(hObject,'Value')
set(hObject,'BackgroundColor', [brt, brt, 0]);
TData(PPos) = 256 * brtn;
else
set(hObject,'BackgroundColor', [0.149, 0.149, 0.149]);
TData(PPos) = 0;
end
end
So how does Matlab realize which button is pressed? And when I want add some more Buttons with different functions doe this still work?
And Jan's reply:
You can use the same callback for different GUI elements without any problem. The difference is the handle of the calling object as provided in the 1st input argument of the callback:
FigH = figure;
uicontrol(FigH, 'Style', 'PushButton', 'Tag', 'Button1', 'String', '1', ...
'Callback', @myCallback, 'Position', [10, 10, 100, 30]);
uicontrol(FigH, 'Style', 'PushButton', 'Tag', 'Button2', 'String', '2', ...
'Callback', @myCallback, 'Position', [130, 10, 100, 30]);
function myCallback(ObjectH, EventData)
Str = get(ObjectH, 'String');
Tag = get(ObjectH, 'Tag');
disp([Str, ' ', Tag]);
end
Now the callback can identify the caller by the contents of ObjectH.
In my example I added the index as additional input argument for the callback to avoid the parsing of the tag. Alternatively you can use the UserData of teh button.
Another of benedikt's comment moved here:
So I also created a figure which I'd like to use. Can I somehow change all Button-Callbacks to one with some code like this?
for i = 1:1024
PPos = ['handles.' 'T' num2str(i)];
set(PPos, 'Callback', @pixel_Callback);
end
Unfortunatley the PropertyInspector somehow crashes when trying to edit it manually. And set obviously is the wrong command here.
And Adam's reply:
set( handles.( ['T' num2str(i)] ), 'Callback', @pixel_Callback )
in your loop should work.
Benedikt's comment moved here. Stop using answers to post comments:
So I changed the code so far. But now I somehow get an Error when trying to read the slider.
It says:
Not enough input arguments.
Error in Leselicht_GUI>pixel_Callback (line 103)
brtn = get(handles.brtnss, 'Value');
Error while evaluating UIControl Callback
And this my Matlab Code:
function varargout = Leselicht_GUI(varargin)
%LESELICHT_GUI MATLAB code file for Leselicht_GUI.fig
% LESELICHT_GUI, by itself, creates a new LESELICHT_GUI or raises the existing
% singleton*.
%
% H = LESELICHT_GUI returns the handle to a new LESELICHT_GUI or the handle to
% the existing singleton*.
%
% LESELICHT_GUI('Property','Value',...) creates a new LESELICHT_GUI using the
% given property value pairs. Unrecognized properties are passed via
% varargin to Leselicht_GUI_OpeningFcn. This calling syntax produces a
% warning when there is an existing singleton*.
%
% LESELICHT_GUI('CALLBACK') and LESELICHT_GUI('CALLBACK',hObject,...) call the
% local function named CALLBACK in LESELICHT_GUI.M with the given input
% arguments.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help Leselicht_GUI
% Last Modified by GUIDE v2.5 20-Aug-2019 10:15:12
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @Leselicht_GUI_OpeningFcn, ...
'gui_OutputFcn', @Leselicht_GUI_OutputFcn, ...
'gui_LayoutFcn', [], ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before Leselicht_GUI is made visible.
function Leselicht_GUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin unrecognized PropertyName/PropertyValue pairs from the
% command line (see VARARGIN)
% Choose default command line output for Leselicht_GUI
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
for i = 1:1024
set(handles.(['T' num2str(i)]), 'Callback', @pixel_Callback);
end
% UIWAIT makes Leselicht_GUI wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = Leselicht_GUI_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
% --- Executes during object creation, after setting all properties.
function slider1_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
function pixel_Callback(hObject, eventdata, handles)
global TData
brtn = get(handles.slider2, 'Value');
Tag = get(hObject, 'Tag');
PPos = 0;
Si = size(Tag);
for i = 2:Si(2)
PPos = PPos*10 + str2num(Tag(i));
end
brt = max(0.3,brtn);
if(get(hObject,'Value') == 1)
set(hObject,'BackgroundColor',[1*brt 1*brt 0]);
TData(PPos) = 256 * brtn;
elseif (get(hObject,'Value') == 0)
set(hObject,'BackgroundColor',[0.149 0.149 0.149]);
TData(PPos) = 0;
end
In before: slider2 is the correct Tag becasue I tried to fix the problem by using a new slider.
The message tells you, that the callback function is called with too few input arguments. Instead of
set(handles.(['T' num2str(i)]), 'Callback', @pixel_Callback);
write:
set(handles.(['T' num2str(i)]), 'Callback', {@pixel_Callback, handles});
If you want to consider my recommendations, replace this by:
for i = 1:1024
set(handles.(['T' num2str(i)]), 'Callback', {@pixel_Callback, handles, i});
end
and simplify the pixel_Callback function:
function pixel_Callback(hObject, eventdata, handles, PPos)
global TData
brtn = get(handles.slider2, 'Value');
brt = max(0.3, brtn);
if get(hObject,'Value')
set(hObject,'BackgroundColor', [brt brt 0]);
TData(PPos) = 256 * brtn;
else
set(hObject,'BackgroundColor',[0.149 0.149 0.149]);
TData(PPos) = 0;
end
Sorry for not using the comment section.
Jan's approach to this was the most simple one but thanks for all your replys, the Matrix now works as it should.
I also want to implement a Mousemode, Guillaume's approach with creating an Image and reading the coordinates from the ButtonDownFunction sounds pretty interesting for this one!
Thanks for your help @all!

Sign in to comment.

More Answers (0)

Categories

Find more on App Building in Help Center and File Exchange

Products

Release

R2016b

Community Treasure Hunt

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

Start Hunting!