Я пытаюсь использовать Cairo с GTK # для создания простого приложения для рисования. Особенность, которая вызывает у меня проблемы, - это рисование линии между двумя точками. После нажатия на область рисования должна появиться строка и следовать курсору до тех пор, пока не будет отпущена кнопка мыши, после чего область рисования будет обновлена, а другая строка будет нарисована. Это должна быть знакомая функция из таких программ, как MS Paint.Приложение простой краски: ссылка на Cairo Контекст движения мыши
В настоящее время I am told способ осуществления таких обновлений в Каире - использовать методы .Save();
и .Restore()
Каира для обработки состояний. Проблема в том, что я не могу найти способ продолжать ссылаться на один и тот же Контекст в обработчике событий, отличном от того, который его создал, поэтому все мои попытки восстановить состояние после рисования строки были менее плодотворными.
В минимальном (но все же видном, жалком) примере рабочего кода ниже не-решение заключается в создании нового Контекста для каждого Draw. Это дает следующие результаты:
Ручка (слева) ведет себя хорошо. Линия, очевидно, не работает, поскольку после перемещения мыши невозможно вернуться к предыдущему состоянию, поэтому все они остаются на экране, чтобы сделать нарисованную диаграмму вентилятора.
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]
Я на права отслеживать здесь, пытаясь ссылаться на этот контекст? Кайро Контексты даже работают таким образом? Если нет, как я могу сделать линию непрерывной повторной визуализации?