ZetCode 图形教程(七)

原文:ZetCode 协议:CC BY-NC-SA 4.0 Cairo 中的图像 原文: https:zetcodegfxcairocairoimages 在 C

原文:ZetCode

协议:CC BY-NC-SA 4.0

Cairo 中的图像

原文: https://zetcode/gfx/cairo/cairoimages/

在 Cairo 图形教程的这一部分中,我们将讨论图像。 我们将展示如何在 GTK 窗口上显示图像。 我们还将用图像创建一些效果。

显示图像

在第一个示例中,我们将显示一个图像。

#include <cairo.h>
#include <gtk/gtk.h>

struct {
  cairo_surface_t *image;  
} glob;

static void do_drawing(cairo_t *);

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{      
  do_drawing(cr);

  return FALSE;
}

static void do_drawing(cairo_t *cr)
{
  cairo_set_source_surface(cr, glob.image, 10, 10);
  cairo_paint(cr);    
}

int main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;

  glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);

  g_signal_connect(G_OBJECT(darea), "draw", 
      G_CALLBACK(on_draw_event), NULL); 
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 220); 
  gtk_window_set_title(GTK_WINDOW(window), "Image");

  gtk_widget_show_all(window);

  gtk_main();

  cairo_surface_destroy(glob.image);

  return 0;
}

该示例显示图像。

glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");

我们从 PNG 图像创建图像表面。 出于效率原因,该函数在主函数中调用。

cairo_set_source_surface(cr, glob.image, 10, 10);

我们从创建的图像表面创建一个绘画源。

cairo_paint(cr);   

我们在窗口上绘制源。

cairo_surface_destroy(glob.image);

最后,表面被破坏。

水印

在图像上绘制信息是很常见的。 写在图像上的文本称为水印。 水印用于识别图像。 它们可能是版权声明或图像创建时间。

#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *, GtkWidget *widget);

struct {
  cairo_surface_t *image;  
} glob;

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{      
  do_drawing(cr, widget);  

  return FALSE;
}

static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
  cairo_set_source_surface(cr, glob.image, 10, 10);
  cairo_paint(cr);
}

static void load_image()
{        
  glob.image = cairo_image_surface_create_from_png("beckov.png"); 
}

static void draw_mark() 
{ 
  cairo_t *ic;
  ic = cairo_create(glob.image);
  cairo_set_font_size(ic, 11);

  cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
  cairo_move_to(ic, 20, 30);
  cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar ");
  cairo_stroke(ic);   
}

int main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;

  load_image();
  draw_mark();

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);

  g_signal_connect(G_OBJECT(darea), "draw", 
      G_CALLBACK(on_draw_event), NULL); 
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); 
  gtk_window_set_title(GTK_WINDOW(window), "Watermark");

  gtk_widget_show_all(window);

  gtk_main();

  cairo_surface_destroy(glob.image);

  return 0;
}

我们在图像上绘制版权信息。

static void load_image()
{        
  glob.image = cairo_image_surface_create_from_png("beckov.png"); 
}

load_image()方法中,我们从 PNG 图像创建图像表面。

static void draw_mark() 
{ 
  cairo_t *ic;
  ic = cairo_create(glob.image);
...

draw_mark()函数中,我们在图像上绘制版权信息。 首先,我们从图像表面创建一个绘图上下文。

cairo_set_font_size(ic, 11);

cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
cairo_move_to(ic, 20, 30);
cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar ");
cairo_stroke(ic);   

然后,我们用白色绘制一个小的文本。

static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
  cairo_set_source_surface(cr, glob.image, 10, 10);
  cairo_paint(cr);
}

图像表面绘制在窗口上。

频谱效应

我们称其为频谱效应,因为它类似于旧的 ZX 频谱计算机。 当您将图像加载到这台计算机时,它逐渐出现在屏幕上。 下一个例子是基于这种经验。

#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *);

struct {
  gboolean timer;
  cairo_surface_t *image;
  cairo_surface_t *surface;
  gint img_width;
  gint img_height;
} glob;

static void init_vars()
{
  glob.image = cairo_image_surface_create_from_png("beckov.png");

  glob.img_width = cairo_image_surface_get_width(glob.image);
  glob.img_height = cairo_image_surface_get_height(glob.image);  

  glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
      glob.img_width, glob.img_height);    
  glob.timer = TRUE;   
}

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{      
  do_drawing(cr);

  return FALSE;
}

static void do_drawing(cairo_t *cr)
{
  cairo_t *ic;

  static gint count = 0;

  ic = cairo_create(glob.surface);

  gint i, j;
  for (i = 0; i <= glob.img_height; i+=7) {
      for (j = 0 ; j < count; j++) {
          cairo_move_to(ic, 0, i+j);
          cairo_line_to(ic, glob.img_width, i+j);
      }
  }

  count++;
  if (count == 8) glob.timer = FALSE;

  cairo_set_source_surface(cr, glob.image, 10, 10);
  cairo_mask_surface(cr, glob.surface, 10, 10);
  cairo_stroke(ic);

  cairo_destroy(ic);  
}

static gboolean time_handler(GtkWidget *widget)
{
  if (!glob.timer) return FALSE;

  gtk_widget_queue_draw(widget);
  return TRUE;
}

int main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;

  init_vars();

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);

  g_signal_connect(G_OBJECT(darea), "draw", 
      G_CALLBACK(on_draw_event), NULL); 
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 325, 250); 
  gtk_window_set_title(GTK_WINDOW(window), "Spectrum");

  g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window);

  gtk_widget_show_all(window);

  gtk_main();

  cairo_surface_destroy(glob.image);
  cairo_surface_destroy(glob.surface);  

  return 0;
}

我们将图像分为由 8 行组成的 n 个部分。 每个周期,图像的每个部分都会变大一个像素。 创建的图像将用作显示城堡图像的遮罩。

struct {
  gboolean timer;
  cairo_surface_t *image;
  cairo_surface_t *surface;
  gint img_width;
  gint img_height;
} glob;

全局结构存储在更多函数中使用的变量。

static void init_vars()
{
  glob.image = cairo_image_surface_create_from_png("beckov.png");

  glob.img_width = cairo_image_surface_get_width(glob.image);
  glob.img_height = cairo_image_surface_get_height(glob.image);  

  glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
      glob.img_width, glob.img_height);    
  glob.timer = TRUE;   
}

init_vars()函数中,我们启动上述变量。

gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
    for (j = 0 ; j < count; j++) {
        cairo_move_to(ic, 0, i+j);
        cairo_line_to(ic, glob.img_width, i+j);
    }
}

我们逐步将线绘制到 n 个部分中的每个部分。

count++;
if (count == 8) glob.timer = FALSE;

8 个步骤后,动画结束。

cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);

使用遮罩操作,我们在窗口上绘制图像的各个部分。

本章介绍了 Cairo 的图像。

根窗口

原文: https://zetcode/gfx/cairo/root/

在 Cairo 图形教程的这一部分中,我们将使用根窗口。 根窗口是我们通常具有图标快捷方式的桌面窗口。

可以使用根窗口进行操作。 从程序员的角度来看,它只是一种特殊的窗口。

透明窗

我们的第一个示例将创建一个透明窗口。 我们将看到窗口对象下方的内容。

#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *);

static void tran_setup(GtkWidget *win)
{        
  GdkScreen *screen;
  GdkVisual *visual;

  gtk_widget_set_app_paintable(win, TRUE);
  screen = gdk_screen_get_default();
  visual = gdk_screen_get_rgba_visual(screen);

  if (visual != NULL && gdk_screen_is_composited(screen)) {
      gtk_widget_set_visual(win, visual);
  }
}

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{      
  do_drawing(cr);  

  return FALSE;
}

static void do_drawing(cairo_t *cr)
{
  cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  cairo_paint(cr);
}

int main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  tran_setup(window);

  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);

  g_signal_connect(G_OBJECT(darea), "draw", 
      G_CALLBACK(on_draw_event), NULL); 
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 250); 
  gtk_window_set_title(GTK_WINDOW(window), "Transparent window");

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

为了创建透明窗口,我们获得了屏幕对象的视觉效果并将其设置为我们的窗口。 在on_draw()方法中,我们绘制屏幕的可视对象。 这产生了部分透明的幻觉。

gtk_widget_set_app_paintable(win, TRUE);

我们必须设置要绘制的应用。

screen = gdk_screen_get_default();

gdk_screen_get_default()方法返回屏幕对象。

visual = gdk_screen_get_rgba_visual(screen);

从屏幕窗口中,我们可以看到它。 视觉内容包含低级显示信息。

if (visual != NULL && gdk_screen_is_composited(screen)) {
    gtk_widget_set_visual(win, visual);
}

并非所有的显示器都支持此操作。 因此,我们检查屏幕是否支持合成并且返回的视觉效果不是NULL。 我们将屏幕的视觉效果设置为窗口的视觉效果。

static void do_drawing(cairo_t *cr)
{
  cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  cairo_paint(cr);
}

我们使用部分透明的源来绘制屏幕窗口。 CAIRO_OPERATOR_SOURCE在我们绘制源代码的地方创建了合成操作。 这是屏幕窗口。 为了获得完全透明,我们将 alpha 值设置为 0 或使用CAIRO_OPERATOR_CLEAR运算符。

图:透明窗口

截屏

根窗口对于截图也是必不可少的。

#include <cairo.h>
#include <gdk/gdk.h>

int main (int argc, char *argv[])
{
  gdk_init(&argc, &argv);

  GdkWindow *root_win = gdk_get_default_root_window();
  gint width = gdk_window_get_width(root_win);
  gint height = gdk_window_get_height(root_win);

  cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
      width, height);

  GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);

  cairo_t *cr = cairo_create(surface);        
  gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);  
  cairo_paint(cr);  

  cairo_surface_write_to_png(surface, "image.png");

  cairo_destroy(cr);
  cairo_surface_destroy(surface);

  return 0;
}

该示例捕获整个屏幕的快照。 在此示例中,我们不使用完整的 GTK 窗口系统。 我们使用 Cairo 和 GDK 库来完成这项工作。

gdk_init(&argc, &argv);

gdk_init()初始化 GDK 库并连接到窗口系统。

GdkWindow *root_win = gdk_get_default_root_window();

我们通过gdk_get_default_root_window()函数调用获得了根窗口。

gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);

我们确定根窗口的宽度和高度。

cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    width, height);

空的图像表面被创建。 它具有根窗口的大小。

GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);

我们使用gdk_pixbuf_get_from_window()函数调用从根窗口中获得一个pixbufpixbuf是描述内存中图像的对象。

cairo_t *cr = cairo_create(surface);        
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);  
cairo_paint(cr);

在上述代码行中,我们在之前创建的图像表面上创建了 Cairo 绘图上下文。 我们将 pixbuf 放在绘图上下文上并将其绘制在表面上。

cairo_surface_write_to_png(surface, "image.png");

使用write_to_png()方法将图像表面写入 PNG 图像。

cairo_destroy(cr);
cairo_surface_destroy(surface);

我们清理资源。

显示信息

在第三个示例中,我们将在桌面窗口上显示一条消息。

#include <cairo.h>
#include <gtk/gtk.h>
#include <pango/pango.h>

static void do_drawing(cairo_t *);

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{      
  do_drawing(cr);  

  return FALSE;
}

static void do_drawing(cairo_t *cr)
{
   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
   cairo_paint(cr);
   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
}

static void setup(GtkWidget *win)
{        
  gtk_widget_set_app_paintable(win, TRUE);
  gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
  gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);

  GdkScreen *screen = gdk_screen_get_default();
  GdkVisual *visual = gdk_screen_get_rgba_visual(screen);

  if (visual != NULL && gdk_screen_is_composited(screen)) {
      gtk_widget_set_visual(win, visual);
  }  
}

int main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *lbl;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  setup(window);

  lbl = gtk_label_new("ZetCode, tutorials for programmers");

  PangoFontDescription *fd = pango_font_description_from_string("Serif 20");
  gtk_widget_modify_font(lbl, fd);  
  gtk_container_add(GTK_CONTAINER(window), lbl);  

  GdkColor color;
  gdk_color_parse("white", &color);
  gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);

  g_signal_connect(G_OBJECT(window), "draw", 
      G_CALLBACK(on_draw_event), NULL); 
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); 

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

该代码在根窗口上显示消息标签。

static void do_drawing(cairo_t *cr)
{
   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
   cairo_paint(cr);
   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
}

我们使用CAIRO_OPERATOR_CLEAR运算符清除窗口背景。 然后我们设置CAIRO_OPERATOR_OVER以绘制标签窗口小部件。

gtk_widget_set_app_paintable(win, TRUE);

我们将操纵应用窗口,因此我们使其可绘制。

gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);

实现此窗口提示会删除窗口边框和装饰。

gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);

我们始终将应用始终放在根窗口的底部。

GdkScreen *screen = gdk_screen_get_default();
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);

if (visual != NULL && gdk_screen_is_composited(screen)) {
    gtk_widget_set_visual(win, visual);
  } 

我们将屏幕的外观设置为应用的外观。

lbl = gtk_label_new("ZetCode, tutorials for programmers");

我们创建一个消息标签。

PangoFontDescription *fd = pango_font_description_from_string("Serif 20");
gtk_widget_modify_font(lbl, fd);  

在 Pango 模块的帮助下,我们为文本选择特定的字体。

gtk_container_add(GTK_CONTAINER(window), lbl);   

标签贴在窗户上。

GdkColor color;
gdk_color_parse("white", &color);
gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);

我们将文本修改为白色。

图:根窗口上的消息

在本章中,我们使用了 Cairo 的桌面窗口。

PyCairo 教程

原文: https://zetcode/gfx/pycairo/

这是 PyCairo 教程。 在本教程中,我们将学习 Python 和 Cairo 库中的 2D 图形编程。

目录

  • 简介
  • 后端
  • 基本绘图
  • 形状&填充
  • 渐变
  • 剪裁&遮罩
  • 透明度
  • 转换
  • 文字
  • 图片
  • 根窗口

PyCairo

PyCairo 是用于 Cairo 库的 Python 模块。 它是对 Cairo C 库的一组 Python 绑定。 它与 C API 紧密匹配,但需要更多 Python 方式的情况除外。

相关教程

Cairo 图形教程涵盖了使用 C 编程语言编写的 Cairo 库。

PyCairo 简介

原文: https://zetcode/gfx/pycairo/introduction/

这是 PyCairo 教程。 本教程将使用 Python 语言教您 Cairo 2D 库的基础知识和一些高级主题。 在大多数示例中,我们将使用 Python GTK 后端生成我们的输出。 可以在此处下载本教程中使用的图像。

计算机图像

有两种不同的计算机图形:矢量图形和栅格图形。 光栅图形将图像表示为像素集合。 矢量图形是使用几何图元(例如点,线,曲线或多边形)来表示图像。 这些基元是使用数学方程式创建的。

两种类型的计算机图形都有优点和缺点。 矢量图形优于栅格的优点是:

  • 较小的大小
  • 无限放大的能力
  • 移动,缩放,填充和旋转不会降低图像质量

Cairo

Cairo 是用于创建 2D 矢量图形的库。 它是用 C 编程语言编写的。 存在其他计算机语言的绑定,包括 Python,Perl,C++ ,C# 或 Java。 Cairo 是一个多平台库; 它适用于 Linux,BSD,Windows 和 OSX。

Cairo 支持各种后端。 后端是用于显示创建的图形的输出设备。

  • X Window 系统
  • Win32 GDI
  • Mac OS X
  • PNG
  • PDF 格式
  • PostScript
  • SVG

这意味着我们可以使用该库在 Windows,Linux,Windows,OSX 上进行绘制,并且可以使用该库创建 PNG 图像,PDF 文件,PostScript 文件和 SVG 文件。

我们可以将 Cairo 库与 Windows OS 上的 GDI+ 库和 Mac OS 上的 Quartz 2D 比较。 Cairo 是一个开源软件库。 从 2.8 版开始,Cairo 是 GTK 系统的一部分。

定义

在这里,我们提供了一些有用的定义。 要在 PyCairo 中进行绘制,我们首先必须创建一个绘制上下文。 绘图上下文包含所有描述绘图方式的图形状态参数。 这包括诸如线宽,颜色,要绘制的表面以及许多其他信息。 它允许实际的绘图函数采用较少的参数来简化界面。

路径是用于创建基本形状(如直线,圆弧和曲线)的点的集合。 路径有两种:开放路径和封闭路径。 在封闭的路径中,起点和终点相遇。 在开放路径中,起点和终点不相交。 在 PyCairo 中,我们从空路径开始。 首先,我们定义一条路径,然后通过抚摸和/或填充它们使它们可见。 在每个stroke()fill()方法调用之后,该路径将被清空。 我们必须定义一条新路径。 如果我们想保留现有路径以用于以后的绘图,则可以使用stroke_preserve()fill_preserve()方法。 路径由子路径组成。

源是我们在绘图中使用的油漆。 我们可以将源与用来绘制轮廓和填充形状的钢笔或墨水进行比较。 有四种基本来源:颜色,渐变,图案和图像。

曲面是我们要绘制的目标。 我们可以使用 PDF 或 PostScript 曲面渲染文档,并通过 Xlib 和 Win32 曲面直接绘制到平台上。

在将源应用于表面之前,先对其进行过滤。 掩码用作过滤器。 它确定在哪里应用源,不在哪里应用。 遮罩的不透明部分允许复制源。 透明零件不允许将源复制到表面。

模式代表在表面上绘制时的光源。 在 PyCairo 中,图案是您可以读取的东西,用作绘制操作的源或蒙版。 图案可以是实心的,基于表面的或渐变的。

数据来源

为了创建本教程,我们使用了以下材料。 Apple Cocoa 制图指南, PyCairo 参考和Cairo 文档。

PyCairo 后端

原文: https://zetcode/gfx/pycairo/backends/

PyCairo 支持各种后端。 后端是可以显示 PyCairo 产生的图形的地方。 我们使用 PyCairo 创建 PNG 图像,PDF 文件,SVG 文件,然后在 GTK 窗口上绘制。

PNG 图像

在第一个示例中,我们创建一个 PNG 图像。

pngimage.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program uses PyCairo to 
produce a PNG image.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

import cairo

def main():

    ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 390, 60)
    cr = cairo.Context(ims)

    cr.set_source_rgb(0, 0, 0)
    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
        cairo.FONT_WEIGHT_NORMAL)
    cr.set_font_size(40)

    cr.move_to(10, 50)
    cr.show_text("Disziplin ist Macht.")

    ims.write_to_png("image.png")

if __name__ == "__main__":    
    main()

此示例是一个小型控制台应用,可创建 PNG 图像。

import cairo

我们导入 PyCairo 模块。

ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 390, 60)
cr = cairo.Context(ims)

我们创建一个曲面,并从该曲面创建一个 Cairo 上下文。 表面是 390x60 像素的图像。

cr.set_source_rgb(0, 0, 0)

我们用黑色墨水绘制文本。 墨水通过set_source_rgb()方法指定。

cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
    cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)

我们通过select_font_face()方法选择一种字体类型,并通过set_font_size()方法设置字体大小。

cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")

我们将图像内的位置移至x = 10.0y = 50.0并绘制文本。

ims.write_to_png("image.png")

write_to_png()方法将表面的内容写入 PNG 图像。

图:Gnome之眼中的 PNG 图像

PDF 文件

在第二个示例中,我们创建一个简单的 PDF 文件。

pdffile.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program uses PyCairo to 
produce a PDF image.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

import cairo

def main():

    ps = cairo.PDFSurface("pdffile.pdf", 504, 648)
    cr = cairo.Context(ps)

    cr.set_source_rgb(0, 0, 0)
    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
        cairo.FONT_WEIGHT_NORMAL)
    cr.set_font_size(40)

    cr.move_to(10, 50)
    cr.show_text("Disziplin ist Macht.")
    cr.show_page()

if __name__ == "__main__":    
    main()

我们必须在 PDF 查看器中打开 PDF 文件。 Linux 用户可以使用 KPDF 或 Evince 查看器。

ps = cairo.PDFSurface("pdffile.pdf", 504, 648)

要渲染 PDF 文件,我们必须使用cairo.PDFSurface对象创建 PDF 曲面。 PDF 文件的大小以磅为单位指定,这是排版的标准。

cr.show_page()

show_page()完成 PDF 文件的渲染。

图:Evince 中的 PDF 文件

SVG 文件

下一个示例创建一个简单的 SVG(可缩放矢量图形)文件。 SVG 文件是基于 XML 的文件格式。

svgfile.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program uses PyCairo to 
produce a SVG file.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

import cairo

def main():

    ps = cairo.SVGSurface("svgfile.svg", 390, 60)
    cr = cairo.Context(ps)

    cr.set_source_rgb(0, 0, 0)
    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
        cairo.FONT_WEIGHT_NORMAL)
    cr.set_font_size(40)

    cr.move_to(10, 50)
    cr.show_text("Disziplin ist Macht.")
    cr.show_page()

if __name__ == "__main__":    
    main()

我们可以使用网络浏览器(例如 Google Chrome)或矢量绘图程序(例如 Inkscape)打开 SVG 文件。

ps = cairo.SVGSurface("svgfile.svg", 390, 60)

要在 PyCairo 中创建 SVG 文件,我们必须使用cairo.SVGSurface对象创建 SVG 表面。

cr.show_page()

show_page()方法调用完成了 SVG 文件的呈现。

SVG file in Chrome

GTK 窗口

在最后一个示例中,我们在 GTK 窗口上绘制。 该后端将在本教程的其余部分中使用。

gtkwindow.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program uses PyCairo to 
draw on a window in GTK.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

from gi.repository import Gtk
import cairo

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("GTK window")
        self.resize(420, 120)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        cr.set_source_rgb(0, 0, 0)
        cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
            cairo.FONT_WEIGHT_NORMAL)
        cr.set_font_size(40)

        cr.move_to(10, 50)
        cr.show_text("Disziplin ist Macht.")

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

该示例弹出一个居中的 GTK 窗口,在该窗口上绘制"Disziplin ist Macht"文本。

from gi.repository import Gtk
import cairo

我们导入必要的 PyCairo 和 GTK 模块。

darea = Gtk.DrawingArea()

我们将使用Gtk.DrawingArea小部件。

darea.connect("draw", self.on_draw)

重新绘制窗口时,会发出draw信号。 我们将该信号连接到on_draw()回调。

def on_draw(self, wid, cr):
...

绘图是在on_draw()方法内部完成的。 第三个参数是 Cairo 上下文。 它是自动提供给我们的; Cairo 库内置在 GTK 系统中。

图:GTK 窗口

在本章中,我们介绍了受支持的 PyCairo 后端。

PyCairo 中的基本绘图

原文: https://zetcode/gfx/pycairo/basicdrawing/

在 PyCairo 教程的这一部分中,我们绘制了一些基本图元。 我们使用填充和描边操作,笔划线,线帽和线连接。

直线

线是非常基本的矢量对象。 要画一条线,我们使用两个方法调用。 通过move_to()调用指定起点。 线的终点通过line_to()调用指定。

lines.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

In this program, we connect all mouse
clicks with a line.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

from gi.repository import Gtk, Gdk
import cairo

class MouseButtons:

    LEFT_BUTTON = 1
    RIGHT_BUTTON = 3

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)        
        self.add(self.darea)

        self.coords = []

        self.darea.connect("button-press-event", self.on_button_press)

        self.set_title("Lines")
        self.resize(300, 200)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        cr.set_source_rgb(0, 0, 0)
        cr.set_line_width(0.5)

        for i in self.coords:
            for j in self.coords:

                cr.move_to(i[0], i[1])
                cr.line_to(j[0], j[1]) 
                cr.stroke()

        del self.coords[:]            

    def on_button_press(self, w, e):

        if e.type == Gdk.EventType.BUTTON_PRESS \
            and e.button == MouseButtons.LEFT_BUTTON:

            self.coords.append([e.x, e.y])

        if e.type == Gdk.EventType.BUTTON_PRESS \
            and e.button == MouseButtons.RIGHT_BUTTON:

            self.darea.queue_draw()           

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在我们的示例中,我们用鼠标左键随机单击窗口。 每次点击都存储在一个列表中。 当我们右键单击窗口时,所有点都与列表中的每个其他点相连。 右键单击将清除窗口。

class MouseButtons:

    LEFT_BUTTON = 1
    RIGHT_BUTTON = 3

GTK 文档仅声明鼠标左键的编号为 1,鼠标右键的编号为 3。我们创建了一个自定义类,其中包含一些鼠标键的标识符。

self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)   

默认情况下,某些事件未启用。 鼠标按下事件就在其中。 因此,我们需要使用set_event()方法启用鼠标按下事件。

self.darea.connect("button-press-event", self.on_button_press)

在此代码示例中,我们对鼠标按下事件做出反应。

cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)

线条以黑色墨水绘制,宽度为 0.5 点。

for i in self.coords:
    for j in self.coords:

        cr.move_to(i[0], i[1])
        cr.line_to(j[0], j[1]) 
        cr.stroke()

我们将列表中的每个点连接到其他每个点。 stroke()调用画线。

del self.coords[:]    

最后,将删除所有坐标。 现在,我们可以创建另一个对象。

def on_button_press(self, w, e):

    if e.type == Gdk.EventType.BUTTON_PRESS \
        and e.button == MouseButtons.LEFT_BUTTON:

        self.coords.append([e.x, e.y])
...

如果按下鼠标左键,我们会将其 x 和 y 坐标添加到self.coords列表中。

if e.type == Gdk.EventType.BUTTON_PRESS \
    and e.button == MouseButtons.RIGHT_BUTTON:

    self.darea.queue_draw()

在按下鼠标右键的情况下,我们调用queue_draw()方法来重绘绘图区域。 所有的点都用线连接。

图:直线

填充和描边

描边操作绘制形状的轮廓,填充操作填充形状的内部。

fillstroke.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This code example draws a circle
using the PyCairo library.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

from gi.repository import Gtk
import cairo
import math

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Fill & stroke")
        self.resize(230, 150)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        cr.set_line_width(9)
        cr.set_source_rgb(0.7, 0.2, 0.0)

        w, h = self.get_size()      

        cr.translate(w/2, h/2)
        cr.arc(0, 0, 50, 0, 2*math.pi)
        cr.stroke_preserve()

        cr.set_source_rgb(0.3, 0.4, 0.6)
        cr.fill()

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在示例中,我们绘制了一个圆圈,并用纯色填充。

import math

用于绘制圆的pi常数需要此模块。

cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)

我们使用set_line_width()方法设置线宽。 我们使用set_source_rgb()方法将光源设置为深红色。

w, h = self.get_size()     

在这里,我们获得了窗口的宽度和高度。 我们需要这些值使圆在窗口上居中。

cr.translate(w/2, h/2)
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.stroke_preserve()

使用translate()方法,我们将图形原点移动到窗口的中心。 我们希望我们的圈子居中。 arc()方法向 Cairo 图形上下文添加了新的圆形路径。 最后,stroke_preserve()方法绘制圆的轮廓。 与stroke()方法不同,它还保留了形状以供以后绘制。

cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()

我们使用fill()方法更改绘制颜色,并用新颜色填充圆。

图:填充和描边

笔划线

每条线可以用不同的笔划线绘制。 笔笔划线定义线条的样式。 笔划线由set_dash()方法指定。 该模式由笔划线列表设置,笔划线列表是浮点值的列表。 他们设置笔划线图案的开和关部分。 stroke()方法使用笔划线创建一条线。 如果笔划线为 0,则禁用笔划线。 如果笔划线的数量为 1,则假定使用对称模式,其中交替的开和关部分由笔划线中的单个值指定。

def on_draw(self, wid, cr):

    cr.set_source_rgba(0, 0, 0, 1)
    cr.set_line_width(2)

    cr.set_dash([4.0, 21.0, 2.0])

    cr.move_to(40, 30)  
    cr.line_to(250, 30)
    cr.stroke()

    cr.set_dash([14.0, 6.0])

    cr.move_to(40, 50)
    cr.line_to(250, 50)
    cr.stroke()

    cr.set_dash([1.0])

    cr.move_to(40, 70)
    cr.line_to(250, 70)
    cr.stroke()                

我们用三个不同的笔划线画了三条线。

cr.set_dash([4.0, 21.0, 2.0])

我们有三个数字的模式。 我们绘制了 4 个点,未绘制 21 个,绘制了 2 个点,然后绘制了 4 个点,绘制了 21 个点。 和 2 未绘制。 该模式轮流直到行尾。

cr.set_dash([14.0, 6.0])

在这种模式下,我们总是绘制 14 点,未绘制 6 点。

cr.set_dash([1.0])

在这里,我们创建了一个对称图案的笔划线,该图案交替出现单个接通和断开点。

图:笔划线

线帽

线帽是线的端点。

  • Cairo.LINE_CAP_BUTT
  • Cairo.LINE_CAP_ROUND
  • Cairo.LINE_CAP_SQUARE

Cairo 有三种不同的线帽样式。

图:正方形,圆和端帽

带有cairo.LINE_CAP_SQUARE帽的线的大小与带有cairo.LINE_CAP_BUTT帽的线的大小不同。 如果一行的宽度是 x 单位,则带cairo.LINE_CAP_SQUARE上限的行的大小将恰好是 x 单位; 开头x / 2个单位,结尾x / 2个单位。

def on_draw(self, wid, cr):

    cr.set_source_rgba(0, 0, 0, 1)
    cr.set_line_width(12)

    cr.set_line_cap(cairo.LINE_CAP_BUTT)
    cr.move_to(30, 50)
    cr.line_to(150, 50)
    cr.stroke()

    cr.set_line_cap(cairo.LINE_CAP_ROUND)
    cr.move_to(30, 90)
    cr.line_to(150, 90)
    cr.stroke()

    cr.set_line_cap(cairo.LINE_CAP_SQUARE)
    cr.move_to(30, 130)
    cr.line_to(150, 130)
    cr.stroke()

    cr.set_line_width(1.5)

    cr.move_to(30, 35)
    cr.line_to(30, 145)
    cr.stroke()

    cr.move_to(150, 35)
    cr.line_to(150, 145)
    cr.stroke()

    cr.move_to(155, 35)
    cr.line_to(155, 145)
    cr.stroke()

该示例绘制具有三个不同线帽的三条线。 通过绘制三条额外的细垂直线,它还将以图形方式显示线的大小差异。

cr.set_line_width(12)

我们的生产线将是 12 个单位宽。 默认线宽为 2。

cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()

在这里,我们用cairo.LINE_CAP_ROUND帽画一条水平线。

cr.set_line_width(1.5)

cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()

这是用来说明大小差异的三条垂直线之一。

图:直线的端帽

线连接

可以使用三种不同的连接样式来连接线。

  • Cairo.LINE_JOIN_MITER
  • Cairo.LINE_JOIN_BEVEL
  • Cairo.LINE_JOIN_ROUND

图:斜角,圆角,斜接线连接

def on_draw(self, wid, cr):

    cr.set_line_width(14)

    cr.rectangle(30, 30, 100, 100)        
    cr.set_line_join(cairo.LINE_JOIN_MITER)
    cr.stroke()

    cr.rectangle(160, 30, 100, 100)
    cr.set_line_join(cairo.LINE_JOIN_BEVEL)
    cr.stroke()

    cr.rectangle(100, 160, 100, 100)
    cr.set_line_join(cairo.LINE_JOIN_ROUND)
    cr.stroke()

在此示例中,我们绘制了三个具有各种线连接的粗矩形。

cr.set_line_width(14)

线宽为 14 个单位。

cr.rectangle(30, 30, 100, 100)        
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()

在这里,我们绘制一个具有cairo.LINE_JOIN_MITER连接样式的矩形。

图:直线连接

贝塞尔曲线

贝塞尔曲线是由数学公式定义的曲线。 绘制曲线的数学方法由 PierreBézier 在 1960 年代后期创建,用于雷诺的汽车制造。

curve_to(x1, y1, x2, y2, x3, y3)

curve_to()方法将三次贝塞尔曲线样条添加到路径。 参数为第一控制点的 x 和 y 坐标,第二控制点的 x 和 y 坐标以及曲线末端的 x 和 y 坐标。

def on_draw(self, wid, cr):

    cr.move_to(20, 40)
    cr.curve_to(320, 200, 330, 110, 450, 40)        
    cr.stroke()

在此示例中,使用curve_to()方法绘制了贝塞尔曲线。

图:贝塞尔曲线

在 PyCairo 教程的这一章中,我们做了一些基本的绘制。

PyCairo 形状和填充

原文: https://zetcode/gfx/pycairo/shapesfills/

在 PyCairo 教程的这一部分中,我们创建一些基本的和更高级的形状。 我们用纯色,图案和渐变填充这些形状。 渐变将在单独的章节中介绍。

基本形状

PyCairo 有一些创建简单形状的基本方法。

def on_draw(self, wid, cr):

    cr.set_source_rgb(0.6, 0.6, 0.6)

    cr.rectangle(20, 20, 120, 80)
    cr.rectangle(180, 20, 80, 80)
    cr.fill()

    cr.arc(330, 60, 40, 0, 2*math.pi)
    cr.fill()

    cr.arc(90, 160, 40, math.pi/4, math.pi)
    cr.fill()

    cr.translate(220, 180)
    cr.scale(1, 0.7)
    cr.arc(0, 0, 50, 0, 2*math.pi)
    cr.fill()

在此示例中,我们创建一个矩形,一个正方形,一个圆形,一个弧形和一个椭圆形。

cr.rectangle(20, 20, 120, 80)
cr.rectangle(180, 20, 80, 80)

rectangle()方法用于创建正方形和矩形。 正方形只是矩形的一种特定类型。 参数是窗口左上角的 x 和 y 坐标以及矩形的宽度和高度。

cr.arc(330, 60, 40, 0, 2*math.pi)

arc()方法创建一个圆。 参数是弧度中心的 x 和 y 坐标,半径以及弧度的开始和结束角度。

cr.arc(90, 160, 40, math.pi/4, math.pi)

在这里,我们画一条弧,是圆的一部分。

cr.scale(1, 0.7)
cr.arc(0, 0, 50, 0, 2*math.pi)

我们使用scale()arc()方法创建一个椭圆。

图:基本形状

可以使用基本图元的组合来创建其他形状。

complex_shapes.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This code example draws another
three shapes in PyCairo.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

from gi.repository import Gtk
import cairo

class cv(object):

    points = ( 
        ( 0, 85 ), 
        ( 75, 75 ), 
        ( 100, 10 ), 
        ( 125, 75 ), 
        ( 200, 85 ),
        ( 150, 125 ), 
        ( 160, 190 ),
        ( 100, 150 ), 
        ( 40, 190 ),
        ( 50, 125 ),
        ( 0, 85 )
    )

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Complex shapes")
        self.resize(460, 240)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        cr.set_source_rgb(0.6, 0.6, 0.6)
        cr.set_line_width(1)

        for i in range(10):
            cr.line_to(cv.points[i][0], cv.points[i][1])

        cr.fill()

        cr.move_to(240, 40)
        cr.line_to(240, 160)
        cr.line_to(350, 160)
        cr.fill()

        cr.move_to(380, 40)
        cr.line_to(380, 160)
        cr.line_to(450, 160)
        cr.curve_to(440, 155, 380, 145, 380, 40)
        cr.fill()

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在此示例中,我们创建一个星形对象,一个三角形和一个修改后的三角形。 这些对象是使用直线和一条曲线创建的。

for i in range(10):
    cr.line_to(cv.points[i][0], cv.points[i][1])

cr.fill()

通过连接点元组中的所有点来绘制星形。 fill()方法用当前颜色填充星形对象。

cr.move_to(240, 40)
cr.line_to(240, 160)
cr.line_to(350, 160)
cr.fill()

这些线创建一个三角形。 最后两点将自动合并。

cr.move_to(380, 40)
cr.line_to(380, 160)
cr.line_to(450, 160)
cr.curve_to(440, 155, 380, 145, 380, 40)
cr.fill()

修改后的三角形是两条直线和一条曲线的简单组合。

图:复杂形状

填充

填充填充形状的内部。 填充可以是纯色,图案或渐变。

纯色

颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 PyCairo 的有效 RGB 值在 0 到 1 的范围内。

def on_draw(self, wid, cr):

    cr.set_source_rgb(0.2, 0.23, 0.9)
    cr.rectangle(10, 15, 90, 60)
    cr.fill()

    cr.set_source_rgb(0.9, 0.1, 0.1)
    cr.rectangle(130, 15, 90, 60)
    cr.fill()

    cr.set_source_rgb(0.4, 0.9, 0.4)
    cr.rectangle(250, 15, 90, 60)
    cr.fill()           

在示例中,我们绘制了四个彩色矩形。

cr.set_source_rgb(0.2, 0.23, 0.9)
cr.rectangle(10, 15, 90, 60)
cr.fill()

set_source_rgb()方法将源设置为不透明的颜色。 参数是红色,绿色,蓝色强度值。 通过调用fill()方法,该源用于填充矩形的内部。

图:纯色

图案

图案是可以用于填充形状的复杂图形对象。

patterns.py

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program shows how to work
with patterns in PyCairo.

Author: Jan Bodnar
Website: zetcode 
Last edited: April 2016
'''

from gi.repository import Gtk
import cairo

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()
        self.create_surpat()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Patterns")
        self.resize(300, 290)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def create_surpat(self):

        sr1 = cairo.ImageSurface.create_from_png("blueweb.png")
        sr2 = cairo.ImageSurface.create_from_png("maple.png")
        sr3 = cairo.ImageSurface.create_from_png("crack.png")
        sr4 = cairo.ImageSurface.create_from_png("chocolate.png")

        self.pt1 = cairo.SurfacePattern(sr1)
        self.pt1.set_extend(cairo.EXTEND_REPEAT)
        self.pt2 = cairo.SurfacePattern(sr2)
        self.pt2.set_extend(cairo.EXTEND_REPEAT)
        self.pt3 = cairo.SurfacePattern(sr3)
        self.pt3.set_extend(cairo.EXTEND_REPEAT)
        self.pt4 = cairo.SurfacePattern(sr4)
        self.pt4.set_extend(cairo.EXTEND_REPEAT)        

    def on_draw(self, wid, cr):

        cr.set_source(self.pt1)
        cr.rectangle(20, 20, 100, 100)
        cr.fill()

        cr.set_source(self.pt2) 
        cr.rectangle(150, 20, 100, 100)
        cr.fill()

        cr.set_source(self.pt3)
        cr.rectangle(20, 140, 100, 100)
        cr.fill()

        cr.set_source(self.pt4)
        cr.rectangle(150, 140, 100, 100)
        cr.fill()

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在此示例中,我们绘制了四个矩形。 这次我们用一些模式填充它们。 我们使用来自 Gimp 图像处理器的四个图案图像。 我们必须保留这些图案的原始大小,因为我们将它们平铺。

我们在draw()方法之外创建图像表面。 每次需要重新绘制窗口时,从硬盘读取数据都不会很有效。

sr1 = cairo.ImageSurface.create_from_png("blueweb.png")

从 PNG 图像创建图像表面。

self.pt1 = cairo.SurfacePattern(sr1)
self.pt1.set_extend(cairo.EXTEND_REPEAT)

从表面创建图案。 我们将模式设置为cairo.EXTEND_REPEAT,这将导致图案通过重复平铺。

cr.set_source(self.pt1)
cr.rectangle(20, 20, 100, 100)
cr.fill()

在这里,我们绘制第一个矩形。 set_source()方法告诉 Cairo 上下文使用图案作为绘图源。 图像图案可能不完全适合形状。 rectangle()创建一个矩形路径。 最后,fill()方法用源填充路径。

本章介绍了 PyCairo 的形状和填充。

PyCairo 渐变

原文: https://zetcode/gfx/pycairo/gradients/

在 PyCairo 教程的这一部分中,我们将介绍渐变。 我们将提到线性和径向渐变。

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘画程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers)

线性渐变

线性渐变是沿着一条线的颜色混合或颜色阴影混合。 它们由 PyCairo 中的cairo.LinearGradient类表示。

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program works with linear
gradients in PyCairo.

author: Jan Bodnar
website: zetcode 
last edited: August 2012
'''

from gi.repository import Gtk
import cairo

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Linear gradients")
        self.resize(340, 390)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        self.draw_gradient1(cr)
        self.draw_gradient2(cr)
        self.draw_gradient3(cr)

    def draw_gradient1(self, cr):

        lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)

        count = 1

        i = 0.1    
        while i < 1.0: 
            if count % 2:
                lg1.add_color_stop_rgba(i, 0, 0, 0, 1)
            else:
                lg1.add_color_stop_rgba(i, 1, 0, 0, 1)
            i = i + 0.1
            count = count + 1      

        cr.rectangle(20, 20, 300, 100)
        cr.set_source(lg1)
        cr.fill()

    def draw_gradient2(self, cr):        

        lg2 = cairo.LinearGradient(0.0, 0.0, 350.0, 0)

        count = 1

        i = 0.05    
        while i < 0.95: 
            if count % 2:
                lg2.add_color_stop_rgba(i, 0, 0, 0, 1)
            else:
                lg2.add_color_stop_rgba(i, 0, 0, 1, 1)
            i = i + 0.025
            count = count + 1        

        cr.rectangle(20, 140, 300, 100)
        cr.set_source(lg2)
        cr.fill()

    def draw_gradient3(self, cr):        

        lg3 = cairo.LinearGradient(20.0, 260.0,  20.0, 360.0)
        lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1) 
        lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1) 
        lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1) 

        cr.rectangle(20, 260, 300, 100)
        cr.set_source(lg3)
        cr.fill()

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

该示例绘制了三个填充有线性渐变的矩形。

lg3 = cairo.LinearGradient(20.0, 260.0,  20.0, 360.0)

在这里,我们创建一个线性渐变。 参数指定直线,沿着该直线绘制渐变。 这是一条水平线。

lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1) 
lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1) 
lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1) 

我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该黑色将停止在大小的 1/10 处。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘画,直到结束。

图:线性渐变

径向渐变

径向渐变是两个圆之间颜色或阴影的混合。 cairo.RadialGradient类用于在 PyCairo 中创建径向渐变。

#!/usr/bin/python

'''
ZetCode PyCairo tutorial

This program works with radial
gradients in PyCairo.

author: Jan Bodnar
website: zetcode 
last edited: August 2012
'''

from gi.repository import Gtk
import cairo
import math

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Radial gradients")
        self.resize(300, 200)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_draw(self, wid, cr):

        self.draw_gradient1(cr)
        self.draw_gradient2(cr)

    def draw_gradient1(self, cr):

        cr.set_source_rgba(0, 0, 0, 1)
        cr.set_line_width(12)

        cr.translate(60, 60)

        r1 = cairo.RadialGradient(30, 30, 10, 30, 30, 90)
        r1.add_color_stop_rgba(0, 1, 1, 1, 1)
        r1.add_color_stop_rgba(1, 0.6, 0.6, 0.6, 1)
        cr.set_source(r1)
        cr.arc(0, 0, 40, 0, math.pi * 2)
        cr.fill()

        cr.translate(120, 0)

    def draw_gradient2(self, cr):        

        r2 = cairo.RadialGradient(0, 0, 10, 0, 0, 40)
        r2.add_color_stop_rgb(0, 1, 1, 0)
        r2.add_color_stop_rgb(0.8, 0, 0, 0)
        cr.set_source(r2)
        cr.arc(0, 0, 40, 0, math.pi * 2)
        cr.fill()   

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在示例中,我们绘制了两个径向渐变。

r1 = cairo.RadialGradient(30, 30, 10, 30, 30, 90)
r1.add_color_stop_rgba(0, 1, 1, 1, 1)
r1.add_color_stop_rgba(1, 0.6, 0.6, 0.6, 1)
cr.set_source(r1)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()

我们画一个圆,并用径向渐变填充其内部。 径向梯度由两个圆定义。 add_color_stop_rgba()方法定义颜色。 我们可以试验圆的位置或半径的长度。 在第一个渐变示例中,我们创建了一个类似于 3D 形状的对象。

r2 = cairo.RadialGradient(0, 0, 10, 0, 0, 40)
r2.add_color_stop_rgb(0, 1, 1, 0)
r2.add_color_stop_rgb(0.8, 0, 0, 0)
cr.set_source(r2)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()  

在此示例中,定义径向渐变的圆和自定义绘制的圆具有共同的中心点。

图:径向渐变

在本章中,我们介绍了 PyCairo 渐变。

PyCairo 剪裁&遮罩

原文: https://zetcode/gfx/pycairo/clipmask/

在 PyCairo 教程的这一部分中,我们将讨论剪切和遮罩操作。

剪裁

剪裁是将绘图限制为特定区域。 这样做是出于效率方面的考虑,并会产生有趣的效果。 PyCairo 具有clip()方法来设置裁剪。

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program shows how to perform
clipping in PyCairo.

author: Jan Bodnar
website: zetcode 
last edited: August 2012
'''

from gi.repository import Gtk, GLib
import cairo
import math
import random

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()
        self.load_image()
        self.init_vars()

    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)

        GLib.timeout_add(100, self.on_timer)

        self.set_title("Clipping")
        self.resize(300, 200)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def load_image(self):

        self.image = cairo.ImageSurface.create_from_png("beckov.png")

    def init_vars(self):    

        self.pos_x = 128
        self.pos_y = 128
        self.radius = 40

        self.delta = [3, 3]        

    def on_timer(self):

        self.pos_x += self.delta[0]
        self.pos_y += self.delta[1]        

        self.darea.queue_draw()
        return True           

    def on_draw(self, wid, cr):

        w, h = self.get_size()

        if (self.pos_x < 0 + self.radius):
            self.delta[0] = random.randint(5, 9)
        elif (self.pos_x > w - self.radius):
            self.delta[0] = -random.randint(5, 9)

        if (self.pos_y < 0 + self.radius): 
            self.delta[1] = random.randint(5, 9)
        elif (self.pos_y > h - self.radius):
            self.delta[1] = -random.randint(5, 9)        

        cr.set_source_surface(self.image, 1, 1)
        cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)
        cr.clip()
        cr.paint()        

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在此示例中,我们将裁剪图像。 圆圈在窗口区域上移动并显示基础图像的一部分。 这就像我们从孔中看一样。

def load_image(self):

    self.image = cairo.ImageSurface.create_from_png("beckov.png")

这是基础图像。 每个计时器周期,我们都会看到此图像的一部分。

if (self.pos_x < 0 + self.radius):
    self.delta[0] = random.randint(5, 9)
elif (self.pos_x > w - self.radius):
    self.delta[0]= -random.randint(5, 9) 

如果圆碰到窗口的左侧或右侧,则圆的移动方向会随机变化。 顶部和底部也一样。

cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)

此行为 Cairo 上下文添加了一条循环路径。

cr.clip()

clip()设置剪切区域。 裁剪区域是当前使用的路径。 当前路径是通过arc()方法调用创建的。

cr.paint()

paint()在当前剪裁区域内的任何地方绘制当前源。

图:剪裁

遮罩

在将源应用于表面之前,先对其进行过滤。 遮罩用作过滤器。 遮罩确定在哪里应用源,在哪里不应用。 遮罩的不透明部分允许复制源。 透明零件不允许将源复制到表面。

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program demonstrates masking.

author: Jan Bodnar
website: zetcode 
last edited: August 2012
'''

from gi.repository import Gtk
import cairo

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()
        self.load_image()

    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Masking")
        self.resize(310, 100)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def load_image(self):    

        self.ims = cairo.ImageSurface.create_from_png("omen.png")

    def on_draw(self, wid, cr):

        cr.mask_surface(self.ims, 0, 0);
        cr.fill()

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

在该示例中,遮罩确定在哪里绘画和在哪里不绘画。

cr.mask_surface(self.ims, 0, 0);
cr.fill()

我们使用图像作为遮罩,从而将其显示在窗口上。

图:遮罩

蒙蔽效果

在此代码示例中,我们将忽略图像。 这类似于我们使用卷帘所做的。

#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program creates a blind down
effect using masking operation.

author: Jan Bodnar
website: zetcode 
last edited: August 2012
'''

from gi.repository import Gtk, GLib
import cairo
import math

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()

        self.init_ui()
        self.load_image()
        self.init_vars()

    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)

        GLib.timeout_add(35, self.on_timer)

        self.set_title("Blind down")
        self.resize(325, 250)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def load_image(self):

        self.image = cairo.ImageSurface.create_from_png("beckov.png")        

    def init_vars(self):        

        self.timer = True
        self.h = 0
        self.iw = self.image.get_width()
        self.ih = self.image.get_height()   

        self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 
            self.iw, self.ih)           

    def on_timer(self):

        if (not self.timer):
            return False

        self.darea.queue_draw()
        return True           

    def on_draw(self, wid, cr):

        ic = cairo.Context(self.ims)

        ic.rectangle(0, 0, self.iw, self.h)
        ic.fill()

        self.h += 1

        if (self.h == self.ih): 
            self.timer = False

        cr.set_source_surface(self.image, 10, 10)
        cr.mask_surface(self.ims, 10, 10)        

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

盲目效应背后的想法很简单。 图像高度为 h 像素。 我们绘制高度为 1px 的 0、1、2 … 线。 每个周期,图像的一部分高 1px,直到整个图像可见为止。

def load_image(self):

    self.image = cairo.ImageSurface.create_from_png("beckov.png")            

load_image()方法中,我们从 PNG 图像创建图像表面。

def init_vars(self):        

    self.timer = True
    self.h = 0
    self.iw = self.image.get_width()
    self.ih = self.image.get_height()   

    self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 
        self.iw, self.ih)               

init_vars()方法中,我们初始化一些变量。 我们启动self.timerself.h变量。 我们得到加载图像的宽度和高度。 然后我们创建一个空的图像表面。 它将用我们之前创建的图像表面的像素线填充。

ic = cairo.Context(self.ims)

我们从空图像源创建一个 cairo 上下文。

ic.rectangle(0, 0, self.iw, self.h)
ic.fill()

我们在最初为空的图像中绘制一个矩形。 矩形每个周期将高出 1 像素。 以这种方式创建的图像稍后将用作遮罩。

self.h += 1

要显示的图像高度增加一个单位。

if (self.h == self.ih): 
    self.timer = False

当我们在 GTK 窗口上绘制整个图像时,我们将停止timer方法。

cr.set_source_surface(self.image, 10, 10)
cr.mask_surface(self.ims, 10, 10)

城堡的图像被设置为绘画的来源。 mask_surface()使用表面的 Alpha 通道作为遮罩来绘制电流源。

本章介绍了 PyCairo 中的剪裁和遮罩。

发布者:admin,转转请注明出处:http://www.yc00.com/web/1754948107a5219464.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信