Утечки памяти Ruby Gtk3 с использованием Cairo

Я пытаюсь создать прототип простого редактора блок-схем в Ruby Gtk3 для Linux. Мне кажется, что я использую Gtk / Cairo либо неадекватно, либо я столкнулся с утечкой памяти.

Симптомы следующие: когда я создаю графический объект (прямоугольник) и перемещаю его, использование памяти быстро увеличивается, как показано ниже.

введите здесь описание изображения

Проблема в использовании Gtk3::DrawingArea.queue_draw.

Что мне не хватает?

Мои версии следующие:

  • ruby 2.4.0p0 (24.12.2016, редакция 57164) [x86_64-linux]
  • gtk3 (3.2.7)

require 'gtk3'

BLUE = [0.1,0.0,0.7]

class Rectangle
	attr_accessor :x,:y,:w,:h
	attr_accessor :color
	def initialize x,y,w,h
		@x,@y,@w,@h= x,y,w,h
		@color=BLUE
	end

	def draw ctx
		ctx.set_source_rgb *color
		ctx.rectangle x,y,w,h
		ctx.fill
	end
end

class Drawer

	def initialize
		init_gui
		@grobs=[]
		@on_rect=nil
	end

	def init_gui
		builder = Gtk::Builder.new
		builder.add_from_file('drawing.glade')
		@window = builder['applicationwindow2']
		@window.signal_connect('destroy'){Gtk.main_quit}

		@drawingArea = builder['drawingarea']
		init_event_handlers
		@window.present
	end

	def init_event_handlers

		@drawingArea.signal_connect "draw" do
			[email protected]_cairo_context
			redraw
		end

		@drawingArea.signal_connect("button-press-event") do |widget, event|
			puts "mouse pressed"
			if @on_rect
				@moving=true
			else
				@grobs << Rectangle.new(event.x,event.y,50,50)
				redraw
			end
		end

		@drawingArea.signal_connect("motion-notify-event") do |widget, event|
			puts "moving rect" if @moving
			if @moving
				@on_rect.x=event.x+@dx
				@on_rect.y=event.y+@dy
				@drawingArea.queue_draw
			else
				if @on_rect=on_rect?(event)
					@dx,@dy=@on_rect.x-event.x,@on_rect.y-event.y
					w,h=@on_rect.w,@on_rect.h
					@drawingArea.queue_draw
				end
			end
		end

		@drawingArea.signal_connect("button-release-event") do |widget, event|
			puts "mouse released"
			@moving=false
			@on_rect=nil
			redraw
		end

		def on_rect? event
			for rect in @grobs
				if event.x>rect.x && event.x < rect.x+rect.w
					if event.y>rect.y && event.y < rect.y+rect.h
						return rect
					end
				end
			end
			nil
		end
	end

	def redraw
		[email protected]_cairo_context
		@grobs.each{|grob| grob.draw(ctx)}
	end

end #class

Drawer.new
Gtk.main

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkApplicationWindow" id="applicationwindow2">
    <property name="name">app_window</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">drawing_2</property>
    <property name="has_resize_grip">True</property>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem" id="menuitem1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem1">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem2">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem3">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem4">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem5">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Edit</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem6">
                        <property name="label">gtk-cut</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem7">
                        <property name="label">gtk-copy</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem8">
                        <property name="label">gtk-paste</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem9">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem3">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_View</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem4">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu3">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem10">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkDrawingArea" id="drawingarea">
            <property name="name">drawing_area</property>
            <property name="width_request">1000</property>
            <property name="height_request">600</property>
            <property name="visible">True</property>
            <property name="app_paintable">True</property>
            <property name="can_focus">True</property>
            <property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
            <property name="margin_left">9</property>
            <property name="margin_right">10</property>
            <property name="margin_top">10</property>
            <property name="margin_bottom">10</property>
            <signal name="drag-begin" handler="drag" swapped="no"/>
            <signal name="drag-motion" handler="drag" swapped="no"/>
            <signal name="scroll-event" handler="scroll" swapped="no"/>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="padding">7</property>
            <property name="pack_type">end</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>


person JCLL    schedule 19.07.2018    source источник
comment
[email protected]_cairo_context redraw Я не мастер в рубине, но я не вижу, где вы разрушаете то, что создали   -  person Alexander Dmitriev    schedule 19.07.2018


Ответы (2)


Я понятия не имею об управлении памятью Ruby, но я предлагаю вам использовать контекст Cairo, который дает вам Gtk, вместо создания своего собственного:

--- test.rb.orig    2018-07-28 08:04:24.958448613 +0200
+++ test.rb 2018-07-28 08:05:11.889968403 +0200
@@ -38,9 +38,8 @@ class Drawer

    def init_event_handlers

-       @drawingArea.signal_connect "draw" do
-           [email protected]_cairo_context
-           redraw
+       @drawingArea.signal_connect "draw" do |widget, ctx|
+           @grobs.each{|grob| grob.draw(ctx)}
        end

        @drawingArea.signal_connect("button-press-event") do |widget, event|
person Uli Schlachter    schedule 28.07.2018

Я сообщил об этой проблеме сопровождающему (kou). Тема находится здесь. Это действительно ошибка (ненужные копии объектов). Благодаря ему теперь это исправлено. Исправление ошибки находится здесь. Для этого исправления требуется cairo> = 1.15.14.

person JCLL    schedule 24.08.2018