I am making a python code that creates a hydro dynamic simulation of the galaxy using swiftsmio. I want to add a progress bar that is able to show the how much time is left until data is fully plotted into an image. I want to use a decorator function so the interpreter knows I want to first run the progress bar before the plot function.
Here is the plot function that I want to add the progress bar decorator to:
def plot(self):
self.render(DataSetName="masses")
fig, axes = plt.subplots(1)
axes.imshow(self.map, norm=LogNorm())
axes.set_title("Mass Map", fontsize=10)
plt.show()
For the progress bar I am using psutil. However, I am not sure whether the progress bar function works.
Here is progress bar function:
import time
import psutil
def progress_bar(duration):
start_time = time.time()
while True:
elapsed_time = time.time() - start_time
# Calculate the CPU usage
cpu_usage = psutil.cpu_percent()
# Calculate the progress as a percentage of the duration
progress = (elapsed_time / duration) * 100
# Check if the progress bar has reached the end
if elapsed_time > duration:
break
# Sleep for 0.1 seconds to allow the CPU usage to be updated
time.sleep(0.1)
# Run the progress bar for 10 seconds
progress_bar(10)
For further information I am using oop. I have all my different maps in different classes which are inherting the render method form the map superclass.
I tried putting the progress bar function inside the decorator function, but the image would be plotted first and the progress bar did not run.
def progress_bar(duration):
def wrapper(*args, **kwargs):
start_time = time.time()
while True:
elapsed_time = time.time() - start_time
# Calculate the CPU usage
cpu_usage = psutil.cpu_percent()
# Calculate the progress as a percentage of the duration
progress = (elapsed_time / duration) * 100
# Check if the progress bar has reached the end
if elapsed_time > duration:
break
# Sleep for 0.1 seconds to allow the CPU usage to be updated
time.sleep(0.1)
return duration(*args, **kwargs)
return wrapper
I am not sure how to implemet decorators into code, so this is probably very wrong.
[EDIT2] I am using the code below to plot a real time signal from serial port.
The issue I have is that the plot lags a lot even though I am not replotting the whole screen.
How can I speed up the process?
function timerCallback(~,~,hObject)
handles = guidata(hObject);
[r,w]=size(handles.y);
if isfield(handles,'s')
try
readasync(handles.s);
n = handles.s.BytesAvailable;
if n > 0
in = fscanf(handles.s,'%d');
handles.y=[handles.y,in];
guidata(hObject,handles);
set(handles.h,'yData',(handles.y-99)*26/855);
startSpot = (w)-1000;
axis([ startSpot, (w+50), 0 , 30 ]);
drawnow
end
catch
end
end
Timer is set to fixedRate at 0.001s.
[EDIT2]
I have moved the plot line outside the timer callback. I can't barely see any difference in performance.
In World Of Warcraft I have created a little coords script that outputs current coords:
local function ou(self,elapsed)
px,py=GetPlayerMapPosition("player")
DEFAULT_CHAT_FRAME:AddMessage(format("( %s ) [%f , %f]",GetZoneText(), px *100, py *100))
end
local f = CreateFrame("frame")
f:SetScript("OnUpdate", ou)
This however spams default chat frame...
How would I create custom frame and how would I access it?
(I can't use custom channel with SendChatMessage)
...I would like to do this WITHOUT making an addon, thanks :)
I found a solution in storing frame in global variable, as I don't intend to create a plugin, the whole program requires a few macros (macro's maximum number of characters is 255).
First macro - prepare function that will set frame attributes later
f = input frame that will be set
x = x coordinate of position
y = y coordinate of position
function setMyFrame(f,x,y)
f:SetSize(288,100)
f:SetPoint("TOPLEFT",UIParent,"TOPLEFT",x,y)
f.text = f.text or f:CreateFontString(nil,"ARTWORK","QuestFont_Shadow_Huge")
f.text:SetAllPoints(true)
end
Second macro - prepare coords function that will set current coords as frame's text
ctotel = time elapsed since last update of the frame
creft = how often should the frame be updated in SECONDS - good one is 0.1 - 10 times a second is performance friendly and fast enaugh to coords update
f = input frame that will be updated
i = "how long it's been since the last update call cycle" (you don't set that - it is inherited from the WoW system)
ctotel = 0
creft = 0.1
function myCoords(f,i)
ctotel = ctotel + i
if ctotel >= creft then
px,py=GetPlayerMapPosition("player")
f.text:SetText(format("( %s ) [%f , %f]",GetZoneText(), px *100, py *100))
ctotel = 0
end
end
Third macro - store frame in global variable and set it and run update script with myCoords as callback
myCoordsFrame = CreateFrame("Frame","MyCoordsFrame",UIParent)
setMyFrame(myCoordsFrame, 500, 0)
myCoordsFrame:SetScript("OnUpdate", myCoords)
Of course in game all macros have to be preceeded with /run and have to be inlined - no line breaks - instead of linebreak just make space...
Also you have to run macros in THIS ^^^ order (first=>second=>third)
Advantage in setting frame and creft as globals:
Frames can't be destroyed while in the world (you have to relog to destroy them) so when it's global you can later move it with
/run setMyFrame(myCoordsFrame, NEW_X_COORDINATE, NEW_Y_COORDINATE)
If you would like the coords to update slower/faster you can do it by resetting the creft - e.g. to almost realtime refresh every 0.05 or even 0.01 seconds:
/run creft = 0.05 ... or even /run creft = 0.01
Make Coords movable - draggable by left mouse (credit to Wanderingfox from WoWhead):
myCoordsFrame:SetMovable(true)
myCoordsFrame:EnableMouse(true)
myCoordsFrame:SetScript("OnMouseDown",function() myCoordsFrame:StartMoving() end)
myCoordsFrame:SetScript("OnMouseUp",function() myCoordsFrame:StopMovingOrSizing() end)
...and as copy-paste ingame macro:
/run myCoordsFrame:SetMovable(true) myCoordsFrame:EnableMouse(true) myCoordsFrame:SetScript("OnMouseDown",function() myCoordsFrame:StartMoving() end) myCoordsFrame:SetScript("OnMouseUp",function() myCoordsFrame:StopMovingOrSizing() end)
I am running the below code 400 times. I have 60 charts on the sheet. Execution time is 300 sec. If I remove this line
minVal = 0.02 * (cht.Chart.Axes(xlValue).MaximumScale - cht.Chart.Axes(xlValue).MinimumScale)
the speed improves to 190 seconds. This line impacts nothing given minVal is overwritten by 0 right after (for the purpose of the test). I am looking to understand why accessing the axis of the chart is so time consuming and for a workaround.
Sub quickAdjustLabels()
Dim cht As Excel.ChartObject
For Each cht In ActiveSheet.ChartObjects
isProdChart = 0
If cht.Chart.SeriesCollection(1).ChartType <> 5 Then 'different from pie
minVal = 0.02 * (cht.Chart.Axes(xlValue).MaximumScale - cht.Chart.Axes(xlValue).MinimumScale)
minVal = 0
For Each myCollection In cht.Chart.SeriesCollection
'if Stack and if not white visible (white visible are the bottom of waterfall charts / white unvisible are the NC stacks) => remove label is too small
If (myCollection.ChartType = xlColumnStacked Or myCollection.ChartType = xlColumnStacked100) And (myCollection.Format.Fill.Visible = msoFalse Or myCollection.Format.Fill.ForeColor.RGB <> 16777215) Then
myCollection.ApplyDataLabels
vals = myCollection.Values
For i = LBound(vals) To UBound(vals)
If Abs(vals(i)) < minVal Then myCollection.Points(i).HasDataLabel = False
Next
End If
If myCollection.Name = Range("Client") Then isProdChart = 1 'Identify productivity charts
Next myCollection
'Remove labels on productivity charts
If isProdChart = 1 Then
For Each myCollection In cht.Chart.SeriesCollection
If myCollection.ChartType = xlColumnStacked Then myCollection.DataLabels.Delete
Next
End If
End If
Next cht
End Sub
Your problem is not the statement that you pointed out, but actually the statements that apply the DataLabels:
myCollection.ApplyDataLabels
myCollection.Points(i).HasDataLabel = False
Setting the DataLabels take longer time the more points you have in your graph. So trying to avoid running these commands unnecessarily could potentially save you some time. Before setting the values, verify that it is necessary to change them
If Not myCollection.HasDataLabels Then
myCollection.ApplyDataLabels
End If
For i = LBound(Vals) To UBound(Vals)
shouldHaveLabel = True
If Abs(Vals(i)) < MinVal Then
shouldHaveLabel = False
End If
If myCollection.Points(i).HasDataLabel <> shouldHaveLabel Then
myCollection.Points(i).HasDataLabel = shouldHaveLabel
End If
Next
I hope this helps you.
I came to this conclusion by running your code on one of my excel-files with 56 graphs.
I added a time-measure that would tell me at the end of the execution how long time it took to execute, and ran it over and over again, commenting out different blocks of code until I could pinpoint which block was the one taking long time.
Dim tm As Date
tm = Now() 'get timestamp when execution started
...here goes the code to measure...
Debug.Print(Now()-tm)*24*60*60 'Show how many seconds execution took
I have designed the following GUI in which there are an axes. I want to save the plot drawn inside them to a jpeg file. However, the file obtained is an image of the overall figure window. This is my code:
X = 0:pi/100:2*pi;
Y = sin(X);
fh = figure;
Pan1 = uipanel(fh,'Units','normalized','Position',[0 0 0.5 1],'title',...
'Panel1');
Pan2 = uipanel(fh,'Units','normalized','Position',[0.5 0 0.5 1],'title',...
'Panel2');
haxes = axes('Parent',Pan2,'Units', 'normalized','Position',...
[0.25 0.25 0.5 0.5]);
hplot = plot(haxes,X,Y);
xlabel(haxes,'Time (second)');
ylabel(haxes,'Amplitude (meter)');
title(haxes,'Sine function');
FileName = uiputfile('*.jpg','Save as');
saveas(hplot,FileName);
saveas only saves figures, not individual plots.
If you have a subplot, or a plot within a uicontrol like you have, you can make a temporary copy of the plot, save it, then delete the temporary copy:
ftmp = figure; atmp = axes;
copyobj(hplot, atmp);
saveas(ftmp, FileName);
delete(ftmp);
If you don't want the temporary copy to flash up on the screen during the copying step, you can use the 'Position' property of the figure to create it off-screen.
Hope that helps!
#Sam's answer is spot on, I just want to add that Matlab is smart enough to know what kind of file you want to save by inspecting the FileName string variable. If you set FileName to something that ends in .jpg, you can save a jpeg. Check out the saves docs to see all the other possible filetypes.
When using the saveas function to create jpeg the resolution is different as when manually saving the figure with File-->Save As..., It's more recommended to use hgexport instead, as follows:
hgexport(gcf, 'figure1.jpg', hgexport('factorystyle'), 'Format', 'jpeg');
This will do exactly as manually saving the figure.
source:
http://www.mathworks.com/support/solutions/en/data/1-1PT49C/index.html?product=SL&solution=1-1PT49C
This is my solution based on Sam Roberts and eykanal's answer:
X = 0:pi/100:2*pi;
Y = sin(X);
fh = figure('toolbar','none','menubar','none');
Pan1 = uipanel(fh,'Units','normalized','Position',[0 0 0.5 1],'title',...
'Panel1');
Pan2 = uipanel(fh,'Units','normalized','Position',[0.5 0 0.5 1],'title',...
'Panel2');
haxes = axes('Parent',Pan2,'Units', 'normalized','Position',...
[0.125 0.1 0.75 0.75]);
hplot = plot(haxes,X,Y);
xlabel(haxes,'Time (second)');
ylabel(haxes,'Amplitude (meter)');
title(haxes,'Sine function');
FileName = uiputfile('*.bmp;*.png;*.jpg;*.tif','Save as');
ftmp = figure('Menu','none','Toolbar','none','Units','normalized',...
'Position',[-1000 -1000 1 1]);
new_axes = copyobj(haxes, ftmp);
set(new_axes,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);
saveas(ftmp, FileName);
delete(ftmp);
delete(fh);