2013-04-12 2 views
1

Я пытаюсь использовать Cairo с GTK # для создания простого приложения для рисования. Особенность, которая вызывает у меня проблемы, - это рисование линии между двумя точками. После нажатия на область рисования должна появиться строка и следовать курсору до тех пор, пока не будет отпущена кнопка мыши, после чего область рисования будет обновлена, а другая строка будет нарисована. Это должна быть знакомая функция из таких программ, как MS Paint.Приложение простой краски: ссылка на Cairo Контекст движения мыши

В настоящее время I am told способ осуществления таких обновлений в Каире - использовать методы .Save(); и .Restore() Каира для обработки состояний. Проблема в том, что я не могу найти способ продолжать ссылаться на один и тот же Контекст в обработчике событий, отличном от того, который его создал, поэтому все мои попытки восстановить состояние после рисования строки были менее плодотворными.

В минимальном (но все же видном, жалком) примере рабочего кода ниже не-решение заключается в создании нового Контекста для каждого Draw. Это дает следующие результаты: Current state of the Painting application

Ручка (слева) ведет себя хорошо. Линия, очевидно, не работает, поскольку после перемещения мыши невозможно вернуться к предыдущему состоянию, поэтому все они остаются на экране, чтобы сделать нарисованную диаграмму вентилятора.

using Gtk; 
using Cairo; 
using System; 

public class Paint : Gtk.Window 
{ 

    bool isDrawing = false; 
    bool isDrawingLine = false; 
    bool isDrawingWithPen = false; 

    double Point_l1x; 
    double Point_l1y; 
    double Point_l2x; 
    double Point_l2y; 

    double Point_p1x; 
    double Point_p1y; 

    Button line; 
    Button pen; 

    static DrawingArea area; 
    Cairo.Context ctx; 

    void OnDrawingAreaExposed (object source, ExposeEventArgs args) 
    { 
     DrawingArea area = (DrawingArea) source; 
     ctx = Gdk.CairoHelper.Create (area.GdkWindow); 

     ((IDisposable) ctx.Target).Dispose(); 
     ((IDisposable) ctx).Dispose(); 
    } 

    public void DrawImage() 
    { 
     //Shouldn't this be referencing an external context? 
     using (Cairo.Context ctx = Gdk.CairoHelper.Create (area.GdkWindow)) 
     { 
      ctx.Color = new Cairo.Color (0, 0, 0); 

      if(isDrawingLine) 
      { 
       ctx.MoveTo (new PointD (Point_l1x, Point_l1y)); 
       ctx.LineTo (new PointD (Point_l2x, Point_l2y)); 
       ctx.Stroke(); 
      } 

      else if(isDrawingWithPen) 
      { 
       ctx.Rectangle(Point_p1x, Point_p1y, 1, 1); 
       ctx.Stroke(); 
      } 
     } 
    } 

    public void LineClicked(object sender, EventArgs args) 
    { 
     isDrawingLine = true; 
     isDrawingWithPen = false; 
    } 

    public void PenClicked(object sender, EventArgs args) 
    { 
     isDrawingLine = false; 
     isDrawingWithPen = true; 
    } 

    void OnMousePress (object source, ButtonPressEventArgs args) 
    { 
     isDrawing = true; 
     if (isDrawingLine) 
     { 
      Point_l1x = args.Event.X; 
      Point_l1y = args.Event.Y; 
     } 
     else if (isDrawingWithPen) 
     { 
      Point_p1x = args.Event.X; 
      Point_p1y = args.Event.Y; 
     } 
    } 

    void OnMouseRelease (object source, ButtonReleaseEventArgs args) 
    { 
     isDrawing = false; 
     DrawImage(); 
    } 

    void OnMouseMotion (object source, MotionNotifyEventArgs args) 
    { 
     if (!isDrawing) 
     { 
      return; 
     } 

     if (isDrawingLine) 
     { 
      Point_l2x = args.Event.X; 
      Point_l2y = args.Event.Y; 
     } 
     else if (isDrawingWithPen) 
     { 
      Point_p1x = args.Event.X; 
      Point_p1y = args.Event.Y; 
     } 
     DrawImage(); 
    } 

    public Paint() : base("Painting application") 
    { 
     area = new DrawingArea(); 
     area.ExposeEvent += OnDrawingAreaExposed; 

     area.AddEvents (
       (int)Gdk.EventMask.PointerMotionMask 
       | (int)Gdk.EventMask.ButtonPressMask 
       | (int)Gdk.EventMask.ButtonReleaseMask); 

     area.ButtonPressEvent += OnMousePress; 
     area.ButtonReleaseEvent += OnMouseRelease; 
     area.MotionNotifyEvent += OnMouseMotion; 

     DeleteEvent += delegate { Application.Quit(); }; 

     SetDefaultSize(500, 500); 
     SetPosition(WindowPosition.Center); 

     VBox vbox = new VBox(); 
     vbox.Add(area); 
     HBox hbox = new HBox(); 

     line = new Button("Line"); 
     pen = new Button("Pen"); 
     hbox.Add(line); 
     hbox.Add(pen); 

     Alignment halign = new Alignment(1, 0, 0, 0); 
     halign.Add(hbox); 

     vbox.Add(hbox); 
     vbox.PackStart(halign, false, false, 3); 

     line.Clicked += LineClicked; 
     pen.Clicked += PenClicked; 

     Add(vbox); 
     ShowAll(); 
    } 

    public static void Main() 
    { 
     Application.Init(); 
     new Paint(); 
     Application.Run(); 
    } 
} 

Если я изменить метод DrawImage для ссылки на контекст, определенный в OnDrawingAreaExposed, все это аварии, предлагая трассировки стека, что я не могу понять:

Stacktrace: 

at (wrapper managed-to-native) Cairo.NativeMethods.cairo_set_source_rgba (intptr,double,double,double,double) <0xffffffff> 
at Cairo.Context.set_Color (Cairo.Color) <0x0002b> 
at Paint.DrawImage() <0x000a3> 
at Paint.OnMouseMotion (object,Gtk.MotionNotifyEventArgs) <0x001af> 
at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object_object (object,intptr,intptr,intptr) <0xffffffff> 
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <0xffffffff> 
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <0x0018b> 
at System.Reflection.MethodBase.Invoke (object,object[]) <0x0002a> 
at System.Delegate.DynamicInvokeImpl (object[]) <0x001a3> 
at System.MulticastDelegate.DynamicInvokeImpl (object[]) <0x0003b> 
at System.Delegate.DynamicInvoke (object[]) <0x00018> 
at GLib.Signal.ClosureInvokedCB (object,GLib.ClosureInvokedArgs) <0x0014f> 
at GLib.SignalClosure.Invoke (GLib.ClosureInvokedArgs) <0x0002f> 
at GLib.SignalClosure.MarshalCallback (intptr,intptr,uint,intptr,intptr,intptr) <0x0050b> 
at (wrapper native-to-managed) GLib.SignalClosure.MarshalCallback (intptr,intptr,uint,intptr,intptr,intptr) <0xffffffff> 
at (wrapper managed-to-native) Gtk.Application.gtk_main() <0xffffffff> 
at Gtk.Application.Run() <0x0000b> 
at Paint.Main() <0x00027> 
at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff> 

Native stacktrace: 

     mono() [0x4961e9] 
     mono() [0x4e6d1f] 
     mono() [0x41dcb7] 
     /lib/x86_64-linux-gnu/libpthread.so.0(+0xfcb0) [0x7f3fd2f07cb0] 
     /usr/lib/x86_64-linux-gnu/libcairo.so.2(cairo_set_source_rgba+0x1) [0x7f3fcaaccc71] 
     [0x41a28dbd] 

Я на права отслеживать здесь, пытаясь ссылаться на этот контекст? Кайро Контексты даже работают таким образом? Если нет, как я могу сделать линию непрерывной повторной визуализации?

ответ

2

Будущие посетители могут быть заинтересованы в том, чтобы знать, что я в конечном итоге оштукатурил решение этой проблемы. Обратите внимание на использование делегирования. Результаты могут быть найдены ниже:

Снимок экрана: The working application

Исходный код:

using Gtk; 
using Cairo; 
using System; 

public class Paint : Gtk.Window 
{ 
    delegate void DrawShape(Cairo.Context ctx, PointD start, PointD end); 

    ImageSurface surface; 
    DrawingArea area; 
    DrawShape Painter; 
    PointD Start, End; 

    bool isDrawing; 
    bool isDrawingPoint; 

    Button line; 
    Button pen; 

    public Paint() : base("Painting application") 
    { 
     surface = new ImageSurface(Format.Argb32, 500, 500); 
     area = new DrawingArea(); 

     area.AddEvents(
      (int)Gdk.EventMask.PointerMotionMask 
      |(int)Gdk.EventMask.ButtonPressMask 
      |(int)Gdk.EventMask.ButtonReleaseMask); 

     area.ExposeEvent += OnDrawingAreaExposed; 
     area.ButtonPressEvent += OnMousePress; 
     area.ButtonReleaseEvent += OnMouseRelease; 
     area.MotionNotifyEvent += OnMouseMotion; 
     DeleteEvent += delegate { Application.Quit(); }; 

     Painter = new DrawShape(DrawLine); 

     Start = new PointD(0.0, 0.0); 
     End = new PointD(500.0, 500.0); 
     isDrawing = false; 
     isDrawingPoint = false; 

     SetDefaultSize(500, 500); 
     SetPosition(WindowPosition.Center); 

     VBox vbox = new VBox(); 
     vbox.Add(area); 
     HBox hbox = new HBox(); 

     line = new Button("Line"); 
     pen = new Button("Pen"); 
     hbox.Add(line); 
     hbox.Add(pen); 

     Alignment halign = new Alignment(1, 0, 0, 0); 
     halign.Add(hbox); 

     vbox.Add(hbox); 
     vbox.PackStart(halign, false, false, 3); 

     line.Clicked += LineClicked; 
     pen.Clicked += PenClicked; 

     Add(vbox); 

     Add(area); 
     ShowAll(); 
    } 

    void OnDrawingAreaExposed(object source, ExposeEventArgs args) 
    { 
     Cairo.Context ctx; 

     using (ctx = Gdk.CairoHelper.Create(area.GdkWindow)) 
     { 
      ctx.Source = new SurfacePattern(surface); 
      ctx.Paint(); 
     } 

     if (isDrawing) 
     { 
      using (ctx = Gdk.CairoHelper.Create(area.GdkWindow)) 
      { 
       Painter(ctx, Start, End); 
      } 
     } 
    } 

    void OnMousePress(object source, ButtonPressEventArgs args) 
    { 
     Start.X = args.Event.X; 
     Start.Y = args.Event.Y; 

     End.X = args.Event.X; 
     End.Y = args.Event.Y; 

     isDrawing = true; 
     area.QueueDraw(); 
    } 

    void OnMouseRelease(object source, ButtonReleaseEventArgs args) 
    { 
     End.X = args.Event.X; 
     End.Y = args.Event.Y; 

     isDrawing = false; 

     using (Context ctx = new Context(surface)) 
     { 
      Painter(ctx, Start, End); 
     } 

     area.QueueDraw(); 
    } 

    void OnMouseMotion(object source, MotionNotifyEventArgs args) 
    { 
     if (isDrawing) 
     { 
      End.X = args.Event.X; 
      End.Y = args.Event.Y; 

      if(isDrawingPoint) 
      { 
       using (Context ctx = new Context(surface)) 
       { 
        Painter(ctx, Start, End); 
       } 
      } 
      area.QueueDraw(); 
     } 
    } 

    void LineClicked(object sender, EventArgs args) 
    { 
     isDrawingPoint = false; 
     Painter = new DrawShape(DrawLine); 
    } 

    void PenClicked(object sender, EventArgs args) 
    { 
     isDrawingPoint = true; 
     Painter = new DrawShape(DrawPoint); 
    } 

    void DrawLine(Cairo.Context ctx, PointD start, PointD end) 
    { 
     ctx.MoveTo(start); 
     ctx.LineTo(end); 
     ctx.Stroke(); 
    } 

    void DrawPoint(Cairo.Context ctx, PointD start, PointD end) 
    { 
     ctx.Rectangle(end, 1, 1); 
     ctx.Stroke(); 
    } 

    public static void Main() 
    { 
     Application.Init(); 
     new Paint(); 
     Application.Run(); 
    } 
}