I'm new to RX but feel it should be able to provide a good solution to a task I wish to solve. After quite a bit of searching I still haven't found a solution.
I have a WPF application within which a control does some work in response to some mouse move events. I would like to reduce the frequency of the events, so that the handler gets called less frequently than is currently the case (as the user moves the mouse across the control). Ideally, what I want is to set up and subscribe to an observer. The observer should observe the mouse move events and call the client code with the most recent event and arguments after a particular time window has elapsed, say 0.2s. Being new to RX, I first replaced my original standard event hook-up with an observer as follows:
var mouseMove = Observable.FromEventPattern<MouseEventArgs>(myControl, "MouseMove");
mouseMove.Subscribe(args => myControl_MouseMove(args.Sender, args.EventArgs));
This seemed to work fine.
I then attempted to modify the observer to get the behaviour I described above. I tried using the Throttle() call and the Sample() call. These didn't produce the outcome I expected (or desire). In fact in certain cases using a particular overload of the Throttle/TimeSpan call killed my application dead, which still I don't understand.
This is an example of what I've tried:
var mouseMove = Observable.FromEventPattern<MouseEventArgs>(myControl, "MouseMove").Throttle(TimeSpan.FromSeconds(0.2));
mouseMove.Subscribe(args => myControl_MouseMove(args.Sender, args.EventArgs));
From reading, Throttle appears to swallow events until the frequency drops below a particular threshold (not quite what I expected), whereas I believe Sample samples the observed events at a regular interval? I would like the client code to be given the most recent event in a given time interval. If no events have been recorded in that interval, then the client should not be called.
Hope someone can help an RX newbie on this.
Oh, I also want to be kept informed of the (reduced frequency) mouse moves for the duration of the control's lifetime.
Max
I think you're on the right track with Sample - what, specifically, did this not do for you?
Example LINQPad snippet:
void Main()
{
var window = new Window();
window.Content = ctrl;
window.Show();
var mouseMove = Observable
.FromEventPattern<MouseEventArgs>(window, "MouseMove")
.Sample(TimeSpan.FromSeconds(0.2));
var disp = mouseMove.Subscribe(args => myControl_MouseMove(args.Sender, args.EventArgs));
window.Closed += (o,e) => disp.Dispose();
}
ItemsControl ctrl = new ItemsControl();
// Define other methods and classes here
private void myControl_MouseMove(object sender, MouseEventArgs args)
{
ctrl.Dispatcher.BeginInvoke((Action)(() => {ctrl.Items.Add(args.GetPosition(ctrl));}));
}
Related
I'm writing a program that has an X11/Xlib interface, and my event processing loop looks like this:
while (XNextEvent(display, &ev) >= 0) {
switch (ev.type) {
// Process events
}
}
The problem is when the window is resized, I get a bunch of Expose events telling me which parts of the window to redraw. If I redraw them in direct response to the events, the redraw operation lags terribly because it is so slow (after resizing I get to see all the newly invalidated rectangles refresh one by one.)
What I would like to do is to record the updated window size as it changes, and only run one redraw operation on the entire window (or at least only two rectangles) when there are no more events left to process.
Unfortunately I can't see a way to do this. I tried this:
do {
XPeekEvent(display, &ev);
while (XCheckMaskEvent(display, ExposureMask | StructureNotifyMask, &ev)) {
switch (ev.type) {
// Process events, record but don't process redraw events
}
}
// No more events, do combined redraw here
}
Which does actually work, but it's a little inefficient, and if an event arrives that I am not interested in the XCheckMaskEvent call doesn't remove it from the queue, so it stays there stopping XPeekEvent from blocking, resulting in 100% CPU use.
I was just wondering whether there is a standard way to achieve the delayed/combined redraw that I am after? Many of the Xlib event processing functions seem to block, so they're not really suitable to use if you want to do some processing just before they block, but only if they would block!
EDIT: For the record, this is the solution I used. It's a simplified version of n.m.'s:
while (XNextEvent(display, &ev) >= 0) {
switch (ev.type) {
// Process events, remember any redraws needed later
}
if (!XPending(display)) {
// No more events, redraw if needed
}
}
FWIW a UI toolkit such as GTK+ does it this way:
for each window, maintains a "damage region" (union of all expose events)
when the damage region becomes non-empty, adds an "idle handler" which is a function the event loop will run when it doesn't have anything else to do
the idle handler will run when the event queue is empty AND the X socket has nothing to read (according to poll() on ConnectionNumber(dpy))
the idle handler of course repaints the damage region
In GTK+, they're changing this over to a more modern 3D-engine oriented way (clean up the damage region on vertical sync) in a future version, but it's worked in the fairly simple way above for many years.
When translated to raw Xlib, this looks about like n.m.'s answer: repaint when you have a damage region and !XPending(). So feel free to accept that answer I just figured I'd add a little extra info.
If you wanted things like timers and idles, you could consider something lke libev http://software.schmorp.de/pkg/libev.html it's designed to just drop a couple of source files in your app (it isn't set up to be an external dependency). You would add the display's file descriptor to the event loop.
For tracking damage regions, people often cut-and-paste the file "miregion.c" which is from the "machine independent" code in the X server. Just google for miregion.c or download the X server sources and look for it. A "region" here is simply a list of rectangles which supports operations such as union and intersect. To add damage, union it with the old region, to repair damage, subtract it, etc.
Try something like the following (not actually tested):
while (TRUE) {
if (XPending(display) || !pendingRedraws) {
// if an event is pending, fetch it and process it
// otherwise, we have neither events nor pending redraws, so we can
// safely block on the event queue
XNextEvent (display, &ev);
if (isExposeEvent(&ev)) {
pendingRedraws = TRUE;
}
else {
processEvent(&ev);
}
}
else {
// we must have a pending redraw
redraw();
pendingRedraws = FALSE;
}
}
It could be beneficial to wait for 10 ms or so before doing the redraw. Unfortunately the raw Xlib has no interface for timers. You need a higher-level toolkit for that (all toolkits including Xt have some kind of timer interface), or work directly with the underlying socket of the X11 connection.
Hi I'm currently making a subliminal messaging program in windows forms and I have a form that appears then quickly disappears.
I now have pretty much everything working except for the timings for my timers that will show and then hide the form.
I'm currently using 2 timers
The timer for showering is at 1000 intervals
The timer for hiding is at 750 intervals.
Now I know this isn't a good choice as the timings will keep changing between how long it takes for a form to be hidden once it has been made to appear.
I can't figure out an algorithm that will make the intervals work together in a way so the revealing of a form then hiding doesn't change but is still done quickly.
I'm needing the form to be hidden very quickly once it appears but to not be shown again for around 3-5 seconds.
Thanks
What you want to do is to chain your timers, starting and stopping them so they retain their interval.
You say you want the form to show after 3-5 seconds and then hide after 750ms (the timer intervals are documented as milliseconds).
If we call the timers Timer_Show and Timer_Hide and the form instance as SubForm then we can have:
public partial class SubForm : Form
{
public SubForm()
{
InitializeComponent();
// This timer will show the screen
Timer Timer_Show = new Timer();
Timer_Show.Tick += Timer_Show_Tick;
Timer_Show.Interval = 3000;
Timer_Show.Enabled = true;
// This timer will hide the screen
Timer Timer_Hide = new Timer();
Timer_Hide.Tick += Timer_Hide_Tick;
Timer_Hide.Interval = 750;
Timer_Hide.Enabled = false;
}
public void Timer_Show_Tick(object sender, EventArgs e)
{
Timer_Show.Stop();
this.Show();
Timer_Hide.Start();
}
public void Timer_Hide_Tick(object sender, EventArgs e)
{
Timer_Hide.Stop();
this.Hide();
Timer_Show.Start();
}
}
I've moved the Timer instantiations to the constructor so you can see what's going on.
First we set the Timer_Show interval to 3000ms (3 seconds). Then we set the Timer_Hide interval to 750ms. We disable Timer_Hide but enable Timer_Show.
After 3 seconds, Timer_Show_Tick will fire. This will then disable Timer_Show, show the current instance of SubForm and then start Timer_Hide.
Then after 750ms Timer_Hide_Tick fires, stops Timer_Hide, hides the current instance of SubForm then starts Timer_Start to restart the whole process.
The screen will always show 3 seconds after it was last shown and will remain visible for 750ms. This will keep your show/hide timers in sync.
If you want to change the 3 seconds to something else then you can instantiate a Random. E.G.
Random rand = new Random();
Have Random as a class level variable and then in Timer_Hide_Tick just before you call Timer_Show.Start() you can call Timer_Show.Interval = rand.NewRand(3000, 5000); which will select a random number between 3000 and 5000 for the interval value so showing the screen won't always be 3 seconds after it was last shown.
Your screen show/hide will still retain their syncronization so you'll never get into the situation where hide fires while the screen is already hidden.
I have trouble getting Map behave properly when calling ZoomToResolution and PanTo
I need to be able to Zoom into specific coordinate and center map.
The only way I got it working is by removing animations:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
Otherwise if I make call like this:
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
It does one or another (i.e. pan or zoom, but not both). If (after animation) I call this code second time (map already zoomed or panned to needed position/level) - it does second part.
Tried this:
control.MapControl.ZoomToResolution(ZoomLevel, MapPoint());
Same issue, internally it calls above commands
So, my only workaround right now is to set Zoom/Pan duration to 0. And it makes for bad UX when using mouse.
I also tried something like this:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
this.MapControl.ZoomDuration = new TimeSpan(750);
this.MapControl.PanDuration = new TimeSpan(750);
Which seems to be working, but then mouse interaction becomes "crazy". Mouse scroll will make map jump and zoom to random places.
Is there known solution?
The problem is the second operation replaces the previous one. You would have to wait for one to complete before starting the next one. But that probably doesn't give the effect you want.
Instead zoom to an extent, and you'll get the desired behavior. If you don't have the extent but only center and resolution, you can create one using the following:
var zoomToExtent = new Envelope(point.X - resolution * MapControl.ActualWidth/2, point.Y, point.X + resolution * MapControl.ActualWidth/2, point.Y);
Btw it's a little confusing in your code that you call your resolution "ZoomLevel". I assume this is a map resolution, and not a level number right? The esri map control doesn't deal with service-specific levels, but is agnostic to the data's levels and uses a more generic "units per pixels" resolution value.
I have some C++ code that performs calculations and I would like to visualize it.
I'm using windows forms (.NET).
The idea is to perform calculations in C++ and to include .h with chart.
As I need fast update, I use timer. As my data is in C++ I should use some tricks to draw it from .h. I was advised to use BeginInvoke() method, here's my proto code from header:
System::Void ActionD ()
{
for (pts = 0; pts < arrlength; pts++) {
chart1->series1->Points->AddXY(test_array_x[pts], test_array_y[pts]);
}
}
private:
System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
MethodInvoker^ mi = gcnew MethodInvoker(this,&ActionD);
chart1->Invoke(mi);
//check if timer works:
Beep(300,500);
}
I have some errors: "...MethodInvoker: a delegate constructor expects 1 argument"
Question is if the general concept of code correct and how can I fix that error?
The C++/CLI compiler in older versions of VS don't produce a very good diagnostic for bad delegate constructor calls. The issue is with &ActionD, it needs to be a fully qualified method name, like this:
MethodInvoker^ mi = gcnew MethodInvoker(this, &Form1::ActionD);
Replace "Form1" with the name of your form class if necessary.
And no, the general concept is not correct. You are using a regular Winforms timer, there's no need at all to use BeginInvoke since the code already runs on the main thread. Nor would you be ahead at all by using an asynchronous timer class, it doesn't make the code any faster.
You make your chart fast by filtering the data, only keeping the Points in the series that you actually need to get an accurate chart drawn. Which doesn't take a lot of points, a few hundred up to a thousand is more than enough. Monitors don't have a lot of pixels so using multiple thousands just keeps the Chart control busy for no benefit. Doing that filtering in a worker thread is the way to get ahead.
I've found a little bit similar topic:
How can I update data in a chart in execution time (in C++ builder)?
So I'm doing this inside my timer:
System::Windows::Forms::DataVisualization::Charting::Series^ seriezz1 = chart1->Series[0];
seriezz1->Points->AddXY(test_array_x[pts], test_array_y[pts]);
It compiles, but crashes at start :(
My label text isn't updating properly in my 3.5 WPF MVVM app.
The do work part lasts long enough that you can see the waiting mouse pointer.
All I ever see is "Parsed" in the label, which is Bound to InfoText.
the Dispatcher and do work lines are in a Command's method.
Ideas?
The code
Dispatcher.Invoke((Action<string>)SetInfoText, "Start Parsing");
//do work
Dispatcher.Invoke((Action<string>)SetInfoText, "Parsed");
private void SetInfoText(string text)
{
InfoText = text;
}
private string _infoText;
public string InfoText
{
get
{
return _infoText;
}
set
{
_infoText = value;
OnPropertyChanged("InfoText");
}
}
The only thing I can think of to explain it is that you're doing the work on the UI thread. This would prevent the dispatcher from redrawing until your work is done. The work being passed in Invoke is placed in the event queue, meaning it will be performed when idle.
The proper way to fix it is to do the work on a separate thread. If you're looking for workarounds though, look here.
Reference: MSDN
EDIT:
There are lots of ways to perform the work on another thread. Read up on BackgroundWorker, ThreadPool, Task Parallell Library, Threads. :)
Here's a really simple way to do the work in a background thread:
System.Threading.ThreadPool.QueueUserWorkItem( state =>
{
Dispatcher.Invoke((Action<string>)SetInfoText, "Start Parsing");
System.Threading.Thread.Sleep(5000); // Simulate work
Dispatcher.Invoke((Action<string>)SetInfoText, "Parsed");
});
Application.Current.Dispatcher.BeginInvoke(new Action(() => this.InfoText="Start Parsing"));
this works for me.
nevertheless i would put my long running process in a backgroundworker. so ui thread will not get blocked.
edit: if you do all your work in ui thread you should look at the overload for BeginInvoke - you can pass a DispatcherPriority. may be this helps too