一、DrawingArea连续实时绘图

Ubuntu20.4 Mono C# gtk 编程习练笔记(四)-LMLPHP

图看上去不是很清晰,KAZAM录屏AVI尺寸80MB, 转换成gif后10MB, 按CSDN对GIF要求,把它剪裁缩小压缩成了上面的GIF,图像质量大不如原屏AVI,但应该能说明原意:随机数据随时间绘制在 gtk 的 drawingArea 上,曲线左移后继续绘制,形成连续的实时数据绘图。

1. 在窗体上放置控件 drawingarea1

drawingarea1是 gtk 的组件,它是 gdk 窗口的一个框,绘图域是 gdkwindow

2. 内存中创建一个足够大的cario图像和cairo context上下文

        drawingarea1Width = drawingarea1.Allocation.Width;
        drawingarea1Height = drawingarea1.Allocation.Height;
        //surfacepub = new ImageSurface(Format.ARGB32, drawingarea1Width, drawingarea1Height);
        surfacepub = new ImageSurface(Format.ARGB32, 1920, 1080);
        ctxpub = new Cairo.Context(surfacepub);

drawingarea1Width和drawingarea1Height 是读取的drawingarea1的宽高尺寸,surfacepub是内存中的图像的surface,ctxpub 是 surface 的 Cairo Context

3. 在gdk的timer事中判断是否绘图点超界,超界了调用moveleft函数让绘图点左移500个像素,衔接绘图。

  private Boolean OnTimedEvent1()
    {
        drawingarea1Width = drawingarea1.Allocation.Width;
        drawingarea1Height = drawingarea1.Allocation.Height;

        iArea1ObjX += 10;
        if (iArea1ObjX > drawingarea1Width)
        {
            moveleft();
            int movdiff = 500;
            iArea1ObjX = drawingarea1Width-movdiff;
            iOldX2 = iOldX2 - movdiff; iOldX1 = iOldX1 - movdiff;

            //Mark by write Moved!
            Gdk.GC gc = new Gdk.GC(drawingarea1.GdkWindow);

            gc.RgbFgColor = new Gdk.Color(0, 0, 0);
            drawingarea1.GdkWindow.DrawRectangle(gc, true, iOldX2, 0, drawingarea1Width, drawingarea1Height);

            gc.RgbFgColor = new Gdk.Color(255,255,255);
            var layout = new Pango.Layout(PangoContext);
            layout.SetText("Moved!");
            layout.FontDescription = Pango.FontDescription.FromString("Serif 12");
            drawingarea1.GdkWindow.DrawLayout(gc, iOldX2, iOldY2, layout);

            gc.Dispose();
            DestroyContext();
            CreateContext();
        }

        ctxpub.MoveTo(iArea1ObjX, iArea1ObjY);
        ctxpub.SetSourceRGB(1, 0, 0);

        // Draw
        var rand = new Random();
        iArea1ObjY = rand.Next(10, 150);
        iOldX1 = iOldX2; iOldY1 = iOldY2;
        iOldX2 = iArea1ObjX; iOldY2 = iArea1ObjY;
        ctxpub.MoveTo(iOldX1, iOldY1);
        ctxpub.LineTo(iArea1ObjX, iArea1ObjY);
        ctxpub.ClosePath();
        ctxpub.Stroke();

        ctxArea1 = Gdk.CairoHelper.Create(drawingarea1.GdkWindow);
        surfacepub.Show(ctxArea1, 0, 0);
        ctxArea1.Dispose();
        return true;
    }

4. drawingarea1 图像左移 moveleft

    private void moveleft()
    {
        int movdiff = 500;
        Gdk.Pixbuf pixbuf = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, drawingarea1Width, 1080);
        pixbuf = pixbuf.GetFromDrawable(drawingarea1.GdkWindow, Gdk.Colormap.System, movdiff, 0, 0, 0, (drawingarea1Width-movdiff), 1080);
        Gdk.Pixbuf pixbuf1 = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, drawingarea1Width, 1080);
        pixbuf.CopyArea(movdiff, 0, drawingarea1Width, 1080, pixbuf1, 0, 0);
        
        Gdk.GC gc = new Gdk.GC(drawingarea1.GdkWindow);
        drawingarea1.GdkWindow.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0, drawingarea1Width, 1080, Gdk.RgbDither.None, 0, 0);
        pixbuf.Dispose();
        pixbuf1.Dispose();
        gc.Dispose();
    }

5.  释放内存surface和其cairo context

    private void DestroyContext()
    {
        surfacepub.Dispose();
        ctxpub.Dispose();
    }

如果在内存surface上显示Hello World, 将图像存成 png 是下面的样子。大小只有4kb,是镂空的,如果在photoshop上的话可以放在任何图层上,在上面的程序中也可以将它拓印到任何共它surface上,drawingarea1.GdkWindow也是一个surface

Ubuntu20.4 Mono C# gtk 编程习练笔记(四)-LMLPHP

二、用Cario贴图做连续图形

创建两个完全相同的内存 surfacepub 和 surfacebak,两个相同的context ctxpub和 context ctxbak,再创建一个drawingarea的context ctxArea1

    private void CreateContext()
    {
        drawingarea1Width = drawingarea1.Allocation.Width;
        drawingarea1Height = drawingarea1.Allocation.Height;
        surfacepub = new ImageSurface(Format.ARGB32, 1920, 1080);
        surfacebak = new ImageSurface(Format.ARGB32, 1920, 1080);
        ctxpub = new Cairo.Context(surfacepub);
        ctxbak = new Cairo.Context(surfacebak);
        ctxArea1 = Gdk.CairoHelper.Create(drawingarea1.GdkWindow);
    }

程序退出时记得把它们全弃用掉

    private void DestroyContext()
    {
        surfacepub.Dispose();
        surfacebak.Dispose();
        ctxpub.Dispose();
        ctxbak.Dispose();
        ctxArea1.Dispose();
    }

在gdk timer事件中完成移动和绘图

    private Boolean OnTimedEvent1()
    {
        drawingarea1Width = drawingarea1.Allocation.Width;
        drawingarea1Height = drawingarea1.Allocation.Height;

        //下一个绘图点 x
        iArea1ObjX += 10;

        //判断若是超出边界
        if (iArea1ObjX > drawingarea1Width)
        {
            //超出的话减掉500个像素点
            int movdiff = 500;
            iArea1ObjX = drawingarea1Width - movdiff;
            iOldX1 = iOldX1 - movdiff; iOldX2 = iOldX2 - movdiff;
            iArea1ObjX += 10;

            //用黑色清除 surfacebak
            ctxbak.SetSourceColor(new Cairo.Color(0, 0, 0));
            ctxbak.Rectangle(0, 0, drawingarea1Width, drawingarea1Height);
            ctxbak.Fill();

            //左移500个像素点后将 surfacepub 印在 ctxbak 所指的 surfacebak 上
            surfacepub.Show(ctxbak, -1*movdiff, 0);

            //将空余出的地方用黑色填充
            ctxbak.SetSourceColor(new Cairo.Color(0, 0, 0));
            ctxbak.Rectangle(drawingarea1Width-movdiff, 0, drawingarea1Width, drawingarea1Height);
            ctxbak.Fill();

            //用白色在 surfacebak 上写字 "Moved!"
            ctxbak.SetSourceColor(new Cairo.Color(1, 1, 1));
            ctxbak.MoveTo(iOldX1, iOldY1);
            ctxbak.ShowText("Moved!");
            ctxbak.Stroke();

            //清除掉 surfacepub
            ctxpub.SetSourceColor(new Cairo.Color(0, 0, 0));
            ctxpub.Rectangle(0, 0, drawingarea1Width, drawingarea1Height);
            ctxpub.Fill();

            //将 surfacebak 上的内容印在 surfacepub上
            surfacebak.Show(ctxpub, 0, 0);

        }

        //在 surfacepub 上画线
        var rand = new Random();
        iArea1ObjY = rand.Next(10, 150);
        iOldX1 = iOldX2; iOldY1 = iOldY2;
        iOldX2 = iArea1ObjX; iOldY2 = iArea1ObjY;
        ctxpub.MoveTo(iOldX1, iOldY1);
        ctxpub.SetSourceRGB(1, 0, 0);
        ctxpub.LineTo(iArea1ObjX, iArea1ObjY);

        ctxpub.Stroke();

        //创建 drawingarea1 的 context
        ctxArea1 = Gdk.CairoHelper.Create(drawingarea1.GdkWindow);
        //将 surfacepub 印在 drawingarea1 上显示
        surfacepub.Show(ctxArea1, 0, 0);

        //释放掉 drawingarea1 的 context
        ctxArea1.Dispose();

        //返回 true , 以便 timer 继续
        return true;
    }

由于图像是在内存的surfacepub上完成后印在drawingarea上的,因此曲线不受屏幕和组件变化影响,或是说即使受到破坏,但下一次镂印显示时又是内存中完整的图像。

Ubuntu20.4 Mono C# gtk 编程习练笔记(四)-LMLPHP

GdkWindow直接在组件屏幕上绘图和用Cairo在内存中连续绘图均完美显现,前者像是windows方式做图,后者比前者感觉更灵巧一些。

01-28 00:10