So, I have a resizable window which draws a graph given on live values it calculates. If I make the window bigger, it redraws and scales everything, also when the graph hits a new maxY and maxX, using those as reference points to determine the position of the other points. This works fine, but if I resize the window, and a lot of things are being scheduled for updating, it the window resizes fine, but the Graph mimics what it was supposed to do a couple (up to 4) seconds ago.
If I understand this correctly, every time I invoke my UI thread, which is blocked, and give him new coordinates to draw, it finishes the current one and then goes on to the next one. I see why you would want that, but since the graph is more or less constantly scaling, it is deleting itself every update anyway, so I would save quite some processing power and runtime, if I would just finish every current task and jump over all the accumulated task right to the newest one, since the stored ones are outdated anyway
Is there a way to do this?
I thought of 3 things which might work, of which the third one the only one is, I know will work or, rather, is possible, even if rather slow:
// (1) If there is a way to check if the UI thread is busy, I could just not send
// a new request to draw another line
if(!uithread.busy)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => this.DrawGraph(new List<List<Coordinates>> { CoordinateListA, CoordinateListB })));
}
// (2) As soon as UI thread finishes the current task, it jumps to the newest one
// (3) In my opinion the ugliest solution, I check how long the last drawing took and
// don't send any draw-requests in that time. I will just wait that certain time and
// just then send him the newest results to draw and measure how long that took
If there is no better solution, I think I will go with (3), but since I hope that there is another way, I wanted to ask if maybe someone else here had a similar issue.
So I fixed it. My Architecture saves all the logs, so I do not need to save coordinates, if I need new values I can just, every time I need new Coordinates, calculate them from the Logs. Like this:
// beginning of the class
private bool _isDrawing = false;
if (!_graphIsDrawing)
{
_graphIsDrawing = true;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => this.DrawGraph(new List<List<Coordinates>> { CoordinateListA, CoordinateListB }, scalingFactors, canvasSize)));
//I need to invoke, since I am working with multiple threads here. Else it
//would be enough to just call 'this.DrawGraph(...)'
}
///////////
public void DrawGraph(List<List<Coordinates>> listOfGraphs, float[] scalingFactor, int[] canvasSize)
{
lock (_graphDrawLock)
{
this._AlgorithmRuntimeViewA.DrawGraph(listOfGraphs[0], scalingFactor, canvasSize);
this._AlgorithmRuntimeViewB.DrawGraph(listOfGraphs[1], scalingFactor, canvasSize);
_graphIsDrawing = false;
}
}
Here I lock it again, so not both threads draw at the same time breaking everything. At the end I set _graphIsDrawing to false again, so I can call it again.
Related
ScottPlot is used to display a real time chart with 10'000+ data points.
Datapoints are added every seconds, so the chart needs to be updated.
A timer perform the refresh every seconds, however, it freezes the whole UI for a few ms, which is annying.
ScottPlot does have a Chart.RenderRequest(); method which is non-blocking, but the chart is still rendered on the UI thread so that does not solve the issue.
And of course, if we try to refresh it from a background worker, a thread exception happens.
var bg = new BackgroundWorker();
bg.DoWork += (s, e) =>
{
Chart.RenderRequest();
};
bg.RunWorkerAsync();
Is there any ways to render the chart in a separate thread with WPF and ScottPlot ?
Edit:
Did timing analysis, Refresh, Render, RefreshRequest, RenderRequest all have similar execution time, despite the "request" are supposedly non-blocking.
Some time ago I faced a similar issue.
I had a lot of data to be displayed in multiple charts, I found that (in my canse) an acceptable limit to have a satisfying user experience is to limit the data rendered by scottplot to about 1 million points.
Here there are some tips that maybe can help you to solve your issue.
If you can, use SignalXY plot. It is designed to render a lot of data, so the rendering is much faster than using any other type of graph.
Unfortunately you must render the graph in the UI thread, but you can limit the use of the UI thread to the call wpfPlot.Refresh(). Create one pair of double array double[] Ys and Xs[] of the maximum size that your data can have. Create a plot of the desired type and keep the instance in your class as a class variable (same thing for the other plottables elements, like crosshair or legend). When you need to update data just change the value of Ys and Xs. Do not add and remove the plots from scottplot wpfPlot.
I write a small example below, just to give you an idea:
double[] times;
double[] values;
SignalXY plot;
private void Initialize()
{
wpfPlot.Plot.Clear(); //This removes all plottables
times = new double[MaxSamples];
values = new double[MaxSamples];
plot = wpfPlot.Plot.AddSignalXY(times,values);
}
private void UpdatePlot()
{
int counter=0;
foreach(var data in myData)
{
times[counter] = data.Time;
values[Counter = data.Value;
counter++;
}
//This tells to scottplot to render only until the index counter, so you
//can have a bigger Xs arrays that can be created only at the beginning
plot.MaxRenderIndex=counter;
//This call must be done in UI thread
wpfPlot.Refresh();
}
These are the two changes that, for me, improved the rendering performances by A LOT. Hope that can be useful for you too.
For the challenge and educational gain, i am currently trying to make a simple game in the console window. I use a very primitive "locked" framerate system as such:
using System.Threading;
// ...
static private void Main(string[] args)
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
Timer timer = new Timer(Update);
timer.Change(0, GameSpeed);
autoEvent.WaitOne();
}
So, a timer ticks every GameSpeed miliseconds, and calls the method Update().
The way that i have understood input in the console window so far is as follows:
The console application has a "queue" where it stores any keyboard input as metadata + an instance of a ConsoleKey enum. The user can add to this queue at any time. If the user holds down, say A, it will add A every computer frame. That is, the actual fastest amount the computer can do, not the locked frames i am working with.
Calling Console.Readkey() will remove and return the first element on this list. Console.KeyAvailable returns a bool indicating whether the list is empty.
If GameSpeedis set to anything higher than 400 everything consistently works fine. The below image displays the results of some Console.WriteLine() debug messages that give the amount of keyboard inputs detected in this locked/custom frame, using the following code:
int counter = 0;
while (Console.KeyAvailable) { counter++; Console.ReadKey(true); }
Console.WriteLine(counter);
Results
I use only the A key. I hold it for some time, then release it again. The GameSpeed is set to 1000. As expected, the first frames give low numbers as i might start pressing half into the frame, and so too with the last frames, as i might release the A early.
Now, the exact same experiment but with a GameSpeed of only 200
As you can see, i've marked the places i begun pressing with yellow. It always, perfectly gets the first frame. But then theres either one, two, or three frames where it acts as if it has gotten no inputs, but then after those frames it's fine and gets around 7 inputs pr frame.
I recognize that you are not supposed to make games in the console window. It is not made for scenarios like this. That does not however eliminate the possibility that there is some specific, logical reason this happens, that i might be able to fix. So, concretely the question is: can anyone provide some knowledge / ideas of why this happens?
If computer specs are needed, just say so in the comments and i'll add them.
Edit:
I think i have found the cause of this error, and it is windows keyboard repeat delay. While you can change this in the control panel, i have searched the web and found no examples of how you would change it in a c# application. The question then boils down to: how do you change windows keyboard repeat delay?
The requirement is like this:
I must issue a command requesting a screenshot. The twist? The command specifies a time - and that time is in the PAST. Not forever, mind you, just within a 30 second window.
My command indicates the actual second, and the corresponding screenshot is pulled from the rolling cache and saved in a permanent location for another process.
To accomplish this, I must take a screenshot every second, and I must preserve them for 30 seconds. After that the cache can purge itself.
My question is
What method for screenshots is least impactful against the desktop?
I can't imagine that one frame per second is going to be terrible for performance. Have you tried the simplest possible method to see if it impacts performance?
Take a look at http://www.dotnetjalps.com/2007/06/how-to-take-screenshot-in-c.html for information on how to get a screen shot.
I would suggest you use a timer:
var ssTimer = new System.Threading.Timer((s) =>
{
GetAndStoreScreenShot();
}, null, 1000, 1000);
You'll need some synchronization on your cache to prevent threading problems (i.e. trying to read the first item in the cache while it's being pushed out). Probably easiest to use a lock, since that's not going to be particularly performance sensitive.
As for what to use for a cache, I'd suggest a LinkedList. It's easy to append to the list (AddLast()) and remove the first item (RemoveFirst()), and since you'll only have 30 of them and requests for screen shots will be relatively infrequent, a sequential scan to get the nth item wouldn't be too time consuming.
Or you can implement a simple circular buffer using List or Array. Increment an index to be the insertion spot, wrapping around when you increment off the end. A lookup then becomes a simple matter of modulo arithmetic.
Along the lines of what Jim said, you could store your working set of screenshots in a ConcurrentDictionary<DateTime, Bitmap>, and have two timers: The first timer would add your screenshot to the dictionary, and the second would remove anything older than 30 seconds from the dictionary.
However, this is going to be terrible for performance pretty much any way you slice it.
Something along these lines:
var imageDictionary = new ConcurrentDictionary<DateTime, Bitmap>();
var screenshotTaker = new System.Timers.Timer(1000);
screenshotTaker.Elapsed += (sender, e) =>
{
Bitmap bmp = GetScreenshot();
imageDictionary.TryAdd(DateTime.Now, bmp);
};
var screenshotRemover = new System.Timers.Timer(1000);
screenshotRemover.Elapsed += (sender, e) =>
{
RemoveExpiredBitmaps();
};
screenshotTaker.Start();
screenshotRemover.Start();
I am connecting to a mysql database online from my local C# .NET Winform application. I want that till the application connects to the online database, a Progress Bar is displayed , and as soon as the database connection is established, the progress bar should also complete incrementing.
please help with code.
how do i get the time taken by the application to connect to the online database , and then set that time as INTERVAL for the progress bar?
Why don't you change ProgressBar Style to marque a line before Query Start's Execution ,than on end you can Change ProgressBar Style in Block's and Give Progressbar.Value = 100; .
If i read that correctly, you would like to have a progress bar continually fill over and over until a connection with the database is established (since it is unknown how long a connection could take prior to completion)
If this is the case you can use a timer/backgroundworker to do this pretty easy.
Sudo code:
Timer a= new Timer();
a.Tick += TickMethod;
a.interval = 2;
BackgroundWorker b = new BackgroundWorker();
b.DoWork += BackgroundMethod();
b.WorkComplete += WorkDone();
void Start()
{
a.Start();
b.RunAsync();
}
void TickMethod()
{
if(progressBar.Value == progressBar.Max)
progressBar.Value = 0;
progresssBar.Step();
}
void BackgroundMethod(object s, Args e)
{
MakeConnection();
}
void WorkDone()
{
a.Stop();
progressBar.Value = progressBar.Max();
}
I'm sorry if thats not what you are looking for.
The "proper" way to use a progress bar for an operation that will take an indetermine time is to set it into Marquee mode. This shows the bar with an animated "shine" effect to show that something is happening, but does not show an actual progress bar (as you don't know how long it will take, you can't estimate a percentage complete). Once you have connected, you can then set the progressbar's Value to its Max so that it moves to 100%.
An alternative approach was used in some versions of IE while fetching web pages. This showed a progress bar that slowly incremented towards 100% over a long period (a predetermined time frame long enough to cover the worst case scenario - e.g. in your case you might choose 30 seconds as that is often the timeout period used to fail a connection anyway). When the connection is finally made, you then advance the bar to 100%. THis gives the impression of progress being made while you're waiting, and as long as your estimate of the worst case scenarion is reasonable, this will give good results.
However, note that .net progress bars don't jump immediately to a new value - they animate so as to "grow" towards the requested value, so if it has to go from 0% to 100% it will take a second or two before it actually displays 100%. This can mean that (for example) your window closes while the progress bar is only showing a value like 20%, which is highly irritating as it makes the user think that it failed in some way. There are two solutions to this:
1) Make your application pause (e.g. Sleep the thread) for a "long enough period" that you are sure the progress bar will reach 100%. This adds an extra delay of several seconds to the end of your operation, and the time needed can be rather unpredictable, so it's not a great solution, especially if the database fetch is often quick.
2) Force the progress bar to "jump" to 100% rather than slowly growing towards 100%. You can do this by setting Value = Max; Value = Max-1; Value = Max. When the progress bar is asked to go "backwards" in this way (to Max-1) it doesn't animate, but immediately updates to show the requested value.
here's the problem: I have a custom hardware device and I have to grab images from it in C#/WPF and display them in a window, all with 120+ FPS.
The problem is that there is no event to indicate the images are ready, but I have to constantly poll the device and check whether there are any new images and then download them.
There are apparently a handful of ways to do it, but I haven't been able to find the right one yet.
Here's what I tried:
A simple timer (or DispatcherTimer) - works great for slower frame rates but I can't get it past let's say, 60 FPS.
An single threaded infinite loop - quite fast but I have to put the DoEvents/it's WPF equivalent in the loop in order for window to be redrawn; this has some other unwanted (strange) consequences such as key press events from some controls not being fired etc..
Doing polling/downloading in another thread and displaying in UI thread, something like this:
new Thread(() =>
{
while (StillCapturing)
{
if (Camera.CheckForAndDownloadImage(CameraInstance))
{
this.Dispatcher.Invoke((Action)this.DisplayImage);
}
}
}).Start();
Well, this works relatively well, but puts quite a load on a CPU and of course completely kills the machine if it doesn't have more than one CPU/core, which is unacceptable. Also, I there is a large number of thread contentions this way.
The question is obvious - are there any better alternatives, or is one of these the way to go in this case?
Update:
I somehow forgot to mention that (well, forgot to think about it while writing this question), but of course I don't need all frames to be displayed, however I still need to capture all of them so they can be saved to a hard drive.
Update2:
I found out that the DispatcherTimer method is slow not because it can't process everything fast enough, but because the DispatcherTimer waits for the next vertical sync before firing the tick event; which is actually good in my case, because in the tick event I can save all pending images to a memory buffer (used for saving images to disk) and display just the last one.
As for the old computers being completely "killed" by capturing, it appears that WPF falls back to software rendering which is very slow. There's probably nothing I can do about.
Thanks for all the answers.
I think you're trying for too simplistic of an approach. Here's what I would do.
a) put a Thread.Sleep(5) in your polling loop, that should allow you to get close to 120fps while still keeping CPU times low.
b) Only update the display with every 5th frame or so. That will cut down on the amount of processing as I'm not sure that WPF is made to handle much more than 60fps.
c) Use ThreadPool to spawn a subtask for each frame that will then go and save it to the disk (in a seperate file per frame), that way you won't be as limited by disk performance. Extra frames will just pile up in memory.
Personally I would implement them in that order. Chances are a or b will fix your problems.
You could do the following (all psuedocode):
1. Have a worker thread running dealing with the capture process:
List<Image> _captures = new List<Image>();
new Thread(() =>
{
while (StillCapturing)
{
if (Camera.CheckForAndDownloadImage(CameraInstance))
{
lock(_locker){_captures.Add(DisplayImage);
}
}
}).Start();
Have the dispatcher timer thread take latest captured image (obviously it will have missed some captures since last tick) and display. Therefore, UI thread is throttled and doing as little as possible, it isn't doing all the "capturing", this is done by worker threads. sorry I can't get this bit to format (but you get the idea):
void OnTimerTick(can't remember params)
{
Image imageToDisplay;
lock(_locker){imageToDisplay = _captures[k.Count - 1];
DisplayFunction(imageToDisplay);
}
it might be that the list is a queue and another thread is used to bleed the queue and write to disk or whatever.