简体   繁体   中英

Update oxyplot model from another thread

It's my first time I using WPF and I have a problem. I can't update my oxyplot model from another thread. I can draw point in not another thread, but when I'm trying to do so in another nothing happens. Now I have this code:

 private void Button_Click(object sender, RoutedEventArgs e)
    {
        doComputingThread compute = new doComputingThread();
        Thread _MainThread = new Thread(new ThreadStart(compute.MainThread));
        _MainThread.Start();
    }

class doComputingThread{
   public doComputingThread()
    {
        DataPlot = new PlotModel();
        DataPlot.Series.Add(new LineSeries());
    }
    public void MainThread()
    {
        bool flag;

        _timer = new System.Timers.Timer();
        _timer.Interval = 10;
        _timer.Elapsed += (sender, e) => { GuiRefresher(true); };
        _timer.Enabled = true;

        Thread _ComputeThread = new Thread(new ThreadStart(ProducerThread));
        _ComputeThread.Start();
    }
    public void ProducerThread()
    {
        //populate queue

        int X = 0;
        int Y = 0;

        for (double t = 0; t < 2 * 3.14; t = t + 0.1)
        {
            X = (int)(Math.Cos(t) * 5000);
            Y = (int)(Math.Sin(t) * 5000);


            Coordinate.X = X;
            Coordinate.Y = Y;
            _queue.Enqueue(Coordinate);
        }
    public void GuiRefresher(object flag)
    {

        if (_queue.TryDequeue(out Coordinate))
        {
            //this part didn't refresh my oxyplot
            Dispatcher.CurrentDispatcher.Invoke(() =>
           {
               (DataPlot.Series[0] as LineSeries).Points.Add(new DataPoint(Coordinate.X, Coordinate.Y));
               DataPlot.InvalidatePlot(true);
           });

}

All working as expected except the part when Dispatcher.CurrentDispatcher . I didn't understand why my plot didn't refresh.

I have some idea that I don't understand what thread is UI thread in this case with WPF and maybe I should initiate my threads in doComputingThread constructor.

Xaml:

<ui:WslMainWindow x:Class="fpga_control.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:oxy="http://oxyplot.org/wpf"
    xmlns:local="clr-namespace:fpga_control"
    xmlns:ui="clr-namespace:Keysight.Ccl.Wsl.UI;assembly=Keysight.Ccl.Wsl"
    xmlns:DynamicVectorImages="clr-namespace:Keysight.Ccl.Wsl.UI.Controls.DynamicVectorImages;assembly=Keysight.Ccl.Wsl" 
    Title="Example 1 (WPF)" Height="461.311" Width="621.393">
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid >
    <oxy:PlotView Model="{Binding DataPlot}" Margin="10,10,152,0" Height="418" VerticalAlignment="Top"/>
    <Button Content="Button" HorizontalAlignment="Left" Margin="464,10,0,0" VerticalAlignment="Top" Width="137" Height="38" RenderTransformOrigin="0.303,1.929" Click="Button_Click"/>
    <TextBox x:Name="txb" HorizontalAlignment="Left" Height="23" Margin="468,53,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="133"/>
</Grid>

Your code appears to be adding the same co-ordinate to the queue in the loop. You're not creating a new Coordinate instance each time during the loop.

Also your _queue.TryDequeue(out Coordinate) line produces a syntax error for me as it's trying to use Coordinate as a variable rather than a class.

Without the definition for Coordinate I can't confirm any of this.

Also, I cannot see where you've actually added your DataPlot to your form or any control on your form. It just doesn't appear that you're displaying anything in any case.

The bottom-line is that your example code isn't a minimal, complete and verifiable example. So I can only guess at what the issues are.

I am going to give you an alternative that I think will give you what you want.

To start with I'm providing a simple definition for Coordinate :

public class Coordinate
{
    public int X;
    public int Y;
}

Now I'm going to use Microsoft's Reactive Framework to do all of the threading, computation, and dispatching of your code - here's how I would write the Button_Click handler:

private void Button_Click(object sender, RoutedEventArgs e)
{
    DataPlot = new PlotModel();
    DataPlot.Series.Add(new LineSeries());

    int steps = 60;
    IObservable<Coordinate> query =
        Observable
            .Generate(
                0,
                n => n < steps,
                n => n + 1,
                n => n * 2.0 * Math.PI / steps,
                n => TimeSpan.FromMilliseconds(10.0))
            .Select(t => new Coordinate()
            {
                X = (int)(Math.Cos(t) * 5000),
                Y = (int)(Math.Sin(t) * 5000),
            });

    IDisposable subscription =
        query
            .ObserveOnDispatcher()
            .Subscribe(c =>
            {
                (DataPlot.Series[0] as LineSeries).Points.Add(new DataPoint(c.X, c.Y));
                DataPlot.InvalidatePlot(true);
            });
}

With this code you no longer need your doComputingThread class at all.

The Observable.Generate code produces the t values for the 60 data-points that your original code produced, but it does so only one value at a time every 10.0 milliseconds. This code is effectively a timer and the producer of the t steps.

The .Select maps the t value to a Coordinate value.

The subscription is the execution of the query . The first step is to marshall the values back to the UI thread with the .ObserveOnDispatcher() call and then the .Subscribe takes each value and runs the c => ... delegate on the UI thread.

This should update the plot nicely.

If you want to stop the plotting early just call subscription.Dispose() and it will stop.

You need to NuGet "System.Reactive" and "System.Reactive.Windows.Threading" to get the bits. You will also need the following using statements to get the code to compile:

using System.Reactive;
using System.Reactive.Linq;

Just don't forget - you still need to add the DataPlot to your form somehow.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM