c# uwp : dispatch webview navigation and capture on each avaible logical cpu - c#

i'm learning code and doing an app that capture google streetview screenshost. Many screenshots. I want to find a way to parallel the queries.
Here the code for single query at time :
// prepare the html code string in a dictionary (I use street view in a iframe)
Dictionary<int, string> htmlCode= SetUpURLListHD();
// prepare the webview and display it on a grid
WebView saveWebView = new WebView();
saveWebView.Width = Width;
saveWebView.Height = Height;
TopGrid.Children.Add(saveWebView);
// navigate to string, wait a moment until street view completely loaded, do a capture, save it to file m.jpg with custom function
for (var m = 0; m < htmlCode.Count; m++)
{
saveWebView.NavigateToString(htmlCode[m]);
await Task.Delay(2000);
await saveWebView.CapturePreviewToStreamAsync(stream);
await SaveSSAsync(stream, m);
}
I have 16 logical cpu, 64Go of ram, and a fiber connection. So, I want to do until 16 queries at time.
For that, I think I need to write 16 functions to create 16 webviews :
async Task ProcessURLHD1Async(string url, int i, int width, int height, ulong firstFrameSize)
{
try
{
WebView saveWebView1 = new WebView();
saveWebView1.Width = width;
saveWebView1.Height = height;
TopGrid.Children.Add(saveWebView1);
...
Second function :
async Task ProcessURLHD2Async(string url, int i, int width, int height, ulong firstFrameSize)
{
try
{
WebView saveWebView2 = new WebView();
saveWebView2.Width = width;
saveWebView2.Height = height;
TopGrid.Children.Add(saveWebView2);
Etc...
I don't know if it's the right way to do that.
I'm now trying to execute the 16 functions at the same time, and when one ended, start again with next html code (next street view mage coded in the dictionary).
For now, no result.
Any help is welcome.
Thank you !
ps : it's my first message after weeks of coding, thanks for all people who help on this site, very useful for me !
EDIT : Ok ! I found ! It's ugly, but it's fast ! :
// below namespace :
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
//Call function on click
private async void GetHDMT_Click(object sender, RoutedEventArgs e)
{
if (outputFolder == null)
{
var folderPicker = new Windows.Storage.Pickers.FolderPicker();
folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop;
folderPicker.FileTypeFilter.Add("*");
outputFolder = await folderPicker.PickSingleFolderAsync();
if (outputFolder != null)
{
// Application now has read/write access to all contents in the picked folder
// (including other sub-folder contents)
Windows.Storage.AccessCache.StorageApplicationPermissions.
FutureAccessList.AddOrReplace("PickedFolderToken", outputFolder);
}
else
{
}
}
if (mylat.Count > 0)
{
await GetImagesAsyncHD();
}
}
private async Task GetImagesAsyncHD()
{
// Make a list of html code with street view iframe.
var Width = 4000;
var Height = 2000;
var urls = SetUpURLListHD(Width, Height);
WebView saveWebView0 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView0);
WebView saveWebView1 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView1);
WebView saveWebView2 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView2);
WebView saveWebView3 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView3); WebView
saveWebView4 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView4);
WebView saveWebView5 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView5);
WebView saveWebView6 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView6);
WebView saveWebView7 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView7); WebView
saveWebView8 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView8);
WebView saveWebView9 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView9);
WebView saveWebView10 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView10);
WebView saveWebView11 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView11);
WebView saveWebView12 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView12);
WebView saveWebView13 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView13);
WebView saveWebView14 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView14);
WebView saveWebView15 = new WebView
{
Width = Width,
Height = Height
};
TopGrid.Children.Add(saveWebView15);
List<Task> tasks = new List<Task>();
Task t0 = Task.Run(async () =>
{
for (var j=0; j<urls.Count; j=j+16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t0);
Task t1 = Task.Run(async () =>
{
for (var j = 1; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t1);
Task t2 = Task.Run(async () =>
{
for (var j = 2; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t2);
Task t3 = Task.Run(async () =>
{
for (var j = 3; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t3);
Task t4 = Task.Run(async () =>
{
for (var j = 4; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t4);
Task t5 = Task.Run(async () =>
{
for (var j = 5; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t5);
Task t6 = Task.Run(async () =>
{
for (var j = 6; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t6);
Task t7 = Task.Run(async () =>
{
for (var j = 7; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t7);
Task t8 = Task.Run(async () =>
{
for (var j = 8; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t8);
Task t9 = Task.Run(async () =>
{
for (var j = 9; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t9);
Task t10 = Task.Run(async () =>
{
for (var j = 10; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t10);
Task t11 = Task.Run(async () =>
{
for (var j = 11; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t11);
Task t12 = Task.Run(async () =>
{
for (var j = 12; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t12);
Task t13 = Task.Run(async () =>
{
for (var j = 13; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t13);
Task t14 = Task.Run(async () =>
{
for (var j = 14; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t14);
Task t15 = Task.Run(async () =>
{
for (var j = 15; j < urls.Count; j = j + 16)
await ProcessURLHD0Async(urls[j], j, Width, Height, firstFrameSize, saveWebView0, outputFolder);
});
tasks.Add(t15);
await Task.WhenAll(tasks);
}
public static async Task ProcessURLHD0Async(string url, int i, int width, int height, ulong firstFrameSize, WebView saveWeb, StorageFolder folder)
{
try
{
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
saveWeb.NavigateToString(url);
});
await Task.Delay(3000);
var kk = 0;
var pixelised = true;
while (pixelised == true)
{
ulong thissize = await CaptureAndSave(saveWeb, i, folder);
if ((thissize > 0.7 * firstFrameSize) || kk > 5)
{
pixelised = false;
}
else
{
await Task.Delay(1000);
kk = kk + 1;
}
}
}
catch (Exception)
{
throw;
}
}
public static async Task<ulong> CaptureAndSave(WebView webv, int i, StorageFolder folder)
{
ulong size = 0;
SoftwareBitmap softwareBitmap = null;
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
BitmapDecoder decoder = null;
// Changes to imageElement must happen on the UI thread.
await webv.Dispatcher.RunTaskAsync(async () =>
{
await webv.CapturePreviewToStreamAsync(stream);
decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
});
StorageFile file_Save = await folder.CreateFileAsync(i + ".jpg", CreationCollisionOption.ReplaceExisting);
if (file_Save != null)
{
using (var streamF = await file_Save.OpenAsync(FileAccessMode.ReadWrite))
{
//StorageFile file_Save = await outputFolder.CreateFileAsync(i + "_" + equals + "_" + size + ".jpg", CreationCollisionOption.ReplaceExisting);
//< encoder to save >
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId, streamF);
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
//</ encoder to save >
//-</ Save Bitmap as File >-
}
}
var basicProperties = await file_Save.GetBasicPropertiesAsync();
size = basicProperties.Size;
while (size < 21)
{
await Task.Delay(100);
basicProperties = await file_Save.GetBasicPropertiesAsync();
size = basicProperties.Size;
}
stream.Dispose();
softwareBitmap.Dispose();
return size;
}
Results : 118 capture (total = 139Mo) in 20 sec against 278 without paralleling !!! 5 GB of ram used at max.
Edit 2 :
To fast... there is an error in above code, I always use Savewebview0, when I correct (Savewebview1, Savewebview2....) the results is... 400 seconds...
All the work is do by the main thread I think, I need to create webviews in others threads, but I think it's not possible...
If anyone has a idea... :(

Ok, after many trials, I have good results.
The code is here for who want to use it :
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
Above code below namespace delcaration.
private async void GetHDMT2_Click(object sender, RoutedEventArgs e)
{
if (outputFolder == null)
{
var folderPicker = new Windows.Storage.Pickers.FolderPicker();
folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop;
folderPicker.FileTypeFilter.Add("*");
outputFolder = await folderPicker.PickSingleFolderAsync();
if (outputFolder != null)
{
Windows.Storage.AccessCache.StorageApplicationPermissions.
FutureAccessList.AddOrReplace("PickedFolderToken", outputFolder);
}
else
{
}
}
if (mylat.Count > 0)
{
await GetImagesAsyncHD2();
}
}
private async Task GetImagesAsyncHD2()
{
// Make a dictionary with html code string (with street view iframe inside .
var Width = 4000;
var Height = 2000;
Dictionary<int,string> urls = SetUpURLListHD(Width, Height);
//Code to estimate time for completely loading the first page (compute a time to use in await task.delay, and the size of first frame nextly used to know if image is pixelised or not (street view fisnish loading or not)
SoftwareBitmap softwareBitmap = null;
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
BitmapDecoder decoder = null;
var k = 0;
WebView saveWebView = new WebView();
saveWebView.Width = Width;
saveWebView.Height = Height;
TopGrid.Children.Add(saveWebView);
var stopwatch = new Stopwatch();
stopwatch.Start();
long elapsed_time=0;
saveWebView.NavigateToString(urls[0]);
await Task.Delay(10);
bool equals = false;
bool findeq = false;
int t = 0;
int countTrue = 0;
bool checka = false;
List<byte[]> bytes400a = null;
List<byte[]> bytes400b = null;
await saveWebView.CapturePreviewToStreamAsync(stream);
decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
bytes400a = GetPixelsP(softwareBitmap);
while (countTrue < 20)
{
await Task.Delay(50);
if (checka)
{
await saveWebView.CapturePreviewToStreamAsync(stream);
decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
bytes400a = GetPixelsP(softwareBitmap);
checka = false;
}
else
{
await saveWebView.CapturePreviewToStreamAsync(stream);
decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
bytes400b = GetPixelsP(softwareBitmap);
checka = true;
}
t = t + 1;
equals = EqualsByte(bytes400a, bytes400b);
if (equals)
{
countTrue = countTrue + 1;
}
if ((equals) && (findeq == false))
{
k = t;
elapsed_time = stopwatch.ElapsedMilliseconds;
SlidetimeCompute.Value = elapsed_time;
findeq = true;
}
if (equals == false) { findeq = false; }
}
stopwatch.Stop();
var elapsed_timeForTest = stopwatch.ElapsedMilliseconds;
await SaveSSAsync(softwareBitmap, "0");
ulong firstFrameSize = await GetSizePicAsync("0");
TopGrid.Children.Remove(saveWebView);
stream.Dispose();
softwareBitmap.Dispose();
double timeTowait = (double)elapsed_time;
timeTowait = Math.Round(timeTowait / 4);
// prepare somes grids
var nj = 7;
List<Grid> grids = new List<Grid>();
Grid tmpgrid0 = new Grid();
grids.Add(tmpgrid0);
Grid tmpgrid1 = new Grid();
grids.Add(tmpgrid1);
Grid tmpgrid2 = new Grid();
grids.Add(tmpgrid2);
Grid tmpgrid3 = new Grid();
grids.Add(tmpgrid3);
Grid tmpgrid4 = new Grid();
grids.Add(tmpgrid4);
Grid tmpgrid5 = new Grid();
grids.Add(tmpgrid5);
Grid tmpgrid6 = new Grid();
grids.Add(tmpgrid6);
Grid tmpgrid7 = new Grid();
grids.Add(tmpgrid7);
Grid tmpgrid8 = new Grid();
grids.Add(tmpgrid8);
Grid tmpgrid9 = new Grid();
grids.Add(tmpgrid9);
Grid tmpgrid10 = new Grid();
grids.Add(tmpgrid10);
Grid tmpgrid11 = new Grid();
grids.Add(tmpgrid11);
Grid tmpgrid12 = new Grid();
grids.Add(tmpgrid12);
Grid tmpgrid13 = new Grid();
grids.Add(tmpgrid13);
Grid tmpgrid14 = new Grid();
grids.Add(tmpgrid14);
Grid tmpgrid15 = new Grid();
grids.Add(tmpgrid15);
Grid tmpgrid16 = new Grid();
grids.Add(tmpgrid16);
// add tasks
List<Task> tasks = new List<Task>();
for (var i = 1; i < nj+1; i++)
{
TopGrid.Children.Add(grids[i]);
await Task.Delay(1000);
tasks.Add(await AddTaskAsync(i, nj, Width, Height, firstFrameSize,outputFolder, grids[i], urls, timeTowait)
);
}
// await all tasks
await Task.WhenAll(tasks);
}
public static async Task<Task> AddTaskAsync(int i, int nj, int width, int height, ulong firstFrameSize, StorageFolder folder, Grid grid, Dictionary<int, string> urls, double timeTowait)
{
var t0 = Task.Run(async () =>
{
for (var j = i; j < urls.Count; j = j + nj)
await ProcessURLHD1Async(urls[j], j, width, height, firstFrameSize, folder, grid, timeTowait);
});
await Task.Delay(200);
return t0;
}
public static async Task ProcessURLHD1Async(string url, int i, int width, int height, ulong firstFrameSize, StorageFolder folder, Grid grid, double timeTowait)
{
try
{
var Dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
await grid.Dispatcher.RunTaskAsync(async () =>
{
var saveWeb1 = new WebView(WebViewExecutionMode.SeparateThread)
{
Width = width,
Height = height
};
grid.Children.Add(saveWeb1);
saveWeb1.NavigateToString(url);
//give street view some delay to load
await Task.Delay((int)timeTowait*5);
var kk = 0;
var pixelised = true;
SoftwareBitmap softwareBitmap = null;
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
BitmapDecoder decoder = null;
while (pixelised == true)
{
await saveWeb1.CapturePreviewToStreamAsync(stream);
decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
StorageFile file_Save = await folder.CreateFileAsync(i + ".jpg", CreationCollisionOption.ReplaceExisting);
if (file_Save != null)
{
using (var streamF = await file_Save.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId, streamF);
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
}
}
var basicProperties = await file_Save.GetBasicPropertiesAsync();
ulong thissize = basicProperties.Size;
while (thissize < 21)
{
await Task.Delay(100);
basicProperties = await file_Save.GetBasicPropertiesAsync();
thissize = basicProperties.Size;
}
if ((thissize > 0.7 * firstFrameSize) || kk > 10)
{
pixelised = false;
}
else
{
await Task.Delay((int)timeTowait*2);
kk = kk + 1;
}
}
stream.Dispose();
softwareBitmap.Dispose();
grid.Children.Clear();
});
}
catch (Exception)
{
throw;
}
}
Best results is with 7 task in parallel. Why 7 ? Don't know.
With single task, 118 captures (141 Mo) in 304 sec, with 7 tasks 58 secs !
Now, I need to find a way to adapt the code to different hardware and internet connection.

Related

How can I prevent the backgroundWorker causing the UI to become sluggish?

I made a C# WinForms application where I plot thousands of real time data points by using charts. I have noticed that during my application is running when I turn on lets say a web-browser the plot freezes. I tried to plot less points but it seems one never knows which program in parallel will be executed so I'm afraid the CPU usage of other programs depending on the PC will effect the performance.
edit:
private void button1_Click(object sender, EventArgs e)
{
///
_cts = new CancellationTokenSource();
_infiniteLoop = InfiniteLoop(_cts.Token);
}
private async Task InfiniteLoop(CancellationToken cancellationToken = default)
{
ushort[] ushortArray = null;
while (true)
{
Task loopMinimumDurationTask = Task.Delay(100, cancellationToken);
Task<ushort []> calculationTask = Task.Run(() => Calculate());
if (ushortArray != null) PlotData(ushortArray);
ushortArray = await calculationTask;
await loopMinimumDurationTask;
}
}
public ushort [] Calculate()
{
init();
daq.ALoadQueue(chArray, chRange, CHANCOUNT);
ScanOptions options = ScanOptions.Background | ScanOptions.Continuous | ScanOptions.ConvertData;
//setup the acquisiton
UL = daq.AInScan(FIRSTCHANNEL, SINGLE_KANAL_NUM, BUFFERSIZE, ref Rate, Range.Bip10Volts, buffer, options);
UL = daq.GetStatus(out daqStatus, out Count, out Index, FunctionType.AiFunction);
if ((Index >= HALFBUFFSIZE) & ReadLower) //check for 50% more data
{
//get lower half of buffer
UL = MccService.WinBufToArray(buffer, ushortArray, 0, HALFBUFFSIZE);
ReadLower = false; //flag that controls the next read
return ushortArray;
}
else if ((Index < HALFBUFFSIZE) & !ReadLower)
{
//get the upper half
UL = MccService.WinBufToArray(buffer, ushortArray, HALFBUFFSIZE, HALFBUFFSIZE);
ReadLower = true;//flag that controls the next read
return ushortArray;
}
return null;
}
public void PlotData(ushort[] datArray_Plot)
{
////////Thread.Sleep(10);
SerialList1.Clear();
for (int b = 0; b < HALFBUFFSIZE; b++)
{
UL = (daq.ToEngUnits(Range.Bip10Volts, datArray_Plot[b], out temp2));
SerialList1.Add(temp2);
SerialList2.Add(temp2);
ikb_p = ikb_p + 1;
}
int out_size = SerialList1.Count / h; //size of downsampled array
if (out_size <= 2)
out_size = 2;
array = SerialList1.ToArray(); //original array
if (h != 1)
array = Downsample(array, out_size); //downsampled array
if (ikb_p > BUFFERSIZE)
{
chart1.Series["Ch0"].Points.SuspendUpdates();
for (int b = 0; b < out_size; b++)
{
chart1.Series["Ch0"].Points.AddY(array[b]); //Plots each sample or use chart1.Series["Ch0"].Points.DataBindY(array);
if (chart1.Series["Ch0"].Points.Count > display_seconds * FREQ / h)
{
chart1.Series["Ch0"].Points.RemoveAt(0);
}
}
//chart1.Series["Ch0"].Points.ResumeUpdates();
chart1.Invalidate();
}
//FFT
if (SerialList2.Count > 4 * HALFBUFFSIZE / CHANCOUNT)
{
chart2.Series["Freq"].Points.Clear();
float sampling_freq = (float)FREQ;
float[] data = SerialList2.ToArray();
double[] dftIn = new double[data.Length];
double[] dftInIm = new double[data.Length];
double[] DftIn = new double[data.Length];
double[] FFTResult = new double[data.Length];
double[] f = new double[data.Length];
double[] power = new double[data.Length];
double[] window = MathNet.Numerics.Window.Hamming(data.Length);
for (int i = 0; i < data.Length; i++)
{
dftIn[i] = window[i] * (double)data[i];
}
for (int i = 0; i < data.Length; i++)
{
dftInIm[i] = 0.0;
}
FFT(dftIn, dftInIm, out reFFT, out imFFT, (int)Math.Log(data.Length, 2));
for (int i = 0; i < data.Length / 2; i++)
{
if (i > 0)
{
float a = sampling_freq / (float)data.Length;
float x = (float)i * a;
double y = Math.Sqrt(reFFT[i] * reFFT[i] + imFFT[i] * imFFT[i]);
f[i] = x;
FFTResult[i] = 2 * y / (data.Length / 2);
power[i] = 0.5 * FFTResult[i] * FFTResult[i];
}
}
double scale = data.Length / sampling_freq;
chart2.Series["Freq"].Points.DataBindXY(f, power);
float stdCh0 = 0;
float avg1 = SerialList2.Average();
float max1 = SerialList2.Max();
float min1 = SerialList2.Min();
float sum1 = (float)SerialList2.Sum(d => Math.Pow(d - avg1, 2));
stdCh0 = (float)Math.Sqrt((sum1) / (SerialList2.Count() - 1));
label5.Text = avg1.ToString("0.000000");
label22.Text = stdCh0.ToString("0.000000");
label70.Text = max1.ToString("0.000000");
label61.Text = min1.ToString("0.000000");
SerialList2.Clear();
label1.Text = count_sample.ToString();
}
///progressBar1
double ratio = (double)count_sample / (seconds * FREQ);
if (ratio > 1.000)
ratio = 1;
progressBar1.Value = (Convert.ToInt32(1000 * ratio));
progressBar1.Invalidate();
progressBar1.Update();
//Display event handlers
if (comboBox2_changed == true)
{
if (comboBox2.SelectedIndex == 0)
{
//chart1.ChartAreas[0].RecalculateAxesScale();
chart1.ChartAreas[0].AxisY.IsStartedFromZero = false;
}
if (comboBox2.SelectedIndex == 1)
{
//chart1.ChartAreas[0].RecalculateAxesScale();
chart1.ChartAreas[0].AxisY.IsStartedFromZero = true;
}
comboBox2_changed = false;
}
if (comboBox1_changed == true)
{
if (comboBox1.SelectedIndex == 0)
{
chart1.Series["Ch0"].ChartType = SeriesChartType.FastLine;
}
else
chart1.Series["Ch0"].ChartType = SeriesChartType.FastPoint;
}
if (num_updown1_changed)
{
display_seconds = (float)numericUpDown1.Value * 0.001f;
h = (int)numericUpDown2.Value;
chart1.Series["Ch0"].Points.Clear();
//chart1.ChartAreas[0].AxisX.Maximum = display_seconds * FREQ / h;
num_updown1_changed = false;
int avg = (int)((double)FREQ * (Decimal.ToDouble(numericUpDown1.Value) / 1000.0) / max_chart_points);
if (avg != 0)
numericUpDown2.Value = avg;
}
if (num_updown2_changed)
{
display_seconds = (float)numericUpDown1.Value * 0.001f;
h = (int)numericUpDown2.Value;
chart1.Series["Ch0"].Points.Clear();
//chart1.ChartAreas[0].AxisX.Maximum = display_seconds * FREQ / h;
num_updown2_changed = false;
}
}
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
_cts.Cancel();
// Wait the completion of the loop before closing the form
try { _infiniteLoop.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore this error
}
You could use threadpriority:
Thread.CurrentThread.Priority = ThreadPriority.Highest;
This is however considered to be poor form in most cases since the operating system is in a better position to decide what program deserves CPU time. And it does not need to follow your request for more time, even if you explicitly ask for it.
If plotting takes a considerable amount of time you might consider:
Can you optimize the plotting somehow?
Can you reduce the number of points?
you could perhaps plot a smaller part of the dataset?
You could pre-process the plot to reduce the point density. Screens typically have a resolution of 2k-4k, so if you have a line-chart with more points the user will not be able to see it anyway.
My suggestion is to scrap the obsolete BackgroundWorker, in favor of an infinite asynchronous loop. The example below assumes the existence of a Calculate method that should run on a background thread and should return the result of one calculation, and an UpdateUI method that should run on the UI thread and should consume this result.
private async Task InfiniteLoop(CancellationToken cancellationToken = default)
{
object calculationResult = null;
while (true)
{
Task loopMinimumDurationTask = Task.Delay(100, cancellationToken);
Task<object> calculationTask = Task.Run(() => Calculate());
if (calculationResult != null) UpdateUI(calculationResult);
calculationResult = await calculationTask;
await loopMinimumDurationTask;
}
}
This design has the following characteristics:
The Calculate and the UpdateUI methods are working in parallel.
If the Calculate completes first, it waits the completion of the UpdateUI before starting the next calculation.
If the UpdateUI completes first, it waits the completion of the Calculate before starting the next update of the UI.
If both the Calculate and the UpdateUI complete in under 100 milliseconds, an extra asynchronous delay is imposed, so that no more than 10 loops per second can occur.
The infinite loop can be terminated by canceling the optional CancellationToken.
The object type for the calculationResult variable in the above example is just for demonstration. Unless the result of the calculation is trivial, you should create a class or struct that can store all the data required for updating the UI on every loop. By eliminating all global state you minimize the number of things that can go wrong.
Usage example:
private CancellationTokenSource _cts;
private Task _infiniteLoop;
private void Form_Load(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
_infiniteLoop = InfiniteLoop(_cts.Token);
}
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
_cts.Cancel();
// Wait the completion of the loop before closing the form
try { _infiniteLoop.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore this error
}

creating a tablelayoutpanel of pictureboxes takes a long time

I'm creating this tablelayoutpanel of picture boxes (66x66 pixels) and while on one PC it is taking less then a second to finish, it takes about 8 seconds on another pc (with the same capabilities...).
I have to create the graphics on run time as I know only on run time which images I need to place.
This is the function:
private void InitializeDrawerCells(int cabinetPartID, int drawerID)
{
PictureBox pb;
try
{
tblDrawerCells.RowStyles.Clear();
tblDrawerCells.ColumnStyles.Clear();
tblDrawerCells.Controls.Clear();
tblDrawerCells.RowCount = 12;
tblDrawerCells.ColumnCount = 16;
for (int i = 0; i < tblDrawerCells.RowCount; i++)
{
RowStyle style = new RowStyle
{
SizeType = SizeType.Absolute,
Height = tblDrawerCells.Height / tblDrawerCells.RowCount
};
tblDrawerCells.RowStyles.Add(style);
}
for (int i = 0; i < tblDrawerCells.ColumnCount; i++)
{
ColumnStyle style = new ColumnStyle
{
SizeType = SizeType.Absolute,
Width = tblDrawerCells.Width / tblDrawerCells.ColumnCount
};
tblDrawerCells.ColumnStyles.Add(style);
}
for (int i = 0; i < tblDrawerCells.ColumnCount; i++)
{
for (int j = 0; j < tblDrawerCells.RowCount; j++)
{
pb = new PictureBox();
if (_DrawerType == eDrawerType.Regular)
{
if (Scenraio == OpenCabinetScenario.Maintenance)
{
if (NarcoticsCabinetManager.CabinetParts[_CurrentCabPartID].Drawers[_DrawerNumber - 1].DrawerType == eDrawerType.Regular)
pb.BackgroundImage = ImageOn[tblDrawerCells.RowCount - j - 1][i];
else
pb.BackgroundImage = ImageReturnOff;
}
else
pb.BackgroundImage = ImageOff;
}
else
pb.BackgroundImage = global::NarcoticsStorageSystem2.Properties.Resources._11x6_Cells;
pb.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
// pb.Dock = System.Windows.Forms.DockStyle.Fill;
pb.Location = new System.Drawing.Point(0, 0);
pb.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
if (_DrawerType == eDrawerType.Regular)
pb.Size = new System.Drawing.Size(66, 66);
else if (_DrawerType == eDrawerType.Return)
pb.Size = new System.Drawing.Size(198, 66);
// if (_Scenraio == OpenCabinetScenario.FillItems)
// {
pb.Click += new System.EventHandler(this.pbCell_Click);
// }
pb.TabStop = false;
pb.Tag = new Tuple<int, int, int, int>(_DrawerNumber, tblDrawerCells.RowCount - j, i + 1, (int)ImageType.Off);
tblDrawerCells.Controls.Add(pb, i, j);
}
}
}
catch (Exception ex)
{
//add ex to log...
}
finally
{
}
}
Any ideas?

Printing a local file on Android via Xamarin.Forms

I need to use the print dialog via Forms. I have found an solution on iOS but the android implementation is giving me problems.
As far as i can see it is not possible to call the Android print manager and parse a file to it.
It can only be a Android.Views.View, is that true?
To do that would be my ideal solution.
I have tried to convert my content (A webview showing a local pdf) to this android view but this seems also not really to work but i am out off my depths here.
in the code below i try to convert a forms.webview to an android.view and then parse it to the print manager.
This produce the print dialog but with a black white page.
var size = new Rectangle(webview.X, webview.Y, webview.Width, webview.Height);
var vRenderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(webview);
var viewGroup = vRenderer.ViewGroup;
vRenderer.Tracker.UpdateLayout();
var layoutParams = new Android.Views.ViewGroup.LayoutParams((int)size.Width, (int)size.Height);
viewGroup.LayoutParameters = layoutParams;
webview.Layout(size);
viewGroup.Layout(0, 0, (int)webview.WidthRequest, (int)webview.HeightRequest);
var printMgr = (Android.Print.PrintManager)Forms.Context.GetSystemService(Android.Content.Context.PrintService);
var docAdt = new Droid.GenericPrintAdapter(Forms.Context, viewGroup);
printMgr.Print("test", docAdt, null);
The next is the "GenericPrintAdapter"
public class GenericPrintAdapter : PrintDocumentAdapter
{
View view;
Context context;
PrintedPdfDocument document;
float scale;
public GenericPrintAdapter(Context context, View view)
{
this.view = view;
this.context = context;
}
public override void OnLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras)
{
document = new PrintedPdfDocument(context, newAttributes);
CalculateScale(newAttributes);
var printInfo = new PrintDocumentInfo
.Builder("MyPrint.pdf")
.SetContentType(PrintContentType.Document)
.SetPageCount(1)
.Build();
callback.OnLayoutFinished(printInfo, true);
}
void CalculateScale(PrintAttributes newAttributes)
{
int dpi = Math.Max(newAttributes.GetResolution().HorizontalDpi, newAttributes.GetResolution().VerticalDpi);
int leftMargin = (int)(dpi * (float)newAttributes.MinMargins.LeftMils / 1000);
int rightMargin = (int)(dpi * (float)newAttributes.MinMargins.RightMils / 1000);
int topMargin = (int)(dpi * (float)newAttributes.MinMargins.TopMils / 1000);
int bottomMargin = (int)(dpi * (float)newAttributes.MinMargins.BottomMils / 1000);
int w = (int)(dpi * (float)newAttributes.GetMediaSize().WidthMils / 1000) - leftMargin - rightMargin;
int h = (int)(dpi * (float)newAttributes.GetMediaSize().HeightMils / 1000) - topMargin - bottomMargin;
scale = Math.Min((float)document.PageContentRect.Width() / w, (float)document.PageContentRect.Height() / h);
}
public override void OnWrite(PageRange[] pages, ParcelFileDescriptor destination,
CancellationSignal cancellationSignal, WriteResultCallback callback)
{
PrintedPdfDocument.Page page = document.StartPage(0);
page.Canvas.Scale(scale, scale);
view.Draw(page.Canvas);
document.FinishPage(page);
WritePrintedPdfDoc(destination);
document.Close();
document.Dispose();
callback.OnWriteFinished(pages);
}
void WritePrintedPdfDoc(ParcelFileDescriptor destination)
{
var javaStream = new Java.IO.FileOutputStream(destination.FileDescriptor);
var osi = new OutputStreamInvoker(javaStream);
using (var mem = new MemoryStream())
{
document.WriteTo(mem);
var bytes = mem.ToArray();
osi.Write(bytes, 0, bytes.Length);
}
}
}
I have now a "working" solution.
This gets the current Android.Webkir.WebView and create the printAdapter from that.
Thanks to #SushiHangover for pointing me in the right direction.
Its a bit of a hack but works for my needs.
If anyone else ever needs this i have included both the Android and iOS code.
#if __ANDROID__
var vRenderer = Xamarin.Forms.Platform.Android.Platform.GetRenderer(webview);
var viewGroup = vRenderer.ViewGroup;
for (int i = 0; i < viewGroup.ChildCount; i++)
{
if (viewGroup.GetChildAt(i).GetType().Name == "WebView")
{
var AndroidWebView = viewGroup.GetChildAt(i) as Android.Webkit.WebView;
var tmp = AndroidWebView.CreatePrintDocumentAdapter("print");
var printMgr = (Android.Print.PrintManager)Forms.Context.GetSystemService(Android.Content.Context.PrintService);
printMgr.Print("print", tmp, null);
break;
}
}
#elif __IOS__
var printInfo = UIKit.UIPrintInfo.PrintInfo;
printInfo.Duplex = UIKit.UIPrintInfoDuplex.LongEdge;
printInfo.OutputType = UIKit.UIPrintInfoOutputType.General;
printInfo.JobName = "AppPrint";
var printer = UIKit.UIPrintInteractionController.SharedPrintController;
printer.PrintInfo = printInfo;
printer.PrintingItem = Foundation.NSData.FromFile(pdfPath);
printer.ShowsPageRange = true;
printer.Present(true, (handler, completed, err) =>
{
if (!completed && err != null)
{
System.Diagnostics.Debug.WriteLine("Printer Error");
}
});
#endif

How to save WebViewBrush as image? (UWP / Universal)

Summary: I have a collection FrameworkElements (basically web view brushes drawn on rectanlges), and I'd like to save each of these as a PNG file in my UWP app.
More details: I followed the example at https://stackoverflow.com/a/17222629/2884981 to split the content of a WebView into separate "pages".
I've put the main bits of code at the bottom.
At the bottom of GetWebPages() I have return pages;
At this point I have a list of all the "pages".
What I'd like to do, is then convert each of those pages into an image (so by the end of it I'd have a collection of PNG files, for instance).
Does anyone know how I can do this? Thanks in advance.
public async Task<WebViewBrush> GetWebViewBrush(WebView webView)
{
// resize width to content
double originalWidth = webView.Width;
var widthString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
int contentWidth;
if (!int.TryParse(widthString, out contentWidth))
{
throw new Exception(string.Format("failure/width:{0}", widthString));
}
webView.Width = contentWidth;
// resize height to content
double originalHeight = webView.Height;
var heightString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
int contentHeight;
if (!int.TryParse(heightString, out contentHeight))
{
throw new Exception(string.Format("failure/height:{0}", heightString));
}
webView.Height = contentHeight;
// create brush
var originalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
WebViewBrush brush = new WebViewBrush
{
SourceName = webView.Name,
Stretch = Stretch.Uniform
};
brush.Redraw();
// reset, return
webView.Width = originalWidth;
webView.Height = originalHeight;
webView.Visibility = originalVisibilty;
return brush;
}
And:
public async Task<IEnumerable<FrameworkElement>> GetWebPages(WebView webView, Windows.Foundation.Size page)
{
// ask the content its width
var widthString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
int contentWidth;
if (!int.TryParse(widthString, out contentWidth))
{
throw new Exception(string.Format("failure/width:{0}", widthString));
}
webView.Width = contentWidth;
// ask the content its height
var heightString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
int contentHeight;
if (!int.TryParse(heightString, out contentHeight))
{
throw new Exception(string.Format("failure/height:{0}", heightString));
}
webView.Height = contentHeight;
// how many pages will there be?
double scale = page.Width / contentWidth;
double scaledHeight = (contentHeight * scale);
double pageCount = (double) scaledHeight / page.Height;
pageCount = pageCount + ((pageCount > (int) pageCount) ? 1 : 0);
// create the pages
var pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
for (int i = 0; i < (int)pageCount; i++)
{
var translateY = -page.Height * i;
var rectanglePage = new Windows.UI.Xaml.Shapes.Rectangle
{
Height = page.Height,
Width = page.Width,
Margin = new Thickness(5),
Tag = new TranslateTransform { Y = translateY },
};
rectanglePage.Loaded += (async (s, e) =>
{
var subRectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
var subBrush = await GetWebViewBrush(webView);
subBrush.Stretch = Stretch.UniformToFill;
subBrush.AlignmentY = AlignmentY.Top;
subBrush.Transform = subRectangle.Tag as TranslateTransform;
subRectangle.Fill = subBrush;
});
pages.Add(rectanglePage);
}
return pages;
}
I'd like to save each of these as a PNG file in my UWP app.
You can get all the rectangles and show them in the ItemsControl in the NavigationCompleted event of WebView like this:
private async void webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
MyWebViewRectangle.Fill = await GetWebViewBrush(webView);
MyPrintPages.ItemsSource = await GetWebPages(webView, new Windows.Foundation.Size(842, 595));
}
Then in a button click event you can save all the items as .png images like this:
private async void Savetoimage_Clicked(object sender, RoutedEventArgs e)
{
var piclib = Windows.Storage.KnownFolders.PicturesLibrary;
foreach (var item in MyPrintPages.Items)
{
var rect = item as Rectangle;
RenderTargetBitmap renderbmp = new RenderTargetBitmap();
await renderbmp.RenderAsync(rect);
var pixels = await renderbmp.GetPixelsAsync();
var file = await piclib.CreateFileAsync("webview.png", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
byte[] bytes = pixels.ToArray();
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)rect.Width, (uint)rect.Height,
0, 0, bytes);
await encoder.FlushAsync();
}
}
}

Capture screenshot of current web site in Universal Apps

Here is how I can capture screen shot of current webpage, but sometimes it is capturing only visible area of webview (what user actually see).
But I'm not sure what was wrong.
I think var heightString = await WebView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" }) was finish too late - but program should wait in if (!int.TryParse(heightString, out height)) to complete... Am I wrong?
private async Task CaptureWebView()
{
int width;
int height;
var originalWidth = WebView.Width;
var originalHeight = WebView.Height;
// ask the content its width
var widthString = await WebView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
// ask the content its height
var heightString = await WebView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
if (!int.TryParse(widthString, out width))
{
throw new Exception("Unable to get page width");
}
if (!int.TryParse(heightString, out height))
{
throw new Exception("Unable to get page height");
}
// resize the webview to the content
WebView.Width = width;
WebView.Height = height;
await DoCapture("captured.png");
WebView.Width = originalWidth;
WebView.Height = originalHeight;
Painter.Width = width;
Painter.Height = height;
var i = await LoadCaptured("captured.png");
Painter.Fill = i;
}
Test repository on Github
At the end problem itself was in WebView.CapturePreviewToStreamAsync() method. I used it to capture full web screen shot, but it didn't work very well. But then I found WebViewBrush and realized that is the right way.
So here is final code snippet or GitHub
private async Task CaptureWebView()
{
int width;
int height;
var originalWidth = WebView.ActualWidth;
var originalHeight = WebView.ActualHeight;
var widthString = await WebView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
var heightString = await WebView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
if (!int.TryParse(widthString, out width))
{
throw new Exception("Unable to get page width");
}
if (!int.TryParse(heightString, out height))
{
throw new Exception("Unable to get page height");
}
// resize the webview to the content
WebView.Width = width;
WebView.Height = height;
var brush = new WebViewBrush();
brush.SetSource(WebView);
Painter.Width = width;
Painter.Height = height;
Painter.Fill = brush;
}

Categories