随着数据被处理并添加到图表 WPF c#,“实时图表”图形刷新变慢

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40906201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:57:35  来源:igfitidea点击:

"Live Charts" graph refresh slowing as data is processed and added to the charts WPF c#

c#wpfvisual-studiolivecharts

提问by charley

Im using live charts to data log incoming serial data. In the long run I will have 6 different sets of variables being sent from an Arduino. Labelled from U-Z. My charts works at relatively good speed when it is simply reading and plotting the data however as soon as the 'if' statement gets involved it slows considerably. My concern is that once I incorporate another 6 pieces of data (although it is only 2 per graph so a total of 3 graphs) the programme will be running too slow to make the data logging worth while.

我使用实时图表来记录传入的串行数据。从长远来看,我将从 Arduino 发送 6 组不同的变量。从 UZ 标记。当我的图表只是读取和绘制数据时,它的工作速度相对较好,但是一旦涉及到“if”语句,它就会大大减慢。我担心的是,一旦我合并了另外 6 个数据(尽管每个图形只有 2 个,所以总共 3 个图形),程序运行速度将太慢而无法进行数据记录。

I have added a text box to read all incoming data and that is updating as expected. As well as this the graph also seems to slow down as more data is added to it, even though I am removing points once they are 'off screen' essentially.

我添加了一个文本框来读取所有传入的数据,并且正在按预期更新。除此之外,随着更多数据添加到图形中,图形似乎也变慢了,即使我在本质上是“屏幕外”时删除点。

Thanks!

谢谢!

public partial class MainWindow : Window, INotifyPropertyChanged
{

    private double _axisMax;
    private double _axisMin;
    Stopwatch stopwatch = new Stopwatch();
    SerialPort myPort;

    public MainWindow()
    {
        InitializeComponent();

        myPort = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);
        myPort.Open();
        //To handle live data easily, in this case we built a specialized type
        //the MeasureModel class, it only contains 2 properties
        //DateTime and Value
        //We need to configure LiveCharts to handle MeasureModel class
        //The next code configures MEasureModel  globally, this means
        //that livecharts learns to plot MeasureModel and will use this config every time
        //a ChartValues instance uses this type.
        //this code ideally should only run once, when application starts is reccomended.
        //you can configure series in many ways, learn more at http://lvcharts.net/App/examples/v1/wpf/Types%20and%20Configuration


        var mapper = Mappers.Xy<MeasureModel>()
            .X(model => model.DateTime.Ticks)   //use DateTime.Ticks as X
            .Y(model => model.Value);           //use the value property as Y

        var mapper2 = Mappers.Xy<Graph1SecondVal>()
            .X(model => model.DateTime.Ticks)   //use DateTime.Ticks as X
            .Y(model => model.Value);

        //lets save the mapper globally.
        Charting.For<MeasureModel>(mapper);
        Charting.For<Graph1SecondVal>(mapper2);


        //the values property will store our values array
        ChartValues = new ChartValues<MeasureModel>();
        ChartValuesTwo = new ChartValues<Graph1SecondVal>();

        //lets set how to display the X Labels
        DateTimeFormatter = value => new DateTime((long)value).ToString("hh:mm:ss");

        AxisStep = TimeSpan.FromSeconds(1).Ticks;
        SetAxisLimits(DateTime.Now);

        //The next code simulates data changes every 300 ms
        Timer = new DispatcherTimer
        {
            Interval = TimeSpan.FromMilliseconds(10)
        };
        Timer.Tick += TimerOnTick;
        IsDataInjectionRunning = false;
        R = new Random();
        DataContext = this;
    }
    public ChartValues<Graph1SecondVal> ChartValuesTwo { get; set; }
    public ChartValues<MeasureModel> ChartValues { get; set; }
    public Func<double, string> DateTimeFormatter { get; set; }

    public double AxisStep { get; set; }

    public double AxisMax
    {
        get { return _axisMax; }
        set
        {
            _axisMax = value;
            OnPropertyChanged("AxisMax");
        }
    }
    public double AxisMin
    {
        get { return _axisMin; }
        set
        {
            _axisMin = value;
            OnPropertyChanged("AxisMin");
        }
    }

    public DispatcherTimer Timer { get; set; }
    public bool IsDataInjectionRunning { get; set; }
    public Random R { get; set; }

    private void RunDataOnClick(object sender, RoutedEventArgs e)
    {
        if (IsDataInjectionRunning)
        {
            stopwatch.Stop();
            Timer.Stop();
            IsDataInjectionRunning = false;
        }
        else
        {
            stopwatch.Start();
            Timer.Start();


            IsDataInjectionRunning = true;
        }
    }

    private void TimerOnTick(object sender, EventArgs eventArgs) // Class is referencing from here!
    {
        var now = DateTime.Now;
        ProcessData();
        SetAxisLimits(DateTime.Now);
        //lets only use the last 30 values
        if (ChartValues.Count > 50) ChartValues.RemoveAt(0);
    }

    private void SetAxisLimits(DateTime now)
    {
        AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 100ms ahead
        AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; //we only care about the last 8 seconds
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        if (PropertyChanged != null) // if subrscribed to event
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    void ProcessData()
    {
        int NumberInt = 0;
        string IncomingSerial = myPort.ReadLine(); // Read incomming serial data
        string StrIncomingSerial = IncomingSerial.ToString(); // convert this data to workable string

        if (StrIncomingSerial.Contains("Z"))
        {
                string Number = myPort.ReadLine(); // Read Serialport
                double Num; // Create variable "Num"
                bool isNum = double.TryParse(Number, out Num); // Is the incomming serial data a number?
                if (isNum) // If it is a number...
                {
                    NumberInt = Convert.ToInt16(Number); // convert string to int
                    textBox1.Text = NumberInt.ToString() + "," + textBox1.Text;

            }

            myPort.DiscardInBuffer();
            ChartValues.Add(new MeasureModel
            {
                DateTime = DateTime.Now,
                Value = NumberInt

            });
        }

        if (StrIncomingSerial.Contains("Y"))
        {
                string Number = myPort.ReadLine(); // Read Serialport
                double Num; // Create variable "Num"
                bool isNum = double.TryParse(Number, out Num); // Is the incomming serial data a number?
                if (isNum) // If it is a number...
                {
                    NumberInt = Convert.ToInt16(Number); // convert string to int
                    textBox1.Text = NumberInt.ToString() + "," + textBox1.Text;
                }

            myPort.DiscardInBuffer();
            ChartValuesTwo.Add(new Graph1SecondVal
            {
                DateTime = DateTime.Now,
                Value = NumberInt
            });
        }

        SetAxisLimits(DateTime.Now);

    }

    private void ReadSerial_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < 10; i++)
        {
            string Number = myPort.ReadLine(); // Read Serialport
            textBox1.Text = Number.ToString() + "," + textBox1.Text;
        }

        myPort.DiscardInBuffer();

    }
}


<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:graph_test_6"
    xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
    xmlns:geared="clr-namespace:LiveCharts.Geared;assembly=LiveCharts.Geared"
    xmlns:chart="http://mindfusion.eu/charting/wpf" x:Class="graph_test_6.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Height="30" Click="RunDataOnClick">
        Inject/Stop Data
    </Button>
    <lvc:CartesianChart Grid.Row="1" AnimationsSpeed="0:0:0" Hoverable="False">
        <lvc:CartesianChart.Series>
            <lvc:LineSeries Values="{Binding ChartValues}" PointGeometrySize="5" StrokeThickness="1" />
            <lvc:LineSeries Values="{Binding ChartValues2}" PointGeometrySize="5" StrokeThickness="1" />
        </lvc:CartesianChart.Series>
        <lvc:CartesianChart.AxisX>
            <lvc:Axis LabelFormatter="{Binding DateTimeFormatter}" 
                      MaxValue="{Binding AxisMax}" 
                      MinValue="{Binding AxisMin}"
                      DisableAnimations="True">
                <lvc:Axis.Separator>
                    <lvc:Separator Step="{Binding AxisStep}"></lvc:Separator>
                </lvc:Axis.Separator>
            </lvc:Axis>
        </lvc:CartesianChart.AxisX>
    </lvc:CartesianChart>
    <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="323,-71,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
    <TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="189" Margin="43,299,0,-199" Grid.Row="1" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="203"/>
    <Button x:Name="ReadSerial" Content="Button" HorizontalAlignment="Left" Height="54" Margin="43,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="ReadSerial_Click"/>
</Grid>

    public class MeasureModel
{
    public DateTime DateTime { get; set; }
    public double Value { get; set; }
}



    public class Graph1SecondVal
{
    public DateTime DateTime { get; set; }
    public double Value { get; set; }
}

采纳答案by Kevin Ross

It's tough to say without getting a profiler on the code to see where the main pinch point is. Here are a few thigns to try

如果没有在代码上使用分析器来查看主要夹点在哪里,就很难说。这里有一些尝试

  • Use an ELSE-IF pattern assuming that the StrIncomingSerial string only has one type of value per read
  • You are trying to parse the string to a number then converting it again, try using the Num you have previously used
  • It appears you are calling SetAxisLimits twice per tick, try removing one of these
  • 使用 ELSE-IF 模式,假设 StrIncomingSerial 字符串每次读取只有一种类型的值
  • 您正在尝试将字符串解析为一个数字,然后再次将其转换,请尝试使用您之前使用过的 Num
  • 看来您每个刻度调用 SetAxisLimits 两次,请尝试删除其中之一

Let me know if that helps

让我知道这是否有帮助

回答by Pulsar79

Read this:

读这个:

https://lvcharts.net/App/examples/v1/wpf/Performance%20Tips

https://lvcharts.net/App/examples/v1/wpf/Performance%20Tips

If you use these attributes, you can get more speed:

如果使用这些属性,可以获得更快的速度:

<lvc:CartesianChart Hoverable="False" DataTooltip="{x:Null}" />

The attibute 'DataTooltip' influences very much.

属性“DataTooltip”影响很大。