performance - Cairo + rubber band selection: gui very very slow -


i'm working on application written in cairo + gtk. please note that, due retrocompatibility issues, forced use python programming language, pygtk wrapper, , gtk libraries, v.2.24. no chance use c/c++ and/or gtk3!

my app need (re)draw big amount of data on screen each invocation of expose method(obviously).

i give users chance manually select objects drawn cairo. since i'm drawing on gtk.drawingareas, seems must manually implement rubber band selection functionality.

this question:

is there way redraw rubber band rectangle @ every mouse move, avoiding redraw others objects on screen?

i redraw only selection rectangle performance reasons.

due big amount of graphical objects, gui terribly slow. unfortunately, despite several attempts, have no choice: redraw or redraw anything!

first thing came in mind: there way overlay intermediate level between drawingarea of data , mouse cursor? calling queue_draw_area() function there aren't performance gains.

a simple, self containing code example below: obviously, in case ionly use cairo draw extremely simple graphic objects.

import gtk gtk import gdk  class canvas(gtk.drawingarea):      # list of points passed argument      def __init__(self, points):         super(canvas, self).__init__()         self.points = points         self.set_size_request(400, 400)          # coordinates of left-top angle of selection rect         self.startpoint = none          self.endpoint = none          # pixmap 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 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()          in range(0, len(self.points)):             currpoint = self.points[i]             currx = float(currpoint[0])             curry = float(currpoint[1])             nextindex = + 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() 

thanks!

it

some general tips when drawing gtk+/cairo:

  • draw as neccessary. idea keep track of areas have changed, , redraw those. gdk in parts automatically you. when calls expose (in gtk3 draw), applies clipping mask "invalidated" pixels changed drawings.
  • you can tell gdk areas should redrawn gdkwindow.invalidate_rect. right call widget.queue_draw invalidates whole window, draw many pixels.
  • if have complex elements in non-invalidated area, you'd still draw / calculate them in expose - they'd not make screen. this, can check event.area (a gdkrectangle). if elements dont intersect area, don't have bother drawing them, pixels clipped anyway.
    • here lies trade-off. saves lot if calculate whether element visible or not. it's faster draw few pixels if saves lot of geometry calculations. have decide case case.
  • expose may called once each invalidate_rect, it's possible gdk recognizes when invalidate several small/overlapping rects, , calls once larger rect.
  • you shouldn't mix 'raw' gdk , cairo calls. gdk calls (operating on gc) going away in gtk3. writing in gtk2, if want reuse code 1 day in project, it'll if write cairo. there might performance differences, too.
  • you should aware of anti-aliasing. default, antialiased in cairo (and don't know whether can turn off). that's thing of time, crisp pixels nicer - , lot faster, too. if want non-antialiased rectangles, draw filled rectangles on integer coordinates, , stroked rectangles (1px lines) on half-integer coordinates.
  • in concrete example, pixmap seems unneccessary. on thing try though render stuff pixmap or cairo surface when changes, in expose copy invalidated areas pixmap over, , draw rubber band on top. however, find it's easier , faster drawing directly. if doing manual buffering, might want think disabling built-in double-buffering (gtkwidget.set_double_buffered) , automatic drawing of background (gtkwidget.set_app_paintable).

here little program make: rubberband.py. took code project of mine , added couple of circles 1 can select. hope can use starting point.


Comments

Popular posts from this blog

Why does Ruby on Rails generate add a blank line to the end of a file? -

keyboard - Smiles and long press feature in Android -

node.js - Bad Request - node js ajax post -