0

我正在努力使我的应用程序从串行端口读取数据并更新 UI 上的仪表更有效,我想就处理 UI 更改的代码征求一些建议。我设置了一个计时器来检查发送到 COM 端口的数据,另一个计时器使用从 COM 端口接收的变量更新 UI。基本上正在发生的事情是我正在旋转仪表。这是我处理图形的代码...

void timer_Tick(object sender, EventArgs e) //Timer regulates how often the gauge is     updated on the UI
{
    if (pictureBox1.Image != null)
        pictureBox1.Image.Dispose(); // dispose old image (you might consider reusing it rather than making a new one each frame)

    Point test = new Point((int)_xCor, (int)_yCor);
    Image img = new Bitmap(400, 400); // The box tht contains the image <--- Play around with this more
    pictureBox1.Image = img; // Setting the img Image to the pictureBox class?


    Graphics g = Graphics.FromImage(pictureBox1.Image); // G represents a drawing surface
    Matrix mm1 = new Matrix();
    //
    mm1.RotateAt((float)(90 + (((12.5 * state) - 20.95) * 6)), new Point((int)_xrotate, (int)_yrotate), MatrixOrder.Append);
    GraphicsPath gp = new GraphicsPath();
    g.Transform = mm1; // transform the graphics object so the image is rotated
    g.DrawImage(imgpic, test); // if the image needs to be behind the path, draw it beforehand
    mm1.Dispose();// prevent possible memory leaks
    gp.Dispose();// prevent possible memory leaks
    g.Dispose(); // prevent possible memory leaks
    pictureBox1.Refresh();
}

我想知道是否有更有效的方法可以在屏幕上旋转图像。我觉得必须有,但我想不通。

4

4 回答 4

2

这是我第二次为 winforms 问题提供 WPF 解决方案。

只需将我的代码复制并粘贴到文件 -> 新项目 -> WPF 应用程序中,然后自己查看结果。

还要看看这段代码有多简单(我使用的是随机值,所以你可以删除它并根据你的需要调整它)。

我使用的绘图(<Path/>XAML 中的部分)对于 Gauge 来说是不够的。我刚刚画了那个路径,我懒得创建一个新的。您应该创建一个新绘图(我建议使用 Expression Blend)。但是您可以看到正在应用的旋转以及它的工作速度。

using System;
using System.Threading;
using System.Windows;
using System.ComponentModel;

namespace WpfApplication4
{
    public partial class Window2
    {
        public Window2()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel: INotifyPropertyChanged
    {
        private double _value;
        public double Value
        {
            get { return _value; }
            set
            {
                _value = value;
                NotifyPropertyChange("Value");
            }
        }

        private int _speed = 100;
        public int Speed
        {
            get { return _speed; }
            set
            {
                _speed = value;
                NotifyPropertyChange("Speed");
                Timer.Change(0, value);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private System.Threading.Timer Timer;

        public ViewModel()
        {
            Rnd = new Random();
            Timer = new Timer(x => Timer_Tick(), null, 0, Speed);
        }

        private void Timer_Tick()
        {
            Application.Current.Dispatcher.BeginInvoke((Action) (NewValue));
        }

        private Random Rnd;
        private void NewValue()
        {
            Value = Value + (Rnd.Next(20) - 10);
        }
    }
}

XAML:

<Window x:Class="WpfApplication4.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" WindowState="Maximized">
    <DockPanel>
        <StackPanel DockPanel.Dock="Top">
            <TextBlock Text="Delay (MS):" Margin="2"/>
            <Slider Width="200" Minimum="100" SmallChange="1" LargeChange="10" Maximum="1500" Value="{Binding Speed}" Margin="2"/>
            <TextBlock Text="Current Value:" Margin="2"/>
            <TextBox Text="{Binding Value}" Margin="2"/>
        </StackPanel>

        <Path Data="M0.95991516,0.5 L73.257382,1.866724 90.763535,1.866724 90.763535,90.822725 66.430534,90.822725 66.430534,26.075016 0.5,24.828653 z" Fill="#FF506077" RenderTransformOrigin="0.861209625003783,0.507482926584064" Stretch="Fill" Stroke="Black">
            <Path.LayoutTransform>
                <TransformGroup>
                    <ScaleTransform ScaleY="1" ScaleX="-1"/>
                    <SkewTransform AngleY="0" AngleX="0"/>
                    <RotateTransform Angle="{Binding Value}" x:Name="Rotation"/>
                    <TranslateTransform/>
                </TransformGroup>
            </Path.LayoutTransform>
        </Path>
    </DockPanel>
</Window>
于 2013-02-05T16:05:22.000 回答
1

很难回答您的问题,因为您对图像“更有效”旋转的要求非常模糊。我不确定您所说的效率是否更高:

  • 更好的性能;
  • 更少的内存使用;
  • 或者只是更少或更优雅的代码

无论如何,除非您正在谈论使代码比我能想出的唯一事情更“优雅”,否则您可以并且可能应该重新使用相同的图像/位图。而不是每次都创建一个新的,你可以清除你正在使用的那个并重新绘制你的图像。

您可能还想检查用于更新 UI 的计时器的刷新率。大约 24 - 30 fps 的帧速率应该足够了。在这种情况下,任何更多的东西都是多余的,它只会浪费 CPU 周期。

您还应该启用双缓冲以防止闪烁。

编辑

根据您的评论,听起来问题不在于性能,而是 COM 端口计时器和 UI 计时器的间隔之间存在差异。听起来更新 UI 的计时器运行速度不够快,无法检测到更改。您的间隔是多少?

于 2013-02-05T14:52:49.740 回答
1

看起来您正在 Windows 窗体中执行此操作?采用:

Graphics.RotateTransform

但是,如果我可以谦虚地建议,如果您正在尝试以图形方式做任何非常有趣的事情,那么升级到 WPF 可能是值得的投资。Windows 窗体依赖于非硬件加速的旧 GDI api(与基于 DirectX 的 WPF 不同),使其成为任何类型严重图形的糟糕平台。无论您使用 winforms 获得多么“高效”,您都永远无法与硬件加速支持的任何东西竞争。

于 2013-02-05T14:54:46.413 回答
0

使用 GDI+ 旋转位图会很慢。您可能实现的最大性能提升是停止为此目的使用位图,而只需使用 GDI+ 矢量图形自定义绘制您的仪表。如果适用,您仍然可以使用位图作为背景并使用矢量图形绘制仪表的针。这将比旋转位图快几个数量级。

接下来我要考虑的是,将图片框与动态位图(即不断变化)结合使用是否真的是正确的方法;每次更新时,图片框可能都会对您的位图进行额外处理,这实际上只是浪费了周期。为什么不自己将位图绘制到屏幕上呢?此外,请确保使用正确的像素格式创建位图以获得最佳绘图性能 (PArgb32bpp)。

最后,除非您的输入数据是不断变化的值流,否则我会考虑完全放弃计时器,而仅在需要重绘屏幕时使用 BeginInvoke 向您的 UI 线程发出信号。您当前的解决方案可能会在计时器滴答之间遭受不必要的延迟,并且可能会比必要的更频繁地重绘仪表。

于 2013-02-05T15:25:03.550 回答