原文: http://zetcode.com/gui/csharpqyoto/paintingII/

在 Qyoto C# 编程教程的这一部分中,我们将继续绘图。 我们将提供一些更复杂的示例。

甜甜圈形状

第一个示例通过旋转一堆椭圆来创建复杂的形状。

  1. using System;
  2. using QtCore;
  3. using QtGui;
  4. /**
  5. * ZetCode Qyoto C# tutorial
  6. *
  7. * This program draws a donut
  8. * shape.
  9. *
  10. * @author Jan Bodnar
  11. * website zetcode.com
  12. * last modified November 2012
  13. */
  14. public class QyotoApp : QMainWindow
  15. {
  16. public QyotoApp()
  17. {
  18. WindowTitle = "Donut";
  19. PaintEvent += OnPaintEvent;
  20. Resize(350, 280);
  21. Move(300, 300);
  22. Show();
  23. }
  24. private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
  25. {
  26. QPainter ptr = new QPainter(this);
  27. DrawDonut(ptr);
  28. ptr.End();
  29. }
  30. void DrawDonut(QPainter ptr)
  31. {
  32. QColor col = new QColor();
  33. col.SetNamedColor("#333333");
  34. ptr.Pen = new QPen(col, 0.5);
  35. ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);
  36. int h = Height;
  37. int w = Width;
  38. ptr.Translate(new QPoint(w/2, h/2));
  39. for (double rot=0; rot < 360.0; rot+=5.0 )
  40. {
  41. ptr.DrawEllipse(-125, -40, 250, 80);
  42. ptr.Rotate(5.0);
  43. }
  44. }
  45. [STAThread]
  46. public static int Main(String[] args)
  47. {
  48. new QApplication(args);
  49. new QyotoApp();
  50. return QApplication.Exec();
  51. }
  52. }

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

  1. QColor color = new QColor();
  2. color.SetNamedColor("#333333");

我们可以使用十六进制表示法来创建颜色对象。

  1. int h = Height;
  2. int w = Width;

在这里,我们确定窗口的宽度和高度。

  1. ptr.Translate(new QPoint(w/2, h/2));

我们将坐标系移到窗口的中间。 这样,我们使绘图在数学上更容易。

  1. for (double rot=0; rot < 360.0; rot+=5.0 )
  2. {
  3. ptr.DrawEllipse(-125, -40, 250, 80);
  4. ptr.Rotate(5.0);
  5. }

我们绘制一个椭圆对象 72 次。 每次,我们将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。

Qyoto 中的绘图 II - 图1

图:多纳圈

灰度图像

在下面的示例中,我们将创建一个灰度图像。

  1. using System;
  2. using QtGui;
  3. using QtCore;
  4. /**
  5. * ZetCode Qyoto C# tutorial
  6. *
  7. * In this example, we create a
  8. * grayscale image.
  9. *
  10. * @author Jan Bodnar
  11. * website zetcode.com
  12. * last modified November 2012
  13. */
  14. public class QyotoApp : QMainWindow
  15. {
  16. QImage sid;
  17. int w, h = 0;
  18. public QyotoApp()
  19. {
  20. WindowTitle = "Gray scale";
  21. PaintEvent += OnPaintEvent;
  22. LoadImage();
  23. Resize(320, 150);
  24. Move(300, 300);
  25. Show();
  26. }
  27. private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
  28. {
  29. QPainter ptr = new QPainter(this);
  30. DrawImages(ptr);
  31. ptr.End();
  32. }
  33. void DrawImages(QPainter ptr)
  34. {
  35. ptr.DrawImage(5, 15, sid);
  36. ptr.DrawImage(w + 10, 15, GrayScale(sid.Copy()));
  37. }
  38. void LoadImage()
  39. {
  40. sid = new QImage("smallsid.jpg");
  41. w = sid.Width();
  42. h = sid.Height();
  43. }
  44. QImage GrayScale(QImage img)
  45. {
  46. for (int i=0; i < w; i++)
  47. {
  48. for (int j=0; j < h; j++)
  49. {
  50. uint c = img.Pixel(i, j);
  51. int gray = Global.qGray(c);
  52. int alpha = Global.qAlpha(c);
  53. img.SetPixel(i, j, Global.qRgba(gray, gray,
  54. gray, alpha));
  55. }
  56. }
  57. return img;
  58. }
  59. public static int Main(String[] args)
  60. {
  61. new QApplication(args);
  62. new QyotoApp();
  63. return QApplication.Exec();
  64. }
  65. }

我们有一个彩色 JPG 图像。 我们把它画在窗口上。 我们创建图像的副本,将其转换为灰度并在原始图像旁边的窗口上绘制。

  1. void LoadImage()
  2. {
  3. sid = new QImage("smallsid.jpg");
  4. w = sid.Width();
  5. h = sid.Height();
  6. }

LoadImage()方法中,我们加载图像并获取其宽度和高度。

  1. QImage GrayScale(QImage img)
  2. {
  3. for (int i=0; i < w; i++)
  4. {
  5. for (int j=0; j < h; j++)
  6. {
  7. uint c = img.Pixel(i, j);
  8. int gray = Global.qGray(c);
  9. int alpha = Global.qAlpha(c);
  10. img.SetPixel(i, j, Global.qRgba(gray, gray,
  11. gray, alpha));
  12. }
  13. }
  14. return img;
  15. }

GrayScale()方法将图像转换为灰度并返回。 我们遍历图像的所有像素。 Pixel()方法返回有问题的像素。 我们使用Global.qGray()方法来获取特定像素的灰度值。 同样,我们获得 alpha 值。 最后,我们使用SetPixel()方法修改像素。 我们将灰色值用于颜色的红色,绿色和蓝色部分。

Qyoto 中的绘图 II - 图2

图:灰度图像

反射

在下一个示例中,我们显示反射图像。 该效果使人产生幻觉,好像图像在水中被反射一样。

  1. using System;
  2. using QtGui;
  3. using QtCore;
  4. /**
  5. * ZetCode Qyoto C# tutorial
  6. *
  7. * In this example we create a reflected image.
  8. *
  9. * @author Jan Bodnar
  10. * website zetcode.com
  11. * last modified November 2012
  12. */
  13. public class QyotoApp : QMainWindow
  14. {
  15. QImage img;
  16. QImage reflected_img;
  17. int iw, ih = 0;
  18. double initial_opacity = 0.7;
  19. double opacity = 0.7;
  20. double step = 0;
  21. const int GAP = 30;
  22. public QyotoApp()
  23. {
  24. WindowTitle = "Reflection";
  25. PaintEvent += OnPaintEvent;
  26. InitExample();
  27. Resize(300, 400);
  28. Move(150, 150);
  29. Show();
  30. }
  31. private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
  32. {
  33. QPainter ptr = new QPainter(this);
  34. DrawImages(ptr);
  35. ptr.End();
  36. }
  37. void InitExample()
  38. {
  39. img = new QImage("slanec.png");
  40. if (img.IsNull())
  41. {
  42. Console.WriteLine("Error loading image");
  43. }
  44. iw = img.Width();
  45. ih = img.Height();
  46. step = opacity / ih;
  47. reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);
  48. CreateReflectedImage();
  49. }
  50. void CreateReflectedImage()
  51. {
  52. QPainter fptr = new QPainter(reflected_img);
  53. int i = 0;
  54. double opacity = 0.7;
  55. while (i < ih)
  56. {
  57. i++;
  58. opacity = opacity - step;
  59. fptr.Opacity = initial_opacity-opacity;
  60. fptr.DrawImage(0, i, img, 0, i, -1, 1);
  61. }
  62. fptr.End();
  63. }
  64. void DrawImages(QPainter ptr)
  65. {
  66. int w = Width;
  67. int h = Height;
  68. ptr.FillRect(0, 0, w, h, Qt.GlobalColor.black);
  69. ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);
  70. QRect r = new QRect(25, 15, iw, ih);
  71. ptr.DrawImage(r, img);
  72. ptr.Translate(0, 2 * ih + GAP);
  73. ptr.Scale(1, -1);
  74. ptr.DrawImage(25, 0, reflected_img);
  75. }
  76. public static int Main(String[] args)
  77. {
  78. new QApplication(args);
  79. new QyotoApp();
  80. return QApplication.Exec();
  81. }
  82. }

我们从当前工作目录加载图像。 我们创建另一个相同大小的空图像。 我们将原始图像逐行复制到新的空白图像,并逐渐增加透明度。

  1. img = new QImage("slanec.png");
  2. if (img.IsNull())
  3. {
  4. Console.WriteLine("Error loading image");
  5. }

我们加载一个 PNG 图片,并进行一些错误检查。

  1. iw = img.Width();
  2. ih = img.Height();
  3. step = opacity / ih;

我们得到图像的宽度和高度。 步进变量控制第二张图像淡出的强度。

  1. reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);

创建一个新的空图像。 它具有原始图像的大小。

  1. void CreateReflectedImage()
  2. {
  3. QPainter fptr = new QPainter(reflected_img);
  4. ...

CreateReflectedImage()方法中,我们绘制空白图像。

  1. while (i < ih)
  2. {
  3. i++;
  4. opacity = opacity - step;
  5. fptr.Opacity = initial_opacity-opacity;
  6. fptr.DrawImage(0, i, img, 0, i, -1, 1);
  7. }

我们将原始图像复制到新图像。 逐行。 不透明度在每个循环中逐步降低。

  1. QRect r = new QRect(25, 15, iw, ih);
  2. ptr.DrawImage(r, img);

第一个图像绘制在窗口上。

  1. ptr.Translate(0, 2 * ih + GAP);
  2. ptr.Scale(1, -1);
  3. ptr.DrawImage(25, 0, reflected_img);

在这里,我们将第二个图像向下移动,比原始图像低一些。 Scale()方法将图像上下翻转。 请注意,平移是图像高度的两倍。 这是必要的,因为缩放操作不仅会翻转图像,还会使图像向上移动。 要了解这一点,只需拍摄一张照片,将其放在桌子上并翻转即可。

Qyoto 中的绘图 II - 图3

图:反射图像

等待效果

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一条线在移动的错觉。 此类效果通常用于通知用户幕后正在进行繁重的任务。 一个示例是通过互联网流式传输视频。

  1. using System;
  2. using QtGui;
  3. using QtCore;
  4. /**
  5. * ZetCode Qyoto C# tutorial
  6. *
  7. * This program draws basic shapes
  8. * available in Qyoto.
  9. *
  10. * @author Jan Bodnar
  11. * website zetcode.com
  12. * last modified November 2012
  13. */
  14. public class QyotoApp : QMainWindow
  15. {
  16. int count = 0;
  17. double[,] trs =
  18. {
  19. { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
  20. { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
  21. { 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 },
  22. { 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 },
  23. { 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 },
  24. { 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 },
  25. { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 },
  26. { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0 }
  27. };
  28. public QyotoApp()
  29. {
  30. WindowTitle = "Waiting";
  31. PaintEvent += OnPaintEvent;
  32. InitExample();
  33. Resize(300, 200);
  34. Move(300, 300);
  35. Show();
  36. }
  37. private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
  38. {
  39. QPainter ptr = new QPainter(this);
  40. DrawLines(ptr);
  41. ptr.End();
  42. }
  43. void InitExample()
  44. {
  45. count = 0;
  46. StartTimer(105);
  47. }
  48. void DrawLines(QPainter ptr)
  49. {
  50. QPen pen = new QPen();
  51. pen.Width = 3;
  52. pen.CapStyle = PenCapStyle.RoundCap;
  53. int w = Width;
  54. int h = Height;
  55. ptr.Translate(w/2, h/2);
  56. ptr.Pen = pen;
  57. int len = trs.GetLength(0);
  58. for (int i=0; i < len; i++)
  59. {
  60. ptr.Opacity = trs[count%8, i];
  61. ptr.DrawLine(0, -10, 0, -40);
  62. ptr.Rotate(45);
  63. }
  64. }
  65. protected override void OnTimerEvent(QTimerEvent e)
  66. {
  67. count++;
  68. Repaint();
  69. }
  70. public static int Main(String[] args)
  71. {
  72. new QApplication(args);
  73. new QyotoApp();
  74. return QApplication.Exec();
  75. }
  76. }

我们用八个不同的 alpha 值绘制八条线。

  1. double[,] trs =
  2. {
  3. { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
  4. { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
  5. ...

这是透明度值的数组。 有 8 行,每行一个位置。 8 行中的每行将连续使用这些值。

  1. count = 0;
  2. StartTimer(105);

在这里,我们启动计数值并启动一个计时器。

  1. QPen pen = new QPen();
  2. pen.Width = 3;
  3. pen.CapStyle = PenCapStyle.RoundCap;

我们使线条更粗一些,以使它们更加可见。 我们用圆帽画线。 带圆帽的线条看起来更好。

  1. for (int i=0; i < len; i++)
  2. {
  3. ptr.Opacity = trs[count%8, i];
  4. ptr.DrawLine(0, -10, 0, -40);
  5. ptr.Rotate(45);
  6. }

在此循环中,我们设置不透明度值。 我们画线并旋转它。 这产生了移动和渐隐线的错觉。

  1. protected override void OnTimerEvent(QTimerEvent e)
  2. {
  3. count++;
  4. Repaint();
  5. }

每次调用计时器事件时,我们都会增加计数值并重新绘制窗口区域。

Qyoto 中的绘图 II - 图4

图:等待 effect

在 Qyoto C# 编程教程的这一部分中,我们结束了有关在 Qyoto 中绘图的讨论。