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ソリューションを提供するのはこれが2回目です。

コードをコピーしてファイル->新しいプロジェクト->WPFアプリケーションに貼り付けるだけで、結果を自分で確認できます。

また、このコードが実際にどれほど単純であるかを見てください(私はランダムな値を使用しているので、それを削除してニーズに合わせることができます)。

私が使用した図面(<Path/>XAMLの部分)は、ゲージには適切ではありません。私はちょうどそのパスをすでに描いていました、そして私は新しいものを作成するのが面倒です。新しい図面を作成する必要があります(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 フォームは、(DirectX 上に構築された WPF とは異なり) ハードウェア アクセラレーションされていない古い GDI API に依存しているため、あらゆる種類の深刻なグラフィックスのプラットフォームとしては不十分です。winforms でどれだけ「効率的」になったとしても、ハードウェア アクセラレーションに支えられたものと競合することはできません。

于 2013-02-05T14:54:46.413 に答える
0

GDI+ を使用してビットマップを回転させると、時間がかかります。おそらく最大のパフォーマンス向上は、この目的でのビットマップの使用をやめ、GDI+ ベクター グラフィックスを使用してゲージをカスタム描画することです。必要に応じて、背景にビットマップを使用し、ゲージの針を描画するためにベクター グラフィックスを使用することもできます。これは、ビットマップを回転させるよりも桁違いに高速です。

次に確認したいのは、画像ボックスをダイナミック ビットマップ (つまり、絶えず変化するもの) と組み合わせて使用​​することが本当に正しい方法であるかどうかです。画像ボックスは、ビットマップが更新されるたびに余分な処理を行っている可能性があります。これは、実際には無駄なサイクルです。自分でビットマップを画面に描画してみませんか? また、最適な描画パフォーマンスを得るために、ビットマップが正しいピクセル形式 (PArgb32bpp) で作成されていることを確認してください。

最後に、入力データが変化する値の絶え間ないストリームでない限り、タイマーを完全に捨てて、BeginInvoke を使用して、画面を再描画するときに UI スレッドに通知することを検討します。現在のソリューションでは、タイマー ティック間の不必要な遅延が発生している可能性があり、必要以上に頻繁にゲージを再描画している可能性もあります。

于 2013-02-05T15:25:03.550 に答える