Hi!
I need to create a progress bar like the following bar with the APP DESIGN:
% Button pushed function: ProcessDataButton
function ProcessDataButtonPushed(app, event)
% Change button name to "Processing"
app.ProcessDataButton.Text = 'Processing...';
% Put text on top of icon
app.ProcessDataButton.IconAlignment = 'bottom';
% Create waitbar with same color as button
wbar = permute(repmat(app.ProcessDataButton.BackgroundColor,15,1,200),[1,3,2]);
% Black frame around waitbar
wbar([1,end],:,:) = 0;
wbar(:,[1,end],:) = 0;
% Load the empty waitbar to the button
app.ProcessDataButton.Icon = wbar;
% Loop through something and update waitbar
n = 10;
for i = 1:n
% Update image data (royalblue)
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
app.ProcessDataButton.Icon(2:end-1, 2:currentProg+1, 1) = 0.25391;
app.ProcessDataButton.Icon(2:end-1, 2:currentProg+1, 2) = 0.41016;
app.ProcessDataButton.Icon(2:end-1, 2:currentProg+1, 3) = 0.87891;
% Pause to slow down animation
pause(.3)
end
% remove waitbar
app.ProcessDataButton.Icon = '';
% Change button name
app.ProcessDataButton.Text = 'Process Data';
end
I need the progress bar to update AFTER calling my function, so I changed it into something like that:
% Button pushed function: ProcessDataButton
function ProcessDataButtonPushed(app, event)
% Change button name to "Processing"
app.ProcessDataButton.Text = 'Processing...';
% Put text on top of icon
app.ProcessDataButton.IconAlignment = 'bottom';
% Create waitbar with same color as button
wbar = permute(repmat(app.ProcessDataButton.BackgroundColor,15,1,200),[1,3,2]);
% Black frame around waitbar
wbar([1,end],:,:) = 0;
wbar(:,[1,end],:) = 0;
% Load the empty waitbar to the button
app.ProcessDataButton.Icon = wbar;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Loop through something and update waitbar
my_function(do things...)
<events?>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% remove waitbar
app.ProcessDataButton.Icon = '';
% Change button name
app.ProcessDataButton.Text = 'Process Data';
end
My problem: I don't know how to keep trace of my updates. Do I need to create an event? I followed this guide: Define Custom Event Data - MATLAB & Simulink - MathWorks Italia, but I did not manage to solve my problem: I need intermediate output before the end of the function to trace my advancements (hope to explain myself, my technical language is poor in this field). Is my path ok or not?
Do you have suggestions? Thank you

댓글 수: 5

Adam Danz
Adam Danz 2021년 2월 19일
@Veronica Taurino it depends on what's controlling your updates. Are the updates done in a loop? Are the updates done in sections of code?
Also, are the updates done in the app file or outside of the app?
Veronica Taurino
Veronica Taurino 2021년 2월 19일
편집: Veronica Taurino 2021년 2월 19일
The updates are done outside the app, in a loop code within an external function (image) called by the buttoncallback.
Adam Danz
Adam Danz 2021년 2월 22일
See the 3 easy steps below.
Gareth
Gareth 2021년 5월 7일
Would be nice if this shipped with App Designer, it is a common thing for User Interfaces.
Adam Danz
Adam Danz 2021년 5월 7일
@GT see uiprogressbar which is not embedded in a button but otherwise the same thing as this.

댓글을 달려면 로그인하십시오.

 채택된 답변

Adam Danz
Adam Danz 2021년 2월 19일
편집: Adam Danz 2023년 8월 15일

5 개 추천

Step 0: Set up the app button
Add a button to your app and add a ButtonPushed callback function. Importantly, the height of the button must be extended to have enough space for the progress bar.
Step 1: Send button handle to external function
Call the external function from within the ButtonPushed callback function and pass the button handle to the pseudo-progress-bar as an input to the external function. I call it a pseudo-progress-bar because it's not a typical waitbar or uiprogressdlg.
app.ProcessDataButton is the button handle.
function ProcessDataButtonPushed(app, event)
myFunction(___, app.ProcessDataButton)
end
Also, define the additional input within the external function.
function myFunction(___, processDataButtonHandle)
Step 2: Set up the progress bar within the external function
Obviously this needs set up prior to the loop that will update the progress bar. Some of this block of code differs from the original example. Thanks to onCleanup in the last line below, the button will return to its original state after the loop or if an error prematurely ends the function.
% Store original button text
originalButtonText = processDataButtonHandle.Text;
% When the function ends, return the original button state
cleanup = onCleanup(@()set(processDataButtonHandle,'Text',originalButtonText,'Icon',''));
% Change button name to "Processing"
processDataButtonHandle.Text = 'Processing...';
% Put text on top of icon
processDataButtonHandle.IconAlignment = 'bottom';
% Create waitbar with same color as button
wbar = permute(repmat(processDataButtonHandle.BackgroundColor,15,1,200),[1,3,2]);
% Black frame around waitbar
wbar([1,end],:,:) = 0;
wbar(:,[1,end],:) = 0;
% Load the empty waitbar to the button
processDataButtonHandle.Icon = wbar;
Step 3: Update the progress bar within a loop
Place the code within the for-loop at the beginning or end of the loop depending on whether you want the progress bar to update before or after your loop processes. This example updates the progress bar at the end of the loop. (note: Code in step 3 was updated on Feb-11-2022 to improve efficiency).
This section uses variables:
  • n - the number of iterations
  • i - the loop variable
n = 10;
for i = 1:n
% % % % % % % % % % % % % % % % % %
% % YOUR LOOP STUFF GOES HERE % %
% % % % % % % % % % % % % % % % % %
% Update progress bar
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
RGB = app.processDataButtonHandle.Icon;
RGB(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
RGB(2:end-1, 2:currentProg+1, 2) = 0.41016;
RGB(2:end-1, 2:currentProg+1, 3) = 0.87891;
app.processDataButtonHandle.Icon = RGB;
% Pause to slow down animation (OPTIONAL)
% pause(.3)
end
To clear the psueo-progress-bar and return the button to normal you have two options
  • Do nothing. The bar will be cleared and the button will return to normal after the external funtion is complete.
  • Exectue clear('cleanup') at any point after the loop within the same function to return the button state.

댓글 수: 14

Veronica Taurino
Veronica Taurino 2021년 2월 23일
Thank you, it works! I thought about it... with your solution I mixed the graphic part with the code, so later I may have trouble with changes in the workflow but for now it works fine! Thank you!! I can move on!
I will post another solution if I change it in the future
Adam Danz
Adam Danz 2021년 2월 23일
These are the basic steps to understand how the pseudo-progressbar is created and controlled. it can be adapted to fit your needs.
> with your solution I mixed the graphic part with the code
With any solution, the section of code that controls the progess will also control the progress bar.
Another approach is to create the progress bar from within the app rather than from within the external function. That just requires moving the existing code around. Then your code could merely call an update function to update the progress bar.
There are several ways to control the pseudo-progress bar once you understand what each section in my answer does.
Veronica Taurino
Veronica Taurino 2021년 2월 23일
편집: Veronica Taurino 2021년 2월 23일
Thank you. Your solution is clear, I get all of it (it is close to my initial idea to solve my issue). I am talking about do not mix the graphic and the code part because an IT engineer I work with told me to use ''events'' to do not mix it, but I have never used events, therefore my previous doubts. I have already written an alternative solution but I get few errors for now. I going to post it if I solve them.
Adam Danz
Adam Danz 2021년 2월 23일
I see. It would help knowing what your loop is going. What would the be the event that triggers the progress update? Is it a variable value change? A graphics update? And does the app have access to that event?
Mark Golberg
Mark Golberg 2022년 2월 11일
How to make the progress bar (along with the "processing..." text) to be less jittering....
It looks as it the who thing is being "deleted" and "re-drawen" during each iteration, creating an unnatural look&feel appearance of the progress bar.
In the example the "processing..." is rock solid, and I see only the progress bar advances.
THANK YOU
Adam Danz
Adam Danz 2022년 2월 11일
@Mark Golberg I know what you mean. There are things we can do to reduce the flicker but I can't remove it completely.
I just attached a simple app that demonstrates my ideas to this thread. You'll see that it flickers but less frequently.
Why is it flickering?
The progress bar is actually created with 3D image data and when the image is updated, it appears that the entire icon (or image) is cleared and re-rendered. This happens under the hood. However, the transition doesn't always appear as a flicker so at least sometimes it happens fast enough to not notice it. We have no control over this but I'd love to hear anyone else's ideas.
Two changes that will decrease frequency of flicker
1. Assign the RGB image once instead of separately for each R/G/B channel. I should have done this in the first place. This will reduce the rendering cycles by 1/3. Replace these lines,
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
processDataButtonHandle.Icon(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
processDataButtonHandle.Icon(2:end-1, 2:currentProg+1, 2) = 0.41016;
processDataButtonHandle.Icon(2:end-1, 2:currentProg+1, 3) = 0.87891;
with this more efficient method,
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
RGB = app.processDataButtonHandle.Icon;
RGB(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
RGB(2:end-1, 2:currentProg+1, 2) = 0.41016;
RGB(2:end-1, 2:currentProg+1, 3) = 0.87891;
app.processDataButtonHandle.Icon = RGB;
2. If your loop has many iterations and the cycles are fast, you can update the embedded progress bar evey n cycles instead of every cycle. For example, if your loop has 100 iterations, you could update the graphic every 5 or 10 iterations. To achieve this, move the progress bar update code to a conditional block demonstrated below. Use mod(i,n)==0 to determine if iteration i is a multiple of n .
% Update every 10 cycles
for i = 1:n
% % % % % % % % % % % % % % % % % %
% % YOUR LOOP STUFF GOES HERE % %
% % % % % % % % % % % % % % % % % %
if mod(i,10)==0 % update every 10 cycles; improve efficiency
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
RGB = app.processDataButtonHandle.Icon;
RGB(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
RGB(2:end-1, 2:currentProg+1, 2) = 0.41016;
RGB(2:end-1, 2:currentProg+1, 3) = 0.87891;
app.processDataButtonHandle.Icon = RGB; % (royalblue)
end
Mark Golberg
Mark Golberg 2022년 2월 11일
unfortunately neither of the tow proposed solutions reduced the jitterging.
It's really a pity. I wanted to use the "old" progress bar, and then saw this cool thread about progress bar that open in a push button. really neat. not sure my clients of the GUI would be too happy if I leave this jittering going on...
Adam Danz
Adam Danz 2022년 2월 12일
Both significantly reduced the jittering when I tested it but neither elimiated it completely.
There are other embedded alternatives if you want to get creative. For example, you could customize a knob or guage that rotates from 0 to 100% or a slider that progresses from 0:100%. You could turn the editable property off so users know that the UI component is not meant for user interaction or you could turn on/off the visibility of the component as needed.
Oren Lee
Oren Lee 2022년 8월 26일
편집: Oren Lee 2022년 8월 26일
Adding to what Adam did above, I did the same thing but with a few changes.
I created a public function in the app that does all this for me so that a more complex loop elsewhere can run on its own. Additionally I am using an "image" and changing the "imageSource" property to fill the bar.
Note that app.Progress is public property of the app which is continually updated
function progressBarUpdate(app,arg,val)
% app
% arg - 'Setup', 'Reset', 'Increment', 'Set', 'Complete'
% val - 0 to 1, based on above selection
switch lower(arg)
case {'setup','reset'}
pos = app.ProgressBar.OuterPosition;
wbar = permute(repmat([1,1,1],pos(4)-2,1,pos(3)-2),[1,3,2]);
wbar([1,end],:,:) = 0;
wbar(:,[1,end],:) = 0;
app.ProgressBar.ImageSource = wbar;
app.progress = 0;
return
case 'increment'
curProg = app.progress + val;
case 'set'
curProg = val;
case 'complete'
curProg = 1;
end
if curProg > 1; curProg = 1; end
if curProg < 0; curProg = 0; end
wbar = app.ProgressBar.ImageSource;
currentProg = min(round((size(wbar,2)-2)*(curProg)),size(wbar,2)-2);
wbar(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
wbar(2:end-1, 2:currentProg+1, 2) = 0.41016;
wbar(2:end-1, 2:currentProg+1, 3) = 0.87891;
app.ProgressBar.ImageSource = wbar;
% The below line is for a percentage display which I have
% overlaid on top of the progressbar
app.ProgressBarText.Text = [num2str(100*curProg,'%0.1f'),'%'];
end
Adam Danz
Adam Danz 2022년 9월 16일
Thanks for sharing that, @Oren Lee!
FABRIZIO MONCELLI
FABRIZIO MONCELLI 2023년 5월 28일
@Oren Lee could you share the full code for the progress bar, please? I tried to follow the steps but the code does not work
Mohammed Azharuddin
Mohammed Azharuddin 2023년 8월 15일
편집: Adam Danz 2023년 8월 15일
@Adam Danz I am trying to use this inside startup function. I am on 2022b. I have used the following code as suggested, and there is no colour change or any colour on the progress button, could you please help me troubleshoot what am I overlooking, Thanks!
originalButtonText = app.ProgressButton.Text;
cleanup = onCleanup(@()set(app.ProgressButton,'Text',originalButtonText,'Icon',''));
app.ProgressButton.Text = 'Processing...';
app.ProgressButton.IconAlignment = 'bottom';
wbar = permute(repmat(app.ProgressButton.BackgroundColor,15,1,200),[1,3,2]);
wbar([1,end],:,:) = 0;
wbar(:,[1,end],:) = 0;
app.ProgressButton.Icon = wbar;
n = 10;
for i = 1:n
%Update progress bar
currentProg = min(round((size(wbar,2)-2)*(i/n)),size(wbar,2)-2);
RGB = app.ProgressButton.Icon;
RGB(2:end-1, 2:currentProg+1, 1) = 0.25391; % (royalblue)
RGB(2:end-1, 2:currentProg+1, 2) = 0.41016;
RGB(2:end-1, 2:currentProg+1, 3) = 0.87891;
app.ProgressButton.Icon = RGB;
% Pause to slow down animation (OPTIONAL)
pause(.3)
end
Tried using pause, and drawnow
Nothing changes.
Also, instead of a button can I use axis? and may be use plot function, by extracting the app.progress value?
Adam Danz
Adam Danz 2023년 8월 15일
What the value for app.ProgressButton.BackgroundColor before you make any changes in startup?
Mohammed Azharuddin
Mohammed Azharuddin 2023년 8월 15일
@Adam Danz Didnt assign any, however, a work around, was I used your idea of patch function and instead of i/n, I just used i in x values to update it, I added for loop in various sections of my startup function, so I know which position it is! Thanks Adam, your comments and replies are really useful.

댓글을 달려면 로그인하십시오.

추가 답변 (0개)

카테고리

도움말 센터File Exchange에서 App Building에 대해 자세히 알아보기

Community Treasure Hunt

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

Start Hunting!

Translated by