3

Cairo + Gtk で書かれたアプリケーションに取り組んでいます。レトロ互換性の問題により、プログラミング言語として Python、ラッパーとして PyGTK、および GTK ライブラリ v.2.24 を使用せざるを得ないことに注意してください。C/C++ や GTK3 を使用する機会はありません!

私のアプリは、公開メソッドを呼び出すたびに、大量のデータを画面に (再) 描画する必要があります (明らかに)

以前 Cairo で描画されたいくつかのオブジェクトを手動で選択する機会をユーザーに与えるだけです。私は gtk.DrawingAreas で描画しているので、ラバー バンドの選択機能を手動で実装する必要があるようです。

これは質問です:

画面上の他のすべてのオブジェクトを再描画するのを避けて、マウスを動かすたびにラバー バンドの四角形を再描画する方法はありますか?

パフォーマンス上の理由から、選択長方形のみを再描画します。

大量のグラフィカル オブジェクトが原因で、私の GUI は非常に遅くなります。残念ながら、何度か試みましたが、選択の余地がありません。すべてを再描画するか、何かを再描画するかです!

最初に頭に浮かんだのは、ほとんどのデータを含む DrawingArea とマウス カーソルの間の中間レベルをオーバーレイする方法はありますか? queue_draw_area() 関数を呼び出しても、パフォーマンスは向上しません。

以下の単純な自己完結型コード例: 明らかに、この場合、私は cairo を使用して非常に単純なグラフィック オブジェクトを描画するだけです。

import gtk
from gtk import gdk

class Canvas(gtk.DrawingArea):

    # the list of points is passed as argument

    def __init__(self, points):
        super(Canvas, self).__init__()
        self.points = points
        self.set_size_request(400, 400)

        # Coordinates of the left-top angle of the selection rect
        self.startPoint = None

        self.endPoint = None

        # Pixmap to drawing rubber band selection
        self.pixmap = None

        self.connect("expose_event", self.expose_handler)
        self.connect("motion_notify_event", self.mouseMove_handler)
        self.set_events(gtk.gdk.EXPOSURE_MASK
                            | gtk.gdk.LEAVE_NOTIFY_MASK
                            | gtk.gdk.BUTTON_PRESS_MASK
                            | gtk.gdk.POINTER_MOTION_MASK
                            | gtk.gdk.POINTER_MOTION_HINT_MASK)

    # Method to paint lines and/or rubberband on screen

    def expose_handler(self, widget, event):

        rect = widget.get_allocation()
        w = rect.width
        h = rect.height
        ctx = widget.window.cairo_create()
        ctx.set_line_width(7)
        ctx.set_source_rgb(255, 0, 0)
        ctx.save()

        for i in range(0, len(self.points)):
            currPoint = self.points[i]
            currX = float(currPoint[0])
            currY = float(currPoint[1])
            nextIndex = i + 1
            if (nextIndex == len(self.points)):
                continue
            nextPoint = self.points[nextIndex]
            nextX = float(nextPoint[0])
            nextY = float(nextPoint[1])
            ctx.move_to(currX, currY)
            ctx.line_to(nextX, nextY)
        ctx.restore()
        ctx.close_path()
        ctx.stroke()

        # rubber band
        if self.pixmap != None:
            width = self.endPoint[0] - self.startPoint[0]
            height = self.endPoint[1] - self.startPoint[1]
            if width < 0 or height < 0:
                tempEndPoint = self.endPoint
                self.endPoint = self.startPoint
                self.startPoint = tempEndPoint

            height = self.endPoint[1] - self.startPoint[1]
            width = self.endPoint[0] - self.startPoint[0]
            widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], self.pixmap, self.startPoint[0], self.startPoint[1], self.startPoint[0], self.startPoint[1], abs(width), abs(height))

    def mouseMove_handler(self, widget, event):
        x, y, state = event.window.get_pointer()
        if (state & gtk.gdk.BUTTON1_MASK):
            if (state & gtk.gdk.CONTROL_MASK):
                if self.startPoint == None:
                    self.startPoint = (x,y)
                    self.endPoint = (x,y)
                else:
                    self.endPoint = (x,y)
                tempPixmap = gtk.gdk.Pixmap(widget.window, 400, 400)

                height = self.endPoint[1] - self.startPoint[1]
                width = self.endPoint[0] - self.startPoint[0]

                gc = self.window.new_gc()
                gc.set_foreground(self.get_colormap().alloc_color("#FF8000"))

                gc.fill = gtk.gdk.STIPPLED
                tempPixmap.draw_rectangle(gc, True, self.startPoint[0], self.startPoint[1], abs(width), abs(height))

                self.pixmap = tempPixmap
                # widget.queue_draw_area()
                widget.queue_draw()
        else:
            if (self.pixmap != None):
                self.pixmap = None

                # ...do something...

                # widget.queue_draw_area(self.startPoint[0], self.startPoint[1], )
                widget.queue_draw()


li1 = [(20,20), (380,20), (380,380), (20,380)]

window = gtk.Window()
canvas = Canvas(li1)
window.add(canvas)
window.connect("destroy", lambda w: gtk.main_quit())
window.show_all()
gtk.main()

ありがとう!

それ

4

2 に答える 2

2

Gtk+/Cairo で描画する際の一般的なヒント:

  • 必要な分だけ描く。アイデアは、変更された領域を追跡し、それらのみを再描画することです。gdk は、これを部分的に自動的に行います。expose( Gtk3 で)呼び出すとdraw、クリッピングマスクが適用されるため、「無効な」ピクセルのみが図面によって変更されます。
  • で再描画する必要がある領域を Gdk に指示できますGdkWindow.invalidate_rect。現在、 への呼び出しはwidget.queue_drawウィンドウ全体を無効にするため、描画するピクセルが多すぎます。
  • 無効化されていない領域に複雑な要素がある場合でも、公開でそれらを描画/計算します-画面に表示されません。event.areaこれについては、 (GdkRectangle)を確認できます。要素がこの領域と交差しない場合、これらのピクセルはいずれにせよクリップされるため、わざわざ描画する必要はありません。
    • ここにトレードオフがあります。要素が表示されるかどうかを計算すると、大幅に節約される場合があります。多くのジオメトリ計算を節約できる場合は、いくつかの余分なピクセルを描画する方が高速な場合があります。ケースバイケースで判断する必要があります。
  • Expose はそれぞれの invalidate_rect に対して 1 回呼び出される可能性がありますが、Gdk が複数の小さい/重複する rect を無効にすることを認識し、より大きな rect で 1 回だけ呼び出すことも可能です。
  • 「生の」Gdk と cairo 呼び出しを混在させないでください。Gdk 呼び出し (gc で動作) は Gtk3 でなくなります。今は Gtk2 でしか書いていないとおっしゃっていますが、いつか別のプロジェクトでコードを再利用したい場合は、今 Cairo で書いていただけると助かります。パフォーマンスの違いもあるかもしれません。
  • アンチエイリアスに注意する必要があります。デフォルトでは、cairo ではすべてがアンチエイリアス処理されています (これをオフにできるかどうかはわかりません)。ほとんどの場合、これは良いことですが、鮮明なピクセルの方が見栄えがよく、高速でもあります。アンチエイリアス処理されていない長方形が必要な場合は、整数座標で塗りつぶされた長方形を描画し、半整数座標でストロークされた長方形 (1 ピクセルの線) を描画します。
  • あなたの具体的な例では、あなたの Pixmap は不要なようです。あなたが試すことができるのは、変更されたときにピックスマップまたはカイロ表面にあなたのものをレンダリングしてexposeから、無効化された領域をピックスマップからコピーし、その上にラバーバンドを描くことです. ただし、直接描画する方が簡単で速いことがよくあります。GtkWidget.set_double_bufferedこの手動バッファリングを行っている場合は、組み込みのダブルバッファリング ( ) と背景の自動描画( ) を無効にすることを検討してくださいGtkWidget.set_app_paintable

これが私が作成した小さなプログラムです: rubbband.py。私は自分のプロジェクトからコードを取得し、選択できるいくつかの円を追加しました。出発点としてご利用いただければ幸いです。

于 2013-04-18T16:49:27.777 に答える
1

私が知っている古いスレッドですが、あるモデルはレンダリングされたサーフェスを別のバッファにコピーしてから、無効化された領域を再描画する可能性があります。Julia 言語でのその手法の例は、ここにあります。実際には、非常に優れたパフォーマンスを達成しているようです。

于 2014-09-19T09:15:46.030 に答える