我正在研究LiveChart在WPF应用程序中的使用,以便在实时、温度测量中进行绘图。我已经建立了一个简单的线图例子,以读取数据在10赫兹,并重新绘制每一个样本。然而,我发现重绘率在1Hz左右。对于WPF动态图表工具来说,这似乎非常慢。我的xaml如下:
<lvc:CartesianChart x:Name="TemperatureChart" Grid.Row="1" LegendLocation="Right" Hoverable="False" DataTooltip="{x:Null}">
<lvc:CartesianChart.Series>
<lvc:LineSeries x:Name="TempDataSeries" Values="{Binding TemperatureData}"></lvc:LineSeries>
</lvc:CartesianChart.Series>
</lvc:CartesianChart>
我的视图模型中的片段如下所示:
ChartValues<ObservableValue> _temperatureData = new ChartValues<ObservableValue>();
public ChartValues<ObservableValue> TemperatureData
{
get => this._temperatureData;
set => this._temperatureData = value;
}
void Initialise()
{
_temperatureMonitor.Subscribe(ProcessTemperatures);
}
void TestStart()
{
_temperatureMonitor.Start();
}
void TestStop()
{
_temperatureMonitor.Stop();
}
void ProcessTemperatures(TemperatureData data)
{
TemperatureData.Add(data.Temperature);
}
我没有处理大量的数据,并且已经测试了100个值的限制。我相信,我的线程,它读取数据没有多少开销,然而,重绘的情节约10点一次。
我正确地实现了绑定吗?是否需要添加属性通知以强制更新?我的理解是,这是由ChartValues处理的。
谢谢。
更新。Oxyplot通过绑定到ObservableColllection of DataPoints,产生了以下所需的结果。使用LiveCharts获得相同的性能会很好,因为它具有非常好的美观。
发布于 2020-08-01 16:14:31
这个图书馆实现得相当差。有一个付费版本,它宣传自己比免费版本更有表现力。我还没有测试付费版本。免费版本的图表控件非常慢,特别是在处理庞大的数据集时。
显然,默认的CartesianChart.AnimationSpeed
默认设置为500 is。在实时场景中增加1/450 in以上的绘图速率将导致“丢失”帧。“丢失”意味着数据最终是可见的,但不是实时绘制的。每个布局无效的呈现传递只需太长时间。
超过450 to将使情节显得滞后(由于跳过的帧)。这是执行不力的结果。当超出默认的动画速度500 of时,应该禁用动画。
无论如何,为了大大超越450 go,您可以做一些事情来提高整体性能:
ObservablePoint
或ObservableValue
,或者通常让数据类型实现INotifyPropertyChanged
。当修改一个固定/不变的数据项集合而不是修改源集合时,您可能会获得更好的结果,例如,通过将items.LineSeries.PointGeometry
设置为 null
**.来添加/删除图形的实际可视点元素。这将删除其他呈现元素。直线笔划本身将保持可见。这将使_significantly_ performance.**false
以禁用鼠标效果。Chart.DataTooltip
设置为{x:Null}
以禁用工具提示objects.Chart.DisableAnimations
到E 238
true
**.的创建。禁用动画将使_significantly_ 提高呈现性能。或者,通过设置** Axis.DisableAnimations
**.**Axis.MinValue
和Axis.MaxValue
来禁用对每个值更改的自动缩放,从而禁用对每个轴选择的动画。在x轴值变化的大多数情况下,您也必须实时调整这两个属性。Axis.Unit
也显着地改善了图表对象在UIElement.CacheMode
上的外观。使用BitmapCache
可以禁用像素快照和修改呈现缩放。低于UIElement
.的BitmapCache.RenderAtScale
值会增加模糊度,但也会呈现1
的性能。
下面的示例通过将360个固定值集的每个ObservablePoint
值向左移动,实时绘制一个正弦图。所有建议的性能调整都是适用的,在绘制速率为1/10 of (100 Of)时,结果是可接受的平滑度。您可以在1/50 is到1/200 is之间使用值,甚至可以在1/10 is以下,如果这仍然可以接受的话。
注意,默认的Windows计时器的分辨率为15.6ms。这意味着当鼠标移动时,值<1/100 is将导致呈现停止。设备输入具有优先级,将使用相同的定时器进行处理。您需要找到绘图速率,这为框架处理UI输入留下了足够的时间。
强烈建议调整您的采样率,以匹配绘图速率,以避免滞后的感觉。或者,实现生产者-消费者模式,以避免松散/跳过数据读数。
DataModel.cs
public class DataModel : INotifyPropertyChanged
{
public DataModel()
{
this.ChartValues = new ChartValues<ObservablePoint>();
this.XMax = 360;
this.XMin = 0;
// Initialize the sine graph
for (double x = this.XMin; x <= this.XMax; x++)
{
var point = new ObservablePoint()
{
X = x,
Y = Math.Sin(x * Math.PI / 180)
};
this.ChartValues.Add(point);
}
// Setup the data mapper
this.DataMapper = new CartesianMapper<ObservablePoint>()
.X(point => point.X)
.Y(point => point.Y)
.Stroke(point => point.Y > 0.3 ? Brushes.Red : Brushes.LightGreen)
.Fill(point => point.Y > 0.3 ? Brushes.Red : Brushes.LightGreen);
// Setup the IProgress<T> instance in order to update the chart (UI thread)
// from the background thread
var progressReporter = new Progress<double>(newValue => ShiftValuesToTheLeft(newValue, CancellationToken.None));
// Generate the new data points on a background thread
// and use the IProgress<T> instance to update the chart on the UI thread
Task.Run(async () => await StartSineGenerator(progressReporter, CancellationToken.None));
}
// Dynamically add new data
private void ShiftValuesToTheLeft(double newValue, CancellationToken cancellationToken)
{
// Shift item data (and not the items) to the left
for (var index = 0; index < this.ChartValues.Count - 1; index++)
{
cancellationToken.ThrowIfCancellationRequested();
ObservablePoint currentPoint = this.ChartValues[index];
ObservablePoint nextPoint = this.ChartValues[index + 1];
currentPoint.X = nextPoint.X;
currentPoint.Y = nextPoint.Y;
}
// Add the new reading
ObservablePoint newPoint = this.ChartValues[this.ChartValues.Count - 1];
newPoint.X = newValue;
newPoint.Y = Math.Sin(newValue * Math.PI / 180);
// Update axis min/max
this.XMax = newValue;
this.XMin = this.ChartValues[0].X;
}
private async Task StartSineGenerator(IProgress<double> progressReporter, CancellationToken cancellationToken)
{
while (true)
{
// Add the new reading by posting the callback to the UI thread
ObservablePoint newPoint = this.ChartValues[this.ChartValues.Count - 1];
double newXValue = newPoint.X + 1;
progressReporter.Report(newXValue);
// Check if CancellationToken.Cancel() was called
cancellationToken.ThrowIfCancellationRequested();
// Plot at 1/10ms
await Task.Delay(TimeSpan.FromMilliseconds(10), cancellationToken);
}
}
private double xMax;
public double XMax
{
get => this.xMax;
set
{
this.xMax = value;
OnPropertyChanged();
}
}
private double xMin;
public double XMin
{
get => this.xMin;
set
{
this.xMin = value;
OnPropertyChanged();
}
}
private object dataMapper;
public object DataMapper
{
get => this.dataMapper;
set
{
this.dataMapper = value;
OnPropertyChanged();
}
}
public ChartValues<ObservablePoint> ChartValues { get; set; }
public Func<double, string> LabelFormatter => value => value.ToString("F");
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
MainWIndow.xaml
<Window>
<Window.DataContext>
<DataModel />
</Window.DataContext>
<CartesianChart Height="500"
Zoom="None"
Hoverable="False"
DataTooltip="{x:Null}"
DisableAnimations="True">
<wpf:CartesianChart.Series>
<wpf:LineSeries PointGeometry="{x:Null}"
Title="Sine Graph"
Values="{Binding ChartValues}"
Configuration="{Binding DataMapper}"/>
</wpf:CartesianChart.Series>
<CartesianChart.CacheMode>
<BitmapCache EnableClearType="False"
RenderAtScale="1"
SnapsToDevicePixels="False" />
</CartesianChart.CacheMode>
<CartesianChart.AxisY>
<Axis Title="Sin(X)"
FontSize="14"
Unit="1"
MaxValue="1.1"
MinValue="-1.1"
DisableAnimations="True"
LabelFormatter="{Binding LabelFormatter}"
Foreground="PaleVioletRed" />
</CartesianChart.AxisY>
<CartesianChart.AxisX>
<Axis Title="X"
DisableAnimations="True"
FontSize="14"
Unit="1"
MaxValue="{Binding XMax}"
MinValue="{Binding XMin}"
Foreground="PaleVioletRed" />
</CartesianChart.AxisX>
</CartesianChart>
</Window>
https://stackoverflow.com/questions/63138397
复制相似问题